反射

1.class类的以及class对象的介绍以及反射介绍

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
1.反射概述:解剖class对象的一个技术
2.问题:能解剖class对象的啥呢?
   a.解剖出成员变量 : 赋值
   b.解剖出成员方法: 调用
   c.解剖出构造方法: new对象

3.用反射的好处:让代码变的更通用,更灵活

4.怎么学反射:
   a.将反射看成是一套API来学
   b.通过涛哥的案例,体会反射的好处,用反射写出来的代码有多牛掰
   
5.问题:玩儿反射,最开始的一步是干啥?
   获取Class对象
    
6.class对象:class文件对应的对象
  class类:描述class对象的类叫做class类
1706167635929

2.反射之获取Class对象

1
2
3
4
5
6
7
1.方式1:调用Object中的getClass方法:
  Class <?> getClass()  
2.方式2:不管是基本类型还是引用类型,jvm都为其提供了一个静态成员:class
    
3.方式3:Class类中的静态方法:
   static Class<?> forName(String className)  
                           className:传递的是类的全限定名(包名.类名)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Person {
    private String name;
    private Integer age;

    public Person() {
    }

    //私有构造
    private Person(String name){
        this.name = name;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Demo01GetClass {
    @Test
   public void get1()throws Exception{
        /*
        1.方式1:调用Object中的getClass方法:
                Class <?> getClass()
         */
        Person person = new Person();
        Class<? extends Person> aClass1 = person.getClass();
        System.out.println("aClass1 = " + aClass1);

        System.out.println("==========================");

        //2.方式2:不管是基本类型还是引用类型,jvm都为其提供了一个静态成员:class
        Class<Person> aClass2 = Person.class;
        System.out.println("aClass2 = " + aClass2);

        System.out.println("===========================");

        /*
             static Class<?> forName(String className)
                           className:传递的是类的全限定名(包名.类名)
         */
        Class<?> aClass3 = Class.forName("com.atguigu.c_reflect.Person");
        System.out.println("aClass3 = " + aClass3);

        System.out.println("==========================");

        System.out.println(aClass1==aClass2);
    }
}

运行结果:

image-20250401201616498

写类的全限定名小技巧:

1.如何快速写类的全限定名:

​ a.直接赋值粘贴

1706168686254

b.直接写类名 -> 回车选中

image-20250401201421951

2.如何检测类的全限定名写对了

​ 按ctrl不放,鼠标点击此类名,能跳到对应的类中,就证明写对了

2.1.三种获取Class对象的方式最通用的一种

1
2
3
4
1. 方式3:Class类中的静态方法:
   static Class<?> forName(String className)  
                           className:传递的是类的全限定名(包名.类名) 
2.原因:参数为String形式,可以和properties文件结合使用                               
1
2
# pro.properties文件
className=com.atguigu.c_reflect.Student
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Demo02GetClass {
    public static void main(String[] args)throws Exception {
        Properties properties = new Properties();
        FileInputStream in = new FileInputStream("module25\\pro.properties");
        properties.load(in);

        String className = properties.getProperty("className");
        System.out.println(className);

        Class<?> aClass = Class.forName(className);
        System.out.println("aClass = " + aClass);

    }
}

运行结果:

image-20250401202901154

2.2.开发中最常用的是哪一种

1
1.直接类名.class-> 最方便使用

3.获取Class对象中的构造方法

3.1.获取所有public的构造方法

1
2
1.Class类中的方法:
  Constructor<?>[] getConstructors()  -> 获取所有public的构造
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class Demo03GetConstructor {
    public static void main(String[] args) {
        //获取Class对象
        Class<Person> aClass = Person.class;
        //获取所有public的构造
        Constructor<?>[] constructors = aClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
    }
}

运行结果:(Person类的定义在反射之获取Class对象

image-20250401204623854

3.2.获取空参构造_public

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
1.Class类中的方法:
  Constructor<T> getConstructor(Class<?>... parameterTypes)->获取指定的public的构造
                                parameterTypes:可变参数,可以传递0个或者多个参数
  a.如果获取的是空参构造:参数不用写
  b.如果获取的是有参构造:参数写参数类型的class对象    
      
2.Constructor类中的方法:
  T newInstance(Object...initargs) -> 创建对象
                         initargs:传递的是构造方法的实参
  a.如果根据无参构造new对象,initargs不写了
  b.如果根据有参构造new对象,initargs传递实参                           
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Demo04GetConstructor {
    public static void main(String[] args)throws Exception {
        //获取Class对象
        Class<Person> aClass = Person.class;
        //获取无参构造
        Constructor<Person> constructor1 = aClass.getConstructor();
        //获取有参构造
        Constructor<Person> constructor2 = aClass.getConstructor(String.class, Integer.class);
        System.out.println("constructor1 = " + constructor1);
        System.out.println("constructor2 = " + constructor2);
        /** 输出:
         * constructor1 = public Person()
         * constructor2 = public Person(java.lang.String,java.lang.Integer)
         */
        
        
        /*
          好比是Person person = new Person()
         */
        Person person1 = constructor1.newInstance();
        Person person2 = constructor2.newInstance("张三", 18);

        //好比是直接输出对象名,默认调用toString
        System.out.println(person1);
        System.out.println(person2);
        /** 输出:
         * Person{name='null', age=null}
         * Person{name='张三', age=18}
         */
        
        
        Person person = new Person();
        System.out.println(person.toString());
        /** 输出:
         * Person{name='null', age=null}
         */
    }
}

3.3.利用空参构造创建对象的快捷方式_public

1
2
3
4
5
Class类中的方法:
  T newInstance() -> 根据空参构造创建对象
      
前提:被反射的类中必须有public的空参构造      
在jdk17中过时了
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class Demo05GetConstructor {
    public static void main(String[] args)throws Exception {
        //获取Class对象
        Class<Person> aClass = Person.class;
        /*
          好比是Person person = new Person()
         */
        Person person = aClass.newInstance();

        //好比是直接输出对象名,默认调用toString
        System.out.println(person);
    }
}

3.4.利用反射获取有参构造并创建对象_public

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
1.Class类中的方法:
  Constructor<T> getConstructor(Class<?>... parameterTypes)->获取指定的public的构造
                                parameterTypes:可变参数,可以传递0个或者多个参数
  a.如果获取的是空参构造:参数不用写
  b.如果获取的是有参构造:参数写参数类型的class对象    
      
2.Constructor类中的方法:
  T newInstance(Object...initargs) -> 创建对象
                         initargs:传递的是构造方法的实参
  a.如果根据无参构造new对象,initargs不写了
  b.如果根据有参构造new对象,initargs传递实参    
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Demo06GetConstructor {
    public static void main(String[] args)throws Exception {
        //获取Class对象
        Class<Person> aClass = Person.class;

        Constructor<Person> constructor = aClass.getConstructor(String.class, Integer.class);
        System.out.println("constructor = " + constructor);

        //创建对象-> 好比是Person person = new Person("三上",26)
        Person person = constructor.newInstance("三上", 26);

        //好比是直接输出Person对象,直接调用toString
        System.out.println(person);
    }
}

运行结果:

image-20250401212641489

3.5.利用反射获取私有构造(暴力反射)

1
2
3
4
5
6
7
8
1.Constructor<?>[] getDeclaredConstructors()获取所有构造方法,包括private  
2.Constructor<T> getDeclaredConstructor(<?>... parameterTypes)  -> 获取指定构造,包括private   
  parameterTypes:参数类型的class对象  
      
      
3.Constructor有一个父类叫做AccessibleObject ,里面有一个方法
  void setAccessible(boolean flag)  -> 修改访问权限
                     flag为true:解除私有权限
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Demo07GetConstructor {
    public static void main(String[] args) {
        Class<Person> aClass = Person.class;
        Constructor<?>[] dc = aClass.getDeclaredConstructors();
        for (Constructor<?> constructor : dc) {
            System.out.println(constructor);
        }
        /** 输出:
         * public Person(java.lang.String,java.lang.Integer)
         * private Person(java.lang.String)
         * public Person()
         */
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Demo08GetConstructor {
    public static void main(String[] args)throws Exception {
        Class<Person> aClass = Person.class;
        Constructor<Person> dc = aClass.getDeclaredConstructor(String.class);
        dc.setAccessible(true);//解除私有权限->暴力反射
        
        Person person = dc.newInstance("三上");
        System.out.println(person);
	
        // 输出:Person{name='三上', age=null}
    }
}

4.反射方法

4.1.利用反射获取所有成员方法_public

1
2
 1.Class类中方法:
    Method[] getMethods() -> 获取所有public的方法,包括父类中的public方法 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
   /**
     * 获取所有的public的方法
     */
    private static void method01() {
        Class<Person> aClass = Person.class;
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }

4.2.反射之获取方法(有参,无参)_public

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
1.Class类中的方法:
  Method getMethod(String name, Class<?>... parameterTypes) 获取指定的public的成员方法
                   name:传递方法名
                   parameterTypes:方法参数类型的class对象
                       
2.调用方法:Method对象中的方法:
  Object invoke(Object obj, Object... args)  -> 执行方法
                obj:根据构造new出来的对象
                args:方法实参 -> 如果有参数,直接传递实参;否则不用传
                    
                返回值:Object -> 接收被执行方法的返回值,如果方法没有返回值,不用接收了    
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    /**
     * 获取指定的public的方法
     */
    private static void method02()throws Exception {
        Class<Person> aClass = Person.class;

        //创建对象
        Person person = aClass.newInstance();

        Method setName = aClass.getMethod("setName", String.class);
          
        //相当于person.setName("柳岩")
        setName.invoke(person,"柳岩");

        System.out.println(person);//好比调用toString方法

        System.out.println("============================");

        Method getName = aClass.getMethod("getName");

        //好比是person.getName()
        Object o = getName.invoke(person);
        System.out.println(o);
    }
/** 输出:
 * Person{name='柳岩', age=null}
 * ============================
 * 柳岩
 */

4.3.反射之操作私有方法

1
2
3
4
5
6
7
1.Method[] getDeclaredMethods()   -> 获取所有的成员方法,包括private 
2.Method getDeclaredMethod(String name, <?>... parameterTypes)-> 获取执行成员方法,包括private      
    
                       name:传递方法名
                       parameterTypes:方法参数类型的class对象
                           
3.解除私有权限:void setAccessible(boolean flag)                             
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/**
     * 反射指定的成员方法:包括private的
     */
    private static void method04()throws Exception {
        Class<Person> aClass = Person.class;
        Person person = aClass.newInstance();
        Method method = aClass.getDeclaredMethod("eat");
        method.setAccessible(true);
        method.invoke(person);
    }

    /**
     * 获取所有成员方法,包括private
     */
    private static void method03() {
        Class<Person> aClass = Person.class;
        Method[] dm = aClass.getDeclaredMethods();
        for (Method method : dm) {
            System.out.println(method);
        }
    }

5.反射成员变量

5.1.获取所有属性

1
2
3
4
Class类中的方法:

1.Field[] getFields() -> 获取所有public的属性
2.Field[] getDeclaredFields()  -> 获取所有属性,包括priavte的    
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
    /**
     * 1.Field[] getFields() -> 获取所有public的属性
     * 2.Field[] getDeclaredFields()  -> 获取所有属性,包括priavte的
     */
    private static void method01() {
        Class<Student> studentClass = Student.class;
        Field[] fields = studentClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("========================");

        Field[] df = studentClass.getDeclaredFields();
        for (Field field : df) {
            System.out.println(field);
        }
    }

运行结果:

image-20250401220415496

5.2.获取指定属性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Class类中的方法:

1.Field getField(String name) -> 获取指定public的属性
2.Field getDeclaredField(String name)  -> 获取指定属性,包括priavte的 
    
3.Field类中的方法:
  void set(Object obj,Object value) -> 为属性赋值,相当于javabean中的set方法
           obj:对象
           value:赋予的值
               
  Object get(Object obj) -> 获取属性值
         obj:对象
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    /**
     * Field getField(String name) -> 获取指定public的属性
     */
    private static void method02()throws Exception {
        Class<Student> studentClass = Student.class;
        Student student = studentClass.newInstance();
        Field age = studentClass.getField("age");
        //调用set方法为属性赋值
        age.set(student,10);
        //调用get方法获取属性值 -> 相当于javabean中的get方法
        Object o = age.get(student);
        System.out.println("o = " + o);
    }

   /**
     * Field getDeclaredField(String name)  -> 获取指定属性,包括priavte的
     */
    private static void method03()throws Exception {
        Class<Student> studentClass = Student.class;
        Student student = studentClass.newInstance();
        Field name = studentClass.getDeclaredField("name");

        //解除私有权限
        name.setAccessible(true);

        //调用set方法为属性赋值
        name.set(student,"柳岩");
        //调用get方法获取属性值 -> 相当于javabean中的get方法
        Object o = name.get(student);
        System.out.println("o = " + o);
    }

6.反射练习(编写一个小框架)

反射的使用场景之——MyBatis执行方法:

1
2
3
public interface 接口名{
    public Employee find()
}
1
2
3
4
5
6
// MyBatis的配置文件
<select id="find" resultType="Employee的全限定名">
    select 列名 from 表名 where 条件
</select>
    
根据接口的class对象,创建一个实现类对象,然后通过配置文件中的方法名反射这个方法,invoke执行这个方法    

练习:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
需求:在配置文件中,配置类的全限定名,以及配置一个方法名,通过解析配置文件,让配置好的方法执行起来
    className=包名.Person
    methodName=eat
    
步骤:
  1.创建properties配置文件,配置信息
    a.问题:properties配置文件放到哪里?
      将来我们开发完之后给用户的是out路径下的class文件,将class文件打包,  如果将配置文件直接放到模块下
      那么out路径下是不会生成这个配置文件的,如果没有配置文件,程序也就运行不起来了
        
      解决:我们可以将配置文件放到src下,放到src下,out路径下就会出现配置文件 
          
    b.问题:将配置文件放到src下,out路径下会自动生成配置文件,但是如果我们将来将所有的配置文件都放到src下,那么src下面会显得特别乱
      解决:我们可以单独创建一个文件夹,将所有的配置文件放到此文件夹下,将此文件夹改成资源目录,取名为resources  
        
  2.读取配置文件,解析配置文件
    a.问题:如果将配置文件放到resources资源目录下,我们怎么读取
      new FileInputStream("模块名\\resources\\properties文件名") -> 这样不行,out路径下没有resources  -> 相当于写死了
    
    b.问题解决:用类加载器
      ClassLoader classLoader = 当前类.class.getClassLoader()  
      InputStream in = classLoader.getResourceAsStream("文件名")//自动扫描resources下的文件->可以简单理解为扫描out路径下的配置文件
      
          
  3.根据解析出来的className,创建Class对象
  4.根据解析出来的methodName,获取对应的方法
  5.执行方法

resources文件夹改变为资源目录

1706178942944

properties配置文件:

1
2
className=com.atguigu.d_reflect.Person
methodName=eat

Person类的内容:

image-20250401225607035
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class Demo01Reflect {
    public static void main(String[] args)throws Exception {
        //1.创建properties集合
        Properties properties = new Properties();
        //2.读取配置文件,解析配置文件
        InputStream in = Demo01Reflect.class.getClassLoader().getResourceAsStream("pro.properties");
        properties.load(in);
        //System.out.println(properties);
        //3.根据解析出来的className,创建Class对象
        //4.根据解析出来的methodName,获取对应的方法
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        Class<?> aClass = Class.forName(className);
        Object o = aClass.newInstance();

        Method method = aClass.getMethod(methodName);
        //5.执行方法
        method.invoke(o);
    }
}

注:本笔记基于尚硅谷课程

本站于2025年3月26日建立
使用 Hugo 构建
主题 StackJimmy 设计