目录
1、初识初始化块
Java 使用构造器来对单个对象进行初始化操作,使用构造器先完成对整个 Java 对象的状态初始化,然后将 Java 对象返回给程序,从而让该 Java 对象的信息更加完整。
与构造器作用非常类似的是初始化块,它也可以对 Java 对象进行初始化操作。
- 初始化块定义的格式:
[修饰符]{
// 代码
}
代码
public class Demo02 {
// 实例初始化块
{
System.out.println("实例初始化块执行了...");
}
// 类初始化块
static {
System.out.println("类初始化块执行了...");
}
// 构造器
public Demo02() {
System.out.println("无参构造器执行了...");
}
public static void main(String[] args) {
new Demo02();
System.out.println("------------------");
new Demo02();
}
}
结果
类初始化块执行了...
实例初始化块执行了...
无参构造器执行了...
------------------
实例初始化块执行了...
无参构造器执行了...
说明
- 一个类里可以有多个初始化块,相同类型的初始化块之间有顺序:先定义的先执行,后定义的后执行。
- 初始化块如果有修饰符的话,只能是 static。
- 使用 static 修饰的初始化块称为类初始化块(静态初始化块)。
- 没有 static 修饰的初始化块称为实例初始化块(非静态初始化块)
- 初始化块里的代码可以包含任何可执行性语句,包括定义局部变量、调用其他对象的方法、以及使用分支、循环语句等。
- 初始化块虽然是 Java 类的一种成员,但它没有名字、标识,因此无法通过类、对象来调用初始化块。
- 实例初始化块只在创建 Java 对象时隐式执行,而且在构造器之前自动执行。
- 类初始化块则在类初始化阶段自动执行。
注:
虽然一个类里允许定义 2 个实例初始化块,但这没有任何意义。
因为实例初始化块是在创建对象时隐式调用的,而且它们总是全部执行,因此完全可以把多个实例初始化块合并成一个实例初始化块,让程序简洁、可读性强。
2、实例初始化块和构造器
实例初始化块、声明实例变量时指定的默认值都可认为是对象初始化代码,它们的执行顺序与源程序中的排列顺序相同。
public class Demo01 {
// 声明实例变量的默认值在前
public int a = 10;
// 实例初始化块在后
{
a = 20;
}
public Demo01() {
}
public static void main(String[] args) {
Demo01 demo01 = new Demo01();
System.out.println(demo01.a); // 20
}
}
public class Demo01 {
// 实例初始化块在前
{
a = 20;
}
// 声明实例变量的默认值在后
public int a = 10;
public Demo01() {
}
public static void main(String[] args) {
Demo01 demo01 = new Demo01();
System.out.println(demo01.a); // 10
}
}
说明
当 java 创建一个对象时,系统先为该对象的所有实例变量分配内存(前提是该类已经被加载过了),接着程序开始对这些实例变量执行初始化。
执行初始化的顺序是:先执行实例初始化块 或 声明实例变量时指定的默认值(执行顺序与它们在源码中的排列顺序相同),再执行构造器中指定的初始值。
与构造器不同的
实例初始化块是一段固定执行的代码,无法接收任何参数,因此实例初始块对一个类中的所有对象执行的初始化操作是相同的。
如何使用
如果有一段初始化处理代码中对所有对象完全相同,且无须接收任何参数,就可以把这段实例初始化代码提取到实例初始化块中。
对实例初始化块的分析
实力初始化块是一个假象,在编译之后的字节码文件中,实例初始化块会消失——实例初始化块会被 “还原” 到每个构造器中,且位于构造器所有代码的前面。
Demo02.java
public class Demo02 {
// 实例初始化块
{
System.out.println("实例初始化块执行了...");
}
// 类初始化块
static {
System.out.println("类初始化块执行了...");
}
// 构造器
public Demo02() {
System.out.println("无参构造器执行了...");
}
public static void main(String[] args) {
new Demo02();
System.out.println("------------------");
new Demo02();
}
}
编译之后的字节码文件 Demo02.class
public class Demo02 {
public Demo02() {
System.out.println("实例初始化块执行了...");
System.out.println("无参构造器执行了...");
}
public static void main(String[] args) {
new Demo02();
System.out.println("------------------");
new Demo02();
}
static {
System.out.println("类初始化块执行了...");
}
}
与构造器类似,创建一个对象时,不仅会执行该类的实例初始化块和构造器,而且系统会先执行其父类中的实例初始化块(如果有)和构造器,一直追溯到祖宗类 Object 类,先执行 Object 类的实例初始化块(如果有)、构造器,最后才执行该类的实例初始化块(如果有)和构造器,返回该类的对象。
代码
class Person {
{
System.out.println("Person-->实例初始化块");
}
public Person() {
System.out.println("Person-->构造器");
}
}
class Student extends Person {
{
System.out.println("Student-->实例初始化块");
}
public Student() {
System.out.println("Student-->构造器");
}
public static void main(String[] args) {
new Student();
}
}
结果
Person-->实例初始化块
Person-->构造器
Student-->实例初始化块
Student-->构造器
如果希望类加载后对整个类(类变量)进行某些初始化操作,就需要使用 static 关键字来修饰初始化块。
3、类初始化块
实例初始化块负责对对象执行初始化;类初始化块负责对类初始化。
系统在类初始化阶段执行类初始化块,而不是创建对象时执行,因此类初始化块总是比实例初始化块先执行。
当 JVM 第一次主动使用某个类时,系统会在类准备阶段为该类的所有类变量分配内存;在初始化阶段负责初始化类变量。
类初始化块、声明类变量时指定的默认值都可认为是类初始化代码,它们的执行顺序与源程序中的排列顺序相同。
public class Demo01 {
public static String name = "java";
static {
name = "JAVA";
}
public static void main(String[] args) {
System.out.println(Demo01.name); // JAVA
}
}
public class Demo01 {
static {
name = "JAVA";
}
public static String name = "java";
public static void main(String[] args) {
System.out.println(Demo01.name); // java
}
}
注:
类初始化块(静态初始化块)属于类的静态成员,同样遵循静态成员不能访问非静态成员的规则。
因此类初始化块不能访问非静态成员(实例变量、实例方法)
与实例初始化块类似的是,系统在类初始化阶段执行类初始化块时,不仅会执行本类的类初始化块,而且还会一直上溯到 Object 类的类初始化块(如果有),先执行 Object 的类初始化块(如果有),然后执行其父类的类初始化块 ……,最后在执行本类的类初始化块。
只有当类初始化完成之后,才可以在系统中使用这个类(类变量、类方法、创建对象)
Java 系统加载并初始化某个类时,总是保证该类的所有父类(直接父类或间接父类)全部加载并初始化。
Root
public class Root {
static {
System.out.println("Root-->静态初始化块");
}
{
System.out.println("Root-->实例初始化块");
}
public Root() {
System.out.println("Root-->无参构造器");
}
}
Mid
public class Mid extends Root{
static {
System.out.println("Mid-->静态初始化块");
}
{
System.out.println("Mid-->实例初始化块");
}
public Mid() {
System.out.println("Mid-->无参构造器");
}
public Mid(String msg) {
// 调用同一个类中的无参构造器
this();
System.out.println("Mid-->有参构造器,参数:" + msg);
}
}
Leaf
public class Leaf extends Mid{
static {
System.out.println("Leaf-->静态初始化块");
}
{
System.out.println("Leaf-->实例初始化块");
}
public Leaf() {
super("java");
System.out.println("Leaf-->无参构造器");
}
}
Application
public class Application {
public static void main(String[] args) {
new Leaf();
System.out.println("-------------------");
new Leaf();
}
}
结果
Root-->静态初始化块
Mid-->静态初始化块
Leaf-->静态初始化块
Root-->实例初始化块
Root-->无参构造器
Mid-->实例初始化块
Mid-->无参构造器
Mid-->有参构造器,参数:java
Leaf-->实例初始化块
Leaf-->无参构造器
-------------------
Root-->实例初始化块
Root-->无参构造器
Mid-->实例初始化块
Mid-->无参构造器
Mid-->有参构造器,参数:java
Leaf-->实例初始化块
Leaf-->无参构造器
标签:初始化,Java,--,System,实例,println,public
From: https://www.cnblogs.com/sunzhongjie/p/17113621.html