2023年11月29日发(作者:)

JDK源码——单例模式

JDK源码中单例模式的应⽤

1Runtime

Runtime类封装了Java运⾏时的环境。每⼀个java程序实际上都是启动了⼀个JVM进程,那么每个JVM进程都是对应这⼀个Runtime实例,此实例是

JVM为其实例化的。每个 Java 应⽤程序都有⼀个 Runtime 类实例,使应⽤程序能够与其运⾏的环境相连接。

由于Java是单进程的,所以,在⼀个JVM中,Runtime的实例应该只有⼀个。所以应该使⽤单例来实现。

1 public class Runtime {

2 private static Runtime currentRuntime = new Runtime();

3

4 public static Runtime getRuntime() {

5 return currentRuntime;

6 }

7 private Runtime() {}

8 }

View Code

以上代码为JDKRuntime类的部分实现,是饿汉式单例模式。在该类第⼀次被classloader加载的时候,实例就被创建出来了。⼀般不能实例化⼀个

Runtime对象,应⽤程序也不能创建⾃⼰的 Runtime 类实例,但可以通过 getRuntime ⽅法获取当前Runtime运⾏时对象的引⽤。

验证:

1 Runtime r1 = time();

2 Runtime r2 = time();

3 n(r1 == r2);

View Code

运⾏结果:

true

2t#getDefaultToolkit()

懒汉式单例。不需要事先创建好,只要在第⼀次真正⽤到的时候再创建就可以了。因为很多时候并不常⽤JavaGUI和其中的对象。如果使⽤饿汉单

例的话会影响JVM的启动速度。

1 public abstract class Toolkit {

2

3 private static Toolkit toolkit;

4

5 public static synchronized Toolkit getDefaultToolkit() {

6 if (toolkit == null) {

7 ileged(

8 new egedAction() {

9 public Void run() {

10 Class cls = null;

11 String nm = perty("t");

12 try {

13 cls = e(nm);

14 } catch (ClassNotFoundException e) {

15 ClassLoader cl = temClassLoader();

16 if (cl != null) {

17 try {

18 cls = ass(nm);

19 } catch (final ClassNotFoundException ignored) {

20 throw new AWTError("Toolkit not found: " + nm);

21 }

22 }

23 }

24 try {

25 if (cls != null) {

26 toolkit = (Toolkit)tance();

27 if (less()) {

28 toolkit = new HeadlessToolkit(toolkit);

29 }

30 }

31 } catch (final InstantiationException ignored) {

32 throw new AWTError("Could not instantiate Toolkit: " + nm);

33 } catch (final IllegalAccessException ignored) {

34 throw new AWTError("Could not access Toolkit: " + nm);

35 }

36 return null;

37 }

38 });

39 loadAssistiveTechnologies();

40 }

41 return toolkit;

42 }

43 }

View Code

以上代码是Toolkit类的单例实现。这⾥类加载时只静态声明了私有toolkit并没有创建Toolkit实例对象,延迟加载加快了JVM启动速度。单例模式作为⼀

种创建模式,在依赖加载的时候应⽤了另⼀种创建对象的⽅式,不是new新的对象,因为Toolkit本⾝是个抽象类不能实例化对象,⽽是通过反射机制

加载类并创建新的实例。

3csEnvironment#getLocalGraphicsEnvironment()

1 public abstract class GraphicsEnvironment {

2 private static GraphicsEnvironment localEnv;

3 public static synchronized GraphicsEnvironment getLocalGraphicsEnvironment() {

4 if (localEnv == null) {

5 localEnv = createGE();

6 }

7 return localEnv;

8 }

9

10 private static GraphicsEnvironment createGE() {

11 GraphicsEnvironment ge;

12 String nm = ileged(new GetPropertyAction("csenv", null));

13 try {

14 Class geCls;

15 try {

16 geCls = (Class)e(nm);

17 } catch (ClassNotFoundException ex) {

18 ClassLoader cl = temClassLoader();

19 geCls = (Class)e(nm, true, cl);

20 }

21 ge = tance();

22 if (isHeadless()) {

23 ge = new HeadlessGraphicsEnvironment(ge);

24 }

25 } catch (ClassNotFoundException e) {

26 throw new Error("Could not find class: "+nm);

27 } catch (InstantiationException e) {

28 throw new Error("Could not instantiate Graphics Environment: "

29 + nm);

30 } catch (IllegalAccessException e) {

31 throw new Error ("Could not access Graphics Environment: "

32 + nm);

33 }

34 return ge;

35 }

36 }

View Code

这⾥类加载时只静态声明了私有localEnv并没有创建实例对象。在GraphicsEnvironment类被第⼀次调⽤时会创建该对象。这⾥的createGE()⽅法也是

通过反射的⽅式创建对象的。

总结:

(1)当⼀个类的对象只需要或者只可能有⼀个时,应该考虑单例模式。

(2)如果⼀个类的实例应该在JVM初始化时被创建出来,应该考虑使⽤饿汉式。

(3)如果⼀个类的实例不需要预先被创建,也许这个类的实例并不⼀定能⽤得上,也许这个类的实例创建过程⽐较耗费时间,也许就是真的没必要

提前创建。那么应该考虑懒汉式。