1. 程式人生 > >構造方法,this,super,final,static

構造方法,this,super,final,static

 

1構造方法

一個Person類,屬性都被private了,外界無法直接訪問屬性,必須對外提供相應的set和get方法。但如果需要在建立物件的同時明確物件的屬性值,就需要構造方法了。

 

1.1定義

構建創造時用的方法,即就是物件建立時要執行的方法

格式:

  修飾符 構造方法名(引數列表)

  {

  }

 

1.2特點

1)構造方法沒有返回值型別。也不需要寫返回值。因為它是為構建物件的,物件建立完,方法就執行結束。

2)構造方法沒有具體的返回值。

3)構造方法名稱必須和類名保持一致。

 

這就可以解釋了,建立物件時,要加一個擴號,這不就是在呼叫方法嘛,

Person p=new Person();

 

例:

public class Person {
	private String name;
	private int age;
	
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}	
}

 

public class Test {
	public static void main(String[] args) {		
		Person p2=new Person("張三",18);
		System.out.println(p2.getName());
		System.out.println(p2.getAge());		
	}
}

 

1.3記憶體圖

 

步驟解釋:

1 main方法進棧

2在堆中開記憶體,賦初值

3有參構造方法進棧

4傳參,賦值

5有參構造彈棧

6地址賦值給p2

7 getname進棧,通過地址找到name,列印

8 getage進棧,通過地址找到age,列印

 

1.4預設構造方法

編譯器在編譯java檔案時,如果發現類中沒有構造方法,會自動新增一個空參構造。

但如果類中只要有一個構造方法,則不會自動新增空參構造了。

如果寫了有參構造方法,那麼new物件時,再用空參的就會報錯。

所以如果寫了有參構造,最好把空參構造也加上。因為使用空參構造的情況更多。有參構造用的比較少,一般測試時用。

當描述的事物在建立其物件時就要明確屬性的值,就用有參的

若建立物件時不需要明確具體的資料,可以不用寫構造方法,因為會自動加預設的空參構造方法。

 

還是上面的例子,因為Person定義了有參,所以test再呼叫空參就會報錯:

 

 

1.5注意的細節

1)一個類中可以有多個構造方法,(空參、部分成員變數是引數、全部成員變數都是引數)這其實是方法過載。

2)構造方法可以被private修飾,這樣其他程式無法建立該類的物件了。

如果類比較重要,就可以這樣,構造方法用private修飾,不讓建立物件。

其他方法可以用static修飾,那麼就可以直接用類.方法,這樣呼叫了。

例如System類,就是這樣的,

eclipse中,在類上面,ctrl+點選該類,可以檢視原始碼:

 

1.6構造方法和一般方法區別

構造方法在物件建立時就執行了,而且只執行一次。

一般方法是在物件建立後,需要使用時才被物件呼叫,並可以被多次呼叫。

 

物件在建立之後需要修改和訪問相應的屬性值時,在這時只能通過set..或者get..方法來操作。

所以構造方法可以不寫,get set必須寫

 

例:

class Person {
	void Person() {
	}
}

 

class PersonDemo {
	public static void main(String[] args) {
		Person p = new Person();
	}
}

這段程式碼的Person前面有返回值,所以不是構造方法,那麼就會自動新增一個空參構造。所以程式碼是可以執行的,可以new物件的。但是不建議這樣寫,可以看到在eclipse中出現警告:

 

2 this關鍵字

之前用this可以解決成員變數和區域性變數同名的問題。

this代表的是物件,哪個物件呼叫了this所在的方法,this就代表哪個物件。

 

this還可以用於在本類中構造方法之間的呼叫。

2.1格式:

  this(引數列表); 

注意:只能構造方法裡用this(),普通方法裡不能用this()。 

例:

public class Person {
	private String name;
	private int age;
	
	public Person(){
//呼叫本類構造方法
		this("李小華",19);		
	}
	
	public Person(String name,int age){
		this.name=name;
		this.age=age;
	}	
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

 

public class Test {
	public static void main(String[] args) {
		Person p1=new Person();		
		System.out.println(p1.getName());
		System.out.println(p1.getAge());
		
		Person p2=new Person("張小美",18);
		System.out.println(p2.getName());
		System.out.println(p2.getAge());
	}	
}

 

這裡的this("李小華",19);

相當於:

 

但是,this必須寫在第一行,如果這樣,是不行的

 

原因是初始化動作要最先執行。

 

2.2記憶體圖

步驟解釋:

1main進棧,看到new,堆中開記憶體,屬性賦初值

2空參進棧

3找第一個引數是name,age的構造方法,進棧,並把引數傳過去

4賦值

5有參構造彈棧,空參構造彈棧

6地址賦給p

7getName進棧,列印getAge進棧,列印

 

例:判斷兩個人是否是同齡人

(之前程式碼)

 

3 super關鍵字

3.1可以用於子父類中構造方法的呼叫

例:

public class Person {
	public Person(){
		System.out.println("這是父類構造方法");
	}
}

 

public class Student extends Person{
	public Student(){
		System.out.println("這是子類構造方法");
	}
}

 

public class Test {
	public static void main(String[] args) {
		Student s=new Student();
	}
}

 

為什麼會這樣呢?因為在建立子類物件時,父類的構造方法會先執行,

子類中所有構造方法的第一行有預設的隱式super();語句。

格式:

