什么是Java拷貝
創(chuàng)建一個(gè)和已知對(duì)象一摸一樣的對(duì)象。
創(chuàng)建對(duì)象5中方法
- new 關(guān)鍵字。
- Class 類的 newInstance() 方法。
- Constructor 類的 newInstance() 方法。
- Object 類 clone() 方法。
- 反序列化。
// 克隆必須實(shí)現(xiàn) Cloneable 接口
// 序列化必須實(shí)現(xiàn) Serializable 接口
public class User implements Cloneable, Serializable {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
'}';
}
@Override
public User clone() {
try {
return (User) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// new 關(guān)鍵字
User user = new User();
user.setUsername("new 關(guān)鍵字");
System.out.println(user);
System.out.println(user.hashCode());
// Class 類的 newInstance() 方法
Class<?> aClass = Class.forName("cn.eric.register.client.test.User");
User user1 = (User) aClass.newInstance();
user1.setUsername("Class 類的 newInstance() 方法");
System.out.println(user1);
System.out.println(user1.hashCode());
// Constructor 類的 newInstance() 方法
User user2 = User.class.getDeclaredConstructor().newInstance();
user2.setUsername("Constructor 類的 newInstance() 方法");
System.out.println(user2);
System.out.println(user2.hashCode());
// Object 類 clone() 方法
User user3 = user.clone();
System.out.println(user3);
System.out.println(user3.hashCode());
// 輸出流,序列化寫入文件
String s = "C:\\Users\\Administrator\\Desktop\\a.txt";
FileOutputStream fileOutputStream = new FileOutputStream(s);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(user);
objectOutputStream.flush();
// 輸入流,讀取文件,反序列化為對(duì)象
FileInputStream fileInputStream = new FileInputStream(s);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
User user4 = (User)objectInputStream.readObject();
System.out.println(user4);
System.out.println(user4.hashCode());
}
基本類型和引用類型
基本類型也稱為值類型
char,boolean,byte,short,int,long,float,double
引用類型包括: 類,接口,數(shù)組,枚舉等

淺拷貝
// lombok
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public class User implements Cloneable, Serializable {
private String username;
private Integer age;
private Cat cat;
@Override
public User clone() {
try {
return (User) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public class Cat {
private String catName;
}
public static void shallowCopy() {
User user = new User();
Cat cat = new Cat();
cat.setCatName("小白");
user.setUsername("張三");
user.setAge(11);
user.setCat(cat);
User user1 = user.clone();
user1.setUsername("李四");
user1.setAge(12);
user1.getCat().setCatName("小黑");
System.out.println(user);
System.out.println(user1);
// 輸出結(jié)果
// User(username=張三, age=11, cat=Cat(catName=小黑))
// User(username=李四, age=12, cat=Cat(catName=小黑))
}
所以以上代碼 cat 修改后 原 對(duì)象也跟著修改了。

String, Integer 也是引用類型為啥沒(méi)有復(fù)制引用而是復(fù)制了值呢?
其實(shí)不是的 String 和 Integer 其實(shí)也是復(fù)制了引用. 只是表象讓我們看起來(lái)像是 復(fù)制的 值.
public static void shallowCopy() {
User user = new User();
Cat cat = new Cat();
cat.setCatName("小白");
user.setUsername("張三");
user.setAge(11);
user.setCat(cat);
User user1 = user.clone();
// user1.setUsername("李四");
// user1.setAge(12);
// user1.getCat().setCatName("小黑");
System.out.println(user);
System.out.println(user1);
System.out.println("user.hash" + user.hashCode());
System.out.println("user1.hash" + user1.hashCode());
System.out.println("user.username.hash" + user.getUsername().hashCode());
System.out.println("user1.username.hash" + user1.getUsername().hashCode());
System.out.println(user.getUsername() == user1.getUsername());
// 輸出內(nèi)容
// User(username=張三, age=11, cat=Cat(catName=小白))
// User(username=張三, age=11, cat=Cat(catName=小白))
// user.hash46723090
// user1.hash46723090
// user.username.hash774889
// user1.username.hash774889
// true
}
我們發(fā)現(xiàn)如果我們注釋掉修改 username 的代碼 發(fā)現(xiàn) username 的 hash 值是一摸一樣的。而且 == 對(duì)比也是相等的.
說(shuō)明 字符串 其實(shí)確實(shí)是復(fù)制的引用。 看起來(lái)像是值引用的原因就是 String 類是 final 修飾的。
String 是不可變的。修改就是重新創(chuàng)建一個(gè)字符串并將棧內(nèi)的指向改為新創(chuàng)建的堆。
Integer 同理。

深拷貝
深拷貝就是所有字段都拷貝一份.即使是引用類型對(duì)象也一樣.
實(shí)現(xiàn)方法就是所有的引用類型對(duì)象也實(shí)現(xiàn)一下
Cloneable接口. 并重寫clone方法.但是此時(shí)有個(gè)問(wèn)題.例如 上方代碼 User 類里不僅僅有 Cat 還有 Dog 還有 Duck 等等.... Cat 里面還有 Snacks 等等 一層一層一個(gè)一個(gè)又一個(gè)的對(duì)象. 代碼量就多了.
使用反序列化實(shí)現(xiàn) 深拷貝是個(gè)不錯(cuò)的辦法. 注意: 所有的類都要實(shí)現(xiàn)
Serializable接口序列化才行.