thinkinginjava学习笔记09_内部类

定义与创建

将一个类定义放在另一个类、方法、作用域、匿名类等地方,就是内部类;内部类只能由外部类对象创建(通过外部方法或者.new方法),内部类对象创建时必须已经有一个外部类对象,并且与之连接(在内部类中会创建一个指向其外部类对象的引用),内部类可以访问到外部类对象的所有成员(包括private);

如:示例代码中,在Parcel类的内部定义了内部类:Contents和Destination,在外部类方法ship中,可以直接创建内部类对象;但是在类外部,只能想创建外部类对象q,然后再通过该对象的contents()和to()方法来创建内部类对象;或者可以通过q2.new来调用内部类的构造方法;注意内部类的引用类型格式是OutClass.InnerClass;

在内部类中,可以使用OutClass.this来调用外部类对象(必然存在该对象,且和内部类对象连接);

当内部类指定是private时,只能在类的内部调用该内部类,外部类对象无法访问并且创建该内部类对象;protected内部类则只有外部类的子类可以访问;通过创建private内部类可以通过类来完成一些工作并且隐藏所有类定义的细节;

在作用域中使用类时,超过作用域该类就无法被访问;如示例代码展示了几种内部类的定义方式;

注意在Parcel7、Parcel8中使用了匿名类,通过基类和接口创建匿名类对象的同时创建类的定义,匿名类的意义为:创建继承自一个基类或者接口的匿名类对象,默认添加继承关系;

使用匿名类时,不要忘记类定义后的分号;

如果如Parcel9中匿名内部类使用外部类中定义的对象,那么编译器会要求其参数引用是final的(如外部类方法destination中),否则将会出错;

如AnonymousConstructor中,可以通过对实例的初始化实现一个匿名构造器功能;如果匿名类中没有直接使用i,则外部方法的参数不需要指定是final的;又如Parcel10中,由于if语句并不能在初始化时被执行,所有匿名类中对实例化的初始化实际效果就相当于是构造器;

匿名内部类只能实现接口和扩展类的其中一个;

使用内部类改进工厂方法

在上一节最后使用到了工厂方法,将多个子类的构造器使用通用接口包装,然后对该接口进行调用即可实现多种子类的构造活动;而由于子类的构造器是不可见的,并且没有必要创建作为工厂的具名类,而只需要一个单一的工厂对象即可,使用匿名内部类改进后的代码为:示例代码;改进后的代码更加具有实际意义(消除了每个子类单独的工厂类);

嵌套类与单元测试

当不需要内部类和外部类的对象之间有联系时,可以将内部类声明为static,这种方式称为嵌套类;

嵌套类由于申明了static,并不像内部类一样,具有一个外部类的对象引用;一旦创建了嵌套类,就说明了嵌套类中的对象并不需要外部类对象的引用,并且不能从嵌套类的对象中访问非静态的外部类对象,没有new方法和this;

正常情况下,接口中是不能放置任何代码的,但是嵌套类可以作为接口的一部分;由于接口中所有的成员都将变为static和public,类也一样,一旦放入接口中,该类就是一个嵌套类;如:示例代码

通过在每个类中添加main()方法可以完成单元测试的工作,但是这样做会将带上已经编译后的额外代码,通过将main方法放置到嵌套类中,可以优化单元测试,如: 示例代码;在编译之后,每个嵌套类(以及内部类)都会产生一个OutClass$InnerClass.class类似的文件,单元测试的类也一样,只要运行java TestBed\$Tester即可进行测试工作;只要在发布时,简单的将该文件删除就可以删除单元测试的内容;

内部类应用

1. 实现多重继承

通过使用内部类独立继承自一个接口的实现完成多重继承的工作;同时由于外部类不能同时继承自两个基类,只能通过内部类完成多重继承;

如:示例代码中,A、B接口可以通过X、Y两种方式实现多重继承;C、D抽象类则只能通过Z来实现多重继承;

2. 在同一个外围类中,可以使用多个内部类以不同的方式实现同一个接口,这样可以在同一个类中完成一个接口的多种实现而不用另外创建一个新类,通过这种方式可以形成更加清晰的代码结构;如Sequence中实现ForwardSelector和ReverseSelector两种Selector接口实现:

