网站快速收录平台,网站制作窍门,免费建站,如何写网站优化目标1. 反射 Java的反射#xff08;reflection#xff09;机制是在运⾏时检查、访问和修改类、接⼝、字段和⽅法的机制#xff1b;这种动态获取信息以及动态调⽤对象⽅法的功能称为java语⾔的反射#xff08;reflection#xff09;机制。 用途 1. 框架开发 2. 注解处理 3.…1. 反射 Java的反射reflection机制是在运⾏时检查、访问和修改类、接⼝、字段和⽅法的机制这种动态获取信息以及动态调⽤对象⽅法的功能称为java语⾔的反射reflection机制。 用途 1. 框架开发 2. 注解处理 3. 动态代理 4. 配置⽂件解析 5. 等等 1. 反射相关的类 2. Class类(反射机制的起源 ) 类 |API 参考 |Android 开发人员 代表类的实体在运⾏的Java应⽤程序中表⽰类和接⼝ 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);}
} 1. 使⽤ Class.forName(类的全路径名); 静态⽅法。 前提已明确类的全路径名。 2. 使⽤ .class ⽅法。 说明仅适合在编译前就已经明确要操作的 Class 3. 使⽤类对象的 getClass() ⽅法; 已经加载的类的对象实例 //获得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()类名.classClass?直接获取类的 Class 对象String.classclazz.getName()String获取全限定类名如 java.lang.StringString.class.getName()clazz.getSimpleName()String获取简单类名如 StringString.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. 背景及定义 枚举是在JDK1.5以后引⼊的。主要⽤途是将⼀组常量组织起来在这之前表⽰⼀组常量通常使⽤定义常量的⽅式 public static final int RED 1;
public static final int GREEN 2;
public static final int WHITE 3; 但是常量举例有不好的地⽅例如可能碰巧有个数字1但是他有可能误会为是RED现在我们可以直接⽤枚举来进⾏组织这样⼀来就拥有了类型枚举类型。⽽不是普通的整形1 优点将常量组织起来统⼀进⾏管理 场景错误状态码消息类型颜⾊的划分状态机等等.... 本质是 java.lang.Enum 的⼦类也就是说⾃⼰写的枚举类就算没有显⽰的继承 Enum 但 是其默认继承了这个类。 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(w1w2);//同一个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. 背景 Lambda表达式是Java SE 8中⼀个重要的新特性。lambda表达式允许你通过表达式来代替功能接⼝。 lambda表达式就和⽅法⼀样,它提供了⼀个正常的参数列表和⼀个使⽤这些参数的主体(body,可以是⼀个表达式或⼀个代码块)。 Lambda 表达式Lambda expression基于数学中的λ演算得名也可称为闭包Closure 。 2. Lambda表达式的语法 (parameters) - expression 或 (parameters) -{ statements; } Lambda表达式由三部分组成 1. paramaters类似⽅法中的形参列表这⾥的参数是函数式接⼝⾥的参数可以包含零个或多个 。这⾥的参数类型可以明确的声明也可不声明⽽由JVM隐含的推断。另外当只有⼀个参数且无参数类型时可以省略掉圆括号。 2. -可理解为“被⽤于”的意思将参数与方法体分开 3. ⽅法体可以是单个表达式或代码块是函数式接⼝⾥⽅法的实现。代码块可返回⼀个值或者什么都不返回这⾥的代码块等同于⽅法的⽅法体。如果是表达式也可以返回⼀个值或者什么都不返回。单个表达式或不用return关键字 直接返回表达式结果可以省略大括号{}。 3. 函数式接⼝ ⼀个接⼝有且只有⼀个抽象⽅法 。 注意 1. 如果⼀个接⼝只有⼀个抽象⽅法那么该接⼝就是⼀个函数式接⼝ 2. 如果我们在某个接⼝上声明了 FunctionalInterface 注解那么编译器就会按照函数式接⼝的定义来要求该接⼝这样如果有两个抽象⽅法程序编译就会报错的。所以从某种意义上来说只要你保证你的接⼝中只有⼀个抽象⽅法你可以不加这个注解。加上就会⾃动进⾏检测的。 //⽆返回值⽆参数
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(xy);};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 xy;};System.out.println(moreParameterReturn.test(300,400));}
} 4. Lambda 表达式和匿名内部类 特性Lambda 表达式匿名内部类引入版本Java 8Java 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在集合当中的使⽤ 注意Collection的 forEach()⽅法是从接⼝ java.lang.Iterable 拿过来的。