当前位置: 首页 > news >正文

【JAVA】Java抽象类与接口详解:特性与实战运用(超详细)

目录

一:抽象

1.1:抽象概念

1.2:抽象类语法

1.3 :抽象类特性

1.4 :抽象类的作用

二:接口(interface)

2.1 接口的概念

2.2 语法规则

2.3接口的使用

2.4 接口特性

2.5 实现多个接口

2.6接口间的继承

2.7 接口使用实例

2.8Clonable 接口和深拷贝

2.9 抽象类和接口的区别

三:总结


一:抽象

1.1:抽象概念

什么是抽象?
越不具体越抽象,信息量的大小来判断。
前面讲的类,就是现实食物在java代码中的一种“抽象的表示形式”
抽象类,把代码中的类,做进一步抽象。
普通类包含:属性 方法(自然描述方法的逻辑怎么样)
进一步抽象就是把方法的实现部分给忽略掉,只保留方法的名字,参数信息(没有方法的实现逻辑)抽象类(很类似C语言中的函数声明)

具体一点:包含抽象方法的类称为抽象类。

下面三个子类都是能画出来的,父类就显的比较抽象,由于也不知道咋画,就直接让draw方法中吧提供任何实现方法:

每个子类怎么叫是具体的,但是父类怎么叫,这个也就很抽象

1.2:抽象类语法

abstract:通过这个关键字修饰的方法,就是抽象方法
通过这个关键字修饰的类,就是抽象类

public abstract  class Shape {  public abstract void draw() ;//抽象方法,不能写具体实现。  
}

一个抽象类可以包含普通的属性和方法、也可以包含抽象方法。

如果没有抽象类,里面的抽象方法就会报错:(一个抽象的方法,不能放到没有抽象的类中)但是如果你是抽象类,类中可以有普通方法。

package object.package2;  
import java.security.KeyStore;  
public  abstract class Shape {  public abstract void draw() ;//抽象方法,不能写具体实现。  //形状的面积  private double area;  private static int m=0;  //也可以包含普通的方法  public double getArea(){  return area;  }  //也可以包含静态方法  public static int getcount(){  return m;  }  
}

在抽象类中可以包含普通方法、静态方法、静态属性、普通属性

重点::但我们引入抽象方法的作用 就是为了子类重写

1.3 :抽象类特性

1. 抽象类不能直接实例化对象(抽象类自身就不能表示一个完整的概念) 

日常开发中确实会存在这种抽象的概念,不应该实例化,给出抽象类一份规则,就可以做出“强制限制”

2. 抽象方法不能是 private 的

非法组合

3. 抽象方法不能被final和static修饰,因为抽象方法要被子类重写 

在Java中,抽象方法不能同时被声明为`final`和`abstract`。`final`关键字表示该方法不能在子类中被重写,而`abstract`关键字则表示该方法没有具体实现,必须在子类中被重写。因此,`public final abstract void draw();`这样的声明是非法的

4. 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰

5. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
6. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量

1.4 :抽象类的作用

本质上起到的效果和final @override之类的是类似的 让编译器更加严格。
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.
有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?

       确实如此. 但是使用抽象类相当于多了一重编译器的校验.

     使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.

很多语法存在的意义都是为了 "预防出错", 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们.

充分利用编译器的校验, 在实际开发中是非常有意义的

抽象类还是很有辨识度的

二:接口(interface)

2.1 接口的概念

接口这个词 范围广泛,不局限与java:
1:C语言中,一个函数,也可以称作为接口
2:java中的interface当然也是接口
3:工作中,把一个大项目,分成多个模块,把模块之间,相互调用的部分也称接口
4:服务器,能够处理哪些亲求,也称作服务器提供的接口
5:图形化界面(GUI)也称作接口
通过上述例子可以看出:接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。

2.2 语法规则

接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。

创建类有这个类

创建的实例

