可以将一个类的定义放在另一个类的定义内部,这就是内部类。
1 创建内部类
class Circle {
double radius = 0;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println("drawshape");
}
}
}
2 连接到外部类
到目前为止,内部类似乎还只是一种名字隐藏和组织代码的模式。这些是很有用,但还不是最引人注目的,它还有其他的用途。当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,所以他能访问其外围对象的所有成员,而不需要任何特殊条件。
3 使用 .this与.new
当你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this。
public class Outer {
class Inner{
Outer getOuter(){
return Outer.this;
}
}
}
有时你可能想要告知某些其他对象,去创建其某个内部类的对象。要实现此目的,你必须在new表达式中提供对其他外部类对象的引用,这需要使用.new语法。
public class DotNew{
public class Inner{}
public static void main(String[] args){
DotNew dn=new DotNew();
DotNew.Inner dni=dn.new Inner();
}
}
4 内部类与向上转型
当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地。这是因为此内部类某个接口的实现--能够完全不可见。所得到的只是指向基类或接口的引用,所以能够很方便地隐藏实现细节。
5 在方法和作用域内的内部类
内部类语法覆盖了大量其他更加难以理解的技术。例如,可以在一个方法里面或者在任意的作用域内定义内部类。
这么做有两个理由:
1: 如前所示,你实现了某类型的接口,于是可以创建并返回对其的引用。
2:你要解决一个复杂的问题,想创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的。
局部内部类
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 PDestinnation(s);
}
public static void main(String[] args){
Parcel5 p=new Parcel5();
Destination d =p.destination("Tasmania");
}
}
6 匿名内部类
匿名内部类中使用一个在外部定义的对象,那么编译器会要求其参数引用是final的。
interface Contents{
int value();
}
public class Parcel7 {
public Contents contents(){
return new Contents() {//匿名内部类,类的使用与定义结合到了一起
private int i=1;
public int value(){
return i;
}
};
}
public static void main(String[] args){
Parcel7 p=new Parcel7();
Contents c=p.contents();
}
}
7 嵌套类
如果不想使内部类对象与外部类对象相互联系,那么可以将内部类声明为static,这就是嵌套类。
我们知道,非static内部类必须通过外部类对象创建并且获得一个该外部类对象的引用。然而,对于static类型的内部类:
1、创建静态内部类对象,不需要外部类的对象
2、静态内部类中不能使用外部类的非静态成员(因为没有外部类对象的引用)
3、静态内部类中可以创建staic类中的数据和字段(普通内部类不可以,因为它通过外部类对象创建,不属于类成员)
public class Parcel11 {
public static class ParcelContents implements Contents{//静态内部类
private static int i=11;//静态内部类中的静态变量
public int value(){
return i;
}
}
public static ParcelContents contents(){
return new ParcelContents();
}
public static void main(String[] args){
Contents c=contents();
}
7.1 接口中的内部类
public interface ClassInInterface {
void howdy();
static class Test implements ClassInInterface{
public void howdy(){
System.out.println("Howdy");
}
}
public static void main(String[] args){
new Test().howdy();
}
}
正常情况下,接口内部内不能放置任何代码,但是嵌套类可以作为接口的一部分。
接口中类自动地是public和static的。类是static了,仅仅只是将该类置于接口的命名空间里,不违反接口的规则。同时,该嵌套类可以实现它命名空间下的接口。
8 为什么需要内部类
内部类使得多重继承的解决方案变得完整,一个外部类可以实现多个接口。然而,只有继承一个抽象的类或具体的类,此时如果想继承多个抽象的类或具体的类,那么内部类可以解决多重继承中的问题。
9 内部类的继承
如何继承一个内部类?根据前面的内容,内部类中会有一个捕获自创建它外部类对象的引用,如果继承该内部类,这个外部类的引用也要被连接初始化,内部类中也不存在可连接的默认对象。
class WithInner{
class Inner{}
}
public class InheritInner extends WithInner.Inner{
//public InheritInner(){} 不能使用该构造器,因为没有获取父类外部类的引用
public InheritInner(WithInner wi) {
wi.super();//通过父类外部类的引用初始化继承的内部类
}
public static void main(String[] args){
WithInner wi=new WithInner();
InheritInner i1=new InheritInner(wi);
}
}
10 内部类可以被覆盖吗
如果有一个类,并且该类有一个内部类,那么创建一个新类继承该外部类,然后在新类中重新创建此内部类会覆盖父类中的内部类吗?
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 BigEgg extends Egg{//继承了一个外部类
public class Yolk{//重写继承外部类中的内部类
public Yolk(){
System.out.println("BigEgg.Yolk()");
}
}
public static void main(String[] args){
new BigEgg();
}
}/*Output:
New Egg()
Egg.Yolk()*/
创建自身对象时,会调用父类构造器,然后父类构造器中顶一起了内部类,然后输出的结果并不是子类中的内部类,也就是说父类并没有获取子类内部类的对象,也就不存在转型。两个内部类没有什么关系,它们各自在自己的命名空间内。
class Egg2{
private Yolk y=new Yolk();//先与构造器前初始化,调用内部类构造器
protected class Yolk{
public Yolk(){//内部类构造函数
System.out.println("Egg2.Yolk()");
}
public void f() {//内部类f()方法
System.out.println("Egg2.Yolk().f()");
}
}
public Egg2(){//初始化之前,先初始化字段
System.out.println("New Egg2()");
}
public void insertYolk(Yolk yy){//获取一个Yolk类或子类的引用
y=yy;
}
public void g(){
y.f();//调用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());//首先初始化父类BigEgg,然后调用本类中的Yolk类的构造函数,然后调用inserYolk()方法
}
public static void main(String[] args){
Egg2 e2=new BigEgg2();//调用BigEgg2()构造器,向上转型为Egg2类
e2.g();
}
}/*Output:
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk().f()
*/
11 局部内部类
可以在类中创建匿名内部类,静态内部类,普通内部类,也可以在代码块里创建内部类,典型的方式就是在一个方法体的内部里面创建。局部内部类不能有访问说明符,因为它不是外部类的一部分,但是它可以方位当前代码块内的常量(Java8中传入到内部类的值不一定是final类型的,只要初始化后不被修改即可),以及此外围类的所有成员。
创建局部内部类和创建匿名内部类比较
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()");
}
public int next(){
System.out.print(name);
return count++;
}
}
return new LocalCounter();
}
Counter getCounter2(final String name){
return new Counter(){//匿名内部类
{
System.out.println("Counter()");
}
public int next(){
System.out.print(name);
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++)
System.out.println(c1.next());
for(int i=0;i<5;i++)
System.out.println(c2.next());
}
}/*Output:
LocalCounter()
Counter()
Local inner0
Local inner1
Local inner2
Local inner3
Local inner4
Anonymous inner5
Anonymous inner6
Anonymous inner7
Anonymous inner8
Anonymous inner9*/
分别创建了局部内部类LocalCounter类和匿名内部类实现了Counter接口,该接口用于返回序列中的下一个值,这两个内部类具有了相同行为和能力。
局部内部类和匿名内部类的区别:局部内部类的名字在方法外是不可见的,局部内部类可以重载构造器,而匿名内部类只能用于实例初始化,没有构造器。
所以当我们需要不止一个内部类的对象时,应该选择局部内部类而不是匿名内部类。