Java 修饰符
修饰符是我们添加到这些定义中来更改其含义的关键字。 Java 语言有各种各样的修饰符,包括:
- 访问控制修饰符
- 非访问修饰符
要使用修饰符,请在类、方法或变量的定义中包含其关键字。 修饰符位于语句的其余部分之前,如下例所示。
public class className {
// ...
}
private boolean myFlag;
static final double weeks = 9.5;
protected static final int BOXWIDTH = 42;
public static void main(String[] arguments) {
// 方法体
}
访问控制修饰符
Java 提供了许多访问控制修饰符来设置类、变量、方法和构造函数的访问级别。 四个访问级别是
- default - 对包可见,默认。 不需要修改器。
- private - 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
- public - 对所有类可见。使用对象:类、接口、变量、方法
- protected - 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
默认访问修饰符 - 无关键字
默认访问修饰符意味着我们没有为类、字段、方法等显式声明访问修饰符。
没有任何访问控制修饰符声明的变量或方法可用于同一包中的任何其他类。 接口中的字段隐式为 public static final,接口中的方法默认为 public。
class Person { String fname = "John"; String lname = "Doe"; String website = "jiyik.com"; int age = 24; public static void main(String[] args) { Person myObj = new Person(); System.out.println("Name: " + myObj.fname + " " + myObj.lname); System.out.println("Website: " + myObj.website); System.out.println("Age: " + myObj.age); } }
私有访问修饰符 - private
声明为私有的方法、变量和构造函数只能在声明的类本身内访问。
私有访问修饰符是最严格的访问级别。 类和接口不能是私有的。
如果类中存在公共 getter 方法,则可以在类外访问声明为私有的变量。
使用私有修饰符是对象封装自身并向外界隐藏数据的主要方式。
public class Main { private String fname = "John"; private String lname = "Doe"; private String website = "john@doe.com"; private int age = 24; public static void main(String[] args) { Main myObj = new Main(); System.out.println("Name: " + myObj.fname + " " + myObj.lname); System.out.println("Website: " + myObj.website); System.out.println("Age: " + myObj.age); } }
公共访问修饰符 - public
可以从任何其他类访问声明为 public 的类、方法、构造函数、接口等。 因此,可以从属于 Java Universe 的任何类访问在公共类中声明的字段、方法、块。
但是,如果我们尝试访问的公共类在不同的包中,那么仍然需要导入公共类。 由于类继承,类的所有公共方法和变量都由其子类继承。
class Web { public String fname = "John"; public String lname = "Doe"; public String website = "jiyik.com"; public int age = 24; } public class Main { public static void main(String[] args) { Web myObj = new Web(); System.out.println("Name: " + myObj.fname + " " + myObj.lname); System.out.println("Website: " + myObj.website); System.out.println("Age: " + myObj.age); } }
受保护的访问修饰符 - protected
在超类中声明为 protected 的变量、方法和构造函数只能由其他包中的子类或受保护成员类的包内的任何类访问。
protected 访问修饰符不能应用于类和接口。 方法、字段可以声明为protected,但是接口中的方法和字段不能声明为protected。
受保护的访问使子类有机会使用辅助方法或变量,同时防止不相关的类尝试使用它。
class Person { protected String fname = "John"; protected String lname = "Doe"; protected String website = "jiyik.com"; protected int age = 24; } class Main extends Person { public int graduationYear = 2018; public static void main(String[] args) { Main myObj = new Main(); System.out.println("Name: " + myObj.fname + " " + myObj.lname); System.out.println("Website: " + myObj.website); System.out.println("Age: " + myObj.age); System.out.println("Graduation Year: " + myObj.graduationYear); } }
访问控制和继承
强制执行以下继承方法的规则 -
- 在父类中声明为公共的方法在所有子类中也必须是公共的。
- 在父类中声明为protected 的方法在子类中必须是protected 或public; 他们不能是 private 的。
- 声明为私有的方法根本不会被继承,因此没有针对它们的规则。
非访问修饰符
Java 提供了许多非访问修饰符来实现许多其他功能。
- 用于创建类方法和变量的静态修饰符。
- 用于完成类、方法和变量的实现的 final 修饰符。
- 用于创建抽象类和方法的抽象修饰符。
- 用于线程的 synchronized 和 volatile 修饰符。
Static 修饰符
Static 变量
static 关键字用于创建独立于为类创建的任何实例而存在的变量。 无论类的实例数量如何,都只存在一个静态变量的副本。
静态变量也称为类变量。 局部变量不能声明为静态。
Static 方法
static 关键字用于创建独立于为类创建的任何实例的方法。
静态方法不使用定义它们的类的任何对象的任何实例变量。静态方法从参数中获取所有数据并从这些参数中计算出一些东西,不引用变量。
可以使用类名后跟一个点和变量或方法的名称来访问类变量和方法。
示例
静态方法和公共方法之间差异的示例:
public class Main { // Static 方法 static void myStaticMethod() { System.out.println("无需创建对象即可调用静态方法"); } // Public 方法 public void myPublicMethod() { System.out.println("公共方法必须通过创建对象来调用"); } // Main 方法 public static void main(String[ ] args) { myStaticMethod(); // 调用静态方法 // myPublicMethod(); This would output an error Main myObj = new Main(); // 创建一个 Main 对象 myObj.myPublicMethod(); // 调用 公共方法 } }
Final 修饰符
Final 变量
final 变量只能显式初始化一次。 声明为 final 的引用变量永远不能重新分配来引用不同的对象。
但是,对象内的数据可以更改。 因此,可以更改对象的状态,但不能更改引用。
对于变量,final 修饰符通常与 static 一起使用从而使常量成为类变量。
public class Test {
final int value = 10;
// 以下是声明常量的示例:
public static final int BOXWIDTH = 6;
static final String TITLE = "Manager";
public void changeValue() {
value = 12; // 将会产生错误
}
}
Final 方法
final 方法不能被任何子类覆盖。 如前所述,final 修饰符防止在子类中修改方法。
使用 final 方法的主要目的是方法的内容不应被任何外部人员更改。
Final 类
使用声明为 final 的类的主要目的是防止类被子类化。 如果一个类被标记为 final,那么任何类都不能从 final 类继承任何特性。
示例
public class Main { final int x = 10; final double PI = 3.14; public static void main(String[] args) { Main myObj = new Main(); myObj.x = 50; // 将会产生错误: cannot assign a value to a final variable myObj.PI = 25; // 将产生一个错误: cannot assign a value to a final variable System.out.println(myObj.x); } }
Abstract 修饰符
抽象类永远不能被实例化。 如果一个类被声明为抽象类,那么唯一的目的就是扩展这个类。
一个类不能既是抽象类又是 final 类(因为 final 类不能被扩展)。 如果一个类包含抽象方法,那么该类应该被声明为抽象的。 否则会抛出编译错误。
抽象类可以包含抽象方法和普通方法。
abstract class Caravan {
private double price;
private String model;
private String year;
public abstract void goFast(); // an abstract method
public abstract void changeColor();
}
抽象方法是一种声明的方法,没有任何实现。 方法体(实现)由子类提供。 抽象方法永远不能是 final 的或 strict 的。
任何扩展抽象类的类都必须实现父类的所有抽象方法,除非子类也是抽象类。
如果一个类包含一个或多个抽象方法,则该类必须声明为抽象的。 抽象类不需要包含抽象方法。
抽象方法以分号结尾。 示例:public abstract sample();
示例
// 抽象类 abstract class Second { public String fname = "John"; public int age = 24; public abstract void study(); // 抽象方法 } // 子类 class Student extends Second { public int graduationYear = 2018; public void study() { // 这里实现抽象方法 System.out.println("Studying all day long"); } } class Main { public static void main(String[] args) { // 创建一个 Student 类的对象 Student myObj = new Student(); System.out.println("Name: " + myObj.fname); System.out.println("Age: " + myObj.age); System.out.println("Graduation Year: " + myObj.graduationYear); myObj.study(); // 调用抽象方法 } }
Synchronized 修饰符
synchronized 关键字用于指示一个方法一次只能被一个线程访问。 synchronized 修饰符可以与四个访问级别修饰符中的任何一个一起应用。
public synchronized void showDetails() {
.......
}
Transient 修饰符
实例变量被标记为 Transient ,从而指示 JVM 在序列化包含它的对象时跳过特定变量。
此修饰符包含在创建变量的语句中,位于变量的类或数据类型之前。
public transient int limit = 55;
public int b;
Volatile 修饰符
volatile 修饰符用于让 JVM 知道访问变量的线程必须始终将其自己的变量私有副本与内存中的主副本合并。
访问 volatile 变量会同步主存储器中所有缓存的变量副本。 Volatile 只能应用于对象或私有类型的实例变量。 volatile 对象引用可以为空。
public class MyRunnable implements Runnable {
private volatile boolean active;
public void run() {
active = true;
while (active) {
// 代码
}
}
public void stop() {
active = false;
}
}
通常, run() 在一个线程(开始使用 Runnable 的线程)中调用,而 stop() 在另一个线程中调用。 如果在第 1 行中使用了 active 的缓存值,则在第 2 行中将 active 设置为 false 时循环可能不会停止。这就是我们想要使用 volatile 的时候。