黑马程序员——4.继承(接口、多态、内部类、异常、包)

来源:互联网 发布:淘宝刷手是什么意思 编辑:程序博客网 时间:2024/03/29 14:22

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

继承:

多个类之间有共性时,可以把共性部分提取出来单独创建一个类,这个类就叫父类或超类;其他的类只要继承这个类,就能拥有这些特性,而不必再单独定义,继承父类的类就叫子类。

继承的好处:

  1. 提高了代码的复用性
  2. 让类与类之间产生了关系,有了这个关系,才有了多态的特性

P.S.

  1. 子类可以直接访问父类中的非私有的属性和行为。
  2. 子类无法继承父类中私有的内容。
  3. 不能单单为了简化书写而定义继承关系,必须是从属关系才可以。

继承的特点:

Java只支持单继承,不支持多继承。
一个类只能有一个父类,不可以有多个父类。

原因:
因为多继承容易出现问题。两个父类中有相同的方法,子类到底要执行哪一个是不确定的(但是java保留了这种功能,并用了另外的实现方式—-多实现)。

Java支持多层继承(继承体系):

C继承B,B继承A,就会出现继承体系。
多层继承出现的继承体系中,使用体系时,查找父类的描述说明以了解体系;在创建对象时,要创建最子类的对象,一是父类可能不能创建对象,二是最子类可用的功能最多。

super关键字:

在子父类中,成员的特点体现:

  1. 成员变量
    this和super的用法很相似。
    this代表本类对象的引用。
    super代表父类的内存空间的标识。

    当本类的成员和局部变量同名用this区分。
    当子父类中的成员变量同名用super区分父类。

  2. 成员函数
    函数的特性:重写(覆盖)

    当子类有与父类一模一样(参数列表个数及返回值类型也要相同)的函数时,子类对象在调用该函数时,会运行子类函数而不是父类函数。在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。

    什么时候使用覆盖操作?
    当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。

    P.S.

    1. 父类中的私有方法不可以被覆盖。
    2. 父类为static的方法无法覆盖。
    3. 覆盖时,子类方法权限一定要大于等于父类方法权限。
  3. 构造函数
    子类构造函数第一行默认有一句super()语句用来访问父类中空参数的构造函数。我们可以自行定义super()的参数列表以调用父类的其他构造函数,但此super调用一定要定义在子类构造函数的开头。

    为什么子类实例化的时候要访问父类中的构造函数呢?
    那是因为子类继承了父类,获取到了父类中内容(属性),所以在使用父类内容之前,要先看父类是如何对自己的内容进行初始化的。

    P.S.

    1. 当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。
    2. 子类构造函数中如果使用this()调用了本类的其他构造函数,那么默认的super();就没有了,因为super和this都只能定义在第一行,所以只能有一个。但是可以保证的是,该this()语句会调用子类中的其他构造函数来访问父类构造函数。就是说,在子类中至少会存在一句构造函数语句用来访问父类构造函数。
    3. super语句必须要定义在子类构造函数的第一行!因为父类的初始化动作要先完成。
class Fu{    Fu(){        super();        //父类方法被子类覆盖,因此调用的是子类的show方法,此时其成员变量num还未进行显示初始化,因此还是默认值0        show();  //先打印“zi show...0”        return;    }    void show(){        System.out.println("fu show" );    }}class Zi extends Fu{    int num = 8;    Zi(){        super();        //通过super初始化父类内容时,子类的成员变量并未显示初始化,等super()父类初始化完毕后,才进行子类的成员变量显示初始化        return;    }    void show(){        System.out.println("zi show..." + num);    }}class ExtendDemo{    public static void main(String[] args){        //初始化子类时先调用父类构造函数        Zi z = new Zi();        //此时子类初始化完毕,再打印“zi show...8”        z.show();    }}

final关键字:

  • final可以修饰类,方法,变量。
  • final修饰的类不可以被继承。
  • final修饰的方法不可以被覆盖。
  • final修饰的变量是一个常量,只能被赋值一次。

被final修饰的变量的写法规范:常量所有字母都大写,多个单词,中间用_连接,例如:public static final double PI = 3.14。

抽象类:

Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。

抽象方法的由来:
多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。

抽象类和抽象方法必须用abstract关键字来修饰,抽象关键字abstract不可以和private、static、final关键字共存。

抽象方法只有方法声明,没有方法体,定义在抽象类中。
格式:修饰符 abstract 返回值类型 函数名(参数列表) ;

抽象类不可以被实例化,也就是不可以用new创建对象。
原因如下:

