java建立對象的五種方式

java中建立對象有幾種方式?

1、使用new關鍵字

如 User user=new User();html

執行這條語句,jvm作了什麼?java

  1. 首先在方法區的常量池中查看是否有new 後面參數(也就是類名)的符號引用,並檢查是否有類的加載信息也就是是否被加載解析和初始化過。若是已經加載過了就不在加載,不然執行類的加載全過程android

  2. 加載完類後,大體作了以下三件事: a、給實例分配內存 b、調用構造函數,初始化成員字段 c、user對象指向分配的內存空間 注意:new操做不是原子操做,b和c的順序可能會調換數據庫

2、使用clone方法

當咱們調用一個對象的clone方法,jvm就會建立一個新的對象,將前面對象的內容所有拷貝進去。用clone方法建立對象並不會調用任何構造函數。由於Object 類的 clone 方法的 原理是從內存中(堆內存)以二進制流的方式進行拷貝,從新分配一個內存塊,那構造函數沒有被執行也是很是正常的了.安全

使用clone方法建立對象的實例:網絡

public class CloneTest implements Cloneable{

    private String name; 

    private int 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;

    }

    public CloneTest(String name, int age) {

        super();

        this.name = name;

        this.age = age;

    }

    public static void main(String[] args) {

        try {

            CloneTest cloneTest = new CloneTest("酸辣湯",18);//todo

            CloneTest copyClone = (CloneTest) cloneTest.clone();

            System.out.println("newclone:"+cloneTest.getName());

            System.out.println("copyClone:"+copyClone.getName());

        } catch (CloneNotSupportedException e) {

            e.printStackTrace();

        }

    }

}

輸出:

newclone:酸辣湯

copyClone:酸辣湯

複製代碼
注意:
1.clone是Object中的方法,Cloneable是一個標識接口,它代表這個類的對象是能夠拷貝的。若是沒有實現Cloneable接口卻調用了clone()函數將拋出異常

2.Object.clone()未作同步處理,線程不安全

3.clone()有深拷貝和淺拷貝兩種方式
複製代碼

3、使用反序列化

序列化是幹什麼的?

簡單說就是爲了保存在內存中的各類對象的狀態(也就是實例變量,不是方法),而且能夠把保存的對象狀態再讀出來。雖然你能夠用你自 己的各類各樣的方法來保存object states,可是Java給你提供一種應該比你本身好的保存對象狀態的機制,那就是序列化。一句話歸納:序列化是指將對象的狀態信息轉換爲能夠存儲或傳輸的形式的過程。jvm

java中要序列化的類必要實現Serializable接口ide

什麼狀況下須要序列化

a)當你想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;函數

b)當你想用套接字在網絡上傳送對象的時候;post

c)當你想經過RMI(遠程方法調用)傳輸對象的時候;

使用反序列化建立對象實例:

1.對象要實現Serializable接口

import java.io.Serializable; 

public class Person implements Serializable { 

    int age; 

    int height; 

    String name; 

    public Person(String name, int age, int height){ 

        this.name = name; 

        this.age = age; 

        this.height = height; 

    } 

}

複製代碼

二、序列化與反序列化

import java.io.FileInputStream; 

import java.io.FileOutputStream; 

import java.io.IOException; 

import java.io.ObjectInputStream; 

import java.io.ObjectOutputStream; 

public class MyTestSer { 

/** * Java對象的序列化與反序列化 */ 

public static void main(String[] args) { 

  Person zhangsan = new Person("zhangsan", 30, 170); 

  Person lisi = new Person("lisi", 35, 175); 

  Person wangwu = new Person("wangwu", 28, 178); 

  try { 

      //須要一個文件輸出流和對象輸出流;文件輸出流用於將字節輸出到文件,對象輸出流用於將對象輸出爲字節 

      ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser")); 

      out.writeObject(zhangsan); 

      out.writeObject(lisi); 

      out.writeObject(wangwu); 

  } catch (IOException e) { 

e.printStackTrace(); 

  } 

  try { 

      ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser")); 

      Person one = (Person) in.readObject(); 

      Person two = (Person) in.readObject(); 

      Person three = (Person) in.readObject(); 

      System.out.println("name:"+one.name + " age:"+one.age + " height:"+one.height); 

      System.out.println("name:"+two.name + " age:"+two.age + " height:"+two.height); 

      System.out.println("name:"+three.name + " age:"+three.age + " height:"+three.height); 

  } catch (Exception e) { 

e.printStackTrace(); 

  } 

  } 

}

運行結果:

name:zhangsan age:30 height:170  //todo

name:lisi age:35 height:175 

name:wangwu age:28 height:178

複製代碼

android中的場景

1.組件間(如activity間)的對象傳遞 (實現Parcelable或Serializable接口)

2.使用 Binder進行進程間的通信傳遞的對象必須實現Parcelable接口

Serializable 是java的序列化接口,使用簡單可是開銷比較大,序列化和反序列化都涉及到大量的I/O操做,效率相對較低。Parcelable是Android提供的序列化方法,更適用於Android平臺,效率很高,可是使用起來比較麻煩.Parcelable主要用在內存序列化上,序列化存儲設備或將序列化後的對象經過網絡傳輸建議使用Serializable。

