Bloch Effective Java - 支持非静态的静态类 - 有多少个实例?

时间:2014-07-25 10:14:28

标签: java oop nested-class static-class

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

引用Joshua Bloch的有效Java - 第22项*:支持非静态成员类。

  

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

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

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

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

那么在那种情况下,使用静态成员类进行Entry的Map是什么意思?为什么不在API上使用接口?然后,每个其他Collections类都可以提供它自己的实现。

[*]刚才意识到我所拥有的PDF版本中的第18项

4 个答案:

答案 0 :(得分:7)

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

当您对变量使用static时,意味着对于此类的所有对象只会有其中一个

static Object thereWillBeOnlyOne = new Object();

然而,在内部类的上下文中,它意味着完全不同的东西。 static内部类与封闭类的对象没有关联,而非静态内部类具有


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> {

我的Map.Entry类使用的TrieMap类不需要引用创建它的对象,因此可以使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;
    }

CharacterIterator对象中的StringWalker引用要作为s迭代的字符串,该字符串仅在StringWalker对象中存在一次。因此,我可以创建许多StringWalker的迭代器,它们都会走相同的字符串。


为什么会这么奇怪?

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

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 {

如果合法的话。

这里吸取了教训。

答案 1 :(得分:4)

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

如果是Map,则仅选择它,因为名称Map.Entry表明EntryMap有关。正如你的建议,仅仅使用一个普通的类是完全合理的。唯一的区别是你不能写Map.Entry

我认为他们应该做的是使用&#34;非静态&#34;的语法。静态嵌套类的内部类(即封闭类中的class),而是创建一个新的关键字来创建非静态的&#34;内部类,因为它们的行为与普通类不同。也许像attached class这样的东西。选择AFAIK关键字static是为了避免使用太多保留关键字,但我认为这只是鼓励混淆。

答案 2 :(得分:3)

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

当嵌套类是静态可以创建它的实例,而无需封闭类的一个实例,这是一个优势,并且基本上静态和非静态的嵌套类之间的主要区别。

  
    

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

  

在调用它的构造函数时进行实例化。与非静态类没有任何不同。当代码首次访问时,嵌套类本身由JVM加载。我认为,与其他课程相比,这并没有任何不同(尽管不是100%肯定,但你可以自己测试)。因此,我认为您可以将这些术语混合在一起,并且通过JVM加载课程&#34;和&#34;实例化课程&#34;。

  
    

那么在那种情况下,使用静态成员类进行Entry的Map是什么意思?为什么不在API上使用接口?

  

如上所述,创建静态嵌套类的实例更容易。你不需要一个封闭的实例,有时(可能大部分时间)正是你想要的。

另见:

(1) Nested Classes

(2) How can the JVM decide if a class is nested into another class?

(3) Loading of a nested class by the JVM

您可以沿着这些行搜索其他参考文献 参考文献(2)似乎是先进的,也是你问题的外围因素。

答案 3 :(得分:2)

  

使用静态成员类进行输入的地图是什么?

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

  

为什么不在API上使用界面?

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