Start with an interview question to talk about the principle of enumeration and dynamic proxy

This article has been originally published on my public account hongyangAndroid.
Reprint please indicate the source:
This article comes from: Zhang Hongyang's blog

Some time ago in the dota group, a buddy went out for an interview. When reviewing the interview questions, he said that he asked about enumeration.

As an Android player, when it comes to enums, it must be:

Enumeration should not be used on Android, it takes up memory, and the @XXXDef annotation should be used instead, balabala...

Such an answer makes me happy.

Unexpectedly, the interviewer asked:

  • What is the principle of enumeration? How much memory do you say it occupies in memory? How to prove it?

I panicked when I heard this, I didn't understand it.

Let's talk about the first question (yes, there is a second question).

The nature of enumeration

There is an article:

http://blog.csdn.net/mhmyqn/article/details/48087247

Well written.

Let's briefly describe it, let's write an enumeration class first:

public enum Animal {
    DOG,CAT
}

Looking at this code, I can't see the principle at all. But everyone should know that java classes will generate class files after compiling.

The closer you get to the bottom, the easier it is to expose the essence.

Let's first javac get Animal.class, and then look at it through the javap command:

javap Animal.class

output:

public final class Animal extends java.lang.Enum<Animal> {
  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 roughly understood the essence of enumeration here. In fact, the enumeration class Animal we wrote is inherited from Enum, and each enumeration object is a static final class object.

Also would like to know more details about what to do, such as when our objects are initialized.

We can add the -c parameter to decompile the code.

You can use javap -help to see the meaning of all parameters.

javap -c Animal.class

output:

public final class Animal extends java.lang.Enum<Animal> {
  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 "<init>":(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 "<init>":(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
}

Alright, now it's time to analyze the code.

However, this code also looks too headache, let's look at it a little bit:

Part of the code in static:

0: new           #4                  // class Animal
3: dup
4: ldc           #7                  // String DOG
6: iconst_0
7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
10: putstatic     #9                  // Field DOG:LAnimal;

The general meaning is new Animal(String,int), and then assign a value to our static constant DOG.

Well, don't watch it, it's 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 like translation, which can be directly converted into java code.

There are indeed, such as jad:

http://www.javadecompilers.com/jad

Let's download a copy first, which is very small:

meiju01.png

The command is also very simple, execute:

./jad -sjava Animal.class

The java file will be 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
        });
    }
}

By this point, I'm sure you know the enum class we wrote:

public enum Animal {
    DOG,CAT
}

The final generation is such a class, then the corresponding methods we use will be understood. Also, how do you take a class like this, with two static int constants than memory, that must be a lot more.

Secondly, we can also answer by the way, why the enumeration object is a singleton.

And the readObject and clone methods are implemented in its Enum class, you will understand at a glance.

This article is not to discuss the principle of enumeration, but to explain to you a lot of "syntactic sugar" and similar things, and you can understand its principle in this way.

Let's look at another one, which sounds a little high-end:

  • Dynamic proxy

Dynamic proxy

The most famous one is retrofit.

问:retrofit的原理是?

答:基于动态代理,然后balabal...

问:那么动态代理的原理是?

答:...

We're still starting with the simplest example.

We 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 a dynamic proxy.

When we investigate the userService.login method, you will find that the invoke method of InvocationHandler is called and relevant information is output.

How can it be so amazing?

We write an interface, we can generate an object of the interface, and then we can intercept its methods.

Keep reading:

First javac Test.java, get the class file.

Then call:

java Test

output:

class com.sun.proxy.$Proxy0
method = login , args = [zhy, 123]

It can be seen that when we call the login method, our method, parameters and other information are intercepted in invoke.

The principle of retrofit is actually like this, intercepting methods and parameters, and then splicing them into a normal Okhttp request according to our annotations on the method, and then executing them.

Want to know the principle, based on the experience in our enumeration, definitely want to see this

com.sun.proxy.$Proxy0 // userService对象输出的全路径

How to get the class file of this class?

Very simple, in the first line of the main method, add:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");  

Then recompile and execute, 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.class

3 directories, 6 files

Then, do you still want to javap -csee it through?

write picture description here

Or take out the jad we just downloaded.

implement:

./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 have been removed.

As you can see, a class that implements IUserSevice is actually generated for us, and when we call its login method, we actually call:

 super.h.invoke(this, m3, new Object[] {
            s, s1
        }); 

m3 is our login method, initialized in the static block. The rest are the parameters we pass in.

Then let's see what super.h is:

package java.lang.reflect;
public class Proxy{
    protected InvocationHandler h;
}

It is the InvocationHandler object we created ourselves.

Looking at this class, and thinking about the login method, why does it call back to the invoke method of the InvocationHandler, do you still feel strange~~

Well, in fact, this buddy has been interviewing for a long time now, and he finally took the time to write it. I hope everyone has a certain harvest~

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325685479&siteId=291194637