7-1,建構函式-概述
1,特點:
(1)函數名與類名相同。
(2)不用定義傳回值類型。
(3)沒有具體的傳回值。
2,建構函式
建構函式就是構建創造對象是調用的函數。
3,作用
可以給對象進行初始化,即對象一建立裡面就包含一些內容。
4,樣本:
class Person{private String name;private int age;Person(){//定義一個空參建構函式Sop("Person run ...");}public void speak(){Sop(name+"::"+age)}}class Demo {public static void main(String[] args) {Person p = new Person();}}
7-2,預設建構函式
1,建立對象都必須通過建構函式初始化。
2,一個類中如果沒有定義過建構函式,那麼該類中一定會有一個預設的空參建構函式,如果在類中定義指定的建構函式,那麼類中的預設的建構函式就沒有了。
3,建構函式是用來給對象初始化的,沒有初始化的對象絕對不能用。
7-3,一般函數和建構函式的區別
區別:
(1)建構函式:對象建立時,就會調用與之對應的建構函式,對對象進行初始化。
一般函數:對象建立後,需要函數功能時才使用。
(2)建構函式:對象建立時會調用,只調用一次。
一般函數:對象建立後,可以被調用多次。
7-4,建構函式-重載
1,什麼時候定義建構函式。
在描述事物時,該事物已存在就具備一些內容,這些內容都定義在建構函式中。
2,重載:函數名相同,參數列表不同。
樣本:
class Person {private String name;private int age;Person() {//函數1name = "baby";age = 1;}Person(String n) {//函數2name = n;}Person(String n,int a) {//函數3name = n;age = a;}public static void main(String[] args) {Person p1 = new Person();//調用函數1Person p2 = new Person("zhangsan");//調用函數2Person p3 = new Person("lisi",20);//調用函數3}}
3,注意,形如:
Person(int a,String n);
Person(String n,int a);
的兩個函數是重載形式。
7-5,建構函式-記憶體配置圖解
Person p = newPerson("lisi",20);
p.speak();
以上兩句在記憶體中表示為:
步驟:
(1)從main函數開始,main函數進棧,局部變數p進棧。
(2)在堆中建立p的實體,分配地址,給變數預設初始化。
(3) new Person("lisi",20);調用了Person的建構函式,把實參傳入,Person(String,int)函數進棧。
(4)把傳入的參數賦值。
(5) Person p = newPerson("lisi",20);執行完畢,把地址值賦給棧中的p,p指向堆中的實體。
(6)Person(String,int)執行完畢,彈棧。
(7)p.speak();調用speak方法,speak()進棧。
(8)執行speak中的輸出語句。
(9)speak()執行完畢,彈棧。
7-6,建構函式-細節
1,建構函式:Person(String n){name=n;}
與一般函數public void setName(String n){name= n;}
不衝突,建構函式在建立對象的一開始的時候就賦值,只調用一次,值不能再改,一般函數可以調用多次,可以在後期對值進行改變。
2,建構函式中可以調用一般函數,一般函數中一般不調用建構函式,建構函式是用來初始化對象的,若非要在一般函數中調用建構函式,需要新建立一個對象。
3,Person(){}與voidPerson(){}不一樣,前者為建構函式,後者為一般函數,建構函式加上修飾符就變成了一般函數,一般函數可以與類名相同。
4,若Person類中已經定義了建構函式,這時預設建構函式已經被覆蓋,若只有一個void Person(){},而main中有Person p = new Person();則編譯失敗,因為voidPerson(){}是一個一般函數主函數無法找到Person(){}給對象初始化,所以報錯誤。
5,建構函式中是有return語句的,return語句是用來結束函數用的,若return後面還有語句,則編譯報錯,因為return後面的語句不會被執行到。
7-7,關鍵字-this使用情境-記憶體配置圖解
1,this表示這個對象的***,比如this.name表示這個對象的name屬性。
2,情境:當成員變數和局部變數重名,可以用關鍵字this來區分。
3,this:代表對象,代表當前對象。
This就是所在函數所屬對象的引用。
簡單說:哪個對象調用了this所在的函數,this就代表哪個對象。
例如:
class Person {private String name;Person(String name) {this.name = name;}public static void main(String[] args) {Person p = new Person("zhangsan");}}
上例中的this指代的就是p對象,this.name指向的是堆記憶體中這個對象的name屬性。
4,記憶體配置圖解:
class Person{private String name;private int age;Person(String name) {this.name = name;}public void speak() {sop(name + ":" + age);}}class ThisDemo {public static void main(String[] args) {Person p = new Person("zhangsan");p.speak();}}
步驟:
(1)從main函數開始,main函數進棧,局部變數p進棧。
(2) new Person("zhangsan");在堆記憶體中開闢空間,分配地址,name和age變數進入堆中的對象,預設初始化為null和0。
(3)Person(name)建構函式進棧。
(4)把"zhangsan"傳入,賦給局部變數name。
(5)函數進入棧會自動帶有一個this引用,因為是p調用Person(),所以this的值為新建立對象的地址0X0078。
(6)Person中的this指向堆中的0X0078。
(7)this.name指向的是0X0078中的name,給此name賦值為"zhangsan"。
(8)Person(name)執行完畢,彈棧。
(9)p = newPerson("zhangsan");把0X0078賦給main中的p。
(10)speak()函數進棧,並內建一個this引用。
(11)p.speak();p調用speak,把p的地址給this。
(12)執行完輸出彈棧。
說明:其實每個函數中都內建一個this,只不過不重名時不用區分,重名時用this區分開來,this都指代當前對象的地址。
如:sop(name + age);的標準寫法是:sop(this.name + this.age);
7-8,this關鍵字使用情境及細節
1,在建構函式中調用建構函式
Person(String name) {this.name = name;}Person(String name,int age) {this(name);//this代表對象,給對象初始化,調用的是同類中上面的那個建構函式this.age = age;}
this也可以用於在建構函式中調用其他建構函式。
注意:只能定義在建構函式的第一行,因為初始化動作要先執行。這說Java的文法,不這樣寫會報錯。
2,範例程式碼記憶體配置圖解:
class Person{private String name;private int age;Person() {name = "baby";age = 1;sop("Person run...");}Person(String name) {this.name = name;}Person(String name,int age) {this(name);this.age = age;}public void speak() {sop(this.name + "-" + this.age);}}class ThisDemo {public static void main(String[] args) {Person p = new Person("zhangsan",30);p.speak();}}
步驟:
(1)從main函數開始,main函數進棧,局部變數p進棧。
(2) newPerson("zhangsan",30);在堆中開闢記憶體空間,分配地址,name和age預設初始化為null和0。
(3)傳參數,Person(name,age)進棧,內建this引用,指向堆記憶體0X0067,局部變數name,age傳參數賦值。
(4)this(name)調用Person(name),內建this引用,指向堆記憶體0X0067,局部變數name賦值。
(5)this.name = name;指Person(name)中的name賦給堆中實體的成員變數name。
(6)Person(name)運行完彈棧。
(7)執行Person(name,age)中的this.age=age;給堆中的成員變數賦值。
(8)Person(name,age)運行完彈棧。
(9)把0X0067賦給main中的p。
3,避免程式出現以下情況:
class Person{private String name;private int age;Person() {this("haha");//2.調用Person(name) 4.調用Person(name)....name = "baby";age = 1;sop("Person run...");}Person(String name) {this();//3.調用Person() 5.調用Person()....this.name = name;}public static void main(String[] args) {new Person(); //1.給Person初始化,調用Person()}}
上述代碼是Person()和Person(name)之間的來回調用,導致棧記憶體溢出。
7-9,this應用
程式:判斷是否是同齡人
public Person {private String name;private int age;Person(){}Person(String name,int age) {this.name = name;this.age = age;}public static void main(String[] args) {Person p1 = new Person("aa",20);Person p2 = new Person("bb",30);boolean b = p1.compare(p2);System.out.println(b);}public static boolean compare(Person p) {/*方法一if(this.age == p.age) {return true;} else {return false;}*///方法二return this.age == p.age;}}
7-10,static關鍵字-資料共用
用static修飾的資料是可以被共用的資料。
如:String name;
staticString country = “cn”;
由於每個人的name是不一樣的,但是country一樣,都是cn,如果不將cn定義為static的,當每建立一個對象,每個對象中都會有cn,浪費記憶體空間。
可以把country定義為static的,這樣所有的對象都可以共用這個資料,哪個對象需要,調用即可。
7-11,static的特點
1,用static修飾的成員先於對象存在,存在於類中,可以用類名直接調用。例如Person.country;
2,不能所有都定義成static的,有些可以被共用,但是有一些是每個對象特有的,不能被共用。
3,特點:
(1)static是一個修飾符,用於修飾成員。
(2)static修飾的成員被所有的對象所共用。
(3)static優先於對象存在,因為static修飾的成員隨著類的載入就已經存在了。
(4)static修飾的成員多了一種調用方式,可以直接被類名調用,格式:類名.靜態成員。
(5)static修飾的資料是共用資料,對象中儲存的是特有資料。
7-12,成員變數和靜態變數的區別
class person{String name; //成員變數,執行個體變數static String country = "cn"; //靜態變數,類變數......}
區別:
(1)兩個變數的生命週期不同。
成員變數:隨著對象的建立而存在,隨著對象的被回收而釋放。
靜態變數:隨著類的載入而存在,隨著類的消失而消失。
(2)調用方式不同。
成員變數:只能被對象調用。
靜態變數:可以被對象調用,也可以被類名調用。
(3)別名不同。
成員變數:也成為執行個體變數。
靜態變數:也成為類變數。
(4)資料存放區位置不同。
成員變數:資料存放區在堆記憶體的對象中,所以也叫對象的特有資料。
靜態變數:資料存放在方法區(共用資料區)的靜態區,所以也叫對象的共用資料。
7-13,static的注意事項
1,注意事項:
(1)靜態方法只能訪問靜態成員。(非靜態既可以訪問靜態,又可以訪問非靜態)。
(2)靜態方法中不可以使用this或者super關鍵字。(因為靜態先載入,沒有對象,不可以用this)
(3)主函數是靜態。
2,main()是靜態,不可以直接調用非靜態方法,可以通過建立對象調用。
非靜態變數在使用的時候前面省略了this,靜態變數在使用時前面省略的是類名。
class Person{public static void main(String[] args) {run();//run必須是靜態,否則報錯}public static void run() {System.out.println("run...");}}
7-14,main函數詳解
1,public static void main(String[] args)
主函數的特殊之處:
(1)格式是固定的。
(2)被JVM識別和調用。
public : 因為許可權必須最大。
static : 程式從main函數開始執行,不需要對象調用main函數,其實是直接被主函數的類名調用的,所以必須是靜態
例如要運行Person類,一般我們在命令列輸入java Person執行,其實JVM調用的是java Person.main。
void : 主函數沒有具體的傳回值。
main : 函數名,不是關鍵字,只是一個JVM識別的固定的名稱。
String[] args : 這是主函數的參數列表,是一個字串數群組類型的參數。
2,調用main函數的時候也要向String[]args傳遞實參,是由JVM傳遞的。
可以使用System.out.println(args);驗證,結果為:
[Ljava.lang.String@c17164
可以說明是一個String的數組。
預設這個數組的長度是0。
JVM向String[] args傳遞的是newString[0]。
3,我們在運行程式的時候,可以給main函數傳遞參數,程式中的任何結果最終都要轉成字串顯示在螢幕上。
4,運行程式時傳值,命令列中,在使用java命令時傳遞。
java Person haha hehe xixi 用空格分隔,作為數組中的元素傳遞給args。
5,String[] 中的args可以變化,不是固定的,args是arguments的簡寫。
6,public static void main(String[] args)
publicstatic void main(int[] x)
二者不衝突,是重載的形式。
如果將後面的int[] x變為String[]x,則衝突。
7-15,static關鍵字-程式記憶體詳解
以下面程式為例:
class Person{private String name;private int age;static String country = "cn";public Person(String name,int age) {this.name = name;this.age = age;}public void show() {System.out.println(Person.country + ":" + this.name + ":" + this.age);}public static void method() {System.out.println(Person.country);}}class StaticDemo{public static void main(String[] args) {Person.method();Person p = new Person("Java",20);p.show();}}
當在命令列用java StaticDemo運行這個程式時,這個類就會被載入進記憶體,需要進行空間的開闢,進入方法區,方法區分為靜態區和非靜態區兩個地區。
步驟:
(1)當執行StaticDemo時,StaticDemo類就載入進記憶體,並開闢空間,預設建構函式StaticDemo()也載入進記憶體,載入進記憶體的方法區的非靜態區。
(2)接著main函數也進入記憶體,進入記憶體方法區的靜態區(靜態區的方法和非靜態區的方法都是被共用的,唯一的不同是封裝的資料不同),非靜態區的所有成員都有一個this所屬,因為非靜態區的方法都被對象調用,而靜態區的方法都是所屬自己的類名。
(3)當java StaticDemo,斷行符號時,函數載入完畢,StaticDemo直接用類名調用主函數,這時main函數進棧,main函數中的所有代碼都在靜態區的static main()中。
(4)運行Person.method();時用到了Person類,這時Person載入,這時JVM會在classpath下找是否有Person.class檔案,若沒有則在當前預設目錄下找,找到後就會將Person.class載入進記憶體,Person類中的建構函式Person(name,age)以及其中的代碼和void show()方法以及其中的代碼都載入進非靜態區;Person中的靜態方法method()和靜態變數country載入進靜態區,country預設初始化為null,因為給他賦值為”cn”,所以馬上顯示初始化為cn,現在country=cn。
(5)Person.method();是用類名調用,說明method()是static的,調用時直接在靜態區的Person類中找method方法,找到後,method方法進棧,注意此方法是沒有this所屬的,只有非靜態區持有this。
說明:method方法在方法區,為何進棧執行。
因為方法區是方法的存放區,也叫方法表,所有的方法都在這裡。而棧是運行區,因為方法內可能會有局部變數,局部變數是需要在棧記憶體中開闢空間儲存的,而且運行完局部變數要釋放,所以要到棧中運行。但System.out.println()方法不會進棧執行,這個方法是用於輸出的,沒有局部變數。棧中只存放方法的局部變數,進棧後method方法開始執行,由於只有一個System.out.println()方法,所以直接到靜態區的method中找到System.out.println()執行,有局部變數時才在棧中開闢空間。
method方法的sop中有Person.country,這時直接從靜態區的Person類中找到country,並且輸出對應的值。
(6)method方法運行完畢,彈棧。
(7)執行Person p = new Person("Java",20);局部變數p進棧。
(8)在堆中為p對象開闢空間,分配地址,成員變數name,age進入記憶體,並預設初始化為null和0,然後執行建構函式初始化,此時建構函式Person(name,age)進棧,給對象中的資料初始化,函數中持有一個this引用,並且值為0X0078,因為0X0078的對象在調用它,局部變數name被賦值為Java,age被賦值為20,因為Person(name,age)進棧並且初始化完畢,就開始執行方法區中的代碼(Person(name,age)方法的代碼),因此this代表0X0078,所以通過這個地址找到堆中的name並賦值為Java,將20賦值給age。
(9)Person(name,age)運行完畢,彈棧。
(10)new Person("Java",20);初始化完畢,把地址0X0078賦給p,p指向堆記憶體。
(11)p.show();show方法進棧,因為show是非靜態,內建一個this引用,this指向0X0078。show中有一個sop,sop中的country在編譯完後已經有類名所屬,直接在靜態區中的Person類中調用,name和age前邊都省略了this,this是show中的this,代表0X0078,所以列印0X0078中的name和age,即Java和20,然後輸出。
(12)輸出完畢,show方法執行完畢,彈棧。
(13)執行到main函數中的return語句,main函數結束,main函數彈棧,程式執行結束,JVM結束。 7-16,static關鍵字什麼時候用。
靜態什麼時候用。
1,靜態變數
當分析對象中所具備的成員變數的值都是相同的,這時這個成員就可以被靜態修飾。只要資料在對象中都是不同的,就是對象的特有資料,必須儲存在對象中,是非靜態。如果是相同的資料,對象不需要做修改,只需要使用即可,不需要儲存在對象中,定義成靜態。
2,靜態函數
函數是否用靜態修飾,就參考一點,就是該函數的功能是否有訪問到對象中的特有資料。簡單點說,從原始碼看,該功能是否需要訪問到非靜態成員變數,如果需要,該功能就是非靜態,如果不需要,就可以將該功能定義成靜態。當然,也可以定義成非靜態,但是非靜態需要被對象調用,而建立對象調用非靜態沒有訪問特有資料的方法,該對象的建立是沒有意義的。
7-17,static靜態代碼塊
靜態代碼塊是隨著類的載入而執行的,而且從這個類載入到消亡,只執行一次。
作用:可以給類進行初始化。
格式:
class Person {static {...}}
應用:不是所有的類都是通過建構函式初始化的,如果一個類中的方法全是靜態,那就不需要建立對象,不建立對象就沒有new的過程,不new就無法對建構函式初始化,而這時就可以用靜態代碼塊對類進行初始化。
7-18,構造代碼塊
1,構造代碼塊定義在類中,它可以給所有對象進行初始化,也就是說,每次new一個對象的時候,它都會執行。
2,構造代碼塊與局部代碼塊的區別
構造代碼塊定義在類中,局部代碼塊定義在方法體中。
3,格式:
class Person {{//構造代碼塊...//抽取出所有對象共性的初始化內容,定義在這裡。//每次建立一個Person對象,這個代碼塊都會執行。}...}
4,程式執行順序
靜態代碼塊-->構造代碼塊-->建構函式
7-19,總結
1,被static修飾的成員具備以下特點:
(1)隨著類的載入而載入。
(2)優先於對象而存在。
(3)被所有對象共用。
(4)可以直接被類名調用。
2,static使用時注意:
(1)靜態方法只能訪問靜態成員。
(2)靜態方法中不可以寫this,super關鍵字。
(3)主函數是靜態。