1、this關鍵字
this是什么?
this是java語言中的一個關鍵字,它存儲在內存的什么地方呢,一起來看一段程序:
public class Customer { private String name; public Customer(){ } public Customer(String _name){ name = _name; } public void setName(String _name){ name = _name; } public String getName(){ return name; } }
public class CustomerTest { public static void main(String[] args) { Customer jack = new Customer("jack"); Customer rose = new Customer("rose"); } }
以上程序的內存結構圖如下所示:
圖1:this內存圖
this可以看做一個變量,它是一個引用,存儲在Java虛擬機堆內存的對象內部,this這個引用保存了當前對象的內存地址指向自身,任何一個堆內存的java對象都有一個this,也就是說創建100個java對象則分別對應100個this。
通過以上的內存圖,可以看出“jack引用”保存的內存地址是0x1111,對應的“this引用”保存的內存地址也是0x1111,所以“jack引用”和“this引用”是可以划等號的。也就是說訪問對象的時候jack.name和this.name是一樣的,都是訪問該引用所指向對象的name屬性。\
this指向“當前對象”,也可以說this代表“當前對象”,this可以使用在實例方法中以及構造方法中,語法格式分別為“this.”和“this(..)”。this不能出現在帶有static的方法當中。
2、Java this關鍵字的使用(在實例方法中)
我們來看看this是否可以出現在static的方法當中,請看以下代碼以及編譯結果:
public class ThisInStaticMethod { public static void main(String[] args) { ThisInStaticMethod.method(); } public static void method(){ System.out.println(this); } }
編譯報錯,如下圖所示:\
圖2:static的方法中不能使用this
通過以上的測試得知this不能出現在static的方法當中,這是為什么呢?
首先static的方法,在調用的時候是不需要創建對象的,直接采用“類名”的方式調用,也就是說static方法執行的過程中是不需要“當前對象”參與的,所以static的方法中不能使用this,因為this代表的就是“當前對象”。\
大家是否還記得在之前的“封裝”過程中,曾編寫屬性相關的set和get方法,set和get方法在聲明的時候不允許帶static關鍵字,我們把這樣的方法叫做實例方法,說到實例方法,大家肯定想到了實例變量,沒錯,實例變量和實例方法都是對象相關,必須有對象的存在,然后通過“引用”去訪問。\
為什么set和get方法設計為實例方法呢?
那是因為set和get方法操作的是實例變量,“不同的對象”調用get方法最終得到的數據是不同的,例如zhangsan調用getName()方法得到的名字是zhangsan,lisi調用getName()方法得到的名字是lisi,顯然get方法是一個對象級別的方法,不能直接采用“類名”調用,必須先創建對象,再通過“引用”去訪問。\
this可以出現在實例方法當中,因為實例方法在執行的時候一定是對象去觸發的,實例方法一定是對象才能去調用的,而this恰巧又代表“當前對象”,所以“誰”去調用這個實例方法this就是“誰”。測試一下,請看以下代碼:
public class Customer { private String name; public Customer(){ } public Customer(String _name){ name = _name; } public void setName(String _name){ name = _name; } public String getName(){ return name; } public void shopping(){ System.out.println("shopping() --> " + this); } }
public class CustomerTest { public static void main(String[] args) { Customer jack = new Customer("jack"); System.out.println("main() ---> " + jack); jack.shopping(); System.out.println("===================="); Customer rose = new Customer("rose"); System.out.println("main() ---> " + rose); rose.shopping(); } }
運行結果如下圖所示:
圖3:測試this
以上代碼的輸出結果具體是什么不重要,重要的是可以看出誰和誰是相等的。運行結果和代碼結合起來分析一下this:
圖4:this指向了當前對象
通過以上內容的學習得知,this可以使用在實例方法當中,它指向當前正在執行這個動作的對象。\
大家是否還記得實例變量怎么訪問?正規的訪問方式是采用“引用.”去訪問。請看下面的代碼:
public class Customer { private String name; public Customer(){ } public Customer(String _name){ name = _name; } public void setName(String _name){ name = _name; } public String getName(){ return name; } public void shopping(){ System.out.println(name + " is shopping!"); } }
public class CustomerTest { public static void main(String[] args) { Customer jack = new Customer("jack"); jack.shopping(); Customer rose = new Customer("rose"); rose.shopping(); } }
運行結果如下圖所示:
圖5:測試結果
將以上部分代碼片段取出來進行分析:
public class Customer { private String name; //實例變量 ... public void shopping(){ //jack調用shopping,當前對象是jack //rose調用shopping,當前對象是rose //name是實例變量,不用“引用”可以訪問?(以上結果表示可以) System.out.println(name + " is shopping!"); //正規的訪問方式應該是“引用.name”,比如 //System.out.println(jack.name + " is shopping!"); //或者 //System.out.println(rose.name + " is shopping!"); //對不起,jack和rose在main方法當中,在這里不可見,不能用 //難道是這樣??? System.out.println(this.name + " is shopping!"); } }
public class CustomerTest { public static void main(String[] args) { Customer jack = new Customer("jack"); jack.shopping(); Customer rose = new Customer("rose"); rose.shopping(); } }
把完整的代碼拿過來:
public class Customer { private String name; public Customer(){ } public Customer(String _name){ name = _name; } public void setName(String _name){ name = _name; } public String getName(){ return name; } public void shopping(){ System.out.println(name + " is shopping!"); System.out.println(this.name + " is shopping!"); } }
public class CustomerTest { public static void main(String[] args) { Customer jack = new Customer("jack"); jack.shopping(); System.out.println("======================="); Customer rose = new Customer("rose"); rose.shopping(); } }
運行結果如下圖所示:
通過以上的測試我們得知:System.out.println(name + " is shopping!")
和System.out.println(this.name + " is shopping!")
是等效的。
也就是說在shopping()這個“實例方法”當中直接訪問“實例變量”name就表示訪問當前對象的name。
換句話說在實例方法中可以直接訪問當前對象的實例變量,而“this.”是可以省略的。“this.”什么時候不能省略呢?
請看以下代碼:
public class Customer { private String name; public Customer(){ } public Customer(String _name){ name = _name; } public void setName(String _name){ name = _name; } public String getName(){ return name; } public void shopping(){ System.out.println(name + " is shopping!"); } }
你有沒有看到name=_name這樣的代碼很丑陋,怎樣可以優雅一些呢?請看:
public class Customer { private String name; public Customer(){ } public Customer(String name){ this.name = name;//這里的“this.”不能省略 } public void setName(String name){ this.name = name;//這里的“this.”不能省略 } public String getName(){ return name; //這里的“this.”可以省略 } public void shopping(){ //這里的“this.”可以省略 System.out.println(name + " is shopping!"); } }
以上代碼當中this.name = name,其中this.name表示實例變量name,等號右邊的name是局部變量name,此時如果省略“this.”,則變成name = name,這兩個name都是局部變量(java遵守就近原則),和實例變量name無關了,顯然是不可以省略“this.”的。\
最終的結論是,this不能出現在static的方法中,可以出現在實例方法中,代表當前對象,大部分情況下this都是可以省略的,只有當在實例方法中區分局部變量和實例變量的時候不能省略。\
接下來我們再來擴展一下this的使用,請看代碼:
public class Customer { private String name; public Customer(){ } public Customer(String name){ this.name = name; } public void setName(String name){ this.name = name; } public String getName(){ return name; } //實例方法 public void shopping(){ System.out.println(name + " is shopping!"); System.out.println(name + " 選好商品了!"); //pay()支付方法是實例方法,實例方法需要使用“引用”調用 //那么這個“引用”是誰呢? //當前對象在購物,肯定是當前對象在支付,所以引用是this this.pay(); //同樣“this.”可以省略 pay(); } //實例方法 public void pay(){ System.out.println(name + "支付成功!"); } }
public class CustomerTest { public static void main(String[] args) { Customer jack = new Customer("jack"); jack.shopping(); System.out.println("======================="); Customer rose = new Customer("rose"); rose.shopping(); } }
運行結果如下圖所示:
圖7:測試結果
通過以上的測試,可以看出在一個實例方法當中可以直接去訪問其它的實例方法,方法是對象的一種行為描述,實例方法中直接調用其它的實例方法,就表示“當前對象”完成了一系列行為動作。
例如在實例方法shopping()中調用另一個實例方法pay(),這個過程就表示jack在選購商品,選好商品之后,完成支付環節,其中選購商品是一個動作,完成支付是另一個動作。
接下來繼續擴展,請看以下代碼:
public class ThisTest { int i = 10; public static void main(String[] args) { System.out.println(i); } }
以上代碼編譯報錯了,請看:\
圖8:編譯錯誤提示信息
為什么會編譯報錯,在main方法中為什么無法直接訪問變量i?
我們來分析一下,首先i變量是實例變量,實例變量要想訪問必須先創建對象,然后通過“引用”去訪問,main方法是static的方法,也就是說main方法是通過“類名”去調用的,在main方法中沒有“當前對象”的概念,也就是說main方法中不能使用this,所以編譯報錯了。
那應該怎么修改呢?請看:
public class ThisTest { int i = 10; public static void main(String[] args) { //這肯定是不行的,因為main方法帶有static,不能用this //System.out.println(this.i); //可以自己創建一個對象 ThisTest tt = new ThisTest(); //通過引用訪問 System.out.println(tt.i); } }
運行結果如下圖所示:
圖9:測試結果
通過以上的測試得知,在static的方法中不能直接訪問實例變量,要訪問實例變量必須先自己創建一個對象,通過“引用”可以去訪問,不能通過this訪問,因為在static方法中是不能存在this的。
其實這種設計也是有道理的,因為static的方法在執行的時候是采用“類名”去調用,沒有對象的參與,自然也不會存在當前對象,所以static的方法執行過程中不存在this。
那么在static方法中能夠直接訪問實例方法嗎?請看以下代碼:
public class ThisTest { public static void main(String[] args) { doSome(); } public void doSome(){ System.out.println("do some..."); } }
編譯報錯了,請看下圖:
圖10:編譯報錯提示信息
為什么在main方法中無法直接調用實例方法doSome()呢?很簡單,因為實例方法必須先創建對象,通過引用去調用,在以上的main方法中並沒有創建對象,更沒有this。所以在main方法中無法直接訪問實例方法。
結論就是:在static的方法中不能直接訪問實例方法。
怎么修改呢?同樣需要先創建對象,請看:
public class ThisTest { public static void main(String[] args) { ThisTest tt = new ThisTest(); tt.doSome(); } public void doSome(){ System.out.println("do some..."); } }
運行結果如下圖所示:
圖11:運行結果
綜上所述,我們需要記住這樣的一個結論:
this不能使用在static的方法中,可以使用在實例方法中,代表當前對象,多數情況下this是可以省略不寫的,但是在區分局部變量和實例變量的時候不能省略,在實例方法中可以直接訪問當前對象實例變量以及實例方法,在static方法中無法直接訪問實例變量和實例方法。
3、Java this關鍵字的使用(在構造方法中)
this還有另外一種用法,使用在構造方法第一行(只能出現在第一行,這是規定,記住就行),通過當前構造方法調用本類當中其它的構造方法,其目的是為了代碼復用。
調用時的語法格式是:this(實際參數列表),請看以下代碼:
public class Date { private int year; private int month; private int day; //業務要求,默認創建的日期為1970年1月1日 public Date(){ this.year = 1970; this.month = 1; this.day = 1; } public Date(int year,int month,int day){ this.year = year; this.month = month; this.day = day; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } }
public class DateTest { public static void main(String[] args) { Date d1 = new Date(); System.out.println(d1.getYear() + "年" + d1.getMonth() + "月" + d1.getDay() + "日"); Date d2 = new Date(2008 , 8, 8); System.out.println(d2.getYear() + "年" + d2.getMonth() + "月" + d2.getDay() + "日"); } }
運行結果如下圖所示:
圖12:運行結果
我們來看看以上程序的無參數構造方法和有參數構造方法:
圖13:無參數構造和有參數構造對比
通過上圖可以看到無參數構造方法中的代碼和有參數構造方法中的代碼是一樣的,按照以上方式編寫,代碼沒有得到重復使用,這個時候就可以在無參數構造方法中使用“this(實際參數列表);”來調用有參數的構造方法,這樣就可以讓代碼得到復用了,請看:
public class Date { private int year; private int month; private int day; //業務要求,默認創建的日期為1970年1月1日 public Date(){ this(1970 , 1, 1); } public Date(int year,int month,int day){ this.year = year; this.month = month; this.day = day; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } }
還是使用以上的main方法進行測試,運行結果如下:
圖14:運行結果
在this()上一行嘗試添加代碼,請看代碼以及編譯結果:
public class Date { private int year; private int month; private int day; //業務要求,默認創建的日期為1970年1月1日 public Date(){ System.out.println("..."); this(1970 , 1, 1); } public Date(int year,int month,int day){ this.year = year; this.month = month; this.day = day; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } }
圖15:編譯報錯信息
通過以上測試得出:this()語法只能出現在構造方法第一行,這個大家記住就行了。