Java 创建对象的四种方式

Java 创建对象的四种方式

  1. 通过new关键字生成对象
  2. 通过java反射机制创建对象
  3. 通过clone对象生成新对象
  4. 通过序列化生成新对象

通过new关键字生成对象

新建一个Student类,如下

public class Student{
    private String name;
    private Float grade;

    public Student(){
    }
    public Student(String name, Float grade){
        this.name = name;
        this.grade = grade;
    }
     public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Float getGrade() {
        return grade;
    }

    public void setGrade(Float grade) {
        this.grade = grade;
    }
}

通过new关键字创建对象

Student s = new Student(); //调用无参构造器
Student s2 = new Student("Han Meimei", 15f) //调用有参构造器

通过java反射机制创建对象

使用Class.getInstance()创建新的对象,如下

Student s3 = Student.class.getInstance();
Student s4 = (Student)Class.forName("Student").getInstance();

使用这种方式是调用的类的无参构造器生成的对象,如果想要调用有参构造器生成对象,应该怎么办?
使用Class.getConstructor()方法

Constructor<Student> studentConstructor = Student.class.getConstructor(String.class, Float.class);
Student s5 = studentConstructor.newInstance("Han Meimei", 15f);

查看Class.getInstance()方法,内部实现是通过调用类的无参构造器生成新的对象的。
所以使用Class.getInstance()方法,要确保类有public的无参构造器。

通过clone对象生成新对象

要使用Object.clone()生成新对象,类必须实现接口Cloneable,并且重写clone()方法。
修改Student如下

public class Student implements Cloneable{
    ...
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();    
    }

    //重写toString方便通过打印检查结果    
    @Override
    public String toString() {
        return "Student{" +
            "name='" + name + '\'' +
            ", grade=" + grade +
            '}';
    }
}

使用clone()生成新的类,如下

Student s6 = (Student)s5.clone();
System.out.println(s6.toString());

可以查看命令行,新生成的对象s6完整复制了s5的属性

Student{name='Han Meimei', grade=15.0}

如果给Student新加一个属性,List<String> subjects 表示学生参加的学科的列表。这时候调用super.clone(),能成功拷贝s5的subjects属性里的值到s6里了么?
修改Student类添加属性subjects和对应个getter,setter方法如下

public class Student implements Cloneable{
    ...
    private List<String> subjects;
    ...
    public List<String> getSubjects() {
        return subjects;
    }

    public void setSubjects(List<String> subjects) {
        this.subjects = subjects;
    }
  }

给s5的subjects属性赋值,并且通过clone()重新生成s6

 List<String> subjects = new ArrayList<String>();
 subjects.add("语文");
 subjects.add("数学");
 s5.setSubjects(subjects);
 Student s6 = (Student) s5.clone();
 System.out.println(s6.toString());

查看s6,s6成功复制了s5的subjects属性

Student{name='Han Meimei', grade=15.0, subjects=[语文, 数学]}

但是我们发现s6比s5多修了一门学科-英语,所以需要修改s6
的subjects。

s6.getSubjects().add("英语");
System.out.println(s6.toString());

查看s6

Student{name='Han Meimei', grade=15.0, subjects=[语文, 数学, 英语]}

这时候我们再打印s5看看

 System.out.println(s5);

命令行输出

Student{name='Han Meimei', grade=15.0, subjects=[语文, 数学, 英语]}

发现s5的subjects属性也改变了!!
这个地方就是一个要注意的坑,查看clone()源码的注释发现:

 * performs a "shallow copy" of this object, not a "deep copy" operation.

什么意思呢?
这个是浅拷贝,不是深拷贝。当拷贝s5的subjects属性只是拷贝了引用。s6的subjects属性和s5的subjects属性指向的是同一块地址,修改其中一个内容,必会导致另一个的修改。
如何解决这个问题?
重新修改clone()方法,

 @Override
public Object clone() throws CloneNotSupportedException {
    Student newStudent = (Student) super.clone();
    if(null != this.getSubjects()){
        List<String> newSubjects = new ArrayList<>();
        newSubjects.addAll(this.getSubjects());
        newStudent.setSubjects(newSubjects);
    }
    return newStudent;
}

重新执行拷贝的代码

Student s6 = (Student) s5.clone();
s6.getSubjects().add("英语");
System.out.println(s6);
System.out.println(s5);

查看命令行输出

Student{name='Han Meimei', grade=15.0, subjects=[语文, 数学, 英语]}
Student{name='Han Meimei', grade=15.0, subjects=[语文, 数学]}

s5的subjects属性并没有改变。

通过序列化生成新对象

通过序列化生成新对象,类必须实现接口Serializable
修改Student类如下

public class Student implements Cloneable, Serializable{
    private static final long serialVersionUID = 1L;
    ...
}

我们将s6转化成二进制写入”Student6.obj”文件中,再通过读取该文件生成新的对象s7

try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("Student6.obj"));
     ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("Student6.obj"))){
        objectOutputStream.writeObject(s6);
        Student s7 = (Student) objectInputStream.readObject();
        System.out.println(s7);
    } catch (IOException e) {
        e.printStackTrace();
    }

查看命令行输出,我们可以看到s7完整生成了。

Student{name='Han Meimei', grade=15.0, subjects=[语文, 数学, 英语]}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值