可以将一个类的定义放在另一个类的定义内部,这就是内部类

创建内部类

内部类看起来就像是一种代码隐藏机制,将类放在其他类的内部,内部类和组合是不同的概念
在外部类的方法里面使用内部类的时候与使用普通类没有什么区别

  1. public class Parcel1 {
  2. class Contents { //把类的定义放置在外围类的里面
  3. private int i = 11;
  4. public int value(){
  5. return i;
  6. }
  7. }
  8. class Destination{
  9. private String label;
  10. Destination(String whereTo){
  11. this.label = whereTo;
  12. }
  13. String readLabel(){
  14. return label;
  15. }
  16. }
  17. public void ship (String dest){ //ship方法里面使用内部类,和使用普通类没什么区别
  18. Contents c = new Contents();
  19. Destination d = new Destination(dest);
  20. System.out.println(d.readLabel());
  21. }
  22. public static void main(String[] args) {
  23. Parcel1 p1 = new Parcel1();
  24. p1.ship("Hello world");
  25. }
  26. }
  1. public class Parcel2 {
  2. class Contents{
  3. private int i = 10;
  4. private int value(){
  5. return i;
  6. }
  7. }
  8. class Destination{
  9. private String label;
  10. Destination (String whereTo){
  11. this.label = whereTo;
  12. }
  13. String readLabel(){
  14. return label;
  15. }
  16. }
  17. public Destination to( String s){
  18. return new Destination(s);
  19. }
  20. public Contents contents(){
  21. return new Contents();
  22. }
  23. public void ship(String dest){
  24. Contents c = new Contents();
  25. Destination d = to(dest);
  26. System.out.println(d.readLabel());
  27. }
  28. public static void main(String[] args) {
  29. Parcel2 p2 = new Parcel2();
  30. p2.ship("Hello World");
  31. Parcel2 p3 = new Parcel2();
  32. //如何操作外部类去链接内部类获取内部类对象
  33. Parcel2.Contents c = p3.contents();
  34. Parcel2.Destination d = p3.to("haha"); 钩子
  35. System.out.println(d.readLabel());
  36. }
  37. }

链接到外部类

10.2重点练习

  1. interface Selector{
  2. boolean end();
  3. Object current();
  4. void next();
  5. }
  6. public class Sequence {
  7. private Object[] items;
  8. private int next = 0;
  9. public Sequence (int size){
  10. items = new Object[size];
  11. }
  12. public void add (Object x){
  13. if (next < items.length){
  14. items[next ++] = x;
  15. }
  16. }
  17. private class SequenceSelector implements Selector{
  18. private int i = 0;
  19. @Override
  20. public boolean end() {
  21. return i == items.length;
  22. }
  23. @Override
  24. public Object current() {
  25. return items[i];
  26. }
  27. @Override
  28. public void next() {
  29. if (i < items.length){
  30. i++;
  31. }
  32. }
  33. }
  34. public Selector selector(){
  35. return new SequenceSelector();
  36. }
  37. public static void main(String[] args) {
  38. Sequence sequence = new Sequence(7);
  39. for (int i = 0 ; i < 7;i++){
  40. sequence.add(Integer.toString(i));
  41. }
  42. Selector selector = sequence.selector();
  43. while (!selector.end()){
  44. System.out.println(selector.current() + "");
  45. selector.next();
  46. }
  47. }
  48. }

.this 和 .new

如果生成对外部对象的引用的时候,可以使用外部类的名字后面加上.this,这样产生的应用会自动的具有正确的类型

  1. public class DotThis {
  2. void f(){
  3. System.out.println("DotThis.f()");
  4. }
  5. public class Inner{
  6. public DotThis outer(){
  7. return DotThis.this; //生成了对外部对象的引用
  8. }
  9. }
  10. public Inner inner(){
  11. return new Inner();//生成内部类的实例
  12. }
  13. public static void main(String[] args) {
  14. DotThis dt = new DotThis();//创建外部类的实例
  15. DotThis.Inner dti = dt.inner();//调用方法创建内部类的实例
  16. dti.outer().f();//使用内部类的方法来调用了外部类的方法
  17. }
  18. }