  1. 抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。
  2. 而且抽象类即使创建了对象,调用抽象方法也没有意义。
  3. 抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。
//定义抽象类abstract class Demo{       //定义抽象方法       //只有方法名,没有方法主体       abstract void show();}//定义子类继承抽象类class DemoA extends Demo{       //覆盖父类的抽象方法       void show(){            //定义抽象方法的方法主体            System.out.println("demoa show" );       }}class DemoB extends Demo{       void show(){            System.out.println("demob show" );       }}class AbstractDemo{       public static void main(String[] args){            DemoA demoA = new DemoA();            demoA.show();            DemoB demoB = new DemoB();            demoB.show();            }}

特殊:抽象类中可以不定义抽象方法,这样做仅仅是为了不让类创建实例对象,通常这个类中的方法有方法体,但是却没有内容。

接口:

当一个抽象类中的所有方法都是抽象的时候,这时可以将该抽象类用另一种形式定义和表示,就是接口。
格式:interface 接口名{}

接口中的成员修饰符是固定的,不可更改:
- 成员常量:public static final
- 成员函数:public abstract
由此得出结论,接口中的成员都是公共的权限。

接口是对外暴露的规则。
接口是程序的功能扩展。

P.S.

  1. 虽然抽象类中的全局变量和抽象方法的修饰符都可以不用写,但是这样阅读性很差。所以,最好写上。
  2. 类与类之间是继承关系,类与接口直接是实现关系。
  3. 接口不可以实例化,能由实现了接口并覆盖了接口中所有的抽象方法的子类实例化。否则,这个子类就是一个抽象类。
//定义接口interface Demo{       //接口中各成员的修饰符固定       public static final int NUM = 4;       //接口中方法都是抽象的       public abstract void show1();       public abstract void show2();}//定义类实现接口class DemoImpl implements Demo{       //实现接口中的抽象方法       public void show1(){}       public void show2(){}}class InterfaceDemo{       public static void main(String[] args){            DemoImpl d = new DemoImpl();            System.out.println(d.NUM);            System.out.println(DemoImpl.NUM);            System.out.println(Demo.NUM);      }}

多实现:

接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。

在java中不直接支持多继承,因为会出现调用的不确定性。
所以,java将多继承机制进行改良,在java中变成了多实现,一个类可以实现多个接口。
接口的出现避免了单继承的局限性。

一个类在继承另一个类的同时,还可以实现多个接口。

    interface A{           public void show();    }    interface Z{           public void show();    }    class Q{        public void method(){        }    }    class Test2 extends Q implements A,Z{    }

多态:

多态指的是一种事物存在的多种形态

例:动物中猫,狗。
猫这个对象对应的类型是猫类型:猫 x = new 猫();
同时猫也是动物中的一种,也可以把猫称为动物:动物 y = new 猫();
动物是猫和狗具体事物中抽取出来的父类型。
父类型引用指向了子类对象。

多态性简单说就是一个对象对应着不同类型。

体现:
父类或者接口的引用指向或者接收自己的子类对象。

作用:
多态的存在提高了程序的扩展性和后期可维护性。

前提:

  1. 需要存在继承或者实现关系。
  2. 需要有覆盖操作。

好处:
- 提高了代码的扩展性,前期定义的代码可以使用后期的内容。
弊端:
- 前期定义的内容不能使用(调用)后期子类的特有内容。

多态时,成员的特点:

