1. 反射
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。
1.1 反射相關的類
|
類名 |
用途 |
|
Class類 |
代表類的實體,在運行的Java應用程序中表示類和接口 |
|
Field類 |
代表類的成員變量(類的屬性) |
|
Method類 |
代表類的方法 |
|
Constructor |
代表類的構造方法 |
1.2 Class類中的相關方法
1.3 Field類中的相關方法
1.4 Method類中的相關方法
1.5 Constructor類中的相關方法
1.6 獲取Class對象的三種方式
在反射之前,我們需要做的第一步就是先拿到當前需要反射的類的Class對象,然後通過Class對象的核心方法,達到反射的目的
- 我們先構造出一個學生類作為演示示例
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");
}
}
- 使用 .class 方法(僅適合在編譯前就已經明確要操作的 Class)。
public class test2 {
public static void main(String[] args) throws ClassNotFoundException {
//直接通過 類名.class 的⽅式得到,該⽅法最為安全可靠,程序性能更⾼
Class c2 = Student.class;
}
}
- 使用類對象的 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 枚舉的使用
- 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;
}
}
}
- 常⽤⽅法
//將枚舉類型的所有成員以數組形式返回並打印
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();
}