Java编程思想 第10章 内部类
标签(空格分隔): JAVA学习
可以将一个类的定义放在另一个类的定义内部,这就是内部类。
10.1 创建内部类
public class Parcell {
class Contents {
private int i = 11;
public int value() {
return i;
}
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() {
return label;
}
}
// Using inner classes looks just like
// using any other class, within Parcell:
public void ship(String dest) {
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcell p = new Parcell();
p.ship("Tasmania");
}
}
/* Output:
Tasmania
*/
如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须像在main()方法中那样,具体地指明这个对象的类型:OuterClassName.InnerClassName(王:直接使用InnerClassName也可以,不知道此处是不是理解有误?)。
public class Parcel2 {
class Contents {
private int i = 11;
public int value() {
return i;
}
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() {
return label;
}
}
public Destination to(String s) {
return new Destination(s);
}
public Contents contents() {
return new Contents();
}
public void ship(String dest) {
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel2 p = new Parcel2();
p.ship("Tasmania");
Parcel2 q = new Parcel2();
// Defining references to inner classes:
Parcel2.Contents c = q.contents();
Parcel2.Destination d = q.to("Borneo");
}
}
10.2 链接到外部类
内部类拥有其外围类的所有元素的访问权。下面的例子说明了这点:
interface Selector {
boolean end();
Object current();
void next();
}
public class Sequence {
private Object[] items;
private int next = 0;
public Sequence(int size) {
items = new Object[size];
}
public void add(Object x) {
if (next < items.length) {
items[next++] = x;
}
}
private class SequenceSelector implements Selector {
private int i = 0;
@Override
public boolean end() {
// 访问外部类Sequence的items对象
return i == items.length;
}
@Override
public Object current() {
return items[i];
}
@Override
public void next() {
if (i < items.length) {
i++;
}
}
}
public Selector selector() {
// 返回外部类Sequence的private类
return new SequenceSelector();
}
public static void main(String[] args) {
Sequence sequence = new Sequence(10);
for (int i = 0; i < 10; i++) {
sequence.add(Integer.toString(i));
}
Selector selector = sequence.selector();
while (!selector.end()) {
System.out.println(selector.current() + " ");
selector.next();
}
}
}
/* Output:
0
1
2
3
4
5
6
7
8
9
*/
10.3 使用.this与.new
如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟.this。下面的示例展示了如何使用.this。
class OuterClass {
// 在外部类之外创建成员内部类的实例
DotThis.Inner inner = new DotThis().new Inner();
}
public class DotThis {
void f() {
System.out.println("DotThis.f()");
}
public class Inner {
public DotThis outer() {
return DotThis.this;
}
}
public Inner inner() {
// 在外部类里面创建成员内部类的实例
return new Inner();
}
public static void main(String[] args) {
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
// or
// DotThis.Inner dti = new DotThis().new Inner();
dti.outer().f();
}
}
/* Output:
DotThis.f()
*/
总结:
- 在外部类里面创建成员内部类的实例:
new InnerClass();
this.new InnerClass();
- 在外部类之外创建成员内部类的实例:
new OutClass().new InnerClass()
静态内部类可以省略new
10.4 内部类与向上转型
10.5 在方法和作用域内的内部类(局部内部类)
局部内部类:
interface Destination {
public String readLabel();
}
public class Parcel5 {
public Destination destination(String s) {
// 局部内部类
class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
Destination d = p.destination("Tasmania");
}
}
PDestination类是destination()方法的一部分,而不是Parcel5的一部分。所以在destination()之外不能访问PDestination。
10.6 匿名内部类
interface Contents {
int value();
}
public class Parcel7 {
public Contents contents() {
return new Contents() { // Insert a class definition
private int i = 11;
@Override
public int value() {
return i;
}
};
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
}
}
上述匿名内部类的语法是下述形式的简化形式:
public class Parcel7b {
class MyContents implements Contents {
private int i = 11;
@Override
public int value() {
return i;
}
}
public Contents contents() {
return new MyContents();
}
public static void main(String[] args) {
Parcel7b p = new Parcel7b();
Contents c = p.contents();
}
}
如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。
public class Parcel9 {
// Argument must be final to use inside
// anonymous inner class:
public Destination destination(final String dest) {
return new Destination() {
private String label = dest;
@Override
public String readLabel() {
return label;
}
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.destination("Tasmania");
}
}
10.6.1 再访工厂方法
10.7 嵌套类
嵌套类:如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static。这通常称为嵌套类。
普通的内部类对象隐式地保存了一个引用,指向创建它的外围类对象。然而,当内部类是static的时候,就不是这样了。嵌套类意味着:
1. 要创建嵌套类的对象,并不需要其外围类的对象。
2. 不能从嵌套类的对象中访问非静态的外围类对象(王:因为外围类对象不一定被创建,所以只能访问静态的外围类对象)。
3. 普通内部类的字段和方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类(王:因为嵌套类声明为static)。但是嵌套类可以包含所有这些东西。
public class Parcel11 {
private static class ParcelContents implements Contents {
private int i = 11;
@Override
public int value() {
return i;
}
}
protected static class ParcelDestination implements Destination {
private String label;
private ParcelDestination(String whereTo) {
label = whereTo;
}
@Override
public String readLabel() {
return label;
}
// Nested classes can contain other static elements:
public static void f() {
}
static int x = 10;
static class AnotherLevel {
public static void f() {
}
static int x = 10;
}
}
public static Destination destination(String s) {
return new ParcelDestination(s);
}
public static Contents contents() {
return new ParcelContents();
}
public static void main(String[] args) {
Contents c = contents();
Destination d = destination("Tasmania");
}
}
10.7.1 接口内部的类
正常情况下,不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分。
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface {
@Override
public void howdy() {
System.out.println("Howdy!");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
/* Output:
Howdy!
*/
书中建议,在每个类中都写一个main()方法,用来测试这个类。这样做有一个缺点,那就是必须带着那些已编译过的额外代码。不过可以使用嵌套类来放置测试代码。
public class TestBad {
public void f() {
System.out.println("f()");
}
public static class Tester {
public static void main(String[] args) {
TestBad t = new TestBad();
t.f();
}
}
}
/* Output:
f()
*/
这生成了一个独立的类TestBed$Tester(要运行这个程序,先执行javac TestBad.java,然后执行java TestBed\$Tester即可,Unix/Linux系统中必须转义$)。
10.7.2 从多层嵌套类中访问外部类的成员
一个内部类被嵌套多少层并不重要——它能透明地访问所有它所嵌入的外围类的所有成员。
class MNA {
private void f() {}
class A {
private void g() {}
public class B {
void h() {
g();
f();
}
}
}
}
public class MultiNestingAccess {
public static void main(String[] args) {
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
MNA.A.B mnaab = mnaa.new B();
mnaab.h();
}
}
可以看到在MNA.A.B中,调用方法g()和f()不需要任何条件(即使它们被定义为private)。
10.8 为什么需要内部类
使用内部类最吸引人的原因是:
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
如果拥有的是抽象的类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承。
10.8.1 闭包与回调
闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类胡权操作所有的成员,包括private成员。
10.9 内部类的继承
10.10 内部类可以被覆盖吗
10.11 局部内部类
局部内部类:
1.在java中将类定义在方法的内部,成为局部内部类。
2.此类不能使用public、private修饰,
3.其作用域被限定在声明此类的方法中。
4.和其他内部类相比,它具有可以访问方法参数的优点。
5.被局部内部类使用的方法参数必须是final的。
例如:
public void funtion(){
class ObjectA{}
}
注意:由于局部内部类并不可见,因此不如使用匿名内部类替代。
只有当需要定义或重写类的构造方法,或者需要多个类的对象时,才建议使用局部内部类。
10.12 内部类标识符
由于每个类都会产生一个.class文件,内部类生成的.class文件的命名规则:外围类的名字,加上”$”,再加上内部类的名字。
例如,LocalInnerClass.java生成的.class文件包括:
Counter.class
LocalInnerClass$1.class
LocalInnerClass$1LocalCounter.class
如果内部类是匿名的,编译器会简单地产生一个数字作为标识符。