java反射

1.什么是反射?

反射允许对成员变量,成员方法和构造方法的信息进行编程访问

就好像反射是个人,把类中的字段(成员方法),构造方法,成员方法挨个获取出来

比如idea自动提示功能就是用的反射来实现的,创建了一个对象,用对象去调用方法,成员变量,构造方法...等,idea就会利用反射把里面能调用的成员方法等获取出来,进行展示

 像以前创建对象或者调用方法的时候,我们可以按ctrl+p 提示,就是通过反射实现的

 说白了就是反射可以从类中东西,常用的有:成员变量,构造方法,成员方法等所有的信息

比如:

获取                                 解刨

字段(成员变量):           获取修饰符 获取名字 获取数据类型 赋值/获取已经记录的值

构造方法:                     获取修饰符 获取名字 获取形参 还可以利用获取出来的构造方法去创建对象

成员方法:                     获取修饰符 获取名字 获取形参 获取返回值 抛出的异常 获取注解 运行方法

注意:在获取的时候不是从java文件中获取的,而是从Class字节码文件当中获取的,首先要先获取Class字节码文件对象,再从字节码文件去获取字段(成员变量),构造方法,,成员方法

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.


反射它也是框架设计的灵魂。什么是框架: 它是一个别人设计好的一个半成品,在使用时只要填入自己的业务代码。好处: 提高开发效率。

反射: 在运行时,它是把类中成员抽取为其他类对象的过程。

2.获取Class对象的三种方式

  1. Class.forName("全类名");
  2. 类名.Class
  3. 对象.getClass();如果有了这个类对象,用对象去调用getClass,而getClass是定义在Object中的,所有的对象都可以调用这个方法

 有三种方式什么时候用哪一种呢?

第一个阶段:编写java文件

A.java---编译成class文件 A.class,在这个阶段是没有把代码加载到内存当中的,是在硬盘/磁盘里进行的操作 也称为源代码阶段 一般在这个阶段会用第一种方式去获取Class字节码对象

package com.demo.a3_fanShe;

public class Student2 {
    private String name;
    private Integer age;

    public Student2() {
    }

    public Student2(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public Integer getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(Integer age) {
        this.age = age;
    }

    public String toString() {
        return "Student2{name = " + name + ", age = " + age + "}";
    }
}

 而在我们输入包名和类名的时候,如果怕输错,可以直接去类名右键,按着下方操作就能复制全类名

package com.demo.a3_fanShe;

public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException {
   /*
         获取Class对象的三种方式:
         1. Class.forName("全类名");
         2. 类名.Class
         3. 对象.getClass();如果有了这个类对象,用对象去调用getClass,而getClass是定义在Object中的,所有的对象都可以调用这个方法
    * */

        //1.第一种方式
        //全类名: 包名 + 类名  com.demo.a3_fanshe.Student2
        Class clazz1 = Class.forName("com.demo.a3_fanShe.Student2");

        //打印
        System.out.println(clazz);
    }
}

结果输出如下:

"C:\Program Files\Java\jdk-17\bin\java.exe" "-javaagent:E:\ideal\IntelliJ IDEA 2022.3.1\lib\idea_rt.jar=64711:E:\ideal\IntelliJ IDEA 2022.3.1\bin" -Dfile.encoding=UTF-8 -classpath D:\ideacj\java-gaojie\target\classes com.demo.a3_fanShe.Demo2
class com.demo.a3_fanShe.Student2

Process finished with exit code 0

第二个阶段:运行代码.

要把字节码文件A.calss 加载到内存当中,称为加载阶段,一般用第二种方式

package com.demo.a3_fanShe;

public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException {
   /*
         获取Class对象的三种方式:
         1. Class.forName("全类名");
         2. 类名.Class
         3. 对象.getClass();如果有了这个类对象,用对象去调用getClass,而getClass是定义在Object中的,所有的对象都可以调用这个方法
    * */

        //1.第一种方式
        //全类名: 包名 + 类名  com.demo.a3_fanshe.Student2
        Class clazz1 = Class.forName("com.demo.a3_fanShe.Student2");

        //2.第二种方式
        Class clazz2= Student2.class;
        System.out.println(clazz2);
    }
}

