一、简历面
1、String, StringBuilder, StringBuffer的区别、使用场景
- String数据内部结构用的是private final char[],是不可变的
- 其实被private final修饰的是引用对象,只要引用不变,char[]数组中的内容是可以变的,但是String没有提供方法修改String,所以String是不可变的。但是,可以用反射把value的访问权限
valueField.setAccessible(true);
,那么外部就可以对value进行修改了。 - 对String进行 “+=”操作 时,会生成一个新的String对象,让引用指向新的对象。如果经常改变String内容的场景,不要用String类型,内存中无引用的对象多了,容易造成内存泄漏,GC就开始工作,性能会降低。
- 其实被private final修饰的是引用对象,只要引用不变,char[]数组中的内容是可以变的,但是String没有提供方法修改String,所以String是不可变的。但是,可以用反射把value的访问权限
- 使用String类的concat与replace方法时,不会对原来的对象产生影响,他们会返回一个全新的对象
- StringBuilder线程不安全,性能较好
- StringBuffer线程安全,加了Synchronized同步锁,保证取数据或者修改数据时的线程安全性,性能较差
StringBuilder, StringBuffer的实现原理:
- 用char[]存储数据,当char[]盛不下时,进行扩容,2倍扩容,避免总是扩容,默认数组的长度是16,如果能够预测char[]的长度的话,如果长度小于16,那么可以不设置,如果比16长的话,应该尽量设置长度,不然的话,会进行扩容,导致性能降低。
2、类加载过程,假如自己定义了一个String类,且String在的包也是java.util.String,在这个String中只写main方法,main方法中打印一句“hello world”,那么程序运行的结果是?
- 类加载过程:加载class文件;验证;准备;解析(将常量池内的符号转换成直接引用);初始化
初始化:为类变量进行初始化,执行类构造器<clinit>方法
,
<clinit>方法
的特点:
- 内容:包括变量的赋值操作和静态语句块(执行顺序,源文件中出现的顺序)
<clinit>方法
和实例构造器<init>方法
不同,不需要显示调用父类构造器,jvm会保证执行子类的<clinit>方法
前,父类的<clinit>方法
已经被执行了,因此object的<clinit>方法
最先执行- 如果一个类没对变量的赋值操作和静态代码块,就可以不为该类生成
<clinit>方法
- 只有一个线程执行
<clinit>方法
,其他线程阻塞
判断2个类是否相同:
首先看他们的类加载器是不是一样的,如果不一样,那么肯定不是同一个类