目錄
- 反射
- 枚舉的使用
- Lambda表達式
- 函數式接口
- 語法
- Lambda表達式語法精簡
- 變量捕獲
- Lambda在集合List中的使用
反射
作用:在Java代碼中,讓一個對象認識到自己
比如一個類的名字,里面的方法,屬性等
讓程序運行的過程,某個對象也能獲取到上述信息
Java提供了一組 API,通過這些 API 拿到指定對象的上述信息
注:API全稱Application Programming Interface,它是一組方法和類,提供給別人使用的,例如Scanner,Queue,List
如:
像這樣得到String的全部信息
獲取用戶輸入的想要的類型(動態的過程)
newInstance:獲取實例
forName:獲取對象
反射的作用:
把序列化的字符串還原成對象(反序列化)
序列化:把一個對象轉成一個字符串
獲取到類的某個屬性(Field):
獲取到某個方法(Method):
如果print1有兩個方法(重寫),具體調用哪個方法也是可以明確使用的
例如有一個print(String x)
如果調用上面的方法,可以用
Method pm=studentClass.getDeclaredMethod(“print”,String.class);
通過后續的參數,區分出當前要獲取的print是哪個版本,后續參數就表示‘獲取方法的參數列表’
完整訪問:
class Student{private int id=1;private String name;public void print(){System.out.println("id:"+id+"name:"+name);}public void print(String p){System.out.println(p+" id:"+id+" name:"+name);}
}
public class Test2 {public void test1() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {Class studentClass=Class.forName("Student");Field idField=studentClass.getDeclaredField("id");//創建一個對象Student student=new Student();//由于student的id是私有的,將它取出,相當于“開鎖”idField.setAccessible(true);//設置里面的值idField.setInt(student,100);int id=idField.getInt(student);//獲取值System.out.println(id);}public void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {//通過反射調用 Student 方法Class studentClass=Student.class;//拿到方法對象Method pm=studentClass.getDeclaredMethod("print",String.class);Student student=new Student();//隨便創建一個對象pm.setAccessible(true);pm.invoke(student,"hello,");//調用方法}public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {Test2 test=new Test2();test.test2();//結果:hello, id:1 name:null}
}
通過反射,能隨意訪問任意對象的任意屬性
反射的缺點:大大降低了效率,繞過了源代碼,提高了維護成本,不到萬不得已不要使用
枚舉的使用
public enum Gender {//每個枚舉項通常用大寫字母來表示,本質上是一組常量//這幾個常量類型都是Gender,但值不同//常量使用都好分割MALE,FEMALE,OTHER
}
在main方法中使用:
此外,枚舉中還能指定屬性和方法(枚舉和類很相似)
枚舉和類的區別:
類可以隨意在外面創建實例,但枚舉不行,
枚舉項的創建必須在 枚舉的內部進行(構造方法必須是私有的)
Color c=Color.RED;System.out.println(c.getName());//結果:紅色
枚舉不可繼承,無法擴展
問:能否通過反射,拿到私有的構造方法,然后在枚舉外面創建出新的枚舉項?
答:不能,Java的反射 API 中,針對枚舉值特殊情況做了特殊處理
Lambda表達式
函數式接口
由于Java沒有其他編程語言擁有的匿名函數(沒有名字的函數,用完一次就扔了,通常作為“回調函數”用,類似于Comparetor,Comparable)
Java引入“函數式接口” 來引申出匿名函數=>這就是Lambda 表達式
如果一個接口里面只有一個抽象方法,就可以稱為函數式接口
語法
Lambda表達式語法精簡
- 參數類型可以省略,如果需要省略,每個參數的類型都要省略。
- 參數的?括號??只有?個參數,那么?括號可以省略
- 如果?法體當中只有?句代碼,那么?括號可以省略
- 如果?法體中只有?條語句,且是return語句,那么?括號可以省略,且去掉return關鍵字。
1.比如優先級隊列調用Comparator
正常用匿名內部類寫法如下:
public class Test5 {class Student{public String name;public int id;}public static void main(String[] args) {PriorityQueue<Student> priorityQueue=new PriorityQueue<>(new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {return o1.id-o2.id;}});}
而使用Lambda表達式更簡單:
public static void main(String[] args) {PriorityQueue<Student> priorityQueue=new PriorityQueue<>((o1, o2) -> o1.id-o2.id);}
(o1,o2)里面表示形參列表,形參的參數類型是可以省略的
-> 是Lambda的關鍵標志,一看到這個箭頭,就知道是Lambda
o1.id-o2.id 是lambda的函數體(如果 lambda 里只有一句return代碼,{ } 就可以省略,return也能省略)
2.例如使用函數式接口:
interface MyInterface{//函數式接口void print(String s);
}public class Test6 {//用lambda表達式MyInterface myInterface=(s)-> System.out.println(s);//不用lambda表達式MyInterface myInterface1=new MyInterface() {@Overridepublic void print(String s) {System.out.println(s);}};
}
可見lambda表達式能大量減少代碼量
3.把 lambda 表達式的內容作為另一個方法的實參(前提是 該形參的類型必須是函數式接口):
interface MyComparator{int compare(String s1,String s2);
}
class MyPriorityQueue{public MyPriorityQueue(MyComparator comparator){//函數式接口作為形參}
}
public class Test7 {public static void main(String[] args) {MyPriorityQueue queue=new MyPriorityQueue((s1,s2)->s1.compareTo(s2));}
}
第四個例子用的比較少,可以了解
lambda表達式最主要的功能就是省略,寫起來簡便
變量捕獲
在 Lambda 表達式里捕獲的變量必須是 final 或者是事實上的 final(即一旦賦值就不會再改變)
lambda 表達式能夠捕獲外部的變量,在內部進行使用
lambda的變量捕獲,能把創建后銷毀掉的變量再拿出來(這是很可怕的一件事)
相當于上頭斷了線,下面還在執行
Lambda 能夠使用捕獲的變量的原因:在定義 lambda 的時候就把捕獲的變量在 lambda 內部拷貝(地址不同)了一份
所以我們不能讓捕獲到的num(變量) 之后還能繼續修改,否則拷貝的和外面的num會出現歧義,
因此,規定捕獲到的變量必須是final的或沒有進行修改的
Lambda在集合List中的使用
public class Test8 {public static void main(String[] args) {List<Integer> list=new ArrayList<>();list.add(1);list.add(2);list.add(3);for (int i=0;i<list.size();i++){//正常寫法System.out.print(list.get(i)*2+" ");}//用lambda表達式寫list.forEach(i -> System.out.print(i*2+" "));}
}