  呼叫本類中的構造方法

  this(實參列表);

  呼叫父類中的空引數構造方法

  super();

  呼叫父類中的有引數構造方法

  super(實參列表);

 

3.2 但是,如果父類定義了有參構造,那麼子類則必須呼叫super(引數),必須手動寫上。

因為父類不會自動建立空參構造了,所以子類的super()就找不到了。

public class Person {
	public Person(String name){	
		System.out.println("這是父類構造方法");
	}	
}

 

改成這樣就可以了:

 

所以一個類只要定義了有參構造,一定再寫一個空參構造,因為有可能被繼承,不寫會導致子類報錯。

 

3.3為什麼子類物件建立都要訪問父類中的構造方法

例:

public class Person {
	int a=1;	
	public Person(String name){
		a=5;
		System.out.println("這是父類構造方法");
	}	
}

 

public class Student extends Person{	
	public Student(){
		super("張三");
		System.out.println("這是子類構造方法");		
	}
}

 

public class Test {
	public static void main(String[] args) {
		Student s=new Student();
		System.out.println(s.a);
	}
}

 

這裡Student是Person的子類,因為Person中在構造方法改變了成員變數的值,所以如果Student不訪問Person的構造方法,那麼就得不到Person中真正的成員變數的值了,也就不是真的繼承了。

 

總結:

子類會繼承父類中的內容,所以子類在初始化時,必須先到父類中去執行父類的初始化動作。這樣,才可以使用父類中的內容。 

子父類是會一級一級的呼叫的,最終是object。

 

3.4如果有this()

如果子類的構造方法第一行寫了this呼叫了本類其他構造方法,那麼super呼叫父類的語句就沒有了。因為this()或者super(),只能定義在構造方法的第一行,因為初始化動作要先執行。

例:

public class Animal {
	public Animal(int age){		
	}
}

 

這裡寫了this(),那麼意思就是呼叫的下面的那個有參構造,而這裡面有super,所以,上面就不能再寫super了,會報錯。

 

3.5總結:

1)如果有this();那麼就不會有super();了。不寫也不會自動加了。

2)構造方法只要直接或間接可以呼叫父類構造方法就可以。

3)this和super都是隻能在構造方法中使用

 

eclipse中也可以有構造方法的快捷方式:

右鍵--source--Generate Constructor using Fields

可以點任意個,一個不選,就是空參構造。

 

可以看到,點出來後,第一行就是super();

 

3.6 super練習

描述學生和工人這兩個類,將他們的共性name和age抽取出來存放在父類中,並提供相應的get和set方法,同時需要在建立學生和工人物件就必須明確姓名和年齡。

public class Person {
	private String name;
	private int age;	
	
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}	
}

 

public class Student extends Person{
	public Student(String name, int age) {
		super(name, age);		
	}	
}

 

public class Worker extends Person{
	public Worker(String name, int age) {
		super(name, age);		
	}
}

 

public class Test {
	public static void main(String[] args) {
		Student s=new Student("小紅帽",18);
		System.out.println(s.getName());
		System.out.println(s.getAge());
		
		Worker w=new Worker("大灰",81);
		System.out.println(w.getName());
		System.out.println(w.getAge());
	}
}

 

super通常是這麼用的,父類的構造方法既可以給自己的物件初始化,也可以給自己的子類物件初始化。

 

總結:

this的三個作用:

  解決成員變數和區域性變數同名問題

  代表本類物件,呼叫本類中的變數和方法

  構造方法之間的呼叫

 

super的兩個作用:

  代表父類物件,呼叫父類的變數和方法

  子父類中構造方法的呼叫

 

4 final關鍵字

有些類在描述完之後,不想被繼承,或者有些類中的部分方法功能是固定的,不想讓子類重寫。這時需要使用關鍵字final,final的意思為最終,不可變。final是個修飾符,可以用來修飾類,類的成員,以及區域性變數。不能修飾構造方法

 

4.1 final修飾類

final修飾類不可以被繼承,但是可以繼承其他類(也叫太監類)

例:

public class Person {
}

public final class Father extends Person{
}

 

這裡的father就不能被繼承了。

 

4.2 final修飾方法

final修飾的方法不可以被子類重寫,但父類中沒有被final修飾方法,子類重寫後可以加final。(也叫太監方法)

例:

public final class Father{
	public final void method1(){		
	}
	public void method2(){		
	}	
}

 

public class Son extends Father{	
	public final void method2(){		
	}
}

這裡son可以重寫method2,不能重寫method1

 

4.3 Final修飾區域性變數

1)區域性變數是基本資料型別(稱為常量):一旦賦值,終身不變

 

 

2)區域性變數是引用資料型別:一旦賦值一個地址,終身不變。但是地址內的物件屬性值可以修改。

 

