Depth understanding of Java reflection
To understand the principle of reflection, we must first understand what type of information. Let's Java runtime information identifying objects and classes, there are two ways: one is the traditional RTTI, which assumes we already know at compile-time type information for all; the other is a reflection mechanism that allows us find and use information on classes at runtime.
1, Class Objects
Understand how RTTI in Java, you first need to know the type of information at run time is how to represent, this is done by the Class object, which contains information about the class. Class object is used to create all the "normal" objects, Java objects to perform RTTI using the Class, even if you are performing a similar type conversion such operations.
Each class will have a corresponding Class object, is stored in the .class file. All classes are at the first time you use it, dynamically loaded into the JVM, when the program creates a static member of a class of reference, it will load the class. Class objects are only loaded when needed, static initialization is done when the class is loaded.
public class TestMain { public static void main(String[] args) { System.out.println(XYZ.name); } } class XYZ { public static String name = "luoxn28"; static { System.out.println("xyz静态块"); } public XYZ() { System.out.println("xyz构造了"); } }
The output is:
Class loader first checks whether the Class object of this class has been loaded before, if not already loaded, the default class loader will look for the corresponding .class file based on the class name.
Want to use run-time type information, you must obtain an object (such as a class Base objects) Class object reference, use the function Class.forName ( "Base") can achieve this purpose, or use base.class. Note that there is very interesting, use the function ".class" to create a Class object when the reference is not automatically initialize the Class object, using forName () will automatically initialize the Class object. In order to use the class to do the preparatory work generally have the following three steps:
- Load: By class loader, bytecode corresponding to find, create a Class object
- Links: class byte code verification, the space allocated for the static field
- Initialization: If the class has a superclass, then its initialization, performs static initialization and static initialization block
Base class {public static int NUM =. 1; static { System.out.println ( "Base" + NUM); } } public class the Main { public static void main (String [] args) { // does not initialize static blocks Class = Base.class of clazz1; System.out.println ( "------"); // initializes Class = clazz2 the Class.forName ( "zzz.Base"); } }
2, do first cast before checking
The compiler will check the legality of the type of downcast, if not legal to throw an exception. Before down conversion type, instanceof Analyzing be used.
class Base { } class Derived extends Base { } public class Main { public static void main(String[] args) { Base base = new Derived(); if (base instanceof Derived) { // 这里可以向下转换了 System.out.println("ok"); } else { System.out.println("not ok"); } } }
3, the reflection: the runtime class information
If you do not know the exact type of an object, RTTI can tell you, but there is a premise: this type must be known at compile time, in order to use RTTI to identify it. Class class libraries together with java.lang.reflect reflections of support, the library comprises a Field, Method and Constructor classes, these classes of objects created by the JVM startup to indicate members corresponding to the unknown class. In this case you can use Contructor create new objects, retrieve and modify the class field associated with the Field object get () and set () method, using invoke () method calls the method associated with a Method object. In addition, can also call getFields (), getMethods () and getConstructors () and many convenient method to returns an array of fields, methods, and constructors object, so the object information may be fully defined at run time, and at compile time you do not need to know anything about the class.
Reflection and there is no magic, when dealing with reflected by the object of an unknown type, JVM simply checking the object, to see which particular class it belongs to. Therefore, the class .class
for the JVM must be accessible, either, or obtained from the network on the local machine. So for the difference between the real and the reflective RTTI only comprising:
- RTTI, the compiler opens and checks .class file at compile time
- Reflective, open and runtime checking .class file
public class Person implements Serializable { private String name; private int age; // get/set方法 } public static void main(String[] args) { Person person = new Person("luoxn28", 23); Class clazz = person.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { String key = field.getName(); PropertyDescriptor descriptor = new PropertyDescriptor(key, clazz); Method method = descriptor.getReadMethod(); Object value = method.invoke(person); System.out.println(key + ":" + value); } }
By the above getReadMethod () get class method calls the function can be invoked by the class method set getWriteMethod () method. Generally speaking, we do not need to use the reflection tools, but they are creating dynamic code will be more useful reflection to support other features in Java, such as serialized JavaBean objects and so on.
4, dynamic proxies
Proxy mode in order to provide additional or different operations, and used to replace the inserted object "real" objects, these operations involve communication "real" object and thus act as an intermediary agent typically role. Java's dynamic proxy agent more than a step forward ideas, it can dynamically create and proxy and dynamically handle calls to the proxy approach. In the dynamic proxy all calls made will be redirected to a single call processor on which the work is to reveal the type of call and determine the appropriate strategy. The following is an example of a dynamic proxy:
Interface and implementation class:
public interface Interface { void doSomething(); void somethingElse(String arg); } public class RealObject implements Interface { public void doSomething() { System.out.println("doSomething."); } public void somethingElse(String arg) { System.out.println("somethingElse " + arg); } }
Dynamic proxy object handler:
public class DynamicProxyHandler implements InvocationHandler { private Object proxyed; public DynamicProxyHandler(Object proxyed) { this.proxyed = proxyed; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { System.out.println("代理工作了."); return method.invoke(proxyed, args); } }
Test categories:
public class Main { public static void main(String[] args) { RealObject real = new RealObject(); Interface proxy = (Interface) Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[] {Interface.class}, new DynamicProxyHandler(real)); proxy.doSomething(); proxy.somethingElse("luoxn28"); } }
Output:
Proxy can be created by calling the static method Proxy.newProxyInstance () dynamic proxy, this method needs to be a class loader, a list of interfaces implemented in the agent (not a class or an abstract class), and InvocationHandler of a class you want to achieve. Dynamic proxy can redirect all calls to the call processor, it is often invokes the constructor processors pass a reference to the "real" objects, which will be called upon to perform intermediary processor tasks, it forwards the request.
To understand the principle of reflection, we must first understand what type of information. Let's Java runtime information identifying objects and classes, there are two ways: one is the traditional RTTI, which assumes we already know at compile-time type information for all; the other is a reflection mechanism that allows us find and use information on classes at runtime.
1, Class Objects
Understand how RTTI in Java, you first need to know the type of information at run time is how to represent, this is done by the Class object, which contains information about the class. Class object is used to create all the "normal" objects, Java objects to perform RTTI using the Class, even if you are performing a similar type conversion such operations.
Each class will have a corresponding Class object, is stored in the .class file. All classes are at the first time you use it, dynamically loaded into the JVM, when the program creates a static member of a class of reference, it will load the class. Class objects are only loaded when needed, static initialization is done when the class is loaded.
public class TestMain { public static void main(String[] args) { System.out.println(XYZ.name); } } class XYZ { public static String name = "luoxn28"; static { System.out.println("xyz静态块"); } public XYZ() { System.out.println("xyz构造了"); } }
The output is:
Class loader first checks whether the Class object of this class has been loaded before, if not already loaded, the default class loader will look for the corresponding .class file based on the class name.
Want to use run-time type information, you must obtain an object (such as a class Base objects) Class object reference, use the function Class.forName ( "Base") can achieve this purpose, or use base.class. Note that there is very interesting, use the function ".class" to create a Class object when the reference is not automatically initialize the Class object, using forName () will automatically initialize the Class object. In order to use the class to do the preparatory work generally have the following three steps:
- Load: By class loader, bytecode corresponding to find, create a Class object
- Links: class byte code verification, the space allocated for the static field
- Initialization: If the class has a superclass, then its initialization, performs static initialization and static initialization block
Base class {public static int NUM =. 1; static { System.out.println ( "Base" + NUM); } } public class the Main { public static void main (String [] args) { // does not initialize static blocks Class = Base.class of clazz1; System.out.println ( "------"); // initializes Class = clazz2 the Class.forName ( "zzz.Base"); } }
2, do first cast before checking
The compiler will check the legality of the type of downcast, if not legal to throw an exception. Before down conversion type, instanceof Analyzing be used.
class Base { } class Derived extends Base { } public class Main { public static void main(String[] args) { Base base = new Derived(); if (base instanceof Derived) { // 这里可以向下转换了 System.out.println("ok"); } else { System.out.println("not ok"); } } }
3, the reflection: the runtime class information
If you do not know the exact type of an object, RTTI can tell you, but there is a premise: this type must be known at compile time, in order to use RTTI to identify it. Class class libraries together with java.lang.reflect reflections of support, the library comprises a Field, Method and Constructor classes, these classes of objects created by the JVM startup to indicate members corresponding to the unknown class. In this case you can use Contructor create new objects, retrieve and modify the class field associated with the Field object get () and set () method, using invoke () method calls the method associated with a Method object. In addition, can also call getFields (), getMethods () and getConstructors () and many convenient method to returns an array of fields, methods, and constructors object, so the object information may be fully defined at run time, and at compile time you do not need to know anything about the class.
Reflection and there is no magic, when dealing with reflected by the object of an unknown type, JVM simply checking the object, to see which particular class it belongs to. Therefore, the class .class
for the JVM must be accessible, either, or obtained from the network on the local machine. So for the difference between the real and the reflective RTTI only comprising:
- RTTI, the compiler opens and checks .class file at compile time
- Reflective, open and runtime checking .class file
public class Person implements Serializable { private String name; private int age; // get/set方法 } public static void main(String[] args) { Person person = new Person("luoxn28", 23); Class clazz = person.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { String key = field.getName(); PropertyDescriptor descriptor = new PropertyDescriptor(key, clazz); Method method = descriptor.getReadMethod(); Object value = method.invoke(person); System.out.println(key + ":" + value); } }
By the above getReadMethod () get class method calls the function can be invoked by the class method set getWriteMethod () method. Generally speaking, we do not need to use the reflection tools, but they are creating dynamic code will be more useful reflection to support other features in Java, such as serialized JavaBean objects and so on.
4, dynamic proxies
Proxy mode in order to provide additional or different operations, and used to replace the inserted object "real" objects, these operations involve communication "real" object and thus act as an intermediary agent typically role. Java's dynamic proxy agent more than a step forward ideas, it can dynamically create and proxy and dynamically handle calls to the proxy approach. In the dynamic proxy all calls made will be redirected to a single call processor on which the work is to reveal the type of call and determine the appropriate strategy. The following is an example of a dynamic proxy:
Interface and implementation class:
public interface Interface { void doSomething(); void somethingElse(String arg); } public class RealObject implements Interface { public void doSomething() { System.out.println("doSomething."); } public void somethingElse(String arg) { System.out.println("somethingElse " + arg); } }
Dynamic proxy object handler:
public class DynamicProxyHandler implements InvocationHandler { private Object proxyed; public DynamicProxyHandler(Object proxyed) { this.proxyed = proxyed; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { System.out.println("代理工作了."); return method.invoke(proxyed, args); } }
测试类:
public class Main { public static void main(String[] args) { RealObject real = new RealObject(); Interface proxy = (Interface) Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[] {Interface.class}, new DynamicProxyHandler(real)); proxy.doSomething(); proxy.somethingElse("luoxn28"); } }
Output:
Proxy can be created by calling the static method Proxy.newProxyInstance () dynamic proxy, this method needs to be a class loader, a list of interfaces implemented in the agent (not a class or an abstract class), and InvocationHandler of a class you want to achieve. Dynamic proxy can redirect all calls to the call processor, it is often invokes the constructor processors pass a reference to the "real" objects, which will be called upon to perform intermediary processor tasks, it forwards the request.