zoukankan      html  css  js  c++  java
  • 一文打尽Java抽象类和接口的相关问题

    相关文章:

    1. 《面向对象再探究》:介绍了面向对象的基本概念
    2. 《详解Java的对象创建》:介绍了对象的创建、构造器的使用
    3. 《一文打尽Java继承的相关问题》:介绍了继承的使用方式和注意事项

    本文来介绍Java的抽象类和接口的使用。

    1. 抽象类

    在前面的文章中提到过:父类比子类更加抽象,子类比父类更加具体。

    《一文打尽Java继承的相关问题》这篇文章中举了动物和狗的例子:

    public class Animal {
        private String name;
        private int age;
    
        public Animal(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public Animal() {
        }
    
        public void say() {
            System.out.println("我是" + name + ",今年" + age + "岁了");
        }
    
    	//getters and setters ...
    }
    
    public class Dog extends Animal {
    
        private String address;
    
        public Dog(String name, int age, String address) {
            super(name, age);
            this.address = address;
        }
    
        public Dog() {
        }
    
        public void say() {
            System.out.println("我叫" + super.getName() + ",今年" + super.getAge() + "岁了,家住" + address + ",汪汪汪...");
        }
    
        public void watchDoor() {
            System.out.println("我在" + address +"看门...");
        }
    
    	//getters and setters ...
    }
    

    上面Dog类很自然地继承了Animal类,没毛病!但是仔细琢磨一下Animal类的say()方法。

    动物是一个很宽泛的概念,如果写代码表示,我们平常见到的大部分生物都可以继承该类。如下图:

    有这么多类继承Animal类,那Animal类的say()方法这样写合适吗?因为每种动物说话的方式都不同,而动物又是这么宽泛的一个概念,我们通常会new Dog()new People(),但很少会去new Animal()

    所以Animal类的say()方法体对其来说就是鸡肋一般的存在,因为即使有方法体,也会被其子类重写。既然这样那就干脆不要方法体了。

    换句话说,我们将Animal类再进行更高层次地抽象,它具有各种动物都有的属性,比如nameage,也有各种动物都有的行为,比如say但是,Animal类并不具体实现该方法,具体的实现交给子类来做

    这样一来,谁继承了Animal类,谁就有了它的属性和行为,子类不用管父类的行为是否合适,因为父类的行为“有名无实”,所以子类只需继承这些“名”,具体的“实”则由子类来完成。

    这样的Animal类就是抽象类

    下面将上例中的Animal类修改为抽象类,Dog类无需改动:

    public abstract class Animal {
        private String name;
        private int age;
    
        public Animal(String name, int age) {//有参构造器
            this.name = name;
            this.age = age;
        }
    
        public Animal() {//无参构造器
        }
    
        public abstract void say();//抽象方法
    
        
        public String getName() {//被具体实现的方法
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        
    	//getters and setters..
    }
    

    下面是抽象类的特点:

    (一)抽象类被abstract关键字修饰。

    public abstract class Animal {
        //......
    }
    

    (二)类中没有方法体的方法叫抽象方法,也需要使用abstract关键字修饰。

    public abstract void say();//抽象方法
    

    (三)有抽象方法的类一定是抽象类。

    (四)没有抽象方法的类也可以是抽象类。

    (五)抽象类中可以有成员变量、构造器、被具体实现的方法。构造器不能是抽象的。

    (六)抽象类不能被实例化,但是可以声明一个抽象类变量

    Animal animal = new Animal();//报错: 'Animal' is abstract; cannot be instantiated
    Anima animal;//抽象类变量,可行
    

    我们以前遇到的类,比如Dog类,是用来描述对象的,但抽象类的方法没有具体实现,所以它没有足够的信息来描述对象,所以抽象类只能被继承用来描述其子类而不能实例化。

    (七)子类扩展抽象父类有两种选择:

    1. 当子类不是抽象类时,子类必须实现抽象父类的抽象方法:
    public class Dog extends Animal {
    	//属性、构造器、其他方法
        
        //实现抽象父类的抽象方法
        @Override
        public void say() {
            System.out.println("我叫" + super.getName() + ",今年" + super.getAge() + "岁了,家住" + address + ",汪汪汪...");
    	}
        
    }
    
    1. 当子类是抽象类时,子类可以实现抽象父类的抽象方法,也可以选择不实现。
    public abstract class Dog extends Animal {
        //属性、构造器、其他方法
        
        //可以选择不实现父类的抽象方法
    }
    

    2. 接口

    2.1. 什么是接口?

    生活中有一种接口是大家每天都在用的,那就是插座。

    不管是什么样的电器,冰箱、电视、电脑、电风扇,只要买回来,就能插上通电。之所以这么便利,就是因为电器生产商和插座生产商都遵守了一个生产规范:我把插座生产成这个模样,你把插头也生产成这个模样,我不管你电器内部是啥样,你也不用管我的插座内部是啥样,大家把产品生产好,各自卖给客户,合作愉快,一起赚钱。这些产品如果以后坏了,那就再买一个遵守规范的就能继续配套使用了。

    想一想,如果没有这个规范,插座和插头生产的千奇百怪,买回来怎么用?只能把插座和冰箱拆开,用手接220V的电线了。

    换句话说,接口就是生产规范 / 标准,或者双方遵守的协议,只要双方都遵守,那么就能愉快地交流、愉快地合作。

    在Java开发中,一个软件系统肯定不是由一个人完成的,而是由一个团队完成的。那如何避免甲写的类乙不能用,乙写的类丙不能用呢,最后导致甲改了乙的代码,乙改了丙的代码?事先定好规范(接口),大家都遵守接口,只要我知道你的接口,那么我不需要知道你的具体代码,就能调用你的类。

    2.2. 接口的使用

    掌握关于接口几个特点就能愉快地写接口了:

    (一)Java中使用inteface关键字来声明一个接口,接口必须是public的,只有公有了才能让大家遵守:

    public interface Runnable {
    
    }
    

    (二)接口中通常写各种抽象方法,但只声明,不能写方法体。

    public interface Runnable {
        /*public abstract*/ void run();
    }
    

    (三)不必将方法声明为public abstract ,在接口中的所有方法都默认的是public abstract修饰。

    (四)接口中不能有成员变量、静态代码块。

    (五)接口中可以含有常量,常量默认是public static final修饰。

    public interface Runnable {
       /*public static final*/ int i = 1;
        void run();
    }
    

    (六)类通过implements关键字实现接口,必须同时实现接口中的所有方法。

    public class Dog implements Runnable {
        @Override
        public void run() {
            System.out.println("跑得飞快");
        }
        
        //......
    }
    

    (七)如果实现接口的类是抽象类,可以不用实现接口中的方法。

    (八)一个类可以同时继承类和实现接口。

    public class Dog extends Animal implements Runnable {
        //......
    }
    

    (九)一个类可以实现多个接口。

    public class Dog implements Runnable, Flyable {
        //......
    }
    

    (十)接口之间可以继承,并且允许多继承。

    public interface A {
        //......
    }
    
    public interface B extends A, Runnable{
        //......
    }
    

    (十一)接口不能被实例化,但是可以声明一个接口变量。

    Runnable runnable = new Runnable();//'Runnable' is abstract; cannot be instantiated
    Runnable runnable;//接口变量,可行
    

    3. 总结

    抽象类用abstract关键字声明,抽象类中除了可以有抽象方法(用abstract关键字声明)外,还可以有普通类的成员变量、构造器、方法。不能被实例化,但可以声明抽象类的变量。子类继承抽象类要实现父类的抽象方法(如果子类是抽象的,则不用实现)。

    接口用interface关键字声明,接口中只能有抽象方法、常量(忽略修饰符)。不能被实例化,但可以声明接口变量。接口之间可以继承,且允许多继承。类实现接口使用implements关键字,且必须实现接口中的抽象方法(如果类是抽象的,则不用实现)。

    总结了抽象类和接口的特点,发现抽象类好像也能作为“接口”使用。那有了抽象类,为什么还要有接口?

    类只能单继承,如果使用抽象类作为“接口”,这意味着一个类只能遵守一份“接口”,显然不符合实际。而接口则灵活多了。

    4. 关于我

    如有错误,还请指正。

  • 相关阅读:
    查看系统运行时间和系统当前时间
    根据端口查进程信息
    SELinux深入理解
    nginx配置文件
    centos7 nginx配置httpsCenos(6.6/7.1)下从源码安装Python+Django+uwsgi+nginx环境部署(二)
    Linux的加密认证功能以及openssl详解
    linux中shell变量$#,$@,$0,$1,$2的含义解释
    理解GRUB2工作原理及配置选项与方法
    在用busybox制作系统过程中遇到的问题
    内核编译选配(VMware篇)
  • 原文地址:https://www.cnblogs.com/xingrenguanxue/p/13434860.html
Copyright © 2011-2022 走看看