zoukankan      html  css  js  c++  java
  • Java 内部类

    一、前言


    二、内部类定义

    内部类定义非常简单,就是把一个类的定义放在另外一个外围类定义的里面。如下面代码所示:

    class OutterClass {
        class InnerClass {    
        }
    }

    你可能会想,内部类和组合有什么区别?就这么简单把内部类的定义放在外部类里面,能惹出多少事儿来?诶,你还别说,还真有不少的事儿。

    三、内部类惹出来的那些事儿

    1.    内部类能访问外围类的所有成员,包括私有成员。

    当生成一个内部类对象时,此对象与制造它的外围类对象之间就有了一种联系,所以它能访问其外围类对象的所有成员,而不需要任何特殊的条件。如下面代码所示:

    class OutterClass {
        private int i = 1;
        class InnerClass {
            public void displayPrivate() {
                System.out.println(i);
            }
        }
    }

    public class MainClass{
        public static void main(String[] args) {
            OutterClass outter =  new OutterClass();
            OutterClass.InnerClass inner = outter.new InnerClass();
            inner.displayPrivate();
        }
    }

    由上面的代码可以看出,内部类能够访问外部类的私有成员变量。在这段代码中,还需要注意的是

    ①生成内部类对象,必须要先有外围类对象,具体的做法请见代码;

    ②内部类能访问外围类的私有成员这一点,C++中的嵌套类是没有这个特性的。

    2.  内部类与static

    ①内部类不能含有static方法;

    ②内部类不能含有static数据成员,除非是static final;

    ③内部类可以继承含有static成员的类。

    3.  匿名内部类

    匿名内部类,看起来非常奇怪。因为它太简洁了,但这也带来一个好处,用匿名内部类写出来的代码通常比较简洁啦!见下面代码(左):

    4. 内部类允许继承多个非接口类型

    分析左边的代码,可以看出这个语法非常的奇怪。但是仔细想想,也比较容易理解。语法的核心就是return new () {匿名类定义},这个匿名类是AnonymouysBase的子类,然后向上转型为AnonymouysBase类。其实左边代码是右边代码的简化,大家可以对照看一下。所以呢,匿名内部类,可以简化代码。关于匿名内部类还需要注意:

    ①匿名内部类所使用的参数必须是final;

    ②匿名内部类因为没有名字,所以不可能有构造函数,只能通过实例初始化来达到一个构造器的效果。

    众所周知,java和c++中其中一个不同的地方在于,java是没有多重继承的。可是在某些时候,我们的确还是需要多重继承的,为此java中提出了“接口”的概念。一个类可以实现多个接口,这就解决了多重继承问题吗?我们仔细想想,其实接口只部分解决了多重继承的问题。而每一个内部类都能独立地继承自一个接口或者类,所以无论外围类是否继承了某个接口或者类,对内部类都没有影响。所以内部类和接口双剑合璧,给出了java中多重继承的完美替代方案。见下面代码:

    class D {}
    abstract class E {}

    class Z extends D {
      E makeE() { return new E() {}; }
    }

    public class MultiImplementation {
      static void takesD(D d) {}
      static void takesE(E e) {}
      public static void main(String[] args) {
        Z z = new Z();
        takesD(z);
        takesE(z.makeE());
      }
    } ///:~

    5.内部类继承

    因为内部类的构造器必须链接到指向外围类对象的引用。所以,当一个类需要继承内部类时,那个神秘的链接到外部类的引用也必须要得到初始化。见代码:

    class WithInner {
      class Inner {}
    }

    public class InheritInner extends WithInner.Inner {
      //! InheritInner() {} // Won't compile
      InheritInner(WithInner wi) {
        wi.super();
      }
      public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner ii = new InheritInner(wi);
      }
    }

    如上面代码所示,InheritInner继承内部类,为了能够使得指向外部类的那个神秘的引用能够得到初始化,需要将外部类的引用作为参数传进内部类的构造函数,并且在内部类的构造函数中代用外部类的super()函数。

    6. 内部类不能被覆盖

    有一个外围类,含有一个内部类。当有另外一个类去继承这个外围类,并且去覆盖这个内部类。真的能覆盖吗?见下面代码:

    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 {
      public class Yolk {
        public Yolk() { System.out.println("BigEgg.Yolk()"); }
      }
      public static void main(String[] args) {
        new BigEgg();
      }
    } /* Output:
    New Egg()
    Egg.Yolk()
    *///:~

    由程序的输出结果可以看到,Egg和它的派生类BigEgg中的内部类Yolk其实是独立的,它们分布在不同的空间。

    7. 内部类VS嵌套类

    Java中的嵌套类是指:将内部类声明为static,那么这个内部类就变成了一个嵌套类。因此,相对于内部类来说,嵌套类两个显著的同点在于:

    要创建嵌套类的对象,并不需要其外围类的对象,所以内部类中那个指向外围类的神秘的引用在嵌套类中就消失了;

    ‚不能从嵌套类的对象,访问其外围类的非static成员;

    ƒ嵌套类中可以包括static成员或方法。

    Java中的嵌套类和c++中的嵌套类,实际上还是有一点不一样的。C++中的嵌套类不能访问外围类的私有成员,但是java中嵌套类可以访问其外围类中static的私有成员。见代码:

    class OutterClass {
        static class AnotherLevel {
           public static void f() {System.out.println("test");}
        }
    }

    public class MainClass {
        public static void main(String[] args) {
            OutterClass.AnotherLevel.f();
        }
    } ///:~

    8.为什么要使用 内部类?

    介绍完java中的内部类,现在我们再回过头来总结一下,为什么要使用内部类?楼主也只能凭着楼主

    的理解进行一下总结:

    ①内部类提供进入其外围类的绿色通道;

    ‚②一般来说,内部类继承自某个类或实现某个接口,和接口一起实现java中多重继承;

    ③private内部类给类的设计者提供了一种途径,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏了实现的细节;

    ④ƒ匿名内部类可以使得代码更加地灵活。

  • 相关阅读:
    .NET Core 1.0正式发布
    【Azure Active Directory】单一登录 (SAML 协议)
    js原型链prototype与__proto__以及new表达式
    Claims Based Authentication and Token Based Authentication和WIF
    try-catch-finally对返回值的影响
    基于任务的异步编程模式,Task-based Asynchronous Pattern
    Session.Abandon-Session.Clear-Session.RemoveAll
    Html.Partial方法和Html.RenderPartial方法
    dynamic和nullable一起使用时的注意
    C#获取文件的Content-Type(MIME Type)的方法
  • 原文地址:https://www.cnblogs.com/jiangxifanzhouyudu/p/6637470.html
Copyright © 2011-2022 走看看