Android面试宝典之《java基础知识点》

写在前面

最近不是很忙,想通过整理下面试题来对自己的知识点进行查漏补缺。本系列打算按照之前在简书上看到一篇《最全的BAT大厂面试题整理》所列的知识点分类进行整理,欢迎志同道合的小伙伴在评论区一起交流学习,如有发现博客中有遗漏或不对之处欢迎批评指正。

知识点目录

  • java中==和equals和hashCode的区别
  • int、char、long各占多少字节数
  • int与integer的区别
  • 谈谈对java多态的理解
  • String、StringBuffer、StringBuilder区别
  • 什么是内部类?内部类的作用
  • 抽象类和接口区别
  • 抽象类的意义
  • 抽象类与接口的应用场景
  • 抽象类是否可以没有方法和属性?
  • 接口的意义
  • 泛型中extends和super的区别
  • 父类的静态方法能否被子类重写
  • 进程和线程的区别
  • final,finally,finalize的区别
  • 序列化的方式
  • Serializable 和Parcelable 的区别
  • 静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
  • 静态内部类的设计意图
  • 成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用
  • 谈谈对kotlin的理解
  • 闭包和局部内部类的区别
  • string 转换成 integer的方式及原理

知识点整理总结

1. java中==和equals和hashCode的区别

在java中 == 是运算符,用于比较两个变量是否相等;equals是object类的方法,用于比较两个对象是否相等,默认object中的equals方法是比较两个对象的地址,跟 == 的结果一样。hashCode也是object类的一个方法,返回一个离散的int型整数,在集合类操作中使用,为了提高查询速度。
java中数据类型可以分为两类:基本数据类型,也称原始数据类型(8大基本数据类型:byte,short,int,long,char,float,double,boolean),他们之间的比较应用 == ,比较的是他们的值。复合数据类型,当他们用 == 比较时比较的是他们内存中的存放地址,所以只有是同一个new出来的对象比较结果才是true。java当中所有的类都是继承自object这个基类,在objec基类中定义了一个equals方法,这个方法的初始行为是比较对象的内存地址,但是在一些类库当中这个方法被覆写了,如String,Integer,Date等,在这些类当中不再是比较类在堆内存中的存放地址了。所以在复合数据类型之间进行equals比较,在没有覆盖equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值,因为objec中的equals方法也是用 == 进行比较的,所以比较后的结果和 == 的结果相同。
如果两个对象根据equals方法比较是相等的,则两个对象的hashCode方法返回的值肯定相等;如果两个对象根据equals方法比较是不等的,则两个对象的hashCode方法返回的值不一定相等。所以集合操作时有如下规则,将对象放入集合中时,首先会判断放入对象的hashCode值是否与集合中的任意一个元素的hashCode值是否相等,如不相等直接放入,如相等再通过equals方法判断放入对象与集合中的任意一个对象是否相等,如equals判断不等,直接放入,否则不放入。在集合get的时候,HashMap也先调用key.hashCode()算出数组下标,然后看equals如果是true就是找到了

2. int、char、long各占多少字节数

int 4个字节,char 1个字节,long 4个字节。

3. int与integer的区别

int是java基本数据类型,Integer是int的包装类,从java5开始引入了自动装箱/拆箱机制,使二者可以相互转换,Integer必须实例化后才能使用,int则不需要,Integer实际是对象的引用,当new 一个Integer时,实际上是生成一个指针指向此对象,而int则是直接存储数值,integer默认值是null,int默认值是0。Integer直接指定值[-128,127]之间时不会new 对象而是直接从取缓存数据。java中类似的包装类还有,Boolean对应boolean,Character对应char,Byte对应byte,Short对应short,Long对应long,Float对应float,Double对应double,这些包装类的引入是为了能将对应的基本数据类型当成对象方便操作。

4. 谈谈对java多态的理解

