• Java的抽象类和接口


    抽象类的实际应用--模板设计(抽象类是约束子类必须有的方法,具体怎么实现的不管)

    接口的实际应用--制定标准

    在开发中,一个类永远不要去继承一个已经实现好的类,要么继承抽象类,要么实现接口,如果两个类同时都

    可以使用的话,优秀使用接口,避免单继承的局限。

    开发原则:
           在类的开发之中,一个类不会去继承一个已经实现好的类,只会继承抽象类或实现接口。
           即:以下的代码是不应该出现在程序之中的:
    class A{}
    class B extends A{}

    1、抽象类实际应用

           抽象类本身是不能直接实例化的。因为其本身包含抽象方法。但是抽象类的时候符合继承关系,只要有继承关系,则肯定会存在对象的向上或向下转型的问题,一但对象发生了向上转型,则肯定一切的方法以被子类覆写过的方法为准。
    abstract class A{
             public abstract void fun() ;
    };
    class B extends A{
             public void fun(){
                       System.out.println("Hello World!!!") ;
             }
    };
    public class Demo32{
             public static void main(String args[]){
                       A a = new B() ;
                       a.fun() ;
             }
    };
           从以上代码之中可以发现,抽象类可以实例化,但是需要通过对象的多态性,通过子类进行实例化操作。
           那么能用抽象类做什么呢?
    违纪卡
    姓名:
    李兴华
    班级:
    小班1006
    时间:
    1990-1-2
    事由:
    上课吃饭
           以后要是取数据的时候,直接从用户填写的地方取,因为上面规定出了是一个模板。
           实际上抽象类就是提供了此种类型的代码结构。
    例如:人分为工人和学生,工人和学生都有说话方法,但是说话的内容要由工人或学生决定,所以此时就可以使用抽象类完成此种功能。
    abstract class Person{
             private String name ;
             private int age ;
             public Person(String name,int age){
                       this.name = name ;
                       this.age = age ;
             }
             public void say(){
                       System.out.println(this.getContent()) ;
             }
             public String getName(){
                       return this.name ;
             }
             public int getAge(){
                       return this.age ;
             }
             public abstract String getContent() ;
    };
    class Worker extends Person{
             private float salary ;
             public Worker(String name,int age,float salary){
                       super(name,age) ;
                       this.salary = salary ;
             }
             public String getContent(){
                       return "工人说 --> 姓名:"+this.getName()+",年龄:"+this.getAge()+",工资:"+this.salary ;
             }
    };
    class Student extends Person{
             private float score ;
             public Student(String name,int age,float score){
                       super(name,age) ;
                       this.score = score ;
             }
             public String getContent(){
                       return "学生说 --> 姓名:"+this.getName()+",年龄:"+this.getAge()+",成绩:"+this.score ;
             }
    };
    public class Demo33{
             public static void main(String args[]){
                       Person p = null ;
                       // p = new Student("张三",20,99.0f) ;
                       p = new Worker("张三",20,999.0f) ;
                       p.say() ;
             }
    };
    练习:
           只要是学生进入到班级里来,就一定要喊报告。
           只要是老师进入到班级里来,也一定要说同学们好。
    abstract class Person{
             private String name ;
             public Person(String name){
                       this.name = name ;
             }
             public void call(){
                       System.out.println(this.getContent()) ;
             }
             public String getName(){
                       return this.name ;
             }
             public abstract String getContent() ;
    };
    class Teacher extends Person{
             public Teacher(String name){
                       super(name) ;
             }
             public String getContent(){
                       return "同学们好,姓名:"+this.getName() ;
             }
    };
    class Student extends Person{
             public Student(String name){
                       super(name) ;
             }
             public String getContent(){
                       return "报告,姓名:"+this.getName() ;
             }
    };
    class Class_{
             public void comeIn(Person p){
                       p.call() ;
             }
    };
    public class Demo34{
             public static void main(String args[]){
                       Class_ c = new Class_() ;
                       c.comeIn(new Student("张三")) ;
                       c.comeIn(new Teacher("李四")) ;
             }
    };
           以上的设计模式就称为模板设计。

    2、接口的实际应用

    总的使用原则:接口的作用就是将方法名称暴露给远程用户。接口在实际应用中更多的作用是来指定标准的。比如说”U盘和打印机都可以插在电脑上使用,因为他们都实现了USB标准的接口,对于电脑来说,只要符合USB接口标准的设备都可以插进来。“
           接口与抽象类一样,如果要想正确的时候也需要通过子类进行实例化操作。
    如下代码所示:
    interface A{
             public void print() ;
    }
    class B implements A{
             public void print(){
                       System.out.println("Hello World!!!") ;
             }
    };
    public class Demo35{
             public static void main(String args[]){
                       A a = new B() ;
                       a.print() ;
             }
    };
    接口的实际应用:定义标准。
           以USB接口为例:
    interface USB{
             public void start() ;
             public void stop() ;
    }
    class MainBoard{
             public void plugIn(USB usb){
                       usb.start() ;
                       usb.stop() ;
             }
    };
    class Mp3 implements USB{
             public void start(){
                       System.out.println("MP3开始工作。") ;
             }
             public void stop(){
                       System.out.println("MP3停止工作。") ;
             }
    };
    class Print implements USB{
             public void start(){
                       System.out.println("打印机开始工作。") ;
             }
             public void stop(){
                       System.out.println("打印机停止工作。") ;
             }
    };
    public class Demo36{
             public static void main(String args[]){
                       MainBoard mb = new MainBoard() ;
                       mb.plugIn(new Mp3()) ;
                       mb.plugIn(new Print()) ;
             }
    };

    2.1 工厂设计模式(接口应用)

    工厂设计模式是在java开发中最常使用的一种设计模式。

    复制代码
    interface Fruit{    // 定义一个水果接口
        public void eat() ;    // 吃水果
    }
    class Apple implements Fruit{
        public void eat(){
            System.out.println("** 吃苹果。") ;
        }
    };
    class Orange implements Fruit{
        public void eat(){
            System.out.println("** 吃橘子。") ;
        }
    };
    public class InterfaceCaseDemo03{
        public static void main(String args[]){
            Fruit f = new Apple() ;    // 实例化接口
            f.eat() ;
        }
    };
    运行结果:
    吃苹果。
    复制代码

      这样的代码可以使吗?有问题吗?

    分析:

      主方法:就应该表示客户端。主方法的代码越少越好。此时,直接在主方法中指定了要操作的子类,如果要更换子类,肯定要修改客户端。

    就表示跟特定的子类耦合在一起了。

      JVM工作原理:程序-》JVM(相当于客户端)-》操作系统。

    问题的解决:客户端通过过渡端,得到特定子类的接口实例,返回接口实例给客户端,接口实例调用接口中的方法。

      此过渡端,在程序中就称为工厂设计(工厂类)

    复制代码
    interface Fruit{    // 定义一个水果接口
        public void eat() ;    // 吃水果
    }
    class Apple implements Fruit{
        public void eat(){
            System.out.println("** 吃苹果。") ;
        }
    };
    class Orange implements Fruit{
        public void eat(){
            System.out.println("** 吃橘子。") ;
        }
    };
    class Factory{    // 定义工厂类
        public static Fruit getInstance(String className){ //注意这里的方法是static修饰的,因为在主方法中是Factory调用
            Fruit f = null ;
            if("apple".equals(className)){    // 判断是否要的是苹果的子类
                f = new Apple() ;
            }
            if("orange".equals(className)){    // 判断是否要的是橘子的子类
                f = new Orange() ;
            }
            return f ;
        }
    };
    public class InterfaceCaseDemo04{
        public static void main(String args[]){
            Fruit f = Factory.getInstance(”apple“) ;    // 实例化接口
            f.eat() ;
        }
    };
    运行结果:
    **吃苹果
    复制代码

      流程是:客户端(主方法)通过工厂类的getInstance()方法,通过传入的参数判断,该获取实例化哪个子类的实例,

    然后把获取的实例通过getInstance()方法返回(return)该实例给主方法中的接口实例,最后通过接口实例调用所需方法。

    2.2、 代理设计模式(接口应用)

      假设现在有以下这种情况:

    1)张三借了李四500块钱。

    2)李四不换,张三生气。

    3)张三找到王五,王五是讨债公司。

    4)王五准备了刀枪

    5)把李四欠的钱还回来了。

    也就是张三找李四借钱,王五代理了张三。

      代理设计:也是在java中应用较多的设计模式,所谓的代理设计就是指一个代理主题来操作真实主题,真实主题执行具体的业务操作,

    而代理主题负责其他相关业务。就好比生活中经常用到的代理上网那样,客户通过网络代理连接网络,由代理服务器完成用户权限,上网操作相关操作。

      分析结果:不管代理操作也好,真实操作也好,其共同目的就是上网,所以用户关心的是如何上网,至于如何操作的,用户不关心。

    interface Network{  
        public void browse() ;    // 浏览
    }
    class Real implements Network{  //真实上网操作类
        public void browse(){
            System.out.println("上网浏览信息") ;
        }
    };
    class Proxy implements Network{  //代理类。
       private Network network ;    // 代理对象
        public Proxy(Network network){  //初始化,把真实对象传给代理对象,向上转型操作。
            this.network = network ;
        }
        public void check(){
            System.out.println("检查用户是否合法。") ;
        }
        public void browse(){
            this.check() ;
            this.network.browse() ;    // 调用真实的主题操作,这里调用的是真实类里的对象。
        }
    };
    public class ProxyDemo{
        public static void main(String args[]){
            Network net = null ;
            net  = new Proxy(new Real()) ;//  指定代理操作,这里两次向上转型操作,第一次向上是实例化代理类,
    第二次向上转型是括号中,把真实类对象传入,以便在代理类中调用真实类中的方法。
    net.browse() ; // 客户只关心上网浏览一个操作 } };
    运行结果:
    检查用户是否合法
    上网浏览信息

    2.3、适配器模式

      此设计,在日后学习java 的图形界面用的非常多。

      在Java中,一个子类实现了一个接口,则肯定在子类中覆写此接口中全部抽象方法。那么这样一来,

    如果一个接口中提供的抽象方法过多,而且没有必要全部实现的话,肯定很浪费,此时就需要一个中间过渡,

    但是此过渡又不希望被直接调用,所以将此过渡器定义成抽象类更合适,即:一个接口首先被一个抽象类(此抽象类成为适配器类)继承,

    并在此抽象类中实现若干方法(方法体为空),则以后的子类直接继承此抽象类(适配器),就可以有选择的覆写所需方法了。

    一个抽象类可以实现一个接口,那么对于抽象类的子类,则必须覆写抽象类和接口的全部(未实现的)抽象方法。(即如果接口中的某类方法在抽象类中实现了(覆写了),那么在抽象类的子类中就可以不用覆写了。因为继承,在父类中的方法,被子类继承了,相当于子类覆写了,子类照样可以调用,覆写该方法。

    interface Window{        // 定义Window接口,表示窗口操作
        public void open() ;    // 打开
        public void close() ;    // 关闭
        public void activated() ;    // 窗口活动
        public void iconified() ;    // 窗口最小化
        public void deiconified();// 窗口恢复大小
    }
    abstract class WindowAdapter implements Window{
        public void open(){} ;    // 打开
        public void close(){} ;    // 关闭
        public void activated(){} ;    // 窗口活动
        public void iconified(){} ;    // 窗口最小化
        public void deiconified(){};// 窗口恢复大小
    };
    class WindowImpl extends WindowAdapter{
        public void open(){
            System.out.println("窗口打开。") ;
        }
        public void close(){
            System.out.println("窗口关闭。") ;
        }
    };
    public class AdapterDemo{
        public static void main(String args[]){
            Window win = new WindowImpl() ;
            win.open() ;
            win.close() ;
        }
    };
    运行结果:

    窗口打开。
    窗口关闭。

    此种设计思路,在java的图形界面编程上使用的非常多。但是在javaee上使用不常见。

    2.4、 内部类的扩展

    之前已经讲过内部类的概念,实际上在一个抽象类中也可以包含一个接口。

    abstract class A{    // 定义抽象类
        public abstract void printA() ;    // 抽象方法
        interface B{    // 定义内部接口
            public void printB() ;    // 定义抽象方法
        }
    };
    class X extends A{    // 继承抽象类
        public void printA(){
            System.out.println("HELLO --> A") ;
        }
        class Y implements B{    // 定义内部类实现内部接口
            public void printB(){
                System.out.println("HELLO --> B") ;
            }
        };
    };
    public class InnerExtDemo01{
        public static void main(String args[]){
            A.B b = new X().new Y() ;  //参考前面内部类的知识,实现内部类实例的方法:外部类.内部类  内部类对象= 外部类对象.new  内部类
            b.printB() ;
        }
    };
    运行结果:
    HELLO-->B

      含有内部接口的抽象类的子类,必须也要定义内部类继承该接口

    反之,在一个接口中也可以定义一个抽象类。

    interface A{    // 定义接口
        public void printA() ;    // 抽象方法
        abstract class B{    // 定义内部抽象类
            public abstract void printB() ;    // 定义抽象方法
        }
    };
    class X implements A{    // 实现接口
        public void printA(){
            System.out.println("HELLO --> A") ;
        }
        class Y extends B{    // 继承抽象类
            public void printB(){
                System.out.println("HELLO --> B") ;
            }
        };
    };
    public class InnerExtDemo02{
        public static void main(String args[]){
            A.B b = new X().new Y() ;
            b.printB() ;
        }
    };

      含有内部抽象类的接口,对于继承他的子类来说,除了要覆写接口中的抽象方法,还要专门定义一个内部类继承抽象类,并覆写全部抽象类

    但是,从个人开发来说,此种设计不太常见,因为代码有些混乱。

      其实,无论对于接口,或者是抽象类,都是要求子类将本类中定义的方法实现,区别也仅仅是接口要求全部实现,抽象类中的非抽象方法不一定要重写。对于接口使用远超过抽象类的问题,网上有很多的解释。为了代码的重用?我觉得不是重点。为了更好的扩展性,太抽象了,和抽象类的定义一样让人无从捉摸。因为接口可以多重继承,这个解释太通俗了,很多刚入门的开发者,不能完全明白。

      我的看法,接口使用多于抽象类,确实是因为接口可以多重继承,便于项目后期的维护和扩展。

      贴一段网上搜的很好的知识点:

    抽象类和接口之间的区别:
    共性:它们都是不断抽取出来的抽象非概念
    区别:
    1、抽象类只能被单继承、接口可以被多实现,避免了单继承的局限性。
    2、抽象类中可以定义抽象方法,和非抽象方法,它可以用于定义体系的基本共性的内容。接口中只能定义抽象方法,它主要用于对象的功能的扩展。
    3、抽象类是继承关系,是is a关系,接口是实现关系是like a关系。
    4、抽象类中的成员修饰符都是自定义的,接口中的修饰符都是固定的。
    记住:不要把接口狭义的理解为interface,应该理解广义些,就是对外提供的规则,凡是对外暴露的都可以是接口。

    下面比较一下两者的具体语法区别:
    1.抽象类可以有构造方法,接口中不能有构造方法。
    2.抽象类中可以有普通成员变量,接口中没有普通成员变量
    3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
    4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
    5. 抽象类中可以包含静态方法,接口中不能包含静态方法
    6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
    7. 一个类可以实现多个接口,但只能继承一个抽象类。

    下面接着再说说两者在应用上的区别:
      接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用,例如,模板方法设计模式是抽象类的一个典型应用,假设某个项目的所有Servlet类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的Servlet都继承这个抽象基类,在抽象基类的service方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码
    (这应该是最全最细的答案了)

     

    比如公司系统中,有系统管理员,普通员工,主管,组长,经理等

    这些角色都有登陆,修改密码这些共同的行为,这些行为规范可以被统一的命名,如Login和UpdatePassword,

      那么,我们可以将这些方法抽到抽象类中,作为抽象方法,等待子类具体实现。当然各角色的身份不一样,所以实际上执行这些行为的步骤可能不一样,例如数据表的不同,则需要不同的子类去实现不同的代码,这就是抽象类比普通类作为基类被继承的其中一个优点。而实际上login操作,对于各类角色是一样的,那么,可以作为非抽象方法被继承,这就体现了抽象类在代码实现方面的优点,可以实现代码的重用。

      实际上你也可以不用抽象类,直接在每个子类定义相应的行为也可以,如MemberLogin、AdminLogin等,但如果是在中大型软件里,基于命名规范化,后续扩展和维护考虑,可能用抽象类好些,统一用Login,这样后面的人看到这个就基本了解情况了,对吧。

      再来说接口,打比方说,现在有A查看公司人员,B查看公司销售数据,C查看员工工资,这些行为对于普通员工,组长,经理,主管来说,并不是共有的行为。那么,抽象类就难以实现各个角色的不同行为,因为类的单继承性。而如果,A、B、C、分别是三个接口的话,那么经理继承ABC,员工继承A,主管继承AC,这样子处理的话,不仅易于理解,而且架构更加清晰,是吧。

      更重要的一点是,假如公司的组织进行了调整,增加了一个职位。对于接口,只要这个角色去继承相应的接口即可,不会对于之前已经良好运行的代码做任何修改。事实上,对于已有代码的修改,是越少越好,因为代价很大,属于重复劳动,也可能出现意想不到的错误。这就是所谓的良好的扩展性,和可维护性

  • 相关阅读:
    c#接口和抽象类的区别(转)
    Dephi阿拉伯数字转换成英文和中文大写
    Code Rush Express Template 制作
    SQL中对学习成绩自动排名次
    Resharper上手指南
    如何實現域控制中部分用戶可以寫Programme files目錄的權限?
    阿拉伯数字转换英文数字表示算法解析及其实现
    BOM 算法
    OGRE1.7.1.1vs2008安装
    近一个月工作小总结
  • 原文地址:https://www.cnblogs.com/lsohvaen001/p/7795583.html
Copyright © 2020-2023  润新知