interface Selector{

? ? char getCharAt(int i);

}

?

class Sequence {

? ? private String s;

? ? ? ? Sequence(String s){

? ? ? ? this.s = s;

? ? }

? ? private Selector fs(){

? ? ? ? return new Selector(){

? ? ? ? ? ? ?public char getCharAt(int i){

? ? ? ? ? ? ? ? ?return s.charAt(i);

? ? ? ? ? ? ?}

? ? ? ? };

? ? }

? ? private Selector rs(){

? ? ? ? return new Selector(){

? ? ? ? ? ? ?public char getCharAt(int i){

? ? ? ? ? ? ? ? ?int index = s.length() - i;

? ? ? ? ? ? ? ? ?return s.charAt(index);

? ? ? ? ? ? ?}

? ? ? ? };

? ? }

? ? public char forwardSelector(int i){return fs().getCharAt(i);}

? ? public char reverseSelector(int i){return rs().getCharAt(i);}

}

3. 实现回调

当一个外部类继承自一个接口的实现时,可以通过内部类完成接口的另一个实现;并在适当的适合进行选择;如:示例代码?中,Callee2继承自MyIncrement,该类是接口Incrementable的一个实现,Callee2在某些时候需要使用继承来的方法,但是同时如果又有对该接口的另外实现,则可以通过新建内部类:Closure来完成,在Closure中返回一个Callee2的钩子,并且只能调用increment方法;并且通过设置内部类为private以及getCallbackReference()方法来隐藏实现过程;

4. 控制框架的编写;

控制框架往往由单个类来创建,用内部类来表示解决问题所必须的各种不同的action(),从而使得实现的细节被封装起来;

注意要点

1. 内部类的继承

由于内部类必须要和一个外部类的对象连接才可以被实例化,所以在继承一个内部类时,在构造方法中应该引用一个其外部类的对象,并且必须使用:

outclassReference.super()

的方式进行引用;如:

class WithInner{

? ? class Inner{}

}

?

public class InheritInner extends WithInner.Inner {

? ? InheritInner(WithInner wi){

? ? ? ? wi.super();

? ? }

}

2. 内部类的覆盖

内部类和外部类是完全独立的个体,各自在自己的命名空间内,如:示例代码中,在new BigEgg()时,先调用基类构造方法,打印New Egg()以及调用基类的内部类Yolk构造方法;此时,BigEgg()中的Yolk类没有起到作用,但是,如果添加BigEgg的构造方法,并且在其中创建Yolk内部类,则是可以完成覆盖的(其实是生成了一个同名方法);真正完成内部类的覆盖,则需要使内部类也继承基类中的内部类,并完成改写操作,如下面的示例;

示例代码中还可以看到内部类的初始化过程:

基类的内部类 -> 基类 -> 基类内部类 -> 子类内部类 -> 子类;

3. 局部内部类

当一个内部类在作用域内创建时,则会局部内部类,局部内部类不能访问说明符,不是外部类的一部分,而是作用域的一部分,所以可以访问作用域内的常量以及外部类的成员;

局部作用域常在方法体内定义,并且被用来重载构造器,或者生成多个内部类对象等;

时间: 2024-10-07 15:27:22

thinkinginjava学习笔记09_内部类的相关文章

Java学习笔记之 内部类

1. 什么是Java中的内部类 内部类(Inner Class)就是定义在另外一个类里面的类.与之对应,包含内部类的类被称为外部类. 它的主要作用: a. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类. b. 内部类的方法可以直接访问外部类的所有数据,包括私有数据. c. 内部类所实现的功能使用外部类同样可以实现.只是有时使用内部类更加方便. d. 内部类可分为:成员内部类,静态内部类,方法内部类,匿名内部类 四种. 简单实例: //外部类HelloWo

thinkinginjava学习笔记05_访问权限

Java中访问权限等级从大到小依次为:public.protected.包访问权限(没有关键词).private: 以包访问权限为界限,public.protected分别可以被任意对象和继承的对象访问,而包访问权限(未加关键词的类.方法等)则不能在包外被访问:在一个类中,不能出现相同类成员:在同一个包中,不可以出现相同类: public作为接口访问权限,将一些方法作为public,即将该方法作为接口,供其他程序调用:private权限下,除了包含该成员的类之外,其他任何类都无法访问该成员,用来

