这一块是Java的核心基础和面试的重点,一定要理解后并且能用自己的话讲出来。
01 说说面向对象和面向过程的区别。
面向过程是一种以过程为中心的编程思想,它将问题分解为一系列步骤,并按照顺序执行这些步骤来解决问题。程序的执行过程是按照预定的流程进行的,数据与操作是分离的。
而面向对象则是以对象为中心的编程思想,它将问题抽象为对象,每个对象都有自己的属性和行为。对象之间通过消息传递来交互,程序的执行是通过对象之间的协作完成的。
具体来说,面向过程编程更注重算法和步骤的实现,而面向对象编程更注重对象的设计和交互。面向过程的代码通常以函数为单位组织,而面向对象的代码则以类和对象为单位组织。
举个例子,比如我们要写一个计算两个数之和的程序。在面向过程的编程中,我们可能会写一个函数来接受两个数作为参数,并返回它们的和。而在面向对象的编程中,我们可能会创建一个“数值”对象,它有“相加”的方法,可以接受另一个数值对象作为参数,并返回它们的和。
总的来说,面向对象编程更加符合人类的思维方式,更易于理解和维护大型复杂的程序。但在一些简单的问题上,面向过程编程可能更加简洁和高效。
02 说一下面向对象的特征。
面向对象的特征,封装、继承、多态。
封装有两层意思,第一层是说把成员属性和行为看作一个密不可分的整体,封装到对象中,第二层是指信息屏蔽,给信息加入访问控制权限,把不需要外界知道的信息隐藏起来,这能保证数据的安全。
继承是指首先定义反映事物一般属性的类,然后在此基础上派生出反映事物特殊属性的类,这样做可以增强代码的可重用性。
面向对象的多态性主要指的是在特定范围内同一方法或同一类型的对象有着不同的操作效果,多态有两种形式,方法多态和对象多态。
方法多态(同样的方法有不同的实现):
- 方法重载:使用同一个方法名称可以根据参数类型与个数的不同实现不同方法体的调用。
- 方法重写:使用同一个方法会根据覆写该方法的子类的不同而实现不同的功能。
对象多态(父子实例之间的转换):
-
对象向上转型(父类定义了标准,子类定义了个性化的实现):
通过父类对象能够接收到所有的子类实例,在调用方法时也会调用被子类覆写过的方法,向上转型的核心意义在于方法接收参数类型及方法返回值类型的统一处理,比如Object是顶级父类,我们就可以使用Object类作为参数去接收任意类型的对象,或者作为返回值返回任意类型的对象。
-
对象向下转型(向上转型实现标准的统一,向下转型可以保持子类实例的个性):
子类向上转型后能调用的方法仅仅是父类中定义的全部方法,子类扩充的方法无法调用,进行对象向下转型后,就可以调用子类中扩充的方法了。
要注意的是!进行向下转型之前一定要发生过向上转型,否者会报ClassCastException异常,可使用instanceof关键字去保证对象向下转型的安全性。
03 类和对象的区别是什么?
类是对客观世界具有某些共同特征的群体的抽象,对象是一个个具体的、可以操作的事物。类是对象的模板,对象是类的实例。
04 类什么时候被加载?
类加载就是把类加载到内存中。
- 创建对象实例时。
- 创建子类对象实例时,父类也会被加载。
- 使用类的静态成员时(静态属性、静态方法),但是对于 final static修饰的静态属性,编译器底层做了优化,它的调用不会触发类加载。
05 静态代码块和普通代码块的区别?
静态代码块对类进行初始化,会随着类加载而执行,并且只会执行一次。普通代码块对对象进行初始化,每创建一次对象,普通代码块就执行一次。
06 对象的创建流程
-
类加载检查:当使用 new 关键字创建对象时,首先检查该类是否已经被加载到内存中,如果没有,则触发类加载过程,从顶级父类开始,往下一直到本类,由类加载器将类的字节码加载进来。在类加载时会执行类中的静态代码块和静态变量的初始化。
-
分配内存空间:在堆内存中为对象分配所需的连续内存区域。这包括对象的实例变量所占用的空间以及一些额外的开销(如对象头)。
-
初始化零值:将分配的内存空间初始化为默认的零值,例如基本数据类型为其对应的默认值(如整数为 0,布尔为 false 等),引用类型为 null。
-
设置对象头:为对象设置必要的对象头信息,如对象的哈希码、GC 信息等。
-
执行构造方法:根据指定的构造方法进行对象的初始化,在构造方法中可以为实例变量赋予具体的值,执行其他必要的初始化逻辑。
构造器中暗含super语句和普通代码块的调用与普通属性的初始化(也就是显式初始化),所以会先把父类普通代码块的调用与普通属性的初始化的工作做好,然后再做子类普通代码块的调用与普通属性的初始化的工作。
初始化顺序(包含了静态属性)如下:
- 父类静态代码块和静态属性初始化(优先级一样,按定义顺序执行)。
- 子类的静态代码块和静态属性初始化。
- 父类普通代码块和普通属性初始化(优先级一样,按定义顺序执行)。
- 父类构造方法
- 子类普通代码块和普通属性初始化
- 子类构造方法
-
对象创建完成:此时对象已经完全创建好,可以被程序使用和操作。
07 static关键字的作用
-
用来声明全局属性,可以在类的任何地方访问,而不需要创建类的实例。
-
声明全局方法,可以通过类名直接调用,而不需要创建类的实例。
-
声明静态代码块,在类加载时执行,且只执行一次。
-
静态导入:使用import static语句可以导入类的静态成员或方法,使其可以直接在代码中使用,而无需使用类名限定。
-
static方法中不能调用非static方法或属性,但是非static方法可以调用static方法或属性。
-
static方法和属性不能使用与对象相关的关键字(this,super等)。
-
final static 修饰的变量被调用时不会引起类加载而导致静态代码块的执行,这是因为底层编译器做了优化。
08 final关键字的作用
1.当final修饰类时,意思是该类是最后的类,表示该类不能再被继承,该类不能有子类。
2.当final修饰方法的时候,表示该方法不能被子类重写,但是能被子类继承。
3.当final修饰属性时,表示该属性不能被修改。
final修饰的属性在定义时,必须被赋初始值,并且不能再修改,赋值可以在如下位置之一:
(1):定义时
(2):构造器中
(3):代码块中
如果final修饰的属性是静态的,那么需要在加载类信息时完成对它的初始化,所以赋值可以在如下位置之一:
(1):定义时
(2):静态代码块中
09 方法重载和方法重写的区别
方法重载是为了能够重复使用方法名。方法名相同,但是方法的参数的类型和个数不同就构成了方法的重载,那么在方法调用时会根据参数的类型和个数去判断要调用哪个方法,方法重载对返回值和方法的访问范围没有要求。
方法重写是指子类写一个与父类同名的方法去覆盖父类中的方法。方法重写要求方法名、参数类型和个数要与父类中的同名方法一致,并且返回类型要与父类的返回类型一致或者是其子类,并且子类方法不能缩小父类的方法的访问范围。
10 HashCode是什么?hashCode()和equals()方法的联系?
HashCode(哈希码)是通过特定算法为对象计算出的一个整数值。
它的主要作用有以下几点:
-
在集合中快速定位:比如在 HashSet、HashMap 等集合中,通过HashCode 可以快速确定元素可能存储的位置,提高查找和操作的效率。
-
实现对象的快速比较和分类:帮助快速判断两个对象在某些情况下是否相似或相同。
-
支持一些基于哈希的数据结构和算法,以优化性能。
需要注意的是,相同的对象应该具有相同的 HashCode,但具有相同 HashCode 的对象不一定完全相同,只是在特定的哈希算法下表现出相似的特征。
hashCode()方法的主要用途是获取哈希码,equals()主要用来比较两个对象是否相等。二者之间有两个约定,如果两个对象相等,它们必须有相同的哈希码;但如果两个对象的哈希码相同,他们却不一定相等。也就是说,equals()比较两个对象相等时hashCode()一定相等,hashCode()相等的两个对象equals()不一定相等。
加分回答:Object类提供的equals()方法默认是用==来进行比较的,也就是说只有两个对象是同一个对象时,才能返回相等的结果。而实际的业务中,我们通常的需求是,若两个不同的对象它们的内容是相同的,就认为它们相等。鉴于这种情况,Object类中equals()方法的默认实现是没有实用价值的,所以通常都要重写。 由于hashCode()与equals()具有联动关系,所以equals()方法重写时,通常也要将hashCode()进行重写,使得这两个方法始终满足相关的约定。
11 说一下单例设计模式吧
单例设计模式确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。对象实例化个数控制住了,就可以减少无用垃圾的产生,避免资源浪费。
单例设计模式有饿汉式和懒汉式两种:
- 饿汉式:在类加载时就创建。适用于程序运行初期就要使用该对象,并且对资源占用不太敏感的场景。比如一些基础的系统服务、全局配置对象等等。
- 懒汉式:在第一次被调用时才创建。适用于对象的创建成本较高,并且程序运行初期不会用到该对象的场景。但是要注意高并发下的线程安全问题。
多例设计模式和单例设计模式类似,只不过可以提供有限个实例对象。
12 枚举通常怎么用?
枚举是一组常量的组合,可以理解为加强版的多例设计模式。
枚举有以下一些常见的使用场景:
-
表示状态或阶段:如表示设备的工作状态(开启、关闭、故障等)、订单的状态(已下单、已支付、已发货等)。
-
表示有限的选项集合:比如选择性别(男、女)、选择颜色(红、蓝、绿等)。
-
定义权限级别:如管理员、普通用户等不同的权限等级。
-
表示季节、月份、星期等周期性概念。
-
在配置文件中定义固定的配置选项。
-
用于模式识别或分类:例如不同的文件类型、数据格式等。
-
作为标志位:来表示某些特定的条件是否满足。