版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luoyanglizi/article/details/50791907
前言
面试的时候被问到Java中新建对象的方式有哪些,一阵语噎。脑子里只想起一个new的方式创建对象,这真是个悲剧。
正文
new关键字创建对象
这一种自然是不必多说了,大家都非常的熟悉。
MyClass myClass = new MyClass();
通过实现Cloneable接口调用clone()方法
这种方式相对就用的比较的少了,不知道也无可厚非。
实现步骤:
- 将想要克隆的对象实现Cloneable接口
- 根据当前对象clone新的对象
public class MyClass implements Cloneable{
/**
*getter和setter省略
*/
public String mName;
/**
*getter和setter省略,为了下一个栗子
*/
public MyFriendClass mMyFriend;
private MyClass mClass;
public MyClass cloneSomething() {
try {
mClass= (Something)this.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return mClass;
}
}
- 此处我们实现了没有使用new关键字也创建了对象。
- clone()方法在Object类中是protected的,所以它只能在object的子类中或相同包名下调用,所在在这里它在MyClass类中被相当于重写了。
- MyClass类必须实现Cloneable接口,不然会报CloneNotSupportedException的错误。
- clone()方法进行的克隆是浅克隆,意思是在克隆的时候仅仅只考虑要克隆的目标对象,而不考虑其引用的对象。举个栗子:
//MyFriendClass就不写出来了,里面就只有一个private String mName和它的getter和setter
MyFriendClass myFriend = new MyFriendClass();
myFriend.setName = "lumia";
MyClass myClass = new MyClass();
myClass.setName("lypeer");
myClass.setMyFriend(myFriend);
MyClass myClassCopy = myClass.cloneSomething();
Log.d("MyClassTest" , myClassCopy.getName());//输出为lypeer
Log.d("MyClassTest" , myClassCopy.getMyFriend().getName);//输出为lumia
//在克隆过来的对象里面修改其引用的对象的值,会发现原对象所引用的值也发生了改变
myClassCopy.getMyFriend().setName("Toxni");
Log.d("MyClassTest" , myClass .getName());//输出为lypeer
Log.d("MyClassTest" , myClass .getMyFriend().getName);//输出为Toxni
也就是说如果clone的原本对象里面有应用其他的对象的话,由于clone是浅克隆的,clone得到的对象将与原对象共享引用的对象,但是非引用的对象使相互独立的。
那么如何实现深克隆呢?
- 使被引用的对象也实现Cloneable接口,并且重写clone方法,使得被引用的对象也可被clone,然后在clone原对象的时候将被引用的对象也clone一遍,将clone得到的被引用对象设置成为clone得到的目标对象的属性值,相当于是原本clone是不会考虑应用对象的,那要考虑的话我们就手动把它clone一遍。(好像说的有点绕……)
- 使用序列化与反序列化来做深克隆。
使用Class.forName()
MyClass myClass = (MyClass)class.forName("com.lypeer.blog.myclass").newInstance();
- 调用Class.forName()方法的时候实际上已经load了MyClass,并且对它进行了初始化(也就是类中的static变量或方法已经run过了),但是还没有产生任何对象。
- 方法底层还是使用了ClassLoader,但是是使用的调用此方法的类的ClassLoader。
- 这种方式只能获得non-primitive class。
- 由于是采用newInstance()方法生成新的类,目标类必须有public default constructor。如果目标类没有public default constructor,就会抛出一个InstantiationException。
使用ClassLoader.loadClass()
MyClass myClass = MyClass.class.getClassLoader().loadClass("com.lypeer.blog.myclass").newInstance();
- ClassLoader.loadClass()与Class.forName()一个很大的区别就是loadClass()执行完毕之后虽然已经load了MyClass,但是并没有对它进行初始化。同样方法结束的时候还没有产生任何对象。
- 使用的是系统的class loader。
- 由于是采用newInstance()方法生成新的类,目标类必须有public default constructor。如果目标类没有public default constructor,就会抛出一个InstantiationException。
通过Object deserialization
MyClass myClass = new MyClass();
myClass.setName("lypeer");
//生成ObjectOutputStream对象
FileOutputStream out = new FileOutputStream("lypeer.txt");
ObjectOutputStream oout = new ObjectOutputStream(out);
//将要复制的对象写入ObjectOutputStream里面
oout.writeObject(myClass);
oout.flush();
//将写入的对象从流里面读出
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("lypeer.txt"));
MyClass myClassCopy = (MyClass) ois.readObject();
这种拷贝就是深拷贝。
结语
综上,Java中共有五种方式来新建对象,其中大致可以分为两类
- 克隆类
- 这种方式新建对象需要有原有的对象,然后新的对象使根据原有对象进行克隆得到的。
- 又分为浅克隆和深克隆,浅克隆为实现Cloneable接口然后使用clone方法,深克隆为deserialization得到对象。
- 浅克隆也可手动改为深克隆。
- 非克隆类
- 这种方式新建对象不需要原有对象。
- 通过new关键字或者通过类名直接生成。
- 通过类名生成的两种方式也有细微的区别和局限性。
That’s all.
Ps : 如果大家知道有别的方式新建对象的话欢迎指出。