Java類的實(shí)例化順序
問(wèn)題描述
在驗(yàn)證《Core Java》第9版4-5代碼時(shí),發(fā)現(xiàn)程序輸出結(jié)果和自己理解的不太一樣。
import java.util.Random;class Employee { private static int nextId; private int id; private String name = ''; private double salary; static {Random generator = new Random();nextId = generator.nextInt(10000); } {id = nextId;nextId++; } public Employee(String name, double salary) {this.name = name;this.salary = salary; } public Employee(double salary) {this('Employee #' + nextId, salary); } public Employee() { } public String getName() {return name; } public double getSalary() {return salary; } public int getId() {return id; }}public class ConstructorTest { public static void main(String[] args) {Employee[] staff = new Employee[3];staff[0] = new Employee('Harry', 40000);staff[1] = new Employee(60000);staff[2] = new Employee();for (Employee e : staff) { System.out.println('id = ' + e.getId() + ', name = ' + e.getName() + ', salary = ' + e.getSalary());} }}
以下是輸出結(jié)果:
id = 6943, name = Harry, salary = 40000.0id = 6944, name = Employee #6944, salary = 60000.0id = 6945, name = , salary = 0.0
根據(jù)第一條語(yǔ)句得出靜態(tài)初始化塊生成的nextId為6943,然后在初始化塊中id被賦值為6943,nextId自增后為6944。再執(zhí)行第一個(gè)構(gòu)造函數(shù);
那么對(duì)于第二個(gè)對(duì)象來(lái)說(shuō),就應(yīng)該直接執(zhí)行初始化塊,此時(shí)id為6944,nextId自增為6945。再執(zhí)行第二個(gè)構(gòu)造函數(shù),此時(shí)this('Employee #' + nextId, salary);語(yǔ)句中的nextId應(yīng)該為6945,為什么輸出結(jié)果為6944呢?
問(wèn)題解答
回答1:這個(gè)類初始化的順序確實(shí)是個(gè)神奇的問(wèn)題,只可根據(jù)結(jié)果去理解。我打了個(gè)斷點(diǎn)去測(cè)試,staff[0] = new Employee('Harry', 40000);和staff[2] = new Employee();都是代碼塊先于構(gòu)造方法執(zhí)行,但staff[1] = new Employee(60000);卻先執(zhí)行走到this('Employee #' + nextId, salary);,然后代碼塊,然后public Employee(String name, double salary)構(gòu)造函數(shù)。如果你使用2,則按你的預(yù)期,代碼塊先于構(gòu)造方法。
public Employee(double salary) { // 1 this('Employee #' + nextId, salary); // 2// this.name = 'Employee #' + nextId; // this.salary = salary;}回答2:
正常來(lái)說(shuō),java 編譯器會(huì)把實(shí)例初始化塊復(fù)制構(gòu)造方法中,具體位置在調(diào)用父類的構(gòu)造方法以后,構(gòu)造方法里面的語(yǔ)句之前,但是存在例外情況。Java 官方的 Tutorials 里說(shuō)初始化塊會(huì)被復(fù)制到每個(gè)構(gòu)造方法里面其實(shí)是不嚴(yán)謹(jǐn)?shù)摹?/p>
具體到這個(gè)例子,需要考慮一個(gè)問(wèn)題,如果編譯器把初始化塊復(fù)制到每個(gè)構(gòu)造方法里面,那么對(duì)于在構(gòu)造方法里面調(diào)用了其他構(gòu)造方法的情況,這個(gè)初始化塊就會(huì)執(zhí)行兩次,就像例子里面的
public Employee(double salary) {this('Employee #' + nextId, salary); // 調(diào)用了另一個(gè)構(gòu)造方法}
如果編譯器把初始化塊里的代碼復(fù)制到了public Employee(double salary)和public Employee(String name, double salary)里面,這個(gè)初始化塊就會(huì)執(zhí)行兩次,為了避免這種情況,編譯器作了一個(gè)簡(jiǎn)單的處理,編譯器發(fā)現(xiàn)public Employee(double salary)調(diào)用了本類的另一個(gè)構(gòu)造方法,就沒(méi)有把初始化塊的代碼拷貝到這個(gè)構(gòu)造方法里面。也就是說(shuō)在初始化第二個(gè)對(duì)象的時(shí)候,這個(gè)初始化塊是推遲到調(diào)用this('Employee #' + nextId, salary);后,在執(zhí)行Employee(String name, double salary)的時(shí)候才執(zhí)行的,由于推遲了初始化塊的執(zhí)行,在決定傳遞的參數(shù) nextId 的時(shí)候,仍然是未自增的值。如果把這個(gè)構(gòu)造方法修改為
public Employee(double salary) { // this('Employee #' + nextId, salary); this.name = 'Employee #' + nextId; this.salary = salary;}
輸出結(jié)果就會(huì)變?yōu)?/p>
id = 5473, name = Harry, salary = 40000.0id = 5474, name = Employee #5475, salary = 60000.0id = 5475, name = , salary = 0.0
而修改之前的情況,反編譯下 class 文件就能看出來(lái)編譯器最后的輸出結(jié)果,這里只貼三個(gè)構(gòu)造方法,可以很明顯的看出來(lái),第二個(gè)構(gòu)造方法并沒(méi)有被復(fù)制初始化塊的內(nèi)容,直接調(diào)用了另一個(gè)構(gòu)造方法。
public Employee(java.lang.String, double); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object.'<init>':()V 4: aload_0 5: ldc #2 // String 7: putfield #3 // Field name:Ljava/lang/String; 10: aload_0 11: getstatic #4 // Field nextId:I 14: putfield #5 // Field id:I 17: getstatic #4 // Field nextId:I 20: iconst_1 21: iadd 22: putstatic #4 // Field nextId:I 25: aload_0 26: aload_1 27: putfield #3 // Field name:Ljava/lang/String; 30: aload_0 31: dload_2 32: putfield #6 // Field salary:D 35: return public Employee(double); Code: 0: aload_0 1: new #7 // class java/lang/StringBuilder 4: dup 5: invokespecial #8 // Method java/lang/StringBuilder.'<init>':()V 8: ldc #9 // String Employee # 10: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 13: getstatic #4 // Field nextId:I 16: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 19: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: dload_1 23: invokespecial #13 // Method '<init>':(Ljava/lang/String;D)V 26: return public Employee(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object.'<init>':()V 4: aload_0 5: ldc #2 // String 7: putfield #3 // Field name:Ljava/lang/String; 10: aload_0 11: getstatic #4 // Field nextId:I 14: putfield #5 // Field id:I 17: getstatic #4 // Field nextId:I 20: iconst_1 21: iadd 22: putstatic #4 // Field nextId:I 25: return
相關(guān)文章:
1. PHPExcel表格導(dǎo)入數(shù)據(jù)庫(kù)怎么導(dǎo)入2. 預(yù)訂金和尾款分別支付3. thinkphp6使用驗(yàn)證器 信息如何輸出到前端頁(yè)面4. javascript - h5微信中怎么禁止橫屏5. macos - 無(wú)法source activate python276. python - 調(diào)用api輸出頁(yè)面,會(huì)有標(biāo)簽出現(xiàn),請(qǐng)問(wèn)如何清掉?7. 運(yùn)行python程序時(shí)出現(xiàn)“應(yīng)用程序發(fā)生異常”的內(nèi)存錯(cuò)誤?8. 我在導(dǎo)入模板資源時(shí)遇到無(wú)法顯示的問(wèn)題,請(qǐng)老師解答下9. empty比isset更嚴(yán)格一點(diǎn)10. javascript - 微信網(wǎng)頁(yè)開(kāi)發(fā)從菜單進(jìn)入頁(yè)面后,按返回鍵沒(méi)有關(guān)閉瀏覽器而是刷新當(dāng)前頁(yè)面,求解決?

網(wǎng)公網(wǎng)安備