JavaSE基础知识(十七)--Java复用代码之初始化及类的加载

Java SE 是什么,包括哪些内容(十七)?

本文内容参考自Java8标准
再次感谢Java编程思想对本文的启发!
这篇博文实际上是为接下来即将谈到的多态继承做准备!
在许多传统的语言中,程序是作为启动过程的一部分立刻被加载的,然后是初始化,紧接着是程序的运行。
但是这些语言的初始化必须小心控制,以确保定义为static的东西,其初始化顺序不会造成麻烦(例如在C++语言中,如果某个static期望另一个static在被初始化之前就能有效地使用它,那么就会出现问题,Java中不会出现这个问题)。
Java采用了不同于C++的加载方式,加载是众多变得更容易的动作之一,因为Java中所有的事物都是对象(一定要记住,Java中,每个类的编译代码都存在于它自己的独立的文件中,该文件只会在需要程序代码的时候才会被加载!),一般情况下可以这么说:类的代码在初次使用的时候才被加载(这通常是指,类的代码通常是在创建它的第一个对象的时候被加载,但是当访问static域或static方法的时候也会发生加载)。
你需要很好的记忆来记住类加载的时机!这样对你编程能力的提高很有帮助!
初次使用之处也是static初始化发生之处,所有的static对象和static代码段都会在加载时根据程序代码中的顺序(即类中的书写顺序)而依次初始化(你还需要记住的是:定义为static的东西只会初始化一次!)。

继承与初始化

下面将通过代码示例让你了解继承的初始化过程,已对发生的一切有一个全局性的把握。
代码示例:

// 继承的初始化
   //类Insect
   class Insect{
      //int类型的类变量i,初始化值为9
      private int i = 9;
      //int类型的类变量i,未初始化
      protected int j;
      //构造方法
      Insect(){
         //打印字符串"i="+i+","+"j="+j
         System.out.println("i="+i+","+"j="+j);
         //给类变量j赋值为39
         j = 39;
      }
      //类的static域,在类加载的时候最先运行得出结果,
      //将方法printInit("static Insect.x1 initialized")的返回值赋值给x1
      private static int x1 = printInit("static Insect.x1 initialized");
      //类的static方法,类加载的时候会执行。
      //在输出字符串s的同时,返回值47
      static int printInit(String s){
         //打印字符串s
         System.out.println(s);
         //返回值47
         return 47;
      }
   }
   //类Beetle继承类Insect
   public class Beetle extends Insect{
      //类的static域,在类加载的时候最先运行得出结果,
      //将方法printInit("Beetle.k initialized")的返回值赋值给k
      private int k = printInit("Beetle.k initialized");
      //构造方法
      public Beetle(){
         //打印k的值。
         System.out.println("k = "+k);
         //打印j的值,j是父类中的变量,在子类中并未重新赋值,所以结果还是父类中的值。
         System.out.println("j = "+j);
      }
      //类的static域,在类加载的时候最先运行得出结果,
      //将方法printInit("static Insect.x2 initialized")的返回值赋值给x2
      private static int x2 = printInit("static Beetle.x2 initialized");
      //程序执行入口main方法
      public static void main(String[] args) {
         //打印字符串"Beetle Constructor"
         System.out.println("Beetle Constructor");
         //创建类Beetle的对象。
         Beetle b = new Beetle();
      }
   }

结果示例:
结果示例!
main方法中的第一句代码是"System.out.println(“Beetle Constructor”);",按正常逻辑来看,结果中最先应该输出的是字符串"Beetle Constructor",但是结果中最先输出的是两个static的初始化内容:
⑴、static Insect.x1 initialized
⑵、static Beetle.x2 initialized

所以我们可以得出,static域的加载时机。
整个执行结果的顺序是:
1、先是父类Insect的static域。
2、然后是子类Beetle的static域。
加载完毕后…
紧接着是执行…
3、所以输出了字符串"Beetle Constructor"。
4、接着是父类Insect的构造方法。
5、然后是子类的类变量k。
6、最后是子类的构造方法。
下面是具体的解释:
PS:你需要知道的一个前提:构造方法也是static的,尽管static关键字并没有显式地写出来,所以更准确的说,类是在它的任何static成员被访问的时候加载的!
在类Beetle上运行Java代码时,所发生的第一件事情就是试图访问Beetle.main()(一个static方法),于是加载器开始启动并试图寻找出Beetle类的编译代码(Java是用一个独立的文件保存的,所以它在名为Beetle.class的文件中),在对它进行加载的过程中,编译器注意到它有一个父类(关键字extends),于是它继续进行加载,不管你是否需要产生一个父类的对象,这都会发生。
如果该父类还有其自身的父类,那么第二个父类就会被加载,以此类推!
接下来,根父类(以上示例中是类Insect)中的static域初始化代码会被立即执行,然后就是下一个子类的static域初始化,以此类推(这种方式很重要,因为子类的static域初始化可能会依赖于父类的成员能否被正确初始化)。
到此为止,必要的类都已经加载完毕,对象可以被创建了。
首先对象中的所有基本类型被会被设置为默认值,对象引用被设置为null–这些是通过将对象内存设置为二进制零值而一举生成的
然后,父类的构造方法会被调用(在上面的示例中,是被自动调用的,你也可以使用关键字super来显式调用父类的构造方法,正如类Beetle构造方法的第一步操作。),子类的构造方法和父类的构造方法一样,以相同的顺序来经历相同的过程,在父类构造方法完成之后,子类的实例变量按其代码的书写顺序被依次初始化,最后,构造方法的其余部分再执行。
实例变量的初始化是在构造方法之前执行的!
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值