在Java中使用静态变量的优点

假设我有两个这样的类:

class A{ private static Random random = new Random(); public A(){ // Do something. } public Integer methodGetsCalledQuiteOften(){ return random.nextInt(); } } class B{ private Random random; public A(){ random = new Random(); // Do something. } public Integer methodGetsCalledQuiteOften(){ return random.nextInt(); } } 

在两个实例化多次实例化并且这两个类的实例方法methodGetsCalledQuiteOften都被调用很多的情况下,使用在类中保存Random()的静态变量是否有任何真正的优点/缺点(时间,内存) A而不是在每个实例中创建一个新的Random()对象,就像在B类中一样?

应用程序是multithreading的,更高级别的随机性是如此我认为我将使用静态SecureRandom 。 如果在分析后这将是一个真正的速度因素,我可能会选择其他东西。

真正的优点/缺点取决于实际代码。 换句话说,它取决于创建B频率与调用方法的频率等相比。您应该对应用程序进行概要分析,看看它是否有合理的差异。

A会比B更高效吗? 当然。 但是,你是否会注意到取决于你的用法。 A会比B使用更少的内存吗? 当然,但是你是否关心取决于你要保留多少个A / B实例。

真正唯一的另一个考虑因素是决定论。 由于您没有为Random实例指定种子,我认为您不关心是否可以从Random重现数字序列。 但是值得注意的是……如果你有一个共享的随机数,那么为某个A实例保证某个确定性数字序列要比在每个实例中保存一个确定性数字序列要困难得多。

对可变的静态说不,m’kay。

这是一个残酷的设计。 通常引用的结果是可测试性失败。 没有重新加载课程就很难重现。 由于许多其他原因,糟糕的设计也很糟糕。 仅举一例,默认情况下,类应与线程无关。 添加一个可变的静态,你是线程敌对的 – 一个不好的地方。

每次创建一个新实例似乎都很浪费。 当然,虽然不要过早优化(如果您正在优化,则不会使用java.util.Random )。 初始看到递增也可能导致问题。

最好的方法是传入一个实例(“Parameterise from Above”或“正确使用构造函数”)。 然后你可以测试,复制,模拟等等。你是高效的,可以翻转不同的实现。

如果您的程序是单线程的,则Random应该是类成员( static )。 这样效率更高一些。 使用SecureRandom时更为重要, SecureRandom最初可能需要相对较长的时间才能播种。

如果多个线程最终调用methodGetsCalledQuiteOften() ,则问题会更复杂一些。 该类是线程安全的,但在并发更改期间保护其状态所需的开销可能与创建新的独立Random实例相当。

我可能会坚持使用static成员,直到我注意到随机数生成器的线程之间存在很多争用,这很可能永远不会发生。

虽然实例化A类比B类实际上(10倍)更快,但这只有在你这样做每秒100.000次时才有意义。

关于线程安全,我的理解是.nextInt()基于.next() ,它应该是线程安全的。 所以multithreading不应该在这里给出任何问题。

但更重要的是,两个类的行为可能会有所不同。 A类实例会为每个实例提供一些随机数字序列,但B类实例可能会给出相同的“随机”数字序列(这种行为在不同的平台上可能会有所不同!)。 Javadoc规范指出:

如果使用相同的种子创建两个Random实例,并且为每个实例创建相同的方法调用序列,则它们将生成并返回相同的数字序列。

因此,如果随机性很重要,您可能需要考虑为Random构造函数提供唯一种子,或者使用较慢的java.security.SecureRandom更好地实现Random。

当实例不期望更改时或者您希望在实例之间共享时,请使用静态变量。

 private final static int SOME_CONSTANT=1; 

就是一个例子。

 public class USA { private final static Map statesOfTheUnion = new HashMap(); // etc } 

可能是另一个。 在后一种情况下,我们不期望状态会在不同实例之间发生变化。 因此,每个实例都有自己的副本是没有意义的。

访问静态资源不是线程安全的。 您可能会在线程环境中获得奇怪/不可预测的结果。

好吧,由于Random类采取必要的步骤来确保线程安全,我认为将其设置为静态没有问题。

而且由于它的状态包括单个AtomicLong对象(8个字节),因此不会浪费大量内存(除非你计划创建大量B实例)。

所以,我会说,两种方式都没什么区别。

如果您使用的是java.util.Random ,请注意它是一个线性同余生成器,具有与相关性相关的众所周知的问题。 对我来说,真正的问题是应用程序应该从单个系列中选择还是从具有不同种子的几个系列中选择。

在时间和内存方面,我无法想到任何重大优势(静态变量将成为类定义的一部分而不是堆上)。 但是,当您知道将从多个位置访问对象时,静态变量很有用。 hvgotcodes是正确的,但使用静态资源不是线程安全的。 但是,如果您只读取静态值,那么使用线程就可以了。

它将使您不必将对已启动对象(A或B)的引用传递到其他对象以便使用random

静态变量的好处(要点):

  1. 可以定义常量而无需占用额外的内存(每个类一个)。

  2. 可以在不实例化类的情况下访问常量。

静态方法的好处:

可以定义与实例无关的行为,而不必担心与类实例的意外交互。