Java知识:恒生电子的坑爹笔试题扩展的知识

来源:互联网 发布:python 函数作为参数 编辑:程序博客网 时间:2024/04/30 13:05

摘自论坛JAVASE板块精华帖

Java代码如下:

public class TestCodeSeg
{
    static
    {
        System.out.println("1");
    }
    {
        System.out.println("2");
    }
    public TestCodeSeg()
    {
        System.err.println("3");
    }
    public static void main(String[] args)
    {
        new TestCodeSeg();
    }
}
求输出结果


正确答案以及分析:

1和2顺序是不变的,因为它们是静态(类装载时被执行),3是在构造函数里。所以,程序执行顺序是静态部分然后是构造函数。

但是输出结果还要受到System.out和System.err的影响。System.out是行缓冲而System.err是不缓冲,所以System.err.println("3")
是立即输出。但System.out.println("1")和System.out.println("2")则因为缓冲的原因可能稍晚(但1和2的顺序不变)。具体输出结果1
先2后是绝对的,但3在哪里出现跟程序执行时情况有关。如果System.err.println("3")执行时,1和2还没输出,再是则是3先:
3
2
1
如果1已经输出但换行符还没输出则是:
13
2
以此类推还有:
1
3
2
等等情况

总结:

1、仔细看题,虽然这个总结很low但是还是很中肯的。
2、System.out是行缓冲而System.err是不缓冲,看来输出顺序和是否缓冲是有很大关系的,这点容易忽略。

扩展:


1、静态代码块、构造代码块和普通代码块如何区分?

静态代码块:在java中使用static关键字声明的代码块。
构造块:直接在类中定义且没有加static关键字的代码块称为{}构造代码块。
普通代码块:在方法或语句中出现的{}就称为普通代码块。把普通代码块称作局部代码块可能更容易理解吧吐舌头

2、构造代码块与构造方法哪个先执行,为什么?


构造代码块先执行。
因为构造代码块中的代码也是在构造方法中执行的。在编译时的编译器看来会默认将构造代码块中的代码移动到构造方法中,并且移动到构造方法内容的前面。
你看到的构造方法的开头隐藏了一些东西,其中就包含了对构造代码块的调用。了解这一点其实也是挺重要的,毕竟一个知识学明白了就会记忆深刻。

另外一道有关执行顺序的题(此题是JAVASE推荐板块的):

就算你懂java有时候也会懵逼


正确答案以及分析:

2
3
a=110 b=0
1
4


别人的分析:

一开始我也猜错了,但是后来想通了,之前总以为调用构造函数之前必须完成类的初始化,其实不一定。
可以从类的生命周期和对象的初始化来分析:
1.类的生命周期是:加载->验证->准备->解析->初始化->使用->卸载,只有在准备阶段和初始化阶段才会涉及类变量的初始化和赋值,因此只针对这两个阶段进行分析;
2.类的准备阶段需要做是为类变量分配内存并设置默认值,因此类变量st为null、b为0;(需要注意的是如果类变量是final在加载阶段就已经完成了初始化,可以把b设置为final试试);
3.类的初始化阶段需要做是执行类构造器(类构造器是编译器收集所有静态语句块和类变量的赋值语句按语句在源码中的顺序合并生成类构造器,对象的构造方法是init,类的构造方法是cinit,可以在堆栈信息中看到),因此先执行第一条静态变量的赋值语句即st = new StaticTest (),此时会进行对象的初始化,对象的初始化是先初始化成员变量再执行构造方法,因此打印2->设置a为110->执行构造方法(打印3,此时a已经赋值为110,但是b只是设置了默认值0,并未完成赋值动作),等对象的初始化完成后继续执行之前的类构造器的语句,接下来就不详细说了,按照语句在源码中的顺序执行即可;

我的分析:


我认为关键在于 Static StaticTest st=new StaticTest();这句话。

先说一下执行顺序:


