Java中的Object类、JDK中的常用接口、深浅拷贝

目录

Object类

1. Object类是Java中所有类的默认父类,无须使用extends来定义。class声明的类都有一个父类,Object类。

2. Object类中的所有方法子类全都可以继承

Java中引用数据类型之间的相等比较使用equlas方法。不能使用"==",其比较的是地址。

3. Object不仅是所有类(class)的父类,JDK对Object类做了扩展。

JDK中的常用接口

1. java.lang.Comparable;比较接口

整体代码

2. java.lang.Cloneable; 克隆接口

整体代码

问题:clone方法产生的对象是否调用构造方法?

深浅拷贝(了解概念)

浅拷贝

深拷贝


Object类

Java中所有类的共同父类——Object类

全名称:包名.类名——java.lang.Object;

1. Object类是Java中所有类的默认父类,无须使用extends来定义。class声明的类都有一个父类,Object类。

因为Object类是所有类的父类,使用Object引用来接所有的类型,参数最高统—化。

package object_test;
import animal.Dog;
import preson.Person;

public class ObjectTest {
    public static void main(String[] args) {
        // object是所有类(包括JDK本身的类、自定义的类)都是object的子类
        Object obj1 = "abc";
        Object obj2 = new Dog();
        Object obj3 = new Person();
    }
}

Java中所有类都是Object的子类,故所有类型都可以发生向上转型变为Object类型。

2. Object类中的所有方法子类全都可以继承

之所以System.out.println(任意的引用类型),就能输出对应的值,是因为默认都调用该类型的toString()方法,Object类存在toString()方法。

package object_test;
import supertest.China;

public class ObjectTest {
    public static void main(String[] args) {
        Dog dog = new Dog();
        System.out.println(dog);
    }
}
//输出结果
animal.Dog@1b6d3586

Object类存在toString()方法

a82ea49ebe18446f85b258babe63ecfc.png

如果想输出当前类中的属性值我们就"覆写"toString方法。

package animal;
public class Dog extends Animal {
    @Override
    public String toString() {    //复写Dog类的toString()
        return "Dog类的toString()方法";
    }
}

package object_test;
import animal.Dog;
import preson.Person;
public class ObjectTest {
    public static void main(String[] args) {
        Object obj2 = new Dog();
        System.out.println(obj2);
    }
}
//输出结果
Dog类的toString()方法

Java中引用数据类型之间的相等比较使用equlas方法。不能使用"==",其比较的是地址。

package object_test;
public class ObjectTest {
    public static void main(String[] args) {
        Student stu1 = new Student("张三", 88);
        Student stu2 = new Student("李四", 90);
        Student stu3 = new Student("李四", 90);
        // "==" 比较的是数值,对于引用类型来讲,保存的内容就是一个地址
        // 比地址
        //虽然stu2和stu3属性相等,输出依然false
        System.out.println(stu1 == stu2);
        System.out.println(stu2 == stu3); 
    }
}

class Student {
    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}
//输出结果
false
false

Object默认的equals()方法

eeffb72fad88480b97f546c12409a2ec.png


此时若需要比较两个对象的属性值是否相同,就需要按照比较规则覆写equals方法。

4c9c6547ac6c4a9da89897e414908c19.png

public class ObjectTest {
    public static void main(String[] args) {
        Student stu1 = new Student("张三", 88);
        Student stu2 = new Student("李四", 90);
        Student stu3 = new Student("李四", 90);   
        // 此时若需要比较两个对象的属性值是否相同,就需要按照比较规则覆写equals方法
        System.out.println(stu2.equals(stu3));
    }
}

class Student {
    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public boolean equals(Object obj) {    //覆写equals()方法,实现比较对象属性
        // 1.若当前对象就是obj
        if (this == obj) {
            return true;
        }
        // 2.此时当前对象和obj指向的对象确实不是一个地址
        // 若此时obj指向的对象和Student压根没关系。
        // 例如:obj指向一个Dog对象,没有可比性,直接返回false
        if (obj instanceof Student) {
            // 3.obj这个引用指向的对象确实是Student类的对象且和当前对象不是一个对象
            // Object obj = new Student();
            Student stu = (Student) obj;
            return this.score == stu.score && this.name.equals(stu.name);
        }
        return false;
    }
}
//输出结果
true

此处向下转型的目的:

obj是Object类的对象,但它实际指向了一个Student类的对象。我们需要比较的是Student类中不同对象的name与score,但Object类中没有name和score属性,故不能用obj比较。需要将obj向下转型还原成Student类型。

