抽象类和接口(浅深克隆拷贝)

简介: 抽象类和接口(浅深克隆拷贝)

一.抽象类

抽象类特点:

1、使用abstract修饰的方法,就是抽象类。

2、包含抽象方法的类,必须设计为抽象类,使用abstract修饰这个类(即抽象类不一定有抽象方法,有抽象方法的一定是抽象类)。

3、抽象类是不能够进行实例化的。

4、抽象类虽然不可以进行实例化,但是可以被继承。(所以抽象类就是为了被继承)

5、当一个普通类继承这个抽象类之后,如果这个抽象类当中包含抽象方法,那么需要重写这个抽象方法,否则代码不能通过编译。

6、如果一个抽象类A继承另一个抽象类B,那么此时这个抽象类A可以不重写B当中的抽象方法。

7、抽象方法不能是private、static、final的。

a8b5b76f83f34479879ee14baa6fb3eb.png

二,接口

接口特点:

1、使用interface来修饰接口。

2、接口当中的成员变量,默认都是public static final修饰的。

3、接口当中的成员方法,默认都是抽象方法,public abstract修饰的。

4、接口当中的普通成员方法,是不能有具体的实现的。

5、接口当中的普通成员方法,如果要有具体的实现,必须加上default修饰(从JDK8开始,才有的)。

6、接口当中可以有静态的成员方法。但是不管是静态的方法还是default方法,都是默认public修饰的(即使你没写系统也会默认进行添加)。

7、接口也是不可以进行实例化的。

8、类和接口的关系是使用implements来关联的。

9、接口当中不能有静态,实例代码块,构造方法。

10、一个抽象类实现一个接口,可以不重写这个抽象方法

11、接口虽然不是类,但它最终编译形成的字节码文件也是.class文件。


4058220ff8b141b1ba74e0a0aaa66aac.png众所周知,当你想要一个类去实现一个复杂的功能时,却发现只可以继承一个父类,这时候就会发现接口的重要作用了。一个类只可以继承一个父类,但却可以实现多个接口,但是接口之间却可以多继承。



16771c81bf7544bba4f4f6d33a95d780.png

三,重要接口举例

(1)comparable<  >

class 类名 implements Comparable<比较的类型>

如下图:

class Student implements Comparable<Student>{
//<>中的为所要比较的类型: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 +
                '}';
    }
//必须重写compareTo方法
    @Override
    public int compareTo(Student o) {
//compareTo:将此对象与指定的对象进行比较以进行排序。 
//以age为比较对象,从小到大
        return this.age-o.age;
    }
}

mian函数实现:

public class Main {
    public static void main(String[] args) {
//创建student数组并初始化
        Student[] students=new Student[3];
        students[0] = new Student("李四",15,88.8);
        students[1] =  new Student("王五",16,99.9);
        students[2] =  new Student("张三",14,37.7);
//通过Arrays.toString输出,Arrays.sort比较student数组中的age
//如果上面未重写compareTo方法,将会报错,即系统不知道通过哪个数据进行比较排序
        System.out.println("排序前"+ Arrays.toString(students));
        Arrays.sort(students);
        System.out.println("排序后"+Arrays.toString(students));
    }
}

运行结果:


26b5ef88082d4fa9bd4995a4027db366.png

如果你想通过age或者score两两比较两个学生对象,这时候就可以使用Comparator

(2)Comparator(比较器


7c565064f9c048029dc5d6695b351932.png

我们还是使用上面那个Student类,但是数据改变了一下,为了方便更好的理解

        Student[] students=new Student[3];
        students[0] = new Student("def",15,99.9);
        students[1] =  new Student("abc",16,88.8);
        students[2] =  new Student("ghi",14,37.7);
//分别重写compare方法进行比较
class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.age-s2.age;
    }
}
class ScoreComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return (int)(s1.score-s2.score);
    }
}
class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
//使用compareTo以字母顺序比较排序
        return s1.name.compareTo(s2.name);
    }
}

分别实例化这三个方法

        AgeComparator ageComparator = new AgeComparator();
        ScoreComparator scoreComparator = new ScoreComparator();
        NameComparator nameComparator = new NameComparator();

mian函数实现:

public class Main {
    public static void main(String[] args) {
//创建Student数组并且初始化
        Student[] students=new Student[3];
        students[0] = new Student("def",15,99.9);
        students[1] =  new Student("abc",16,88.8);
        students[2] =  new Student("ghi",14,37.7);
        System.out.println("排序前"+ Arrays.toString(students));
//Age
        AgeComparator ageComparator = new AgeComparator();
        Arrays.sort(students,ageComparator);
        System.out.println("排序后Age"+Arrays.toString(students));
//Score
        ScoreComparator scoreComparator = new ScoreComparator();
        Arrays.sort(students,scoreComparator);
        System.out.println("排序后Score"+Arrays.toString(students));
//Name
        NameComparator nameComparator = new NameComparator();
        Arrays.sort(students,nameComparator);
        System.out.println("排序后Name"+Arrays.toString(students));
    }
}

实验结果:

383ef7eab26c4029beb5f392f08bdae3.png

(3)Cloneable


