Java反射

1.反射

        关于反射需要知道什么叫反射,如何 获取 / 使用 反射类对象。

        反射就是一个Java程序从生成的字节码文件中获取到这个字节码文件中的 类 以及类的属性、方法、构造方法等抽取出来 放入到 一个Class类对象中,从而实现突破 java 的封装特性,利用字节码文件获取到封装好的类 的使用权限,这种将 封装类中成员抽取为 一个类对象的过程就叫做反射。   

        反射是框架的灵魂,提高开发效率。

        框架:别人设计好的一个半成品,其他人拿到后填入自己的代码逻辑就可快速运行程序,达到快速开发的目的。

2.获取反射类对象(Class)

        任意一个字节码文件都会通过 ClassLoader类加载器,加载到JVM内存中,并以Class类对象的形式存在。

        注意:一个类的字节码只会被加载一次,多次加载形成的类对象是同一个。

获取 Class类对象的方式有三种:

  1. Class.forName("类路径")
  2. 类名.class
  3. 对象.getClass();

以 Student类为例子:

创建一个需要拿到的类 Student

package com.zjw.反射;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    
    private String name;
    private Integer age;
}

 用三种方式分别获取到 Student类的反射类对象,打印反射类对象,输出结果都为:

com.zjw.反射.Student

由此可见一个字节码只会被加载一次,不同方式获取的Class类对象都是一个。

        1. Class.forName("类路径")

package com.zjw.反射;

public class Test {
    
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        
        //1.根据 Student 类路径获取 Student 类 反射类对象
        Class<Student> aClass = (Class<Student>) Class.forName("com.zjw.反射.Student");
        System.out.println("1.aClass" + aClass);//com.zjw.反射.Student
    }
}

        2. 类名.class

        //2.根据类名获取 Student 反射类对象
        Class<Student> studentClass = Student.class;
        System.out.println("2.studentClass" + studentClass);//com.zjw.反射.Student

        3. 对象.getClass();

        //3.通过类对象获取类的反射类对象
        Student student = new Student();
        Class<? extends Student> aClass1 = student.getClass();
        System.out.println("3.aClass1" + aClass1);//com.zjw.反射.Student

 

3.通过反射类获取类对象

        获取反射类对应的类对象需要用到 Class类的 newInstance(); 方法,得到Class类对应的类,与 new Student(); 方法得到的 类一样。

        //使用第一种方法获取反射类对象 aClass
        Class<Student> aClass = (Class<Student>) Class.forName("com.zjw.反射.Student");

        //使用 反射类.newInstance(); 
        Student instance = aClass.newInstance();  //相当于 new Student();
        Student instance1 = aClass.newInstance(); //new两个对象 地址不同

        System.out.println("两个对象地址判断=" + (instance==instance1)); 
        //两个对象地址判断=false

         扩展:spring 的核心是 IOC控制反转,其中spring.xml 配置文件中<bean class="com.aaa.service.UserService"/> 就是通过类路径得到该类的反射类,并通过newInstance()方法创建对象。

        mybatis中的  sqlSession.getMapper(UserDao.class); 同理,得到UserDao.class反射类,mybatis创建 UserDao的接口的类对象。

4.获取反射类中的属性成员对象

        字节码中的类属性成员被ClassLoader加载到内存中是以 Field类对象的形式存在。

        4.1 获取属性成员对象

        获取属性成员对象常用的有 4个方法:

  1. 反射类.getDeclaredFields();  //获取本类中的所有属性成员对象
  2. 反射类.getDeclaredField("属性名");  //获取本类中指定名字的属性成员对象
  3. 反射类.getFields();  //获取本类以及父类中 public属性对象
  4. 反射类.getField("属性名");  //获取本类以及父类中指定的 public属性对象

创建父类 People:

@Data
public class People {
    public String address;
    private Integer phone;

    public People() {
        System.out.println("父类无参构造器");
    }
    public People(String address, Integer phone) {
        this.address = address;
        this.phone = phone;
    }
    //方法
    public void show(){
        System.out.println("父类方法");
    }
}

 创建子类 Doctor:

@Data
public class Doctor extends People{
    private String name;
    @MyAnnotation("18")
    private Integer age;
    public Integer IDCard;
    String sex;
    public Doctor(){
        System.out.println("子类无参构造器");
    }
    public Doctor(String name) {
        System.out.println("有参构造器--" + name);
        this.name = name;
    }
    private Doctor(Integer age) {
        System.out.println("有参构造器--" + age);
        this.age = age;
    }
    public Doctor(String name, Integer age, Integer IDCard, String sex) {
        this.name = name;
        this.age = age;
        this.IDCard = IDCard;
        this.sex = sex;
    }
    //方法
    public void fun01(){
        System.out.println("方法01");
    }
    public void fun02(String name){
        System.out.println("方法02" + name);
    }
    private void fun03(String name ,Integer age){
        System.out.println("方法03" + name + "--" + age );
    }
}

 使用反射类拿到对应的属性成员对象:

