久久综合九色综合97婷婷-美女视频黄频a免费-精品日本一区二区三区在线观看-日韩中文无码有码免费视频-亚洲中文字幕无码专区-扒开双腿疯狂进出爽爽爽动态照片-国产乱理伦片在线观看夜-高清极品美女毛茸茸-欧美寡妇性猛交XXX-国产亚洲精品99在线播放-日韩美女毛片又爽又大毛片,99久久久无码国产精品9,国产成a人片在线观看视频下载,欧美疯狂xxxx吞精视频

有趣生活

當(dāng)前位置:首頁(yè)>職場(chǎng)>java枚舉法的用法(從一道面試題開始說(shuō)起)

java枚舉法的用法(從一道面試題開始說(shuō)起)

發(fā)布時(shí)間:2024-01-24閱讀(13)

導(dǎo)讀前段時(shí)間在dota群,一哥們出去面試,回顧面試題的時(shí)候,說(shuō)問(wèn)到了枚舉。作為一名Android選手,談到枚舉,那肯定是:Android上不應(yīng)該使用枚舉,占內(nèi)存....

前段時(shí)間在dota群,一哥們出去面試,回顧面試題的時(shí)候,說(shuō)問(wèn)到了枚舉。

作為一名Android選手,談到枚舉,那肯定是:

Android上不應(yīng)該使用枚舉,占內(nèi)存,應(yīng)該使用@XXXDef注解來(lái)替代,balabala…

這么一回答,心里美滋滋。

沒想到面試官問(wèn)了句:

  • 枚舉的原理是什么?你說(shuō)它占內(nèi)存到底占多少內(nèi)存呢,如何佐證?

聽到這就慌了,沒了解過(guò)呀。

下面說(shuō)第一個(gè)問(wèn)題(沒錯(cuò)還有第二個(gè)問(wèn)題)。

枚舉的本質(zhì)

有篇文章:

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

寫得挺好的。

下面還是要簡(jiǎn)述一下,我們先寫個(gè)枚舉類:

public Enum Animal {

DOG,CAT

}

  • 1
  • 2
  • 3

看著這代碼,完全看不出來(lái)原理。不過(guò)大家應(yīng)該都知道java類編譯后會(huì)產(chǎn)生class文件。

越接近底層,本質(zhì)就越容易暴露出來(lái)了。

我們先javac搞到Animal.class,然后通過(guò)javap命令看哈:

javap Animal.class

  • 1

輸出:

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 {};

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

其實(shí)到這里我們已經(jīng)大致知道枚舉的本質(zhì)了,實(shí)際上我們編寫的枚舉類Animal是繼承自Enum的,每個(gè)枚舉對(duì)象都是static final的類對(duì)象。

還想知道更多的細(xì)節(jié)怎么辦,比如我們的對(duì)象什么時(shí)候初始化的。

我們可以添加-c參數(shù),對(duì)代碼進(jìn)行反編譯。

你可以使用javap -help 查看所有參數(shù)的含義。

javap -c Animal.class

  • 1

輸出:

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

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

好了,現(xiàn)在可以分析代碼了。

但是,這代碼看起來(lái)也太頭疼了,我們先看一點(diǎn)點(diǎn):

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;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

大致含義就是new Animal(String,int),然后給我們的靜態(tài)常量DOG賦值。

好了,不看了,好煩。我們轉(zhuǎn)念想一下,如果這個(gè)字節(jié)碼咱們能看懂,那就是有規(guī)則的,只要有規(guī)則,肯定有類似翻譯類的工具,直接轉(zhuǎn)成java代碼的。

確實(shí)有,比如jad:

http://www.javadecompilers.com/jad

我們先下載一份,很小:

java枚舉法的用法(從一道面試題開始說(shuō)起)(1)

命令也很簡(jiǎn)單,執(zhí)行:

./jad -sjava Animal.class

  • 1

就會(huì)在當(dāng)前目錄生成java文件了。

輸出如下:

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

});

}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

到這,我相信你知道我們編寫的枚舉類:

public enum Animal {

DOG,CAT

}

  • 1
  • 2
  • 3

最終生成是這樣的類,那么對(duì)應(yīng)的我們所使用的方法也就都明白了。此外,你如何拿這樣的類,跟兩個(gè)靜態(tài)INT常量比內(nèi)存,那肯定是多得多的。

其次,我們也能順便回答,枚舉對(duì)象為什么是單例了。

