Java中有多少个非访问修饰符?
在 Java 中,非访问修饰符是指除了 public、private 和 protected 以外的修饰符。Java 中共有多少个非访问修饰符呢?我们来一一看看。
1. static
static 表示静态的。它可以修饰变量、方法和内部类。
修饰变量时,该变量变成了类变量,即不属于任何一个对象,而是属于类的本身。被该变量赋值后,就可以在任何地方通过类名直接调用。
public class Example {
static int count = 0; // 类变量
int id; // 实例变量
Example() {
count++;
id = count;
}
}
public class Test {
public static void main(String[] args) {
Example e1 = new Example();
System.out.println(e1.id); // 1
Example e2 = new Example();
System.out.println(e2.id); // 2
System.out.println(Example.count); // 2
}
}
修饰方法时,该方法变成了类方法,即不属于任何一个对象,而是属于类的本身。被该方法调用时,不需要先创建对象,在调用时直接使用类名即可。
public class Example {
static void sayHello() {
System.out.println("Hello");
}
}
public class Test {
public static void main(String[] args) {
Example.sayHello(); // Hello
}
}
修饰内部类时,该内部类变成了静态内部类,即不属于任何一个外部类的对象,而是属于外部类本身。
public class Example {
static class Inner {
void sayHello() {
System.out.println("Hello");
}
}
}
public class Test {
public static void main(String[] args) {
Example.Inner i = new Example.Inner();
i.sayHello(); // Hello
}
}
2. final
final 表示最终的、不可变的。
修饰变量时,该变量成为了常量,一经赋值后就不可再修改。
public class Example {
final int id = 1; // 常量
}
public class Test {
public static void main(String[] args) {
Example e = new Example();
System.out.println(e.id); // 1
e.id = 2; // 编译错误
}
}
修饰方法时,该方法表示不能被重写。
public class Example {
final void sayHello() {
System.out.println("Hello");
}
}
public class Test extends Example {
void sayHello() { // 编译错误
System.out.println("Hi");
}
}
修饰类时,该类表示不能被继承。
final public class Example {
// ...
}
public class Test extends Example { // 编译错误
// ...
}
3. abstract
abstract 表示抽象的。它可以修饰类、方法。
修饰类时,该类表示抽象类,即不能被实例化的类,只能被继承,其子类必须实现抽象类中的抽象方法。
abstract public class Example {
abstract void sayHello();
}
public class Test extends Example {
void sayHello() {
System.out.println("Hello");
}
}
修饰方法时,该方法表示抽象方法,即没有方法体的方法,只有声明,子类必须实现该方法。
public class Example {
abstract void sayHello();
}
public class Test extends Example {
void sayHello() {
System.out.println("Hello");
}
}
4. synchronized
synchronized 表示同步的。它可以修饰方法和代码块。
修饰方法时,该方法表示同步方法,即在一个线程访问该方法时,其他线程必须等待,直到该方法执行完毕后才能访问。
public class Example {
synchronized void sayHello() {
System.out.println("Hello");
}
}
public class Test {
public static void main(String[] args) {
Example e = new Example();
new Thread(() -> e.sayHello()).start();
new Thread(() -> e.sayHello()).start();
new Thread(() -> e.sayHello()).start();
}
}
修饰代码块时,该代码块表示同步代码块,在该代码块内的同步部分只能由一个线程访问,其他线程必须等待。
public class Example {
void method() {
synchronized (this) {
// ...
}
}
}
public class Test {
public static void main(String[] args) {
Example e = new Example();
new Thread(() -> e.method()).start();
new Thread(() -> e.method()).start();
new Thread(() -> e.method()).start();
}
}
5. volatile
volatile 表示易失的。它可以修饰变量。
修饰变量时,该变量表示易失变量,即多线程并发模型下可以保证其内存可见性。
public class Example {
volatile int count = 0;
}
public class Test {
public static void main(String[] args) {
Example e = new Example();
new Thread(() -> {
for (int i = 0; i < 1000000; i++) {
e.count++;
}
}).start();
new Thread(() -> {
for (int i = 0; i < 1000000; i++) {
e.count++;
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {}
System.out.println(e.count); // 可能是 2000000,也可能小于 2000000
}
}
在上面的代码中,如果 count 变量没有被 volatile 修饰,那么可能会出现 e.count 小于 2000000 的情况。
6. transient
transient 表示短暂的。它可以修饰变量。
修饰变量时,该变量表示短暂变量,即在对象序列化时不会存储该变量的值。
import java.io.*;
public class Example implements Serializable {
transient int id = 1;
}
public class Test {
public static void main(String[] args) {
Example e = new Example();
try {
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("test.txt")
);
out.writeObject(e);
out.close();
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("test.txt")
);
Example e2 = (Example) in.readObject();
System.out.println(e2.id); // 0
in.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
在上面的代码中,变量 id 被 transient 修饰后,在对象序列化后,id 的值变为 0。
7. native
native 表示本地的。它可以修饰方法。
修饰方法时,该方法表示本地方法,即用 C 或 C++ 写的方法,可以通过 Java Native Interface(JNI)在 Java 中调用。
public class Example {
native void sayHello();
}
public class Test {
static {
System.loadLibrary("example");
}
public static void main(String[] args) {
Example e = new Example();
e.sayHello();
}
}
在上面的代码中,通过 Example 类的 native 方法 sayHello(),在 Java 中调用了 C 代码实现的方法。
总结
到这里,我们就看完了 Java 中的 7 个非访问修饰符,包括:
static
final
abstract
synchronized
volatile
transient
native
它们各自有不同的特点和用途,在实际开发中需要根据实际需求选择合适的修饰符。