4.4 Final修飾成員變數

必須手動賦值或構造方法賦值。因為成員變數有預設值null,不賦值就終身是null了。

只要在分配地址之前(也就是建立物件之前)賦值就可以。

例:

 

這樣才可以:

 

或這樣:

public class Person {
	final String name2;	
	public Person(String name2){
		this.name2=name2;
	}	
}

 

5 static關鍵字

static修飾的成員變數屬於類,不屬於這個類的某個物件。

static修飾後就共享了(有一個改變,就全變了)。

 

5.1例:

public class Student {
	private String name;
	private String schoolname;
	
	public Student() {
		
	}

	public Student(String name, String schoolname) {		
		this.name = name;
		this.schoolname = schoolname;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSchoolname() {
		return schoolname;
	}

	public void setSchoolname(String schoolname) {
		this.schoolname = schoolname;
	}	
}

 

public class Test {
	public static void main(String[] args) {
		Student s1=new Student("小紅","清華大學");
		Student s2=new Student("小明","清華大學");
		Student s3=new Student("小亮","清華大學");
		s2.setSchoolname("北京大學");
		
		System.out.println(s1.getSchoolname());
		System.out.println(s2.getSchoolname());
		System.out.println(s3.getSchoolname());
	}
}

 

說明schoolname是屬於每一個物件的

 

改:private static String schoolname;

 

這時schoolname是共享的了,是屬於Student類的了。

 

5.2記憶體圖

 

(資料共享區也叫 靜態區)

記憶體圖步驟說明:

1 test.class進共享區,掃描到main,student.class,也進入

2因為main是static,所以main進靜態區

3掃描student.class,有static的schoolname,就進靜態區,並賦值初始值null

4 main方法進棧

5 有new,就在堆中開記憶體,找到屬性 name賦值null(這時不會再找schoolname了,因為已經在靜態區了)

6 分配地址

7 構造方法進棧,傳值:小紅,清華大學

8 this.name,找到地址,然後找到name,賦值小紅

9 根據地址去靜態區找schoolname(這裡應該用類.變數才對,但是用物件.變數還是能找到的)靜態區的schoolname被改值為清華大學

10構造方法彈棧

11 地址賦值給s1

12 s2的new,s3的new,過程是一樣的...

13 執行到s2.setSchoolname("北京大學")這一步時,靜態區schoolname被改成北京大學

14 後面System.out.println訪問的就全是北京大學了

 

5.3 Static特點

1)屬於類,不屬於物件

如果一個物件將static成員變數值進行了修改,其他物件的也跟著變,就是共享一個。

2)通過類名直接呼叫,不建議用物件呼叫(會有警告)

 

這樣就可以:

 

 

5.4注意的地方

1)不能用this和super呼叫(因為this代表本類物件,super代表父類物件,但是被static後已經不屬於物件了)

2)靜態內容是優先於物件存在,只能訪問靜態

3)同一個類中,靜態成員只能訪問靜態成員,不能訪問非靜態

簡單記:

非靜態可以調靜態

靜態不能調非靜態

  

因為static是進共享區的,所以可以理解為先有static show2,後有age,所以在show2中找不到age。

 

這也解釋了,一個類中有main方法時,其他方法必須加static,才可以呼叫。

 

 

這裡的methods();其實寫全了,是Test.methods();

 

如果方法不加static,就得new一個test物件,來呼叫(因為物件能呼叫變數和方法)

 

 

5.5特殊的地方

1)main方法是特殊的,main方法為靜態方法僅僅為程式執行入口,它不屬於任何一個物件,可以定義在任意類中。main是虛擬機器呼叫。

2)多型中也有特殊:

多型呼叫方法中,編譯看左邊,父類有,編譯成功,父類沒有,編譯失敗

執行靜態方法,執行父類中的靜態方法,

執行非靜態方法,執行子類的重寫方法(因為靜態了,就屬於類了)

成員變數,編譯執行全是父類

例:

public class Father {
	static int i=1;
	public static void f(){
		System.out.println("這是父類靜態方法");
	}
}

 

public class Son extends Father{
	static int i=2;
	public static void f(){
		System.out.println("這是子類重寫後的靜態方法");
	}
}

 

public class Test {
	public static void main(String[] args) {
		Father f=new Son();
		System.out.println(f.i);
		f.f();
	}
}

 

5.6定義靜態常量

靜態常量一旦賦值,終身不變。可以類.變數名呼叫。

 

5.6.1定義格式:

  public static final 資料型別 變數 = ;

  (這裡不一定非要Public,也可以私有,但是沒意義,所以都是public)

 

5.6.2規則

static寫final前面,

變數名全部大寫,多個單詞使用下劃線連線。

例:public static final String SCHOOL_NAME = "北京大學";

Math.PI也是靜態常量

 

5.6.3介面中的每個成員變數都預設使用 public static final 修飾。

所有介面中的成員變數已是靜態常量,由於介面沒有構造方法,所以必須顯示賦值。可以直接用介面名訪問。(因為用final定義的成員變數必須賦值)