Type information
What is RTTI?
RTTI is Runtime Type Information. As the name suggests, it is the information that identifies objects and classes at runtime. There are two kinds of RTTI. One is "traditional" RTTI, which assumes that we already know all types at compile time; The other is the "reflection" mechanism, which allows us to discover and use class information at run time.
The simplest example is:
List<Shape> shapeList = Arrays.asList(new Circle(), new Square(), new Triangle()); for(Shape shape : shapeList) { shape.draw(); }
When an element is taken from the shapeList (in fact, the element is saved in an array, and the array will hold all elements as objects), the element will be automatically transformed back to the corresponding Object. In other words, the following two processes will occur after saving and retrieving objects:
(Object) shape // Upward transformation (Shape) object // Downward transformation
This is actually the most basic form of RTTI. In Java, all type conversions are checked for type correctness at run time.
Class object: class information
Class object contains information related to class. In fact, class object is used to create all objects in this class in Java. Every time the compilation is completed, a class object will be generated and saved in Class file. The JVM uses ClassLoader to load objects. All classes are dynamically loaded into the JVM after they are used for the first time (static members are referenced, except static constants) or after the object is created with the new keyword. The so-called dynamic loading is to load when it is used.
Class.forName("package.name.CanonicalName");
This method is a method to get the reference of Class object and call Class After forname(), the Class will be initialized. There is also a newInstance() method in the Class object, which can be used to create a new instance of the object. In addition, there are many practical methods in Class object to obtain Class information, such as Class interface, method, member variable and so on.
Class literals
We can also use it Class to reference the class object.
Class intClass = int.class;
Generalized Class reference
Starting with Java SE 5, we can use generics to type qualify Class objects.
Class<Integer> intClass = int.class; // legal intClass = double.class; // illegal
instanceof
In addition to ensuring the correctness of type conversion and obtaining runtime type information through Class objects, RTTI also has a third form, instanceof. We can use this keyword to determine whether an object is an instance of a Class, such as:
Animal[] animals = {new Dog(), new Fish()}; for (Animal animal : animals) { // Before downward transformation, use instanceof to judge the type if (animal instanceof Fish) { Fish fish = (Fish) animal; fish.swim(); } }
Reflection: omnipotent
RTTI will be opened and checked at compile time Class file and use this information to do something useful, and reflection will be opened and checked at run time Class file, which is the real difference between RTTI and reflection. Java through class and Java The lang.reflect class library supports the concept of reflection. Let's take a look at this Code:
public static void main(String[] args) throws Exception { // At compile time, class The result of forname() is unknowable, and the runtime information can only be obtained through reflection Class<?> klass = Class.forName(args[0]); Method[] methods = klass.getMethods(); for (Method method : methods) { System.out.println(method); } }
In the above main() method, instantiate the Class object by reading the command line parameters, and then print the methods in the object. At this time, the klass object is completely unknown, but we can get the information through reflection, create objects or call methods.
Reflection is pervasive. Neither private methods nor private inner class methods, even anonymous class methods, can escape the call of reflection. The same is true for private domains. Only the final domain will not be modified. Reflection can be said to leave a back door for our program, but in general, from the comparison of advantages and disadvantages brought by reflection, the advantages outweigh the disadvantages.
Use occasion
It is impossible to know which classes the object or class may belong to at compile time. The program only depends on the runtime information to find the real information of the object and class.
Main role
Through reflection, the program code can access the internal information of the class loaded into the JVM, obtain the attribute information of the loaded class, obtain the method of the loaded class, and obtain the construction method information of the loaded class
advantage
Reflection improves the flexibility and expansibility of Java programs, reduces coupling and improves adaptability. It allows programs to create and control objects of any class without hard coding the target class in advance
Reflection defect
- Performance first
Reflection includes some dynamic types, so the JVM cannot optimize this code. Therefore, reflection operations are much less efficient than those non reflection operations. We should avoid using reflection in code that is often executed or in programs that require high performance.
- Internal exposure
Since reflection allows code to perform operations that are not allowed under normal circumstances (such as accessing private properties and methods), the use of reflection may lead to unexpected side effects - code has functional errors and reduces portability. Reflective code destroys abstraction, so when the platform changes, the behavior of the code may also change.
- Using reflection blurs the internal logic of the program
Programmers hope to see in the source code that the logic and reflection of the program bypass the technology of the source code, which will bring maintenance problems. Reflective code is more complex than the corresponding direct code.
- Security restrictions
Using reflection technology requires that the program must run in an environment without security restrictions. If a program must run in an environment with security restrictions, such as an Applet, then this is a problem
Dynamic agent
Agent is one of the most common design patterns, which can make our code more flexible, such as doing some extra work before operation. Here is an example of a simple proxy:
interface Interface { void doSomething(); void doSomethingElse(String args); } class RealObject implements Interface { @Override public void doSomething() { System.out.println("doSomething"); } @Override public void doSomethingElse(String args) { System.out.println("doSomethingElse " + args); } } class SimpleProxy implements Interface { Interface mInterface; public SimpleProxy(Interface anInterface) { mInterface = anInterface; } @Override public void doSomething() { System.out.println("SimpleProxy doSomething"); mInterface.doSomething(); } @Override public void doSomethingElse(String args) { System.out.println("SimpleProxy doSomethingElse"); mInterface.doSomethingElse(args); } }
In short, proxy is to separate the method calls of the actual object, allowing us to modify some operations or add additional operations. The idea of dynamic proxy goes further than that of proxy. It allows us to dynamically create proxy and dynamically process the calls to the proxy methods.
public class SimpleDynamicProxy { public static void consumer(Interface interf) { interf.doSomething(); interf.doSomethingElse("args"); } public static void main(String[] args) { System.out.println("---------- no proxy ----------"); RealObject real = new RealObject(); consumer(real); System.out.println("---------- simple proxy ----------"); consumer(new SimpleProxy(real)); System.out.println("---------- dynamic proxy ----------"); // Create proxy object: set ClassLoader, interface Class object and InvocationHandler Interface proxy = (Interface) Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[]{Interface.class}, // The Class object in the array must be an interface and cannot be repeated new DynamicProxyHandler(real)); consumer(proxy); } /** * The proxy object must implement its own InvocationHandler, and all calls will be redirected to the call processor */ static class DynamicProxyHandler implements InvocationHandler { /** * For the proxy object, the call request will be forwarded to the "actual" object */ private Object proxied; public DynamicProxyHandler(Object proxied) { this.proxied = proxied; } /** * The method receives three parameters: the instance of the proxy object, the instance of the called method and the parameters of the method. * We generally determine the measures to be taken when calling this method here. */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args); // If the method parameter is not empty, the parameter is output if (args != null) { for (int i = 0; i < args.length; i++) { System.out.println("args[" + i + "] = " + args[i]); } } // Forward the request to the proxy object return method.invoke(proxied, args); } } }
Using dynamic proxy is actually very simple. First define your own InvocationHandler, and then use proxy Newproxyinstance creates a proxy object.
Advantages of using dynamic agents
Dynamic agents have two main advantages:
- More flexibility. We do not need to specify a proxy class to represent a proxy object when designing and implementing, but we can delay this designation until the program runs and the JVM can implement it.
- Dynamic agent is more unified and concise.
The first point can be seen from the above example. We must determine SimpleProxy as the proxy of the Interface in advance and write the corresponding code of each method, while the dynamic proxy allows us to generate any type of dynamic proxy class by using the reflection mechanism. Second, it is also obvious that when using dynamic agents, we only need to modify in one place and write less code.