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在集合当中的使⽤