51074ae4e5c541c1a9b3cefac3ef5079.png

 Ⅰ、浅拷贝:

class Person  implements Cloneable{
    public int age=10;
    @Override
//重写克隆方法要抛出一个CloneNotSupportedException异常
    protected Object clone() throws CloneNotSupportedException {
//将传过来的对象在新的地址克隆一份,重写clone方法
        return super.clone();
//因为方法被protected修饰,所以需要使用super调用
    }
    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}
public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person=new Person();
//clone方法属于object,向上转型需要强转,同时还要抛出一个克隆异常
        Person person1=(Person)person.clone();
        System.out.println(person);
        System.out.println(person1);
    }
}

下图就是克隆的过程:



2f06da19fea2483e90b226881135a1cd.png

结果:


e6e132b20d3c40ed9c44420586db3ae4.png

但是这仅仅只是 浅拷贝,也就是说Person中如果引用了其他对象的变量,去改变任意一个person的那个引用变量,两个person中的这个变量的值都将发生改变,也就是说,这两个实例化的person和person1中的这个变量指的是同一个变量,并不是像age一样克隆拷贝成两个,那我们看看下面代码:

class Student{
    public int score=90;
}
class Person  implements Cloneable{
    public int age=10;
    public Student s=new Student();
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}
public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person=new Person();
        Person person1=(Person)person.clone();
        System.out.println(person.s.score);
        System.out.println(person1.s.score);
        System.out.println("======================================");
//只更改person1的score的值,而person的score的值并没有修改
//那输出结果为什么呢????
        person1.s.score=88;
        System.out.println(person.s.score);
        System.out.println(person1.s.score);
    }
}

9c80305440cc4d76a4919445ebb9a268.png


Ⅱ、深拷贝

而深拷贝就是连引用对象的变量也可以拷贝一份新的。

实现深拷贝其实我们只需要让Student也实现接口Cloneable,代码做一些小的修改。

class Student implements Cloneable{
    public int score=90;
//与Person一样重写一下克隆方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Person  implements Cloneable{
    public int age=10;
    public Student s=new Student();
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person tmp  = (Person) super.clone();
//将Person类中实例化的s也克隆一份
        tmp.s=(Student) this.s.clone();
        return tmp;
    }
    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}


efba4dc8f23f46a184f1366886ea59ea.png

其结果就发生了改变!!


ae21956a5aaf46678dd79c58a28925c1.png

四、接口与抽象类的异同

区别 抽象类 接口
1 结构组成 普通类+抽象方法 抽象方法+全局变量
2 子类使用

使用extends关键字继承。

抽象类继承抽象类可以不用重写或者不重写全部抽象方法。

普通类继承要重写全部抽象方法。

使用implements关键字继承

3 关系 一个抽象类可以实现多个接口

接口不可以继承抽象类,但接口可以使用extends去继承多个父接口

4 子类限制 一个子类只可以继承一个抽象类 一个子类可以实现多个接口
5 成员变量 既可以有变量也可以有常量 只能是常量,而且默认修饰符为:public static final

即:当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。

ps:如果文章有什么错误,请各位大佬指点,同时期待你们的点赞和评论!!!

目录
相关文章
|
机器学习/深度学习 人工智能 Go
【AI绘画】Gradio工具
【AI绘画】Gradio工具
280 1
|
编解码 API 开发工具
|
12月前
|
Java
Byte 高位/低位简介绍(大端格式/小端格式)
Byte 高位/低位简介绍(大端格式/小端格式)
1438 1
给头像加个口罩网站html源码
源码由HTML+CSS+JS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果,也可以上传到服务器里面,重定向这个界面
214 0
|
Kubernetes Cloud Native 开发者
构建高效云原生应用:Kubernetes与微服务架构的融合
【5月更文挑战第31天】 在数字化转型和技术迭代的大潮中,企业对于敏捷、可扩展的IT基础设施需求日益增长。云原生技术以其独特的优势成为推动这一进程的关键力量。本文深入探讨了如何通过结合Kubernetes容器编排和微服务架构来构建和维护高效、可靠的云原生应用。我们将剖析这种技术整合的必要性,揭示其背后的原理,并讨论在实际部署过程中可能遇到的挑战及解决方案。通过案例分析和最佳实践的分享,旨在为开发者和架构师提供一套行之有效的云原生应用构建指南。
|
安全 区块链 UED
带你读《自主管理身份:分布式数字身份和可验证凭证》精品文章合集
带你读《自主管理身份:分布式数字身份和可验证凭证》精品文章合集
|
小程序 IDE API
8月开发者日回顾|小程序开发实用工具分享
8月开发者日回顾|小程序开发实用工具分享
128 0
|
前端开发
WebSocket使用及优化(心跳机制与断线重连)
WebSocket使用及优化(心跳机制与断线重连)
3322 0
WebSocket使用及优化(心跳机制与断线重连)
|
网络安全 PHP
PHPcurl访问HTTPS网址出错解决方法
PHPcurl访问HTTPS网址出错解决方法
WGCLOUD学习使用 - 服务器负载值过高会告警吗
会 如果主机的负载值超过告警阈值,就会发送告警通知