阿里的一道笔试题:java初始化

令狐-少侠 2015-01-18 02:21:19
这是去年阿里的一道笔试题,很有难度,百思不得其解;
代码如下:
public class StaticTest {
public static int k=0;
public static StaticTest s1=new StaticTest("s1");
public static StaticTest s2=new StaticTest("s2");
public static int i=print("i");
public static int n=99;
public int j=print("j");

{
print("构造块");
}

static
{
print("静态块");
}

public static int print(String s)
{
System.out.println(++k+":"+s+"\ti="+i+"\tn="+n);
++n;
return ++i;
}

public StaticTest(String s)
{
System.out.println(++k+":"+s+"\ti="+i+"\tn="+n);
++i;
++n;
}

public static void main(String[] args) {
new StaticTest("init");
}
}

输出结果:
1:j	i=0	n=0
2:构造块 i=1 n=1
3:s1 i=2 n=2
4:j i=3 n=3
5:构造块 i=4 n=4
6:s2 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

哪位可以解释一下?研究了很久没搞明白。
原文:http://blog.csdn.net/yutianzuijin/article/details/11935035
...全文
1737 63 打赏 收藏 转发到动态 举报
写回复
用AI写文章
63 条回复
切换为时间正序
请发表友善的回复…
发表回复
vZou 2016-12-29
  • 打赏
  • 举报
回复
jay007333说的没错,可以通过javap -verbose 指令查看,可以看到在static(){....}中调用了类的<init>构造器,那么对于i中出现两次重复值,怎么解释?
  • 打赏
  • 举报
回复
指正来了: 你说人家skyhitnow和“空指针不懂404的忧伤”在第一页已经说的那么透彻了,相关的java language specification 文档也找到了,你再这自说自话的再说一次,有意思! 一个类的加载分:load, link, initialize和invoke四个阶段,其中Link又分verify, prepare, (optionally) resolve三个阶段。静态变量的内存分配(姑且这么叫)是在link中的preparation阶段完成的!再多说一句:既然你用java,揍不要扯什么声明和定义的区别,只能把你自己绕进去!那是人家c/c++程序员的话题。。。 至于你说的啥primitive type在栈上分配还是个引用?那你叫boxing情何以堪啊! 你先把jsl第十二章读明白了再来吧! 还有,图画的不错,可惜一知半解啊。。。 这个题目本身已经木有什么油水了。你要是真想补习java基础呢,揍试着把执行这章里有关interface执行顺序的内容再复习一下,结合类的加载执行过程,再出个图吧。那样会很有用处!
引用 54 楼 ooppookid 的回复:
楼主,不错的问题啊。 首先,jvm处理变量时候,变量的声明(分配内存,先要分配一个内存给变量,然后再给值)和初始化赋值不是一次执行的(jsl里有)。 我总结的比较简单的原则,如下(当然可能有遗漏,希望各位补充和指正): 父类先于子类; 静态先于非静态; 变量和块先于构造方法; 变量声明先于执行(变量赋值、块执行);(配合第二条,所有静态成员都声明完了之后,才会为他们进行赋值操作) 变量赋值与块优先级相同;(也就是你题中静态块、结构块上移,会先执行) 同优先级的从上至下执行;
令狐-少侠 2015-01-22
  • 打赏
  • 举报
回复
引用 50 楼 jay007333 的回复:
最后是9-11行 打错了。。。。。
先谢谢热心回复啊! 但是,你的解释有问题啊 这段程序涉及到递归初始化的问题; 你说的,“暂停"这个是想当然。并没有暂停的。到了s1,是对s1进行赋值,接下来是需要做的是类的加载、初始化,但是此时处在上次加载完毕后,初始化的过程中,这里就涉及到了递归的问题。
令狐-少侠 2015-01-22
  • 打赏
  • 举报
回复
引用 49 楼 jay007333 的回复:
简单点解释就是:静态变量首先加载,结果在初始化静态变量s1时,调用实例化对象方法。导致s1之后的静态变量初始化暂停,转而初始化实例变量(注:此时静态变量的初始化就暂停了 直接跳到public int j=print("j");)。 所以1-3行输出结果是public static StaticTest s1=new StaticTest("s1");导致。 所以4-6行输出结果是继续初始化静态变量public static StaticTest s2=new StaticTest("s2");导致。 7-8行输出结果为继续初始化静态变量public static int i=print("i"); public static int n=99; public int j=print("j");导致 9-10行输出结果为main方法内导致!
暂停,这个说法,有根据吗?虽然可以解释现象。
jay007333 2015-01-22
  • 打赏
  • 举报
