Java建立对象的几种方式

前言:原先一直没怎么关注Java建立对象的方式,由于基本上只知道用new😂,直到58面试官问我Java有哪些方式能够建立对象。因此稍微整理了一下几种建立对象的方式。java

1.使用new关键字建立对象

这是Java中最经常使用,且简单的方式建立对象。其实这种方式是经过调用构造函数(有参、无参……)建立对象的。如:String name = new String("hello"); 那执行这条语句时JVM作了什么?面试

  1. 首先在方法区的常量池中查看是否有new 后面参数(也就是类名)的符号引用,并检查是否有类的加载信息也就是是否被加载解析和初始化过。若是已经加载过了就不在加载,不然执行类的加载全过程。
  2. 加载完类后,大体作了以下三件事:

    a、给实例分配内存:此内存中存放对象本身的实例变量和从父类继承过来的实例变量(即便这些从超类继承过来的实例变量有可能被隐藏也会被分配空间),同时这些实例变量被赋予默认值(零值); b、调用构造函数,初始化成员字段:在Java对象初始化过程当中,主要涉及三种执行对象初始化的结构,分别是实例变量初始化实例代码块初始化以及构造函数初始化; c、user对象指向分配的内存空间: 注意:new操做不是原子操做,b和c的顺序可能会调换。express

2.使用clone方法建立对象

顾名思义,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:安全

  1. 要想让一个对象支持clone,必须让这个对象对应的类实现Cloneable接口(标识接口),同时此类中也要重写clone方法;其实Cloneable接口至关于一个合法的证实,代表此类能够合法的进行clone;Cloneable接口的注释中写着:A class implements the 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.
  2. Object类的clone()方法是线程不安全的;
  3. clone()有浅拷贝和深拷贝两种模式;

    浅拷贝是拷贝被拷贝对象的值(表层),若被拷贝对象的属性有引用类型的,则只拷贝引用的地址;深拷贝是拷贝被拷贝对象的全部值(深层),如有被拷贝对象有引用类型的属性,则也要拷贝其引用类型的属性所对应的对象;深拷贝还有完全深拷贝和未完全深拷贝等状况,其实完全深拷贝是很难的;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

3.使用反射建立对象

3.1Java反射机制

首先解释一下什么是Java的反射机制;Java 反射机制在程序运行时,对于任意一个类,都可以知道这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意一个方法和属性。这种动态的获取信息以及动态调用对象的方法的功能称为java 的反射机制。 反射机制很重要的一点就是“运行时”,其使得咱们能够在程序运行时加载、探索以及使用编译期间彻底未知的 .class 文件。换句话说,Java 程序能够加载一个运行时才得知名称的 .class 文件,而后获悉其完整构造,并生成其对象实体、或对其 fields(变量)设值、或调用其 methods(方法)。ide

3.2使用反射建立对象

主要包括两个步骤:函数

  1. 获取类的Class对象实例,获取方式主要有:
    • Class.forName("类全路径");
    • 类名.class; 如:Animal.class;
    • 对象名.getClass();
  2. 经过反射建立类对象的实例对象;获取Class对象实例后,能够经过Java反射机制建立类对象实例对象;主要有两种方式:
    • Class.newInstance():调用无参的构造方法,必需确保类中有无参数的可见的构造函数,不然将会抛出异常;
    • 调用类对象的构造方法:
  3. 强制转换成用户所需类型;

举个简单的例子:测试

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引用指向的对象属性值为默认值;关于反射详细的内容后续单独写篇文章介绍;

4.使用反序列化建立对象

  1. 序列化:

    提及反序列化,首先得介绍序列化;什么是序列化呢?咱们把变量从内存中变成可存储或传输的过程称之为序列化(廖雪峰老师官网给出的解释),其实就是把对象写入IO流中;Java中要序列化的类必须实现Serializable接口;

  2. 序列化场景:

    • 全部可在网络上传输的对象都必须是可序列化的;如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,不然会出错;
    • 全部须要保存到磁盘的java对象都必须是可序列化的;一般建议:程序建立的每一个JavaBean类都实现Serializeable接口;
  3. 序列化与反序列化实现方式:序列化的对象所对应的类必须实现Serializable接口或Externalizable接口;

    • Serializable接口序列化举例:Serializable接口是一个标记接口,不用实现任何方法。一旦实现了此接口,该类的对象就是可序列化的;固然还有Externalizable接口序列化方式,详细的状况另行介绍;
    • 反序列化:从IO流中恢复对象
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本身生成的对象,不经过构造方法生成;

相关文章
相关标签/搜索