打印结果:

"C:\Program Files\Java\jdk-17\bin\java.exe" "-javaagent:E:\ideal\IntelliJ IDEA 2022.3.1\lib\idea_rt.jar=64913:E:\ideal\IntelliJ IDEA 2022.3.1\bin" -Dfile.encoding=UTF-8 -classpath D:\ideacj\java-gaojie\target\classes com.demo.a3_fanShe.Demo2
class com.demo.a3_fanShe.Student2

Process finished with exit code 0

第二种获取的字节码文件和第一种是一样的对象,可以看下面的打印结果

第三个阶段:创建对象

我们可以在这个类的当中去创建对象.比如 A a = new A();此时就叫做运行阶段,一般用第三种方式,

用对象去调用getClass方法也可以获取到字节码对象

package com.demo.a3_fanShe;

public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException {
   /*
         获取Class对象的三种方式:
         1. Class.forName("全类名");
         2. 类名.Class
         3. 对象.getClass();如果有了这个类对象,用对象去调用getClass,而getClass是定义在Object中的,所有的对象都可以调用这个方法
    * */

        //1.第一种方式
        //全类名: 包名 + 类名  com.demo.a3_fanshe.Student2
        Class clazz1 = Class.forName("com.demo.a3_fanShe.Student2");

        //2.第二种方式
        Class clazz2= Student2.class;

        //3.第三种方式
        Student2 s =new Student2();
        Class clazz3 = s.getClass();
        System.out.println(clazz3);
    }
}

结果输出如下:跟上面两个是一样的

"C:\Program Files\Java\jdk-17\bin\java.exe" "-javaagent:E:\ideal\IntelliJ IDEA 2022.3.1\lib\idea_rt.jar=65018:E:\ideal\IntelliJ IDEA 2022.3.1\bin" -Dfile.encoding=UTF-8 -classpath D:\ideacj\java-gaojie\target\classes com.demo.a3_fanShe.Demo2
class com.demo.a3_fanShe.Student2

Process finished with exit code 0

从上面的分析我们知道,任意一个字节码都会通过ClassLoader类加载器,加载到JVM内存中,并以Class类对象的形式存在。注意:一个类的字节码只会被加载一次

那什么时候该用哪种呢?

第一种方式最为常用,第二种一般更多的是当做参数进行传递,第三种当我们已经有了这个类的对象时,才可以使用。

在java中万物皆可为对象

字节码               可以看成 Class 对象

构造方法            Constructor 对象

字段(成员变量)   Field 对象

成员方法             Method对象

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

通过Class类对象,调用newInstance()  为了方便阅读我们放到一个类中

package com.demo.a3_fanShe.demo3;




public class Demo3 {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //用第一种方式 获取Class
        Class<Student> clazz= (Class<Student>) Class.forName("com.demo.a3_fanShe.demo3.Student");

        //通过反射类得到对应的类对象---之前方式:new Student来获取Student类的对象。
        Student student1  = clazz.newInstance();
        Student student2 = clazz.newInstance();
        System.out.println(student1);
        System.out.println(student2);
    }


}
class Student {
    private String name;
    private Integer age;

    public Student() {
    }

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public Integer getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(Integer age) {
        this.age = age;
    }

    public String toString() {
        return "Student3{name = " + name + ", age = " + age + "}";
    }
}

 打印结果如下

"C:\Program Files\Java\jdk-17\bin\java.exe" "-javaagent:E:\ideal\IntelliJ IDEA 2022.3.1\lib\idea_rt.jar=52854:E:\ideal\IntelliJ IDEA 2022.3.1\bin" -Dfile.encoding=UTF-8 -classpath D:\ideacj\java-gaojie\target\classes com.demo.a3_fanShe.demo3.Demo3
Student3{name = null, age = null}
Student3{name = null, age = null}

