《Java安全编码标准》一3.3 DCL02-J将所有增强for语句的循环变量声明为final类型

简介: 本节书摘来自华章出版社《Java安全编码标准》一书中的第3章,第3.3节,作者 (美)Fred Long,Dhruv Mohindra,Robert C. Seacord,Dean F. Sutherland,David Svoboda,更多章节内容可以访问云栖社区“华章计算机”公众号查看

3.3 DCL02-J将所有增强for语句的循环变量声明为final类型

Java 5平台(也因for-each风格出名)引入了增强的for语句,它用来对对象集合进行迭代。与基本的for语句不同,在基本的for语句中,给循环变量赋值是不能对循环的迭代次序有所影响。但在增强的for语句中,给循环变量赋值就可以有影响,而不是像程序员通常认为的那样。这使我们认识到应避免给在for循环中的循环变量赋值。
详情请见JLS的14.14.2节“增强的for语句”[JLS 2005]。
一个增强的for循环通常采用以下这种形式:

??for (ObjType obj : someIterableItem) {
????// ...
??}
这等价于以下形式的基本for循环:
??for (Iterator myIterator = someIterableItem.iterator();
???????myIterator.hasNext();) {
???ObjType obj = myIterator.next();
???// ...
??}
AI 代码解读

所以,一个给循环变量赋值的动作,等价于修改循环体的局部变量的值,这个变量的初始值会被循环迭代器引用。这种修改不一定是错误的,但它会模糊循环的行为,或者意味着对增强型for语句的内在实现,这在理解上有问题。
可以将所有的for语句中的循环变量声明为final。这个final声明可以让Java编译器做一个标志,并且拒绝对这个循环变量的任何赋值。

3.3.1 不符合规则的代码示例

这个不符合规则的代码示例想要使用增强型的for循环处理对象集合。此外,它还希望跳过对集合中某一个元素的处理。

Collection<ProcessObj> processThese = // ...?

for (ProcessObj processMe: processThese) {
??if (someCondition) { // found the item to skip
????someCondition = false;
????processMe = processMe.getNext(); // attempt to skip to next item
??}
??processMe.doTheProcessing(); // process the object
}
AI 代码解读

这种跳过到下一个集合元素的想法看起来是可以实现的,因为已经成功赋值,并且processMe变量的值也更新了。然而,和基本型的for循环不同,这个赋值并没有改变循环执行的迭代次序。因此,虽然需要跳过的元素跳过了,但是它之后的元素被处理了两次。
注意,如果声明processMe为final,在试图对它进行这样的赋值时,就会产生编译器错误。

3.3.2 符合规则的方案

这个符合规则的方案可以正确地处理在集合中的每一个对象,并且每一个对象只会被处理
一次。

Collection<ProcessObj> processThese = // ...?

for (final ProcessObj processMe: processThese) {
??if (someCondition) { // found the item to skip
????someCondition = false;
????continue; // skip by continuing to next iteration
??}
??processMe.doTheProcessing(); // process the object
}
AI 代码解读

3.3.3 风险评估

给增强型的for循环的循环变量赋值,不影响整体的迭代次序,这样会导致程序员产生困惑,并且会让数据的状态不一致。
image

自动化检查 这条规则可以很容易通过使用静态分析来实行。

3.3.4 参考书目

[JLS 2005] 14.14.2节 增强的for语句

