Java類初始化和例項化
Java有以下幾種方式建立類物件:
- 利用new關鍵字
- 利用反射Class.newInstance
- 利用Constructor.newIntance(相比Class.newInstance多了有參和私有建構函式)
- 利用Cloneable/Object.clone()
- 利用反序列化
Constructor.newInstance不支援帶原型入參的建構函式。
呼叫Class.getConstructor()方法獲取無參預設構造Constructor時,如果使用者自定義了有參建構函式,因為此時java並不會生成預設建構函式,所以Class.getConstructor()方法因找不到無參預設建構函式而拋異常。此時需要顯示定義預設建構函式:
// Initialization.java
public class Initialization {
private int age = 2000;
private int salary = age + 1000;
private String name = "Tom";
public Initialization() {
print();
}
public Initialization(Integer salary, String name) {
print();
this.salary = salary;
this.name = name;
print();
}
/**
* Static code
*/
{
salary += 500;
}
private void print() {
System.out.println("age=" + this.age);
System.out.println("salary=" + this.salary);
System.out.println("name=" + this.name);
}
public static Initialization construct(int salary, String name) throws Exception {
Constructor<Initialization> constructorWithNoParams = Initialization.class.getConstructor();
Constructor<Initialization> constructorWithParams = Initialization.class.getConstructor(Integer.class, String.class);
return salary <= 0 || name == null ? constructorWithNoParams.newInstance() : constructorWithParams.newInstance(salary, name);
}
public Initialization deSerialize() throws Exception {
// 寫物件
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("student.txt"));
output.writeObject(this);
output.close();
// 讀取物件
ObjectInputStream input = new ObjectInputStream(new FileInputStream("student.txt"));
return (Initialization) input.readObject();
}
}
再來看下Initialization類被編譯為.class檔案後的資訊:
public class Initialization {
private int age = 2000;
private int salary;
private String name;
public Initialization() {
this.salary = this.age + 1000;
this.name = "Tom";
this.salary += 500;
this.print();
}
public Initialization(Integer salary, String name) {
this.salary = this.age + 1000;
this.name = "Tom";
this.salary += 500;
this.print();
this.salary = salary.intValue();
this.name = name;
this.print();
}
private void print() {
System.out.println("age=" + this.age);
System.out.println("salary=" + this.salary);
System.out.println("name=" + this.name);
}
public static Initialization construct(int salary, String name) throws Exception {
Constructor constructorWithNoParams = Initialization.class.getConstructor(new Class[0]);
Constructor constructorWithParams = Initialization.class.getConstructor(new Class[]{Integer.class, String.class});
return salary > 0 && name != null?(Initialization)constructorWithParams.newInstance(new Object[]{Integer.valueOf(salary), name}):(Initialization)constructorWithNoParams.newInstance(new Object[0]);
}
public Initialization deSerialize() throws Exception {
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("student.txt"));
output.writeObject(this);
output.close();
ObjectInputStream input = new ObjectInputStream(new FileInputStream("student.txt"));
return (Initialization)input.readObject();
}
public static void main(String[] args) throws Exception {
Initialization result = construct(0, "Paul");
}
}
1、無論例項變數還是例項程式碼塊,均遵從先父類後子類的初始化順序。
2、對例項變數直接賦值或者利用例項程式碼塊賦值,編譯器會其程式碼填充到類的建構函式中。不允許書寫順序靠前的例項程式碼初始化在其後定義的例項變數。
Java強制要求Object物件(Object是Java的頂層物件,沒有超類)之外的所有物件建構函式的第一條語句必須是超類建構函式的呼叫語句或者是類中定義的其他的建構函式,如果我們既沒有呼叫其他的建構函式,也沒有顯式呼叫超類的建構函式,那麼編譯器會為我們自動生成一個對超類建構函式的呼叫。這樣確保當前物件完成初始化前其父類已完成初始化,從而構建完整的物件。
如果預設建構函式內部呼叫了有參建構函式,僅允許在有參建構函式裡呼叫父類建構函式。
靜態程式碼塊
- 多個static按編碼順序依次處理
- static變數的申明和初始化是兩個不同的操作
- static變數在編譯期已確認值
以下程式碼是等價的:
// code list1
public class StaticInitialization {
static {
data = 1;
}
public static void main(String[] args) {
System.out.println(data);
}
private static int data = 2;
}
// code list 2
public class StaticInitialization {
private static int data;
static {
data = 1;
data = 2;
}
public static void main(String[] args) {
System.out.println(data);
}
}
code list 2的位元組碼為:
public class StaticInitialization {
public StaticInitialization();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #3 // Field data:I
6: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
9: return
static {};
Code:
0: iconst_1
1: putstatic #3 // Field data:I
4: iconst_2
5: putstatic #3 // Field data:I
8: return
}
可以看到static變數在編譯期就已放到常量const指向的記憶體地址裡。
初始化順序
如果沒有繼承關係,初始化順序為:靜態程式碼、靜態程式碼塊 > 成員變數、例項程式碼塊 > 建構函式。否則,初始化順序為先父後子。
所以初始化順序:父類static靜態變數 > 父類static程式碼塊 > 子類static靜態變數 > 子類static程式碼塊 > 父類變數 > 父類例項程式碼塊 > 父類建構函式 > 子類變數 > 子類例項程式碼塊 > 子類建構函式
舉個例子:
public class StaticTest {
public static void main(String[] args) {
staticFunction();
}
private static int data;
static StaticTest st;
static { //靜態程式碼塊
System.out.println("1");
}
{ // 例項程式碼塊
System.out.println("2");
}
static {
data = 1;
}
static {
st = new StaticTest();
}
StaticTest() { // 例項構造器
System.out.println("3");
System.out.println("a=" + a + ",b=" + b);
}
public static void staticFunction() { // 靜態方法
System.out.println("4");
}
int a = 110; // 例項變數
static int b = 112; // 靜態變數
}
/**
* 輸出結果
* 1
* 2
* 3
* a=110,b=0
* 4
*/
相關文章
- C++ 結構體例項和類例項的初始化C++結構體
- java 例項變數初始化Java變數
- 淺談Java中的例項初始化器Java
- java中父類宣告子類例項化Java
- 關於類的初始化以及類的例項化一些思考
- 來說說 Java 中的例項初始化器Java
- 來說說Java中的例項初始化器Java
- Java的類的例項化順序Java
- Java中內部類的例項化Java
- 類和例項
- 類的例項化順序和分析
- java中靜態初始化塊,例項初始化塊,建構函式區別Java函式
- Java 類初始化順序Java
- Java類初始化順序Java
- python中類的建立和例項化Python
- C#中類的建立和例項化C#
- Java子類和父類的初始化執行順序Java
- 解析Java類和物件的初始化過程(轉)Java物件
- Java--例項化Java
- 類變數的初始化時機總是處於例項變數的初始化時機之前!變數
- css樣式初始化程式碼例項CSS
- 樣式初始化程式碼例項分享
- Java類初始化執行流程Java
- java類初始化的順序Java
- Python中類建立和例項化過程Python
- python 類和例項Python
- java多型抽象類例項Java多型抽象
- Dart - 抽象類的例項化Dart抽象
- 【Java面試高頻】i++和++i的區別,單例模式的多種實現以及區別,類和例項初始化順序,不看血虧Java面試單例模式
- ThinkPHP6 例項化 Http 類和依賴注入PHPHTTP依賴注入
- Java中String類的初始化?Java
- Java類(繼承)初始化順序Java繼承
- ruby中的類例項變數和例項的例項變數變數
- vue 原始碼學習(二) 例項初始化和掛載過程Vue原始碼
- vue 原始碼學習(二) 例項初始化和掛載過程Vue原始碼
- 初始化python類的例項時,私有變數的值與上一個例項的相同,問題定位Python變數
- JVM初探(五):類的例項化JVM
- objective-c 通過類名例項化類Object