1. 如何实现对象克隆?
有两种方式:
实现
Cloneable
接口并重写clone
方法(浅拷贝);实现
Serializable
,通过对象的序列化和反序列化实现真正的深度克隆(深拷贝),代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class MyUtil {
private MyUtil() {
throw new AssertionError();
}
public static <T> T clone(T obj) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
// 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放
}
}注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型界定,可以检查出要克隆对象是否支持序列化,这项检查是编译器完成的,不是运行时抛出异常,这种方案明显优于使用object类的clone方法克隆对象。让问题在编译的时候暴漏出来总是优于把问题留在运行时。
2. 内部类访问局部变量的时候,为什么加final?
内部类和局部变量的生命周期不同,方法结束后局部变量的生命周期就结束了,而内部类只要有引用就不结束,内部类生命周期>=局部变量
Java会在编译时在内部类的构造方法里边,将局部变量以参数形式传递给内部类
而如果局部变量发生改变,内部类不知情的场景,所以要加
fianl
,保证引用不可改变注意:在Java8中,可以不适用final关键字,但是如果我们修改了局部变量还是会发生错误,从而保证局部变量的引用不变。
3. transient 关键字
如果使用
transient
修饰变量,当对象存储时,它的值不需要维持。换句话来说就是用transient
修饰的变量不参与序列化的过程。
4. String 和 StringBuild,StringBuffer的区别
String
是只读字符串,创建完成之后是不能修改,如果修改也是创建一个新对象,回收旧对象的过程,所以执行效率比较低,StringBuild
和StringBuffer
可以改变,StringBuild
线程不安全,但是效率比较高,StringBuffter
线程安全效率快慢:
Stringbuilder
>StringBuffer
>String
5. String 为什么设计成不可变
- 安全性
- 本身是
final
类不可修改,不可变极为安全 - String 常被用来作为
HashMap
的key
如果可变会引来安全问题,例如两个key
相同
- 本身是
- 效率高
- 通过字符串池可以节省很多空间
- 每一个
String
对应一个hashcode
,再次使用不用重新计算
6. java 中的四种引用及应用场景
- 强引用
- 弱引用—-引用到达,不可达就会被回收,即便是内存充足,可用于图片
Bitmap
缓存,当不再使用Bitmap
时,就会被回收 - 软引用—–内存不足时被回收,也可用于
Bitmap
回收,当内存不足时,可回收 - 虚引用—-它指向的对象回收时,它本身会被加入到引用队列中,这样我们就知道它指向的对象何时被销毁