Java面试需要准备的东西很多,包括但不限于Java基础、常用API类、面向对象思想、经典jdk源码分析,集合类、并发与多线程、IO/NIO/AIO、反射与代理、JVM、TCP/IP、数据库、redis、数据结构与算法、设计模式、Git、Maven、分布式/微服务/云服务、前后端分离、vue全家桶、spring全家桶等等。要想每一个知识点都掌握显然难度不小,但在平常学习过程中对一些面试经常会遇到的问题做点总结,深扒一下底层原理,就可以在面试中脱颖而出。本篇内容主要是和面向对象有关的知识点,相关问题在实际面试中出现的频率非常高,值得记录下来跟道友一起分享。
1 面向对象的优点?如何理解面向对象
世间万物皆对象,对象有具体的的实例化,任何方法或者属性都要写在对象(类)里面。易扩展、易维护、面向对象三大特征(封装,继承,多态)。
封装是把过程和数据包围起来,对数据的访问只能通过已定义的权限访问控制符规定的范围。
继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。
多态性是指允许不同类的对象对同一消息作出响应。可以把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
2 接口与抽象类的区别
2.1 什么是抽象类?
含有abstract修饰符的class即为抽象类,abstract类不能创建实例对象。含有abstract方法的类必须定义为abstract class, abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体子类中实现,因此不能定义抽象构造方法或抽象静态方法(自己和子类都无法使用,编译出错)。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。
2.2 什么是接口?
接口(interface)可以看成是抽象类的一种特例,接口的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final
2.3 接口与抽象类的语法区别
从构造方法、静态方法、非抽象方法、抽象方法、普通成员变量、静态成员变量以及继承几个方面讨论两者的区别。
抽象类可以有构造方法,接口中不能有构造方法。抽象类中的构造方法是为了初始化,而不是实例化。
抽象类中可以包含静态方法, jdk1.8前接口中不能包含静态方法,1.8后接口可以直接定义静态方法体,用接口名调用。
抽象类中可以包含非抽象的普通方法,接口中的所有方法必须是抽象的,不能有非抽象的普通方法。
抽象类和接口的抽象方法访问权限,jdk1.8以前,抽象类的方法默认访问权限为protected,jdk 1.8时,抽象类的方法默认访问权限变为default ;jdk 1.8以前,接口中的方法必须是public的 ,jdk1.8时,接口中的方法可以是public的,也可以是default的 ,jdk 1.9时,接口中的方法可以是private的。
抽象类中可以有普通成员变量,接口中没有普通成员变量(成员变量类型默认为public static final)。
抽象类和接口中都可以包含静态成员变量,但是抽象类中的静态成员变量的访问类型可以是任意的,但接口中定义的变量只能是public static final 类型,并且默认即为public static final类型。
一个类可以只能继承一个抽象类,但是确能同时继承多个接口。
2.4 应用区别
接口的应用往往是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约,定义一组规范。而抽象类在代码实现方面发挥作用,可以实现代码的重用。例如模板方法设计模式,假设某个项目的所有Servlet类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的Servlet都继承这个抽象基类,在抽象基类的service方法中完成权限判断、记录日志访问日志和处理异常的代码,在各个子类中只用写完成各自的业务逻辑代码。
3 类的组成和执行顺序
组成:属性、方法、静态块、非静态块。
执行顺序: 先父类,后子类,静态块 静态字段 非静态块 非静态字段 构造器 方法。
4 String与StringBuffer、StringBuilder的区别?
4.1 三者的语法区别
String 是字符串常量,而StringBuffer和StringBuilder是字符串变量。由String创建的字符串内容是不可变的,而由StringBuffer和StringBuilder创建的字符内容是可以改变的。StirngBuffer是线程安全的,而StringBuilder是非线程安全的。StringBuilder支持StringBuffer的所有操作,因为他不执行同步,不会有线程安全带来额外的系统消耗,所以速度更快。
4.2 关于String的不可变
4.2.1底层源码设计
虽然String、StringBuffer和StringBuilder都是final类,生成的对象都是不可变,并且内部也都是靠char数组实现的,但是不同之处在于String类中的char数组是final的,而StringBuffer和StringBuilder都是继承AbstractStringBuilder类,它们的内部实现都是靠这个父类完成的,而这个父类中定义的char数组只是一个普通私有变量,可以用append追加。
4.2.1为什么奖String设计成不可变
首先是字符串常量池的需要,当创建一个String对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。
其次,在Java中String对象的哈希码被频繁地使用,比如在hashmap等容器中,字符串不变性保证了hash码的唯一性,因此可以放心地进行缓存,这也是一种性能优化手段,意味着不必每次都去计算新的哈希码。
最后,从安全性反面考虑,String被许多的Java类库用来当作参数,例如网络连接地址,文件路径,还有反射机制需要的String参数,假若Stirng不是固定不变的,将会引起各种安全隐患。String类的不变性使得同一个字符串可以安全的被多个线程共享。
5 重载和重写的区别?
5.1 什么是重载?
重载(Overload)是指在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,不能通过返回类型是否相同来判断。
5.2 什么是重写?
重写是指在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型都相同的情况下,对方法体进行修改和重写,这就是重写。有一种情况下,子类中方法的返回值是父类中方法返回值的子类时可以说返回类型不同。另外在子类中重写的方法的访问权限不能少于父类中被重写的方法
5.3 重载和重写的区别
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;
重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。