第10章 内部类
10.1 创建内部类
public class Parcel {
class A{
String f(){
return "内部类A";
}
}
// 使用内部类
public void getMsg(A a){
System.out.println(a.f());
}
// 获得内部类
public A getA(){
return new A();
}
}
public class Test {
public static void main(String[] args) {
Parcel parcel = new Parcel();
Parcel.A a = parcel.getA();
parcel.getMsg(a);
}
}
10.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 addObject(Object o) {
if (next < items.length) {
items[next++] = o;
}
}
// 获得迭代器
public Selector getSelector() {
return new MySelector();
}
private class MySelector 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 class Test {
public static void main(String[] args) {
Sequence sequence = new Sequence(10);
// 向序列中添加对象
for (int i = 0; i < 10; i++) {
sequence.addObject(i);
}
// 获得迭代器
Selector selector = sequence.getSelector();
while (!selector.end()){
System.out.println(selector.current());
selector.next();
}
}
}
10.3 使用.this和.new
public class Do {
private String s;
public Do(String s) {
this.s = s;
}
public class inner {
public Do getDo() {
// 获得外部类对象
return Do.this;
}
}
public void f() {
System.out.println(s);
}
}
public class Test {
public static void main(String[] args) {
Do d = new Do("ding");
// 创建内部类对象
Do.inner inner = d.new inner();
inner.getDo().f();
}
}
10.4 内部类与向上转型
private内部类完全阻止了依赖类型的编码,并且隐藏了实现的细节。
public interface Contents {
void value();
}
public class Parcel {
private class PContents implements Contents{
@Override
public void value() {
System.out.println("hello");
}
}
public Contents get(){
return new PContents();
}
}
public class Test {
public static void main(String[] args) {
Parcel parcel = new Parcel();
Contents contents = parcel.get();
contents.value();
}
}
10.5 在方法和作用域内的内部类
// 在方法中的内部类
public class Parcel {
public Contents getCon(){
class DCon implements Contents{
@Override
public void value() {
System.out.println("hello");
}
}
return new DCon();
}
}
// 在作用域内的内部类
public class Parcel {
public void f(int i){
if (i > 10){
class DCon implements Contents{
@Override
public void value() {
System.out.println("输入大于10");
}
}
DCon dCon = new DCon();
dCon.value();
}
}
}
10.6 匿名内部类
可以是继承父类或者实现接口
// 基本匿名内部类
public class Parcel {
public Contents f(){
Contents contents = new Contents() {
@Override
public void value() {
System.out.println("基本匿名内部类");
}
};
return contents;
}
}
// 带参数构造器的匿名内部类,实质上调用的是父类的构造器
public class Do {
protected String s;
public Do(String s) {
this.s = s;
}
public void e() {
System.out.println(s);
}
}
public class Parcel {
public Do f(){
Do d = new Do("ding"){
@Override
public void e() {
System.out.println(s);
}
};
return d;
}
}
// 初始化成员变量,传入的参数必须是final的
public class Parcel {
public Contents f(final String name) {
return new Contents() {
private String ProName = name;
@Override
public void value() {
}
};
}
}
// 构造器效果
public class Parcel {
public Contents getC(final String name){
return new Contents() {
private String proName;
{
if (name.contains("d")){
proName = name;
}
}
@Override
public void value() {
System.out.println(proName);
}
};
}
}
// 成员变量初始化也可以使用匿名内部类,同时可以使用外部类中的任何成员
public class Parcel {
private String age = "15";
private Contents contents = new Contents() {
@Override
public void value() {
String s = age;
}
};
}
注意:
方法中,从外部传入匿名内部类的元素必须是final的,但是在其他情况下不是必须的,比如父类构造器参数。
10.6.1 匿名内部类实现工厂方法
public interface Service {
void e();
void f();
}
public interface ServiceFactory {
Service getService();
}
public class ServiceImp1 implements Service {
// 一般情况下,工厂对象只需要一个,所以使用静态对象
public static ServiceFactory factory = new ServiceFactory() {
@Override
public Service getService() {
return new ServiceImp1();
}
};
@Override
public void e() {
System.out.println("1.e");
}
@Override
public void f() {
System.out.println("1.f");
}
}
public class ServiceImp2 implements Service {
public static ServiceFactory factory = new ServiceFactory() {
@Override
public Service getService() {
return new ServiceImp2();
}
};
@Override
public void e() {
System.out.println("2.e");
}
@Override
public void f() {
System.out.println("2.f");
}
}
public class Test {
public static Service exe(ServiceFactory factory){
Service service = factory.getService();
return service;
}
public static void main(String[] args) {
Service service1 = exe(ServiceImp1.factory);
service1.e();
service1.f();
Service service2 = exe(ServiceImp2.factory);
service2.e();
service2.f();
}
}
/*
1.e
1.f
2.e
2.f
*/
10.7 嵌套类
嵌套类:将内部类声明成static,该内部类对象就不会和外部类对象有任何联系。
public class Parcel {
public static class PaC implements Contents{
@Override
public void value() {
}
}
/*
注意:嵌套类可以在静态方法和非静态方法中new,但是普通内部类只能在非静态方法中new,换句话说,
普通类必须依赖某个外部对象,而静态内部类不依赖具体的外部对象
*/
public static Contents getCon(){
return new PaC();
}
}
public class Test {
public static void main(String[] args) {
Contents con = Parcel.getCon();
// 创建静态内部类对象
Parcel.PaC paC = new Parcel.PaC();
}
}
接口内部类
接口内部类默认是public static的
如果希望某些代码被该接口的实现所共用,该方式是一种合适的方式
public interface Contents {
void value();
// 甚至可以实现外部接口
class Dc implements Contents{
@Override
public void value() {
System.out.println("hello");
}
public static void main(String[] args) {
Dc dc = new Dc();
dc.value();
}
}
}
public class Parcel implements Contents{
@Override
public void value() {
// 直接使用接口中的内部类
Dc dc = new Dc();
dc.value();
}
}
多层嵌套访问外部类成员
public class Parcel {
public String f(){
return "a";
}
public class M{
public class N{
// 无论嵌套多少层,内部总是能访问外部的成员
private String s = f();
}
}
}
10.8 为什么需要内部类
最重要的因素:内部类可以独立的继承一个接口的实现,解决了多重继承问题。
实现接口的方式
public interface A {
void f();
}
interface B{
void e();
}
// 实现多个
class C implements A,B{
@Override
public void f() {
}
@Override
public void e() {
}
}
// 实现单个,在内部类中实现其他
class D implements A{
@Override
public void f() {
}
class E implements B{
@Override
public void e() {
}
}
}
继承类
public class A {
void f(){
}
}
class B{
void e(){
}
}
// 由于单继承原因,通过内部类继承的方式实现伪多继承
class C extends A{
class D extends B{
}
}
内部类其他的优势
- 可以有多个实例,每个实例都有自己的状态信息,并与外部类独立;
- 外部类可以有多个内部类以不同的方式实现、继承相同的接口、类;
- 内部类创建的时刻并不依赖外部类;
10.8.1 闭包与回调
闭包: 是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。
内部类是面向对象的闭包,它不仅包含外围类对象的信息(个人理解为成员变量和方法),还拥有指向其的引用。
// 统一接口
public interface IncrementAble {
void increment();
}
// 简单实现方式
public class Called1 implements IncrementAble {
private int i =0;
@Override
public void increment() {
i++;
System.out.println(i);
}
}
// 需要第二种实现方式继承的类
public class MyIncrement {
public void increment(){
System.out.println("基类方法");
}
}
// 继承的类和要实现的接口方法同名,此时使用内部类就可以解决这个问题,此时也是闭包概念的运用
public class Called2 extends MyIncrement {
private int i =0;
@Override
public void increment() {
super.increment();
i++;
System.out.println(i);
}
private class Closuse implements IncrementAble{
@Override
public void increment() {
Called2.this.increment();
}
}
public IncrementAble getClo(){
return new Closuse();
}
}
public class Caller {
private IncrementAble ia;
// Caller需要一个实现了IncrementAble接口的对象参数,Caller可以通过它回调increment()方法
public Caller(IncrementAble incrementAble){
ia = incrementAble;
}
public void go(){
ia.increment();
}
}
public class Test {
public static void main(String[] args) {
Called1 called1 = new Called1();
Called2 called2 = new Called2();
Caller c1 = new Caller(called1);
c1.go();
Caller c2 = new Caller(called2.getClo());
c2.go();
}
}
10.8.2 内部类与控制框架
控制框架用来解决响应事件的需求,在GUI中,大量使用,其中包含大量的内部类。
通过模拟一个温室系统例子展示控制框架,及内部类的优越性。
/**
* 定义要控制的事件
* 为什么使用抽象类而不是接口?
* 因为此处含有默认的行为(基于时间去实现)
*/
public abstract class Event {
/**
* 定义事件执行时间
*/
private long eventTime;
/**
* 定义事件延迟执行时间
*/
protected final long delayTime;
public Event(long delayTime){
this.delayTime = delayTime;
start();
}
/**
* 计算事件执行的时间
*/
public void start(){
eventTime = System.currentTimeMillis()+delayTime;
}
/**
* 表示事件可以执行了
*/
public boolean ready(){
return System.currentTimeMillis() >= eventTime;
}
/**
* 执行事件的方法
*/
public abstract void action();
}
public class Controller {
private List<Event> list = new ArrayList<>();
/**
* 添加事件
* @param event
*/
public void addEvent(Event event){
list.add(event);
}
/**
* 执行事件
*/
public void run(){
while (list.size()>0){
// 注意,此处是新建了一个list对象
for (Event event : new ArrayList<Event>(list)) {
// 当该事件准备好时,执行
if (event.ready()){
System.out.println(event);
event.action();
// 删除执行完毕的事件
list.remove(event);
}
}
}
}
}
/**
* 温室系统的运作
*/
public class MyController extends Controller {
/**
* 灯光系统
*/
private boolean light = false;
public class LightOn extends Event{
public LightOn(long delayTime) {
super(delayTime);
}
@Override
public void action() {
light = true;
}
@Override
public String toString() {
return "light is on";
}
}
public class LightOff extends Event{
public LightOff(long delayTime) {
super(delayTime);
}
@Override
public void action() {
light = false;
}
@Override
public String toString() {
return "light is off";
}
}
/**
* 响铃系统
*/
public class Bell extends Event{
public Bell(long delayTime) {
super(delayTime);
}
@Override
public void action() {
// 两种方法,新建对象或者调用该对象,并重新计时
//addEvent(new Bell(delayTime));
addEvent(this);
start();
}
@Override
public String toString() {
return "开始!";
}
}
/**
* 重复运行系统
*/
public class ReStart extends Event{
private Event[] events;
/**
* @param delayTime 延迟执行时间
* @param events 重新开始的事件
*/
public ReStart(long delayTime,Event[] events) {
super(delayTime);
this.events = events;
// 创建restart对象时第一次添加对象
for (Event event : events) {
addEvent(event);
}
}
@Override
public void action() {
// 重复添加对象
for (Event event : events) {
addEvent(event);
event.start();
}
// 最后将自己添加
addEvent(this);
start();
}
@Override
public String toString() {
return "ReStart";
}
}
/**
* 终止运行事件
*/
public static class Terminate extends Event{
public Terminate(long delayTime) {
super(delayTime);
}
@Override
public void action() {
System.exit(0);
}
@Override
public String toString() {
return "结束";
}
}
}
public class Test {
public static void main(String[] args) {
MyController gc = new MyController();
gc.addEvent(gc.new Bell(900));
Event[] events = {gc.new LightOn(200),
gc.new LightOff(800)};
gc.addEvent(gc.new ReStart(2000, events));
// 判断外界的输入参数,终止程序
if (args.length == 1) {
gc.addEvent(new MyController.Terminate(new Integer(args[0])));
}
gc.run();
}
}
10.9 内部类的继承
public class WithInner extends Out{
public WithInner(){
super();
System.out.println("基类构造器");
}
public class Inner{
public Inner(){
System.out.println("内部类构造器");
}
public void print(){
System.out.println("hello");
}
}
}
public class ExcendInner extends WithInner.Inner {
public ExcendInner(WithInner wi) {
/*
内部类是依附于某个对象的存在,此处相当于普通类的super();
父类必须初始化,但是编译器并不知道怎么做,需要在这里明确指明
*/
wi.super();
}
public static void main(String[] args) {
WithInner withInner = new WithInner();
ExcendInner excendInner = new ExcendInner(withInner);
excendInner.print();
}
}
10.10 内部类可以被覆盖吗
public class Egg {
private Yolk y;
public Egg(){
System.out.println("new Egg");
y = new Yolk();
}
protected class Yolk{
public Yolk(){
System.out.println("Egg.Yolk");
}
}
}
public class BigEgg extends Egg {
protected class Yolk{
public Yolk(){
System.out.println("Big.Yolk");
}
}
public static void main(String[] args) {
new BigEgg();
}
/*
new Egg
Egg.Yolk
可以看到,此处新建的仍然是基类的内部类,基类和子类的内部类可以认为是完全独立的存在
*/
}
// 在子类中继承内部类
public class Egg {
private Yolk y;
public Egg(){
System.out.println("new Egg");
setY(new Yolk());
}
public void setY(Yolk yolk){
y = yolk;
}
public void g(){
y.f();
}
protected class Yolk{
public Yolk(){
System.out.println("Egg.Yolk");
}
public void f(){
System.out.println("基类方法");
}
}
}
public class BigEgg extends Egg {
public BigEgg(){
System.out.println("new BigEgg");
setY(new Yolk());
}
protected class Yolk extends Egg.Yolk{
// 此处继承内部类为什么不用调用父类的构造器?
// 因为外部类继承了内部类基类的外部类,该外部类对象就是内部类指向的对象
public Yolk(){
System.out.println("Big.Yolk");
}
@Override
public void f() {
System.out.println("子类方法");
}
}
public static void main(String[] args) {
BigEgg bigEgg = new BigEgg();
bigEgg.g();
}
/*
// 初始化基类
new Egg
// 基类中创建内部类,内部类初始化
Egg.Yolk
// 初始化子类
new BigEgg
// 子类中创建内部类,内部类初始化基类
Egg.Yolk
// 子类中创建内部类,内部类初始化
Big.Yolk
// 调用f()(现在是重写后的方法)
子类方法
*/
}
10.11 局部内部类
public interface Counter {
int next();
}
public class LocalClass {
private int count =0;
public Counter getC1(final String name){
class C implements Counter{
C(){
System.out.println(name);
}
@Override
public int next() {
return ++count;
}
}
return new C();
}
public Counter getC2(final String name){
return new Counter() {
{
System.out.println(name);
}
@Override
public int next() {
return ++count;
}
};
}
public static void main(String[] args) {
LocalClass localClass = new LocalClass();
Counter c1 = localClass.getC1("局部内部类");
Counter c2 = localClass.getC2("匿名内部类");
System.out.println(c1.next());
System.out.println(c2.next());
}
}
总结: 可以看到,局部内部类和匿名内部类没有什么区别,它使用的场景有两个:需要使用具名构造器,或者是需要不止一个对象。
10.12 内部类标识符
一般情况:外围类名+$+内部类名
匿名内部类:外围类名+$+数字
局部内部类:外围类名+$+数字+类名