Think in Java第四版 读书笔记4第九章第十章

第九章 抽象类与接口

9.1抽象类和抽象接口

抽象类可以有具体实现的方法(并不是所有方法都是abstract的)(比如这样 abstract void test3();)
子类继承抽象类要么要实现(覆盖)抽象类的abstract方法,要么子类也要声明为抽象类
具有抽象方法的类必须是抽象类
抽象类无法实例化(因为它具有未实现的方法)
可以声明一个没有抽象方法的抽象类(这样做可以避免该类实例化)
在这里插入图片描述
代码比较简单就略过了
抽象类使得我们对现实事物的抽象更加显而易见,更加明确。
一个例子

public abstract class Super {
	abstract void print();
	public Super() {
		print();
	}

}
public class Sub extends Super{
	int x = 20;

	void print() {
		System.out.println(x);
	}

}
public class Test {

	public static void main(String[] args) {
		Sub super1 = new Sub();
		super1.print();
	}

}

输出

0
20

这道题目和上一章的例子类似不再赘述

9.2接口

接口是一个极其严格的抽象类,所有方法必须没有实现。
接口可以变相实现多重继承(一个类可以实现多个接口 但是只能继承一个父类)
接口中所有方法默认是public的(即使没有写),实现类实现时会自动加上public字段(继承父类或实现接口,被覆盖方法的访问权限只能扩大,不能缩小,即父类或接口中的方法修饰符是public,子类或实现类只可以是public,不能是private的protected的,否则报错Cannot reduce the visibility of the inherited method from 父类或接口)

9.3完全解耦

前面的章节我们提到继承比较笨重,这里也会体现出来。
假设有一个方法操作的是一个类,那么我们只能传入该类及其子类的实例,而如果该方法操作的是一个接口类型,那么只要传入的实例实现了该接口,就可以起作用。接口比继承更灵活
书中讲述了一个例子,例子使用继承+多态实现了策略模式的效果
之后的滤波器的出现,证明了继承的局限性,解决方案是将Processor抽象为接口而不是类
在无法修改类时(类在类库中),可以通过适配器模式,增加一个适配器,让新的类能适配原来的接口。

9.4 Java的“多重继承”

通过implements多个接口实现“多重继承”
使用接口的原因
1.使用接口类型的方法,进行向上转型可以接受多种类型的参数
2.防止程序员创建接口对象(无法实例化抽象类 同样的,无法实例化接口)
练习
创建一个接口,该接口继承了另外两个接口(接口可以extends不止一个东西,接口不能implements接口,只能extends接口)然后从后面两个接口多重继承第三个接口。

interface CanDo {
	void doIt();
}
interface CanDoFaster extends CanDo {
	void doFaster();
}
interface CanDoMore extends CanDo {
	void doMore();
}
interface CanDoMost extends CanDoMore, CanDoFaster {
	void doMost();
}
class Doer implements CanDoMost {
	public void doIt() {}
	public void doMore() {}
	public void doFaster() {}
	public void doMost() {}
}
public class Test {
	public static void main(String[] args) {
		Doer d = new Doer();
		((CanDoMore)d).doMore();
		((CanDoFaster)d).doFaster();
		((CanDo)d).doIt();	
	}
}

这就是传说中的菱形继承(仅限于接口的继承)
在这里插入图片描述

9.5通过继承扩展接口

接口新增的方法有2,第一直接在接口中新增方法,第二通过继承其他接口获取新的方法。

9.5.1组合接口时方法名冲突

例子

public interface interface1 {
	void method1();

}
public interface interface2 {
	void method1();
}
public class Test implements interface2,interface1{
	public void method1() {
		// TODO Auto-generated method stub
		
	}
}

这样写没有问题,因为interface1 interface2中的method1方法完全相同。
但是如果把interface1改成这样(修改了返回值类型)

public interface interface1 {
	int method1();
}

那么我们在实现类test无论怎么写都会出现问题,不管我们将返回值改为什么类型总和interface1或interface2其中一个冲突。
因此,最明智的方式是新增方法或者继承接口时最好不要出现同名方法(即使程序允许),否则可能引起混乱

9.6适配接口

举了一个实现Readable例子
然后使用继承+实现接口的方法让一个类既是RandomDoubles的又可以作为Scanner的参数(Readable的)

9.7 接口中的域

由于接口中的域默认是final static的(也是public的),在SE 5 之前,这是产生enum类型的唯一方式,所以,以前的代码可能是这么写的
在这里插入图片描述
这些域不是接口的一部分 存在静态存储区域。

9.8 嵌套接口

package suround;

import suround.A.E;

class A {
	interface B {
		void f();
	}

	public class BImp implements B {
		public void f() {
		}
	}

	private class BImp2 implements B {
		public void f() {
		}
	}

	interface C {
		void f();
	}

	class CImp implements C {
		public void f() {
		}
	}

	private class CImp2 implements C {
		public void f() {
		}
	}

	//接口也可以被实现为private的 但限于类内部
	private interface D {
		void f();
	}

	//实现D接口的private类
	private class DImp implements D {
		public void f() {
		}
	}

	//实现D接口的public类
	private class DImp2 implements D {
		public void f() {
		}
	}

	public D getD() {
		return new DImp2();
	}

	private D dRef;

	public void receiveD(D d) {
		dRef = d;
		dRef.f();
	}

	//接口嵌套接口 G接口必须是public 的,这是interface对内部的性质
	interface E {
		interface G {
			void f();
		}

		// public 是多余的,默认就是public,如果写出private则报错
		public interface H {
			void f();
		}

		void g();
	}

}

public class NestingInterfaces {
	public class BImp implements A.B {
		public void f() {
		}
	}

	class CImp implements A.C {
		public void f() {
		}
	}

	// 无法实现private的接口
	// class DImp implements A.D{
	// public void f() {
	// }
	// }

	
	//实现E接口只要实现E,不需要实现E内部的接口
	class EImp implements E {
		public void g() {
		}
	}
	
	class EGImp implements E.G {
		public void f() {
		}
	}
	
	class EImp2 implements E {
		public void g() {
		}
		class EG implements E.G{
			public void f() {
			}
		}
	}

	public static void main(String[] args) {
		A a = new A();
		//A.D class A的D接口为private 不可访问
		
		//A.DImp 也无法访问
		
		//getD返回DImp2对象 它是private 的无法调用其方法
		//a.getD().f();
		
		//只有A对象内部才能调用DImp2 的 private方法
		A a2 = new A();
		a2.receiveD(a.getD());
	}

}

9.9 接口与工厂

实例
在这里插入图片描述
在这里插入图片描述
工厂模式的作用:利用向上转型来实现一套代码可以适用于不同类型的对象,调用不同类型对象的方法。将实际对象的实现隐藏(封装)起来,使用者只能看到工厂和工厂方法

总结

9.1 抽象类简介
9.2 接口–严格的抽象类
9.3 接口的目的之一— 利用向上转型进行完全解耦
9.4 接口的目的之二—实现多重继承的思想
9.5 接口extends接口
9.5.1 实现或者继承接口时的命名冲突问题(尽量通过人为避免)
9.6 当接口不能直接使用 可以利用适配器来适配接口
9.7 接口中的域默认是public static final的
9.8 接口嵌套以及一些特性
9.9 接口与工厂模式(可以实现代码复用)

第十章 内部类

10.1 创建内部类

内部类常见用法示例

public class Parcel1 {
	class Contents{//内部类1
		private int i = 11;
		public int value() {
			return i;
		}
	}
	class Destination{//内部类2
		private String labelString;
		public Destination(String whereTo) {
			labelString = whereTo;
		}
		String readLabel(){
			return labelString;
		};
	}
	
	public void ship(String dest) {
		Contents contents = new Contents();//外部类方法中进行内部类实例化
		Destination destination = new Destination(dest);//外部类方法中进行内部类实例化
		System.out.println(destination.readLabel());//外部类方法 使用内部类实例调用内部类方法
	}
	
	public static void main(String [] args){
		Parcel1 parcel1 = new Parcel1();
		parcel1.ship("Tasmania");
	}
}

使用外部类对象获取内部类实例

public class Parcel2 {
	class Contents{//inner class1
		private int i = 11;
		public int value() {
			return i;
		}
	}
	class Destination{//inner class2
		private String labelString;
		public Destination(String whereTo) {
			labelString = whereTo;
		}
		String readLabel(){
			return labelString;
		};
	}
	
	public Destination to(String s){//返回内部类实例
		return new Destination(s);
	}
	
	public Contents contents(){//返回内部类实例
		return new Contents();
	}
	
	public void ship(String dest) {
		Contents contents = contents();//outer class方法中调用inner class方法进行inner class实例化
		Destination destination = to(dest);//outer class方法中用inner class方法进行inner class实例化
		System.out.println(destination.readLabel());//outer class方法 使用inner class实例调用inner class方法
	}
	
	public static void main(String [] args){
		Parcel2 parcel2 = new Parcel2();
		parcel2.ship("Tasmania");
		
		Parcel2 qParcel2 = new Parcel2();
		Contents contents = qParcel2.contents();//使用outer class调用inner class方法获取inner class实例
		Destination destination = qParcel2.to("Borneo");//使用outer class调用inner class方法获取inner class实例
		Contents contents1 = qParcel2.new Contents();//也可以这样创建实例
	}
}

10.2 链接到外部类

内部类对象隐式持有外部类对象的引用,因此内部类对象可以访问外部类对象的所有方法和域(包括private类型的)。

public interface Selector {
	boolean end();
	Object current();
	void next();
}

public class Sequence {
	private Object[] itemsObjects;
	private int next = 0;

	public Sequence(int size) {
		itemsObjects = new Object[size];
	}

	public void add(Object x) {
		if (next < itemsObjects.length) {
			itemsObjects[next++] = x;
		}
	}

	private class SequenceSelector implements Selector {
		private int i = 0;

		public boolean end() {
			return i == itemsObjects.length;// inner class 访问outer class私有变量
		}

		public Object current() {
			return itemsObjects[i];
		}

		public void next() {
			if (i < itemsObjects.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 selector = sequence.selector();
		while (!selector.end()) {
			System.out.print(selector.current() + " ");
			selector.next();
		}
	}

}

输出

0 1 2 3 4 5 6 7 8 9 

这是一个典型的迭代器模式。
在这里插入图片描述

10.3 使用.this 与.new

一句话:内部类返回外部类对象可以使用"外部类类名.this" 外部类返回内部类对象可以使用"外部类.new 内部类类名",示例如下
.this的使用

public class DotThis {
	void f(){
		System.out.println("DotThis.f()");
	}
	public class Inner{
		public DotThis outer(){
			return DotThis.this;//内部类返回外部类对象
			//单独写this表示内部类的this
		}
	}
	
	public Inner inner(){
		return new Inner();
	}

	public static void main(String[] args) {
		DotThis dt = new DotThis();
		DotThis.Inner dti = dt.inner();
		dti.outer().f();
	}
}

.new 的使用

public class Parcel3 {
	private int zz;
	int xl;
	class Contents{//inner class1
		private int i = 11;
		public int value() {
			return i;
		}
	}
	class Destination{//inner class2
		private String labelString;
		public Destination(String whereTo) {
			labelString = whereTo;
		}
		String readLabel(){
			return labelString;
		};
	}
	
	public static void main(String [] args){
		Parcel3 p = new Parcel3();
		Contents contents = p.new Contents();//使用外部类对象获取内部类对象
		Destination destination = p.new Destination("Ta");//使用外部类对象获取内部类对象
	}
}

正如我们前面说的,创建内部类对象时,内部类对象默认持有外部类对象的引用,因此,内部类对象不能单独存在,必须使用外部类对象.new 的方式来创建内部类对象(有一个例外就是创建的内部类是静态内部类,它不需要对外部类对象的引用)
更简单的一个.new的例子

public class Outer {
	class Inner { 
		Inner() { System.out.println("Outer.Inner()"); } 
	}
}
public class OtherOuter {
	public static void main(String[] args) {
		// must first create outer class object:
		Outer o = new Outer();
		// then create inner class object:
		Inner oi = o.new Inner();			
	}
}

10.4 内部类与向上转型

内部类的一个作用是隐藏实现
一个私有内部类实现了什么接口,对外部调用者来说完全不可见,也不可以创建对象
示例

public interface Contents {
	int value();
}
public interface Destination {
	String readLabel();
}

public class Parcel4 {
	private class PContents implements Contents {//私有内部类
		private int i = 11;

		public int value() {
			return i;
		}
	}
	
	protected class PDestination implements Destination{//protected内部类
		String label;
		private PDestination(String whereTo){
			label = whereTo;
		}
		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 c = parcel4.contents();
		Destination d = parcel4.destination("Test");
		//protected 权限在同一个包里可以访问
		PDestination destination = (PDestination) parcel4.destination("Test");
		//PContents 是private的无法访问
		//PContents co;
		//Destination destination = parcel4.new PDestination("a");
	}

}

如果是将上述程序作为第三方包使用,客户端只能访问到Destination和Contents 无法访问到私有的Contents 和protected的PDestination,也无法进行向下转型,这样就达成了内部类的实现细节隐藏,客户端只能调用公共接口的的方法 而不能调用PDestination和Contents里面的其他方法。
练习题证明了
1.protected的内部类实现的接口,在继承关系中,可以实现向下转型。
2.内部类可以修改外部类域 调用外部类的方法
3.内部类和外部类可以相互访问各自的private方法和变量

10.5 在方法和作用域内的内部类(局部内部类,只在限定范围有效)

1定义在方法中

public interface Destination {
	String readLabel();
}
//定义在方法中的内部类(局部内部类)
public class Parcel5 {
	//PDestination是destination方法的一部分,在destination方法之外无法访问PDestination(隐藏了PDestination)
	public Destination destination(String s){
		//只要不在destination方法中,可以在其他地方添加新的PDestination类而且不会有命名冲突,但是不推荐,容易产生歧义
		class PDestination implements Destination{
			private String label;
			private PDestination(String whereTo){
				label = whereTo;
			}
			public String readLabel() {
				return label;
			}
		}
		return new PDestination(s);
	}
	
	public static void main(String[] args) {
		Parcel5 p = new Parcel5();
		Destination d = p.destination("ABC");
	}
}

2定义在方法内部的作用域

//任意作用域嵌入内部类 同样可以达到隐藏实现的目的
public class Parcel6 {
	private void interalTracking(boolean b){
		if (b) {
			class TrackingSlip{
				private String id;
				TrackingSlip(String id) {
					this.id = id;
				}
				String getSlip(){
					return id;
				}
			}
			//TrackingSlip的使用范围仅限于if分支结束前,TrackingSlip定义之后
			//但是不管进不进if的case TrackingSlip都会参与编译(而不是进入之后才编译)
			TrackingSlip ts = new TrackingSlip("slip");
			String string = ts.getSlip();
			System.out.println(string);
		}
	}
	
	public static void main(String[] args) {
		Parcel6 p = new Parcel6();
		p.interalTracking(true);
	}
}

10.6 匿名内部类

1.匿名内部类简单示例

public interface Contents {
	int value();
}
public class Parcel7 {
	public Contents contents(){
		return new Contents() {//返回时才创建一个继承自Contents的匿名对象
			private int i = 11;
			public int value() {
				return i;
			}
		};//注意分号不可少
	}
	
	public static void main(String[] args) {
		Parcel7 p = new Parcel7();
		Contents contents = p.contents();
	}
}

2.匿名内部类的实际效果示例

public class Parcel7b {//Parcel7 是 Parcel7b的简化形式
	public Contents contents() {
		return new MyContents();
	}

	class MyContents implements Contents {
		private int i = 11;

		public int value() {
			return 0;
		}
	}

	public static void main(String[] args) {
		Parcel7b p = new Parcel7b();
		Contents contents = p.contents();
	}
}

3.创建带参构造方法的匿名内部类示例

public class Parcel8 {// 创建带参构造方法的匿名内部类
	public class Wrapping {// 普通内部类
		private int i;

		Wrapping(int x) {
			i = x;
		}

		public int value() {
			return i;
		}

	}

	public Wrapping wrapping(int x) {
		return new Wrapping(x) {
			@Override
			public int value() {
				return super.value() * 47;
			}
		};
	}

	public static void main(String[] args) {
		Parcel8 p = new Parcel8();
		Wrapping wrapping = p.wrapping(1);
		System.out.println(wrapping.value());
	}

}

4.匿名内部类定义字段时同时进行初始化

public interface Destination {
	String readLabel();
}
public class Parcel9 {
	public Destination destination(final String dest){//匿名内部类的参数需要是final的
		return new Destination() {
			private String label = dest;//匿名内部类定义字段时同时进行初始化
			public String readLabel() {
				return label;
			}
		};
	}
	
	public static void main(String[] args) {
		Parcel9 p = new Parcel9();
		Destination d = p.destination("ABC");
	}
}

5.为匿名内部类创建构造器示例
匿名内部类本来没有名字,因此要有构造器本来是不可能的,但是,可以通过使用new 类名(实例化)的方式创建一个抽象类(需要实现抽象类内部细节)来达到效果

public abstract class Base {
	public Base(int i){
		System.out.println("Base constructor, i "+i);
	}
	public abstract void f();
}

public class AnonymousConstructor {
	public static Base getBase(int i){//i没有在内部类直接使用,所以可以不用定义为final的
		return new Base(i) {//匿名内部类的含参构造方法 如果父类用于多个参数,此处可以传递多个参数
			{
				System.out.println("Inside instance initializer ");
			}
			@Override
			public void f() {
				System.out.println("In anonymous f()");
			}
		};
	}

	public static void main(String[] args) {
		//先调用基类构造方法
		//调用匿名内部类静态代码块代码
		Base base = getBase(47);
		//调用f方法
		base.f();
	}

}

6.匿名内部类的局限

public interface Destination {
	String readLabel();
}
public class Parcel10 {
	public Destination destination(final String dest, final float price) {// 匿名内部类直接使用的参数需要是final的
		return new Destination() {
			private int cost;
			{
				// 静态代码块初始化
				cost = Math.round(price);
				if (price > 0) {
					System.out.println("cost initialize cost=" + cost);
				}
			}
			private String label = dest;// 匿名内部类定义字段时同时进行初始化

			public String readLabel() {
				return label;
			}
		};
	}

	public static void main(String[] args) {
		Parcel10 p = new Parcel10();
		Destination d = p.destination("ABC", 101);
	}
}
//匿名内部类可以实现接口 也可以扩展类 但是不能同时继承和实现
//实现接口也只能实现一个接口

10.6 内部类与工厂模式

public interface Game {
	boolean move();
}
public interface GameFactory {
	Game getGame();
}

Game实现类

public class Checkers implements Game{
	private Checkers(){}
	private int moves = 0;
	private static final int MOVES = 3;

	public boolean move() {
		System.out.println("CHeckers move "+ moves);
		return ++moves != MOVES;
	}
	
	public static GameFactory factory = new GameFactory() {//匿名内部类
		
		public Game getGame() {
			return new Checkers();
		}
	};

}
public class Chess implements Game{
	private Chess(){}
	private int moves = 0;
	private static final int MOVES = 4;

	public boolean move() {
		System.out.println("Chess move "+ moves);
		return ++moves != MOVES;
	}
	
	public static GameFactory factory = new GameFactory() {//匿名内部类
		
		public Game getGame() {
			return new Chess();
		}
	};

}

每种game内都有一个匿名内部类用于返回GameFactory示例
测试类

public class Games {
	public static void playGame(GameFactory factory) {
		Game s = factory.getGame();
		while (s.move()) {
		}
	}
	public static void main(String [] args){
		playGame(Checkers.factory);
		playGame(Chess.factory);
	}

}

10.7嵌套类

什么是嵌套类:书中的定义是static的内部类就是嵌套类,这似乎与网络和我的理解有出入。
嵌套类与普通内部类的区别(个人理解应该是静态内部类和非静态内部类的区别):
1.静态内部类创建对象时不需要外围类的对象
2.不能从静态内部类对象中访问非静态的外围类对象
3.非静态内部类需要this关键字来访问外围类 而静态内部类不需要
一个静态内部类的示例

public interface Contents {
	int value();
}
public interface Destination {
	String readLabel();
}
public class Parcel11 {
	private static class ParcelContents implements Contents {//静态内部类1
		private int i = 11;

		public int value() {
			return i;
		}
	}

	private static class ParcelDestination implements Destination {//静态内部类2
		private String label;

		public ParcelDestination(String whereTo) {
			label = whereTo;
		}

		public String readLabel() {
			return label;
		}

		public static void f() {
		};

		static int x = 10;

		static class AnotherInner {//静态内部类2的内部类
			public static void f() {
			}

			static int x = 10;
		}
	}

	public static Destination destination(String s) {
		return new ParcelDestination(s);
	}

	public static Contents contents() {
		return new ParcelContents();
	}

	public static void main(String[] args) {
		Parcel11 p = new Parcel11();
		Destination d = p.destination("ABC");
	}
}

10.7.0编译时的内部类带有$符号,这个特点在很多内存泄漏的工具里面有用

public class Ex19 {
	Ex19() { System.out.println("Ex19()"); }
	private class Ex19Inner {
		Ex19Inner() { System.out.println("Ex19Inner()"); } 
		private class Ex19InnerInner {
			Ex19InnerInner() {
				System.out.println("Ex19InnerInner()");
			}
		}
	}
	private static class Ex19Nested {
		Ex19Nested() { System.out.println("Ex19Nested()"); }
		private static class Ex19NestedNested {
			Ex19NestedNested() { 
			System.out.println("Ex19NestedNested()"); 
			}
		}
	}	
	public static void main(String[] args) {
		Ex19Nested en = new Ex19Nested();
		Ex19Nested.Ex19NestedNested enn = new Ex19Nested.Ex19NestedNested();
		Ex19 e19 = new Ex19();
		Ex19.Ex19Inner ei = e19.new Ex19Inner();
		Ex19.Ex19Inner.Ex19InnerInner eii = ei.new Ex19InnerInner();
	}
}

/* compiler produces:
* Ex19$Ex19Inner$Ex19InnerInner.class
* Ex19$Ex19Inner.class
* Ex19$Ex19Nested$Ex19NestedNested.class
* Ex19$Ex19Nested.class
* Ex19.class
*/

10.7.1 接口内部的类

//通常 不能再接口放置任何代码,但是静态内部类例外,将静态内部类放置在接口的命名空间不违法接口的规则
public interface ClassInInterface {
	void howdy();
	class Test implements ClassInInterface{//该类默认拥有接口内部的属性,即是public static的
		public void howdy() {
			System.out.println("Howdy");
		}
		
		public static void main(String [] args) {
			new Test().howdy();
		}
	}

创建公共代码使得每个实现某一接口的任何类都能复用,接口的内部类可以实现这个功能
比如下面这个例子Nested 可以被任何继承了In接口的类使用

interface In {
	class Nested {
		Nested() { System.out.println("Nested()"); }
		public void hi() { System.out.println("hi"); }		
	}
}

public class Ex20 implements In {
	public static void main(String[] args) {
		In.Nested in = new In.Nested();
		in.hi();				
	}
}

内部类与外部类(接口)联动的例子

interface In {
	String f();
	String g(); 
	class Nested {
		static void testIn(In i) { 
			System.out.println(i.f() + i.g());
		}		
	}		
}

public class Ex21 implements In {
	public String f() { return "hello "; }
	public String g() { return "friend"; }
	public static void main(String[] args) {
		Ex21 x = new Ex21();
		In.Nested.testIn(x);						
	}
}

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

无论嵌套多少层,内部类可以访问所有外围类的成员

public class MNA {
	private void f() {
	}
	
	class A{
		private void g(){}
		public class B{
			void h(){
				g();//访问上层private方法
				f();//访问上上层private方法
			}
		}
	}

}
import inner10dot7dot2.MNA.A;
import inner10dot7dot2.MNA.A.B;

public class TestMultiNestingAccess {

	public static void main(String[] args) {
		MNA mna = new MNA();
		A mnaa = mna.new A();
		B mnaab = mnaa.new B();
		mnaab.h();
		//mnaab.g();//访问失败
		//内部类访问外部类方法 不是内部类实例访问外部类方法,而是在方法内部可以访问
	}
}

10.8 为什么需要内部类

如果实现两个接口,有几种方式来实现呢?

public interface A {

}
public interface B {

}

方式一 直接实现

public class TestImplement implements A,B{

}

方式二 通过内部类实现

public class TestInner implements A{
	B makeB(){
		return new B() {
		};
	}

}

测试类

public class Test {
	static void takeA(A a){}
	static void takeB(B b){}

	public static void main(String[] args) {
		TestImplement testImplement = new TestImplement();
		takeA(testImplement);
		takeB(testImplement);
		
		TestInner testInner = new TestInner();
		takeA(testInner);
		takeB(testInner.makeB());
	}

}

既然都可以实现,那么内部类有什么特别的呢?
上面可能看不出区别,但是如果不是实现接口而是继承两个不相关的类呢?由于java禁止多重继承的关系,我们无法让一个类继承两个类。因此我们只能通过内部类来实现这种多重继承。这是内部类的最大优势之一。
除了多重继承,内部类还有以下特性
1.内部类可以有多个实例,每个实例之间相互独立,与外部类信息也独立
2.多个内部类可以以不同的形式实现或继承接口和类
3.创建内部类对象的时刻不依赖外部类对象的创建(没明白。。)
4.内部类是一个独立的实体(内部类只能继承或实现一个类或接口,返回的内部类对象就是这个接口或者类对象)

10.8.1 闭包与回调

个人理解通过内部类可以返回一个封闭的对象调用它的方法,实现者对内部类的控制方便
上例子

//接口定义
public interface Incrementable {
    void increment();
}

//实现Incrementable的第一种方式 直接通过类实现接口
public class Callee1 implements Incrementable{
	private int i = 0;

	public void increment() {
		i++;
		System.out.println("direct implements "+i);
	}
}
public class MyIncrement {
	void increment(){
		System.out.println("Other operation");
	}
	static void f(MyIncrement mi){
		mi.increment();
	}
}
class Callee2 extends MyIncrement {
	private int i = 0;

	// Callee2继承了类MyIncrement,所以覆写的是MyIncrement的increment方法,
	// 如果要实现接口Incrementable的increment方法,只能通过内部类独立地实现。
	public void increment() {
		super.increment();
		i++;
		System.out.println("by inner class "+i);
	}

	//实现Incrementable的第二种方式-- 内部类实现Incrementable 这是个闭包
	private class Closure implements Incrementable {

		public void increment() {
			Callee2.this.increment();
		}
		
	}
	
	//通过方法返回内部类对象 用于调用内部类方法,通过内部类提供的闭包功能,完成回调(callback)的功能,而不是通过指针实现回调。
	// Closure是private,所以外部只能通过下面的方法得到接口的引用,也就限制了外部可以做的事情。
	Incrementable getCallbackReference(){
		return new Closure();
	}
}
//作用 获取Incrementable引用以调用其方法
class Caller {
	private Incrementable callbackReference;

	Caller(Incrementable cbh) {
		callbackReference = cbh;
	}

	void go() {
		callbackReference.increment();
	}
}
public class Callbacks {
	public static void main(String[] args) {
		Callee1 c1 = new Callee1();
		Callee2 c2 = new Callee2();
		MyIncrement.f(c2);//调用静态方法 c2.i+1
		Caller caller1 = new Caller(c1);
		Caller caller2 = new Caller(c2.getCallbackReference());
		caller1.go();//c1.i+1
		caller1.go();//c1.i+1
		caller2.go();//c2.i+1
		caller2.go();//c2.i+1
	}
}

输出

Other operation
by inner class 1
direct implements 1
direct implements 2
Other operation
by inner class 2
Other operation
by inner class 3

可以看到第二种实现中继承的父类MyIncrement已经有了increment方法,此时可以通过内部类实现Incrementable的

10.8.2 内部类与控制框架(没看明白他想表达什么,略过)

10.9 内部类的继承

由于内部类的创建依赖于外部类 因此在创建内部类时,指向外部类的引用必须初始化。

public class WithInner {
	class Inner{}
}
public class InheritInner extends WithInner.Inner{
	//默认构造方法报错 因为需要一个外部类的实例 (No enclosing instance of type WithInner is available due to some intermediate constructor invocation)
	//public InheritInner() {}
	
	public InheritInner(WithInner withInner) {//必须要有一个非默认构造方法,否则还是报错-- 需要一个外部类的实例 
		withInner.super();
	}

	public static void main(String[] args) {
		WithInner withInner = new WithInner();
		InheritInner lInheritInner = new InheritInner(withInner);
	}

}

10.10 内部类可以被覆盖么

举个例子 一个类创建了一个内部类,用另一个类继承外围类,在这个“另一个类中重新定义同名的内部类”,看起来好像是覆盖了第一个类的内部类,真实情况是这样吗?

public 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{//继承了Egg之后 各自的内部类没有关联,各自在自己的命名空间,相互独立不存在覆盖的问题
	public class Yolk{//与Egg中的Yolk类没有关系
		public Yolk() {
			System.out.println("BigEgg.yolk");
		}
	}
	
	public static void main(String [] args){
		new BigEgg();
	}

}

输出为

new Egg
Egg.yolk

从例子的输出结果看 内部类不可以被覆盖,但是可以明确的继承某一个内部类(上面这个例子不是继承和覆盖)

public class Egg2 {
	private Yolk y;
	protected class Yolk{
		public Yolk() {
			System.out.println("Egg2.Yolk()");
		}
		
		public void f() {
			System.out.println("Egg2.f()");
		}
	}
	
	public Egg2() {
		System.out.println("new Egg2()");
		y = new Yolk();
	}
	
	public void insertYolk(Yolk yy){
		y = yy;
	}
	
	public void g(){
		y.f();
	}

}
public class BigEgg2 extends Egg2 {
	public BigEgg2() {
		insertYolk(new Yolk());
	}

	public class Yolk extends Egg2.Yolk {//指定继承Egg2中的Yolk
		public Yolk() {
			System.out.println("BigEgg2.Yolk()");
		}

		public void f() {
			System.out.println("BigEgg2.Yolk().f()");
		}
	}

	public static void main(String[] args) {
		Egg2 e2 = new BigEgg2();
		e2.g();
	}
}

/*
输出
new Egg2()
Egg2.Yolk()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk().f()

分析
new Egg2()//创建BigEgg2对象先调用父类构造方法Egg2
Egg2.Yolk()//父类构造方法中调用了Egg2.Yolk的构造方法
Egg2.Yolk()//BigEgg2.Yolk的对象前先调用父类构造方法Egg2.Yolk()
BigEgg2.Yolk()//创建子类对象
BigEgg2.Yolk().f()//调用子类方法
*/

10.11 局部内部类 VS 匿名内部类

很多情况下 我们可以使用局部内部类(在方法中) 或者匿名内部类,那么他们有什么不同

public interface Counter {
	int next();
}

//局部内部类VS匿名内部类
public class LocalInnerClass {
	private int count = 0;

	//局部内部类
	Counter getCounter(final String name){
		class LocalCounter implements Counter{
			public LocalCounter() {
				System.out.println("创建局部内部类");
			}
			@Override
			public int next() {
				System.out.println(name);
				return count++;
			}
		}
		return new LocalCounter();
	}
	
	//相同的功能用匿名内部类实现
	Counter getCounter2(final String name){
		return new Counter() {
			//匿名内部类没有有名字的构造方法
			{
				System.out.println("创建匿名内部类");
			}
			@Override
			public int next() {
				System.out.println(name);
				return count++;
			}
		};
	}
	
	public static void main(String [] args) {
		LocalInnerClass lic = new LocalInnerClass();
		Counter c1 = lic.getCounter("局部内部类");
		Counter c2 = lic.getCounter2("匿名内部类");
		for (int i = 0; i < 5; i++) {
			System.out.println(c1.next());
		}
		for (int i = 0; i < 5; i++) {
			System.out.println(c2.next());
		}
	}

}
/*
局部内部类VS匿名内部类:
局部内部类可以重载构造器,而匿名内部类只能用于实例初始化.
使用局部内部类的而不使用匿名内部类的另一个理由:需要不止一个该内部类的对象.
*/

10.12 内部类标识符 $

java在编译的时候每个类都会产生一个.class文件该文件包含创建该对象的全部信息,而内部类也不例外,不过内部类比较特殊,它的命名方式是"外围类名$内部类名.class"
该命名在我们分析内存泄漏时很有用,其他暂时不知道还有什么作用.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值