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

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

一.抽象类

抽象类特点:

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:如果文章有什么错误,请各位大佬指点,同时期待你们的点赞和评论!!!

目录
相关文章
|
6月前
|
设计模式 Java 编译器
面向对象编程中的继承与多态:深入理解父类引用指向子类实例
面向对象编程中的继承与多态:深入理解父类引用指向子类实例
用原型链的方式写一个类和子类
用原型链的方式写一个类和子类
34 0
|
1月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
51 1
|
6月前
|
存储 编译器 程序员
【C++】类和对象①(什么是面向对象 | 类的定义 | 类的访问限定符及封装 | 类的作用域和实例化 | 类对象的存储方式 | this指针)
【C++】类和对象①(什么是面向对象 | 类的定义 | 类的访问限定符及封装 | 类的作用域和实例化 | 类对象的存储方式 | this指针)
|
设计模式 Java 程序员
谈谈你对深克隆和浅克隆的理解
一个工作了5年的程序员私信我说,前几天去阿里面试被问到这样一个这样的面试题,说谈谈你对深克隆和浅克隆的理解。他回答说深克隆是克隆值,浅克隆是克隆引用,当时他只说了这样一句话,回答完以后,他看到面试官的表情很诧异,面试也没有继续深入追问了。小伙伴们,如果是你来回答,你也会这样回回答吗?
82 0
原型链继承: 原理:将父类的实例作为子类的原型
原型链继承: 原理:将父类的实例作为子类的原型
|
存储
【克隆方法+深浅拷贝】
【克隆方法+深浅拷贝】
67 0
|
设计模式 Java API
一文读懂深克隆与浅克隆的关系
在Java提供的API中,不需要手动创建抽象原型接口,因为Java已经内置了Cloneable抽象原型接口,自定义的类型只需实现该接口并重写Object.clone()方法即可完成本类的复制。 通过查看JDK的源码可以发现,其实Cloneable是一个空接口。Java之所以提供Cloneable接口,只是为了在运行时通知Java虚拟机可以安全地在该类上使用clone()方法。而如果该类没有实现 Cloneable接口,则调用clone()方法会抛出 CloneNotSupportedException异常。 一般情况下,如果使用clone()方法,则需满足以下条件。
67 0
一文彻底搞懂父类引用指向子类对象问题
一文彻底搞懂父类引用指向子类对象问题
216 1
|
存储 C++ 开发者
你还不进来看看C++类与对象【7】 —— 动态多态底层原理剖析&&(纯)虚析构解决父类指针不能释放子类属性问题嘛
你还不进来看看C++类与对象【7】 —— 动态多态底层原理剖析&&(纯)虚析构解决父类指针不能释放子类属性问题嘛
136 0
你还不进来看看C++类与对象【7】 —— 动态多态底层原理剖析&&(纯)虚析构解决父类指针不能释放子类属性问题嘛