zoukankan      html  css  js  c++  java
  • Java笔记:抽象类、接口

    这篇笔记主要是抽象类和接口,还有简单介绍下三种设计模式:模板模式、工厂模式、命令模式

    1.抽象方法和抽象类
    (1)抽象方法和抽象类都必须使用abstract修饰符来定义,包含抽象方法的类只能被定义成抽象类,抽象类里面可以没有抽象方法。抽象方法里面没有方法体。
    (2)抽象类不能被实例化,不能使用new关键字来调用抽象类的构造器来创建抽象类的实例,只能被当成父类来继承。

    package cn.lsl;
    
    public abstract class Shape {
        {
            System.out.println("抽象类的中的普通初始化块");
        }
        private String color;
        public abstract double calPerimeter();
        public abstract String getType();
        public Shape(){}
        public Shape(String color){
            System.out.println("Shape的构造器");
            this.color = color;
        }
        public String getColor() {
            return color;
        }
        public void setColor(String color) {
            this.color = color;
        }
    }
    package cn.lsl;
    
    public class Triangle extends Shape {
        private double a;
        private double b;
        private double c;
        
        public Triangle(String color, double a, double b, double c) {
            super(color);
            this.setSides(a, b, c);
        }
        
        public void setSides(double a, double b, double c){
            if(a>=b+c || b>=a+c || c>=a+b){
                System.out.println("三角形两边之和必须大于第三边");
                return;
            }
            this.a = a;
            this.b = b;
            this.c = c;
        }
    
        @Override
        public double calPerimeter() {
            return a + b + c;
        }
    
        @Override
        public String getType() {
            // TODO Auto-generated method stub
            return "三角形";
        }
    }
    package cn.lsl;
    
    public class Circle extends Shape{
        private double radius;
        public Circle(String color, double radius){
            super(color);
            this.radius = radius;
        }    
        public void setRadius(double radius) {
            this.radius = radius;
        }
    
        @Override
        public double calPerimeter() {
            return 2 * Math.PI * radius;
        }
    
        @Override
        public String getType() {
            return getColor() + "圆形";
        }
        
        public static void main(String[] args) {
            Shape s1 = new Triangle("黑色",3,4,5);
            Shape s2 = new Circle("黄色",3);
            System.out.println(s1.getType());
            System.out.println(s1.calPerimeter());
            System.out.println(s2.getType());
            System.out.println(s2.calPerimeter());
        }
    }

    2.接口
    (1)接口是一种特殊的“抽象类”,接口里面不能包含普通方法,接口里面的方法都是抽象方法。
    (2)接口定义的是一种规范,实现这个接口的类所要遵守的规范,接口不关心这些类内部的状态数据,也不关心这些类里方法的实现细节,它只是规定这批类里面必须提供哪些方法。
    (3)定义接口使用的是interface关键字,接口可以有多个直接父接口,但接口只能继继承接口,不能继承类。

    [修饰符] interface 接口名 extends 父接口1, 父接口2{
    
    }

    (4)接口里面不能包含构造器和初始化块,接口里面可以包含Field(只能是常量)、方法(只能是抽象方法)、内部类、枚举类
    因为接口里面只能定义常量Field,所以以下两行代码的结果是一样的

    int a = 15;
    public static final int a = 15;

    (5)因为接口里面定义的全部是抽象方法,所以接口里面不允许定义静态方法(不能使用static修饰接口里面定义的方法)
    (6)当接口扩展父接口的时候,将会获得接口里面定义的所有抽象方法、常量、内部类、枚举类。

    package cn.lsl;
    interface interfaceA{
        int a = 1;
        void testA();
    }
    
    interface interfaceB{
        int b = 2;
        void testB();
    }
    
    interface interfaceC extends interfaceA, interfaceB{
        int c = 3;
        void testC();
    }
    
    public class InterfaceTest {
        public static void main(String[] args){
            System.out.println(interfaceC.a);
            System.out.println(interfaceC.b);
            System.out.println(interfaceC.c);
        }
    }

    因为interfaceC接口继承了interfaceA和interfaceB,所以interfaceC接口中获得他们的Field常量
    可以通过接口名来访问常量Field
    (7)一个类可以继承一个父类,并同时实现多个接口,实现接口用implements关键字。
    一个类实现了一个或多个接口后,这个类必须完全实现这些接口里所定义的全部抽象方法,若这个类保留了从父类中继承到的抽象方法,那么这个类也必须定义成抽象类。
    格式:

    [修饰符] class 类名 extends 父类 implements 接口1, 接口2{
        
    }

    eg:一个模拟打印机的例子

    package cn.lsl;
    
    public interface Output {
        int MAX_CACHE_LINE = 50;
        void out();
        void getData(String msg);
    }
    package cn.lsl;
    
    public interface Product {
        int getProduceTime();
    }
    package cn.lsl;
    
    public class Printer implements Output, Product {
        private String[] printData = new String[MAX_CACHE_LINE];
        private int dataNum = 0;
        @Override
        public int getProduceTime() {
            // TODO Auto-generated method stub
            return 45;
        }
    
        @Override
        public void out() {
            // TODO Auto-generated method stub
            while(dataNum > 0){
                System.out.println("打印机打印:" + printData[0]);
                //把作业队列整体前移一位,并将剩下的作业数减1
                System.arraycopy(printData, 1, printData, 0, --dataNum);
            }
        }
    
        @Override
        public void getData(String msg) {
            // TODO Auto-generated method stub
            if(dataNum >= MAX_CACHE_LINE){
                System.out.println("输出队列已满,添加失败");
            }else{
                printData[dataNum++] = msg;
            }
        }
        
        public static void main(String[] args) {
            Output o = new Printer();
            o.getData("Java");
            o.getData("JavaSe");
            o.out();
            o.getData("Android");
            o.getData("JavaEE");
            o.out();
            Product p = new Printer();
            System.out.println(p.getProduceTime());
            
            Object obj = p;
        }
    }

    3.模板模式
    如果一个抽象父类中提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,这就是一种模板设计模式。
    例如以下一个例子,父类中的普通方法依赖于一个抽象方法,抽象方法则推迟到子类中提供实现。

    package cn.lsl;
    
    public abstract class SpeedMeter {
        private double turnRate;
        public SpeedMeter(){}
        public abstract double getRadius();
        public void setTurnRate(double turnRate){
            this.turnRate = turnRate;
        }
        public double getSpeed(){
            return Math.PI * 2 * getRadius() * turnRate;
        }
    }
    package cn.lsl;
    
    public class CarSpeedMeter extends SpeedMeter{
        @Override
        public double getRadius() {
            return 0.28;
        }
        public static void main(String[] args) {
            CarSpeedMeter csm = new CarSpeedMeter();
            csm.setTurnRate(15);
            System.out.println(csm.getSpeed());
        }
    }

    4.工厂模式
    接口体现的是一种规范和实现分离的思想,利用接口可以极好地降低程序各模块之间的偶尔,从而提高系统的可扩展性和可维护性。
    一个情景案例:
    假设有一个Conputer类要组合一个输出设备,有两种选择,一是让Computer类组合一个Printer,二是让Computer类组合一个Output。
    假设让Computer类组合一个Printer对象,如果有一天要使用BetterPrinter来代替Printer,于是就要对Computer类源代码进行修改。如果系统中多个类组合了Printer,那么修改的工作量将非常大。
    而如果采用Computer类组合一个Output类型的对象,将Computer类与Printer类完全分离。那么当Printer对象切换到BetterPrinter对象时,系统完全不受影响。

    package cn.lsl;
    
    public class Computer {
        private Output out;
        public Computer(Output out){
            this.out = out;
        }
        public void keyIn(String msg){
            out.getData(msg);
        }
        public void print(){
            out.out();
        }
    }

    上面的程序Computer不再负责创建Output对象,而是提供一个Output工厂来负责生成Output对象。

    package cn.lsl;
    
    public class OutputFactory {
        public Output getOutput(){
            //return new Printer();
            return new BetterPrinter();
        }
        public static void main(String[] args) {
            OutputFactory of = new OutputFactory();
            Computer c = new Computer(of.getOutput());
            c.keyIn("Java");
            c.keyIn("JavaSe");
            c.print();
        }
    }

    上面程序由getOutput()这个方法返回一个Output实现类的实例,具体要创建哪个对象由该方法决定。如果要将Printer改成BetterPrinter实现类,只要让BetterPrinter实现Output接口,
    并改变OutputFactory类中的getOutput方法即可。

    package cn.lsl;
    
    public class BetterPrinter implements Output, Product {
        private String[] printData = new String[MAX_CACHE_LINE];
        private int dataNum = 0;
        @Override
        public int getProduceTime() {
            // TODO Auto-generated method stub
            return 45;
        }
    
        @Override
        public void out() {
            // TODO Auto-generated method stub
            while(dataNum > 0){
                System.out.println("高速打印机打印:" + printData[0]);
                //把作业队列整体前移一位,并将剩下的作业数减1
                System.arraycopy(printData, 1, printData, 0, --dataNum);
            }
        }
    
        @Override
        public void getData(String msg) {
            // TODO Auto-generated method stub
            if(dataNum >= MAX_CACHE_LINE){
                System.out.println("输出队列已满,添加失败");
            }else{
                printData[dataNum++] = msg;
            }
        }
        
        public static void main(String[] args) {
            Output o = new BetterPrinter();
            o.getData("Java");
            o.getData("JavaSe");
            o.out();
            o.getData("Android");
            o.getData("JavaEE");
            o.out();
            Product p = new BetterPrinter();
            System.out.println(p.getProduceTime());
            
            Object obj = p;
        }
    }

    并改变

    public Output getOutput(){
            return new BetterPrinter();
    }

    5.命令模式
    有这样一个场景:某个方法要完成某一个行为的时候,但这个行为的具体实现无法确定,必须等到执行该方法时候才可以确定。这个方法不仅需要普通数据可以变化,甚至还有方法执行体也需要变化。
    我们可以用命令模式来完成这个场景。

    package cn.lsl;
    
    public interface Command {
        void process(int[] target);
    }

    Command接口里面定义了一个process方法,这个方法没有方法体,因为无法确定这个处理行为

    package cn.lsl;
    
    public class ProcessArray {
        public void process(int[] target, Command cmd){
            cmd.process(target);
        }
    }

    ProcessArray类里面包含一个process方法,这个方法无法确定处理数组的行为,所以传入一个参数,这个Command参数负责对数组的处理行为。
    通过Command接口,实现ProcessArray类与具体“处理行为”分离。

    package cn.lsl;
    
    public class PrintCommand implements Command{
    
        @Override
        public void process(int[] target) {
            // TODO Auto-generated method stub
            for(int tmp : target){
                System.out.println("迭代数组:" + tmp);
            }
        }
    }
    package cn.lsl;
    
    public class AddCommand implements Command{
    
        @Override
        public void process(int[] target) {
            // TODO Auto-generated method stub
            int sum = 0;
            for(int tmp : target){
                sum += tmp;
            }
            System.out.println("数组总和:" + sum);
        }
    }
    package cn.lsl;
    
    public class CommandTest {
        public static void main(String[] args) {
            ProcessArray pa = new ProcessArray();
            int[] target = {1,2,5,3};
            pa.process(target, new PrintCommand());
            System.out.println("---------------");
            pa.process(target, new AddCommand());
        }
    }
  • 相关阅读:
    LeetCode278. 第一个错误的版本
    LeetCode275. H 指数 II
    LeetCode274. H 指数
    LeetCode273. 整数转换英文表示
    LeetCode268. 缺失数字
    LeetCode264. 丑数 II
    LeetCode263. 丑数
    关于解决Chrome新版本中cookie跨域携带和samesite的问题处理
    java将list转为树形结构的方法
    Python pycharm selenium hyrobot 学习中遇到的问题汇总2
  • 原文地址:https://www.cnblogs.com/EvanLiu/p/3139144.html
Copyright © 2011-2022 走看看