Java基礎(四) 對象

 對象操作與內存

網上關於如何理解對象的文章很多,這裏就不在多做闡述了。

這裏重點看一下對象創建和使用時,內存裏是怎麼做的。

public class Student {
	
	public String name;
	
	public int age;
	
	public void say() {
		System.out.println("name = " + name + ", age= " + age);
	}

}
public class Test {
	
	public static void main(String[] args) {
		
		Student s = new Student();
		
		s.name = "張三";
		
		s.age = 21;
		
		s.say();   // name = 張三, age= 21
		
	}

}

運行上面的代碼,首先java虛擬機(JVM)會把Test.java編譯成字節碼文件Test.class,並把Test.class加載進內存中的方法區

然後JVM就會調用主方法(main()方法),main()方法進。緊接着就是創建Student類型的引用s,如果此時字節碼文件Student.class已經編譯完成,JVM就會直接把它加載進內存,反之,JVM會先把Student.java編譯成字節碼文件Student.class,然後再把Student.class加載進內存。

接下來是創建Student對象。JVM會在堆內存中給Student對象分配一塊內存,並且給Student對象的各個成員變量賦初始值,完成Student對象的初始化之後,JVM會把給Student對象分配的內存的地址值賦值給指向Student對象的引用s。然後就可以根據引用s所記錄的地址值在堆內存中找到Student對象,從而給Student對象的各成員變量賦值。

之後JVM調用Student對象的say()方法,say()方法進棧,say()方法執行完成之後彈棧。

至此main()方法中的代碼全部執行完成,main()方法彈棧。此時堆內存中的Student對象沒有任何引用指向它,垃圾回收機制(Garbage Collection)會不定時的回收它。

具體可以參考下圖:

 

import關鍵字和package關鍵字

java中類的全稱是包名加上類名。package關鍵字可以表示某個類所在包的包名。

package org.hu.test.entity;   // entity包下的Person

public class Person {

	public void say() { 
		System.out.println("person in entity");
	}
	
}

上面的Person類,它的全稱是org.hu.test.entity.Person。打個比方,package名稱就像是我們的姓氏,而class名稱就像是我們的名字。

所以如果存在相同名字的類,就可以用包名區分開來。

package org.hu.test.controller;  // controller包下的Person

public class Person {
	
	public void say() { 
		System.out.println("person in controller");
	}

}
package org.hu.test.controller;

public class Test {
	
	public static void main(String[] args) {
		
		org.hu.test.entity.Person p = new org.hu.test.entity.Person(); // 使用entity包下的Person
		p.say(); 	// person in entity
		
	}

}

但是從上面創建Person對象的代碼中可以體會到,這樣寫代碼實在太麻煩了。於是import關鍵字出現了,只需要在java文件開頭部分導入相應的類的全名一次,之後就只需要寫類名就可以引用該類。

import可以只導入一個包裏的一個類文件,也可以用“*”導入一個包中的所有類文件。

package org.hu.test.controller;

import org.hu.test.entity.Person;	// 導入一個類文件
//import org.hu.test.entity.*;	// 導入包下所有類文件

public class Test {
	
	public static void main(String[] args) {
		
		Person p = new Person();
		p.say(); 	// person
		
	}

}

但是一般情況下,我們不會用到一個包內所有的類文件,而且導入所有類文件反而增加了內存的開銷。

 

權限修飾符 

  本類 同一包下 不同包下(子類) 不同包下(無關類)
private y      
默認 y y    
protected y y y  
public y y y y

四種權限修飾符的作用域如上所示。

但是需要明確的是, protected修飾符作用的“不同包下的子類”,指的是用該修飾符修飾的成員只能在子類的內部使用。具體看這樣一個例子:

package org.hu.test.entity;          //  entity包下

public class Person {
	
	public String name;
	
	protected String city = "四川";
}
package org.hu.test.controller;					// controlller包下

import org.hu.test.entity.Person;

public class Student extends Person {
	
	public void getcity() {
		System.out.println("city: " + city);
	}
	
}
package org.hu.test.controller;					// controller包下

public class Test {
	
	public static void main(String[] args) {

		Student s = new Student();
//		s.city;			// 錯誤, 在不同包下,用protected修飾的成員只能在子類中訪問
		s.getcity();	// city: 四川
	}

}

tip:如果一個類中全部都是靜態方法,那麼就可以把該類的構造方法聲明爲private,不讓別的類用該類來創建對象。

