Java基础语法:static修饰符
一、静态变量
描述:
- 在类中,使用'static'修饰的成员变量,就是静态变量,反之为非静态变量。
区别:
- 静态变量属于类的,可以使用类名来访问;非静态变量是属于对象的,必须使用对象来访问。
- 静态变量对于类而言在内存中只有一个,能被类的所有实例所共享;实例变量对于类的每个实例都有一份,它们之间互不影响。
- 静态变量在加载类的过程中分配内存,实例变量在创建对象时分配内存,所以静态变量可以使用类名来直接访问,而不需要使用对象来访问。
示例:
public class Student {
private static int age = 20;
private double score = 100;
public static void main(String[] args) {
Student student = new Student();
System.out.println(age);//20
System.out.println(Student.age);//20
/* 不建议使用对象访问静态成员 */
System.out.println(student.age);//20
System.out.println(student.score);//100.0
}
}
二、静态方法
描述:
- 在类中,使用'static'修饰的成员方法,就是静态方法,反之为非静态方法。
区别:
- 静态方法数属于类的,可以使用类名来调用;非静态方法是属于对象的,必须使用对象来调用。
- 静态方法不可以直接访问类中的非静态变量和非静态方法,但是可以直接访问类中的静态变量和静态方法;非静态方法可以直接访问类中的非静态变量和非静态方法,也可以直接访问类中的静态变量和静态方法。
注意:
- 'this'和'super'在类中属于非静态的变量,静态方法中不能使用。
- 父类的静态方法可以被子类继承,但是不能被子类重写。
- 父类的非静态方法不能被子类重写为静态方法。
示例:
public class Student {
private static int age = 20;
private double score = 100;
public static void staticSay() {
System.out.println(age);
}
public void say() {
System.out.println(age + " " + score);
}
public static void main(String[] args) {
Student.staticSay();//20
Student student = new Student();
/* 不建议使用对象调用静态方法 */
student.staticSay();//20
student.say();//20 100.0
}
}
三、静态代码块
描述:
- 在类中,使用'static'修饰的匿名代码块,就是静态代码块。
- 因为没有名字,在程序中并不能主动调用这些匿名代码块。
区别:
- 静态代码块是在类加载完成之后就自动执行,并且只执行一次;匿名代码块是在每次创建对象的时候自动执行的,并且在构造器执行之前执行。
- 静态代码块不可以直接访问类中的非静态变量和非静态方法,但是可以直接访问类中的静态变量和静态方法;匿名代码块可以直接访问类中的非静态变量和非静态方法,也可以直接访问类中的静态变量和静态方法。
- 静态代码块的作用是给类中的静态成员变量初始化赋值;匿名代码块的作用是给对象的成员变量初始化赋值,但是因为构造器也能完成这项工作,所以匿名代码块使用的并不多。
注意:
- 'this'和'super'在类中属于非静态的变量,静态代码块中不能使用。
- 在构造器中给静态变量赋值,并不能保证能赋值成功,因为构造器是在创建对象的时候才执行,但是静态变量可以不创建对象而直接使用类名来访问。
示例:
public class Student {
/* 静态代码块 */
static {
staticSay();
age = 10;
staticSay();
}
/* 匿名代码块 */
{
say();
age = 30;
score = 80.0;
say();
}
/* 构造器 */
Student() {
say();
age = 40;
score = 90.0;
say();
}
/* 静态成员变量赋初始值,定义在静态代码块之下,会比静态代码块后执行 */
private static int age = 20;
/* 非静态成员变量赋初始值,定义在匿名代码块之下,会比匿名代码块后执行 */
private double score = 100.0;
/* 静态方法 */
public static void staticSay() {
System.out.println(age);
}
/* 非静态方法 */
public void say() {
System.out.println(age + " " + score);
}
public static void main(String[] args) {
/*
本次执行顺序为:静态代码块-->静态成员变量赋初始值-->匿名代码块-->非静态成员变量赋初始值-->构造器
所以创建对象会在控制台依次打印:
0
10
20 0.0
30 80.0
30 100.0
40 90.0
*/
Student student = new Student();
student.say();//40 90.0
}
}
四、创建和初始化对象的过程
步骤:
- 类加载,同时初始化类中静态变量,赋默认值。
- 若静态变量有指定初始值,且代码定义在静态代码块之上,对静态变量进行显式赋值。
- 执行静态代码块。
- 若静态变量有指定初始值,且代码定义在静态代码块之下,对静态变量进行显式赋值。
- 创建对象,分配内存空间,同时初始化成员变量,赋默认值。
- 调用父类构造器。
- 若成员变量有指定初始值,且代码定义在匿名代码块之上,对成员变量进行显式赋值。
- 执行匿名代码块。
- 若成员变量有指定初始值,且代码定义在匿名代码块之下,对成员变量进行显式赋值。
- 执行构造器。
- 若有变量引用,返回内存地址赋给变量。
示例:
public class Person {
private int age = 10;
public Person() {
System.out.println("Person构造器");
print();
}
public void print() {
System.out.println("Person print方法: age = " + age);
}
}
class Student extends Person {
/* 赋初始值定义在匿名代码块之上,会比匿名代码块先执行 */
private int age = 20;
static {
System.out.println("Student静态代码块");
}
{
System.out.println(age);
System.out.println("Student匿名代码块");
}
public Student() {
System.out.println(age);
System.out.println("Student构造器");
}
public void print() {
System.out.println("student print方法: age = " + age);
}
public static void main(String[] args) {
/*
结果为在控制台依次打印:
Student静态代码块
Person构造器
student print方法: age = 0
20
Student匿名代码块
20
Student构造器
*/
new Student();
}
}
五、静态导包
描述:
- 静态导包就是java包的静态导入,用'import static'代替'import'来静态导入包,是JDK1.5中的新特性,意思是导入这个类里的静态方法。
- 静态导包的好处就是在有很多重复调用静态方法的时候可以简化一些操作,如果仅有一到两次调用,不如直接写来的方便。
示例:
import static java.lang.Math.PI;
import static java.lang.Math.random;
public class Test {
public static void main(String[] args) {
/* 非静态导入需要使用Math.random()的方式调用 */
System.out.println(random());//0.05775943381246629
System.out.println(PI);//3.141592653589793
}
}