黑马程序员——Java基础知识——面向对象(三)

来源:互联网 发布:中国好声音 网络版权 编辑:程序博客网 时间:2024/05/06 19:29

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


一、多态

       就是一个抽象的种类可以对应多种属于它这类的具体事物,多态就是事物存在的多种体现形态。例如:动物中有猫、狗,猫这个对象对应的类型是猫类型(猫 x=new 猫());同时猫也是动物中的一种,也可以把猫称为动物,可以这样表示:动物 y=new 猫();那么动物就是猫和狗这些具体事物中向上抽取出来的父类型,父类的引用可以指向子类型对象。

       多态的体现就是父类的引用指向或接收自己的子类对象。例如:动物 y=new 猫(),表示父类类型的变量y引用指向了子类型的对象。类与类之间必须有继承或实现 关系,并且 子类中复写了父类中的方法,这时才可以使用多态。

       多态的出现提高了程序的可扩展性和后期可维护性。例如需要一个父类的不确定的子类对象作为参数传递给一个函数时,定义时可以将这个父类的引用作为形式参数传递给这个函数,而在调用时传入一个子类对象。但在多态中父类引只能访问父类中的成员,也就是父类引用不能调用子类中特有的方法,如代表动物的y不能调用猫对象的抓老鼠的功能。

       父类的引用指向子类对象(例:动物 y=new 猫())的过程中,是猫类型向上转型,提升为动物类型。而当我们需要用父类的引用调用子类的特有功能时,就需要将父类的引用向下转型,转型成子类类型,例如把上面的动物类型向下转换为猫类型(猫 c=(猫)y),这样就可以调用抓老鼠的功能了。但注意不要将父类对象转成子类类型。我们能转换的是父类引用指向了自己的子类对象时,该引用可以向上提升,也可以向下转换,多态自始自终都是子类对象在做着变化。

          下面就以猫狗为例,介绍一下多态的使用。

//定义父类Animal,表示动物abstract class Animal{abstract void eat();}//定义Cat类,表示猫类,继承Animal类class Cat extends Animal{    //复写父类中的方法public void eat(){System.out.println("吃鱼");}//定义特有抓老鼠方法。public void catchMouse(){System.out.println("抓老鼠");}}//定义表示狗的Dog类,继承Animal类class Dog extends Animal{//复写父类中的方法public void eat(){System.out.println("吃骨头");}//定义特有看家方法public void guardHome(){System.out.println("看家");}}class DuoTaiDemo {public static void main(String[] args) {//父类Animal引用指向子类Cat类对象Animal a=new Cat();//调用共性方法                a.eat();Animal b=new Dog();//将父类引用向下转换为Dog类型Dog d=(Dog)b;//调用特有方法。 d.guardHome();//在调用function方法时,将子类对象作为实际参数传给函数。function(new Cat());}//定义父类引用为形式参数,调用时传入其子类对象public static void function(Animal a){a.eat();}}
       多态中成员的特点:

     (1)非静态成员函数的特点:

         在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有,编译失败。

         在运行时期:参阅子类对象所属的类中是否有调用的方法。这就是说,子类复写了父类中的函数,在多态中,父类引用调用这个函数时,调用的是子  类中的方法。

        概括:成员函数在调用时,编译看左边,运行看右边。

     (2)成员变量的特点 

           无论编译和运行,都参考左边(引用变量所属的类)。如:多态中的父类引用访问成员变量时,如果父类和子类有同名的成员变量,那么被调用的是父   类中的成员变量。

     (3)静态成员函数的特点

          无论编译和运行,都参考左边。因为静态函数可以通过类名实现调用,在内存中静态函数随着类的加载而存在于静态方法区中,不需要创建对象。

  下面通过一个小程序了解一下多态的应用,如下:

/*需求:电脑运行实例,电脑运行基于主板。*///定义一个接口,表示插口规则interface PCI{public abstract void open();public abstract void close();}//定义表示主板的类class MainBoard{//定义核心功能,运行public void run(){System.out.println("mainboard run ");}//定义扩展功能,利用PCIpublic void usePCI(PCI p)//PCI p = new NetCard()//接口型引用指向自己的子类对象。{if(p!=null){p.open();p.close();}}}//定义网卡,实现PCI,符合规则class NetCard implements PCI{//复写PCI的功能public void open(){System.out.println("netcard open");}public void close(){System.out.println("netcard close");}}//定义声卡,实现PCI,符合规则class SoundCard implements PCI{public void open(){System.out.println("SoundCard open");}public void close(){System.out.println("SoundCard close");}}class DuoTaiDemo {public static void main(String[] args) {MainBoard mb = new MainBoard();//主板运行mb.run();//将网卡对象作为实际参数传入,实现上网功能。mb.usePCI(new NetCard());//将声卡对象作为实际参数传入,实现发音功能。mb.usePCI(new SoundCard());}}


