Java中的Object和 Objects(对象工具类)

发布时间:2024年01月18日

目录

前言:

Object:

toString:

equals:

练习题:

clone:

浅克隆(Object 中的 clone)、深克隆

区别:

Objects 类:


前言:

之前学继承的时候我们说过:

子类的无参构造会默认访问父类的无参构造,那为什么默认访问的不是有参构造?


class Student extends Person {

    public Student(){
        super();//默认访问
    };

    public STudent(String name,int age){
        super(name,age);//手动访问
    }

解释是:

我们知道 有参构造的前提是 有成员变量

而作为顶级父类,无法抽取子类中共性的成员变量,因此没有成员变量。从而也就没有 有参构造(只有无参构造方法)。


Object:

  • Object 是 Java 中的顶级父类。所有的类都直接或间接的继承于 Object 类。
  • Object 类中的方法可以被所有子类访问,所以我们要学习 Object 类和其中的方法

Object 类中的方法:

一共 11 个成员方法,这里先介绍 3 个。如下:


toString:

:返回对象的字符串表达形式

源码:

这个方法的默认实现返回的是一个表示对象内存地址(十六进制)的字符串

使用:

Object obj = new Object();
String s = obj.toString();
System.out.println(s);//字符串:java.lang.Object@1540e19d  包名+@+地址

//Student默认继承于Object,可使用其中的方法
Student stu=new Student("张三",23);
String s1=stu.toString();
System.out.println(s1);//字符串:com.lt.test.Student@677327b6 包名+@+地址


// Student类
public class Student {
    String name;
    int age;
  // 构造方法和set、get
}

继上:

在以前我们常常直接打印过对象名如:System.out.println(stu);

控制台会输出://com.lt.test.Student@677327b6

//问题:为什么输出的内容和toString的地址值的形式完全一样呢?

//System:类名

//out:静态变量

//System.out:获取打印的对象

//println():方法

//参数:要打印的内容

看看 println 的源码:

逻辑:

    • 参数进去后被转成 String 类型的字符串
    • newLine()负责换行

接着去看 valueOf 的源码

逻辑:

    • 如果obj是null,那么函数会返回字符串"null"。
    • 如果obj不是null,则函数会调用该对象的toString()方法并将返回值作为结果返回。(每个Java对象都有默认的toString()方法。)


//得出核心逻辑

/* 当我们打印一个对象的时候,底层会调用对象的toString 方法,把对象转为字符串

然后打印在控制台,打印完换行处理。*/

因为 toString返回的都是地址值,但是意义不大,我想要返回属性,这时重写toString即可,如:

public class Student {
    private String name;
    private int age;

