精华内容
下载资源
问答
  • 单例对象

    千次阅读 2018-10-25 09:57:16
    他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存。   2. 单例模式和静态类的区别 首先理解一下什么是静态类,静态类就是一个类里面都是静态方法和静态field,构造器被private修饰,...

    Java实现单例的5种方式


    1. 什么是单例模式

    单例模式指的是在应用整个生命周期内只能存在一个实例。单例模式是一种被广泛使用的设计模式。他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存。
     

    2. 单例模式和静态类的区别

    首先理解一下什么是静态类,静态类就是一个类里面都是静态方法和静态field,构造器被private修饰,因此不能被实例化。Math类就是一个静态类。

    知道了什么是静态类后,来说一下他们两者之间的区别:

    1)首先单例模式会提供给你一个全局唯一的对象,静态类只是提供给你很多静态方法,这些方法不用创建对象,通过类就可以直接调用;

    2)单例模式的灵活性更高,方法可以被override,因为静态类都是静态方法,所以不能被override;

    3)如果是一个非常重的对象,单例模式可以懒加载,静态类就无法做到;

    那么时候时候应该用静态类,什么时候应该用单例模式呢?首先如果你只是想使用一些工具方法,那么最好用静态类,静态类比单例类更快,因为静态的绑定是在编译期进行的。如果你要维护状态信息,或者访问资源时,应该选用单例模式。还可以这样说,当你需要面向对象的能力时(比如继承、多态)时,选用单例类,当你仅仅是提供一些方法时选用静态类。

    3.如何实现单例模式

    ※ 1. 饿汉模式

    所谓饿汉模式就是立即加载,一般情况下再调用getInstancef方法之前就已经产生了实例,也就是在类加载的时候已经产生了。这种模式的缺点很明显,就是占用资源,当单例类很大的时候,其实我们是想使用的时候再产生实例。因此这种方式适合占用资源少,在初始化的时候就会被用到的类。

    class SingletonHungary {
        private static SingletonHungary singletonHungary = new SingletonHungary();
        //将构造器设置为private禁止通过new进行实例化
        private SingletonHungary() {
    
        }
        public static SingletonHungary getInstance() {
            return singletonHungary;
        }
    }

      

    ※ 2. 懒汉模式

    懒汉模式就是延迟加载,也叫懒加载。在程序需要用到的时候再创建实例,这样保证了内存不会被浪费。针对懒汉模式,这里给出了5种实现方式,有些实现方式是线程不安全的,也就是说在多线程并发的环境下可能出现资源同步问题。

    首先第一种方式,在单线程下没问题,在多线程下就出现问题了。

    // 单例模式的懒汉实现1--线程不安全
    class SingletonLazy1 {
        private static SingletonLazy1 singletonLazy;
    
        private SingletonLazy1() {
    
        }
    
        public static SingletonLazy1 getInstance() {
            if (null == singletonLazy) {
                try {
                    // 模拟在创建对象之前做一些准备工作
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                singletonLazy = new SingletonLazy1();
            }
            return singletonLazy;
        }
    }

     

    我们模拟10个异步线程测试一下:

    public class SingletonLazyTest {
    
        public static void main(String[] args) {
    
            Thread2[] ThreadArr = new Thread2[10];
            for (int i = 0; i < ThreadArr.length; i++) {
                ThreadArr[i] = new Thread2();
                ThreadArr[i].start();
            }
        }
    
    }
    
    // 测试线程
    class Thread2 extends Thread {
        @Override
        public void run() {
            System.out.println(SingletonLazy1.getInstance().hashCode());
        }
    }

     

    运行结果:

    124191239
    124191239
    872096466
    1603289047
    1698032342
    1913667618
    371739364
    124191239
    1723650563
    367137303

     

    可以看到他们的hashCode不都是一样的,说明在多线程环境下,产生了多个对象,不符合单例模式的要求。

    那么如何使线程安全呢?第二种方法,我们使用synchronized关键字对getInstance方法进行同步。

    // 单例模式的懒汉实现2--线程安全
    // 通过设置同步方法,效率太低,整个方法被加锁
    class SingletonLazy2 {
        private static SingletonLazy2 singletonLazy;
    
        private SingletonLazy2() {
    
        }
    
        public static synchronized SingletonLazy2 getInstance() {
            try {
                if (null == singletonLazy) {
                    // 模拟在创建对象之前做一些准备工作
                    Thread.sleep(1000);
                    singletonLazy = new SingletonLazy2();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return singletonLazy;
        }
    }

     

    使用上面的测试类,测试结果:

    1210004989
    1210004989
    1210004989
    1210004989
    1210004989
    1210004989
    1210004989
    1210004989
    1210004989
    1210004989

     

    可以看到,这种方式达到了线程安全。但是缺点就是效率太低,是同步运行的,下个线程想要取得对象,就必须要等上一个线程释放,才可以继续执行。

    那我们可以不对方法加锁,而是将里面的代码加锁,也可以实现线程安全。但这种方式和同步方法一样,也是同步运行的,效率也很低。

    // 单例模式的懒汉实现3--线程安全
    // 通过设置同步代码块,效率也太低,整个代码块被加锁
    class SingletonLazy3 {
    
        private static SingletonLazy3 singletonLazy;
    
        private SingletonLazy3() {
    
        }
    
        public static SingletonLazy3 getInstance() {
            try {
                synchronized (SingletonLazy3.class) {
                    if (null == singletonLazy) {
                        // 模拟在创建对象之前做一些准备工作
                        Thread.sleep(1000);
                        singletonLazy = new SingletonLazy3();
                    }
                }
            } catch (InterruptedException e) {
                // TODO: handle exception
            }
            return singletonLazy;
        }
    }

     

    我们来继续优化代码,我们只给创建对象的代码进行加锁,但是这样能保证线程安全么?

    // 单例模式的懒汉实现4--线程不安全
    // 通过设置同步代码块,只同步创建实例的代码
    // 但是还是有线程安全问题
    class SingletonLazy4 {
    
        private static SingletonLazy4 singletonLazy;
    
        private SingletonLazy4() {
    
        }
    
        public static SingletonLazy4 getInstance() {
            try {
                if (null == singletonLazy) {        //代码1
                    // 模拟在创建对象之前做一些准备工作
                    Thread.sleep(1000);
                    synchronized (SingletonLazy4.class) {
                        singletonLazy = new SingletonLazy4(); //代码2
                    }
                }
            } catch (InterruptedException e) {
                // TODO: handle exception
            }
            return singletonLazy;
        }
    }

      

    我们来看一下运行结果:

    1210004989
    1425839054
    1723650563
    389001266
    1356914048
    389001266
    1560241484
    278778395
    124191239
    367137303

     

    从结果看来,这种方式不能保证线程安全,为什么呢?我们假设有两个线程A和B同时走到了‘代码1’,因为此时对象还是空的,所以都能进到方法里面,线程A首先抢到锁,创建了对象。释放锁后线程B拿到了锁也会走到‘代码2’,也创建了一个对象,因此多线程环境下就不能保证单例了。

    让我们来继续优化一下,既然上述方式存在问题,那我们在同步代码块里面再一次做一下null判断不就行了,这种方式就是我们的DCL双重检查锁机制。

    //单例模式的懒汉实现5--线程安全
    //通过设置同步代码块,使用DCL双检查锁机制
    //使用双检查锁机制成功的解决了单例模式的懒汉实现的线程不安全问题和效率问题
    //DCL 也是大多数多线程结合单例模式使用的解决方案
    class SingletonLazy5 {
    
        private static SingletonLazy5 singletonLazy;
    
        private SingletonLazy5() {
    
        }
    
        public static SingletonLazy5 getInstance() {
            try {
                if (null == singletonLazy) {
                    // 模拟在创建对象之前做一些准备工作
                    Thread.sleep(1000);
                    synchronized (SingletonLazy5.class) {
                        if(null == singletonLazy) {
                            singletonLazy = new SingletonLazy5();
                        }
                    }
                }
            } catch (InterruptedException e) {
                // TODO: handle exception
            }
            return singletonLazy;
        }
    }

      

    运行结果:

    124191239
    124191239
    124191239
    124191239
    124191239
    124191239
    124191239
    124191239
    124191239
    124191239

     

    我们可以看到DCL双重检查锁机制很好的解决了懒加载单例模式的效率问题和线程安全问题。这也是我们最常用到的方式。

    ※ 3. 静态内部类

    我们也可以使用静态内部类实现单例模式,代码如下:

    //使用静态内部类实现单例模式--线程安全
    class SingletonStaticInner {
        private SingletonStaticInner() {
    
        }
        private static class SingletonInner {
            private static SingletonStaticInner singletonStaticInner = new SingletonStaticInner();
        }
        public static SingletonStaticInner getInstance() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return SingletonInner.singletonStaticInner;
        }
    }

     

    可以看到使用这种方式我们没有显式的进行任何同步操作,那他是如何保证线程安全呢?和饿汉模式一样,是靠JVM保证类的静态成员只能被加载一次的特点,这样就从JVM层面保证了只会有一个实例对象。那么问题来了,这种方式和饿汉模式又有什么区别呢?不也是立即加载么?实则不然,加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。

    可以说这种方式是实现单例模式的最优解。

    ※ 4. 静态代码块

    这里提供了静态代码块实现单例模式。这种方式和第一种类似,也是一种饿汉模式。

    //使用静态代码块实现单例模式
    class SingletonStaticBlock {
        private static SingletonStaticBlock singletonStaticBlock;
        static {
            singletonStaticBlock = new SingletonStaticBlock();
        }
        public static SingletonStaticBlock getInstance() {
            return singletonStaticBlock;
        }
    }

     

    ※ 5. 序列化与反序列化

    LZ为什么要提序列化和反序列化呢?因为单例模式虽然能保证线程安全,但在序列化和反序列化的情况下会出现生成多个对象的情况。运行下面的测试类,

    public class SingletonStaticInnerSerializeTest {
    
        public static void main(String[] args) {
            try {
                SingletonStaticInnerSerialize serialize = SingletonStaticInnerSerialize.getInstance();
                System.out.println(serialize.hashCode());
                //序列化
                FileOutputStream fo = new FileOutputStream("tem");
                ObjectOutputStream oo = new ObjectOutputStream(fo);
                oo.writeObject(serialize);
                oo.close();
                fo.close();
                //反序列化
                FileInputStream fi = new FileInputStream("tem");
                ObjectInputStream oi = new ObjectInputStream(fi);
                SingletonStaticInnerSerialize serialize2 = (SingletonStaticInnerSerialize) oi.readObject();
                oi.close();
                fi.close();
                System.out.println(serialize2.hashCode());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    
    //使用匿名内部类实现单例模式,在遇见序列化和反序列化的场景,得到的不是同一个实例
    //解决这个问题是在序列化的时候使用readResolve方法,即去掉注释的部分
    class SingletonStaticInnerSerialize implements Serializable {
    
        /**
         * 2018年03月28日
         */
        private static final long serialVersionUID = 1L;
    
        private static class InnerClass {
            private static SingletonStaticInnerSerialize singletonStaticInnerSerialize = new SingletonStaticInnerSerialize();
        }
    
        public static SingletonStaticInnerSerialize getInstance() {
            return InnerClass.singletonStaticInnerSerialize;
        }
    
    //  protected Object readResolve() {
    //      System.out.println("调用了readResolve方法");
    //      return InnerClass.singletonStaticInnerSerialize;
    //  }
    }

       

    可以看到:

    865113938
    1078694789

      

    结果表明的确是两个不同的对象实例,违背了单例模式,那么如何解决这个问题呢?解决办法就是在反序列化中使用readResolve()方法,将上面的注释代码去掉,再次运行:

    865113938
    调用了readResolve方法
    865113938

     

    问题来了,readResolve()方法到底是何方神圣,其实当JVM从内存中反序列化地”组装”一个新对象时,就会自动调用这个 readResolve方法来返回我们指定好的对象了, 单例规则也就得到了保证。readResolve()的出现允许程序员自行控制通过反序列化得到的对象。
     

    展开全文
  • Scala进阶_单例对象

    2021-01-20 12:40:57
    单例对象 scala中没有Java中的静态成员,我们想要定义类似于Java的static变量、static方法,就要使用到scala中的单例对象——object. 定义单例对象 单例对象表示全局仅有一个对象(类似于Java static概念) 定义单例...
  • 在应用这个模式时,单例对象的类必须保证只有一个实例存在。 我们知道,在面向对象的思想中,通过类的构造函数可以创建对象,只要内存足够,可以创建任意个对象。所以,要想限制某一个类只有一个单例对象,就需要在...

    什么是单例

    单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在

    我们知道,在面向对象的思想中,通过类的构造函数可以创建对象,只要内存足够,可以创建任意个对象。所以,要想限制某一个类只有一个单例对象,就需要在他的构造函数上下功夫。

    实现对象单例模式的思路是:

    1、一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);

    2、当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;

    3、同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

    最简单的单例代码:

    /**
     * @author 633805 LYH
     * @version V1.0
     * @description 单例的最简单写法  不加锁  将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例
     * @create 2019-05-24 8:31
     * @since 1.7
     */
    public class Singleton3 {
    
        private static Singleton3 singleton3;
    
        private void Singleton3() {
    
        }
    
        public static Singleton3 getSingleton3() {
            if (singleton3 == null) {
                singleton3 = new Singleton3();
            }
            return singleton3;
        }
    
    }

    以上Java代码,就实现了一个简单的单例模式。我们通过将构造方法定义为私有,然后提供一个getInstance方法,该方法中来判断是否已经存在该类的实例,如果存在直接返回。如果不存在则创建一个再返回。wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

    线程安全的单例

    如果有两个线程同时执行到if(instance==null)这行代码,这是判断都会通过,然后各自会执行instance = new Singleton();并各自返回一个instance,这时候就产生了多个实例,就没有保证单例!

    上面这种单例的实现方式我们通常称之为懒汉模式,所谓懒汉,指的是只有在需要对象的时候才会生成(getInstance方法被调用的时候才会生成)。上面的这种懒汉模式并不是线程安全的,所以并不建议在日常开发中使用。基于这种模式,我们可以实现一个线程安全的单例的,如下:

    /**
     * @author 633805 LYH
     * @version V1.0
     * @description 懒汉模式  锁的粒度太大
     * @create 2019-05-24 14:40
     * @since 1.7
     */
    public class Singleton4 {
        private static Singleton4 singleton4;
        private void Singleton4() {}
    
        public static synchronized Singleton4 getInstance() {
            if (singleton4 == null) {
                singleton4 = new Singleton4();
            }
            return singleton4;
        }
    }

    通过在getInstance方法上增加synchronized,通过锁来解决并发问题。这种实现方式就不会发生有多个对象被创建的问题了。 

    双重校验锁 

    上面这种线程安全的懒汉写法能够在多线程中很好的工作,但是,遗憾的是,这种做法效率很低,因为只有第一次初始化的时候才需要进行并发控制,大多数情况下是不需要同步的。

    我们其实可以把上述代码做一些优化的,因为懒汉模式中使用synchronized定义一个同步方法,我们知道,synchronized还可以用来定义同步代码块,而同步代码块的粒度要比同步方法小一些,从而效率就会高一些。如以下代码:  

    /**
     * @author 633805 LYH
     * @version V1.0
     * @description 双重校验锁的实现方式,静态成员变量singleton必须通过volatile来修饰,保证其初始化不被重排,否则可能被引用到一个未初始化完成的对象。
     * @create 2019-05-24 14:46
     * @since 1.7
     */
    public class Singleton5 {
    
        private volatile static Singleton5 singleton5;
        private Singleton5 (){}
    
        public static Singleton5 getSingleton() {
            if (singleton5 == null) {
                synchronized (Singleton5.class) {
                    if (singleton5 == null) {
                        singleton5 = new Singleton5();
                    }
                }
            }
            return singleton5;
        }
    }

    上面这种形式,只有在singleton == null的情况下再进行加锁创建对象,如果singleton!=null的话,就直接返回就行了,并没有进行并发控制。大大的提升了效率。

    从上面的代码中可以看到,其实整个过程中进行了两次singleton == null的判断,所以这种方法被称之为"双重校验锁"。

    还有值得注意的是,双重校验锁的实现方式中,静态成员变量singleton必须通过volatile来修饰,保证其初始化不被重排,否则可能被引用到一个未初始化完成的对象。

    饿汉模式

    前面提到的懒汉模式,其实是一种lazy-loading思想的实践,这种实现有一个比较大的好处,就是只有真正用到的时候才创建,如果没被使用到,就一直不会被创建,这就避免了不必要的开销。

    但是这种做法,其实也有一个小缺点,就是第一次使用的时候,需要进行初始化操作,可能会有比较高的耗时。如果是已知某一个对象一定会使用到的话,其实可以采用一种饿汉的实现方式。

    所谓饿汉,就是事先准备好,需要的时候直接给你就行了。这就是日常中比较常见的"先买票后上车",走正常的手续。

    如以下代码,饿汉模式:

    /**
     * @author 633805 LYH
     * @version V1.0
     * @description 饿汉模式
     * @create 2019-05-24 14:49
     * @since 1.7
     */
    public class Singleton6 {
    
        private static Singleton6 singleton6 = new Singleton6();
        private Singleton6() {}
    
        public static Singleton6 getInstance() {
            return singleton6;
        }
    
    
        //第二种   饿汉变种
         /*   private  Singleton6 instance = null;
            static {
                instance = new Singleton6();
            }
            private Singleton6 (){}
            public static Singleton6 getInstance() {
                return this.instance;
            }*/
    }

    饿汉模式中的静态变量是随着类加载时被完成初始化的。饿汉变种中的静态代码块也会随着类的加载一块执行。

    以上两个饿汉方法,其实都是通过定义静态的成员变量,以保证instance可以在类初始化的时候被实例化。

    因为类的初始化是由ClassLoader完成的,这其实是利用了ClassLoader的线程安全机制。ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。也正是因为这样, 除非被重写,这个方法默认在整个装载过程中都是同步的(线程安全的)

    除了以上两种饿汉方式,还有一种实现方式也是借助了calss的初始化来实现的,那就是通过静态内部类来实现的单例:

    public class Singleton {  
        private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
        }  
    }  

    前面提到的饿汉模式,只要Singleton类被装载了,那么instance就会被实例化。

    而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。

    使用静态内部类,借助了classloader来实现了线程安全,这与饿汉模式有着异曲同工之妙,但是他有兼顾了懒汉模式的lazy-loading功能,相比较之下,有很大优势。

    单例的破坏

    前文介绍过,我们实现的单例,把构造方法设置为私有方法来避免外部调用是很重要的一个前提。但是,私有的构造方法外部真的就完全不能调用了么?

    其实不是的,我们是可以通过反射来调用类中的私有方法的,构造方法也不例外,所以,我们可以通过反射来破坏单例。

    除了这种情况,还有一种比较容易被忽视的情况,那就是其实对象的序列化和反序列化也会破坏单例。

    如使用ObjectInputStream进行反序列化时,在ObjectInputStream的readObject生成对象的过程中,其实会通过反射的方式调用无参构造方法新建一个对象。

    所以,在对单例对象进行序列化以及反序列化的时候,一定要考虑到这种单例可能被破坏的情况。

    可以通过在Singleton类中定义readResolve的方式,解决该问题:

    /**
     * 使用双重校验锁方式实现单例
     */
    public class Singleton implements Serializable{
        private volatile static Singleton singleton;
        private Singleton (){}
        public static Singleton getSingleton() {
            if (singleton == null) {
                synchronized (Singleton.class) {
                    if (singleton == null) {
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    
        private Object readResolve() {
            return singleton;
        }
    } 

     枚举实现单例

    /**
     * @author 633805 LYH
     * @version V1.0
     * @description 枚举实现单例
     * @create 2019-05-24 14:59
     * @since 1.7
     */
    public enum Singleton7 {
        INSTANCE;
        public void  whateverMethod() {}
    }

     

    以上,就实现了一个非常简单的单例,从代码行数上看,他比之前介绍过的任何一种都要精简,并且,他还是线程安全的。

    这些,其实还不足以说服我们这种方式最优。但是还有个至关重要的原因,那就是:枚举可解决反序列化会破坏单例的问题

    关于枚举实现单例可以参考:为什么我墙裂建议大家使用枚举来实现单例

    不使用synchronized实现单例

    前面讲过的所有方式,只要是线程安全的,其实都直接或者间接用到了synchronized,那么,如果不能使用synchronized的话,怎么实现单例呢?

    使用Lock?这当然可以了,但是其实根本还是加锁,有没有不用锁的方式呢?

    答案是有的,那就是CAS。CAS是一项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

    在JDK1.5 中新增java.util.concurrent(J.U.C)就是建立在CAS之上的。相对于对于synchronized这种阻塞算法,CAS是非阻塞算法的一种常见实现。所以J.U.C在性能上有了很大的提升。

    借助CAS(AtomicReference)实现单例模式:

    import java.util.concurrent.atomic.AtomicReference;
    
    /**
     * @author 633805 CAS(AtomicReference)实现单例模式
     * @version V1.0
     * @description 对类的描述
     * @create 2019-05-24 15:01
     * @since 1.7
     */
    public class Singleton8 {
        private static final AtomicReference<Singleton8> INSTANCE = new AtomicReference<Singleton8>();
    
        private Singleton8() {}
    
        public static Singleton8 getInstance() {
            for (;;) {
                Singleton8 singleton = INSTANCE.get();
                if (null != singleton) {
                    return singleton;
                }
    
                singleton = new Singleton8();
                if (INSTANCE.compareAndSet(null, singleton)) {
                    return singleton;
                }
            }
        }
    }

    用CAS的好处在于不需要使用传统的锁机制来保证线程安全,CAS是一种基于忙等待的算法,依赖底层硬件的实现,相对于锁它没有线程切换和阻塞的额外消耗,可以支持较大的并行度。不过CAS只能只能实现单个共享变量的原子性,而且失败会不断自旋,还容易出现ABA的问题。

    展开全文
  • scala的单例对象

    2020-02-13 20:48:19
    单例对象 scala中没有Java中的静态成员,我们想要定义类似于Java的static变量、static方法,就要使用到scala中的单例对象——object. 定义单例对象 单例对象表示全局仅有一个对象(类似于Java static概念) 定义单例...

    单例对象
    scala中没有Java中的静态成员,我们想要定义类似于Java的static变量、static方法,就要使用到scala中的单例对象——object.
    定义单例对象
    单例对象表示全局仅有一个对象(类似于Java static概念)
    定义单例对象和定义类很像,就是把class换成object
    在object中定义的成员变量类似于Java的静态变量
    可以使用object直接引用成员变量
    示例
    示例说明
    定义一个Dog单例对象,保存狗有几条腿
    在main方法中打印狗腿的数量
    参考代码
    scala object _08ObjectDemo { // 定义一个单例对象 object Dog { // 定义腿的数量 val LEG_NUM = 4 } def main(args: Array[String]): Unit = { println(Dog.LEG_NUM) } }
    在这里插入图片描述
    答案:
    在这里插入图片描述
    在单例对象中定义成员方法
    在object中定义的成员方法类似于Java的静态方法
    示例
    示例说明
    设计一个单例对象,定义一个能够打印分割线(15个减号)的方法
    在main方法调用该方法,打印分割线
    参考代码
    scala object _09ObjectDemo { object PrintUtil { // 打印分割线 def printSpliter() = { // 字符串乘法,表示返回多少个字符串 println("-" * 10) } } def main(args: Array[String]): Unit = { PrintUtil.printSpliter() } }
    在这里插入图片描述
    答案:---------
    在这里插入图片描述

    展开全文
  • Scala>scala单例对象

    2020-04-06 16:53:12
    文章目录单例对象定义单例对象单例对象中定义成员方法工具类案例main方法定义main方法实现App Trait来定义入口 单例对象 scala中没有Java中的静态成员,若想要定义类似于Java的static变量、static方法,就要使用到...

    单例对象

    scala中没有Java中的静态成员,若想要定义类似于Java的static变量、static方法,就要使用到scala中的单例对象——object.


    定义单例对象

    单例对象表示全局仅有一个对象(类似于Java static概念)

    • 定义单例对象和定义类很像,就是把class换成object
    • 在object中定义的成员变量类似于Java的静态变量
    • 可以使用object直接引用成员变量

    示例

    • 定义一个Dog单例对象,保存狗有几条腿
    • 在main方法中打印狗腿的数量

    参考代码

    object _08ObjectDemo {
    
      // 定义一个单例对象
      object Dog {
        // 定义腿的数量
        val LEG_NUM = 4
      }
    
      def main(args: Array[String]): Unit = {
        println(Dog.LEG_NUM)
      }
    }
    

    在单例对象中定义成员方法

    • 在object中定义的成员方法类似于Java的静态方法

    示例

    • 设计一个单例对象,定义一个能够打印分割线(15个减号)的方法
    • 在main方法调用该方法,打印分割线

    参考代码

    object _09ObjectDemo {
    
      object PrintUtil {
    
        // 打印分割线
        def printSpliter() = {
          // 字符串乘法,表示返回多少个字符串
          println("-" * 10)
        }
      }
    
      def main(args: Array[String]): Unit = {
        PrintUtil.printSpliter()
      }
    }
    

    工具类案例

    需求

    • 编写一个DateUtil工具类专门用来格式化日期时间
    • 定义一个方法,用于将日期(Date)转换为年月日字符串,例如:2030-10-05

    步骤

    • 定义一个DateUtil单例对象,定义日期格式化方法(format)
    • 使用SimpleDateFormat将日期转换为字符串

    参考代码

    object _10ObjectDemo {
    
      object DateUtils {
        // 在object中定义的成员变量,相当于Java中定义一个静态变量
        // 定义一个SimpleDateFormat日期时间格式化对象
        val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
    
        // 相当于Java中定义一个静态方法
        def format(date: Date) = simpleDateFormat.format(date)
      }
    
      // main是一个静态方法,所以必须要写在object中
      def main(args: Array[String]): Unit = {
        println(DateUtils.format(new Date()))
      }
    }
    

    main方法

    scala和Java一样,如果要运行一个程序,必须有一个main方法。在Java中main方法是静态的,而在scala中没有静态方法。在scala中,这个main方法必须放在一个单例对象中。

    定义main方法

    main方法

    def main(args:Array[String]):Unit = {
        // 方法体
    }
    

    示例

    • 创建一个单例对象,在该单例对象中打印"hello, scala"

    参考代码

    object Main5 {
      def main(args:Array[String]) = {
        println("hello, scala")
      }
    }
    

    实现App Trait来定义入口

    创建一个object,继承自App Trait(特质),然后将需要编写在main方法中的代码,写在object的构造方法体内。

    object 单例对象名 extends App {
        // 方法体
    }
    

    示例

    • 继承App特质,来实现一个入口。同样输出"hello, scala"
      参考代码
    object Main5 extends App {
      println("hello, scala")
    }
    
    展开全文
  • Scala 单例对象

    2018-10-29 11:51:33
    单例对象 对象是只有一个实例的类。当它被引用时,它会被怠慢创建,就像lazy val一样。 作为顶层值,对象是单例。 作为一个封闭类的成员或本地值,它的行为与lazy val完全相同。 1 定义单例对象 一个对象是一个值。...
  • 以前想的是先判断 == null,先判断==null,就要在一开始就加synchronized,今天看到可以先判断 != null,这样就不会产生性能问题,有启发。 public static Manager getInstance() { if (inst !...
  • Scala中的单例对象

    2020-04-17 21:49:24
    scala中的单例对象 说明: 1.Scala中由于没有像Java中的static关键字,因此Scala类不存在静态成员。但是Scala可以使用“object”关键字实现单例模式(其实理解为Java中的static也行)。 2.Scala中使用单例模式时需要...
  • scala中产生单例对象

    千次阅读 2018-08-29 16:03:05
    大家:  好!看了点scala产生单例的代码,分享出来,仅供参考。 package day02 import scala.collection.mutable.ArrayBuffer /** * 功能: 演示scala中的单例对象 ... //单例对象,不需要...
  • 单例对象属性的更新--影子实例

    千次阅读 2019-08-14 14:10:37
    通常,为了实现配置信息的实时更新,会有一个线程不停检测配置文件或配置数据库的内容,一旦发现变化,就更新到单例对象的属性中。在更新这些信息的时候,很可能还会有其他线程正在读取这些信息,造成意想不到的后果...
  • 单例对象释放问题

    千次阅读 2018-03-08 12:21:21
    单例模式就是全局只有一个类A产生的对象不允许产生多个对象。1.static成员变量实现了同类对象间信息共享2.static成员类外存储,求类大小,并不包含在内3.static成员是命名空间属于累的全局变量,存储在data区4....
  • Scala单例对象

    2017-05-07 16:38:46
    不属于类单个实例的方法和值属于单例对象,用关键字object而不用calss来标记。
  • 我们可以理解他为单例生命周期,而购物车我们可以理解为会话生命周期,对于单例和会话两个不同的生命周期的bean,我们应该怎样耦合呢,下面我们给出我们的答案。 @Component public classs StoreService{ @...
  • 单例对象的创建与销毁

    千次阅读 2018-11-22 10:24:56
    单例模式中,每个bean定义只生成一个对象实例。  单例模式的构造函数是私有的,没有办法直接使用new调用构造函数,所以不会创建新对象。它只能通过它的一个静态方法得到实例,而这个静态方法可以去调构造函数产生...
  • 对于JDK1.2后的JVM HotSpot来说,判断对象可以回收需要经过可达性分析,由于单例对象被其类中的静态变量引用,所以JVM认为对象是可达的,不会被回收。 另外,对于JVM方法区回收,由堆中存在单例对象,所以单例类也...
  • 多线程与单例对象之间的关系

    千次阅读 2017-12-20 09:39:00
    在我在考虑考虑他们的时候思考了以下几个问题: 1、我们通常都将dao层(数据库连接层)设置成单例,这样的话如果每次处理数据库中的数据都...3、一个单例模式创建的对象是可以同时被多个线程处理的,如果一个对象被...
  • 关于IOC容器的多例对象(prototype)和单例对象(singleton) 默认是单例对象 单例对象只在容器初始化建立时创建一次 多例对象是在使用时才创建可以创建多次
  • 单例对象无法被回收的真正原因要从java垃圾回收器开始说起 JAVA垃圾对象判断准则 Java中判断一个对象是否可以被回收是根据“可达性”来判定的。 可达性: 以一系列的称为 “GC Roots”的对象作为起始点,然后向下...
  • Scala单例对象Object Scala没有单例静态属性的说法,不过提供了单例对象object。 单独的object并不能算是类型定义(从抽象层面来说) Object是无参的(不通过new 关键字来创建没有机会来传参) Scala的单例对象...
  • Spring创建好的单例对象是否存在线程安全的问题?

    千次阅读 多人点赞 2019-03-23 10:16:17
    前言: 这是我在一次面试中被问到过的问题,但是当时我回答的并不是太好,最近在学习多线程知识的时候又对这个问题有了新的理解,所以这篇文章主要... 单例 singleton 整个应用中只创建一个实例 原型 p...
  • 一个对象单例,但依赖的对象对象,此时,该依赖的对象变成单例 原因: 对象单例,因此只实例化和初始化一次,因此该属性只被赋值一次,因此在该对象中,被依赖的对象始终为同一个对象 解决方法: 方法一:...
  • 为什么执行单例对象的方法不会有线程安全的问题? spring单例bean对象 ,因为对象被实例化后,实例的方法只是磁盘上的一段代码, 不存在线程安全问题。多线程对springbean 只存在读取关系, 至于数据库连接哪里,...
  • 如果这个类是单例模式的,再使用CreateInstance,不会直接调用Instance的那个实例,而是又创建了一个。会出现明明应该单例调用的地方,数据不一致的情况。 此时,就需要了解怎么反射调用Instance实例。   ...
  • 可以,打个比方:单例是一个厨房,线程是厨师,方法是挂在墙上的菜谱,现在多个厨师在一个厨房里照着墙上的菜谱做菜,并不冲突。什么时候冲突?厨房里只有一个水龙头(单例变量等),厨师都要去接水,这个时候就会...
  • scala 单例对象 伴生对象

    千次阅读 2016-11-18 17:18:09
    1.单例对象Scala中没有静态方法或静态字段,但可以使用object这个语法结构来实现相同的功能。Object与class在语法层面上很相似,除了不能提供构造器参数外,object可以拥有class的所有特性。废话不多说,直接上代码...
  • PHP实例化对象单例的方法: 三私一公:2个私有方法,1个私有属性,1个公共方法  private function __construct(){} //不可以继承构造方法  private function __clone(){}//不可以继承克隆方法  privare static $...
  • 文章目录Spring的Bean作用域创建单例的方式是否线程安全怎么判断使用已经创建好的单例对象是否线程安全Spring的单例模式与线程安全有状态对象和无状态对象 Spring的Bean作用域 作用域 字符 描述 单例 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 319,109
精华内容 127,643
关键字:

单例对象