网站建设功能宁德市自然资源局
在Java面向对象编程中,抽象类和接口是两个基础且强大的概念。它们为代码设计提供了结构化和多态性的基础,但各自有不同的特点和使用场景。本文将详细介绍这两个概念的特征,并对它们进行比较分析。
抽象类的特征
抽象类是一种特殊的类,它不能被直接实例化,需要通过子类继承并实现其抽象方法才能使用。
1. 基本特征
使用abstract关键字声明
可以包含抽象方法(没有方法体的方法)
可以拥有普通方法、属性和构造方法
子类通过extends关键字继承
2. 重要特性
抽象类不能直接实例化对象
抽象方法不能是private的(因为需要被子类重写)
抽象方法不能被final和static修饰
如果类中有抽象方法,则该类必须声明为抽象类
子类必须实现父类中的所有抽象方法,否则子类也必须是抽象类
抽象类的注意的地方:
1.抽象类和抽象方法都使用abstract进行修饰
2.抽象类不能进行实例化,但是普通类是可以的!
3.抽象类不一定包含抽象方法,但是包含抽象方法的一定是抽象类。
4.抽象类当中 可以定义成员变量和成员方法!
5.当一个普通类 继承我们的抽象类了,此时在普通类当中一定要重写抽象类中的抽象方法!!!
6.抽象类存在的最大意义就是 为了被继承
7.当一个抽象类B继承了抽象类A,此时抽象类B不需要重写抽象类A的抽象方法,但是当一个普通类C,继承了抽象类B,此时就需要重写所有没有被重写的抽象方法(包含A,B抽象类的)
8.一定满足重写的要求
9.final关键字 不可能同时作用在一个方法或者类上(final和abstract天生冲突,因为abstract还需要重写,final不可以变动了)!
10.抽象类当中可以存在构造方法,在子类实例化的时候,会帮助父亲的成员进行初始化!
代码案例:
如果使用普通的继承画图像类
package demo1;class Shape{public void draw(){}
}class Cycle extends Shape{@Overridepublic void draw() {System.out.println("⚪");}
}class Rect extends Shape{@Overridepublic void draw() {System.out.println("矩形");}
}
public class Test {public static void drawMap(Shape shape){shape.draw();}public static void main(String[] args) {drawMap(new Cycle());drawMap(new Rect());}
}
如果使用抽象类继承,代码
package demo2;/*** Created with IntelliJ IDEA.* Description:* User: laichangyang* Date: 2025-04-02* Time: 14:55*///抽象类
abstract class Shape{public abstract void draw();
}class Cyele extends Shape{@Overridepublic void draw() {System.out.println("⚪");}
}class Rect extends Shape{@Overridepublic void draw() {System.out.println("矩形");}
}
public class Test {public static void drawMap(Shape shape){shape.draw();}public static void main(String[] args) {drawMap(new Cyele());drawMap(new Rect());}}
好像和不使用抽象类实现的效果,为什么还要脱裤子放屁呢???
抽象类可以帮助我们检查是否重写了抽象方法,没有重写,就会报错,这个是它的重要作用。
还有就是,抽象类不可以实例化,但是如果不使用抽象类,父类(基类)就可以实例化了。
接口的特征
接口是一种完全抽象的类型,它只定义行为规范,不提供实现。
1. 基本特征
使用interface关键字声明
包含抽象方法(Java 8之前)
方法默认是public abstract
字段默认是public static final
类通过implements关键字实现接口
2. 重要特性
接口不能直接实例化
接口中的方法都是抽象的(Java 8前)
接口没有构造方法
接口不能包含普通变量,只能有常量
一个类可以实现多个接口
接口之间可以多继承(使用extends关键字)
接口:其实就是对一个标准的规范!!!可以算抽象类的进一步抽象
接口注意的地方:
1.接口是使用interface来进行定义的
2.接口当中 不能有实现的方法,但是有2种类型的方法需要注意:
2.1静态方法可以有具体的实现
2.2这个方法被default关键字修饰,也是可以的【1.8开始引入这个特征】
3.接口当中的方法默认是public abstract 修饰的
4.接口当中的成员变量默认是:public static final 修饰的
5.接口也不能通过关键字new 来进行实例化
6.类和接口之间 使用关键字implements来进行关联
7.当一个类实现一个接口后,这个类必须重写这个接口当中的抽象方法
8.当接口存在default方法,可以选择重写,也可以不重写,具体看业务需求
9.不管是接口还是抽象类,他们仍然是可以发生向上转型的
10.子类实现接口的方法的时候,这个方法一定是public修饰的!!!
11.接口当中不能有构造方法和代码块
12.一个类 不能实现接口当中的方法 这个类,可以定义为抽象类
一个类可以实现多个接口,可以解决多继承的问题!!!
如果继承和接口同时存在,先继承类,然后实现接口
如果使用接口实现前面的画图
package demo3;interface Ishpe{
// int age=1;//默认是 public static finalvoid draw();//默认是public abstract// default public void draw2(){
// System.out.println("1234");
// }
//
// public static void draw3(){
// System.out.println("1234");
// }}class Cycle implements Ishpe{@Overridepublic void draw() {System.out.println("⚪");}// @Override
// public void draw2() {
// System.out.println("实现接口,default修饰方法的重写");
// }
}class Rect implements Ishpe{@Overridepublic void draw() {System.out.println("矩形");}
}public class Test {public static void drawMap(Ishpe shape){shape.draw();}public static void main(String[] args) {Ishpe ishpe= new Cycle();Ishpe ishpe1=new Rect();drawMap(ishpe);drawMap(ishpe1);drawMap(new Cycle());drawMap(new Rect());}
}
接口案例Computer类
先设计接口USB,包含打开和关闭驱动,两个抽象方法
package demo4;public interface USB {void openDevice();void closeDevice();
}
然后设计电脑的组件,键盘和鼠标
package demo4;public class KeyBoard implements USB{@Overridepublic void openDevice() {System.out.println("打开键盘服务!");}@Overridepublic void closeDevice() {System.out.println("关闭键盘服务!");}//键盘特有的方法public void input(){System.out.println("键盘输入文字");}
}
package demo4;public class Mouse implements USB{@Overridepublic void openDevice() {System.out.println("打开鼠标服务!");}@Overridepublic void closeDevice() {System.out.println("关闭鼠标服务!");}//鼠标特有的方法public void click(){System.out.println("点击鼠标!");}
}
鼠标和键盘类,使用USB接口,需要重写接口的抽象方法,打开和关闭驱动,不同类又有自己特有的方法
接下来设计Computer类
package demo4;public class Computer {public void open(){System.out.println("打开电脑!");}public void close(){System.out.println("关闭电脑!");}public void useDevice(USB usb){usb.openDevice();if(usb instanceof KeyBoard){KeyBoard keyBoard=(KeyBoard)usb;//向下转型keyBoard.input();}else if(usb instanceof Mouse){Mouse mouse=(Mouse)usb;mouse.click();}usb.closeDevice();}public static void main(String[] args) {Computer computer=new Computer();computer.useDevice(new KeyBoard());System.out.println("_______________");computer.useDevice(new Mouse());}}
使用Computer的使用,先new一个,先打开电脑,然后使用驱动,最后关闭电脑
代码的运行结果
接口和继承一起使用的案例:
package demo5;/*** Created with IntelliJ IDEA.* Description:* User: laichangyang* Date: 2025-04-02* Time: 18:54*/interface IFlying{void fly();
}interface ISwimming{void swim();
}interface IRunning{void run();
}abstract class Animal{private String name;private int age;public Animal(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}public abstract void eat();
}class Dog extends Animal implements IRunning,ISwimming{public Dog(String name, int age) {super(name, age);}@Overridepublic void swim() {System.out.println(this.getName()+"正在狗刨!");}@Overridepublic void run() {System.out.println(this.getName()+"正在跑!");}@Overridepublic void eat() {System.out.println(this.getName()+"正在吃狗粮!");}
}class Bird extends Animal implements IFlying{public Bird(String name, int age) {super(name, age);}@Overridepublic void fly() {System.out.println(this.getName()+"正在飞");}@Overridepublic void eat() {System.out.println(this.getName()+"正在吃鸟粮");}
}class Robot implements IRunning{@Overridepublic void run() {System.out.println("机器人在跑");}
}public class Test {public static void testFly(IFlying iFlying){iFlying.fly();}public static void testRun(IRunning iRunning){iRunning.run();}public static void testSwim(ISwimming iSwimming){iSwimming.swim();}public static void main(String[] args) {testFly(new Bird("小鸟",2));testSwim(new Dog("小狗",3));testRun(new Dog("小狗",5));testRun(new Robot());}
}
每一个接口可以代表一种属性,比起之前写死在动物类里面的优势在于,不是每一个动物都会飞和跑的,如果一个动物有这个属性,就调用这个接口就可以了。
如果给一个无序数组,大家都知道调用sort排序,那么是一个类,我们进行排序,怎么操作呢?
首先看数组的排序
package demo6;import java.util.Arrays;public class Test {public static void main(String[] args) {int[] array={1,8,9,6,5,4,3,2};Arrays.sort(array);System.out.println(Arrays.toString(array));}
}
我们先构建学生类,包含姓名,年龄,分数等信息,然后建立一个学生类的数组,然后对数组排序,观察排序前和排序后
package demo6;import java.util.Arrays;/*** Created with IntelliJ IDEA.* Description:* User: laichangyang* Date: 2025-04-02* Time: 19:17*/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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
}
public class Test {public static void main(String[] args) {Student[] array= new Student[3];array[0]=new Student("zhangsan",19,85.5);array[1]=new Student("lisi",20,80.5);array[2]=new Student("wangwu",19,95.5);System.out.println("排序前:"+Arrays.toString(array));Arrays.sort(array);System.out.println("排序后:"+Arrays.toString(array));}public static void main1(String[] args) {int[] array={1,8,9,6,5,4,3,2};Arrays.sort(array);System.out.println(Arrays.toString(array));}
}
我们的程序明显是有问题的,排序的时候,我们根据什么排序,一个类有那么多属性。
输出结果:
通过观察
排序的时候是强制转换成Comparable类(一个接口),我们当然需要重写抽象的方法,那么我们使用一个接口,并且重写方法
最后发现还是不行,我们需要手动实现一下排序
最终的代码:
package demo6;import java.util.Arrays;/*** Created with IntelliJ IDEA.* Description:* User: laichangyang* Date: 2025-04-02* Time: 19:17*/class Student implements Comparable<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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}@Overridepublic int compareTo(Student o) {int ret=this.age-o.age;if(ret>=0) return 1;else return 0;}
}public class Test {public static void bubbleSort(Comparable[] comparables){for(int i=0;i<comparables.length-1;i++){for(int j=0;j<comparables.length-i-1;j++){if(comparables[j].compareTo(comparables[j+1])>0){Comparable tmp=comparables[j];comparables[j]=comparables[j+1];comparables[j+1]=tmp;}}}}public static void main(String[] args) {Student[] array= new Student[3];array[0]=new Student("zhangsan",19,85.5);array[1]=new Student("lisi",20,80.5);array[2]=new Student("wangwu",19,95.5);System.out.println("排序前:"+Arrays.toString(array));//Arrays.sort(array);bubbleSort((array));System.out.println("排序后:"+Arrays.toString(array));}public static void main1(String[] args) {int[] array={1,8,9,6,5,4,3,2};Arrays.sort(array);System.out.println(Arrays.toString(array));}
}
还有一个接口Comparator也可以实现比较,看一下它怎么使用吧
具体使用
package demo7;import java.util.Comparator;/*** Created with IntelliJ IDEA.* Description:* User: laichangyang* Date: 2025-04-02* Time: 19:54*/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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
}class AgeComparator implements Comparator<Student>{@Overridepublic int compare(Student o1, Student o2) {return o1.age-o2.age;}
}class ScoreComparator implements Comparator<Student>{@Overridepublic int compare(Student o1, Student o2) {return (int)(o1.score-o2.score);}
}
public class Test {public static void main(String[] args) {Student student1 = new Student("zhangsan",30,48);Student student2 = new Student("lisi",20,80);AgeComparator ageComparator= new AgeComparator();System.out.println(ageComparator.compare(student1,student2));System.out.println("------------");ScoreComparator scoreComparator = new ScoreComparator();System.out.println(scoreComparator.compare(student1,student2));}
}
抽象类和接口的比较
何时使用抽象类,何时使用接口?
使用抽象类的情况:
需要在多个相关类之间共享代码和状态
需要非公开的方法和成员变量
类之间有明确的继承关系和层次结构
希望修改基类后,所有派生类都受到影响
使用接口的情况:
不相关的类需要共享同一行为(如不同类实现Comparable接口)
需要多重继承
只关心对象能做什么,而不是它是什么
需要规范一组相关或不相关的类
总结
抽象类和接口是Java中实现抽象和多态的两种重要机制。抽象类更适合用于表示类之间的继承关系和共享实现,而接口则更适合定义对象的行为规范和能力。在实际开发中,我们需要根据具体需求选择合适的抽象机制,有时甚至需要结合使用两者,以创建灵活、可扩展的代码结构。
随着Java语言的发展,接口也在不断增强功能(如Java 8引入的默认方法),使得接口和抽象类之间的界限变得越来越模糊。但理解它们各自的特点和适用场景,仍然是Java开发者必须掌握的基础知识。