java 单例模式
java单例模式
今天和同事在项目中用了一下单例模式,闲下来了解了一下单例模式的实现方式。参考了 http://cantellow.iteye.com/blog/838473 这篇博客。了解到主要有懒汉模式和饿汉模式两种。前者实现了我们所知道的lazy load。
在这里我选了两种实现方式自己实现了一下。
饿汉
public class Singleton {
public static String NAME = "Singleton";
private static Singleton singleton = new Singleton();
private Singleton() {
System.out.println("initial Singleton");
}
public static Singleton getSingleton() {
return singleton;
}
}
懒汉
public class InnerSingleton {
public static String NAME = "InnerSingleton";
private static class Singleton {
private static final InnerSingleton INNER_SINGLETON = new InnerSingleton();
}
private InnerSingleton() {
System.out.println("initial InnerSingleton");
}
public static InnerSingleton getSingleton() {
return Singleton.INNER_SINGLETON;
}
}
思考
看了类加载和类初始化的一些相关知识,参考 https://www.cnblogs.com/zhguang/p/3154584.html
不管使用什么样的类加载器,类都是在第一次被用到时,动态加载到JVM的。这句话有两层含义: Java程序在运行时并不一定被完整加载,只有当发现该类还没有加载时,才去本地或远程查找类的.class文件并验证和加载;
当程序创建了第一个对类的静态成员的引用(如类的静态变量、静态方法、构造方法——构造方法也是静态的)时,才会加载该类。Java的这个特性叫做:动态加载。
需要区分加载和初始化的区别,加载了一个类的.class文件,不以为着该Class对象被初始化,事实上,一个类的初始化包括3个步骤:加载(Loading),由类加载器执行,查找字节码,并创建一个Class对象(只是创建); 链接(Linking),验证字节码,为静态域分配存储空间(只是分配,并不初始化该存储空间),解析该类创建所需要的对其它类的应用; 初始化(Initialization),首先执行静态初始化块static{},初始化静态变量,执行静态方法(如构造方法)。
类初始化的时机分以下几种情况
使用new关键字实例化对象的时候
读取或设置一个类的静态字段(被final修饰,已在编译期把结果放入常量池的静态字段除外, 举个例子?)
调用一个类的静态方法
使用java.lang.reflect包的方法对类进行反射调用的时候
当初始化一个类的时候,如果发现其父类还没有进行初始化,需要先初始化其父类
虚拟机启动时要执行的主类(包含main()方法的类)
当我把类加载和初始化分开来看时,我发现不论是饿汉模式还是懒汉模式都是在调用getInstance方法时,才会将构造器里的话打印出来,这样看是否都是懒汉模式呢?现在看起来实例都是在类初始化的时候创建的。留有疑问。
更新 解答疑问
public static void main(String[] args) {
String a = Singleton.NAME;
String b = InnerSingleton.NAME;
}
可以使用这种方法触发类的装载,在这里类的装载=加载+初始化。使用上述方法的结果如下图所示:
而如果只装载类,利用classloader,两种方法是都不会打印出来的。可是如果利用classforname方法,是和上述截图的结果一致的。这是因为,classforname除了会将类加载还会对static变量进行初始化,参考 https://blog.csdn.net/qq_27093465/article/details/52262340