  1. 成员变量
    编译和运行都参考等号的左边。
  2. 成员函数(非静态)
    编译看左边,运行看右边。
  3. 静态函数
    编译和运行都看左边。
//一个抽象的动物类abstract class Animal{       //动物的共有方法吃东西,但是具体吃东西的方式不确定,所以是抽象的       abstract void eat();}//猫也是动物的一种class Cat extends Animal{       //覆盖抽象方法,定义猫的吃东西方法       void eat(){            System.out.println("吃鱼");       }       //猫除了动物中都有的吃东西方法外,还有自己的特有的抓老鼠的方法       void catchMouse(){            System.out.println("抓老鼠");       }}class DuoTaiDemo{     public static void main(String[] args){          //自动类型提升,把猫对象提升到了动物类型。但是特有功能无法访问,作用就是限制对特有功能的访问。          //专业讲:向上转型,将子类型隐藏。就不能使用子类的特有方法了。          Animal a = new Cat();          a.eat();  //该对象还是猫对象,eat方法为“吃鱼”          //a.catchMouse();//特有方法不能使用,报错          //如果还想用具体动物猫的特有功能。          //你可以将该对象进行向下转型。          Cat c = (Cat)a; //向下转型的目的是为了能够使用子类中的特有方法。          c.eat();          c.catchMouse();          //注意:对于转型,自始至终都是子类对象在做类型的变化。          //Animal a = new Dog();          //Cat c = (Cat)a;//但是类型不能随意转换,否则可能会报出ClassCastException的异常     }     public static void method(Animal a){          a.eat();     }}

instanceof :用于判断对象的具体类型,只能用于引用数据类型判断,通常在向下转型前用于健壮性的判断。

class DuoTaiDemo{       public static void main(String[] args){       }       public static void method(Animal a){            a.eat();             //如果a为猫对象             if(a instanceof Cat){                  Cat c = (Cat )a;                  c.catchMouse();             }             //如果a为狗对象             else if (a instanceof Dog){                  Dog d = (Dog )a;                  d.lookHome();             }       }}

内部类:

将一个类定义在另一个类的里面,里面那个类就称为内部类(内置类,嵌套类)。

访问特点:
- 内部类可以直接访问外部类中的成员,包括私有成员。
- 而外部类要访问内部类中的成员必须要建立内部类的对象。

内部类的位置:

内部类定义在成员位置上,可以被private、static成员修饰符修饰。被static修饰的内部类只能访问外部类中的静态成员。

如果内部类是静态的,内部类成员也是静态的,可以不用创建内部类对象,直接调用。

内部类被static修饰:

内部类在外部类的成员位上且被static修饰时,内部类就具有了静态的特性,只能访问外部类的静态变量,出现了访问局限,创建对象时可以直接用外部类名创建——new WaiBu.NeiBu().function()

P.S.

  1. 如果内部类中定义了静态成员,该内部类也必须是静态的!
  2. 内部类能直接访问外部类中的成员,因为内部类持有了外部类的引用,外部类名.this。
  3. 内部类定义在局部位置上,也可以直接访问外部类中的成员。同时可以访问所在局部中的局部变量,但必须是被final修饰的。
//定义外部类class Outer{       //外部类成员变量       int num = 3;       //定义内部类       class Inner{             //内部类的成员变量             int num = 4;             void show(){                   //内部类的局部变量                   int num = 5;                   //对局部变量的调用                   System.out.println(num);                   //对内部类成员变量的调用                   System.out.println(this.num);                   //对外部类成员变量的调用——外部类名.this                   System.out.println(Outer.this.num);             }       }       //外部类成员调用内部类方法时,要创建内部类对象来调用       void method(){             new Inner().show();       }}class InnerClassDemo{       public static void main(String[] args){             //定义内部类时要通过外部类             new Outer().Inner().show();       }

匿名内部类:

就是内部类的简化写法。

前提:
内部类可以继承一个外部类或者或实现一个接口。

格式:
new 外部类名或者接口名(){覆盖类或者接口中的代码(也可以自定义内容)}

简单理解:
就是建立一个带内容的外部类或者接口的子类匿名对象。

什么时候使用匿名内部类呢?
通常使用方法是接口类型参数,并且该接口中的方法不超过三个,可以将匿名内部类作为参数传递。

好处:
增强阅读性。

abstract class Demo{       abstract void show();}class Outer{       int num = 4;       void method(){             //继承一个外部类             new Demo(){                   //覆盖方法                   void show(){                        System.out.println("show......" + num);                  }             }.show();//使用方法       }}class InnerClassDemo{       public static void main(String[] args){             new Outer().method();       }}
interface Inter{       void show1();       void show2();}class Outer{       public void method(){            //使用匿名内部类的方法创建对象            Inter in = new Inter(){                   //实现接口,实现方法                   public void show1(){                          System.out.println("...show1...." );                   }                   public void show2(){                          System.out.println("...show2...." );                   }            };            //匿名内部类对象调用方法            in.show1();            in.show2();       }}class InnerClassDemo{       public static void main(String[] args){             new Outer().method();       }}

异常的体系:

异常:是在运行时期发生的不正常情况。

在java中用类的形式对不正常情况进行了描述和封装对象。描述不正常的情况的类,就称为异常类。

