JDK设计模式之—单例模式和static关键字
首先了解static 关键字
static声明的方法是静态方法,static声明的成员变量为静态成员变量。对于该类的所有对象来说,static的成员变量和static只有一份存储空间
即使没有创建该类的对象(实列),也可以使用该类static的成员变量和static方法。
static所修饰的成员变量和静态代码块 当虚拟机对该类初始化的时候就会被创建,也就是 即使没有创建这个类的实列,java虚拟机也能根据类名在运行时数据区的方法内找到他们。(也就是说类的初始化和创建类的实列并不是一回事)
静态方法中不能使用this关键字
这个也很好理解。当一个对象创建好之后,java虚拟机就会给它分配一个引用自身的指针:this。也就是说 this是当前对象的引用。红字部分说了 是当前对象的引用,而执行static方法的时候 对象的实列可能还没被创建,所static方法中不能引用this关键字。同理 不止是this关键字,static方法中 不可访问任何非static的成员变量。同理 非static的成员变量 在类被初始化的时候才会被创建
为什么作为程序入口的main方法是static方法
把main()方法定义成static的静态方法,java虚拟机只要加载了main方法所属的类,就能执行main方法。而无需先创建这个类的实列
试想:如果main方法不是static的时候 运行main方法需要创建该类的实列,类的main是创建实列的入口。
补充:不管是静态方法,还是普通方法 他的字节码都位于方法区内。
为什么 static 成员变量 要和final一起用 public static final int xxx ;
因为static关键字修饰的成员变量,程序中只有一份。多个线程同时操作的时候 会产生并发的bug。final关键字 修饰的变量不能被更改
所以static final连用 可以防止静态变量 并发的bug。
static 也可有ThreadLocal连用 同样可以防止线程并发的bug
利用static关键字 实现单例模式(懒汉式)
在程序启动或单件模式类被加载的时候,单件模式实例就已经被创建。
2.1:类的构造函数定义为private的,保证其他类不能实例化此类,
2.2:然后提供了一个静态实例static XX xx= new XX()并返回给调用者。
它的好处是只在类初始化的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。
缺点也很明显,即使这个单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。
public class Singleton{ //类被搜索加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。 private static Singleton instance = new Singleton();//static的属性,是在类定义被JVM所加载时 就初始化完毕的,是类的所有实例共有的 private Singleton(){//私有的构造方法 } public static Singleton getInstance(){ return instance; } public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1==s2); } }
以下补充类的初始化过程,了解一下 类的初始化和创建类的实列区别
饿汉式单例模式
当程序第一次访问单件模式实例时才进行创建。
public class Singleton{ private static Singleton instance ; private Singleton(){ } public static synchronized Singleton getInstance(){ if(instance==null){ instance = new Singleton(); } return instance; } public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1==s2); } }
注意synchronized关键字的使用,防止因为并发导致创建多个对象
反序列化打破单例模式
反序列化会创建一个对象,打破了单例只有一个实列的约定。
public class Singleton implements Serializable{ private static final long serialVersionUID = 1L; private static Singleton instance ; private Singleton(){ } public static synchronized Singleton getInstance(){ if(instance==null){ instance = new Singleton(); } return instance; } public static void main(String[] args) throws Exception { Singleton s1 = Singleton.getInstance(); ByteArrayOutputStream buf = new ByteArrayOutputStream(); ObjectOutputStream o = new ObjectOutputStream(buf); o.writeObject(s1); ObjectInputStream in = new ObjectInputStream( new ByteArrayInputStream(buf.toByteArray())); Singleton s2 = (Singleton) in.readObject(); System.out.println(s1==s2); } }
解决办法: 在类中增加readResolve()方法
private Object readResolve(){ return instance; }
如果一个类提供了readResolve()方法,重新指定反序列化的对象。
那么在执行反序列化操作时,先按照默认的方式或者用户自定义的方式进行反序列化,最后调用readResolve()方法,该方法返回的对象为反序列化的最终结果。