Java同步块使用类监视器时为何必须书写.class后缀?
为什么使用类监视器的同步块必须加
.class后缀,不能直接写类名 这根本不是“帮编译器区分类监视器和实例监视器”的设计,本质是Java语法规则的硬性要求,说穿了就几个很直白的原因:
- 首先
synchronized的括号里传参要求非常明确:必须是一个对象引用,基本类型值、没有实际值的语法标记都不能放在这,不然编译直接报错。 - 单独写类名(比如例子里的
SomeClass)在表达式位置根本不是合法的Java代码。类名本身只是你写代码时用的语法符号,不是运行时真实存在的实体,你总不能把一个代码里的标记当对象传给锁吧?平时你写类名,要么是声明变量类型、要么是导包、要么是点出来调用静态方法/构造函数,从来没有能单独把类名当一个值写在逻辑里的用法。 - 你写的
SomeClass.class是Java规范里明确定义的类字面量写法,编译器遇到这个结构,会自动帮你拿到JVM加载SomeClass时生成的全局唯一的java.lang.Class实例的引用——这个实例是堆里实实在在存在的对象,刚好满足synchronized要对象当锁的要求。
别被大驼峰命名的约定误导了,编译器从来不会靠命名格式判断你写的是类还是变量。命名规范只是大家约定的代码风格,不是语法强制要求,Java完全允许你定义大驼峰开头的变量。如果真的允许
synchronized(SomeClass)这种写法,会直接产生无法解决的歧义。
举个很简单的例子,下面这段代码是完全符合Java语法的:
public class Demo { // 大驼峰命名的变量,语法上没有任何问题 private static final Object SomeClass = new Object(); void testMethod() { // 如果允许直接写SomeClass,这里到底是要锁SomeClass类的Class对象,还是锁上面定义的静态Object变量? synchronized (SomeClass) { // 业务逻辑 } } static class SomeClass {} }
这种没法判定语义的歧义是编程语言设计时绝对要规避的,所以Java从一开始就定死了:要拿到类对应的Class对象引用,要么写类名.class,要么拿实例调用getClass(),没有任何模糊的空间。
至于“加了.class就是用类监视器”,只是这个语法带来的自然结果——毕竟你拿到的是类对应的全局唯一Class对象,所有用这个对象当锁的代码自然就实现了类级别的同步,不是为了区分锁类型专门加的标记。
内容的提问来源于stack exchange,提问作者John Smith
相关产品推荐
相关产品推荐

