1. 反射

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。

1.1 反射相關的類

類名

用途

Class類

代表類的實體,在運行的Java應用程序中表示類和接口

Field類

代表類的成員變量(類的屬性)

Method類

代表類的方法

Constructor

代表類的構造方法

1.2 Class類中的相關方法

JAVA SE 反射,枚舉與lambda表達式_System

1.3 Field類中的相關方法

JAVA SE 反射,枚舉與lambda表達式_java_02

1.4 Method類中的相關方法

JAVA SE 反射,枚舉與lambda表達式_代碼塊_03

1.5 Constructor類中的相關方法

JAVA SE 反射,枚舉與lambda表達式_java_04

1.6 獲取Class對象的三種方式

在反射之前,我們需要做的第一步就是先拿到當前需要反射的類的Class對象,然後通過Class對象的核心方法,達到反射的目的

  1. 我們先構造出一個學生類作為演示示例
class Student {
    //私有屬性name
    private String name = "xiaozhuxiaozhu";
    //公有屬性age
    public int age = 18;
    //不帶參數的構造⽅法
    public Student(){
        System.out.println("Student()");
    }

    private Student(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }

    private void eat(){
        System.out.println("i am eat");
    }

    public void sleep(){
        System.out.println("i am pig");
    }

    private void function(String str) {
        System.out.println(str);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

1.使用 Class.forName(“類的全路徑名”); 靜態方法(前提:已明確類的全路徑名。

public class test1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //通過 Class 對象的 forName() 靜態⽅法來獲取,⽤的最多,但可能拋出 ClassNotFoundException 異常
        //注意這⾥是類的全路徑,如果有包需要加包的路徑
        Class c1 = Class.forName("reflection.Student");
    }
}
  1. 使用 .class 方法(僅適合在編譯前就已經明確要操作的 Class)。
public class test2 {
    public static void main(String[] args) throws ClassNotFoundException {
        //直接通過 類名.class 的⽅式得到,該⽅法最為安全可靠,程序性能更⾼
        Class c2 = Student.class;
    }
}
  1. 使用類對象的 getClass() 方法
public class test3 {
    public static void main(String[] args) throws ClassNotFoundException {
        Student s1 = new Student();
        Class c3 = s1.getClass();
    }
}
1.7 反射的使用

接下來我們開始使用反射,我們依舊反射上面的Student類
注意:所有和反射相關的包都在 import java.lang.reflect 包下面。

package reflection;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;


public class demo_test {
    public static void main(String[] args) throws ClassNotFoundException, 
    	InstantiationException,
     IllegalAccessException, 
     NoSuchMethodException,
     SecurityException, 
     IllegalArgumentException, 
     InvocationTargetException,
     NoSuchFieldException {
        // 獲取Class對象
        Class<?> c1 = Class.forName("reflection.Student");
        Object newInstance = c1.newInstance();;
        Student student = (Student)newInstance;
        System.out.println(student);

        // 獲取構造方法
        Constructor<?> constructors = c1.getDeclaredConstructor(String.class, int.class);
        //設置為true後可修改訪問權限
        constructors.setAccessible(true);
        Object newInstance1 = constructors.newInstance("zhangsan", 20);
        Student student1 = (Student)newInstance1;
        System.out.println(student1);

        // 反射私有⽅法
        Method method = c1.getDeclaredMethod("function", String.class);
        //設置為true後可修改訪問權限
        method.setAccessible(true);
        method.invoke(student1, "hello");

        // 反射私有屬性
        Field field = c1.getDeclaredField("name");
        //設置為true後可修改訪問權限
        field.setAccessible(true);
        field.set(student1, "lisi");
        System.out.println(student1);

    }
}

2. 枚舉

枚舉是在JDK1.5以後引⼊的。本質是一個特殊的類,是 java.lang.Enum 的⼦類,⾃⼰寫的枚舉類,就算沒有顯示的繼承 Enum ,但是其默認繼承了這個類。枚舉的關鍵字為enum,一般表示一組常量,比如一年的 4 個季節,一個年的 12 個月份,一個星期的 7 天,方向有東南西北等。

2.1 枚舉的定義
限定符 enum 枚舉名稱{
	常量1,常量2,常量3....;
}
2.2 枚舉的使用
  1. switch語句
public enum testenum {
    RED,BLACK,WHITE;
    public static void main(String[] args) {
        testenum t1 = testenum.BLACK;
        switch (t1) {
            case RED:
                System.out.println("紅色");
                break;
            case BLACK:
                System.out.println("黑色");
                break;
            case WHITE:
                System.out.println("白色");
                break;
            default:
                break;
        }
    }   
}
  1. 常⽤⽅法

JAVA SE 反射,枚舉與lambda表達式_java_05

//將枚舉類型的所有成員以數組形式返回並打印
testenum[] values = testenum.values();
for (int i = 0; i < values.length; i++) {
	System.out.println(values[i]);    
}   
2.3 枚舉的構造方法

注意:當枚舉對象有參數後,需要提供相應的構造函數

注意:枚舉的構造函數默認是私有的 這個⼀定要記住

public enum testenum {
    RED("紅色",1),BLACK("黑色",2),WHITE("白色",3);
    private String color;
    private int key;
    private testenum(String color,int key){
        this.color=color;
        this.key=key;
    }
}
2.4 枚舉與反射

關於枚舉與反射,請記住一個結論:你不能通過反射獲取枚舉類的實例!

這也是<為什麼枚舉實現單例模式是安全的?>問題的答案.至於為什麼呢,等我學完單例模式之後在來補充…

3. Lambda表達式

Lambda 表達式(Lambda expression),基於數學中的λ演算得名,也可稱為閉包(Closure) ,是Java SE 8中⼀個重要的新特性. Lambda表達式允許你通過表達式來代替功能接口。 lambda表達式就和方法⼀樣,它提供了⼀個正常的參數列表和⼀個使用這些參數的主體(主體可以是⼀個表達式或⼀個代碼塊)。

3.1 Lambda表達式語法

基本語法: (parameters) -> expression 或(parameters) -> {statement;}

Lambda表達式由三部分組成:

1. paramaters:類似方法中的形參列表,這⾥的參數是函數式接口裏的參數。

2. ->:可理解為“被用於”的意思

3. 方法體:可以是表達式也可以代碼塊,是函數式接口裏方法的實現。代碼塊可返回一個值或者什麼

都不反回,這裏的代碼塊塊等同於方法的方法體。

// 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)

注意:

如果一個接口只有一個抽象方法,那麼該接口就是⼀個函數式接口

如果我們在某個接口上聲明瞭 @FunctionalInterface 註解,那麼編譯器就會按照函數式接口的定義來要求該接口,這樣如果有兩個抽象方法,程序編譯就會報錯的。所以,從某種意義上來説,只要你保證你的接口中只有一個抽象方法,你可以不加這個註解。加上就會自動進行檢測的。

@FunctionalInterface
interface NoParameterNoReturn {
//注意:只能有⼀個⽅法
void test();
}
3.3 變量捕獲
@FunctionalInterface
interface NoParameterNoReturn {
	void test();
}

public static void main(String[] args) {
	int a = 10;
	NoParameterNoReturn noParameterNoReturn = ()->{
		// a = 99; error
		System.out.println("捕獲變量:"+a);
	};
	noParameterNoReturn.test();
}