内部类有重要的特性,把一些逻辑相关的类组织在一起,并控制内部的类的可见性.内部类和组合是完全不同的概念.
内部类的常见情况是: 外部类有一个方法,返回指向内部类的引用.
10.2链接到外部类
当生成一个内部类对象时,此对象有访问制造他的外围对象(enclosing Object)有了一种联系,它能访问外围对象的所有成员,不需要任何特殊条件.此外内部类也拥有外围类的所有元素的访问权限.
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
public interface Selector {
boolean end();
void next();
Object current();
}
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
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 SequenceSelector implements Selector {
private int i = 0;
@Override
public boolean end() {
return i == items.length;
}
@Override
public void next() {
if (i < items.length) {
i++;
}
}
@Override
public Object current() {
return items[i];
}
}
public Selector getSelector() {
return new SequenceSelector();
}
public static void main(String[] args) {
Sequence sequence = new Sequence(10);
for (int i = 0; i < 10; i++) {
sequence.add(i);
}
Selector selector = sequence.getSelector();
while (!selector.end()) {
System.out.print(selector.current()+" ");
selector.next();
}
}
}
//output---
0 1 2 3 4 5 6 7 8 9
上述代码是迭代器模式.迭代器模式,提供一个迭代接口Selector 包含 next(),current(),end() 3个协议,由其他具有容器类型的数据结构自己区根据 selector接口各自迭代.可以达到不同数据结构容器可以按照相同规则进行遍历数据.
内部类private SelectorSequence 可以自动拥有外部类items的访问.这是怎么做到的呢?编译器会自动处理所有的细节.内部类的对象只能在与其外部类对象相关联的情况下才能被创建(当内部类是非静态类时).
10.3使用.this 和.new
在内部类中返回外部类引用可以采用.this形式直接返回.例如:
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
public class Dotthis {
void f() {
System.out.println("Out---");
}
public class Inner {
public Dotthis outer() {
return Dotthis.this;
}
}
public Inner getInner() {
return new Inner();
}
public static void main(String[] args) {
Dotthis dotthis = new Dotthis();
Dotthis.Inner inner = dotthis.getInner();
inner.outer().f();
}
}
//output---
Out---
如果在外部(其他类)想创建内部对象 可以通过.new,例如
public static void main(String[] args)
Dotthis dotthis = new Dotthis();
Dotthis.Inner inner = dotthis.new Inner();
Dotthis.Inner inner2 = new Dotthis().new Inner();
inner.outer().f();
inner2.outer().f();
}
如果内部类是 静态内部类(嵌套类)则可以直接new Inner();在其他的第三方类可以通过new 外部类.静态内部类()的方式(new Dotthis.Inner();)
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
public class Dotthis {
void f() {
System.out.println("Out---");
}
public static class Inner {
public void in() {
System.out.println("in----");
}
}
public Inner getInner() {
return new Inner();
}
public static void main(String[] args) {
Dotthis dotthis = new Dotthis();
Dotthis.Inner inner = new Inner();
Dotthis.Inner inner2 = new Inner();
inner.in();
}
inner1 和 inner2并不是指向同一个 对象.
10.4内部类和向上转型
当一个类只想对外提供具有某种接口的功能(返回基类或接口引用),但是想隐藏具体的实现细节.那么就内部类就有了用武之地(当将内部类向上转型为基类,尤其是转型为接口,内部类可以发挥作用).
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
public interface Contents {
int value();
}
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
public interface Describ {
String readLabel();
}
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
public class Parcel4 {
private class PContent implements Contents {
@Override
public int value() {
return 20;
}
}
private class PDescrib implements Describ {
String label;
public PDescrib(String label) {
this.label = label;
}
@Override
public String readLabel() {
return label;
}
}
public Describ getDecib(String label) {
return new PDescrib(label);
}
public Contents getContens() {
return new PContent();
}
public static void main(String[] args) {
Parcel4 parcel4 = new Parcel4();
Contents contents = parcel4.getContens();
Describ describ = parcel4.getDecib("hello ");
System.out.println(describ.readLabel() + contents.value());
}
}
//output---
hello 20
10.5在方法和作用域内的内部类
通常,如果读写类中包含的内部类,那么他们都是”平凡的”内部类,简单且容易理解.然而有些更加难理解的技术,例如:在一个方法或者任意作用域里面定义内部类.这么做有两个理由:
1.实现了某一个类型的接口,创建并返回接口的引用.(通常是希望创建局部的权限,希望在这个区域之外不能访问和修改)
2.要解决一个复杂的问题,想创建一个辅助类帮助,但不希望这个类是公用的.
一般可以在下面这些地方使用:
- 定义在方法中
- 定义在作用域内,此作用域在方法内有效
- 实现接口的匿名类
- 一个匿名类,扩展了非默认构造器的类
- 一个匿名类,执行了字段初始化
- 一个匿名类,通过实例初始化实现构造(匿名类不可能有构造器)
解释:
1.在方法内部创建的内部类,只有在该方法内可以调用,其他地方是不能调用的,因为此内部类是方法的一部分,而不是之外的外部类的一部分.在其他地方可以出现同名的类,不会冲突.
2.在作用域之内的,编译的时候和其他的类一起同时编译,但是除了在此作用域之外是不可访问的,其他的和普通类一样.可以在if,switch...case..中.
10.6匿名内部类
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
public class Parcel8 {
//传递构造函数参数
public Wrapping getWrap(int value) {
return new Wrapping(value) {
@Override
public int getValue() {
return super.getValue() * 3;
}
};
}
//创建新的字段,初始化
public Describ getDesrib(String describ) {
String out = "sss";
return new Describ() {
private String label = describ;
@Override
public String readLabel() {
return label + out;
}
};
}
//进行匿名类里面做构造器的行为,
public Base getBase(int base) {
return new Base(base) {
//这里是实例初始化 形式
{
System.out.println("inner Constructor");
y = base;
}
@Override
public void f() {
System.out.println("inner y=" + y);
}
};
}
public static void main(String[] args) {
Parcel8 parcel8 = new Parcel8();
Wrapping wrapping = parcel8.getWrap(1);
System.out.println(wrapping.getValue());
Describ describ = parcel8.getDesrib("parcel8");
System.out.println(describ.readLabel());
Base base = parcel8.getBase(300);
base.f();
}
}
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
public class Wrapping {
private int i;
Wrapping(int i) {
this.i = i;
}
public int getValue() {
return i;
}
}
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
public interface Describ {
String readLabel();
}
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-15.
*/
public abstract class Base {
public Base(int i) {
System.out.println("Base Constructor" + i);
}
public abstract void f();
}
10.6.1 利用内部匿名类构造工厂方法优化.
(把生产工厂合并到具体的产品中,避免为每个产品单独创建一个工厂类)
工厂方法 原理UML
image.png
以下是标准工厂方法和 采用匿名内部类优化的工厂方法对比:
image.png
其中:pencil,pencilFactory 是标准工厂方法,pen是改良后的.
发现改良后的 少了一个PenFactory类.
Ifactory.java
package tinking_in_java.innerClass.FactoryMethod;
/**
* Created by leon on 17-12-16.
*/
public interface IFactory {
IProduct creatProduct();
}
Iproduct.java
package tinking_in_java.innerClass.FactoryMethod;
/**
* Created by leon on 17-12-16.
*/
public interface IProduct {
void write();
}
Pencil.java
package tinking_in_java.innerClass.FactoryMethod;
/**
* Created by leon on 17-12-16.
*/
public class Pencil implements IProduct {
String factory;
public Pencil(String factoryName) {
factory = factoryName;
}
@Override
public void write() {
System.out.println(factory + "制造,用铅笔写字");
}
}
PencilFactory.java
package tinking_in_java.innerClass.FactoryMethod;
/**
* Created by leon on 17-12-16.
*/
public class PencilFactory implements IFactory {
@Override
public IProduct creatProduct() {
return new Pencil("福建");
}
}
Pen.java
package tinking_in_java.innerClass.FactoryMethod;
/**
* Created by leon on 17-12-16.
*/
public class Pen implements IProduct {
String factory;
public Pen(String factory) {
this.factory = factory;
}
@Override
public void write() {
System.out.println(factory + "制造,使用圆珠笔写字");
}
public static IFactory penFactory = new IFactory() {
@Override
public IProduct creatProduct() {
return new Pen("上海");
}
};
}
Client.java
package tinking_in_java.innerClass.FactoryMethod;
/**
* Created by leon on 17-12-16.
*/
public class Client {
public void write() {
//标准的工厂方法
IFactory factory = new PencilFactory();
IProduct pencil = factory.creatProduct();
pencil.write();
//用匿名内部类改造后的 工厂方法.
IProduct pen = Pen.penFactory.creatProduct();
pen.write();
}
public static void main(String[] args) {
Client client = new Client();
client.write();
}
}
10.7嵌套类
如果不需要内部对象与外围类对象有关系,可以采用将内部类申明为static.这通常称嵌套类,因为是static类型,嵌套类不保存外围对象的this引用,所以嵌套类是无法访问外围类的非static类型的变量.
1.创建嵌套对象,不需要依赖其外围对象. 可以直接new XXX.ssss();
2.不能从嵌套类中访问外围对象的非静态变量(对象)
嵌套类和普通内部类的区别.普通内部类 里面不能有静态变量static xxx(可以有静态常量static final xxx,不能是对象),也不能包含嵌套类.
但是嵌套类具备这些限制.可以使用static xxx和static final xxx
嵌套类可以用于作为测试代码:编译的时候会单独生成一个NetsTest$Test.class ,打包时可以把这个删除即可,可以方便的测试
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-16.
*/
public class NetsTest {
public void test() {
System.out.println("test----");
}
public static class Test {
public static void main(String[] args) {
NetsTest netsTest = new NetsTest();
netsTest.test();
}
}
}
10.7.1 接口内部的类(接口嵌套类)
通常不能在接口中实现方法,但是可以在接口中定义嵌套类(因为在接口中).可以实现外围接口.通常接口内的常量,class 都会自动是 public static .原因在 :
static修饰就表示它属于类的,随的类的加载而存在的,如果是非static的话,就表示属于对象的,只有建立对象时才有它,而接口是不能建立对象的,所以接口的常量必须定义为static
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-16.
*/
public interface innerInterface {
void show();
class inner implements innerInterface {
@Override
public void show() {
System.out.println("show in interface");
}
public static void main(String[] args) {
inner inner = new inner();
inner.show();
}
}
}
10.7.2多重内部类
一个内部类嵌套多少层不重要,意思是可以嵌套任意个.但是都是会关联前一个父对象.
10.8为什么需要内部类
内部类可以继承或者实现某些接口,内部类可以操作和访问外围类对象.所以说内部类提供了某种进入外围类的窗口.内部类可以独立继承一个(接口)的实现,与外围类是否已经继承没有关系.
用内部类解决需要多重继承(继承多个具体类或抽象类)问题.
如果不需要解决”多重继承”的问题可以使用其他方法.
内部类还有一些特性:
1.内部类可以有多个实例,并且独立,保持和外围类信息相互独立.
2.单个外围类 可以让多个内部类以不同的方式实现同一接口,或继承同一类.
3.创建内部类的时刻,不依赖外围类对象的创建.(内部类和外围类是同时创建的)
4.对于实现接口或者继承关系,内部类不需要使得外围类符合is-a关系,只需要内部类符合即可.(例如在 迭代器模式中的Sequence 和selector的关系)
10.8.1 闭包和回调(同一个意思的不同说法)
10.9内部类的继承
继承内部类的构造器必须指向外围对象的引用,在继承内部类的时候会比较复杂.问题在于:指向外围对象的秘密引用必须初始化,而导出类中不存在可链接的默认对象.必须采用特殊的语法.
在导出类构造函数加外围类对象,并且 .super();
package tinking_in_java.innerClass;
/**
* Created by leon on 17-12-16.
*/
class WithInner {
public class inner {
}
}
public class InnerHertInner {
public class InnerHerit extends WithInner.inner {
public InnerHerit(WithInner withInner) {
withInner.super();
}
}
public static void main(String[] args) {
InnerHertInner innerHertInner = new InnerHertInner();
}
}
10.10内部类可以覆盖吗?
他们是独立的类,不会有影响.
网友评论