• java之反射


    目录

    反射

    反射与正常创建对象的区别

    反射功能

    反射的优缺点

    Class类

    前言

    常用一些方法

    class对象的创建方式

    有那些类型有Class对象

    反射的属性获取

    获得当前类加载器

    获得当前类的父类字节码对象

    获得当前类所实现接口的字节码对象集

    获取对应类名

    获取对应的属性

    获取对应的方法

    获得构造器

    反射的操作 

    关闭程序的权限检测

    通过反射创建对象

    直接创建对象

    通过构造器创建对象

    反射调用方法

    反射获取属性值

    关于反射的性能分析

    反射操作泛型

    前言:

    一些基本类型含义 

    获得方法参数类型 

    获取方法泛型返回值类型

    反射操作注解

    获得类上的注解

    通过反射获得类上所有的注解

    获取指定名字的注解(以Sign注解为例)

    获得类内字段上的注解

    反射获取类方法上的注解

    反射和注解学习使用到的类及注解

    反射

    反射是java被视为动态语言的关键,反射机制允许程序在执行期间借助于反射API获取任何类的内部信息,并且能够直接操纵任意对象的内部属性和方法(精髓:先获取后操纵)

    反射本质:一个对象在创建时会加载该类的信息到方法区,在堆中产生一个该类的字节码对象(一个类只有一个字节码对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子可以看到类的结构,所以我们形象的称之为反射

    反射与正常创建对象的区别

    反射功能

    • 在运行时判断任意一个对象所处的类
    • 在运行时构造任意一个类的对象
    • 在运行时判断人一个类所具有的成员变量和方法
    • 在运行时获取泛型信息
    • 在运行时调用任一个对象的成员变量和方法
    • 在运行时处理注解

    反射的优缺点

    • 优点:可以实现动态创建对象和编译,体现很大的灵活性
    • 缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求,这类操作总是慢于直接执行相同的操作

    Class类

    前言

    对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪个接口。对于每个类而言,JRE都为其保留一个不变的Class类型对象。一个Class对象包含了特定的某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息

    注意:

    • Class本身也是一个类
    • Class对象只能由系统建立对象
    • 一个加载的类在jvm中只会有一个Class实例
    • 一个Class对象对应的是一个加载到jvm中的一个.class文件
    • 每个类的实例都会记得自己是哪个Class实例所生成
    • 通过Class可以完整得到一个类中的所有被加载的结构
    • Class类是Reflection的根源,针对任何你想动态加载运行的类,唯有先获得相应的Class对象

    常用一些方法

    class对象的创建方式

    1. //通过包名与类名获得
    2. Class aClass = Class.forName("包名.类名");
    3. //通过对象获得
    4. Class aClass = 具体对象.getClass();
    5. //通过类名获得
    6. Class aClass = 类名.class;
    7. //基本内置类型包装类的Type属性
    8. Class aClass = Integer.Type;

    有那些类型有Class对象

    • class:外部类,成员内部类,局部内部类,匿名内部类
    • interface:接口
    • []:数组(只要元素类型与维度一样,那么就是同一个class)
    • enum:枚举
    • annotation:注解
    • primitive type:基本数据类型
    • void:void
    1. Class c1 = Object.class;//类
    2. Class c2 = Comparable.class;//接口
    3. Class c3 = String[].class;//一维数组
    4. Class<int[][]> c4 = int[][].class;//二维数组
    5. Class c5 = Override.class;//注解
    6. Class c6 = ElementType.class;//基本数据类型
    7. Class c7 = Integer.class;//基本数据类型
    8. Class c8 = void.class;//void
    9. Class c9 = Class.class;//Class
    10. 反射的属性获取

      获得当前类加载器

      语法:ClassLoader classloader=字节码对象.getClassLoader()

      1. Class c1 = Class.forName("reflact.Test03");
      2. //获得对应的类加载器
      3. ClassLoader cl = c1.getClassLoader();
      4. //获得当前系统类加载器
      5. ClassLoader cl2 = ClassLoader.getSystemClassLoader();
      6. //获得系统类加载器的加载路径
      7. String property = System.getProperty("java.class.path");

      获得当前类的父类字节码对象

      语法:Class superclass = 字节码对象.getSuperclass();

      1. Class c1 = People.class;
      2. Classsuper People> superclass = c1.getSuperclass();
      3. System.out.println(superclass);

      获得当前类所实现接口的字节码对象集

      语法:Class[] interfaces = 字节码对象.getInterfaces();

      1. Class c1 = People.class;
      2. Class<?>[] interfaces = c1.getInterfaces();
      3. //获得当前类接口的字节码对象
      4. for (Class<?> anInterface : interfaces) {
      5. System.out.println(anInterface);
      6. }

      获取对应类名

      String getName():获得包名.类名

      String getSimpleName():获得类名

      1. Class c1 = People.class;
      2. System.out.println("获得包名和类名"+c1.getName());//包名.类名
      3. System.out.println("获得类的名字"+c1.getSimpleName());//类名

      获取对应的属性

      Field[] getFields():获得字节码对象对应类包括其父类的所有public属性

      Field[] getDeclaredFields():获得字节码对象对应类的所有修饰符的所有属性

      Field getField(String name):获得字节码对象对应类或父类的名字为name的public属性,如果当前类和父类都有该属性,那么就获取当前类的属性

      Field getDeclaredField(String name):获得字节码对象对应类的名字为name的属性

      1. Class c1 = People.class;
      2. Field[] fields = c1.getFields();//只能获取public属性,也包括静态的和父类的
      3. for (Field field : fields) {
      4. System.out.println(field);
      5. }
      6. System.out.println("------------");
      7. Field[] fields1 = c1.getDeclaredFields();//可以获取全部属性,只能获取当前类的
      8. for (Field field : fields1) {
      9. System.out.println(field);
      10. }
      11. System.out.println("------------");
      12. //获取本类及父类public的名叫name的属性
      13. Field name = c1.getField("money");//public java.lang.String reflact.People.name
      14. System.out.println(name);
      15. //暴力获取本类private的名叫age的属性
      16. Field age = c1.getDeclaredField("age");
      17. System.out.println(age);

      获取对应的方法

      Method[] getMethods():获取对应类及对应父类的所有public方法

      Method[] getDeclaredMethods():获取本类的所有方法

      Method getMethod(String name,[类型.class,类型.class]):获得本类或父类对应方法名为name,参数类型为对应类型的public方法

      Method getDeclaredMethod(String name):获取本类的名为name没有参数的方法

      1. Class c1 = People.class;
      2. //获取类及父类的public修饰的所有方法
      3. Method[] methods = c1.getMethods();
      4. for (Method method : methods) {
      5. System.out.println(method);
      6. }
      7. System.out.println("--------------");
      8. //获取本类的所有方法
      9. Method[] methods1 = c1.getDeclaredMethods();
      10. for (Method method : methods1) {
      11. System.out.println(method);
      12. }
      13. System.out.println("--------------");
      14. //获取当前类或父类中的eat中有2个int类型参数的被public修饰的方法
      15. Method eat = c1.getMethod("eat", int.class, int.class);
      16. System.out.println(eat);
      17. //获得本类的无参方法say
      18. Method say = c1.getDeclaredMethod("say");
      19. System.out.println(say);

      获得构造器

      Constructor[] getConstructors():获得本类所有被public修饰的构造器

      Constructor[] getDeclaredConstructors():获得本类的所有构造器

      Constructor getConstructor():获得本类被public修饰的的空参构造器

      Constructor getDeclaredConstructor([类型.class,类型.class]):获得本类对应参数类型的构造器

      注意:普通反射之所以可以获取父类中的资源,主要是因为其在父类中继承了该资源,而构造器是不能被继承的,所以普通反射中不可以获取父类的构造器

      1. Class c1 = People.class;
      2. //获得所有的本类被public修饰的构造方法
      3. Constructor[] constructors = c1.getConstructors();
      4. for (Constructor constructor : constructors) {
      5. System.out.println(constructor);
      6. }
      7. System.out.println("----------------");
      8. //获得本类的所有构造器
      9. Constructor[] constructors1 = c1.getDeclaredConstructors();
      10. for (Constructor constructor : constructors1) {
      11. System.out.println(constructor);
      12. }
      13. System.out.println("----------------");
      14. //获得本类被public修饰的空参构造器
      15. Constructor constructor = c1.getConstructor();
      16. System.out.println(constructor);
      17. //获得本类的被private修饰的有参构造器
      18. Constructor constructor1 = c1.getDeclaredConstructor(String.class, int.class, String.class);
      19. System.out.println(constructor1);

      反射的操作 

      关闭程序的权限检测

      语法:属性.setAccessible(true)——默认为false

      注意:如果要操纵权限的私有属性(或者本身不可达的属性),那么就必须关闭权限检测

      通过反射创建对象

      直接创建对象

      语法:People people = 字节码对象.newInstance();

      注意:此种创建对象方式不能获取本类的被private修饰(其他修饰的可以,但是无参构造器创建方式不可以)的无参构造器所创建的对象

      1. Class c1 = People.class;
      2. People people = c1.newInstance();
      3. System.out.println(people);

      通过构造器创建对象

      语法:

      • People people = 无参构造器.newInstance();
      • People people = 有参构造器.newInstance(对应有参构造器类型的参数值列表);

      注意:如果获取的构造器是私有的,那么你想创建对象需要关闭权限检测

      1. Class c1 = People.class;
      2. //获取本类被public修饰的无参构造
      3. Constructor constructor = c1.getConstructor();
      4. //通过无参构造创建无参对象
      5. People people = constructor.newInstance();
      6. System.out.println(people);
      7. //获取本类的被private修饰的有参构造
      8. Constructor constructor1 = c1.getDeclaredConstructor(String.class, int.class, String.class);
      9. //给与有参构造修改的权限
      10. constructor1.setAccessible(true);
      11. //通过有参构造创建有参对象
      12. People people1 = constructor1.newInstance("nana", 18, "女");
      13. System.out.println(people1);

      反射调用方法

      语法:获取到的方法.invoke(方法所在的对象,[方法参数列表])——返回值为null

      注意:在调用私有方法前需要关闭权限检测

      1. Class c1 = People.class;
      2. //因为我有非private无参构造
      3. People people = c1.newInstance();
      4. System.out.println(people);
      5. //普通方法调用
      6. people.eat(2,3);
      7. Method eat = c1.getMethod("eat", int.class, int.class);
      8. //不用设置权限
      9. eat.invoke(people,1,1);
      10. //获取私有方法
      11. Method say = c1.getDeclaredMethod("say");
      12. say.setAccessible(true);
      13. //调用私有方法,因为该方法为无参方法,如果有参,后面需加参数列表
      14. say.invoke(people,null);

      反射获取属性值

      获取属性值:Object o = 获取到的属性.get(属性所在的对象);

      设置属性值:获取到的属性.set(属性所在的对象, 你要设置的属性值);

      注意:在获取或设置属性时,需要关闭权限检测

      1. Class c1 = People.class;
      2. People people = c1.newInstance();
      3. //直接获取非私有属性
      4. System.out.println(people.name);
      5. //设置非私有属性
      6. people.name="lan";
      7. System.out.println(people.name);
      8. //获取私有属性对象
      9. Field age = c1.getDeclaredField("age");
      10. //关闭权限检测
      11. age.setAccessible(true);
      12. Object o = age.get(people);
      13. System.out.println(o);
      14. //为私有属性age赋值
      15. age.set(people, 23);
      16. System.out.println(age.get(people));

      关于反射的性能分析

      总结:普通方法执行>关闭权限检测执行>不关闭权限检测执行

      反射操作泛型

      前言:

      • java采用泛型擦除的机制来引入泛型,java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是一旦编译完成,所有和泛型有关的类型全部擦除
      • 为了通过反射操作这些类型,java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型,但又和原始类型齐名的类型

      一些基本类型含义 

      • ParameterizedType:表示一种参数化类型,比如Collection
      • GenericArrayType:表示一种元素化类型是参数化类型或者类型变量的数组类型
      • TypeVariable:是各种类型变量的公共父接口
      • WildcardType:代表一种通配符类型表达式

      获得方法参数类型 

      获取方法所有的参数类型及泛型信息

      语法:Type[] types = 获得到的方法.getGenericParameterTypes();

      获取泛型中的真实类型

      语法:Type[] at = 参数化类型泛型.getActualTypeArguments();

      1. public class Test05 {
      2. private void test1(Map<String,People> map, List list){
      3. System.out.println("test1");
      4. }
      5. public static void main(String[] args) throws NoSuchMethodException {
      6. Class<Test05> c = Test05.class;
      7. //获取方法,因为本类
      8. Method test1 = c.getDeclaredMethod("test1", Map.class, List.class);
      9. //通过方法来获得参数类型集
      10. Type[] types = test1.getGenericParameterTypes();
      11. for (Type type : types) {
      12. System.out.println(type);//java.util.Map,java.util.List
      13. //判断泛型的类型是否属于参数化类型
      14. if (type instanceof ParameterizedType){
      15. //获得他们真实类型
      16. Type[] at = ((ParameterizedType) type).getActualTypeArguments();
      17. for (Type type1 : at) {
      18. System.out.println(type1);
      19. }
      20. }
      21. }
      22. }
      23. }

      获取方法泛型返回值类型

      语法:Type returnType = 获得的方法.getGenericReturnType();

      1. public class Test05 {
      2. public Map<String,People> test2(){
      3. System.out.println("test2");
      4. return null;
      5. }
      6. public static void main(String[] args) throws NoSuchMethodException {
      7. Class<Test05> c = Test05.class;
      8. //获取方法,因为本类
      9. Method test2 = c.getMethod("test2");
      10. //获得泛型返回值类型信息
      11. Type returnType = test2.getGenericReturnType();
      12. //是否返回值类型属于参数化类型
      13. if (returnType instanceof ParameterizedType){
      14. //获取参数化类型的真实类型
      15. Type[] at = ((ParameterizedType) returnType).getActualTypeArguments();
      16. for (Type type : at) {
      17. System.out.println(type);
      18. }
      19. }
      20. }
      21. }

      反射操作注解

      获得类上的注解

      通过反射获得类上所有的注解

      语法:Annotation[] annotations = 字节码对象.getAnnotations();

      获取指定名字的注解(以Sign注解为例)

      语法:Sign annotation = 字节码对象.getAnnotation(Sign.class);

      获取指定注解的值:String value = 获得到的注解.注解属性名();

      1. Class c = Class.forName("reflact.Childern");
      2. //获得该类上的所有注解
      3. Annotation[] annotations = c.getAnnotations();
      4. for (Annotation annotation : annotations) {
      5. System.out.println(annotation);
      6. }
      7. //获得该类上的指定注解
      8. Sign annotation = c.getAnnotation(Sign.class);
      9. //获取注解的值
      10. String value = annotation.value();
      11. System.out.println(value);

      获得类内字段上的注解

      以FieldSign为例——字段上的特定注解名

      语法:FieldSign annotation = 获得到的字段.getAnnotation(FieldSign.class);

      1. Class c = Class.forName("reflact.Childern");
      2. //获取字段属性
      3. Field name = c.getDeclaredField("name");
      4. //在字段中获取注解
      5. FieldSign annotation = name.getAnnotation(FieldSign.class);
      6. //以下三个为我在注解内定义的3个属性
      7. System.out.println(annotation.column());
      8. System.out.println(annotation.length());
      9. System.out.println(annotation.type());

      反射获取类方法上的注解

      以MethodSign为例——方法上的特定注解名

      语法:MethodSign annotation = 获得到的方法.getAnnotation(MethodSign.class);

      1. Class c = Class.forName("reflact.Childern");
      2. //获取方法
      3. Method eat = c.getDeclaredMethod("eat");
      4. //获得方法上的注解
      5. MethodSign annotation = eat.getAnnotation(MethodSign.class);
      6. //通过注解获取里面的值
      7. String spot = annotation.spot();
      8. System.out.println(spot);

      反射和注解学习使用到的类及注解

      1. @Sign("db_Children")
      2. class Childern{
      3. @FieldSign(column = "db_id",type = "int",length = 10)
      4. private int id;
      5. @FieldSign(column = "db_age",type = "int",length = 10)
      6. private int age;
      7. @FieldSign(column = "db_name",type = "varchar",length = 3)
      8. private String name;
      9. @MethodSign(spot = "db_spot")
      10. private void eat(){
      11. System.out.println("干饭");
      12. }
      13. }
      14. @Target(ElementType.TYPE)
      15. @Retention(RetentionPolicy.RUNTIME)
      16. @interface Sign{
      17. String value();
      18. }
      19. @Target(ElementType.FIELD)
      20. @Retention(RetentionPolicy.RUNTIME)
      21. @interface FieldSign{
      22. String column();
      23. String type();
      24. int length();
      25. }
      26. @Target(ElementType.METHOD)
      27. @Retention(RetentionPolicy.RUNTIME)
      28. @interface MethodSign{
      29. String spot();
      30. }

    11. 相关阅读:
      微信小程序学习
      flink-cdc实时增量同步mysql数据到hbase
      StarkNet 性能路线图
      salesforce零基础学习(一百二十)快去迁移你的代码中的 Alert / Confirm 以及 Prompt吧
      Asp-Net-Core开发笔记:使用alpine镜像并加入健康检查
      动态IP与静态IP的区别,你选对了吗?
      架构成长之路 | 图解分布式共识算法Paxos议会协议
      IDEA连接hadoop hdfs
      11.6区间估计、置信区间
      事件监听-@TransactionalEventListener与@EventListener的介绍、区别和使用
    12. 原文地址:https://blog.csdn.net/m0_60027772/article/details/126181614