.new 使用外部类实例加上.new的形式对内部类进行实例化

  1. public class Parcel3 {
  2. class Contents{
  3. private int i = 11;
  4. public int value(){
  5. return i;
  6. }
  7. }
  8. class Destination{
  9. private String label;
  10. Destination(String whereTo){
  11. this.label = whereTo;
  12. System.out.println("wuhu");
  13. }
  14. String readLabel(){
  15. return label;
  16. }
  17. }
  18. public static void main(String[] args) {
  19. Parcel3 p3 = new Parcel3();//创建外围类的实例
  20. Parcel3.Contents c = p3.new Contents();//利用外围类加上.new的方式创建实例
  21. Parcel3.Destination d = p3 .new Destination("Hello");
  22. }
  23. }

内部类和向上转型

当内部类向上转型为基类或者接口的时候,这是因为这个内部类(某个接口的实现)能够完全不可见,而且不能够使用,得到的只是指向基类或者接口的引用,可以隐藏细节
当取得了一个指向基类或接口的引用时,甚至无法找到它的确切类型

  1. class Parcel4{
  2. private class PContents implements Contents{ //私有的。除了Parcel4没有人可以访问
  3. private int i= 0;
  4. @Override
  5. public int value() {
  6. return i;
  7. }
  8. }
  9. protected class PDestination implements Destination{ //除了Parcel4和他的子类。
  10. 没人可以访问
  11. private String Label;
  12. private PDestination(String whereTo){
  13. this.Label = whereTo;
  14. System.out.println(Label);
  15. }
  16. @Override
  17. public String readLabel() {
  18. return Label;
  19. }
  20. }
  21. public Contents contents(){
  22. return new PContents();//通过方法来返回一个接口的实例
  23. }
  24. public Destination destination(String s){
  25. return new PDestination(s);
  26. }
  27. }
  28. public class TestParcel {
  29. public static void main(String[] args) {
  30. Parcel4 parcel4 = new Parcel4();
  31. Contents c = parcel4.contents();
  32. Destination d = parcel4.destination("hah");
  33. }
  34. }

在方法和作用域中的内部类

在方法中或者在任意的作用域中定义内部类
(1)实现某个类型的接口,可以创建并且返回对它的引用
局部内部类:在方法的作用域中创建一个类

  1. public class Parcel5 {
  2. public Contents contents() {
  3. class PContents implements Contents {//在方法内部的类,局部内部类
  4. private int i = 11;
  5. @Override
  6. public int value() {
  7. return i;
  8. }
  9. }
  10. return new PContents();//返回接口的实现类
  11. }
  12. public Destination destination(String s) {
  13. class PDestination implements Destination {
  14. private String label;
  15. PDestination(String s1) {
  16. label = s1;
  17. }
  18. @Override
  19. public String readLabel() {
  20. return label;
  21. }
  22. }
  23. return new PDestination(s);
  24. }
  25. public static void main(String[] args) {
  26. final Parcel5 parcel5 = new Parcel5();
  27. Contents contents = parcel5.contents();
  28. Destination destination = parcel5.destination("hehe");
  29. System.out.println(contents.value());
  30. System.out.println(destination.readLabel());
  31. }
  32. }

匿名内部类

匿名内部类实际上值得时父类引用指向子类的实现
创建一个继承来自一个接口的匿名类对象,通过new表达式返回的引用被自动转型为对接口的引用

  1. public class Parcel7 {
  2. public Contents contents (){
  3. return new Contents() { //匿名类,单独拿出就是一个类
  4. private int i = 0;
  5. @Override
  6. public int value() {
  7. return i;
  8. }
  9. }; //匿名内部类最后加分号
  10. }
  11. public static void main(String[] args) {
  12. Parcel7 parcel7 = new Parcel7();
  13. Contents c = parcel7.contents();
  14. }
  15. }

