Java 创建对象的四种方式

来源:互联网 发布:wow淘宝刷坐骑会封号吗 编辑:程序博客网 时间:2024/05/20 07:36

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()方法,

 @Overridepublic 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=[语文, 数学, 英语]}