Java特性之列舉、註解和Lambda表示式
導語: Java語言自誕生起,經歷了兩次較大的革新:第一次是在2004年,Java5引入了列舉型別、註解和泛型;第二次是在2014年,Java8引入了lambda表示式。本文就重點介紹一下列舉、註解和lambda表示式。
列舉
定義列舉型別
Java5使用關鍵字enum來表示列舉型別。定義一個列舉很簡單,如下所示:
public enum Season{
SPRING,SUMMER,AUTUMN,WINTER;
}
複製程式碼
上述程式碼建立了一個名為Season的列舉型別,它的四個成員分別為:SPRING,WINTER。列舉型別的例項都是常量,故按照Java命名規則,其成員都應該大寫。
建立列舉例項
Season season=Season.SPRING;
複製程式碼
上述程式碼建立了一個列舉的引用,並將Season中的SPRING賦給該例項。
列舉常用方法
values()
方法用於返回在列舉中按照宣告順序產生的常量值組成的陣列。
ordinal()
方法返回某個列舉常量的索引(從0開始)。
案例:
//values()是列舉的陣列(按照列舉中定義的順序構成的陣列)
for(Season s:Season.values()){
//ordinal()是當前列舉成員的下標(索引)
System.out.println(s.ordinal()+":"+s);
}
複製程式碼
為了加深對列舉的熟悉程度,這裡寫一個小練習,用enum來模擬交通訊號燈。 程式碼如下:
public enum TrafficLight {
RED,GREEN,YELLOW;
}
public class TestLight {
private TrafficLight light=TrafficLight.RED;
public void change(){
switch(light){
case RED: //對於列舉,在case中不能寫成TrafficLight.RED,只能寫RED
this.light=TrafficLight.GREEN;
break ;
case GREEN:
this.light= TrafficLight.YELLOW;
break;
case YELLOW:
this.light=TrafficLight.RED;
break;
default:
break;
}
}
@Test
public void test(){
int i;
for(i=0;i<10;++i){
System.out.println(this.light);
change();
}
}
}
複製程式碼
注意: 在case中,列舉成員不能寫成Season.RED,必須寫成RED。
enum構造方法
列舉也可以有構造方法,這樣在定義列舉的成員變數的時候,就可以用構造方法來進行初始化。 注意:
- 列舉的構造方法只能用
private
來修飾,否則報錯。 - 列舉的常量必須在最前面定義,並以
;
隔開。
在enum中定義構造方法和普通方法:
public enum Orientation {
NORTH("北京"),SOUTH("南京"),WEST("西藏"),EAST("上海");
private String city;
private Orientation(String city){ // 列舉中的構造方法必須是private修飾,否則報錯
this.city=city;
}
public String getCity() {
return city;
}
}
@Test
public void test2(){
Orientation o1=Orientation.NORTH;
Orientation o2=Orientation.SOUTH;
Orientation o3=Orientation.WEST;
Orientation o4=Orientation.EAST;
System.out.println(o1.getCity());
System.out.println(o2.getCity());
System.out.println(o3.getCity());
System.out.println(o4.getCity());
}
複製程式碼
EnumMap的使用
EnumMap是一種特殊的Map,它要求所有的鍵都必須來自同一個列舉。 請看EnumMap使用的案例:
//EnumMap使用
@SuppressWarnings({"unchecked","unused"})
@Test
public void test3(){
//建立的EnumMap例項的鍵為Orientation列舉型別,值為String型別,引數為鍵型別的Class物件
EnumMap<Orientation,String> enumMap=new EnumMap<Orientation,String>(Orientation.class);
enumMap.put(Orientation.NORTH,"beijing");
enumMap.put(Orientation.SOUTH,"nanjing");
enumMap.put(Orientation.WEST,"xizang");
enumMap.put(Orientation.EAST,"shanghai");
for(Orientation o:Orientation.values()){
System.out.println(enumMap.get(o));
}
System.out.println();
for(String str:enumMap.values()){
System.out.println(str);
}
}
複製程式碼
註解
註解(Annotation,又稱元資料),它是在Java5引入的重要概念。註解是寫在程式碼裡的特殊標記,它以標準化和結構化的方式,採用能被編譯器檢查、驗證的格式儲存有關程式的額外資訊。
註解的分類
按照生成方式和功能的不同,Java的註解可以分為三大類:
- 內建註解
- 自定義註解
- 元註解
內建註解
內建註解就是Java內部已經寫好的註解,程式設計師可以直接在程式碼中使用。
Java5預定義了3種標準註解,具體如下:
@Override
該註解表示當前方法是重寫的父類中的方法,如果方法名寫錯了或者返回型別等錯誤,那麼編譯器就會報錯。
例如,下面的程式碼中:
public class Father {
public void fun1(){
System.out.println("this is father's fun1()");
}
}
public class Son extends Father {
@Override
public void fun2(){ //此處會報錯,因為方法fun2()不是重寫的父類中的方法,要把fun2()改成fun1()
}
}
複製程式碼
報錯的截圖如下:
@Deprecated
該註解表示某個類或方法已經過時,當使用已經過時的類或方法時,編譯器會發出警告(注意: 是警告,不會報錯)。過時的意思是該類或方法已經不合時宜,已經不建議使用,已經有新的類或方法取代它們了。
public class Father {
@Deprecated
public void fun2(){
System.out.println("fun2方法已經過時!");
}
public static void main(String[] args) {
Father father=new Father();
father.fun2(); //使用fun2方法,會在fun2上畫刪除線
}
}
複製程式碼
上述程式碼中,在fun2方法上加了@Deprecated
註解,這表示fun2方法已經過時,不建議使用,所以在使用fun2方法時,會出現刪除線的標誌,具體截圖如下:
SuppressWarnings
該註解用於關閉指定的編譯器警告資訊。例如:
List list=new ArrayList();
這句程式碼沒有寫泛型,那麼就會出現警告資訊。可以使用@SuppressWarnings("unchecked")註解來消除警告資訊。
@SuppressWarnings註解裡有一個名為value的String型別的陣列,該陣列用於接收像unchecked這類關鍵字,故註解@SuppressWarnings()裡面引數的完整寫法為@SuppressWarnings(value={"xxx","xxx"}),也可以簡寫為@SuppressWarnings({"xxx","xxx"})。當只有一個引數時,可以寫成@SuppressWarnings("xxx")
常用@SuppressWarnings("...")關鍵字舉例
unchecked
消除沒有進行型別檢查操作的警告。
unused
消除程式元素沒有被使用的警告。
自定義註解
自定義註解就是程式設計師自己定義的註解。
自定義註解的寫法
public @interface MyAnnotation{
......}
複製程式碼
注意:
- 在自定義註解裡寫成員變數,要接括號,例如:public int id();
- 程式設計師自己定義一個介面繼承Annotation是不會被編譯器當做註解的,所以要想自定義註解只能寫成@interface xxx{}
- @interface xxx,這樣寫之後編譯器會自動繼承Annotation介面。
下面寫一個完整的自定義註解,程式碼如下:
public @interface MyAnnotation {
public int id();
//name有一個預設值,如果在使用該註解時沒有給name賦值,那麼就會使用預設值
public String name() default "xurenyi";
}
複製程式碼
元註解
元註解就是給註解本身進行的註解。Java8在java.lang.Annotation包下提供了6個元註解:
@Target
該註解表示被修飾的註解能用於哪些元素型別。它有一個引數ElementType用於表示適用的元素型別,其值有CONSTRUCTOR(建構函式)、METHOD(方法)、PACKAGE(包)、PRAMETER(引數)、TYPE(類、介面、註解型別、列舉)、FIELD(成員變數)、LOCAL_VARIABLE(區域性變數)、ANNOTATION_TYPE(標準註解)。
@Target註解中也有一個名為value的ElementType型別的陣列,故引數的寫法同@SuppressWarnings註解。
該註解用法如下:
@Target({ElementType.METHOD,ElementType.CONSTRUCTOR,ElementType.PARAMETER,ElementType.TYPE,ElementType.PACKAGE})
public @interface MyAnnotation {
public int id();
//name有一個預設值,如果在使用該註解時沒有給name賦值,那麼就會使用預設值
public String name() default "xurenyi";
}
複製程式碼
@Retention
表示被修飾註解的儲存級別。引數RetentionPolicy表示儲存級別。RetentionPolicy的取值有:SOURCE(只保留在原始碼中,編譯時直接丟棄)、 CLASS(保留在class檔案中,但執行時jvm不能獲取註解資訊。)、RUNTIME(保留在class檔案中,並且執行時jvm可以獲取註解資訊)。
該註解的用法如下:
@Retention(RetentionPolicy.RUNTIME) //儲存級別
public @interface MyAnnotation {
public int id();
//name有一個預設值,如果在使用該註解時沒有給name賦值,那麼就會使用預設值
public String name() default "xurenyi";
}
複製程式碼
@Documented
指定被修飾的註解將被javadoc或其他類似工具提取成檔案。
@Inherited
表示被修飾的註解具有繼承性。也就是,加入某個類被@XXX註解修飾了,那麼當有子類繼承該類時,子類也自動會被@XXX註解修飾。
@Repeatable
這是Java8新增的重複註解。
Type Annotation
型別註解,是Java8新增的元註解,可以用在任何用到型別的地方。
在程式碼中,我們可以使用反射來讀取註解資訊,案例如下:
public @interface MyAnnotation {
public int id();
//name有一個預設值,如果在使用該註解時沒有給name賦值,那麼就會使用預設值
public String name() default "xurenyi";
}
public class TestEnumAnnotation {
public static void main(String[] args) throws NoSuchMethodException {
TestEnumAnnotation ea=new TestEnumAnnotation();
// 獲得TestEnumAnnotation的Class類物件
//Class<TestEnumAnnotation> clazz= (Class<TestEnumAnnotation>) ea.getClass();
Class<TestEnumAnnotation> clazz=TestEnumAnnotation.class;
// 獲得testMyAnnotation()方法
Method method=clazz.getMethod("testMyAnnotation");
// 如果註解MyAnnotation存在於方法method中
if(method.isAnnotationPresent(MyAnnotation.class)){
// 獲取方法中的註解
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
// 獲取註解中的變數
int id=annotation.id();
String name=annotation.name();
System.out.println(id+":"+name);
}
}
//測試自定義註解
@MyAnnotation(id=25,name="張三李四王五")
public void testMyAnnotation(){
System.out.println("這是在測試自定義註解。。。。");
}
}
複製程式碼
lambda表示式
lambda表示式簡介
lambda是Java8的新特色,它的寫法為:引數列表->lambda表達體 箭頭的左邊是引數列表,如果沒有引數,可以直接寫一對括號;箭頭右邊是lambda表達體,可以理解為lambda的具體實現。
簡單lambda表示式舉例
()->23.6
這個lambda表示式會返回23.6,相當於double myVal(){return 23.6;};
這個方法。
當lambda需要引數時,那麼就需要在左側的引數列表中指定。例如,(value)->(value%2)==0
,這個lambda表示式的意思為:當value是偶數時,則返回true,否則返回false。
函式式介面
函式式介面是隻有一個抽象方法的介面。例如:
// 函式式介面:只有一個(且只能有一個)抽象方法的介面
@FunctionalInterface //指定該介面是函式式介面
public interface MyVal {
double getVal();
}
複製程式碼
介面MyVal就是一個函式式介面,因為它只有一個抽象方法。程式碼中的@FunctionalInterface
註解用於指定某個介面是函式式介面。
lambda表示式與函式式介面的應用
@Test
public void test1(){
// lambda表示式在函式式介面中的應用(1)
MyVal myVal=()->24.6;
System.out.println(myVal.getVal());
}
複製程式碼
當把lambda表示式()->24.6
賦給介面myVal引用後,該lambda表示式會自動建立一個實現了介面抽象方法的類的例項。介面中的抽象方法由lambda表示式來實現。
lambda表示式與匿名內部類
lambda表示式是匿名內部類的一種簡化。
什麼是匿名內部類
匿名內部類,顧名思義,就是沒有名字的內部類。接下來,結合一段程式碼來講解一下什麼是匿名內部類:
public class AnonymousTest {
public void fun1(){
System.out.println("this is anonymous's fun1()");
}
// 匿名內部類介紹
@SuppressWarnings("unused")
@Test
public void test2(){
AnonymousTest anonymousTest=new AnonymousTest(){
@Override
public void fun1(){
System.out.println("this is son's fun1()");
}
public void fun2(){
System.out.println(".........");
}
};
anonymousTest.fun1();
}
}
複製程式碼
上述程式碼中的AnonymousTest anonymousTest=new AnonymousTest(){......}
程式碼片段就產生了一個匿名內部類。{......}
中就是匿名內部類的具體程式碼,而anonymousTest就是該匿名內部類的父類。
lambda表示式與匿名內部類的應用
分別使用匿名內部類和lambda表示式來實現同一個功能(字串逆序輸出)
public class AnonymousTest {
public static void main(String[] args) {
AnonymousTest anonymousTest=new AnonymousTest();
// 使用匿名內部類來實現
anonymousTest.display("qishiyi",new MyStringFunction(){
public String reverse(String str){
int i;
String result="";
for(i=str.length()-1;i>=0;--i){
result=result+str.charAt(i);
}
return result;
}
});
}
public void display(String str,MyStringFunction myStringFunction){
System.out.println(myStringFunction.reverse(str));
}
}
複製程式碼
@Test
public void test1(){
AnonymousTest anonymousTest=new AnonymousTest();
// 使用lambda表示式來實現
anonymousTest.display("xurenyi",(str)->{
int i;
String result="";
for(i=str.length()-1;i>=0;--i){
result=result+str.charAt(i);
}
return result;
});
}
複製程式碼