public class Arrays {             //  工具類 Arrays
    private Arrays() {}

    ......}

public class Collections {    //  工具類 Collections
    private Collections() {
    .......}

 

成員變量與局部變量

  • 類中位置不同
    • 成員變量:類中方法外
    • 局部變量:方法體中或方法簽名中
  • 內存中位置不同
    • 成員變量:堆內存
    • 局部變量:棧內存
  • 生命週期不同
    • 成員變量:隨着對象創建而存在,隨着對象消失而消失
    • 局部變量:隨着方法調用而存在,隨着方法調用結束而消失
  • 初始化值不同
    • 成員變量:有默認初始值
    • 局部變量:無默認初始值,必須定義賦值才能使用

tip:局部變量名可以與成員變量名一致,方法中使用採用“就近原則”。

public class Student {
	
	public String name;
	
	public int age;
	
	public void say() {
		int age = 18;
		String name = "李四";
		System.out.println("name = " + name + ", age= " + age);
	}

}
public class Test {
	
	public static void main(String[] args) {
		
		Student s = new Student();
		
		s.name = "張三";
		
		s.age = 21;
		
		s.say();    // name = 李四, age= 18
		
	}

}

 

代碼塊

局部代碼塊:方法中。

構造代碼塊:類中方法外,優先於構造方法執行。

靜態代碼塊:類中方法外,隨着類的加載而加載, 優先於主方法執行。

public class Person {
	
	public String name;
	
	public int age;
	
	static {
		System.out.println("Person靜態代碼塊");
	}
	
	{
		System.out.println("Person構造代碼塊");
	}

	public Person() {
		System.out.println("Person空參構造");
	}

}
public class Student extends Person {
	
	static {
		System.out.println("Student靜態代碼塊");
	}
	
	{
		System.out.println("Student構造代碼塊");
	}

	public Student() {
		super();
		System.out.println("Student空參構造");
	}

}
public class Test {
	
	public static void main(String[] args) {

		new Student();
		
		/* 
		    Person靜態代碼塊
			Student靜態代碼塊
			Person構造代碼塊
			Person空參構造
			Student構造代碼塊
			Student空參構造
		 */
		
	}

}

 

this關鍵字

this代表當前對象的引用。在對象創建的時候,this就被賦值當前對象的地址值。

public class Person {
	
	{
		System.out.println("this: " + this);
	}

}
public class Test {
	
	public static void main(String[] args) {

		Person p = new Person();        //  p: org.hu.test.entity.Person@15db9742
		System.out.println("p: " + p);  //  this: org.hu.test.entity.Person@15db9742
										
	}

}

 

構造方法的重載

  1. 如果某類中不存在構造方法,系統默認給此類提供空參構造函數
  2. 如果某類中存在構造方法,系統不會再給此類提供空參構造方法

 

static關鍵字 

類中用static關鍵字修飾的成員不再是某個對象的私有屬性,而是所有對象共有屬性,也可以稱之爲類成員。

用static修飾的成員的特點:

  1. 隨着類的加載而加載(更準確的說是隨着字節碼文件的加載而加載)
  2. 優先於對象而存在
  3. 可以直接通過類名調用

tip:靜態方法不可以訪問非靜態成員變量,靜態只能訪問靜態

public class Person {
	
	public String name;
	public static String city;

	public void say() {
		System.out.println("name= " + name + ", city=" + city);
	}
	
}
public class test {
	
	public static void main(String[] args) {
		
		Person.city = "四川";
		Person p = new Person();
		p.name = "李四";
		p.say();   // name= 李四, city=四川
		p.city = "上海";	
		p.say();  //  name= 李四, city=上海
	}

}

 用static關鍵字修飾的成員在內存中的位置是方法區中的共享區,普通方法則在非共享區。可以參考下圖:

二者區別可以用解壓縮文件和壓縮文件來形容。雖然它們都是隨着類的加載而加載,但是前者一開始就是解壓縮的狀態,所以可以直接用類名來調用,而後者則需要創建對象來解壓縮才能使用。

 

繼承

java只支持單繼承,但是可以多層繼承。

如果類之間可以多繼承,那麼在子類中就無法區分從多個父類中繼承來的同名成員。

public class Person {
	
	private String name = "person";
	
	public String city = "四川";
	
	public Integer age = 21;

}
public class Student extends Person {
	
	public String city = "上海";
	
	public String sex = "man";
	
