单例形式的学习,设计形式之单例情势

第二章饿汉式(上来就创建私有的本类对象)

/*

*

* 单例设计模式:保证类在内存中只有一个对象

*/

public static void main(String[] args) {

// Singleton s1= new Singleton(); //3.由于私有构造方法,类外无法直接new
对象

/*Singleton s1= Singleton.s;

Singleton s2= Singleton.s;

System.out.println;*/

//上述两行会导致能够修改s的值所以得私有成员变量

//5.重新通过get方法获得成员变量

Singleton s1= Singleton.getInstance();

Singleton s2= Singleton.getInstance();

System.out.println;

}

}

/*

* 饿汉式

*/

class Singleton{

//1.私有构造方法,类外无法直接new 对象,其他类无法访问该方法

private Singleton() {

}

//2.创建本类对象

// public static Singleton s =new
Singleton();//会导致能够修改s的值所以得私有成员变量

private static Singleton s =new Singleton();

//4.对外提供公共的访问方法get方法

public static Singleton getInstance() {

return s;

}

}

System.out.println(“main execute!  2”);

可以想到的一种办法,在每次调用公共接口的时候,检查是否这个类的实例是否已经创建,这样就需要一个标识,来标识类的实例是否已经被创建,如果被创建了,就直接返回这个类的实例,如果没有创建,就先创建,再返回。所以这种方式,类还是需要持有自身的实例,由于类的实例持有是通过保存这个对象的引用来实现的,所以可以通过判断这个引用是否为null,就可以判断实例是否已经被创建。

第四章饿汉式和懒汉式的区别

  1. 饿汉式是空间换时间,在服务器启动的时候就创建了对象
  2. 懒汉式是时间还空间,在用的时候再创建对象,但是不推荐本模式
  3. 在多线程访问时,饿汉式不会创建多个对象,懒汉式有可能会创建多个对象

static关键字:

还有一种是枚举的方式,由于枚举类型的枚举值的对象创建是由java自己实现的,它的构造器是私有的。

第三章懒汉式(单例的延迟加载,用的时候再创建对象)

/*

* 懒汉式:单利模式的延迟加载模式

* 会有安全隐患

*/

class Singleton2 {

//1.创建本类对象,但不实例化

private澳门网上正规赌场网址, static Singleton2 s;

//2.私有化构造方法

private Singleton2 () {

}

//3.对外提供公共的访问方法

public static Singleton2 getInstance() {

if(s==null) {

//多线程情况下,当线程1进入if后等待,cpu使用权被线程2抢去了也进入了if判断语句那么最终会造成多个对象被创建出来

//所以懒汉式一般不使用

s=new Singleton2();

}

return s;

}

}

//可以使用“类名.”方式调用.也可以用“引用.”,即使用的是“引用.”,底层还是用的“类名.”  ,静态方法中不能直接访问非静态数据.  //静态方法中不能使用this、非静态数据包括非静态方法和成员变量!

public static void m2(){

System.out.println(“m2….”);

}

//入口

public static void main(String[] args){

StaticTest03 st = new StaticTest03();

st.m1();

m2();

//静态的方法按照正规的方式访问:“类名.” 
//静态的方法也能用“引用.”访问  st.m2();
//编译阶段检查出st是StaticTest03类型,编译通过,运行的时候,仍然使用
“StaticTest03.”的方式访问。//该方法执行不需要对象。//空的引用去访问成员的时候会出现空指针异常。//m2方法不是“成员”而是静态的。//所以即使引用是空的,也不会报空指针异常。(其实也就是说,m2
方法执行底层是 不需要这个对象的。)(重要)

StaticTest03 s = null;

s.m2();

}

}

第三节

变量分类:

1.局部变量

2.成员变量(实例变量,非静态变量)

3.静态变量(方法区)

什么时候变量声明成静态变量?(所有的java对象都有这个属性,并且这个属性对应的值都是一样的。没必要让每个对象都保留一份,我们声明成静态变量,在方法区块只有一个,所有的对象公用一个,目的在节省内存)静态变量在类加载时初始化,而且只执行一次。

如果这个属性所有的对象都有,并且这个属性的值是相同的,则该属性声明成

静态的属性。

成员变量:成员变量在创建对象的时候初始化,并且存储在堆中的每一个对象中。

静态变量:在类加载阶段赋值,并且只赋值一次。

//静态变量,被存储在方法区.

//所有的java对象共享这一份。没必要在堆中创建去浪费空间。

//所以静态变量是类级别的,使用“类名.”的方式访问.

第四节 单例模式

单例模式是23种设计模式中最简单的一种设计模式。

