Bloch有效的Java——静态类优于非静态类——有多少实例



我想知道封闭类可以创建多少个静态成员类的实例。我假设只有一个,但是下面的Bloch摘录对我来说没有意义。

引用Joshua Bloch的Effective Java - Item 22*:选择静态成员类而不是非静态。

私有静态成员类的一个常见用途是表示由其外围类表示的对象的组件。例如,考虑一个Map实例,它将键与值关联起来。许多Map实现对于映射中的每个键值对都有一个内部Entry对象。虽然每个条目都与映射相关联,但条目上的方法(getKey、getValue和setValue)不需要访问映射。因此,使用非静态成员类来表示条目是一种浪费:最好使用私有静态成员类。如果在条目声明中不小心省略了静态修饰符,映射仍然可以工作,但是每个条目将包含对映射的多余引用,这会浪费空间和时间。

他声明映射为映射中的每个键值对创建一个Entry对象,即静态成员类的多个实例。

所以我的假设是错误的!这意味着我对静态成员类的理解是错误的。每个人都知道静态成员变量的行为,例如经典的静态final字符串——对象只有一个实例。

这是否意味着当封闭对象实例化时,静态成员类实际上没有实例化?

在这种情况下,Map使用Entry的静态成员类有什么意义呢?为什么不直接在API上使用接口呢?其他的Collections类都可以提供自己的实现。

[*]刚刚意识到这是PDF版本的第18项,我有

这是对static关键字的常见误解。

当你将static与变量一起使用时,它意味着对于该类的所有对象或类似的东西只有一个。

static Object thereWillBeOnlyOne = new Object();

然而,在内部类的上下文中,它的含义完全不同。 static内部类与外围类的对象没有连接,而非静态内部类与外围类的对象有连接。


A static内部类:

public class TrieMap<K extends CharSequence, V> extends AbstractMap<K, V> implements Map<K, V> {
  private static class Entry<K extends CharSequence, V> implements Map.Entry<K, V> {

我的TrieMap类使用的Map.Entry类不需要引用创建它的对象,因此可以将其改为static以节省不必要的引用。


static内部类:

public final class StringWalker implements Iterable<Character> {
  // The iteree
  private final String s;
  // Where to get the first character from.
  private final int start;
  // What to add to i (usually +/- 1).
  private final int step;
  // What should i be when we stop.
  private final int stop;
  // The Character iterator.
  private final class CharacterIterator implements Iterator<Character> {
    // Where I am.
    private int i;
    // The next character.
    private Character next = null;
    CharacterIterator() {
      // Start at the start.
      i = start;
    }
    public boolean hasNext() {
      if (next == null) {
        if (step > 0 ? i < stop : i > stop) {
          next = s.charAt(i);
          i += step;
        }
      }
      return next != null;
    }

StringWalker对象中的CharacterIterator指向要迭代的字符串s,该字符串在StringWalker对象中只存在一次。因此,我可以创建多个StringWalker迭代器,它们都遍历相同的字符串。


为什么这么奇怪?

这种看似不合逻辑的二元性源于Cstatic关键字的使用。

C中,您可以(或至少过去能够)做:

void doSomething () {
   static int x = 1;
   if ( x < 3 ) {
   } else {
   }
   x += 1;
}

并且每次调用该函数时,x将与上次离开时一样-在本例中是递增的。

这个概念是static关键字表明变量在范围上被它的封闭块包围,而在语义上被它的父块包围。也就是说,上面的代码大致相当于:

int x = 1;
void doSomething () {
   if ( x < 3 ) {
   } else {
   }
   x += 1;
}

但是x只允许在函数内引用

把这个概念带入Java,现在事情变得更有意义了。static内部类的行为完全像在类外部声明的那样,而非static内部类与它的封闭实例绑定得更紧密——实际上它可以直接引用实例。

:

class Thing {
   static Object thereWillBeOnlyOne = new Object();

的行为很像

Object thereWillBeOnlyOne = new Object();
class Thing {

如果它是合法的

本课到此结束

我认为Java团队把这个命名搞砸了。静态内部类(严格来说,它们的正确名称是"静态嵌套类")与普通类没有任何不同,除了它有一个花哨的名字(Something.MyClass而不是MyClass),并且可以被设为私有(即不能从其他类实例化)。

Map的情况下,它被单独选择,因为Map.Entry的名称清楚地表明EntryMap有关。正如您所建议的那样,使用普通类来实现这一点是完全合理的。唯一的区别是你不能写Map.Entry

我认为他们应该做的是为静态嵌套类使用"非静态"内部类的语法(即只是封闭类中的class),而不是发明一个新的关键字来创建"非静态"内部类,因为它们的行为与正常类不同。也许是attached class之类的。AFAIK选择关键字static是为了避免有太多的保留关键字,但我认为这只是鼓励混乱。

是的,无论嵌套类是静态的,都可以有多个嵌套类的实例。

当嵌套类是静态的,你可以创建它的实例,而不需要一个封闭类的实例,这是一个好处,基本上是静态和非静态嵌套类之间的主要区别。

这是否意味着当封闭对象实例化时,静态成员类实际上没有实例化?

当它的构造函数被调用时被实例化。与非静态类没有任何不同。嵌套类本身在代码第一次访问时由JVM加载。同样,我认为这与其他类相比没有任何不同(虽然不是100%确定,但你可以自己测试)。所以我认为你把术语"通过JVM加载类"one_answers"实例化类"混在一起了。

在这种情况下,Map使用Entry的静态成员类有什么意义呢?为什么不直接在API上使用接口呢?

如前所述,创建静态嵌套类的实例更容易。你不需要一个封闭实例,有时(也许大多数时候)正是你想要的。

参见:

(1)嵌套类

(2) JVM如何判断一个类是否嵌套到另一个类中?

(3) JVM加载嵌套类

您可以沿着这些行搜索其他参考文献。
参考文献(2)似乎是高级的,对你的问题有点次要。

Map使用Entry的静态成员类有什么意义?

这是因为它使包结构在逻辑上是正确的。

为什么不直接在API上使用接口呢?

现在,这是一个没有人愿意被拖入的设计讨论。

最新更新