一文汇总Java 修饰符

目录

修饰符

注解和接口(Annotation & Interface)

访问修饰符(Access Modifier)

万能的final(Omni-final)

互斥修饰符(Mutually Exclusive Modifiers)

修饰符的申明顺序(Declaration Order of Modifiers)


修饰符

在《Java关键字分类解析》一文里已经对Java的所有关键字进行了分类归组,并对部分关键字做了一些简单的介绍分析。不过对于修饰符这部分值得更详细的探讨,所以本文就来讲述下这些修饰符在Java中的功能及应用。

Java的关键字里总共有11种修饰符,但实际上还有一种访问修饰符(Access Modifier),那就是“没有修饰”的修饰符,也就是不加任何修饰符在作用对象上。这种修饰符没有固定名称,以下都是出现过的的名字:“默认(default)”、“无修饰(No Modifier)”、“包私有(Package-Private)”、“包可见(Package)”。本文将以(package)来表示该隐形的修饰符,然后针对一共12种修饰符来作阐述。

对于所有Java的概念,可以应用修饰符的对象有三种:类(Class)、方法(Method)、变量(Variable)。进一步考虑,Java可以在类的定义里定义另一个类,所以对于类定义的位置又分出:顶层类(Top-level Class),即直接定义在文件包下的类;和嵌套类(Nested Class)。对于变量,根据其是定义在类中还是方法中,可分别定义为:类字段(Class Field)和局部变量(Local Variable)。

再进一步分类的话,嵌套类还可以分成静态嵌套类(Static Nested Class)和内部类(Inner Class),不过这只是static修饰符起的效果,所以不进一步区分。同样的对于方法也不区分静态方法和对象方法,对字段也不分静态字段(Static Field)和实例变量(Instance Variable)。对于局部变量,其实还可以细分出方法参数(Method Parameter),但它的效果基本跟方法内直接定义的变量效果一致,所以不做区分。这里也不对接口(interface)进行讨论,因为它基本相当于是完全抽象类(abstract class)

这样就得到了5种基本的修饰符作用对象,但不是所有的修饰符都可以作用在每一种对象上,所以把12种修饰符在Java中实际可作用的对象总结成下表:

MODIFIER CLASS METHOD VARIABLE
TOP-LEVEL CLASS NESTED CLASS CLASS FIELD LOCAL VARIABLE
private NO YES YES YES NO
protected NO YES YES YES NO
public YES YES YES YES NO
(package) YES YES YES YES
abstract YES YES YES NO NO
final YES YES YES YES YES
native NO NO YES NO NO
static NO YES YES YES NO
strictfp YES YES YES NO NO
synchronized NO NO YES NO NO
transient NO NO NO YES NO
volatile NO NO NO YES NO

注解和接口(Annotation & Interface)

在Java Syntax表中可以找到12种不同的修饰符,不包括(package),但包含了注解(Annotation)。由于注解可以进行自定义,也不同于本文主要讨论的修饰符针对一个作用对象只能出现一次(或没有),注解可以有多个同时作用在同一个对象上,所以不对注解做详细介绍。

另一方面,Java也有一个系统类叫Modifier,它内部定义了12种静态常量,其中11中对应着11种关键字指定的修饰符,另外一种也不是(package),而是interface。

这些静态常量其实只是比特位标记,Java库的ClassMethodField都有getModifiers()方法返回指定对象所拥有的修饰符。Modifier类里还有许多静态方法来辅助检测指定的修饰符是否存在。虽然没有任何判定(package)修饰符的方法,但其实检测对象没有publicprotectedprivate任一种修饰符,那就说明它是(package)修饰符了。

自Java 7开始,Modifier还提供方法返回可应用于各对象的修饰符的汇总,对于这些源码提供的信息也侧面反应出了表的内容:

对于interface是修饰符,想必主要是为了区别类和接口。其实Modifier还有很多非公开的常量定义,这些都不在本文讨论范围。本文的也不将interface认定为修饰符,所以研究对象还是基于表中罗列的12种修饰符。

访问修饰符(Access Modifier)

对于访问修饰符,Java开发者都不会陌生,它们的作用主要是限制类自身和其成员的可访问域。下表就是对于访问限制的总结:

MODIFIER CLASS PACKAGE SUBCLASS WORLD
public YES YES YES YES
protected YES YES YES NO
(package) YES YES NO NO
private YES NO NO NO

(想起大学刚学Java的时候,我还是将这些修饰符跟现实生活来对照进行记忆,而现在对它们的理解则已经成了条件反射式。)

回看之前的表格可以看到对于顶层类(Top-level Class),只有public(package),所以这里也提一条对于类定义的限制:
一个文件只能有一个(可以没有)公开顶层类,且该类必须和文件同名。

万能的final(Omni-final)

几乎所有修饰符都有不能应用的对象,唯独final是可以作用在所有对象上都可以修饰,但是它们的意义不完全相同。

  • 类(class): 用final修饰的类不能定义子类。
  • 方法(Method): 用final修饰的方法不能被子类覆盖。
  • 变量(Variable): 用final修饰的变量只能被初始化一次,之后变量不能再被赋值。

互斥修饰符(Mutually Exclusive Modifiers)

对一个指定的作用对象而言,可以拥有多个不同的修饰符,但不是所有修饰符都可以同时修饰一个对象的,很多修饰符之间都有互斥性。比如4种访问修饰符之间就是互斥的,只能限定一个且只有一个访问修饰符。但访问修饰符和基础修饰符之间没有任何互斥关系。

abstract几乎和其他所有基础修饰符都有互斥关系,毕竟abstract表示所修饰对象表示其内容将由子类决定,自身没有具体实现。唯一的例外就是在修饰类的时候,abstract可以和strictfp组合使用。这也可以理解,因为抽象类可以有部分方法是被实现的。

volatile修饰符只能作用在类字段上,它基本用在多线程编程方面,用以表明线程要访问字段的值时必须获取其最新的内容。它和final不能同时修饰一个字段,因为final表示字段在初始化话只能读不能写,也就不用担心它有新的值。

修饰符的互斥约束基本就这些,其实也不用去记忆它们,现代的Java编译器都能在编译时就告诉开发者哪些是不合法的修饰符组合,IDE也会对这些违规写法抛出错误来提醒开发者。

修饰符的申明顺序(Declaration Order of Modifiers)

当某个对象的修饰符满足了上述的所有条件,那这些修饰符就可以合法存在并对这个对象起作用。不过之于多个修饰在申明顺序,Java编译器并没用做强制的规定。

比如想要定义一个公开的不加入到序列化的静态常量,下边在制定类中的定义方式都是有效的(即使说statictransient放在一起一般没什么意义):

public static final transient int zero = 0;
static transient public final int one = 1;
final public transient static int two = 2;
transient final static public  int three = 3;

但为了保持编写风格的一致性,以及代码的可读性,对于修饰符的申明顺序还是有要求的。其实前面Modifier提供的修饰符汇总方法就可以反映出修饰符的申明顺序。另外,在Java Language Specification的Classes一章里也对类修饰符、方法修饰符、字段修饰符的罗列顺序也就是通常申明时要遵循的顺序。归总如下(这里将注解也包括进来)就是

  • Annotation
  • public
  • protected
  • private
  • static
  • abstract
  • final
  • native
  • synchronized
  • transient
  • volatile
  • strictfp

这个顺序也不用记忆,虽然编译器不会对它们进行约束,但有很多Checkstyle工具都会帮助开发者设定代码风格限定条件,并对不符合条件的写法抛出错误指示。


原文链接:

Java修饰符及其作用对象

你可能感兴趣的:(Java)