先final初始化(没有final就算了,不过可以把int a改成final int a试试有奇效),然后static初始化顺序由上到下,他在初始化Static StaticTest st=new StaticTest();

这句话的时候发生了实例化!我当时就懵了!此时有两种想法:1、先实例化(此时实例化用到的数据是尚未初始化完全的数据,是JVM分配的默认值)再初始化;2、先初始化再实例化。到底选哪个?

根据输出来看是第一种,确切的说是JAVA类可以没初始化完全的时候就进行实例化。

简单说:JAVA类可以没初始化完全的时候就进行实例化。

类执行顺序(阿里面试题)

  1. public class StaticTest {  
  1.   
  1.     public static int k = 0;// 1  
  1.     public static StaticTest s1 = new StaticTest("s1");// 2  
  1.     public static StaticTest s2 = new StaticTest("s2");// 3  
  1.     public static int i = print("i");// 4  
  1.     public static int n = 99;// 5  
  1.     public int j = print("j");// 6  
  1.   
  1.     {  
  1.         print("构造块");  
  1.     }// 7  
  1.   
  1.     static {  
  1.         print("静态块");  
  1.     }// 8  
  1.   
  1.     public static int print(String s) {  
  1.         System.out.println(++k + ":" + s + "\ti=" + i + "\tn=" + n + "\tprint");  
  1.         ++n;  
  1.         return ++i;  
  1.     }  
  1.   
  1.     public StaticTest(String s) {  
  1.         System.out.println(++k + ":" + s + "\ti=" + i + "\tn=" + n  
  1.                 + "\tStaticTest");  
  1.         ++i;  
  1.         ++n;  
  1.     }// 9  
  1.   
  1.     public static void main(String[] args) {  
  1.         new StaticTest("init");  
  1.     }  
  1. }  


执行结果


  1. /** 
  1.      *  
  1.      * 输出结果: 
  1.      *  
  1.      * syso1 ===="1:j       i=0     n=0     print" 
  1.      *  
  1.      * syso2 ===="2:构造块     i=1     n=1     print" 
  1.      *  
  1.      * syso3 ===="3:s1      i=2     n=2     StaticTest" 
  1.      *  
  1.      * syso4 ===="4:j       i=3     n=3     print" 
  1.      *  
  1.      * syso5 ===="5:构造块     i=4     n=4     print" 
  1.      *  
  1.      * syso6 ===="6:s2      i=5     n=5     StaticTest" 
  1.      *  
  1.      * syso7 ===="7:i       i=6     n=6     print" 
  1.      *  
  1.      * syso8 ===="8:静态块     i=7     n=99    print" 
  1.      *  
  1.      * syso9 ===="9:j       i=8     n=100   print" 
  1.      *  
  1.      * syso10===="10:构造块    i=9        n=101   print" 
  1.      *  
  1.      * syso11===="11:init i=10   n=102  StaticTest" 
  1.      */  

网上分析很多,找一下能找到很多,但是关键点我罗列一下:


1、JAVA类可以没初始化完全的时候就进行实例化。比如下面两行代码:
  1. public static StaticTest s1 = new StaticTest("s1");// 2  
  1. public static StaticTest s2 = new StaticTest("s2");// 3 

就是因为他们其余的static没有来得及初始化就实例化s1,s2了。其实本质上还是static的顺序问题,s1和s2都是static里面的,
他们和其余的static是要看先后顺序的,s1和s2还没有实例化完是轮不到下面的static代码执行的。


2、明白初始化与实例化各自执行哪些代码,更早的准备阶段(属于类加载里面的内容)所执行内容也增加上。

准备阶段:为变量等分配空间以及赋JVM初值、final代码
初始化:static代码(static变量、static代码块)

实例化:非static代码(非static、非final变量、普通代码块、构造方法(最后执行))



各位看官是否知道这个博客怎么排版比较好吗,用MarkDown编辑器?

原创粉丝点击