接口
在Java程序設計語言中,接口不是類莺债,而是對希望符合這個接口的類的一組需求杆勇。
接口中所有方法都自動是public方法夕土。因此在接口中聲明方法時粪小,不必提供關鍵字public。但是在實現(xiàn)接口時必須把方法聲明為public贡歧。
提供實例字段和方法實現(xiàn)的任務應該由實現(xiàn)接口的那個類來完成滩租。可以將接口看成是沒有實例字段的抽象類利朵。
為了讓類實現(xiàn)一個接口需要完成兩個步驟:
- 將類聲明為實現(xiàn)給定的接口律想。
- 對接口中的所有方法提供定義。
需要使用關鍵字implements
class Employee implements Comparable
也可以為其提供一個類型參數(shù)
public class Employee implements Comparable<Employee>
在對有接口的類進行繼承時绍弟,要注意“反對稱”規(guī)則技即。如果子類要對父類的接口方法進行覆蓋,那么就必須做好準備比較子類和父類樟遣,而不能僅僅使用強制類型轉換而叼。
如果不同子類中的同名接口方法有不同的含義身笤,就應該將屬于不同類的對象之間的比較視為非法。因此在方法開始前應該進行以下檢測:
if(getClass()!=other.getClass()) throw new ClassCastException():
如果存在一個能夠比較子類對象的通用算法葵陵,那么可以在超類中提供一個compareTo方法液荸,并將這個方法聲明為final。
接口的屬性
接口不是類脱篙。不能用new運算符實例化一個接口娇钱。但是可以聲明接口的變量。接口變量必須引用實現(xiàn)了這個接口的類對象绊困。
Comparable x;
x=new Employee(...);
也可以從通用性較高的接口擴展到專用性較高的接口文搂,和建立類的繼承層次一樣。
接口中不能包含實例字段秤朗,但是可以包含常量煤蹭。接口中的字段總是public static final
默認方法
可以為接口方法提供一個默認實現(xiàn),必須用default修飾符標記這樣一個方法取视。
default int compareTo(T other){retern 0;}
默認方法可以調用其他方法疯兼。
“類優(yōu)先”原則:接口的默認方法和超類中定義的方法產(chǎn)生沖突,只會考慮超類方法贫途。
對象克隆
為一個包含對象引用的變量建立副本時:原變量和副本都是同一個對象的引用,任何一個變量改變都會影響另一個變量待侵。
var original=new Employee("John Public",50000);
Employee copy=original;
copy.raiseSalary(10);//oops--also changed original
如果希望copy是一個新對象丢早,它的初始狀態(tài)與original相同,但是之后它們各自會有自己不同的狀態(tài)秧倾,這種情況下就要使用clone方法
Employee copy=original.clone();
copy.raiseSalary(10);//OK--original unchanged
Object類如何實現(xiàn)clone
它對于這個對象一無所知怨酝,所有只能逐個字段地進行拷貝。如果對象中的所有數(shù)據(jù)字段都是數(shù)值或其他基本類型那先,拷貝這些字段沒有任何問題农猬。但是如果對象包含子對象的引用,拷貝字段就會得到相同子對象的另一個引用售淡,這樣一來斤葱,原對象和克隆的對象仍然會共享一些信息。默認的克隆操作是“淺拷貝”揖闸,并沒有克隆對象中引用的其他對象揍堕。
注意
- Object類中的clone方法聲明為protected,子類只能調用受保護的clone方法來克隆它自己的對象汤纸。必須從新定義clone為public才能允許所有方法克隆對象衩茸。
- Cloneable接口的出現(xiàn)于接口的正常使用并沒有關系。這個接口只是作為一個標記贮泞,指示類設計者了解克隆過程楞慈。如果一個對象請求克隆幔烛,但是沒有實現(xiàn)這個接口,就會生成一個檢查型異常囊蓝。
即使淺拷貝實現(xiàn)能夠滿足要求饿悬,還是需要實現(xiàn)Cloneable接口,將clone重新定義為public慎颗,再調用super.clone()乡恕。
lambda表達式
lambda表達式是一個可傳遞的代碼塊,可以再以后執(zhí)行一次或多次俯萎。(函數(shù)式編程)
可以了解一下函數(shù)式編程
為什么要使用lambda表達式傲宜?
在Java中,你不能直接傳遞代碼段夫啊。Java是一種面向對象語言函卒,所以必須構造一個對象,這個對象的類需要有一個方法包含所需的代碼撇眯。
lambda表達式的語法
參數(shù)报嵌,箭頭(->)以及一個表達式
可以像寫方法一樣,把表達式的代碼放在{}中熊榛,并包含顯式的return語句锚国。
參數(shù)可以放在括號中,沒有參數(shù)仍然要提供空括號玄坦。例如
()->{for(int i=100;i>=0;i--) System.out.println(i);}
如果可以推導出lambda表達式的參數(shù)類型血筑,則可以忽略其類型。
如果方法只有一個參數(shù)煎楣,而且這個參數(shù)的類型可以推導得出豺总,那么甚至還可以省略掉小括號。
無需指定lambda表達式的返回類型择懂。
函數(shù)式接口
Java中有很多封裝代碼塊的接口喻喳,如ActionListener或Comparator,lambda表達式于這些接口是兼容的困曙。
對于只有一個抽象方法的接口表伦,需要這種接口的對象時,就可以提供一個lambda表達式赂弓。這種接口稱為函數(shù)式接口绑榴。
lambda表達式可以轉換為接口
例如:
Arrays.sort
方法的第二個參數(shù)需要一個Comparator實例,Comparator就是只有一個方法的接口盈魁,所以可以提供一個lambda表達式:
//lambda表達式可以傳遞到函數(shù)式接口翔怎。
Arrays.sort(planets,(first,second)->first.length()-second.length());
在Java中,對lambda表達式所能做的也只是轉換為函數(shù)式接口。甚至不能把lambda表達式賦給類型為Object的變量赤套,Object不是函數(shù)式接口飘痛。
方法引用
對比lambda表達式和方法引用
//lambea 表達式轉換為接口
var timer=new Timer(1000,event->System.out.println(event));
表達式System.out::println
是一個方法引用,它指示編譯器生成一個函數(shù)式接口的實例容握,覆蓋這個接口的抽象方法來調用給定的方法宣脉。
var timer=new Timer(1000,System.out::println);
類似于lambda表達式,方法引用也不是一個對象剔氏。不過塑猖,為一個類型為函數(shù)式接口的變量賦值時會生成一個對象。
方法引用的三種情況
- object::instanceMethod
方法引用等價于向方法傳遞參數(shù)的lambda表達式谈跛。對于System.out::print,對象是System.out羊苟,所以方法表達式等價于x->System.out.print(x)。 - Class::instanceMethod
在這種情況下感憾,第1個參數(shù)會成為方法的隱式參數(shù)蜡励。例如,String::compareToIgnoreCase等同于(x,y)->x.compareToIgnoreCase(y)阻桅。 - Class::staticMethod
在這種情況下凉倚,所有參數(shù)都傳遞到靜態(tài)方法:Math::pow等價于(x,y)->Math.pow(x,y)。
注意:只有當lambda表達式的體只調用一個方法而不做其他操作時嫂沉,才能把lambda表達式重寫為方法引用稽寒。
可以在方法引用中使用this
和super
。
變量作用域
lambda表達式可以捕獲外圍作用域中變量的值趟章。在Java中瓦胎,要確保所捕獲的值是明確定義的,這里有一個重要的限制:在lambda表達式中尤揣,只能引用值不會改變的變量。
如果在lambda表達式中引用一個變量柬祠,而這個變量可能在外部改變北戏,這也是不合法的。
lambda表達式中捕獲的變量必須實際上是事實最終變量(這個變量初始化之后就不會再為它賦新值)漫蛔。
幾個需要注意的點:
- lambda表達式的體與嵌套塊有相同的作用域嗜愈,在lambda表達式中聲明與一個局部變量同名的參數(shù)或局部變量是不合法的。
- lambda表達式中同樣也不能有同名的局部變量莽龟。
- 在一個lambda表達式中使用this關鍵字時蠕嫁,是指創(chuàng)建這個lambda表達式的方法的this參數(shù)。
內部類
內部類是定義在另一個類中的類
使用內部類訪問對象狀態(tài)
內部類的對象會有一個隱式引用毯盈,指向實例化這個對象的外部類對象剃毒。通過這個指針,它可以訪問外部對象的全部狀態(tài)。
一個內部類方法可以訪問自身的數(shù)據(jù)字段赘阀,也可以訪問創(chuàng)建它的外圍類對象的數(shù)據(jù)字段益缠。
內部類構造器
如果內部類沒有定義構造器,編譯器會為內部類生成一個無參數(shù)構造器基公。
public TimePrinter(TalkingClock clock)//這里TalkingClock是外部類
{
outer=clock;
}
在外部類方法鐘構造一個內部類對象后幅慌,編譯器就會將當前外部類的this引用傳遞給這個構造器:
var listener=new TimePrinter(this);
內部類的特殊語法規(guī)則
表達式 OuterClass.this 表示外圍類引用
一般來說,最新構造的內部類對象的外圍類引用被設置為創(chuàng)建內部類對象的方法的this引用轰豆。不過胰伍,也可以通過顯式地命名將外圍類引用設置為其他的對象。例如:
var jabberer=new TalkingClock(1000,true);
TalkingClock.TimePrinter listener=jabberer.new TimePrinter();
在外圍類的作用域之外引用內部類: OuterClass.InnerClass
要注意的地方
- 內部類中聲明的所有靜態(tài)字段都必須是final酸休,并初始化為一個編譯時常量骂租。如果這個字段不是一個常量,就可能不唯一雨席。
- 內部類不能有static方法菩咨。
為什么要使用內部類?
內部類可以訪問外圍類的私有數(shù)據(jù)陡厘,但是外部類則不行抽米。
由于內部類擁有更大的訪問權限,所以天生就比常規(guī)類更加強大糙置。
局部內部類
如果一個類只在某個方法中創(chuàng)建這個類型的對象時使用了一次云茸,可以在一個方法中局部地定義這個類。
public void start()
{
class TimePriter implements ActionListener //這是一個局部內部類
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone,the time is "
+Instant.ofEpochMilli(event.getWhen()));
if(beep)Toolkit.getDefaultToolkit().beep();
}
}
var listener=new TimePriter();
var timer=new Timer(interval,listener);
timer.start();
}
- 聲明局部類時不能有訪問說明符(public或private)谤饭。局部類的作用域被限定在聲明這個局部類的塊中标捺。
- 局部類有一個很大的優(yōu)勢,即對外部世界完全隱藏揉抵。除了定義它的這個方法以外亡容,沒有任何方法能夠知道局部類的存在。
匿名內部類
假如只想創(chuàng)建這個類的一個對象冤今,甚至不需要為類指定名字闺兢。
匿名內部類的語法
new SuperType(Construction parameters)
{
inner class methods and data
}
SuperType可以是接口,也可以是一個類戏罢。如果是接口屋谭,內部類就要實現(xiàn)這個接口。如果是一個類龟糕,內部類就要擴展這個類桐磁。
因為構造器的名字必須與類名相同,而匿名內部類沒有類名讲岁,所以我擂,匿名內部類不能有構造器衬以。實際上,構造參數(shù)要傳遞給超類(superclass)構造器扶踊。
匿名內部類使用技巧:“雙括號初始化”
var listener=new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone,the time is "
+Instant.ofEpochMilli(event.getWhen()));
if(beep) Toolkit.getDefaultToolkit().beep();
}
};
生成靜態(tài)方法當前類的類名
一般我們使用getClass的時候泄鹏,調用的是this.getClass(),而靜態(tài)方法沒有this秧耗。所以應該使用如下表達式:
new Object(){}.getClass().getEnclosingClass()//gets class of static method
在這里备籽,new Object(){}會建立Object的匿名子類的一個匿名對象,而getEnclosingClass則得到其外圍類分井,也就是包含這個靜態(tài)方法的類车猬。
靜態(tài)內部類
如果在使用內部類的時候只是為了把一個類隱藏在另外一個類的內部,并不需要內部類有外圍類對象的一個引用尺锚,可以將內部類聲明為static珠闰。
靜態(tài)內部類就類似于其他內部類,只不過靜態(tài)內部類的對象沒有生成它的外圍類對象的引用瘫辩。
如果在靜態(tài)方法中構造內部類伏嗜,則必須使用靜態(tài)內部類。
靜態(tài)內部類需要注意的地方
- 只要內部類不需要訪問外圍類對象伐厌,就應該使用靜態(tài)內部類承绸。
- 與常規(guī)內部類不同,靜態(tài)內部類可以有靜態(tài)字段和方法
- 在接口中聲明的內部類自動是static和public
訪問靜態(tài)內部類
//Pair類是ArrayAlg的一個公共內部類
ArrayAlg.Pair p=ArrayAlg.minmax(d);
服務加載器
代理
這兩塊知識點需要的話再回來學