前面提到了多种关键字,比如static、final,下面我们就来看看这些关键字到底有什么用?
1. static
在《Java编程思想》P86页有这样一段话: “static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。”
简而言之,static的作用就是方便在没有创建对象的情况下来调用变量或方法。
static可以用来修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。
使用场景
-
修饰成员变量和成员方法: 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被 static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。调用格式:
类名.静态变量名
类名.静态方法名()
static
变量也称作静态变量,静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化,且static
成员变量的初始化顺序按照定义的顺序进行初始化。 -
静态代码块: 静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次.
-
静态内部类(static 修饰类的话只能修饰内部类): 静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建。2. 它不能使用任何外围类的非 static 成员变量和方法。
-
静态导包(用来导入类中的静态资源,1.5 之后的新特性): 格式为:
import static
这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。
例题:
来看下面这个例子,先思考一下类加载的过程,以及静态变量的使用,然后考虑结果。
public class Singleton {
private static Singleton instance = new Singleton(); // 1: instance = null
public static int x = 0; // 2: x = 0 7: x = 0
public static int y; // 3: y = 0
public Singleton() { // 4:
x++; // 5: x = 1
y++; // 6: y = 1
}
public static Singleton getInstance() {
return instance;
}
public static void main(String[] args) {
Singleton instance = getInstance();
System.out.println(instance.x); // 0
System.out.println(instance.y); // 1
}
}
我们来推导一下上面语句的执行过程:
- 首先将类编译成字节码文件,并加载到JVM中
- 然后就是连接过程了,连接过程分为三步,
- 验证:确保class文件的字节流中包含的信息符合当前虚拟机的要求,比如文件格式、语句检查、字节码验证等
- 准备:此阶段为类变量(静态变量)分配内存,并设置默认值,一般分配在方法区(元数据区)
- 解析:将类中的符号引用转换为直接引用,指向方法或变量在方法区的内存位置。
- 前面都是准备阶段,现在是初始化阶段,包括正式执行代码,首先为静态变量赋值,然后执行静态代码块中的内容,具体过程为:父类静态变量->父类静态代码块->子类静态变量->子类静态代码块。
所以上面代码的执行过程是先为所有静态变量赋初始值,然后执行静态代码块中的内容,最后结果是0,1
如果是下面的代码呢?结果是1,1,请思考。
public class Singleton {
public static int x = 0;
public static int y;
private static Singleton instance = new Singleton();
public Singleton() {
x++;
y++;
}
public static Singleton getInstance() {
return instance;
}
public static void main(String[] args) {
Singleton instance = getInstance();
System.out.println(instance.x);
System.out.println(instance.y);
}
}
2. final
final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。
一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变量再次初始化的话,编译器会报编译错误。
final变量
final成员变量表示常量,只能被赋值一次,赋值后值不再改变(final要求地址值不能改变)。因此final修饰成员变量时,必须要显示初始化。
final方法
final修饰方法一般有两个原因,
- 一是为了锁定方法,避免在继承的时候被修改
- 二是提高效率,因为final方法在编译时已经静态绑定,不需要在运行时再动态绑定。
final类
当用final修饰一个类时,表明这个类不能被继承。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
3. this
this 关键字用于引用类的当前实例。 例如:
class Manager {
Employees[] employees;
void manageEmployees() {
int totalEmp = this.employees.length;
System.out.println("Total employees: " + totalEmp);
this.report();
}
void report() { }
}Copy to clipboardErrorCopied
在上面的示例中,this 关键字用于两个地方:
- this.employees.length:访问类 Manager 的当前实例的变量。
- this.report():调用类 Manager 的当前实例的方法。
此关键字是可选的,这意味着如果上面的示例在不使用此关键字的情况下表现相同。 但是,使用此关键字可能会使代码更易读或易懂。
4. super
super 关键字用于从子类访问父类的变量和方法。 例如:
public class Super {
protected int number;
protected showNumber() {
System.out.println("number = " + number);
}
}
public class Sub extends Super {
void bar() {
super.number = 10;
super.showNumber();
}
}Copy to clipboardErrorCopied
在上面的例子中,Sub 类访问父类成员变量 number 并调用其父类 Super 的 showNumber()
方法。
使用 this 和 super 要注意的问题:
- 在构造器中使用
super()
调用父类中的其他构造方法时,该语句必须处于构造器的首行,否则编译器会报错。另外,this 调用本类中的其他构造方法时,也要放在首行。 - this、super 不能用在 static 方法中。
简单解释一下:
被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享。而 this 代表对本类对象的引用,指向本类对象;而 super 代表对父类对象的引用,指向父类对象;所以, this 和 super 是属于对象范畴的东西,而静态方法是属于类范畴的东西。