public class DoctorTest {
    public static void main(String[] args) throws NoSuchFieldException {
        /*
            通过反射类拿到对应类的属性
            getDeclaredFields(); 拿到本类所有属性
            getFields(); 拿到本类与父类所有public属性
         */
        //通过第二种方法拿到 Doctor的反射类对象 doctorClass
        Class<Doctor> doctorClass = Doctor.class;
        
        //通过反射类拿到本类中全部的属性成员对象
        Field[] declaredFields = doctorClass.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println("Doctor DeclaredField-" + field);
        }
        /*  Doctor DeclaredField-private java.lang.String com.zjw.反射.Doctor.name
            Doctor DeclaredField-private java.lang.Integer com.zjw.反射.Doctor.age
            Doctor DeclaredField-public java.lang.Integer com.zjw.反射.Doctor.IDCard
            Doctor DeclaredField-java.lang.String com.zjw.反射.Doctor.sex     */
        
        //拿到本类中指定属性的属性成员对象
        Field age = doctorClass.getDeclaredField("age");
        System.out.println(age);  
        //private java.lang.Integer com.zjw.反射.Doctor.age
        
        //通过反射类拿到本类以及父类中 public修饰的属性成员对象
        Field[] fields = doctorClass.getFields();
        for (Field field : fields) {
            System.out.println("Doctor Field-" + field);
        }
        /*  Doctor Field-public java.lang.Integer com.zjw.反射.Doctor.IDCard
            Doctor Field-public java.lang.String com.zjw.反射.People.address  */
        
        //拿到本类中指定的属性成员对象
        Field idCard = doctorClass.getField("IDCard");
        System.out.println(idCard);
        //public java.lang.Integer com.zjw.反射.Doctor.IDCard
        
        //拿到父类中指定的属性成员对象
        Field address = doctorClass.getField("address");
        System.out.println(address);
        //public java.lang.String com.zjw.反射.People.address
    }
}

        4.2  拿到属性成员对象后的赋值

        复制常用的方法:

        1. set(要赋值类对象,值);

        2 .setAccessible(true): 设置允许访问私有属性

        3. getAnnotation(注解.class):获取属性上的注解对象

public class DoctorTest {
    public static void main(String[] args) throws Exception {

        //通过第二种方法拿到 Doctor的反射类对象 doctorClass
        Class<Doctor> doctorClass = Doctor.class;

        // 通过反射 newInstance 要赋值类对象
        Doctor doctor = doctorClass.newInstance();

        //拿到本类中指定属性的属性成员对象
        Field age = doctorClass.getDeclaredField("age");

        // 为 doctor 类 的 age 赋值  因为 age属性为私有属性所以需要设置允许访问
        age.setAccessible(true); //设置允许访问私有属性
        age.set(doctor,16); // 相当于 doctor.setAge(16);

        System.out.println(doctor);
        //Doctor(name=null, age=16, IDCard=null, sex=null)

        //获取 age属性上的注解
        MyAnnotation annotation = age.getAnnotation(MyAnnotation.class);
        System.out.println(annotation.value());
        //结果:18

        /*
            注意:自定义注解,设置@Retention(value = RetentionPolicy.RUNTIME)
                 要在运行时才能够显示注解内容,否则出现空指针异常
        */
    }
}

         上一节中自定义注解:

@Target(value = {ElementType.TYPE,ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
    String value() default "";
    int age() default 18;
    String[] hobby() default {};
}

5.获取反射类中的方法类对象

        5.1 获取方法类对象

获取方法类对象常用的有 4个方法:

  1. 反射类.getDeclaredMethods();  //获取本类中的所有Method方法对象
  2. 反射类.getDeclaredMethod("方法名",数据类型.class,...);  //获取本类中指定名字和参数的方法对象
  3. 反射类.getMethods();  //获取本类以及父类中所有 public修饰的 方法对象
  4. 反射类.getMethod("方法名",String.class,...);  //获取本类以及父类中指定的 public修饰的方法对象

 用到的父类子类见4.1。

1. 获取本反射类中 所有方法对象

        /*
            反射类获取方法对象
         */
        //用第三种方式,通过对象获取反射类
        Doctor newDoctor = new Doctor();
        Class<? extends Doctor> aClass = newDoctor.getClass();
        
        //通过反射创建对象
        //Doctor insDoctor = aClass.newInstance();

        //获取本反射类中 所有方法对象
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }
        /*  打印结果
            public boolean com.zjw.反射.Doctor.equals(java.lang.Object)
            public java.lang.String com.zjw.反射.Doctor.toString()
            protected boolean com.zjw.反射.Doctor.canEqual(java.lang.Object)
            public int com.zjw.反射.Doctor.hashCode()
            public java.lang.String com.zjw.反射.Doctor.getName()
            public void com.zjw.反射.Doctor.setName(java.lang.String)
            public java.lang.Integer com.zjw.反射.Doctor.getAge()
            public void com.zjw.反射.Doctor.setAge(java.lang.Integer)
            public java.lang.Integer com.zjw.反射.Doctor.getIDCard()
            public void com.zjw.反射.Doctor.setIDCard(java.lang.Integer)
            public java.lang.String com.zjw.反射.Doctor.getSex()
            public void com.zjw.反射.Doctor.setSex(java.lang.String)
            public void com.zjw.反射.Doctor.fun01()
            public void com.zjw.反射.Doctor.fun02()
            private void com.zjw.反射.Doctor.fun03()      */

