当前位置:

首页 > 编程开发 > Java抽象类和接口如何使用

Java抽象类和接口如何使用

什么是抽象类什么是抽象类呢?抽象类顾名思义就是很抽象,就是当我们没有足够的信息去描述这个类的时候我们就可以先不用描述,这样的类就是抽象类。用代码举个例子:classShape{publicvoiddraw(){System.out.println("我要画图形!!");}}classcircleextendsShape{@Overridepublicvoiddraw(){System.out.println("我要画圆形!!!");}}classrectextendsShape{@Overridepubl

本文目录

    什么是抽象类

    什么是抽象类呢?抽象类顾名思义就是很抽象,就是当我们没有足够的信息去描述这个类的时候我们就可以先不用描述,这样的类就是抽象类。

    用代码举个例子:

    class Shape {
        public void draw() {
            System.out.println("我要画图形!!");
        }
    }
    class circle extends Shape {
        @Override
        public void draw() {
            System.out.println("我要画圆形!!!");
        }
    }
    class rect extends Shape {
        @Override
        public void draw() {
            System.out.println("我要画矩形!!!");
        }
    }
    public class TestDemo3 {
        public static void drawShape(Shape shape) {
            shape.draw();
        }
        public static void main(String[] args) {
            drawShape(new circle());
            drawShape(new rect());
        }
    }

    正好也通过这个小小的案例来复习一下多态,我们创建一个Shape父类,然后又创建两个子类分别为 rect类和circle类 然后利用这两个类使用同一个方法,产生的形态是不一样的这就是多态,但是

    我们可以发现我只用了这两个子类来画图形分别是圆形和矩形,我并没有调用父类来实现这个方法,为啥呢???就是因为这个父类并不能画出图形,那这个父类的draw方法的实现岂不是没有用,我只需要它父类要有这方法,通过子类重写就可以了,不需要父类的方法有具体的实现。

    没错,这就引入到我们今天的主题抽象类

    上面父类的draw方法由于它的具体实现根本没有用处,所以我们就可以不写,这也正符合抽象类的定义,当不能有足够的信息去描述它,那我们就把它叫做抽象类。

    抽象类语法

    上面父类的draw方法没有具体的实现我们就可以把它叫做抽象方法。利用abstract来修饰

    public abstract void draw();

    Java抽象类和接口如何使用

    但是这样为啥会报错呢??

    原因是被abstract修饰的方法叫做抽象方法,如果一个类里面有抽象方法,那这个类一定是抽象类,所以类也要被abstract来修饰。

    Java抽象类和接口如何使用

    但是相反,抽象类里面一定有抽象方法么???

    Java抽象类和接口如何使用

    编译器并不会报错,所以答案是不一定,抽象类里面可以有抽象方法也可以没有抽象方法。

    抽象类能否被实例化呢???

    Java抽象类和接口如何使用

    所以,抽象类是不能实例化的。 

    抽象类能否像普通类一样定义成员变量和方法,构造方法呢???

    Java抽象类和接口如何使用

    抽象类与普通类的区别就是在与抽象类不能实例化,其他定义成员变量,成员方法,构造方法等等都是一样的。

     既然抽象类不能实例化,那他存在的意义是什么呢???

    老铁思考的有道理,其实抽象类最大的意义就是被继承,因为抽象类不能实例化对象,只能依靠子类来重写父类(就是抽象类)的方法来完成业务需求。

    怎么继承抽象类呢???有哪些注意的点呢???

    Java抽象类和接口如何使用

     这样继承为啥会出错呢??原因是在子类继承抽象类的时候,子类需要重写父类的所有方法或者子类需要用abstract修饰(抽象类被抽象类继承或者子类重写抽象类(父类)的所有方法)。

    Java抽象类和接口如何使用

    这样就正确了。

    Java抽象类和接口如何使用

    这里还要注意当我们没有重写父类的方法时而是用abstract来修饰,这时当你在次继承这个子类的时候也就是这个子类变成了父类,下一个子类还是要继续重写这个父类的方法和这个父类的父类的方法。 

    抽象方法能否别static和final修饰呢???

    Java抽象类和接口如何使用

     抽象方法不能被static  和final 修饰,因为子类要重写父类的方法,同时可以省略访问修饰限定符,默认是public。

    总结抽象类:

    • 我们把不能足够描述清楚一个对象的类叫做抽象类。

    • 被abstract修饰的方法称为抽象方法,被abstract修饰的类称为抽象类

    • 抽象方法必须在抽象类里面,也就是说,只要有抽象方法,类名也必须利用abstract来修饰,相反,抽象类里面可以没有抽象方法,也可以有抽象方法。

    • 抽象类不能实例化,除了不能实例化之外其他与普通类一样可以定义成员变量,成员方法,构造方法等。同时构造方法和类方法(被static修饰的方法)不能被abstract来修饰

    • 抽象方法的访问修饰限定符不能是private,如果省略默认是public,同时抽象方法不能被final修饰。

    • 子类继承抽象类的时候,子类必须重写抽象类的所有方法并且要有方法的具体实现,如果重写那子类还是抽象类,必须用abstract来修饰。

    • 抽象类中的方法没有具体实现,要通过子类重写在子类中实现。

    • 一个类只能继承一个抽象类

    接口

    说到接口我们会想到什么呢???我一开始想到的就是充电接口插排等等,比如充电接口,只要符合那个插口的标准,我们都可以使用那个接口来进行充电。这就是把标准进行统一起来,然后大家就可以根据不同的标准来使用不同的接口,比如苹果手机与安卓手机的手机接口就是不一样的,他们就是两种不同的标准,安卓手机用安卓的接口,苹果用苹果的接口这就将标准统一起来。

    而在Java中也是一样的,我们把这个标准或者可以说是一种公共的规范叫做接口,只要符合这一接口的标准我们就可以使用它。

    听着这抽象的概念你可能现在还是不太明白这到底是什么,我在来拿代码举个例子:

    class Animal {
        public String name;
        public int age;
        public void eat() {
            System.out.println(this.name+"吃饭***!");
        }
    }

    这里我定义了一个动物类,这个动物有名字,年龄,还有吃饭的行为。

    class Animal {
        public String name;
        public int age;
        public void eat() {
            System.out.println(this.name+"吃饭***!");
        }
        public void swim() {
            System.out.println("我要游泳~~~~");
        }
    }
    class Fish extends Animal {
    }

    接着我又定义了一个鱼类(子类)继承这个动物类(父类),我想让这个鱼有这个游泳的行为,但是这就会有一个问题,在父类定义了一个swim方法,接着我还要定义很多类,难道所有的类都要有这个游泳的行为么???答案是不可以的。所以我们就不能把这个swim方法定义在父类里面,那我们就可以把这个方法定义在子类(鱼类)里面,这样就符合了,但是如果我还要定义1000个动物都会游泳难道都要在自己类中写这个游泳的方法么??这样做显然是不可行的。那我们该怎么做呢??

    我们可以就提供一个公共的接口,这也是一种标准,只要符合这一标准都可以使用这个接口或者可以说可以实现这一功能。

    看了上面的引例我们应该接口是干什么的了,接下来我们来学一下接口的语法。

    怎么定义接口

    定义接口要使用interface关键字:

    //创建一个接口
    //创建接口要是用interface关键字  接口的命名最好是形容词其他的也可以
    interface IFlying{
        //这就是一个flying接口
    }

    接口中的成员变量:

    Java抽象类和接口如何使用

    这样会报错,接口中的成员变量都是常量,所以必须初始化,接口中的成员变量会被隐式指定为public static final 修饰的。

    interface IFlying{
        //接口中的成员变量默认都是被public static final修饰的常量
        //这里的成员变量不可改变
       public static final int a =10;
    }

    接口中的成员方法:

    Java抽象类和接口如何使用

    interface IFlying{
        //接口中的成员方法都是抽象方法,默认是public abstract 
        //其中public abstract 可以被省略
        //接口中的成员方法不能有具体的实现
        public abstract void eat();
        //一般就写成:
        //void eat();
        default void sleep(){
            //接口中的方法想要具体实现,要加上default修饰
        }
        //接口中可以有静态方法的具体实现
        public static void method() {
            System.out.println("我是静态的方法!!");
        }
    }

    接口能否实例化???

    Java抽象类和接口如何使用

    接口是不能被实例化的。

    怎么使用接口???

    实现的接口如下:

    interface IFlying{
        void eat();
    }
    interface IRunning{
        void run();
    }
    interface ISwimming{
        void swim();
    }
    interface IClimbing{
        void climb();
    }
    //创建一个Ant类 
    //利用implements关键字来实现接口
    //一个类可以实现多个接口,接口之间利用逗号连接
    //实现了接口必须要在接口中重写接口中的方法
    //重写方法快捷键:鼠标移动到implements关键字上然后 alt+enter
    class Ant implements IClimbing,IRunning{
        public String name;
        @Override
        public void run() {
            System.out.println(this.name+"要跑步");
        }
        @Override
        public void climb() {
            System.out.println(this.name+"爬山");
        }
    }
    • 接口的使用时利用implements关键字与类连接,类与接口之间使用implements连接的。

    • 一个类可以实现多个接口,多个接口之间利用逗号连接。

    • class Ant implements IClimbing,IRunning.的意思是类Ant可以实现两个功能,既可以爬又可以跑。

    • 类实现接口时,必须要重写接口中的方法。如果不重写该类还是抽象类,要用abstract来修饰。

    接口能否有静态方法和代码块呢???

    Java抽象类和接口如何使用

    接口中是不能有静态代码块和构造方法的。 

    接口间的继承

    我们这里总结一下类与接口之间的联系

    • 类与类之间是继承关系利用extends来连接   代表子类继承了父类

    • 类与接口之间是利用implements来连接, 代表类能实现某个功能

    • 接口与接口之间也可以进行联系,利用extends 接口A和接口B  interface A enxtends B 代表接口A拓展了接口B的功能。

    这里来讲一下接口与接口之间的继承

    我们利用extends关键字将两个接口连接起来,这样就实现了接口之间的继承。

    例如:

    interface IRunning extends IFlying{
        //类IRunning拓展了IFlying的功能
        void run();
        //接口与接口之间继承后IRunning拓展了IFlying功能
        //有了IRunning的功能的类也要重写IFlying这个方法
    }
    • 类IRunning拓展了IFlying的功能

    • 接口与接口之间继承后IRunning拓展了IFlying功能

    • 有了IRunning的功能的类也要重写IFlying这个方法

    几个重要的接口

    接口comparable

    我们这里举一个例子:

    比如我们要进行给一个学生进行排序,我们之前学过Arrays的sort方法,好我们来尝试一下这个方法对学生进排序。

    class Student{
        public String name;
        public int age;
        public double score;
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
        }
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", score=" + score +
                    '}';
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            Student[] student = new Student[4];
            student[0] = new Student("张三",18,88);
            student[1] = new Student("李四",98,98);
            student[2] = new Student("王二麻子",8,18);
            student[3] = new Student("赵老八",58,38);
            System.out.println(Arrays.toString(student));
            Arrays.sort(student);
            System.out.println(Arrays.toString(student));
        }
    }

    Java抽象类和接口如何使用

    从这里发现Arrays.sort方法是比较具体的数字大小的,而我们这里比较学生这个对象并没有指定,我们究竟依靠什么来比较这个学生对象,目前这个学生有名字,分数,年龄,我们到底依靠什么比较是根据我们的需求来定,那我们要具体的比较学生对象的某一个学生怎么比较呢·???看报错信息也就是这个异常,我们需要提供这个comparable这个接口然后重写这个comparable方法。

    怎样提供接口????

    Java抽象类和接口如何使用

     我们根据类要实现一个接口是利用关键字implements来连接的。

    Java抽象类和接口如何使用

     然后使用comparable这个接口,尖括号里面写上你要排序的类。

    好这样我们就实现了这个接口,当然看前面那个红线就知道会有报错,这也就是当我们实现一个接口我们必须重写这个接口中的方法,然后Alt+enter重写这个接口中的方法。

    Java抽象类和接口如何使用

    好,我们就重写了这样的一个方法,比如我们要比较年龄按照升序排列:

    Java抽象类和接口如何使用 

    然后调用Arrays.sort方法就可以进行排序了。

    Java抽象类和接口如何使用

     同样我们还可以根据名字排序:

    Java抽象类和接口如何使用

    由于,名字是String类型也就是引用类型所以我们要调用compareTo方法来进行比较。

    同样的我们还可以根据分数来排序这个学生对象。

    Java抽象类和接口如何使用

     comparator接口-比较器

    我们刚才使用comparable这个接口会有一个缺陷,就比如当我们已经实现按照年龄排序好了,但是有个人突然把他改成了名字比较,如果是未来做项目开发的时候,那就会给程序猿造成很大的困扰,就怕有一天别人修改了那段代码,所以我们就有了这个comparator这个比较器,我们还是把他封装起来,不用在去学生这个类中去修改。

    对学生年龄排序:

    class AgeComparator implements Comparator{
        @Override
        public int compare(Student o1, Student o2) {
            return o1.age - o2.age;
        }
    }

    对学生分数排序:

    class ScoreComparator implements Comparator{
        @Override
        public int compare(Student o1, Student o2) {
            return (int)(o1.score - o2.score);
        }
    }
    对学生名字排序:
    class NameComparator implements Comparator{
        @Override
        public int compare(Student o1, Student o2) {
            return o1.name.compareTo(o2.name);
        }
    }

    我们还是利用Arrays.sort方法里面再加一个比较器的这个参数就可以比较了。

     public static void main(String[] args) {
            Student[] student = new Student[4];
            student[0] = new Student("张三",18,88);
            student[1] = new Student("李四",98,98);
            student[2] = new Student("王二麻子",8,18);
            student[3] = new Student("赵老八",58,38);
            System.out.println(Arrays.toString(student));
            AgeComparator ageComparator = new AgeComparator();
            Arrays.sort(student,ageComparator);
            System.out.println(Arrays.toString(student));
        }

    这样我们将根据什么排序,实例化对应的对象,通过对象调用重写comparator的方法就可以进行比较,不需要担心类中被修改。

    cloneable接口深入理解深拷贝与浅拷贝

    我们之前学过数组中的克隆方法,就是把一个数组中的内容全部拷贝到另外一个数组中去。

    今天我们学的cloneable接口可以将一个对象的属性拷贝到另外一个对象里面去。

    怎么使用cloneable接口

    我们创建一个人这个类,人这个类中有两个属性,一个是分数,一个是smartphone这个对象(引用类型)。

    class SmartPhone{
        public int money = 9999;
    }
    class Person{
        public int score = 96;
        SmartPhone smartPhone = new SmartPhone();
    }
    public class TestDemo {
        public static void main(String[] args){
            Person person1 = new Person();
        }
    }

    我们现在要将这个person这个类实现cloneable接口,利用implements连接,同样的我们要重写这个cloneable这个接口中的方法。

    Java抽象类和接口如何使用

    这里我们要注意·重写cloneable这个方法,它的返回类型是object,object是Java中所有类的父类。

    好,完成了接口的操作,我们接下来完成克隆的工作,我们怎么使用clone这个方法来进行克隆呢???

    浅拷贝:
    //浅拷贝
    class SmartPhone{
        public int money = 9999;
    }
    class Person implements  Cloneable{
        public int score = 96;
        SmartPhone smartPhone = new SmartPhone();
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
        @Override
        public String toString() {
            return "Person{" +
                    "score=" + score +
                    ", smartPhone=" + smartPhone +
                    '}';
        }
    }
    public class TestDemo {
        public static void main(String[] args) throws CloneNotSupportedException {
            Person person1 = new Person();
            Person person2 = (Person)person1.clone();
            System.out.println(person1.smartPhone.money);
            System.out.println(person2.smartPhone.money);
            person1.smartPhone.money=9;
            System.out.println("===============拷贝之后=================");
            System.out.println(person1.smartPhone.money);
            System.out.println(person2.smartPhone.money);
        }
    }

    Java抽象类和接口如何使用

     原因是什么呢??画一下图理解一下

    Java抽象类和接口如何使用

     这就是浅拷贝,对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此

    为浅拷贝。只要将money值进行修改两个对象的money的值都改变,没有彻底的拷贝。

    深拷贝
    //深拷贝
    class SmartPhone implements  Cloneable{
        public int money = 9999;
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
        @Override
        public String toString() {
            return "SmartPhone{" +
                    "money=" + money +
                    '}';
        }
    }
    class Person implements  Cloneable{
        public int score = 96;
        SmartPhone smartPhone = new SmartPhone();
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Person tmp = (Person)super.clone();
            tmp.smartPhone= (SmartPhone) this.smartPhone.clone();
            return tmp;
        }
        @Override
        public String toString() {
            return "Person{" +
                    "score=" + score +
                    ", smartPhone=" + smartPhone +
                    '}';
        }
    }
    public class TestDemo {
        public static void main(String[] args) throws CloneNotSupportedException {
            Person person1 = new Person();
            Person person2 = (Person)person1.clone();
            System.out.println(person1.smartPhone.money);
            System.out.println(person2.smartPhone.money);
            person1.smartPhone.money=9;
            System.out.println("===============拷贝之后=================");
            System.out.println(person1.smartPhone.money);
            System.out.println(person2.smartPhone.money);
        }
    }

    Java抽象类和接口如何使用

     此时为深拷贝,进行了彻底的拷贝。对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

    Java抽象类和接口如何使用

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Java
    相关文章 更多
    Java单元测试入门:使用JUnit 5编写第一个测试用例
    Java单元测试入门:使用JUnit 5编写第一个测试用例

    面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

    Java变量未初始化报错解析:局部变量与成员变量默认值差异
    Java变量未初始化报错解析:局部变量与成员变量默认值差异

    深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

    详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

    Java中final与static final常量的定义及初始化规则
    Java中final与static final常量的定义及初始化规则

    深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

    Java抽象方法与接口方法的区别及选型建议
    Java抽象方法与接口方法的区别及选型建议

    深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

    Java实现HTTP请求与JSON解析的完整代码示例
    Java实现HTTP请求与JSON解析的完整代码示例

    本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

    Java递归出现StackOverflowError的原因分析及栈空间优化方案
    Java递归出现StackOverflowError的原因分析及栈空间优化方案

    详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

    Java空指针异常(NPE)的代码排查与修复实例
    Java空指针异常(NPE)的代码排查与修复实例

    针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。