记一个有趣的代码片段
昨天看到一个十分有意思的问题,当时看到问题的我也是瞬间懵了。其实就是自己掌握的知识不扎实。现在我把它分开经行解析下,如有不对,请指正!主要还是自己对基础知识理解的不扎实,以后还会遇到许许多多的问题,就干脆起了个java学习有趣代码片段(一)。
第一个问题
代码如下
package com.zhb;
public class Test {
static Test test = new Test("3");
static{
System.out.println("1");
}
{
System.out.println("2");
}
Test(String s){
System.out.println(s);
}
public static void staticFunction(){
System.out.println("4");
}
public static void main(String[] args) {
staticFunction();
}
}
看到这段代码,我心想我刚刚总结了关于static代码块的总结,这个还不在话下!结果应该是 1 2 3 4 。
运行后,我发现,什么鬼!
* 正确的运行结果:2 3 1 4 *
拆解分析
1.首先我们先再复习下关于代码块的知识
package com.zhb;
public class Demo1 {
/**
* 静态代码块
*/
static{
System.out.println("静态代码块");
}
/**
* 构造代码
*/
{
System.out.println("构造代码块");
}
/**
* 无参构造函数
*/
public Demo1(){
System.out.println("无参构造函数");
}
/**
* 有参数的构造函数
*/
public Demo1(int num){
System.out.println("有参数的构造函数");
}
public static void main(String[] args) {
System.out.println("==================");
new Demo1();
System.out.println("==================");
new Demo1(2);
}
}
执行结果
静态代码块==================
构造代码块
无参构造函数==================
构造代码块
有参数的构造函数
从中我们可以看出
- 静态代码块它是随着类的加载而被执行,只要类加载了就会执行,而且只加载一次,主要用于给类进行初始化;
- new一个对象的时候总是先执行构造代码,在执行构造函数,但是有一点需要注意构造代码不是在构造函数之前运行,它是依托构造函数执行。
- 构造代码块每创建一个对象时,就会执行一次。同时构造函数是给特定的对象进行初始化,而构造代码是给所有的对象进行初始化,作用域不同。
- 执行顺序:静态代码块> 构造代码块 > 构造函数
2,重现原题中的结构代码
package com.zhb;
public class Demo2 {
static Demo1 demo1 =new Demo1() ;
/**
* 静态构造代码
*/
static{
System.out.println("Demo2静态构造代码块");
}
/**
* 构造代码
*/
{
System.out.println("Demo2构造代码块");
}
/**
* 无参构造函数
*/
public Demo2(){
System.out.println("Demo2无参构造函数");
}
/**
* 有参数的构造函数
*/
public Demo2(int num){
System.out.println("Demo2有参数的构造函数");
}
public static void main(String[] args) {
System.out.println("==================");
new Demo2();
System.out.println("==================");
new Demo2(2);
}
}
执行结果:
静态代码块
构造代码块
无参构造函数
Demo2静态构造代码块==================
Demo2构造代码块
Demo2无参构造函数==================
Demo2构造代码块
Demo2有参数的构造函数
从中我们知道
- 静态变量和静态构造代码块是同级别的;
- static按照顺序执行
进一步演练
package com.zhb;
public class Demo2 {
//static Demo1 demo1 =new Demo1() ;
static Demo2 demo2 =new Demo2() ;
/**
* 静态构造代码
*/
static{
System.out.println("Demo2静态构造代码块");
}
/**
* 构造代码
*/
{
System.out.println("Demo2构造代码块");
}
/**
* 无参构造函数
*/
public Demo2(){
System.out.println("Demo2无参构造函数");
}
/**
* 有参数的构造函数
*/
public Demo2(int num){
System.out.println("Demo2有参数的构造函数");
}
public static void main(String[] args) {
System.out.println("==================");
new Demo2();
System.out.println("==================");
new Demo2(2);
}
}
此时的代码结构和原题是一样的。
- 顺序执行静态部分,首先执行 static Demo2 demo2 =new Demo2() ;
- 此时,new一个对象的时候总是先执行构造代码。
执行System.out.println(“Demo2构造代码块”); - 执行构造函数 System.out.println(“Demo2无参构造函数”);此时demo2 结束
- 顺序执行静态构造代码
- 后面的正常执行(前面已解释)
执行结果:
Demo2构造代码块
Demo2无参构造函数
Demo2静态构造代码块==================
Demo2构造代码块
Demo2无参构造函数==================
Demo2构造代码块
Demo2有参数的构造函数
此时明白了原程序为什么是那样的结果了
第二个问题
我们把原题的程序修改成如下
package com.zhb;
public class Test {
static Test test = new Test();
Test test2 = new Test();
public static void main(String[] args) {
}
}
执行后报错
- Exception in thread “main” java.lang.StackOverflowError *
这个问题是为什么呢?
静静的分析下
1.我们知道首先运行static.
2.static Test test = new Test(); 初始化Test类,会把类的成员变量实例化
3.实例化test2,但是Test test2 = new Test();此时又会实例化test2,一直进行循环
4.导致栈溢出
这里又引出一个问题:类的实例变量初始化的过程
class B {
private int b = 10;
public B(){
b = 100;
}
}
编译成class文件后,使用命令 javap -c B.class 反编译
// 第一部分:父类的<init>()方法
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
// 第二部分:实例变量初始化,也就是定义变量时的赋值
4: aload_0
5: bipush 10
7: putfield #2 // Field b:I
// 第三部分:构造函数方法体
10: aload_0
11: bipush 100
13: putfield #2 // Field b:I
16: return
学习到这里总结一下前面学习的内容就是:
* Java实例变量在初始化时的顺序是父类的初始化代码(xxx—>xxx—>xxx)—>定义变量时直接赋值—>构造函数代码块。*
由于我也是菜鸟一枚,很多也是一知半解,如有不对,请指正。