02-反射

反射

一、反射的机制概念

(1)图示

(2)优点和缺点

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

(3)反射的主要API

(4)反射获取类的Class对象

  • 1、创建一个实体类

    //创建实体类:pojo,entity
    class User{...}
    
  • 2、通过反射获取类的Class对象

    public class Test1 {
        public static void main(String[] args) throws ClassNotFoundException {
            //通过反射获取类的Class对象
            //Class.forName("类的路径")
            Class c1=Class.forName("Reflection.User");
            System.out.println(c1);
        }
    }
    
  • 3、一个类只有一个Class对象

    public class Test1 {
        public static void main(String[] args) throws ClassNotFoundException {
            Class c2=Class.forName("Reflection.User");
            Class c3=Class.forName("Reflection.User");
            //一个类在内存中只有一个Class对象
            //一个类被加载后,类的整个结构都会被封装在class对象中
            //因此c2和c3的哈希值一样
            System.out.println(c2.hashCode());
            System.out.println(c3.hashCode());
        }
    }
    

二、Class类

(1)Class类概述

  • 1、Class本身也是一个类
  • 2、Class对象只能由系统建立对象
  • 3、一个加载的类在JVM中只会有一个Class实例【无论创建多少个,他们的哈希值都是一样的】
  • 4、针对任何你想动态加载、运行的类,唯有先获得相应的Class对象才行

(2)Class类的常用方法

(3)获取Class类的实例

  • 1、创建目标类的一个对象

    public class Test2 {
        public static void main(String[] args) {
            //创建一个Student对象
            Person person=new Student();
            System.out.println("24岁,是"+person.name);
        }
    }
    
    class Person{...}
    class Student extends Person{...}
    
  • 2、通过对象获得

    Class c1=person.getClass();
    System.out.println(c1.hashCode());
    
  • 3、通过forName获得

    Class c2=Class.forName("Reflection.Student");
    
  • 4、通过类名获得

    Class c3=Student.class;
    
  • 5、基本内置类型的包装类都有一个Type属性

    Class c4=Integer.class;
    System.out.println(c4);
    /**
    * 输出结果
    * int
    */
    
  • 6、获得父类类型

    Class c5=c1.getSuperclass();
    System.out.println(c5);
    

(4)哪些类型可以有Class对象

  • 1、图示

  • 2、示例代码

    public class Test3 {
        public static void main(String[] args) {
            Class c1=Object.class;//类
            Class c2= Comparable.class;//接口
            Class c3=String[].class;//一维数组
            Class c4=int[][].class;//二维数组
            Class c5=Override.class;//注解
            Class c6=Element.class;//枚举
            Class c7=Integer.class;//基本数据类型
            Class c8=void.class;//void
            Class c9=Class.class;//class
        }
    }
    

(5)类的加载

  • 1、加载到内存,会产生一个类对应的Class对象
  • 2、链接,链接结束后,为static修饰的变量分配内存
  • 3、初始化:< clinit>(){.......},里面放着所有类变量的赋值动作以及静态代码块内的语句

(6)类什么时候初始化

  • 1、主动引用(会发生类的初始化)

    • ①图示

  • ②new一个对象

    package Reflection;
    
    public class Test4 {
        static {
            System.out.println("main函数被加载");
        }
        public static void main(String[] args) {
            //1、new一个类的对象
            Son son=new Son();
        }
    }
    
    class Father{
        static int b=2;
        static {
            System.out.println("父类被加载");
        }
    }
    class Son extends Father{
        static {
            System.out.println("子类被加载");
            m=300;
        }
        static int m=100;
        static final int M=1;
    }
    
    /**
     * main函数被加载
     * 父类被加载
     * 子类被加载
     */
    
  • ③反射

    //反射也会产生主动引用
    Class.forName("Reflection.Son");
    
  • 2、被动引用(不会发生类的初始化)

    • ①图示

  • ②调用父类的静态变量

    //子类调用父类的静态变量,子类不会被加载
    System.out.println(Son.b);
    
    /**
     * main函数被加载
     * 父类被加载
     * 2
     */
    
  • ③定义该类的数组

    //子类和父类都不会加载
    Son[] array=new Son[5];
    
    /**
     * main函数被加载
     */
    
  • ④引用常量

    System.out.println(Son.M);
    
    /**
     * main函数被加载
     */
    

