目录

  • 类变量(静态变量)和静态代码块
  • 类变量(静态变量)和构造代码块
  • static final变量引用
  • 两个类关于final变量的引用
  • 非法的前向引用(Illegal forward reference)
  • 实例变量的初始化顺序
  • 构造器的多态行为

类变量(静态变量)和静态代码块

public class staticTest {static {i=9;System.out.println("初始化");
//        System.out.println(i);}static   int i=1;int u=2;public static void main(String[]args){//        staticTest ss=new staticTest();System.out.println(staticTest.i);}
}

目标输出

初始化
1

类加载的5个过程:加载,验证,准备,解析,初始化。1
首先在类的准备阶段为静态变量分配内存,并进行初始化,因为不被fian修饰,因此此时赋值统一为0。类的初始化阶段就是执行类构造器 <clinit>()方法的过程。该方法是javac编译器的自动生成物。首先 <clinit>()方法是由编译器自动收集中的所有类变量的赋值动作和静态语句块(static{}块)中的语句和合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序决定的。此时会向类变量赋真正的值。
输出解释:
①:静态代码块和静态变量都会默认执行,此时并没有创建类对象。
②:静态代码块中可以修改静态变量i的值。但不能进行访问,会报出非法的前向引用错误,原因下面介绍。
③:结果i为1,不为9。因为静态代码块在静态变量初始化之前。会先将i赋值为9,哉赋值为1。

补充:<clinit>()方法与类的构造函数(或者说实例构造器<init>()方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。因此在虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.Object。由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作

类变量(静态变量)和构造代码块

public class staticTest {{i=9;System.out.println("初始化");
//        System.out.println(i);}static   int i=1;int u=2;public static void main(String[]args){//        staticTest ss=new staticTest();System.out.println(staticTest.i);}
}

输出结果:

1Process finished with exit code 0

此时并不会执行构造代码块,因为并不会创建staticTest对象,因此不会调用构造代码块。

static final变量引用

public class staticTest{static {System.out.println("初始化");
//        System.out.println(i);}static final int i = 1;int u = 2;public static void main(String[]args){//        staticTest ss=new staticTest();System.out.println(staticTest.i);}
}

输出:

1Process finished with exit code 0

调用staticTest类,就会触发类加载过程,此时静态变量和静态代码块都会执行。

两个类关于final变量的引用

 class test11 {static {System.out.println("初始化");
//        System.out.println(i);}static final int i = 1;int u = 2;}
public class staticTest{public static void main(String[]args){//        staticTest ss=new staticTest();System.out.println(test11.i);}
}

输出:

1Process finished with exit code 0

此时并没有输出"初始化",说明没有执行静态代码块,为什么呢?
因为其实在编译阶段通过常量传播优化,已经将常量i的值1存在类staticTest的常量池中了,之后类staticTest对常量test11 .i的引用,实际上都转化为staticTest对自身常量池的引用了。也就是说,实际上staticTest类中的class文件中并没有类test11 的符号引用,了两个类在编译成class文件之后就不存在任何联系了。所以并不会进行staticTest类的引用,也就不会进行类初始化。

非法的前向引用(Illegal forward reference)

public class staticTest{static {i=3;System.out.println("初始化");//   System.out.println(i); 非法前向引用}static  int i = 1;int u = 2;public static void main(String[]args){//        staticTest ss=new staticTest();System.out.println(staticTest.i);}
}

解释:
为什么可以修改但不可以访问?
可以修改是因为此时可以将i重新赋值,这是在类初始化阶段进行的,但是执行静态代码块时,并没有执行静态变量的初始化。因此并不能在静态变量初始化前访问静态变量。只能在之后。

public class staticTest{//     static {//         i=3;
//         System.out.println("初始化");
//     //   System.out.println(i); 非法前向引用
//     }static  int i = 1;static {i=3;System.out.println("初始化");System.out.println(i);}int u = 2;public static void main(String[]args){//        staticTest ss=new staticTest();System.out.println(staticTest.i);}
}

结果:

初始化
3
3Process finished with exit code 0

实例变量的初始化顺序

引用java类变量与实例变量的初始化顺序

在初始化完成后,进入代码的执行阶段,程序会从main()函数进入,一直执行到new语句创建类的对象(每一个新的实例),在创建时会对类的实例变量分配内存并置为默认的初始化值 0 或 false 等。
一旦虚拟机完成了为新的对象分配内存和为实例变量初始化为默赋予正确认的初始值后,接下来就会为执行实例变量的初始化赋值语句与非static{}代码块。
下面的例子中,i 会先被置为0再被置为8
public int i=8;
与初始化过程类似的,所有实例变量的赋值动作和非static{}代码块,执行的的顺序是由语句在源文件中出现的顺序所决定的,非static{}代码块中只能访问到定义在非static{}代码块之前的变量,定义在它之后的变量,在前面的非static{}代码块可以赋值,但是不能访问。
我们定义了一个构造方法时,他会在上述操作执行后再执行构造方法内的操作,与代码顺序无关。

代码:

class tt{public tt(){System.out.println(temp);System.out.println("hahah");}String temp; {System.out.println(temp);temp="oo";
//        System.out.println(temp);}
}

输出:

null
oo
hahah

构造代码块中temp的引用在temp变量定义之后并没有进行赋值,因此直接为null,在构造代码块中进行赋值为oo,之后再执行构造方法下的代码,打印出oo。

构造器的多态行为

引用

class Glyph {void draw() {System.out.println("Glyph.draw()");}Glyph() {draw();}
}class RoundGlyph extends Glyph {private int radius = 1;public RoundGlyph(int r) {radius = r;System.out.println("RoundGlyph.RoundGlyph(),radius=" + radius);}void draw() {System.out.println("RoundGlyph.draw(), radius=" + radius);}}
public class PolyConstructors {public static void main(String[] args) {new RoundGlyph(5);}}

输出的结果是:

RoundGlyph.draw(), radius=0
RoundGlyph.RoundGlyph(),radius=5

分析:

  1. 从结果中可以看到draw()调用的是子类中的draw()。这个可以这样理解:非静态非final方法是动态绑定的,方法调用者对象的实际类型来决定调用哪一个方法,这里是通过new RoundGlyph()(虽然此时还是一个未完全初始化的不完整对象)来最终能调用到draw()这个方法,所以应该是调用RoundGlyph中的draw()方法。
  2. radius=0。 ???这是怎么回事???现在来解释这个问题。
    之前的关于初始化顺序的文章中强调过,构造器的调用是在成员初始化完成之后的。但是这里,通过构造器执行draw()方法的时候,radius进行初始化了么?答案是没有。所以此时打印出的radius是在未赋值之前的默认值0。
    关于初始化顺序再说的详细点儿,int radius = 1,这句话实际上给radius赋过两次值,第一次是默认值0,第二次是1。new RoundGlyph()一执行之后,先在堆中划出一片区域,所有的非静态成员都先初始位成了二进制的0,此时的radius是0。等到慢慢初始化,轮到radius了,才第二次赋值为1。所以会出现上面的结果。、
    ————————————————
    版权声明:本文为CSDN博主「xdugucc」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/xdugucc/article/details/78220518

静态代码块、构造代码块、非法的前向引用相关推荐

  1. 静态代码块 构造代码块 构造方法的执行顺序

    今天我总结了一下java中静态代码块 构造代码块 构造方法的执行顺序及其注意问题 执行顺序: 静态代码块>构造代码块>构造方法体 首先要知道静态代码块是随着类的加载而加载,而构造代码块和构 ...

  2. [转载] Java静态代码块/构造代码块/构造函数/静态变量/成员变量(相关示例)

    参考链接: Java程序的输出| 构造函数 近期做牛客网的Java开发笔试题,发现这类型的题目较多,很容易混淆,特将相关概念和相关示例整理如下,供大家参考^_^ 1. 静态代码块在类加载时即运行,而且 ...

  3. java静态代码块、静态方法、静态变量、构造代码块、普通代码块、成员变量执行顺序

    package smartt.styy.auth.model.voRsp;public class Test {static {String test5="555";//局部变量t ...

  4. Java中构造函数,静态代码块,构造代码块的执行顺序

    静态代码块:用static声明,jvm加载类的时候执行,只执行一次. 构造代码块:由{}定义,每一次创建对象的时候执行. 构造函数:对象一建立就调用相应的构造函数. 静态代码块 1.它是随着类的加载而 ...

  5. java gui构造工具_Java Web框架 静态代码块、构造代码块、构造函数、普通代码块 执行顺序 Decompiler JD-GUI 反编译工具...

    1.下载jd-gui-windows-1.4.0. http://jd.benow.ca/ 2.通过jd-gui.exe查看.class文件,用于分析类编译过程. 3.源文件. // 加载相应的 He ...

  6. Java中普通代码块,构造代码块,静态代码块区别

    Java中普通代码块,构造代码块,静态代码块区别及代码 示例 //执行顺序:(优先级从高到低.)静态代码块>mian方法 >构造代码块>构造方法. 其中静态代码块只执行一次.构造代码 ...

  7. 牛客网Java刷题知识点之什么是代码块、普通代码块、静态代码块、同步代码块、构造代码块以及执行顺序...

    不多说,直接上干货! 这种形式的程序段我们将其称之为代码块,所谓代码块就是用大括号({})将多行代码封装在一起,形成一个独立的数据体,用于实现特定的算法.一般来说代码块是不能单独运行的,它必须要有运行 ...

  8. java加载类时静态代码块、构造代码块、构造方法执行顺序

    构造代码块作用:https://blog.csdn.net/hspingcc/article/details/54893853 package com.spring.partise;class A{A ...

  9. Java提高篇——静态代码块、构造代码块、构造函数以及Java类初始化顺序

    构造函数 public HelloA(){//构造函数} 关于构造函数,以下几点要注意: 1.对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数时不会运行的. 2.构造函数的作用是 ...

最新文章

  1. 独立服务器和虚拟服务器的区别,BlueHost虚拟主机与独立服务器的主要区别
  2. 干货 | 为你解读34篇ACL论文
  3. SAP系统如何快速上手?
  4. 【牛客网】牛客练习赛19 F 算式子【数学--递推 、前缀、数字】
  5. c语言生命游戏代码大全,c++生命游戏源码
  6. 第一个错误的版本_寻找第一个错误的版本
  7. Django自定义一个简单的中间件,并使用此中间件
  8. 开发健康状态监控接口
  9. 苹果开发者账号可以创建多少测试证书_苹果开发者账号相关问题解答—企业开发者账号...
  10. postgresql 的 libdir 在哪里?
  11. 2016年CIO的五个优先级
  12. softmax 激活函数
  13. U盘装完系统后,不能用,写保护,无法格式化,变为RAW解决办法
  14. 微信小程序获取用户绑定手机号
  15. pagefile.sys 分页文件貌似不能放在移动硬盘上
  16. 先进驾驶员辅助系统ADSA
  17. 做uni-app时,遇到后台返回base64码,将base64码转为图片,但是图片没有显示出来的解决方法
  18. JavaScript之减速运动的实现
  19. 用户画像第一章(企业级360°全方位用户画像_环境搭建)
  20. 事业单位工资计算机公积金计算,事业单位住房公积金基数怎么算?

热门文章

  1. 博图v15调用fc105_调用FC105问题
  2. vue2+elementui 饿了么ui的一些食用方法
  3. 送客户的祝福语_送给客户的事业祝福语大全(含贺词、寄语等)
  4. 如何快速为没有书签pdf制作书签
  5. 《wpf专题--基于wpf制作登录页面》
  6. 腾讯家低调开发的良心工具?目前无任何付费机制还挺好用~
  7. 说点理论-什么叫TED背景
  8. linux系统中怎么截取某一天的日志,Linux系统如何截取线上日志
  9. pytorch 拾贝集
  10. 老猿学5G扫盲贴:3GPP规范文档命名规则及同系列文档阅读指南