To understand the principle of reflection, you must first understand what type of information is. Java allows us to identify the information of objects and classes at runtime, and there are two main ways: one is the traditional RTTI, which assumes that we already know all type information at compile time; the other is the reflection mechanism, which allows us to discover and use the information of classes at runtime .
1. Class object
To understand how RTTI works in Java, you first need to know how type information is represented at runtime. This is done by Class objects, which contains information related to classes. Class objects are used to create all "regular" objects. Java uses Class objects to perform RTTI, even if you are performing operations like type conversion.
Each class will generate a corresponding Class object, which is saved in a .class file. All classes are loaded dynamically to the JVM when they are used for the first time. This class will be loaded when the program creates a reference to a static member of the class. Class objects are loaded only when needed, and static initialization is performed 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 static block");}} public XYZ() {System.out.println("xyz constructed");}}The output result is:
The class loader first checks whether the Class object of this class has been loaded. If it has not been loaded, the default class loader will look up the corresponding .class file based on the class name.
To use type information at runtime, you must obtain a reference to the Class object of the object (such as a Base object). You can use the function Class.forName("Base") to achieve this, or use base.class. Note that it is interesting. When using the function ".class" to create a reference to a Class object, the Class object will not be automatically initialized, and using forName() will automatically initialize the Class object. Preparations for using classes generally have the following 3 steps:
•Loading: Completed by the class loader, find the corresponding bytecode, and create a Class object
•Link: Verify the bytecode in the class and allocate space for the static domain
•Initialization: If the class has a superclass, it is initialized, and the static initializer and static initialization block are executed.
public class Base {static int num = 1;static {System.out.println("Base " + num);}}public class Main {public static void main(String[] args) {// static blocks will not be initialized Class clazz1 = Base.class;System.out.println("------");// Class clazz2 = Class.forName("zzz.Base");}} 2. Check before type conversion
The compiler will check if the type downward transition is legal, and if it is not legal, an exception will be thrown. Before converting the type down, you can use instanceof to judge.
class Base { }class Derived extends Base { }public class Main {public static void main(String[] args) {Base base = new Derived();if (base instanceof Derived) {// Here you can down-convert System.out.println("ok");}else {System.out.println("not ok");}}} 3. Reflection: Runtime information
If you don't know the exact type of an object, RTTI can tell you, but there is a prerequisite: this type must be known at compile time so that RTTI can be used to identify it. The Class class supports reflection together with the java.lang.reflect class library. This class library contains Field, Method and Constructor classes. The objects of these classes are created by the JVM at startup to represent the corresponding members in unknown classes. In this way, you can use the Contructor to create a new object, use the get() and set() methods to obtain and modify the fields associated with the Field object in the class, and use the invoke() method to call the method associated with the Method object. In addition, many convenient methods such as getFields(), getMethods() and getConstructors() can also be called to return an array representing fields, methods, and constructor objects. In this way, the object information can be completely determined at runtime without knowing anything about the class at compile time.
There is nothing magical about the reflection mechanism. When dealing with an object of unknown type through reflection, the JVM simply checks the object to see which specific class it belongs to. Therefore, the .class of that class must be fetchable for the JVM, either on the local machine or from the network. So the real difference between RTTI and reflection is only:
•RTTI, the compiler opens and checks .class files at compile time
•Reflect, open and check .class files at runtime
public class Person implements Serializable {private String name;private int age;// get/set method}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);}} The above call the get function of the class through the getReadMethod() method, and the getWriteMethod() method can be used to call the set method of the class. Generally speaking, we don't need to use reflection tools, but they are more useful in creating dynamic code. Reflection is used in Java to support other features such as object serialization and JavaBeans.
4. Dynamic Agent
The proxy mode is to provide additional or different operations, and the inserted objects are used to replace "actual" objects, which involve communication with "actual" objects, so the proxy usually acts as an intermediary. Java's dynamic proxy is one step ahead of the idea of proxy, which can dynamically create and proxie and handle calls to the proxy methods dynamically. All calls made on the dynamic proxy are redirected to a single call processor, and its job is to reveal the type of the call and determine the corresponding policy. Here is an example of a dynamic proxy:
Interface and implementation classes:
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 processor:
public class DynamicProxyHandler implements InvocationHandler {private Object proxyed;public DynamicProxyHandler(Object proxyed) {this.proxyed = proxyed;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {System.out.println("The proxy is working.");return method.invoke(proxyed, args);}} Test class:
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");}}The output result is as follows:
You can create a dynamic proxy by calling Proxy static method Proxy.newProxyInstance(). This method requires a class loader, a list of interfaces you want the proxy to implement (not a class or abstract class), and an implementation class of InvocationHandler. The dynamic proxy can redirect all calls to the calling processor, so the constructor of the caller usually passes a reference to the "actual" object, which will forward the request when the caller performs the mediation task.
The above is the in-depth understanding of Java reflection introduced by the editor to you. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. Thank you very much for your support to Wulin.com website!