秦疆的JavaSE课程笔记:73 面向对象 static关键字详解
- 先看变量
public class Student {
private static int age; //静态变量
private double score; //非静态变量
public static void main(String[] args) {
System.out.println(Student.age);
System.out.println(Student.score); //报错“无法从static上下文引用非static字段”
Student s1 = new Student();
System.out.println(s1.age); //IDEA提示,“通过实例引用访问static成员OOP.demo.Student.age。可改成:System.out.println(age);
System.out.println(s1.score);
}
}
-
推荐使用类名访问静态变量,比如这里就是用
Student.age
访问静态变量age
。 -
但在静态方法中使用类名访问非静态变量就会出错,比如这里
main
方法是静态的,访问非静态变量score
就报错了。 -
我的看法:如果分析内存的话,类静态成员是在和类一起加载的,这时非静态变量应该是尚未加载,所以无法调用。而通过
Student s1 = new Student();
语句实例化对象之后,非静态变量才完成加载,这时才可以调用。 -
将类型实例化之后,通过对象调用对象属性就没有任何限制,比如这里的
s1.age
和s1.score
。 -
s1.age
那个提示,实际是在说走了弯路,因为静态成员可以直接调用,没必要再通过对象调用。当然这里只是作为示例。 -
再看看方法
public class Student {
public void run() {} //非静态方法
public static void go() {} //静态方法
public static void main(String[] args) {
go();
run(); //报错,无法从static上下文引用非static方法
Student s2 = new Student();
s2.run();
s2.go(); //IDEA提示:通过实例访问static成员
}
}
-
报错和提示的原因都是和上面一样的。
-
代码块和静态代码块
public class Demo {
//代码块(匿名代码块)
//程序在运行时并不能主动调用这些匿名代码块
//在构造器之前执行
{
}
//加上static关键词,就是静态代码块了
//类加载时,就会执行且只执行一次。
static {
}
}
- 演示如下:
public class Person {
//匿名代码块
{
System.out.println("匿名代码块");
}
//静态代码块
static {
System.out.println("静态代码块");
}
//构造方法
public Person() {
System.out.println("构造方法");
}
//主程序方法
public static void main(String[] args) {
Person person1 = new Person();
System.out.println("***********");
Person person2 = new Person();
}
}
====运行结果如下====
静态代码块
匿名代码块
构造方法
***********
匿名代码块
构造方法
-
静态代码块和类一起加载,所以最早执行。
-
第二个时匿名代码块,会在构造方法之前执行。
-
构造方法执行最晚,是在主程序中创建实例对象
person
时才执行的。 -
之前说过,静态代码块仅在类加载时执行一次,和是否调用构造方法无关。
-
但是匿名代码块似乎每次创建对象时,就会先执行匿名代码块,再执行构造方法。
-
静态导入包
//import java.lang.Math; 一般导入的写法
//import java.lang.Math.random; 若直接导入一个类会报错
import static java.lang.Math.random; //但是可以加关键词static导入,这就是静态导入包
import static java.lang.Math.PI; //查看源码可知,PI是一个由final修饰的double类型常量
public class Test {
public static void main(String[] args) {
//一般情况下,调用Math类中的方法和变量
System.out.println(Math.random());
System.out.println(Math.PI);
//静态导入包后,调用Math类的方法和变量
System.out.println(random());
System.out.println(PI);
}
}
- 秦疆老师提到一个话题之外的小细节,用
final
修饰的类,是无法被继承的。