回复
最后是9-11行 打错了。。。。。
jay007333 2015-01-22
  • 打赏
  • 举报
回复
简单点解释就是:静态变量首先加载,结果在初始化静态变量s1时,调用实例化对象方法。导致s1之后的静态变量初始化暂停,转而初始化实例变量(注:此时静态变量的初始化就暂停了 直接跳到public int j=print("j");)。 所以1-3行输出结果是public static StaticTest s1=new StaticTest("s1");导致。 所以4-6行输出结果是继续初始化静态变量public static StaticTest s2=new StaticTest("s2");导致。 7-8行输出结果为继续初始化静态变量public static int i=print("i"); public static int n=99; public int j=print("j");导致 9-10行输出结果为main方法内导致!
flurriedCat 2015-01-22
  • 打赏
  • 举报
回复
Do not go ah. . Let me a while to learn English.
引用 37 楼 micropentium6 的回复:
引用 36 楼 lwb314 的回复:
[quote=引用 33 楼 micropentium6 的回复:]
我来了董小姐,网速太卡,网页基本打不开啊
我擦,真的有6个套套哦! 尼玛,你来答一下好了![/quote]
令狐-少侠 2015-01-22
  • 打赏
  • 举报
回复
引用 58 楼 ooppookid 的回复:
还有就是对于8中基本类型,我的理解是: 声明就是:在栈中创建一个变量为j的引用(指向默认地址(字面值是0)) 赋值就是:在栈里查找有没有字面值(你的赋值,例如5)的地址,没找到,就开辟一个存放这个值的地址,然后j将指向这个地址。 因为没有深入研究过这个,可能我的理解有偏差,可以讨论一下
现在使用new 创建了对象,再次使用同样的方法进行调试。
public class OrdinayClassInitTest {
	/**
	 * 非静态字段
	 */
	private int k =10;//(1)
	private Long longL = 100L;
	private long L = 1000L;
	/**
	 * 静态字段
	 */
	private static int k1 =10;//(2)
	private static Long longL1 = 100L;
	private static long L1 = 1000L;
	static{
		System.out.println("本程序运行到了静态代码段!");
	}
	public static void main(String[] args) {
		OrdinayClassInitTest oInitTest = new OrdinayClassInitTest();
	}
}
令狐-少侠 2015-01-22
  • 打赏
  • 举报
回复
引用 58 楼 ooppookid 的回复:
还有就是对于8中基本类型,我的理解是: 声明就是:在栈中创建一个变量为j的引用(指向默认地址(字面值是0)) 赋值就是:在栈里查找有没有字面值(你的赋值,例如5)的地址,没找到,就开辟一个存放这个值的地址,然后j将指向这个地址。 因为没有深入研究过这个,可能我的理解有偏差,可以讨论一下
对于非静态变量,这个问题 我写了一个普通的测试程序,你单步调试会发现,如果不使用new 新建对象,那么非静态的赋值语句就走不到的; 请在(1)和(2)处分别打上断点,观察本例的运行过程。 例子如下:
public class OrdinayClassInitTest {
	/**
	 * 非静态字段
	 */
	private int k =10;//(1)
	private Long longL = 100L;
	private long L = 1000L;
	/**
	 * 静态字段
	 */
	private static int k1 =10;//(2)
	private static Long longL1 = 100L;
	private static long L1 = 1000L;
	static{
		System.out.println("本程序运行到了静态代码段!");
	}
	public static void main(String[] args) {
	}
}
jay007333 2015-01-22
  • 打赏
  • 举报
回复
引用 52 楼 qiao_198911 的回复:
[quote=引用 50 楼 jay007333 的回复:] 最后是9-11行 打错了。。。。。
先谢谢热心回复啊! 但是,你的解释有问题啊 这段程序涉及到递归初始化的问题; 你说的,“暂停"这个是想当然。并没有暂停的。到了s1,是对s1进行赋值,接下来是需要做的是类的加载、初始化,但是此时处在上次加载完毕后,初始化的过程中,这里就涉及到了递归的问题。[/quote] 确实是因为递归调用导致的暂停的假象,一时没想到好的术语表达。。。。。
猿人林克 2015-01-22
  • 打赏
  • 举报
