導語: 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構造方法
枚舉也可以有構造方法,這樣在定義枚舉的成員變量的時候,就可以用構造方法來進行初始化。
注意:
- 枚舉的構造方法只能用
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;
});
}