Adolph
Engineer
Engineer
  • UID623
  • Fans2
  • Follows1
  • Posts72
Reads:894Replies:0

Java Reflection

Created#
More Posted time:Nov 9, 2016 9:49 AM
1. Intro
1.1. What is Java Reflection
Java Reflection is a mechanism that enables us to obtain internal information about a class such as class functions, attributes, parent classes and interfaces at runtime. Reflection also enables us to instantiate objects, call methods and obtain variable values by calling get/set methods at runtime. Even if a method or an attribute is private, it can be called through reflection. This ability of “penetrating the class” is called introspection and is vital during framework development. Under some circumstances, we can only determine the class to be used at runtime, and cannot use the class during the compilation phase. As a result, we can only use the classes that will exist during runtime through reflection (such classes comply with certain rules, such as JDBC). This is a common scenario for reflection usage.
Another common scenario is that we have no idea about the internal information about the class at compilation, and such information is only available at class runtime. For example, in the Object/Relational Mapping (ORM) framework, we can only obtain the various attributes of the class at runtime, and then we obtain the class’s attribute name and value through reflection and store the data into the database. This is also a typical application scenario of reflection.
1.2 Class
Now that we see how reflection can be used to manipulate the class information, what is a class?


After we compile a Java project, all the Java files will be compiled into a .class file. These class objects carry the raw information of this class type such as the parent class, interface, constructor, method and attribute. These class files will be loaded to the virtual machines by ClassLoader during program runtime. After a class is loaded, Java virtual machine will automatically generate a class object in memory. By creating “new” objects, we are actually creating objects using these classes, only that this process is not transparent to us.
In the sections below, we will demonstrate some frequently-used reflection APIs and try to explain reflection from the perspective of code.
2. Reflection class and construct an object
2.1 Obtain a class object
Before proceeding to check out the information about a class, first you should obtain the class object. All Java class types, including the basic class type such as an array, have associated class objects. If you know the class name during the compilation stage, you can obtain a class object using the method below.
Class<?> myObjectClass = MyObject.class;

If you have obtained an object, and you want to obtain the class object of this object, you can achieve it using the method below:
Student me = new Student("mr.simple");
Class<?> clazz = me.getClass();


If you fail to get the target type during compilation, but you know its full class path, you can obtain the class object using the method below:
Class<?> myObjectClass = Class.forName("com.simple.User");

Class<?> myObjectClass = Class.forName("com.simple.User");
When using the Class.forName() method, you must provide the full name of the class including the name of the packet where the class is located. For example, the User class is located in the com.simple packet, so its full class path is com.simple.User.
If you fail to find the corresponding class in the compiled directory (classpath) when calling the Class.forName() method, the error of ClassNotFoundException will be thrown.
Interface description
// To load the specified Class object. Parameter 1 is the full path of the class to be loaded, for example, "com.simple.Student". (frequently-used method).
public static Class<?> forName (String className)

// To load the specified Class object. Parameter 1 is the full path of the class to be loaded, for example"com.simple.Student";
// Parameter 2 indicates whether to initialize this Class object, and Parameter 3 is the specified ClassLoader for loading this class.
public static Class<?> forName (String className, boolean shouldInitialize, ClassLoader classLoader)