	public void getPersonInfo() {
		System.out.print("Person city: " + super.city);
		System.out.println(" ,age: " + this.age);
//		System.out.println("sex: " + super.sex);    // 錯誤 : super只能訪問父類成員
	}
	
	public void getStudentInfo() {
		System.out.print("Student city: " + this.city);
		System.out.println(" , sex: " + sex); 		// 直接寫成員變量名也可以,系統會自動加上this.前綴
//		System.out.println("name: " + super.name);	// 錯誤: 子類不能繼承父類的非私有成員
	}
	
}
public class Test {
	
	public static void main(String[] args) {

		Student s = new Student();
		s.getStudentInfo(); 			// Student city: 上海 , sex: man
		s.getPersonInfo();   			// Person city: 四川 ,age: 21
		
	}

}

從上面代碼可以看出:

  • 子類只能繼承父類非私有的成員。
  • 子父類成員同名,採用就近原則--子類成員覆蓋父類成員,但是可以用this和super區分子父類成員。
  • this和super的區別
    • this既可以訪問子類中的成員變量,也可以訪問父類中的成員變量。
    • super 只能訪問父類中的成員變量。
public class Person {
	
	public String name;
	
	public Person() {
		System.out.print("Person空參構造方法");
	}

}
public class Student extends Person {
	
	public Student() {
		System.out.println(", Student空參構造方法");
	}
	
	public Student(String name) {
		this.name = name;
		System.out.println(", Studnet有參構造方法");
	}
	
}
public class Test {
	
	public static void main(String[] args) {

		Student s1 = new Student();  	// Person構造方法, Student構造方法
		
		Student s2 = new Student("張三");  // Person空參構造方法, Studnet有參構造方法
		
	}

}

通過上面可以看到,子類的每一個構造方法中,如果不手動加上super(),系統都會默認幫我們加上。

如果我們自定義一個類而且沒有繼承任何類呢?那麼這個類也還是會調用Object類的空參構造,因爲在Java中所有的類都繼承於Object類,但不用在聲明一個類時顯示的extends Object

但是通過之前所說的構造方法的重載,我們又可得知:

如果某類中存在構造方法,系統不會再給此類提供空參構造方法

所以如果父類中只有有參構造方法,子類的構造方法中必須調用父類的有參構造方法(或者用this調用本類其他構造方法)。

看下面的例子:

public class Person {
	
	public String name;
	
	public Person(String name) {
		System.out.print("Person有參構造方法");
	}

}
public class Student extends Person {
	
	// Implicit super constructor Person() is undefined. Must explicitly invoke another constructor
	/*public Student() {
		System.out.println(", Student空參構造方法");  
	}*/
	
	public Student() {
		//super("張三");  	// 錯誤: 一個構造方法中super()和this()不得同時出現
		this("張三");		// 調用本類的其他構造方法
		System.out.println(", Studnet空參構造方法");
	}
	
	public Student(String name) {
//		System.out.println("Studnet空參構造方法, ");   // 錯誤, 不可在調用父類構造方法之前寫語句
		super(name);       							  // 必須調用父類的有參構造
		System.out.print(", Studnet有參構造方法");
	}
	
}
public class Test {
	
	public static void main(String[] args) {

		Student s1 = new Student();  	   // Person有參構造方法, Studnet有參構造方法, Studnet空參構造方法
		
		Student s2 = new Student("張三");  // Person有參構造方法, Studnet有參構造方法
		
	}

}

 總結:

  • 子類構造方法必須訪問父類構造方法
  • f構造方法中有this()沒super(),有super()沒this()
  • super()/this()必須放在構造方法中第一句,防止子類在構造方法中使用尚未從父類繼承下來的成員變量
public class Person {
	
	private void say() {
		System.out.println("i am person");				// 子類無法繼承父類的私有方法
	}
	
	public void start() {
		System.out.print("person go, ");
	}
	
	public void limit() {
		System.out.println("person limit");
	}

}
public class Student extends Person {
	
	public void say() {
		System.out.println("i am student");
	}
	
	public void start() {
		super.start();							// 可以通過super訪問被子類重寫的父類方法
		System.out.println("student go");
	}
	
	/*void limit() {
		System.out.println("student limit");    // 子類方法權限需要大於或等於父類, 才能完成對父類方法的重寫
	}*/
	
	public void limit () {
		System.out.println("student limit");
	}
}
public class Test {
	