二、Object类

         Object类是类层次结构的根类,每个类都使用Object作为超类(父类),所有对象(包括数组)都实现这个类的方法,所以该类中的定义的是所有对象都具备的功能。

         Object中定义了用于判断对象是否相等的equals(Object obj)方法,注意在建立对象,自定义比较方法时,只要沿袭Object类中的功能,建立自己特有的比较内容,这就是覆盖,注意传入的参数类型一定要是Object类型,否则不是覆盖,而是重载。

     例如:

//定义一个类,系统会默认为Object的子类class Demo {private int num;Demo(int num){this.num = num;}//复写equals方法,用于定义比较内容public boolean equals(Object obj)//Object obj = new Demo();{        //判断传入的实际参数是否是本类类型if(!(obj instanceof Demo))return false;//向下转换Demo d = (Demo)obj;return this.num == d.num;}}

三、内部类

        当一个类定义在另一个类的里面,里面哪个类就称为内部类,也叫内置类、嵌套类。当描述事物时,有时事物的内部还有事物,这时就可以用内部类描述内部的事物。如人可以定义成一个类,那么人的器官属于人这个类,又有自己特有的功能,这时就可以用内部类描述器官,定义在人这个类中。

        在编译时,如果类中有内部类,生成的.class文件会含有这样的文件:Test$1.class。编译器将会把内部类翻译成用$分割外部类名和内部类名的常规类文件。

        内部类可以直接访问外部类中的成员,包括私有。是因为内部类中持有了一个外部类的引用(外部类名.this)。而外部类要访问内部类,则必须建立内部类的对象。

        外部其他类如何访问内部类,根据内部类的不同定义与在外部类中的不同位置,有下面几种不同的访问格式。

        (1)当内部类定义在外部类的成员位置上,并且非私有,则在外部其他类中,可以直接建立内部类对象。

            格式:外部类名.内部类名 变量名=外部类对象.内部类对象。例如:

