目录
🎓抽象类
🎈抽象类语法
🎈抽象类特性
🎈抽象类的作用
🎓接口
🎈语法规则
🎈接口特性
🎈接口使用(实现USB接口)
🎈实现多个接口
🎈接口间的继承
🎈接口使用实例 (给对象数组排序)
🌈Comparable接口<比较>
🌈Comparator接口<比较器>
🚩抽象类和接口的区别
🎓抽象类
抽象类自己本身是不具有什么特性的,就比如Shape类中我们有draw方法,但是Shape并没有什么具体的形状,而继承的子类比如三角形类,矩形类,圆形类等都是在有具体图形,我们称Shape类是抽象类。
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的, 如果 一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
在打印图形例子中 , 我们发现 , 父类 Shape 中的 draw 方法好像并没有什么实际工作 , 主要的绘制图形都是由 Shape的各种子类的 draw 方法来完成的 . 像这种没有实际工作的方法 , 我们可以把它设计成一个 抽象方法 (abstract method) , 包含抽象方法的类我们称为 抽象类 (abstract class)
🎈抽象类语法
// 抽象类:被abstract修饰的类
abstract class Shape1{
// 抽象方法:被abstract修饰的方法,没有方法体
abstract public void draw();
abstract void calcArea();
// 抽象类也是类,也可以增加普通方法和属性
public int age;
public void func(){};
}
🎈抽象类特性
1. 抽象类不能直接实例化对象 new
肯定很多人疑问,抽象类不能实例化对象,那么存在意义在哪?—抽象类存在的意义是为了被继承
public class Test {
Shape1 shape1=new Shape1();
}
//Shape1是抽象类,不能实例化
2.抽象类和普通类不一样的是抽象类可以包含抽象方法,可以包含普通类所包含的成员(上述语法已讲述)
3.抽象类和抽象方法都是由abstract修饰的,方法中没有具体的实现体(上述语法)
4.抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰(被abstract修饰就不用重写方法,应用于有些类不需要这个方法)
abstract class Shape1{
// 抽象方法:被abstract修饰的方法,没有方法体
abstract public void draw();
}
class Cycle1 extends Shape1{
@Override
public void draw() {
System.out.println("画⚪");
}
}
5.如果一个抽象类B继承了一个抽象类A,此时B当中不需要重写A的重写方法,但如果B再被
普通类继承,就需要重写。
6.抽象方法不能被private,final和static修饰,因为抽象方法要被子类重写我们可以这样想,方法就是为了重写设置成私有的,怎么能被重写?并且我们在 重写的时候说到 (java子类方法权限要等于大于父类的访问权限?)
7. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
8. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量❗A类abstract class A{ public int age; public String name; public A(int age,String name){ this.age=age; this.name=name; } abstract public void func();
❗B类
class B extends A{ public B(){ super(8,"chenle"); } public void func(){ System.out.println("sadasd"); } }
❗运行区
🎈抽象类的作用
有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法
呢
🎓接口
🎈语法规则
public interface 接口名称{
// 抽象方法
public abstract void method1(); // public abstract 是固定搭配,可以不写
public void method2();
abstract void method3();
void method4();
// 注意:在接口中上述写法都是抽象方法,跟推荐方式4,代码更简洁
}
提示:1. 创建接口时, 接口的命名一般以大写字母 I 开头.2. 接口的命名一般使用 "形容词" 词性的单词.3. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性
🎈接口特性
1.使用interface来修饰接口
2.接口当中的成员方法,不能有具体的实现 [默认是public]
所以子类的重写方法不能默认不写,子类的重写方法是default,而子类的访问权限要大于父类的访问权限,所以我们必须在子类中的重写方法前写上public
- 抽象方法:默认是public abstract方法
- JDK1.8开始 ,允许有可以实现的方法,但是这个方法只能由default修饰
- 可以实现一个静态方法
interface Ishape{ public abstract void draw();//抽象方法 void draw();//这是最好的表达抽象方法方式 default public void draw1(){ System.out.println("默认方法"); } public static void draw2(){ System.out.println("静态方法"); } }
3.成员变量默认是public static final 修饰的(是不能修改的)
成员方法 默认是public abstract 修饰(其他修饰符都是错的)
public static final int age=10;//public static final void draw3();//public abstract
4.接口不能被实例化(抽象类也不能实例化)因为接口中的方式默认为抽象方法
5.类和接口之间采用implements来实现多个接口
一旦类实现接口之后,就必须重写这个接口里面的抽象方法。接口里的普通成员方法可以重写也可以不重写。
interface Itest{ void draw3();//public abstract default public void draw1(){ System.out.println("默认方法"); } public static void draw2(){ System.out.println("静态方法"); } } class Cycle implements Itest{ @Override public void draw3() { System.out.println("必须重写"); } @Override public void draw1() { System.out.println("可以重写也可以不重写"); } }
6.将接口想象成特殊的类,我们可以当作参数,接口不能实例化,但是可以new普通的类。
interface Ishape{ void draw(); } class Cycle implements IShape{ @Override public void draw() { System.out.println("画⚪"); } } class Rect implements IShape{ @Override public void draw() { System.out.println("画矩形"); } } class Flowers implements IShape{ @Override public void draw() { System.out.println("画❀"); } } public class Test3 { public static void Map(IShape iShape){ iShape.draw(); } public static void main(String[] args) { Cycle cycle=new Cycle(); Map(cycle); Map(new Flowers()); Map(new Rect()); }
相当于 Ishape ishape=new Cycle();
ishape.draw();实现创建对象,进行对接口的调用。
7. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
一个接口就是一个java文件
8.接口中不能有静态代码块和构造方法
9.如果不想实现接口的方法,那么就将这个类定义成抽象类,但是如果这个类被其他类继承,那么必须重写。(这个和抽象类类似)
🎈接口使用(实现USB接口)
public class 类名称 implements 接口名称{
// ...
}
请实现笔记本电脑使用 USB 鼠标、 USB 键盘的例子1. USB 接口:包含打开设备、关闭设备功能2. 笔记本类:包含开机功能、关机功能、使用 USB 设备功能3. 鼠标类:实现 USB 接口,并具备点击功能4. 键盘类:实现 USB 接口,并具备输入功能
package Interfaces;
//USB接口
interface Iusb{
void openDevice();
void closeDevice();
}
//鼠标类
class Mouse implements Iusb{
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标");
}
public void click(){
System.out.println("点击鼠标");
}
}
// 键盘类,实现USB接口
class KeyBoard implements Iusb {
@Override
public void openDevice() {
System.out.println("打开键盘");
}
@Override
public void closeDevice() {
System.out.println("关闭键盘");
}
public void inPut(){
System.out.println("键盘输入");
}
}
// 笔记本类:使用USB设备
class Computer {
public void powerOn() {
System.out.println("打开笔记本电脑");
}
public void powerOff() {
System.out.println("关闭笔记本电脑");
}
public static void useDevice(Iusb iusb){
iusb.openDevice();
if(iusb instanceof Mouse){
Mouse mouse=(Mouse) iusb;
mouse.click();
}else if(iusb instanceof KeyBoard){
KeyBoard keyBoard=(KeyBoard) iusb;
keyBoard.inPut();
}
iusb.closeDevice();
}
}
public class Test4 {
public static void main(String[] args) {
Computer computer = new Computer();
//打开设备
computer.powerOn();
//使用鼠标设备
computer.useDevice(new Mouse());
//使用键盘设备
computer.useDevice(new KeyBoard());
//关闭设备
computer.powerOff();
}
}
🎈实现多个接口
一个类可以实现多个接口。使用implements用逗号隔开。【可以解决多继承的问题】
我们可以定义一个动物类,然后狗类继承了动物类,我们实现多个接口,有些动物是又会飞又会跑,又会游泳的。
interface Running{
void run();
}
interface Flying{
void fly();
}
interface Swimming{
void swim();
}
我们实现多个接口,而不是实现多个类,因为java中只能继承一个类,而不能继承多个类,但是一个类可以实现多个接口。我们不需要创建一个swim类,fiy类,run类,然后里面有自己的swim,fly,run的成员方法,然后狗类一个一个继承,因为java中不能多继承,所以代码的重复,而实现多个接口正是可以解决java中的多继承问题,让一个狗类同时implement多个接口,然后重写方法,即可完成了一个类继承多个接口的方法。
❗各接口
interface Running{
void run();
}
interface Flying{
void fly();
}
interface Swimming{
void swim();
}
❗Animal类和各种子类
class Animal{
int age;
String name;
Animal(int age,String name){
this.age=age;
this.name=name;
}
void eat(){
System.out.println("正在吃饭");
}
}
class Dog extends Animal implements Running,Swimming{
public Dog(int age, String name) {
super(age, name);
}
@Override
public void run() {
System.out.println(name+"正在跑");
}
@Override
public void swim() {
System.out.println(name+"正在游泳");
}
public void eat(){
System.out.println(name+"吃狗粮");
}
}
class Bird extends Animal implements Flying{
public Bird(int age, String name) {
super(age, name);
}
@Override
public void fly() {
System.out.println(name+"正在飞");
}
public void eat(){
System.out.println(name+"吃鸟粮");
}
}
class Duck extends Animal implements Flying,Swimming,Running{
public Duck(int age, String name) {
super(age, name);
}
public void run() {
System.out.println(name+"正在跑");
}
@Override
public void swim() {
System.out.println(name+"正在游泳");
}
public void fly() {
System.out.println(name+"正在跑");
}
public void eat(){
System.out.println(name+"吃鸭粮");
}
}
❗测试方法
public class Test6 {
public static void walk(Running running){
running.run();
}
public static void func(Animal animal){
animal.eat();
}
public static void main(String[] args) {
walk(new Dog(13,"chq"));
walk(new Duck(12,"chhh"));
}
}
🎈接口间的继承
就是说我们D1类是一个既能完成A1的行为,也能完成B1的行为,还能完成C1接口行为,然后我们接口C1继承了A1和B1的接口,其实C1继承A1和B1的行为,就相当于如果类实施了C1,那么这三个A1,B1,C1都是行动的。
接口间的继承相当于把多个接口合并在一起.
🎈接口使用实例 (给对象数组排序)
❗Animal类
class Student{
public int age;
public String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
❗运行
这段代码是错误的,我们不管是按照什么来排序的,不管是年龄还是姓名都是错误的,我们可以来分析一下这段错误。
🌈Comparable接口<比较>
✅按照年龄来比较(Comparable 接口, 并实现其中的 compareTo 方法)
class Student implements Comparable<Student>{
public int age;
public String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Student o) {
if(this.age>o.age){
return 1;
}else if(this.age<o.age){
return -1;
}else{
return 0;
}
}
}
当Student类实施了Comparable接口并且实现了compareTo方法,我们就可以根据年龄来进行比较。
我们可以看到底层,底层代码的比较是调用compareTo方法比较
public static void main(String[] args) {
Student student1=new Student(18,"chenle");
Student student2=new Student(13,"zhang");
if(student1.compareTo(student2)>0){
System.out.println("student1>student2");
}else {
System.out.println("student2>student1");
}
}
🌈Comparator接口<比较器>
✅按照姓名来比较(实现Comparator接口,并实现compare方法)
我们是不能更改age的比较方法,那样势必回影响后面的运行,如果代码已经运行了一个月了,如果更改会影响很多。
所以我们需要利用 另外一个接口Comparator<T>以及接口里的compare方法
Comparator<T>接口中有很多方法,我们只需要compare抽象方法即可。
❗AgeCompare
class AgeCompare implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
}
❗运行
public static void main(String[] args) {
Student[] students=new Student[3];
students[0]=new Student(18,"chenle");
students[1]=new Student(13,"zhang");
students[2]=new Student(9,"hao");
AgeCompare ageCompare=new AgeCompare();
Arrays.sort(students,ageCompare);
System.out.println(Arrays.toString(students));
}
❗NameCompare
因为比较字符串的底层是利用Comparable接口中的compareTo方法
class NameCompare implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
❗运行
public static void main(String[] args) {
Student[] students=new Student[3];
students[0]=new Student(18,"chenle");
students[1]=new Student(13,"zhang");
students[2]=new Student(9,"hao");
NameCompare nameCompare=new NameCompare();
Arrays.sort(students,nameCompare);
System.out.println(Arrays.toString(students));
}
我个人更推荐第二种,我们自己单独设置个类,然后我们直接利用比较器比较,如果比较整型直接return俩者相减即可,如果比较字符串,我们需要利用到compareTo方法进行比较。
🚩抽象类和接口的区别
祝你逃出苦难向春山。