==是关系运算符
一、toString
1、源代码上toString()方法的默认实现:
- 类名@对象的内存地址转换为十六进制的形式
2、toString方法的作用:
- 通过调用这个方法可以将一个java对象转换成字符串表示形式。
- 建议所有的子类都重写toString()方法
- 输出引用的时候,会自动调用toString方法
public class Test05 {
public static void main(String[] args) {
MyTime t=new MyTime(1970,1,1);
//MyTime类重写toString()方法之前,输出的结果为:Day08.MyTime@5594a1b5
String s1=t.toString();
System.out.println(s1);
System.out.println(t.toString());
//注意:输出引用的时候,会自动调用toString方法
System.out.println(t);
}
}
class MyTime{
private int year;
private int month;
private 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+"日";
}
}
二、equals
1、equals源代码,默认实现:
public boolean equals(Object obj){
return (this==obj);
}
2、equals的作用:通过equals来判断两个对象是否相等。
- 判断两个基本数据类型的数据是否相等就直接使用“==”即可。双等号比较的是值。
- 当使用“==”来比较两个对象是否相等的时候,比较的是对象内存地址是否相等。因此双等号无法比较两个对象是否相等。我们要比较的是两个对象的内容是否相等。
- 因此我们需要重写equals方法。
public class Test06 {
public static void main(String[] args) {
int a = 10;
int b = 10;
System.out.println(a == b);
MyTime t1=new MyTime(2022,7,1);
MyTime t2=new MyTime(2022,7,1);
System.out.println(t1==t2);
boolean flag=t1.equals(t2);
System.out.println(flag);
MyTime t3=new MyTime(2022,7,2);
boolean flag1=t1.equals(t3);
System.out.println(flag1);
MyTime t4=null;
System.out.println(t1.equals(t4));
}
}
class MyTime{
private int year;
private int month;
private 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+"日";
}
//重写Object类的equals方法
//比较两个类的年月日
public boolean equals(Object obj){
//获取第一个类的年月日
int year1=this.year;
int month1=this.month;
int day1=this.day;
//获取第二个类的年月日
if(obj instanceof MyTime){
MyTime t=(MyTime) obj;
int year2=t.year;
int month2=t.month;
int day2=t.day;
if(year1==year2 || month1==month2 || day1==day2){
return true;
}
}
return false;
}
}
上面的代码可以正常比较对象,null也行,但是效果过低,改进方法如下:
修改之后的equals方法:
public boolean equals(Object obj){
if(obj==null){
return false;
}//空指针直接返回
if(!(obj instanceof MyTime)){
return false;
}//如果obj不是一个MyTime,就没有必要比较了,直接返回false
if(this==obj){
return true;
}//如果两者内存相同,则直接返回
//内存地址相同的时候指向的堆内存的对象一定是同一个
//如果程序执行到此处,则说明obj不是null,obj是MyTime类型
MyTime t=(MyTime)obj;//直接向下转型
return this.year==t.year && this.day==t.day && this.month==t.month)
}
三、String对toString和equals的重写
- 比较两个字符串不能使用双等号,要使用equals方法。
public class Test07 {
public static void main(String[] args) {
//大部分情况下,采用这样的方式创建字符串对象
String s1="hello";
String s2="abc";
//实际上String也是一个类,不属于基本数据类型
//既然String是一个类,则一定存在构造方法
String s3=new String("Test1");
String s4=new String("Test1");
System.out.println(s3==s4);
//new了两次,两个对象内存地址,s3保存的内存地址和s4的不同。
//因此比较两个字符串不能使用双等号。需要使用equals方法。
//String类已经重写了equals方法。
System.out.println(s3.equals(s4));
//如果String没有重写toString方法,则输出的结果含有16进制的地址
String x=new String("wenxin");
System.out.println(x.toString());
//由最后的结果可以得知String有重写toString方法
}
}
总结:
1、java中的基本数据类型使用“==”来判断是否相等。
2、java中的引用数据类型使用equals方法来判断是否相等。
String对toString和equals的重写:
public class Test08 {
public static void main(String[] args) {
Student s1=new Student(111,"北京二中");
Student s2=new Student(111,"北京二中");
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
Student s3=new Student(222,new String("北京二中"));
Student s4=new Student(222,new String("北京二中"));
System.out.println(s3==s4);
System.out.println(s3.equals(s4));
}
}
class Student{
int no;
String school;
public Student(){};
public Student(int no,String school){
this.no=no;
this.school=school;
}
//重写toString方法
public String toString(){
return "学号:"+no+",所在学校:"+school;
}
//重写equals方法
//需求:当一个学生的学号相等,并且学校相同的时候,表示同一个学生
public boolean equals(Object o){
if(o==null || !(o instanceof Student)){
return false;
}
if(this==o){
return true;
}
Student s= (Student) o;
if(this.school.equals(s.school) && this.no==s.no){
return true;
}
return false;
}
}
四、finalize方法
1、在Object当中的源码:
protected void finalize() throws Throwable{}
2、finalize()方法只有一个方法体,里面没有代码,而且是protected修饰的。
3、finalize()方法执行的时机:
- 当一个java对象即将被垃圾回收器回收的时候,垃圾回收器负责调用
- finalize()是为程序员准备的一个时机,即垃圾销毁时机。如果希望在对象销毁时机执行一段代码的话,就要写到finalize当中。
4、重写finalize方法:
public class Test09 {
public static void main(String[] args) {
Person p=new Person();
}
}
class Person{
//重写finalize方法
protected void finalize() throws Throwable{
System.out.println("即将被销毁!");
}
}
五、hascode方法
- 这个方法不是抽象方法,带有native关键字,底层调用C++程序
- hashCode()方法返回的是哈希码:
- 实际上就是一个java对象的内存地址,经过哈希算法得到的一个值。
- 所以hashCode()方法的执行结果可以等同于一个java对象的内存地址。
public class Test09 {
public static void main(String[] args) {
Object o=new Object();
int hashcodeValue=o.hashCode();
System.out.println(hashcodeValue);
}
}
六、匿名内部类
1、内部类:在类的内部又定义了一个新的类。
2、内部类的分类:
- 静态内部类:类似于静态变量
- 实例内部类:类似于实例变量
- 局部内部类:类似于局部变量
class T1{
//该类在类的内部,所以称为内部类
//由于前面有static,所以是静态内部类
static class Inner{
}
//该类在类的内部,所以称为内部类
//由于没有static修饰,因此称为实例内部类
class Inner2{
}
public void doSome{
int i=100;
//该类在类的内部,所以称为内部类
//该类在方法的内部,因此称为局部内部类,只在这个方法当中起作用
class Inner3{
}
}
}
3、使用内部类编写的代码,可读性差,尽量不要使用。
4、匿名内部类是局部内部类的一种。因为这个类没有名字,因此叫匿名内部类。
public class Test10 {
public static void main(String[] args) {
//方法一:写一个接口的实现类Com
MyMath1 m=new MyMath1();
m.sum(new Com(),100,200);
//方法二:使用匿名内部类,这样就不需要写一个接口的实现类了,直接在此处实现接口的抽象方法即可
MyMath1 mm=new MyMath1();
mm.sum(new Com(){
public int sum(int x,int y){
return x+y;
}
},100,200);
}
}
//负责计算的接口
interface Computer{
int sum(int a,int b);
}
//实现接口的类
class Com implements Computer{
public int sum(int a,int b){
return a+b;
}
}
class MyMath1{
public void sum(Computer c,int x,int y){
int r=c.sum(x,y);
System.out.println(x+"+"+y+"="+r);
}
}