Enumeration, dynamic proxy principle, enumeration dynamic proxy
Some time ago in the dota group, a buddy went out for an interview and reviewed the interview questions.
As an Android player, when talking about enumeration, it must be:
Enumeration should not be used on Android, occupying the memory, instead of using the @ XXXDef annotation, balabala...
The answer is fascinating.
I did not expect the interviewer to ask:
What is the principle of enumeration? How much memory does it occupy?
I am panic when I hear this. I have never understood it.
The first question is as follows (yes, there is a second question ).
Nature of enumeration
Well written.
Next, let's briefly describe an enumeration class:
public enum Animal { DOG,CAT}
Looking at this code, we can't see how it works. However, we should all know that the class file will be generated after the java class is compiled.
The closer it is to the underlying layer, the more likely it is to be exposed.
We first get Animal. class through javac, and then use the javap command to see:
javap Animal.class
Output:
public final class Animal extends java.lang.Enum { public static final Animal DOG; public static final Animal CAT; public static Animal[] values(); public static Animal valueOf(java.lang.String); static {};}
In fact, we have already roughly understood the nature of enumeration. In fact, the enumerated Animal class we wrote inherits from Enum, and each enumerated object is a static final class object.
I also want to know more details, such as when our object is initialized.
We can add the-c parameter to decompile the code.
You can use javap-help to view the meaning of all parameters.
javap -c Animal.class
Output:
public final class Animal extends java.lang.Enum { public static final Animal DOG; public static final Animal CAT; public static Animal[] values(); Code: 0: getstatic #1 // Field $VALUES:[LAnimal; 3: invokevirtual #2 // Method "[LAnimal;".clone:()Ljava/lang/Object; 6: checkcast #3 // class "[LAnimal;" 9: areturn public static Animal valueOf(java.lang.String); Code: 0: ldc #4 // class Animal 2: aload_0 3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 6: checkcast #4 // class Animal 9: areturn static {}; Code: 0: new #4 // class Animal 3: dup 4: ldc #7 // String DOG 6: iconst_0 7: invokespecial #8 // Method "
":(Ljava/lang/String;I)V 10: putstatic #9 // Field DOG:LAnimal; 13: new #4 // class Animal 16: dup 17: ldc #10 // String CAT 19: iconst_1 20: invokespecial #8 // Method "
":(Ljava/lang/String;I)V 23: putstatic #11 // Field CAT:LAnimal; 26: iconst_2 27: anewarray #4 // class Animal 30: dup 31: iconst_0 32: getstatic #9 // Field DOG:LAnimal; 35: aastore 36: dup 37: iconst_1 38: getstatic #11 // Field CAT:LAnimal; 41: aastore 42: putstatic #1 // Field $VALUES:[LAnimal; 45: return}
Now you can analyze the code.
However, this code also seems a headache. Let's take a look at it first:
Static code:
0: new #4 // class Animal3: dup4: ldc #7 // String DOG6: iconst_07: invokespecial #8 // Method "
":(Ljava/lang/String;I)V10: putstatic #9 // Field DOG:LAnimal;
The general meaning is new Animal (String, int), and then assign a value to our static constant DOG.
Okay. Don't read it. It's so annoying. Let's think about it. If we can understand this bytecode, there are rules. As long as there are rules, there must be tools similar to the translation class, which can be directly converted into java code.
Yes, for example, jad:
Download a copy first, very small:
The command is also very simple. Execute:
./jad -sjava Animal.class
The java file is generated in the current directory.
The output is as follows:
public final class Animal extends Enum{ public static Animal[] values() { return (Animal[])$VALUES.clone(); } public static Animal valueOf(String s) { return (Animal)Enum.valueOf(Animal, s); } private Animal(String s, int i) { super(s, i); } public static final Animal DOG; public static final Animal CAT; private static final Animal $VALUES[]; static { DOG = new Animal("DOG", 0); CAT = new Animal("CAT", 1); $VALUES = (new Animal[] { DOG, CAT }); }}
At this point, I believe you know the enumerated classes we have compiled:
public enum Animal { DOG,CAT}
The final generation is such a class, so we will understand the corresponding methods. In addition, how do you use such a class, compared with the memory of two static INT constants, is certainly much more.
Secondly, we can also answer by the way why the enumerated object is a singleton.
In addition, the readObject and clone methods are implemented in the Enum class. You can see at a glance.
This article is not intended to discuss the enumeration principle, but to show you that many similar things like "syntactic sugar" can follow these ideas to understand its principles.
Let's look at another one, which sounds a little more advanced:
Dynamic proxy dynamic proxy
This is also famous for its wide fit.
Q: What is the principle of fit? A: Based on Dynamic proxy, and then balabal... Q: What is the principle of dynamic proxy? A :...
We start with a simple example.
Let's write an interface:
public interface IUserService{ void login(String username, String password);}
Then, use the dynamic proxy to generate a proxy object and call the login method:
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;public class Test{ public static void main(String[] args){ IUserService userService = (IUserService) Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("method = " + method.getName() +" , args = " + Arrays.toString(args)); return null; } }); System.out.println(userService.getClass()); userService.login("zhy","123"); }}
Well, this should be the simplest example of dynamic proxy.
When we investigate the userService. login method, you will find that the InvocationHandler's invoke method is called and relevant information is output.
Why is it so amazing?
We have written an interface to generate an object for this interface, and then we can intercept its methods.
Continue Viewing:
FirstJavac Test. java to get the class file.
Then call:
java Test
Output:
class com.sun.proxy.$Proxy0method = login , args = [zhy, 123]
We can see that when we call the login method, invoke intercepts our method, parameters, and other information.
The principle of adaptive fit is actually like this. We Intercept methods and parameters, splice them into a normal Okhttp request based on our annotations on the method, and then execute them.
I want to know the principle. Based on our enumeration experience, I 'd like to see this.
Com. sun. proxy. $ Proxy0 // full path of userService object output
How can I obtain the class file of this class?
In the first line of the main method, add:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Then re-compile and execute the command, and you will see it in the current directory.
MacBook-Pro:tmp zhanghongyang01$ tree .├── IUserService.class├── IUserService.java├── Test$1.class├── Test.class├── Test.java└── com └── sun └── proxy └── $Proxy0.class3 directories, 6 files
Then, you wantJavap-c ~~
Let's take out the jad we just downloaded.
Run:
./jad -sjava com/sun/proxy/\$Proxy0.class
In the same directory of jad, you will find the java file of Proxy0:
package com.sun.proxy;import IUserService;import java.lang.reflect.*;public final class $Proxy0 extends Proxy implements IUserService{ public $Proxy0(InvocationHandler invocationhandler) { super(invocationhandler); } public final void login(String s, String s1) { super.h.invoke(this, m3, new Object[] { s, s1 }); } private static Method m3; static { m3 = Class.forName("IUserService").getMethod("login", new Class[] { Class.forName("java.lang.String"), Class.forName("java.lang.String") }); }}
For ease of understanding, some equals, hashCode, and other methods are deleted.
As you can see, we actually generate a class that implements IUserSevice for us. We call its login method, which is actually called:
super.h.invoke(this, m3, new Object[] { s, s1 });
M3 is our login method, initialized in the static block. The rest are our input parameters.
Let's see what super. h is:
package java.lang.reflect;public class Proxy{ protected InvocationHandler h;}
Is the InvocationHandler object we have created.
Looking at this class and thinking about the login method, why do you still find it strange to call back the InvocationHandler's invoke method ~~
Well, in fact, this guy has been interviewing for a long time and finally finished writing it out. I hope you will have some gains ~