创建 Java 对象,跟”回”字一样也有好几种写法。虽然简单,但是也涉及到了几个 Java 关键的基础知识,比如反射、克隆、序列化与反序列化,所以面试也经常会遇到,然后不断扩展的问。
第一种:通过 new 关键字创建
这一种没啥好说的,从学 Java 第一天就不停的跟两样东西打交道,一个的是 new 关键字,一个是NullPointerException 😂。代码如下
测试对象代码 Person.java
package cn.coder4j.blog.demo.code.crete.object.method;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 人员类
*/
public class Person implements Cloneable {
/**
* 年龄
*/
private Integer age;
/**
* 用户名
*/
private String name;
public Person(Integer age, String name) {
this.age = age;
this.name = name;
}
public Person() {
}
/**
* Getter method for property <tt>age</tt>.
*
* @return property value of age
*/
public Integer getAge() {
return age;
}
/**
* Setter method for property <tt>age</tt>.
*
* @param age value to be assigned to property age
*/
public void setAge(Integer age) {
this.age = age;
}
/**
* Getter method for property <tt>name</tt>.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property <tt>name</tt>.
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return ReflectionToStringBuilder.reflectionToString(this,
ToStringStyle.SHORT_PREFIX_STYLE);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
创建对象代码
/**
* 通过 new 关键字去创建对象
*/
@Test
public void testNew() {
Person person = new Person();
person.setAge(18);
person.setName("kiwi");
System.out.println(person);
}
第二种:通过 class 的 newInstance() 方法
这里就涉及到了 Java 基础里面的反射知识了,大多数框架也是通过这种方式创建的对象,比如说 spring。通过反射拿到 class 对象,再直接调用 newInstance() 方法就可以直接创建出一个对象。获得 class 对象的方法也有好几种,这里直接通过类来获得。代码如下:
创建对象代码
/**
* 通过类反射
*/
@Test
public void testClassReflect() throws IllegalAccessException, InstantiationException {
Person person = Person.class.newInstance();
person.setAge(18);
person.setName("kiwi");
System.out.println(person);
}
第三种:通过 constructor 的 newInstance() 方法
与第二种方法一样,同样是通过反射。也是拿到 class 对象,不过这里,拿到对象后,又多了一步去拿构造函数。可能有人觉得疑问了,与第二种达到的结果是一样的,但是还要多写一些代码,为什么还要用他。其实对于有无参构造函数的类来说,两种方法都一样,哪个都可以使用。但是是对于只有有参构造函数的类来说,只能使用第三种。因为第二种无法指定构建函数。所以因为大多数框架使用的都是第二种包括 spring,所以当你的 bean 没有无参构造函数时,框架就会报错,他是不会帮你用第三种的。代码如下:
创建对象代码
/**
* 通过构建函数反射创建
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
@Test
public void testConstructReflect() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<Person> constructor = Person.class.getConstructor();
Person person = constructor.newInstance();
person.setAge(18);
person.setName("kiwi");
System.out.println(person);
}
/**
* 通过有参构造函数反射创建
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
@Test
public void testConstructWithParamReflect() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<Person> constructor = Person.class.getConstructor(Integer.class, String.class);
Person person = constructor.newInstance(18, "kiwi");
System.out.println(person);
}
第四种:通过 clone
可以看到 Person.java 实现了接口 Cloneable 并覆盖了 clone 方法(其实只是调用了父类方法),然后调用对象的 clone 方法就可以创建一个一毛一样的对象。需要注意的是这是是浅克隆,啥是浅克隆?浅克隆是指复制出来的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。也就是说修改克隆后的对象中的引用变量,也会导致原对象也产生变化。深克隆反之。代码如下:
创建对象代码
/**
* 通过克隆
*/
@Test
public void testClone() throws CloneNotSupportedException {
Person person = new Person();
person.setAge(18);
person.setName("kiwi");
Person personClone = (Person)person.clone();
System.out.println(personClone);
}
第五种:通过反序列化
序列化与反序列化有很多种,包括 json,xml 其实都是。这里使用的是 java 原生的 Serializable 实现的序列化。很多 rpc 框架,比如 dubbo 使用的就是这种方式,这里需要类实现 jdk 的 Serializable 接口,并且给他一个 serialVersionUID 属性。代码如下:
测试对象代码 PersonDto.java
package cn.coder4j.blog.demo.code.crete.object.method;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.io.Serializable;
/**
* 人员类
*/
public class PersonDto implements Serializable {
private static final long serialVersionUID = 3911118195380172132L;
/**
* 年龄
*/
private Integer age;
/**
* 用户名
*/
private String name;
public PersonDto(Integer age, String name) {
this.age = age;
this.name = name;
}
public PersonDto() {
}
/**
* Getter method for property <tt>age</tt>.
*
* @return property value of age
*/
public Integer getAge() {
return age;
}
/**
* Setter method for property <tt>age</tt>.
*
* @param age value to be assigned to property age
*/
public void setAge(Integer age) {
this.age = age;
}
/**
* Getter method for property <tt>name</tt>.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property <tt>name</tt>.
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return ReflectionToStringBuilder.reflectionToString(this,
ToStringStyle.SHORT_PREFIX_STYLE);
}
}
创建对象代码
/**
* 通过反序列化
* @throws IOException
* @throws ClassNotFoundException
*/
@Test
public void testSerializable() throws IOException, ClassNotFoundException {
PersonDto person = new PersonDto();
person.setAge(18);
person.setName("kiwi");
// 把对象序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person"));
oos.writeObject(person);
oos.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person"));
PersonDto personSeri = (PersonDto) ois.readObject();
System.out.println(personSeri);
}
这里就说完了,比较常用的几种 Java 对象创建方法了。由此可以看出单例模式是无法保证系统中只有一个对象的。