1. JAVA 中面向对象的特征有哪些?
主要有四大特性:封装、继承、多态、抽象(很多人也认为只有三大特性)
封装
封装的思想保证了类内部数据结构的完整性,使用户无法轻易直接操作类的内部数据,这样降低了对内部数据的影响,提高了程序的安全性和可维护性。
优点:
- 只能通过规定方法访问数据。
- 隐藏类数实现细节。
- 方便修改实现。
- 方便加入控制语句。
继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或类从父 类继承方法,使得子类具有父类相同的行为。
还有一个地方需要知道的是,这里还有一个概念叫做组合,可以在面试的时候提一下。组合其实很简单,就是单纯的将某个对象引入当前类,让当前类有引入类的功能,因为 JAVA 只能单继承,所以某些场景下组合其实更合适。
特点:
- 继承父类的重用。
- 继承可以多层继承。
- 一个类只能继承一个父类。
- 父类中
private
修饰的不能被继承。 - 构造方法不能被继承。
多态
多态是同一个行为具有多个不同表现形式或形态的能力。产生的场景是将子类对象赋给父类的引用,会产生多态。如:Father son = new Son()
此时的对象son
只能调用Father
的方法(指的是子类重写或者继承父类的那些方法),而不能调用本身定义的一些方法。因为多态中强调:编写 java 程序时,引用类型变量只能调用其编译时类型的变量,不能调用其运行时类型变量。
特点:
- 继承父类的重用。
- 继承可以多层继承。
- 一个类只能继承一个父类。
- 父类中 private 修饰的不能被继承。
- 构造方法不能被继承。
必要条件 : 继承、重写、父类引用指向子类对象。
作用:
- 不必编写每一子类的功能调用,可以直接把不同子类当父类看,屏蔽子类间的差异(也可以说隐藏了细节),提高代码的通用率/复用率。
- 父类引用可以调用不同子类的功能,提高了代码的扩充性和可维护性。
抽象
用 abstract
关键字来修饰一个类时,这个类叫作抽象类。抽象类是它的所有子类的公共属性的集合,是包含一个或多个抽象方法的类。但不意味着抽象类中只能有抽象方法,它和普通类一样,可以拥有普通的成员变量、方法。
特点:
- 抽象类不能被实例化。抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
- 构造方法,类方法(用
static
修饰的方法)不能声明为抽象方法。 - 被定义为
abstract
的类需要被子类继承,但是被修饰为final
的类是不能被继承和改写的,这两者是不能一起用来做修饰的。
2. JAVA 中的基本数据类型有哪些,对应的大小和包装类型是什么?
八种基本数据类型: int
、short
、float
、double
、long
、boolean
、byte
、char
。
封装类分别是: Integer
、Short
、Float
、Double
、Long
、Boolean
、Byte
、Character
。
大小分别是(byte): int:4
、short:2
、float:4
、double:8
、long:8
、boolean:1
、byte:1
、char:2
。
3. Java 的引用类型有哪些?
一共有四种引用,分别是强引用、软引用(SoftReference
)、弱引用(WeakReference
)、虚引用(PhantomReference
)。
- 强引用
最普遍的一种引用方式。如String s = "abc"
, 变量s
就是字符串abc
的强引用。只要强引用存在,则垃圾回收器就不会回收这个对象。 - 软引用(
SoftReference.java
)
用于描述还有用但非必须的对象,如果内存足够,不回收,如果内存不足,则回收。一般用于实现内存敏感的高速缓存,软引用可以和引用队列ReferenceQueue
联合使用,如果软引用的对象被垃圾回收,JVM 就会把这个软引用加入到与之关联的引用队列中。 - 弱引用(
WeakReference.java
)
弱引用和软引用大致相同,弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。 - 虚引用(
PhantomReference.java
)
就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。 虚引用主要用来跟踪对象被垃圾回收器回收的活动。
4. String、StringBuffer 和 StringBuilder 的区别有哪些?
String
String
是一个不可变(值是不可变的)的类对象,这就导致每次对String
的操作都会生成新的String
对象,在操作频繁的场景中可能会出现性能问题和内存溢出的情况。String 类中使用 final 关键字修饰字符数组来保存字符串, private final char value[] ,所以 String 对象是不可变的。
StringBuffer
StringBuffer
是一个值可变的类对象,内部维护的是字符数组,使用字符数组保存字符串 char[] value 。另外 StringBuffer
是线程安全的,因为StringBuffer
所有 public
方法都是 synchronized
修饰的。这也就会导致在数据量大的情况下的性能问题。
StringBuilder
StringBuilder
和StringBuffer
是一样的,都是字符串变量。区别在于StringBuilder
的方法都没有加synchronized
修饰。所以在性能上要优于 StringBuffer
,但是StringBuilder
在多线程环境下不是线程安全的。
对于三者使用的总结:
- 操作少量的数据: 适用 String
- 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
- 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer
5. == 与 equals(重要)
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象(基本数据类型= = 比较的是值,引用数据类型 = =
比较的是内存地址)。
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使⽤情况:
情况 1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
情况 2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来比较两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)
说明:
String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。
当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。