反射枚舉與lambda
Java的反射(reflection)機制是在運作狀态中,
對于任意一個類,都能夠知道這個類的所有屬性和方法;
對于任意一個對象,都能夠調用它的任意方法和屬性,可以修改部分類型資訊;
動态擷取資訊 動态調用對象方法
Java檔案被編譯後,生成了.class檔案,
JVM此時就要去解讀.class檔案 ,被編譯後的Java檔案.class也被JVM解析為 一個對象,這個對象就是 java.lang.Class .
這樣當程式在運作時,每個java檔案就最終變成了Class類對象的一個執行個體(jdk1.8之後Class對象存放在堆裡)。
用途:
利用Java的反射機制通過反射來擷取所需的私有成員或是方法 。
開發各種通用架構
運作時類型(RTTI)和編譯時類型
例如Person p = new Student();這句代碼中p在編譯時類型為Person,運作時類型為Student。
反射相關的類
Class類
常用獲得類相關的方法
getClassLoader() 獲得類的加載器 雙親委派模型(不能破壞雙親委派模型)
雙親委派模型的優點
常用獲得類中屬性相關的方法(以下方法傳回值為Field相關)
getField(String name) 獲得某個公有的屬性對象
getFields() 獲得所有公有的屬性對象
getDeclaredField(String name) 獲得某個屬性對象
getDeclaredFields() 獲得所有屬性對象
獲得類中注解相關的方法
getAnnotation(Class annotationClass) 傳回該類中與參數類型比對的公有注解對象 getAnnotations() 傳回該類所有的公有注解對象
getDeclaredAnnotation(Class annotationClass) 傳回該類中與參數類型比對的所有注解對象 getDeclaredAnnotations() 傳回該類所有的注解對象
獲得類中構造器相關的方法(以下方法傳回值為Constructor相關)
獲得類中方法相關的方法(以下方法傳回值為Method相關)
獲得Class對象的方式;
第一種,使用 Class.forName(“類的全路徑名”); 靜态方法。前提:已明确類的全路徑名。
第二種,使用 類名.class 方法。 說明:僅适合在編譯前就已經明确要操作的 Class
類名.class: Class<?> c2 = Student.class;
第三種,使用類對象的 getClass() 方法
Student s1 = new Student();
Class c1 = s1.getClass()
// 建立對象
public class ReflectClass {
public static void reflectNewInstance() {
try {
Class<?> classStudent =
Class.forName(“com.gaobo.reflectdemo.Student”);
Object objectStudent = classStudent.newInstance();
Student student = (Student) objectStudent;
System.out.println(“獲得學生對象:”+student);
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 反射私有的構造方法
public static void reflectPrivateConstructor() {
try {
Class<?> classStudent =
Class.forName("com.gaobo.reflectdemo.Student");
//注意傳入對應的參數
Constructor<?> declaredConstructorStudent =
classStudent.getDeclaredConstructor(String.class,int.class);
//Constructor<?> declaredConstructorStudent
// = classStudent.getConstructor();
//setAccessible 設定為true後 可修改通路權限
declaredConstructorStudent.setAccessible(true);
Object objectStudent =
declaredConstructorStudent.newInstance("高博",15);
//Object objectStudent =
// declaredConstructorStudent.newInstance();
Student student = (Student) objectStudent;
System.out.println("獲得私有構造哈數且修改姓名和年齡:"+student);
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 反射私有屬性
public static void reflectPrivateField() {
try {
Class<?> classStudent =
Class.forName("com.gaobo.reflectdemo.Student");
Field field = classStudent.getDeclaredField("name");
field.setAccessible(true);
//可以修改該屬性的值
Object objectStudent = classStudent.newInstance();
Student student = (Student) objectStudent;
//field.set(修改的屬性的對象的引用, 修改的值);
field.set(student,"小明");
//field.get(這個對象的某個屬性)
String name = (String) field.get(student);
System.out.println("反射私有屬性修改了name:"+ name);
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 反射私有方法
public static void reflectPrivateMethod() {
try {
Class<?> classStudent = Class.forName("com.gaobo.reflectdemo.Student");
Method methodStudent = classStudent.
getDeclaredMethod("function",String.class);
System.out.println("私有方法的方法名為:"+methodStudent.getName());
//私有的一般都要加
methodStudent.setAccessible(true);
Object objectStudent = classStudent.newInstance();
Student student = (Student) objectStudent;
//通過反射調用方法
methodStudent.
invoke(student,"我是給私有的function函數傳的參數");
} catch (Exception ex) {
ex.printStackTrace();
}
}
反射優點和缺點
優點:
- 對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法
- 增加程式的靈活性和擴充性,降低耦合性,提高自适應能力
-
反射已經運用在了很多流行架構如:Struts、Hibernate、Spring 等等。
缺點:
- 使用反射會有效率問題。會導緻程式效率降低。具體參考這裡:http://www.imooc.com/article/293679
- 反射技術繞過了源代碼的技術,因而會帶來維護問題。反射代碼比相應的直接代碼更複雜
枚舉
本質:是 java.lang.Enum 的子類,也就是說,自己寫的枚舉類,就算沒有顯示的繼承 Enum ,但是其預設繼承了這個類。
将常量組織起來統一進行管理
使用:
switch語句
TextEnum testEnum2 = TextEnum.BLACK;
switch (testEnum2) {
case RED:
System.out.println(“red”);
break;
case BLACK:
System.out.println(“black”);
break;
case WHITE:
System.out.println(“WHITE”);
break;
default:
break;
}
枚舉的構造方法預設是私有的
- 1、當枚舉對象有參數後,需要提供相應的構造函數
- 2、枚舉的構造函數預設是私有的 這個一定要記住
枚舉優點缺點
優點: 1. 枚舉常量更簡單安全 。 2. 枚舉具有内置方法 ,代碼更優雅
缺點: 1. 不可繼承,無法擴充
拿到枚舉枚舉的構造方法
public static void reflectPrivateConstructor() {
try {
Class<?> classStudent =
Class.forName(“com.gaobo.enumdemo.TestEnum”);
//注意傳入對應的參數,獲得對應的構造方法來構造對象,目前枚舉類是提供了兩個參數分别是String和int。
Constructor<?> declaredConstructorStudent
= classStudent.getDeclaredConstructor(String.class,int.class,
String.class,int.class);
//設定為true後可修改通路權限
declaredConstructorStudent.setAccessible(true);
Object objectStudent
= declaredConstructorStudent.newInstance("綠色",666,"fafa",99);
TextEnum testEnum = (TextEnum) objectStudent;
System.out.println("獲得枚舉對象:"+testEnum);
} catch (Exception ex) {
ex.printStackTrace();
}
}
實作一個單例模式
1、 控制構造方法 private
2、 引用是static的
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class){
if(uniqueInstance == null){
//進入區域後,再檢查一次,如果仍是null,才建立執行個體
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
1、枚舉本身就是一個類,其構造方法預設為私有的,且都是預設繼承與 java.lang.Enum
2、枚舉可以避免反射和序列化問題
Lambda表達式
基本文法: (parameters) -> expression 或 (parameters) ->{ statements; }
- paramaters:類似方法中的形參清單,這裡的參數是函數式接口裡的參數。這裡的參數類型可以明确的聲明 也可不聲明而由JVM隐含的推斷。另外當隻有一個推斷類型時可以省略掉圓括号。
- ->:可了解為“被用于”的意思
-
statements:方法體:可以是表達式也可以代碼塊,是函數式接口裡方法的實作。代碼塊可傳回一個值或者什麼都不傳回,這裡的代碼塊塊等同于方法的方法體。如果是表達式,也可以傳回一個值或者什麼都不傳回
// 1. 不需要參數,傳回值為 2 () -> 2
// 2. 接收一個參數(數字類型),傳回其2倍的值 x -> 2 * x
// 3. 接受2個參數(數字),并傳回他們的和 (x, y) -> x + y
// 4. 接收2個int型整數,傳回他們的乘積 (int x, int y) -> x * y
// 5. 接受一個 string 對象,并在控制台列印,不傳回任何值(看起來像是傳回void) (String s) -> System.out.print(s)
函數式接口定義:一個接口有且隻有一個抽象方法
文法精簡
- 參數類型可以省略,如果需要省略,每個參數的類型都要省略。
- 參數的小括号裡面隻有一個參數,那麼小括号可以省略
- 如果方法體當中隻有一句代碼,那麼大括号可以省略
- 如果方法體中隻有一條語句,其是return語句,那麼大括号可以省略,且去掉return關鍵字
變量捕獲
匿名内部類的變量捕獲
變量要麼是被final修飾,如果不是被final修飾的 你要保證在使用之前,沒有修改
new Test(){
@Override
public void func() {
System.out.println(“我是内部類,且重寫了func這個方法!”);
System.out.println(“我是捕獲到變量 a == “+a +” 我是一個常量,或者是一個沒有改變過值的變量!”);
}
Lambda的變量捕獲
int a = 10;
NoParameterNoReturn noParameterNoReturn = ()->{
// a = 99; error
System.out.println(“捕獲變量:”+a);
Lambda在集合當中的使用
Collection接口
forEach() 方法示範
list.forEach(new Consumer() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
list.forEach((s)->System.out.println(s));
List接口
sort()方法的示範
list.sort(new Comparator() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
list.sort( (o1,o2)-> o1.compareTo(o2) );
Map接口
HashMap 的 forEach()
map.forEach(new BiConsumer<Integer, String>() {
@Override
public void accept(Integer integer, String s) {
System.out.println("key: “+integer +” value: "+s);
}
});
map.forEach((key,value) -> System.out.println("key: “+key +” value: "+value));
優點:
- 代碼簡潔,開發迅速
- 友善函數式程式設計
- 非常容易進行并行計算
-
Java 引入 Lambda,改善了集合操作
缺點:
- 代碼可讀性變差
- 在非并行計算中,很多計算未必有傳統的 for 性能要高
- 不容易進行調試