为什么要创建两个单例java类示例?

gfttwv5a  于 2021-06-30  发布在  Java
关注(0)|答案(2)|浏览(412)

我在网上浏览了一些相关的主题,像这样,这样,这样,这样的问题,但我一无所获。以下是我的简化代码:
主要活动:

package com.test.staticvariables;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Test1 test1 = Test1.getInstance();
    // do something
    Test2.printTest1Instances();
  }
}

测试1:

package com.test.staticvariables;

public class Test1 {

  private static Test1 test1;

  static {
    System.out.println("Initializing Test1, loader: " + " " + Test1.class.getClassLoader());
  }

  static synchronized Test1 getInstance() {
    if (test1 == null) {
      test1 = new Test1();
    }
    return test1;
  }

  private Test1() {}
}

测试2:

package com.test.staticvariables;

public class Test2 {

  private static final Test1 test1;
  // private static final Test1 test1 = Test1.getInstance();
  // private static final Test1 test1 = getTest1Instance();

  static {
    System.out.println("Initializing Test2, loader: " + " " + Test2.class.getClassLoader());
    test1 = Test1.getInstance();
  }

  static Test1 getTest1Instance() {
    return Test1.getInstance();
  }

  private Test2() {}

  static void printTest1Instances() {
    System.out.println("Test1 class variable: " + test1);
    System.out.println("Test1 instance variable: " + Test1.getInstance());
  }
}

结果:

Initializing Test1, loader: dalvik.system.PathClassLoader[DexPathList...]
Initializing Test2, loader: dalvik.system.PathClassLoader[DexPathList...]
Test1 class variable: com.test.staticvariables.Test1@2a7bfa4
Test1 instance variable: com.test.staticvariables.Test1@7e2a464

为什么要创建类的两个示例 Test1 ( 2a7bfa4 以及 7e2a464 )?
请注意 Test2 只包含静态方法,没有示例化。
应用程序在一个本机进程中运行,因此类应该由同一个类加载器加载(如果我理解正确的话)。
声明并初始化(在静态方法或静态初始化块的内部或外部)包含其他类示例的最终静态变量是错误的/不好的做法?或者在某些情况下是错误的?

xdnvmnnf

xdnvmnnf1#

这里没问题

我用纯java而不是android编写了类似的代码。我选择了一个吉利根岛的主题,以减少混乱比 Test1/2 .
似乎运行正常。
首先,单身一族, Gilligan .

package work.basil.example;

import java.time.Instant;

public class Gilligan
{
    private static Gilligan gilligan;  // Hold a singleton.

    static
    {
        System.out.println( "Static class loading of Gilligan, loader: " + " " + Gilligan.class.getClassLoader() + " at " + Instant.now() );
    }

    static synchronized Gilligan getInstance ( )
    {
        // Lazy loading of our singleton, an instance of `Gilligan`.
        if ( gilligan == null )
        {
            gilligan = new Gilligan();
        }
        return gilligan;
    }

    // Constructor - private
    private Gilligan ( ) {}
}

第二节课是 Island ,保持对同一个 Gilligan .

package work.basil.example;

import java.time.Instant;

public class Island
{
    private static final Gilligan gilligan;

    static
    {
        System.out.println( "Static class loading of Island, loader: " + " " + Island.class.getClassLoader() + " at " + Instant.now() );
        gilligan = Gilligan.getInstance();
    }

    // Constructor - private
    private Island ( ) {}

    static void proveSingleton ( )
    {

        boolean isSingleton = ( Island.gilligan == Gilligan.getInstance() );
        System.out.println( "Island.gilligan: " + gilligan );
        System.out.println( "Gilligan.gilligan: " + Gilligan.getInstance() );
        System.out.println( "Gilligan is a singleton: " + isSingleton );
    }
}

最后,在一个名为 Solo .

package work.basil.example;

public class Solo
{
    public static void main ( String[] args )
    {
        Gilligan g = Gilligan.getInstance();
        Island.proveSingleton();
    }
}

运行时:

Static class loading of Gilligan, loader:  jdk.internal.loader.ClassLoaders$AppClassLoader@73d16e93 at 2020-12-13T00:10:13.009691Z
Static class loading of Island, loader:  jdk.internal.loader.ClassLoaders$AppClassLoader@73d16e93 at 2020-12-13T00:10:13.028805Z
Island.gilligan: work.basil.example.Gilligan@4c873330
Gilligan.gilligan: work.basil.example.Gilligan@4c873330
Gilligan is a singleton: true

挥发性

正如有人评论的那样,您可能看到缓存内存可见性问题。如果是这样,用 volatile 可能需要。
如果您不熟悉这个关键字和这个问题,并且您正在使用线程,请学习java内存模型。阅读和重读brian goetz等人的java并发实践。
您可以考虑以不同的方式实现您的单例。

多类加载器

你的应用程序运行时是否涉及多个类加载器?如果是,请编辑您的问题进行解释。看看这个问题,带有几个不同类加载器的单例类。

对单例使用枚举

我相信java世界的共识是,在java中使用简单但功能强大的枚举工具是实现单例的最佳和最安全的方法。

package work.basil.example;

enum Gilligan
{
    INSTANCE;
}

要访问示例,请使用 Gilligan.INSTANCE 而不是调用getter方法。
并且不需要在第二个类中存储对singleton的静态引用。简单使用 Gilligan.INSTANCE 在您的代码中,只要您需要访问singleton。
警告:对单例使用枚举并不能解决运行时多个类装入器的问题。
请参阅:使用枚举实现单例(在java中)
我将跳过关于使用单例有时如何掩盖糟糕的oop设计,并可能使测试变得困难的常见警告。

g6baxovj

g6baxovj2#

这似乎是个很有趣的问题。我运行了相同的项目,但直接从公共静态voidmain和它返回了预期的良好结果。什么都没坏。

因此,它必须与android以及如何在项目中加载类有关。

相关问题