目录
前言:
Object:
toString:
equals:
练习题:
clone:
浅克隆(Object 中的 clone)、深克隆
区别:
Objects 类:
之前学继承的时候我们说过:
子类的无参构造会默认访问父类的无参构造,那为什么默认访问的不是有参构造?
class Student extends Person {
public Student(){
super();//默认访问
};
public STudent(String name,int age){
super(name,age);//手动访问
}
解释是:
我们知道 有参构造的前提是 有成员变量
而作为顶级父类,无法抽取子类中共性的成员变量,因此没有成员变量。从而也就没有 有参构造(只有无参构造方法)。
- Object 是 Java 中的顶级父类。所有的类都直接或间接的继承于 Object 类。
- Object 类中的方法可以被所有子类访问,所以我们要学习 Object 类和其中的方法
Object 类中的方法:
一共 11 个成员方法,这里先介绍 3 个。如下:
:返回对象的字符串表达形式
源码:
这个方法的默认实现返回的是一个表示对象内存地址(十六进制)的字符串
使用:
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
}
比较两个对象是否相等
先看代码:
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
对象克隆:把 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
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 一旦接口被实现,就表示当前类的对象可以被克隆
总结一下上面的结论:
克隆对象:
细节:
方法在底层会帮我们创建一个对象,并把原对象中的数据拷贝过去。
书写过程:
- 在子类中重写 Object 中的 clone()方法,
- 让 子类实现 Cloneable 接口
- 对象并调用 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 ]
我们知道
基本数据类型:存储的是真实的值
引用数据类型:存储的是另一个空间的地址值
浅拷贝、浅克隆:
基本数据类型 拷贝数据值
引用数据类型 拷贝地址值
注意点:
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);
控制台:
对象工具类:提供了一些操作对象的方法
方法一演示:
先创建一个 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