为了解决什么问题?为了保证JVM(虚拟机)中某一个类型的java对象永远只有一个。为了节省内存的开销。(这就是优点)(我们知道对象创建完成户,会放到堆中,如果堆中的实例过多,将会存在特别多的垃圾,这样会导致一些问题,比如内存溢出
,使用单例模式,只会创建一个实例,显著减少了对象实例的个数,同时也提高了性能,因为不会频繁的创建对象,这只是它的一个好处。)

//==两边如果是基本数据类型,可以比较这两个基本数据类型是否相等。

//==两边如果是引用数据类型,则比较的是内存地址。

实现单例模式

什么是设计模式:设计模式是可以重复利用的解决方案。

单例模式要领:

1.构造方法私有化

2.对外提供一个公开的静态的获取当前类型对象的方法.

3.提供一个当前类型的静态变量。

单例模式分为两种:

饿汉式单例:在类加载阶段就创建了对象。

懒汉式单例:用到对象的时候才会创建对象。

public class Singleton{ //懒汉式单例

//静态变量

private static Singleton s;

//将构造方法私有化

private Singleton(){}

//对外提供一个公开获取Singleton对象的方法.

public static Singleton getInstance(){

if(s==null){

s = new Singleton();

}

return s;

}

}

/*

饿汉式单例模式

*/

public class Customer{

//类加载时只执行一次。

private static Customer c = new Customer();

//构造方法私有化

private Customer(){}

//提供公开的方法

public static Customer getInstance(){

return c;

}

}+

public static Singleton getInstance(){

单例模式

第二节

public class Singleton{

第一章介绍

单例设计模式:保证类在内存中只有一个对象

静态方法不用创建对象也能直接访问该方法。

这种方式实现的单例模式也叫做饿汉式,实例的创建时机是在类加载的时候静态变量初始化的时候。至于为什么叫做饿汉式,可能是因为这种方式是在类加载的时候获得唯一实例的,而不考虑类加载时是否需要这个类的实例(可能只是初次调用这个类的其他类方法就会进行唯一的创建)。

第五章单例模式的第三种方法final

class Singleton3 {

//1.创建本类对象,但不实例化

public static final Singleton3 s= new
Singleton3();

//2.私有化构造方法

private Singleton3 () {

}

//3.对外提供公共的访问方法

public static Singleton3 getInstance() {

return s;

}

}

2.static修饰的方法叫做“静态方法”.

    }

}

public static Singleton getInstance(){

}

private Singleton(){};

*/

}

System.out.println(“3”);

}

