天天看點

反射枚舉與lambda

反射枚舉與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();
    }
}
           

反射優點和缺點

優點:

  1. 對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法
  2. 增加程式的靈活性和擴充性,降低耦合性,提高自适應能力
  3. 反射已經運用在了很多流行架構如:Struts、Hibernate、Spring 等等。

    缺點:

  4. 使用反射會有效率問題。會導緻程式效率降低。具體參考這裡:http://www.imooc.com/article/293679
  5. 反射技術繞過了源代碼的技術,因而會帶來維護問題。反射代碼比相應的直接代碼更複雜

枚舉

本質:是 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; }

  1. paramaters:類似方法中的形參清單,這裡的參數是函數式接口裡的參數。這裡的參數類型可以明确的聲明 也可不聲明而由JVM隐含的推斷。另外當隻有一個推斷類型時可以省略掉圓括号。
  2. ->:可了解為“被用于”的意思
  3. 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)

函數式接口定義:一個接口有且隻有一個抽象方法

文法精簡

  1. 參數類型可以省略,如果需要省略,每個參數的類型都要省略。
  2. 參數的小括号裡面隻有一個參數,那麼小括号可以省略
  3. 如果方法體當中隻有一句代碼,那麼大括号可以省略
  4. 如果方法體中隻有一條語句,其是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));

優點:

  1. 代碼簡潔,開發迅速
  2. 友善函數式程式設計
  3. 非常容易進行并行計算
  4. Java 引入 Lambda,改善了集合操作

    缺點:

  5. 代碼可讀性變差
  6. 在非并行計算中,很多計算未必有傳統的 for 性能要高
  7. 不容易進行調試