目录
打赏
0
0
0
0
1408
分享
相关文章
除了变量,final还能修饰哪些Java元素
在Java中,final关键字不仅可以修饰变量,还可以用于修饰类、方法和参数。修饰类时,该类不能被继承;修饰方法时,方法不能被重写;修饰参数时,参数在方法体内不能被修改。
57 3
|
22天前
|
《从头开始学java,一天一个知识点》之:循环结构:for与while循环的使用场景
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问&quot;`a==b`和`equals()`的区别&quot;,大脑突然空白
64 22
|
27天前
|
java变量与数据类型:整型、浮点型与字符类型
### Java数据类型全景表简介 本文详细介绍了Java的基本数据类型和引用数据类型,涵盖每种类型的存储空间、默认值、取值范围及使用场景。特别强调了`byte`、`int`、`long`、`float`、`double`等基本类型在不同应用场景中的选择与优化,如文件流处理、金融计算等。引用数据类型部分则解析了`String`、数组、类对象、接口和枚举的内存分配机制。
55 15
|
28天前
|
课时11:Java数据类型划分(浮点类型)
课时11介绍了Java中的浮点数据类型。主要内容包括:1. 定义小数,默认使用Double类型;2. 定义Float变量,需在数值后加&quot;F&quot;或&quot;f&quot;进行强制转换;3. 观察不同类型计算结果,如Int型除法会丢失精度,需至少包含一个Double或Float类型以确保准确性。总结指出,在复杂计算中推荐使用Double类型以避免精度损失。
|
28天前
|
课时10:Java数据类型划分(整型类型)
本文主要围绕Java中整型数据展开,详细讲解整型变量、常量的概念,整型数据运算规则,包括数据溢出问题及解决方法,数据类型转换(自动转换与强制转换)的原理和注意事项,同时介绍了整型数据默认值的相关知识,以及byte数据类型与int数据类型的关系和使用场景,帮助读者全面掌握Java整型数据的相关内容。
Java泛型类型擦除以及类型擦除带来的问题
本文主要讲解Java中的泛型擦除机制及其引发的问题与解决方法。泛型擦除是指编译期间,Java会将所有泛型信息替换为原始类型,并用限定类型替代类型变量。通过代码示例展示了泛型擦除后原始类型的保留、反射对泛型的破坏以及多态冲突等问题。同时分析了泛型类型不能是基本数据类型、静态方法中无法使用泛型参数等限制,并探讨了解决方案。这些内容对于理解Java泛型的工作原理和避免相关问题具有重要意义。
Java快速入门之判断与循环
本文介绍了编程中的流程控制语句,主要包括顺序结构、判断结构(if语句和switch语句)以及循环结构(for、while和do...while)。通过这些语句可以精确控制程序的执行流程。if语句有三种格式,分别用于简单条件判断、二选一判断和多条件判断。switch语句适用于有限个离散值的选择判断,而循环结构则用于重复执行某段代码,其中for循环适合已知次数的情况,while循环适合未知次数但有明确结束条件的情况,do...while则是先执行后判断。文中还提供了多个示例和练习,帮助读者理解并掌握这些重要的编程概念。
Java智慧工地(源码):数字化管理提升施工安全与质量
随着科技的发展,智慧工地已成为建筑行业转型升级的重要手段。依托智能感知设备和云物互联技术,智慧工地为工程管理带来了革命性的变革,实现了项目管理的简单化、远程化和智能化。
57 5
Java循环操作哪个快?
本文探讨了Java中stream API与传统for循环在性能上的对比,通过多个示例分析了不同场景下两者的优劣。作者指出,尽管stream API使代码更简洁,但不当使用会降低可读性和性能,特别是在处理大数据量时。实验结果显示,在多数情况下,普通for循环的性能优于stream API,尤其是在单次操作耗时较短但需多次执行的场景中。文章建议开发者在设计初期就考虑全局流程,避免重复使用stream流,以提升代码质量和性能。
125 1
Java循环操作哪个快?
Java循环操作哪个快?
本文探讨了Java中Stream API与传统for循环的性能对比及适用场景。作者通过实际案例分析,指出在某些情况下,过度使用Stream API会导致代码可读性和维护性下降。测试结果显示,在数据量较小的情况下,普通for循环的性能优于Stream API,尤其是在涉及多次类似操作时。因此,建议在开发中根据具体需求选择合适的遍历方式,以提高代码的可读性和性能。
118 5
Java循环操作哪个快?