  1. 以前正常流程代码和问题处理代码相结合,现在将正常流程代码和问题处理代码分离,提高阅读性。
  2. 其实异常就是java通过面向对象的思想将问题封装成了对象,用异常类对其进行描述。
  3. 不同的问题用不同的类进行具体的描述。比如角标越界、空指针异常等等。
  4. 问题很多,意味着描述的类也很多,将其共性进行向上抽取,形成了异常体系。

不正常情况分成了两大类:

  1. 一般不可处理的:Error
    特点:是由jvm抛出的严重性问题。
    这种问题发生,一般不针对性处理,直接修改程序。
  2. 可以处理的:Exception

Throwable:
无论是error,还是异常、问题,问题发生就应该可以抛出,让调用者知道并处理。
该体系的特点就在于Throwable及其所有的子类都具有可抛性。

可抛性到底指的是什么呢?怎么体现可抛性呢?
其实是通过两个关键字来体现的:throws throw,凡是可以被这两个关键字所操作的类和对象都具备可抛性。

该体系的特点:
子类的后缀名都是用其父类名作为后缀,阅读性很强。

Throwable中的方法:

  1. getMessage():获取异常信息,返回字符串。
  2. toString():获取异常类名和异常信息,返回字符串。
  3. printStackTrace():获取异常类名和异常信息,以及异常出现在程序中的位置(jvm默认异常处理机制),返回值void。
  4. printStackTrace(PrintStream s):通常用该方法将异常内容保存在日志文件中,以便查阅。

通过throw将异常抛出。

throws和throw的区别:

  1. throws用于标识函数暴露出的异常类,并且可以抛出多个,用逗号分隔。throw用于抛出异常对象。
  2. thorws用在函数上,后面跟异常类名。throw用在函数内,后面跟异常对象。

定义功能方法时,需要把出现的问题暴露出来让调用者去处理,那么就通过throws在函数上标识。
在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。

class Demo{    //在可能会发生异常的类的main函数上标明可能出现的异常    public static int method(int[] arr, int index)throws NullPointerException,ArrayIndexOutOfBoundsException{        if(arr == null){            //数组为空时,抛出空参数异常,通过字符串传入异常描述            throw new NullPointerException("数组的引用不能为空!");        }        if(index >= arr.length ){            //角标越界,抛出角标越界异常,通过通过字符串传入异常描述            throw new ArrayIndexOutOfBoundsException("数组的角标越界:" + index);        }        return arr[index];    }}class ExceptionDemo{    public static void main(String[] args){        int[] arr = new int[3];        //异常发生时,会出现特定的异常信息        Demo.method(arr,30);    }}

自定义异常:

可以自定义出现的问题

对于角标为负数的情况,可以用负数角标异常来表示,负数角标这种异常在java中并没有定义过。
那就按照java异常的创建思想,面向对象,将负数角标进行自定义描述,并封装成对象。

这种自定义的问题描述称为自定义异常。

P.S.
如果让一个类成为异常类,必须要继承异常体系,因为只有成为异常体系的子类才有资格具备可抛性,才可以被两个关键字所操作:throws、throw。

自定义类继承Exception或者其子类,通过构造函数定义异常信息。

    //自定义类继承Exception类    Class DemoException extends Exception    {         //通过构造函数传入异常信息         DemoException(String message)         {              //Exception类中已有构造方法,直接父类完成构造方法就可以              super(message);         }    }

异常的分类:

  1. 编译时被检测异常:只要是Exception和其子类都是,除了特殊子类RuntimeException体系。
    这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。
    这样的问题都可以针对性的处理。

  2. 编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类。
    这种问题的发生,无法让功能继续,运算无法运行,更多是因为调用的原因导致的或者引发了内部状态的改变导致的。
    那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行调整。

所以自定义异常时,要么继承Exception,要么继承RuntimeException。

异常处理:

try{    可能会出现异常的代码}catch(异常类 变量){    异常的处理方式}finally{    一定会执行的代码}

P.S.