(7)类加载器(加载阶段)

  • (1)作用:类加载器作用是用来把类(class)装载进内存的。

  • (2)类加载器分类

    • 1、引导类加载器:JVM自带的,负责Java平台核心库(该加载器无法直接获取)
    • 2、扩展类加载器:负责jre/lib/ext目录下的jar包装入工作库
    • 3、系统类加载器:负责java -classpath目录下的类与jar包装入工作,最常用的加载器
  • (3)类加载器关系图(系统类->扩展类->引导类)

  • (4)示例代码

    public class Test5 {
        public static void main(String[] args) throws ClassNotFoundException {
            //获取系统类加载器
            ClassLoader c1=ClassLoader.getSystemClassLoader();
    
            //获取系统类加载器的父类加载器-->扩展类加载器
            ClassLoader c2=c1.getParent();
    
            //获取扩展类加载器的父类加载器-->根加载器(因为无法获取,所以为null)
            ClassLoader c3=c2.getParent();
    
            //测试当前类是哪个加载器加载的(系统类加载器)
            ClassLoader c4=Class.forName("Reflection.Test5").getClassLoader();
    
            //测试JDK内置类是谁加载的(根加载器)
            ClassLoader c5=Class.forName("java.lang.Object").getClassLoader();
        }
    }
    

三、创建运行时类的对象

### (1)获取运行时类的完整结构
  • 0、创建User类的Class对象

    Class c1=Class.forName("Reflection.User");
    
  • 1、getName和getSimpleName【获得类名】

    //获得类的名字
    System.out.println(c1.getName());//获得包名+类名
    System.out.println(c1.getSimpleName());//获得类名
    
  • 2、getFields和getDeclaredFields【获得属性】

    //获得类的属性
    Field[] fields=c1.getFields();//只能找到public的属性
    
    fields=c1.getDeclaredFields();//找到全部的属性
    for (Field field : fields) {
        System.out.println(field);
    }
    
  • 3、getDeclaredField【获得指定属性】

    //获取指定属性的值
    Field name=c1.getDeclaredField("name");
    System.out.println(name);
    
  • 4、getMethods和getDeclaredMethods【获得方法】

    //获取类的方法
    Method[] methods=c1.getMethods();//获得本类及其父类的public方法
    for (Method method : methods) {
        System.out.println(method);
    }
    
    methods=c1.getDeclaredMethods();//获得本类的所有方法
    for (Method method : methods) {
        System.out.println(method);
    }
    
  • 5、getDeclaredMethod【获得指定方法】

    //获得指定方法
    Method getName=c1.getDeclaredMethod("getName",null);
    Method setName=c1.getDeclaredMethod("setName", String.class);
    System.out.println(getName);
    System.out.println(setName);
    
  • 6、getConstructors和getDeclaredConstructors【获得构造器】

    //获得类的构造器
    Constructor[] constructors=c1.getConstructors();//获得public的构造器
    System.out.println(constructors);
    constructors=c1.getDeclaredConstructors();//获得全部的构造器
    System.out.println(constructors);
    
  • 7、getDeclaredConstructor【获得指定构造器】

    //获得指定的构造器
    Constructor declaredConstructor=c1.getDeclaredConstructor(String.class,int.class,int.class);
    System.out.println(declaredConstructor);
    

四、通过反射实例化对象

(1)通过newInstance()方法创建对象

  • 1、该类的条件

    • ①类必须有一个无参的构造器(否则会报错)
    • ②类的构造器的访问权限需要足够
  • 2、示例代码

    public class Test7 {
        public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            //获得Class对象
            Class c1=Class.forName("Reflection.User");
    
                //构造一个对象
                User user=(User) c1.newInstance();//本质是调用了无参构造
                System.out.println(user);
        }
    }
    

