什么是Java拷贝
创建一个和已知对象一摸一样的对象。
创建对象5中方法
- new 关键字。
- Class 类的 newInstance() 方法。
- Constructor 类的 newInstance() 方法。
- Object 类 clone() 方法。
- 反序列化。
// 克隆必须实现 Cloneable 接口
// 序列化必须实现 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 关键字
User user = new User();
user.setUsername("new 关键字");
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();
// 输入流,读取文件,反序列化为对象
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
引用类型包括: 类,接口,数组,枚举等
浅拷贝
// 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);
// 输出结果
// User(username=张三, age=11, cat=Cat(catName=小黑))
// User(username=李四, age=12, cat=Cat(catName=小黑))
}
所以以上代码 cat 修改后 原 对象也跟着修改了。
String, Integer 也是引用类型为啥没有复制引用而是复制了值呢?
其实不是的 String 和 Integer 其实也是复制了引用. 只是表象让我们看起来像是 复制的 值.
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());
// 输出内容
// 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
}
我们发现如果我们注释掉修改 username 的代码 发现 username 的 hash 值是一摸一样的。而且 == 对比也是相等的.
说明 字符串 其实确实是复制的引用。 看起来像是值引用的原因就是 String 类是 final 修饰的。
String 是不可变的。修改就是重新创建一个字符串并将栈内的指向改为新创建的堆。
Integer 同理。
深拷贝
深拷贝就是所有字段都拷贝一份.即使是引用类型对象也一样.
实现方法就是所有的引用类型对象也实现一下
Cloneable
接口. 并重写clone
方法.但是此时有个问题.例如 上方代码 User 类里不仅仅有 Cat 还有 Dog 还有 Duck 等等.... Cat 里面还有 Snacks 等等 一层一层一个一个又一个的对象. 代码量就多了.
使用反序列化实现 深拷贝是个不错的办法. 注意: 所有的类都要实现
Serializable
接口序列化才行.