zoukankan      html  css  js  c++  java
  • Java类与对象初始化的过程(一道经典的面试题)

    本文不再以ClassLoader的视角解释这些问题。

    首先,Java代码有个特点,就是成员变量可以在前面的方法中使用,在后面定义。这一特性,很多人说Java了不起,可是为什么呢?Java为何能够这样呢?

    我们首先来看一道面试题: 

    写出上面代码的运行结果。
    其实对于Java了解比较深入的人,不屑于解决这道题,因为看代码写运行结果,再常规不过,可是这个题,要是写准了,还真的不容易,因为,我们要以类似C语言的视角审视这道题。
    OK,我们首先理解一个基本概念,内存空间一定是先申请,再初始化,再使用的。例如C语言中的malloc分配空间,然后初始化写0,然后使用。其实Java也是如此的。
    首先,我们从main方法入手,只有一行new对象(第26行)。
    new对象首先要加载类,这是肯定的了,因此“Test”需要加载类,也就是加载Test的字节码文件到方法区(永久代)中。这时需要把类中的“static”部分处理完成。
    也就是说,static部分是随着Test的字节码文件进入到永久代的。
    它的过程是:先把所有的static部分申请空间,然后再给每一个static成员由上至下分配初始值。初始值有两种,一种是Java对于基本数据类型的默认初始值,这个默认初始值在申请空间之时就给每一个成员赋予了,另一种是Java程序员利用“=”对成员进行赋初始值。
    如图所示,五个静态成员在永久代首先被分配内存。
    此时,k=0,t1=null,t2=null,i=0,n=0。空间申请完成之后,我们把0赋值给k,虽然k被分配内存之后就是0,但是依然还要把0再赋值给k,因为“=”右边的“0”是程序员对k的赋值。然后是给t1实例一个对象,也就是t1原本是null,现在把new出来的对象赋值给t1,于是就要构建一个Test对象。构建对象时,要把所有的非static部分初始化一份,放入堆内存。
    这时,你就理解了Java语法中,为什么静态成员是“类名.成员”,而非静态成员是“对象.成员”了。因为所属关系不同。
    那么我们开始找非静态的成员
    如图,有j和一个构造块。
    因此是先给j申请空间,然后运行print("j")方法,把方法的返回值交给j。
    于是,这个程序的第一段打印结果出来了:
    打印:
    此后,k=1 , n=1 , i=1。
    然后是接着找非静态的部分,就只有构造块了,因此是:
    此后,k=2 , n=2 , i=2。
    这时,我们构造对象的准备工作做完了,也就是非静态的代码都执行完了,因此开始实例化对象,Java实例化对象使用构造方法,因此执行:
    打印:
    此后,t1不再是null,然后初始化t2,过程和t1一样,因此运行结果是:
    再然后初始化静态的i,因此是执行:
    输出打印:
    然后初始化 n(第6行),直接把n赋值为99。但是什么都不打印。
    然后再往下是静态块:
    输出打印:
    至此,所有的静态部分也都初始化完毕了,可以new Test("init")了:
    输出打印:
    所以总体打印如下:
    总结,其实Java本身也是代码从上往下走的,只不过静态部分和非静态部分在两个次元里。Java的成员有分配空间,赋默认值两个过程,且首先为全体成员申请空间,然后由上至下逐一赋值。
  • 相关阅读:
    在LINUX中添加按键的驱动并编译进入内核
    什么是Dojo
    Dojo EnhancedGrid Pagination
    再Repeater模板中,如何获取里面的控件 客户端ID ??
    需求分析的大道理
    PL/SQL块结构和组成元素
    ORACLE SQL:经典查询练手第二篇
    企业信息化的定义、内涵
    背景需要需求规格
    asp.net(c#)两时间段每天是星期几,周几(时间段日历显示)的问题解
  • 原文地址:https://www.cnblogs.com/atom-wangzh/p/8718323.html
Copyright © 2011-2022 走看看