05java课堂笔记 - Object类的方法、匿名内部类
p1、关于object类中的toString()方法
1、源代码语句:
public String toString(){
return this.getclass().getName() + "@" + Integer.toHexString(hashCode());
}
- 方法的默认实现是:类名@对象的内存地址的十六进制形式
2、toString()方法的作用?
- toString()方法是一个实例方法,将对象转换成字符串,返回一个“以文本方式表示此对象”的字符串
- toString()方法将一个“java对象”转换成“字符串表示形式”
3、java中所有的类都默认继承object类,object类的toString()方法,作用是输出一些当前对象的信息比如类标记等
- toString()方法应该是一个简洁的、翔实的、易阅读的方法,建议所有的子类都重写此方法
- 使用 print(引用名/对象名) 语句直接输出引用时,程序会自动调用该引用的 toString()方法
示例1:toString()的作用

public class Test{ //Test默认继承Object,故本类中有toString()方法 /* public String toString(){ return getClass().getName() + "@" +Integer.toHexString(hashCode()); } */ public static void main(String[] args){ //输出的字符串文本可以等同看做对象在堆内存中的内存地址 Test t = new Test(); System.out.println(t.toString()); //Test@15db9742 Product pro = new Product(); System.out.println(pro.toString()); //Product@6d06d69c System.out.println(pro); //Product@6d06d69c 自动调用toString()方法 Persion per = new Persion(); System.out.println(per.toString()); //[name=AAA, age=22, gender=女] System.out.println(per); //[name=AAA, age=22, gender=女] } } class Product{ //默认继承Object /* public String toString(){ return getClass().getName() + "@" +Integer.toHexString(hashCode()); } */ } class Persion { String name = "AAA"; int age = 22; String gender = "女"; @Override public String toString() { return "[name=" + name + ", age=" + age + ", gender=" + gender + "]"; } }
示例2:toString方法应用

public class Test{ public static void main(String[] args){ //一个日起对象转换成字符串形式的话,我们可能更希望看到具体的日期信息 MyTime mt = new MyTime(1970,1,1); String s = mt.toString(); //MyTime类重写toString()方法之前 System.out.println(s); //MyTime@15db9742 //MyTime类重写toString()方法之后 System.out.println(s); //1970/1/1 System.out.println(mt.toString()); //1970/1/1 System.out.println(mt); //1970/1/1 输出引用时则自动调用该引用的toString()方法 } } class MyTime { int year; int month; int day; public MyTime(){} public MyTime(int year,int month,int day){ this.year = year; this.month = month; this.day = day; } //重写toString()方法,越简洁越好,可读性越强越好 //向简洁的、翔实的、易阅读的方向发展 @Override public String toString() { //return this.year + "年" + this.month + "月" + this.day + "日"; return this.year + "/" + this.month + "/" + this.day; } }
p2、关于object类中的equals()方法
1、equals()方法的源代码:
public boolean equals(Object obj){
return (this == obj);
}
- 以上是该方法的默认实现:比较两个对象的内存地址是否相等
2、equals()方法的作用?
- equals()方法是一个实例方法,判断两个对象是否相等
3、equals()方法的默认实现够不够用?
- 判断两个对象是否相等不能用“==”,因为“==”是基本数据类型的运算符,只能判断两个对象的内存地址是否相等,不能判断两个对象的内容
- equals()方法默认采用“==”来判断两个java对象是否相等,只能判断两个对象的内存地址,不能判断两个对象的内容,因此这里不能用“==”,equals()方法的默认实现不够用,需要子类重写equals()方法
示例1:如何比较基本型数据、如何比较对象,equals()方法

public class Test{ public static void main(String[] args){ //判断两个基本数据类型的数据是否相等 直接使用“==” int a = 100; int b = 100; //这里“==”判断a中保存的数据和b中保存的数据是否相等 System.out.println("整数:" + (a == b)); //true //判断两个字符型数据是否相等 char c = 'a'; char d = 'a'; System.out.println("字符:" + (c == d)); //true //判断两个字符串数据是否相等 String e = "abc"; String f = "abc"; System.out.println("字符串:" + (e == f)); //true //判断两个java对象是否相等 如何比较呢? MyTime t1 = new MyTime(2008,8,8); //t1 = 0x1234 MyTime t2 = new MyTime(2008,8,8); //t2 = 0x3658 //"=="只能判断两个对象的内存地址是否相等 //这里“==”判断的是t1中保存的内存地址和t2中保存的内存地址是否相等 System.out.println(t1 == t2); //false //比较对象的字符串形式会是什么结果呢? System.out.println(t1); //MyTime@15db9742 System.out.println(t2); //MyTime@6d06d69c System.out.println(t1.toString() == t2.toString()); //false //重写equals()方法之前,比较的是两个对象的内存地址 /* boolean flag = t1.equals(t2); System.out.println(flag); //false */ //重写equals()方法之后,比较的是两个对象的内容 boolean flag = t1.equals(t2); System.out.println(flag); //true //再创建一个新日期 MyTime t3 = new MyTime(2008,8,9); System.out.println(t1.equals(t3)); //false } } class MyTime { int year; int month; int day; public MyTime(){} public MyTime(int year,int month,int day){ this.year = year; this.month = month; this.day = day; } //默认的equals()方法 /* public boolean equals(Object obj) { return (this == obj); } */ //重写equals()方法 //到底该怎么重写?你认为两个对象什么时候表示相等,你就怎么写 @Override public boolean equals(Object obj) { //当年相同、月相同、日也相同的时候,表示两个日期相同,两个对象相等 //获取第一个日期的年月日 //获取第二个日期的年月日 //开始比较 //获取第一个日期的年月日 int year1 = this.year; int month1 = this.month; int day1 = this.day; //获取第二个日期的年月日,多态关系,找不到对象,不能直接获取子类对象特有的属性 /* int year2 = obj.year; int month2 = obj.month; int day2 = obj.day; */ if(obj instanceof MyTime){ MyTime mytime = (MyTime)obj; int year2 = mytime.year; int month2 = mytime.month; int day2 = mytime.day; if(year1 == year2 && month1 == month2 && day1 == day2){ return true; } } //程序能够执行到此处表示日期不相同对象不相等 return false; } }
示例2:改良equals()方法

public class Test{ public static void main(String[] args){ //判断两个java对象是否相等 如何比较呢? MyTime t1 = new MyTime(2008,8,8); //t1 = 0x1234 MyTime t2 = new MyTime(2008,8,8); //t2 = 0x3658 //"=="只能判断两个对象的内存地址是否相等 //这里“==”判断的是t1中保存的内存地址和t2中保存的内存地址是否相等 System.out.println(t1 == t2); //false //重写equals()方法之后,比较的是两个对象的内容 System.out.println(t1.equals(t2)); //true //再创建一个新日期 MyTime t3 = new MyTime(2008,8,9); System.out.println(t1.equals(t3)); //false //这个程序有bug吗?可以运行,但是效率怎么样?低,如何改造? MyTime t4 = null; System.out.println(t1.equals(t4)); //false MyTime t5 = t1; System.out.println(t1.equals(t5)); //true MyTime t6 = new MyTime(2008,8,10); System.out.println(t1.equals(t6)); //false } } class MyTime { int year; int month; int day; public MyTime(){} public MyTime(int year,int month,int day){ this.year = year; this.month = month; this.day = day; } /* //改良equals()方法 @Override public boolean equals(Object obj) { //如果obj是空,直接返回false if(obj == null) return false; //如果obj不是一个MyTime,没必要比较了,直接返回false if(!(obj instanceof MyTime)) return false; //如果this和obj的内存地址相同,也没必要比较了,直接返回false //如果内存地址相同,指向的堆内存的对象肯定是同一个 if(this == obj) return true; //程序能够执行到此处,说明obj不是null,obj是一个MyTime类型 MyTime t = (MyTime)obj; if(this.year == t.year && this.month == t.month && this.day == t.day) return true; //程序能够执行到此处,表示两个对象不相等 return false; } */ //再次精简改良equals()方法 @Override public boolean equals(Object obj) { if(obj == null || !(obj instanceof MyTime)) return false; if(this == obj) return true; MyTime t = (MyTime)obj; return this.year == t.year && this.month == t.month && this.day == t.day; //返回true或false } }
p3、java语言当中的字符串String类有没有重写toString()方法,有没有重写equals()方法
总结:
1、String类已经重写了equals()方法,比较两个String对象时必须使用equals()方法,比较两个字符串也不要使用“==”,equals是通用的
2、String类已经重写了toString方法
大结论:
1、java中什么类型的数据可以使用“==”判断?
java中基本数据类型判断是否相等,使用“==”
2、java中什么类型的数据需要使用equals()判断?
Java中所有的引用数据类型统一使用equals()方法来判断是否相等
示例1:如何比较两个字符串,String类是否重写了equals()方法,是否重写了toString()方法

public class Test{ public static void main(String[] args){ //大部分情况下再用这样的方式创建字符串对象 String s1 = "hello"; String s2 = "abc"; //实际上String也是一个类,不属于基本数据类型 //既然String是一个类,那么一定存在构造方法 String s3 = new String("test1"); String s4 = new String("test1"); //new两次,就有两个对象内存地址 System.out.println(s3 == s4); //false //比较两个字符串能不能使用"=="?不能,必须调用equals()方法 //String类已经重写equals()方法了 System.out.println(s3.equals(s4)); //true //String类有没有重写toString方法呢? String s5 = new String("动力节点"); //如果String没有重写toString()方法,输出结果应该是:java.lang.String@十六进制地址 //经测试String类已经重写了toString()方法 System.out.println(s5.toString()); //动力节点 System.out.println(s5); //动力节点 } }
示例2:两个String对象进行比较的时候必须使用equals方法

public class Test{ public static void main(String[] args){ Student s1 = new Student(111,"梁庄六小"); Student s2 = new Student(111,"梁庄六小"); System.out.println(s1); //学号:111,学校名称:梁庄六小 System.out.println(s2); //学号:111,学校名称:梁庄六小 System.out.println(s1 == s2); //false System.out.println(s1.equals(s2)); //true Student s3 = new Student(222,new String("二实小")); Student s4 = new Student(222,new String("二实小")); System.out.println(s3); //学号:111,学校名称:梁庄六小 System.out.println(s4); //学号:111,学校名称:梁庄六小 System.out.println(s3 == s4); //false System.out.println(s3.equals(s4)); //true } } class Student{ int no; String school; public Student(){} public Student(int no,String school){ this.no = no; this.school = school; } //重写toString()方法 @Override public String toString(){ return "学号:" + no + ",学校名称:" + school; } //重写equals()方法 //需求:当一个学生的学号相等、学校相同时,表示同一个学生 //equals()方法的编写模式是固定的,框架基本一致 @Override public boolean equals(Object obj){ if(obj == null || !(obj instanceof Student)) return false; if(this == obj) return true; Student student = (Student)obj; /* if(this.no == student.no && this.school.equals(student.school)) return true; return false; */ return this.no == student.no && this.school.equals(student.school); //字符串用“==”比较可以吗?不可以 //比较String对象时,必须使用equals()方法,否则比较的结果为false //return this.no == student.no && this.school == student.school; } }
示例3:equals()方法的深层次剖析,equals()重写要彻底

public class Test{ public static void main(String[] args){ User u1 = new User("zhangsan",new Address("内黄","牡丹街","111111")); User u2 = new User("zhangsan",new Address("内黄","牡丹街","111111")); System.out.println(u1.equals(u2)); //true,这里调用的是User类的equals()方法 User u3 = new User("zhangsan",new Address("内黄","牡丹街","111112")); System.out.println(u1.equals(u3)); //false } } class User{ //用户名 String name; //家庭住址 Address addr; public User(){} public User(String name,Address addr){ this.name = name; this.addr = addr; } //重写equals()方法 //重写规则:当两个用户名和家庭住址都相同时,表示同一个用户 //这个equals()判断的是User对象和User对象是否相等 @Override public boolean equals(Object obj){ if(obj == null || !(obj instanceof User)) return false; if(this == obj) return true; User u = (User)obj; //if(用户名相同 && 家庭住址相同) 此时两个对象相等 /* if(this.name.equals(u.name) && this.addr.equals(u.addr)) return true; return false; */ return this.name.equals(u.name) && this.addr.equals(u.addr); //这里分别调用的是String类和Address类的equals() } } class Address{ String city; String street; String zipcode; public Address(){} public Address(String city,String street,String zipcode){ this.city = city; this.street = street; this.zipcode = zipcode; } //这里的equals()判断的是Address对象和Address对象是否相等 @Override public boolean equals(Object obj){ if(obj == null || !(obj instanceof Address)) return false; if(this == obj) return true; Address a = (Address)obj; //if(城市相同 && 街道相同 && 邮编相同) 此时两个对象相等 /* if(this.city.equals(a.city) && this.street.equals(a.street) && this.zipcode.equals(a.zipcode)) return true; return false; */ return this.city.equals(a.city) && this.street.equals(a.street) && this.zipcode.equals(a.zipcode); //这里调用的都是String类的equals() } }
p4、关于Object类中的 finalize()方法
1、在Object类中的源代码:
protected void finalize() throws Throwable{}
GC:负责调用finalize()方法
2、finalize()方法没有方法体,而且是protected修饰的
3、finalize()方法只需要重写,不需要程序员手动调用,JVM的垃圾回收器GC程序会自动调用这个方法
4、finalize()方法的执行时机:
当一个Java对象即将被垃圾回收器回收的时候,垃圾回收器负责调用finalize()方法
5、finalize()方法实际上是SUN公司为java程序员准备的一个时机:垃圾回收时机
如果希望在对象销毁时机执行一段代码的话,这段代码要写到finalize()方法当中
6、提示:Java中的垃圾回收器不轻易启动的,垃圾太少或时间没到则可能不会启动
7、建议启动垃圾回收器:
System.gc();
示例1:垃圾回收器自动调用finalize()方法

public class Test{ public static void main(String[] args){ /* //创建对象 Person p = new Person(); //怎么把Person对象变成垃圾? p = null; */ /* //多造点垃圾 for(int i = 0;i < 1000000;i++){ Person p = new Person(); p = null; } */ for(int i = 0;i < 1000;i++){ Person p = new Person(); p = null; //有一段代码可以建议启动垃圾回收器 if(i % 10 == 0){ //每10个对象启动一次 System.gc(); //建议启动垃圾回收器 } } } } //项目开发中有这样的业务需求:所有对象在JVM中被释放的时候,请记录一下释放时间! //记录对象被释放的时间点,这个负责记录的代码就写到finalize()方法中 class Person{ //重写finalize方法 //Person类型的对象被垃圾回收器回收的时候,垃圾回收器负责调用:p.finalize(); protected void finalize() throws Throwable{ System.out.println(this + "即将被销毁!"); } }
p5、关于Object类中的 hashCode()方法
1、源代码:
public native int hashCode();
这个方法不是抽象方法,带有native关键字,底层调用C++程序
2、hashCode()方法的返回值是一个整型的哈希码:
实际上是一个java对象的内存地址,经过哈希算法,得出的一个值,所以hashCode()方法的执行结果可以等同看做一个Java对象的内存地址
示例1:Object类中的 hashCode()方法

public class Test{ public static void main(String[] args){ Object o = new Object(); int hashCodeValue = o.hashCode(); //对象的内存地址经过哈希算法转换成一个数字,可以等同看作内存地址 System.out.println(hashCodeValue); //366712642 MyClass mc = new MyClass(); int hashCodeValue2 = mc.hashCode(); System.out.println(hashCodeValue2); //1829164700 MyClass mc2 = new MyClass(); System.out.println(mc2.hashCode()); //2018699554 } } class MyClass{ }
p6、匿名内部类
1、什么是内部类?
在类的内部又定义了一个新的类,称为内部类
2、内部类的分类:
静态内部类:类似于静态变量
实例内部类:类似于实例变量
局部内部类:类似于局部变量
3、使用内部类编写的代码,可读性很差,能不用尽量不用
4、匿名内部类:
是局部内部类的一种,因为这个类没有名字而得名
5、学习匿名内部类只为能认识这类代码,但不建议使用匿名内部类
缺点1:太复杂、太乱、可读性差
缺点2:因为类没有名字,所以不能重复使用
示例1:内部类概述

public class Test{ //静态变量 static String country; //该类在类的内部,故为内部类 //有static修饰,故为静态内部类 static class Inner1{} //实例变量 int age; //该类在类的内部,故为内部类 //无static,故为实例内部类 class Inner2{} public void doSome(){ //局部变量 int i = 100; //该类在类的内部,故为内部类 //局部内部类 class Inner3{} } public void doOther(){ //doSome()方法中的局部内部类Inner3,在doOther()方法中不能使用 } //main()方法 入口 public static void main(String[] args){} }
示例2:接口 实现类 多态

public class Test{ //main()方法 入口 public static void main(String[] args){ //调用MyMath中的mySum方法 MyMath mm = new MyMath(); mm.mySum(new ComputeImpl(),100,200); } } //计算接口 interface Compute{ //抽象方法 int sum(int a,int b); } //Compute接口的实现类 class ComputeImpl implements Compute{ //对方法的实现 public int sum(int a,int b){ return a+b; } } //数学求和 class MyMath{ public void mySum(Compute c,int x,int y){ int retValue = c.sum(x,y); System.out.println(x + "+" + y + "=" + retValue); } }
示例3:使用匿名内部类创建对象

public class Test{ //main()方法 入口 public static void main(String[] args){ //调用MyMath中的mySum方法 MyMath mm = new MyMath(); //这样写代码表示ComputeImpl这个类是有的 //mm.mySum(new ComputeImpl(),100,200); //如果不编写实现类,那么使用匿名内部类创建的对象,表示ComputeImpl这个类名时没有的 //这里表面上看好像直接new了接口,实际上后面的{}花括号代表了接口的实现 //这里的{}话括号就是匿名内部类 //不建议使用匿名内部类,因为一个类没有名字,就不能重复使用,而且代码太乱可读性太差 //mm.mySum(new Compute(){接口的实现},100,200); mm.mySum(new Compute(){ public int sum(int a,int b){ return a + b; } },100,200); //100+200=300 } } //计算接口 interface Compute{ //抽象方法 int sum(int a,int b); } //Compute接口的实现类 /* class ComputeImpl implements Compute{ //对方法的实现 public int sum(int a,int b){ return a+b; } } */ //数学求和 class MyMath{ public void mySum(Compute c,int x,int y){ int retValue = c.sum(x,y); System.out.println(x + "+" + y + "=" + retValue); } }
//即使再小的帆也能远航!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?