1 JDK类库的根类:Object
1.1 这个老祖宗类中的方法我们需要先研究一下,因为这些方法都是所有子类通用的。
任何一个类默认继承Object。就算没有直接继承,最终也会间接继承。
1.2 Object类当中有哪些常用的方法?
我们去哪里找这些方法呢?
第一种方法:去源代码当中。(但是这种方式比较麻烦。源代码也比较难)
第二种方法:去查阅java的类库的帮助文档。
什么是API?
应用程序编程接口。(Application Program Interface)
每个JDK的类库就是一个javase的API。
每一个API都会配置一套API帮助文档。
SUN公司提前下好的这套类库就是API。(一般每一份API都对应一份API帮助文档。)
目前为止我们只需要知道这几个方法即可。
protected Object clone() //负责对象克隆的
int hashCode() // 获取对象哈希值的一个方法。
String toString() // 将对象转换成字符串形式
boolean equals(Object obj) // 获取对象哈希值的一个方法
protected void finalize() //垃圾回收器负责调用的方法
1.3 toString()方法
以后所有类的toString()方法是需要重写的。
重写规则,越简单越明了就好。
System.out.println(引用);这个会自动调用“引用.”的toString()方法。
String类是SUN写的,toString()方法已经重写了。
String类是SUN写的,toString方法已经重写了。
1.4 equals()方法
以后所有类的equals方法也需要重写,因为Object中的equals方法比较的是两个对象的内存地址,我们应该比较内容,所以需要重写。
重写规则:自己定,主要看是什么和什么相等时表示两个对象相等。
基本数据类型比较使用==
对象和对象比较:调用equals方法
String类是SUN编写的,所以String类的equals方法重写了。
以后判断两个字符串是否相等,最好不要使用==,要调用字符串对象的equals方法。
注意:重写equals方法的时候要彻底。
1.5 finalize()方法。
这个方法是protected修饰的,在Object类中这个方法的源代码是?
protected void finalize() throws Throwable{ }
重写toString()方法的案例:
/* * 关于Object类中的toString()方法 * 1 源代码长什么样? * public String toString(){ * return this.getClass().getName() + "@" + Integer.toHexString(hashCode()); * } * 源代码上toString()方法默认实现是: * 类名@对象的内存地址转换成十六进制的形式 * * 2 SUN公司设计toString()方法的目的是什么? * toString()方法的作用是: * toString()方法的设计目的是:通过调用这个方法可以将一个“java对象”转换成“字符串”的形式。 * * 3 其实SUN公司开发java语言的时候,建议所有的子类都去重写toString()方法。 * toString()方法应该是一个简洁的、详实的、易阅读的。 * */ public class Test01 { public static void main(String[] args) { MyTime myTime = new MyTime(1970,1,1); // 一个日期对象转换成字符串形式的话,我可能还是希望看到具体的日期信息。 String s1 = myTime.toString(); //MyTime类重写toString()方法之前 System.out.println(s1);// MyTime@十六进制 MyTime@10f87f48 //MyTime类重写toString()方法之后 System.out.println(myTime.toString());// 1970年1月1日 // 注意:输出引用的时候,会自动调用该引用的toString()方法。 System.out.println(s1); } } 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()方法 // 这个toString()方法怎么重写呢? // 越简洁越好,可读性越强越好 // 向简洁的、详实的、易阅读的方向发展 public String toString(){ return this.year + "年" + this.month + "月" + this.day + "日"; } }
重写Object中的equals方法案例:
/* * 关于Object类中的equals()方法 * 1 equals方法的源代码 * public boolean equals(Object obj){ * return (this == obj); * } * 以上这个方法是Object类的默认实现 * * 2 SUN公司设计equals方法的目的是什么? * 以后编程的过程中,都要通过equals方法来判断两个对象是否相等。 * equals方法是判断两个对象是否是相等的。 * * 3 我们需要研究一下Object类给的这个默认的equals方法够不够用!!!! * 在Object类中的equals方法当中,默认采用的是“==”判断两个java对象是否相等。 * 而“==”判断的是两个java对象的内存地址,我们应该判断两个java对象的内容是否相等,所以老祖宗的equals方法不够用。 * 需要子类重写equals。 * * 4 判断两个java是否相等,不能用“==”,因为“==”比较的是两个对象的内存地址。 * */ public class Test02 { public static void main(String[] args) { // 判断两个基本数据类型的数据是否相等直接使用“==”就行。 int a = 100; int b = 100; // 这个“==”是判断a中保存的100和b中保存的100是否相等。 System.out.println(a == b);// true 相等 false 不相等 // 判断两个java对象是否相等,我们怎么办?能直接使用“==”吗? // 创建一个日期对象是:1970年1月1日。 MyTime1 t1 = new MyTime1(1970,1,1);// MyTime t1 = 0x1234; // 创建一个新的日期对象,但表示的日期也是:1970年1月1日。 MyTime1 t2 = new MyTime1(1970,1,1);// MyTime t1 = 0x2345; // 测试一下:比较两个对象是否相等,能不能使用“==”??? // 这里的“==”判断的是:t1中保存的对象地址和t2中保存的对象地址是否相等。 System.out.println(t1 == t2);//false // 重写Object equals方法之前(比较的是对象的内存地址) /*boolean flag = t1.equals(t2); System.out.println(flag);*/ // 重写Object equals方法之后(比较的是内容) boolean flag = t1.equals(t2); System.out.println(flag); MyTime1 t3 = new MyTime1(1970,1,22); boolean flag1 = t1.equals(t3); System.out.println(flag1); // 我们这个程序有bug吗?可以运行,但是效率怎么样?低(怎么改造) MyTime1 t4 = null; System.out.println(t1.equals(t4)); } } class MyTime1{ int year; int month; int day; public MyTime1() { } public MyTime1(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } // 默认的equals方法 /*public boolean equals(Object obj){ return (this == obj); }*/ // 重写Object类的equals方法 // 怎么重写?赋值黏贴。相同的返回值类型、相同的方法名、相同的形式参数列表。 // equals到底应该怎么重写?你自己定,你认为两个对象什么相等的时候表示相等,你就怎么重写。 /*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 MyTime1){ MyTime1 t = (MyTime1)obj; int year2 = t.year; int month2 = t.month; int day2 = t.day; if (year1 == year2 && month1 == month2 && day1 == day2){ return true; } } // 如果程序能够执行到此处表示日期不相等。 return false; }*/ // 改良equals方法 /*public boolean equals(Object obj){ // 如果obj是空,直接返回false if (obj == null){ return false; } // 如果obj的类型不是MyTime1的话,也返回false if(!(obj instanceof MyTime1)){ return false; } // 如果this和obj保存的内存地址相同,没必要比较了,直接返回true。 // 内存地址相同的时候指向的堆内存的对象肯定是同一个咯。 if (this == obj){ return true; } // 程序能够执行到此处说明什么? // 说明obj不是null。obj是MyTime1类型。 MyTime1 t = (MyTime1) obj; if(this.year == t.year && this.month == t.month && this.day == t.day){ return true; } // 程序能到这里返回false return false; }*/ // 再次改良 /*public boolean equals(Object obj){ // 如果obj是空,直接返回false if (obj == null){ return false; } // 如果obj的类型不是MyTime1的话,也返回false if(!(obj instanceof MyTime1)){ return false; } // 如果this和obj保存的内存地址相同,没必要比较了,直接返回true。 // 内存地址相同的时候指向的堆内存的对象肯定是同一个咯。 if (this == obj){ return true; } // 程序能够执行到此处说明什么? // 说明obj不是null。obj是MyTime1类型。 MyTime1 t = (MyTime1) obj; return this.year == t.year && this.month == t.month && this.day == t.day; }*/ // 再再次改良 public boolean equals(Object obj){ // 如果obj是空,直接返回false if (obj == null && !(obj instanceof MyTime1)){ return false; } // 如果this和obj保存的内存地址相同,没必要比较了,直接返回true。 // 内存地址相同的时候指向的堆内存的对象肯定是同一个咯。 if (this == obj){ return true; } // 程序能够执行到此处说明什么? // 说明obj不是null。obj是MyTime1类型。 MyTime1 t = (MyTime1) obj; return this.year == t.year && this.month == t.month && this.day == t.day; } }
重写Object中equals()方法案例:
import java.util.Objects; // String对象比较的时候必须使用equals方法。 public class Test03 { public static void main(String[] args) { /*Student a = new Student("123"); Student b = new Student("123"); System.out.println(a == b); System.out.println(a.equals(b));*/ Student a = new Student(111,new String("123")); Student b = new Student(111,new String("123")); System.out.println(a == b); System.out.println(a.equals(b)); } } class Student{ // 学号 int no; // 基本数据类型,比较时用== // 所在学校 String school;// 引用数据类型,比较时用equals方法 public Student() { } public Student(int no, String school) { this.no = no; this.school = school; } // 重写toString()方法 public String toString(){ return "学号" + no + ",所在学校名称" + school; } // 重写equals方法 // 需求:当一个学生的学号相等,并且学校相同时,表示同一个学生 // 思考:这个equals该怎么重写呢? // equals方法的编写模式都是固定的。架子都是差不多的 public boolean equals(Object obj) { if (obj == null && !(obj instanceof Student)){ return false; } if (this == obj){ return true; } Student s = (Student)obj; return this.no == s.no && this.school.equals(s.school); // 字符串用双等号比较可以吗? // 不可以 //return this.no == s.no && this.school == s.school; } }
重写Object中equals()方法案例:
/* * java语言当中的字符串String有没有重写toString方法,有没有重写equals方法。 * * 总结: * 1 String类已经重写了equals方法,比较两个字符串不能使用==,必须使用equals。 * equals是通用的。 * * 2 String类已经重写了toString()方法。 * * 大结论: * java中什么类型的数据可以使用“==”来判断。 * java中基本数据类型比较是否相等,使用== * * java中什么类型的数据需要使用equals来判断。 * java中所有的引用数据类型统一使用equals方法来判断是否相等。 * * 这是规矩。 * */ public class Test04 { 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两次,两个对象内存地址,s3保存的内存地址和s4保存的内存地址不同。 // == 判断的是内存地址。不是内容。 System.out.println(s3 == s4);//false // 比较两个字符串能不能使用双等号? // 不能,必须调用equals方法。 // equals才是判断对象的值。 // String类已经重写equals方法了 System.out.println(s3.equals(s4)); // String类有没有重写toString()方法呢? String s = new String("xlWu"); // 如果String没有重写toString()方法,输出结果:java.lang.String@十六进制地址 // 经过测试:String类已经重写了toString()方法 System.out.println(s.toString());//xlWu System.out.println(s);//xlWu } }
重写Object中equals()方法要重写彻底案例:
import java.util.Objects; // equals方法重写的时候要彻底。 public class Test05 { public static void main(String[] args) { //Address address = new Address("重庆","渝澳大道","招商锦星汇"); User u = new User("zhangsan",new Address("重庆","渝澳大道","招商锦星汇")); User u1 = new User("zhangsan",new Address("重庆","渝澳大道","招商锦星汇")); System.out.println(u.equals(u1)); User u2 = new User("zhangsan",new Address("重庆","渝澳大道","读书一村")); System.out.println(u.equals(u2)); } } class User{ //用户名 String name; //地址 Address addr; public User() { } public User(String name, Address addr) { this.name = name; this.addr = addr; } // 重写equals方法 // 重写规则:当一个用户的用户名和家庭住址都相同,表示同一个用户。 // 这个equals判断的是User对象和User对象是否相等。 public boolean equals(Object obj) { // 用户名和用户名相同,住址和住址相同的时候,认定是同一个用户。 if(obj == null && !(obj instanceof User)){ return false; } if (this == obj){ return true; } User user = (User)obj; if (this.name.equals(user.name) && this.addr.equals(user.addr)){ return true; } return false; } } 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对象是否相等。 public boolean equals(Object obj){ if (obj == null && !(obj instanceof Address)){ return false; } if (this == obj){ return false; } Address address = (Address)obj; if (this.city.equals(address.city) && this.street.equals(address.street) && this.zipcode.equals(address.zipcode)){ return true; } return false; } }
Object中finalize()方法案例:
/* * 关于Object类中的finalize()方法(非重点) * 1 在Object类中的源码: * protected void finalize() throws Throwable{ } * * GC:负责调用finalize()方法。 * * 2 finalize()方法只有一个方法体,里面没有代码,而且这个方法是protected修饰的。 * * 3 这个方法不需要程序员手动调用,JVM的垃圾回收器负责调用这个方法。 * 不像equals() toString(),equals()和toString()方法是需要你写代码调用的。 * finalize()只需要重写,重写完将来自动还有程序来调用。 * * 4 finalize()方法的执行机制: * 当一个java对象即将被垃圾回收器回收的时候,垃圾回收器负责finalize()方法 * * 5 finalize()方法实际上是SUN公司为java程序员准备的一个世纪,垃圾销毁机。 * 如果希望在对象销毁时机执行一段代码的话,这段代码要写到finalize()方法中。 * * 6 静态代码块的作用是什么? * static{ * ... * } * 静态代码块在类加载时刻执行,并且只执行一次。 * 这是一个SUN准备的类加载时机 * * finalize()方法同样也是SUN为程序员准备的一个时机。 * 这个时机是垃圾回收时机。 * * 7 提示: * java中的垃圾回收器不是轻易启动的,垃圾太少,或者时间没到,种种条件下,有可能启动,也有可能不启动。 * */ public class Test06 { public static void main(String[] args) { // 创建对象 Person person = new Person(); // 怎么把Person对象变成垃圾? person = null; // 多制造点垃圾 for (int i = 0; i < 100000000; i++){ Person p = new Person(); p = null; } // 有一段代码可以建议垃圾回收器启动 System.gc();// 建议启动垃圾回收器,(只是建议,可能不启动,也可能启动。启动概率高了一些。) } } /* * 项目开发中有这样的业务需求:所有对象在JVM中被释放的时候,请记录一下释放时间!!! * 记录对象被是释放的时间点,这个负责记录的代码写到哪里? * 写到finalize()方法中。 * */ class Person{ // 重写finalize()方法 // Person类型的对象被垃圾回收器回收的时候,垃圾回收器负责调用:p.finalize()。 protected void finalize() throws Throwable { // this代表当前对象 System.out.println(this + "即将被销毁!"); } }
Object中的hashCode()方法案例:
/* * hashCode方法 * 在Object中的hashCode方法是怎样的? * public native int hashCode(); * 这个方法不是抽象方法,带有native关键字,底层调用c++程序 * * hashCode()方法返回的是哈希值: * 实际上就是一个java对象的内存地址,经过哈希算法,得出一个值。 * 所以hashCode()方法的执行结果可以等同看做一个java对象的内存地址。 * */ public class Test07 { public static void main(String[] args) { Object object = new Object(); int hashCodeValue = object.hashCode(); // 对象内存地址经过哈希算法转换的一个数字,可以等同看做内存地址 System.out.println(hashCodeValue);//1915910607 MyClass myClass = new MyClass(); int hashCodeValue1 = myClass.hashCode(); System.out.println(hashCodeValue1);//189568618 } } class MyClass{ }
利用IDEA重写Object中的toString()和equals()方法案例:
import java.util.Objects; /*IDEA自动生成重写方法*/ public class MyTime4 { private int year; private int month; private int day; public MyTime4() { } public MyTime4(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } public String toString() { return "MyTime4{" + "year=" + year + ", month=" + month + ", day=" + day + '}'; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyTime4 myTime4 = (MyTime4) o; return year == myTime4.year && month == myTime4.month && day == myTime4.day; } }