Java语言中的修饰符
修饰符 | 类 | 成员方法 | 构造方法 | 成员变量 | 局部变量 |
---|---|---|---|---|---|
abstract | √ | √ | — | — | — |
static | — | √ | — | √ | — |
public | √ | √ | √ | √ | — |
protected(受保护的) | — | √ | √ | √ | — |
private(私有的) | — | √ | √ | √ | — |
synchronized(同步的) | — | √ | — | — | — |
native(本地的) | — | √ | — | — | — |
transient(暂时的) | — | — | — | √ | — |
volatile(易失的) | — | — | — | √ | — |
final(不可改变) | √ | √ | — | √ | √ |
- 修饰顶层:abstract public final(static protected private不能)
- 成员方法和成员变量可以有多种修饰符,局部变量只能用final修饰
7.1 访问控制修饰符
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同的包 |
---|---|---|---|---|---|
公开 | Public | √ | √ | √ | √ |
受保护 | Protected | √ | √ | √ | — |
默认 | 没有访问控制修饰符 | √ | √ | — | — |
私有 | Private | √ | — | — | — |
7.2 abstract修饰符
abstract修饰符可用来修饰类和成员方法:
- 用abstract修饰的类表示抽象类,抽象类位于继承树的抽象层,抽象类不能被实例化,即不允许创建抽象类本身的实例。没有用abstract修饰的类称为具体类,具体类可以被实例化。
- 用abstract修饰的方法表示抽象方法,抽象方法没有方法体。抽象方法用来描述系统具有什么功能,但不提供具体的实现。没有用abstract修饰的方法称为具体方法,具体方法具有方法体。
使用abstarct修饰符需要遵循以下语法规则:
- 抽象类中可以没有抽象方法,但包含了抽象方法的类必须被定义为抽象类。如果子类没有实现父类中所有的抽象方法,那么子类也必须定义为抽象类,否则编译出错。例如,以下代码中Sub类继承了Base类,但Sub类仅实现了Base类中的method1()抽象方法,而没有实现method2()抽象方法,因此Sub类必须声明为抽象类,否则编译出错:
abstract class Base{
abstract void method1();
abstract void method2();
}
//编译出错,Sub必须声明为抽象类
Class Sub extends Base{
void method1(){
System.out.println("method1")
}
}
- 没有抽象静态方法
abstract class Base{
static abstract void method10;//编译出错,static和abstract修饰符不能连用
static void method2(){···}//合法,抽象类中可以有静态方法
}
- 抽象类中可以有非抽象的构造方法,创建子类的实例时可能会调用这些构造方法,关于父类构造方法的调用规则参见本书第11章的11.2.3节(子类调用父类的构造方法)。抽象类不能被实例化,然而可以创建一个引用变量,其类型是一个抽象类,并让它引用非抽象的子类的一个实例。例如:
abstract class Base{}
class Sub extends Base{
public static void main(String[] args) {
Base base1 = new Base();//错误:Base是抽象的; 无法实例化
Base base2 = new Sub();
}
}
- 抽象类及抽象方法不能被final修饰符修饰。abstract修饰符与final修饰符不能连用,因为抽象类只有允许创建其子类,它的抽象方法才能被实现,并且只有它的具体子类才能被实例化,而用final修饰的类不允许拥有子类,用final修饰的方法不允许被子类方法覆盖,两此把abstract修饰符与final修饰符连用,会导致自相矛盾。
- 抽象方法不能被private修饰符修饰。这是因为如果方法是抽象的,表示父类只声明具备某种功能,但没有提供实现。这种方法有待于某个子类去实现它。则abstract必须可见。假如Java允许把父类的方法同时用abstract和private修饰,那就意味着在父类中声明一个永远无法实现的方法,所以在Java中不允许出现这一情况。
7.3 final修饰符
final具有“不可改变的”的含义,它可以修饰非抽象类、非抽象成员方法和变量:
- 用final修饰的类不能被继承,没有子类。
- 用final修饰的方法不能被子类的方法覆盖。
- 用final修饰的变量表示常量,只能被赋一次值。
final不能用来修饰构造方法,因为“方法覆盖”这一概念仅适用于类的成员方法,而不适用于类的构造方法,父类的构造方法和子类构造方法之间不存在覆盖关系,因此private类型的方法默认为final
7.3.1 final类
在第6章的6.7.3节(继承关系的最大弱点:打破封装)曾经指出,继承关系的弱点是打破封装,子类能够
访问父类的实现细节,而且能以方法覆盖的方式修改实现细节。在以下情况,可以考虑把类定义为final类型,使得这个类不能被继承:
- 不是专门为继承而设计的类,类本身的方法之间有复杂的调用关系。假如随意创建这些类的子类,子类有可能会错误地修改父类的实现细节。
- 出于安全的原因,类的实现细节不允许有任何改动。
- 在创建对象模型时,确信这个类不会再被扩展。
7.3.2 final方法
在某些情况下,出于安全的原因,父类不允许子类覆盖某个方法,此时可以把这个方法声明为final类型。
7.3.3 final 变量
用final修饰的变量表示取值不会改变的常量。final变量具有以下特征:
- final修饰符可以修饰静态变量、实例变量和局部变量,分别表示静态常量、实例常量和局部常量。例如,某个中学的学生都有出生日期、姓名和年龄这些属性,其中学生的出生日期永远不会改变,姓名有可能改变,年龄每年都会变化。此外,该中学在招收学生时,对学生的年龄做了限制,只会招收年龄在10~23岁的学生。以下
是Student类的源程序,其中学生的最大年龄及最小年龄为静态常量,学生的出生日期为实例常量。 - final类型的变量都必须显式初始化
- final变量只能赋一次值。例如,以下程序代码试图给var1实例常量和var局部常量赋两次值,并且试图改变final类型的参数param的值,这会导致编译错误:
public class Sample{
private final int varl=1;//定义并初始化varl实例常量
public Sample(){
varl=2;//编译出错,不允许改变varl实例常量的值
public void method(final int param){
final int var2=1;//定义并初始化var2局部常量
var2++;//编译出错,不允许改变var2局部常量的值
param++;//编译出错,不允许改变final类型参数的值
}
}
}
- 如果将引用类型的变量用final修饰,那么该变量只能始终引用一个对象,但可以改变对象的内容,例如:
public class Sample{
public int var;
public Sample(int var){
this.var = var;
}
public static void main(String args[]) {
final Sample s = new Sample(1);
//合法,定义并初始化 final类型的引用变量s
s.var = 2;
//合法,修改引用变量s所引用的Sample对象的var属性
s = new Sample(2);
//编译出错,不能改变引用变量s所引用的Sample对象
}
}
7.4 static 修饰符
static修饰符可以用来修饰类的成员变量、成员方法和代码块:
- 用static修饰的成员变量表示静态变量,可以直接通过类名来访问。
- 用static修饰的成员方法表示静态方法,可以直接通过类名来访问。
- 用static修饰的程序代码块表示静态代码块,当Java虚拟机加载类时,就会执行该代码块。
7.4.1 static 变量
-
类的成员变量有两种:一种是被static修饰的变量,叫类变量,或静态变量;一种是没有被static修饰的变量,叫实例变量
-
在类的内部,可以在任何方法内直接访问静态变量;在其他类中,可以通过某个
类的类名来访问它的静态变量。 -
static变量在某种程度上与其他语言(如C语言)中的全局变量相似。Java语言不支持不属于任何类的全局变量,静态变量提供了这一功能,它有两个作用:
-
能被类的所有实例共享,可作为实例之间进行交流的共享数据。
-
如果类的所有实例都包含一个相同的常量属性,可把这个属性定义为静态常量类型,从而节省内存空间。例如某种类型的变压器Transformer类,它所接受的最小输入电压为110V,最大输入电压为220V,输出电压为15V,这是对所有变压器Transformer对象适用的属性,可在Transformer类中按如下方式定义它们:
static final int MAX INPUT VOLTAGE=220; static final int MIN_INPUT_VOLTAGE=110; static final int OUTPUT_VOLTAGE=15;
-
7.4.2 staic方法
成员方法分为静态方法和实例方法。用static修饰的方法叫静态方法或类方法。青态方法也和静态变量一样,不需创建类的实例,可以直接通过类名来访问.
7.4.3 static 代码块
类中可以包含静态代码块,它不存在于任何方法体中。Java虚拟机加载类时,会执行这些静态代码块。如果类中包含多个静态块,那么Java虚拟机按它们在类中出现的顺序依次执行它们,每个静态代码块只会被执行一次。例如以下Sample类中包含两个静态代码块。运行Sample类的main()方法时,Java虚拟机首先加载Sample类,在加载的过程中依次执行两个静态代码块。Java虚拟机机载Sample类后,再执行main()方法:
7.4.4 用 static 进行静态导入
从JDK5开始引入了静态导入语法(import static),其目的是为了在需要经常访问同一个类的方法或成员变量的场合,简化程序代码。
7.5 小结
- 访问控制修饰符
- public的访问级别最高,其次是protected、默认和private。
- 成员变量和成员方法可以处于4个访问级别中的一个:公开、受保护、默认或私有。
- 顶层类可以处于公开或默认级别,顶层类不能被protected和private修饰。
- 局部变量不能被访问控制修饰符修饰。
- abstract修饰符
- 抽象类不能被实例化
- 抽象类中可以没有抽象方法,但包含抽象方法的类必须为抽象方法
- 如果子类没有实现父类中所有的抽象方法,则子类也必须定义为抽象类
- 抽象类不能定义为final和static类型
- 抽象方法不能定义为private,final,static类型
- 没有抽象构造方法
- 抽象方法没有方法体
- final修饰符
- 用final修饰的类不能被继承。
- 用final修饰的方法不能被子类的方法覆盖。
- private类型的方法都默认为final方法,因而不能被子类的方法覆盖。
- final类型的变量必须被显式初始化,并且只能被赋一次值。
- static修饰符
- 静态变量在内存中只有一个备份,在类的所有实例中共享。
- 在静态方法中不能直接访问实例方法和实例变量。
- 静态方法中不能使用this和super关键字。
- 静态方法不能被 abstract 修饰。
- 静态方法和静态变量都可以通过类名直接被访问。
- 当类被加载时,静态代码块只被执行一次。类中不同的静态代码块按它们在类中出现的顺序被依次执行。