回复
还有就是对于8中基本类型,我的理解是: 声明就是:在栈中创建一个变量为j的引用(指向默认地址(字面值是0)) 赋值就是:在栈里查找有没有字面值(你的赋值,例如5)的地址,没找到,就开辟一个存放这个值的地址,然后j将指向这个地址。 因为没有深入研究过这个,可能我的理解有偏差,可以讨论一下
猿人林克 2015-01-22
  • 打赏
  • 举报
回复
引用 56 楼 qiao_198911 的回复:
[quote=引用 54 楼 ooppookid 的回复:] 楼主,不错的问题啊。 首先,jvm处理变量时候,变量的声明(分配内存,先要分配一个内存给变量,然后再给值)和初始化赋值不是一次执行的(jsl里有)。 我总结的比较简单的原则,如下(当然可能有遗漏,希望各位补充和指正): 父类先于子类; 静态先于非静态; 变量和块先于构造方法; 变量声明先于执行(变量赋值、块执行);(配合第二条,所有静态成员都声明完了之后,才会为他们进行赋值操作) 变量赋值与块优先级相同;(也就是你题中静态块、结构块上移,会先执行) 同优先级的从上至下执行;
你总结的原则整体上是对的。 jvm处理静态量和非静态量是不同的:静态量在加载完毕之后,就赋予了默认值,0值和null值;二非静态的需要显示调用new 构造器才分配内存空间,以及赋予默认值。 按照程序的代码的要求,进行赋值,是比较靠后的事情。[/quote] 你说的没错,static是这样。但是非static的你说的片面了,对于8个基本类型以及他们的封装类(比如例子里的j)和String(比较特殊),不是你说的new之后才分配空间的,当然其他的都是你说的那样没错。 看看我写的博文,帮忙修改修改啊~共同学习一下
令狐-少侠 2015-01-22
  • 打赏
  • 举报
回复
引用 54 楼 ooppookid 的回复:
楼主,不错的问题啊。 首先,jvm处理变量时候,变量的声明(分配内存,先要分配一个内存给变量,然后再给值)和初始化赋值不是一次执行的(jsl里有)。 我总结的比较简单的原则,如下(当然可能有遗漏,希望各位补充和指正): 父类先于子类; 静态先于非静态; 变量和块先于构造方法; 变量声明先于执行(变量赋值、块执行);(配合第二条,所有静态成员都声明完了之后,才会为他们进行赋值操作) 变量赋值与块优先级相同;(也就是你题中静态块、结构块上移,会先执行) 同优先级的从上至下执行;
你总结的原则整体上是对的。 jvm处理静态量和非静态量是不同的:静态量在加载完毕之后,就赋予了默认值,0值和null值;二非静态的需要显示调用new 构造器才分配内存空间,以及赋予默认值。 按照程序的代码的要求,进行赋值,是比较靠后的事情。
猿人林克 2015-01-22
  • 打赏
  • 举报
回复
花了点时间验证了一下,应该没有问题(只能说不错,可能会缺,最好实际了解jvm,然后自己抽象出来)。 因为这个题覆盖的东西蛮多的也蛮经典的,所以简单跟踪验证了一下,简单化了个流程图,如下: http://blog.csdn.net/ooppookid/article/details/42967353
猿人林克 2015-01-22
  • 打赏
  • 举报
回复
楼主,不错的问题啊。 首先,jvm处理变量时候,变量的声明(分配内存,先要分配一个内存给变量,然后再给值)和初始化赋值不是一次执行的(jsl里有)。 我总结的比较简单的原则,如下(当然可能有遗漏,希望各位补充和指正): 父类先于子类; 静态先于非静态; 变量和块先于构造方法; 变量声明先于执行(变量赋值、块执行);(配合第二条,所有静态成员都声明完了之后,才会为他们进行赋值操作) 变量赋值与块优先级相同;(也就是你题中静态块、结构块上移,会先执行) 同优先级的从上至下执行;
Intboy 2015-01-22
  • 打赏
  • 举报