并且其Enum類中對(duì)readObject和clone方法都進(jìn)行了實(shí)現(xiàn),看一眼你就明白了。

本文并不是為了去討論枚舉的原理,而是想要給大家說(shuō)明的是很多“語(yǔ)法糖”類似的東西,都能按照這樣的思路去了解它的原理。

下面我們?cè)倏匆粋€(gè),聽起來(lái)稍微高端一點(diǎn)的:

  • 動(dòng)態(tài)代理

動(dòng)態(tài)代理

這個(gè)比較出名的就是retrofit了。

問(wèn):retrofit的原理是?

答:基于動(dòng)態(tài)代理,然后balabal...

問(wèn):那么動(dòng)態(tài)代理的原理是?

答:...

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我們依然從一個(gè)最簡(jiǎn)單的例子開始。

我們寫一個(gè)接口:

public interface IUserService{

void login(String username, String password);

}

  • 1
  • 2
  • 3

然后,利用動(dòng)態(tài)代理去生成一個(gè)代理對(duì)象,去調(diào)用login方法:

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");

}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

好了,這應(yīng)該是最簡(jiǎn)單的動(dòng)態(tài)代理的例子了。

當(dāng)我們?nèi)フ{(diào)研userService.login方法,你會(huì)發(fā)現(xiàn)InvocationHandler的invoke方法調(diào)用了,并且輸出了相關(guān)信息。

怎么會(huì)這么神奇呢?

我們寫了一個(gè)接口,就能產(chǎn)生一個(gè)該接口的對(duì)象,然后我們還能攔截它的方法。

繼續(xù)看:

先javac Test.java,得到class文件。

然后調(diào)用:

java Test

  • 1

輸出:

class com.sun.proxy.$Proxy0

method = login , args = [zhy, 123]

  • 1
  • 2

可以看到當(dāng)我們調(diào)用login方法的時(shí)候,invoke中攔截到了我們的方法,參數(shù)等信息。

retrofit的原理其實(shí)就是這樣,攔截到方法、參數(shù),再根據(jù)我們?cè)诜椒ㄉ系淖⒔猓テ唇訛橐粋€(gè)正常的Okhttp請(qǐng)求,然后執(zhí)行。

想知道原理,根據(jù)我們枚舉中的經(jīng)驗(yàn),肯定想看看這個(gè)

com.sun.proxy.$Proxy0 // userService對(duì)象輸出的全路徑

  • 1

這個(gè)類的class文件如何獲取呢?

很簡(jiǎn)單,你在main方法的第一行,添加:

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

  • 1
  • 2

然后重新編譯、執(zhí)行,就會(huì)在當(dāng)前目錄看到了。

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

然后,還想通過(guò)javap -c來(lái)看么~~

還是拿出我們剛才下載的jad吧。

執(zhí)行:

./jad -sjava com/sun/proxy/$Proxy0.class

  • 1

在jad的同目錄,你就發(fā)現(xiàn)了Proxy0的java文件了:

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")

});

}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

為了便于理解,刪除了一些equals,hashCode等方法。

你可以看到,實(shí)際上為我們生成一個(gè)實(shí)現(xiàn)了IUserSevice的類,我們調(diào)用其login方法,實(shí)際上就是調(diào)用了:

super.h.invoke(this, m3, new Object[] {

s, s1

});

  • 1
  • 2
  • 3

m3即為我們的login方法,靜態(tài)塊中初始化的。剩下是我們傳入的參數(shù)。

那我們看super.h是什么:

package java.lang.reflect;

public class Proxy{

protected InvocationHandler h;

}

  • 1
  • 2
  • 3
  • 4

就是我們自己創(chuàng)建的InvocationHandler對(duì)象。

看著這個(gè)類,再想login方法,為什么會(huì)回調(diào)到InvocationHandler的invoke方法,你還覺得奇怪么~~

好了,實(shí)際上這個(gè)哥們面試距離現(xiàn)在挺久了,終于抽空寫完了,希望大家有一定的收獲~

java枚舉法的用法(從一道面試題開始說(shuō)起)(2)

TAGS標(biāo)簽:  java  舉法  用法  一道  試題  java枚舉法的用法

歡迎分享轉(zhuǎn)載→http://www.avcorse.com/read-234030.html

Copyright ? 2024 有趣生活 All Rights Reserve吉ICP備19000289號(hào)-5 TXT地圖HTML地圖XML地圖