美文网首页
Java之反射Reflection

Java之反射Reflection

作者: 刘启敏 | 来源:发表于2016-12-13 15:52 被阅读25次
Paste_Image.png

什么是Java的反射

Java反射是可以让我们在运行时获取类的函数,属性,父类,接口等Class内部信息的机制。通过反射还可以让我们在运行期实例化对象,调用方法,通过调用get/set方法获取变量的值,即使方法或属性是私有的也可以通过反射的形势调用,这种看透class的能力被称为内省,这种能力在框架开发中尤为重要。有些情况下,我们要使用的类在运行时才会确定,这个时候我们不能在编译器就使用它,因此只能通过反射的形势来使用在运行才存在的类(该类符合某种特定的规范,例如JDBC),这是反射用得比较多的场景。
还有一个比较常见的场景就是编译时我们对于类的内部信息不可知,必须得到运行时才能获取类的具体信息。比如ORM框架,在运行时才能够获取类中的各个属性,然后通过反射的形式获取其属性名和值,存入数据库。这也是反射比较经典的应用场景之一。

下面我会为大家演示反射的一些常用的api,从代码的角度理解反射。
反射的相关api列举出来:

接口说明

<pre>// 加载指定的 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)</pre>

获取构造函数接口

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

获取类中的方法接口

<pre>// 获取 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 ()</pre>

获取类中的成员属性接口

<pre>// 获取 Class 对象中指定属性名的属性,参数一为属性名
public Method getDeclaredField (String name)

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

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

// 获取该 Class 对象中的所有公有属性 ( 包含从父类和接口类集成下来的公有属性 )
public Method[] getFields ()</pre>

获取Class对象

在你想检查一个类的信息之前,你首先需要获取类的 Class 对象。Java 中的所有类型包括基本类型,即使是数组都有与之关联的 Class 类的对象。如果你在编译期知道一个类的名字的话,那么你可以使用如下的方式获取一个类的 Class 对象。
<pre>Class<?> myObjectClass = MyObject.class;</pre>
如果你已经得到了某个对象,但是你想获取这个对象的 Class 对象,那么你可以通过下面的方法得到:
<pre>Student me = new Student("mr.simple");
Class<?> clazz = me.getClass();</pre>
如果你在编译期获取不到目标类型,但是你知道它的完整类路径,那么你可以通过如下的形式来获取 Class 对象:
<pre>Class<?> myObjectClass = Class.forName("com.simple.User");</pre>

在使用 Class.forName()方法时,你必须提供一个类的全名,这个全名包括类所在的包的名字。例如 User 类位于 com.simple 包,那么他的完整类路径就是 com.simple.User。如果在调用 Class.forName()方法时,没有在编译路径下(classpath)找到对应的类,那么将会抛出 ClassNotFoundException。

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

一旦你拿到 Class 对象之后,你就可以为所欲为了!当你善用它的时候它就是神兵利器,当你心怀鬼胎之时它就会变成恶魔。但获取 Class 对象只是第一步,我们需要在执行那些强大的行为之前通过 Class 对象构造出该类型的对象,然后才能通过该对象释放它的能量。 我们知道,在 java 中要构造对象,必须通过该类的构造函数,那么其实反射也是一样一样的。但是它们确实有区别的,通过反射构造对象,我们首先要获取类的 Constructor(构造器)对象,然后通过 Constructor 来创建目标类的对象。还是直接上代码的。
<pre> 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();
}
}</pre>

Person.java

<pre>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 ");
}

}</pre>

Student.java

<pre>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;
}</pre>

Examination.java

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

反射获取类中定义的方法

要获取当前类中定义的所有方法可以通过 Class 中的 getDeclaredMethods 函数,它会获取到当前类中的 public、default、protected、private 的所有方法。而 getDeclaredMethod(String name, Class...<?> parameterTypes)则是获取某个指定的方法。代码示例如下 :
<pre> 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();
    }
}</pre>

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

要获取当前类以及父类中的所有 public 方法可以通过 Class 中的 getMethods 函数,而 getMethod 则是获取某个指定的方法。代码示例如下 :
<pre>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();
    }
}</pre>

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

反射当前类中定义的属性

要获取当前类中定义的所有属性可以通过 Class 中的 getDeclaredFields 函数,它会获取到当前类中的 public、default、protected、private 的所有属性。而 getDeclaredField 则是获取某个指定的属性。代码示例如下 :
<pre>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();
    }
}</pre>

获取当前类、父类中定义的公有属性

要获取当前类以及父类中的所有 public 属性可以通过 Class 中的 getFields 函数,而 getField 则是获取某个指定的属性。代码示例如下 :
<pre> 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();
    }
}</pre>

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

获取Class对象中实现的接口

<pre>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());
}
}</pre>

获取Class对象的父类

<pre>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();
}</pre>

相关文章

  • Java基础之反射

    Java基础之—反射(非常重要)Java中反射机制详解Java进阶之reflection(反射机制)——反射概念与...

  • java基础_反射

    相关文章 : 1. 公共技术点之 Java 反射 Reflection;2. Java反射原理简析;3. java...

  • Java反射机制入门

    Java反射机制入门 一、什么是反射 JAVA反射机制(The JAVA reflection mechanism...

  • Java反射

    1. 什么是反射(Reflection )? Java 语言的反射(Reflection)机制,就是通过动态的方式...

  • Reflection反射 和 Mirror镜像

    /* Reflection反射 和 Mirror镜像 Java 中的Reflection反射 是一种在运行时...

  • Java 反射机制

    [1]. java反射详解[2]. Java Reflection(反射机制)详解[3]. 深入理解Java类型...

  • Java之反射Reflection

    什么是Java的反射 Java反射是可以让我们在运行时获取类的函数,属性,父类,接口等Class内部信息的机制。通...

  • 反射之一

    总结内容源自一下文章粗浅看java反射机制反射机制应用实践谈谈java反射机制Java Reflection(反射...

  • 反射之二

    总结内容源自一下文章粗浅看java反射机制反射机制应用实践谈谈java反射机制Java Reflection(反射...

  • Chapter 13 . 反射机制

    阅读原文 Chapter 13 . 反射机制 13.1 Java反射机制研究及应用 Java Reflection...

网友评论

      本文标题:Java之反射Reflection

      本文链接:https://www.haomeiwen.com/subject/hdivmttx.html