目录
复习知识点
1,变量"先声明,再使用";
2,staic方法和代码块,不能访问普通的成员变量和普通方法
3,子类构造方法如果没有显示调用父类的构造方法,就会默认调用父类无参的构造方法;
4,static代码块只会被执行一次,普通代码块,每当创建对象,都会被执行
先看简单的,然后逐步深入。
一、 先说结论, 只对一个类而言,初始化先后顺序:
(静态变量、静态代码块):按照声明的先后顺序,进行初始化。
(普通变量、普通代码块):按照声明的先后顺序,进行初始化。
(静态变量、静态初始化块)----->(普通变量、普通初始化块)-----> 构造器
-
(静态变量、静态代码块):按照声明的先后顺序,进行初始化。
public class Test {
public void main(String[] args){
new A();
}
}
class A{
//静态变量
static int s_i = 888;
static{ //注意这里用到了静态变量s_i
System.out.println("静态代码块, s_i = " + s_i);
}
}
结果正常输出
静态代码块, s_i = 888
2.(普通变量、普通代码块):按照声明的先后顺序,进行初始化。
同上1.2.1,把静态变量、静态代码块的“static"修饰符去掉,就可以验证,不多赘述。
3. ⭐⭐⭐(静态变量、静态初始化块)----->(普通变量、普通初始化块)-----> 构造器
3.1 (静态变量、静态初始化块)----->(普通变量、普通初始化块)
public class Test {
public static void main(String[] args){
new A();
}
}
class A{
//普通变量i
int i = 0;
static{
System.out.println("静态代码块, i = " + i);
}
}
编译时就会报错。
static方法和static代码块,不能访问普通的成员变量和普通方法
因为static方法和static代码块,会先进行初始化,
普通成员变量和普通代码块,后进行初始化。
3.2. 小杂烩
public class Test {
public static void main(String[] args){
new A();
}
}
class A{
A(){
System.out.println("A的构造方法" );
}
//普通变量i
B b = new B();
//静态变量
static B s_b = new B(1);
//普通代码块
{
System.out.println("普通代码块" );
}
//静态代码块
static{
System.out.println("静态代码块" );
}
}
class B{
B(){
System.out.println("B()无参构造方法");
}
B(int i){
System.out.println("B(int i)有参构造方法");
}
}
结果
创建静态变量s_b,调用B(int i)有参构造方法
静态代码块
创建普通变量b,调用B()无参构造方法
普通代码块
A的构造方法
可以看到,A构造方法即使放在最前面,也依旧要垫底输出
说明了初始化顺序:(静态变量、静态初始化块)----->(普通变量、普通初始化块)-----> 构造器
二,有继承的情况下⭐⭐⭐⭐⭐
先执行(静态变量、静态初始化块),从最顶层父类到子类,再到当前类,依次执行。
【同时注意,(静态变量、静态初始化块) 只会执行一次!!后面无论创建多少次对象,都不会再执行】
再执行(普通变量、普通初始化块)-----> 构造器,从最顶层父类到子类,再到当前类,依次执行。
【同时注意,这里的构造器,是被子类调用的构造器(因为父类的构造器可以有多个呀)。】
如上图,总体上依旧是先执行static静态的,然后再执行普通的。具体过程:
会先执行类A的(静态变量、静态初始化块);再执行B的(静态变量、静态初始化块);再执行。。。。;最后执行N的(静态变量、静态初始化块),最终执行N的main()方法(如果有的话)。
如果N中有main()方法,那么再执行完main()方法,就结束了。
如果没有main()方法,或者main()方法里new 了N的对象。
那就会再从类A开始,执行类A的(普通变量、普通初始化块),然后执行A的被调用的构造方法;再执行B的(普通变量、普通初始化块),然后执行B的被调用的构造方法;.............;
最后执行N的(普通变量、普通初始化块),然后执行N的构造方法
上代码,先看简单的
package com.lei.java.test;
class A{
//普通代码块
{
System.out.println("A的普通代码块" );
}
//静态代码块
static{
System.out.println("A的static代码块" );
}
A(){
System.out.println("A()无参构造" );
}
}
class B extends A{
//普通代码块
{
System.out.println("B的普通代码块" );
}
//静态代码块
static{
System.out.println("B的static代码块" );
}
B(){
System.out.println("B()无参构造" );
}
}
public class C extends B{
//普通代码块
{
System.out.println("C的普通代码块" );
}
//静态代码块
static{
System.out.println("C的static代码块" );
}
C(){
System.out.println("C()无参构造" );
}
//C的main方法
public static void main(String[] args){
System.out.println("C的main()方法");
}
}
结果
A的static代码块
B的static代码块
C的static代码块
C的main()方法
是不是很整齐,安排的明明白白。如我们所预料的那样,先是静态代码块,从父类到子类,被依次执行了。
不过,为什么普通代码块没有被执行呢?,因为没有创建对象。
实例对象里有它的普通成员变量,普通代码块会在实例对象创建的时候,初始化普通成员变量。实例对象创建的时候,JVM会给它分配堆的内存空间,有空间才能存东西呀。这里可以不必深究,继续。
重头戏,在C的main方法里,加一行代码,new C(),创建C对象。
//C的main方法
public static void main(String[] args){
System.out.println("C的main()方法");
new C();//加上这一行,创建C的实例对象
}
结果
A的static代码块
B的static代码块
C的static代码块
C的main()方法
A的普通代码块
A()无参构造
B的普通代码块
B()无参构造
C的普通代码块
C()无参构造
我们之前的初始化顺序,得到了最终的验证。
(静态变量、静态初始化块)----->(普通变量、普通初始化块)-----> 构造器
(静态变量、静态初始化块)(先父后子,最后main)----->【(普通变量、普通初始化块)-----> 构造器】(先父后子)
继续验证(静态变量、静态初始化块) 只会执行一次!!
public class Test {
public static void main(String[] args){
new A();
new A();
}
}
class A{
//普通代码块
{
System.out.println("A的普通代码块" );
}
//静态代码块
static{
System.out.println("A的static代码块" );
}
A(){
System.out.println("A()无参构造" );
}
}
结果
A的static代码块
A的普通代码块
A()无参构造
A的普通代码块
A()无参构造
A的static代码块 只被执行了一次。
再复杂一些,
class A{
//普通代码块
{
System.out.println("A的普通代码块" );
}
//静态代码块
static{
System.out.println("A的static代码块" );
}
A(){
System.out.println("A()无参构造" );
}
}
public class B extends A{
//普通变量
A a = new A();
B(){
System.out.println("B()无参构造");
}
public static void main(String[] args){
System.out.println("B的main方法========");
new B();
}
}
结果,及逐行分析
A的static代码块 加载A,执行static代码块
B的main方法======== B没有其它static成员,于是执行main方法
A的普通代码块 mian方法中new B(),调用B的无参构造,而它默认调用父类无参构造,于是回到A,由于A已经被加载过,A的static代码块或变量只会被执行一次,所以不执行了。于是执行A的普通代码块。
A()无参构造 执行A的构造方法。至此,所有static内容执行完毕,下面开始执行普通内容。
A的普通代码块 回到A,执行普通代码块
A()无参构造 执行A的无参构造,A结束
B()无参构造 执行B的无参构造,new B()结束
千万注意,调用父类的构造方法,不是意味着,它只执行父类的构造方法中的内容,而是会进行成员变量初始化,运行代码块,执行构造方法中的内容等等。
大招,留给各位练习。
class A{
//普通代码块
{
System.out.println("A的普通代码块" );
}
//静态代码块
static{
System.out.println("A的static代码块" );
}
A(){
System.out.println("A()无参构造" );
}
A(int ai){
System.out.println("A(int i)有参构造, ai = " + ai);
}
}
public class B extends A{
//普通代码块
{
System.out.println("B的普通代码块" );
}
//静态代码块
static{
System.out.println("B的static代码块" );
}
B(){
System.out.println("B()无参构造" );
}
//普通变量
A a = new A();
//静态变量
static A s_a = new A(888);
B(int bi){
//因为B的无参构造,默认调用父类的无参构造,
//为了效果,我们这里就让B的有参构造,调用父类的有参构造
super(bi);
System.out.println("B(int i)有参构造,bi = " + bi );
}
public static void main(String[] args){
System.out.println("B的main方法==================================");
new B();
new B(888);
}
}
结果,
A的static代码块
B的static代码块
A的普通代码块
A(int i)有参构造, ai = 888
B的main方法==================================
A的普通代码块
A()无参构造
B的普通代码块
A的普通代码块
A()无参构造
B()无参构造
A的普通代码块
A(int i)有参构造, ai = 888
B的普通代码块
A的普通代码块
A()无参构造
B(int i)有参构造,bi = 888
三、参考博文
其中原理,是jvm的内容,推荐博文