在匿名内部类中定义字段的时候还可以对其进行初始化

  1. public class Parcel9 {
  2. public Destination destination(final String desk){
  3. return new Destination() {
  4. private String Label = desk; //对Label进行初始化
  5. @Override
  6. public String readLabel() {
  7. return Label;
  8. }
  9. };
  10. }
  11. public static void main(String[] args) {
  12. Parcel9 parcel9 = new Parcel9();
  13. Destination destination = parcel9.destination("wuhu");
  14. }
  15. }

匿名内部类没有名字,如何匿名内部类中做一些类似构造器的行为?利用实例初始化

  1. abstract class Base{
  2. public Base(int i){
  3. System.out.println("Base Constructor.i = " + i);
  4. }
  5. public abstract void f();
  6. }
  7. public class AnonymousConstructor {
  8. public Base getBase(int i){
  9. return new Base(i) {
  10. {
  11. System.out.println("实例初始化");//使用实例初始化的方式做出类似构造器的行为
  12. }
  13. @Override
  14. public void f() {
  15. System.out.println("匿名类中的f()");
  16. }
  17. };
  18. }
  19. public static void main(String[] args) {
  20. AnonymousConstructor anonymousConstructor = new AnonymousConstructor();
  21. Base base = anonymousConstructor.getBase(10);
  22. base.f();
  23. }
  24. }
  1. public class Parcel10 {
  2. public Destination destination(final String desk, final float price){
  3. return new Destination() {
  4. private int cost;
  5. {
  6. cost = Math.round(price);
  7. if (cost > 500){
  8. System.out.println("超出预算啦");
  9. }
  10. }
  11. private final String Label = desk; //在定义字段时对其进行初始化
  12. @Override
  13. public String readLabel() {
  14. return Label;
  15. }
  16. };
  17. }
  18. public static void main(String[] args) {
  19. Parcel10 parcel10 = new Parcel10();
  20. Destination destination = parcel10.destination("玩具",501.001f);
  21. }
  22. }

匿名内部类和继承相比有些受限,匿名内部类可以扩展类,也可以实现接口,但是不能二者兼备,而且如果实现接口也只能实现一个接口
匿名内部类的工厂方法

  1. interface Service{
  2. void method1();
  3. void method2();
  4. }
  5. interface ServiceFactory{
  6. Service getService();
  7. }
  8. class Imp1 implements Service{
  9. @Override
  10. public void method1() {
  11. System.out.println("Imp1.method1()");
  12. }
  13. @Override
  14. public void method2() {
  15. System.out.println("Imp1.method2()");
  16. }
  17. public static ServiceFactory factory(){
  18. return new ServiceFactory() {
  19. @Override
  20. public Service getService() {
  21. return new Imp1();
  22. }
  23. };
  24. }
  25. }
  26. public class Factories {
  27. public static void serviceCun(ServiceFactory serviceFactory){
  28. Service s = serviceFactory.getService();
  29. s.method1();
  30. s.method2();
  31. }
  32. public static void main(String[] args) {
  33. serviceCun(Imp1.factory());
  34. }
  35. }

嵌套类

如果不需要内部类对象和外部类对象之间有联系,那么可以将内部类声明称为static,
普通内部类对象隐式的保存一个引用,这个引用指向了创建它的外部类对象,但是如果内部类是static的话就不是这样的了
如果内部类是static的话:如果要创建嵌套类的对象,就不需要外部类的对象了
不能从嵌套类的对象中访问不是静态的外部类对象
普通内部类中的字段和方法只能放在类的外部层次中,普通内部类中不能有static字段和数据也不能有嵌套类,但是嵌套类中可以有这些东西

  1. public class Parcel11 {
  2. private static class ParcelContents implements Contents{
  3. private int i = 0
  4. @Override
  5. public int value() {
  6. return i;
  7. }
  8. }
  9. protected static class ParcelDestination implements Destination{
  10. private String Label;
  11. ParcelDestination(String whereTo){
  12. this.Label = whereTo;
  13. }
  14. @Override
  15. public String readLabel() {
  16. return Label;
  17. }
  18. }
  19. public static void f(){}
  20. static int x = 10;
  21. static class AnotherLevel{
  22. public static void f(){}
  23. static int x = 20;
  24. }
  25. public static Destination destination(String s){
  26. return new ParcelDestination(s);
  27. }
  28. public static Contents contents(){
  29. return new ParcelContents();
  30. }
  31. public static void main(String[] args) {
  32. Contents c = contents(); //如果内部类是static的话:如果要创建嵌套类的对象
  33. 就不需要外部类的对象了
  34. Destination d = destination("hah");
  35. AnotherLevel.f();
  36. f();
  37. System.out.println(x);
  38. System.out.println(AnotherLevel.x);
  39. }
  40. }