多态,继承,封装是java的三大特性。
java中的多态又称“动态绑定”是指在“执行期间”(而非编译期间)判断所引用的实际对象类型,根据其实际的类型调用其相应的方法。所以实际当中是通过动态去找要调用方法的,new的是谁就找谁的方法,动态绑定帮助我们的程序可扩展性达到了极致。多态的存在有三个必要条件:要有继承(两个类存在继承关系);要有重写(在子类里面重写从父类继承下来的方法);父类引用指向子类对象。三个条件一旦满足,当你调用父类里面被重写的方法时,实际当中new的时哪个子类对象,就会调用该子类对象重写后的方法。

5. String、StringBuffer、StringBuilder区别

String是常量一旦声明就不可更改,StringBuffer和StringBuilder是变量声明后可修改。StringBuffer中很多方法中带有synchronized关键字是线程安全的,而StringBuilder是线程不安全的。因为上面两个特性,三者的执行速度:StringBuilder > StringBuffer > String。
使用场景:String 适用于少量字符串操作的情况;StringBuilder 适用于单线程下在字符串缓冲区进行的大量操作的情况;StringBuffer 适用于多线程下在字符缓冲区进行大量操作的情况

6. 什么是内部类?内部类的作用

在java中可以将一个类定义在另一个类里面或者一个方法里面,这样的类叫做内部类。内部类分为:成员内部类,静态内部类,匿名内部类和局部内部类。
内部类的作用有:每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个实现,对于内部类没有影响,内部类使得多继承的解决方案变得完整。方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。方便编写事件驱动程序。方便编写线程代码。

7. 抽象类和接口区别

抽象类中可以有非抽象方法,接口中不能有非抽象方法;抽象类中可以有私有的成员变量和方法,接口中只能有公共静态(public static final)常量且方法全部为公共抽象(public abstract)方法;一个普通类只能继承(extends)一个抽象类,一个普通类可以实现(implements)多个接口类;抽象类只能继承一个类,接口可以继承多个接口类;(注:在指定java编译环境为1.8时可以在接口中声明静态方法,且不用强制子类来实现它。)

8. 抽象类的意义

在java面向对象的概念中,所有对象都是通过类来描绘的,但并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体对象,这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但本质上相同的具体概念的抽象。

9. 抽象类与接口的应用场景

抽象类应用场景:有一些方法且想让它们中的一部分有默认实现,另一部分由继承他的子类实现;基本功能可能需要不断改变。
接口的应用场景:所有的方法都需要子类去实现;想实现多继承,由于java不支持多继承,子类不能够继承多个类,但可以实现多个接口。

10. 抽象类是否可以没有方法和属性?

可以,但是这样的抽象类没有任何存在的意义。因为抽象类存在的意义就是对一类事物的公共概念抽取的抽取,抽取出的特性(方法)可以有默认实现也可以由继承它的子类去实现。

11. 接口的意义

java中的接口可以理解为是一种规范或是一种协议,是对系统某些公共行为的抽象。接口的定义一方面可以方便多人协同开发,比如我开发的部分功能需要你来提供实现,那么我们就可以通过接口来定义约束,那么最终你只需要提供对应接口实现的实例就可以使用我开发的功能;另一方面接口的定义有利于降低代码模块之间的耦合度,达到高内聚低耦合的设计目的,比如随着程序的演化发现部分功能实现需要更新,如果使用接口方式只需更改接口实现类的具体实现即可,无需更改方法调用类的逻辑。

12. 泛型中extends和super的区别

”<? extends T>“上界通配符,限定了元素的上界,即使用该通配符的元素类型只能是T和T的子类,且使用上界通配符的类只能对外提供元素,不能向内添加元素;”<? super T> “下界通配符,限定了元素的下届,即使用该通配符的元素类型只能是T和T的基类,且使用下届通配符的类只能向内添加元素,不能对外提供元素。
所以在使用场景上遵循PECS(Produce Extends Consumer Super)原则,即频繁往外读取内容的用上界extends,经常向内添加元素的用下届super。

13. 父类的静态方法能否被子类重写

