目录
Object类
查看API文档,可以看到API文档中关于Object类的定义如下:
Object类所在包是java.lang包。Object 是类层次结构的根,每个类都可以将 Object 作为超类。所有类都直接或者间接的继承自该类。
查看API文档可以看到,在Object类中提供了一个无参构造方法,如下所示:
但是一般情况下很少去主动的创建Object类的对象,调用其对应的方法。更多的是创建Object类的某个子类对象,然后通过子类对象调用Object类中的方法。
在定义的类中,无参构造和有参构造的函数体中第一行默认为super(),默认会调用父类的无参构造,因为最顶层的父类Object中只有无参构造,没有 有参构造。
常见方法
public String toString()
//返回该对象的字符串表示形式(可以看做是对象的内存地址值)
public boolean equals(Object obj)
//比较两个对象地址值是否相等;true表示相同,false表示不相同
protected Object clone()
//对象克隆
案例1:toString方法
-
创建一个学生类,提供两个成员变量(name , age);并且提供对应的无参构造方法和有参构造方法以及get/set方法
-
创建一个测试类(ObjectDemo01),在测试类的main方法中去创建学生对象,然后调用该对象的toString方法获取该对象的字符串表现形式,并将结果进行输出
Student类
public class Student {
private String name ; // 姓名
private String age ; // 年龄
// 无参构造方法和有参构造方法以及get和set方法略
...
}
ObjectDemo01测试类
public class ObjectDemo01 {
public static void main(String[] args) {
// 创建学生对象
Student s1 = new Student("itheima" , "14") ;
// 调用toString方法获取s1对象的字符串表现形式
String result1 = s1.toString();
// 输出结果
System.out.println("s1对象的字符串表现形式为:" + result1);
//s1对象的字符串表现形式为:com.itheima.api.system.demo04.Student@3f3afe78
}
}
为什么控制台输出的结果为:com.itheima.api.system.demo04.Student@3f3afe78;
包名.类名@地址值经加密后的值
查看一下Object类中toString方法的源码,如下所示:
public String toString() { // Object类中toString方法的源码定义
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
其中getClass().getName()对应的结果就是:com.itheima.api.system.demo04.Student;Integer.toHexString(hashCode())对应的结果就是3f3afe78。
常常将"com.itheima.api.system.demo04.Student@3f3afe78"这一部分称之为对象的内存地址值。但是一般情况下获取对象的内存地址值没有太大的意义。获取对象的成员变量的字符串拼接形式才算有意义,怎么实现呢?此时就需要在Student类中重写Object的toString方法。可以通过idea开发工具进行实现,具体步骤如下所示:
1.在空白处使用快捷键:alt + insert。此时会弹出如下的对话框
2.选择toString,此时会弹出如下的对话框
同时选择name和age属性,点击OK。此时就会完成toString方法的重写,代码如下所示:
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
这段代码就是把Student类中的成员变量进行了字符串的拼接。重写完毕以后,再次运行程序,控制台输出结果如下所示:
s1对象的字符串表现形式为:Student{name='itheima', age='14'}
此时就可以清楚的查看Student的成员变量值,因此重写toString方法的意义就是以良好的格式,更方便的展示对象中的属性值
再来查看一下如下代码的输出:
// 创建学生对象
Student s1 = new Student("itheima" , "14") ;
// 直接输出对象s1
System.out.println(s1);
运行程序进行测试,控制台输出结果如下所示:
Student{name='itheima', age='14'}
可以看到和刚才的输出结果是一致的。那么此时也就证明直接输出一个对象,那么会默认调用对象的toString方法,因此如上代码的等同于如下代码:
// 创建学生对象
Student s1 = new Student("itheima" , "14") ;
// 调用s1的toString方法,把结果进行输出
System.out.println(s1.toString());
因此后期为了方便进行测试,常常是通过输出语句直接输出一个对象的名称。
System.out.println("s1对象的字符串表现形式为:" + result1);
//System是类名
//out是静态变量
//System.out获取到打印的对象
//println()方法
//参数:表示打印的内容
//当打印一个对象的时候,底层会调用对象的toString方法,把对象变成字符串
//然后再打印在控制台上,打印完毕换行处理
小结:
-
在通过输出语句输出一个对象时,默认调用的就是toString()方法
-
输出地址值一般没有意义,可以通过重写toString方法去输出对应的成员变量信息(快捷键:atl + insert , 空白处 右键 -> Generate -> 选择toString)
-
toString方法的作用:以良好的格式,更方便的展示对象中的属性值
-
一般情况下Jdk所提供的类都会重写Object类中的toString方法
案例2:equals方法
-
在测试类(ObjectDemo02)的main方法中,创建两个学生对象,然后比较两个对象是否相同
代码如下所示:
public class ObjectDemo02 {
public static void main(String[] args) {
// 创建两个学生对象
Student s1 = new Student("itheima" , "14") ;
Student s2 = new Student("itheima" , "14") ;
// 比较两个对象是否相等
System.out.println(s1 == s2);//false
}
}
因为"=="号比较的是对象的地址值,而通过new关键字创建了两个对象,它们的地址值是不相同的。因此比较结果就是false。
调用Object类中的equals方法进行比较,代码如下所示:
// 调用equals方法比较两个对象是否相等
boolean result = s1.equals(s2);
// 输出结果
System.out.println(result);//false
为什么结果还是false呢?可以查看一下Object类中equals方法的源码,如下所示:
public boolean equals(Object obj) { // Object类中的equals方法的源码
return (this == obj);
}
通过源码可以发现默认情况下equals方法比较的也是对象的地址值。比较内存地址值一般情况下是没有意义的,我们希望比较的是对象的属性,如果两个对象的属性相同,就认为是同一个对象;
那么要比较对象的属性,就需要在Student类中重写Object类中的equals方法。equals方法的重写,也可以使用idea开发工具完成,具体的操作如下所示:
1.在空白处使用快捷键:alt + insert。此时会弹出如下的对话框
选择equals() and hashCode()方法,此时会弹出如下的对话框
点击next,会弹出如下对话框:
选择neme和age属性点击next,此时就会弹出如下对话框:
取消name和age属性(因为此时选择的是在生成hashCode方法时所涉及到的属性,关于hashCode方法后期再做重点介绍),点击Finish完成生成操作。生成的equals方法和hashCode方法如下:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) && Objects.equals(age, student.age); // 比较的是对象的name属性值和age属性值
}
@Override
public int hashCode() {
return 0;
}
hashCode方法暂时使用不到,可以将hashCode方法删除。重写完毕以后运行程序进行测试,控制台输出结果如下所示:
true
此时equals方法比较的是对象的成员变量值,而s1和s2两个对象的成员变量值都是相同的。因此比较完毕以后的结果就是true。
小结:
-
默认情况下equals方法比较的是对象的地址值
-
比较对象的地址值是没有意义的,因此一般情况下都会重写Object类中的equals方法,重写之后比较的是对象中的变量值
public class ObjectDemo3 {
public static void main(String[] args) {
String s = "abc";
StringBuilder sb = new StringBuilder("abc");
System.out.print1n(s.equals(sb));// false
//因为equals方法是被s调用的,而s是字符串
//所以equals要看String类中的
//字符串中的equals方法,先判断参数是否为字符串
//如果是字符串,再比较内部的属性
//但是如果参数不是字符串,直接返回false
System.out.print1n(sb.equals(s));// false
//因为equals方法是被sb调用的,而sb是StringBuilder
//所以这里的equals方法要看StringBuilder中的equals方法
//那么在StringBuilder当中,没有重写equals方法
//使用的是object中的
//在object当中默认是使用==号比较两个对象的地址值
//而这里的s和sb记录的地址值是不一样的,所以结果返回false
}
}
案例2:对象克隆
把A对象的属性值完全拷贝给B对象,也叫对象拷贝,对象复制
对象克隆的分类:
深克隆和浅克隆
浅克隆:
不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来
基本数据类型拷贝过来的是具体的数据,引用数据类型拷贝过来的是地址值。
Object类默认的是浅克隆
深克隆:
基本数据类型拷贝过来,字符串复用,引用数据类型会重新创建新的
代码实现:
//在Object中,clone属性为protected
//Cloneable
//如果一个接口里面没有抽象方法
//表示当前的接口是一个标记性接口
//现在Cloneable表示一旦实现了,那么当前类的对象就可以被克降
//如果没有实现,当前类的对象就不能克隆
public class User implements Cloneable {
private int id;
private String username;
private String password;
private String path;
private int[] data;
//无参构造、有参构造、get/set方法
public String toString() {
return "角色编号为:" + id + ",用户名为:" + username
+ "密码为:" + password + ", 游戏图片为:" + path + ", 进度:" + arrToString();
}
public String arrToString() {
StringJoiner sj = new StringJoiner(", ", "[", "]");
for (int i = 0; i < data.length; i++) {
sj.add(data[i] + "");
}
return sj.toString();
}
@Override
protected Object clone() throws CloneNotSupportedException {
//调用父类中的clone方法
//相当于让Java帮我们克隆一个对象,并把克隆之后的对象返回出去。
//先把被克隆对象中的数组获取出来
int[] data = this.data;
//创建新的数组
int[] newData =new int[data.length];
//拷贝数组中的数据
for (int i = 0; i < data.length; i++) {
newData[i] = data[i];
}
//调用父类中的方法克隆对象
User u=(User)super.clone();
//因为父类中的克隆方法是浅克隆,替换克隆出来对象中的数组地址值
u.data =newData;
return u;
}
}
public class ObjectDemo4 {
public static void main(String[] args) throws CloneNotSupportedException {
// protected object clone(int a) 对象克隆
//1.先创建一个对象
int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
User u1 = new User(1, "zhangsan", "1234qwer", "girl11", data);
//2.克隆对象
//细节:
//方法在底层会帮我们创建一个对象,并把原对象中的数据拷贝过去。
//书写细节:
//1.重写Object中的clone方法
//2.让javabean类实现Cloneable接口
//3.创建原对象并调用clone就可以了
//User u2 =(User)u1.clone();
//验证一件事情:Object中的克隆是浅克隆
//想要进行深克隆,就需要重写clone方法并修改里面的方法体
//int[] arr = u1.getData();
//arr[0] = 100;
//两个对象中的arr[0]都发生改变,说明Object中的克隆是浅克隆
//System.out.println(u1);
//System.out.println(u2);
//以后一般会用第三方工具进行深克隆
//1.第三方写的代码导入到项目中
//2.编写代码
//Gson gson =new Gson();
//把对象变成一个字符串
//String s=gson.toJson(u1);
//再把字符串变回对象就可以了
//User user =gson.fromJson(s, User.class);
//int[] arr=u1.getData();
//arr[0] = 100;
//打印对象
//System.out.println(user);
}
}
「java深克隆工具」下载链接
链接:https://pan.quark.cn/s/6391652eae9f
具体使用方法可以参考
【黑马程序员Java零基础视频教程_上部(Java入门,含斯坦福大学练习题+力扣算法题和大厂java面试题)】
Objects类
查看API文档,可以看到API文档中关于Objects类的定义如下:
Objects类所在包是在java.util包下,因此在使用的时候需要进行导包。并且Objects类是被final修饰的,因此该类不能被继承。Objects类提供了一些对象常见操作的方法。比如判断对象是否相等,判断对象是否为null等等。
查看一下API文档,看一下Objects类中的成员,如下所示:
可以发现Objects类中没有无参构造方法,因此不能使用new关键字去创建Objects的对象。同时可以发现Objects类中所提供的方法都是静态的。因此可以通过类名直接去调用这些方法。
常见方法
public static String toString(Object o)
// 获取对象的字符串表现形式
public static boolean equals(Object a, Object b)
// 先做非空判断,再比较两个对象是否相等
public static boolean isNull(Object obj)
// 判断对象是否为null
public static boolean nonNull(Object obj)
// 判断对象是否不为null
常见方法:
public static <T> T requireNonNull(T obj)
// 检查对象是否不为null,如果为null直接抛出异常;如果不是null返回该对象;
public static <T> T requireNonNullElse(T obj, T defaultObj)
// 检查对象是否不为null,如果不为null,返回该对象;如果为null返回defaultObj值
public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier)
// 检查对象是否不为null,如果不为null,返回该对象;如果 // 为null,返回由Supplier所提供的值
上述方法中的T可以理解为是Object类型。
案例1:演示重点学习方法
实现步骤:
-
创建一个学生类,提供两个成员变量(name , age);并且提供对应的无参构造方法和有参构造方法以及get/set方法,并且重写toString方法和equals方法
-
创建一个测试类(ObjectsDemo01), 在该类中编写测试代码
Student类
public class Student {
private String name ; // 姓名
private String age ; // 年龄
// 其他代码略
...
}
ObjectsDemo01测试类
public class ObjectsDemo01 {
public static void main(String[] args) {
// 调用方法
method_04() ;
}
// 测试nonNull方法
public static void method_04() {
// 创建一个学生对象
Student s1 = new Student("itheima" , "14") ;
// 调用Objects类中的nonNull方法
boolean result = Objects.nonNull(s1);
// 输出结果
System.out.println(result);
}
// 测试isNull方法
public static void method_03() {
// 创建一个学生对象
Student s1 = new Student("itheima" , "14") ;
// 调用Objects类中的isNull方法
boolean result = Objects.isNull(s1);
// 输出结果
System.out.println(result);
}
// 测试equals方法
public static void method_02() {
// 创建两个学生对象
Student s1 = new Student("itheima" , "14") ;
Student s2 = new Student("itheima" , "14") ;
// 调用Objects类中的equals方法,比较两个对象是否相等
boolean result = Objects.equals(s1, s2);
//细节:
//1.方法的底层会判断s1是否为null,如果为null,直接返回false
//2.如果s1不为null,那么就利用s1再次调用equals方法
//3.此时s1是Student类型,所以最终还是会调用Student中的equals方法。
// 如果没有重写,比较地址值,如果重写了,就比较属性值。
// 输出结果
System.out.println(result);
}
// 测试toString方法
public static void method_01() {
// 创建一个学生对象
Student s1 = new Student("itheima" , "14") ;
// 调用Objects中的toString方法,获取s1对象的字符串表现形式
String result = Objects.toString(s1);
// 如果Student没有重写Object类中的toString方法,此处还是返回的对象的地址值
System.out.println(result);
}
}
案例2:演示需要了解的方法
public class ObjectsDemo02 {
public static void main(String[] args) {
// 调用方法
method_03();
}
// 演示requireNonNullElseGet
public static void method_03() {
// 创建一个学生对象
Student s1 = new Student("itheima" , "14") ;
// 调用Objects对象的requireNonNullElseGet方法,
//该方法的第二个参数是Supplier类型的,查看源码发现Supplier是一个函数式接口
// 那么就可以为其传递一个Lambda表达式,
//而在Supplier接口中所定义的方法是无参有返回值的方法
//因此具体调用所传入的Lambda表达式如下所示
Student student = Objects.requireNonNullElseGet(s1, () -> {
return new Student("itcast", "14");
});
// 输出
System.out.println(student);
}
// 演示requireNonNullElse
public static void method_02() {
// 创建一个学生对象
Student s1 = new Student("itheima" , "14") ;
// 调用Objects对象的requireNonNullElse方法
Student student = Objects.requireNonNullElse(s1, new Student("itcast", "14"));
// 输出
System.out.println(student);
}
// 演示requireNonNull
public static void method_01() {
// 创建一个学生对象
Student s1 = new Student("itheima" , "14") ;
// 调用Objects对象的requireNonNull方法
Student student = Objects.requireNonNull(s1);
// 输出
System.out.println(student);
}
}