导航
我们为什么需要这些非访问修饰符?
像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
- 访问控制修饰符 : default, public , protected, private(详细介绍见:《封装、继承、多态》,中的访问修饰符)
- 非访问控制修饰符 : static,final, abstract , synchronized,volatile
四个访问修饰符,解决了Java中类与类之间访问权限的设置问题。而非访问控制修饰符,各有各的妙用,且听我一一道来:
static修饰符
static 修饰符,可以用来修饰类方法和类变量。
static变量
- 修饰变量时,称变量为静态变量:
static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。
static方法
- 修饰方法时,称方法为静态方法:
static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
来个实例:
class Person {
public static int totalnum; // totalnum为静态变量变量,static修饰
private String name;
private int age;
Person(String name,int age) {
this.name = name ;
this.age = age;
Person.totalnum++;
}
public void die(){
totalnum--;
}
public static void destroy() {
totalnum = 0;
// age = 18; 会报错,static方法中不能使用非static变量
}
}
public class Test {
public static void main(String[] args){
Person person1 = new Person("小猪",18);
System.out.println("小猪出生后,人类总人数:"+person1.totalnum); // Person.totalnum结果与一样
Person person2 = new Person("小牛",20);
System.out.println("小牛出生后,人类总人数:"+person2.totalnum); // Person.totalnum结果与一样
person2.die();
System.out.println("小牛死亡后,人类总人数:"+Person.totalnum);
Person.destroy();
System.out.println("人类灭亡后,人类总人数:"+Person.totalnum);
}
}
-------------------------------------------------
输出结果为:
小猪出生后,人类总人数:1
小牛出生后,人类总人数:2
小牛死亡后,人类总人数:1
人类灭亡后,人类总人数:0
我用比较白话的方式谈谈自己理解:被static 修饰,就是指这些变量和方法是类本身的东西,与具体的实例化对象无关,我们在调用时应该直接从类中调用:类名.变量/方法。所以调用static方法时,static方法内是不能操作非static变量的,因为非static变量都是属于实例化后的对象的,调用static方法是从类的层面调用,并不具体到某一个对象。
final修饰符
final:最终的、最后的。
final修饰符,可以用来修饰方法、变量以及类。
final 变量
- final变量一旦赋值后,不能被重新赋值。并且被 final 修饰的实例变量必须显式指定初始值。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
class Person {
public static final int max_totalnum = 10;
public static int totalnum;
private String name;
private int age;
Person(String name,int age) {
this.name = name ;
this.age = age;
Person.totalnum++;
}
}
public class Test {
public static void main(String[] args){
// Person.max_totalnum = 20 ; 会报错,无法修改final变量的值
}
}
final 方法
- 父类中的 final 方法可以被子类继承,但是不能被子类重写。
声明 final 方法的主要目的是防止该方法的内容被修改。
class Person {
public static final int max_totalnum = 10;
public static int totalnum;
private String name;
private int age;
Person(String name,int age) {
this.name = name ;
this.age = age;
Person.totalnum++;
}
static final void destroy() {
Person.totalnum = 0;
}
}
class Woman extends Person {
Woman(String name, int age) {
super(name, age);
}
// void destroy(){ } 无法重写,会报错
}
final 类
- final 类不能被继承,没有类能够继承 final 类的任何特性。
final class Person {
private String name;
private int age;
Person(String name,int age) {
this.name = name ;
this.age = age;
}
}
class Woman extends Person { // 会报错,无法继承,因为Person为final类
}
abstract 修饰符
abstract修饰符用于抽象类与接口当中,关于抽象类与接口详细介绍见:《抽象类与接口》
抽象类
-
被abstract修饰的类为抽象类,抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
-
一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。
-
抽象类可以包含抽象方法和非抽象方法。
abstract class Person { //抽象类,注意不能同时被 abstract 和 final 修饰
public static int totalnum;
private String name;
private int age;
Person(String name,int age) {
this.name = name ;
this.age = age;
}
public abstract void die(); //抽象类中可以有抽象方法,有抽象方法一定要声明为抽象类
public static void destroy() { //抽象类中也可以有非抽象方法
totalnum = 0;
}
}
public class Test {
public static void main(String[] args){
new Person(); // 报错,抽象类无法实例化
}
}
抽象方法
-
抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。
-
抽象方法不能被声明成 final 和 static。
-
任何继承抽象类的非抽象子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
-
抽象方法的声明以分号结尾,如:public abstract void die(); 不能再加{}
abstract class Person {
public static int totalnum;
private String name;
private int age;
Person(String name,int age) {
this.name = name ;
this.age = age;
}
public abstract void die();
public static void destroy() {
totalnum = 0;
}
}
class Woman extends Person{ // Woman 为 Person 的 非抽象 子类
Woman(String name, int age) {
super(name, age);
}
@Override // 非抽象子类,必须实现抽象父类的所有抽象方法,也是一种重写
public void die() {
// TODO Auto-generated method stub
Person.totalnum--;
}
}
synchronized,volatile与transient
synchronized 修饰符
- synchronized 关键字声明的方法同一时间只能被一个线程访问。synchronized 修饰符可以应用于四个访问修饰符。
volatile 修饰符
-
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
-
一个 volatile 对象引用可能是 null。
public class MyRunnable implements Runnable
{
private volatile boolean active;
public void run()
{
active = true;
while (active) // 第一行
{
// 代码
}
}
public void stop()
{
active = false; // 第二行
}
}
通常情况下,在一个线程调用 run() 方法(在 Runnable 开启的线程),在另一个线程调用 stop() 方法。 如果 第一行 中缓冲区的 active 值被使用,那么在 第二行 的 active 值为 false 时循环不会停止。
但是以上代码中我们使用了 volatile 修饰 active,所以该循环会停止。
transient 修饰符
-
序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
-
该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
public transient int limit = 55; // 不会持久化
public int b; // 持久化