Student stu = (Student) obj;

用到向下转型的时机:

当一个父类引用实际上指向了一个子类实例时,我们需要调用子类独有的属性或方法时才会用到向下转型。
例如:Person per = new China);
per是指向China对象的Person类型,如果需要用到China独有的属性或方法,就需要把per还原为China类型。

3. Object不仅是所有类(class)的父类,JDK对Object类做了扩展。

Object类可以接收所有引用数据类型的对象(接口,数组,类)

package object_test;
interface ITest{
    void test();
}
class TestImpl implements ITest{
    @Override
    public void test() {
        System.out.println("TestImpl");
    }
}
public class ObjectTest {
    public static void main(String[] args) {
        //接口引用
        ITest i1 = new TestImpl();
        //整型数组引用
        int[] date = new int[10];
        //只要是引用类型,都可以用Object接收
        Object obj1 = i1;
        Object obj2 = date;
}

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


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

JDK中的常用接口

接口优先原则

当一个场景既可以使用抽象类也可以使用接口定义时,优先考虑使用接口(更灵活)。

1. java.lang.Comparable;比较接口

当一个类实现了Comparable接口,表示该类具备了可比较的能力。

package object_test;
import java.util.Arrays;
public class ComparableTest {
    public static void main(String[] args) {
        //date整型数组,创建了3个整型变量
        int[] date = new int[]{
                8, 3, 5
        };
        Arrays.sort(date);
        System.out.println(Arrays.toString(date));
        //对象数组
        //per是Person类型的数组,创建了三个Person类型的变量
        Person[] per = new Person[]{
                new Person("张三", 30),
                new Person("李四", 18),
                new Person("王五", 20)
        };
        Arrays.sort(per); //报错
        System.out.println(Arrays.toString(per));
    }
}

class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

8e8a383a4196466d8713751a402ecc08.png

由于Person这个类型是自定义类型,对于编译器来说,不像int这样大小关系一目了然。到底哪个Person对象大,哪个Person对象小,编译器无从得知。
要让Person这个类型具备可比较的能力,即让JDK知道Person对象"谁大谁小",就需要让Person类实现Compareable接口,覆写抽象方法compareTo。

覆写抽象方法compareTo

660f75a56cfa4cc890bf3efeea5b9563.png

此处int返回值

>0 表示当前对象this > 传入对象o
=0 表示当前对象this = 传入对象o
<0 表示当前对象this < 传入对象o 

    @Override
    public int compareTo(Object o) {
        if (this == o) {
            return 0;
        }
        if (o instanceof Person) {
            //当前传入的o就是Person类型的引用,向下转型还原为Person
            //要比较Person对象的大小关系,就要用到Person独有的属性,向下转型
            Person per = (Person) o;
            //此时根据年龄大小比较
            return this.age - per.age;
        }
        //报错,抛出异常
        throw new IllegalArgumentException("不是Person类型,无法比较!");
    }

Arrays.sort()方法的内部实际上就是根据Compable接口的compareTo方法的返回值进行比较。


覆写sort()方法,升序排序对象数组

    public static void sort(Comparable[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j].compareTo(arr[j + 1]) > 0) {
                    //前一个大于后一个,交换j、j+1
                    Comparable tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                }
            }
        }
    }

arr[j].compareTo(arr[j +1]) > 0; 我就认为arr[j] ">" arr[j + 1]
返回值>0表示当前对象(this)"大于"传入对象(o)。

整体代码

package object_test;
import java.util.Arrays;
public class ComparableTest {
    public static void sort(Comparable[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j].compareTo(arr[j + 1]) > 0) {
                    //前一个大于后一个,交换j、j+1
                    Comparable tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                }
            }
        }
    }

    public static void main(String[] args) {
        //date整型数组,创建了3个整型变量
        int[] date = new int[]{
                8, 3, 5
        };
        Arrays.sort(date);
        System.out.println(Arrays.toString(date));
        //对象数组
        //per是Person类型的数组,创建了三个Person类型的变量
        Person[] per = new Person[]{
                new Person("张三", 30),
                new Person("李四", 18),
                new Person("王五", 20)
        };
        sort(per);
        System.out.println(Arrays.toString(per));
    }
}