	public static void main(String[] args) {

		Student s1 = new Student(); 	
		s1.say();			// i am student
		s1.start();			// person go, student go
		s1.limit();			// student limit
	}

}

從上面的代碼可以看出來,如果子父類成員方法同名,子類方法將重寫父類的非私有成員方法(子類方法權限需要大於等於父類),但是我們依然可以通過super來訪問被子類重寫的父類方法。

 

final關鍵字

  • final修飾的類:子類無法繼承(如String,System)
  • final修飾的方法:子類無法重寫(父類中的方法需要可以讓外部使用,但是又不希望被子類重寫,可以用final修飾)
  • final修飾的變量:1. 基本數據類型變量:又稱之爲常量。 2. 引用數據類型變量:final用來修飾引用數據類型變量時,和C語言中使用const關鍵字固定指針的用法一樣,固定的是引用指向的對象的地址,但地址上存放的數據還是可以更改。
public class Person {
	
	public String name;
	
	public int age;
	
	public Person() {
		this.name = "李四";
		this.age = 18;
	}

	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;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

}
public class Test {
	
	public static void main(String[] args) {

		final Person p = new Person();
//		p = new Person();   		// 引用p不可以再指向新的對象
		System.out.println(p); 		// Person [name=李四, age=18]
		p.setName("張三");
		p.setAge(21);
		System.out.println(p);  	// Person [name=張三, age=21]
	}

}

tips:final修飾的變量必須初始化。有兩種方式:

  1. 定義時直接賦值。
  2. 構造函數賦值。(很少)

 

多態 

public class Person {
	
	public String name = "person";
	
	public static String work = "work";
	
	public void say() {
		System.out.println("i am person");
	}
	
	public static void start() {
		System.out.println("start to work");
	}

}
public class Student extends Person {
	
	public String name = "student";
	
	public static String work = "study";
	
	public void say() {
		System.out.println("i am student");
	}
	
	public static void start() {
		System.out.println("start to study");
	}
	
}
public class Test {
	
	public static void main(String[] args) {

		Person p = new Student();
		
		System.out.println(p.name);   // person
		
		System.out.println(p.work);   // work
		
		p.say();  					  // i am student
		
		p.start();  				  // start to work
		
	}

}

從上面的代碼中可以看出來,當父類和子類中成員的定義完全一致,使用父類引用p指向子類Student對象,分別調用子類對象的靜態成員變量、靜態成員方法、非靜態成員變量、非靜態成員方法,除非靜態成員方法,調用的其他成員都是父類的成員。

靜態成員是隨着類的加載而加載,p.work和p.start()其實就可以替換成Person.work和Person.start(),所以調用結果是父類成員也就不奇怪了。

p.name爲什麼輸出也是父類的成員變量呢?其實在堆內存中,一個對象的內部是分爲兩塊區域的,一塊區域存放的是繼承自父類的成員變量(super),另一塊區域存放的纔是本類的成員變量(this)。由於是父類引用調用的成員變量name,所以在內存中訪問到的其實是繼承自父類的成員變量。

只有父類引用調用成員方法say()的時候,才使用了子類對象中的成員方法,這就是動態綁定。

具體可以參考下圖:

總結起來,要體現多態有三個前提:

  1. 繼承
  2. 重寫父類成員方法
  3. 父類引用指向子類對象

多態的好處:將父類作爲參數傳遞,提高代碼的可擴展性

多態的弊端:不能使用子類的特有功能

 

abstract關鍵字

用abstract關鍵字修飾的類是抽象類,用abstract關鍵字修飾的方法是抽象方法,abstract關鍵字不能用來修飾變量

抽象方法只存在於抽象類中,但是抽象類中可以沒有抽象方法。

抽象類是無法被實例化的,但是抽象類中依然存在構造方法,上面提到過:

子類構造方法必須訪問父類構造方法 

所以抽象類中的構造方法就是爲了非抽象子類能夠實例化而存在的。

關鍵字衝突:

  1. abstract關鍵字和private關鍵字衝突,使用前者的目的是要子類去實現關鍵字修飾的方法,使用後者的目的是爲了不讓子類看到關鍵字修飾的成員。

  2. abstract關鍵字和static關鍵字衝突,前者修飾的方法沒有具體的實現,後者修飾的方法可以直接用類名調用,而用類名調用抽象方法毫無意義。

  3.  

    abstract關鍵字和final關鍵字衝突,使用前者修飾的方法是爲了讓子類重寫,使用後者修飾的方法是爲了不讓子類重寫。 