回复
好牛逼的样纸,debug研究研究,类加载顺序,先什么后什么……
skyhitnow 2015-01-21
  • 打赏
  • 举报
回复
引用 42 楼 micropentium6 的回复:
sorry, I am not sure why you raise the argument on the value on variable n. I thought we both agree that default value exists for static variables. I might misunderstood you when you said:"嵌套的new不再管静态变量随后的静态变量赋值,并不是因为它们都有了初值,这些初值也并不是在main中的new引发的,而是在初始化之前完成的。".After the second read, I got what you meant. But I would suggest you replacing "初值" with "缺省值",which could completely rule out any misunderstanding... are we on the same page now?
引用 40 楼 skyhitnow 的回复:
[quote=引用 39 楼 micropentium6 的回复:] are you talking to me or qiao_198911? [quote=引用 35 楼 skyhitnow 的回复:] [quote=引用 33 楼 micropentium6 的回复:] the paragraph you referred to is probably out of date, this is the latest JLS: 12.3.2 Preparation of a Class or Interface Type Preparation involves creating the static fields (class variables and constants) for a class or interface and initializing such fields to the default values (§4.12.5). This does not require the execution of any source code; explicit initializers for static fields are executed as part of initialization (§12.4), not preparation. See the part was removed: "preparation does not require the execution of any Java virtual machine code" This piece of verbiage proves that there are default values for static fields in the class during the preparation stage(§4.12.5). However, this particular behavior on this code sample, we only have observation... [quote=引用 32 楼 skyhitnow 的回复:] [quote=引用 31 楼 micropentium6 的回复:] I don't agree with you on this one. Class variable does have it "default value" regardless of the availability of static initializers! As I indicated previously, static variable with primitive type will be given a default value 0, reference type on the other hand, will be "assigned" with null. a good analogy is the implementation of C, the uninitialized variable in .bss segment will be "initialized" by kernel to arithmetic 0 or null pointers. these static initializers are supposed to be invoked before any class instance is created. However, according to the textual order rule, if the instantiation of a class variable requires invoking instance initializers before the completion of static initializers, this particular instance will be in an "incomplete" status, which means the remaining static variables that is after this class variable will only have their "default value". Of course, this is my understanding but we failed to find this "rule" in official document to support ourselves. The "初值" you referred to is actually the "initialized variable". It's different from a variable with default value... my 2cents! [quote=引用 28 楼 skyhitnow 的回复:] 嵌套的new不再管静态变量随后的静态变量赋值,并不是因为它们都有了初值,这些初值也并不是在main中的new引发的,而是在初始化之前完成的。如果是因为静态变量都已经有了初值而不再理会初始化阶段,那main中的new应该也不会引起类的初始化了。关键的一点是,在未完成初始化的类上调用new,会采取什么行动?我觉得在new任何对象的时候,都会检查该类的初始化状态。如果初始化已经正常完成,那肯定是直接执行实例化阶段了。但是,初始化尚未完成,该怎么办?这是要有定义的,而不是简单的第一次调用new时初始化,如果第一次初始化标示类处于错误状态,那随后的new难道因为它是第二次new就不再查看类的状态,直接去实例化吗?对于其中的细节我没有研究,希望精通的大神讲解一下。
5.4.2 Preparation Preparation involves creating the static fields for the class or interface and initializing those fields to their standard default values (§2.5.1). Preparation should not be confused with the execution of static initializers (§2.11); unlike execution of static initializers, preparation does not require the execution of any Java virtual machine code.[/quote][/quote] 这不是说明了为什么n是0,而不是99吗?如果初始化完成,它就是99。0这个值当然不是在初始化阶段赋予的。[/quote][/quote] 我当然是和你说,我并没否认你引用的内容,我引用的那段来自jvm规范,它们之间有矛盾吗?[/quote][/quote] 抱歉,我用词却有不妥,意思你已明白,共识算达成了吧。
  • 打赏
  • 举报
回复
I can ensure you my target is not you! BTW, thank you for showing me the paragraph you found in jvm spec, it's quite helpful!
引用 41 楼 skyhitnow 的回复:
引用 38 楼 micropentium6 的回复:
[quote=引用 36 楼 lwb314 的回复:] [quote=引用 33 楼 micropentium6 的回复:]
我来了董小姐,网速太卡,网页基本打不开啊
我砸场子贴在此: http://bbs.csdn.net/topics/390971281 http://bbs.csdn.net/topics/390963458[/quote] 看来我得闪哪[/quote]
  • 打赏
  • 举报
