JAVA对象实例化的方法
本贴主要对JAVA实例化方式做个简单的总结,尤其是Unsafe的方式,可能很多人都没了解过,这个是sun工程师留的一个后门,可以称为“黑科技”
New对象实例
// 直接new对象实例
Productor productor = new Productor();
注意:任何类都有构造方法,但是new指令只能创建非抽象类的对象;构造方法不能是静态的,因为静态方法不能使用this,而构造方法中可以使用
反射机制
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
反射机制创建对象分为两种,一种是Class类的newInstance(),另一种是java.lang.reflect.Constructor类的newInstance()。
两者区别在于:
- Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
- Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
事实上Class的newInstance方法内部调用Constructor的newInstance方法。
反射机制创建对象,使用的是类加载机制,newInstance()的特点是在调用时才创建对象,通过类加载机制Class.forName("xxx").newInstance()创建对象,xxx可以从配置文件当中获取实际的值,这样达到了解耦的目的,也是Spring依赖注入的原理
// 1.通过反射调用Java.lang.Class
Productor productor = (Productor) Class.forName("com.quancheng.Productor").newInstance();
Productor productor = Productor.class.newInstance();
// 2.java.lang.reflect.Constructor类的newInstance()实例方法
Constructor<Productor> constructor = Productor.class.getConstructor();
Productor productor = constructor.newInstance();
clone()方法
clone方法分为浅拷贝和深拷贝
- 浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
- 深拷贝:不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值。
浅拷贝的效果就是,对引用对象的操作,会影响到所有引用了该对象的对象
public class Object {
// clone方法定义
protected native Object clone() throws CloneNotSupportedException;
}
注意:
- Cloneable只是一个标志,想要使用super.clone(),则需要实现Cloneable接口,否则就会抛出CloneNotSupportedException异常;
- clone()实现的是浅复制,在重载clone方法的时候,需要转为深复制。即属性存在对象引用的时候,需要对引用属性再进行clone()复制,直到没有对象引用;
public class Appliction {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User();
user.setName("Tom");
user.setAge(20);
user.setWeight(120);
System.err.println(user);
User u = (User) user.clone();
System.err.println(u);
}
}
class User implements Cloneable {
private String name;
private int age;
private Integer weight;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
......
}
反序列化
序列化对象就是对象此刻在内存中的状态转成的字节码。通过实现Serializable接口进行序列化。同Cloneable一样,Serializable也是一个空接口,作为一个标志使用。通过ObjectStream的writeObject()方法和readObject()方法来序列化和反序列化
还有一个Externalizable接口同样可以实现序列化,Externalizable继承了Serializable,同时增加了writeExternal()和readExternal()两个方法,可以指定序列化哪些属性,对于需要隐藏的属性,在前面加上transient就可以。
注意:序列化和反序列化是深复制,static、transient 后的变量无法序列化
Unsafe
使用Unsafe"黑科技"实例化对象
public class Appliction {
private static Unsafe U;
public static void main(String[] args) throws InstantiationException {
User user = (User) U.allocateInstance(User.class);
user.setName("Tom");
System.err.println(user.getName());
}
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
U = (Unsafe) theUnsafe.get("null");
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
class User implements Cloneable {
private String name;
private int age;
private Integer weight;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
......
}
注意:使用Unsafe方法实例化对象不会调用对象的构造方法
关于Unsafe的学习可以参见这篇博客:
https://blog.csdn.net/luoyoub/article/details/79918104