我想知道封闭类可以创建多少个静态成员类的实例。我只假设一个,但是以下Bloch的摘录对我来说没有意义。
引用Joshua Bloch的有效Java - 第22项*:支持非静态成员类。
私有静态成员类的常见用法是表示由其封闭类表示的对象的组件。例如,考虑一个Map键,它将键与值相关联。许多Map实现都为地图中的每个键值对都有一个内部Entry对象。虽然每个条目都与一个映射关联,但条目上的方法(getKey,getValue和setValue)不需要访问映射。因此,使用非静态成员类来表示条目是浪费的:私有静态成员类是最好的。如果您不小心在条目声明中省略了静态修饰符,则地图仍然有效,但每个条目都将包含对地图的多余引用,这会浪费空间和时间。
他声明地图为地图中的每个键值对创建了一个Entry对象,即静态成员类的多个实例。
所以我的假设是错的!这意味着我对静态成员类的理解是错误的。每个人都知道静态成员变量的行为方式,例如经典的静态最终字符串 - 只有一个对象的实例。
这是否意味着当实例化封闭对象时,实际上没有实例化静态成员类?
那么在那种情况下,使用静态成员类进行Entry的Map是什么意思?为什么不在API上使用接口?然后,每个其他Collections类都可以提供它自己的实现。
[*]刚才意识到我所拥有的PDF版本中的第18项
答案 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
的迭代器,它们都会走相同的字符串。
这种看似不合逻辑的二元性源于static
中C
关键字的使用。
在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
表明Entry
与Map
有关。正如你的建议,仅仅使用一个普通的类是完全合理的。唯一的区别是你不能写Map.Entry
。
我认为他们应该做的是使用&#34;非静态&#34;的语法。静态嵌套类的内部类(即封闭类中的class
),而是创建一个新的关键字来创建非静态的&#34;内部类,因为它们的行为与普通类不同。也许像attached class
这样的东西。选择AFAIK关键字static
是为了避免使用太多保留关键字,但我认为这只是鼓励混淆。
答案 2 :(得分:3)
是的,无论嵌套类是静态的,您都可以拥有嵌套类的许多实例。
当嵌套类是静态可以创建它的实例,而无需封闭类的一个实例,这是一个优势,并且基本上静态和非静态的嵌套类之间的主要区别。
这是否意味着当实例化封闭对象时,实际上没有实例化静态成员类?
在调用它的构造函数时进行实例化。与非静态类没有任何不同。当代码首次访问时,嵌套类本身由JVM加载。我认为,与其他课程相比,这并没有任何不同(尽管不是100%肯定,但你可以自己测试)。因此,我认为您可以将这些术语混合在一起,并且通过JVM加载课程&#34;和&#34;实例化课程&#34;。
那么在那种情况下,使用静态成员类进行Entry的Map是什么意思?为什么不在API上使用接口?
如上所述,创建静态嵌套类的实例更容易。你不需要一个封闭的实例,有时(可能大部分时间)正是你想要的。
另见:
(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上使用界面?
现在,这是一个设计讨论,没有人愿意被拖入。