不能,子类重写父类方法只能是普通方法,静态方法可以继承不能被重写,子类中可以存在和父类同方法名且同出入参的静态方法,但是该方法不是重写的方法,即不会覆盖父类的方法,也不存在多态的性质,调用时会根据是子类或父类的引用去调用其对应的方法。

14. 进程和线程的区别

进程是系统进行资源分配和调度的一个独立单位,线程是进程的一个实体,是cpu调度和分配的基本单位。一个程序至少有一个进程,一个进程至少有一个线程。
在系统开销方面,每个进程需要独立的地址空间,系统开销比较大,而线程虽拥有独立的堆栈空间,但是共享数据,彼此之间使用相同的地址控件,具有开销较小、切换快、效率高的特点。
在程序健壮性方面,因进程之间有相互独立的地址空间,所以其安全性较高,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是进程中的不同执行路径,一个线程死掉就等于整个进程死掉。
在通信机制方面,不同进程之间因相互独立所以进程间通信相对复杂,而同一进程中的不同线程因共享数据段所以通信机制很方便。

15. 进程和线程使用场景选择

需要频繁创建销毁的优先使用线程,因为进程的创建销毁过程代价较大;需要大量计算、频繁切换、耗时的操作优先使用线程;因为对cpu系统的效率使用上线程更占优势,所以可能要发展到多机分布的用进程,多核分布的用线程;需要更稳定安全时,选择进程,需要速度时选择线程。

16. final,finally,finalize的区别

final是一种关键字、修饰符,可以用来修饰类,方法及变量,被它修饰的元素具有不可变的特性。如final修饰的变量是一个常量,定义后变量的值就不能在被修改;final修饰的方法只能被调用,不能被覆盖重写,但是可以被重载;final修饰的类不能被其它类继承。
finally是一种异常处理机制,只能和try/catch结合使用,finally包裹的代码块意为最终要执行的代码块,不管try包裹的代码块是否发生异常均会执行。常用在io流处理可能产生异常的场景,finally代码块中执行对io流的关闭和释放操作。
finalize是一个方法名,它属于Object的一个方法,是在GC清理它所属对象时被调用的,所以Object的任意子类都可以重写此方法,在其中释放系统资源或做其它清理工作。但是它因为不能确定调用时机所以不建议被使用,我们应该在程序中明确的时机去释放资源,不能依赖finalize()方法。

17. 序列化的方式

java原生序列化:JavaBean通过实现Serializable接口实现序列化,虽无强制要求但建议声明serialVersionUID达到更好的向上向下的兼容性
FastJson序列化:阿里巴巴提供的sdk,导入后使用其api可以将JavaBean转化为String字符串完成序列化,可以将字符串反序列化为JavaBean
ProtoBuff序列化:可以导入百度封装的jprotobuff框架避免编写.proto文件,通过工具中的注解完成JavaBean的序列化和反序列化操作
内存占用:jprotobuff < fastjson < serializable ; 执行效率:serializable > jprotobuff > fastjson

18. Serializable 和Parcelable 的区别

Serializable和Parcelable是Android代码中常用的两种序列化方式,其中Serializable是java支持的序列化方式,使用简单,需要序列化的对象只需要实现Serializable接口即可,不需要实现任何方法,Serializable只是一个标识接口,java内部通过反射进行序列化操作,这种机制会在序列化的时候创建大量临时对象,容易触发gc,而Parcelable是Android内部支持的一种序列化方式,它的原理是将需要序列化的对象进行分解,分解后的每一部分都是intent所支持的数据类型,使用Parcelable实现序列化相对比较复杂,除要实现Parcelable接口外还需复写describeContents()和writeToParcel()方法,还需要实例化静态内部对象CREATOR实现Parcelable.Creator接口,使用Parcelable性能比Serializable较高,Parcelable不能使用在需要将数据存储在磁盘上的情况。
所以在使用选择时:Android内部的数据传递,内存缓存选择使用Parcelable,内存开销小效率高;需存储到磁盘和网络传输数据的场景使用Serializable。

