公共技术点之 Java反射 Reflection

公共技术之 Java反射 Reflection

1. 了解Java中的反射

1.1 什么是Java的反射

Java反射是可以让我们在运行时获取类的函数、字段、父类、接口等Class内部信息的机制。通过反射还可以让我们在运行期实例化对象,调用方法,通过调用get/set方法获取变量的值,即使方法或字段是私有的的也可以通过反射的形式调用,这种“看透class”的能力被称为内省,这种能力在框架开发中尤为重要。

有些情况下,我们要使用的类在运行时才会确定,这个时候我们不能在编译期就使用它,因此只能通过反射的形式来使用在运行时才存在的类(该类符合某种特定的规范,例如JDBC),这是反射用得比较多的场景。

还有一个比较常见的场景就是编译时我们对于类的内部信息不可知,必须得到运行时才能获取类的具体信息。比如ORM框架,在运行时才能够获取类中的各个字段,然后通过反射的形式获取其字段名和值,存入数据库。这也是反射比较经典应用场景之一。

1.2 Class类

那么既然反射是操作Class信息的,Class又是什么呢?

当我们编写完一个Java项目之后,所有的Java文件都会被编译成一个.class文件,这些Class对象承载了这个类型的父类、接口、构造函数、方法、字段等原始信息,这些class文件在程序运行时会被ClassLoader加载到虚拟机中。当一个类被加载以后,Java虚拟机就会在内存中自动产生一个Class对象。我们通过new的形式创建对象实际上就是通过这些Class来创建,只是这个过程对于我们是不透明的而已。

下面的章节中我们会为大家演示反射的一些常用api,从代码的角度理解反射。

2. 反射Class以及构造对象

2.1 获取Class对象

在你想检查一个类的信息之前,你首先需要获取类的Class对象。Java中的所有类型包括基本类型,即使是数组都有与之关联的Class类的对象。如果你在编译期知道一个类的名字的话,那么你可以使用如下的方式获取一个类的Class对象。

Class<?> myObjectClass = MyObject.class;

如果你已经得到了某个对象,但是你想获取这个对象的Class对象,那么你可以通过下面的方法得到:

    Student me = new Student("mr.simple");
    Class<?> clazz = me.getClass();

如果你在编译期不知道类的名字,但是你知道它的完整的类路径,那么你则以如下的形式来获取Class对象:

Class<?> myObjectClass = Class.forName("com.simple.User");

在使用Class.forName()方法时,你必须提供一个类的全名,这个全名包括类所在的包的名字。例如User类位于com.simple包,那么他的完整类路径就是com.simple.User。

如果在调用Class.forName()方法时,没有在编译路径下(classpath)找到对应的类,那么将会抛出ClassNotFoundException。

接口说明

// 加载指定的Class对象,参数1为要加载的类的完整路径,例如"com.simple.Student". ( 常用方式 )
public static Class<?> forName (String className)

// 加载指定的Class对象,参数1为要加载的类的完整路径,例如"com.simple.Student";
// 参数2为是否要初始化该Class对象,参数3为指定加载该类的ClassLoader.
public static Class<?> forName (String className, boolean shouldInitialize, ClassLoader classLoader)

2.2 通过Class对象构造目标类型的对象

一旦你拿到Class对象之后,你就可以为所欲为了!它就像潘多拉的魔盒,但更多的时候当你善用它的时候它就是神兵利器,当你心怀鬼胎之时它就会变成恶魔。但获取Class对象只是第一步,我们需要在执行那些强大的行为之前通过Class对象构造出该类型的对象,然后才能通过该对象释放它的能量。