class Person implements Comparable {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        if (this == o) {
            return 0;
        }
        if (o instanceof Person) {
            //当前传入的o就是Person类型的引用,向下转型还原为Person
            //要比较Person对象的大小关系,就要用到Person独有的属性,向下转型
            Person per = (Person) o;
            //此时根据年龄大小比较
            return this.age - per.age;
        }
        //报错,抛出异常
        throw new IllegalArgumentException("不是Person类型,无法比较!");
    }
}

77f89295d0344ab3b8969c6b9530eaf4.png

2. java.lang.Cloneable; 克隆接口

原对象和新产生对象确实是两个独立的对象,新产生的对象是通过原对象"拷贝"而来的,属性值和原对象完全一致。

要让一个类具备可复制的能力,实现Cloneable接口,覆写clone方法 。

package object_test;
public class CloneableTest {
    public static void main(String[] args) {

    }
}

class Animal implements Cloneable{
    String name;

    public Animal(String name) {
        this.name = name;
    }
}

此时普通子类Animal并未覆写父类接口Cloneable的抽象方法,原因是父类Cloneable中就没有抽象方法。

76cd9d59e51942ff94ec403f7caebff7.png

类似Cloneable接口,称之为"标记"接口,这个接口本身内部没有任何抽象方法,只有打上这个标记的子类才具备克隆的能力。
JVM在运行时会检查所有实现了Cloneable接口的子类,赋予其克隆的能力。clone方法是Object提供的方法。

覆写clone方法 

    public Animal Clone(){
        Animal newAnimal = null;
        try {
            newAnimal = (Animal) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        return newAnimal;
    }

覆写Object类的clone()方法,返回值为Animal类,最后返回Animal类型的newAniaml。避免类型转换。

整体代码

package object_test;
public class CloneableTest {
    public static void main(String[] args) {
        Animal animal1 = new Animal("test");
        // animal2是通过animal1克隆复制来的,确实是两个独立的对象,属性值完全相同
        Animal animal2 = animal1.Clone();
        System.out.println(animal1 == animal2);
        System.out.println(animal2.name);
    }
}

class Animal implements Cloneable {
    String name;

    public Animal(String name) {
        this.name = name;
    }

    public Animal Clone() {
        Animal newAnimal = null;
        try {
            newAnimal = (Animal) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        return newAnimal;
    }
}

241d66ab54a94a35a460af9465cd2755.png

false说明animal1与animal2地址不同;输出test表示,animal1的name属性成功克隆给animal2的name属性了。


若此时,新创建animal3,Animal animal3 = new Animal("test");
animal2和animal3到底有何不—样?
animal1和animal3彼此之间没有任何关系,只是产生animal3的时候属性值恰好等于animal1而已。
animal2的产生是依赖于animal1而产生,animal3的产生和animal1没任何关系。

问题:clone方法产生的对象是否调用构造方法?

答:不会。 

在Java中产生—个新对象有两种方式

1. 最常见的通过构造方法产生对象
new 类();——>当有new关键字,就在堆上开辟该类相应属性的空间,给属性赋默认值。
2. 通过clone()方法产生对象

调用clone()方法时,JVM会开辟与原对象内存大小完全相同的新空间,并将对象中属性的值从原对象中复制—份。(不推荐,Java之父都不推荐使用这种方式产生对象)

深浅拷贝(了解概念)

浅拷贝

浅拷贝,最外层对象确实是由原对象的clone方法产生的新对象,属性值与原对象保持一致,原对象和新对象的内部若包含其他类的引用,这些引用指向的对象是相同的,并没有产生新对象

94d2a6cb6779438085df45c3a25e9b51.png

克隆的对象和原对象确实是两个独立的对象——b1和b2。
对象b1的内部若包含了其他类的引用a,克隆后的b2包含的其他类的引用并没有产生新的对象——>浅拷贝,b1.a和b2.a指向相同的A对象。

浅拷贝就是克隆的对象和原对象内部包含的其他对象是一个东西。

深拷贝

深拷贝,最外层对象确实是由原对象的clone方法产生的新对象,属性值与原对象保持一致,原对象和新对象的内部若包含其他类的引用,这些引用指向的对象也是新对象

3d8b35fbca8744a1a35ea4ed92c6f702.png

b2克隆对象内部包含的其他类的引用也产生了新的对象——>深拷贝,b1.a和b2.a指向不同的a对象。

深拷贝就是克隆对象和原对象内部包含的其他对象也是克隆出来的。

Java中深拷贝的实现方式:

1.递归使用clone方法。

2.序列化。(json字符串)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瘦皮猴117

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值