Java 類中不僅可以定義變量和方法,,還可以定義類,這樣定義在類內部的類就被稱為內部類,。根據(jù)定義的方式不同,,內部類分為四大類靜態(tài)內部類,成員內部類,,局部內部類,,匿名內部類四種。
但是在這之前我想先講下類的加載順序,,所謂磨刀不誤砍柴工嘛,,先把基礎溫習溫習1.類的加載順序
一個類在java編譯器中是如何加載的,它的加載順序是如何,?這些涉及到了靜態(tài)變量,、靜態(tài)塊、代碼塊,、構造方法,、成員變量和成員方法等等,但是有一個統(tǒng)一的原則-----變量定義的先后順序決定初始化順序,,而在不同變量之間,,又存在著某些規(guī)則(先靜態(tài)對象,再非靜態(tài)對象)
這邊大家容易混淆的是就是代碼塊和構造函數(shù),,代碼塊包括三個:
1.靜態(tài)代碼塊:使用static關鍵字和{}聲明的代碼塊(不能存在于方法中,,是所屬于類的)
執(zhí)行時機:靜態(tài)代碼塊在類被加載的時候就運行了,而且只運行一次,,并且優(yōu)先于各種代碼塊以及構造函數(shù),。如果一個類中有多個靜態(tài)代碼塊,會按照書寫順序依次執(zhí)行
作用:一般情況下,,如果有些代碼需要在項目啟動的時候就執(zhí)行,,這時候就需要靜態(tài)代碼塊。比如一個項目啟動需要加載的很多配置文件等資源,,我們就可以都放入靜態(tài)代碼塊中,。
2.構造代碼塊:和靜態(tài)代碼塊的區(qū)別是少了static關鍵字
執(zhí)行時機:構造代碼塊在創(chuàng)建對象時被調用,每次創(chuàng)建對象都會調用一次,,但是優(yōu)先于構造函數(shù)執(zhí)行,。需要注意的是,,聽名字我們就知道,構造代碼塊不是優(yōu)先于構造函數(shù)執(zhí)行,,而是依托于構造函數(shù),也就是說,,如果你不實例化對象,,構造代碼塊是不會執(zhí)行的
作用:和構造函數(shù)的作用類似,都能對對象進行初始化,,并且只要每創(chuàng)建一個對象,,構造代碼塊都會執(zhí)行一次。但是反過來,,構造函數(shù)則不一定每個對象建立時都執(zhí)行(多個構造函數(shù)情況下,,建立對象時傳入的參數(shù)不同則初始化使用對應的構造函數(shù))。
利用每次創(chuàng)建對象的時候都會提前調用一次構造代碼塊特性,,我們可以做諸如統(tǒng)計創(chuàng)建對象的次數(shù)等功能,。
3.普通代碼塊:普通代碼塊和構造代碼塊的區(qū)別是,構造代碼塊是在類中定義的,,而普通代碼塊是在方法體中定義的,。且普通代碼塊的執(zhí)行順序和書寫順序一致
構造函數(shù):在創(chuàng)建對象時調用,在main方法之后執(zhí)行
總的順序是------靜態(tài)代碼塊>構造代碼塊>構造函數(shù)>普通代碼塊
最后再來一個案例,,驗證一下上面所講的知識是否正確(實踐是檢驗真理的唯一標準,,哈哈)
public class CodeBlock {
static{
System.out.println('靜態(tài)代碼塊');
}
public CodeBlock(){
System.out.println('無參構造函數(shù)');
}
{
System.out.println('構造代碼塊');
}
public void sayHello(){
{
System.out.println('普通代碼塊');
}
}
public static void main(String[] args) {
System.out.println('執(zhí)行了main方法');
new CodeBlock().sayHello();;
}
}
idea運行截圖1
2.關于父子類的加載順序
1.首先執(zhí)行父類的靜態(tài)內容(包括靜態(tài)變量和靜態(tài)代碼塊),再去執(zhí)行子類的靜態(tài)內容(若子類沒有直接下一步),,執(zhí)行完之后進入下一步
2.如果有就執(zhí)行父類的構造代碼塊,,父類的構造代碼塊執(zhí)行完畢,接著執(zhí)行父類的構造方法,;父類的構造方法執(zhí)行完畢之后,,它接著去看子類有沒有構造代碼塊,如果有就執(zhí)行子類的構造代碼塊,。子類的構造代碼塊執(zhí)行完畢再去執(zhí)行子類的構造方法,。
3.再來驗證一下。
package com.tys.extend;
public class SuperClass {
static{
System.out.println('父類靜態(tài)代碼塊');
}
{
System.out.println('父類構造代碼塊');
}
public SuperClass(){
System.out.println('父類構造函數(shù)');
}
public static void main(String[] args) {
SubClass sb = new SubClass();
System.out.println('------------');
SubClass sb1 = new SubClass();
}
}
class SubClass extends SuperClass{
static{
System.out.println('子類靜態(tài)代碼塊');
}
{
System.out.println('子類構造代碼塊');
}
public SubClass(){
System.out.println('子類構造函數(shù)');
}
}
idea運行截圖2
從上圖我們new了兩個對象,,但是因為靜態(tài)變量只加載一次,,所以new第二個對象的時候,不會再去調了
3 內部類的加載時序
內部類:內部類是延時加載的,,也就是說只會在第一次使用時加載,。不使用就不加載。由此,,可以很好的用于單例模式(不使用不加載:避免了餓漢式的內存浪費,;可巧妙避免線程安全問題)