 看下面一個例子:

public abstract class Person {
	
	public Person() {
		System.out.println("Person構造方法");  	// 抽象類的構造方法是爲了讓非抽象子類可以創建對象對象
	}
	
	public abstract void say();					// 抽象方法:只有方法簽名, 沒有具體的實現
	
	public void work() {
		System.out.println("person work");	// 抽象父類中的可以有普通成員方法, 子類可以正常繼承使用
	}

}
public class Student extends Person {

	@Override
	public void say() {
		System.out.println("i am student");			// 非抽象子類必須重寫抽象父類的抽象方法
	}
	
//	public abstract void studentsay();				// 非抽象類中不可以存在抽象方法
	
}
public abstract class Teacher extends Person {
	
//	private abstract void say1();		 // 錯誤: 關鍵字衝突
	
//	public static abstract void say2();		 // 錯誤: 關鍵字衝突
	
//	private final abstract void say3();		 // 錯誤: 關鍵字衝突
	
	public abstract void teachersay();   // 抽象子類可以不重寫抽象父類的的抽象方法 

}
public class Test {
	
	public static void main(String[] args) {

		Student s = new Student(); 		// Person構造方法 
		s.say();						// i am student
		s.work();						// person work
		
//		Person p = new Person();        // 抽象類無法實例化, 因爲抽象類中【可能】有抽象方法
	}

}

由於抽象類無法被實例化,所以即使Teacher類繼承了Person類,但是Teacher類依然無法實例化,所以抽象類父類如果沒有非抽象子類繼承的話是沒有意義的。而同時,非抽象子類又必須實現抽象父類中的抽象方法。

所以abstract關鍵字的意義在於制訂規範。只要父類制定好規範,子類就要按照這個標準來寫。

典型的例子,就是集合體系。

public abstract class Collection implements Fetchable, Value, Filterable {.......}

 

接口

特點:類可以實現多個接口,接口可以繼承多個接口。

接口中的所有方法都是抽象方法,所以接口不可實例化。接口中的成員變量都是靜態常量。

實現接口的類必須重寫抽象方法,或者該類本身是抽象類。

public interface Intf {
		
	int num = 10;					// 系統會默認加上關鍵字 public static final
	
	void say();					// 系統會默認加上關鍵字 public abstract
	
	static public final int num1 = 1;		// public static final 三個關鍵字的順序可以任意交換位置
	
	final static public int num2 = 2;
	
}
public class Person implements Intf{

	{
//		num = 20;					// 	接口中的成員變量是常量,無法修改
	}
	
	@Override
	public void say() {
		System.out.println("i am person");	
	}	

}
public class Test {
	
	public static void main(String[] args) {

		Intf p = new Person();
		System.out.println(p.num); 	 // 10 (接口中的變量是靜態的)
		p.say();		         // i am person
		
	}

}

之前談到繼承的時候,寫過這樣的話:

如果類之間可以多繼承,那麼在子類中就無法區分從多個父類中繼承來的同名成員。

實現接口就不會存在這樣的情況,因爲接口中的方法全部都是抽象方法,而抽象方法是沒有具體實現的,抽象方法最終都要子類去重寫,所以實現的多個接口中出現同名方法並不礙事。

與此同時,接口中的成員變量都是靜態常量,實現多個接口時可以用接口名直接調用,所以在成員變量上多實現也不衝突。

public interface Intf {
	
	int num = 10;
	
	void say();	
	
}
public interface Intf2 {
	
	int num = 20;
	
	void say();
	
}
public class Person implements Intf, Intf2  {
	
	@Override
	public void say() {
		System.out.print("implement Intf num is " + Intf.num);	
		System.out.println(", implement Intf2 num is " + Intf2.num);		
	}	
}
public class Test {
	
	public static void main(String[] args) {

		Person p = new Person();
		p.say();			// implement Intf num is 10, implement Intf2 num is 20
	}

}

接口和抽象類的區別:

  • 抽象類:is的關係,體現繼承體系中共性的東西,對內製定規範
  • 接口:like的關係,體現繼承體系的擴展功能, 對外提供規則

 

內部類

類也可以作爲一個類的成員,這樣的類稱之爲成員內部類,也叫內部類。

創建內部類對象有兩種方法,第一種可以直接創建內部類對象,第二種可以在類的成員方法中創建內部類對象。

非靜態內部類不可以有靜態成員,因爲隨着類的字節碼加載的時候,靜態成員需要隨之一起加載進來,但是此時內部類的字節碼還沒有加載。要做比喻的話,就好像人還沒進屋子,心臟就進來了,這樣是絕對不行的。

public class Person {
	
