前言:原先一直沒怎麼關注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本身生成的對象,不經過構造方法生成;