Object类
程序示例
/*
Object:Class Object是类Object结构的根。
每个班(class)都有Object作为超类。
所有对象(包括数组)都实现了这个类的方法。
每个类都直接或者间接的继承Object类
Object类中的方法:
public int hashCode()返回对象的哈希码值。
支持这种方法是为了散列表,如HashMap提供的那样。
注意:这里的哈希码值是根据哈希算法计算出来的一个值,这个值和地址有关系,但是并不是实际
的地址值。简单理解为地址值的另一种表现形式
public final 类 getClass()返回此Object的运行时类。
返回的类对象是被表示类的static synchronized方法锁定的对象。
public String getName()返回由类对象表示的实体的名称
(类,接口,数组类,原始类型或void),作为String 。
*/
public class Student2 extends Object{
}
public class StudentTest {
public static void main(String[] args) {
//创建Student2对象
Student2 s = new Student2();
//调用继承父类Object的hashCode()方法
System.out.println(s.hashCode()); //输出:1163157884
//了解了哈希算法是地址值的另一种表现形式,那我再创建一个对象
//那么s1与s之间的哈希值是不是应该不一样
//答案是不一定,因为它空间就那么大,你取模有可能发生不同的数值取模的结果是一样的。
Student s1 = new Student();
System.out.println(s1.hashCode()); //输出:1956725890
//将s的地址值赋值给s2
Student s2 = s;
System.out.println(s2.hashCode()); //输出:1163157884
System.out.println("*************************");
//创建s3对象调用继承父类Object的getClass()方法
Student s3 = new Student();
//返回此Object的运行时类。
System.out.println(s3.getClass()); //输出:class com.shujia.wyh.day18.Student
Class c = s3.getClass();
//public String getName()
// 返回由类对象表示的实体的名称(类,接口,数组类,原始类型或void),作为String 。
System.out.println(c.getName()); //输出:com.shujia.wyh.day18.Student
System.out.println("****************************");
//链式编程
System.out.println(s3.getClass().getName());
}
}
哈希算法图解:
![](https://img2020.cnblogs.com/blog/2648122/202112/2648122-20211213232007809-901689575.png)
/*
public String toString()返回对象的字符串表示形式。
一般来说, toString方法返回一个“textually代表”这个对象的字符串。
结果应该是一个简明扼要的表达,容易让人阅读。 建议所有子类覆盖此方法。
该toString类方法Object返回一个由其中的对象是一个实例,
该符号字符`的类的名称的字符串@ ”和对象的哈希码的无符号的十六进制表示。
换句话说,这个方法返回一个等于下列值的字符串:
getClass().getName() + '@' + Integer.toHexString(hashCode())
//Integer.toHexString(hashCode()):将hashCode()值反过来转成具体的地址值
Integer类中有一个toHexString方法将哈希值转换成地址值
public static String toHexString(int i)
返回整数参数的字符串表示形式,作为16位中的无符号整数。
我们虽然掌握了toString()的方法使用,但是呢打印的结果是一个地址值,我们拿到地址值是没有意义的
我们正常打印一个对象,其实是想看该对象中的成员变量的值。
又因为toString()方法的返回值是String类型,并且属于Object类中的
而Object类是所有类的父类,那么我们自己定义类的时候,就可以重写该方法,重写里面的实现。
将来调用toString()方法的时候,严格遵循编译看左,运行看右,所以调用的是重写后的方法。
类最终写法:4.0版本:
成员变量:private
构造方法:无参,有参
成员方法:getXxx()和setXxx(),..
toString():自动生成即可
*/
public class Student3 {
private String name;
private int age;
public Student3() {
}
public Student3(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;
}
//自己重写的父类Object的toString()方法
// @Override
// public String toString() {
// return "姓名:" + name + ",年龄:" + age;
// }
//自动生成的重写父类Object的toString()方法
@Override
public String toString() {
return "Student3{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class StudentTest2 {
public static void main(String[] args) {
Student3 s = new Student3();
//调用继承自父类Object的toString()方法
//返回对象的字符串表示形式。
System.out.println(s.toString());//输出:com.shujia.wyh.day18.Student3@4554617c
System.out.println("*************************");
System.out.println(s.getClass().getName());
System.out.println("*****************************");
//toString()方法等价于以下内容
//getClass().getName() + '@' + Integer.toHexString(hashCode())
//s.getClass().getName() + "@" + Integer.toHexString(s.hashCode())
//this.getClass().getName() + "@" + Integer.toHexString(this.hashCode())
System.out.println(s.toString());
System.out.println(s.getClass().getName()+"@"+Integer.toHexString(s.hashCode()));
s.setName("小花");
s.setAge(18);
//这样重写后的toString()方法就代替了之前的show()方法
System.out.println(s.toString());
}
}
/*
public boolean equals(Object obj)指示一些其他对象是否等于此。
今后想要弄明白一个类中的方法是如何实现的时候,API也没告诉我们怎么办呢?
看源码。将鼠标光标放在方法名上 ctrl+鼠标左键
通过观察源码发现:Object类中的equals方法实现底层依旧是 ==
public boolean equals(Object obj) {
return (this == obj);
}
而==比较引用数据类型比较的是地址值,当地址值不一样的时候返回的是false
==:
基本类型:比较的是值是否相同
引用类型:比较的地址值是否相同
equals:(为什么equals不能比较基本数据类型?因为调用equals方法的是一个对象,本身就不是基本数据类型了)
引用数据类型:
不需要我们自己重写,自动生成即可
重写之前,默认调用的是Object类中的equals方法,默认比较的是地址值
重写之后,比较的是对象成员变量值
*/
import java.util.Objects;
public class Student4 {
private String name;
private int age;
public Student4() {
}
public Student4(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;
}
@Override
public String toString() {
return "Student3{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//重写的equals()方法
@Override
public boolean equals(Object o) { //s3.equals(s4)
//this -- s3
//o -- s4
//先判断两者地址值是否相同
if (this == o) return true;
//在判断这个对象是不是null || 这两个对象的class文件是不是一样的
if (o == null || getClass() != o.getClass()) return false;
//然后o向下转型获取age和name,再去判断
Student4 student4 = (Student4) o;
return age == student4.age &&
Objects.equals(name, student4.name);
}
}
public class StudentTest3 {
public static void main(String[] args) {
Student4 s1 = new Student4("阿伟", 18);
Student4 s2 = new Student4("阿伟", 18);
// new了两次
System.out.println(s1==s2);//结果为false
//将s1的地址值赋给了s3
Student4 s3 = s1;
System.out.println(s1==s3);//结果为true
System.out.println("***************************");
System.out.println(s1.equals(s2)); //false equals()方法重写之后//结果为:true
System.out.println(s1.equals(s3)); //true
System.out.println(s1.equals(s1)); //true
//但是,我们两个对象的成员变量值都一样,从现实生活角度出发,我们应该创建的是同一个学生
// 所以我们想要重写继承自父类Object的equals()方法,让重写之后,比较的是对象成员变量值
Student4 s4 = new Student4("小朱", 18);
System.out.println(s3.equals(s4)); //输出结果:false
}
}
/*
protected void finalize()
throws Throwable当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用该对象。
一个子类覆盖了处理系统资源或执行其他清理的finalize方法。
这个方法简单理解为就是用来垃圾回收的,什么时候回收呢?
不确定。这里涉及到了GC机制
protected Object clone()
创建并返回此对象的副本。
clone的方法Object执行特定的克隆操作。
首先,如果此对象的类不实现接口Cloneable ,则抛出CloneNotSupportedException 。
将想要调用克隆方法的类实现Cloneable接口
通过观察API发现,Cloneable接口中没有成员变量也没有成员方法
今后看到类似于Cloneable接口里面什么都没有的接口,我们称之为标记接口
拷贝在IT行业中常见两种:
浅拷贝
拷贝的时候,重新创建一个对象,成员变量值和被拷贝的一样,但是后续修改与原先的没有关系
深拷贝
拷贝的时候,没有创建新的对象,只是改个名字,地址值都一样,修改拷贝后的连带影响到原先的
*/
import java.util.Objects;
public class Student5 implements Cloneable{//加上了实现克隆的接口
private String name;
private int age;
public Student5() {
}
public Student5(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;
}
@Override
public String toString() {
return "Student3{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student5 student5 = (Student5) o;
return age == student5.age &&
Objects.equals(name, student5.name);
}
//重写了继承自父类Object的clone()方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class StudentTest4 {
public static void main(String[] args) throws CloneNotSupportedException {
Student5 s1 = new Student5("华根", 18);
System.out.println(s1.toString());
//CloneNotSupportedException//克隆不支持异常
//当想要被克隆的类加上了实现克隆的接口,就可以被克隆
//这里其实隐含了多态
//Student5对象s1,克隆出来的还是Student5的对象
//所以父类的引用指向子类的对象
Object obj = s1.clone();
System.out.println(obj.toString());//克隆过后发现 s1.toString() 和 obj.toString()输出结果一样
//那我克隆有什么意义呢?现在我直接将s1赋给s2
Student5 s2 = s1;
System.out.println(s2.toString());//输出结果和 s1.toString() 一样
//这样 s1.toString() 和 s2.toString()输出结果不还是一样的吗?
System.out.println("**************************");
//现在我将s2的年龄改为20岁
s2.setAge(20);
System.out.println(s1.toString());//我们发现当我们改了s2之后s1也跟着变了
System.out.println(s2.toString());//s1与s2这样的拷贝方式是深拷贝
System.out.println(obj.toString());//但是克隆出来的没有影响,所以克隆是浅拷贝
}
}