回复
sorry, I am not sure why you raise the argument on the value on variable n. I thought we both agree that default value exists for static variables. I might misunderstood you when you said:"嵌套的new不再管静态变量随后的静态变量赋值,并不是因为它们都有了初值,这些初值也并不是在main中的new引发的,而是在初始化之前完成的。".After the second read, I got what you meant. But I would suggest you replacing "初值" with "缺省值",which could completely rule out any misunderstanding... are we on the same page now?
引用 40 楼 skyhitnow 的回复:
引用 39 楼 micropentium6 的回复:
are you talking to me or qiao_198911? [quote=引用 35 楼 skyhitnow 的回复:] [quote=引用 33 楼 micropentium6 的回复:] the paragraph you referred to is probably out of date, this is the latest JLS: 12.3.2 Preparation of a Class or Interface Type Preparation involves creating the static fields (class variables and constants) for a class or interface and initializing such fields to the default values (§4.12.5). This does not require the execution of any source code; explicit initializers for static fields are executed as part of initialization (§12.4), not preparation. See the part was removed: "preparation does not require the execution of any Java virtual machine code" This piece of verbiage proves that there are default values for static fields in the class during the preparation stage(§4.12.5). However, this particular behavior on this code sample, we only have observation... [quote=引用 32 楼 skyhitnow 的回复:] [quote=引用 31 楼 micropentium6 的回复:] I don't agree with you on this one. Class variable does have it "default value" regardless of the availability of static initializers! As I indicated previously, static variable with primitive type will be given a default value 0, reference type on the other hand, will be "assigned" with null. a good analogy is the implementation of C, the uninitialized variable in .bss segment will be "initialized" by kernel to arithmetic 0 or null pointers. these static initializers are supposed to be invoked before any class instance is created. However, according to the textual order rule, if the instantiation of a class variable requires invoking instance initializers before the completion of static initializers, this particular instance will be in an "incomplete" status, which means the remaining static variables that is after this class variable will only have their "default value". Of course, this is my understanding but we failed to find this "rule" in official document to support ourselves. The "初值" you referred to is actually the "initialized variable". It's different from a variable with default value... my 2cents! [quote=引用 28 楼 skyhitnow 的回复:] 嵌套的new不再管静态变量随后的静态变量赋值,并不是因为它们都有了初值,这些初值也并不是在main中的new引发的,而是在初始化之前完成的。如果是因为静态变量都已经有了初值而不再理会初始化阶段,那main中的new应该也不会引起类的初始化了。关键的一点是,在未完成初始化的类上调用new,会采取什么行动?我觉得在new任何对象的时候,都会检查该类的初始化状态。如果初始化已经正常完成,那肯定是直接执行实例化阶段了。但是,初始化尚未完成,该怎么办?这是要有定义的,而不是简单的第一次调用new时初始化,如果第一次初始化标示类处于错误状态,那随后的new难道因为它是第二次new就不再查看类的状态,直接去实例化吗?对于其中的细节我没有研究,希望精通的大神讲解一下。
5.4.2 Preparation Preparation involves creating the static fields for the class or interface and initializing those fields to their standard default values (§2.5.1). Preparation should not be confused with the execution of static initializers (§2.11); unlike execution of static initializers, preparation does not require the execution of any Java virtual machine code.[/quote][/quote] 这不是说明了为什么n是0,而不是99吗?如果初始化完成,它就是99。0这个值当然不是在初始化阶段赋予的。[/quote][/quote] 我当然是和你说,我并没否认你引用的内容,我引用的那段来自jvm规范,它们之间有矛盾吗?[/quote]
skyhitnow 2015-01-21
  • 打赏
  • 举报
回复
引用 38 楼 micropentium6 的回复:
引用 36 楼 lwb314 的回复:
[quote=引用 33 楼 micropentium6 的回复:]
我来了董小姐,网速太卡,网页基本打不开啊
我砸场子贴在此: http://bbs.csdn.net/topics/390971281 http://bbs.csdn.net/topics/390963458[/quote] 看来我得闪哪
加载更多回复(42)

67,512

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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