在Java编程语言中,有许多修饰符可以使用,它们大致可以分为两大类:访问控制修饰符、其他类型的修饰符。
这些修饰符主要用于指定类、方法或变量的特性,并且通常位于声明语句的开头部分。下面通过一些示例来进一步说明这一点:
public class ExampleClass { // 定义一个公共类private boolean flag; // 私有布尔型变量static final double MONTHS = 12.0; // 静态最终常量表示月份protected static final int DEFAULT_SIZE = 50; // 受保护的静态整型常量public static void main(String[] args) { // 主函数入口点// 函数的具体实现代码}
}
这段代码展示了不同种类的修饰符如何应用于类定义以及其内部成员(如变量和方法)上。public使得ExampleClass对所有其他类可见;private确保了flag仅限于该类内访问;而static final组合则用于创建不可改变的常数MONTHS和DEFAULT_SIZE,其中后者还添加了protected以允许子类访问。最后,main方法被标记为public static,这是启动程序的标准方式。
在Java中,访问控制修饰符用于限制对类、变量、方法和构造方法的访问。Java提供了四种不同的访问权限级别:
- 默认访问权限(也称为包级私有),在同一包内可见,不使用任何修饰符。
- 私有访问权限,使用private修饰符指定,在同一类内可见。
- 公有访问权限,使用public修饰符指定,对所有类可见。
- 受保护的访问权限,使用protected修饰符指定,对同一包内的类和所有子类可见。
默认访问修饰符
默认访问修饰符不需要使用任何关键字。使用默认访问修饰符声明的变量和方法只对同一个包内的类可见。接口中的变量默认是public static final,而接口中的方法默认是public。
示例:
String version = "1.5.1";
boolean processOrder() {return true;
}
私有访问修饰符 - private
私有访问修饰符是最严格的访问级别,被声明为private的方法、变量和构造方法只能在其所属类内部访问。类和接口不能声明为private。
示例:
public class Logger {private String format;public String getFormat() {return this.format;}public void setFormat(String format) {this.format = format;}
}
在这个例子中,Logger类中的format变量是私有的,因此其他类不能直接访问它。为了使其他类能够操作该变量,定义了两个公共方法:getFormat()(返回format的值)和setFormat(String)(设置format的值)。
公有访问修饰符 - public
被声明为public的类、方法、构造方法和接口可以被任何其他类访问。如果几个相互访问的public类分布在不同的包中,则需要导入相应public类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。
示例:
public static void main(String[] arguments) {// ...
}
Java程序的main()方法必须设置为public,否则Java解释器将无法运行该类。
受保护的访问修饰符 - protected
被声明为protected的变量、方法和构造器能被同一个包中的任何其他类访问,也能被不同包中的子类访问。protected修饰符不能用于修饰类和接口,但可以用于方法和成员变量。接口的成员变量和成员方法不能声明为protected。
示例:
class AudioPlayer {protected boolean openSpeaker(Speaker sp) {// 实现细节}
}class StreamingAudioPlayer extends AudioPlayer {boolean openSpeaker(Speaker sp) {// 实现细节}
}
在这个例子中,AudioPlayer类中的openSpeaker方法被声明为protected,使得StreamingAudioPlayer子类可以重载该方法。如果将openSpeaker方法声明为private,则除了AudioPlayer之外的类将无法访问该方法。如果将其声明为public,则所有类都可以访问该方法。如果我们只想让该方法对其所在类的子类可见,则应将其声明为protected。
访问控制和继承
请注意以下关于方法继承的规则:
- 父类中声明为public的方法在子类中也必须为public。
- 父类中声明为protected的方法在子类中要么声明为protected,要么声明为public,不能声明为private。
- 父类中声明为private的方法不能被继承。
非访问修饰符
除了访问控制修饰符外,Java还提供了一些非访问修饰符,以实现其他功能。
static 修饰符
static 修饰符用于创建类方法和类变量。静态变量和方法独立于类的实例,无论一个类实例化多少对象,静态变量都只有一份拷贝。静态方法不能使用类的非静态变量,只能通过参数列表获取数据并进行计算。
静态变量:
- 使用 static 关键字声明的变量称为静态变量或类变量。
- 静态变量在类的所有实例之间共享,无论创建多少个对象,静态变量只有一个副本。
静态方法:
- 使用 static 关键字声明的方法称为静态方法。
- 静态方法可以直接通过类名调用,不需要创建类的实例。
- 静态方法不能访问类的非静态成员(即实例变量和实例方法)。
示例:
public class InstanceCounter {private static int numInstances = 0;protected static int getCount() {return numInstances;}private static void addInstance() {numInstances++;}InstanceCounter() {InstanceCounter.addInstance();}public static void main(String[] arguments) {System.out.println("Starting with " + InstanceCounter.getCount() + " instances");for (int i = 0; i < 500; ++i) {new InstanceCounter();}System.out.println("Created " + InstanceCounter.getCount() + " instances");}
}
在这个例子中,InstanceCounter 类使用了 static 修饰符来创建类变量 numInstances 和类方法 getCount() 和 addInstance()。每次创建一个新的 InstanceCounter 对象时,addInstance() 方法会被调用,从而增加 numInstances 的值。main 方法展示了如何通过类名直接访问静态变量和方法。
运行结果:
Starting with 0 instances
Created 500 instances
final 修饰符
final 修饰符在Java中用于创建不可变的变量、方法和类。它提供了多种用途,包括确保数据的一致性和防止继承或重写。
final 变量可以被显式初始化,并且只能初始化一次。一旦赋值后,final 变量的值就不能再改变。如果 final 变量引用的是一个对象,那么该引用不能指向其他对象,但对象内部的数据是可以修改的。
示例:
public class Test {final int value = 10; // final 变量,只能初始化一次// 声明常量的实例public static final int BOXWIDTH = 6;static final String TITLE = "Manager";public void changeValue() {// value = 12; // 这行代码会导致编译错误,因为 value 是 final 的}public static void main(String[] args) {Test test = new Test();System.out.println(test.value); // 输出 10// test.value = 15; // 编译错误}
}
在这个例子中,value 是一个 final 变量,只能初始化一次。尝试在 changeValue 方法中修改它的值会导致编译错误。BOXWIDTH 和 TITLE 是静态常量,通常与 static 修饰符一起使用来创建类常量。
final 方法
final 方法可以被子类继承,但不能被子类重写(覆盖)。声明 final 方法的主要目的是防止该方法的内容被修改。
示例:
public class Test {public final void changeName() {// 方法体System.out.println("Name changed");}
}class SubTest extends Test {// 以下代码会导致编译错误,因为 changeName 方法是 final 的// @Override// public void changeName() {// System.out.println("Name changed in subclass");// }
}public class Main {public static void main(String[] args) {Test test = new Test();test.changeName(); // 输出 "Name changed"SubTest subTest = new SubTest();subTest.changeName(); // 输出 "Name changed"}
}
在这个例子中,Test 类中的 changeName 方法被声明为 final,因此 SubTest 类不能重写该方法。
final 类
final 类不能被继承,没有任何类能够继承 final 类的特性。这通常用于防止类的扩展,以确保类的行为不会被改变。
示例:
public final class Test {// 类体public void display() {System.out.println("This is a final class");}
}// 以下代码会导致编译错误,因为 Test 类是 final 的
// class SubTest extends Test {
// // 类体
// }public class Main {public static void main(String[] args) {Test test = new Test();test.display(); // 输出 "This is a final class"}
}
在这个例子中,Test 类被声明为 final,因此不能被任何其他类继承。尝试创建一个继承自 Test 的子类会导致编译错误。
abstract 修饰符
abstract 修饰符在Java中用于创建抽象类和抽象方法。抽象类不能被实例化,其主要目的是为了将来对该类进行扩展。一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类必须声明为抽象类,否则将出现编译错误。
抽象类
抽象类可以包含抽象方法和非抽象方法。抽象方法是没有具体实现的方法,其具体实现由子类提供。任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
示例:
abstract class Caravan {private double price;private String model;private String year;public abstract void goFast(); // 抽象方法public abstract void changeColor();
}
在这个例子中,Caravan 类是一个抽象类,包含两个抽象方法 goFast 和 changeColor。任何继承 Caravan 的子类都必须实现这两个方法。
抽象方法
抽象方法是一种没有任何实现的方法,其具体实现由子类提供。抽象方法不能被声明为 final 或 static。
示例:
public abstract class SuperClass {abstract void m(); // 抽象方法
}class SubClass extends SuperClass {@Overridevoid m() {// 具体实现System.out.println("Method m is implemented in SubClass");}
}
在这个例子中,SuperClass 是一个抽象类,包含一个抽象方法 m。SubClass 继承了 SuperClass 并实现了 m 方法。
synchronized 修饰符
synchronized 关键字用于确保方法或代码块在同一时间只能被一个线程访问。synchronized 修饰符可以应用于四个访问修饰符(public, private, protected, 默认)。
示例:
public synchronized void showDetails() {// 方法体System.out.println("Showing details...");
}
在这个例子中,showDetails 方法被声明为 synchronized,这意味着在同一时间只有一个线程可以访问该方法。
transient 修饰符
序列化的对象包含被 transient 修饰的实例变量时,Java 虚拟机 (JVM) 会跳过这些特定的变量,不将其序列化到文件中。transient 修饰符用于定义那些不需要持久化的变量。
示例:
public class MyClass implements Serializable {public transient int limit = 55; // 不会被序列化public int b; // 会被序列化
}
在这个例子中,limit 变量被声明为 transient,因此在序列化时不会被保存到文件中。
volatile 修饰符
volatile 修饰符用于确保成员变量在每次被线程访问时都从共享内存中重新读取,并且当成员变量发生变化时,会强制线程将变化值写回共享内存。这样可以保证多个线程看到的是同一个值。
示例:
public class MyRunnable implements Runnable {private volatile boolean active;public void run() {active = true;while (active) {// 代码}}public void stop() {active = false;}
}
在这个例子中,active 变量被声明为 volatile,确保在一个线程调用 run 方法时,另一个线程调用 stop 方法可以正确地终止循环。
通过这些示例,可以看到 abstract、synchronized、transient 和 volatile 修饰符在不同上下文中的使用方式及其作用。