	public Integer num = 10;

	public class Inner { 
		
//		public static String name;      // 非靜態成員內部類中不可以有靜態成員
		public Integer num = 20;
		
		public void print() {
			Integer num = 30;
			System.out.print("內部類方法num: " + num);	
			System.out.print(", 內部類成員num: " + this.num);
			System.out.println(", 類成員num: " + Person.this.num);
		}
		
	}
	
	public static class staticInner {
		
		public static void print() {			// 靜態成員內部類纔可以有靜態成員
			System.out.println("靜態內部類的靜態方法");
		}
		
	}
	
	public void print() {
		Inner in = new Inner();		//	通過成員方法創建內部類對象
		in.print();							
	}
	
}
public class Test {
	
	public static void main(String[] args) {
		
		Person.Inner pi = new Person().new Inner();		// 創建非靜態內部類對象
		
		pi.print();			// 內部類方法num: 30, 內部類成員num: 20, 類成員num: 10
		
		Person.staticInner psi= new Person.staticInner();	// 創建靜態內部類對象
		
		psi.print(); 		// 靜態內部類的靜態方法
	}

}

局部內部類

存在於成員方法中的類,稱之爲局部內部類。

public class Person {

	public void method() { 
		Integer age = 10;       // jdk 1.8 之前,age需要定義爲final
		
		class Inner {			
			public void print() {
				System.out.println("age: " + age);
			}
			
		}
		
		Inner in = new Inner();
		in.print();
	}
	
}
public class Test {
	
	public static void main(String[] args) {
		
		Person p = new Person();
		p.method();
	}

}

在jdk1.8版本之前,局部內部類想要訪問局部變量,局部變量需要用關鍵字final修飾爲常量。

在print()方法彈棧之後,method()方法隨之彈棧,這時堆內存中的Inner對象沒有引用指向它,但是垃圾回收機制並不是馬上回收它,那麼問題就出現了。

print()方法中使用了變量num,而num卻已經隨着mehod()方法的彈棧消失了。

所以虛擬機需要保證Inner對象在成爲垃圾被回收之前還能訪問到num,因此規定num需要成爲常量,存放到方法區的常量池中,不會隨着method方法的彈棧而消失。

匿名內部類

匿名內部類是局部內部類的一種。它和局部內部類的不同體現在:

  1. 匿名內部類沒有類名
  2. 匿名內部類是抽象類或者接口的子類 

 使用匿名內部類一般只是使用其中一個方法,如果要使用類中的多個方法,反而沒有使用局部內部類來的方便。、當然如果一定要使用匿名內部類,可以用父類引用指向子類對象。

public abstract class Teacher {
	
	public abstract void study();
	
	public abstract void teach();
	
}
public interface Student {
	
	public abstract void study();
	
}
public class Person {
	
	public void method1() { 
		
		new Student() {			// 完整的樣子: new [類名 extends] 抽象類/接口
			@Override
			public void study() {
				System.out.println("student study");
			}
		}.study();
		
	}
	
	public void method2() {
		
		Teacher t = new Teacher() {			// 父類引用指向子類對象
			@Override
			public void study() {
				System.out.print("teacher study");
			}
			
			@Override
			public void teach() {
				System.out.print(", teacher teach");
				
			}
			
			public void play() {
				System.out.println(", teacher play");
			}
		};
		
		t.study();
		t.teach();
//		t.play();			// 用父類引用指向匿名內部類無法調用匿名內部類的特有方法
		
	}
}
public class Test {
	
	public static void main(String[] args) {
		
		Person p = new Person();
		p.method1();		// student study
		p.method2(); 		// teacher study, teacher teach
		
	}

}

在開發中,匿名內部類可以作爲參數傳遞。下一章就會講到集合,現在提前看這樣一個例子:

public class Test {
	
	public static void main(String[] args) {
		
		TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {   // 匿名內部類當做參數傳遞
			@Override
			public int compare(String o1, String o2) {
				return o1.compareTo(o2);
			}
		});
		
	}

}

public interface Comparator<T> {......}

這裏創建TreeSet對象,給它傳遞一個實現Comparator接口的類,但是這個類創建出來只用一次,太浪費了。所以只創建一個匿名內部類作爲參數傳遞過去。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章