1. new 一个对象在 Java 内部做了哪些工作?
从静态角度来看, new 一个对象表示创建一个类的对象实例.
从 JVM 运行角度来看, 当 JVM 执行到 new 字节码时, 首先会去查看类有没有被加载到内存以及初始化, 如果是第一次使用该类, 则首先加载该类. 加载完成后便会在堆内存分配该对象实例的内存空间, 虚拟机栈分配对象实例的应用内存.
2. 抽象类是否可以定义构造函数? 如果能, 是否能 new 一个抽象类?
抽象类同样也可以定义构造函数, 但是它不能 new 一个抽象类.
3. 既然不能 new 一个抽象类, 那它定义构造函数有什么意义呢?
抽象类中的构造函数只能通过构造函数链调用, 也就是从其他类中的构造函数调用, 它的作用可以初始化抽象类中的一些初始值.
4. String 是否是基本数据类型? 它与 StringBuilder,StringBuffer 有什么区别?
String 不是基本数据类型.
String 是不可变的, 尽管它能在程序中多次赋值以及拼接, 但实际上每一次赋值都是在内存中重新开辟一块内存空间.
StringBuilder 和 StringBuffer 是可变的, 多次对它们赋值不会在内存中开辟一块内存空间, StringBuilder 不是线程安全, StringBuffer 是线程安全的.
5. StringBuilder 与 StringBuffer 的内部实现原理是什么?
StringBuffer 与 StringBuilder 的不同点在于 StringBuffer 在 append 方法加了 synchronized 关键字, 它是线程安全的.
它们都是继承自 AbstractBuilder, 内部实现都是一个可变数组, 数组初始长度为 16. 当调用 append 方法拼接字符串时, 其内部实际上是调用了 System.arraycopy 将字符串拷贝进了可变数组.
6. StringBuilder 的扩容机制是什么?
StringBuilder 在内部是一个字符数组, 默认大小为 16, 当容量超过 16 时, 会进行扩容, 新的数组大小是之前数组大小的 2 倍 + 2, 也就是第一次扩容大小为 34. 扩容后将以前的数组拷贝到新数组中.
7. String str = "a" 与 String str = new String("b") 有什么区别?
String str = "a", 首先会去常量池中查找是否有 "a" 字符串, 如果有则直接指向它, 没有则在常量池中创建并指向它.
String str = new String("b") 则会在堆内存中创建一个 String 对象实例, 并指向它, 同时它也会在常量池中创建 "b" 对象.
8. == 与 equals 比较有什么区别?
== 比较的是引用地址,
equals 通常比较的是值, equals 在 Object 中的实现仍然是 ==, 所以如果要通过 equals 比较值就必须重写 equals.
9. 重写 equals 方法需要注意什么?
在重写 equals 方法时, 一定要重写 hashCode 方法, hashCode 方法是计算对象的 hash 值.
在 Java 中规定:
equals 等于 true, 则它们的 hashCode 一定相等;
equals 等于 false, 则它们的 hashCode 可能相等可能不相等, 也就是如果 hashCode 相等, 则 equals 不一定相等.
之所以要重写 hashCode 方法, 主要是应用在集合中的判断.
如果没有重写类的 hashCode 方法, 只重写了 equals 方法, 当两个对象 equals 等于 true 时, 它们的 hashCode 不相等. 此时如果将它们作为 key 放到 Map 集合中, 由于它们的 hash 值不相等, 所以 Map 认为它们是不相等的 key, 此时在 Map 中将会在逻辑上存在两个相等的 key 值, 不符合我们对程序的预期. 所以在重写 equals 方法时必须重写 hashCode 方法.
10. 重写 hashCode 方法需要注意什么?
在设计散列函数时, 应该尽量避免冲突. 如果频繁的产生散列冲突, 在将对象作为 key 存放在 Map 中时, 会将不同的 key 值散列到一个位置, 对 Map 的性能会有所影响. 可以参考 String 的 hashCode 实现, 将质数 31 数字作为乘法因子.
关注 CoderBuff 公众号, 回复 "面试" 获取更多
来源: https://www.cnblogs.com/yulinfeng/p/11371189.html