class類的初始化##
C++中引入了構造器這個概念(constructor)的概念,這是在創建一個對象時被自動調用的特殊方法。
Java也引入了構造器
構造器的主要的作用就是確保每個對象都會得到初始化。創建對象時,如果其類具有構造器,Java就會在用戶操作對象之前自動調用相應的構造器,從而確保初始化的進行。
public class Initial extends Parent {
public static int i=j+1;
public Initial()
{
System.out.println("1");
System.out.println(i);
}
public void get()
{
super.get();
System.out.println("4");
}
}
class Parent
{
public static int j=4;
public Parent()
{
System.out.println("2");
}
public void get()
{
System.out.println("3");
}
}
...
public static void main(String[] args) {
// TODO Auto-generated method stub
Initial initial=new Initial();
initial.get();
}
...
在上面類Initial和Parent類各有一個不帶參數的構造器,這可以保證在使用對象之前,已經被初始化了
由於構造器和類名必須完全相同,所以不用在遵從方法首字母小寫的編碼風格
Initial initial=new Initial();
如果Initial()是Initial類的唯一的構造器,那么編譯器不會允許其他任何方式創建Initial對象。new Initial()會給相應的對象分配空間,並返回對新建對象的引用
不帶任何參數的構造器被稱為默認構造器。
默認構造器###
默認構造器又稱為無參構造器,它的作用是創建一個默認對象。如果類中沒有構造器,則編譯器會自動創建一個默認構造器。
public Bird(){}
...
public static void main(String[] args) {
Bird bird=new Bird();
}
...
如果已經定義了一個構造器(無論是否有參數),編譯器不會再自動創建默認構造器.意思就是,如果定義了構造器,那么只能用已經定義的構造器來構造對象,不能再使用默認構造器了。
繼承和初始化####
在思考這個問題時,就要考慮到類的加載先后順序。Java的加載方式不同於C++。Java中的所有事物都是對象。每個類的編譯代碼都存在自己的獨立文件里。該文件只在需要使用程序代碼時才會被加載。一般來說,類的代碼在初次使用才會加載。這通常指的是加載發生於創建類的第一個對象,但是當訪問static域或static方法時,也會發生加載。(構造器是沒有顯式使用static的靜態方法),因此在使用構造器創建對象時就會加載類。
運行最上面的代碼時,代碼的入是main()函數,由於main()函數是靜態函數,所以加載main函數所在的類的編譯代碼。接下來是Initial initial=new Initial();創建Initial對象,需要加載Initial類的編譯代碼(Initial.class).在對它進行加載的過程中編譯器會發現它有一個基類,於是繼續進行加載。如果該基類還有其他基類,則第二個基類就會被加載,如此類推,根基類的static初始化會被執行,然后是下一個導出類,以此類推。這種方式很重要,因為導出類的static的初始化可能會依賴基類成員能否被正確的初始化
類中成員的初始化###
類加載的順序以及繼承類之間的static初始化順序現在清楚了,但是在一個類之中的初始化順序是怎么樣的?
class Dogs
{
int a;
boolean b;
char c;
float f;
double d;
Dogs dogs;
public void PrintInf()
{
System.out.println("a "+a);
System.out.println("b "+b);
System.out.println("c "+c);
System.out.println("d "+d);
System.out.println("dogs "+dogs);
}
public Dogs(int i)
{
System.out.println("Dog("+i+")");
}
public Dogs()
{
}
}
class Dogss{
Dogs dogs=new Dogs(0);
static Dogs dogs1=new Dogs(1) ;
public Dogss()
{
System.out.println("Dogss()");
dogs3=new Dogs(33);
}
static Dogs dogs2=new Dogs(2);
static Dogs dogs3=new Dogs(3);
}
public class Dog {
public static void main(String[] args) {
// TODO Auto-generated method stub
Dogss dogss=new Dogss();
new Dogs().PrintInf();
}
}
運行結果
Dog(1)
Dog(2)
Dog(3)
Dog(0)
Dogss()
Dog(33)
a 0
b false
c
d 0.0
dogs null
上面這些代碼可以看出
1、首先關於基本類型的初始化可以發現,在構造對象之后,基本類型數據成員都會首先初始化一個默認值,沒有自定義初始化的對象會默認為null。如上面的dogs被默認為null。
2、其次在類的內部,加載時首先是先初始化靜態變量,然后初始化正常對象(這里重點說明一下,不是static的基本類型變量,在沒有使用構造器創建對象時是沒有存儲位置的,所以也不會先於構造器初始化),然后調用構造器。就如同上面的結果一樣。先初始化了Dog(1),Dog(2),Dog(3),然后接下來是正常變量Dog(0),最后才是構造器
加載類之后,先初始化靜態變量(對象和基本類型數據),接下來初始化正常創建的對象,(要使用構造器的話,因為只使用靜態參數的話是不需要使用構造器的)接下來調用構造器,初始化基本類型數據為默認值,調用自定義基本數據初始化方法,最后調用構造器里面的內容
總結一下對象的創建過程,假設有個名為Dog的類
1.即使沒有顯式使用static關鍵字,構造器也是靜態方法。因此當第一次創建Dog的對象時,或者Dog類的靜態方法被首次訪問時,Java解釋器就會加載Dog類,即尋找Dog.class文件
2.然后載入Dog.class文件,這是有關靜態初始化的所有動作都會執行(比如靜態變量被初始化,靜態對象的初始化,靜態方法的初始化)。因此靜態初始化只會在Class對象被首次加載的時候進行一次
3.當用new Dog()創建對象的時候,首先在堆上為Dog對象分配足夠的存儲空間。
4.這塊存儲空間現在為空,自動將Dog對象中的基本類型數據都設置為默認值,引用被設置為null
5.執行所有出現在字段定義出的初始化動作
6.執行構造器