接口内部的类

嵌套类可以作为接口的一部分,并且放在接口中的任何类都自动的为public和static的

  1. public interface ClassInInterface {
  2. void howdy();
  3. class Test implements ClassInInterface{
  4. @Override
  5. public void howdy() {
  6. System.out.println("Howdy!");
  7. }
  8. public static void main(String[] args) {
  9. new Test().howdy();
  10. }
  11. }
  12. }

从多层嵌套类中访问外围类成员

一个内部类被嵌套多少层不重要,它都能透明的访问它所嵌入的外围类的所有成员

  1. class MNA{
  2. private void f(){}
  3. class A{
  4. private void g(){}
  5. public class B{
  6. void h(){
  7. g();
  8. f();
  9. }
  10. }
  11. }
  12. }
  13. public class MultiNestingAccess {
  14. public static void main(String[] args) {
  15. MNA mna = new MNA();
  16. MNA.A mnaa = mna.new A();
  17. MNA.A.B mnaab = mnaa.new B();
  18. mnaab.h();
  19. }

为什么需要内部类

首先每个内部类都能独立的继承来自一个接口的实现,所以无论外围类是否已经继承过了某个接口类的实现,对内部类来说都是没有影响的
内部类有效的继承了“多重继承”,内部类允许继承多个非接口类型

  1. interface A{}
  2. interface B{}
  3. class X implements A,B{}
  4. class Y implements A{
  5. B makeB(){
  6. return new B() {}; //返回一个B的引用
  7. }
  8. }
  9. public class MultiInterfaces {
  10. static void takesA(A a){}
  11. static void takesB(B b){}
  12. public static void main(String[] args) {
  13. X x = new X();
  14. Y y = new Y();
  15. takesA(x);
  16. takesA(y);
  17. takesB(x);
  18. takesB(y.makeB());
  19. }
  20. }

如果拥有的时抽象类或者具体的类不是接口,那么只能使用内部类来实现多继承

  1. class D{}
  2. abstract class E{}
  3. class Z extends D{
  4. E makeE(){
  5. return new E(){};
  6. }
  7. }
  8. public class MultiImplementation {
  9. static void takeD(D d){}
  10. static void takeE(E e){}
  11. public static void main(String[] args) {
  12. Z z = new Z();
  13. takeD(z);
  14. takeE(z.makeE());
  15. }
  16. }

内部类还有一些其他的特性
(1)内部类可以有多个实例,每个实例都有自己的状态信息,并且与外围类对象的信息相互独立
(2)在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承一个类
(3)创建内部类对象的时刻并不依赖外围类

内部类的继承

内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,哪个指向外围类对象的秘密引用必须被初始化,而在导出类中不再存在可连接的默认对象

  1. class WithInner{
  2. class Inner{}
  3. }
  4. public class InheritInner extends WithInner.Inner { //只继承了内部类
  5. InheritInner (WithInner withInner){ //不能只传递一个指向外围类的对象
  6. //必须要在构造器中用super来初始化继承内部类的外围类
  7. withInner.super();
  8. }
  9. public static void main(String[] args) {
  10. final WithInner withInner = new WithInner();
  11. InheritInner inheritInner = new InheritInner(withInner);
  12. }
  13. }

内部类可以被覆盖吗

  1. class Egg{
  2. private Yolk y;
  3. protected class Yolk{
  4. public Yolk(){
  5. System.out.println("Egg.Yolk");
  6. }
  7. }
  8. public Egg(){
  9. System.out.println("new Egg");
  10. y = new Yolk();
  11. }
  12. }
  13. public class BigEgg extends Egg{
  14. public class Yolk{
  15. public Yolk(){
  16. System.out.println("BigEgg.Yolk");
  17. }
  18. }
  19. public static void main(String[] args) {
  20. new BigEgg();
  21. }
  22. }

当继承了某个外围类的时候,内部类并没有什么变换,这两个内部类时两个完全独立的实体

  1. class Egg{
  2. private Yolk y;
  3. protected class Yolk{
  4. public Yolk(){
  5. System.out.println("Egg.Yolk");
  6. }
  7. }
  8. public Egg(){
  9. System.out.println("new Egg");
  10. y = new Yolk();
  11. }
  12. }
  13. public class BigEgg extends Egg{
  14. public class Yolk{
  15. public Yolk(){
  16. System.out.println("BigEgg.Yolk");
  17. }
  18. }
  19. public static void main(String[] args) {
  20. final BigEgg bigEgg = new BigEgg();
  21. BigEgg.Yolk by = bigEgg.new Yolk();
  22. }
  23. }
  1. class Egg2{
  2. protected class Yolk{
  3. public Yolk(){System.out.println("Egg2.Yolk()");}
  4. public void f(){System.out.println("Egg2.Yolk.f()");}
  5. }
  6. private Yolk yolk = new Yolk();
  7. public Egg2(){System.out.println("new Egg2()");}
  8. public void insertYolk(Yolk yolk1){yolk = yolk1;}
  9. public void g(){yolk.f();}
  10. }
  11. public class BigEgg2 extends Egg2{
  12. public class Yolk extends Egg2.Yolk{
  13. public Yolk(){System.out.println("BigEgg2.Yolk()");}
  14. public void f(){System.out.println("BigEgg2.Yolk.f()");}
  15. }
  16. public BigEgg2(){insertYolk(new Yolk());}
  17. public static void main(String[] args) {
  18. final Egg2 egg2 = new BigEgg2();
  19. egg2.g();
  20. }
  21. }

局部内部类

局部内部类一个典型的方式是在方法的方法体内创建,局部内部类中不能有访问说明符,因为它并不是外围类的一部分,但是它可以访问当前代码块的常量,还有他的外围类的所有成员
为什么使用局部内部类而不是匿名内部类?
我们需要一个构造器,或者重载构造器,但是匿名内部类中没有构造器,仅仅只有实例初始化起到了类似构造器的功能

  1. interface Counter{int next();}
  2. public class LocalInnerClass {
  3. private int count =0;
  4. Counter getCounter(String name){ // 局部内部类在方法内部创建
  5. class LocalCount implements Counter{
  6. public LocalCount(){System.out.println("LocalCount()");}
  7. @Override
  8. public int next() {
  9. System.out.print(name);
  10. return count++;
  11. }
  12. }
  13. return new LocalCount();
  14. }
  15. Counter getCounter2(String name){ //匿名内部类
  16. return new Counter() {
  17. {
  18. System.out.println("Counter()");
  19. }
  20. @Override
  21. public int next() {
  22. System.out.print("Counter()");
  23. return count++;
  24. }
  25. };
  26. }
  27. public static void main(String[] args) {
  28. final LocalInnerClass localInnerClass = new LocalInnerClass();
  29. Counter c1 = localInnerClass.getCounter("LocalInner");
  30. Counter c2 = localInnerClass.getCounter2("AnonymousInner");
  31. for (int i = 0 ; i < 5 ; i++){
  32. System.out.println(c1.next()); //两个内部类中的next方法访问的是同一个count
  33. }
  34. for (int i = 0 ; i < 5 ; i ++){
  35. System.out.println(c2.next());
  36. }
  37. }
  38. }