1. 反射
1.?反射相關的類
2. Class類(反射機制的起源 )
1. Java程序運行的生命周期
-
編譯階段
-
Java源代碼(.java文件)經過javac編譯器編譯
-
生成與平臺無關的字節碼文件(.class文件)
-
字節碼文件包含類的結構信息和方法指令
-
-
類加載階段
-
JVM通過類加載器(ClassLoader)讀取.class文件
-
將字節碼數據轉換為JVM內部的數據結構
-
創建對應的java.lang.Class對象
-
-
Class對象的作用
-
每個加載的類在JVM中都有唯一的Class對象
-
Class對象包含類的完整元數據:
-
類名、修飾符、包信息
-
字段(屬性)信息
-
方法信息
-
構造器信息
-
注解信息
-
-
-
反射機制
-
通過Class對象可以獲取類的運行時信息
-
動態操作類的能力包括:
-
創建類的實例
-
調用方法和訪問字段
-
修改訪問權限
-
動態代理
-
-
2.?獲得Class對象的三種?式
package reflection;public class Student {//私有屬性private String name = "zhangshan";//公有屬性public int age = 18;//不帶參數的共有構造方法public Student(){System.out.println("student()");}//帶一個參數的公有構造方法public Student(String name){this.name = name;System.out.println("Student(name)");}//帶一個參數的私有構造方法private Student(int age){this.age = age;System.out.println("Student(age)");}//帶兩個參數的私有構造方法private Student(String name,int age){this.name = name;this.age = age;System.out.println("Student(name,age)");}//公有方法public void sleep(){System.out.println("I am sleepy");}//私有方法private void eat(String food){System.out.println("I love delicious food");}private void run(){System.out.println("Run fast");}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}public static void main(String[] args) {Student student = new Student();System.out.println(student);}
}
//獲得Class對象的三種?式
class Main {public static void main(String[] args) {//1. 通過Class.forName獲取class對象Class<?> c1 = null;try{c1 = Class.forName("reflection.Student");}catch(ClassNotFoundException e){e.printStackTrace();}//2.直接通過 類名.class 的?式得到,該?法最為安全可靠,程序性能更?//這說明任何?個類都有?個隱含的靜態成員變量 classClass<?> c2 = Student.class;//3. 通過getClass獲取Class對象Student s3 = new Student();//創建對象Class<?> c3 = s3.getClass();//?個類在 JVM 中只會有?個 Class 實例,即我們對上?獲取的//c1,c2,c3進? equals ?較,發現都是trueSystem.out.println(c1.equals(c2));System.out.println(c1.equals(c3));System.out.println(c2.equals(c3));}
}
3.?Class類中的相關?法
1. 獲取類信息
方法 | 返回值 | 用途 | 示例 |
---|---|---|---|
Class.forName("全限定類名") | Class<?> | 動態加載類 | Class.forName("java.lang.String") |
對象.getClass() | Class<?> | 獲取對象的?Class ?對象 | "hello".getClass() |
類名.class | Class<?> | 直接獲取類的?Class ?對象 | String.class |
clazz.getName() | String | 獲取全限定類名(如?"java.lang.String" ) | String.class.getName() |
clazz.getSimpleName() | String | 獲取簡單類名(如?"String" ) | String.class.getSimpleName() |
clazz.getPackage() | Package | 獲取包信息 | String.class.getPackage() |
clazz.getModifiers() | int | 獲取修飾符(需用?Modifier ?解析) | Modifier.isPublic(modifiers) |
clazz.getSuperclass() | Class<?> | 獲取父類 | Integer.class.getSuperclass() |
clazz.getInterfaces() | Class<?>[] | 獲取實現的接口 | List.class.getInterfaces() |
2. 常?獲得類相關的?法:
3. 獲取注解(Annotation)
方法 | 返回值 | 用途 | 示例 |
---|---|---|---|
clazz.getAnnotations() | Annotation[] | 獲取類上的所有注解 | clazz.getAnnotations() |
clazz.getAnnotation(注解類) | Annotation | 獲取類上的指定注解 | clazz.getAnnotation(Deprecated.class) |
field.getAnnotations() | Annotation[] | 獲取字段上的所有注解 | field.getAnnotations() |
method.getAnnotations() | Annotation[] | 獲取方法上的所有注解 | method.getAnnotations() |
constructor.getAnnotations() | Annotation[] | 獲取構造方法上的所有注解 | constructor.getAnnotations() |
4. 獲取構造方法(Constructor)
方法 | 返回值 | 用途 | 示例 |
---|---|---|---|
clazz.getDeclaredConstructors() | Constructor<?>[] | 獲取所有聲明的構造方法(包括私有) | clazz.getDeclaredConstructors() |
clazz.getConstructors() | Constructor<?>[] | 獲取所有公共構造方法 | clazz.getConstructors() |
clazz.getDeclaredConstructor(參數類型...) | Constructor<?> | 獲取指定參數類型的構造方法(包括私有) | clazz.getDeclaredConstructor(String.class) |
clazz.getConstructor(參數類型...) | Constructor<?> | 獲取指定參數類型的公共構造方法 | clazz.getConstructor() |
constructor.setAccessible(true) | void | 設置私有構造方法可訪問 | constructor.setAccessible(true) |
constructor.newInstance(參數...) | Object | 通過構造方法創建實例 | constructor.newInstance("Tom") |
5. 獲取屬性(Field)
方法 | 返回值 | 用途 | 示例 |
---|---|---|---|
clazz.getDeclaredFields() | Field[] | 獲取所有聲明的屬性(包括私有) | clazz.getDeclaredFields() |
clazz.getFields() | Field[] | 獲取所有公共屬性(包括繼承的) | clazz.getFields() |
clazz.getDeclaredField("name") | Field | 獲取指定名稱的屬性(包括私有) | clazz.getDeclaredField("age") |
clazz.getField("name") | Field | 獲取指定名稱的公共屬性 | clazz.getField("name") |
field.setAccessible(true) | void | 設置私有屬性可訪問 | field.setAccessible(true) |
field.get(obj) | Object | 獲取屬性值 | field.get(user) |
field.set(obj, value) | void | 設置屬性值 | field.set(user, "Tom") |
6. 獲取方法(Method)
方法 | 返回值 | 用途 | 示例 |
---|---|---|---|
clazz.getDeclaredMethods() | Method[] | 獲取所有聲明的方法(包括私有) | clazz.getDeclaredMethods() |
clazz.getMethods() | Method[] | 獲取所有公共方法(包括繼承的) | clazz.getMethods() |
clazz.getDeclaredMethod("方法名", 參數類型...) | Method | 獲取指定方法(包括私有) | clazz.getDeclaredMethod("setName", String.class) |
clazz.getMethod("方法名", 參數類型...) | Method | 獲取指定公共方法 | clazz.getMethod("toString") |
method.setAccessible(true) | void | 設置私有方法可訪問 | method.setAccessible(true) |
method.invoke(obj, 參數...) | Object | 調用方法 | method.invoke(user, "Tom") |
package reflection;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class Test{//類的實例化public static void reflectNewInstance(){try{Class<?> classStudent = Class.forName("reflection.Student");Object objectStudent = classStudent.newInstance();//Student student = (Student) objectStudent;System.out.println("獲得學生對象:"+objectStudent);} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}//獲取類的公有(public)帶一個參數構造方法并實例化public static void reflectPublicConstructor(){try{Class<?> classStudent = Class.forName("reflection.Student");//獲取類的公有(public)無參構造方法Constructor<?> con = classStudent.getConstructor(String.class);//利用構造方法進行實例化Object studentNewInstance = con.newInstance("wangwu");//Student student = (Student) studentNewInstance;System.out.println("獲得公有帶一個參數構造方法并修改姓名:"+studentNewInstance);} catch (Exception e) {throw new RuntimeException(e);}}//獲取類的私有(private)帶一個參數構造方法并實例化public static void reflectPrivateConstructor(){try{Class<?> classStudnt = Class.forName("reflection.Student");//獲取類的私有(private)帶一個參數構造方法Constructor<?> con = classStudnt.getDeclaredConstructor(int.class);//繞過 Java 的訪問控制檢查,允許你訪問或調用原本不可見的成員(如 private 構造方法、方法或字段)。con.setAccessible(true);//實例化Object studentNewInstance = con.newInstance(20);System.out.println("獲得私有帶一個參數構造方法并修改年齡:"+studentNewInstance);} catch (Exception e) {throw new RuntimeException(e);}}//獲取類的所有構造方法并實例化public static void reflectionConstructor(){try{Class<?> classStudent = Class.forName("reflection.Student");//取類的所有構造方法Constructor<?>[] con = classStudent.getDeclaredConstructors();//使所有構造方法繞過 Java 的訪問控制檢查,允許訪問或調用原本不可見的成員for(Constructor<?> constructor:con){constructor.setAccessible(true);}//實例化Object s1 = con[3].newInstance();Object s2 = con[0].newInstance("lihua",23);System.out.println("獲得公有帶一個參數構造方法并修改姓名:"+s1);System.out.println("獲得私有帶兩個參數構造方法并修改姓名和年齡:"+s2);} catch (Exception e) {throw new RuntimeException(e);}}//獲取私有屬性public static void reflectPrivateField(){try {Class<?> classStudent = Class.forName("reflection.Student");//實例化Object s1 = classStudent.newInstance();//獲取私有屬性Field field = classStudent.getDeclaredField("name");field.setAccessible(true);//修改私有屬性field.set(s1,"xh");//獲取修改后的私有屬性String name = (String) field.get(s1);System.out.println("修改之后的私有屬性:"+name);} catch (Exception e) {throw new RuntimeException(e);}}//獲取私有方法public static void reflectPrivateMethod(){try {Class<?> classStudent = Class.forName("reflection.Student");//獲取私有方法Method method = classStudent.getDeclaredMethod("eat",String.class);System.out.println("獲取私有?法的?法名為:"+method.getName());method.setAccessible(true);//實例化Object s1 = classStudent.newInstance();//方法調用method.invoke(s1,"vegetable");} catch (Exception e) {throw new RuntimeException(e);}}public static void main(String[] args) {reflectNewInstance();reflectPublicConstructor();reflectPrivateConstructor();reflectionConstructor();reflectPrivateField();
3.?反射優點和缺點
1. 優點
1. 動態性(運行時操作類)
無需在編譯時確定類,可以在運行時動態加載類、調用方法、訪問屬性。
2. 訪問私有成員
通過?
setAccessible(true)
?可以繞過 Java 的訪問控制,訪問?private
?方法、屬性和構造方法。3. 泛型擦除時獲取真實類型
由于 Java 泛型在運行時會被擦除(Type Erasure),可以通過反射獲取泛型的實際類型。
4. 注解處理
反射可以讀取類、方法、字段上的注解,實現靈活的配置和擴展。
5. 動態創建和操作對象
可以在運行時動態創建對象、調用方法,適用于?高度靈活?的場景。
2. 缺點
大家都說 Java 反射效率低,你知道原因在哪里么_慕課手記
1. 性能較差
-
反射比直接調用慢 10~100 倍,主要因為:
-
JVM 無法優化反射調用(如方法內聯)。
-
需要額外的安全檢查(如?
AccessibleObject.setAccessible()
)。
-
-
影響場景:
-
高頻調用的代碼(如循環內使用反射)。
-
高性能要求的系統(如交易系統、游戲引擎)。
-
2. 破壞封裝性
-
setAccessible(true)
?可以繞過?private
?限制,導致:-
代碼安全性降低(惡意代碼可能篡改私有數據)。
-
破壞面向對象的封裝原則(如?
final
?字段被修改)。
-
3. 代碼可讀性和維護性差
-
反射代碼通常?冗長、難以調試,IDE 也無法提供智能提示。
4. 編譯時檢查失效
-
反射調用在?編譯期不會檢查錯誤(如方法名拼寫錯誤、參數類型不匹配),只能在運行時拋出異常。
5. 安全問題
-
反射可以?繞過安全管理器(SecurityManager),可能導致:
-
私有 API 被非法調用。
-
敏感數據泄露(如通過反射獲取?
Password
?字段)。
-
getDeclaredMethods()
、getDeclaredFields()
?或?getDeclaredConstructors()
?獲取的方法、屬性或構造方法的順序是不確定的,具體順序取決于 JVM 的實現(如 OpenJDK 和 Oracle JDK 可能不同)。所以我們可以使用 Arrays.sort?按名稱、修飾符、參數類型等自行排序。
優點 | 缺點 |
---|---|
動態加載和操作類 | 性能差(比直接調用慢 10~100 倍) |
可訪問私有成員 | 破壞封裝性 |
支持泛型擦除時的類型獲取 | 代碼可讀性差 |
強大的注解處理能力 | 編譯時檢查失效 |
適用于框架和靈活架構 | 可能引發安全問題 |
2. 枚舉
1. 背景及定義
public static final int RED = 1;
public static final int GREEN = 2;
public static final int WHITE = 3;

2. Enum 類的常??法

3. 關鍵點:
-
枚舉常量必須放在枚舉類的最前面,并用逗號?
,
?分隔,最后一個常量后用分號?;?,
后面才能定義字段和方法。。 -
枚舉的構造方法是自動調用的,構造方法必須與枚舉常量的參數匹配(無參常量 → 無參構造方法;帶參常量 → 帶參構造方法)。
-
構造方法默認是?
private
,不能聲明為?public
?或?protected
(因為枚舉的實例只能由枚舉自身創建)。 -
構造方法調用是隱式的,當枚舉類被 JVM 加載時,所有枚舉常量會被初始化,并自動調用對應的構造方法(不能手動調用構造方法),例如?WHITE("White",5);
-
枚舉常量是單例的,構造方法只會被調用一次:
-
每個枚舉常量本質上是一個靜態實例,相當于:
public static final EnumDom WHITE = new EnumDom("White",10);
(枚舉類型(enum)的構造方法默認是私有的(private),這意味著你不能直接使用new關鍵字來創建枚舉實例。 枚舉常量必須通過枚舉類型本身隱式創建。例如?WHITE("White",5);)
7.?在Java中,枚舉常量的引用不可變,但若設計不當(含非?final
?字段),其內部狀態可能被修改。強烈建議將枚舉設計為完全不可變。
4. 使用
public enum EnumDom {RED,//無參枚舉常量GREEN("Green"),//帶一個參數的枚舉常量WHITE("White",5);//帶兩個參數的枚舉常量//枚舉類型(enum)的構造方法默認是私有的(private),這意味著你不能直接使用new關鍵字來創建枚舉實例。// 枚舉常量必須通過枚舉類型本身隱式創建。//public static final EnumDom WHITE = new EnumDom("White",10);//構造方法必須匹配枚舉常量的參數類型和數量//無參構造方法(可不寫,java會自動提供)private EnumDom(){}public String name;public int code;//帶一個參數的構造方法private EnumDom(String name){this.name = name;}//帶兩個參數的構造方法private EnumDom(String name,int code){this.name = name;this.code = code;System.out.println(this.name+" "+this.code);}//方法private void color(String name){this.name = name;//非final字段,可以修改System.out.println(this.name);}// @Override
// public String toString() {
// return "EnumDom{" +
// "name='" + name + '\'' +
// ", code=" + code +
// '}';
// }public static void main(String[] args) {//直接調用枚舉常量//枚舉常量在類加載時通過構造方法初始化,且僅初始化一次(線程安全)。EnumDom w1 = EnumDom.WHITE;EnumDom w2 = EnumDom.WHITE;System.out.println(w1);System.out.println(w2);System.out.println(w1==w2);//同一個WHITE//以數組形式返回枚舉類型的所有成員EnumDom[] enumDom = EnumDom.values();for(EnumDom e: enumDom){System.out.print(e+" ");//獲取枚舉成員的索引位置System.out.println(e.ordinal());}//將普通字符串轉換為枚舉實例EnumDom e1 = EnumDom.valueOf("RED");System.out.println(e1);//比較兩個枚舉成員在定義時的順序System.out.println(enumDom[0].compareTo(enumDom[2]));//方法調用enumDom[0].color("red");enumDom[1].color("green");}
}
5. 枚舉和反射
通過反射我們可以獲取枚舉常量本身,非final字段,方法,構造方法信息,注解信息
不可以獲取/操作的內容:
-
無法通過構造方法創建新的枚舉實例
-
嘗試反射調用構造方法會拋出
IllegalArgumentException: Cannot reflectively create enum objects
-
-
無法修改final字段(除非使用特殊技巧)
-
常規反射無法修改final字段
-
需要先修改Field的modifiers字段(不推薦)
-
-
無法獲取編譯器生成的某些特殊方法
-
如
values()
和valueOf()
方法在字節碼中是編譯器生成的
-
-
無法改變枚舉常量的順序(ordinal)
-
ordinal是final的且由編譯器決定
-
-
無法刪除或添加枚舉常量
-
枚舉集合在運行時是固定的
-
package enumeration;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;public class Test {public static void main(String[] args) {try{Class<?> clazz = Class.forName("enumeration.EnumDom");//獲取所有枚舉常量并調用對應的構造方法Object[] enumDoms = clazz.getEnumConstants();//打印所有枚舉成員for(Object em :enumDoms ){System.out.println(em);}//獲取枚舉構造方法Constructor<?>[] con = clazz.getDeclaredConstructors();for(Constructor<?> constructor:con){constructor.setAccessible(true);}//獲取指定枚舉構造方法,包含繼承的Enum的構造方法的參數Constructor<?> con1 = clazz.getDeclaredConstructor(String.class,int.class,String.class);//無法通過反射創建新實例//Object e1 = con[0].newInstance();//拋出異常 Cannot reflectively create enum objects//System.out.println(e1);//獲取枚舉類的方法Method method = clazz.getDeclaredMethod("color",String.class);method.setAccessible(true);method.invoke(EnumDom.RED,"red");//在反射中可以直接調用枚舉常量method.invoke(enumDoms[1],"green");} catch (Exception e) {throw new RuntimeException(e);}}
}
3. Lambda表達式
1. 背景
2. Lambda表達式的語法
(parameters) -> expression 或(parameters) ->{ statements; }
Lambda表達式由三部分組成:1. paramaters:類似?法中的形參列表,這?的參數是函數式接??的參數(可以包含零個或多個) 。這?的參數類型可以明確的聲明也可不聲明?由JVM隱含的推斷。另外當只有?個參數且無參數類型時可以省略掉圓括號。2. ->:可理解為“被?于”的意思,將參數與方法體分開3. ?法體:可以是單個表達式或代碼塊,是函數式接???法的實現。代碼塊可返回?個值或者什么都不返回,這?的代碼塊等同于?法的?法體。如果是表達式,也可以返回?個值或者什么都不返回。單個表達式或不用return關鍵字 直接返回表達式結果可以省略大括號{}。
3.?函數式接?
//?返回值?參數
@FunctionalInterface
interface NoParameterNoReturn {void test();
}
//?返回值?個參數
@FunctionalInterface
interface OneParameterNoReturn {void test(int a);
}
//?返回值多個參數
@FunctionalInterface
interface MoreParameterNoReturn {void test(int a,int b);
}
//有返回值?參數
@FunctionalInterface
interface NoParameterReturn {int test();
}
//有返回值?個參數
@FunctionalInterface
interface OneParameterReturn {int test(int a);
}
//有返回值多參數
@FunctionalInterface
interface MoreParameterReturn {int test(int a,int b);
}
public class Test {public static void main(String[] args) {//內部類NoParameterNoReturn noParameterNoReturn1 = new NoParameterNoReturn() {@Overridepublic void test() {System.out.println("?返回值?參數1");}};noParameterNoReturn1.test();NoParameterNoReturn noParameterNoReturn =()->System.out.println("?返回值?參數2");noParameterNoReturn.test();//當只有一個參數時,無參數類型,可以不需要()OneParameterNoReturn oneParameterNoReturn = x->{System.out.print("?返回值一個參數:");System.out.println(x);};oneParameterNoReturn.test(10);MoreParameterNoReturn moreParameterNoReturn = (x,y)->{System.out.print("?返回值多個參數:");System.out.println(x+y);};moreParameterNoReturn.test(10,20);//當 Lambda 體不使用 return 語句時,直接返回表達式結果不需要大括號NoParameterReturn noParameterReturn = ()->100;System.out.print("有返回值無參數:");System.out.println(noParameterReturn.test());//當 Lambda 體使用 return 語句時,必須使用大括號 {} 包裹代碼塊OneParameterReturn oneParameterReturn = (int x)->{return x;};System.out.print("有返回值一個參數:");System.out.println(oneParameterReturn.test(200));MoreParameterReturn moreParameterReturn = (x,y)->{System.out.print("有返回值多個參數:");return x+y;};System.out.println(moreParameterReturn.test(300,400));}
}
4.?Lambda 表達式和匿名內部類
特性 | Lambda 表達式 | 匿名內部類 |
---|---|---|
引入版本 | Java 8 | Java 1.1 |
語法簡潔性 | 更簡潔 | 相對冗長 |
適用場景 | 僅適用于函數式接口(單個抽象方法) | 適用于任何接口或抽象類 |
生成類文件 | 不生成額外.class文件 | 生成外部類$數字.class 文件 |
this關鍵字含義 | 指向外部類實例 | 指向內部類自身實例 |
1. 變量捕獲
Lambda 表達式可以捕獲外部作用域的變量,這種特性稱為"變量捕獲"(Variable Capture)。這是 Lambda 表達式強大功能之一,但也需要遵循特定規則。
1. 局部變量捕獲
Lambda 可以捕獲方法中的局部變量,但有嚴格限制:
-
被捕獲的局部變量必須是?final 或 effectively final(即初始化后不再修改)
-
原因:Lambda 可能在原始變量生命周期結束后執行,Java 需要保證值的一致性
2. 實例變量捕獲
Lambda 可以自由捕獲所在類的實例變量:
-
可以讀取
-
可以修改
-
不需要是 final
3. 靜態變量捕獲
Lambda 可以自由捕獲靜態變量:
-
可以讀取
-
可以修改
-
不需要是 final
變量類型 | 可讀性 | 可修改性 | final要求 |
---|---|---|---|
局部變量 | 是 | 否 | 必須effectively final |
實例變量 | 是 | 是 | 不需要 |
靜態變量 | 是 | 是 | 不需要 |
interface Student{void fun();
}
public class Test2 {public int a = 10;//實例變量public static int b = 20;//靜態變量public void fuction(){int c = 30;//局部變量Student student = ()->{a = 40;b = 50;//被捕獲的局部變量必須final 或 effectively final(即初始化后不再修改)//c = 60;//err System.out.println(a);//40System.out.println(b);//50System.out.println(c);//30};student.fun();}public static void main(String[] args) {Test2 test2 = new Test2();test2.fuction();}
}
5.?Lambda在集合當中的使?
