java编程思想读书笔记 第十章 内部类(上)

本文详细介绍了Java内部类的概念、创建方式及应用场景,包括内部类与外部类的关联、使用.this与.new语法、向上转型等高级特性。

可以将一个类的定义放在另一个类的定义内部,这就是内部类。内部类是一种非常有用的特性,因为它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可见性。

1、创建内部类
创建内部类的方式就如同你想的一样,把类的定义置于外围类的里面:
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 readLable(){
return label;
}
}
// 在Parcel1使用内部类就和使用其他类一样
public void ship (String dest) {
Contents contents = new Contents();
Destination destination = new Destination(dest);
System.out.println(destination.readLable());
}
public static void main(String[] args) {
Parcel1 parcel1 = new Parcel1();
parcel1.ship(“test main”);
}
}
当我们在ship()方法里面使用内部类的时候,与使用普通类没什么不同。在这里,实际的区别只是内部类的名字是嵌套在Parcel1里面的。不过你将会看到,这并不是唯一的区别。
最大的区别就,外部类将有一个方法,该方法返回一个指向内部类的引用;例子如下:
public class Outer {
class Inner{
public void to(){
System.out.println(“Inner”);
}
}
public Inner getInner() {
return new Inner();
}
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.getInner();
inner.to();
}
}
输出:Inner
如果想从外部类的非静态方法之外的任意位置来创建某个内部类的对象,那么必须像在main()方法那样,具体地指明这个对象的类型:OuterClassName.InnerClassName.

2、链接到外部类
当生成一个内部类的对象时,此对象与制造它的外围对象之间就有一种联系,所以它能访问其外围对象的所有成员,而不需要任何特殊条件。此外,内部类还拥有其外围类的所有元素的访

问权,例子如下:
public 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 SequenceSelectoer 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 SequenceSelectoer();
}
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.print(selector.current() + ” “);
selector.next();
}
}
}
输出:0 1 2 3 4 5 6 7 8 9
在这个例子中,你仔细观察的话你会发现方法end()、current()和next()都用到了objects,这是一个引用,它并不是SequenceSelectoer 的一部分,而是外围类中的一个private自动。然

而内部类可以访问其外围类的方法和字段,就像拥有它们似的,这就带来了很大的方便。所以内部类自动拥有对其外部类所有成员的访问权。

3、使用.this与.new
如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this。这样产生的引用自动地具有正确的类型。例子如下:
public class DoThis {
void f(){
System.out.println(“DoThis.f()”);
}
class Inner {
public DoThis outer() {
return DoThis.this;
}
}
public Inner inner(){
return new Inner();
}
public static void main(String[] args) {
DoThis dt = new DoThis();
DoThis.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 inner = dn.new Inner();
}
}
要想直接创建内部类的对象,你不能按照你想的那样,去引用外部类的名字的DotNew ,而是必须使用外部类的对象来创建该内部类的对象。

4、内部类与向上转型
当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地。这是因为此内部类–某个接口的实现–能够完全不可见,并且不可用。所得到的知识指向基类或接

口的引用。例子如下:
public interface Destination {
String readLabel();
}
public interface Contents {
int value();
}
现在Contents 和Destination 表示客户端程序员可用的接口。当取得了一个指向基类或接口的引用时,甚至可能无法找出它确切的类型,例子如下:
public class Parcel4 {
private class PContents implements Contents{
private int i = 11;
@Override
public int value() {
return i;
}
}
protected class PDestination implements Destination{
private String label;
private PDestination(String whereTo){
label = whereTo;
}
@Override
public String readLabel() {
return label;
}
}
public Destination destination(String s) {
return new PDestination(s);
}
public Contents contents() {
return new PContents();
}
}
public class TestParcel {
public static void main(String[] args) {
Parcel4 parcel4 = new Parcel4();
Contents contents = parcel4.contents();
Destination destination = parcel4.destination(“test”);
}
}
Parcel4 内部类增加了一些新东西:内部类PContents是private,所以除了Parcel4 ,没有人能访问它。PDestination是protected ,所以只有Parcel4 及其子类、还有Parcel4 同一个包

中的类能访问PDestination,其他类都不能访问。这意味着,如果客户端程序员想了解或访问这些成员,那是要受限制的。实际上,甚至不能向下转型成private内部类,因为不能访问其

名字,就像在TestParcel 类中看到的那样。通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏了实现的细节。从客户端程序员的角度来看,由于不能访问任何新增加的、

原本不属于公共接口的方法,所以扩展接口是没有价值的。
5、在方法和作用域内的内部类
可以在一个方法里面或者在任意的作用域内定义内部类,有两个理由:(1)如前所示,你实现了某类型的接口,于是可以创建并返回对其的作用;(2)你要解决一个复杂的问题想创建一

个类来辅助你的解决方案,但是你又不希望这个类是公共可用的。
接下来的例子拿前面的例子来修改,以用来实现:(1)一个定义在方法中的类;(2)一个定义在作用域内的类,此作用域在方法的内部;(3)一个实现了接口的匿名类;(4)一个匿名

类,它扩展了有非默认构造器的类;(5)一个匿名类,它执行字段初始化;(6)一个匿名类,它通过实例化实现构造(匿名类不可能有构造器)。
第一个例子展示了在方法的作用域内(而不是在其他类的作用域内)创建一个完整的类,这被称作局部内部类,例子如下:
public class Parcel5 {
public Destination destination(String s){
class PDestination implements Destination{
private String label;
private PDestination(String whereTo){
label = whereTo;
}
@Override
public String readLabel() {
// TODO Auto-generated method stub
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel5 parcel5 = new Parcel5();
Destination destination = parcel5.destination(“test”);
}
}
PDestination类是destination()方法的一部分,而不是Parcel5 的一部分。所以,在destination()之外不能访问PDestination。注意出现在return语句中的向上转型–返回的是

Destination的引用,它是PDestination的基类。你可以在同一个子目录下的任意类中对某个内部类使用类标识符PDestination,这并不会有命名冲突。
接下来用展示如何在任意的作用域内嵌套一个内部类,例子如下:
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 parcel6 = new Parcel6();
parcel6.track();
}
}
TrackingSlip类被嵌入在if语句的作用域内,这并不是说该类的创建是有条件的,它其实与别的类一起编译过了。然而,在定义TrackingSlip的作用域之外,它是不可用的,除此之外,它与普通类一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值