1. 程式人生 > >學習筆記之《Java核心技術卷I》---- 第六章 介面、lambda表示式與內部類

學習筆記之《Java核心技術卷I》---- 第六章 介面、lambda表示式與內部類

  • 介面中的所有方法都自動地屬於public。因此,在介面中宣告方法時,不必提供關鍵字public;但是在實現介面的類中,必須在實現介面中的方法時把介面中的方法宣告為public,如果不宣告,那就預設包訪問許可權,編譯器會報錯
  • 實現Comparabale介面,必須實現其中的compareTo()。並且在呼叫x.compareTo(y)時,若x小於y,返回一個負數;若x等於y,返回0;若x大於y,返回一個正數
  • 介面中不能有例項域,介面中的資料成員都是預設為public static final的,且只能被public,static或者final修飾。程式碼佐證如下:
public class Test1 {
	public static void main(String[] args){
		I ii = new II();
		System.out.println(ii.a);//ii.a會出現警告資訊
	}
}
interface I{
	int a = 5;
	void modifier();
}
class II implements I{
	@Override
	public void modifier() {
		// TODO Auto-generated method stub
		a = 6;//報錯
	}
}
  • 介面不是類,不能使用new運算子例項化一個介面。但可以宣告介面的變數,介面變數必須引用實現了介面的類物件
  • 一個實現了介面的類必須實現介面中定義的方法或者將這個類本身宣告為abstract
  • 一個類只可以繼承一個父類,可以實現多個介面
  • 介面中允許有靜態方法的定義和實現,該靜態方法無法被繼承;但是一個的靜態方法可以被子類繼承甚至覆蓋。程式碼佐證如下:
public class Test1 {
	public static void main(String[] args){
		II.hello();//編譯報錯
		II.test();//註釋上一行後,輸出test
	}
}
interface I{
	int a = 5;
	static void hello() {
		System.out.println("hello");
	}
}
class A{
	static void test() {
		System.out.println("test");
	}
}
class II extends A implements I{

}
  • 介面中的方法只能被public、abstract、default、static和strictfp修飾
  • 如果介面中的方法被default修飾,則該方法可以被類繼承甚至覆蓋。程式碼佐證如下:
public class Test1 {
	public static void main(String[] args){
		new II().hello();//hello
		II.test();//test
	}
}
interface I{
	int a = 5;
	default void hello() {
		System.out.println("hello");
	}
}
class A{
	static void test() {
		System.out.println("test");
	}
}
class II extends A implements I{

}
  • 如果一個介面定義了一個預設方法,然後又在超類或另一個介面定義了同樣的方法。那麼子類會如何處置這個方法?

超類優先。如果超類提供了一個具體方法,在介面中同名而且有相同引數型別的預設方法將會被忽略。程式碼佐證如下: 

public class Test1 {
	public static void main(String[] args){
		new A().hello();//B
	}
}
interface I{
	int a = 5;
	default void hello() {
		System.out.println("I");
	}
}
class B{
	public void hello() {
		System.out.println("B");
	}
}
class A extends B implements I{
	
}

介面衝突。如果一個超介面提供了一個預設方法,另一個介面提供了一個同樣的預設方法,那麼在實現介面的類中必須覆蓋這個方法解決衝突。程式碼佐證如下:

/*
若把A中覆蓋的hello方法註釋,那麼會報錯
*/
public class Test1 {
	public static void main(String[] args){
		new A().hello();//I
	}
}
interface I{
	int a = 5;
	default void hello() {
		System.out.println("I");
	}
}
interface II{
 	default void hello() {
		System.out.println("II");
 	}
}
class A implements I,II{
	@Override
	public void hello() {
		// TODO Auto-generated method stub
		I.super.hello();
	}
}

注意,在A中使用的是I.super.hello() 

  • 實現Comparator<T>介面,必須實現compare(T,T)方法
  • 預設的克隆操作是淺拷貝,即並沒有克隆物件中的其他物件
  • 如果一個自定義類的物件想要使用clone方法,則必須:
  1. implements Cloneable
  2. 重新定義clone方法,並指定public訪問修飾符(在Object中,clone方法為protected)
  • 在此,對P227註釋的理解如下:由於Object中的clone方法時protected,又因為你的自定義類與Object並不是位於同一個包,所以clone方法只是對你這個類可見,但是對你的類的例項物件並不可見;也就是說你的類的所有方法都可以呼叫clone方法,但是你的例項物件不能呼叫clone方法。程式碼如下:
public class Test1 {
	public static void main(String[] args) throws CloneNotSupportedException{
		A a1 = new A();
		A a2 = (A)a1.clone();//編譯報錯,因為clone方法只對子類以及同包的類可見,對於物件來說是不可見的
		Object object = new Object();
		object.clone();//編譯報錯,因為clone方法只對子類以及同包的類可見,對於物件來說是不可見的
	}
}
class A implements Cloneable{
	public A testClone() throws CloneNotSupportedException {
		return (A)new A().clone();//無錯,clone方法對於子類(類內部)是可見的
	}
}
  • 證明繼承自Object的clone為淺拷貝程式碼如下:
/*
輸出結果為:
false
true
結果表明:clone方法對於物件a1,確實是完整地複製了另外一份給a2,因此它們指向的是不同的堆
但是對於a1裡面的物件array,則只是簡單地拷貝了array的地址給a2.array,因此a1.array和a2.array指向的是同一個堆
*/
public class Test1 {
	public static void main(String[] args) throws CloneNotSupportedException{
		A a1 = new A();
		A a2 = a1.clone();
		System.out.println(a1 == a2);//false
		System.out.println(a1.array == a2.array);//true
	}
}
class A implements Cloneable{
	public int array[] = {1,2,3};
	@Override
	public A clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return (A) super.clone();
	}
}
  • 將上面clone改為深複製(即對於物件中的每個物件都進行復制),程式碼如下:
public class Test1 {
	public static void main(String[] args) throws CloneNotSupportedException{
		A a1 = new A();
		A a2 = a1.clone();
		System.out.println(a1 == a2);//false
		System.out.println(a1.array == a2.array);//false
	}
}
class A implements Cloneable{
	public int array[] = {1,2,3};
	@Override
	public A clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		A a = (A) super.clone();
		a.array = array.clone();
		return a;
	}
}
  • lambda表示式語法:(引數1,引數2,...),箭頭(->)以及一個表示式。 比如:(int x,int y)->{if(x>=0) return x;else return y;}
  • lambda表示式求字串陣列按字串長度排序例子:
String[] planets = {"Mercury","Venus","Earth","Mars"};
Arrays.sort(planets,(first,second)->{return first.length() - second.length();});
System.out.println(Arrays.toString(planets));//[Mars, Venus, Earth, Mercury]
  • 如果某種介面只有一個抽象方法,那麼這種介面就稱為函式式介面。此時可以用lambda表示式(實現介面中唯一的抽象方法)來表示這種介面的物件
  • lambda表示式具體知識(沒明白,先跳過)
  • 使用內部類的原因:
  1. ​​​​​​​內部類可以訪問該類定義所在的作用域中的資料,包括私有的資料
  2. 內部類可以對同一個包中的其他類不可見
  3. 當想要定義一個回撥函式且不想編寫大量程式碼時,使用匿名內部類比較便捷
  • 使用外圍類的引用:OutClass.this    例項化內部類物件:outObject.new InnerClass(construction parameters)  引用內部類:OutClass.InnerClass
/*
輸出結果:
5
4
*/
public class Test1 {
	public static void main(String[] args){
	    A.B b = new A().new B();
	}
}
class A{
	private int a = 4;
	public class B{
	private int a = 5;
	public B() {
		// TODO Auto-generated constructor stub
	    System.out.println(a);
	    System.out.println(A.this.a);
		}
	}
}
  • 區域性內部類:在方法中定義類。區域性類不能使用public或private修飾。它的作用域被限定在宣告這個區域性類的塊中。區域性內部類可以使用方法中的引數,但不能修改該引數的值(方法的引數預設為final)
/*
以下程式碼報錯,因為檢視修改fianl型變數s的值
將報錯行註釋後輸出hello
*/
public class Test1 {
	public static void main(String[] args){
		A a = new A();
		a.hello("hello");
	}
}
class A{
	private int a = 4;
	public void hello(String s) {
		class B{
			public B(){
				s = "sc";//報錯
				System.out.println(s);
			}
		}
		new B();
	}
}
  • 匿名內部類:建立一個實現了某個介面的類的新物件,且這個實現了介面的類需要就地實現介面中的所有abstract方法
/*
輸出結果:
hello
*/
public class Test1 {
	public static void main(String[] args){
		B b = new B() {
			public void hello() {
				System.out.println("hello");
			};
		};
		b.hello();
	}
}
interface B{
	void hello();
}
  • 靜態內部類無法引用其外圍類的例項域
  • 代理(不明白,以後再看)