从一个简单的单例模式说java的类加载机制

在这段代码中,我们使用了一个简单的单例模式,定义了两个int类型的静态变量,其中x我们为它赋值位0,y则不做任何赋值,在Singleton的构造方法中分别对x,y ++,main方法中分别打印x,y值,可能大家已经猜到了结果,那么我们来验证下,

public class Singleton {

    private static Singleton instance = new Singleton();
    public Singleton() {
        x++;
        y++;
    }
    public static int x = 0;
    public static int y;
    public static Singleton getInstance() {
        return instance;
    }
    public static void main(String[] args) {
        Singleton instance = getInstance();
        System.out.println(instance.x);
        System.out.println(instance.y);
    }
}

同学们你的答案猜对了吗?

 

为什么会是0,1??

下面我们将

private static Singleton instance = new Singleton();

这段代码移动到下x,y值后面,输出结果又会是什么呢?小伙伴们不妨在猜下,

public class Singleton {

    public Singleton() {
        x++;
        y++;
    }
    public static int x = 0;
    public static int y;
    private static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
    public static void main(String[] args) {
        Singleton instance = getInstance();
        System.out.println(instance.x);
        System.out.println(instance.y);
    }
}

 

 

 要解释这个不同的输出结果,我们首先要了解类加载机制的三个步骤,

  1. 加载
  2. 链接  

2.1.验证

2.2. 准备

2.3. 解析

  1. 初始化

首先类按照顺序加载,比如说这段代码

private static Singleton instance = new Singleton();

如果在类中的第一行,就第一个被加载,
 然后是连接阶段,分为三个阶段
 1.验证
 文件格式的验证:魔术因子,版本号,字节流,常量池中的数据,常量的引用等等
 元数据的验证:是否被final修饰,是不是抽象类,是不是有父类,子类这些,重载的合法性,
 字节码验证:保持当前指针不会被其他类引用,程序计数器中的指令,不会跳转到不合法的字节码中
 符号验证:比如说引用类中的字段,方法,是否对当前的类课件等等,
 2.准备,当验证通过之后,便开始为类的静态变量赋初始值,
 3.解析,所谓解析就是指,刚刚验证,并且初始化的常量寻找这些常量的类,接口,方法的符号引用,并且将这些符号引用转换成直接引用
  最好初始化静态变量的值

 

在第一个代码片段中,首先加载的是构造方法,然后才是x,y,接着就是调用构造方法,然后是获取实例的方法,根据类加载的三个步骤,

首先是加载,类加载的最终的目的就是将java字节码数据,加载到jvm内存中,比如说大家都知道jvm的内存结构,最常用的两块就是堆内存和栈内存,那么加载阶段就是将这些数据加载到jvm内存中;

其次是链接,链接的过程上面基本已经交代了,需要注意的是,在链接阶段的准备和解析的过程中,x=0,这个0并不是=(等于的),而是x的默认值,需要在初始化阶段这个等号才会起作用,将0,赋值给x,所以当new Singleton()方法执行x++后,执行到了x=0,又将原来的x=1 赋值为了x=0

但是当我们将new Singleton()方法移动到x,y之后,为什么又变成了两个1呢,请小伙伴们思考思考,如果有答案了,可以@我,或者评论,其实文章中已经详细的解释了,相信各位小伙伴可以找到答案。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单例模式是一种常见的设计模式,可以确保一个类只有一个实例,并提供全局访问点。延时加载(Lazy Loading)是指在需要使用实例时才创建实例,以节省资源。下面是一个Java单例模式延时加载的实现。 ``` public class Singleton { private static Singleton instance = null; private Singleton() { // 私有构造函数 } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` 在上面的代码中,getInstance() 方法使用了 synchronized 关键字来确保线程安全。当第一次调用 getInstance() 方法时,如果 instance 为 null,则创建一个新的 Singleton 实例。如果 instance 已经被创建,则直接返回现有的实例。 由于延时加载的特性,这种实现方法可能存在一些性能问题,因为每次调用 getInstance() 方法都需要进行同步。如果 Singleton 实例的创建非常耗时,那么这种实现方法可能会导致性能瓶颈。为了避免这个问题,可以使用双重检查锁定(Double-Checked Locking)的方式来实现单例模式延时加载,如下所示。 ``` public class Singleton { private static volatile Singleton instance = null; private Singleton() { // 私有构造函数 } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` 在上面的代码中,getInstance() 方法中的双重检查锁定机制可以确保线程安全,并且只有在需要创建 Singleton 实例时才进行同步。关键字 volatile 可以确保 instance 变量在线程之间的可见性,从而避免了单例模式的另一个潜在问题——多线程环境下的指令重排序。 总之,单例模式的延时加载可以通过懒汉式或者双重检查锁定机制来实现。需要根据具体的场景选择适合的实现方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值