zoukankan      html  css  js  c++  java
  • 【Java】代码块及Java对象的初始化顺序

    前言

    在程序编写之中可以直接使用{...}定义的一段语句就是代码块。根据代码块的位置以及关键字的不同可以分为4种:普通代码块、构造块、静态块以及同步代码块(多线程相关)。

    普通代码块

    写在方法里面的代码块就是普通代码块

    public static void main(String args[]){
      {
        int num = 0;
      }
      int num=100;
    }
    

    {...}表示的是一个作用域,内部定义的变量的可以起作用的范围仅在{...}这个范围内。

    上面代码中{int num=0;}的num在离开{...}后就被销毁了,于是可以在外部又可以定义int num=100

    若是写成以下:

    public static void main(String args[]){
      int num=100;
      {
        int num = 0;    //报错:Duplicate local variable num
      }
    }
    

    因为外部也存在num这个变量,且有效。所以,这样定义会出错。

    普通代码块的作用就是为了防止在方法中编写代码过多,产生变量重名,于是对一个方法中的代码进行局部的分割。但是建议一个方法中的代码不要太长,尽量不使用普通代码块。

    构造块

    如果将一个代码块放在类里面,那么就是一个构造块。构造块的作用是为了给对象进行初始化

    我们知道构造函数的作用也是为了给对象进行初始化,那么这两者有什么区别呢?

    public  class Student {
    	private String name;
    	private int age;
    
    //无参构造函数
    public Student() {
    	System.out.println("constructor with no args ");
    	System.out.println("name:"+this.name + " age:"+this.age);
    	this.name = "no name";
    	this.age = 18;
    }
    
    //有参构造函数
    public Student(String name, int age){
    	System.out.println("constructor with args");
    	System.out.println("name:"+this.name + " age:"+this.age);
    	this.name = name;
    	this.age = age;
    }
    
    //构造块
    {
    	System.out.println("constructor block ");
    	name = "cbname";
    	age = 20;
    }
    
    public static void main(String[] args) {
    	new Student();
    	System.out.println("==========");
    	new Student("sakura", 19);
    }
    /*
    output:
    constructor block
    constructor with no args
    
    name:cbname age:20
    ==========
    
    constructor block
    constructor with args
    name:cbname age:20
    */
    

    可以看出每次创建对象时,都会调用一次构造块,并且构造块的优先于构造函数执行。有对象的创建,才会调用构造快。

    构造块与构造函数的区别在于:每个对象被构造块初始化的那部分变量拥有的初始值是一样的,构造块对所有对象的效果是一样的。然而每个对象可能会使用不同构造函数,不同的构造函数初始化对象的方式是不同的

    静态块

    使用static修饰的代码块就叫做静态代码块或者直接叫静态块。

    前面在介绍static关键字时,介绍了一部分static修饰代码块的知识。

    • 静态块在类加载时执行,且只会执行一次执行顺序优先主函数、构造函数和构造块
    • 静态代码块主要用于初始化类中的static属性(类属性),而构造块是初始化对象中的属性
    • 一个类中可以有多个静态代码块, 执行顺序依照静态代码块的声明顺序。静态代码块可以在类的任意位置定义,在方法中不可以声明静态块

    创建对象的初始化顺序

    Java类的初始化顺序

    对于一个类(没有继承)的初始化情况

    public  class Student {
    	private String name="no name";
    	private int age=18;
    	private static int id=1;
    	//无参构造函数
    	public Student() {
    		System.out.println("======");
    		System.out.println("无参构造函数");
    		System.out.println("姓名:"+name+" 年龄:"+age);
    	}
    
    	//有参构造函数
    	public Student(String name, int age){
    		System.out.println("======");
    		System.out.println("有参构造函数");
    		System.out.println("姓名:"+this.name+" 年龄:"+this.age);
    		this.name = name;
    		this.age = age;
    		System.out.println("姓名:"+this.name+" 年龄:"+this.age);
    	}
    
    	//构造块
    	{
    		System.out.println("======");
    		System.out.println("构造块");
    		System.out.println("姓名:"+this.name+" 年龄:"+this.age);
    		this.name = "cbname";
    		this.age = 18;
    	}
    
    	//静态代码块
    	static {
    		System.out.println("======");
    		System.out.println("静态块");
    		System.out.println("静态变量id="+id);
    	}
    
    	public static void main(String[] args) {
    		System.out.println("======");
    		System.out.println("主方法");
    		new Student();
    		new Student("小王",20);
    	}
    }
    
    /*
    output:
    ======
    静态块
    静态变量id=1
    ======
    主方法
    ======
    构造块
    姓名:no name 年龄:18
    ======
    无参构造函数
    姓名:cbname 年龄:18
    ======
    构造块
    姓名:no name 年龄:18
    ======
    有参构造函数
    姓名:cbname 年龄:18
    姓名:小王 年龄:20
    */
    

    静态代码块、构造代码块、构造函数和主函数的执行顺序为:

    静态代码块>主函数>构造代码块>构造函数

    在加上静态属性、普通属性,他们的初始化执行顺序就为:

    静态变量、静态代码块 > 主函数 > 指定初始值的属性 > 构造代码块 > 构造函数

    对于有继承的情况

    class Person{
    	private String name="Person没有名字";
    	private int age=10;
    	private static int id=1;
    	//无参构造函数
    	public Person() {
    		System.out.println("======");
    		System.out.println("Person无参构造函数");
    		System.out.println("Person 姓名:"+this.name+" 年龄:"+this.age);
    	}
        //构造块
        {
            System.out.println("======");
            System.out.println("Person 构造块");
            System.out.println("Person 姓名:"+this.name+" 年龄:"+this.age);
            this.name = "pcbname";
            this.age =11 ;
        }
    
        //静态代码块
        static {
            System.out.println("======");
            System.out.println("Person 静态块");
            System.out.println("Person 静态变量id="+id);
        }
     }
    
    public  class Student extends Person{
    	private String name="Student没有名字";
    	private int age=18;
    	private static int id=2;
    	//无参构造函数
    	public Student() {
    		//自动调用父类的无参构造函数 super();
    		System.out.println("======");
    		System.out.println("Student无参构造函数");
    		System.out.println("Student 姓名:"+this.name+" 年龄:"+this.age);
    	}
        //有参构造函数
    public Student(String name, int age) {
    	//自动调用父类的无参构造函数 super();
    	System.out.println("======");
    	System.out.println("Student有参构造函数");
    	System.out.println("Student 姓名:"+this.name+" 年龄:"+this.age);
    	this.name = name;
    	this.age = age;
    }
    
        //构造块
        {
            System.out.println("======");
            System.out.println("Student 构造块");
            System.out.println("Student 姓名:"+this.name+" 年龄:"+this.age);
            this.name = "scbname";
            this.age = 19;
        }
    
        //静态代码块
        static {
            System.out.println("======");
            System.out.println("Student 静态块");
            System.out.println("Student 静态变量id="+id);
        }
    
        public static void main(String[] args) {
            System.out.println("======");
            System.out.println("主方法");
            System.out.println("
    --------第一次创建Studet对象--------");
            new Student();
            System.out.println("
    --------第二次创建Studet对象--------");
            new Student("小夏",20);
        }
    }
    /*
    ======
    
    Person 静态块
    
    Person 静态变量id=1
    ======
    
    Student 静态块
    
    Student 静态变量id=2
    ======
    
    主方法
    
    --------第一次创建Studet对象--------
    ======
    
    Person 构造块
    
    Person 姓名:Person没有名字 年龄:10
    ======
    
    Person无参构造函数
    
    Person 姓名:pcbname 年龄:11
    ======
    
    Student 构造块
    
    Student 姓名:Student没有名字 年龄:18
    ======
    
    Student无参构造函数
    Student 姓名:scbname 年龄:19
    
    --------第二次创建Studet对象--------
    ======
    
    Person 构造块
    
    Person 姓名:Person没有名字 年龄:10
    ======
    
    Person无参构造函数
    
    Person 姓名:pcbname 年龄:11
    ======
    
    Student 构造块
    
    Student 姓名:Student没有名字 年龄:18
    ======
    
    Student有参构造函数
    Student 姓名:scbname 年龄:19
    */
    

    观察代码结果,分析,对于有继承关系的类,初始化顺序按如下进行:

    1. 执行父类的静态代码块,并初始化父类静态成员变量
    2. 执行子类的静态代码块,并初始化子类静态成员变量
    3. 执行父类的构造代码块,执行父类的构造函数,若普通成员变量指定了初始值则先执行初始值的赋值,然后返回执行构造函数
    4. 执行子类的构造代码块,执行子类的构造函数,若普通成员变量指定了初始值则先执行初始值的赋值,然后返回执行构造函数

    小结

    本文介绍的三种代码块,普通块、构造块(在构造匿名内部类时可充当其构造函数使用)、静态块可以使用用于初始化静态变量。理清Java程序初始化的执行顺序可以很清楚地掌握程序的执行过程。对一个类而言,静态变量和静态代码块> 主函数 > 指定初始值的属性 > 构造代码块 > 构造函数。对于有继承的来说,父类静态块和静态变量 > 子类静态块和静态变量 > 父类指定初始值的属性 > 父类构造块 > 父类构造函数 > 子类指定初始值的属性 > 子类构造块 > 子类构造函数。

    参考:

    [1] Eckel B. Java编程思想(第四版)[M]. 北京: 机械工业出版社, 2007

    [2] 萌小Q. Java提高篇——静态代码块、构造代码块、构造函数以及Java类初始化顺序[EB/OL]. /2018-12-02. https://www.cnblogs.com/Qian123/p/5713440.html.

  • 相关阅读:
    File
    Include and Require
    Date and Time
    css3的nth-child选择器使用示例
    document.title
    php获取从百度搜索进入网站的关键词的详细代码
    Iphone手机、安卓手机浏览器控制默认缩放大小的方法
    离线宝调用
    织梦DedeCMS网站地图模板
    禁止选择文本和禁用右键 v3.0
  • 原文地址:https://www.cnblogs.com/myworld7/p/10053367.html
Copyright © 2011-2022 走看看