频道澳门葡京手机版网址
登录注册
澳门葡京手机版网址 > 澳门葡京手机版网址 > App开发 > 其他 > 正文
Java 面试题 short s = 1; s = s + 1; 与 s += 1; 背后的秘密解析
2018-08-15 10:41:43           
收藏   我要投稿

案例一:

short s = 1; 

s = s + 1;

上述代码能否通过编译,为什么?

案例二:


short s = 1;

s += 1;

上述代码能否通过编译,为什么?

以上两个问题是非常经典的一道Java面试题,面试中出现的频率在70%以上。答案可能大家都非常清楚了,案例一可以不能通过编译,案例二可以,那么这又是为什么呢?

答案要从运算符的优先级、运算机制、默认数据类型和类型转换的机制中来寻找。

首先,大家来分析“ s = s + 1; ”。在这个表达式中,涉及到了加法(+)和赋值(=)两种运算。加法运算的优先级高于赋值运算,因此会先编译加法运算。在这个过程中,加号左侧为 short 类型的变量 s,右侧为常量值 1,1的默认数据类型为 int 类型,这种情况下,s 会自动类型提升,隐式转换为 int 类型。这个过程结束之后,会继续对赋值运算进行编译。此时,等号左侧为 short 类型变量 s,等号右侧为 int 类型变量,要把 int 类型变量的值赋给 short 类型变量,由于 int 类型的数据宽长为 32 位,short 类型的数据长度为16位,而变量的值在编译期间是无法确定的,所以编译器会认为在这个赋值过程中会发生数据信息丢失,从而编译不能通过。

大家再来看一些情况:


byte a = 3;// 编译正确

byte b = 1000;// 编译出错 Type mismatch: cannot convert from int to byte

float c = .7;// 编译出错 Type mismatch: cannot convert from double to float

在这个例子中,大家会有这样的疑问,同样是由 int 类型常量赋给 byte 类型变量,为什么 a = 3 可以通过,而 b = 1000 却不能通过。这是因为,对于常量,编译器是可以确定其值是否在目标类型的数值范围之内,3在 byte 类型的范围之内,因此转换后不会有数据信息丢失,编译成功,而1000超出了 byte 的数值范围,转换会造成数据信息丢失,转换失败。而 float c = .7,.7为 double 类型的常量,double 类型的精度高于 float 类型,如果从 double 转换为 float 则会产生精度的损失,所以编译不通过。(注意:double d = .7; float f = .7f; 这里,d 大于 f。大家可以通过程序验证一下)。

接下来,大家再来看第二个案例。这个案例中,只涉及到一个复合赋值“ += ”。复合赋值 E1 op= E2 等价于简单赋值 E1 =(T)((E1)op(E2)),其中 T 是 E1 的类型,除非 E1 只被计算一次。也就是说,复合赋值表达式自动地将它们所实行的计算的结果转型为其左侧变量的类型。如果结果的类型与该变量的类型相同,那么这个转型不会造成任何影响。然而,如果结果的类型比该变量的类型要宽,那么复合赋值操作符将悄悄地实行一个窄化原始类型转换。所以,s += 1; 先编译加法运算,再编译强制类型转换,最后编译赋值运算,这样,运算符左右两侧数据类型一致,编译通过。

补充点小常识,大家通过对字节码文件进行反编译,得到其对应的 JVM 指令,从 JVM 指令是否一致,就可以判断出两种操作的实质是否一样。首先来看源码一:


public class Demo {
	public static void main(String[] args) {
		short s = 1;
		s += 1;
	}
}

运行 javap -c -verbose Demo.class,得到结果一如下:


Classfile /C:/Users/Administrator/Desktop/Demo.class
  Last modified 2018年7月20日; size 268 bytes
  MD5 checksum 88b70724a8593e4946d538b0460c5bc1
  Compiled from "Demo.java"
public class Demo
  minor version: 0
  major version: 54
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #2  // Demo
  super_class: #3 // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #3.#12// java/lang/Object."":()V
#2 = Class  #13// Demo
#3 = Class  #14// java/lang/Object
#4 = Utf8
#5 = Utf8()V
#6 = Utf8Code
#7 = Utf8LineNumberTable
#8 = Utf8main
#9 = Utf8([Ljava/lang/String;)V
  #10 = Utf8SourceFile
  #11 = Utf8Demo.java
  #12 = NameAndType  #4:#5 // "":()V
  #13 = Utf8Demo
  #14 = Utf8java/lang/Object
{
  public Demo();
 descriptor: ()V
 flags: (0x0001) ACC_PUBLIC
 Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1// Method java/lang/Object."":()V
4: return
LineNumberTable:
  line 3: 0

  public static void main(java.lang.String[]);
 descriptor: ([Ljava/lang/String;)V
 flags: (0x0009) ACC_PUBLIC, ACC_STATIC
 Code:
stack=2, locals=2, args_size=1
0: iconst_1
1: istore_1
2: iload_1
3: iconst_1
4: iadd
5: i2s
6: istore_1
7: return
LineNumberTable:
  line 5: 0
  line 6: 2
  line 7: 7
}
SourceFile: "Demo.java"

再来看另一段源码二:


public class Demo {
	public static void main(String[] args) {
		short s = 1;
		s = (short) (s + 1);
	}
}

运行 javap -c -verbose Demo.class,得到结果二如下:


Classfile /C:/Users/Administrator/Desktop/Demo.class
  Last modified 2018年7月20日; size 288 bytes
  MD5 checksum 1100ccec295a1315d82752f3af1e9300
  Compiled from "Demo.java"
public class com.cml.springcloud.Demo
  minor version: 0
  major version: 54
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #2  // com/cml/springcloud/Demo
  super_class: #3 // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #3.#12// java/lang/Object."":()V
#2 = Class  #13// com/cml/springcloud/Demo
#3 = Class  #14// java/lang/Object
#4 = Utf8
#5 = Utf8()V
#6 = Utf8Code
#7 = Utf8LineNumberTable
#8 = Utf8main
#9 = Utf8([Ljava/lang/String;)V
  #10 = Utf8SourceFile
  #11 = Utf8Demo.java
  #12 = NameAndType  #4:#5 // "":()V
  #13 = Utf8com/cml/springcloud/Demo
  #14 = Utf8java/lang/Object
{
  public com.cml.springcloud.Demo();
 descriptor: ()V
 flags: (0x0001) ACC_PUBLIC
 Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1// Method java/lang/Object."":()V
4: return
LineNumberTable:
  line 3: 0

  public static void main(java.lang.String[]);
 descriptor: ([Ljava/lang/String;)V
 flags: (0x0009) ACC_PUBLIC, ACC_STATIC
 Code:
stack=2, locals=2, args_size=1
0: iconst_1
1: istore_1
2: iload_1
3: iconst_1
4: iadd
5: i2s
6: istore_1
7: return
LineNumberTable:
  line 5: 0
  line 6: 2
  line 7: 7
}
SourceFile: "Demo.java"

从得到的结果,大家可以看出,其对应的 JVM 指令是一致的,由此可以判定这两种表达式是等价的。

点击复制链接 与好友分享!回澳门葡京手机版网址澳门葡京手机版网址
上一篇:异常体系Throwable教程
下一篇:基础常识的漏洞解析
相关文章
图文推荐
点击排行

关于大家 | 联系大家 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 澳门葡京手机版网址_澳门新莆京娱乐_www.88807.com - 点此进入--致力于做实用的IT技术学习网站

XML 地图 | Sitemap 地图