可以将一个类定义放在另一个类的定义内部,这就是内部类。

10.1 创建内部类

创建内部类就是把类的定义置于外部类里面。

public class Parcell {
class contents{
int i=0;
public void GetI(){
System.out.println("contents"+i);
i++;
} }
class Destintion{
private String label;
public void GetI(String s){
System.out.println(s);
}
}
public contents GetontentsC(){
return new contents();
}
public void ship(String dest){
contents c=new contents();
c.GetI();
Destintion d=new Destintion();
d.GetI(dest);
} public static void main(String[] args){
Parcell p=new Parcell();
p.ship("dwdwdw");
Parcell.contents c=p.GetontentsC();
c.GetI();
}
}

如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须具体的指明这个对象的类型:OuterClassName.InnerClassName.

10.2 链接到外部类

当生成一个内部类对象时,此对象和制造它的外围对象之间有一种联系,所有,它能访问它外围对象的所有成员,而不需要特殊条件。

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;
}
public class SequenceSelector implements Selector{
private int i=0;
@Override
public boolean end() {
if (i==items.length)
return false;
return true;
} @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(Integer.toString(i));
}
Selector s=sequence.selector();
while (s.end()){
System.out.print(s.current());
s.next();
}
}
}
interface Selector{
boolean end();
Object current();
void 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 d=new DotThis();
DotThis.Inner in=d.inner();
in.outer().f();
}
}

创建某个内部类对象时,需要使用.new。

public class Parcel3 {
class Contents{
private int i=11;
public int value(){return i;}
}
public class Destination{
private String label;
Destination(String whereTo){
label=whereTo;
}
String readLabel(){
return label;
}
}
public static void main(String[] args){
Parcel3 p=new Parcel3();
Parcel3.Contents c=p.new Contents();
Parcel3.Destination d=p.new Destination("123");
System.out.println(d.label);
}
}
10.4 内部类与向上转型

当将内部类向上转型为基类,尤其是一个接口的时候,内部类就很有用了。因为内部类的某个接口的实现是可以完全不可见并且不可用的。

public class TestParcel {
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Contents c = p.contents();
Destination d = p.destination("tasmaina");
}
} interface Destination {
String readLabel();
} interface Contents {
int value();
} class Parcel4 {
private class PContents implements Contents {
private int i = 11; @Override
public int value() {
return i;
}
} protected class PDestionation implements Destination {
private String label; private PDestionation(String whereTo) {
label = whereTo;
} @Override
public String readLabel() {
return null;
}
} public Contents contents() {
return new PContents();
} public Destination destination(String s){
return new PDestionation(s);
}
}

PDestination是protected,所以只要Parce4及其子类,还有与Parce4同一个包中的类能访问PDestination。

private内部类给类设计者提供了一条途径,通过这种方式,可以完全阻止任何依赖于类型的编码,并且完全隐藏实现细节。

10.5 在方法和作用内的内部类

在方法的作用域内,创建一个完整的类

public class Parcel5 {
public Destination destination(String s){
class PDestination implements Destination{
private String label;
private PDestination(String whereTo){
System.out.println(whereTo);
label=whereTo;
}
@Override
public String readLabel() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args){
Parcel5 p=new Parcel5();
Destination d=p.destination("sa");
}
}

在任意的作用域嵌入一个内部类

public class Parcel6 {
private void internalTracking1(boolean b) {
if (b) {
class TrackingSlip {
private String id; TrackingSlip(String s) {
id = s;
System.out.println(id);
} String getSlip() {
return id;
}
}
TrackingSlip trac=new TrackingSlip("asd");
// TrackingSlip ts = new TrackingSlip("slip");
//String s = ts.getSlip();
}
}
public void track() {
internalTracking1(true);
}
public static void main(String[] args){
Parcel6 p=new Parcel6();
p.track();
}
}

TrackingSlip类被嵌入在if语句的作用域内,这不是说该类创建是有条件的,它其实与别的类一起编译过。但是在TrackingSlip的作用域之外,它是不可用的。

10.6 匿名内部类

public class Parcel7 {
public Contents contents(){
return new Contents() {
private int i=11;
@Override
public int value() {
System.out.println(i);
return i;
}
};
}
public static void main(String[] args){
Parcel7 p7=new Parcel7();
p7.contents().value();
}
}

contents()方法将返回值的生成与表示这个返回值的类的定义结合在一起。

通过new表达式返回的引用被自动向上转型。

public class Parcel7b {
class ABContents implements Contents{
private int i=11;
@Override
public int value() {
System.out.println(i);
return i;
}
}
public Contents contents(){
return new ABContents();
}
public static void main(String[] args){
Parcel7b b=new Parcel7b();
Contents c=b.contents();
c.value();
}
}

下面的基类需要一个有参数的构造器

public class Parcel8 {
public Wrapping wrapping(int i){
return new Wrapping(i){
};
}
public static void main(String[] args){
Parcel8 p8=new Parcel8();
Wrapping wr=p8.wrapping(7);
}
}
class Wrapping{
public Wrapping(int i){
System.out.println(i);
}
}

Wrapping只是一个具有具体实现的普通类,但它还是被其导出类当作公共"接口"来使用。

在匿名内部类尾部的分号,不是用来标记内部类结束的,它标记的是表达式的结束,只不过这个表达式正好表示匿名类。

如果定义一个匿名内部类,并希望它使用一个外部定义的对象,那么就要求参数引用是final的。

public class Parcel9 {
public Destination wrapping(final int i){
return new Destination(){
private int label=i;
@Override
public String readLabel() { System.out.println(label);
return "i";
}
};
}
public static void main(String[] args){
Parcel9 p9=new Parcel9();
Destination wr=p9.wrapping(4);
wr.readLabel();
}
}

在匿名类中不可能有命令构造器,但通过实例初始化,就能够达到为匿名内部类创建一个构造器的效果。

public class Parcel8 {
public Wrapping wrapping(int i){
return new Wrapping(i){
{System.out.println(i+1);}
};
}
public static void main(String[] args){
Parcel8 p8=new Parcel8();
Wrapping wr=p8.wrapping(7);
}
}
class Wrapping{
public Wrapping(int i){
System.out.println(i);
}
}

匿名内部类与正规的继承相比有些受限,因为匿名类既可以扩展类,也可以是实现接口,但不能两者兼备,如果是实现接口,也只能实现一个。

10.6.1 再访工厂模式

优先使用类而不是接口。如果设计里面需要某个接口,那么必须了解它。

10.7 嵌套类

如果不需要内部类对象与其外围对象有联系,可以使用内部类声明为static。这称为嵌套类。

普通的内部类对象隐试地保存了一个引用,指向创建它的外围类对象。当内部类是static的时,就不是这样的了。

嵌套类意味着:

  • 要创建嵌套类的对象,并不需要其外围类的对象。
  • 不能从嵌套类的对象访问非静态外围对象。

普通内部类的字段与方法,只能放在类的外部层次上,所有普通的内部类不能有static数据和static字段,也不能包含嵌套类。

public class Parcel11 {
private static class parceContents implements Contents{ @Override
public int value() {
return 0;
}
}
public static Contents contents(){
return new parceContents();
}
public static void main(){
Contents c=contents();
}
}

在一个普通的内部类中,通过一个特殊的this引用可以链接到其外围类对象,嵌套类就没有这个特殊的this引用,这使得它类似一个static方法。

10.7.1 接口内部的类

正常情况下,不能再接口中放置任何代码,但嵌套类可以作为接口的一部分,放到接口中的任何类都自动是public和static的。

public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface{ @Override
public void howdy() {
System.out.println("howdy");
} public static void main(String[] args){
new Test().howdy();
}
}
}

如果想要创建谋和代码,使得它们可以被某个接口的所有不同实现所公用,那么使用接口内部的嵌套类会显得很方便。

10.7.2 从多层嵌套类中访问外部的成员

一个内部类被嵌套多少层,它都能透明的访问所有它所嵌入的外围类对象。

class MNA {
private void f(){System.out.println("f");}
class A{
private void g(){System.out.println("g");}
public class B{
void h(){
g();
f();
}
}
}
}
public class Multi{
public static void main(String[] args){
MNA mna=new MNA();
MNA.A maa=mna.new A();
MNA.A.B maab=maa.new B();
maab.h();
}
}

.new 语法能参数正确的作用域,所有不必再调用构造器时限定类名。

为什么需要内部类

内部类继承自某个类或实现某个接口,内部类的代码操作创建它外围类的对象,所有可以认为内部类提供了某种进入外围类的接口。

每个内部类都能独立地继承自一个实现,所有无论外围类是否已经继承了某个实现,对于内部类都没有影响。

在一个类中,以某种形式实现两个接口,有两种选择:使用单一类,或者使用内部类。

但是对于抽象类或者具体的类,就只能使用内部类实现多重继承了。

public class MultiImplementation {
static void takesD(D d) {
} static void takesE(E e) {
} public static void main(String[] args) {
Z z = new Z();
takesD(z);
takesE(z.makeE());
}
}
class D {
}
abstract class E {
}
class Z extends D {
E makeE() {
return new E() {
};
}
}
10.8.1 闭包与回调

闭包是一个可调用对象,它记录了一些信息,这些信息来自于创建它的作用域,通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有成员,包括private成员。

通过回调,对象能够携带一些信息,这些信息允许它在稍后某个时刻调用初始的对象。

通过内部类实现回调:

public class Callbacks {
public static void main(String[] args){
Callee1 c1=new Callee1();
Callee2 c2=new Callee2();
MyIncrement.f(c2);
Caller Caller1=new Caller(c1);
Caller Caller2=new Caller(c2.getCallbackReference());
Caller1.go();
Caller1.go();
Caller2.go();
Caller2.go();
}
}
interface Incrementable{
void increment();
}
class Callee1 implements Incrementable{
private int i=0;
@Override
public void increment() {
i++;
System.out.println(i);
}
} class MyIncrement{
public static int i=1;
public void increment(){
System.out.println("Other operation");
}
public static void f(MyIncrement mi){
mi.increment();
}
}
class Callee2 extends MyIncrement{
private int i=0;
public void increment(){
super.increment();
i++;
System.out.println(i);
}
private class Closure implements Incrementable{
@Override
public void increment() {
Callee2.this.increment();
}
}
Incrementable getCallbackReference(){
return new Closure();
}
}
class Caller{
private Incrementable callbackReference;
Caller(Incrementable cbh){
callbackReference=cbh;
}
void go(){callbackReference.increment();}
}
10.8.2 内部类与控制框架

应用程序框架就是被设计用以解决某类特定问题的一个类或一组类。要应用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法。在覆盖后的方法中,编写代码定制应用程序框架提供的通用解决方案,以解决特定的问题。

控制框架是一类特殊的应用程序框架,它用来解决响应事件的需求。主要用来响应事件的系统被称为事件驱动系统

首先,接口描述了要控制的事件:

public abstract class Event {
private long eventTime;
protected final long delayTime;
public Event(long delayTime){
this.delayTime=delayTime;
start();
}
public void start(){
eventTime=System.nanoTime()+delayTime;
}
public boolean ready(){
return System.nanoTime()>=eventTime;
}
public abstract void action();
}

触发事件的实际控制框架:

import java.util.ArrayList;
import java.util.List; public class Controller {
private List<Event> eventList=new ArrayList<Event>();
public void addEvent(Event c){
eventList.add(c);
}
public void run(){
while (eventList.size()>0){
for (Event e:new ArrayList<Event>(eventList)) {
if (e.ready()){
System.out.println(e);
e.action();
eventList.remove(e);
}
}
}
}
}

在现在的设计中,我们还不知道Event到底做了什么。但是这个设计关键就是使变化的事物与不变的事物相互分离。而这就是内部类要做的事情。

内部类允许:

  • 控制框架的完整实现是由单个的类创建,从而使得实现的细节被封装了起来。内部类用来表示解决问题所必须的各种不同的action()。
  • 内部类能够很容易的访问外围类的任意成员,所以可以避免这种实现变得很笨拙。
public class GreenhouseControls extends Controller {
private boolean light = false; public class LightOn extends Event {
public LightOn(long delayTime) {
super(delayTime);
} @Override
public void action() {
light = true;
} public String toString() {
return "Light is on";
}
} public class LightOff extends Event {
public LightOff(long delayTime) {
super(delayTime);
} @Override
public void action() {
light = false;
} public String toString() {
return "Light is off";
}
} public class Restart extends Event {
private Event[] eventList; public Restart(long delayTime, Event[] eventList) {
super(delayTime);
this.eventList = eventList;
for (Event e : eventList)
addEvent(e);
} public void action() {
for (Event e : eventList) {
e.start();
addEvent(e);
}
start();
addEvent(this);
} public String toString() {
return "Restarting system";
}
} public static void main(String[] args){
GreenhouseControls gc=new GreenhouseControls();
Event[] eventList={
gc.new LightOn(20000),
gc.new LightOff(40000)
};
//gc.addEvent(gc.new LightOn(20000));
//gc.addEvent(gc.new LightOff(40000)); gc.addEvent(gc.new Restart(200000,eventList));
gc.run();
}
}

10.9 内部类的继承

内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,那个指向外围类对象的秘密的引用必须被初始化,而在导出类中,不再存在可连接的默认对象。

public class InheritInner extends WithInner.Inner{
InheritInner(WithInner wi){//必须指向一个外围对象的引用
wi.super();
}
public static void main(String[] args){
WithInner wi=new WithInner();
InheritInner i=new InheritInner(wi);
}
}
class WithInner{
class Inner{}
}

10.10 内部类可以被覆盖吗

public class BigEgg extends Egg{
public class Yolk{
public Yolk(){
System.out.println("BigEgg.Yolk()");
}
}
public static void main(String[] args){
new BigEgg();
}
}
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 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();
}
} 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();
}
}

10.11 局部内部类

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.print(name);
return count++;
}
}
return new LocalCounter();
}
Counter getCounter2(final String name)
{
return new Counter()
{
{
System.out.println("Counter()");
}
@Override
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 "),
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());
}
}
interface Counter
{
int next();
}

Java编程思想之十 内部类的更多相关文章

  1. Java编程思想学习(八) 内部类

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结. 一.内部类的分类 总的来讲内部类分为普通内部类,匿 ...

  2. Java编程思想之十四 类型信息

    第十四章 类型信息 运行时类型信息使得你可以在程序运行时发现和使用类型信息 14.1 为什么需要RTTI 面向对象编程中基本的目的是:让代码只操作对基类的引用. 多态: import java.uti ...

  3. Java编程思想学习(十六) 并发编程

    线程是进程中一个任务控制流序列,由于进程的创建和销毁需要销毁大量的资源,而多个线程之间可以共享进程数据,因此多线程是并发编程的基础. 多核心CPU可以真正实现多个任务并行执行,单核心CPU程序其实不是 ...

  4. Java编程思想学习(十) 正则表达式

    正则表达式是一种强大的文本处理工具,使用正则表达式我们可以以编程的方法,构造复杂的文本模式,并且对输入的字符串进行搜索.在我看来,所谓正则表达式就是我们自己定义一些规则,然后就可以验证输入的字符串是不 ...

  5. Java编程思想之十八 枚举类型

    关键字enum可以将一组具名的值的有限集合创建为一种新的类型, 而这些具名的值可以作为常规的程序组件使用.这是一种非常有用的功能. 18.1 基本enum特性 创建enum时,编译器会为你生成一个相关 ...

  6. Java编程思想之十二 通过异常处理错误

    Java的基本概念是结构不佳的代码不能运行余下的问题必须在运行期间解决,这就需要错误源能通过某种方式,把适当的信息传递给某个接收者--该接收者将知道如何正确处理这里问题. 12.1 概念 使用异常所带 ...

  7. Java编程思想学习(十五) 注解

    注解Annotation又叫元数据,是JDK5中引入的一种以通用格式为程序提供配置信息的方式.使用注解Annotation可以使元数据写在程序源码中,使得代码看起来简洁,同时编译器也提供了对注解Ann ...

  8. Java编程思想学习(十四) 枚举

    关键字enum可以将一组具名的值有限集合创建一种为新的类型,而这些具名的值可以作为常规的程序组件使用. 基本enum特性 调用enum的values()方法可以遍历enum实例,values()方法返 ...

  9. Java编程思想学习(十二) 数组和容器

    一.数组 1).数组的多种初始化方式 下面总结了初始化数组的多种方式,以及如何对指向数组的引用赋值,使其指向另一个数组对象.值得注意的是:对象数组和普通数组的各种操作基本上都是一样的:要说有什么不同的 ...

随机推荐

  1. 《 .NET并发编程实战》实战习题集 - 4 - 如何重用一次性资源

    如何重用以下一次性资源代码呢? string text; using (var stream = new StreamReader(path)) { text = stream.ReadToEnd() ...

  2. C#反射_两合并更新实体

    #region 更新实体模型 /// <summary> /// 更新实体模型 /// </summary> /// <typeparam name="T&qu ...

  3. Java自学-I/O 缓存流

    Java 缓存流BufferedReader,PrintWriter 以介质是硬盘为例,字节流和字符流的弊端: 在每一次读写的时候,都会访问硬盘. 如果读写的频率比较高的时候,其性能表现不佳. 为了解 ...

  4. Java 之 JDK1.8之前日期时间类

    一.JDK1.8之前日期时间类 二. java.lang.System类 System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1 ...

  5. 12个有趣的C语言问答

    转自:http://www.admin10000.com/document/913.html 1,gets() 方法 Q:以下代码有个被隐藏住的问题,你能找到它吗? 1 2 3 4 5 6 7 8 9 ...

  6. 实验吧简单的sql注入3

    今天早上起来发现有人评论说我没更新实验吧sql注入3,主要是因为前段时间都去做bugku去了 但是重做这道题发现以前的姿势不行了,exp()报错不再溢出,现在不能用这个姿势,所以这里重新整理了一遍思路 ...

  7. AIX—日常运维命令总结

    1. 查看AIX服务器的物理构造信息,包括服务器网络配置信息 # prtconf # ifconfig -a # lsattr -E -l mem0 :查看系统内存大小 # netstat -in : ...

  8. Window 2003 IIS + MySQL + PHP + Zend 环境配置

    图文详解 下载 Windows 2003 Zend, PHP, PHPMyadmin 与 MySQL Windows 2003 安装包中包含了 Zend,PHP 5.2.17,PHPWind8.7 和 ...

  9. Nginx 核心配置-自定义错误页面

    Nginx 核心配置-自定义错误页面 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 生产环境中错误页面一般都是UI或开发工程师提供的,他们已经在软件中定义好了,我们这里就简单写个h ...

  10. Feign 报错:No fallback instance of type class xxx found for feign client xxx

    通常需要确认配置内容: 开启 Hystrix:feign.hystrix.enabled=true Fallback类需要注解@Component 出处:https://www.jianshu.com ...