Java基础之静态变量与成员变量
我们这里不去讨论诸如静态变量只在初始化一次,多个实例访问同一个静态变量,或者是静态方法只能访问静态方法这样的内容。我们着重讨论的是静态与非静态在类加载与实例化的过程中他们的初始化顺序问题。
一
对于类的实例化要进行类的加载,在进行类的实例化真正创建实例
二
静态代码块>main方法>构造代码块>构造方法。这个的意思就是如果你调用main方法,无论你调不调用这个main方法所在类的任何内容,静态代码块的内容一定会先加载
public class Demo2 {
public static int numb;
static {
System.out.println("我是普通静态块");
numb = 100;
}
{
System.out.println("我是普通代码块");
}
public Demo2(){
System.out.println("我被初始化了");
}
public static void main(String[] args) {
}
}
输出:
我是普通静态块
三
调用static变量时,类本身也会被实例化,如果这个类是编译器编译器常量即final staitc,则调用这个变量,类不会初始化,除非把这个final static初始化成random.nextInt()
class Demo5{
public static final int num1 = 5;
public static int num = 10;
static {
System.out.println("这是Demo5静态块");
}
}
class Demo6{
public static final int num1 = 5;
public static int num = 10;
static {
System.out.println("这是Demo6静态块");
}
}
class Demo7{
public static final int num1 = new Random().nextInt(600);
public static int num = 10;
static {
System.out.println("这是Demo7静态块");
}
}
public class Demo4 {
public static void main(String[] args) {
System.out.println(Demo5.num1);
System.out.println("-------------华丽丽的分割线--------------");
System.out.println(Demo6.num);
System.out.println("-------------华丽丽的分割线--------------");
System.out.println(Demo7.num1);
}
}
输出:
5
-------------华丽丽的分割线--------------
这是Demo6静态块
10
-------------华丽丽的分割线--------------
这是Demo7静态块
189
四
对于静态代码块和静态变量的初始化顺序,没有先后顺序,谁在前面谁先执行
五
成员变量赋值先于构造方法,而成员变量与构造代码块,谁在上面谁先执行,并且构造方法执行几次,构造块执行几次
class Demon{
int i=print();
{
System.out.println("构造块");
}
public int print(){
System.out.println("我是成员变量");
return 15;
}
Demon(){
System.out.println("我是构造方法");
}
}
public class Demo3 {
public static void main(String[] args) {
Demon demon = new Demon();
}
}
输出:
我是成员变量
构造块
我是构造方法
六
对于那些直接初始化的变量,他们并不是直接初始化的,而是先要赋予一个默认的值,之后再进行初始化:即如果是基本数据类型默认值是0,类变量初始值是null。我们可以从一个简单的例子可以看出。同时从上面的总结看就是
1. 我们调用Singleton.count1
和Singleton.count2
2. 前面总结调用非常量的static,对象会被实例化,所以内部的成员要被调用。
1. 因为ourInstance排在第一,ourInstance默认初始化为null,再显示式初始化,此时count1和count2都是默认初始化为0;
2. 调用构造方法outInstance的构造方法,由于此时count1和count2都是0;++后count1为1,count2为2调用println语句
3. 此时ourInstance执行完毕,调用public static int count1;
和public static int count2 = 1;
count1没有进行赋值,count1还是1,count2重新赋值又变成了1
public class Singleton {
private static Singleton ourInstance = new Singleton();
public static int count1;
public static int count2 = 1;
public static Singleton getInstance() {
return ourInstance;
}
private Singleton() {
System.out.println("我被初始化了");
count1++;
count2++;
count2++;
System.out.println("count2: "+count2);
}
public static void main(String[] args) {
System.out.println("count1 : "+Singleton.count1);
System.out.println("count2 : "+Singleton.count2);
}
}
/**
*console:
*我被初始化了
*count2:2
*count1 : 1
*count2 : 1
*/
我们再增加一个测试
public class Singleton {
private static Singleton ourInstance = new Singleton();
public static int count1;
public static int count2 = 1;
public static Singleton getInstance() {
return ourInstance;
}
{
System.out.println("构造块");
count1++;
count1++;
count1++;
System.out.println("构造块:count1:"+count1);
}
private Singleton() {
System.out.println("我被初始化了");
count1++;
count2++;
count2++;
System.out.println("构造方法:count2: "+count2);
}
public static void main(String[] args) {
System.out.println("count2 : "+Singleton.count2);
System.out.println("count1 : "+Singleton.count1);
}
}
输出
构造块
构造块:count1:3
我被初始化了
构造方法:count2: 2
count2 : 1
count1 : 4
N
a:静态变量的 默认初始化
b:静态变量的显示初始化
c:成员变量的默认初始化
d:成员变量的显示初始化
a->b->c->d
a,b在类的装载阶段完成,c,d在实例化阶段完成
public class T implements Cloneable{
public static int k = 0;
public static T t1 = new T("t1");//3
public static T t2 = new T("t2");//6
public static int i = print("i");//7
public static int n = 99;
public int j = print("j");//1 4 9
{
print("构造快");//2 5 10
}
static {
print("静态块");//8
}
public T(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n; ++ i;
}
public static int print(String str){
System.out.println((++k) +":" + str + " i=" + i + " n=" + n);
++n;
return ++ i;
}
public static void main(String[] args){
T t = new T("init");//11
}
}
输出
1:j i=0 n=0
2:构造快 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造快 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99
9:j i=8 n=100
10:构造快 i=9 n=101
11:init i=10 n=102‘
解析
- 静态变量全部默认初始化
public static int k = 0;
显示初始化(由于在这里k本身就是0,并不明显,等会我们可以将k变成-5测试);public static T t1 = new T("t1");
显示初始化- 向下执行t1里面的static t1,t2是不会初始化的,因为这是静态变量只会初始化一次,显然通过最外层的t初始化的。
- 既然下面的static变量无法执行,只能执行成员变量
public int j = print("j")
print("j")
执行++k,i为0,n为0(i和n都是默认初始化不会去用 ``public static int i = print(“i”);public static int n = 99)`)输出 1:j i=0 n=0同时++i,++n;i=1,n=1
- 调用构造块
print("构造快");
++k,++i,++n 输出 2:构造快 i=1 n=1
- 调用构造方法
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n)
并且i++;k++,n++;输出3:t1 i=2 n=2
public static T t2 = new T("t2")
开始执行,过程同3- 执行
public static int i = print("i")
i被显示初始化。- 在前面中i=6,k=6,n=6,++k,++n,++i,i就被初始化为7。输出 7:i i=6 n=6
- 执行
public static int n = 99;
n被显示初始化,n=99 - 执行静态块
1.print("静态块");
++k,++n,++i 输出 8:静态块 i=7 n=99 - 执行成员变量
public int j = print("j");
- ++k,++n,++i输出 9:j i=8 n=100
- 执行构造快
- ++k,++n,++i.输出 10:构造快 i=9 n=101
- 最后执行构造方法,输出 11:init i=10 n=102
现在我们将k变成-5看看
输出:
-4:j i=0 n=0
-3:构造快 i=1 n=1
-2:t1 i=2 n=2
-1:j i=3 n=3
0:构造快 i=4 n=4
1:t2 i=5 n=5
2:i i=6 n=6
3:静态块 i=7 n=99
4:j i=8 n=100
5:构造快 i=9 n=101
6:init i=10 n=102
- 我们针对这个可以进行非常多灵活的测试,比如将public static T t1 = new T(“t1”);移到第一个执行
输出:
1:j i=0 n=0
2:构造快 i=1 n=1
3:t1 i=2 n=2
-4:j i=3 n=3
-3:构造快 i=4 n=4
-2:t2 i=5 n=5
-1:i i=6 n=6
0:静态块 i=7 n=99
1:j i=8 n=100
2:构造快 i=9 n=101
3:init i=10 n=102
- 将静态代码块转移到第一个
输出:
1:静态块 i=0 n=0
-4:j i=1 n=1
-3:构造快 i=2 n=2
-2:t1 i=3 n=3
-1:j i=4 n=4
0:构造快 i=5 n=5
1:t2 i=6 n=6
2:i i=7 n=7
3:j i=8 n=99
4:构造快 i=9 n=100
5:init i=10 n=101
看到++k是1,然后++k又变成了-4验证了前面所说的先默认初始化,在显示初始化。同时静态代码块和静态变量的执行顺序取决于你写的顺序
[参考] https://blog.youkuaiyun.com/qq_35654259/article/details/84402451
总结
(静态变量)静态代码块>(成员变量)构造代码块>构造方法
对于父类与子类
1在new B一个实例时首先要进行类的装载。(类只有在使用New调用创建的时候才会被java类装载器装入)
2,在装载类时,先装载父类A,再装载子类B
3,装载父类A后,完成静态动作(包括静态代码和变量,它们的级别是相同的,安装代码中出现的顺序初始化)
4,装载子类B后,完成静态动作
类装载完成,开始进行实例化
1,在实例化子类B时,先要实例化父类A
2,实例化父类A时,先成员实例化(非静态代码)
3,父类A的构造方法
4,子类B的成员实例化(非静态代码)
5,子类B的构造方法
[参考]https://www.cnblogs.com/xunbu7/p/5079376.html