2.2 Construct an object of the target type through the Class object
Once you have the Class object, you can do anything you want. It can be an angel or a devil, depending on how you use it. But obtaining the Class object is only the first step. We need to construct an object of this type using the Class object before executing those powerful functions, so that their strength can be fully leveraged through this object. We know that to construct an object in Java, we must use the constructor of the class. Reflection is similar but with a difference in that to construct an object through reflection, we first need to obtain the constructor (Constructor) object of the class, and then create an object of the target class through the Constructor. Let’s just look at the code directly.
private static void classForName() {
        try {
            // Obtain the Class object
            Class<?> clz = Class.forName("org.java.advance.reflect.Student");
            // Obtain the Constructor through the Class object. The constructor of the Student class has a string parameter
            // So here we need to pass the parameter type (For the Student class, see the code later on)
            Constructor<?> constructor = clz.getConstructor(String.class);
            // Create an object for the Student class through the Constructor
            Object obj = constructor.newInstance("mr.simple");
            System.out.println(" obj :  " + obj.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


Using the code above, we can construct an object with the full class name at runtime.
Obtain the constructor interface
// Obtain a public constructor and the parameter is variadic. If the constructor contains parameters, you need to pass the parameter type to the getConstructor method.
public Constructor<T> getConstructor (Class...<?> parameterTypes)
// Obtain all the public constructors of the target class
public Constructor[]<?> getConstructors ()


Note: After you obtain the Constructor, Method and Field through reflection, you should set the Accessible mark of the object to true before the reflection call to improve the reflection speed. When the value is true, it indicates that the reflected object should cancel Java access checks during usage. When the value is false, it indicates that the reflected object should implement Java access checks. For example:
Constructor<?> constructor = clz.getConstructor(String.class);
   // Set the Accessible of Constructor
   constructor.setAccessible(true);

   // Set the Accessible of Method
   Method learnMethod = Student.class.getMethod("learn", String.class);
   learnMethod.setAccessible(true);


The Student and other related classes will be used in later sections, so we present their code here.
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 {
    // Grade
    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

// Breathe interface
public interface Breathe {
    public void breathe();
}


3. Obtain functions in the class through reflection
3.1 Obtain methods defined in the current class
To obtain all the methods defined in the current class, you can use the getDeclaredMethods function in Class. It will obtain all the public, default, protected and private methods in the current class. The getDeclaredMethod(String name, Class...<?> parameterTypes) is used to obtain a specified method. The code is as below:
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);
            // Obtain the parameter type list of the method
            Class<?>[] paramClasses = learnMethod.getParameterTypes() ;
            for (Class<?> class1 : paramClasses) {
                System.out.println("learn the parameter type of the method:" + class1.getName());
            }
            // Identify whether it is a private function. This approach can also be used to identify private attributes
            System.out.println(learnMethod.getName() + " is private "
                    + Modifier.isPrivate(learnMethod.getModifiers()));
            learnMethod.invoke(student, "java ---> ");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


3.2 Obtain all the public methods defined in the current class and the parent class
To obtain all the public methods of the current class and its parent class, you can use the getMethods function in Class. The getMethod function is used to obtain a specified method. The code is as below:
private static void showMethods() {
        Student student = new Student("mr.simple");
        // Obtain all the methods
        Method[] methods = student.getClass().getMethods();
        for (Method method : methods) {
            System.out.println("method name : " + method.getName());
        }

        try {
            // The getMethod function can only be used to get public methods. If you use it to get private methods, an error will be thrown, such as in this case.
            Method learnMethod = student.getClass().getMethod("learn", String.class);
            // Identify whether it is a private function. This approach can also be used to identify private attributes
            System.out.println(learnMethod.getName() + " is private " + Modifier.isPrivate(learnMethod.getModifiers()));
            // Call the learn function
            learnMethod.invoke(student, "java");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


Interface description
// Obtain a function with the specified name and parameter in the Class object. Parameter 1 is the function name, and Parameter 2 is the parameter type list.
public Method getDeclaredMethod (String name, Class...<?> parameterTypes)

// Obtain all the functions in this Class object (excluding functions inherited from its parent class).
public Method[] getDeclaredMethods ()

// Obtain **public** functions in the specified Class object. Parameter 1 is the function name and Parameter 2 is the parameter type list.
public Method getMethod (String name, Class...<?> parameterTypes)

// Obtain all the **public** functions in this Class object (including functions inherited from its parent class and the interface class).
public Method[] getMethods ()


Here you need to pay attention that the getDeclaredMethod and getDeclaredMethods functions include the private, protected, default and public functions. In addition, only functions defined in the class can be obtained through these two functions, and functions inherited from the parent class cannot be obtained. Nevertheless the getMethod and getMethods functions only include public functions, and public functions in the parent class can also be obtained.
4. Obtain attributes in the class through reflection
The method to obtain attributes is very similar to that to obtain methods described in Section 3. The only difference is that you should replace the getMethod function with the getField function and replace the getDeclaredMethod function with the getDeclaredField function.
4.1 Obtain attributes defined in the current class
To obtain all the attributes defined in the current class, you can use the getDeclaredFields function in Class. It will obtain all the public, default, protected and private attributes in the current class. While the getDeclaredField function is used to obtain a specified attribute. The code is as below:
private static void showDeclaredFields() {
        Student student = new Student("mr.simple");
        // Obtain all the public attributes in the current class and its parent class
        Field[] publicFields = student.getClass().getDeclaredFields();
        for (Field field : publicFields) {
            System.out.println("declared field name : " + field.getName());
        }

        try {
            // Obtain a public attribute in the current class and its parent class
            Field gradeField = student.getClass().getDeclaredField("mGrade");
            // Obtain the attribute value
            System.out.println(" my grade is : " + gradeField.getInt(student));
            // Set the attribute value
            gradeField.set(student, 10);
            System.out.println(" my grade is : " + gradeField.getInt(student));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


4.2 Obtain the public attributes defined in the current class and the parent class
To obtain all the public attributes of the current class and its parent class, you can use the getFields function in Class. The getField function is used to obtain a specific attribute. The code is as below:
private static void showFields() {
        Student student = new Student("mr.simple");
        // Obtain all the public attributes in the current class and its parent class
        Field[] publicFields = student.getClass().getFields();
        for (Field field : publicFields) {
            System.out.println("field name : " + field.getName());
        }

        try {
            // Obtain a public attribute in the current class and its parent class
            Field ageField = student.getClass().getField("mAge");
            System.out.println(" age is : " + ageField.getInt(student));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


Interface description
// Obtain the attribute of a specified attribute name in the Class object. Parameter 1 is the attribute name.
public Method getDeclaredField (String name)

// Obtain all the attributes in this Class object (excluding attributes inherited from its parent class).
public Method[] getDeclaredFields ()

// Obtain **public** attributes in the specified Class object. Parameter 1 is the attribute name.
public Method getField (String name)

// Obtain all the **public** attributes in this Class object (including public attributes inherited from its parent class and the interface class).
public Method[] getFields ()


Here you need to pay attention that the getDeclaredField and getDeclaredFields functions include the private, protected, default and public attributes. In addition, only attributes defined in the class can be obtained through these two functions, and attributes inherited from the parent class cannot be obtained. Nevertheless the getField and getFields functions only include public attributes, and public attributes in the parent class can also be obtained.
5. Obtain the parent class and interface through reflection
5.1 Obtain the parent class
Obtain the parent class of the Class object.
Student student = new Student("mr.simple");
    Class<?> superClass = student.getClass().getSuperclass();
    while (superClass != null) {
        System.out.println("Student's super class is : " + superClass.getName());
        // Then obtain the parent class of the parent class, until the final Object class is reached. The parent class of Object is null.
        superClass = superClass.getSuperclass();
    }


5.2 Obtain the interface
Obtain the interfaces implemented by the Class object.
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. Obtain the annotation information
In framework development, the combined use of annotation and reflection is the most common. In annotation definition, we use @Target to specify the types the annotation applies to. See the example below:
@Target({
            ElementType.METHOD, ElementType.FIELD, ElementType.TYPE
    })
    @Retention(RetentionPolicy.RUNTIME)
    static @interface Test {

    }


The @target in the above annotation indicates that the annotation only applies to functions. For other types such as Type, Field and Parameter, please refer to the reference materials provided above. Through reflection APIs we can also obtain the objects of the type, attribute and function of a Class object and then obtain the corresponding annotation information through the getAnnotation interface of these objects. First, we need to add an annotation on the target object, for example:
@Test(tag = "Student class Test Annoatation")
public class Student extends Person implements Examination {
    // Grade
    @Test(tag = "mGrade Test Annotation ")
    int mGrade;

    // ......
}


Then get the annotation information through related annotation functions, as shown below:
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("The Test annotation tag of the attribute: " + testAnnotation.tag());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


Output result is: >
class Annotatation tag = Student class Test Annoatation
The Test annotation tag of the attribute: mGrade Test Annotation
Interface description
// Obtain the annotation of a specified type
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) ;
// Obtain all the annotations in the Class object.
public Annotation[] getAnnotations() ;
Guest