Object 类是 Java 中的顶级父类, 所有的类都直接或间接继承于 Object 类.
Object 类中的方法可以被所有子类访问.
Object 类没有成员变量, 所以只有无参构造方法.
Java 中, 子类的共性才会往父类中去抽取, 然而不可能有一个属性是所有类的共性, 所以在 Object 这个类中, 是没有成员变量的.
任意一个类的构造方法, 在第一行, 都有一个隐藏的 super();, 默认访问父类的无参构造, 那为什么是默认访问无参构造而不是有参构造呢? 因为在顶级父类 Object 类中, 只有无参构造.
Object 类一共有 11 个成员方法. 其中有三个最常见:
程序示例:
public class demo1 {public static void main(String[] args) {/*public string tostring() 返回对象的字符串表示形式public boolean equals(Object obj) 比较两个对象是否相等protected object clone(int a) 对象克隆*/// 1.tostring 返回对象的字符串表示形式Object obj = new Object();String str1 = obj.toString();System.out.println(str1); // java.lang.Object@119d7047// @ 前面是包名加类名, @ 是一个固定格式, @ 后面部分表示地址值// 对于自定义的类的对象, 也是同样的规则:Student stu = new Student();String str2 = stu.toString();System.out.println(str2); // Object_demo.Student@4eec7777// 直接打印对象, 和调用 toString 方法的效果是一样的:System.out.println(str1); // java.lang.Object@119d7047System.out.println(str2); // Object_demo.Student@4eec7777// 细节:// System: 类名// out: 静态变量// System.out: 获取打印的对象// println(): 方法// 参数: 表示打印的内容// 核心逻辑:// 当我们打印一个对象的时候, 底层会调用对象的 tostring 方法, 把对象变成字符串// 然后再打印在控制台上, 打印完毕换行处理// 思考: 默认情况下, 因为 0bject 类中的 tostring 方法返回的的是地址值// 所以, 默认情况下, 打印一个对象打印的就是地址值// 但是地址值对于我们是没什么意义的// 我想要看到对象内部的属性值, 我们该怎么办?// 处理方案: 重写父类 0bject 类中的 toString 方法// tostring 方法的结论:// 如果我们打印一个对象, 想要看到属性值的话, 那么就重写 tcostring 方法就可以了// 在重写的方法中, 把对象的属性值进行拼接}
}
程序示例:
public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
toString()
源码:
getClass().getName()
用于获取包名加类名, Integer.toHexString(hashCode())
用于获取对象的地址值, 然后进行了一些复杂的运算, 再转换为 16 进制, 再和前面的内容进行了拼接.
第二个方法: equals()
, 程序示例:
public class demo2 {public static void main(String[] args) {/* public static boolean equals(Object obj) 比较两个对象是否相等 */Student student1 = new Student();Student student2 = new Student();boolean res = student1.equals(student2);System.out.println(res); // false// 这里是用 Student 类的对象调用了 equals 方法,// 然而 Student 类显然没有 equals 方法, 因此是调用了 Object 里面的 equals 方法.// Object 里面的 equals 方法是用 == 号比较两个对象的地址值是否相同.// 这两个 Student 类的对象, 都是 new 出来的, 地址肯定是不一样的. 因此用 equals 方法将返回 false.// 地址值的比较其实意义不大, 往往真正希望比较的是两个对象内部的属性值是否相同.// 现在父类 Object 的 equals 方法就已经不能满足要求了, 于是就需要在子类 Student 中重写 equals 方法.// 重写这个 equals 方法, 并不需要我们自己手动书写, 可以借助 IDEA 的快捷键.// 过程很简单, 一直 next 既可, 最终生成了 equals 方法和 hashCode 方法, 但是 hashCode 方法暂时用不上, 可以选择先删除.}
}
Object 类中的 equals()
方法:
Object 里面的 equals()
方法是用 == 号比较两个对象的地址值是否相同.
重写 Object 类里面的 equals()
方法的过程:
重写之后的 Student 类:
import java.util.Objects;public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}// 重写之后的 equals 方法比较的就是对象内部的属性值了.@Overridepublic boolean equals(Object o) {if (this == o) return true; // 先比较是否为同一个对象, 如果是, 就没必要比较内容了, 直接返回 trueif (o == null || getClass() != o.getClass()) return false;Student student = (Student) o; // 强制类型转换return age == student.age && Objects.equals(name, student.name);}
}
此时比较两个 Student 类的对象, 比较的就是属性值了:
public class demo2 {public static void main(String[] args) {/* public static boolean equals(Object obj) 比较两个对象是否相等 */Student student1 = new Student();Student student2 = new Student();Student student3 = new Student("zhangsan", 23);Student student4 = new Student("zhangsan", 23);boolean res1 = student1.equals(student2);boolean res2 = student3.equals(student4);System.out.println(res1); // trueSystem.out.println(res2); // true}
}
程序示例:
public class demo3 {public static void main(String[] args) {String s = "abc";StringBuilder sb = new StringBuilder("abc");// 这个 equals 方法是 s 调用的, 所以应该看 String 里面的 equals 方法System.out.println(s.equals(sb)); // false// 字符串中的 equals 方法, 先判断参数是否为字符串// 如果是字符串, 再比较内部的属性// 但是如果参数不是字符串, 直接返回 alse// 这里传递过来的显然不是字符串, 是一个 StringBuilder 对象// 这个 equals 方法是 sb 调用的, 所以应该看 StringBuilder 里面的 equals 方法System.out.println(sb.equals(s)); // false// 在 StringBuilder 当中, 没有重写 equals 方法// 使用的是 Object 中的// 在 Object 当中默认是使用 == 号比较两个对象的地址值// 而这里的 s 和 sb 记录的地址值是不一样的, 所以结果返回 false}
}
String 类里面的 equals()
方法:
StringBuilder 类没有重写 equals()
方法:
进入父类 AbstractStringBuilder 中查看, 发现也没有重写 equals 方法, 则是使用了 Object 类的 equals()
方法:
第 3 个方法: clone()
作用: 把 A 对象的属性值完全拷贝给 B 对象, 也叫对象拷贝, 对象复制.
可以看见, clone()
方法的修饰符为 protected, 所以 clone 方法只能被本包中的类和其他包中的子类使用, Object 类是写在 java.lang 包的, 显然我们不能把代码写在 lang 包下, 所以如果想要使用 clone()
方法, 就只能自己重写这个方法.
程序示例:
JavaBean 类:
// Cloneable
// 如果一个接口里面没有抽象方法
// 表示当前的接口是一个标记性接口
// 现在 Cloneable 表示一旦实现了, 那么当前类的对象就可以被克降
// 如果没有实现, 当前类的对象就不能克隆import java.util.StringJoiner;public class User implements Cloneable {private int id;private String username;private String password;private String path;private int[] data;public User() {}public User(int id, String username, String password, String path, int[] data) {this.id = id;this.username = username;this.password = password;this.path = path;this.data = data;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public int[] getData() {return data;}public void setData(int[] data) {this.data = data;}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();}@Overrideprotected Object clone() throws CloneNotSupportedException {// 调用父类中的 clone 方法// 相当于让 Java 帮我们克隆一个对象, 并把克隆之后的对象返回出去. return super.clone();}
}
测试类:
public class demo4 {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();System.out.println(u1);System.out.println(u2);}
}
Java 中, 克隆有两种方式.
第一种方式, 先创建一个新的对象, 再把原来的对象的属性值全部拷贝到新对象中, 对于基本数据类型, 拷贝的是值, 对于引用类型的变量, 拷贝的是地址值.
如果有变量是记录着数组的地址的, 于是拷贝之后, 两个变量指向了同一个数组.
这种方式叫做浅克隆或者浅拷贝.
第二种克隆方式, 也是会先创建一个对象, 对于各个属性, 如果是基本数据类型, 还是会将值直接拷贝过来, 如果是引用数据类型, 就不会直接拷贝地址值了, 而是会重新将这些引用型变量的地址所指向的内容, 全部再创建一份出来, 比如将原有的数组再创建出来一份一模一样的, 新老数组有不同的地址值, 新对象里面的属性记录的就是新数组的地址值. 对于字符串, 只要不是 new 出来的, 都是统一放在串池中进行管理的, 是会被复用的.
这种方式叫做深克隆或者深拷贝.
Object 中的 clone()
方法, 是浅克隆.
public class demo4 {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);User u2 = (User) u1.clone();// 验证一件事情: Object 中的克隆是浅克隆// 想要进行深克隆, 就需要重写 clone 方法并修改里面的方法体int[] arr = u1.getData();arr[0] = 100;System.out.println(u1);System.out.println(u2);}
}
重写改写 Javabean 类:
// Cloneable
// 如果一个接口里面没有抽象方法
// 表示当前的接口是一个标记性接口
// 现在 Cloneable 表示一旦实现了, 那么当前类的对象就可以被克降
// 如果没有实现, 当前类的对象就不能克隆import java.util.StringJoiner;public class User implements Cloneable {private int id;private String username;private String password;private String path;private int[] data;public User() {}public User(int id, String username, String password, String path, int[] data) {this.id = id;this.username = username;this.password = password;this.path = path;this.data = data;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public int[] getData() {return data;}public void setData(int[] data) {this.data = data;}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();}@Overrideprotected 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 demo4 {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);// 以后一般会用第三方工具进行克隆// 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);}
}