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);
    }
}
View Code

 

以下补充类的初始化过程,了解一下 类的初始化和创建类的实列区别  

饿汉式单例模式

当程序第一次访问单件模式实例时才进行创建。

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);
    }
    
}
View Code

注意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);
        
    }
    
}
View Code

解决办法: 在类中增加readResolve()方法

private Object readResolve(){
        return instance;
    }

如果一个类提供了readResolve()方法,重新指定反序列化的对象。

那么在执行反序列化操作时,先按照默认的方式或者用户自定义的方式进行反序列化,最后调用readResolve()方法,该方法返回的对象为反序列化的最终结果。

posted @ 2018-05-29 00:35  palapala  阅读(330)  评论(0编辑  收藏  举报