内部类是封装的一种形式,是定义在类或接口中的类。编程
即定义的内部类做为外部类的一个普通成员(非static),就像下面这样:设计模式
public class Outer { class Inner{ private String id = "夏天"; public String getId() { return id; } } public Inner returnInner(){ return new Inner(); } public void show(){ Inner in = new Inner(); System.out.println(in.id); } }
咱们经过以上一个简单的示例,能够得出如下几点:ide
能够看到,咱们像使用正常类同样使用内部类,但实际上,内部类有许多奥妙,值得咱们去学习。至于内部类的用处,咱们暂且不谈,先学习它的语法也不迟。咱们在另一个类中再试着建立一下这个Inner对象吧:学习
class OuterTest{ public static void main(String[] args) { //!false:Inner in = new Inner(); Outer o = new Outer(); o.show(); Outer.Inner in = o.returnInner(); //!false: can't access --System.out.println(in.id); System.out.println(in.getId()); } }
哦呦,有意思了,咱们在另外一个类OuterTest
中再次测试咱们以前定义的内部类,结果出现了很是明显的变化,咱们陷入了沉思:测试
Inner in = new Inner();
建立内部类实例。returnInner
方法,来建立一个实例,成功!Outer.Inner
,即外部类名.内部类名
。getId
方法访问吧,成功!说到这,咱们大概就能猜想到:内部类的存在能够很好地隐藏一部分具备联系代码,实现了那句话:我想让你看到的东西你随便看,不想让你看的东西你想看,门都没有。this
其实咱们以前在分析ArrayList
源码的时候,曾经接触过内部类。咱们在学习迭代器设计模式的时候,也曾领略过内部类带了的奥妙之处。下面我经过《Java编程思想》上:经过一个内部类实现迭代器模式的简单案例作相应的分析与学习:
首先呢,定义一个“选择器”接口:设计
interface Selector { boolean end();//判断是否到达终点 void next();//移到下一个元素 Object current();//访问当前元素 }
而后,定义一个序列类Sequence:指针
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 selector() { return new SequenceSelector(); } }
SequenceSelector
以private修饰,实现了Selector
接口,提供了方法的具体实现。items
,能够得出结论:内部类自动拥有对其外部类全部成员的访问权。当内部类是非static时,当外部类对象建立了一个内部类对象时,内部类对象会产生一个指向外部类的对象的引用,因此非static内部类能够看到外部类的一切。code
Sequence
的selector
方法返回了一个内部类实例,意思就是用接口类型接收实现类的实例,实现向上转型,既隐藏了实现细节,又利于扩展。咱们看一下具体的测试方法:
public static void main(String[] args) { Sequence sq = new Sequence(10); for (int i = 0; i < 10; i++) { sq.add(Integer.toString(i)); } //产生咱们设计的选择器 Selector sl = sq.selector(); while (!sl.end()) { System.out.print(sl.current() + " "); sl.next(); } }
咱们稍微修改一下最初的Outer:
public class Outer { String id = "乔巴"; class Inner{ private String id = "夏天"; public String getId() { return id; } public String getOuterId(){ return Outer.this.id; } public Outer returnOuter(){ return Outer.this; } } public static void main(String[] args) { Outer o = new Outer(); System.out.println(o.new Inner().getId());//夏天 System.out.println(o.new Inner().getOuterId());//乔巴 } }
return Outer.this;
,即外部类名.this
。Outer.this.id
。咱们来测试一波:
public static void main(String[] args) { Outer.Inner oi = new Outer().new Inner(); System.out.println(oi.getId());//夏天 Outer o = oi.returnOuter(); System.out.println(o.id);//乔巴 }
Outer.Inner oi = new Outer().new Inner();
,即在外部类对象后面用.new 内部类构造器
。咱们对内部类指向外部类对象的引用进行更加深刻的理解与体会,咱们会发现,上面的代码在编译以后,会产生两个字节码文件:
Outer$Inner.class
和Outer.class
。咱们对Outer$Inner.class
进行反编译:
确实,内部类在建立的过程当中,依靠外部类对象,并且会产生一个指向外部类对象的引用。
即在方法做用域内建立一个完整的类。
public class Outer { public TestOuter test(final String s){ class Inner implements TestOuter{ @Override public void testM() { //!false: s+="g"; System.out.println(s); } } return new Inner(); } public static void main(String[] args) { Outer o = new Outer(); o.test("天乔巴夏").testM();//天乔巴夏 } } interface TestOuter{ void testM(); }
须要注意两点:
JDK1.8以后能够不用final显式修饰传入参数和局部变量,但其自己仍是至关于final修饰的,不可改变。咱们去掉final,进行反编译:
能够将内部类定义在任意的做用域内:
public class Outer { public void test(final String s,final int value){ final int a = value; if(value>2){ class Inner{ public void testM() { //!false: s+="g"; //!false: a+=1; System.out.println(s+", "+a); } } Inner in = new Inner(); in.testM(); } //!false:Inner i = new Inner(); } public static void main(String[] args) { Outer o = new Outer(); o.test("天乔巴夏",3); } }
一样须要注意的是:
即用static
修饰的成员内部类,归属于类,即它不存在指向外部类的引用。
public class Outer { static int a = 5; int b = 6; static class Inner{ static int value; public void show(){ //!false System.out.println(b); System.out.println(a); } } } class OuterTest { public static void main(String[] args) { Outer.Inner oi = new Outer.Inner(); oi.show(); } }
须要注意的是:
new Outer.Inner()
建立内部类对象。static final
修饰)。这个类型的内部类,看着名字就怪怪的,咱们先看看一段违反咱们认知的代码:
public class Outer { public InterfaceInner inner(){ //建立一个实现InterfaceInner接口的是实现类对象 return new InterfaceInner() { @Override public void show() { System.out.println("Outer.show"); } }; } public static void main(String[] args) { Outer o = new Outer(); o.inner().show(); } } interface InterfaceInner{ void show(); }
真的很是奇怪,乍一看,InterfaceInner
是个接口,而Outer类的inner方法怎么出现了new InterfaceInner()
的字眼呢?接口不是不能建立实例对象的么?
确实,这就是匿名内部类的一个使用,其实inner方法返回的是实现了接口方法的实现类对象,咱们能够看到分号结尾,表明一个完整的表达式,只不过表达式包含着接口实现,有点长罢了。因此上面匿名内部类的语法其实就是下面这种形式的简化形式:
public class Outer { class Inner implements InterfaceInner{ @Override public void show(){ System.out.println("Outer.show"); } } public InterfaceInner inner(){ return new Inner(); } public static void main(String[] args) { Outer o = new Outer(); o.inner().show(); } } interface InterfaceInner{ void show(); }
不只仅是接口,普通的类也能够被看成“接口”来使用:
public class Outer { public OuterTest outerTest(int value) { //参数传给匿名类的基类构造器 return new OuterTest(value) { @Override public int getValue() { return super.getValue() * 10; } }; } public static void main(String[] args) { Outer o = new Outer(); System.out.println(o.outerTest(10).getValue());//100 } } class OuterTest { public int value; OuterTest(int value) { this.value = value; } public int getValue() { return value; } }
须要注意的是:
内部类能够被继承,可是和咱们普通的类继承有些出处。具体来看一下:
public class Outer { class Inner{ private int value = 100; Inner(){ } Inner(int value){ this.value = value; } public void f(){ System.out.println("Inner.f "+value); } } } class TestOuter extends Outer.Inner{ TestOuter(Outer o){ o.super(); } TestOuter(Outer o,int value){ o.super(value); } public static void main(String[] args) { Outer o = new Outer(); TestOuter tt = new TestOuter(o); TestOuter t = new TestOuter(o,10); tt.f(); t.f(); } }
咱们能够发现的是:
class A extends Outer.Inner{}
。o.super();
,即都须要传入外部类对象做为参数。能够看到的一点就是,内部类内部的实现细节能够被很好地进行封装。并且Java中存在接口的多实现,虽然必定程度上弥补了Java“不支持多继承”的特色,但内部类的存在使其更加优秀,能够看看下面这个例子:
//假设A、B是两个接口 class First implements A{ B makeB(){ return new B() { }; } }
这是一个经过匿名内部类实现接口功能的简单的例子。对于接口而言,咱们彻底能够经过下面这样进行,由于Java中一个类能够实现多个接口:
class First implements A,B{ }
可是除了接口以外,像普通的类,像抽象类,均可以定义独立的内部类去单独继承并实现,使用内部类使“多重继承”更加完善。
因为后面的许多内容尚未涉及到,学习到,因此总结的比较浅显,并无作特别深刻,特别真实的场景模拟,以后有时间会再作系统性的总结。若是有叙述错误的地方,还望评论区批评指针,共同进步。
参考:
《Java 编程思想》
https://stackoverflow.com/questions/70324/java-inner-class-and-static-nested-class?r=SearchResults