2. 获取本类中指定的方法对象 无参

        Method fun01 = aClass.getDeclaredMethod("fun01");

当获取有参方法时,如果为多参,参数类型则需要按照方法的形参顺序排序,方法需要区分重载方法。

        //获取本类中指定的 私有方法对象 有参
        Method fun03 = aClass.getDeclaredMethod("fun03", String.class,Integer.class);

3. 获取本类及父类中 public修饰的全部方法对象

4. 获取本类 / 父类中指定的 public修饰的方法

        //获取本类及父类中 public修饰的全部方法对象
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        //结果不仅有本类及父类的 public修饰方法 ,还有 父类的父类 Object中的 public 方法

        //获取本类中指定的 public修饰的方法
        Method fun02 = aClass.getMethod("fun02", String.class);
        //获取父类中的 public修饰的方法
        Method show = aClass.getMethod("show");

        5.2 调用方法类对象

        调用方法对象的方法:

        方法对象.invoke(调用方法的对象 , 对应参数 , ...);  //执行方法

        方法对象.setAccessible(true);  //开启访问权限

        Method fun01 = aClass.getDeclaredMethod("fun01");
        //执行方法
        fun01.invoke(newDoctor); //方法01

        Method fun03 = aClass.getDeclaredMethod("fun03", String.class,Integer.class);
        //执行方法 私有方法需要开启访问权限
        fun03.setAccessible(true);
        fun03.invoke(newDoctor,"周",18);  //方法03周--18

        Method fun02 = aClass.getMethod("fun02", String.class);
        //执行方法
        fun02.invoke(newDoctor,"黄");  //方法02黄

        Method show = aClass.getMethod("show");
        //执行方法
        show.invoke(newDoctor); //父类方法

6.获取反射类中的构造对象

        6.1 获取构造对象

        获取构造对象的常用方法:

  1. 反射类.getDeclaredConstructors();  //获取本类中的所有构造器对象
  2. 反射类.getDeclaredConstructor("属性名");  //获取本类中指定的构造器对象
  3. 反射类.getConstructors();  //获取本类中的 public构造器对象
  4. 反射类.getConstructor("属性名");  //获取本类指定的 public构造器对象
        //通过第一种方法拿到 Doctor的反射类对象 doctorClass
        Class<Doctor> forNameClass = (Class<Doctor>) Class.forName("com.zjw.反射.Doctor");
        //创建对象时自动调用无参构造器 以及父类的无参构造器
        Doctor newedInstance = forNameClass.newInstance();
        //控制台:父类无参构造器
        //       子类无参构造器
        
        //获取本类所有构造器对象
        Constructor<?>[] declaredConstructors = forNameClass.getDeclaredConstructors();
        for (Constructor<?> constructor : declaredConstructors) {
            System.out.println(constructor);
        }
        /*  打印结果:
            public com.zjw.反射.Doctor(java.lang.String,java.lang.Integer,java.lang.Integer,java.lang.String)
            private com.zjw.反射.Doctor(java.lang.Integer)
            public com.zjw.反射.Doctor(java.lang.String)
            public com.zjw.反射.Doctor()    */
        
        //得到无参构造器
        Constructor<Doctor> declaredConstructor = forNameClass.getDeclaredConstructor();
        
        //得到指定有参构造函数
        Constructor<Doctor> constructor1 = forNameClass.getDeclaredConstructor(String.class);
        
        //获取本类所有 public构造器对象
        Constructor<?>[] constructors = forNameClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        /*  打印结果:
            public com.zjw.反射.Doctor(java.lang.String,java.lang.Integer,java.lang.Integer,java.lang.String)
            public com.zjw.反射.Doctor(java.lang.String)
            public com.zjw.反射.Doctor()    */

        6.2 调用构造函数

        调用构造函数即为新建对象,同 new Doctor("小小");

        方法:构造器对象. newInstance(参数值);

        //无参
        Constructor<Doctor> declaredConstructor = forNameClass.getDeclaredConstructor();
        //调用
        Doctor doctor1 = declaredConstructor.newInstance();
        /*  父类无参构造器
            子类无参构造器 */
        //有参
        Constructor<Doctor> constructor1 = forNameClass.getDeclaredConstructor(String.class);
        //调用
        Doctor doctor2 = constructor1.newInstance("小小");
        /*  父类无参构造器
            有参构造器小小 */

获取私有构造器,在调用时也需要设置访问权限 constructor2.setAccessible(true);

        //得到私有构造器
        Constructor<Doctor> constructor2 = forNameClass.getDeclaredConstructor(Integer.class);
        //调用
        constructor2.setAccessible(true);
        Doctor doctor3 = constructor2.newInstance(18);
        /*  父类无参构造器
            有参构造器--18    */
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值