  1. finally代码块只有一种情况不会被执行,就是在之前执行了System.exit(0)。
  2. try是一个独立的代码块,在其中定义的变量只在该变量块中有效。

处理过程:

  1. try中检测到异常会将异常对象传递给catch
  2. catch捕获到异常进行处理。
  3. finally里通常用来关闭资源。比如:数据库资源,IO资源等。

异常处理的原则:

  1. 函数内容如果抛出需要检测的异常,那么函数上必须要声明。
    否则,必须在函数内用try/catch捕捉,否则编译失败。

  2. 如果调用到了声明异常的函数,要么try/catch,要么throws,否则编译失败。

  3. 什么时候catch,什么时候throws呢?
    功能内容可以解决,用catch。
    解决不了,用throws告诉调用者,由调用者解决。

  4. 一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性处理。
    内部有几个需要检测的异常,就抛几个异常,抛出几个,就catch几个。

try catch finally 代码块组合特点:

  1. try catch finally
  2. try catch(多个):当没有资源需要释放时,可以不用定义finally。
  3. try finally:异常无法直接catch处理,但是资源必须关闭。
class Demo{    public int show(int index)throws ArrayIndexOutOfBoundsException{        if(index < 0)            throw new ArrayIndexOutOfBoundsException("越界啦!");        int[] arr = new int[3];        return arr[index];    }}class ExceptionDemo{    public static void main(String[] args){        Demo d = new Demo();        //try中存放可能出现异常的语句        try{            int num = d.show(-3);            System.out.println("num = " + num);        }         //try中出现异常时,catch会捕捉相应异常进行处理        catch(ArrayIndexOutOfBoundsException e){            //打印异常信息            System.out.println(e.toString());            //退出jvm            System.exit(0);        }         //通常用于关闭(释放)资源        finally{            //由于前面执行了System.exit(0);,故不会执行此语句。            System.out.println("finally");        }        System.out.println("over");    }}

异常的注意事项:

  1. RuntimeException以及其子类如果在函数中被throw抛出,可以不用在函数上声明。
  2. 子类在覆盖父类方法时,父类的方法如果抛出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类。
  3. 如果父类抛出多个异常,那么子类只能抛出父类异常的子集。简单说:子类覆盖父类只能抛出父类的异常或者子类的子集。
  4. 如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛,就只能try。

包:

  • 对类文件进行分类管理。
  • 给类提供多层命名空间。
  • 写在程序文件的第一行。

类名的全称的是:包名.类名。

包也是一种封装形式。

//包名,写在程序的开头,这时候编译出来的class文件会放在mypack文件夹下package mypack;class PackageDemo{       public static void main(String[] args){            System.out.println("Hello Package!");       }}

包之间的访问:

使用到另外的包中的类时,需要写类名的全称:包名.类名(packa.DemoA)。
- 被访问的包中的类权限必须是public的。
- 类中的成员的权限:public或者protected。

protected是为其他包中的子类提供的一种权限,不同包中的子类可以访问父类中被protected修饰的成员

包之间可以被访问的成员:                public  protected   default private同一类中的成员      ok      ok         ok      ok同一包中的成员      ok      ok         ok  不同包中的子类      ok      ok     不同包中成员        ok            

import:

导入:将某个包下类导入进来,为了简化类名的书写而产生,写在package下面,导入的类在调用时不用再写包名(如果各包中类有重名时,调用该类时就必须使用包名区别开)

导包语句如下:
import packa.test.java; //导入某个类
import packa.*; //导入包中的所有类(不包括多级目录下的)
import packa.abc.*; //导入二级目录下的所有类

Jar包:

Java的压缩包。
- 方便项目的携带。
- 方便于使用,只要在classpath设置jar路径即可。

数据库驱动,SSH框架等都是以jar包体现的。

Jar包的操作:
通过jar.exe工具对jar的操作。

DOS命令行操作:

  • 创建jar包:
    jar -cvf mypack.jar packa packb

  • 查看jar包
    jar -tvf mypack.jar [>定向文件]

  • 解压缩
    jar -xvf mypack.jar

  • 自定义jar包的清单文件
    jar –cvfm mypack.jar mf.txt packa packb

0 0
原创粉丝点击