Think in java 读书笔记
pikzas
2019.05.05
第十章 内部类
知识点
1.什么是内部类
可以将一个类定义在另一个类的内部
class OuterClass{ class InnerClass{ } }
2.内部类的主要特征是什么
内部类的主要特征是内部类可以随意访问外部类的属性和方法,不论其访问修饰符是什么
public interface Selector { boolean end(); Object current(); void next(); } public class Seq { private Object[] items; private int next = 0; public Seq(int size){ items = new Object[size]; } public void add(Object item){ if(next < items.length){ items[next++] = item; } } private class SeqSelector 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 getSelector(){ return new SeqSelector(); } public static void main(String[] args) { Seq sq = new Seq(10); for (int i =0 ; i < sq.items.length ; i++){ sq.add(Integer.toString(i)); } Selector selector = sq.getSelector(); //此时,外部类对象的引用会被传递给内部类对象 while (!selector.end()){ System.out.println(selector.current()); selector.next(); } } }
3.适用于内部类的一些特殊语法
.new 由外部类对象新创建内部类对象
class Outer{ class Inner{} } class Demo{ public static void main(String[] args){ Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); } }
.this 在内部类如果想要获取外部类对象的引用
class DotThis{ void f() {System.out.println("DotThis.f()");} class Inner{ public DotThis getOuterObj(){ return DotThis.this; } } public Inner getInner(){ return new Inner(); } public static void main(String[] args){ DotThis dotThis = new DotThis(); DotThis.Inner inner = dotThis.getInner(); System.out.println(inner.getOuterObj().equals(dotThis)); // 输出为true inner.getOuterObj().f(); // inner.getOuterObj() 等同于 dotThis } }
.new 语法也可已看出,必须要先有外部类实例,再由这个实例.new 出内部类。(静态内部类除外)
4.内部类与接口的配合使用
内部类实现接口,并将内部类访问限定为private或者protected,从而将实现隐藏在该内部类中。
public interface Dest { int value(); } public class Demo { private class DestImp implements Dest{ @Override public int value() { return 0; } } public Dest getDest(){ return new DestImp(); } } public class Test { public static void main(String[] args) { Demo demo = new Demo(); Dest dest = demo.getDest(); dest.value(); } }
5.局部内部类
以上看到的内部类都是直接定义在外部类的作用域内,如果让一个内部类定义在外部类的某个方法内部时,这就称作局部内部类。
并且当作用域超出了这个方法的时候,外部是访问不到该类的。
public interface Fruit { String desc(); } public class Test { public Fruit getFruit(){ class Apple implements Fruit{ @Override public String desc() { return "i am apple"; } } return new Apple(); } // getFruit方法之外,是访问不到Apple类的 // private Fruit fruit = new Apple(); }
6.匿名内部类
上面的局部内部类将Apple的定义和new 创建新对象的语法放在一起就是匿名内部类。如果返回的是具体的类,看起来也就没什么特殊了。
public interface Fruit { String desc(); } public class Test2 { public Fruit getFruit(){ return new Fruit(){ @Override public String desc() { return "i am apple"; } }; } public static void main(String[] args) { Test2 test2 = new Test2(); Fruit fruit = test2.getFruit(); System.out.println(fruit.desc()); } }
6.1.匿名内部类可以是具体类,甚至可以对其方法进行override
public class Wrapping { private int i; public Wrapping(int x){ this.i = x; } public int value(){ return i; } } public class Outer { public Wrapping getItem(int x){ return new Wrapping(x){ @Override public int value(){ return super.value() * 47; } }; } public Wrapping getItem2(int x){ return new Wrapping(x); } public static void main(String[] args) { Outer outer = new Outer(); Wrapping wrapping = outer.getItem(10); Wrapping wrapping2 = outer.getItem2(10); System.out.println(wrapping.value()); System.out.println(wrapping2.value()); } } ------输出结果------ 470 10
6.2.对匿名内部类的字段进行初始化
- 使用外部类传入的数据进行初始化,此时该数据必须为final的
public interface SampleInterface { int value(); } public class InitDemo { public SampleInterface getSample(final int x){ //此处final必须为final的 可以理解为外部对象指向的数据不能变动,否则内部类对象会很困惑。 return new SampleInterface() { private int i = x; @Override public int value() { return i; } }; } }
- 通过初始化代码块完成类似于构造器的功能
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){ //此处的变量不用是final的,因为内部类并没用到i return new Base(i){ { System.out.println("inside instance"); // 通过静态代码块可以实现初始化 } public void f(){ System.out.println("in inner f()"); } }; } public static void main(String[] args) { Base base = getBase(47); base.f(); } }
从上面对于传入内部类的参数是否要加上final的修饰规定是,如果内部类用到了外部传入的那个变量,则需要为final的。
内部类可以拿来扩展类,也可以拿来实现接口,但是不能两个都做到,也只能一次实现一个接口。
7.工厂方法的内部类实现方式
public interface Game { boolean move(); } public interface GameFactory { Game getGame(); } public class Chess implements Game { private Chess(){}; private int moves = 0; private static final int MOVES = 4; @Override public boolean move() { System.out.println("Chess moves" + moves); return ++moves != MOVES; } public static GameFactory factory = new GameFactory(){ public Game getGame(){ return new Chess(); } }; } public class Checkers implements Game { private Checkers(){} private int moves = 0; private static final int MOVES = 3; @Override public boolean move() { System.out.println("Checker moves" + moves); return ++moves != MOVES; } public static GameFactory factory = new GameFactory() { @Override public Game getGame() { return new Checkers(); } }; } public class Test { public static void playGame(GameFactory factory) { Game game = factory.getGame(); while(game.move()); } public static void main(String[] args) { playGame(Chess.factory); playGame(Checkers.factory); } } -------输出结果---- Chess moves0 Chess moves1 Chess moves2 Chess moves3 Checker moves0 Checker moves1 Checker moves2
8.嵌套类(也成为静态内部类)
前面讲到的内部类的创建都依赖于外部类的对象,如果内部类定义的时候是static的,那么外部对象就不需要了。
同时因为内部类是static的,那么如果想要使用外部类中的属性或者方法,那么那些属性或者方法必须也是static的。
public class Outer { private int x = 123; private static int y = 999; public static class OneImpl implements OneInterface{ public void f(){ // System.out.println("inner class" + x); 此处因为x不是static的,会提示编译错误。 System.out.println("inner class " + y); } } public static void main(String[] args) { OneInterface one = new OneImpl(); one.f(); } }
8.1.嵌套类的应用
由于接口中的属性和方法默认都是public static的,所以其中可以来嵌套一些需要子类实现都通用的方法。
public interface DemoInterface { void fun(); class DemoImpl implements DemoInterface{ public void fun(){ System.out.println("可以实现我自己的外部接口"); } } } public class DemoTest implements DemoInterface { @Override public void fun() { System.out.println("DemoTest"); } public static void main(String[] args) { DemoInterface demo = new DemoTest(); DemoInterface demo2 = new DemoImpl(); demo.fun(); demo2.fun(); } } -----输出结果----- DemoTest 可以实现我自己的外部接口
嵌套类不同于普通的内部类的另一点在于,编译生成的.class文件名称都是形如Outer$Inner.class这个格式。
而且嵌套类内部还能有嵌套类,以及static声明的属性及方法。
9.内部类无论内部有多少层,内一层的类都可以无条件的访问外部类
10.内部类存在的意义
内部类存在的主要目的是为了解决java没有多继承导致的一些麻烦
- java可以很好的解决多接口的实现问题,如下例子
public interface A { } public interface B { } public class X implements A,B { } public class Y implements A { B getB(){ return new B() {}; } } public class TestOne { public static void funA(A a) { } public static void funB(B b) { } public static void main(String[] args) { X x = new X(); Y y = new Y(); funA(x); funA(y); funB(x); funB(y.getB()); } }
- 但是对于要求子类同时实现抽象类和父类问题时,只有内部类可以派上用场
public class M { } public abstract class N { } public class Z extends M { N getN(){ return new N() {}; } } public class TestTwo { public static void funM(M m) { } public static void funN(N n) { } public static void main(String[] args) { Z z = new Z(); funM(z); funN(z.getN()); } }
11.内部类的继承
内部类与外部类紧紧联系在一起,那么如果想要继承一个外部类的内部类,就需要使用到特殊的语法 OutClass.super();
public class DemoOuter { class Inner{} } public class ExtInner extends DemoOuter.Inner { public ExtInner(DemoOuter demoOuter){ demoOuter.super(); //必须添加如此这般的构造器并显示指定父类对象引用 } }
12.内部类初始化顺序
内部类的构造器会先调用,然后才会调用外部类构造器。
内部类不存在覆盖问题,Egg中的Yolk和BigEgg中的Yolk完全是两个不同的类。
public class Egg { class Yolk{ public Yolk(){ System.out.println("Egg York"); // 1 3 } public void f(){ System.out.println("Egg f()"); } } private Yolk york = new Yolk(); public Egg(){ System.out.println("Egg"); // 2 } public void insert(Yolk y){ this.york = y; } public void g(){ york.f(); } } public class BigEgg extends Egg { class Yolk extends Egg.Yolk{ public Yolk(){ System.out.println("BigEgg Yolk"); // 4 } public void f(){ System.out.println("BigEgg f()"); // 5 } } public BigEgg(){ insert(new Yolk()); } public static void main(String[] args) { BigEgg egg = new BigEgg(); // 0 egg.g(); } } -----输出结果----- Egg York Egg Egg York BigEgg Yolk BigEgg f()
13.局部内部类和匿名内部类的区别
前面讲过,将显示的类声明写出来,在返回一个对象的方式叫做局部内部类,但是如果直接new 接口并返回,那么他就是匿名内部类。
如果某个场景下,需要返回一个接口的多种不同实现,那么只有局部内部类做得到,匿名内部类是做不到的。
14.内部类标识符
匿名内部类 --> LocalInnerClass$1.class
局部内部类 --> LocalInnerClass$Inner.class
来源:https://www.cnblogs.com/Pikzas/p/11341893.html