zoukankan      html  css  js  c++  java
  • JavaSE 学习笔记03丨继承、接口、多态、内部类

    Chapter. 5 继承

    继承作为面向对象的三大特征之一,它是多态的前提。它主要解决的问题是共性抽取

    Java中的继承,是单继承、多级继承的。

    已存在的类,被称为超类、基类、父类(parent class);新类,被称为子类(subclass)、派生类。

    每一个子类的直接父亲是唯一的,但一个父亲可拥有多个子类。子类比父类拥有的功能更加丰富。

    5.1 继承的格式

    定义父类的格式(普通类的定义)

    public class 父类名称{
        //...
    }
    

    定义子类的格式

    public class 子类名称 extends 父类名称{
        //...
    }
    

    5.2 继承中成员的访问特点

    优先使用本类的成员变量与成员方法,若没有,则往上寻找。

    当局部变量名、本类与父类的成员变量名均相同时,可以用以下语法进行区分使用。

    • 局部变量:直接写该变量名
    • 本类的成员变量:this.成员变量名
    • 父类的成员变量:super.成员变量名

    5.2.1 覆写(Override)

    由于,父类中的有些方法对于子类并不一定适用。因此需要一个新的方法来覆盖父类中的这个方法。

    覆写(或称覆盖、重写),发生在继承关系当中,方法的名称一样,参数列表也一样

    区分:重载(Overload),方法的名称一样,参数列表不一样

    特点:覆写创建的是子类对象,则优先使用子类方法。

    @Override:注解(annotation),写在方法前面,用于检测是否为有效的正确覆写。(属于安全检测手段)

    注意事项:

    1. 子类方法的返回值必须小于等于父类方法的返回值范围。

      java.long.Object类是所有类的公共最高父类(祖宗类)

    2. 子类方法的权限必须大于等于父类方法的权限修饰符。

      public > protected > (default) > private

      (default)代表什么都不写,直接留空。

    3. 若父类抛出多个异常,子类覆写父类方法时,应该抛出 与父类相同的异常 或者 父类异常的子类 或者 不抛出异常;若父类没有抛出异常,子类覆写时不能抛出异常,此时子类产生该异常,只能捕获处理而不能声明抛出。

    /* Phone.java文件中 */
    public class Phone {
        public void show(){
            System.out.println("显示号码");
        }
    }
    
    /* NewPhone.java文件中 */
    public class NewPhone extends Phone {
        @Override
        public void show(){
            super.show();
            System.out.println("显示头像");
            System.out.println("显示号码归属地");
        }
    }
    

    5.3 继承中构造方法的访问特点

    1. 子类构造方法当中有一个默认隐含的super()调用,所以一定是先调用父类无参构造方法,后执行子类的构造方法。

    2. 子类构造中可通过super关键字,来调用父类重载的构造方法。

      /* fa.java文件中 */
      public class fa{
          public fa(){
              System.out.println("父类无参构造!");
          }
          public fa(int num){
              System.out.println("父类构造方法!");
          }
      }
      /* son.java文件中 */
      public class son{
          public son(){
              super(20); //调用父类重载的构造方法
              System.out.println("子类构造方法!");
          }
      }
      
    3. super的父类构造调用,必须为子类构造方法中的第一个只能为第一条语句。因此,super()this()这两种构造调用,不能同时使用。

    5.4 抽象类

    如果父类当中的方法不确定如何进行{}方法体实现,那么这应该是一个抽象方法,相应地,这个父类也必然是抽象类。人们只将它作为派生其他类的基类,而不作为想使用的特定的实例类。

    • 抽象方法:在方法返回值之前加上abstract关键字,然后去掉大括号,直接分号结束。

    • 抽象类:抽象方法所在的类,必须是抽象类。需在class之前利用abstract关键字去声明。

    public abstract class Animal{ //抽象类
        public abstract void eat(); //抽象方法
        public void normalMethod(){ //普通的成员方法
            //...
        }
    }
    

    注意事项:

    1. 不能创建抽象类对象,必须用一子类去继承抽象父类。

    2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

    3. 抽象类中,不一定包含抽象方法,但是具有抽象方法的类必定是抽象类。

    4. 子类必须覆写抽象父类当中所有的抽象方法(除非该子类也是抽象类),即子类中,去掉抽象方法的abstract关键字,然后补上方法体大括号。

      public class Cat extends Animal{
          @Override
          public void eat(){
              System.out.println("The cat eats fish");
          }
      }
      

    Chapter 6. 接口

    接口interface)技术,主要用来描述类具有什么功能,而并不给出每个功能的具体实现。一个类可以实现implement)一个或多个接口,并在需要接口的地方,随时使用实现了相应接口的对象。

    6.1 接口概述

    在Java中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。重要的是,接口不能提供哪些功能,绝不能含有实例域,也不能在接口中实现方法、静态代码块、构造方法。提供实例域和方法实现的任务,应该由实现接口的那个类(可以看做为接口的子类)来完成,实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。

    举个例子,Arrays类中的sort方法承诺可以对对象数组进行排序,但要求满足前提——对象所属的类必须实现了Comparable接口。

    接口是一种引用数据类型,它的内部主要封装了方法,包含抽象方法(JDK7以前),默认方法和静态方法(JDK8),私有方法(JDK9)。

    6.2 定义格式与基本实现

    接口的定义格式:(使用interface关键字)

    public interface 接口名称{ 
        //抽象方法
        //默认方法
        //静态方法
        //私有方法
    }
    

    在使用IDEA创建interface时,应该新建interface而不是class

    但是,换成关键字interface之后,.java文件编译生成的字节码文件仍然为.class

    类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可称为接口的子类。实现使用implements关键字(注意s

    class 类名 implements 接口名{
        //重写接口中抽象方法(必须!)
        //重写接口中默认方法(可选)
    }
    

    6.2.1 含有抽象方法

    抽象方法: 使用 abstract 关键字修饰,没有方法体。该方法供子类实现使用

    定义接口:

    public interface LiveAble{ 
        public abstract void eat();//定义抽象方法
        public abstract void sleep();
    }
    

    定义实现类:

    public class Animal implements LiveAble{
        @Override
        public void eat(){
            System.out.println("获取食物");
        }
        @Override
        public void sleep(){
            System.out.println("地上睡觉");
        } 
    }
    

    TIPS:对于IDEA而言,鼠标选中整个接口名称,按alt + Enter键就能方便覆写:

    定义测试类:

    public class InterfaceDemo{
        public static void main(String[] args){
            Animal a = new Animal(); //创建子类对象
            a.eat(); a.sleep();
        }
    }
    

    6.2.2 含有默认方法

    默认方法:使用default修饰,不可省略,供子类调用或者子类覆写

    定义接口:

    public interface LiveAble{
        public default void fly(){
            System.out.println("天上飞");
        }
    }
    

    实现类可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。

    1. 继承默认方法来定义实现类:

      public class Animal implements LiveAble{
          //继承,什么都不用写,直接调用
      }
      
    2. 重写默认方法来定义实现类:

      public class Animal implements LiveAble{
          @Override
          public void fly(){
              System.out.println("自由自在地飞");
          }
      }
      

    定义测试类:

    public class InterfaceDemo{
        public static void main(String[] args){
            Animal a = new Animal(); //创建子类对象
            a.fly(); //1 的输出结果为:天上飞;2 的输出结果为:自由自在地飞。
        }
    }
    

    6.2.3 含有静态方法

    静态方法:使用static修饰,供接口直接调用。

    定义接口:

    public interface LiveAble{
        public static void run(){
            System.out.println("跑起来!");
        }
    }
    

    静态与.class文件相关,只能使用接口名调用,不可通过实现类的类名或者实现类的对象调用。

    定义实现类:

    public class Animal implements LiveAble{
        //无法重写静态方法!
    }
    

    定义测试类:

    public class InterfaceDemo{
        public static void main(String[] args){
            //Animal.run(); //错误写法,无法继承方法也不能调用。
            LiveAble.run();
        }
    }
    

    6.2.4 含有私有方法

    若一个接口中有多个默认方法,且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法去调用。从设计的角度来讲,私有的方法是对默认方法和静态方法的辅助。

    • 普通私有方法:只有默认方法可以调用。

      private 返回值类型 方法名称(参数列表){
          方法体
      }
      
    • 静态私有方法:默认方法和静态方法可以调用。

      private static 返回值类型 方法名称(参数列表){
          方法体
      }
      

    举例定义接口:

    public interface LiveAble{
    	public default void eat(){
            System.out.println("Eat something...");
            methodCommon();
        }
        public default void sleep(){
            System.out.println("Go to sleep...");
            methodCommon();
        }
        private void methodCommon(){ //该方法是专门为了上面两个方法所抽取出来的,不应被实现类访问。
            System.out.println("AAA");
            System.out.println("BBB");
            System.out.println("CCC");
        }
    }
    

    6.2.5 含有常量

    接口当中其实也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰,从效果上看就是接口的“常量”。

    定义接口:

    public interface LiveAble{
        public static final int num = 233;
        //public 说明接口内外部都可使用;static 使得num与对象无关;final 使得num不可变
    }
    

    注意,接口当中的常量必须进行赋值!

    6.3 实现多个接口

    6.3.1 接口与抽象类的比较

    使用抽象类表示通用属性会存在问题——每个类只能拓展于一个类。不同于C++的多继承特性,为了避免将语言变得非常复杂或者效率降低,Java语言利用接口机制,来实现多继承的大部分功能,由此每个类可以同时实现多个接口

    6.3.2 定义格式

    public class 实例类名 implements 接口A, 接口B{
        //覆盖所有抽象方法
    }
    

    注意事项:

    1. 若实现类所实现的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
    2. 若实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆写。(也就说,不能直接继承这些默认方法)
    3. 一个类中若直接父类当中的方法,与接口当中的默认方法产生了冲突,优先使用父类当中的方法。

    6.4 接口之间的多继承

    类与类之间是单继承的,而接口与接口之间是多继承的。

    接口的继承也是使用extends关键字。

    定义父接口:

    public interface A(){
    	public default void method(){
            System.out.println("AAA");
        }
    } 
    public interface B(){
        public default void method(){
            System.out.println("BBB");
        }
    }
    

    定义子接口:

    public interface C extends A, B{
        @Override
        public default void method(){
            System.out.println("23333");
        }
    }
    

    注意事项:

    1. 如果父接口中的默认方法有重名的,那么子接口需要重写一次。
    2. 子接口重写默认方法时,default关键字可以保留。但是,子类重写默认方法时,default关键字不可以保留。

    Chapter 7. 多态

    多态:指同一行为,具有多个不同表现形式。

    "is-a"规则的另一种表述法是置换法则,它表明程序中出现超类对象的任何地方都可以用子类对象置换。在Java中,对象变量是多态的,一个Employee父类变量既可引用一个Employee类对象,也可以引用一个Employee类的任何一个子类的对象。

    多态的好处在于,使得程序编写得更简单,并具有良好的拓展。

    7.1 多态的体现

    定义格式为:

    父类名称 变量名 = new 子类对象; // Parent a = new Son();
    变量名.方法名(); //a.method();
    

    父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

    7.2 多态中访问成员变量的两种方式

    1. 通过对象名称直接访问成员变量,看new等号的左边是谁,优先用谁,没有则向上找。

      如果子类定义个age成员变量,但父类没有定义,则编译器无法找到age变量

    2. 通过成员方法间接访问成员变量,看该方法属于谁,优先用谁,没有则向上找。

      子类没有覆盖重写,则向父类找;子类如果覆盖重写,则向子类找。

    7.3 多态中访问成员方法的规则

    new的是谁,就优先使用谁,若没有该方法则向上找。

    Parent obj = new Son();
    obj.method(); //父子都有该方法,优先使用子类
    obj.methodOfParent(); //子类没有,但父类有,则向上找到父类。
    

    7.4 向上转型与向下转型

    • 对象的向上转型(较安全)

      父亲引用指向子类对象,然而缺点是,对象一旦向上转型为父类,则无法调用子类原本特有的内容。

      格式为:父类名称 对象名 = new 子类名称;

    • 对象的向下转型

      实际上是一个还原动作,将父类对象还原为原来的子类对象。如果对象创建的时候不是某个类,但此时若要向下转型成该类,则会报错。

      格式为:子类名称 对象名 = (子类名称) 父类对象;

      Animal animal = new Cat(); //创建一个子类Cat对象,然后将它作为父类使用
      Cat cat = (Cat) animal; //本来是Cat,已经被当做Animal,现在还原回来成为本来的Cat
      

    案例实现:笔记本具备使用USB设备的功能,且都预留可插入USB设备的USB接口。定义USB接口,具备基本的开启与关闭功能。鼠标和键盘若要在电脑上正常使用,则必须遵守USB规范,实现USB接口,否则无法使用。

    定义USB接口:

    public interface USB {
        void open(); //打开某个设备
        void close(); //关闭某个
    }
    

    定义鼠标类:(实现接口)

    public class Mouse implements USB{
        public void open(){
            System.out.println("鼠标连接成功!红灯闪烁!");
        }
        public void close(){
            System.out.println("鼠标连接断开!红灯熄灭!");
        }
        public void click(){
            System.out.println("鼠标单击!");
        }
    }
    

    定义键盘类:(实现接口)

    public class KeyBoard implements USB{
        public void open(){
            System.out.println("键盘连接成功!绿灯常亮!");
        }
        public void close(){
            System.out.println("键盘连接断开!绿灯熄灭!");
        }
        public void type(){
            System.out.println("键盘打字!");
        }
    }
    

    定义笔记本类:(使用接口)

    public class Laptop {
        public void run(){
            System.out.println("笔记本运行中....");
        }
        public void useUSB(USB usb){ //usb已经发生向上转型了
            //当笔记本对象调用该功能时,必须给其传递一个符合USB规则的USB设备
            if( usb != null){
                usb.open();
                if(usb instanceof Mouse){
                    Mouse m = (Mouse) usb; //将usb向下转型(还原)为鼠标类
                    m.click(); //调用特定方法
                }
                else if(usb instanceof KeyBoard){
                    KeyBoard kb = (KeyBoard) usb;//将usb向下转型(还原)为键盘类
                    kb.type(); //调用特定方法
                }
                usb.close();
            }
        }
        public void shutDown(){
            System.out.println("笔记本关闭....");
        }
    }
    

    Chapter 8. 内部类

    如果一个事物内部包含另一个事物,那么这就是一个类内部包含另一个类。如类A定义在另一个类B里面,那么内部的类A称为内部类inner class),类B称为外部类。

    而内部类有两种:成员内部类 与 局部内部类(包含匿名内部类)。

    8.1 成员内部类的定义与使用

    一、定义

    成员内部类:定义在类中方法外的类。

    定义格式为

    修饰符 class 外部类名称{
        修饰符 class 内部类名称{
            //...
        }
    }
    

    访问特点:

    • 内部类可以直接访问外部类的数据域(包括私有成员
    • 外部类要访问内部类的成员,必须要建立内部类的对象。

    编译成文件时,外部类为XXX.class,其内部类为XXX$YYY.class

    二、使用

    1. 间接方式:在外部类的方法中,使用内部类;然后main只是调用外部类的方法;

    2. 直接方式:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称;

      Body.Heart myheart = new Body().new Heart();
      myheart.beat();
      

    如果外部类成员变量、内部类成员变量、内部类成员方法中局部变量发生了重名现象,要访问外部类的成员变量需用:外部类名称.this.外部类成员变量名。举例使用如下:

    public class Outer{
        int num = 666;
        
        public class Inner{
            int num = 233;
            
            public void methodInner(){
                int num = 30; //内部类方法的局部变量
                System.out.println(num); //局部变量,就近原则
            	System.out.println(this.num); //内部类成员变量
            	System.out.println(Outer.this.num); //外部类成员变量
            }
        }
    }
    

    8.2 局部内部类的定义与使用

    一、定义

    局部内部类:一个类定义在一个方法(包括main)内部

    只有当前所属的方法才能够使用它,超出该方法则不能再使用。

    普通局部内部类的定义格式:

    修饰符 class 外部类名称{
        
        修饰符 返回值类型 外部类方法名称(参数列表){
            class 局部内部类名称{
                //...
            }
        }
    }
    

    二、使用

    public class Outer{
        
        public void methodOuter(){
            class Inner{ //局部内部类,注意没有任何权限修饰符修饰!
                int num = 233;
                public void methodInner(){
                    System.out.println(num);
                }
            }
            Inner inner = new Inner();
            inner.methodInner();
        } 
    }
    

    注意,局部内部类倘若希望访问其所在方法的局部变量,那么该局部变量必须是“有效final”(要么被final修饰,要么只被赋一次值)

    局部变量是跟随方法走的,在栈内存当中,故方法运行结束之后立刻出栈,局部变量就立刻消失。

    new出来的对象在堆内存当中且持续存在,直到垃圾回收消失。

    8.3 匿名内部类的定义与使用

    一、定义

    匿名内部类anonymous inner class)(开发中,最常用的内部类):局部内部类的简化写法,它本质为一个带具体实现的、父类或者父接口的、匿名的 子类对象

    如果接口的实现类(或父类的子类)只需使用一次,那么可以省略掉该实现类(或子类)的定义,无需再创建个.java文件,而直接改为匿名内部类。

    定义格式:(前提是该匿名内部类必须继承一个父类或者实现一个父接口)

    接口名称 对象名 = new 接口名称(){
      	//方法覆写
        @Override
        public void 成员方法名称{
            //...
        }
    }; //别忘了分号
    

    二、使用(以接口为例)

    public class DemonMain{
        public static void main(String[] args){
            /* 普通写法:
            MyInterface obj = new MyInterfaceImpl();
            obj.method();
            */
            //使用匿名内部类,但不属于匿名对象。
            //等号左边:多态赋值,接口类型引用该子类对象
            MyInterface obj = new MyInterface(){ //等号右边:定义并创建该接口的子类对象
                @Override
                public void method(){
                    System.out.println("匿名内部类实现了方法!");
                }
            }; //大括号内是匿名内部类的内容。
            obj.method(); //调用覆写的方法
            
            //使用匿名内部类,而且省略了对象名称,故也属于匿名对象
            new MyInterface(){
                @Override
                public void method2(){
                    System.out.println("匿名内部类实现了方法!");
                }
            }.method2(); //注意此处调用了方法。
        }
    }
    

    匿名内部类在创建对象的时候,只使用唯一一次(若希望多次创建对象,且类的内容一样的话,那么就必须使用单独定义的实现类)。

    注意区分,匿名对象,在调用方法时,只能调用唯一一次(若希望对同一对象调用多次方法,那么就必须为该对象起名字)。

    hero.setSkill(new Skill(){
    	@Override
        public void use(){
            System.out.println("匿名内部类与匿名对象的同时使用!")
        }
    });
    
  • 相关阅读:
    关于virtualbox配置centos7的网络问题
    centos7在命令行下安装图形界面
    ajax后台返回指定的错误码
    h5前端animate等js特效问题汇总
    tp5中的input助手函数
    使网页滑动效果更加流畅
    关于vagrant环境下项目中图片缓存的问题
    h5图片预览功能
    微信jssdk遇到的一些问题汇总
    curl请求curl_exec返回false,curl_error返回空
  • 原文地址:https://www.cnblogs.com/J-StrawHat/p/13875521.html
Copyright © 2011-2022 走看看