考虑以下Java类:
public class ClassMembers {
;
int field;;;
void method() {} ;;
;
;;
;;;
class Inner {}
;
}
它包含许多零散的分号。
这是合法的Java代码吗?换句话说,它能编译吗?
让我们学习。
类成员和杂散分号
当您得知前面的示例编译时没有错误时,您可能会也可能不会感到惊讶。
杂散分号是允许的类成员。
一切都在JLS中
所有这些都在Java语言规范JLS(https://docs.oracle.com/javase/specs/jls/se21/html/)中定义。
第8.1.7节(https://docs.oracle.com/javase/specs/jls/se21/html/jls-8.html#jls-8.1.7)称为“类主体和成员声明”。它定义了以下产品:
ClassBody:
{ {ClassBodyDeclaration} }
ClassBodyDeclaration:
ClassMemberDeclaration
InstanceInitializer
StaticInitializer
ConstructorDeclaration
ClassMemberDeclaration:
FieldDeclaration
MethodDeclaration
ClassDeclaration
InterfaceDeclaration
;
它说一个类的主体可能包含零个或多个类主体声明。
允许的类体声明是:
- 类成员;
- 实例初始化器;
- 静态初始化器;
- 构造函数
反过来,允许的类成员是:
- 字段;
- 方法;
- 类;
- 接口;
- 分号字符
因此,分号字符是允许的类“成员”。
然而,他们不是真正的成员。
零散的分号和生成的类文件
虽然源代码中允许使用杂散分号,但它们不会对编译的类文件产生任何影响。
下面的Java类:
public class OnlySemiColons {
;
;;
;;;
}
相当于以下Java类:
public class Empty {}
换句话说,它们的区别仅在于类名。
这是两个类的javap-v
输出之间的区别:
1,5c1,5
< Classfile /tmp/blog/OnlySemiColons.class
< Last modified Feb 10, 2024; size 207 bytes
< SHA-256 checksum 7ab6fcd28276909c579669b2f0a6401ee9785c1bb1afb3119dcd7fd829907ba2
< Compiled from "OnlySemiColons.java"
< public class blog.OnlySemiColons
---
> Classfile /tmp/blog/Empty.class
> Last modified Feb 10, 2024; size 189 bytes
> SHA-256 checksum b276e6806d458674f9178b351dd486b20943d7bf7b72634545140f03c94c0346
> Compiled from "Empty.java"
> public class blog.Empty
9c9
< this_class: #7 // blog/OnlySemiColons
---
> this_class: #7 // blog/Empty
19,20c19,20
< #7 = Class #8 // blog/OnlySemiColons
< #8 = Utf8 blog/OnlySemiColons
---
> #7 = Class #8 // blog/Empty
> #8 = Utf8 blog/Empty
24c24
< #12 = Utf8 OnlySemiColons.java
---
> #12 = Utf8 Empty.java
26c26
< public blog.OnlySemiColons();
---
> public blog.Empty();
37c37
< SourceFile: "OnlySemiColons.java"
---
> SourceFile: "Empty.java"
因此,除了类的名称之外,它们的类文件的内容是相同的。
顶级声明和零散的分号
以下Java代码编译时没有错误:
public class TopLevel {
};
;;;;
interface Foo {};;
;
record Bar() {};
杂散分号是允许的顶级声明。
一切都在JLS中
再一次,这一切都在Java语言规范中定义。
第7.3节定义了编译单位。
以下是普通编译单元的制作:
OrdinaryCompilationUnit:
[PackageDeclaration] {ImportDeclaration} {TopLevelClassOrInterfaceDeclaration}
因此,一个普通的编译单元由以下部分组成:
- 可选的包声明;
- 零份或多份import
- 零个或多个顶级类或接口。
因此,空文件是有效的编译单元。它不会生成类文件,但javac会“编译”它而不会出错。
但我离题了。。。
顶层声明稍后在第7.6节(https://docs.oracle.com/javase/specs/jls/se21/html/jls-7.html#jls-7.6)中定义:
TopLevelClassOrInterfaceDeclaration:
ClassDeclaration
InterfaceDeclaration
;
允许的顶级声明包括:
- 类声明;
- 接口声明;
- 分号字符。
因此,分号字符是允许的顶级声明。
不允许作为包声明
就像以下代码无法编译一样:
public class Foo {}
// does not compile!
package com.example;
class Example {}
以下内容也无法编译:
;;; // does not compile!
package com.example;
class Example {}
包声明(如果存在)必须是源文件的第一个声明。
不允许作为进口报关单
就像以下代码无法编译一样:
import java.util.ArrayList;
public class Example {}
// does not compile
import java.util.List;
class Util {}
以下内容也无法编译:
import java.util.ArrayList;
;;; // does not compile
import java.util.List;
class Util {}
导入声明之间不能有顶级声明。
但是。。。
另一方面,以下示例编译时没有错误:
;;;
class Stray {}
;;;
public class TopLevelStart {
}
它是未命名包的类,没有导入声明。
因此,在这种情况下,文件可能以顶级声明开头。而且,由于分号是允许的顶级声明,因此文件可以以分号开头。
仅仅因为你不能,并不意味着你应该
以下是JLS第7.6节(https://docs.oracle.com/javase/specs/jls/se21/html/jls-7.html#jls-7.6)的摘录(重点是我的):
在编译单元中类和接口声明级别出现的额外“;”标记对编译单元的含义没有影响。Java编程语言中允许使用任意分号,这只是对习惯在类声明后放置“;”的C++程序员的让步。它们不应该在新的Java代码中使用。
所以它告诉我们两件事。
首先,它暗示了为什么在语言中添加了零散的分号。这样Java的语法对C++程序员来说就很熟悉了。
其次,它明确指出,在新的Java代码中不应使用杂散分号
这不是惯用语,可能会无缘无故地让代码的读者感到困惑。
结论
不要在代码中使用零散的分号。
它们可能是一个有趣的Java琐事。但是,如前所述:
- 它们不地道;
- 它们可能会无缘无故地让代码的读者感到困惑。
您可以在这个GitHub中(https://github.com/objectos/blog-examples/tree/main/2024/02/11)找到示例的源代码。
原文链接:https://www.objectos.com.br/blog/stray-semicolons-in-java.html
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2998.html
暂无评论