• Java学习日记基础篇(六)—— 抽象类、接口、final


    抽象类

    为什么要有抽象类?

      因为父类方法有不确定性,我们在Animal中定义了一个方法,但是它会被子类的方法覆盖掉,我们就不知道这个方法原本是做什么的

     1 public class test1 
     2 {
     3     public static void main(String[] args) {
     4     }
     5 }
     6 
     7 class Animal
     8 {
     9     String name;
    10     int age;
    11     
    12     //动物会叫
    13     public void cry()
    14     {
    15         System.out.println("不知道怎么叫");
    16         //问题是这个方法永远都不会用到
    17     }
    18 }
    不会被用到的父类方法

      当父类的一些方法不能确定时,可以用 abstract 关键字来修饰该方法或类,称为抽象方法和抽象类。

     1 //抽象类
     2 abstract class Animal
     3 {
     4     String name;
     5     int age;
     6     //动物会叫
     7     abstract public void cry();
     8 }
     9 
    10 //抽象类仍然可以被继承
    定义一个抽象类
     1 public class test1 
     2 {
     3     public static void main(String[] args) {
     4     }
     5 }
     6 //抽象类
     7 abstract class Animal
     8 {
     9     String name;
    10     int age;
    11     abstract public void cry();
    12 }
    13 //当一个类继承的父类是抽象类的话
    14 //需要我们把抽象类中的所有的抽象方法全部实现
    15 class Cat extends Animal
    16 {
    17     //实现父类的cry抽象方法
    18     public void cry()
    19     {        
    20     }
    21 }
    继承一个抽象类

    抽象类的注意事项

    1. 用abstract 关键字来修饰一个类时,这个类就叫做抽象类
    2. 用abstract 关键字来修饰一个方法时,这个方法就叫做抽象方法
    3. 抽象方法在编程中用的不是很多,但是爱考
    4. 抽象类不能被实例化 —— 不能被 new 抽象类
    5. 抽象类可以没有抽象方法
    6. 一旦类包含了abstract方法,则这个类必须声明为abstract类
    7. 抽象方法不能包含主体 
    8. 抽象类中可以有实现的方法,但是如果前面加上abstract就不能被实现

    接口 

     为什么要有抽象类?

      usb插槽就是现实中的接口—— 我们可以把手机,U盘都插到插槽上而不用担心出问题,因为usb插槽的厂家和做设备的厂家都遵守了统一的规定和尺寸,排线等等,但是给设备的内部结构显然是不相同的

       硬件上的设计在软件中也是大量存在的

    package test;
    /*
     * 作者:woliaoa
     * 功能:接口的实现
     * 时间:18.9.16
     */
    public class test2 
    {
        public static void main(String[] args) 
        {
            Computer computer  = new Computer(); //创建
            Camera camera1 = new Camera(); //创建Camera
            Phone phone1 = new Phone(); //创建Phone
            computer.useUsb(camera1); //使用computer中的定义的useUsb方法,并把对象camera1传递给形参
            computer.useUsb(phone1);//使用computer中的定义的useUsb方法,并把对象phone1传递给形参
            
        }
    
    }
    //定义一个接口
    interface Usb
    {
        //在接口中声明了两个方法
        public void start();
        public void stop();
    }
    
    //编写照相机类,并实现Usb接口 —— implements是实现的意思
    //一个重要的原则,当一个类实现了一个接口,就要求该类把这个接口的所有方法统统实现
    class Camera implements Usb
    {
        public void start()
        {
            System.out.println("我是相机,我开始工作了");
        }
        public void stop()
        {
            System.out.println("我是相机,我停止工作了");
        }
    }
    
    class Phone implements Usb
    {
        //实现接口中的所有方法
        public void start()
        {
            System.out.println("我是手机,我开始工作了");
        }
        public void stop()
        {
            System.out.println("我是手机,我停止工作了");
        }
    }
    //计算机类,
    class Computer
    {
        //开始使用Usb接口
        public void useUsb(Usb usb)//前面的是Usb接口 后面的是局部变量usb
        {
            usb.start(); //让形参usb,调用Usb接口中的start方法
            usb.stop();  //让形参usb,调用Usb接口中的stiop方法
        }
    }
    
    
    运行结果:
    我是相机,我开始工作了
    我是相机,我停止工作了
    我是手机,我开始工作了
    我是手机,我停止工作了
    用代码实现USB接口

      接口就是给出一些没有内容的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。语法:

    //定义一个接口
    interface 接口名
    {
    	被初始化的变量;
    	方法;
    }
    
    //使用一个接口
    class 类名 implement 接口
    {
    	变量;
    	方法;
    }
    

    接口的注意事项  

    1.  接口不能被实例化 —— 不能被new
    2. 接口中的所有方法都不能有主体,抽象类中可以有非抽象方法被实现,但是接口中的方法都不能被实现—— 接口是更加抽象的抽象类
    3. 一个类可以实现多个接口—— class Camera implements Usb,Usb3.0 {.....}
    4. 接口中可以有变量,但是要被初始化【变量不能用private和protected修饰】
      1. 接口中的变量本质上都是static的,而且是final的,不管你加不加static修饰
      2. 在java开发中,我们经常把 经常用的变量,定义在接口中,作为全局变量使用,因为它是静态的。
        访问形式:接口名.变量名
    5. 一个接口不能继承其它的类,但是可以继承别的接口
    public class test2 
    {
        public static void main(String[] args) 
        {        
            System.out.println(Usb.a);    //调用Usb接口中的a变量
        }
    
    }
    //定义一个接口
    interface Usb
    {
        int a=1;
        //在接口中声明了两个方法
        public void start();
        public void stop();
    }
    调用接口中的变量

     小结:接口是更加抽象的抽象类,抽象类里有些方法可以有方法体,接口里的所有方法都没有方法体。接口体现了程序设计的多态和高内聚和低耦合的设计思想。

     继承和接口的区别

      Java中没有多继承,只有多重继承,但是可以利用接口实现多重继承

    小猴子不仅像学习猴子的跳技能,还想学习小鸟的飞行技能,鱼的游泳技能,这时因为java不允许有多继承,所以可以使用接口来弥补这一点

     1 interface Fish 
     2 {
     3     public void swimming();
     4 }
     5 interface Bird
     6 {
     7     public void fly();
     8 }
     9 class Monkey
    10 {
    11     int name;
    12     public void jump()
    13     {
    14         System.out.println("猴子会跳");
    15     }
    16 }
    17 //开发的时候,项目经理会定义一堆项目接口,备注上功能,然后让你来完成这个功能
    18 class LittleMonkey extends Monkey implements Fish,Bird
    19 {
    20     public void swimming() 
    21     {}
    22     public void fly() 
    23     {}    
    24 }
    继承多个接口

      Java的继承是单继承,也就是一个类最多只能有一个父类,这种单继承的机制可以保证类的纯洁性,比C++中的多继承机制间接(对C++的一种改良)。但是,它对子类的扩展有一定的影响,所以我们认为:

    1. 实现接口可以看做是对继承的一种补充
    2. 实现接口可在不打破继承关系的前提下,对某个类功能扩展,非常灵活

    还有一点继承是层级式的,不太灵活。
    假如我们修改了类1,就意味着其它的所有类都会被修改,牵一发而动全身。这也是Java只允许单继承的所带来的缺点。所以java中引入了接口很好的解决了这一点。因为接口只针对实现接口的类才起作用

    案例:用接口实现多态

    //汽车接口
    interface Car
    {
        String getName();    //汽车名称
        int getPrice();        //获得汽车售价
    }
    
    //宝马
    class BMW implements Car
    {
        public String getName(){
            return "BMW";
        }
        public int getPrice() {
            return 300000;
        }
    }
    
    //奇瑞qq
    class CheryQQ implements Car
    {
        public String getName() {
            return "CherryQQ";
        }
        public int getPrice() {
            return 20000;
        }
    }
    
    //汽车出售店
    public class CarShop
    {
        //售车收入
        private int money = 0;
        //卖出一部车
        public void sellCar(Car car){
            System.out.println("车型:" + car.getName() + " 单价: " + car.getPrice());
            //增加卖出车售价的收入
            money += car.getPrice();
        }
        //售车总收入
        public int getMoney(){
            return money;
        }
        
        public static void main(String []args){
            CarShop aShop = new CarShop();
            //卖出一辆宝马
            aShop.sellCar(new BMW());
            //卖出一辆奇瑞QQ
            aShop.sellCar(new CheryQQ());
            System.out.println("总收入: " + aShop.getMoney());
        }
    }
    
    
    
    运行结果:
    车型:BMW 单价: 300000
    车型:CherryQQ 单价: 20000
    总收入: 320000
    经典案例:用接口实现多态

      继承是多态得以实现的基础,从字面上理解,多态就是一种类型(都是Car类型)表现出多种状态(宝马和奇瑞QQ)将一个方法调用同这个方法的所属的主体(也就是对象或类)关联起来叫做绑定,分为前期绑定和后期绑定两种

    • 前期绑定:在程序运行之前进行绑定,由编译器和连接程序实现,又叫做静态绑定。比如static方法和final方法,注意:这里也包括private方法,因为它是隐式final的
    • 后期绑定:在运行时根据对象的类型进行绑定,由方法调用机制实现,因此又叫做动态绑定或运行时绑定。除了前期绑定外的所有方法都属于后期绑定
    •  1     public void sellCar(Car car)
       2     {
       3         System.out.println("车型:" + car.getName() + " 单价: " + car.getPrice());
       4         //增加卖出车售价的收入
       5         money += car.getPrice();
       6     }
       7     
       8 后期绑定:在编译的时候我们并不知道将来的Car是宝马还是奇瑞,只有在运行的瞬间才知道car是什么类型
       9 
      10 public class Carshop
      11 {
      12     int a = 1;
      13 }
      14 前期绑定:在程序运行之前就知道a一定是int且等于1
      前期绑定和后期绑定的例子

      总结:所以在编译的时候能够确定的类型就是前期绑定,在运行的时候才能知道是哪一种类型的话称之为后期绑定

    多态就是在后期绑定这种机制上实现的。多态给我们带来的好处是消除了类之间的耦合关系,使程序更容易扩展。比如上例中,新增一种类型汽车的销售,值需要让新定义的类去实现Car接口的所有方法,而无需对原有代码做任何修改,CarShop类的sellCar(Car car)方法就可以处理新的车型了。新增代码如下:

    //桑塔纳汽车
    class Santana implements Car{
    	public String getName(){
    		return "Santana";
    	}
    	public int getPrice() {
    		return 80000;
    	}
    }
    

    final  

     final可以修饰变量或者方法,在实际开发中,使用很广泛。注:final定义的方法或变量的名字都用下划线定义如

    final float rate_aaa_bbb = 3.1415926

    在某些情况下,程序员可能有以下需求:

    1. 当不希望父类的某个方法被子类覆盖(override)时,可以用final关键字修饰
    2. 当不希望类的某个变量的值被修改,可以用final修饰
    3. 当不希望类被继承时,可以用final修饰
    需求1的final实例
     1 public class test4
     2 {
     3     public static void main(String[] args) 
     4     {        
     5     }
     6 }
     7 
     8 class Aaa
     9 {
    10     //假如说我开发了一个特别好的功能,但是不想别人修改它,此时可以在前面加上一个final类
    11     //给方法用public修饰,则表示不可悲修改,不可悲覆盖
    12     final public void sendMes()
    13     {
    14         System.out.println("发送消息");
    15     }
    16 }
    17 
    18 class Bbb extends Aaa
    19 {
    20     public void sendMes()
    21     {
    22         System.out.println("发送消息");
    23     }
    24 }
    25 
    26 
    27 报错:
    28 Cannot override the final method from Aaa
    修改final方法的后果
    public class test4
    {
        public static void main(String[] args) 
        {        
            Aaa aaa = new Aaa();
            Bbb bbb = new Bbb();
            bbb.show();
        }
    }
    
    class Aaa
    {
        int a=0;    //如果不给a赋值,a的默认值为0
    }
    class Bbb extends Aaa
    {
        public Bbb()
        {
            a++;
        }
    
        public void show() {
            System.out.println("a=="+a);
        }
    }
    需求2的final实例——通过Bbb类修改Aaa类中a的值

      这时可以在a前面定义加上final防止a被修改

    final int a = 1;
    
    1 //如果不希望某个类被继承,可以在前面加上final
    2 //final修饰类则表示该类,不能被继承
    3 final class Aaa
    4 {
    5     int a=0;    //如果不给a赋值,a的默认值为0
    6 }
    需求3实例

    如果一个变量是final的,则必须初始化(赋初值),否则编译不会通过。先定义再赋值也不行

    final的注意事项

    1. final修饰的变量又叫常量,一般用xx_xx_xx来命名
    2. final修饰的变量在定义是,必须赋初值,并且以后不能再赋值

    final什么时候用

    1. 因为安全的考虑,类的某个 方法不允许修改
    2. 不想让类被其它类继承
    3. 某些变量的值时固定不变的,比如国家的汇率
  • 相关阅读:
    06-tree Shaking
    05-babel-解析高级js语法+polyfill按需注入
    Symbol.iterator
    回调
    finally
    then的参数
    通过简单例子看Promise(一)
    作为Promise构造函数参数的函数
    resolved和rejected
    resolve和reject
  • 原文地址:https://www.cnblogs.com/houzhaohui/p/9655726.html
Copyright © 2020-2023  润新知