目录
面试题一:String str = “i” 与 String str = new String(“i”) ⼀样吗?
不⼀样,因为内存的分配方式不一样。
- String str = “i” 的方式,Java 虚拟机会将其分配到常量池中;
- String str = new String(“i”) 则会被分到堆内存中。
举个栗子:
public class StringTest {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
String str3 = new String("abc");
String str4 = new String("abc");
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // false
System.out.println(str3 == str4); // false
System.out.println(str3.equals(str4)); // true
}
}
- 在执行 String str1 = “abc” 的时候,JVM 会首先检查字符串常量池中是否已经存在该字符串对象,如果已经存在, 那么就不会再创建了,直接返回该字符串在字符串常量池中的内存地址;如果该字符串还不存在字符串常量池中, 那么就会在字符串常量池中创建该字符串对象,然后再返回。
- 所以在执行 String str2 = “abc” 的时候,因为字符串常量池中已经存在“abc”字符串对象了,就不会在字符串常量池中再次创建了,所以栈内存中 str1 和 str2 的内存地址都是指向 “abc” 在字符串常量池中的位置,所以 str1 = str2 的运行结果为 true。
- 在执行 String str3 = new String(“abc”) 的时候,JVM 会⾸先检查字符串常量池中是否已经存在“abc”字符串,如果已经存在,则不会在字符串常量池中再创建了;如果不存在,则就会在字符串常量池中创建 “abc” 字符串对象, 然后再到堆内存中再创建⼀份字符串对象,把字符串常量池中的 “abc” 字符串内容拷贝到内存中的字符串对象中, 然后返回堆内存中该字符串的内存地址,即栈内存中存储的地址是堆内存中对象的内存地址。String str4 = new String(“abc”) 是在堆内存中⼜创建了⼀个对象,所以 str 3 == str4 运行的结果是 false。
str1、str2、str3、str4 在内存中的存储状况如下图所示:
面试题二:值传递和引用传递的区别的什么?
- 值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。
- 引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的是引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。
追问:Java 中的参数传递时传值呢?还是传引用?
直接看这位大佬的文章—>Java中的参数传递,到底是值传递还是引用传递?
面试题三:深克隆和浅克隆的区别?
- 浅克隆:拷贝对象和原始对象的引用类型引用同⼀个对象。浅克隆只是复制了对象的引用地址,两个对象指向同⼀个内存地址,所以修改其中任意的值,另⼀个值都会随之变化。
- 深克隆:拷贝对象和原始对象的引用类型引用不同对象。深拷贝是将对象及值复制过来,两个对象修改其中任意的值另⼀个值不会改变,这就是深拷贝(例:JSON.parse() 和 JSON.stringify(),但是此⽅法⽆法复制函数类型)。
补充: 深克隆的实现就是在引用类型所在的类实现 Cloneable 接口,并使用 public 访问修饰符重写 clone 方法。
Java 中定义的 clone 没有深浅之分,都是统⼀的调用 Object 的 clone 方法。
追问:如何实现对象的克隆?(深克隆)
- 实现 Cloneable 接口并重写 Object 类中的 clone() 方法;
- 实现 Serializable 接⼝,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆。