高手请进:关于JAVA的静态初始化时机!!

FengPrince 2012-09-13 03:41:03

class A
{
public static final A a=null;
static{System.out.println("A");}
}
public class Test
{
public static void main(String[] args)
{
Object o=A.a;
}
}

我觉得应该什么都不输出,但运行结果输出A。
我想知道,A.a能否在编译期确定值??我认为能确定!
高手说说自己的意见。。。
...全文
1324 42 打赏 收藏 转发到动态 举报
写回复
用AI写文章
42 条回复
切换为时间正序
请发表友善的回复…
发表回复
化身天 2015-05-29
  • 打赏
  • 举报
回复
引用 41 楼 olijino 的回复:
[quote=引用 25 楼 ticmy 的回复:] 当然,这也并不是说所有String和几个基本类型的常量就会放到B的常量池中去 比如a是int类型,其值为32767,使用的就是sipush 32767,表示操作数直接跟在操作码后面 如果值为32768,还是会从常量池用ldc指令取值 但,不管如何,这种值都放到了B.class中,要么在B的常量池中,要么跟在字节码后面作为参数 [Quote=引用 24 楼 的回复:] 不单单是null的问题,但null确实是问题之一 更重要的一个问题是会写到B.class常量池中的类型是有限的:String和几个基本类型 而A.a的类型是A 如果将a的类型改为String,为null仍然会去getstatic 如果a的类型为String,且有一个字符串值,那么B就使用ldc指令直接从B的常量池中取值,这表示a的值在编译的时候已经放到B.class中了 …… [/Quote]
膜拜[/quote] 常量池不是线程共享的吗
andy2lee 2013-06-10
  • 打赏
  • 举报
回复
引用 25 楼 ticmy 的回复:
当然,这也并不是说所有String和几个基本类型的常量就会放到B的常量池中去 比如a是int类型,其值为32767,使用的就是sipush 32767,表示操作数直接跟在操作码后面 如果值为32768,还是会从常量池用ldc指令取值 但,不管如何,这种值都放到了B.class中,要么在B的常量池中,要么跟在字节码后面作为参数 [Quote=引用 24 楼 的回复:] 不单单是null的问题,但null确实是问题之一 更重要的一个问题是会写到B.class常量池中的类型是有限的:String和几个基本类型 而A.a的类型是A 如果将a的类型改为String,为null仍然会去getstatic 如果a的类型为String,且有一个字符串值,那么B就使用ldc指令直接从B的常量池中取值,这表示a的值在编译的时候已经放到B.class中了 …… [/Quote]
膜拜
andy2lee 2013-06-10
  • 打赏
  • 举报
回复
引用 23 楼 FengPrince 的回复:
[Quote=引用 21 楼 的回复:] 这很好办,javap -c Test一下看看取A.a用的什么指令 引用 20 楼 的回复: 引用 17 楼 的回复: 那叫编译期常量,就是编译的时候就能确定值的常量。 假如A.a的值是编译期常量,如果B类中使用了A.a,编译的是时候在B.class存的就不是A.a的符号引用,而直接是A.a的值,这就不是主动使用了 引用 9 楼 的回复: 引用 5 楼 …… [/Quote] getstatic 我想问的是为什么A.a不是编译期常量,难道因为它是null吗??
高手,五体投地,太深奥了
woai5611793 2012-09-24
  • 打赏
  • 举报
回复
好吧、我来说说java对象实例化步骤吧!
1 在创建类之前,java虚拟机会检查类是否加载,如果没有加载,就加载这个类,类加载之前,会先加载所有父类(如果有父类的话);
2 、在堆中非配对象空间,递归分配所有父类和子类的属性空间,注意是属性,属性默认初始化;
3、进行行属性赋值
4、检查父类子类是否有静态代码块,如果有,则先执行父类、然后子类静态代码块;
5、然后递归调用父类构造器(默认调用父类的无参构造器),调用本类构造器;
静态代码块只执行一次;
static final 修饰的属性,一旦赋值不能修改,也就是只能初始化,不能被修改
wleexi 2012-09-24
  • 打赏
  • 举报
回复
A.a 是常量, 在编译期间就替换处理了, 运行期直接使用值.....
shareyoung 2012-09-23
  • 打赏
  • 举报
回复
具体来说是这样的,在运行“Object o=A.a;”语句之前,系统会自动加载这个类(注意并非生成该类对象,也并不开辟堆空间。) 加载类的时候,会先加载静态变量,然后加载静态块,运行静态块内的语句。
shareyoung 2012-09-23
  • 打赏
  • 举报
回复
在调用这个类变量之前就会加载这个类,并在加载时执行静态初始化块内的语句。
zoey_243502895 2012-09-23
  • 打赏
  • 举报
回复
静态块会在初始化时运行的~
wjc2web 2012-09-22
  • 打赏
  • 举报
回复
static{System.out.println("A");
静态块,肯定会输入,在类加载是就会输出了,
静态块只运行一次,这也是静态块的优点。
  • 打赏
  • 举报
回复
这个说得对[Quote=引用 3 楼 的回复:]
class A
{
public static final A a=null;
static{System.out.println("A");}
}
public class Test
{
public static void main(String[] args)
{
//这行混淆了你的概念,去掉这行再试试Object o=A.a;
}
……
[/Quote]
Jemoky 2012-09-20
  • 打赏
  • 举报
回复
这个程序main方法中使用了A.a,所以执行的时候到这句话的时候,就会先去加载A这个类
,加载的过程中类的初始化,会先给静态变量赋值,并且要执行那些静态代码块,所以
出现了打印A

JNI中就会用到这个方式来加载一些东西。
M44346460 2012-09-20
  • 打赏
  • 举报
回复
Think in Java
SFLDSIMON501055 2012-09-16
  • 打赏
  • 举报
回复
class FinalTest1{

public static final int a = 5;

static{
System.out.println("static code block");
}
}

public class FinalTest {

public static void main(String[] args) {
int a = FinalTest1.a;
}
}

static没有被执行,类没有被初始化,因为a是编译时的常量。


class FinalTest1{

public static final int a = new Integer(5);

static{
System.out.println("static code block");
}
}

public class FinalTest {

public static void main(String[] args) {
int a = FinalTest1.a;
}
}

执行了static,类被初始化,因为a是非编译时的常量。
SFLDSIMON501055 2012-09-16
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]
引用 1 楼 的回复:

肯定是要输出的,程序加载类A时,肯定会先给静态变量赋值并运行静态代码块的内容。

你要知道,类加载->类连接->类初始化这三步,类初始化并非一定会执行的!
例如,下面的输出就不行执行初始化操作。。。

Java code


class A
{
public static final int i=1;
stati……
[/Quote]

你这只是 在引用类变量,何来的类初始化?! 类变量在链接时就给静态变量分配空间并赋默认值了, 你这代码没有创建类,不存在类初始化

类初始化肯定是会给静态变量赋值并运行静态代码块的内容。。
dongchengxiaomin 2012-09-14
  • 打赏
  • 举报
回复
class A
{
//public static final int i=1;
// public static final A a=null;
public static int i=1;
static{System.out.println("A");}
}
public class Test
{
public static void main(String[] args)
{
int i=A.i;
//Object o=A.a;
}
}
FengPrince 2012-09-13
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]

不单单是null的问题,但null确实是问题之一

更重要的一个问题是会写到B.class常量池中的类型是有限的:String和几个基本类型

而A.a的类型是A

如果将a的类型改为String,为null仍然会去getstatic

如果a的类型为String,且有一个字符串值,那么B就使用ldc指令直接从B的常量池中取值,这表示a的值在编译的时候已经放到B.class中了
……
[/Quote]
好吧,就认为null不是编译期常量算了。
结贴,谢了。。。
PS:不知道是我网速原因还是CSDN的问题,新的回复老是加载不上来,每次总是有好几楼显示不了,真心耽误时间。
wei1204061 2012-09-13
  • 打赏
  • 举报
回复
高手云集
龙四 2012-09-13
  • 打赏
  • 举报
回复
当然,这也并不是说所有String和几个基本类型的常量就会放到B的常量池中去

比如a是int类型,其值为32767,使用的就是sipush 32767,表示操作数直接跟在操作码后面

如果值为32768,还是会从常量池用ldc指令取值

但,不管如何,这种值都放到了B.class中,要么在B的常量池中,要么跟在字节码后面作为参数

[Quote=引用 24 楼 的回复:]

不单单是null的问题,但null确实是问题之一

更重要的一个问题是会写到B.class常量池中的类型是有限的:String和几个基本类型

而A.a的类型是A

如果将a的类型改为String,为null仍然会去getstatic

如果a的类型为String,且有一个字符串值,那么B就使用ldc指令直接从B的常量池中取值,这表示a的值在编译的时候已经放到B.class中了
……
[/Quote]
龙四 2012-09-13
  • 打赏
  • 举报
回复
不单单是null的问题,但null确实是问题之一

更重要的一个问题是会写到B.class常量池中的类型是有限的:String和几个基本类型

而A.a的类型是A

如果将a的类型改为String,为null仍然会去getstatic

如果a的类型为String,且有一个字符串值,那么B就使用ldc指令直接从B的常量池中取值,这表示a的值在编译的时候已经放到B.class中了



[Quote=引用 23 楼 的回复:]

引用 21 楼 的回复:

这很好办,javap -c Test一下看看取A.a用的什么指令

引用 20 楼 的回复:

引用 17 楼 的回复:

那叫编译期常量,就是编译的时候就能确定值的常量。

假如A.a的值是编译期常量,如果B类中使用了A.a,编译的是时候在B.class存的就不是A.a的符号引用,而直接是A.a的值,这就不是主动使用了

引用 9 楼 ……
[/Quote]
FengPrince 2012-09-13
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 的回复:]

