Java新特性之枚舉、註解、lambda表達式

導語: Java語言自誕生起,經歷了兩次較大的革新:第一次是在2004年,Java5引入了枚舉類型、註解和泛型;第二次是在2014年,Java8引入了lambda表達式。本文就重點介紹一下枚舉、註解和lambda表達式。

枚舉

定義枚舉類型

Java5使用關鍵字enum來表示枚舉類型。定義一個枚舉很簡單,如下所示:

public enum Season{
	SPRING,SUMMER,AUTUMN,WINTER;
}

上述代碼創建了一個名爲Season的枚舉類型,它的四個成員分別爲:SPRING,SUMMER,AUTUMN,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;
        });
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章