8--黑马程序员---技术总结之抽象类、内部类

----------------------ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

一.抽象类

      1.抽象类的概念
      在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有
包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
      抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。
      比如,在一个图形编辑软件的分析设计过程中,就会发现问题领域存在着圆、三角形这样一些具体概念,它们是不同的,但是它
们又都属于形状这样一个概念,形状这个概念在问题领域并不是直接存在的,它就是一个抽象概念。而正是因为抽象的概念在问题领
域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能够实例化的。通常在编程语句中用abstract修饰的类是抽象类。在
Java中,含有抽象方法的类称为抽象类,不能用来生成对象。抽象类是不完整的,它只能用作基类。在面向对象方法中,抽象类主要
用来进行类型隐藏和充当全局变量的角色。
      抽象类的声明格式为
 abstract class A {
  ···
 }

        2.抽象类的特点
        1)abstract类中可以有abstract方法
与普通的类相比,abstract类可以有abstract方法。对于abstract方法,只允许声明,不允许实现,而且不允许使用final修饰abstract方
法。下面的AbstractDemo1类中的min()方法是abstract方法。

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. abstract class AbstractDemo1 {  
  2.     abstract int min(int x,int y);  //抽象方法不允许实现  
  3.     int max(int x,int y) {          //非抽象方法必须实现  
  4.         return x>y?x:y;  
  5.     }  
  6. }  
        当然,反之,有抽象方法的类一定是抽象类
        注意:abstract类也可以没有abstract方法。

         2)abstract类不能用new运算符创建对象
        对于abstract类,不能使用new运算符创建该类的对象,需产生其子类,由子类创建对象,如果一个类是abstract类的子类,它必须具体实现父类的abstract方法,这就是为什么不允许使用final修饰abstract方法的原因。在下面的代码示例,使用了abstract类。

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:12px;">public class AbstractDemo2 {  
  2.   
  3.     /**使用抽象类,并为之创建对象,调用其抽象方法  
  4.      * @param args  
  5.      */  
  6.     public static void main(String[] args) {  
  7.         // TODO Auto-generated method stub  
  8.         A a;   //A a = new A();是非法的,因为abstract类不能用new运算符创建对象  
  9.         B b = new B();  
  10.         int max = b.max(12, 13);    //调用最大值方法算最大值  
  11.         int min = b.min(12, 13);    //调用最小值方法算最小值  
  12.         System.out.println("12和13的最大值是" + max);   //输出结果是:12和13的最大值是13  
  13.         System.out.println("12和13的最小值是" + min);   //输出结果是:12和13的最小值是12  
  14.     }  
  15.   
  16. }  
  17. abstract class A {  
  18.     abstract int min(int x, int y);   //定义抽象方法算最小值  
  19.     int max(int x, int y) {           //定义抽象方法算最大值  
  20.         return x > y ? x : y;          //返回最大值  
  21.     }  
  22. }  
  23. class B extends A {  
  24.     int min(int x, int y) {           //abstract方法必须实现  
  25.         return x < y ? x : y;          //返回最小值  
  26.     }  
  27. }</span>  
          一个abstract类只关心它的子类是否具有某种功能,并不关心功能的具体行为,功能的具体行为由子类负责实现,抽象类中的抽象方法可以强制子类必须给出这些方法的具体实现。
        在下面的代码示例中,有一个abstract的“图形”类,图形类要求其子类都必须有具有计算面积的功能。

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class AbstractDemo3 {  
  2.   
  3.     /**创建一个abstract的图形类,要求子类必须具有计算面积的功能  
  4.      * @黑马ZWF  
  5.      */  
  6.     public static void main(String[] args) {  
  7.         // TODO Auto-generated method stub  
  8.         梯形  t = new 梯形(2.0, 5.2, 12.3);  
  9.         圆 y = new 圆(5.6);  
  10.         System.out.println("该梯形的面积是:" + t.求面积());   //输出结果是:该梯形的面积是:44.28  
  11.         System.out.println("该圆的面积是:" + y.求面积());     //输出结果是:该圆的面积是:98.52034561375999  
  12.     }  
  13. }  
  14. abstract class 图形 {     //定义抽象类  
  15.     public abstract double 求面积();       //定义抽象方法求面积();这样可以要求子类必须实现  
  16. }  
  17. class 梯形  extends 图形 {  //定义梯形类继承图形类  
  18.     double a, b, h;  
  19.     梯形(double a, double b, double h){  
  20.         this.a = a;  
  21.         this.b = b;  
  22.         this.h = h;  
  23.     }  
  24.     public double 求面积() {           //实现图形的抽象方法  
  25.         return ((1/2.0) * (a + b) * h);  
  26.     }  
  27. }  
  28. class 圆 extends 图形 {        //定义圆类继承图形类  
  29.     double r;  
  30.     final double PI = 3.1415926535;  
  31.     圆(double r) {  
  32.         this.r = r;  
  33.     }  
  34.     public double 求面积() {           //实现图形的抽象方法  
  35.         return PI * r * r;  
  36.     }  
  37. }  

 

        3.比较区别
        与具体类比较
        1)抽象类不能直接实例化,并且对抽象类使用new运算符会导致编译时错误。虽然一些变量和值在编译时的类型可以是抽象的,但是这样的变量和值必须或者为 null,或者含有对非抽象类的实例的引用(此非抽象类是从抽象类派生的)。
        2)允许(但不要求)抽象类包含抽象成员。
        3)抽象类不能被密封。

        与接口比较
         抽象类表示该类中可能已经有一些方法的具体定义,但是接口就仅仅只能定义各个方法的界面(方法名,参数列表,返回类型),并不关心具体细节。
        接口是引用类型的,和抽象类的相似之处有三点:
        1)不能实例化;
        2)包含未实现的方法声明;
        3)派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员)。
        抽象类与接口紧密相关。然而接口又比抽象类更抽象,这主要体现在它们的差别上:
        1)类可以实现无限个接口,但仅能从一个抽象(或任何其他类型)类继承,从抽象类派生的类仍可实现接口,从而得出接口是用来解决多重继承问题的。
        2)抽象类当中可以存在非抽象的方法,可接口不能,且它里面的方法只是一个声明必须用public来修饰没有具体实现的方法。
        3)抽象类中的成员变量可以被不同的修饰符来修饰,可接口中的成员变量默认的都是静态常量(static final)。
        4)抽象类是对象的抽象,然而接口是一种行为规范。
        抽象类里面可以有非抽象方法但接口里只能有抽象方法 声明方法的存在而不去实现它的类被叫做抽像类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽像类,并让它指向具体子类的一个实例。不能有抽像构造函数或抽像静态方法。Abstract 类的子类为它们父类中的所有抽像方法提供实现,否则它们也是抽像类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。接口(interface)是抽像类的变体。在接口中,所有方法都是抽像的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽像的,没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对像上调用接口的方法。由于有抽像类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口。
        4.抽象类的用法要求总结
        1)abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。
        2)在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的成员方法都是abstract的。
        3)abstract class和interface所反映出的设计理念不同。其实abstract class表示的是"is-a"关系,interface表示的是"like-a"关系。
        4)实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法。接口中则不能有实现方法。
        5)接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值。
        6)抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。
        7)接口中的方法默认都是 public,abstract 类型的。

         二.内部类

         1)非静态成员内部类
          A:定义,:在一个外部类中有成员变量和成员方法,那么成员内部类就是把整个一个类当成了外部类的成员对待了
          B: 访问方式:内部类访问外部类,内部类可以直接访问外部类,包括私有成员,因为内部类拥有外部类的引用是类名.this。外部类访问内部类,外部类访问外部类的成员,必须要建立内部类的对象
          格式:外部类名.内部类名 = 外部类对象.内部类对象;
          Outer.Inner oi = new Outer().new Inner();//outer代表外部类,Inner代表内部类
          C:存放位置:在外部类里,在外部类的成员方法外.
          D:修饰符:final、abstract、public、private、protected和static等,那么被static修饰的就是下面所说的静态内部类.

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:12px;">public class InnerDemo1 {  
  2.   
  3.     /**展示非静态成员内部类的创建方式  
  4.      * @黑马ZWF  
  5.      */  
  6.     public static void main(String[] args) {  
  7.         // TODO Auto-generated method stub  
  8.         Outer.Inner oi = new Outer().new Inner();  //非静态成员内部类的创建方式  
  9.         oi.show();          //调用内部类的show()方法  
  10.     }  
  11.   
  12. }  
  13. class Outer {  
  14.     private String name = "张三"; //设置私有变量  
  15.     class Inner {  
  16.         public void show() {  
  17.             System.out.println(Outer.this.name);    //读取name变量并打印  
  18.         }  
  19.     }  
  20. }</span>  
        2)静态内部类
       A:定义,就是在成员内部类的基础上加上static
       B:格式:外部类名.内部类名 = 外部类名.内部类对象;
       Outer.Inner oi = new Outer.Inner();
       C:存放位置:和成员内部类一样,就是多了个static

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class InnerDemo2 {  
  2.   
  3.     /**展示静态内部类的创建方式  
  4.      * @黑马ZWF  
  5.      */  
  6.     public static void main(String[] args) {  
  7.         // TODO Auto-generated method stub  
  8.         Outer.Inner oi1 = new Outer.Inner();    //静态内部类的创建方式  
  9.         oi1.show();                 //输出结果是:Hello World!  
  10.         Outer.Inner2 oi2 = new Outer.Inner2();  
  11.         oi2.show2();                //输出结果是:Hello World!2  
  12.     }  
  13. }  
  14. class Outer {  
  15.     static class Inner {  
  16.         public void show() {  
  17.             System.out.println("Hello World!");  
  18.         }  
  19.     }  
  20.     static class Inner2 {  
  21.         public static void show2() {  
  22.             System.out.println("Hello World!2");    //注意不要在非静态的内部类中定义静态的方法  
  23.         }  
  24.     }  
  25. }  
         3)局部内部类
         A:定义,在外部类成员方法中定义的内部类,他更像局部变量
         B: 注意:
        第一:方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
        第二:方法内部类对象不能使用该内部类所在方法的非final局部变量。因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!例如,如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。 正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。
        第三:方法内部类的修饰符。与成员内部类不同,方法内部类更像一个局部变量 。可以用于修饰方法内部类的只有final和abstract。
        第四:静态方法内的方法内部类。静态方法是没有this引用的,因此在静态方法内的内部类遭受同样的待遇,即:只能访问外部类的静态成员。

        (4)匿名类:匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。这就要采用另一种形式的new语句,也就是匿名类。这种形式的new语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。它还创建那个类的一个新实例,并把它作为语句的结果而返回。要扩展的类和要实现的接口是new语句的操作数,后跟匿名类的主体。如果匿名类对另一个类进行扩展,它的主体可以访问类的成员、覆盖它的方法等等,这和其他任何标准的类都是一样的。如果匿名类实现了一个接口,它的主体必须实现接口的方法。
        A:前提:内部类可以继承或实现一个外部类或者接口。
        B:格式为:new 外部类名或者接口名(){覆盖类或者接口中的代码,(也可以自定义内容。)}
        C:简单理解:就是建立一个带内容的外部类或者接口的子类的匿名对象。
        D: a,继承式的匿名类
                b,接口式(也可以叫实现式的,名字无所谓)的匿名类
                 c,参数式的匿名类

