内部类
简单来说,/将一个类的定义放在另一个类的定义内部,这就是内部类。内部类是一种非常有用的特性,因为它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可视性。
10.1 创建内部类
把类的定义置于外围类的里面。
/**
* 创建内部类
* @author Administrator
*/
public class Parcel1 {
class Contents{
private int i = 11;
public int value() {
return i;
}
}
class Destination{
private String label;
public Destination(String whereTo) {
label = whereTo;
}
String readLabel(){
return label;
}
}
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) {
Parcel1 p = new Parcel1();
p.ship("哈哈");
}
}
10.2 链接到外部类
当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了以一种联系。所以它能访问其外围对象的所有成员,而不需要任何特殊条件。此外,内部类还拥有其外围类的所有元素的访问权。
这是如何做到的呢?当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。然后,在你访问此外围类的成员时,就是那个引用来选择外围类的成员。
public class Sequence {
private Object[] items;
private int next = 0;
public Sequence(int size) {
this.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() {
return i == items.length;
}
@Override
public Object current() {
return items[i];
}
@Override
public void next() {
if (i < items.length) {
i++;
}
}
}
public Selector selector(){
return new SequenceSelector();
}
public static void main(String[] args) {
Sequence sequence = new Sequence(10);
for (int i = 0; i < 10; i++) {
sequence.add(i);
}
Selector selector = sequence.selector();
while (!selector.end()){
System.out.println(selector.current());
selector.next();
}
}
}
10.3 使用.this和.new
如果想要生成对外部对象的引用,可以用外部类名后跟.this的形式。
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();
dti.outer().f();
}
}
如果要创建内部类的对象,必须在new表达式时提供其外部类的引用。形式是外部类引用.new形式。
public class DotNew {
public class Inner {}
public static void main(String[] args) {
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner();
}
}
10.4 内部类与向上转型
当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地。(从实现了某个接口的对象,得到对此接口的引用,与向上转型为这个对象的基类,实质上效果是一样的)这是因为此内部类—某个接口的实现—能够完全不可见,并且不可用。所得到的只是指向基类或接口的引用,所以能够很方便地隐藏实现细节。
/**
* 内部类向上转型
* @author Administrator
*/
class Parcel4{
private class pContents implements Content{
private int i = 11;
@Override
public int value() {
return i;
}
}
protected class PDestination implements Destinations{
private String label;
public PDestination(String whereTo) {
label = whereTo;
}
public String readLabel(){
return label;
}
}
public Destinations destinations(String s) {
return new PDestination(s);
}
public Content contents() {
return new pContents();
}
}
public class TestParcel {
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Content c = p.contents();
Destinations d = p.destinations("哈哈");
}
}
10.5 在方法和作用域的内部类
/**
* 方法内部类
* @author Administrator
*
*/
public class Parcel5 {
public Destinations destination(String s) {
class pDestination implements Destinations{
private String label;
public pDestination(String whereTo) {
label = whereTo;
}
public String readLabel(){
return label;
}
}
return new pDestination(s);
}
}
/**
* 作用域内部类
* @author Administrator
*
*/
public class Parcel6 {
private void internalTracking(boolean b) {
if(b){
class TrackingSlip{
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip(){
return id;
}
}
TrackingSlip ts = new TrackingSlip("slip");
String s= ts.getSlip();
}
}
public void track() {
internalTracking(true);
}
public static void main(String[] args) {
Parcel6 p = new Parcel6();
p.track();
}
}
10.6 匿名内部类
默认构造匿名内部类
/**
* 匿名内部类
* @author Administrator
*
*/
public class Parcel7 {
public Content content() {
return new Content() {
private int i = 11;
@Override
public int value() {
return i;
}
};
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Content c = p.content();
}
}
带参数匿名内部类
public class Parcel8 {
public Wrapping wrapping(int x) {
return new Wrapping(x) {
private int i = 11;
@Override
public int value() {
return super.value() * 47;
}
};
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Wrapping w = p.wrapping(8) ;
}
}
匿名内部类如果使用一个在其外部定义的对象,其参数的引用必须是final 的。
10.7 嵌套类
如何不需要内部类和外部类有联系,可以将内部类声明为static,通常称为嵌套类。
特性:创建嵌套类的对象,并不需要其外围对象。
不能从嵌套类的对象中访问非静态的外围类对象
- 接口中的内部类
正常情况下接口中不能放置任何代码,但嵌套类可以作为接口中国的一部分。
/**
* 接口中的内部类
* @author Administrator
*
*/
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface{
@Override
public void howdy() {
System.out.println("hai");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
10.8 为什么需要内部类
每个内部类都能独立地继承自一个实现,所以无论外围类是否已经继承了某个实现,对应内部类都没有影响。
- 内部类可以有多个实例,每个实例都有自己的状态信息,并且与外部类信息相独立。
- 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
- 创建内部类对象的时刻并不依赖于外围类对象的创建。
闭包与回调:闭包是一个可调用对象,它记录了一些信息,这些信息来自于创建它的域。
回调的价值在于可以在运行时动态地决定调用什么方法。
内部类与控制框架:
框架的完整实现是由单个类创建,从而使得实现的细节被封装了起来。
内部类能够很容易的访问外围类的任意成员。
(这部分demo无法运行)
10.9 内部类的继承##
因为内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,必须用特殊的语法。
/**
* 内部类的继承
* @author Administrator
*
*/
class WithInner{
class Inner{}
}
public class InheritInner extends WithInner.Inner{
public InheritInner(WithInner wi) {
wi.super(); //初始化
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}
10.10 内部类可以别覆盖吗
不能被覆盖。继承了外围类并覆盖了内部类时,两个内部类是是完全独立的实体,各自在自己的命名空间里。
可以明确的继承某一个内部类。
/**
* 明确继承内部类,覆盖方法
* @author Administrator
*
*/
class Egg2 {
protected class Yolk{
public Yolk() {
System.out.println("Egg2.Yolk()");
}
public void f() {
System.out.println("Egg2.Yolk.f()");
}
}
private Yolk y = new Yolk();
public Egg2(){
System.out.println("new Egg2");
}
public void insertYolk(Yolk yy) {
y = yy;
}
public void g() {
y.f();
}
}
public class BigEgg2 extends Egg2{
public class Yolk extends Egg2.Yolk{
public Yolk() {
System.out.println("BigEgg2.Yolk()");
}
public void f() {
System.out.println("BigEgg2.Yolk.f()");
}
}
public BigEgg2() {
insertYolk(new Yolk());
}
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
}
10.11 局部内部类
局部内部类可以定义在方法体内,不能有访问说明符,因为它不是外围类的一部分;但是可以访问外围类成员。
/**
* 局部内部类
* @author Administrator
*/
interface Counter{
int next();
}
public class LocalInnerClass {
private int count = 0;
Counter getCounter (final String name){
class LocalCounter implements Counter{
public LocalCounter() {
System.out.println("LocalCounter()");
}
@Override
public int next() {
System.out.println(name +" "+count);
return count++;
}
}
return new LocalCounter();
}
Counter getCounter2 (final String name){
return new Counter() {
{
System.out.println("Counter()");
}
@Override
public int next() {
System.out.println(name +" "+count);
return count++;
}
};
}
public static void main(String[] args) {
LocalInnerClass lic = new LocalInnerClass();
Counter c1 = lic.getCounter("Local Inner");
Counter c2 = lic.getCounter2("Anonymous Inner");
for (int i = 0; i < 5; i++) {
c1.next();
}
for (int i = 0; i < 5; i++) {
c2.next();
}
}
}
与匿名内部类比较:可以有一个命名的构造器,可以创建不止一个局部内部类对象。
10.12 内部类标识符
每个类都会产生.class文件,内部类也会产生一个.class文件,命名格式:外围类名字+$+内部类名字。
10.13 总结
接口和内部类结合起来就能解决多重继承的问题。