java初始化知識總結
1、java初始化基礎知識:
public class Initialization { int a; char b; short s; float f; long lo; double dou; byte e; boolean flag; Object obj; public void print(){ System.out.println("int a="+a+"/nchar b="+b+" /n"+" short s="+s+"/n float f="+f+"/n long lo="+lo+"/n double dou="+dou+"/n byte e=" } public static void main(String [] args){ Initialization init = new Initialization(); init.print(); } |
輸出結果為:
int a=0 char b= short s=0 float f=0.0 long lo=0 double dou=0.0 byte e=0 boolean flag=false Object obj=null |
可見,java會為類的基本型別的變數提供一個初始值,各型別初始值不同,非基本型別初始為null。
static資料的初始化:
static 資料會發生上述同樣的事情(基本型別,獲得對應基本型別的初始化值;非基本型別,初始化為null) 。但是,由於static值只有一個儲存區域,所以static值只會被初始化一次,看下面的例子:
public class Cupboard { static Bowl bowl = new Bowl(); public Cupboard(){ System.out.println("initialization Cupboard"); } } public class Bowl { public Bowl(){ System.out.println("init ing Bowl~"); } } public static void main(String [] args){ Cupboard cup = new Cupboard(); cup = new Cupboard(); } |
輸出結果:
init ing Bowl~ initialization Cupboard initialization Cupboard |
所以說,static資料只會在第一次進行初始化,之後就不會了。
其實,當我們建立一個類之後,這個類可能包含一些靜態變數和一些非靜態變數。當我們建立了這個類的很多例項之後,這些許許多多的例項都有自己的一塊獨立空間來存放自己的資料,而這些資料就是非靜態變數。靜態變數的資料是唯一存放一塊儲存區域中,每個例項都有一個指向這塊區域的指標,這就是每個例項也能呼叫靜態變數的原因。
2、初始化順序:
首先我們要知道,當我們通過建構函式來初始化某個例項時,這個例項的成員域和成員方法是先於構造方法被初始化的。舉個例子:
class Test01{ public Test01(int i){ System.out.println("Test01 of constractor : " + i); } } public class Test02{ private Test01 t1 = new Test01(1); private int n = 10; public Test02(){ System.out.println("Test02 of constructor : " + n); } private Test01 t2 = new Test01(2); public static void main(String[] args){ Test02 test = new Test02(); } } |
輸出結果是:
Test01 of constractor : 1 Test01 of constractor : 2 Test02 of constructor : 10 |
通過輸出,可見當生成Test02的例項test時,它並不是首先呼叫其構造方法而是先是成員變數的初始化,而且成員的初始化的順序以成員變數的定義順序有關,先定義的先初始化,初始化後再呼叫構造方法。其實成員變數的初始化,在類的所有方法呼叫之前進行,包括構造方法。
這也好理解,我們經常用構造方法來對某些成員變數進行操作,有時也要呼叫成員方法,既然能呼叫,肯定是成員域和成員方法先初始化了嘛。
例項變數初始化器與例項初始化器 :
例項變數初始化器:我們可以在定義例項變數的同時,對例項變數進行賦值。比如:
public class InstanceVariableInitializer { private int i = 1; private int j = i + 1; } |
如果我們以這種方式為例項變數賦值,那麼在建構函式執行之前會先完成這些初始化操作。
那麼我們知道如果一個類的成員變數沒有在定義時,系統會給予系統預設的值,當我們像上面那樣對例項變數賦值時,系統在給予初值和例項變數初始化器賦初值這兩種方式,在執行時間上有先後嗎?為了解決這個問題,我們看看下面的例題:
public class Test04 { private static Test04 t1 = new Test04(); private static int i1; private static int i2 = 2; public Test04(){ i1++; i2++; } public static void main(String[] args) { Test04 t2 = new Test04(); System.out.println("t2.i1 = " + t2.i1); System.out.println("t2.i2 = " + t2.i2); } } |
執行結果:
t2.i1 = 2 t2.i2 = 3 |
為什麼是這種結果呢?其實整個過程是這樣的:首先執行給t1,i1,i2分別給予初始值null,0,0,再執行Test04 t1 =new Test04(),這樣i1++,i2++被執行,i1,i2都變為1,執行完畢後接著執行int i1; i1,i2的值仍然是1,1,當執行int i2 = 2時i2被賦予了值,即i1 = 1,i2=2;再執行Test04 t2 = new Test04(),i1,i2再執行++,此時i1 =2,i2 =3,輸出i1,i2,結果就是:t2.i1 = 2,t2.i2 = 3。 通過上面的程式碼我們可以認為系統預設值的給予比通過等號的賦予先執行。
接著,我們加上普通變數,試驗一下,一個類同時含有static變數與普通變數時,兩種變數定義與初始化的順序:
public class User02 { static User02 u=new User02(); static int i=1; int j=2; User02(){ System.out.println("i="+i+", j="+j); } } class test{ public static void main(String[] args) { User02 u=new User02();} } |
執行結果:
i=0, j=2 i=1, j=2 |
通過這個結果,表面上是普通變數的定義初始化這兩個步驟處於靜態變數的初始化和定義之間,但我認為首先定義及初始化靜態變數,當執行到new User02()時,再定義及初始化普通變數。
例項初始化器:
public class InstanceInitializer { private int i = 1; private int j; { j = 2; } } |
上面程式碼中花括號內程式碼,在Java中就被稱作例項初始化器,其中的程式碼同樣會先於建構函式被執行。
其實兩種初始化器都是一樣的,例項變數初始化器只是把兩個步驟表面上合成了一個,但內部還是分兩步:先定義變數並賦予預設值,接著對變數賦值實行初始化。
如果我們定義了例項變數初始化器與例項初始化器,那麼編譯器會將其中的程式碼放到類的建構函式中去,這些程式碼會被放在對超類建構函式的呼叫語句之後(Java強制要求Object物件之外的所有物件建構函式的第一條語句必須是超類建構函式的呼叫語句或者是類中定義的其他的建構函式,如果我們即沒有呼叫其他的建構函式,也沒有顯式呼叫超類的建構函式,那麼編譯器會為我們自動生成一個對超類建構函式的呼叫指令),建構函式本身的程式碼之前。
Java是按照程式設計順序來執行例項變數初始化器和例項初始化器中的程式碼的,並且不允許順序靠前的例項初始化器或者例項變數初始化器使用在其後被定義和初始化的例項變數。所以下面的兩段程式碼編譯是不會通過的:
public class InstanceInitializer { { j = i; } private int i = 1; private int j; } |
public class InstanceInitializer { private int j = i; private int i = 1; } |
之所以不會通過是因為系統認為i是未經定義的變數,不過下面的程式碼卻繞過了這個檢查:
public class InstanceInitializer { private int j = getI(); private int i = 1; public InstanceInitializer() { i = 2; } private int getI() { return i; } public static void main(String[] args) { InstanceInitializer ii = new InstanceInitializer(); System.out.println(ii.j); } } |
最後輸出的值是0。為什麼是0呢?原因就是首先程式先定義j和i,並賦初值0,接著先執行j = getI()語句,getI()方法返回i值,i此時當然是0嘛,所以j就是0,接著執行i = 1語句,接著再執行建構函式中的 i = 2語句。
剛剛講述了一個類(不講繼承)中初始化的順序,下面我們加上繼承,加上靜態變數,我們來看看整個順序是怎麼樣的:
class Sample { Sample(String s) { System.out.println(s); } Sample() { System.out.println("Sample預設建構函式被呼叫"); } } class Test{ static{ System.out.println("父類static 塊 1 執行"); } static Sample staticSam1=new Sample("父類 靜態成員staticSam1初始化"); Sample sam1=new Sample("父類 sam1成員初始化"); static Sample staticSam2=new Sample("父類 靜態成員staticSam2初始化"); static{ System.out.println("父類 static 塊 2 執行"); } Test() { System.out.println("父類 Test預設建構函式被呼叫"); } Sample sam2=new Sample("父類 sam2成員初始化"); } class TestSub extends Test { static Sample staticSamSub=new Sample("子類 靜態成員staticSamSub初始化"); TestSub() { System.out.println("子類 TestSub 預設建構函式被呼叫"); } Sample sam1=new Sample("子類 sam1成員初始化"); static Sample staticSamSub1=new Sample("子類 靜態成員staticSamSub1初始化"); static{System.out.println("子類 static 塊 執行");} Sample sam2=new Sample("子類 sam2成員初始化"); } public static void main(String str[]) { new TestSub(); } |
輸出結果如下:
父類 static 塊 1 執行 父類 靜態成員staticSam1初始化 父類 靜態成員staticSam2初始化 父類 static 塊 2 執行 --------父類靜態成員初始化 子類 靜態成員staticSamSub初始化 子類 靜態成員staticSamSub1初始化 子類 static 塊 執行 -------子類靜態成員初始化 父類 sam1成員初始化 父類 sam2成員初始化 父類 Test預設建構函式被呼叫 -------父類普通成員初始化和建構函式執行 子類 sam1成員初始化 子類 sam2成員初始化 子類 TestSub 預設建構函式被呼叫 -------父類普通成員初始化和建構函式執行 |
由此我們可以得出java初始化順序的結論:
1 繼承體系的所有靜態成員初始化(先父類,後子類);
2 父類初始化完成(普通成員的初始化-->建構函式的呼叫);
3 子類初始化(普通成員-->建構函式);
說到繼承還要講一點,子類建構函式中的內容開始執行前肯定是要先初始化父類的成員然後呼叫父類的建構函式,但如果父類的建構函式中呼叫了自己的一個與子類重名的方法,那系統會呼叫哪個呢?我們來看例程:
class SuperClass{ public SuperClass(){ System.out.println("SuperClass of constructor"); m(); } public void m(){ System.out.println("SuperClass.m()"); } } public class SubClassTest extends SuperClass { private int i = 10; public SubClassTest(){ System.out.println("SubClass of constructor"); super.m(); m(); } public void m(){ System.out.println("SubClass.m(): i = " + i); } public static void main(String[] args){ SuperClass t = new SubClassTest(); } } |
執行結果是:
SuperClass of constructor SubClass.m(): i = 0 SubClass of constructor SuperClass.m() SubClass.m(): i = 10 |
在生成物件時,父類呼叫的M()方法,不是父類的 M()方法,而時子類中被重寫了的M()方法!!並且還出現一個怪異的現象,子類的privte int i 也被父類訪問到,這不是和我們說private的成員只能在本類使用的原則相違背了嗎?其實我們說的這條原則是編譯期間所遵守的,在JAVA程式的編譯期間,它只檢查語法的合法性,在JAVA的JVM中,即執行期間,不管你宣告的什麼,對於JVM來說都是透明的,而多型是在執行期間執行的,所以能拿到SubClass的private成員,一點都不奇怪,只是此時還沒執行 i = 10,所以在父類的構造方法中呼叫m()時,系統只能將i賦予系統初值0。
運用以上知識解釋下面的例程:
一:
class A{ private int i = 9; protected static int j; static{ System.out.println("-- Load First SuperClass of static block start!-- "); System.out.println("j = " + j); System.out.println("-- Load First SuperClass of static block End -- "); } public A(){ System.out.println("------- Load SuperClass of structor start --------"); System.out.println("Frist print j = " + j); j = 10; m(); System.out.println("k = " + k); System.out.println("Second print j = " + j); System.out.println("----------- Load SuperClass End ----------- "); } private static int k = getInt(); public static int getInt(){ System.out.println("Load SuperClass.getInt() "); return 11; } static{ System.out.println("--- Load Second SuperClass of static block!-------"); System.out.println("j = " + j); System.out.println("k = " + k); System.out.println("-- Load Second SuperClass of static block End -- "); } public void m(){ System.out.println("SuperClass.m() , " + "j = " +j); } } class B extends A{ private int a = 10; static{ System.out.println("---- Load SubClass of static block!------"); System.out.println("-- Load SubClass of static block End -- "); } public B(){ System.out.println("Load SubClass of structor"); m(); System.out.println("--- Load SubClass End ---- "); } public void m()...{ System.out.println("SubClass.m() ," + "a = " + a ); } } public class Test1...{ public static void main(String[] args)...{ A a = new B(); } } |
輸出結果為:
-- Load First SuperClass of static block start!-- j = 0 -- Load First SuperClass of static block End -- Load SuperClass.getInt() --- Load Second SuperClass of static block!------- j = 0 k = 11 -- Load Second SuperClass of static block End -- ---- Load SubClass of static block!------ -- Load SubClass of static block End -- ------- Load SuperClass of structor start -------- Frist print j = 0 SubClass.m() ,a = 0 k = 11 Second print j = 10 ----------- Load SuperClass End ----------- Load SubClass of structor SubClass.m() ,a = 10 --- Load SubClass End ---- |
二:
public class User02 { static User02 u02=new User02(); static User02 getUser02(){ return u02; } static int n=5; public User02(){ n++; System.out.println(n);//1 } } class test{ public static void main(String[] args) { User02 u02=User02.getUser02(); System.out.println(u02.n);//5 } } |
上面的輸出結果是:1,5。整個過程是這樣的:首先程式執行到User02 u02=User02.getUser02();,接著初始化User02的靜態成員變數u02和n,此時只是對這兩個靜態變數進行簡單的定義,賦予他們預設值u02=null,n=0。然後再執行u02=new User02();,呼叫User02的建構函式,建構函式中對n進行了自加,所以n輸出1,好了,這時u02初始化完畢,輪到n了,於是n就重新被賦值5,所以在main函式中輸出的是5。
但是當我把static int n=5;語句中的static去掉之後,系統就會先執行int n=5;,接著再執行構造器中的n++,,所以最後輸出的結果是6,6。此時系統還是先初始化User02中的靜態變數u02,為其設定預設值null,然後做的事不是初始化n為其設預設值0,而是要執行u02=new User02();接著初始化u02。當呼叫建構函式之前,要對物件的成員域和成員方法首先進行初始化,所以此時執行語句int n=5;,接著建構函式中的動作開始,n自加成6,所以兩處輸出都為6.
三:
public class User { static User u=new User(); public static User getUser(){ return u; } User01 u01=User01.getUser01(); public User(){ System.out.println("User新建一次"); } } public class User01 { static User01 u01=new User01(); public static User01 getUser01(){ return u01; } User u=User.getUser(); public User01(){ System.out.println("User01新建一次"); } } public class Test { public static void main(String[] args) { User u=User.getUser(); User01 u01=User01.getUser01(); System.out.println(u.u01); System.out.println(u01.u); } } |
輸出結果:
User01新建一次 |
本篇文章是對以下網頁的總結:
相關推薦
java初始化知識總結
1、java初始化基礎知識: public class Initialization { int a; char b; short s; float f; long lo; double dou; byte e; boolean flag;
java 初始化總結
Java初始化的順序如下: public class tian1 extends tian{ //4.初始化塊 { System.out.print("1\n"); } //6.子類構造器 tian1(){ System.out.print("2\n"); }
Java初始化順序(靜態變量、靜態初始化塊、實例變量、實例初始化塊、構造方法)
靜態初始化 都對 class block 註釋 執行順序 blog 中一 成員變量 1、執行順序 1.1、一個類中的初始化順序 (靜態變量、靜態初始化塊)=>(變量、初始化塊、構造器)。 1.2、兩個具有繼承關系類的初始化順序 父類的(靜態變量、
【Java】日誌知識總結和經常使用組合配置(commons-logging,log4j,slf4j,logback)
ng- binder mono leading black auto erb param 1.2 Log4j Apache的一個開放源碼項目,通過使用Log4j,我們能夠控制日誌信息輸送的目的地是控制臺、文件、GUI組件、甚至是套接口服務 器
Java集合框架知識總結
log 增強for循環 stat sort 訪問 字符串 繼承 操作類 map對象 兩大體系:Collection,Map 一、Collection: List 接口 : List:裏面對象全部是有序的(通過三種方法來遍歷) ArrayList,LinkedLis
Java初始化塊
ack rate 普通 rgs -c 相同 ring 初始化塊 修飾符 1、使用初始化塊 [修飾符]{ //初始化塊的可執行性代碼 } 初始化塊雖然也是Java類的一種成員,但它沒有名字,也就沒有標識,因此無法通過類、對象來調用初始化塊。
Java 初始化基類
class Art{ Art(){System.out.println("Art constructor");} } class Drawing extends Art{ Drawing(){System.out.println("Drawing constructor");} } public
Java初始化順序
如何 eache 區別 stat 類型 基礎 返回值 內存結構 第一次 *********************繼承中的對象初始化順序(內存結構)************************ private 修飾的屬性 和方法 不能被繼承 父類中 private
java 初始化與例項化的區別
class A{ public A(){ ……//初始化 } public static void main(String&nb
java初始化配置檔案, 直接使用PropUtil.get(key)獲取值
package com.audaque.cas.server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*;
Java初始化例項
Java初始化例項 類有三個地方可以初始化例項,當類例項被初始化後,以後每一個生成的物件都會帶有初始化後的值。 三個初始化例項的地方: 方法 建構函式 程式碼塊 q)初始化程式碼塊和建構函式誰先執行 a)示例 class Bik
Java 初始化
static 初始化 static 成員初始化順序 package object; class Bowl { Bowl(int marker) { System.out.printf("Bowl("+marker+")\n"); } void f1(
java面試基礎知識總結(二)
五、Object 通用方法 equals()方法 等價關係 Ⅰ 自反性 x.equals(x); // true Ⅱ 對稱性 x.equals(y) == y.equals(x); // true Ⅲ 傳遞性 if (x.equals(y)
java面試基礎知識總結(一)
一、資料型別 包裝型別 八個基本型別: boolean/1 byte/8 char/16 short/16 int/32 float/32 long/64 double/64 基本型別都有對應的包裝型別,基本型別與其對應的包裝型別之間的賦值使用自動裝箱與拆箱完成。 Integer
C/C++陣列初始化的總結
目錄 陣列定義不初始化會被隨機賦值 陣列初始化的幾種形式 陣列初始化為某一固定值 memset(陣列名,值,sizeof(陣列名)) fill(begin(),end(),value) 以前剛開始學陣列初始化,一直都沒搞太清楚,包括參加PAT考試,直到看了胡凡的《演算
【Java】Java序列化學習總結 2018-10-5
Java序列化學習總結 什麼是序列化 我們的物件並不只是存在記憶體中,還需要傳輸網路,或者儲存起來下次再加載出來用,所以需要Java序列化技術。 Java序列化技術正是將物件轉變成一串由二進位制位元組組
詳解java初始化順序
初始化 構造器初始化 自動初始化會在構造器呼叫之前發生 如: public class Counter{ int i; Counter(){ i=7; } } 那麼,i會被先初始化為0,然後在建構函式中賦值為7。對於所有的基本
初學Java 初始化塊 十三
初始化塊是什麼?在Java中就是在構造器之前執行的一段程式碼,它的用處是,如果一段初始化處理程式碼對所有物件完全相同,且無須接受任何引數,就可以把這段提取到初始化塊中.在C#中,沒有初始化塊public class Person{ //定義一個初始化塊 { int a =
java 初始化塊
初始化塊: 靜態初始化塊;非靜態初始化塊 執行順序: 所有的靜態初始化塊都優先執行,其次才是非靜態的初始化塊和建構函式,它們的執行順序是: &nbs
JAVA一些基本知識總結
自增、自減運算子是單目運算子,可以放在操作元之前,也可以放在操作元之後。操作元必須時一個整型或浮點型變數。自增、自減運算子的作用是使變數的值增1或減1。放在操作元前面的自增、自減運算子,會先將變數的值加1(減1),然後再使該變數參與表示式的運算。放在操作元后面的自增、自減運算子,會先使變數參與表示式