我已经在互联网上浏览了一些相关主题,例如this和这里的问题,例如this、this和this,但我一无所获。这是我的简化代码:
主要活动:
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
仅包含静态方法,它不会被实例化。
该应用程序在单个本机进程中运行,因此这些类应该由同一个类加载器加载(如果我理解正确的话)。
声明和初始化(在静态方法或静态初始化块内部或外部)保存其他类实例的最终静态变量是错误/不好的做法吗?还是在某些情况下是错误的?
我用纯 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
正如有人评论的那样,您可能会看到缓存内存可见性问题。如果是这样,可能需要用
volatile
标记你的单例变量。
如果不熟悉该关键字和此问题,并且您正在使用线程,请研究 Java 内存模型。反复阅读 Brian Goetz 等人撰写的 Java 并发实践。
您可能会考虑以不同的方式实现您的单例。
您的应用程序运行时是否涉及多个类加载器?如果是这样,请编辑您的问题以进行解释。请参阅问题,具有多个不同类加载器的单例类。
Enum
作为单例我相信 Java 世界的共识是,使用 Java 中简单但功能强大的 enum 工具 是实现单例的最佳且最安全的方法。
package work.basil.example;
enum Gilligan
{
INSTANCE;
}
要访问实例,请使用
Gilligan.INSTANCE
而不是调用 getter 方法。
并且无需在第二个类上存储对单例的静态引用。只要在代码中需要访问单例的地方使用
Gilligan.INSTANCE
即可。
警告:为单例使用枚举并不能解决运行时多个类加载器的问题。
我将跳过有关使用单例有时会掩盖不良 OOP 设计并可能使测试变得困难的常见警告。
包 com.test.staticvariables;
导入android.app.Activity; 导入 android.os.Bundle;
公共类 MainActivity 扩展 Activity {
@覆盖 protected void onCreate(Bundle savingInstanceState) {
//---> 在为您的活动创建视图之前初始化您的单例
Test1 test1 = Test1.getInstance();
super.onCreate(savedInstanceState); setContentView(R.layout.layout_of_activity);
//做某事 Test2.printTest1Instances();
} }