thinkinginjava学习笔记06_复用类

MarsEdit粘代码好麻烦,所有代码交给github:https://github.com/lozybean/MyJavaLearning 复用一个类常用的两种方式:组合.继承: 组合 将对象引用置于新类中,新类就完成了这些对象的复用: Java中,每个非基本对象都有一个toString方法,当需要一个String对象时直接调用:对象的引用在类的定义中会被初始化为null:引用初始化可以在四个地方进行:对象定义时 > 实例初始化 > 类构造器中 ?> 使用对象之前(按初始化顺序排列):

thinkinginjava学习笔记07_多态

在上一节的学习中,强调继承一般在需要向上转型时才有必要上场,否则都应该谨慎使用: 向上转型和绑定 向上转型是指子类向基类转型,由于子类拥有基类中的所有接口,所以向上转型的过程是安全无损的,所有对基类进行的操作都可以同样作用于子类:如示例代码中,Music.tune方法调用时,需要的参数是基类Instrument,而传入一个子类:Wind类的对象时,该方法一样可以被调用,并且play方法执行的是Wind类的对象重载的方法: 在向上转型的设计中,只编写和基类打交道的代码,这样所有的特定子类都可以正确

thinkinginjava学习笔记04_初始化与清理

java沿用了c++的构造器,使用一个和类名完全一样的方法作为类的构造器,可以有多个构造器来通过不同的参数进行构造,称为重载:不仅是构造器可以重载,其他方法也一样通过不同的形参以及不同的返回值来实现重载: 当创建一个新的对象的时候,java就需要对该对象进行初始化(如果没有创建构造器,java会自动创建一个无参构造器,也称默认构造器,并对对象进行初始化),构造器函数只对对象进行初始化操作,并没有任何返回值(也不是返回void): java中区分两个方法时,使用每个方法的参数列表(包括参数的顺序)

Java面向对象学习笔记 -- 6(内部类、Timer)

1. 内部类 内部类就是在一个类的内部定义的类,有:静态内部类.成员内部类,局部内部类.匿名内部类. -1) 静态内部类:使用static修饰,声明在类体中, 静态内部类中可以访问外部类的静态成员,开发很少用 -2) 成员内部类:声明在类体中,不使用static,具有类的成员特征,也就是,必须有类的实例才能创建内部类实例,内部类实例可以访问共享外部类的成员变量 -3) 局部内部类:把类声明在方法中,就是局部内部类,作用域类似局部变量,出了方法就不能使用该类. -4) 匿名内部类:非常常见,可以写

Java学习笔记:内部类/匿名内部类的全面介绍

编写java程序时,一般一个类(或者接口)都是放在一个独立的java文件中,并且类名同文件名(如果类是public的,类名必须与文件名一致:非public得,无强制要求).如果想把多个java类放在一个java文件中,则只能有一个public类.如下面的两个类放在同一个文件中就会报错,无法编译通过. 可以看出,因为TestOne.java文件中已经有一个public类TestOne,这时再加了一个public类TestTwo就报错了.如果将类TestTwo前面的public修饰符去掉就没有问题了

thinkinginjava学习笔记10_容器

Java中并没有像Perl.Python.Ruby那样对容器有直接的支持,但是可以依靠容器类来完成相同的工作: 泛型 使用一个ArrayList对象可以保存一系列的对象,如: ArrayList apples = new ArrayList(); 申明一个ArrayList对象apples,接下来可以往apples中添加对象:apples.add(object),也可以根据索引获取对象:apples.get(index),此时,放置的对象都是Object类型的,这是不安全的类型,因为不能保证所有

thinkinginjava学习笔记03_基本语法

由于java是c系语言,基本保留c语言的所有基本操作,就快速过一下: java中的基本操作符仅仅对基本类型有效:=.==.!=对所有对象有效(值传递),String类支持+.+=; 在对基本数据进行算术运算或者按位运算时,只要类型比int小(char.byte.short)都会自动转换成int,最终结果都是int:java中,如果两个类型不同的值进行运算,则会转换成教大的类型再进行运算,并且得到的结果为较大的类型,如float和double运算时,float自动转换成double,并且运算结果为