Process finished with exit code 0

 注意:如果没加泛型,对象就是Object

像这些都有反射

spring---spring.xml====><bean class="com.aaa.service.UserService"/>

--spring根据你传递的类路径----得到该类的反射类---并通过newInstance()创建类对象。

mybatis--=====> sqlSession.getMapper(UserDao.class);---->UserDao.class反射类---->mybatis创建UserDao接口的类对象。

 4.利用反射获取构造方法

Class中用于获取构造方法的方法

Constructor<?>[ ] getConstructors(): 返回所有公共(public)构造方法对象的数组

Constructor<?>[ ] getDeclaredConstructors():返回所有构造方法对象,包括私有

Constructor<T>getConstructor(Class<?>...parameterTypes):返回单个公共(public)构造方法对象

Constructor<T>getDeclaredConstructor(Class<?>...parameterTypes):返回单个构造方法对象,包括私有

Constructor类中用于创建对象的方法

T newInstance(Object...initargs): 根据指定的构造方法创建对象

setAccessible(boolean flag): 设置为true,表示取消访问检查

package com.demo.a3_fanShe.demo4;

public class Student {
    private String name;
    private int age;
    public Student(){

    }
    public Student(String name){
        this.name=name;
    }
    protected Student(int age){
        this.age=age;
    }
    private Student(String name,int age){
        this.name=name;
        this.age=age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

package com.demo.a3_fanShe.demo4;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;

public class Demo4 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        /*

         Class中用于获取构造方法的方法
           Constructor<?>[ ] getConstructors(): 返回所有公共(public)构造方法对象的数组
           Constructor<?>[ ] getDeclaredConstructors():返回所有构造方法对象,包括私有
           Constructor<T>getConstructor(Class<?>...parameterTypes):返回单个公共(public)构造方法对象
           Constructor<T>getDeclaredConstructor(Class<?>...parameterTypes):返回单个构造方法对象,包括私有

         Constructor类中用于创建对象的方法
           T newInstance(Object...initargs): 根据指定的构造方法创建对象
           setAccessible(boolean flag): 设置为true,表示取消访问检查
         */

        //1.获取Class字节码文件对象
        Class<Student> clazz = (Class<Student>) Class.forName("com.demo.a3_fanShe.demo4.Student");



        //2.获取所有公共的构造方法
        Constructor[]  cons= clazz.getConstructors();
        for (Constructor con : cons) {

        }
        //3.获取所有构造方法,包括私有的
        Constructor[] cons2= clazz.getDeclaredConstructors();
        for (Constructor con2 : cons2) {

        }

        //4.获取单个公共的空参构造对象 不加类型.class
        Constructor cons3 = clazz.getDeclaredConstructor();


        //5.获取单个公共的有参构造对象 加上对应的类型.class
        Constructor cons4 = clazz.getDeclaredConstructor(String.class);


        //6.获取受保护的有参构造 加上对应的类型.class
        Constructor cons5 = clazz.getDeclaredConstructor(int.class);

        //7.获取两个参数的 加上对应的类型.class
        Constructor cons6 = clazz.getDeclaredConstructor(String.class, int.class);

        //8.获取权限修饰符
        int modifiers = cons6.getModifiers();


        //9.获取到构造方法所有的参数
        Parameter[] parameters = cons6.getParameters();
        for (Parameter parameter : parameters) {

        }

        //10.也可以创建对象
        //表示临时取消权限的校验,也称为暴力反射
        cons6.setAccessible(true);
        Student student = (Student) cons6.newInstance("小丽", 15);
        System.out.println(student);
    }
}

第一种:获取所有公共的构造方法

第二种:获取所有构造方法,包括私有的...等

 第三种:获取单个公开的空参构造对象,不加类型.class

 第四种:获取公共有参构造 ,获取的类型要和构造参数里的数据类型保持一致

 第五种:获取单个私有的有参构造,注意!这样会报错的,要把getConstuotor前加上Declared,因为不加只能获取公共的,获取不到私有的和受保护的

 

 第六种:获取私有,有参构造,两个参数的 加上对应的类型

 第七种:获取权限修饰符

 这里的public 是1 是底层的运行效率 有关系的 详细可以去看看 原码 补码 反码

 权限修饰符获取到有什么用呢?

