一个 static 还能难得住我? (3)

我们在开发过程中,经常会使用 static 关键字作为日志打印,下面这行代码你应该经常看到

private static final Logger LOGGER = LogFactory.getLoggger(StaticTest.class);

然而把 static 和 final 去掉都可以打印日志

private final Logger LOGGER = LogFactory.getLoggger(StaticTest.class); private Logger LOGGER = LogFactory.getLoggger(StaticTest.class);

但是这种打印日志的方式存在问题

对于每个 StaticTest 的实例化对象都会拥有一个 LOGGER,如果创建了1000个 StaticTest 对象,则会多出1000个Logger 对象,造成资源的浪费,因此通常会将 Logger 对象声明为 static 变量,这样一来,能够减少对内存资源的占用。

static 经常用作单例模式

由于单例模式指的就是对于不同的类来说,它的副本只有一个,因此 static 可以和单例模式完全匹配。

下面是一个经典的双重校验锁实现单例模式的场景

public class Singleton { private static volatile Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }

来对上面代码做一个简单的描述

使用 static 保证 singleton 变量是静态的,使用 volatile 保证 singleton 变量的可见性,使用私有构造器确保 Singleton 不能被 new 实例化。

使用 Singleton.getInstance() 获取 singleton 对象,首先会进行判断,如果 singleton 为空,会锁住 Singletion 类对象,这里有一些小伙伴们可能不知道为什么需要两次判断,这里来解释下

如果线程 t1 执行到 singleton == null 后,判断对象为 null,此时线程把执行权交给了 t2,t2 判断对象为 null,锁住 Singleton 类对象,进行下面的判断和实例化过程。如果不进行第二次判断的话,那么 t1 在进行第一次判空后,也会进行实例化过程,此时仍然会创建多个对象。

类的构造器是否是 static 的

这个问题我相信大部分小伙伴都没有考虑过,在 Java 编程思想中有这么一句话 类的构造器虽然没有用 static 修饰,但是实际上是 static 方法,但是并没有给出实际的解释,但是这个问题可以从下面几个方面来回答

static 最简单、最方便记忆的规则就是没有 this 引用。而在类的构造器中,是有隐含的 this 绑定的,因为构造方法是和类绑定的,从这个角度来看,构造器不是静态的。

从类的方法这个角度来看,因为 类.方法名不需要新创建对象就能够访问,所以从这个角度来看,构造器也不是静态的

从 JVM 指令角度去看,我们来看一个例子

public class StaticTest { public StaticTest(){} public static void test(){ } public static void main(String[] args) { StaticTest.test(); StaticTest staticTest = new StaticTest(); } }

我们使用 javap -c 生成 StaticTest 的字节码看一下

public class test.StaticTest { public test.StaticTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void test(); Code: 0: return public static void main(java.lang.String[]); Code: 0: invokestatic #2 // Method test:()V 3: new #3 // class test/StaticTest 6: dup 7: invokespecial #4 // Method "<init>":()V 10: astore_1 11: return }

我们发现,在调用 static 方法时是使用的 invokestatic 指令,new 对象调用的是 invokespecial 指令,而且在 JVM 规范中 说到

一个 static 还能难得住我?

一个 static 还能难得住我?

从这个角度来讲,invokestatic 指令是专门用来执行 static 方法的指令;invokespecial 是专门用来执行实例方法的指令;从这个角度来讲,构造器也不是静态的。

一个 static 还能难得住我?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgfdzx.html