(2)通过构造器创建对象

  • 1、示例代码

    public class Test7 {
        public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
            //获得Class对象
            Class c1=Class.forName("Reflection.User");
    
            //通过构造器创建对象
            Constructor constructor=c1.getDeclaredConstructor(String.class,int.class,int.class);
            User user=(User) constructor.newInstance("我修院",114514,24);
            System.out.println(user);
        }
    }
    

(3)扩展一:通过反射调用普通方法

  • 1、示例代码

    //如何通过反射调用普通方法
    
    //1.构造一个实例化对象user
    User user=(User) c1.newInstance();
    //2.通过反射获取目标方法
    Method setName=c1.getDeclaredMethod("setName", String.class);
    //3.通过【目标方法.invoke(实例化对象,"方法的值")】来激活目标方法
    setName.invoke(user,"我修院");
    
    Method getName=c1.getDeclaredMethod("getName",null);
    System.out.println(getName.invoke(user,null));
    

(4)扩展二:通过反射操作属性

  • 1、示例代码

    //通过反射操作属性
    User user2=(User)c1.newInstance();
    Field name=c1.getDeclaredField("name");
    
    
    name.setAccessible(true);//name为私有属性,用setAccessible(true)关闭程序的安全检测
    name.set(user2,"淳平");
    System.out.println(user2.getName());
    

(5)setAccessible()方法

  • 1、不能直接操作私有属性和私有方法,我们需要通过setAccessible(true)来关闭程序的安全检测
  • 2、setAccessible()为true时可以提高反射的效率,也可以使私有成员可以访问

五、反射操作泛型【了解】

https://blog.csdn.net/qq_45017999/article/details/106047261

六、反射操作注解【重点】

(1)编写注解阶段

  • 1、类名的注解

    //类名的注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface tableDJ{
        String value();
    }
    
  • 2、属性的注解

    //属性的注解
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface fieldDJ{
        String columnName();//表名
        String type();//类型
        int length();//长度
    }
    

(2)目标类编写

  • 1、原始状态

    class Student2{
        private int id;
        private int age;
        private String name;
    
        public Student2() {
        }
    
        public Student2(int id, int age, String name) {
            this.id = id;
            this.age = age;
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Worker{" +
                    "id=" + id +
                    ", age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
  • 2、加入类注解

    @tableDJ("db_student")
    class Student2{...}
    
  • 3、加入属性注解

    @fieldDJ(columnName ="db_id",type = "int",length = 10)
    private int id;
    @fieldDJ(columnName ="db_age",type = "int",length = 10)
    private int age;
    @fieldDJ(columnName ="db_name",type = "String",length = 3)
    private String name;
    

(3)通过反射操作注解

  • 1、获取注解

    public class Test10 {
        public static void main(String[] args) throws Exception {
            //创建Class对象
            Class c1=Class.forName("Reflection.Student2");
            
            //通过反射获取注解
            Annotation[] annotations=c1.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
        }
    }
    
  • 2、获得注解的value的值

    //获得注解的value的值
    //1.获取指定的注解
    tableDJ a1=(tableDJ)c1.getAnnotation(tableDJ.class);
    //2.用value()获得该注解的value值
    String v1=a1.value();
    System.out.println(v1);
    
  • 3、获得指定属性的注解

    //获得类指定属性的注解
    //1.获得该属性
    Field f=c1.getDeclaredField("id");
    //获得该属性的注解
    fieldDJ annotation=f.getAnnotation(fieldDJ.class);
    //逐个打印注解值
    System.out.println(annotation.columnName());
    System.out.println(annotation.length());
    System.out.println(annotation.type());
    
posted @ 2023-02-25 17:37  RAUw7  阅读(4)  评论(0)    收藏  举报