底层会获取到权限修饰符,像下面的能获取到 空参的,有参的,但是没有两个参数的,因为是私有的

  第八种:获取到构造方法所有的参数

有什么用呢?

这样就可以图形化看到提示的参数了

 第九种:也可以创建对象,但是要注意,getDeclaredConstructor只是能让你看到私有的构造,但是还不能用,要想用还需要在前面加上setAccessible(true);  称为暴力反射

 

5.获取反射类中的成员变量

Class类中用于获取成员变量的方法

Field[ ] getFields();    返回所有公共(public)成员变量对象的数组,包括继承的

Field[ ] getDeclaredFields(); 返回所有成员变量对象,包括私有,

Field getField(String name); 返回单个公共(public)成员变量对象,包括继承的

Field getDeclaredField(Sting name); 返回单个成员变量对象,包括私有

Field类中用于创建对象的方法

void set(Object obj,Object value ):  赋值

Object get(Object obj)获取值

package com.demo.a3_fanShe.demo5;

public class Student {
    private String name;
    private int age;
    public String gender;

    public Student() {
    }

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return gender
     */
    public String getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + "}";
    }
}
package com.demo.a3_fanShe.demo5;

import java.lang.reflect.Field;

public class Demo5 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        /*

      Class类中用于获取成员变量的方法
         Field[ ] getFields();    返回所有公共(public)成员变量对象的数组,包括继承的
         Field[ ] getDeclaredFields(); 返回所有成员变量对象,包括私有,
         Field getField(String name); 返回单个公共(public)成员变量对象,包括继承的
         Field getDeclaredField(Sting name); 返回单个成员变量对象,包括私有

      Field类中用于创建对象的方法
         void set(Object obj,Object value ):  赋值
         Object get(Object obj)获取值


         */
        //1.获取Class字节码文件的对象
        Class clazz = Class.forName("com.demo.a3_fanShe.demo5.Student");

        //2.获取所有公共的成员变量
        Field[] fields = clazz.getFields();
        for (Field field : fields) {

        }

        //3.获取所有成员变量包括私有
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {

        }

        //4.获取单个的成员变量
        Field gender = clazz.getField("gender");


        //5.获取单个私有的成员变量
        Field name = clazz.getDeclaredField("name");


        //6.获取成员变量的权限修饰符
        int modifiers = name.getModifiers();

        //7.获取成员变量名字
        String n = name.getName();

        //8.获取成员变量的数据类型
        Class<?> type = name.getType();
        System.out.println(type);

        //9.获取成员变量记录得值
        Student s = new Student("张三",18,"日本");
        name.setAccessible(true);
        String value = (String) name.get(s);
        
       //10.修改对象里面记录的值
        name.set(s,"李四");
        System.out.println(s);
    }
}

第一种:获取所有公共的成员变量

第二种:获取所有成员变量包括私有

 第三种:获取单个公共的成员变量

这里需要的参数需要加上成员变量的名字

  第四种:获取单个私有的成员变量

,这里需要的参数需要加上成员变量的名字,注意!这里也需要加上Declared,因为不加只能获取公共的,获取不到私有的和受保护的

 第五种:获取成员变量的修饰符

 第六种:获取成员变量的名字

 第七种:获取成员变量的数据类型

 第八种:获取成员变量记录的值

获取变量的值需要先创建一个对象,但是要注意:因为访问的是私有的,需要暴力反射

打印一个是因为通过一个name成员变量获得的

 第九种:修改对象里面记录的值

第一个参数表示要把里面哪个对象里面的值修改,第二个参数你要修改成什么

6. 获取成员方法对象

Class类中用于获取成员方法的方法

Method[ ]getMethods(); 返回所有公共(public)成员方法对象的数组,包括继承的

