Java初始化块及执行过程解析

问题:Java对象初始化方式主要有哪几种?分别是什么?

针对上面的问题,想必大家脑海中首先浮现出的答案是构造器,没错,构造器是Java中常用的对象初始化方式。

还有一种与构造器作用非常相似的是初始化块,它可以对Java对象进行初始化操作。下面主要阐述Java的初始化块及执行过程。

Java初始化块其实是Java类的成员之一,其语法格式如下:

[修饰符]{

  //初始化块的可执行代码

  ...

}

初始化块的修饰符只能是static,使用static修饰符的初始化块称为静态初始化块,后面会介绍到。

下面通过一段程序代码看看创建对象Dog时发生了什么。

public class Dog {
  //定义一个初始化块
  {
    System.out.println("第一个初始化块");
  }
  //定义第二个初始化块
  {
    System.out.println("第二个初始化块");
  }
  //定义无参的构造器
  public Dog() {
    System.out.println("Dog的无参构造器");
  }
  public static void main(String[] args) {
    new Dog();
  }
}

运行结果为:

第一个初始化块
第二个初始化块
Dog的无参构造器

从运行结果可以看出:当创建一个Dog对象时,程序先执行初始化块后执行构造器,而且两个初始化的执行顺序是按照前后顺序执行的。

由于初始化块只在创建Java对象时隐士的执行(所有的初始化块全部执行),为了让程序更加的简洁和可读性更强,一般一个类里最多只有一个初始化块。

既然初始化块和构造器都是创建对象的时候执行,那么它们有什么区别呢?

从某种程度上来看,初始化块是构造器的补充,与构造器不同的是,初始化块是一段固定执行的代码,不能接收任何的参数,而构造器是可以接收参数的。如果程序中有两个构造器,并且它们有公共的无需接收参数的代码,则可以把这些相同的公共代码放到初始化块中,提高代码的可维护性。

当普通初始化块被static修饰时,则这个初始化块就是静态初始化块,也被称为类初始化块。

普通初始化块负责对对象进行初始化,而静态初始化块负责对类进行初始化,所以静态初始化块总是比普通初始化块先执行。

静态初始化块通常用于对类变量执行初始化处理,不能对实例变量进行初始化处理。

静态初始化块也是类的静态成员,仍然需要遵循静态成员不能访问非静态成员的规则,包括不能访问实例变量和实例方法。

初始化块执行的时候会一直上溯到java.lang.Object类,先执行Object的初始化块,再执行其父类的初始化块...最后才执行该类自己的初始化块。

下面看下这段代码的执行顺序结果:

class Biology{
  static {
    System.out.println("Biology的静态初始化块");
  }
  {
    System.out.println("Biology的普通初始化块");
  }
  public Biology(){
    System.out.println("Biology的无参构造器");
  }
}
class Animal extends Biology{
  static {
    System.out.println("Animal的静态初始化块");
  }
  {
    System.out.println("Animal的普通初始化块");
  }
  public Animal(){
    System.out.println("Animal的无参构造器");
  }
  public Animal(String name){
    this();//调用同类中的重载构造器
    System.out.println("Animal的带参构造器,name="+name);
  }
}
class Cat extends Animal{
  static {
    System.out.println("Cat的静态初始化块");
  }
  {
    System.out.println("Cat的普通初始化块");
  }
  public Cat(){
    //调用父类中带参数的构造器
    super("Java典籍");
    System.out.println("Cat的无参构造器");
  }
}
public class Test {
  public static void main(String[] args) {
    new Cat();
    System.out.println("=========");
    new Cat();
  }
}

执行结果如下:

Biology的静态初始化块
Animal的静态初始化块
Cat的静态初始化块
Biology的普通初始化块
Biology的无参构造器
Animal的普通初始化块
Animal的无参构造器
Animal的带参构造器,name=Java典籍
Cat的普通初始化块
Cat的无参构造器
=========
Biology的普通初始化块
Biology的无参构造器
Animal的普通初始化块
Animal的无参构造器
Animal的带参构造器,name=Java典籍
Cat的普通初始化块
Cat的无参构造器

