《JAVA SE》面向对象编程(下篇)

男娘i 2023-09-29 14:23 73阅读 0赞

前言

上一篇讲到了接口,接下来将补充一下常用的接口以及Object类的初识,链接如下:
《JAVA SE》面向对象编程(中篇)

《JAVA SE》面向对象编程(上篇)


一、 Object类初识

✦JAVA中的万物之母 : Object类
✦全名称:包名.类名
✦java.lang.Object

  1. Object类是Jvav中所有类的默认父类,无须使用extends来定义。
  2. class声明的类都有一个父类,Object类。
  3. 因为Object类是所有类的父类,使用Object引用来接受所有的类型,参数最高统一化
    eg:
    在这里插入图片描述
    在这里插入图片描述

注:Java中所有类型都可以发生向上转型变成Object类型

  1. Object类中所有的方法都被子类继承下来了~~
  2. 之所以System.out.println(任意的引用类型) = 》是因为里面默认都会调用该类型的toString()方法,因为Object类存在toString();

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
如果想输出当前类的属性值,我们就得覆写toString()方法!!!
在这里插入图片描述

  1. JAVA中引用数据类型之间的相等比较使用equals方法!!不能用“==”,比较的是地址。

在这里插入图片描述

自己覆写equals方法,完成对自己定义的Student类比较属性的方法。

  1. @Override
  2. public boolean equals(Object obj) {
  3. // 1.若当前对象就是obj
  4. if (this == obj) {
  5. return true;
  6. }
  7. // 2.此时当前对象和obj指向的对象确实不是一个地址
  8. // 若此时obj指向的对象和Student压根没关系,obj指向一个Dog对象,没有可比性,直接返回false
  9. if (obj instanceof Student) {
  10. // 3.obj这个引用指向的对象确实是Student类的对象且和当前对象不是一个对象
  11. // Object obj = new Student();
  12. Student stu = (Student) obj;
  13. // 所有引用类型比较属性值一定要用equals方法,"=="比的是地址!!!!!!!!
  14. return this.score == stu.score && this.name.equals(stu.name);
  15. }
  16. return false;
  17. }
  1. Object不仅是所有类的父类,JDK对Object类做了扩展。Object类可以接受所有引用数据类型的对象(接口、数组、类)

因此在Java中,若一个方法参数或者返回值是Object类型,说明该参数或者返回值可以是任意引用数据类型(数组、类、接口)

此时除了8大基本类型没法用Object类来接收之外,所有类型都能使用Object类来接收

包装类应运而生~

二、接口使用实例

接口优先原则,当一个场景既可以使用抽象类也可以使用抽象类定义时,优先考虑使用接口(更灵活),以下将介绍两个JDK内置的常用接口。

2.1 Comparable 接口

接下来将用一个例子介绍java.lang.Comparable接口:

给对象数组排序

给定一个学生类

  1. class Student {
  2. private String name;
  3. private int score;
  4. public Student(String name, int score) {
  5. this.name = name;
  6. this.score = score;
  7. }
  8. @Override
  9. public String toString() {
  10. return "[" + this.name + ":" + this.score + "]";
  11. }
  12. }

再给定一个学生对象数组, 对这个对象数组中的元素进行排序(按分数降序).

  1. Student[] students = new Student[] {
  2. new Student("张三", 95),
  3. new Student("李四", 96),
  4. new Student("王五", 97),
  5. new Student("赵六", 92),
  6. }

按照我们之前的理解, 数组工具类我们有一个现成的 sort 方法, 能否直接使用这个方法呢?

  1. Arrays.sort(students);
  2. System.out.println(Arrays.toString(students));
  3. // 运行出错, 抛出异常.
  4. Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to
  5. java.lang.Comparable

仔细思考, 不难发现, 和普通的整数不一样, 两个整数是可以直接比较的, 大小关系明确. 而两个学生对象的大小关系怎么确定? 需要我们额外指定

