Java反射

我们日常写的代码基本上都是固定的,所以无论运行多少次,其结果也都是固定的,但是在某些特殊场合中编写代码时不确定要创建什么类型的对象,也不确定要调用什么样的方法,这些都希望通过运行时传递 的参数来决定,该机制叫做动态编程技术,也就是反射机制。
简而言之一句话:反射机制就是用于动态创建对象并且动态调用方法的机制
而且,更重要的是,目前主流的框架底层都是采用反射机制来实现的

动态编程技术也就是要编写通用的代码,这就要借助一个Java的lang包下的Class类

Class类

基本概念:
1.java.lang.Class类的实例可以用于描述Java应用程序中的类和接口,不再仅仅表示堆区中的一块内存了,也表示一种数据类型。
2.该类没有公共构造方法,该类的实例(也就是整个类的信息,这个整体)由Java虚拟机和类加载器自动构造完成,本质上就是加载到内存中的运行时类。

获取Class对象的方式:
使用数据类型.class的方式可以获取对应类型的Class对象。
使用引用/对象.getClass()的方式可以获取对应类型的Class对象。
使用包装类.TYPE的方式可以获取对应基本数据类型的Class对象。
使用Class.forName()的方式来获取参数指定类型的Class对象。
使用类加载器ClassLoader的方式获取指定类型的Class对象

例子:

public class ClassTest {
    public static void main(String[] args) throws Exception{
        // 1.使用数据类型.class的方式可以获取对应类型的Class对象
        Class c1 = String.class;
        // 打印会自动调用toString()方法,查询手册可知
        // Class类重写了toString()方法,打印的是类或者接口的完全限定名,也就是包名.类名
        System.out.println("使用数据类型.class的方式获取到的对应类型的Class对象为c1 = " + c1);

        c1 = int.class;
        // 如果是基本数据类型,则打印的就是基本数据类型的名称
        System.out.println("使用数据类型.class的方式获取到的对应类型的Class对象为c1 = " + c1);

        c1 = void.class;
        // 如果是void,返回的就是void
        System.out.println("使用数据类型.class的方式获取到的对应类型的Class对象为c1 = " + c1);

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

        // 2.使用对象.getClass()的方式获取对应的Class对象
        String str1 = new String("hello");
        c1 = str1.getClass();
        System.out.println("使用对象.getClass()的方式获取对应的Class对象为c1 = " + c1);

        Integer it1 = 20;
        c1 = it1.getClass();
        System.out.println("使用对象.getClass()的方式获取对应的Class对象为c1 = " + c1);

        int num = 5;
        // num.getClass();
        // 上面这一句会报错,因为基本数据类型的变量不能调用方法,基本数据类型不是对象

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

        // 3.使用包装类.TYPE的方式来获取对应基本数据类型的Class对象
        c1 = Integer.TYPE;
        System.out.println("使用包装类.TYPE的方式来获取对应基本数据类型的Class对象为c1 = " + c1);

        c1 = Integer.class;
        System.out.println("使用包装类.TYPE的方式来获取对应基本数据类型的Class对象为c1 = " + c1);

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

        // 4.调用Class类中的forName方法来获取对应的Class对象
        //c1 = Class.forName("String"); // 报错,要求写完整的名称
        c1 = Class.forName("java.lang.String");
        System.out.println("调用Class类中的forName方法来获取对应的Class对象为c1 = " + c1);

        c1 = Class.forName("java.util.Date");
        System.out.println("调用Class类中的forName方法来获取对应的Class对象为c1 = " + c1);

        // c1 = Class.forName("int"); // 报错,不能获取基本数据类型的Class对象

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

        // 5.使用类加载器的方式来获取Class对象
        ClassLoader classLoader = ClassTest.class.getClassLoader();
        c1 = classLoader.loadClass("java.lang.String");
        System.out.println("使用类加载器的方式来获取Class对象为c1 = " + c1);
    }
}

结果为

