本文分享至华为云社区《CodeNavi 中代码语句的节点和节点属性》。作者:Uncle_Tom
1. 前期回顾
-
《寻找适合编写静态分析规则的语言》
根据代码检查中的一些痛点,提出了希望寻找一种适合编写静态分析规则的语言。- 可以满足用户对代码检查不断增加的各种需求;
- 使用户能够通过增加或减少对检查约束条件的控制,实现快速调整检查中出现的误报和漏报;
- 这种检查语言能够有较低的使用门槛,使用户更专注于检查业务,而不需要关注工具是如何实现的。
我们称这种检查规则语言为:CodeNavi。文中给出了这种检查规则语言的两个应用场景,以此来说明这种检查规则语言如何满足用户在编写静态检查规则时的期望。
-
《CodeNavi 规则的语法结构》
介绍 CodeNavi 检查规则语言在编写规则的基本语法格式。CodeNavi 检查规则语言,通过对代码节点和节点属性的逻辑条件的组合来完成检查的约束条件,从而完成代码检查中需要满足的缺陷模式适配,找到满足要求的代码点。 -
《CodeNavi 规则的基础节点和节点属性》
介绍 CodeNavi 检查规则语言在编写时需要使用基础节点和节点属性,这些基础节点包括:节点类型、字面量、一般变量、枚举、成员变量(字段),以及数组。 -
《CodeNavi 中代码表达式的节点和节点属性》
介绍 CodeNavi 检查规则语言如何描述代码中的表达式。这些节点主要包括:对象创建表达式、强制类型转换、类型判断表达式、一元表达式、二元表达式、条件表达式/三目运算、方法引用表达式、lambda表达式,以及匿名内部类表达式。 -
本篇将继续介绍 CodeNavi 中代码里语句和代码块的节点和节点属性。
- 语句节点包括:赋值语句,
- 控制流语句包括:跳转语句、条件控制、Switch控制、循环控制、异常处理、静态语句、同步代码块。
2. CodeNavi 中的节点和节点属性
程序是由空格分隔的字符串组成的序列。在程序分析中,这一个个的字符串被称为"token",是源代码中的最小语法单位,是构成编程语言语法的基本元素。
Token可以分为多种类型,常见的有关键字(如if、while)、标识符(变量名、函数名)、字面量(如数字、字符串)、运算符(如+、-、*、/)、分隔符(如逗号,、分号;)等。
我们只需要给代码的不同节点给出一个定义,然后通过条件语句来描述对这些节点的要求,使之符合缺陷检查的模式,就可以完成检查规则的定义。
2.1. 规则节点和节点属性图例
程序是由空格分隔的字符串组成的序列。在程序分析中,这一个个的字符串被称为"token",是源代码中的最小语法单位,是构成编程语言语法的基本元素。
Token可以分为多种类型,常见的有关键字(如if、while)、标识符(变量名、函数名)、字面量(如数字、字符串)、运算符(如+、-、*、/)、分隔符(如逗号,、分号;)等。
我们只需要给代码的不同节点给出一个定义,然后通过条件语句来描述对这些节点的要求,使之符合缺陷检查的模式,就可以完成检查规则的定义。
2.1.1. 节点
-
图例
-
节点和子节点都使用个图例
-
规则语言中使用节点的 “英文名”,这样便于规则的编写。
2.2. 节点集
-
图例:
-
节点的集合。
2.3. 属性
-
图例
-
规则语言中使用属性的 “英文名”,这样便于规则的编写。
3. 语句
程序语言中包含多种语句,每种语句都有其特定的作用。
- 样例代码
// 表达式语句
// 用于执行表达式,如赋值、方法调用等。
int a = 10; // 赋值表达式
System.out.println("Hello, CodeNavi!"); // 方法调用表达式
// 条件语句
// 用于基于条件执行不同的代码分支。
// if 语句
int score = 75;
if (score > 70) {
System.out.println("Pass");
}
// if-else 语句
if (score > 70) {
System.out.println("Pass");
} else {
System.out.println("Fail");
}
// switch 语句
int month = 4;
switch (month) {
case 1: System.out.println("January"); break;
case 2: System.out.println("February"); break;
// ...
default: System.out.println("Invalid month");
}
// 循环语句
// 用于重复执行一段代码。
// for 循环
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
// while 循环
int i = 0;
while (i < 10) {
System.out.println(i++);
}
// do-while 循环
int i = 0;
do {
System.out.println(i++);
} while (i < 10);
// 跳转语句
// 用于控制程序流程,如跳过循环的迭代或退出循环。
// break 语句
for (int i = 0; i < 10; i++) {
if (i == 5) break;
System.out.println(i);
}
// continue 语句
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) continue; // 跳过偶数
System.out.println(i);
}
// 异常处理语句
// 用于处理程序运行时可能出现的异常。
// try-catch 语句
try {
int result = 10 / divisor;
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero");
}
// try-catch-finally 语句
try {
// 可能抛出异常的代码
} catch (Exception e) {
// 异常处理
} finally {
// 无论是否发生异常都会执行的代码
}
// 返回语句
// 用于从方法返回一个值或退出方法。
public int max(int a, int b) {
return (a > b) ? a : b;
}
3.1. 赋值语句(assignStatement)
赋值语句是编程中最基本的操作之一,几乎所有的编程语言都支持赋值操作。
赋值语句用于将一个值或表达式的结果存储到一个变量中。赋值语句的基本形式是将一个表达式的结果赋给一个变量名。
其作用主要包括:
-
初始化变量
在程序开始时,使用赋值语句给变量赋予初始值。 -
存储数据
将计算结果或常量值存储到一个变量中,以便在程序的后续部分使用。 -
更新变量值
在程序执行过程中,可能需要更新变量的值以反映程序状态的变化。 -
简化代码
通过将复杂表达式的结果赋值给变量,可以简化代码,提高可读性。 -
样例代码
int a = 10;
String b = "string" + a;
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
lhs | 左值 | valueAccess类节点 | a = 10; | assignStatement ass where ass.lhs.name == “a”; |
rhs | 右值 | literal类节点、valueAccess类节点、functionCall类节点 | a = 10; | assignStatement ass where ass.rhs.value == 10; |
4. 控制流语句
控制流语句是编程语言中用于控制程序执行流程的语句,它们决定了程序中代码的执行顺序。
-
代码块分组
使用大括号 {} 可以将多条语句组织在一起作为一个整体来执行。 -
条件执行
允许程序根据条件判断来选择不同的执行路径。例如,if 语句可以根据条件为真或假来执行不同的代码块。 -
多路径选择
使用 switch 语句可以根据变量值选择多个执行路径之一。 -
循环执行
允许程序重复执行一段代码直到满足特定条件。例如,for 和 while 循环可以在满足循环条件时不断迭代。 -
异常处理
使用 try、catch 和 finally 块可以捕获和处理程序运行时发生的异常。 -
跳转语句
程序中的跳转语句允许改变程序的执行流程,使得程序可以跳过某些代码块或直接跳到程序的其他部分执行。包括:break、continue、return、throw。
4.1. 跳转语句
程序中的跳转语句允许改变程序的执行流程,使得程序可以跳过某些代码块或直接跳到程序的其他部分执行。
跳转语句:
- break 语句:立即终止最近的 for 或 while 循环。
- continue 语句:跳过当前循环的剩余部分,开始下一次迭代。
- return 语句:从当前函数返回,并可以携带一个值。
- throw 语句:用于抛出一个异常,可以是预定义的异常类型或用户自定义的异常类型。
4.1.1. continue语句(continueStatement)
continue 语句用于跳过当前循环的剩余部分,并开始执行下一次循环的迭代。这通常用于在满足某个特定条件时不想执行循环体中剩余的代码块。
以下是一些使用 continue 语句的场景:
-
跳过不符合条件的元素
处理一个集合,比如数组或列表,并且只想对满足特定条件的元素执行操作时,可以使用 continue 来跳过不满足条件的元素。 -
避免重复处理
如果循环中检测到某个元素已经被处理过,可以使用 continue 跳过对它的进一步处理。 -
减少嵌套循环的复杂性
在嵌套循环中,如果外层循环的某个条件满足,可能就不需要执行内层循环的任何迭代,这时可以使用 continue 来跳过内层循环。
- 样例代码
for (int i = 0; i < 10; i++) {
// 跳过偶数
if (i % 2 == 0) {
continue;
}
// 只打印奇数
System.out.println(i);
}
- 图例
4.1.2. break语句(breakStatement)
break 语句的作用是立即终止最内层的 for、while 或其他循环结构的执行。当执行到 break 语句时,程序会跳出当前的循环体,继续执行循环之后的代码。
break 语句的一些主要作用:
-
提前退出循环
当满足特定条件时,你可能希望立即退出循环,而不是等待循环自然结束。 -
避免无限循环
在某些情况下,循环的条件可能依赖于循环体内部的某些操作结果,使用 break 可以避免无限循环。 -
搜索和查找
在搜索或查找任务中,一旦找到所需的元素或满足特定条件,使用 break 可以立即退出循环。 -
错误处理
在循环中进行某些操作时,如果发生错误或异常情况,可以使用 break 来退出循环。 -
控制复杂循环逻辑
在嵌套循环中,break 可以用来从外层循环中退出,而不仅仅是内层循环。 -
样例代码
// break 语句
for (int i = 0; i < 10; i++) {
if (i == 5) {
break;
}
System.out.println(i);
}
- 图例
4.1.3. return语句(returnStatement)
return 语句的作用是从一个函数或子程序中返回一个值,并终止该函数的执行。
return 语句是函数实现其目的的关键,它允许函数将结果传递给调用者,并在必要时提前退出函数的执行。
以下是 return 语句的一些关键作用:
-
返回结果
函数通常用于执行某些计算或操作,并返回结果。使用 return 语句可以将这些结果传递回调用函数的地方。 -
提前退出函数
如果函数在执行过程中遇到某些条件,可能需要立即退出并返回一个值(或不返回任何值)。 -
控制函数的流程
在复杂的函数中,return 可以用于控制程序的执行流程,例如从嵌套的逻辑结构中返回。 -
错误处理
在函数中检测到错误或异常情况时,可以使用 return 语句返回错误代码或错误信息。 -
简化代码
通过使用 return,可以在满足特定条件时简化代码逻辑,避免使用多个 if 语句或条件判断。 -
样例代码
// 返回语句
// 用于从方法返回一个值或退出方法。
public int max(int a, int b) {
return (a > b) ? a : b;
}
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
returnValue | 返回值 | 任意节点 | return a; | returnStatement rs where rs.returnValue.name == “a”; |
4.1.4. 异常抛出语句(throwStatement)
使用 throw 语句抛出异常是异常处理机制的一部分,它允许开发者明确地指出程序中可能出现的错误,并提供一种机制来处理这些错误。
throw 语句用于在代码的特定位置手动抛出一个异常。这个异常可以是预定义的异常类型,也可以是用户自定义的异常类型。
throw 语句的一些主要作用:
-
异常处理
当程序检测到一个错误或异常情况时,可以使用 throw 抛出一个异常,从而中断正常的程序流程。 -
错误传播
在方法中抛出的异常可以被方法的调用者捕获和处理,或者继续向上传播,直到被捕获或导致程序终止。 -
强制方法实现
在的接口或抽象类中,可以使用 throw 语句来强制实现某些方法,尽管这些方法没有具体的实现代码。 -
方法签名中的异常声明
当一个方法声明它可能会抛出特定的异常时,这些异常需要在方法的签名中声明。如果这些异常是必须被调用者处理的,可以使用 throw 语句来抛出。 -
样例代码
throw new Exception("Get exception!");
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
operand | 抛出异常 | node | throw new Exception(); | throwStatement tr where tr.operand is objectCreationExpression; |
4.2. 条件控制(ifBlock)
条件控制语句的作用是允许程序根据不同的条件来决定执行哪些代码块。
以下是条件控制语句的主要作用:
-
决策制定
条件控制语句允许程序基于特定的条件(通常是变量或表达式的值)来做出决策。 -
功能开关
在开发过程中,条件控制语句可以用于临时启用或禁用某些功能。 -
状态管理
在需要根据程序的状态来执行不同操作时,条件控制语句可以检查状态并做出相应的响应。 -
避免错误
通过检查条件,可以避免执行可能导致错误的代码,例如除以零或访问未初始化的变量。 -
逻辑选择
条件控制语句允许程序在多个选项中选择一个执行,例如使用 if 语句来选择两个不同的操作之一。 -
代码分支
它们可以创建代码的分支路径,使得程序可以根据不同的条件执行不同的代码段。 -
增强程序的灵活性
允许程序根据不同的输入或状态来动态调整其行为。 -
实现复杂的逻辑
多个条件控制语句可以嵌套使用,以实现更复杂的逻辑判断。 -
控制循环的执行
在循环中使用条件控制语句可以决定何时开始或结束循环的迭代。 -
资源管理
条件控制语句可以用于在满足特定条件时分配或释放资源。 -
错误处理
它们可以用于检测错误条件,并执行错误处理代码,比如记录日志或向用户报告错误。 -
样例代码
if(num > 10) { // if block
// then block
System.out.println("m > 10");
} else {
// else block
System.out.println("m <= 10");
}
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
condition | if条件 | binaryOperation节点 | if (m > 10) { System.out.println(“m > 10”); } else { System.out.println(“m <= 10”); } | ifBlock ib where ib.condition contain binaryOperation bo where bo.operator == “>”; |
thenBlock | if条件为true执行的语句 | block语句块 | if (m > 10) { System.out.println(“m > 10”); } else { System.out.println(“m <= 10”); } | ifBlock ib where ib.thenBlock contain stringLiteral ss where ss.value == “m > 10”; |
elseBlock | if条件为false执行的语句 | block语句块 | if (m > 10) { System.out.println(“m > 10”); } else { System.out.println(“m <= 10”); } | ifBlock ib where ib.elseBlock contain stringLiteral ss where ss.value == “m <= 10”; |
4.3. Switch控制
switch 语句是一种选择控制流语句,它允许程序根据不同的条件执行不同的代码块。switch 语句的主要作用是提供一个替代多个 if…else if…else 语句的方法,使得代码更加清晰和易于管理。
以下是 switch 语句的一些关键特点和作用:
-
多条件选择
switch 语句允许你根据不同的变量值或表达式结果来选择执行不同的代码块。 -
默认行为
switch 语句通常包含一个 default 子句,用于处理没有匹配到任何 case 的情况,类似于 if…else if…else 语句中的 else 部分。 -
减少嵌套
相比使用多个 if…else if…else 语句,switch 语句可以减少代码的嵌套层次,使代码结构更清晰。 -
可中断性
在 switch 语句中,可以使用 break 语句来中断当前 case 的执行,防止代码继续执行到下一个 case。 -
提高可读性
switch 语句通过明确的 case 标签,使得代码的意图更加明显,便于阅读和理解。 -
易于维护
当需要添加或修改条件分支时,switch 语句提供了一种更直观的方式,有助于维护和扩展代码。 -
样例代码
switch (m) {
case 12:
System.out.println("hello");
break;
case 10:
case 15:
System.out.println("CodeNavi");
break;
default:
System.out.println("default value");
}
4.3.1. switch语句块(switchBlock)
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
selector | switch语句的判断条件 | 任意节点 | switch (m) {} | switchBlock swb where swb.selector.name == “m”; |
4.3.2. case语句块(caseStatement)
-
图例
-
规则样例
caseStatement cs where cs contain literal;
4.3.3. default语句块(defaultStatement)
-
图例
-
规则样例
switchBlock swb where swb contain defaultStatement;
4.4. 循环控制
循环控制语句是程序设计中非常基础且强大的工具,它们使得程序能够以一种高效和灵活的方式处理重复性任务。常见的循环控制语句包括 for 循环、while 循环和 do…while 循环等。
以下是循环控制语句的一些关键特点和作用:
-
重复执行
循环控制语句允许代码块在满足循环条件时重复执行,这使得处理大量数据或执行重复任务变得简单高效。 -
条件控制
循环控制语句通常与条件语句结合使用,根据条件的变化来决定是否继续执行循环或退出循环。 -
迭代操作
循环控制语句常用于迭代数据结构(如数组、列表、集合等)中的元素,执行对每个元素的操作。 -
控制执行流程
循环控制语句中的 break 和 continue 可以用来改变循环的执行流程。break 用于立即退出循环,而 continue 用于跳过当前迭代,直接进入下一次循环。 -
实现算法逻辑
许多算法和数据处理任务需要循环结构来实现,例如排序、搜索、遍历等。 -
资源管理
在处理文件、网络连接等资源时,循环可以用来重复读取或写入数据,直到达到预期的条件。 -
模拟时间延迟
在某些情况下,循环可以用来模拟时间延迟,例如通过循环等待某个事件发生。 -
减少代码冗余
通过循环,可以避免编写重复的代码块,使得程序更加简洁和易于维护。
4.4.1. 循环语句块(loopBlock)
包括:forBlock, forEachBlock, doWhileBlock, whileBlock
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
condition | 循环条件 | 任意节点 | while(i > list.size()) { i++; } | whileBlock wb where wb.condition contain binaryOperation bo where bo.lhs.name == “i”; |
body | 循环体 | body语句块 | while(i > list.size()) { i++; } | whileBlock wb where wb.body contain unaryOperation; |
body.statementNum | body语句块的语句数量 | 数值 | while(i > list.size()) { i++; System.out.print(“xxx”); } | whileBlock wb where wb.body.statementNum == 2; |
firstStatement | loopBlock的第一条语句 | 任意节点 | while(i > list.size()) { i++; System.out.print(“xxx”); } | whileBlock wb where wb.firstStatement contain unaryOperation; |
lastStatement | loopBlock的最后一条语句 | 任意节点 | while(i > list.size()) { i++; System.out.print(“xxx”); } | whileBlock wb where wb.lastStatement contain functionCall; |
4.4.2. for循环代码块(forBlock)
- 样例代码
for (int i = 0; i < list.size(); i++) {
i++;
}
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
initialization | 变量初始化 | variableDeclaration、 variableAccess节点 | for (int i = 0; i < list.size(); i++) {} | forBlock fb where fb.initialization.name == “i”; |
condition | 循环条件 | binaryOperation | for (int i = 0; i < list.size(); i++) {} | forBlock fb where fb.condition.rhs is functionCall; |
iteration | 迭代操作 | unaryOperation节点 | for (int i = 0; i < list.size(); i++) {} | forBlock fb where fb.iteration.operator == “++”; |
body | 循环体 | body语句块 | for (int i = 0; i < list.size(); i++) { i++; } | forBlock fb where fb.body contain variableAccess vs where vs.name == “i”; |
body.statementNum | body语句块的语句数量 | 数值 | for (int i = 0; i < list.size(); i++) { i++; } | forBlock fb where fb.body.statementNum == 1; |
firstStatement | forBlock的第一条语句 | 任意节点 | for (int i = 0; i < list.size(); i++) { i++; } | forBlock fb where fb.firstStatement contain unaryOperation; |
lastStatement | forBlock的最后一条语句 | 任意节点 | for (int j1 = 1; j1 < 50 && k1 < 50; j1++, k1++) { System.out.println(j1) k1++; } | forBlock fb where fb.lastStatement contain unaryOperation; |
4.4.3. forEach循环代码块(forEachBlock)
- 样例代码
List<String> list = Arrays.asList("1","2","3");
for (String str : list) {
System.out.println(str)
}
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
variable | 循环变量 | variableDeclaration节点 | for (String str : list) { System.out.println(str); } | forEachBlock fb where and( fb.variable is variableDeclaration, fb.variable.name == “str” ); |
iterable | 被循环的变量 | 任意节点 | for (String str : list) { System.out.println(str); } | forEachBlock fb where fb.iterable.name == “list”; |
body | 循环体 | body语句块 | for (String str : list) { System.out.println(str); } | forEachBlock fb where fb.body contain functionCall; |
body.statementNum | body语句块的语句数量 | 数值 | for (String str : list) { System.out.println(str); } | forEachBlock fb where fb.body.statementNum == 1; |
firstStatement | forEachBlock的第一条语句 | 任意节点 | for (String str : list) { System.out.println(str); } | forEachBlock fb where fb.body contain functionCall; |
lastStatement | forEachBlock的最后一条语句 | 任意节点 | for (String str : list) { System.out.println(str + “1”); System.out.println(str + “2”); } | forEachBlock fb where fb.lastStatement contain functionCall; |
4.4.4. while循环语句块(whileBlock)
- 样例代码
int i = 0;
while(i < 10) {
i = i + 1;
}
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
condition | 循环条件 | 任意节点 | while(i > list.size()) { i++; } | whileBlock wb where wb.condition contain binaryOperation bo where bo.lhs.name == “i”; |
body | 循环体 | body语句块 | while(i > list.size()) { i++;} | whileBlock wb where wb.body contain unaryOperation; |
body.statementNum | body语句块的语句数量 | 数值 | while(i > list.size()) { i++; System.out.print(i); } | whileBlock wb where wb.body.statementNum == 2; |
firstStatement | whileBlock的第一条语句 | 任意节点 | while(i > list.size()) { i++; System.out.print(i); } | whileBlock wb where wb.firstStatement contain unaryOperation; |
lastStatement | whileBlock的最后一条语句 | 任意节点 | while(i > list.size()) { i++; System.out.print(i); } | whileBlock wb where wb.lastStatement contain functionCall; |
4.4.5. do-while语句块(doWhileBlock)
- 样例代码
int i = 0;
do {
i++;
System.out.print(i);
} while (i < 10);
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
condition | 循环条件 | 任意节点 | do { i++; } while (i < 10); | doWhileBlock wb where wb.condition contain binaryOperation bo where bo.lhs.name == “i”; |
body | 循环体 | body语句块 | do { i++; } while (i < 10); | doWhileBlock wb where wb.body contain unaryOperation; |
body.statementNum | body语句块的语句数量 | 数值 | do { i++; System.out.print(i); } while (i < 10); | whileBlock wb where wb.body.statementNum == 2; |
firstStatement | forBlock的第一条语句 | 任意节点 | do { i++; System.out.print(i); } while (i < 10); | doWhileBlock wb where wb.firstStatement contain unaryOperation; |
lastStatement | forBlock的最后一条语句 | 任意节点 | do { i++; System.out.print(i); } while (i < 10); | doWhileBlock wb where wb.lastStatement contain functionCall; |
4.5. 异常处理
程序中的异常处理是一种错误检测和响应机制,它有助于编写更加健壮、可靠和易于维护的软件。
它的作用包括但不限于以下几点:
-
错误检测
异常处理允许程序在运行时检测到错误或异常情况,并采取相应的措施。 -
防止程序崩溃
通过捕获和处理异常,程序可以在遇到错误时继续运行,而不是直接崩溃或终止。 -
支持自定义异常
开发者可以定义自己的异常类型,以更精确地表达特定错误情况。 -
资源管理
异常处理确保即使在发生错误的情况下,程序也能够正确地释放或管理资源,如文件句柄、网络连接等。 -
提供错误信息
异常处理允许程序在发生错误时提供有用的错误信息,帮助开发者或用户诊断问题。 -
分离错误处理代码
通过将错误处理逻辑与正常业务逻辑分离,可以使代码更加清晰和易于维护。 -
支持多级错误处理
异常处理允许程序在不同级别上捕获和处理错误,例如在函数内部捕获异常,然后在调用者中进一步处理。 -
控制程序流程
异常处理提供了一种控制程序流程的方式,允许程序在特定错误发生时跳转到特定的错误处理代码。 -
维护程序的健壮性
通过合理地使用异常处理,可以增强程序的健壮性,使其能够优雅地处理各种意外情况。 -
样例代码
try {
int num = 1/0;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
System.out.println("finally");
}
4.5.1. 异常捕捉代码块(exceptionBlock)
包括: tryBlock、catchBlock、finallyBlock
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
tryBlock | try语句块 | tryBlock节点 | try { int num = 1/0; } catch (Exception e) { throw new RuntimeException(e); } finally { System.out.println(“finally”); } | exceptionBlock eb where eb.tryBlock contain variableDeclaration; |
catchBlocks | 所有的catch语句块集 | catchBlock节点的集合 | try { int num = 1/0; } catch (Exception e) { throw new RuntimeException(e); } finally { System.out.println(“finally”); } | exceptionBlock eb where eb.catchBlocks contain cb where cb contain throwStatement; |
finallyBlock | finally语句块 | finallyBlock节点 | try { int num = 1/0; } catch (Exception e) { throw new RuntimeException(e); } finally { System.out.println(“finally”); } | exceptionBlock eb where eb.finallyBlock contain functionCall fc where fc.name == “println”; |
4.5.3. catch语句块(catchBlock)
- 节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
parameters | 参数集 | node list | try { int num = 1/0; } catch (Exception e) { throw new RuntimeException(e); } finally { System.out.println(“finally”); } | catchBlock cb where cb.parameters contain p where p.name == “e”; |
4.5.4. tryWithResources异常捕捉代码块(tryWithResources)
- 样例代码
try (Statement stmt = con.createStatement()) {
System.out.println("try-with-resources block");
} catch (SQLException e1) {
System.out.println(e1);
} catch (Exception e2) {
System.out.println(e2);
}
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
tryBlock | try语句块 | tryBlock节点 | try (Statement stmt = con.createStatement()) { System.out.println(“try-with-resources block”); } catch (SQLException e1) { System.out.println(e1); } catch (Exception e2) { System.out.println(e2); } | tryWithResources eb where eb.tryBlock contain variableDeclaration; |
catchBlocks | 所有的catch语句块 | catchBlock节点的集合 | try (Statement stmt = con.createStatement()) { System.out.println(“try-with-resources block”); } catch (SQLException e1) { System.out.println(e1); } catch (Exception e2) { System.out.println(e2); } | tryWithResources eb where eb.catchBlocks contain cb where cb contain throwStatement; |
finallyBlock | finally语句块 | finallyBlock节点 | try (Statement stmt = con.createStatement()) { System.out.println(“try-with-resources block”); } catch (SQLException e1) { System.out.println(e1); } catch (Exception e2) { System.out.println(e2); } | tryWithResources eb where eb.finallyBlock contain functionCall fc where fc.name == “println”; |
resources | 任意节点 | 若干statements节点的集合 | try (Statement stmt = con.createStatement()) { System.out.println(“try-with-resources block”); } catch (SQLException e1) { System.out.println(e1); } catch (Exception e2) { System.out.println(e2); } | tryWithResources twr where twr.resources contain re where re contain functionCall fc where fc.name == “createStatement”; |
4.6. 静态语句块(staticBlock)
静态语句块(也称为静态初始化块)是一种特殊的代码块,它使用关键字 static 标记。
静态语句块在类或接口的主体中定义,并且在任何对象被创建之前,或者任何静态方法被调用之前执行。由于静态语句块的执行时机和作用域,在类的初始化和资源管理方面,扮演着重要的角色。
静态语句块的作用主要包括以下几点:
-
初始化静态变量
静态语句块在类加载时执行,可以用来初始化类的静态变量。 -
静态常量赋值
静态语句块可以用来给静态常量赋值,虽然通常静态常量直接赋值更为常见。 -
静态初始化顺序
静态语句块的执行顺序是在类变量声明之后,构造函数之前,这可以用来控制类的初始化顺序。 -
延迟初始化
静态语句块可以用于延迟初始化,即只有在类被实际使用时才进行初始化。 -
类级别的条件判断
静态语句块可以包含逻辑判断,根据条件来执行不同的初始化代码。 -
执行一次性操作
静态语句块在类第一次被加载到JVM时执行,并且只执行一次。这使得它适合执行那些只需要执行一次的操作。 -
静态资源分配
静态语句块可以用来分配静态资源,如加载配置文件、初始化数据库连接等。 -
静态工厂方法
静态语句块可以与静态工厂方法结合使用,以提供类的实例化逻辑。 -
静态代码复用
在某些情况下,静态语句块可以用于复用静态代码,避免在多个构造函数或方法中重复相同的初始化代码。 -
样例代码
static {
int n = 5;
}
-
图例
-
规则样例
staticBlock sb where sb contain variableDeclaration;
4.7. 同步代码块(synchronizedBlock)
同步代码块的作用主要是确保多线程环境下的线程安全。
以下是同步代码块的几个关键作用:
-
确保原子性
同步代码块确保了代码块内的一系列操作作为一个整体执行,不可被其他线程中断,从而保证了操作的原子性。 -
防止数据竞争
当多个线程尝试同时访问和修改共享资源时,同步代码块可以防止出现数据竞争(Race Condition)的问题。 -
实现线程互斥
同步机制通过锁定机制实现线程互斥,即在同一时刻只允许一个线程执行同步代码块。 -
避免死锁
虽然同步本身可能导致死锁,但正确使用同步代码块可以避免或减少死锁的发生。 -
提高数据一致性
通过同步代码块,可以确保对共享数据的所有访问都是有序的,从而维护数据的一致性。 -
控制并发访问
同步代码块可以用来控制对特定资源的并发访问,例如,限制同时访问数据库连接的线程数量。 -
实现线程通信
同步代码块可以与wait()、notify()和notifyAll()方法一起使用,实现线程间的协调和通信。 -
保护关键资源
同步代码块可以用来保护那些不应该被多个线程同时修改的关键资源。 -
提高程序的可维护性
通过集中管理对共享资源的访问,同步代码块可以提高程序的可维护性。 -
实现线程安全的单例模式
同步代码块常用于实现延迟加载的线程安全的单例模式。 -
样例代码
// 使用对象锁(任意对象):
synchronized(Object) {
// 同步代码块
}
// 使用类锁(当前类的Class对象):
synchronized(ClassName.class) {
// 同步代码块
}
-
图例
-
节点属性
名称 | 描述 | 值类型 | 示例 | DSL 规则 |
---|---|---|---|---|
lock | 锁 | 任意节点 | synchronized (i) { System.out.println(“hello”); } | synchronizedBlock sy where sy.lock contain variableAccess va where va.name == “i”; |
body | 同步块 | block语句块 | synchronized (i) { System.out.println(“hello”); } | synchronizedBlock sy where sy.body contain functionCall fc where fc.name == “println”; |
body.statementNum | body语句块的语句数量 | 数值 | synchronized (i) { System.out.println(“hello”);} | synchronizedBlock sb where sb.body.statementNum == 1; |
firstStatement | synchronizedBlock的第一条语句 | 任意节点 | synchronized (i) { System.out.println(“hello”); } | synchronizedBlock sb where sb.firstStatement contain unaryOperation; |
lastStatement | synchronizedBlock的最后一条语句 | 任意节点 | synchronized (i) { System.out.println(“hello”); } | synchronizedBlock sb where sb.lastStatement contain functionCall; |
5. CodeNavi插件
-
在Vscode 的插件中,查询:
codenavi
,并安装。 -
使用插件的链接安装: https://marketplace.visualstudio.com/items?itemName=HuaweiCloud.codenavi