让我们的 Student 类实现 Comparable 接口, 并实现其中的 compareTo 方法:

  1. class Student implements Comparable {
  2. private String name;
  3. private int score;
  4. public Student(String name, int score) {
  5. this.name = name;
  6. this.score = score;
  7. }
  8. @Override
  9. public String toString() {
  10. return "[" + this.name + ":" + this.score + "]";
  11. }
  12. @Override
  13. public int compareTo(Object o) {
  14. if(this == 0){
  15. return 0; //返回0表示相等
  16. }
  17. if(o instanceof Student){
  18. //当前传入的o就是Student类型的引用,向下转型还原为Student
  19. //要比较Student对象的大小关系,就要用到Student的独有属性,向下转型
  20. Student stu = (Student)o;
  21. return this.score - stu.score;
  22. }
  23. //若传入不是学生类,则抛出异常
  24. throw new IllegalArgumentException("不是学生类型,无法比较!")
  25. }

在 sort 方法中会自动调用 compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象.

然后比较当前对象和参数对象的大小关系(按分数来算).

✦如果当前对象应排在参数对象之前, 返回小于 0 的数字;

✦如果当前对象应排在参数对象之后, 返回大于 0 的数字;

✦如果当前对象和参数对象不分先后, 返回 0;

再次执行程序, 结果就符合预期了

  1. // 执行结果
  2. [[王五:97], [李四:96], [张三:95], [赵六:92]]

注意事项:
对于 sort 方法来说, 需要传入的数组的每个对象都是 “可比较” 的, 需要具备 compareTo 这样的能力. 通过重写 compareTo 方法的方式, 就可以定义比较规则。

为了进一步加深对接口的理解, 我们可以尝试自己实现一个 sort 方法来完成刚才的排序过程(使用冒泡排序):(其实Arrays.sort()内部也是和下面代码类似的,只是被封装了)

  1. public static void sort(Comparable[] array) {
  2. for (int bound = 0; bound < array.length; bound++) {
  3. for (int cur = array.length - 1; cur > bound; cur--) {
  4. if (array[cur - 1].compareTo(array[cur]) > 0) {
  5. // 说明顺序不符合要求, 交换两个变量的位置
  6. Comparable tmp = array[cur - 1];
  7. array[cur - 1] = array[cur];
  8. array[cur] = tmp;
  9. }
  10. }
  11. }
  12. }

再次执行代码

  1. sort(students);
  2. System.out.println(Arrays.toString(students));
  3. // 执行结果
  4. [[王五:97], [李四:96], [张三:95], [赵六:92]]

2.2 Clonable 接口

Java 中内置了一些很有用的接口, java.lang.Clonable 就是其中之一.

在这里插入图片描述

类似于Clonable接口,把这种接口称之为“标记接口”,这种接口本身内部没有任何的抽象方法,只有打上这个标记的子类才拥有克隆能力!

在这里插入图片描述

JVM在运行时会检查所有实现了Cloneable接口的子类,赋予其克隆的能力。

注意:clone()是Object类提供的方法。

Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException 异常.

  1. class Animal implements Cloneable {
  2. private String name;
  3. @Override
  4. public Animal clone() {
  5. Animal o = null;
  6. try {
  7. o = (Animal)super.clone();
  8. } catch (CloneNotSupportedException e) {
  9. e.printStackTrace();
  10. }
  11. return o;
  12. }
  13. }
  14. public class Test {
  15. public static void main(String[] args) {
  16. Animal animal = new Animal();
  17. Animal animal2 = animal.clone();
  18. System.out.println(animal == animal2);
  19. }
  20. }
  21. // 输出结果
  22. // false

2.3 深拷贝VS浅拷贝

Cloneable 拷贝出的对象是一份 “浅拷贝”

观察以下代码:

  1. public class Test {
  2. static class A implements Cloneable {
  3. public int num = 0;
  4. @Override
  5. public A clone() throws CloneNotSupportedException {
  6. return (A)super.clone();
  7. }
  8. }
  9. static class B implements Cloneable {
  10. public A a = new A();
  11. @Override
  12. public B clone() throws CloneNotSupportedException {
  13. return (B)super.clone();
  14. }
  15. }
  16. public static void main(String[] args) throws CloneNotSupportedException {
  17. B b = new B();
  18. B b2 = b.clone();
  19. b.a.num = 10;
  20. System.out.println(b2.a.num);
  21. }
  22. }
  23. // 执行结果
  24. 10

通过 clone 拷贝出的 b 对象只是拷贝了 b 自身, 而没有拷贝内部包含的 a 对象. 此时 b 和 b2 中包含的 a 引用仍然是指向同一个对象. 此时修改一边, 另一边也会发生改变。

Java中实现深拷贝的方法有两种:

  1. 递归使用clone()方法
  2. 序列化(json字符串)

总结

抽象类和接口都是 Java 中多态的常见使用方式. 都需要重点掌握. 同时又要认清两者的区别。
核心区别:
抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.

发表评论

表情:
评论列表 (有 0 条评论,73人围观)

还没有评论,来说两句吧...

相关阅读

    相关 java编程面向对象

    面向对象是指,我们考虑问题时,把任何东西看做是对象,以对象为单位,考虑它的属性及方法。 计算机要描述的是现实世界。光有基本数据类型,是不能满足我们的需要的。在这个大千世界里,