static{

同样的,和一般类一样,后面还可以按照需要,定义类和实例的属性或者方法。这些都是可以的。

static{

public class Singleton{

//入口

上面的所有的类内的单例对象引用也可以定义为final。

}

private Singleton(){};//私有的构造方法

static{

    instance=new Singleton();

public void m1(){

private Singleton(){};//私有的构造方法

1.static修饰的变量叫做“静态变量”.

instance=new Singleton();

System.out.println(“main execute!  1”);

public class Singleton{

一般情况下工具类中的方法大部分都是静态方法。

那么如何解决这个问题呢?首先想到的可能是加锁,既然在对象创建的未完成的时候可能有其他的线程访问这个资源,那么可以将这个getInstance()加上synchronized关键字,每次只允许一个线程访问这个方法,这样确实杜绝了上面所说的问题,但是由于在这么大的粒度下面加上同步,将会非常影响效率。

以下例子演示:static定义静态语句块

public static Singleton getInstance(){

System.out.println(“1”);

public static Singleton getInstance(){

static定义的静态语句块在类加载阶段执行,并且只执行一次,并且是自上而下的顺序执行。(main方法执行之前,static就已经执行。从上而下的执行,先执行static
后执行main)

}

//静态方法

public static Singleton getInstance(){

//静态语句块

    }

}

还有一种方法是静态内部类的方法,饿汉式的方式没有多线程下的诸多问题,但是由于其不具有延迟性,而不被采用,但是可以将这种类加载时由类直接创建实例的方式的优点利用,通过静态内部类的方式,这样既保证了延迟创建对象,又多线程安全。

}

        if(instance==null){

public static void main(String[] args){

private static  Singleton Instance=null;

System.out.println(“2”);

}

//成员方法

    if(instance==null)

public class StaticTest01{

    synchronized(Singleton.class){

public class StaticTest03{

}

3.static还可以定义静态语句块.

    if(instance==null)

//成员方法必须使用“引用.”调用

    }

那么,如何保证某一个类只存在一个实例呢?对象的创建是通过类的构造函数来实现的(也可以通过clone方式,但是这种方法前提是首先有一个对象;或者通过反序列化,这种方法同样也需要原本类的对象存在),所以就需要保证类的构造函数只能被调用一次。若是将构造函数的权限设置为public的,这样显然无法满足要求。所以需要将构造方法隐藏起来,而且构造方法只在类还未创建实例的情况下被调用,如果类已经创建了实例,则不再调用。

与这种方法不同的另一种实现叫做懒汉式,可能是因为懒汉是要到事情迫在眉睫的才会去做,而懒汉式的单例模式实现这种方式是在需要这个类的实例再去创建这个实例,是一种延迟创建对象的实现方式。

因为这种方法效率非常差,所以还需要另外想办法解决多线程下单例模式的问题。所以有人想到一种将同步的粒度缩小的办法,叫做双重校验锁。

这种就是懒汉式的设计单例模式了,这种方式虽然是实现了延迟创建对象的功能(由于对象的创建是需要相当程度的占用资源,如果大量的对象创建都集中在类加载时,这样是非常不好的),但是这种方式只能保证在单线程下是安全的,也就是在多线程下是有问题的。

INSTANCE;//枚举值,java会在创建class的时候将它定义为static
final的,它的值是Singleton类型的对象

private Singleton(){};//私有的构造方法

}

    }

    return Singleton.instance;

}

private static  Singleton Instance=null;

    return Singleton.instance;

    instance=new Singleton();

public class Singleton{

这种方法解决了上一个方法效率的问题,也解决了只创建唯一的实例,但是由于重排序机制,将可能会导致双重校验锁失效,想到了既然问题是重排序机制下其他线程访问了未初始化完成的对象造成的可能错误。那么可以想办法让jvm不进行重排序,volatile关键字有两种语义,一个是线程间的可见性,保证其标识的值是最新的值,从内存中读取,而不是缓存的值,另一种语义是禁止重排序。所以可以用volatile解决双重校验锁失效的问题。 

单例模式,顾名思义,指的是一个类只存在一个实例。

return Singleton.instance;

这样的话,类就需要持有一个自身类的实例,当需要调用者调用公共的接口想要获得这个类的实例的时候,统一的将这个实例返回。

Singleton.INSTANCE就是singleton类型的唯一实例了。

public enum Singleton{

private Singleton(){};//私有的构造方法

}

private  volatile static  Singleton Instance=null;

}

那么,如何实现这种呢?即不是让类加载的时候就进行对象创建,而是等到真正需要这个对象的时候(也就是调用公共的接口想要获得实例的时候),可以想到的是,将创建对象的过程放到接口内,这样就可以保证创建对象的时机是在调用这个公共接口的时候。但是显然仅仅这样做,是没有办法保证实例是唯一的,每一次的调用都将会创建一个新的实例返回。

if(instance==null){

所以可以将构造函数的访问权限设置为最私密的private,同时暴露一个公共的接口给调用者,而在接口处统一的返回实例给调用者。如此,对于每一个需要对象的调用者,可以通过这个接口保证返回的都是同一个实例。

        return SingletonHoder.instance;

}

private static  Singleton Instance=null;

public static synchronized Singleton getInstance(){

synchronized(Singleton.class){

if(instance==null){

public class Singleton{

public class Singleton{

}

            instance=new Singleton();

    return Singleton.instance; 

        }

}

}

private Singleton(){};//私有的构造方法

if(instance==null){

        private static Singleton instance=new Singleton();

原因是因为,由于对象的创建不具有原子性,对象的创建可以简单的分为几个阶段1、在堆中开辟可用的空间
2、对象进行初始化
3、将对象的引用复制给引用变量,也就是“=”这个操作。java虚拟机jvm会对指令进行重排序来优化程序,jvm会保证单线程下重排序不会对结果产生影响,假设重排序可能是先把对象的引用传给引用变量,然后再进行初始化对象,就算是这样,只要保证在调用对象之前,已经完成了初始化,那么重排序将不会影响结果。但是在多线程的情况之下,这种重排序的机制将可能出现问题,假设线程1为第一个调用new
Singleton的线程,而且在它创建对象的时候发生了重排序,jvm在未将对象初始化的引用先传递给了引用变量instance,而此时,线程2也在访问getInstance(),那么当它进行instance==null判断的时候,将会认为对象已经创建(实际上对象还未初始化)而直接返回,如果此时再继续进行对象的调用,将会发生错误,因为对象没有初始化完成。而且多个线程最大权限访问同一个资源,也是会出问题的。(线程1进行对象初始化后还未进行引用赋值,线程2此时将判断对象还未创建,从而进入if子句中进行对象创建,这样就无法保证实例唯一)

    }

}

return Singleton.instance;

private static  Singleton Instance=new Singleton();//持有一个类的实例

    private static class SingletonHolder{

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注