19. 静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?

可以被继承,不可以被重写,因为静态属性和静态方法是属于类的,不是属于某个类的实例,可以之间通过类名.属性/方法去调用,如果子类中存在和父类中相同的静态属性或静态方法,此时父类中对应的静态属性和静态方法称为“隐藏”,在调用时根据是子类或者父类的引用调用不同的静态方法或静态属性,正因静态属性和静态方法不可以被重写,所以不能实现多态,不能实现父类的引用指向不同子类对象的操作。

20. 静态内部类的设计意图

内部类的设计意图:问题6的回答。
静态内部类设计意图:与普通内部类不同,静态内部类的创建不需要依赖外部类的实例,所以静态内部类中不存在外部类的引用,静态内部类不可引用外部类中非static的成员,这两条属性也决定了静态内部类的设计意图,当外部类需要私有内部类,而内部类无需外部类资源,并且内部类可以单独创建时,需要使用静态内部类,常用场景如Builder模式(类的构造器有多个参数,且大多数参数都是可选时使用Builder模式)。

21. 成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用

首先理解内部类的定义和作用:问题6的回答。
成员内部类:它是外部类的一个成员,可以无限制访问外部类所有成员的属性和方法,成员内部类中不能存在任何static的变量和方法,使用时需先创建了外部类才能创建内部类;项目中应用场景最多的内部类,一般用在该内部类和外部类存在直接逻辑关系,不会单独使用的场景。
静态内部类:见问题20回答。
局部内部类:嵌套在方法中,其只能在该方法中使用,不能加访问修饰符,不能与外部类重名,可以直接访问外部类中的所有成员变量和方法,但是只能访问其嵌套方法中final修饰的局部变量;局部内部类一般用在解决复杂问题时需创建一个辅助类,且不希望这个类是公共可用的。
匿名内部类:没有类名的内部类,当所在方法的形参需被匿名内部类使用时,则该形参必须为final修饰的,可以直接访问外部类中的所有成员变量和方法;匿名内部类常用在对事件的监听回调的场景中。

22. 谈谈对kotlin的理解
待补充。。。

23. 闭包和局部内部类的区别

在局部内部类中使用了外部方法中的局部变量此时的变量因为不能随着方法执行完毕而消失,因为内部类的实例可能还会用的这个变量,所以该变量需要使用final修饰(final修饰的变量会转为常量,因此不会随着方法的执行完毕而消失),这种局部内部类使用外部方法的局部变量场景就形成了闭包。
闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含创建内部类作用域的信息,还自动拥有一个指向外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。

24. string 转换成 integer的方式及原理

string转integer调用的方法是Integer.valueOf(String s),而valueOf()方法的实现是先调用parseInt(String s,int radix)方法将String转换成int,然后再调用valueOf(int i)将int转换成Integer。在valueOf(int i)方法中先判断int属于[-128,127]之间直接从常量池中取出对应缓存数据返回,如在范围外则new Integer(i)返回。
在调用parseInt(String s,int radix)方法时,radix传入为固定值基数10,在parseInt()方法中,先判断string的长度是否大于0,如不大于0则抛出NumFormatException,然后通过s.charAt(0)方法取出String第一位字符值,根据ASCII码表判断是否为符号中的+/-号并作相应标识,如第一位是符号但不是正负号或者是正负号但string长度为1则直接抛出NumFormatException,再然后在while循环中通过调用Character.digit(char ch,int radix)方法依次计算出String指定位置的字符对应的十进制数值,通过while进行累加得到最终结果。
其中Character.digit(char ch,int radix)方法中也是根据ASCII码表判断字符是否是0~9的数字,如不是返回-1,到parseInt()方法中得到Character.digit()方法返回值为-1则抛出NumFormatException。
注意上面的valueOf()和parseInt()方法为Integer类中的方法,charAt()为String类中的方法,digit()为Character类中的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值