Java 反射

简介: Java 反射

一  反射的概述

  1. 什么是反射?在程序运行中分析类的一种能力image.png

反射能做什么?(1)分析类:加载并初始化一个类,查看类的所有属性和方法 (2)查看并使用对象:查看一 个对象的所有属性和方法,使用对象的任意属性和方法

  1. 反射的应用场景:(1)构造通用的工具类时 (2)搭建具有高度灵活性和扩展性的系统框架

二  类加载器(ClassLoader)

  1. 作用:负责将类的字节码文件(.class文件)加载到内存中,并生成对应的Class文件
  2. Class对象:一个后缀名为 .java的源文件对应一个后缀名为 .class的字节码文件,一个字节码文件又对应一个Class对象
  3. 类加载时机:
(1)创建类的实例:
        Student stu = new Student(); //使用类加载器,将Student类的字节码文件加载到内存中
注:如果再次使用该方法创建Student类对象,类加载器将不再加载该类的字节码文件,因为一个类的字节码文件只会被加载一次
(2)访问类的静态成员时:
        Calendar.getInstance();  //使用类加载器,将Calendar类的字节码文件加载到内存中
(3)初始化类的子类:
        class User extends Person{}
        User user = new User();  //使用类加载器,先加载父类Person类的文件,再加载子类User类文件
(4)反射方式创建类的Class对象:
        Class clazz = Class.forname("类的正名");
        类的正名:包名 + 类名:如 javatest.Test
总结:第一次使用类中的成员时,类加载器就会将该类的字节码文件加载到内存中

三  获取Class对象的方式

1 使用Object类中的getClass()方法:Class clazz = 对象名.getClass();
2 类的静态属性:Class clazz = 类名.class;
3 Class类的静态方法:Class clazz = Class.forName("类的正名");

四  通过反射方式获取构造方法并使用

  1. Constructor:构造器对象 :即其对应类的构造方法
  2. 通过Class对象获取构造器对象
1 getConstructor​(Class<?>... parameterTypes):根据参数列表,获取对应的构造器对象(即对应类的构造方法)仅限公共的构造方法。
其中Class<?>...:可变的参数,代表Class类型的数组, ?:通配符,代表不确定的任意类型,根据构造方法进行对应,即Class<?>... parameterTypes 代表参数类别
2 getDeclaredConstructor​(Class<?>... parameterTypes):根据参数列表,获取对应的构造器对象,可获得私有的构造方法
3 getConstructors​():直接获取此类所有的构造方法(不报含私有方法)
4 getDeclaredConstructors()​:直接获取此类所有的构造方法
例:Class<?>... parameterTypes
若参数为 String name , 则为clazz.getConstructor(String.class);
若参数为 int age , 则为clazz.getConstructor(int.class);

3.Constructor的常用方法

1 getName​(): 获取构造函数名:以字符串形式返回此构造函数的名称 
2 newInstance​(Object... initargs): 根据此构造函数和指定参数创建对象,返回为Object类型对象,需要下转型
3 在调用私有构造方法创建对象是需要结合:
  Constructor对象名.setAccessible(boolean flag); + Constructor对象名.newInstance​(Object... initargs);
  当flag为true是,开始暴力反射,即可以调用私有构造方法

4.代码演示:

Person类
package javatest;
//Person类
public class Person {
    //公共的无参构造
    public Person() {}
    //公共的带参构造
    public Person(String name){
        System.out.println(name);
    }
    //私有的的带参构造
    private Person(int age){
        System.out.println(age);
    }
}
测试类:
import java.lang.reflect.Constructor;
public class Test {
    public static void main(String[] args) throws Exception {
        //需求通过反射的方式创建Person类型的对象并使用其构造方法
        //若不需要反射:可直接 Person p = new Person();
        //1.获取Person类的字节码文件对象
        Class clazz = Class.forName("javatest.Person"); //文件可能不存在,抛出异常
        //或Class clazz = Person.class; //通过;类名也可得到CLass对象
        //2.根据第一步,获取到的字节码文件对象获取指定的构造器对象
        //2.1获取公共的无参构造
        Constructor con1 = clazz.getConstructor(); //可能不存在该类型参数列表的构造方法,所以要抛出异常
        System.out.print("获取公共的无参构造: ");
        System.out.println(con1);
        //2.2获取公共的有参构造
        Constructor con2 = clazz.getConstructor(String.class); //参数为String类型,需要String.class作为参数
        System.out.print("获取公共的有参构造: ");
        System.out.println(con2);
        //2.3获取私有的有参构造
        Constructor con3 = clazz.getDeclaredConstructor(int.class); //参数为int类型,需要int .class作为参数
        System.out.print("获取私有的有参构造:  ");
        System.out.println(con3);
        //2.4直接获取所有的公共构造
        System.out.println("-------------------------------");
        System.out.print("直接获取所有的公共构造: ");
        Constructor[] cons = clazz.getConstructors();//获取所有公共构造
        //遍历cons数组
        for (Constructor con : cons) {
            System.out.println(con);
        }
        //3.根据构造器对象和参数,创建对应的Person类对象
        System.out.println("-------------------------------");
        //首先根据获取构造器名字
        System.out.print("获取构造器名字: ");
        System.out.println(con2.getName());
        //根据con2构造器创建对象
        System.out.print("通过公共带参构造方法,创建对象,并输出参数:");
        Person p = (Person) con2.newInstance("张三");//默认为Object类型,通过下转型(强制转换)得到Person对象
        //通过有无地址值,确定是否存在
        System.out.print("p的地址为: ");
        System.out.println(p);
    }
}

5.结果展示:

image.png

6.总结:通过”全类名“先获得Class对象,根据Class对象获得Constructor对象(构造器对象)通过Constructor对象可以创建Person类对象

 通过反射方式获取成员方法并使用

  1. Method:方法对象:及其对应类的成员方法
  2. 通过Class对象获取方法
1 getMethod(String name, Class<?>...parameterTypes):返回一个Method对象,仅公共成员方法
其中:name方法名, Class<?>...parameterTypes方法的参数列表
2 getDeclaredMethod(String, Class<?>...):返回一个Method对象,可获取私有方法
3 getMethod():返回此类中的所有(不含私有)方法数组
4 getDeclaredMethods():返回此类中的所有方法数组
注:getMethod()得到父类的方法,即一定会得到Object类中的方法

3. Method的常用方法

1 getName():放回方法名
2 invoke( Object obj, Object...args):在指定对象上调用此方法,参数为args
其中:obj代表对象名 , 返回值为Object类型 
3 在调用私有方法是需要结合:
  Method对象名.setAccessible(boolean flag); + Method对象名.invoke( Object obj, Object...args);
  当flag为true是,开始暴力反射,即可以调用私有方法

4.代码演示

Person类:
package javatest;
//Person类
public class Person {
    //公共的无参方法
    public void show1(){
        System.out.println("我是公共的无参方法");
    }
    //公共的有参方法
    public void show2(int a){
        System.out.println("我是公共的有参方法,参数为" + a);
    }
    //私有的有参方法
    private int show3(int a, int b){
        System.out.println("我是私有的有参方法,参数和为" + (a + b));
        return (a + b);
    }
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Test {
    public static void main(String[] args) throws Exception {
        //需求通过反射的方式获取Person类的成员方法并调用
        //若不需要反射:可直接 Person p = new Person();
        //1.获取Person类的字节码文件对象
        Class clazz = Class.forName("javatest.Person"); //文件可能不存在,抛出异常
        //或Class clazz = Person.class; //通过;类名也可得到CLass对象
        //2.获取该类的构造器对象,然后创建Person对象
        Constructor con = clazz.getConstructor(); //获得系统提供的无参构造方法
        Person person = (Person) con.newInstance(); //向下转型,获得Person对象
        //3.获取该类的成员方法对象,然后调用此方法
        //3.1调用公共的无参方法
        Method method1 = clazz.getMethod("show1");
        //打印方法
        System.out.println(method1);
        //打印方法名
        System.out.println(method1.getName());
        //调用此方法
        method1.invoke(person); //person作为对象进行调用
        System.out.println("-----------------------------");
        //3.2调用公共的有参方法
        Method method2 = clazz.getMethod("show2", int.class);
        //调用此方法
        method2.invoke(person,2); //person作为对象进行调用, arge参数
        System.out.println("-----------------------------");
        //3.2调用私有的有参方法
        Method method3 = clazz.getDeclaredMethod("show3", int.class, int.class);
        //调用此方法
        method3.setAccessible(true);
        int sum = (int)method3.invoke(person,1,2); //person作为对象进行调用
        System.out.println("sum:" + sum);
        System.out.println("-----------------------------");
        //3.4一次性调用所有的公共方法
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);  //会得到Object类的方法
        }
    }
}