使用数据类型.class的方式获取到的对应类型的Class对象为c1 = class java.lang.String
使用数据类型.class的方式获取到的对应类型的Class对象为c1 = int
使用数据类型.class的方式获取到的对应类型的Class对象为c1 = void
============================================================
使用对象.getClass()的方式获取对应的Class对象为c1 = class java.lang.String
使用对象.getClass()的方式获取对应的Class对象为c1 = class java.lang.Integer
============================================================
使用包装类.TYPE的方式来获取对应基本数据类型的Class对象为c1 = int
使用包装类.TYPE的方式来获取对应基本数据类型的Class对象为c1 = class java.lang.Integer
============================================================
调用Class类中的forName方法来获取对应的Class对象为c1 = class java.lang.String
调用Class类中的forName方法来获取对应的Class对象为c1 = class java.util.Date
============================================================
使用类加载器的方式来获取Class对象为c1 = class java.lang.String

常用方法:

方法声明 功能介绍
static Class<?> forName(String className) 用于获取参数指定类型对应的Class对象并返回
T newInstance() 用于创建该Class对象所表示类的新实例

Constructor类

基本概念:
java.lang.reflect.Constructor类主要用于描述获取到的构造方法信息

Class类的常用方法:

方法声明 功能介绍
Constructor getConstructor(Class<?>... parameterTypes) 用于获取此Class对象所表示类型中参数指定的公共构造方法
Constructor<?>[] getConstructors() 用于获取此Class对象所表示类型中所有 的公共构造方法

Constructor类的常用方法:

方法声明 功能介绍
T newInstance(Object... initargs) 使用此Constructor对象描述的构造方法对象代表类型的新实例
int getModififiers() 获取方法的访问修饰符
String getName() 获取方法的名称
Class<?>[] getParameterTypes() 获取方法所有参数的类型

例子:
先定义Person类

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

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类

public class PersonConstructorTest {
    public static void main(String[] args) throws Exception{
        // 1.使用原始方式以无参形式构造Person类型的对象并打印
        Person p1 = new Person();
        System.out.println("原始方式以无参方式构建的对象是: " + p1);

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

        // 2.使用反射机制以无参形式构造Person类型的对象并打印
        // 动态创建对象,创建对象的类型可以从键盘输入,也可以从配置文件中读取
        Class c1 = Class.forName("ExperimentOnMyOwn.reflect.Person");
        // 这个方法过时了
        System.out.println("反射机制以无参方式构建的对象是: " + c1.newInstance());

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

        // 优化
        // 获取Class对象对应类中的无参构造方法,也就是Person类中的无参构造方法
        Constructor constructor = c1.getConstructor();
        // 使用获取到的无参构造方法来构造对应类型的对象,也就是Person类型的对象
        System.out.println("反射机制优化的方法以无参方式构建的对象是: " + constructor.newInstance());

        // 3.使用原始方式以有参方式构造Person类型的对象并打印
        Person p2 = new Person("zhangfei", 30);
        System.out.println("原始方式以有参方式构建的对象是: " + p2);

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

        // 4.使用反射机制以有参方式构造Person类型的对象并打印
        // 获取Class对象对应类中的有参构造方法,也就是Person类中的有参构造方法
        Constructor constructor1 = c1.getConstructor(String.class, int.class);
        // 使用获取到的有参构造方法来构造对应类型的对象,也就是Person类型的对象
        // newInstance方法中的实参是用于给有参数构造方法的形参进行初始化的,也即是给name和age进行初始化的
        System.out.println("反射机制优化的方法以有参方式构建的对象是: " + constructor1.newInstance("zhangfei", 30));

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

        // 5.使用反射机制获取Person类中所有的公共构造方法并打印
        Constructor[] constructors = c1.getConstructors();
        System.out.println("反射机制获取Person类中的所有公共构造方法有:");
        System.out.println();
        for (Constructor ct : constructors) {
            System.out.println("构造方法的访问修饰符是: " + ct.getModifiers()); // 查询手册可知,打印1就是public
            System.out.println("构造方法的方法名称是: " + ct.getName());
            Class[] parametersType = ct.getParameterTypes();
            System.out.println("构造方法的所有参数类型是: ");
            for (Class cs : parametersType) {
                System.out.print(cs + " ");
            }
            System.out.println();
            System.out.println("============================================================");
        }
    }
}