    // 构造函数、getter和setter...

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

刚才我们解释完了 打印的底层逻辑会调用了 toString 方法

因此重写toString方法后 打印对象 名 可直接打印属性

main{
Student stu=new Student("张三",18);
System.ou.println(stu)//张三, 18
}


equals:

比较两个对象是否相等

先看代码:

Student stu1=new Student();
Student stu2=new Student();
System.out.println(stu1.equals(stu2));//false

为什么是 false?

不是说 equals 比较的是属性吗,这里 stu1 和 stu2 的 name 都为 null,age 都为 0 啊,不是 true吗?

现在看 equals 源码:

如果没有对 equals 方法进行重写,使用的是 Object内的 equals 方法。也就是“==”来比较,比较的是引用类型的对象的地址值。而且 stu1,stu2 都是 new 出来的,并且在堆中的不同位置,所以结果一定是 false。

在 Student 类中重写 equals 试试(alt+insert ->equals,hashcode 快捷键

public class Student {
    String name;
    int age;

   //构造方法、set和get..

    
    //重写equals
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
}

这时我们再来比较一下(现在比较的是属性

Student stu1=new Student();
Student stu2=new Student();
System.out.println(stu1.equals(stu2));//true   ---name都为null,age都为0
Student stu1=new Student("张三",23);
Student stu2=new Student("李四",24);
System.out.println(stu1.equals(stu2));//false 

练习题:

String s="123";
StringBuilder sb=new StringBuilder("123");
//试问输出结果:true或false
 System.out.println(s.equals(sb));

 System.out.println(sb.equals(s));

答案都是 false;

解:

第一个:

//是 s调用的equals

//所以要看String类中的equals方法

先看 String 中的 equals:

//代码逻辑:

//先判断参数类型是否是 String 类型(这里 sb 明显不是)

//若是字符串,在比较内部属性,

//但若不是字符串,直接返回false


第二个

//是sb调用的equals

//所以要看StringBuilder类中的equals方法

//而StringBuilder方法没有重写equals方法

//默认继承Object类中的equals

//所以用的是==号比较的地址值

明显 s 和 sb 的地址值不同

因此 false


clone:

对象克隆:把 A 的对象得属性值完全拷贝给 B 对象,也叫对象拷贝,对象复制

我们先得创建一个对象

public class User {
    //属性
    private int id;
    private String username;
    private String password;
    private String path;
    private int[]date;

    //构造方法+set和get方法....

    //toString方法
    public String toString() {
        return "角色编号为:"+id+",用户名:"+username+",密码"+password+",游戏图片:"+path+",进度:"+arrToString();
    }

    //数组拼接方法
    public String arrToString(){
        StringJoiner sj=new StringJoiner(",","[","]");//间隔符,前缀,后缀
        for (int i = 0; i <date.length ; i++) {
            sj.add(date[i]+" ");//拼接
        }
        return sj.toString();
    }
}
public class Test01 {
    public static void main(String[] args) throws CloneNotSupportedException {
    	//要克隆对象,我们必须先创建一个对象
        
        int[]date={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};//数组
        User u1=new User(1,"张三","qwe","girl1",date);创建一个User对象

		u1.clone();  //报错

我们用 u1 直接去调用 clone 方法会报错

这是为什么?

...

解决:

要使子类对象能够调用clone()方法,不仅需要子类实现Cloneable接口,还需要在子类中显式地重写clone()方法(alt+insert 快捷键)。

如下,在 User 类中重写 clone()方法 ,并实现 Cloneable:

public class User implements Cloneable{

    ......
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Cloneable 接口是啥?

可见接口内什么都没有,

这时要知道:如果一个接口内没有抽象方法

则该接口表示的是一个标志型接口

Cloneable 一旦接口被实现,就表示当前类的对象可以被克隆


总结一下上面的结论:

克隆对象:

细节:

方法在底层会帮我们创建一个对象,并把原对象中的数据拷贝过去。

书写过程:

  1. 在子类中重写 Object 中的 clone()方法,
  2. 让 子类实现 Cloneable 接口
  3. 对象并调用 clone 就可以了。
User u2=(User)u1.clone();//将对象u1拷贝给对象u2
System.out.println(u1);//角色编号为:1,用户名:张三,密码qwe,游戏图片:girl1,进度:[1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,0 ]
System.out.println(u2);//角色编号为:1,用户名:张三,密码qwe,游戏图片:girl1,进度:[1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,0 ]

浅克隆(Object 中的 clone)、深克隆

我们知道

基本数据类型:存储的是真实的值

引用数据类型:存储的是另一个空间的地址值


浅拷贝、浅克隆:
基本数据类型 拷贝数据值

引用数据类型 拷贝地址值

注意点:

  • 基本数据类型一方的改变不会影响另一方
  • 引用数据类型一方的改变会影响另一方---访问的是同一个地址值
User u2=(User)u1.clone();//将对象u1拷贝给对象u2
u1.setId(2);//u1改变基本数据类型
u1.getDate()[1]=100;//改变应用数据类型
System.out.println(u1);
System.out.println(u2);

控制台:

角色编号为:2,用户名:张三,密码qwe,游戏图片:girl1,进度:[1 ,100 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,0 ]

角色编号为:1,用户名:张三,密码qwe,游戏图片:girl1,进度:[1 ,100 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,0 ]


深拷贝、深克隆

基本数据类型:拷贝数据

String s="aa":拷贝后会在串池中复用

其他引用数据类型:重新创建一个新的引用数据类型对象,再拷贝值

如图:

区别:


我们知道 Object 中的 clone()方法实现的是浅克隆

那我们如何实现深克隆:

有两个方法:

1:在 User 中为重写的 equals 方法修改为下面逻辑:

 @Override
    protected Object clone() throws CloneNotSupportedException {
    
        //在此案例中浅克隆和深克隆的区别主要是数组,所以要单独针对数组实现深克隆
        int []date =this.date;//获取被克隆对象中的数组

        //创建新数组
        int[]newDate=new int[date.length];
        //拷贝数组中的数据
        for (int i = 0; i < date.length; i++) {
            newDate[i]=date[i];
        }
        //调用Object中的方法克隆对象
        User u=(User) super.clone();
        //换掉数组地址值即可
        u.date=newDate;
        return u;//返回拷贝后的对象
    }

这时数组就独立了,不会受到原数组的影响

User u2=(User)u1.clone();
u1.setId(2);
u1.getDate()[1]=100;
System.out.println(u1);
System.out.println(u2);

控制台:


方法 2:

工具类:

1.

2.导入项目

//用第三方工具实现深克隆
//先创建对象
Gson gson=new Gson();
//先把对象u1变成一个字符串;
String s = gson.toJson(u1);
//再把字符串变成对象即可,该对象就是深克隆后得到的对象
User u3 = gson.fromJson(s, User.class);//将字符串s转为User类型的对象
date[1]=100;//克隆之后,改变数组值
System.out.println(u1);
System.out.println(u3);

控制台:



Objects 类:

对象工具类:提供了一些操作对象的方法

方法一演示:

先创建一个 JavaBean

public class Person {
    String name;

   //构造方法,set和get


    //重写了equals方法,此时比较的是属性
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person test01 = (Person) o;
        return Objects.equals(name, test01.name);
    }
}
public class Test02 {
    public static void main(String[] args) {
        Person p1=null;
        Person p2=new Person("张三");
       	Person p3=new Person("张三")

        System.out.println(p1.equals(p2));//空指针异常
    	
    	//区别
     	//先非空判断
        System.out.println(Objects.equals(p1,p2));//false---p1为空

        System.out.println(Objects.equals(p2,p3));//true---属性相同
    }
}

Objects 下 equals 的逻辑:

方法二三:

Person p4=new Person();
Person p5=null;
System.out.println(Objects.isNull(p4));//false
System.out.println(Objects.isNull(p5));//ture

System.out.println(Objects.nonNull(p4));//ture
System.out.println(Objects.nonNull(p5));//false


文章来源:https://blog.csdn.net/2301_79947226/article/details/135650784
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。