5、接口初始化规则和类加载、连接、初始化案例剖析

5.1、接口初始化规则

在了解接口的初始化规则前,先搞清类的初始化规则。

当java虚拟机初始化一个类时,会先初始化它的所有父类。但是这条规则并不适用于接口。

  • 在初始化一个类时,并不会先初始化它所实现的接口;
  • 在初始化一个接口时,并不会先初始化它的父接口;

  使用一句话总结:实现类或者子接口的初始化并不会导致父接口的初始化。

  只有当程序首次主动使用特定接口的静态变量(运行期常量)时,才会导致接口的初始化。这里需要注意的是接口中定义的变量都是常量,而常量又分为编译期常量和运行期常量,编译期常量的值在编译期间就可以确定,直接存储在了调用类的常量池中,所以访问接口中的编译期常量并不会导致接口的初始化,只有访问接口中的运行期常量才会引起接口的初始化。

下面通过代码验证上述结论:

package com.shtec.init;

import java.util.UUID;

/**
 * 在初始化一个类时,并不会先初始化它所实现的接口;
 * 在初始化一个接口时,并不会先初始化它的父接口;
 * @author sunhao
 *
 */
public class Demo3 {

    public static void main(String[] args) {
        //在初始化一个类时,并不会先初始化它所实现的接口;
        System.out.println(child3.a);
        //输出: 1
        //没有输出“Parent3 invoked”,说明父接口Parent3并没有被初始化。

        //在初始化一个接口时,并不会先初始化它的父接口;
        System.out.println(Parent3.str);
        //输出:
        //Parent3 invoked
        //a58ee35a-6590-416d-bf6b-ba6976f0d268
    }

}
interface GrandPa{
    //下面这段代码相当于普通类中的“静态代码块”,打印出来就相当于该接口被初始化了;
    public static Thread t = new Thread(){
        {
            System.out.println("GrandPa invoked");
        }
    };
}
interface Parent3 extends GrandPa{
    //运行期常量
    String str = UUID.randomUUID().toString();
    //下面这段代码相当于普通类中的“静态代码块”,打印出来就相当于该接口被初始化了;
    public static Thread t = new Thread(){
        {
            System.out.println("Parent3 invoked");
        }
    };
}
class child3 implements Parent3{
    public static int a = 1;
}

5.2、类加载、连接、初始化案例剖析

下面使用两个案例复习一下类型的加载、连接和初始化过程;

package com.shtec.init;

public class Demo4 {

    public static void main(String[] args) {
        System.out.println("a=" + Singleton.a);
        System.out.println("b=" + Singleton.b);
        //输出:
        //a=1
        //b=1
    }

}
//定义一个单例类
class Singleton{

    public static int a;
    public static int b = 0;
    private static Singleton singleton = new Singleton();

    private Singleton(){
        a++;
        b++;
    }
    public static Singleton getInstance(){
        return singleton;
    }
}

  如果对上面Demo4的代码输出没有疑问,请继续下面的代码示例:

package com.shtec.init;

/**
 * 分析:
 *         将代码【public static int b = 0;】放在构造方法下面,输出结果为什么变了?
 *         请注意使用“类的加载、连接(验证、准备和解析)和初始化过程分析该案例”;
 *         Singleton在初始化之前,会先经历连接阶段中的准备阶段,准备阶段会为类的【静态变量】分配内存,并将其初始化为【默认值】,
 *                 此时,a=0;singleton=null,b=0;
 *         然后Singleton经历初始化阶段,代码自上而下执行,为类的静态变量赋予正确的初始值
 *                 此时,a=0;
 *                     singleton会创建一个对象,调用Singleton类的构造方法,此时a=1;b=1;
 *                 接着初始化静态变量b,b的值又被更改为0;
 *         所以最终输出:
 *                 //a=1
 *                //b=0
 * @author sunhao
 *
 */
public class Demo4 {

    public static void main(String[] args) {
        System.out.println("a=" + Singleton.a);
        System.out.println("b=" + Singleton.b);
        //输出:
        //a=1
        //b=0
    }

}
//定义一个单例类
class Singleton{

    public static int a;

    private static Singleton singleton = new Singleton();

    private Singleton(){
        a++;
        b++;
    }
    public static int b = 0;//注意,本段代码调动了位置

    public static Singleton getInstance(){
        return singleton;
    }
}

原文地址:https://www.cnblogs.com/sunhao1234/p/12333791.html

时间: 2024-10-07 15:20:25

5、接口初始化规则和类加载、连接、初始化案例剖析的相关文章

类加载,初始化

