static关键字的使用
引入
一、静态变量
-
属性:按是否使用static修饰,分为静态属性 vs 非静态属性
① 非静态属性 —— 成员变量 / 实例变量:
我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态变量。当修改其中一个对象中的实例变量(非静态变量)时,不会导致其他对象中的同样的属性值的修改
② 静态属性 —— 类变量:
我们创建了类的多个对象,多个对象共享一个静态变量。当通过某个对象修改静态变量时,会导致其他对象调用此静态变量时,也是修改过的。
-
static修饰属性的加载:
① 静态变量随着类的加载而加载,可以通过“ 类 . 静态变量”的方式进行调用
② 静态变量的加载要早于对象的创建
③ 由于类只会加载一次,故静态变量再内存中也只会存在一份,存放在方法区的静态域中 -
调用范围
类变量(静态属性) 成员变量(非静态属性) 类 Yes 对象 Yes
-
静态属性内存调用图
二、静态方法
-
随着类的加载而加载,可以通过“ 类.方法 ”的方式进行调用
-
调用范围
静态方法 非静态方法 类 Yes 对象 Yes -
static注意点
① 在静态方法中,不能使用this关键字、super关键字
this代表当前对象,super代表调用父类构造器,和静态不是一个生命周期,当静态方法随类的加载而加载时,对象还没有被加载。
② 关于静态属性 / 静态方法的使用,可以从生命周期的角度去理解
三、使用static
-
一个属性声明为static,需满足:
① 该属性可以被多个对象所共享
② 该属性会随着对象的调用而不断变值 -
一个方法声明为static,需满足:
① 需要在方法内操作静态属性
② 工具类中的方法,习惯上声明为static -
静态属性不能出现在构造器中,构造器是用于创建对象的,和静态属性的生命周期不同。
-
main()方法
① main()方法作为程序的入口
② main()方法也是一个普通的静态方法
③ main()方法可以作为我们的控制台交互的方式(之前,使用Scanner)。
四、代码块(初始化块)
-
作用:
用来初始化类
-
分类:
静态代码块
① 内部可以有输出语句
② 随着类的加载而执行,而且只执行一次
③ 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
④ 静态代码块的执行,优先于非静态代码块的执行
⑤ 静态代码块内只能调用静态的属性和静态的方法,不能调用非静态结构非静态代码块
① 内部可以有输出语句
② 随着对象的创建而执行
③ 每创建一个对象,就执行一次非静态代码块
④ 既可以调用静态结构,也可以调用非静态结构
五、静态代码块、非静态代码块及构造器的执行顺序
-
第1步:类加载
① main()方法作为程序的主进口,当main方法运行时,不会先执行main方法中的代码,而是首先进行类的加载,包括本类与对应父类的加载。
② 类加载时,静态代码块、静态属性、静态方法都会被同时加载
③ 加载时,静态代码块会同时执行,先执行父类的静态代码块,再执行子类(本类)的静态代码块
④ 加载过后,可以在main方法中不需要实例化对象,就可以马上直接调用类中的静态属性和静态方法 -
第2步:执行main方法中的第一行代码
-
第3步:类实例化为对象
① 实例化为对象后,类中的非静态代码块、构造器、非静态属性、非静态方法被加载
② 实例化后首先执行父类的非静态代码块,父类的空参构造器
③ 再执行子类(本类)的非静态代码块,子类(本类)的构造器 (根据调用情况决定是空参还是实参) -
测试代码
class Father{ static String name = "父类静态属性"; public int age = 10; static { System.out.println("父类静态代码块"); } { System.out.println("父类非静态代码块"); } public Father(){ System.out.println("父类空参构造器"); } public Father(int age){ System.out.println("父类带参构造器"); } } class Son extends Father{ char sex = 'M'; static { System.out.println("子类静态代码块"); } { System.out.println("子类非静态代码块"); } public Son(){ System.out.println("子类空参构造器"); } public Son(char sex){ System.out.println("子类带参构造器"); } public static void main(String[] args) { System.out.println("main方法首行代码"); // 第1次实例化对象 Son s1 = new Son('m'); System.out.println("首次实例化本类对象后的第一条代码"); // 第2次实例化本类对象 Son s2 = new Son(); System.out.println("第二次实例化本类对象后的第一条代码"); // 实例化父类对象 Father f = new Father(); System.out.println("实例化父类后的第一条代码"); } }
-
执行顺序
-
内存调用
六、静态类
-
package com.oop.StaticType; //静态导入包 import static java.lang.Math.random; import static java.lang.Math.PI; public class StaticImportPackage { public static void main(String[] args) { // Math是java中long包下的一个工具类 System.out.println(Math.random()); System.out.println(random()); System.out.println(PI); } }