<pre name="code" class="java">class Outer{   //在外部类的成员位置上定义一个内部类   class  Inner   {       void method()  {   System.out.println("method run");          }  }}class InnerDemo{public static void main(String[]args){//建立内部类的对象Outer.Inner oi=new Outer().new Inner();//调用内部类的方法oi.method();}}


   当内部类在外部类的成员位置上时,可以被成员修饰符修饰。例如:

        private:将内部类在外部类中进行封装。

        static:被static修饰的内部类就具有静态的特性,但只能访问外部类中的静态成员,出现了访问权限。

       在外部其他类中,访问static内部类的非静态成员的格式为:new 外部类名.内部类名().方法名();而访问static内部类的静态成员格式为:外部类名.内部类名.方法名()。例如:

class Outer{   //在外部类的成员位置上定义一个静态内部类   static class Inner   {       //定义静态方法   static void open()   {              System.out.println("open");      }   void method()  {             System.out.println("method run");          }   }}class InnerDemo{public static void main(String[]args){//调用静态内部内部类的非静态方法new Outer.Inner().method();//调用静态内部内部类的静态方法    Outer.Inner.method();}}
       当内部类中定义了静态成员时,该内部类也必须定义成静态内部类。当外部类中的静态方法访问内部类时,内部类必须是静态内部类。在实际的开发中,内部类通常被定义成私有的。
        (2)内部类也可以定义在局部:内部类可以定义在外部类的方法中,创建这个类型的对象时,仅使用一次,那么可在这个方法中定义内部类。定义在局部的内部类不可以被成员修饰符(如public、private、static等)修饰,因为它的作用于被限定在了声明这个局部类的代码块中。定义在局部的内部类可以直接访问外部类中的成员,因为还持有外部类中的引用。要特别注意的是,内部类不能访问它所在的局部中非最终变量,只能访问被final修饰的局部变量。例如:

class Outer{int x = 3;void method(final int a){//内部类只能访问final修饰的局部变量final int y = 4;        //在局部定义一个内部类class Inner{void function(){System.out.println(a);System.out.println(y);}}    //在方法内部调用内部类的方法。new Inner().function();}}class  InnerClassDemo3{public static void main(String[] args) {Outer out = new Outer();out.method(7);out.method(8);}}
执行结果为:
          

      被传入的参数是一个常量,但当方法被调用完后,传入的常量和方法都从栈内存中释放了,所以当再次调用这个方法时,就可以重新传入被final修饰的变量。

     匿名内部类:

     就是内部类的简写格式。定义一个匿名内部类,这个内部类必须继承一个类或者实现一个接口,格式:new 父类或接口(){定义子类的内容}。可以把匿名内部类理解为一个带内容的匿名子类对象。注意匿名内部类中定义的方法少于3个。

      匿名类的出现简化了代码书写,但匿名内部类只能调用父类或接口中有的方法,不能调用自己的特有方法,不能做强转动作,如果继承的父类或接口中方法较多时,使用内部类阅读性会很差,调用效率低,所以匿名内部类中定义的方法一般少于3个。 

     以一个匿名内部类的小程序,来表明其使用。例如:

abstract class InnerDemo{   abstract void show();}class Outer{   public void function(){   //定义一个匿名内部类   InnerDemo d = new InnerDemo(){   //复写父类方法    void show()    {      System.out.println("show");    }           //定义特有方法    void run()   {      System.out.println("run");   }};                 d.show();         d.run();//编译失败;不能调用特有方法。        }}


四、异常

        就是程序在运行时出现不正常情况。就像人都会生病、路上有时候会堵车,问题是现实生活中一个具体的事物,可以通过Java类的形式进行描述,并封装成对象。其实就是Java对不正常情况进行描述后的对象体现。在程序运行时各方面都可能会出现各种问题,例如用户输入一些非法参数、设备出现问题、存储空间不足、代码错误无法运行等。例如:

class ExceptionDemo{public static void main(String[]args){int x=5;int y=0;        int z=div(x,y);//由于y=0,无法运算,程序出现异常}public static int div(int x,int y){return x/y;}} 

       Java中问题划分为严重的问题和非严重的问题。对于严重的问题,Java通过Error类进行描述。对Error一般不编写针对性的代码对其进行处理;对于非严重的问题,Java通过Exception类进行描述。对于Exception可以使用针对性的处理方式进行处理。无论Error或者Exception都有一些共性内容。比如:出现的不正常情况的信息,引发的原因等。把共性内容向上抽取,最后构成了Java的异常体系:

        Throwable

            |----Error 

            |----Exception  

     Error和Exception有很多子类,它们的子类名都是以父类名作为后缀。

     异常体系中的所有类以及建立的对象都具备可抛性,也就是说可以被throw和throws操作,只有异常体系具备这个特点。

     Exception的大部分异常在编译时,如果没有处理(没有抛没有try),编译会失败。该异常被标示,代表着可以被处理;而在Exception有一个特殊的子类RuntimeException以及它的子类在编译时不需要处理,编译器不检查。该异常发生时,不处理,让程序停止,这时需要对代码进行修正。

      异常的处理:

      Java中提供了特有的语句对异常进行处理,格式如下:

      try
     {
需要被检测的代码;
    }
    catch(异常类 变量)

   {
处理异常的代码;(处理方式)
   }
  finally
 {
一定要执行的语句;
 }

     finally中的定义的通常是关闭系统资源的语句。如果要定义一些必须要执行的语句,可以用try{}finally{}格式,将语句放在finally代码中。但如果程序前面前面执行到System.exit(0),程序会结束,fianlly不会被执行。

    throw和throws:throw定义在函数内,用与抛出异常对象;throws定义在函数上,用于抛出异常类,可以抛出多个异常类,用逗号隔开。当函数内容有throw抛出异常对象,并未处理,必须要在函数上声明,否则编译失败。但是函数内如果抛出的是RuntimeException异常,函数上可以不用声明。

    当在函数中出现了throw抛出异常对象,要么在内部用try catch语句进行处理,要么在函数上声明让方法调用者去处理。一般情况下函数中出现异常,并未处理,函数上需要声明,通过throws的关键字声明该功能可有会出现的异常类型。而调用者需要进行处理。可以继续抛出或者trycatch处理;如果在函数中抛出RuntimeException异常对象。函数上不用声明,编译通过,而调用者也不需要进行处理。这是因为不需要让调用者处理,当该异常发生时,希望程序停止。因为程序运行时出现了无法继续运算的状况,程序停止后,对代码进行检查修正。

        catch中对捕获到的异常对象的常见操作:

             String getMessage();获取异常的信息

             String  toString(); 获取异常类名和异常信息             

             void   printStackTrace();获取异常类名和异常信息,以及异常出现在程序中的位置。JVM默认的异常处理机制,就是在调用printStackTrace方法,打印异常的堆栈的跟踪信息。
              void   printStackTrace(PrintStream s)//将异常内容保存在日志文件中,以便查阅。

       多异常的处理:声明异常时,建议声明更为具体的异常,这样处理的也可以更具体。对方声明几个异常,就对应有几个catch代码块,如果多个catch代 码块中的异常出现继承关系,把处理父类异常的catch代码块放在最下面。否则会报错,因为其余的catch语句执行不到。

       下面通过一段代码,看一下Java中对异常的处理。如下:


class Demo{int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException//声明了该功能中存在的异常。{int[] arr = new int[a];System.out.println(arr[4]);return a/b;}}class  ExceptionDemo2{public static void main(String[] args) //throws Exception{Demo d = new Demo();//检测异常                try{int x = d.div(5,0);System.out.println("x="+x);}//处理异常                catch (ArithmeticException e){System.out.println(e.toString());System.out.println("被零除了!!");}catch (ArrayIndexOutOfBoundsException e){System.out.println(e.toString());System.out.println("角标越界啦!!");}}}
 自定义异常:
           因为项目中会出现特有的问题,而这些问题并未被java所描述并封装对象。所以对这些特有的问题可以按照java中的面向对象思想。将特有的问题,进行自定义的异常封装。定义类继承Exception或者RuntimeException,这样做是为了让该类也具备可抛性和操作异常的共性方法。这就叫做自定义异常。

           在自定义的异常类中,要定义自定义的信息,可以使用父类已经定义好的功能。把异常信息传递给父类的构造函数。因为父类中已经把异常信息的操作都完成了。所以子类只要在构造时,将异常信息通过super语句传递给父类即可,那么就可以通过getMessage()等方法获取自定义的异常信息。在自定义异常时,如果该异常发生时,无法再继续运算,那么可以让自定义的异常类继承RuntimeException。

下面的代码就是表示一个自定义的异常类:

class MyException extends Exception{MyException(String msg){   //把信息传递给父类。    super(msg);}}
          Java中的异常处理方式,可以将问题进行封装,将正常流程语句和问题处理代码分离,便于阅读。在处理异常时,如果catch代码块中处理不了异常,但该异常并不属于       该功能出现的异常,可以将异常转换后,再抛出和该功能相关的异常。或者异常可以处理,但需要将异常产生后和本功能相关的问题提供出去,让调用者知道,并处理。也可     以将捕获异常处理后,转换新的异常。例如:用ATM机给别人转账,当ATM机出现故障,可以去其他的地方转,也可以告诉对方转账不成功。还要注意的是当子类覆盖父类方     法涉及到异常时,子类抛出的异常必须是父类的异常的子类或子集,而父类或者接口没有异常抛出时,子类的方法内容中有异常,只能trycatch处理,不能抛。
   下面是一个处理异常的练习,如下:

/*毕老师用电脑上课。但电脑可能出现的问题:电脑蓝屏,电脑冒烟。可是当电脑冒烟发生后,出现讲课进度无法继续。出现了讲师的问题:课时计划无法完成。*///定义电脑蓝屏异常class LanPingException extends Exception{LanPingException(String message){super(message);}}//定义电脑蓝屏异常class MaoYanException extends Exception{MaoYanException(String message){super(message);}}//定义老师讲课异常class NoPlanException extends Exception{NoPlanException(String msg){super(msg);}}class Computer{          private int state = 3;//电脑运行        public void run()throws LanPingException,MaoYanException{if(state==2)throw new LanPingException("蓝屏了");if(state==3)throw new MaoYanException("冒烟了");System.out.println("电脑运行");}//电脑重启        public void reset(){state = 1;System.out.println("电脑重启");}}class Teacher{private String name;private Computer comp;Teacher(String name){this.name = name;comp = new Computer();}public void teach()throws NoPlanException{try{comp.run();}catch (LanPingException e){comp.reset();}catch (MaoYanException e){test();throw new NoPlanException("课时无法继续"+e.getMessage());}System.out.println("讲课");}public void test(){System.out.println("练习");}}class ExceptionTest {public static void main(String[] args) {Teacher t = new Teacher("毕老师");try{t.teach();}catch (NoPlanException e){System.out.println(e.toString());System.out.println("换老师或者放假");}}}


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

0 0
原创粉丝点击