结果为

原始方式以无参方式构建的对象是: Person{name='null', age=0}
============================================================
反射机制以无参方式构建的对象是: Person{name='null', age=0}
============================================================
反射机制优化的方法以无参方式构建的对象是: Person{name='null', age=0}
原始方式以有参方式构建的对象是: Person{name='zhangfei', age=30}
============================================================
反射机制优化的方法以有参方式构建的对象是: Person{name='zhangfei', age=30}
============================================================
反射机制获取Person类中的所有公共构造方法有:

构造方法的访问修饰符是: 1
构造方法的方法名称是: ExperimentOnMyOwn.reflect.Person
构造方法的所有参数类型是: 

============================================================
构造方法的访问修饰符是: 1
构造方法的方法名称是: ExperimentOnMyOwn.reflect.Person
构造方法的所有参数类型是: 
class java.lang.String int 
============================================================

Fileld类

基本概念:
java.lang.reflect.Field类主要用于描述获取到的单个成员变量信息。

Class类的常用方法:

方法声明 功能介绍
Field getDeclaredField(String name) 用于获取此Class对象所表示类中参数指定的单个成员 变量信息
Field[] getDeclaredFields() 用于获取此Class对象所表示类中所有成员变量信息

Field类的常用方法:

方法声明 功能介绍
Object get(Object obj) 获取参数对象obj中此Field对象所表示成员变量的数 值
void set(Object obj, Object value) 将参数对象obj中此Field对象表示成员变量的数值修 改为参数value的数值
void setAccessible(boolean flag) 当实参传递true时,则反射对象在使用时应该取消 Java 语言访问检查
int getModififiers() 获取成员变量的访问修饰符
Class<?> getType() 获取成员变量的数据类型
String getName() 获取成员变量的名称

例子:

public class PeronFieldTest {
   public static void main(String[] args) throws Exception{
       // 1.使用原始方式来构造对象以及获取成员变量的数值并打印
       Person p1 = new Person("zhangfei", 30);
       System.out.println("原始方式获取到的成员变量数值为: " + p1.getName());

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

       // 2.使用反射机制来构造对象以及获取成员变量的数值并打印
       // 2.1 获取Class对象
       Class c1 = Class.forName("ExperimentOnMyOwn.reflect.Person");
       // 2.2 根据Class对象获取对应的有参构造方法
       Constructor constructor = c1.getConstructor(String.class, int.class);
       // 2.3 使用有参构造方法来得到Person类型的对象
       Object object = constructor.newInstance("zhangfei", 30);
       // 2.4 根据Class对象获取对应的成员变量信息
       Field field = c1.getDeclaredField("name");
       // 设置Java语言访问检查的取消 暴力反射
       field.setAccessible(true);
       // 2.5 使用Person类型的对象来获取成员变量的数值,也就是成员变量name的数值
       System.out.println("反射机制获取到的成员变量数值为: " + field.get(object));

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

       // 3.使用原始方式修改指定对象中成员变量的数值后再打印
       p1.setName("guanyu");
       System.out.println("修改后成员变量的数值为: " + p1.getName());

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

       // 4.使用反射机制修改指定对象中成员变量的值后再次打印
       field.set(object, "guanyu");
       System.out.println("获取到的成员变量数值为: " + field.get(object));

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

       // 5.获取Class对象对应类中所有的成员变量
       Field[] declaredFields = c1.getDeclaredFields();
       for (Field ft : declaredFields) {
           System.out.println("获取到的访问修饰符为: " + ft.getModifiers()); // 打印2为private
           System.out.println("获取到的数据类型为: " + ft.getType());
           System.out.println("获取到的成员变量名称为: " + ft.getName());
           System.out.println("================================");
       }
   }
}

Method类

基本概念:
java.lang.reflect.Method类主要用于描述获取到的单个成员方法信息。

Class类的常用方法:

方法声明 功能介绍
Method getMethod(String name, Class<?>... parameterTypes) 用于获取该Class对象表示类中名字为name参数parameterTypes的指定公共成员方法
Method[] getMethods() 用于获取该Class对象表示类中所有公共成员方法

Method类的常用方法:

方法声明 功能介绍
Object invoke(Object obj, Object... args) 使用对象obj来调用此Method对象所表示的成 员方法,实参传递args
int getModififiers() 获取方法的访问修饰符
Class<?> getReturnType() 获取方法的返回值类型
String getName() 获取方法的名称
Class<?>[] getParameterTypes() 获取方法所有参数的类型
Class<?>[] getExceptionTypes() 获取方法的异常信息

例子:

public class PersonMethodTest {
    public static void main(String[] args) throws Exception{
        // 1.使用原始方式构造对象并调用方法打印结果
        Person p1 = new Person("zhangfei", 30);
        System.out.println("调用方法的返回值是:" + p1.getName());

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

        // 2.使用反射机制构造对象并调用方法打印结果
        // 2.1 获取Class对象
        Class c1 = Class.forName("ExperimentOnMyOwn.reflect.Person");
        // 2.2 根据Class对象来获取对应的有参构造方法
        Constructor constructor = c1.getConstructor(String.class, int.class);
        // 2.3 使用有参构造方法构造对象并记录
        Object object = constructor.newInstance("zhangfei", 30);
        // 2.4 根据Class对象来获取对应的成员方法
        Method method = c1.getMethod("getName");
        // 2.5 使用对象调用成员方法进行打印
        // 表示使用object对象调用method表示的方法,也就是调用getName方法来获取姓名
        System.out.println("调用方法的返回值是:" + method.invoke(object));

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

        // 3.使用反射机制来获取类中的所有成员方法并打印
        Method[] methods = c1.getMethods();
        for (Method mt : methods) {
            System.out.println("成员方法的修饰符是: " + mt.getModifiers());
            System.out.println("成员方法的返回值类型是: " + mt.getReturnType());
            System.out.println("成员方法的名称是: " + mt.getName());
            System.out.println("成员方法形参列表的类型是: "); 
            Class<?>[] parameterTypes = mt.getParameterTypes();
            for (Class ct : parameterTypes) {
                System.out.print(ct + " ");
            }
            System.out.println();
            System.out.println("成员方法的异常类型列表是: ");
            Class<?>[] exceptionTypes = mt.getExceptionTypes();
            for (Class ct: exceptionTypes) {
                System.out.print(ct + " ");
            }
            System.out.println();
            System.out.println("=========================================");
        }
    }
}

结果为

调用方法的返回值是:zhangfei
============================================================
调用方法的返回值是:zhangfei
============================================================
成员方法的修饰符是: 1
成员方法的返回值类型是: class java.lang.String
成员方法的名称是: toString
成员方法形参列表的类型是: 

成员方法的异常类型列表是: 

=========================================
成员方法的修饰符是: 1
成员方法的返回值类型是: class java.lang.String
成员方法的名称是: getName
成员方法形参列表的类型是: 

成员方法的异常类型列表是: 

=========================================
成员方法的修饰符是: 1
成员方法的返回值类型是: void
成员方法的名称是: setName
成员方法形参列表的类型是: 
class java.lang.String 
成员方法的异常类型列表是: 

=========================================
成员方法的修饰符是: 1
成员方法的返回值类型是: int
成员方法的名称是: getAge
成员方法形参列表的类型是: 

成员方法的异常类型列表是: 

=========================================
成员方法的修饰符是: 1
成员方法的返回值类型是: void
成员方法的名称是: setAge
成员方法形参列表的类型是: 
int 
成员方法的异常类型列表是: 

=========================================
成员方法的修饰符是: 273
成员方法的返回值类型是: void
成员方法的名称是: wait
成员方法形参列表的类型是: 
long 
成员方法的异常类型列表是: 
class java.lang.InterruptedException 
=========================================
成员方法的修饰符是: 17
成员方法的返回值类型是: void
成员方法的名称是: wait
成员方法形参列表的类型是: 
long int 
成员方法的异常类型列表是: 
class java.lang.InterruptedException 
=========================================
成员方法的修饰符是: 17
成员方法的返回值类型是: void
成员方法的名称是: wait
成员方法形参列表的类型是: 