提示:(都是建议不强制
1. 创建接口时, 接口的命名一般以大写字母 I 开头.
2. 接口的命名一般使用 "形容词" 词性的单词.(类/变量名:名次;方法/命名:动词/动宾短语;接口命名:形容词)
3. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性(不用加public abstract).

2.3接口的使用

和抽象类类似,接口也是不能直接实例化.要想使用接口,就需要创建类,“实现”接口

public class 类名称 implements 接口名称{// ...}

接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。

代码:
USB接口:
package object.package4;  public interface USB {  void openDevice();  void closeDevice();  
}
Mouse类:
public class Mouse  implements USB{  @Override  public void openDevice() {  System.out.println("插入鼠标");  }  @Override  public void closeDevice() {  System.out.println("拔出鼠标");  }  
}
KeyBoard类:
public class KeyBoard implements USB {  @Override  public void openDevice() {  System.out.println("插入键盘");  }  @Override  public void closeDevice() {  System.out.println("拔出键盘");  }  
}
Test类:
public class Test {  public static void main(String[] args) {  USB usb=new Mouse();//虽然接口这里,不使用继承术语,此处仍然是向上转型 触发多态  usb.openDevice();  usb.closeDevice();  USB usb2=new KeyBoard();  usb2.openDevice();  usb2.closeDevice();  }  
}运行:
插入鼠标
拔出鼠标
插入键盘
拔出键盘

2.4 接口特性

1. 接口类型是一种引用类型,但是不能直接new接口的对象
接口时抽象类的更进一步,比抽象类更抽象。
2. 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错)

3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
4. 重写接口中方法时,不能使用默认的访问权限
5. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量
6. 接口中不能有静态代码块和构造方法(抽象类中有实例属性,需要通过构造方法来初始化,接口没有实例属性)
7. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class

8. 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
只要类中包含抽象方法,必须设为抽象类1:类自己声明的抽象方法2:从抽象类继承3:实现从接口声明
9. jdk8中:接口中还可以包含default方法。
被default关键字修饰的方法,时可以添加方法实现的

2.5 实现多个接口

在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口.
(java中允许实现多个接口 因为接口中没有实例属性)
接下来有个实例:
先创建三个接口
 

Iflying接口:
public interface Iflying {  void fly();  
}
IRunning接口:
public interface IRunning {  void run();  
}
ISwimming
public interface ISwimming {  void swim();  
}
创建一个父类Animal:
//小动物父类  
public class Animal {  private String name;  public Animal(String name) {  this.name = name;  }  public void setName(String name) {  this.name = name;  }  public String getName() {  return name;  }  
}
Cat类:
//Cat时Animal的子类,实现了Runnable接口  
public class Cat extends Animal implements IRunning {  public Cat(String name) {  super(name);  }  @Override  public void run() {  System.out.println(getName() + "使用四条腿跑步");  }  
}
Fish类:
public class Fish extends Animal implements ISwimming {  public Fish(String name) {  super(name);  }  public void swim() {  System.out.println(getName() + "用尾巴游泳");  
}  
}
Frog类:(两xi动物)
public class Frog extends Animal implements IRunning,ISwimming{//使用多个接口,实现多个接口的功能,用,分隔  public Frog(String name) {  super(name);  }  @Override  public void run() {  System.out.println(getName()+"一跳一跳跑");  }  @Override  public void swim() {  System.out.println(getName()+"使用四条腿游泳");  }  
}
Duck类:(三系动物)
public class Duck extends Animal implements ISwimming,IRunning,Iflying{  public Duck(String name) {  super(name);  }  @Override  public void run() {  System.out.println(this.getName() + " 使用两条腿跑");  }  @Override  public void swim() {  System.out.println(this.getName() + " 用两条腿游泳");  }  @Override  public void fly() {  System.out.println(this.getName() + "用翅膀飞翔");  }  
}
Test类:(main)
public class Test {  public static void main(String[] args) {  
//        Animal animal=new Cat("小猫");  
//        System.out.println(animal.getName());  
//  
//        IRunning running = new Cat("小猫");  
//        running.run();  Cat cat = new Cat("小猫");  System.out.println(cat.getName());  cat.run();  Fish fish = new Fish("小鱼");  System.out.println(fish.getName());  fish.swim();  //两xi动物  Frog frog =new Frog("小青蛙");  System.out.println(frog.getName());  frog.run();  frog.swim();  //水陆空三种动物  Duck duck = new Duck("小鸭子");  System.out.println(duck.getName());  duck.fly();  duck.run();  duck.swim();  }  
}
运行:
//小猫
//小猫使用四条腿跑步
//小鱼
//小鱼用尾巴游泳
//小青蛙
//小青蛙一跳一跳跑
//小青蛙使用四条腿游泳
//小鸭子
//小鸭子用翅膀飞翔
//小鸭子 使用两条腿跑
//小鸭子 用两条腿游泳

上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口
**在类中方法可以使用快捷键 alt 加回车 可以生成 构造 和方法**

接口应该在什么场景呢?
接口存在的意义,是协助程序员之间良好沟通,实际开发中,一个程序员通常不是一个人完成的。
通过接口可以明确的约定,模块之间的交互方式。

2.6接口间的继承

一个类,实现自一个接口,但是接口和接口之间,可以继承(可以多继承)也可以说是把多个小接口,合并成一个大接口。
此时就意味着继承出来的接口,同时具有多个接口中的抽象方法

创建一个两栖接口:
package object.package5;  
//两栖动物就是既可以跑也可以游泳  
public interface IAmphibious extends IRunning,ISwimming{  //此处可以不用声明  //此处以及用于run 和swing  
}
然后我们前面创建的Frok类 就可以这样写:
public class Frog extends Animal implements IAmphibious{//使用接口继承,然后是用总的接口,也就是多个接口合并为一个

2.7 接口使用实例

实现对象大小关系比较:
(比较两个数字的大小)
创建Student类:
package object.package6;  public class Student implements Comparable<Student> {//<>泛型参数  //如何对比两个学生大小关系。  private String name;  private int id;  private double score;  public Student(int id,String name,double score) {  this.name = name;  this.id = id;  this.score = score;  }  public static void main(String[] args) {  Student s1 = new Student(1,"张三",90);  Student s2 = new Student(2,"李四",80);  //此处是我们手动调用comparaeTo方法,而不是使用<>符号  System.out.println(s1.compareTo(s2));  //  System.out.println(s1>s2);//这种情况我们称作为运算符重载  }  @Override  public int compareTo(Student o) {//这个方法是实例方法,不是静态方法。内涵了this,当前对象  //参数还有一个o,表示另一个要比较的对象。  //如果this比o小,返回-1;如果this和o相等,返回0;如果this比o大,返回1。  //比如先比较成绩,成绩高就是大  //如果成绩相等,那就比较id  if (this.score < o.score) {  return -1;  } else  if (this.score > o.score) {  return 1;  }else {  if(this.id<o.id){  return -1;  }else if (this.id >o.id){  return 1;  }else{  return 0;  }  }  }  
}
让我们的 Student 类实现 Comparable 接口, 并实现其中的 compareTo 方法使用创建一个单独的类,如何与comparator接口package object.package6;  
//comparator 和comparable不太一样  
//comparable 需要把被比较的类 实现这个接口  
//而Comparator则是创建一个单独的类,实现Comparator接口。  
public class Student2 {  private int id;  private String name;  private double score;  public Student2(int id, String name, double score) {  this.id = id;  this.name = name;  this.score = score;  }  public static void main(String[] args) {  Student2 s1 = new Student2(1,"张三",90);  Student2 s2 = new Student2(2,"李四",80);  }  
}comparable是拿着this和参数o进行比较;
comparator是拿着两个参数进行比较。
这是comparator
public static void main(String[] args) {  Student2 s1 = new Student2(1,"张三",90);  Student2 s2 = new Student2(2,"李四",80);  StudentComparator comparator = new StudentComparator();  System.out.println(comparator.compare(s1,s2));  
}
这是使用comparable
package object.package6;  
import java.util.Comparator;  
public  class StudentComparator implements Comparator<Student2> {  @Override  public int compare(Student2 o1, Student2 o2) {  if (o1.getScore() > o2.getScore()) {  return 1;  } else if (o1.getScore() < o2.getScore()) {  return -1;  } else {  if (o1.getId() > o2.getId()) {  return 1;  } else if (o1.getId() < o2.getId()) {  return -1;  }  }  return 0;  }  
}public static void main(String[] args) {  Student s1 = new Student(1,"张三",90);  Student s2 = new Student(2,"李四",80);  //此处是我们手动调用comparaeTo方法,而不是使用<>符号  System.out.println(s1.compareTo(s2));  //  System.out.println(s1>s2);//这种情况我们称作为运算符重载  }

2.8Clonable 接口和深拷贝

隆是创建新对象,新对象的内容(属性)和旧对象是一样的。
允许克隆对象复制

我们先找个例子:
public class Test {  public static void main(String[] args) {  Student s =new Student(1,"张三",90);  Student s1=s;  }  
}

这个大家认为是克隆吗 ?
不是的 这个仅仅就是引用,把s创建的实例化的实例的地址传给s,然后把s的地址传给s1.。
这个代码只是创建新的引用,此时对象本身只有一个。
Java 中内置了一些很有用的接口, Clonable 就是其中之
Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 "拷贝". 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException 异常

package object.package7;  public class Student implements Cloneable {//使用idea标准库提供的方法实现Cloneable接口,实现克隆功能。  private int id;  private String name;  private double score;  public Student(int id, String name, double score) {  this.id = id;  this.name = name;  this.score = score;  }  public int getId() {  return id;  }  public String getName() {  return name;  }  public double getScore() {  return score;  }  public void setId(int id) {  this.id = id;  }  public void setName(String name) {  this.name = name;  }  public void setScore(double score) {  this.score = score;  }  
//使用快捷键添加Cloneable接口,并实现clone()方法  @Override  protected Object clone() throws CloneNotSupportedException {  return super.clone();  }  public static void main(String[] args) throws CloneNotSupportedException {  Student s1=new Student(1,"张三",99);  //希望能够 克隆出s2和s1一样的内容,不同的对象。  //此时我们就得到了一个新的对象s2,但是内容和s1一样。处于不同的地址上。  Student s2=(Student) s1.clone();  System.out.println(s1.getId());  System.out.println(s2.getName());  System.out.println(s1.getScore());  }  }
运行:
1
张三
99.0
1
张三
99.0

//使用==比较两个引用是否指向同一个对象  
System.out.println(s1==s2);  
//还可以修改一个对象,,看类一个对象是否一起改变  
s2.setScore(88);  
System.out.println(s1.getScore());  
System.out.println(s2.getScore());
运行:
false
99.0
88.0

由此可以看出 s1 、s2,只是克隆的复制的关系,也没有指向同一个引用,
然后我们修改s2 的成绩,修改成功后,分别打印s1、s2 的成绩,s2发生改变,不会影响s1也同时证明两个引用不指向同一个对象。
 

2.8.1:深浅拷贝

深拷贝 :如果递归进行,就是深拷贝,否则浅拷贝
如果类中的属性都是内置类型,不涉及深浅拷贝问题,如果设计引用类型,就会有影响。

出一个浅拷贝的例子:
money类:
package object.package7;  public class Money {  private double amount;  public void setAmount(double amount) {  this.amount = amount;  }  public double getAmount() {  return amount;  }  
}
person类:
package object.package7;  public class Person implements Cloneable{  private int id;  Money money=new Money();  @Override  public Person  clone() throws CloneNotSupportedException {  return(Person) super.clone();  }public static void main(String[] args) throws CloneNotSupportedException {  Person p1 = new Person();  p1.id = 1;  p1.money.setAmount(1000);  
//        System.out.println(p1.id);  
//        System.out.println(p1.money.getAmount());  
//  
//        Person p2 = p1.clone();  
//        System.out.println(p2.id);  Person p2 = p1.clone();  p2.id=2;  System.out.println(p1.id);  System.out.println(p2.id);  p2.money.setAmount(2000);  System.out.println(p1.money.getAmount());  System.out.println(p2.money.getAmount());  }  
}
运行:
1
2
2000.0
2000.0

被注释的是我们克隆的步骤
下面就是我们修改p2的值,但是p1也发生改变。拷贝操作,只是把person对象拷贝了一下,而Perosn内部的持有的Money对象,没有真正的拷贝。这就是浅拷贝。

插入一个内存图:(给大家解释一下)

虽然p1 和p2是指向两个不同的对象
但是p1.money和p2.money仍然是同一个对象。
我通过对拷贝的修改,money修改后,就会反馈回p1的money

深拷贝,则是表示,不光要把person对象本身进行拷贝,也需要把Person内部持有的引用类型的属性,也去拷贝。
引用类型属性中还有引用类型,持续的递归拷贝进去,保证所有内部持有的引用类型,都应该拷贝一份新的变量值。

public Person  clone() throws CloneNotSupportedException {  Person p=(Person) super.clone();  p.money= (Money) p.money.clone();  return p;  
}

Money类:


此处Person中只有一个引用类似的成员,只需要针对Money在clone一下

2.9 抽象类和接口的区别

抽象类和接口都是 Java 中多态的常见使用方式. 都需要重点掌握. 同时又要认清两者的区别(重要!!! 常见面试题).
核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.
如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口
再次提醒:
抽象类存在的意义是为了让编译器更好的校验, Animal 这样的类我们并不会直接使用, 而是使用它的子类.
万一不小心创建了 Animal 的实例, 编译器会及时提醒我们.

这个我们好好了解就可以,主要就是第5点,我们接口就是为了满足多继承。

三:总结

本文主要介绍了Java中的抽象类和接口概念及其应用。抽象类通过abstract关键字定义,包含抽象方法(无具体实现)和普通方法,不能被实例化,需通过子类继承并重写抽象方法。接口是一种公共行为规范,使用interface定义,方法默认public abstract,类通过implements实现接口并重写方法。接口支持多继承,而类只能单继承。抽象类适用于有共同属性和行为的场景,接口则用于定义公共行为标准。文章还通过实例展示了接口的继承、比较接口(Comparable和Comparator)以及克隆接口(Cloneable)的用法,并对比了抽象类和接口的核心区别。

http://www.dtcms.com/a/399041.html

相关文章:

  • Shell 脚本知识体系
  • 怎么创立网站官网开发多少钱一个
  • RK3568+MCU实时机器人解决方案
  • (队列)Leetcode239 滑动窗口的最大值
  • 企业网站的建设意义付费阅读网站代码
  • 青岛网站上排名美容医疗 网站建设
  • 网站实名认证必须做么做网站的html框架
  • 采用libreoffice将word、excel等文件转换为pdf格式
  • vue3 用el-dialog实现用户协议,内容是富文本, 滚动到最后才允许关闭
  • 考研408《操作系统》复习笔记,第四章(1)《文件管理概念》
  • 如何整合 openSSL custom provider (以 TRNG 舉例)
  • 做电影网站一年赚多少钱wordpress 二级页面菜单 404
  • JAVA学习笔记——判断和循环的概念和一些习题
  • Java `synchronized` 关键字高频面试题(原理+场景+底层实现)
  • 微信企业号可以做微网站吗查看wordpress访问记录
  • 企业建站程序哪个好asp简单网站开发
  • 法术光环释义
  • todesk远程到被控Mac后不显示画面
  • 上网行为安全(2)
  • 网站颜色搭配技巧网站建设征税标准
  • 虚拟主机建网站网站建设技术主管
  • Transformer原理学习(4)注意力机制
  • Linux epoll 事件机制深度解析
  • 仿制网站软件王烨名字含义
  • 网站建设教程 乐视网冠辰网站建设
  • 网站建设方案说明微信里的小程序怎么删除
  • vue <img 图片标签 图片引入
  • 防伪网站怎么做为什么打开网址都是seo综合查询
  • 做极速赛车网站怎么做网站视频
  • DP4363远程无钥匙进入(PKE)技术:便利与安全的完美融合