4、使用反射

經過反射來建立類對象的實例,有兩個步驟:

  1. 首先咱們得拿到類對象的Class

    如何獲取? 有三種方式(反射章節會詳細講解)

    • 類.class,如Person.class
    • 對象.getClass()
    • Class.forName("類全路徑")
  2. 經過反射建立類對象的實例對象

在拿到類對象的Class後,就能夠經過Java的反射機制來建立類對象的實例對象了,主要分爲兩種方式:

  • Class.newInstance()

  • 調用類對象的構造方法

舉個栗子:

首先準備一個Person的類:

public class Person {

    private String name;

    private int age;

    public Person() {

    }

    public Person(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

    public String toString() {

        return "Person{" +

                "name='" + name + '\'' +

                ", age=" + age +

                '}';

    }

}

複製代碼
使用Class.newInstance()建立對象:
public class ClassNewInstance {

    public static void main(String[] args) throws IllegalAccessException, InstantiationException {

        Person person = Person.class.newInstance();

        person.setAge(18);

        person.setName("酸辣湯");

        System.out.println(person);

    }

}

運行結果:

Person{name='酸辣湯', age=18}

複製代碼

注意 :newInstance建立對象實例的時候會調用無參的構造函數,因此必需確保類中有無參數的可見的構造函數,不然將會拋出異常。

調用類對象的構造方法——Constructor

Constructor是Java反射機制中的構造函數對象,獲取該對象的方法有如下幾種:

  • Class.getConstructors():獲取類對象的全部可見的構造函數

  • Class.getConstructor(Class... paramTypes):獲取指定的構造函數

獲取類對象全部的構造方法並遍歷:

public class ConstructorInstance {

    public static void main(String[] args) {

        Class p = Person.class;

        for(Constructor constructor : p.getConstructors()){

            System.out.println(constructor);

        }

    }

}

運行結果:

public com.eft.reflect.Person()

public com.eft.reflect.Person(java.lang.String,int)

複製代碼

獲取指定的構造方法

經過Class.getConstructor(Class... paramTypes)便可獲取類對象指定的構造方法,其中paramTypes爲參數類型的Class可變參數,當不傳paramTypes時,獲取的構造方法即爲默認的構造方法。

public class ConstructorInstance {

    public static void main(String[] args) throws Exception {

        Class p = Person.class;

        Constructor constructor1 = p.getConstructor();//獲取默認的構造方法

        Constructor constructor2 = p.getConstructor(String.class,int.class);//獲取指定的構造方法

        System.out.println(constructor1);

        System.out.println(constructor2);

    }

}

運行結果:

public com.eft.reflect.Person()

public com.eft.reflect.Person(java.lang.String,int)

複製代碼
經過構造方法建立對象

Constructor對象中有一個方法newInstance(Object ... initargs),這裏的initargs即爲要傳給構造方法的參數,如Person(String,int),經過其對應的Constructor實例,調用newInstance方法並傳入相應的參數,便可經過Person(String,int)來建立類對象的實例對象。

測試代碼以下:

public class ConstructorInstance {

    public static void main(String[] args) throws Exception {

        Class p = Person.class;

        Constructor constructor = p.getConstructor(String.class,int.class);

        Person person = (Person) constructor.newInstance("酸辣湯",18);

        System.out.println(person); 

    }

}

運行結果:

Person{name='酸辣湯', age=18}

複製代碼

如上經過反射建立對象,只是反射機制中的冰山一角,詳細瞭解java反射知識,能夠參考這篇 java反射全解

5、使用Unsafe

sun.misc.Unsafe中提供allocateInstance方法,僅經過Class對象就能夠建立此類的實例對象,並且不須要調用其構造函數、初始化代碼、JVM安全檢查等。它抑制修飾符檢測,也就是即便構造器是private修飾的也能經過此方法實例化,只需提類對象便可建立相應的對象。因爲這種特性,allocateInstance在java.lang.invoke、Objenesis(提供繞過類構造器的對象生成方式)、Gson(反序列化時用到)中都有相應的應用。

直接看例子

package cn.eft.llj.unsafe;
import java.lang.reflect.Field;

import sun.misc.Unsafe;
public class Demo9 {

    static Unsafe unsafe;

    static {
        //獲取Unsafe對象
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class C1 {
        private String name;

        private C1() {
            System.out.println("C1 default constructor!");
        }

        private C1(String name) {
            this.name = name;
            System.out.println("C1 有參 constructor!");
        }
        
        public void test(){
            System.out.println("執行了test方法");
        }
    }

    public static void main(String[] args) throws InstantiationException {
        C1 c= (C1) unsafe.allocateInstance(C1.class);
        System.out.println(c);
        c.test();
    }
}
複製代碼

輸出結果:

cn.eft.llj.unsafe.Demo9$C1@6bc7c054
執行了test方法
複製代碼

關於Unsafe的詳細瞭解,能夠參考這篇Java魔法類:Unsafe應用解析

結尾

若是還有其餘建立對象的方式,歡迎在評論區留言~

相關文章
相關標籤/搜索