成员方法的异常类型列表是: 
class java.lang.InterruptedException 
=========================================
成员方法的修饰符是: 1
成员方法的返回值类型是: boolean
成员方法的名称是: equals
成员方法形参列表的类型是: 
class java.lang.Object 
成员方法的异常类型列表是: 

=========================================
成员方法的修饰符是: 257
成员方法的返回值类型是: int
成员方法的名称是: hashCode
成员方法形参列表的类型是: 

成员方法的异常类型列表是: 

=========================================
成员方法的修饰符是: 273
成员方法的返回值类型是: class java.lang.Class
成员方法的名称是: getClass
成员方法形参列表的类型是: 

成员方法的异常类型列表是: 

=========================================
成员方法的修饰符是: 273
成员方法的返回值类型是: void
成员方法的名称是: notify
成员方法形参列表的类型是: 

成员方法的异常类型列表是: 

=========================================
成员方法的修饰符是: 273
成员方法的返回值类型是: void
成员方法的名称是: notifyAll
成员方法形参列表的类型是: 

成员方法的异常类型列表是: 

=========================================

获取其他结构信息

方法声明 功能介绍
Package getPackage() 获取所在的包信息
Class<? super T> getSuperclass() 获取继承的父类信息
Class<?>[] getInterfaces() 获取实现的所有接口
Annotation[] getAnnotations() 获取注解信息
Type[] getGenericInterfaces() 获取泛型信息

例子:
先定义Student类和MyAnnotation注解

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
@MyAnnotation
public class Student<T, E> extends Person implements Comparable, Serializable {
    @Override
    public int compareTo(Object o) {
        return 0;
    }
}

测试类

public class StudentTest {
    public static void main(String[] args) throws Exception{
        // 获取Student类型的Class对象
        Class c1 = Class.forName("ExperimentOnMyOwn.reflect.Student");
        System.out.println("获取到的包信息是:" + c1.getPackage());
        System.out.println("获取到的父类信息是:" + c1.getSuperclass());
        System.out.println("获取到的接口信息是:"); //
        Class[] interfaces = c1.getInterfaces();
        for (Class ct : interfaces) {
            System.out.print(ct + " ");
        }
        System.out.println();

        System.out.println("获取到的注解信息是:");
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation at : annotations) {
            System.out.print(at + " ");
        }
        System.out.println();

        System.out.println("获取到的泛型信息是:");
        Type[] genericInterfaces = c1.getGenericInterfaces();
        for (Type tt : genericInterfaces) {
            System.out.print(tt + " ");
        }
        System.out.println();
    }
}

结果为

获取到的包信息是:package ExperimentOnMyOwn.reflect
获取到的父类信息是:class ExperimentOnMyOwn.reflect.Person
获取到的接口信息是:
interface java.lang.Comparable interface java.io.Serializable 
获取到的注解信息是:
@ExperimentOnMyOwn.reflect.MyAnnotation() 
获取到的泛型信息是:
interface java.lang.Comparable interface java.io.Serializable 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容

  • Kotlin 跟 Java 可以无缝衔接,因此 Kotlin 能够使用 Java 的反射机制。另外,Kotlin ...
    fengzhizi715阅读 1,697评论 0 3
  • 能够分析类能力的程序称为反射(reflective)。反射机制的功能非常强大,主要提供了如下功能: 对于任意一个类...
    codersm阅读 276评论 2 1
  • ## 引言 ### java中创建对象有几种方式? #### 1.使用new关键字 #### 2.使用clone方...
    芋头888阅读 587评论 1 0
  • 本篇文章已授权微信公众号guolin_blog(郭霖)独家发布 转载请注明出处:https://www.jians...
    ming152阅读 6,792评论 4 79
  • Java反射 概述 Java反射机制可以让我们在编译期(Compile Time)之外的运行期(Runtime)检...
    Leocat阅读 1,133评论 0 0