前言:原先一直没怎么关注Java建立对象的方式,由于基本上只知道用new😂,直到58面试官问我Java有哪些方式能够建立对象。因此稍微整理了一下几种建立对象的方式。java
这是Java中最经常使用,且简单的方式建立对象。其实这种方式是经过调用构造函数(有参、无参……)建立对象的。如:String name = new String("hello"); 那执行这条语句时JVM作了什么?面试
a、给实例分配内存:此内存中存放对象本身的实例变量和从父类继承过来的实例变量(即便这些从超类继承过来的实例变量有可能被隐藏也会被分配空间),同时这些实例变量被赋予默认值(零值); b、调用构造函数,初始化成员字段:在Java对象初始化过程当中,主要涉及三种执行对象初始化的结构,分别是实例变量初始化、实例代码块初始化以及构造函数初始化; c、user对象指向分配的内存空间: 注意:new操做不是原子操做,b和c的顺序可能会调换。express
顾名思义,clone就是克隆,也就是复制;使用某个对象的clone()方法时(前提是此对象的对应的类中已经实现clone方法),JVM根据被拷贝的对象分配内存、建立新的对象,而后会把被clone的对象的值全都拷贝进去;clone()方法是属于Object类的,clone是在堆内存中用二进制的方式进行拷贝,从新分配给对象一块内存;Object类的clone方法是一个native方法,它的注释中写着:Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object. The general intent is that, for any object {@code x}, the expression:安全
Cloneable
interface to indicate to the {@link java.lang.Object#clone()} method that it is legal for that method to make a field-for-field copy of instances of that class.浅拷贝是拷贝被拷贝对象的值(表层),若被拷贝对象的属性有引用类型的,则只拷贝引用的地址;深拷贝是拷贝被拷贝对象的全部值(深层),如有被拷贝对象有引用类型的属性,则也要拷贝其引用类型的属性所对应的对象;深拷贝还有完全深拷贝和未完全深拷贝等状况,其实完全深拷贝是很难的;bash
clone详情可查阅大佬博客:详解Java中的clone方法 举个简单的clone对象的例子:网络
public class Animal implements Cloneable{
private String name = null;
private int age = 0;
public Animal(){ }
public Animal(String name, int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Animal animal = (Animal) super.clone();
animal.name = new String(name);
return animal;
}
public static void main(String[] args) throws CloneNotSupportedException {
Animal dog = new Animal("Dog", 1);
Animal smallDog = (Animal)dog.clone();
System.out.println("dog:" + dog.getName() + ", " + dog.getAge());
System.out.println("smallDog:" + smallDog.getName() + ", " + smallDog.getAge());
System.out.println(dog);
System.out.println(smallDog);
}
}
复制代码
程序运行的结果为: dog:Dog, 1 smallDog:Dog, 1 createobject.Animal@4554617c createobject.Animal@74a14482 可见,两个对象的属性值同样,而地址不同,因此clone dog对象成功;app
首先解释一下什么是Java的反射机制;Java 反射机制在程序运行时,对于任意一个类,都可以知道这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意一个方法和属性。这种动态的获取信息以及动态调用对象的方法的功能称为java 的反射机制。 反射机制很重要的一点就是“运行时”,其使得咱们能够在程序运行时加载、探索以及使用编译期间彻底未知的 .class 文件。换句话说,Java 程序能够加载一个运行时才得知名称的 .class 文件,而后获悉其完整构造,并生成其对象实体、或对其 fields(变量)设值、或调用其 methods(方法)。ide
主要包括两个步骤:函数
举个简单的例子:测试
public class Apple {
private int price;
protected String color;
public String name;
public Apple(){}
public Apple(String name, int price, String color){
this.name = name;
this.price = price;
this.color = color;
}
public int getPrice() {
return price;
}
public void setPrice(int price){
this.price = price;
}
public String getColor(){
return color;
}
public void setColor(String color){
this.color = color;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public String printMsg(){
return "name:" + name + ", price:" + price + ", color:" + color;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//正常new对象
Apple apple1 = new Apple("Apple1", 6666, "red");
System.out.println("Apple1:" + apple1);
System.out.println("Apple1: " + apple1.printMsg());
//使用反射建立对象
//获取类的Class对象实例
Class cls = Class.forName("reflect.Apple");
//根据Class对象实例获取Constructor对象
Constructor appleConstructor = cls.getConstructor();
//根据Constructor对象的newInstance方法获取反射类对象
Object appleObject = appleConstructor.newInstance();
//强制类型转换
Apple apple2 = (Apple)appleObject;
System.out.println("Apple2:" + apple2);
// System.out.println("Apple2:" + appleObject);
System.out.println("Apple2: " + apple2.printMsg());
}
}
复制代码
运行结果为: Apple1:reflect.Apple@4554617c Apple1: name:Apple1, price:6666, color:red Apple2:reflect.Apple@74a14482 Apple2: name:null, price:0, color:null apple1和apple2保存的地址不同,成功反射一个Apple对象,反射时使用的是无参构造函数,因此apple2引用指向的对象属性值为默认值;关于反射详细的内容后续单独写篇文章介绍;
序列化:
提及反序列化,首先得介绍序列化;什么是序列化呢?咱们把变量从内存中变成可存储或传输的过程称之为序列化(廖雪峰老师官网给出的解释),其实就是把对象写入IO流中;Java中要序列化的类必须实现Serializable接口;
序列化场景:
序列化与反序列化实现方式:序列化的对象所对应的类必须实现Serializable接口或Externalizable接口;
import java.io.Serializable;
/**
* 需序列化的对象对应的类
*/
public class Person implements Serializable {
private String name;
private int age;
//没有无参构造方法
public Person(String name, int age){
// System.out.println("反序列化,你调用我了么?");
this.name = name;
this.age = age;
}
@Override
public String toString(){
return "Person{ " + "name = '" + name + '\'' + ", age = " + age + '}'; } } 复制代码
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
/**
* 序列化步骤:
* ①建立一个ObjectOutputStream输出流
* ②调用ObjectOutputStream对象的writeObject输出可序列化对象
*/
public class WriteObject {
public static void main(String[] args){
try{
//建立ObjectOutputStream输出流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("person.txt"));
//将对象序列化到文件s
Person person = new Person("冯澍滢", 25);
objectOutputStream.writeObject(person);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("success~");
}
}
复制代码
import java.io.FileInputStream;
import java.io.ObjectInputStream;
/**
* 反序列化步骤:
* ①建立一个ObjectInputStream输入流
* ②调用ObjectInputStream对象的readObject()获得序列化的对象
*/
public class ReadObject {
public static void main(String[] args){
try{
//建立一个ObjectInputStream输入流
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("person.txt"));
Person person = (Person)objectInputStream.readObject();
System.out.println(person);
}catch (Exception e){
e.printStackTrace();
}
}
}
复制代码
反序列化时能够在对应类的构造方法中添加输出语句测试反序列化时有没有调用构造方法;结果代表,反序列化并不会调用构造方法;反序列的对象是由JVM本身生成的对象,不经过构造方法生成;