Java创建对象的5种方式
- 直接new,调用了构造器
- 通过clone(),没有调用构造器
- 通过反射,调用了构造器
- 通过反序列化,没有调用构造器
- 通过Unsafe类的allocateInstance()方法,没有调用构造器
1. 直接new
public class CreateByNew {
public CreateByNew() {
System.out.println("调用了构造...");
}
public static void main(String[] args) {
CreateByNew c1 = new CreateByNew();
CreateByNew c2 = new CreateByNew();
System.out.println(c1 == c2);//false
}
}
输出:
调用了构造...
调用了构造...
false
2. 通过clone()
需要实现Cloneable接口,可分为深克隆和浅克隆。clone()后的新对象会复制原对象的属性,但是并不会调用构造函数。
public class CreateByClone implements Cloneable {
public int temp;
public CreateByClone() {
System.out.println("调用了构造..");
}
public static void main(String[] args) throws CloneNotSupportedException {
CreateByClone c1 = new CreateByClone();
c1.temp = 222;
CreateByClone c2 = (CreateByClone) c1.clone();
System.out.println(c2.temp);
System.out.println(c1 == c2);
}
}
输出:
调用了构造..
222
false
3. 通过反射
反射创建对象:
- class.newInstance():调用了无参构造
- 获取对应的Constructor,调用constructor的newInstance(),调用对应构造函数创建对象
public class CreateByReflection {
private int temp;
public int getTemp() {
return temp;
}
public CreateByReflection() {
System.out.println("调用了空参构造...");
}
public CreateByReflection(int temp) {
this.temp = temp;
System.out.println("调用了带参构造...");
}
public static void main(String[] args) throws Exception {
Class clazz = CreateByReflection.class;
//通过无参构造反射创建
CreateByReflection c1 = (CreateByReflection) clazz.newInstance();
//通过带参构造反射创建
Constructor constructor = clazz.getDeclaredConstructor(int.class);
CreateByReflection c2 = (CreateByReflection) constructor.newInstance(10);
System.out.println(c2.getTemp());
System.out.println(c1 == c2);
}
}
输出:
调用了空参构造...
调用了带参构造...
10
false
4. 反序列化创建对象
需要被序列化的对象实现Serializable接口,不会调用构造,反序列化回来的对象的属性值与序列化之前一致,但是是一个新对象。
public class CreateBySerializable {
public static void main(String[] args) {
Person p1 = new Person("二狗", 18);
writeObject(p1);
Person p2 = readObjcet();
System.out.println(p2);
System.out.println(p1 == p2);
}
public static void writeObject(Person person) {
FileOutputStream fileOut = null;
ObjectOutputStream out = null;
try {
fileOut = new FileOutputStream("person.txt");
out = new ObjectOutputStream(fileOut);
out.writeObject(person);
System.out.println("Serialized data is saved");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static Person readObjcet() {
Person temp = null;
FileInputStream fileIn = null;
ObjectInputStream in = null;
try {
fileIn = new FileInputStream("person.txt");
in = new ObjectInputStream(fileIn);
temp = (Person) in.readObject();
System.out.println("Deserialized Person...");
return temp;
} catch (Exception e) {
e.printStackTrace();
return null;
}finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fileIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Person implements Serializable {
public Person() {
System.out.println("调用了空参构造...");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("调用了带参构造...");
}
public String name;
public int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
输出:
调用了带参构造...
Serialized data is saved
Deserialized Person...
Person{name='二狗', age=18}
false
5. 通过Unsafe类
Unsafe类通过native方法直接操作内存分配空间,创建对象。此时对象并没有执行构造,只是在内存中分配了空间,所有属性此时都是对应类型的0值。而且该对象并不被JVM管理,需要我们自己回收。
Unsafe类的构造为私有,且通过@CallerSensitive方法保证只有BootStrap类加载器加载的类才可以调用Unsafe类中的方法。
所以只能通过反射获取Unsafe类实例。
public class CreateByUnsafe {
public static void main(String[] args) throws Exception {
//基于反射获取Unsafe实例
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
People p1 = (People) unsafe.allocateInstance(People.class);
People p2 = (People) unsafe.allocateInstance(People.class);
p1.age = 18;
System.out.println(p1);
System.out.println(p1 == p2);
//返回成员变量在内存中的地址相对于对象内存地址的偏移量
Field f = People.class.getDeclaredField("age");
long offset = unsafe.objectFieldOffset(f);
System.out.println(offset);//12
// markword:8bytes(64bits) + class pointer:4bytes(32bits) == 12 bytes
}
}
class People {
public int age;
@Override
public String toString() {
return "People{" +
"age=" + age +
'}';
}
}
输出:
People{age=18}
false
12