java虚拟机与程序的生命周期 1.java虚拟机结束生命周期的原因 1.执行system.exit()方法. 2.程序正常执行结束. 3. 程序在执行过程中遇到了异常或错误终止. 4.由于操作系统出现错误而导致java虚拟机进程终止. 2.类的加载.连接与初始化 加载:查找并加载类的二进制数据.类的加载时将类的.class文件中的二进制数据读入到内存中,将其存放到数据区的方法区中,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.加载.class文件的方式:

JAVA类加载和初始化

Java程序运行由java虚拟机负责.类从加载到虚拟机内存到卸载出内存,包括 加载-----链接-----初始化-----使用------卸载 链接具体包括:验证-----准备-----解析 加载:由类加载器执行,查找字节码并从这些字节码中创建一个Class对象. 链接:验证类中的字节码:为静态域分配存储内存并赋予默认值:解析这个类创建的对其他类的所有引用. 初始化:该类具有基类,则对其初始化,执行静态初始化和静态初始化块. 类初始化的时机:程序中首次使用才初始化. 首次主动使用: 1.    

CS231n 卷积神经网络与计算机视觉 6 数据预处理 权重初始化 规则化 损失函数 等常用方法总结

1 数据处理 首先注明我们要处理的数据是矩阵X,其shape为[N x D] (N =number of data, D =dimensionality). 1.1 Mean subtraction 去均值 去均值是一种常用的数据处理方式.它是将各个特征值减去其均值,几何上的展现是可以将数据的中心移到坐标原点,Python中的代码是 X -= np.mean(X, axis = 0). 对于图像处理来说,每个像素的值都需要被减去平均值 ( X -= np.mean(X)), 也可以分别处理RGB

【转载】Java系列笔记(1) - Java 类加载与初始化

Java系列笔记(1) - Java 类加载与初始化 原文地址:http://www.cnblogs.com/zhguang/p/3154584.html 目录 类加载器 动态加载 链接 初始化 示例 类加载器 在了解Java的机制之前,需要先了解类在JVM(Java虚拟机)中是如何加载的,这对后面理解java其它机制将有重要作用. 每个类编译后产生一个Class对象,存储在.class文件中,JVM使用类加载器(Class Loader)来加载类的字节码文件(.class),类加载器实质上是一

java类加载与初始化

第一段: class A{ public A(){ this.list(); } public void list(){ System.out.println("in a list.."); } } class B extends A{ private final static B instance  = new B(); //这里会调用list,是在还没构造结束的时候就调用了,但这里不会错 public static B getInstance(){ System.out.print

C++ 初始化形式、变量初始化规则、类构造函数的初始化列表

类构造函数的初始化列表,举例 一个对象的构造分两部分,首先是分配空间,然后初始化. 只要有对象生成,不管是以什么形式生成,都会调用构造函数进行初始化. 然后下面有个例子,在蓝色区域Big类的复制构造函数中,使用初始化列表进行成员的初始化(方法1)没有问题,而如果不使用初始化列表.直接在函数里用里面注释掉的代码(方法2)则会报错:Base类没有合适的构造函数. // W3-课程作业2-4.cpp : Defines the entry point for the console applicati

C++中不同变量的初始化规则

当定义没有初始化式的变量(如int i;)时,系统有可能会为我们进行隐式的初始化.至于系统是否帮我们隐式初始化变量,以及为变量赋予一个怎样的初始值,这要取决于该变量的类型以及我们在何处定义的该变量. 1]内置类型变量的初始化     内置变量是否自动初始化,取决于该变量定义的位置.     ①在全局范围内的内置类型变量均被编译器自动初始化为0. #include<iostream> using namespace std; //全局范围内的部分内部变量 int gi; //被自动初始化为0 f

C++ 变量初始化规则

 定义没有初始化式的变量时,系统有时候会帮我们初始化变量.系统如何初始化取决于变量的类型以及变量定义的位置. 内置类型变量是否自动初始化取决于变量定义的位置.函数体外定义的变量初始成0:函数体内定义的变量不进行自动初始化.除了用作赋值操作的左操作数,其他任何使用未初始化变量的行为都是未定义的,不要依赖未定义行为. 以int类型为例,一段简单的测试代码: #include <iostream> using namespace std; int a; int main() { int b; cou

c++ 变量定义 的初始化规则

当定义没有初始化式的变量(如int i;)时,系统有可能会为我们进行隐式的初始化.至于系统是否帮我们隐式初始化变量,以及为变量赋予一个怎样的初始值,这要取决于该变量的类型以及我们在何处定义的该变量.            1]内置类型变量的初始化           内置变量是否自动初始化,取决于该变量定义的位置.           ①在全局范围内的内置类型变量均被编译器自动初始化为0值 1 #include<iostream> 2 3 using namespace std; 4 5 //