1. 程式人生 > 程式設計 >Java特性之列舉、註解和Lambda表示式

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構造方法

列舉也可以有構造方法,這樣在定義列舉的成員變數的時候,就可以用構造方法來進行初始化。 注意:

  1. 列舉的構造方法只能用private來修飾,否則報錯。
  2. 列舉的常量必須在最前面定義,並以;隔開。

在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()

    }
}
複製程式碼

報錯的截圖如下:

@Override註解報錯

@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{
		......}
複製程式碼

注意:

  1. 在自定義註解裡寫成員變數,要接括號,例如:public int id();
  2. 程式設計師自己定義一個介面繼承Annotation是不會被編譯器當做註解的,所以要想自定義註解只能寫成@interface xxx{}
  3. @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;
        });
    }
複製程式碼