Method[ ]getDeclaredMethods(); 返回所有成员方法对象的数组,不包括继承的,包括私有

Method getMethod(Sting name,Class<?>...parameterTypes): 返回单个(public)公共成员方法对象,包括继承的

Method getDeclaredMethod(Sting name,Class<?>...parameterTypes): 返回单个成员方法对象,包括私有

Method类中用于创建对象的方法
Object invoke(Object obj,Object...args): 运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

获取方法的修饰符

获取方法的名字

获取方法的形参

获取方法的返回值

获取方法的抛出的异常

package com.demo.a3_fanShe.demo6;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public void sleep(){
        System.out.println("睡觉");
    }


    private void eat(String something){
        System.out.println("在吃"+ something);
    }
    private void eat(String something,int a){
        System.out.println("在吃"+ something);
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
package com.demo.a3_fanShe.demo6;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class Demo6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        /*
        Class类中用于获取成员方法的方法
            Method[ ]getMethods(); 返回所有公共(public)成员方法对象的数组,包括继承的
            Method[ ]getDeclaredMethods(); 返回所有成员方法对象的数组,不包括继承的,包括私有
            Method getMethod(Sting name,Class<?>...parameterTypes): 返回单个(public)公共成员方法对象,包括继承的
            Method getDeclaredMethod(Sting name,Class<?>...parameterTypes): 返回单个成员方法对象,包括私有

        Method类中用于创建对象的方法
            Object invoke(Object obj,Object...args): 运行方法
            参数一:用obj对象调用该方法
            参数二:调用方法的传递的参数(如果没有就不写)
            返回值:方法的返回值(如果没有就不写)

            获取方法的修饰符
            获取方法的名字
            获取方法的形参
            获取方法的返回值
            获取方法的抛出的异常

         */

        //1. 获取Class字节码文件对象
        Class clazz = Class.forName("com.demo.a3_fanShe.demo6.Student");

        //2.获取里面所有公共的方法对象(包含父类中所有的方法)
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
           /* System.out.println(method);*/
        }

        //3.获取子类/本类中所有的方法(不能获取父类的,但是可以获取本类的私有方法)
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) {

        }

        //4.获取指定的单一方法
        Method eat = clazz.getDeclaredMethod("eat", String.class);

        //5.获取方法的修饰符
        int modifiers = eat.getModifiers();

        //6.获取方法的名字
        String name = eat.getName();

        //7.获取方法的形参(把参数的对象获取到,并放到数组中)
        Parameter[] parameters = eat.getParameters();
        for (Parameter parameter : parameters) {

        }

        //8.获取方法的抛出的异常
        Class[] exceptionTypes = eat.getExceptionTypes();
        for (Class exceptionType : exceptionTypes) {

        }

        //9.方法运行
        /*
          Method类中用于创建对象的方法
            Object invoke(Object obj,Object...args): 运行方法
            参数一:用obj对象调用该方法
            参数二:调用方法的传递的参数(如果没有就不写)
            返回值:方法的返回值(如果没有就不写)
         */

        Student s=new Student();
        eat.setAccessible(true);
        //参数一s:表示方法的调用者
        //参数二:"汉堡包"表示在调用方法的时候传递的实际参数

        String res = (String) eat.invoke(s, "汉堡包");
        System.out.println(res);
    }
}

第一种:获取里面所有公共的方法对象

注意:(包含父类中所有的方法,它的父类是object)

 第二种:获取子类/本类中所有的方法包括私有

 第三种:获取指定的单个的方法

第一个参数表示: 方法的名字  第二个:参数的类型 是为了方法的重载,因为如果有两个

,它也不知道获取的是第一个eat,还是第二个

获取第一个指定的eat

 获取第二个指定的eat

 注意:要获得私有还是要加Declared

 第四种:获取方法的修饰符

 

 第五种:获取方法的名字

 第五种:获取方法的形参(把参数的对象放到一个数组中)

 

 第五种:获取方法抛出的异常

 第六种:方法运行

 还能获取返回值

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值