1.static关键字的介绍
static是一个成员修饰符.其作用:
- 1.静态修饰 类的属性 和 类的方法
static String name; // 修饰类的属性
static void test1(){...} // 修饰类的方法
- 2.在类中构成代码块
static{...}
- 3.构成静态内部类
static class xxx{}
- 4.静态导包
import static java.lang.Integer.*;
如需具体了解static的用法,请先看别的博客吧!我还没写
2.子类父类的加载初始化顺序
在首次使用类(子类)时
1.查看该子类的
父类信息
是否加载在内存方法区
如果没有加载到方法区,则先加载父类信息
2.先加载父类的静态属性和静态代码块(静态属性和静态代码块的加载顺序由在类中的前后位置决定.)
3.父类的静态属性和代码块加载完,再加载子类的静态属性和静态代码块。
4.然后加载 父类的 成员变量(非static修饰的属性)之后 执行父类的构造方法
5.再加载子类的 成员变量 之后 执行子类的构造方法
顺序就是 :
父类静态变量/父类静态代码块
——>> 子类静态变量/子类静态代码块
——>> 父类成员变量
——>> 父类构造方法
——>> 子类成员变量
——>> 子类构造方法
代码为先:
public class Person {
//静态变量(类变量)
public static String name = print("父类的name静态变量初始化");
//成员变量
public String age = print("父类的age成员变量初始化");
静态代码块
static{
System.out.println("父类的静态代码块");
}
//构造方法
public Person(){
System.out.println("父类构造器");
}
//输出显示用的
public static String print(String aaa){
System.out.println(aaa);
return aaa;
}
}
public class Student extends Person {
static{
System.out.println("子类的静态代码块");
}
public static String name = print("子类的name静态变量初始化");
public String age = print("子类的age成员变量初始化");
public Student(){
System.out.println("子类对象构造器");
}
}
public static void main(String[] args) {
Student student = new Student();
}
//输出结果
父类的name静态变量初始化
父类的静态代码块
子类的静态代码块
子类的name静态变量初始化
父类的age成员变量初始化
父类构造器
子类的age成员变量初始化
子类对象构造器
如果你对加载顺序了解,那你很棒。但以上不是本博客重点
3.重点(加载顺序可能错乱吗?答案:可能)
错误点: 在静态代码块中初始化本类
先看一个单类(没有父类)
public class Person {
public String age = print("父类的age成员变量初始化");
static{
System.out.println("父类的静态代码块");
Person p1 = new Person();
System.out.println("父类的静态代码块---------"+Person.name);
}
public static String name = print("父类的name静态变量初始化");
public Person(){
System.out.println("父类构造器");
}
public static String print(String aaa){
System.out.println(aaa);
return aaa;
}
}
public static void main(String[] args) {
Person p2 = new Person();
System.out.println(Person.name);
}
//输出
①父类的静态代码块
②父类的age成员变量初始化
③父类构造器
④父类的静态代码块---------null
⑤父类的name静态变量初始化
⑥父类的age成员变量初始化
⑦父类构造器
⑧父类的name静态变量初始化 //这行输出的是name值(不要当作在这里又初始化了)
我们会发现父类的成员变量和构造器先于静态变量初始化了。
反编译后的部分person.class代码:
由上面我们可以知道,在类信息加载的时候,是先定义后赋值的。而我们实例化类的时候,需要(获得成员变量信息,构造器信息)对成员变量信息进行初始化及本对象进行实例化(不用管类的静态变量是否完成赋值)。
现在我们对上面代码一步一步进行梳理:
1.在主方法中 发现 new Person();所以去寻找类信息,但是类还没有加载过,所以加载person类信息
2.加载Person类信息,把Person类中的信息先定义(未赋值)。然后从上向下寻找static修饰符修饰的变量或者代码块(谁在前,先执行谁,如果是静态变量name在前,则会执行对name的赋值操作)。发现代码块
static{ System.out.println("父类的静态代码块"); Person p1 = new Person(); System.out.println("父类的静态代码块---------"+Person.name); }
然后执行代码块
3.执行代码块中的代码,输出第一行信息①。然后发现 Person p1 = new Person(); 再去寻找Person类的信息。这时发现Person类信息(未赋值)已经存在。则对p1对象进行实例化。
4.实例化时不考虑对象的类的静态变量,只对成员变量进行实例化 ,输出第二行信息②. 然后执行构造方法内的代码,输出第三行信息③。
5.这时完成对p1的实例化,执行代码
System.out.println("父类的静态代码块---------"+Person.name);
,但是这是的Person类的静态变量name只是开辟空间还没有赋值。所以输出④6.这是静态代码块执行完成所以继续向下寻找static修饰的变量和代码块,发现:类静态变量name ,对静态变量那么进行赋值,所以执行输出⑤。
7.这时相对于main方法,第2~6步,算是完成对Person类信息的加载。开始进行对p2的实例化。先对成员变量实例化,输出⑥。在执行构造方法,输出⑦。
8.在执行main方法中的
System.out.println(Person.name);
输出⑧(Person.name已在第6步完成赋值)。
还可以吗? 试试下面的一段代码。(提示:即使有父类,也要明白。类信息加载不等于类变量完成赋值。类信息加载后(未赋值)也可以拿去对 对象进行实例化)
4.试试代码
public class Person {
public String age = print("父类的age成员变量初始化");
static{
System.out.println("父类的静态代码块");
Student student = new Student();
System.out.println("父类的静态代码块Stadent的name---------"+Student.name);
}
public static String name = print("父类的name静态变量初始化");
public Person(){
System.out.println("父类构造器");
}
public static String print(String aaa){
System.out.println(aaa);
return aaa;
}
}
public class Student extends Person {
static{
System.out.println("子类的静态代码块");
}
public static String name = print("子类的name静态变量初始化");
public String age = print("子类的age成员变量初始化");
public Student(){
System.out.println("子类对象构造器");
}
}
public static void main(String[] args) {
Student student = new Student();
}
//输出
父类的静态代码块
父类的age成员变量初始化
父类构造器
子类的age成员变量初始化
子类对象构造器
父类的静态代码块Stadent的name---------null
父类的name静态变量初始化
子类的静态代码块
子类的name静态变量初始化
父类的age成员变量初始化
父类构造器
子类的age成员变量初始化
子类对象构造器
有兴趣慢慢的分析一下,(工作中,没人这样写。因为这样写的人都被辞退了)