第九章
可以选择在接口中显示地将方法声明为public,但即使不这样做它们也是public
放入接口域(成员)为static和final,在定义时必须初始化(编译器不会给默认值)也可以非常量表达式初始化,这些域不是接口的一部分,它们的值被存储在该接口的静态存储区域内。
**
组合接口时的名字冲突
**
interface CanFight{void fight();}
interface CanSwim{void swim();}
interface CanFly{void fly();}
class ActionCharacter
{
public void fight(){}
}
class Hero extends ActionCharacter implements CanFight,CanSwim,CanFly
{
public void swim(){}
public void fly(){}
}
CanFight和ActionCharacter都有一个相同的void fight()方法,这并不会引起错误,即使Hero没有显式地提供fight()的定义,其定义也因ActionCharacter而随之而来,这样使得创建Hero对象成为可能。当然返回类型不同时,也自然报错。
如果参数列表不同,显然也是不被允许的,因为覆盖、实现和重载交织在一起,重载方法又不能通过返回类型区分。实践时,请尽量避免这种情况。
嵌套接口
class A
{
interface B{ void f();}
public class BImp implements B{ public void f(){}}
private class BImp2 implements B{ public void f(){}}
public interface C{ public void f();}
class CImp implements C{ public void f(){}}
private class CImp2 implements C{ public void f(){}}
private interface D{ public void f();}
private class DImp implements D{ public void f(){}}
public class DImp2 implements D{ public void f(){}}
public D getD(){ return new DImp2();}
private D dRef;
public void receiveD(D d){ dRef=d; dRef.f();}
}
interface E
{
interface G{ void f();}
public interface H{ void f();} //Redundant"public" 可省略”public“
void g();
//"The interface member type I can only be public", 接口内的接口只能为”public“
//private interface I{}
}
public class test
{
public class BImp implements A.B{ public void f(){}}
class CImp implements A.C{ public void f(){}}
//Cannot implement a private interface except within that interface's defining class
//除了定义该私有接口的类以外,不能使用私有接口
//class DImp implements A.D{ public void f(){}}
class EImp implements E{ public void g(){}}
class EGImp implements E.G{ public void f(){}}
public static void main(String[] args)
{
A a=new A();
//A.D ad= a.getD(); //Can't access A.D 不能定义
//A.DImp2 di2= a.getD(); //返回值为A.D类型
//a.getD().f(); //Cannot access a member of the interface 返回类型D为private
a.receiveD(a.getD());
}
}
作为嵌套接口,接口也可以被实现为private。
Private接口可以强制接口中的方法定义不能添加任何类型信息(也就是说,不允许向上转型)
第十章
链接到外部类
内部类自动拥有对其外部类所有成员的访问权。因为,当某个外围类的对象创建了一个内部类的对象时,此内部类对象必定会秘密捕捉一个指向那个外围类对象的引用。然后,在访问此外围类成员时就用该引用访问外围类成员(编译器会处理所有细节)。
使用.this与.new
如果需要生成对外部类的引用,可以使用外部类的名字后紧跟“.this”(有圆点,下同),这样产生的引用自动地具有正确的类型。
public class test
{
void f(){ System.out.println("test.f()");}
public class Inner
{
public test outer()
{
return test.this;
}
}
public Inner inner(){ return new Inner();}
public static void main(String[] args) {
test t= new test();
test.Inner ti=t.inner();
ti.outer().f();
}
}
//运行结果:
//test.f()
有时可能需要告知其他对象,去创建某个内部类对象。要实现此目的,需要用new表达式中提供对其他外部类对象的引用,这需要“.new”语法。
public class test
{
public class Inner{}
public static void main(String[] args) {
test t= new test();
test.Inner ti= t.new Inner();
}
}
想要直接创建内部类对象,不能直接去引用外部类的名字,而是必须使用外部类的对象来创建该内部类对象。
在拥有外部类对象之前是不可能创建内部类对象的。因为内部类对象会暗暗地连接到创建它的外部类对象上(但是嵌套类,即静态内部类,是不需要对外部类的引用的)。
匿名内部类
Contents()方法将返回值的生成与表示这个返回值的类的定义结合在一起。注释部分为“取名”了的“匿名类”
interface Contents{}
public class test
{
public Contents contents()
{
return new Contents(){
private int i=11;
public int value(){ return i;}
};
}
public static void main(String[] args) {
test t= new test();
Contents c= t.contents(); //c为test类
//System.out.println(c.toString());
}
}
/*
public class test
{
class MyContents implements Contents
{
private int i=11;
public int value(){ return i;}
}
public Contents contents(){ return new MyContents();}
public static void main(String[] args)
{
test t= new test();
Contents c= t.contents();
}
}*/
嵌套类
如果不需要内部类对象与外围类对象之间的联系,那么可以将内部类声明为static
嵌套类意味着:
要创建嵌套类的对象,并不需要其外围类的对象
不能从嵌套类的对象中访问非静态的外围类对象
接口内部类
正常情况下,不能在接口内部放置任何代码,但嵌套类可以作为接口一部分。放入接口中的任何类都自动地是public和static的,因为类是static的,只是将嵌套类置于接口的命名空间内,并不违反接口的规则。甚至在内部类都可以实现外围接口。
public interface test
{
void howdy();
class Test implements test
{
public void howdy()
{
System.out.println("Howdy!");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
//输出:
//Howdy!
如果想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共用,那么使用内部接口的嵌套类将会很方便。
一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员。
内部类的继承
因为内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,情况会变得复杂。即,指向外围类对象的“秘密的“引用必须被初始化,而在导出类中不再存在可连接的默认对象,因此需要特殊的语法:
class outer
{
class inner{}
}
public class test extends outer.inner
{
//test(){} 编译错误!
test(outer o)
{
o.super();
}
}
内部类可以被覆盖吗
class Egg
{
private Yolk y;
protected class Yolk
{
public Yolk(){ System.out.println("Egg.Yolk()");}
}
public Egg()
{
System.out.println("new Egg()");
y= new Yolk();
}
}
public class test extends Egg
{
public class Yolk{ public Yolk(){ System.out.println("test.Yolk()");}}
public static void main(String[] args) {
new test();
}
}
//输出:
/*
new Egg()
Egg.Yolk()
*/
默认构造器是编译器自动生成的,这里调用基类的默认构造器。可能大家会觉得,既然创建了test的对象,所使用的应该是“覆盖后“的Yolk版本(和之前的方法覆盖对比看),但从输出结果来看并非如此。
原因是,继承某个外围类的时候,内部类并没有发生特别的变化。这两个内部类是安全独立的两个实体,各自在各自的命名空间。当然,也可以明确继承某类的内部类。
class Egg
{
protected class Yolk
{
public Yolk(){ System.out.println("Egg.Yolk()");}
public void f(){ System.out.println("Egg.f()");}
}
private Yolk y= new Yolk();
public Egg(){ System.out.println("new Egg()");}
public void insertYolk(Yolk yy){ y=yy;}
public void g(){ y.f();}
}
public class test extends Egg
{
public class Yolk extends Egg.Yolk //明确继承Egg的内部类
{
public Yolk(){ System.out.println("test.Yolk()");} //覆盖内部的方法
public void f(){ System.out.println("test.f()");}
}
public test(){ insertYolk(new Yolk());}
public static void main(String[] args) {
Egg e= new test(); //向上转型
e.g(); //调用的g()是覆盖后的方法
}
}
//输出结果:
/*
Egg.Yolk()
new Egg()
Egg.Yolk()
test.Yolk()
test.f()
*/
局部内部类
典型的代表就是方法题内创建,局部类不能有访问说明符,因为不是外围类的一部分,但可以访问当前代码块内的常量,以及此外围类的所有成员。
interface Counter{ int next();}
public class test
{
private int count=0;
Counter getCounter(final String name)
{
class LocalCounter implements Counter
{
//局部内部类可以有构造方法
public LocalCounter(){ System.out.println("LocalCounter()");}
public int next()
{
System.out.println(name);
return count++;
}
}
return new LocalCounter();
}
//用匿名类展示
Counter getCounter2(final String name)
{
return new Counter(){ //匿名类
{ System.out.println("Counter()");}
public int next()
{
System.out.println(name);
return count++;
}
};
}
public static void main(String[] args) {
test temp= new test();
Counter
c1=temp.getCounter("Local Inner"),
c2=temp.getCounter2("Anonymous");
for(int i=0;i<5;i++) System.out.println(c1.next());
for(int i=0;i<5;i++) System.out.println(c2.next());
}
}
//输出:
/*
LocalCounter()
Counter()
Local Inner
0
Local Inner
1
Local Inner
2
Local Inner
3
Local Inner
4
Anonymous
5
Anonymous
6
Anonymous
7
Anonymous
8
Anonymous
9
*/
选用局部内部类的理由:
1、我们需要一个已命名的构造器,或重载构造器,而匿名内部类只能用于实例化。2、需要不止一个该内部类的对象。
[1] Bruce Eckel.Java编程思想[M].陈昊鹏,北京:机械工业出版社出版社.2007