static修飾符
由static修飾的變數稱為靜態變數或者類變數,當類被初始化時候就會被建立。這裡通過乙個例子來說明他的特點。
public
class test
當我們編寫如上的**時候,編譯器會提示非法的前向引用,是因為i被初始化的時候還找不到j的值,但是我們將j改為static型別的值時候,編譯順利通過,同時執行也沒有問題。
public
class test
這裡可以說明j是在類初始化的時候就會進行初始化。
同時static當類被裝載時候被初始化,同時只會初始化一次,一旦被初始化,就會一直存在於記憶體中。
父類構造器
我們知道當乙個類繼承於其他的類時候,當子類被呼叫時候,如果不指定父類的建構函式型別,將會隱式的呼叫父類的無參構造方法,但是我們可以通過顯式呼叫super指定父類應該執行的建構函式。這裡通過乙個例子來說明這種關係。
public
class one
public
one(int i)
}
public
class
twoextends
one
public
two(int i)
}
這裡two繼承於one,同時也實現了有參和無參構造方法。
public
class
three
extends
two
}
three類繼承於two,我們例項化three,這裡通過super指定呼叫two的有參建構函式,我們看下列印的log
e/test:one函式無參
e/test: two函式有參
e/test: three函式構造
由於three繼承於two,two繼承於one,又由於two並沒有指定one中應該執行什麼建構函式,所以最先執行的是one的無參構造,three類中顯式呼叫指定了執行two類中的有參構造,所以接著執行two的有參構造,最後再是three的建構函式。
編譯時型別和執行時候型別
public
class one
public
void
cout()
}
public
class
twoextends
one
}
這裡我們同樣通過乙個例子來進行**,類one 和類two同樣都擁有乙個變數i,同時在one的建構函式中,呼叫cout函式。例項化two,會發現輸出的是0。為什麼不是2,或者22.
實際上建構函式僅僅是為類中的變數進行初始化的操作,類不是由構造器進行構造的,當我們進行new操作的時候,two類中的記憶體空間都被劃分了出來,裡面的變數都是空值,例如上列中的i,由於還沒有執行構造,所以值為0。
但是問題又來了,既然是two中的i沒有值,那為什麼呼叫父類的cout函式會定位到two的函式中,這裡面牽扯到執行時變數和編譯時候變數問題。
我們將上面的**更改一下,如下,將two新增函式new,在one中我們呼叫this.new(),發現編譯器找不到這個函式,說明在編譯時候這個this是one型別的,同樣我們通過列印
this.getclass()執行可以發現這個class是two。
所以this指標當在編譯時候是指向當前的類,但是在執行中是會指向當前正在初始化的類。
public
class one
public
void
cout()
}
public
class
twoextends
one
public
void
new()
}
這裡我們在對這個類進行修改,
public
class one
public
void
cout()
}
我們子啊呼叫this.cout()前列印this.i,這裡我們可以看到執行的結果變為了2,0,不是說執行時候的this會指向子類嗎,這裡不是應該為0嗎,但是這裡是this.i是乙個基本型別的變數,當我們通過this呼叫乙個函式型別的時候,將會指向其實際引用的物件(這裡是two),但是當我們呼叫乙個例項變數的時候,將會由宣告其實例變數的物件決定。(這裡是one,其在one中宣告)
還記得上面說過了static修飾得變數將會在類被初始化時候就會被初始化,這裡我們將two修改一下。
public
class
twoextends
one
}
這樣的話,輸出的值就會正常的變為22。
繼承成員變數和繼承方法的區別
public
class one
}
public
class
twoextends
one
}
依次執行下列**
two two=new two();
log.e("test","i的值:"+two.i);
log.e("test","i的值:"+two.cout);
one one=new two();
log.e("test","i的值:"+one.i);
log.e("test","i的值:"+one.cout);
one onetwo=two;
log.e("test","i的值:"+onetwo.i);
log.e("test","i的值:"+onetwo.cout);
可以發現,two two=new two();中輸出的值為22,22.
這個很正常。
one one=new two();中我們發現輸出的值依次為2,22.
從這裡我們可以看出當我們執行繼承時候,其中的變數值會以宣告的變數中的值為準,但是函式又會擁有子類中的特性。
one onetwo=two;
這裡同樣會輸出2,22.但是two.i輸出22,onetwo.i輸出2,但是他們又指向同一塊記憶體,說明這個物件中同時存在了i=2,i=22的變數。
總結:例項化類中的變數會和宣告的類保持一致,但是其中的函式會和繼承的子類中函式一致。
從上面的例子可以看出來,為什麼
one one=new two();
log.e(「test」,」i的值:」+one.i);
log.e(「test」,」i的值:」+one.cout);
會輸出兩個不同的值,我們考慮他們在記憶體中的位置,發現當我們建立如上的類的時候,它不僅會初始化子類中的變數 ,同時會初始化父類中的變數,他們同時存在乙個物件中,子類中的同名函式和同名的變數會對父類的變數和函式進行覆蓋,當我們通過父類宣告的時候,函式會呼叫子類的習慣,當我們直接通過父類呼叫該值時候,是呼叫父類的值。
當我們需要通過子類執行父類的方法或者變數時候,通過super來進行執行,但是我們發現不能執行return super。
說明super不是乙個物件的引用,甚至不是乙個變數,它僅僅用來呼叫父類的函式和變數。
關於final關鍵字相信在實際運用中也是多次遇見了,被final關鍵字修飾有如下的性質
類:不能被繼承(如string)
函式:不能被重寫
變數:不能被修改
關於final的初始化
public
class
twoextends
one
}
如上**所示,如果普通變數會被編譯器初始化,那麼被final修飾的變數必須由程式設計師手動對其進行修飾,如上的**就會提示i沒有被初始化。
乙個被final修飾的普通變數有如下的方式進行初始化。
乙個被final修飾的static變數有如下的方式進行初始化。
Java踩坑記錄
1.quartz整合spring框架service層物件注入為null解決方案 jobdetailfactorybean中注入的是乙個cn.itcast.quartz.hellojob實現類的全路徑,底層會反射建立出乙個hellojob的物件,但是該物件不是由spring管理的,所以業務層的物件無法...
SQL踩坑集合,持續更新
1.oracle中null資料的計算 select count from pier.testxhh t where t.name a 結果 0 select count from pier.testxhh t where t.name a 結果 1 select count from pier.te...
Java踩坑記 資料溢位
public static void main string args 上述 本身是在專案中,計算n天前的時間。表面上看起來這樣計算時間好像並沒有什麼問題,而且在n的值較小的時候。問題也並沒有暴露出來。我們可以看到,上述 種我們期待的輸出應該是false 但是實際的輸出卻是true。仔細思考下就會發...