5.结果演示:可以看出只有俩个为Person类的公共方法,其余为Object类方法image.png

6. 总结:通过”全类名“先获得Class对象,根据Class对象获得Constructor对象(构造器对象)通过构造器创建Person类对象,通过Class对象获取方法,再通过method类方法结合Person类对象调用方法

 通过反射方式获取成员变量并使用

  1. Field对象:域(属性、成员变量)对象,及其对应类的成员变量
  2. 通过Class对象获取属性
1 getField(String name):返回一个Field对象,仅公共属性
其中:name:属性名
2 getDeclaredField(String name):返回一个Field对象,可获取私有属性
3 getField():返回此类中的所有属性(不含私有)方法数组
4 getDeclaredFields():返回此类中的所有属性方法数组

3. Field的常用方法

1 set(Object obj, Object value):设置obj对象的指定属性值为value
2 setAccessible(boolean flag):将此属性的可访问性设置为指定布尔值
3 get(Object obj):访问obj对象的属性
4 equals​(Object obj):将此Field与指定的对象进行比较
5 getName():返回此Field对象表示的属性的名称

4.代码演示

Person类:
package javatest;
//Person类
public class Person {
    //公有的属性
    public String name;
    //私有的属性
    private int age;
    //重写toString方法为了方便打印各个属性值
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Test {
    public static void main(String[] args) throws Exception{
        //需求:通过反射获取成员变量并使用
        //1.获取Person类的字节码文件对象
        Class clazz = Class.forName("javatest.Person");
        //或:Class clazz = Person.class;
        //2.通过字节码文件对象获取构造器对象,然后创建Person类对象
        Constructor con = clazz.getConstructor();
        Person person = (Person)con.newInstance();
        //合并版 Person person = (Person)clazz.getConstructor().newInstance(); 链式编程
        //3.设置Person类对象的属性值
        Field field1 = clazz.getField("name");
        field1.set(person, "张三");
        Field age = clazz.getDeclaredField("age");
        age.setAccessible(true); //将私有属性设置为可设置权限
        age.set(person, 1);
        System.out.println(person);
    }
}

5.结果演示:可以看出只有俩个为Person类的公共方法,其余为Object类方法image.png

6. 总结:通过”全类名“先获得Class对象,根据Class对象获得Constructor对象(构造器对象)通过构造器创建Person类对象,通过Class对象获取方法,再通过method类方法结合Person类对象调用方法



目录
相关文章
|
2天前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
1月前
|
监控 Java
Java基础——反射
本文介绍了Java反射机制的基本概念和使用方法,包括`Class`类的使用、动态加载类、获取方法和成员变量信息、方法反射操作、以及通过反射了解集合泛型的本质。同时,文章还探讨了动态代理的概念及其应用,通过实例展示了如何利用动态代理实现面向切面编程(AOP),例如为方法执行添加性能监控。
|
1月前
|
Java
Java的反射
Java的反射。
31 2
|
2月前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
39 0
[Java]反射
|
3月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
78 9
Java——反射&枚举
|
2月前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
32 2
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
|
2月前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
25 0
|
3月前
|
Java 程序员 编译器
Java的反射技术reflect
Java的反射技术允许程序在运行时动态加载和操作类,基于字节码文件构建中间语言代码,进而生成机器码在JVM上执行,实现了“一次编译,到处运行”。此技术虽需更多运行时间,但广泛应用于Spring框架的持续集成、动态配置及三大特性(IOC、DI、AOP)中,支持企业级应用的迭代升级和灵活配置管理,适用于集群部署与数据同步场景。
|
3月前
|
存储 安全 Java
扫盲java基础-反射(一)
扫盲java基础-反射(一)