java 枚举类型分析
最近做android开发,需要用到枚举值,这样可以连续赋值,我按之前c++那样书写,如下所示:
public enum ColorSelect {
RED_BAGE = 0,
GREEN_BAGE,
BLUE_BAGE;
}
编译不过。
我将赋值语句修改了以后,如下所示:
public enum ColorSelect {
RED_BAGE ,
GREEN_BAGE,
BLUE_BAGE;
}
编译通过。说明C++那样的赋值方法不适用java。所以,我需要弄清楚的是:
1. 在java平台上,如何初始化枚举值。
2.像上述那样的枚举类型ColorSelect,没有赋值,为什么switch(ColorSelect) 可以运行?它是通过字符串还是整数值来匹配枚举类型。
为了弄清楚上述问题,我自己就写了个示范程序,将该程序编译后,看看结果,是否可以找到答案。
一. 如何初始化枚举值
下面是我的示范程序:
自己定义的枚举类:
public enum ColorSelect { //定义枚举类ColorSelect
RED_BAGE , //定义三个枚举对象
GREEN_BAGE,
BLUE_BAGE;
}
通过反编译,这段代码反编译后的程序是:
public final class ColorSelect extends Enum
{ public static ColorSelect valueOf(String s) //公有函数,该方法通过字符串获取对应的ColorSelect 对象
{
return (ColorSelect)Enum.valueOf(test/Enum/TestEnum$ColorSelect, s);
} public static ColorSelect[] values() //公有函数该方法获取所有的ColorSelect对象
{
ColorSelect acolorselect[] = ENUM$VALUES; //ENUM$VALUES是ColorSelect数组,下面的代码会将其进行初始化
int i = acolorselect.length;
ColorSelect acolorselect1[] = new ColorSelect[i];
System.arraycopy(acolorselect, 0, acolorselect1, 0, i);
return acolorselect1;
} public static final ColorSelect BLUE_BAGE; // 定义公共成员BLUE_BAGE,属于ColorSelect对象
private static final ColorSelect ENUM$VALUES[]; //定义私有成员ENUM$VALUES[],属于ColorSelect数组
public static final ColorSelect GREEN_BAGE; // 定义公共成员GREEN_BAGE,属于ColorSelect对象
public static final ColorSelect RED_BAGE; // 定义公共成员RED_BAGE,属于ColorSelect对象
static
{//static里面这段代码,将上面定义的是三个公共成员ColorSelect对象进行初始化,然后将他们全部赋值给ENUM$VALUES
RED_BAGE = new ColorSelect("RED_BAGE", 0); //初始化BLUE_BAGE
GREEN_BAGE = new ColorSelect("GREEN_BAGE", 1); //初始化GREEN_BAGE
BLUE_BAGE = new ColorSelect("BLUE_BAGE", 2); //初始化BLUE_BAGE
ColorSelect acolorselect[] = new ColorSelect[3];
ColorSelect colorselect = RED_BAGE;
acolorselect[0] = colorselect;
ColorSelect colorselect1 = GREEN_BAGE;
acolorselect[1] = colorselect1;
ColorSelect colorselect2 = BLUE_BAGE;
acolorselect[2] = colorselect2;
ENUM$VALUES = acolorselect;
} private ColorSelect(String s, int i) //私有函数,调用父类进行初始化ColorSelect对象
{
super(s, i);
}
}
从该反编译的代码看,有两个公有成员函数:
public static ColorSelect valueOf(String s); 该方法通过字符串获取对应的ColorSelect 对象
public static ColorSelect[] values(); 该方法获取所有的ColorSelect 对象
这两种方法,在java里面可以直接调用。
下面的是ColorSelect的数据成员。
public static final ColorSelect BLUE_BAGE;
private static final ColorSelect ENUM$VALUES[];
public static final ColorSelect GREEN_BAGE;
public static final ColorSelect RED_BAGE;
公有数据就是BLUE_BAGE,GREEN_BAGE,RED_BAGE,它们本身属性就是ColorSelect类。所以,从这里可以看到枚举类定义的成员变量也是类。后续的代码:
RED_BAGE = new ColorSelect("RED_BAGE", 0);
GREEN_BAGE = new ColorSelect("GREEN_BAGE", 1);
BLUE_BAGE = new ColorSelect("BLUE_BAGE", 2);
这三行相当于初始化这三个类,并且每个类分配唯一的序号,按定义时的顺序从0递增。我们在java代码,可以调用ColorSelect.BLUE_BAGE.ordinal()来得到对应的序号值。
通过以上分析,java枚举是一个类,不是语言本身实现的,而是编译器实现的,我们可以直接调用里面的方法。Enum 本身就是个普通的 class, 可以有很多自定义方法用来实现不同的功能。如果我们不自定义里面的方法,编译器就能初始化,默认顺序从0递增。我们也可以自定义方法,这样就能随便赋值。
下面是我们自定义的java枚举类型,可以赋值。
<span style="font-weight: normal;"> public enum Temp {
/*通过括号赋值,而且必须有带参构造器和一属性跟方法,否则编译出错
* 赋值必须是都赋值或都不赋值,不能一部分赋值一部分不赋值
* 如果不赋值则不能写构造器,赋值编译也出错*/
absoluteZero(-459), freezing(32),boiling(212), paperBurns(451);
private final int value;
public int getValue() {
return value;
}
//构造器默认也只能是private, 从而保证构造函数只能在内部使用
Temp(int value) {
this.value = value;
}
}</span>
这就是一个枚举类,自定义方法来赋值。java不像C++那样简便,初始第一个值,后续值递增。赋值必须是都赋值或都不赋值,不能一部分赋值一部分不赋值。如果不赋值则不能写构造器,赋值编译也出错。
Temp temp = null;
Log.i("Temp##",temp.freezing.getValue()+"");
在主函数中,我们调用temp.freezing.getValue(),得到对应的值32。
如果大家感觉自定义枚举类麻烦,其实也可以用别的方法来代替自定义枚举类。如下所示:
public class ColorSelect {
private static final int RED_BAGE = 1;
private static final int GREEN_BAGE = 3;
private static final int BLUE_BAGE = 5;
}
这种方法也比较方便。
二. 第一个疑问搞清楚了,我们现在要解决第二个问题。Switch到底通过字符串,还是整型来分辨枚举变量。
下面是java代码段:
ColorSelect test = ColorSelect.BLUE_BAGE;
switch(test){
case RED_BAGE:
Log.i("TEST####1","a");
break;
case GREEN_BAGE:
Log.i("TEST####2","b");
break;
case BLUE_BAGE:
Log.i("TEST####3","c");
break;
default:
Log.i("TEST####4","d");
}
Log.i("TEST####ret", "e");
反编译后的代码:
{ //主函数
ColorSelect colorselect = ColorSelect.BLUE_BAGE;
ai = $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect(); //调用子函数$SWITCH_TABLE$test$Enum$TestEnum$ColorSelect(),返回数组赋值给ai
i = colorselect.ordinal(); //得到该枚举类型BLUE_BAGE的序号
ai[i]; //switch根据ai[i]值来进行判断
JVM INSTR tableswitch 1 3: default 56 // 1 3是ai[i]的取值范围
// 1 73
// 2 84
// 3 95;
goto _L1 _L2 _L3 _L4
_L1:
Log.i("TEST####4", "d");
_L6:
Log.i("TEST####ret", "e");
return;
_L2:
Log.i("TEST####1", "a");
continue; /* Loop/switch isn't completed */
_L3:
Log.i("TEST####2", "b");
continue; /* Loop/switch isn't completed */
_L4:
Log.i("TEST####3", "c");
if(true) goto _L6; else goto _L5
_L5:
}
private static int $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect[];
}
//子函数
static int[] $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect() //
{
int ai[] = $SWITCH_TABLE$test$Enum$TestEnum$ColorSelect;
if(ai == null)
{
ai = new int[ColorSelect.values().length];
try //下面的代码,将每一个枚举类型的序号值作为数组ai的下标,然后赋一个整数值
{
int i = ColorSelect.BLUE_BAGE.ordinal();
ai[i] = 3;
}
catch(NoSuchFieldError nosuchfielderror2) { }
try
{
int j = ColorSelect.GREEN_BAGE.ordinal();
ai[j] = 2;
}
catch(NoSuchFieldError nosuchfielderror1) { }
try
{
int k = ColorSelect.RED_BAGE.ordinal();
ai[k] = 1;
}
catch(NoSuchFieldError nosuchfielderror) { }
$SWITCH_TABLE$test$Enum$TestEnum$ColorSelect = ai;
}
return ai;
}
子函数$SWITCH_TABLE$test$Enum$TestEnum$ColorSelect()的功能:
通过int i = ColorSelect.BLUE_BAGE.ordinal()得到该类的唯一序号,作为数组下标,并且赋值给ai[i]。这样,每一个枚举类对应都有唯一的整数对应。然后返回数组ai[]。这个功能是编译器实现的。我感觉,子函数的功能有些多余,其实可以直接用之前初始化枚举类的时候的下标号,直接作为标识符也行,就不用再给每个枚举类重新赋整数值。
主函数里面,调用子函数$SWITCH_TABLE$test$Enum$TestEnum$ColorSelect(),switch()里面的参数是ai[i],根据这个整数值来判断分支。Java里面枚举值通过编译器给每个枚举类型赋整型数值,然后Switch通过识别这些整数来进行判断分支,这就解决了第二个疑问。
同时我们通过对比switch的java代码和反编译的代码,可以得到大概下面的结论。
1.每个标志位的"continue"说明该分支break。
2.最后一个分支的标志是if (true) goto _Lx,else goto _x。
同时我们通过对比switch的java代码和反编译的代码,可以得到大概下面的结论。
1.每个标志位的"continue"说明该分支break。
2.最后一个分支的标志是if (true) goto _Lx,else goto _x。
同时,还有些疑问,没弄明白?
我写了好几个switch的例子,反编译出来的代码有一个共同规律。
反编译代码1:
JVM INSTR tableswitch 1 3: default 56
// 1 73
// 2 84
// 3 95;
goto _L1 _L2 _L3 _L4
反编译代码2:
JVM INSTR tableswitch 0 2: default 60
// 0 77
// 1 88
// 2 99;
goto _L1 _L2 _L3 _L4
这两个,标明 1, 2,3相互之间数值都相差11。第二个也是。这个搞不懂?同时,这些数值代表什么?
如果以上哪些不对的地方,请大家指正。
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
(转自:http://blog.csdn.net/bage1988320/article/details/6690845)
java 枚举类型分析的更多相关文章
- 深入理解Java枚举类型(enum)
https://blog.csdn.net/javazejian/article/details/71333103 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(en ...
- 【转】java枚举类型enum的使用
原文网址:http://blog.csdn.net/wgw335363240/article/details/6359614 java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到 ...
- 【转】掌握java枚举类型(enum type)
原文网址:http://iaiai.iteye.com/blog/1843553 1 背景 在java语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具有int常量.之前我们通常利用 ...
- 转载 java枚举类型enum的使用 (原文地址:http://blog.csdn.net/wgw335363240/article/details/6359614)
java枚举类型enum的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java中定义的常量值不采用enmu枚举类型,而采用public final static 类型来定义呢?以前我们都是采 ...
- Java 枚举类型简介
目录 Java 枚举示例 Java 枚举构造函数 枚举类型是用于定义常量集合的特殊类型,更确切的说,JAVA枚举类型是一种特殊的 java 类.枚举类型可以包含常量.方法等.在 java5 中添加了 ...
- 【转载】Java枚举类型的使用
枚举类型概念 package com.lxq.enumm; public class EnumDemoOne { private enum InnerEnum { RED, GREEN, YELLOW ...
- Java枚举类型使用示例
Java枚举类型使用示例 学习了:https://www.cnblogs.com/zhaoyanjun/p/5659811.html http://blog.csdn.net/qq_27093465/ ...
- Java枚举类型的使用,数值的二进制表示
一.Java枚举类型的使用 首先请看这段代码: package java上课; public class EnumTest { public static void main(String[] arg ...
- java 枚举类型和数据二进制等问题思考
.以下代码的输出结果是什么? int X=100; int Y=200; System.out.println("X+Y="+X+Y); System.out.println(X+ ...
随机推荐
- SSM框架Web程序的流程(Spring SpringMVC Mybatis)
SSM框架的Web程序主要用到了三个技术: Spring:用到了注解和自动装配,就是Spring的两个精髓IOC(反向控制)和 AOP(面向切面编程). SpringMVC:用到了MVC模型,将逻辑代 ...
- ARM 处理器的几个相关术语
生产ARM的厂商很多,自然ARM处理器的名字就五花八门.但是,它们有些共同点,那就是:架构和核心. 架构这个概念太宽不太懂,一般不同的架构会有不同的指令集,在不同的架构下面还可以有多种核心.核心就是指 ...
- Windows下尝试PHP7提示丢失VCRUNTIME140.DLL的问题解决
前天PHP7.0.0正式版发布了,有一些比较好的改进,官方也说速度比php5.6快了两倍,性能上有了很大提升,并且也发布了从php5.x向php7迁移的问题,所以今后php网站迁移后能够大幅度的提升网 ...
- Linux系统排查1——内存篇
常见工作中,计算机系统的资源主要包括CPU,内存,硬盘以及网络,过度使用这些资源将使系统陷入困境.本系列一共四篇博文,结合我在实习期间的学习,介绍一些常见的Linux系统排障工具及方法. 第1篇——内 ...
- canvas API ,通俗的canvas基础知识(二)
上文我们讲到了画一条线,画矩形,写文字,总算是有了一个好的开头,如果还没有看的同学出门左转,先看看那篇,这里就不多做叙述了,接下来我们看比较复杂的一些属性和方法! 讲之前呢,我还是想温习一下,毕竟上文 ...
- linux 创建连接命令 ln -s 软链接
这是linux中一个非常重要命令,请大家一定要熟悉.它的功能是为某一个文件在另外一个位置建立一个同不的链接,这个命令最常用的参数是-s, 具体用法是:ln -s 源文件 目标文件. 当 我们需要在不同 ...
- 【转】Quartus II调用modelsim无缝仿真
Quartus II调用modelsim无缝仿真 ★emouse 思·睿博客文章★ 原创文章转载请注明:http://emouse.cnblogs.com 本篇文章为转载,写的不错,最近在学mode ...
- jquery.validate.unobtrusive.js插件作用
在 ASP.NET MVC 中启用 Unobtrusive JavaScript 功能,可以在运行时由服务器端根据Model中设置的验证规则,自动生成客户端验证js代码(结合jquery.valida ...
- .net学习笔记---HttpRuntime类
HttpRuntime在ASP.NET处理请求中负责的是创建HttpContext对象以及调用HttpApplicationFactory创建HttpApplication. 其定义如下: publi ...
- .NET生成word文档服务器配置常见问题
注意:安装office2003的时候一定要选择 "完全安装" 而不是 "典型安装" 错误:System.Runtime.InteropServices.COME ...