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类对象调用方法



目录
相关文章
|
18天前
|
Java C++
Java反射的简单使用
Java反射的简单使用
24 3
|
18天前
|
Java
【专栏】Java反射机制,该机制允许程序在运行时获取类信息、动态创建对象、调用方法和访问属性
【4月更文挑战第27天】本文探讨了Java反射机制,该机制允许程序在运行时获取类信息、动态创建对象、调用方法和访问属性。反射通过Class、Constructor、Method和Field类实现。文中列举了反射的应用场景,如动态创建对象、调用方法、访问属性和处理注解,并提供了相关实例代码演示。
|
2天前
|
缓存 安全 Java
【Java——反射机制详解】
RTTI(Run-Time Type Identification)运行时类型识别。在《Thinking in Java》一书第十四章中有提到,其作用是在运行时识别一个对象的类型和类的信息。主要有两种方式:一种是“传统的”RTTI,它假定我们在编译时已经知道了所有的类型;另一种是“反射”机制,它允许我们在运行时发现和使用类的信息。 反射就是把java类中的各种成分映射成一个个的Java对象 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
|
2天前
|
安全 Java API
Java反射(Reflection)的技术性文章
Java反射(Reflection)的技术性文章
9 1
|
3天前
|
JSON Java 数据库连接
Java的反射
Java的反射
|
16天前
|
安全 Java API
JAVA-不安全的反射--RCE
JAVA不安全的反射造成的RCE小案例
|
18天前
|
SQL 存储 Java
【Java反射详解】
【Java反射详解】
15 1
|
18天前
|
Java
JAVA难点包括异常处理、多线程、泛型和反射,以及复杂的分布式系统知识
【5月更文挑战第2天】JAVA难点包括异常处理、多线程、泛型和反射,以及复杂的分布式系统知识。入坑JAVA因它的面向对象特性、平台无关性、强大的标准库和活跃的社区支持。
43 2
|
18天前
|
Java 测试技术
滚雪球学Java(24):Java反射
【4月更文挑战第13天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
24 0
滚雪球学Java(24):Java反射
|
18天前
|
Java
Java 反射
Java 反射