代码改变世界

C\C++ 程序员从零开始学习Android - 个人学习笔记(八) - java基础 - 继承、抽象类、接口、内部类(待续)

2012-02-02 16:04  CreateLight  阅读(537)  评论(1编辑  收藏  举报

1,继承

使用extends从一个现有类(super类)继承一个子类。可以用final修饰一个类,使其不能被继承。

子类不继承super类的构造器。

子类继承super类的方法和域变量。

java是单根继承体系,Object类是所有类(包括接口和抽象类)的最根部(super)的类。Object类没有super类。

1.1 方法继承

  所有的非构造器方法默认都是可被继承的;子类可访问父类中除了private修饰的任何方法。

  子类可覆盖(override)super类中继承的方法。使用final关键字修饰,可令相应的方法不允许被子类覆盖。

  子类覆盖super类的方法时,方法的可见性只能增加(变宽)或不变,而不能减少(缩窄)。比如super类中的方法是protected,子类覆盖方法能将其可见性扩展为public或无修饰符(包可见),而不能将其减少为privete.

  子类覆盖super类的方法时,必须保持相同的函数名、参数类型和顺序;子类方法的返回类型可以和super类相同,也可以为super类方法返回类型的子类。

  用super.f()访问父类方法。

1.2 域继承

  所有域都会被继承,在子类中占用相应的内存空间。

  域不可覆盖,如果在子类中定义了和父类域相同名称相同类型的域,则两者各自有独立的存储空间,互不干扰。用this.i 访问子类域,super.i 访问super类域。   

  

1.3 多态

  super类变量可引用子类对象。

  对象方法会有隐式参数this,来确定是向哪个对象发送消息(调用其方法);static 方法是类方法,不是对象方法,没有this参数。

  当调用对象方法时,会确定实际调用哪个方法,此决策过程称之为方法绑定:

    a, 首先查看对象类型(通过this参数)和方法名,比如a.f(),a的类型为A,则列出A类及其super类中的所有名为 f 的方法,作为候选方法。

    b, 然后查看方法参数(类型和顺序),找出最合适的方法,此步称之为方法重载,可能涉及到类型转换。

    c, 如果是private、final、static方法,则编译器已经可以完全确定对方法的引用,至此的步骤称之为静态绑定,编译时即可确定,以下步骤则需运行时才能确定。

    d, 程序运行时,一定会选择和a所引用对象的类型最合适的那个类型中的方法,比如先在A中寻找,如果找不到,则在其super类中寻找,依次类推。

    e,每次调用方法都需要搜索,开销巨大,因此虚拟机预先为每个类创建一个方法表,其中列出了所有方法的签名和实际调用的方法,因此之前的搜索只需要在这个表中进行即可。

  实例:

    a.f(); // A a;

    运行时动态绑定过程:

    1, 根据this参数,确定a的实际类型,可能为A,也可能为A的子类。

    2,搜索该类型的方法表,找出与f 匹配的方法,至此已完成绑定。

    3,调用此方法。

 

2, 抽象类

 用abstract修饰一个类,将令其为抽象类。抽象类不能被实例化。

  2.1 方法

  抽象类中可以像普通类一样定义方法。

  抽象类中可以定义抽象方法,用abstract修饰,抽象方法没有方法体,只有抽象类才能定义抽象方法。

  如果一个抽象类的子类是非抽象类,则必须实现super类的抽象方法;否则该子类需要声明为一个抽象类(以继承抽象方法)。

  一个非抽象方法可以被子类覆盖为抽象方法,对应的,此子类亦成为了一个抽象类。

  2.2 域

   和普通类无异。

3,接口

  接口不是类,是对类的一组需求的描述,用interface定义接口。实现接口使用implements关键字。接口不能被实例化。接口可以被另一个接口继承以达成扩展的目的。

  3.1 方法

    接口中所有方法自动成为public,不需要显式声明为public。

    接口不能包含静态方法。

  3.2 域

    接口不能含有实例域。但接口可以包含常量。接口中所有的域被自动设置为public static final。  

  3.3 使用

    一个类可以实现多个接口,类必须实现接口中定义的方法。


4,内部类

  内部类可以用static、private修饰。

  private内部类只对外围类可见,可以用来隐藏包可见性。

  内部类是编译器现象,和虚拟机无关。内部类会被编译器翻译成常规类,为其单独生成.class文件,类名为“外围类$内部类",例: A$B // B是A的内部类

  在外围类的作用域之外,可以这样引用内部类:"外围类.内部类",例: A.B //B是A的内部类

  4.1 非static内部类

    4.1.1 方法

      内部类的所有非static方法(包括构造器方法),都会由编译器插入一个隐式参数,这个参数引用了包含这个内部类的类的实例对象。

      内部类的方法通过这个隐式参数,可以直接访问外围类对象的域变量和方法。

      外围类能够访问的所有域变量和方法(包括继承自super类的非private域和方法),内部类都可以访问(包括private和static修饰的那些)。

      外围类方法也可以访问内部类所有域变量和方法。

      非static内部类不能定义static方法,也不能定义静态内部类。

    4.1.2 域

      非static内部类不能定义static域。

      B为A的内部类,则内部类使用 A.this.i 语法访问A的域变量 i 。用A.super.i 语法访问A的直接super类的域变量 i 。

    4.1.3 实例化

    内部类只能由对其可见的类的方法实例化:private内部类只能由外围类的方法进行实例化,protected和默认内部类可由同一个包中的类方法进行实例化,public内部类可由所有类的方法进行初始化。

    由于内部类的构造器方法包含了隐式的指向外围类对象的参数,所以实例化一个内部类必须提供该参数。

    例:B是A的内部类。要实例化B,必须先实例化A,A a = new A(); 然后A.B b = a.new B(); 这里实际上等价于 A.B b = new B(a);编译器会自动给B的构造方法添加引用外围类实例对象的参数。

      或者在A的方法中,使用隐式的this参数进行初始化:A.B b = this.new B();  可简写为:B b = new B();

  4.2 static 内部类

    static内部类除了没有对外围类对象的引用之外,其他的和常规内部类一样。

    定义在接口内的内部类自动成为public和static。

    

  4.3 局部内部类

    定义在一个局部块(方法内部、初始化块、静态初始化块)中的类称之为局部内部类,局部内部类不能用public、private、protected修饰,其作用域限定于块内部。

    局部内部类除了可以访问对象的域变量外,还可以访问块内的局部变量,但局部变量必须以final修饰。

    当实例化(显然只能在局部块内部)一个局部内部类时,编译器会将内部类所用到的局部变量作为参数传递给其构造方法,然后在内部类中新建立域变量(同样用final修饰),用以保存局部变量,对局部变量的访问都是对该域变量的访问。使用final修饰以保证局部变量和保存在域变量中的副本一致。

  4.4 匿名内部类