代码示例:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class InnerDemo3 {  
  2.     /**匿名内部类的使用  
  3.      * @黑马ZWF  
  4.      */  
  5.     public static void main(String[] args) {  
  6.         // TODO Auto-generated method stub  
  7.         Outer o = new Outer();  
  8.         o.print();  
  9.     }  
  10. }  
  11.   
  12. class Demo {  
  13.     public void show() {  
  14.         System.out.println("Hello World!");  
  15.     }  
  16. }  
  17.   
  18. class Outer {  
  19.     public void print() {   //匿名内部类  
  20.         new Demo() {  
  21.               
  22.         }.show();  
  23.     }  
  24. }  


            匿名内部类的注意事项
         注意匿名类的声明是在编译时进行的,实例化在运行时进行。这意味着for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。
在使用匿名内部类时,要记住以下几个原则:
     1)匿名内部类不能有构造方法。  
     2)匿名内部类不能定义任何静态成员、方法和类。  
     3)匿名内部类不能是public,protected,private,static。  
     4)只能创建匿名内部类的一个实例。
        5)一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。  
     6)因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
        7)内部类只能访问外部类的静态变量或静态方法。
        8)匿名类和内部类中的中的this :有时候,我们会用到一些内部类和匿名类。当在匿名类中用this时,这个this则指的是匿名类或内部类本身。这时如果我们要使用外部类的方法和变量的话,则应该加上外部类的类名
          匿名内部类的作用
        Java的内部类和C++中的嵌套类有本质的不同:C++的嵌套类没有指向包装类的句柄。仅仅表达一个封装的概念;但是Java的内部类不同,它可以访问包装类的成员(这表示它拥有指向包装类的句柄)。
        匿名内部类是内部类的一种简化写法:return new Wrapper {
                                        ...
                                     };
      等价于:Wrapped extends Wrapper {
          ...
          }
          return new Wrapped();
      难道匿名内部类就只这一点作用吗?考虑一下下面的例子

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. interface ICount {  
  2.     int count();  
  3. }  
  4.    
  5. class Parent {  
  6.     int i = 0;  
  7.     int count() {  
  8.       return i++;  
  9.     }  
  10. }  

         有一个类Child,它既想继承Parent的count()方法,又想实现ICount接口中的count方法,这个时候怎么办呢?内部类就可以大显身手了:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. class Child extends Parent {  
  2.     ICount getCount() {  
  3.         return new ICount {  
  4.         int i = 0;  
  5.         int count() {  
  6.             return (i *= 2);  
  7.         }  
  8.       }  
  9.     }  
  10. }  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值