从上面的执行结果来看,第一次创建Cat对象时,由于系统还未有Cat对象,因此需要先加载并初始化Cat类,初始化Cat类会先执行其顶层父类的静态初始化块,再执行其直接父类的静态初始化块,最后才执行其本身的静态初始化块。

一旦Cat类初始化成功后,其在虚拟机中一直存在,当第二次再创建Cat对象时 无需再对Cat类进行初始化。

综上,一个类初始化的执行顺序为:父类静态初始化块,该类静态初始化块,父类普通初始化块,父类构造器,该类普通初始化块,该类构造器。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

时间: 2019-09-15

Java编程中二维数组的初始化和基本操作实例

Java二维数组的声明和初始化 1.二维数组可以看成以数组为元素的数组: 2.Java中二维数组的声明和初始化应按照从高维到低维的顺序进行. 实例: 静态初始化: Array2.java: 程序代码 public class Array2 { public static void main(String args[]) { int a[][] = {{1,2},{3,4,5,6},{7,8,9}} ; for(int i=0 ; i <a.length ; i++) { for(int j=0

Java 8跳过本次循环,继续执行以及跳出循环,终止循环的代码实例

在Java8之前,最开始使用for i 循环,很老旧,后来有了高级的for each 循环,然后这个跳出本次循环和跳出所有的for循环,都简单,稍微没见过的就是跳出多层for循环. 然后就是Java8出的foreach循环,这个循环里面,break和continue都不管用啦.需要使用return,这个只能跳过本次循环,还是会继续执行for循环的,那么怎么跳出这个Java8的foreach循环呢? 下面对所有的循环,都来了一次操作. 看看如何,跳出当前循环,继续执行:或者直接跳出for循环:或者

Java中List与Map初始化的一些写法分享

Java的在还没有发现新写法之前时,我一直是这么初始化List跟Map: 复制代码 代码如下: //初始化List    List<string> list = new ArrayList</string><string>();    list.add("www.jb51.net");    list.add("string2");    //some other list.add() code......    list.add

Java中初始化List的5种方法示例

前言 List是java重要的数据结构之一,我们经常接触到的有ArrayList.Vector和LinkedList三种,他们都继承来自java.util.Collection接口,类图如下 Java 中经常需要使用到 List,下面简单介绍几种常见的初始化方式. 1.构造 List 后使用 List.add 初始化 这是最常规的做法,用起来不太方便. 2.使用 {{}} 双括号语法 这种方式相对方便了一些. 外层的{}定义了一个 LinkedList 的匿名内部类.内层的{}的定义了一个实例初

java 中数组初始化实例详解

1.数组初始化 定义数组的方式: int[] arr1; 或  int arr1[]; 数组初始化 通过上边的定义,我们只是得到了一个数组的引用.这时已经为引用分配了存储空间,但是还没有给数组对象本身分配任何空间.想要给数组对象分配存储空间,必须使用初始化表达式. a.在数组创建的地方进行初始化,如: int[] arr1 = {1,2,3,4,5}; 这种方式等价于使用new来进行存储空间分配. b.给数组的引用赋值,如: int[] arr1 = {1,2,3,4,5}; int[] arr

Java中static静态变量的初始化完全解析

静态变量初始化顺序 1.简单规则 首先先看一段最普遍的JAVA代码: public class Test { public static Test1 t = new Test1(); public static int a = 0; public static int b; public static void main(String[] arg) { System.out.println(Test.a); System.out.println(Test.b); } } class Test1

java调用shell命令并获取执行结果的示例

使用到Process和Runtime两个类,返回值通过Process类的getInputStream()方法获取 package ark; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; public class ReadCmdLine { public st

浅谈Java变量的初始化顺序详解

规则1(无继承情况下):对于静态变量.静态初始化块.变量.初始化块.构造器,它们的初始化顺序依次是(静态变量.静态初始化块)>(变量.初始化块)>构造器证明代码: 复制代码 代码如下: public class InitialOrderTest {    // 静态变量    public static String staticField = "静态变量";    // 变量    public String field = "变量";    // 静

浅谈JAVA中输入输出流实例详解

java语言的输入输出功能是十分强大而灵活的,美中不足的是看上去输入输出的代码并不是很简洁,因为你往往需要包装许多不同的对象.在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流....本文的目的是为大家介绍JAVA中输入输出流实例详解. 流的层次结构 定义:        java将读取数据对象成为输入流,能向其写入的对象叫输出流.结构图如下: 1.输入输出: 输入/输出(Input/Output)是指对某

浅谈js-FCC算法Friendly Date Ranges(详解)

让日期区间更友好! 把常见的日期格式如:YYYY-MM-DD 转换成一种更易读的格式. 易读格式应该是用月份名称代替月份数字,用序数词代替数字来表示天 (1st 代替 1). 记住不要显示那些可以被推测出来的信息: 如果一个日期区间里结束日期与开始日期相差小于一年,则结束日期就不用写年份了.月份开始和结束日期如果在同一个月,则结束日期月份就不用写了. 另外, 如果开始日期年份是当前年份,且结束日期与开始日期小于一年,则开始日期的年份也不用写. 我的代码: function makeFriendl

浅谈AngularJs指令之scope属性详解

AngularJS使用directive()方法类定义一个指令: .directive("name",function(){ return{ }; }) 上面是定义一个指令的主体框架,该方法接受两个参数: 1.第一个参数:name表示定义的指令的名称(angularjs会用这个name注册这个指令) 2.第二个参数:函数,该番薯必须返回一个对象或者一个函数,但通常我们会返回一个对象.return后接的就是返回的对象. 在返回的对象中有一个scope属性,这个属性用来修饰指令的作用域.

java类中元素初始化顺序详解

复制代码 代码如下: public class Test4 {    @Test    public void test(){        child child = new child();    }} class parent{    public static String parentStaticField = "父类静态变量";    public String parentNormalField ="父类普通变量";    static {      

浅谈Java变量赋值运算符及相关实例 原创

Java程序中,需要进行大量的计算,所以要使用到运算符号,下面来给大家说明Java赋值运算符如何运用. 赋值运算符以符号"="表示,它是一个二元运算符(对两个操作数作处理),其功能是将右方操作数所含的值赋给左方操作数.例如: int a = 100; 该表达式是将100赋值给变量 a .左方的操作数必须是一个变量 ,而右边的才做书则可以是任何表达式,包括变量(如 a .number).常量(123.'book').有效表达式(如55*66).         1:使用赋值运算符为变量赋

C++构造函数初始化顺序详解

1.构造函数.析构函数与拷贝构造函数介绍 构造函数 1.构造函数不能有返回值 2.缺省构造函数时,系统将自动调用该缺省构造函数初始化对象,缺省构造函数会将所有数据成员都初始化为零或空 3.创建一个对象时,系统自动调用构造函数 析构函数 1.析构函数没有参数,也没有返回值.不能重载,也就是说,一个类中只可能定义一个析构函数 2.如果一个类中没有定义析构函数,系统也会自动生成一个默认的析构函数,为空函数,什么都不做 3.调用条件:1.在函数体内定义的对象,当函数执行结束时,该对象所在类的析构函数会被

浅谈Nginx10m+高并发内核优化详解

何为高并发 默认的Linux内核参数考虑的是最通用场景,不符合用于支持高并发访问的Web服务器,所以需要修改Linux内核参数,这样可以让Nginx拥有更高的性能: 在优化内核时,可以做的事情很多,不过,我们通常会根据业务特点来进行调整,当Nginx作为静态web内容服务器.反向代理或者提供压缩服务器的服务器时,期内核参数的调整都是不同的,这里针对最通用的.使Nginx支持更多并发请求的TCP网络参数做简单的配置: 这些需要修改/etc/sysctl.conf来更改内核参数. 配置方法 配置详析

Java方法参数装配顺序详解

自动化装配的确有很大的便利性,但是却并不能适用在所有的应用场景,比如需要装配的组件类不是由自己的应用程序维护,而是引用了第三方的类库,这个时候自动装配便无法实现,Spring对此也提供了相应的解决方案,那就是通过显示的装配机制--Java配置和XML配置的方式来实现bean的装配. 从左到右依次装配,参数的值一旦确定,即使后面修改了该值,方法拿到的值也不会随之变化了. 代码如下 class Solution { public int a; @Override public String toSt