这很好办,javap -c Test一下看看取A.a用的什么指令

引用 20 楼 的回复:

引用 17 楼 的回复:

那叫编译期常量,就是编译的时候就能确定值的常量。

假如A.a的值是编译期常量,如果B类中使用了A.a,编译的是时候在B.class存的就不是A.a的符号引用,而直接是A.a的值,这就不是主动使用了

引用 9 楼 的回复:

引用 5 楼 ……
[/Quote]
getstatic
我想问的是为什么A.a不是编译期常量,难道因为它是null吗??
加载更多回复(22)
静态分析工具承诺无需开发人员费劲就能找出代码中已有的缺陷。当然,如果有多年的编写经验,就会知道这些承诺并不是一定能兑现。尽管如此,好的静态分析工具仍然是工具箱中的无价之宝。在这个由两部分组成的系列文章的第一部分中,高级软件工程师 Chris Grindstaff 分析了 FindBugs 如何帮助提高代码质量以及排除隐含的缺陷。代码质量工具的一个问题是它们容易为开发人员提供大量但并非真正问题的问题——即 伪问题(false positives)。出现伪问题时,开发人员要学会忽略工具的输出或者放弃它。FindBugs 的设计者 David Hovemeyer 和 William Pugh 注意到了这个问题,并努力减少他们所报告的伪问题数量。与其他静态分析工具不同,FindBugs 不注重样式或者格式,它试图只寻找真正的缺陷或者潜在的性能问题。   FindBugs 是什么?   FindBugs 是一个静态分析工具,它检查类或者 JAR 文件,将字节码与一组缺陷模式进行对比以发现可能的问题。有了静态分析工具,就可以在不实际运行程序的情况对软件进行分析。不是通过分析类文件的形式或结构来确定程序的意图,而是通常使用 Visitor 模式(请参阅 参考资料)。图 1 显示了分析一个匿名项目的结果(为防止可怕的犯罪,这里不给出它的名字):   在FindBugs的GUI中,需要先选择待扫描的.class文件(FindBugs其实就是对编译后的class进行扫描,藉以发现一些隐藏的bug。)。如果你拥有这些.class档对应的源文件,可把这些.java文件再选上,这样便可以从稍后得出的报告中快捷的定位到出问题的代码上面。此外,还可以选上工程所使用的library,这样似乎可以帮助FindBugs做一些高阶的检查,藉以发现一些更深层的bug。   选定了以上各项后,便可以开始检测了。检测的过程可能会花好几分钟,具体视工程的规模而定。检测完毕可生成一份详细的报告,藉由这份报告,可以发现许多代码中间潜在的bug。比较典型的,如引用了空指针(null pointer dereference), 特定的资源(db connection)未关闭,等等。如果用人工检查的方式,这些bug可能很难才会被发现,或许永远也无法发现,直到运行时发作…当除掉了这些典型的(classic) bug后,可以确信的是,我们的系统稳定度将会上一个新的台阶。   以目前遇到的状况来看,FindBugs可以有两种使用时机。   开发阶段   当Developer完成了某一部分功能模块开发的时候(这通常是指代码撰写完成,并已debug通过之后),可藉由FindBugs对该模块涉及的java文件进行一次扫描,以发现一些不易察觉的bug或是效能问题。交付新版的时候,开发团队可以跑一下FindBugs,除掉一些隐藏的Bug。FindBugs得出的报告可以作为该版本的一个参考文档一并交付给测试团队留档待查。   在开发阶段使用FindBugs,一方面开发人员可以对新版的品质更有信心,另一方面,测试人员藉此可以把更多的精力放在业务逻辑的确认上面,而不是花大量精力去进一些要在特殊状况下才可能出现的BUG(典型的如Null Pointer Dereference)。从而可以提高测试的效率。   维护阶段   这里指的是系统已经上线,却发现因为代码中的某一个bug导致系统崩溃。在除掉这个已暴露的bug之后,为了快速的找出类似的但还未暴露的 bug,可以使用FindBugs对该版的代码进行扫描。当然,在维护阶段使用FindBugs往往是无奈之举,且时间紧迫。此外,如果本来在新版交付的时候就使用过FindBugs的话,往往意味着这种bug是FindBugs还无法检测出的。这也是FindBugs局限的地方。   FindBugs出到目前的版本,功能已经相当强大,不过也有待完善的地方。从实际使用来看,有一些隐藏的bug并不能靠FindBugs直接发现。那么,可不可以撰写一个新的 Detector,来发现这种将一个未初始化的reference传来传去而形成的潜在的bug呢?理论上来讲,应该是可以的。这个 Detector目前还未实现。哪位如果有兴趣的话,可以参考FindBugs, Part 2: Writing custom detectors(扩展阅读)这篇文章,帮忙实现这个Detector。实现一个新的Detector,便可以检测出一种新型的bug,这样不知又可以帮开发人员省去多少人工检查的时间,功德无量啊。   FindBugs也不能发现非java的Bug。对于非java撰写的代码,如javascript,SQL等等,要找出其中可能的bug,FindBugs是无能为力的。当然,javascript中的bug似乎还不至于使系统崩溃,而SQL中的bug往往又跟业务逻辑相关,只要测试仔细一些应该是可以发现的。   FindBugs不过是一个工具。作为开发人员,当然首先要在编程的时候努力避免引入bug,而不要依赖于某个工具来为自己把关。不过由于代码的复杂性,一些隐藏的bug确实很难靠咱们的肉眼发现。这时,应用一些好的工具或许就可以帮你发现这样的bug。这便是FingBug存在的价值。

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