0
点赞
收藏
分享

微信扫一扫

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

郝春妮 2022-03-22 阅读 33
java

文章目录


语法话题

本期的话题如下:

关于该表达式,在类似于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<>();

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

举报

相关推荐

0 条评论