我们知道,在java中药构造对象,必须通过该类的构造函数,那么其实反射也是一样一样的。但是它们确实有区别的,通过反射构造对象,我们首先要获取类的Constructor(构造器)对象,然后通过Constructor来创建目标类的对象。还是直接上代码的。

    private static void classForName() {
        try {
            // 获取Class对象
            Class<?> clz = Class.forName("org.java.advance.reflect.Student");
            // 通过Class对象获取Constructor,Student的构造函数有一个字符串参数,
            // 因此这里需要传递参数的类型 ( Student类见后面的代码 )
            Constructor<?> constructor = clz.getConstructor(String.class);
            // 通过Constructor来创建Student对象
            Object obj = constructor.newInstance("mr.simple");
            System.out.println(" obj :  " + obj.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

通过上述代码,我们就可以在运行时通过完整的类名来构建对象。

获取构造函数接口

// 获取一个公有的构造函数,参数为可变参数,如果构造函数有参数,那么需要将参数的类型传递给getConstructor方法
public Constructor<T> getConstructor (Class...<?> parameterTypes)
// 获取目标类所有的公有构造函数
public Constructor[]<?> getConstructors ()

由于后面还会用到Student以及相关的类,我们在这里就先给出它们的代码吧。

Person.java

public class Person {
    String mName;

    public Person(String aName) {
        mName = aName;
    }

    private void sayHello(String friendName) {
        System.out.println(mName + " say hello to " + friendName);
    }

    protected void showMyName() {
        System.out.println("My name is " + mName);
    }

    public void breathe() {
        System.out.println(" take breathe ");
    }
}

Student.java

public class Student extends Person implements Examination {
    // 年级
    int mGrade;

    public Student(String aName) {
        super(aName);
    }

    public Student(int grade, String aName) {
        super(aName);
        mGrade = grade;
    }

    private void learn(String course) {
        System.out.println(mName + " learn " + course);
    }

    public void takeAnExamination() {
        System.out.println(" takeAnExamination ");
    }

    public String toString() {
        return " Student :  " + mName;
    }

Breathe.java

// 呼吸接口
public interface Breathe {
    public void breathe();
}

Examination.java

// 考试接口
public interface Examination {
    public void takeAnExamination();
}

3 反射获取类中函数

3.1 获取当前类中定义的方法

要获取当前类中定义的所有方法可以通过Class中的getDeclaredMethods函数,它会获取到当前类中的public、default、protected、private的所有方法。而getDeclaredMethods则是获取某个指定的方法。代码示例如下 :

 private static void showDeclaredMethods() {
      Student student = new Student("mr.simple");
        Method[] methods = student.getClass().getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("declared method name : " + method.getName());
        }

        try {
            Method learnMethod = student.getClass().getDeclaredMethod("learn", String.class);
            // 获取方法的参数类型列表
            Class<?>[] paramClasses = learnMethod.getParameterTypes() ;
            for (Class<?> class1 : paramClasses) {
                System.out.println("learn 方法的参数类型 : " + class1.getName());
            }
            // 是否是private函数,字段是否是private也可以使用这种方式判断
            System.out.println(learnMethod.getName() + " is private "
                    + Modifier.isPrivate(learnMethod.getModifiers()));
            learnMethod.invoke(student, "java ---> ");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3.2 获取当前类、父类中定义的公有方法

要获取当前类以及父类中的所有public方法可以通过Class中的getMethods函数,而getMethod则是获取某个指定的方法。代码示例如下 :

    private static void showMethods() {
        Student student = new Student("mr.simple");
        // 获取所有方法
        Method[] methods = student.getClass().getMethods();
        for (Method method : methods) {
            System.out.println("method name : " + method.getName());
        }

        try {
            // 通过getMethod只能获取公有方法,如果获取私有方法则会抛出异常,比如这里就会抛异常
            Method learnMethod = student.getClass().getMethod("learn", String.class);
            // 是否是private函数,字段是否是private也可以使用这种方式判断
            System.out.println(learnMethod.getName() + " is private " + Modifier.isPrivate(learnMethod.getModifiers()));
            // 调用learn函数
            learnMethod.invoke(student, "java");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

接口说明

// 获取Class对象中指定函数名和参数的函数,参数一为函数名,参数2为参数类型列表
public Method getDeclaredMethod (String name, Class...<?> parameterTypes)

// 获取该Class对象中的所有函数( 不包含从父类继承的函数 )
public Method[] getDeclaredMethods ()

// 获取指定的Class对象中的**公有**函数,参数一为函数名,参数2为参数类型列表
public Method getMethod (String name, Class...<?> parameterTypes)

// 获取该Class对象中的所有**公有**函数 ( 包含从父类和接口类集成下来的函数 )
public Method[] getMethods ()

这里需要注意的是getDeclaredMethod和getDeclaredMethods包含private、protected、default、public的函数,并且通过这两个函数获取到的只是在自身中定义的函数,从父类中集成的函数不能够获取到。而getMethod和getMethods只包含public函数,父类中的公有函数也能够获取到。

4 反射获取类中的字段

获取字段和章节3中获取方法是非常相似的,只是从getMethod函数换成了getField,从getDeclaredMethod换成了getDeclaredField罢了。

4.1 获取当前类中定义的字段

要获取当前类中定义的所有字段可以通过Class中的getDeclaredMethods函数,它会获取到当前类中的public、default、protected、private的所有方法。而getDeclaredMethods则是获取某个指定的方法。代码示例如下 :

    private static void showDeclaredFields() {
        Student student = new Student("mr.simple");
        // 获取当前类和父类的所有公有字段
        Field[] publicFields = student.getClass().getDeclaredFields();
        for (Field field : publicFields) {
            System.out.println("declared field name : " + field.getName());
        }

        try {
            // 获取当前类和父类的某个公有字段
            Field gradeField = student.getClass().getDeclaredField("mGrade");
            // 获取属性值
            System.out.println(" my grade is : " + gradeField.getInt(student));
            // 设置属性值
            gradeField.set(student, 10);
            System.out.println(" my grade is : " + gradeField.getInt(student));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

4.2 获取当前类、父类中定义的公有字段

要获取当前类以及父类中的所有public字段可以通过Class中的getFields函数,而getField则是获取某个指定的字段。代码示例如下 :

    private static void showFields() {
        Student student = new Student("mr.simple");
        // 获取当前类和父类的所有公有字段
        Field[] publicFields = student.getClass().getFields();
        for (Field field : publicFields) {
            System.out.println("field name : " + field.getName());
        }

        try {
            // 获取当前类和父类的某个公有字段
            Field ageField = student.getClass().getField("mAge");
            System.out.println(" age is : " + ageField.getInt(student));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

接口说明

// 获取Class对象中指定字段名的字段,参数一为字段名
public Method getDeclaredField (String name)

// 获取该Class对象中的所有字段( 不包含从父类继承的字段 )
public Method[] getDeclaredFields ()

// 获取指定的Class对象中的**公有**字段,参数一为字段名
public Method getField (String name)

// 获取该Class对象中的所有**公有**字段 ( 包含从父类和接口类集成下来的公有字段 )
public Method[] getFields ()

这里需要注意的是getDeclaredMethod和getDeclaredMethods包含private、protected、default、public的函数,并且通过这两个函数获取到的只是在自身中定义的函数,从父类中集成的函数不能够获取到。而getMethod和getMethods只包含public函数,父类中的公有函数也能够获取到。

5 反射获取父类与接口

5.1 获取父类

获取Class对象的父类。

    Student student = new Student("mr.simple");
    Class<?> superClass = student.getClass().getSuperclass();
    while (superClass != null) {
        System.out.println("Student‘s super class is : " + superClass.getName());
        // 再获取父类的上一层父类,直到最后的Object类,Object的父类为null
        superClass = superClass.getSuperclass();
    }

5.2 获取接口

获取Class对象中实现的接口。

    private static void showInterfaces() {
        Student student = new Student("mr.simple");
        Class<?>[] interfaceses = student.getClass().getInterfaces();
        for (Class<?> class1 : interfaceses) {
            System.out.println("Student‘s interface is : " + class1.getName());
        }
    }

6 获取注解信息

在框架开发中,注解加反射的组合使用是最为常见形式的。关于注解方面的知识请参考公共技术点之Java 注解 Annotation,定义注解时我们会通过@Target指定该注解能够作用的类型,看如下示例:

    @Target({
            ElementType.METHOD, ElementType.FIELD, ElementType.TYPE
    })
    @Retention(RetentionPolicy.RUNTIME)
    static @interface Test {

    }

上述注解的@target表示该注解只能用在函数上,还有Type、Field、PARAMETER等类型,可以参考上述给出的参考资料。通过反射api我们也能够获取一个Class对象获取类型、字段、函数等相关的对象,通过这些对象的getAnnotation接口获取到对应的注解信息。

首先我们需要在目标对象上添加上注解,例如 :

@Test(tag = "Student class Test Annoatation")
public class Student extends Person implements Examination {
    // 年级
    @Test(tag = "mGrade Test Annotation ")
    int mGrade;

    // ......
}

然后通过相关的注解函数得到注解信息,如下所示 :

    private static void getAnnotationInfos() {
        Student student = new Student("mr.simple");
        Test classTest = student.getClass().getAnnotation(Test.class);
        System.out.println("class Annotatation tag = " + classTest.tag());

        Field field = null;
        try {
            field = student.getClass().getDeclaredField("mGrade");
            Test testAnnotation = field.getAnnotation(Test.class);
            System.out.println("字段的Test注解tag : " + testAnnotation.tag());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

输出结果为 :

>

class Annotatation tag = Student class Test Annoatation
字段的Test注解tag : mGrade Test Annotation 

接口说明

// 获取指定类型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) ;
// 获取Class对象中的所有注解
public Annotation[] getAnnotations() ;

杂谈

反射作为Java语言的重要特性,在开发中有着极为重要的作用。很多开发框架都是基于反射来实现对目标对象的操作,而反射配合注解更是设计开发框架的主流选择,例如ActiveAndroid,因此深入了解反射的作用以及使用对于日后开发和学习必定大有益处。

时间: 2024-12-12 18:19:17

公共技术点之 Java反射 Reflection的相关文章

Java反射reflection与注解annotation的应用(自动测试机)

一.关于自动测试机 1.什么是自动测试机? 对类中的指定方法进行批量测试的工具 2.自动测试机又什么用? a.避免了冗长的测试代码 当类中的成员方法很多时,对应的测试代码可能会很长,使用测试能够让测试代码非常简洁 b.降低了类与测试代码之间的耦合性 以防万一,测试时应该对类中所有方法进行测试.当我们对类中的一个方法做改动时,很可能我们的测试代码也要做相应改动(如改变了方法名,返回值类型等等),使用测试机能降低类与测试代码之间的耦合性,大大降低不必要的工作量. c.增加了测试的灵活性 测试类与待测

Android公共技术点

公共技术点之  Android动画基础 公共技术点之  View的事件传递 公共技术点之  View的绘制流程 公共技术点之  Java反射Reflection 公共技术点之  Java注解Annotation 公共技术点之  Java动态代理 来源:http://a.codekk.com/

【软件工程】JAVA反射技术

JAVA反射技术 Java 反射技术应用广泛,它能够配置:类的全限定名.方法和参数,完成对象的初始化,甚至是反射某些方法.这样就可以大大增强 Java 的可配置’性, pring IoC 的基本原理也是如此,当然 Spring IoC 的代码要复杂得多. Java 的反射内容繁多,包括对象构建.反射方法.注解.参数.接口等. 通过反射构建对象 Java 中允许通过反射配置信息构建对象,比如 ReflectServicelmpl.java 然后通过反射的方法去构建它 这里的代码就是生成一个对象,然

java反射基础知识(二)

1. 了解 Java 中的反射 1.1 什么是 Java 的反射 Java 反射是可以让我们在运行时获取类的函数.属性.父类.接口等 Class 内部信息的机制.通过反射还可以让我们在运行期实例化对象,调用方法,通过调用 get/set 方法获取变量的值,即使方法或属性是私有的的也可以通过反射的形式调用,这种"看透 class"的能力被称为内省,这种能力在框架开发中尤为重要. 有些情况下,我们要使用的类在运行时才会确定,这个时候我们不能在编译期就使用它,因此只能通过反射的形式来使用在运

小怪兽 Java反射技术 等你来打

Java反射技术,是java的难点,也是程序员进化过程中的必打小怪兽,这里就根据java api好好研究一下java的反射技术. Class Fields Methods 请先回忆一下[java类的定义]. 一.反射是什么 反射是什么?百度百科这样定义:JAVA反射机制是在运行状态下,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取对象信息以及动态调用对象方法的功能称为java语言的反射机制. 可以这样理解,大家小时候有木有玩过平

java反射技术的应用

JAVA 反射机制的简单应用 开发的时候有时候会碰到这样的情况,我们在写程序的时候并不知道需要调用某个对象的哪个方法,只有程序运行后,我们才能够知道.或许我们需要根据客户端传过来的某个String 参数的值来判断我们应该执行哪个方法.在这种情况下JAVA 的反射执行就可以帮上忙了.下面是我做的一个简单的测试代码,提供给大家做个参考. import  java.lang.reflect.InvocationTargetException;import  java.lang.reflect.Meth

Java反射机制(Reflection)

Java反射机制(Reflection) 一.反射机制是什么 Java反射机制是程序在运行过程中,对于任意一个类都能够知道这个类的所有属性和方法;对于任意一个对象都能够调用它的任意一个方法和属性,这种动态获取类信息以及动态调用对象方法的功能就是JAVA语言的反射机制. 二.反射机制能干什么 (1)在运行时判断任意一个对象所属的类 (2)在运行时构造任意一个类的对象 (3)在运行时判断任意一个类所具有的成员变量和方法 (4)在运行时调用任意一个对象的方法 (PS:注意反射机制都是在程序运行时,而不

Java反射技术学习总结

-------<a href="http://www.itheima.com/"">android培训</a>.<a href="http://www.itheima.com/" ">java培训</a>期待与您交流!---------- Java反射技术就是将java中的类的各个组成部分反射出来变为java中相应的数据类型获得. Java同样将类中的各个组成部分进行封装,变为一个个Java类. C

Android中Java反射技术的使用示例

import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import android.os.Bundle; import android.app.Activity; /** * Demo描述: * Android中Java反射技术的使用示例 * 在Java中描述字节码文件(xxx.class)的类叫Class * 反射的过程可视为剖析Class的过