【Java语法解析】i+++j应该如何计算?

本文探讨了Java中表达式`i+++j`的解析方式,解释了编译器遵循的最长可能解析原则,该原则导致表达式被解析为`(i++) + j`。文章通过示例展示了该原则在Unicode转义和类型定义中的例外情况。


语法话题

本期的话题如下:

i+++j应该如何计算?

关于该表达式,在类似于C / C++中讨论是没有多大意义的,因为C / C++依赖于实现的硬件结构,不同的环境结果也会不同。不过在Java中,这个结果是固定的,不受其运行的硬件环境与平台所影响。


语法分析

我们可以将表达式解析成两种可能的形式:

  1. i + (++j)
  2. (i++) + j

以上的括号不是必须的,这里仅为了清晰起见(后面也是如此)。

我们可以通过程序来测试。

package test;

public class Test {
	public static void main(String[] args) {
		int i = 10;
		int j = 20;
		int k = i+++j;
		System.out.println("i=" + i);
		System.out.println("j=" + j);
		System.out.println("k=" + k);
	}
}

如果解析为第1种形式,则j的值会增1,如果解析为第2种形式,则i的值会增1,程序运行结果如下:

i=11
j=20
k=30

由运行结果可知,编译器将表达式解析为第2种形式,即:

(i++) + j

最长可能解析原则

编译器对原始输入进行符号解析时,使用最长可能解析原则,也就是说,在解析符号的时候,编译器会尽可能多的去结合有效的符号,例如上面的表达式:

i+++j

“+”与“++”都是有效的符号,但是“+++”不是有效的符号,因此,经过解析后,最终将表达式解析为以下4个符号,即:

  1. i
  2. ++
  3. +
  4. j

这也就是表达式:

(i++) + j

不过,需要留意的是,最长可能解析原则只会尽力去结合有效的符号,即使这样结合并不符合语法规则。例如表达式:

i--j

如果将其解析为:

i - (-j)

这样是符合语法规则的,不过,因为符号“--”是有效的符号,根据最长可能解析原则,编译器会将其解析为:

i-- j

这当然不符合语法规则,最终也会产生编译错误。


原则使用原因

可是,编译器为何要采用这种原则来解析符号呢?简单的理解就是,如果不使用最长可能解析原则,字符多的有效符号就得不到解析。例如,“--”以“-”为前缀,如果没有这种规则,则总是会解析为两个“-”,而不是“--”。

同样,在转义序列中,也存在类似的情况。例如,八进制转义“\101”,根据最长解析原则,编译器总是会将其解析为“\101”,而不是解析为“\10”与“1”,也不会解析为“\1”,“0”与“1”。而对于“\431”,因为“\431”不是一个有效的八进制转义(有效范围是\0 ~ \377),故编译器会将其解析为“\43”与“1”,而不是“\4”,“3”与“1”。

程序示例:

package test;

public class Test {
	public static void main(String[] args) {
		// \101是字符A的八进制转义。
		System.out.println("\101");
		// \43是字符#的八进制转义。
		System.out.println("\431");
	}
}

程序运行结果如下:

A
#1

问题思考

给定如下的语句:

System.out.println("\101")

根据之前的介绍,八进制转义“\101”不会解析为“\10”与“1”两个字符,也不会解析为“\1”,“0”与“1”三个字符,如果我们期望输出“\10”与“1”两个字符,或是“\1”,“0”与“1”三个字符呢,该怎样调整输出?


原则的例外

不过,为了更好的完成符号的解析,最长可能解析原则有两种例外的情况。

例外1——Unicode转义

当编译器从原始输入中解析Unicode转义时(\uxxxx的格式,其中xxxx为4位十六进制的数值),如果\uxxxx前面存在1个(或奇数个)“\”,则\uxxxx不会转义为对应的Unicode字符,而是保持原始输入内容,接受后续的处理。

package test;

public class Test {
	public static void main(String[] args) {
		// 根据最长可能解析原则,\u0041会解析为其所转义的Unicode字符(A)。
		System.out.println("\u0041");
		// 不会使用最长可能解析原则,\\u0041(7个字符)会作为原始输入。
		// 在后续的解析中,\\为转义序列,编译器会将其解析为\。
		System.out.println("\\u0041");
	}
}

程序运行结果如下:

A
\u0041

例外2——类型的一部分

在类型的上下文中,例如:

List<List<Integer>> list = new ArrayList<>();

连续出现的“>”总是会解析为单个大于号字符(作为类型的一部分),而不会作为整体解析为移位运算符“>>”或“>>>”。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值