Java学习第四章(二)

视频链接:https://www.bilibili.com/video/BV1Rx411876f?p=1

视频范围P526 - P542

源码及API文档

1.概述

从哪可以看object类当中的常用方法?

  • 第一种:去源代码中找(比较麻烦,源代码也比较难)
  • 第二种:去查阅java的类库的帮助文档

API

  • API是应用程序编程接口(Application Program Interface)
  • 整个JDK的类库就是一个javase的API
  • 每一个API都会配置一套API帮助文档
  • SUN公司提前写好的这套类库就是API(一般每一份API都对应一份API帮助文档)

目前为止只需要知道以下几个方法就行

  1. protected Object clone() //负责对象克隆的
  2. int hashCode() //获取对象哈希值的一个方法
  3. boolean equals(Object obj) //判断两个对象是否相等
  4. String toString() //将对象转换成字符串形式
  5. protected void finalize() //垃圾回收器负责调用的方法

2.Object的toString方法

  1. 源码:
public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
  1. 源代码上toString()方法的默认实现是:类名@对象的内存地址转换为十六进制的形式
  2. toString()方法设计目的:通过调用这个方法可以将一个“java对象”转换成“字符串表示形式”
  3. SUN公司开发java语言的时候,建议所有的子类都去重写toString()方法
    toString()方法应该是一个简洁的、详实的、易阅读的

代码示例

时间类

package toString;

public 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()方法
	public String toString() {
		
		return this.year + "年" + this.month + "月" + this.day + "日";
	}	
}

测试类

package toString;

public class Test01 {

	public static void main(String[] args) {
		
		MyTime t1 = new MyTime(1997,7,27);
		
		//一个日期对象转换成字符串形式的话,更希望看到具体的日期信息
		String s1 = t1.toString();
		
		//MyTime类重写toString()方法之前
		//System.out.println(s1);//输出为:toString.MyTime@1175e2db
		
		//MyTime类重写toString()之后
		System.out.println(s1);//输出为:1997年7月27日
		
		//等价于
		System.out.println(t1.toString());//输出为:1997年7月27日
		
		//注意:输出引用的时候,会自动调用该引用的toString()方法
		System.out.println(t1);//输出为:1997年7月27日
	}
}

3.Object的equals方法

3.1 基本概念

  1. 源码(Object类的默认实现):
    public boolean equals(Object obj) {
        return (this == obj);
    }
  1. SUN公司设计目的:通过equals方法来判断两个对象是否相等
    代码示例

时间类

package toString;

public 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方法
	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;
    }	
}

测试类

package toString;

public class Test02 {

	public static void main(String[] args) {
		
		MyTime t1 = new MyTime(1997,7,27);
		MyTime t2 = new MyTime(1997,7,27);
		
		//测试一下,两个对象是否相等
		//这里的“==”判断的是:t1中保存的对象内存地址和t2中保存的对象内存地址是否相等
		System.out.println(t1 == t2);//输出为:false
		
		//重写的equals方法之前(比较的是对象内存地址)
		//boolean flag = t1.equals(t2);
		//System.out.println(flag);//输出为:false
		
		//重写的equals方法之后(比较的是内容)
		boolean flag1 = t1.equals(t2);
		System.out.println(flag1);//输出为:true
	}
}

但是当遇到下面情况的时候

package toString;

public class Test02 {

	public static void main(String[] args) {
		
		MyTime t1 = new MyTime(1997,7,27);
		MyTime t4 = null;
		System.out.println(t1.equals(t4));//输出为:false
	}
}

也可以运行,但是效率太低了,于是对时间类进行改良

时间类

package toString;

public 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) {
		
		//如果obj是空,直接返回false
		if (obj == null) {
			return false;
		}
		
		//如果obj不是一个MyTime,没必要比较了,直接返回false
		if (!(obj instanceof MyTime)) {
			return false;
		}
		
		//如果this和obj保存的内存地址相同,没必要比较了,直接返回true
		if (this == obj) {
			return true;
		}
		
		MyTime t = (MyTime)obj;
		if (this.year == t.year && this.month == t.month && this.day == t.day) {
			return true;
		}
		
		//程序能到这里返回false
		return false;
    }
}

进一步改良

package toString;

public 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) {
		
		//如果obj是空,或者obj不是一个MyTime 直接返回false
		if (obj == null || !(obj instanceof MyTime)) {
			return false;
		}
		
		//如果this和obj保存的内存地址相同,没必要比较了,直接返回true
		if (this == obj) {
			return true;
		}
		
		MyTime t = (MyTime)obj;
		return this.year == t.year && this.month == t.month && this.day == t.day;
    }
}

3.2 String类重写toString和equals

  1. String类已经重写了toString方法
  2. String类已经重写了equals方法,比较两个字符串不能使用==,必须使用equals,equals是通用的
  3. java中基本数据类型比较是否相等,使用==
  4. java中所有的引用数据类型统一使用equals方法来判断是否相等
package toString;

public class Test03 {

	public static void main(String[] args) {
		
		//大部分情况下,采用这样的方式创建字符串对象
		String s1 = "hello";
		String s2 = "abc";
		
		//实际上String也是一个类,不属于基本数据类型,存在构造方法
		String s3 = new String("Test1");
		String s4 = new String("Test1");
		
		//==判断的是内存地址,不是内容
		System.out.println(s3 == s4);//输出为:false
		
		//比较两个字符串必须调用equals方法
		//String类已经重写了equals方法
		System.out.println(s3.equals(s4));//输出为:true
		
		//String类已经重写了toString方法
		String x = new String("西南大学");
		//如果没有重写那么输出结果为:java.lang.String@十六进制的地址
		System.out.println(x.toString());//输出为:西南大学
		System.out.println(x);//输出为:西南大学
	}
}

3.3 重写Object的equals方法

学生类

package toString;

public 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 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;
	}	
}

测试类

package toString;

public class Test04 {

	public static void main(String[] args) {
		
		Student s1 = new Student(5108,"西南大学");
		Student s2 = new Student(5108,"西南大学");
		
		System.out.println(s1 == s2);//输出为:false
		System.out.println(s1.equals(s2));//输出为:true
		
		//下面情况为特殊情况,用student的类中最后注释掉的方法返回就会出现问题
		//Student s3 = new Student(5108,new String("西南大学"));
		//Student s4 = new Student(5108,new String("西南大学"));
		
		//System.out.println(s1 == s2);//输出为:false
		//System.out.println(s1.equals(s2));//输出为:false
	}
}

3.4 equals方法深层次理解

注意:equals方法重写的要彻底

地址类

package toString;

public 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方法
	//这里的equals方法判断的是:Address对象和Address对象是否相等	
}

用户类

package toString;

public 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 u = (User)obj;
		
		return this.name.equals(u.name) && this.addr.equals(u.addr);
	}
}

测试类

package toString;

public class Test05 {

	public static void main(String[] args) {
		
		User u1 = new User("liujie",new Address("重庆","北碚区","5108"));
		User u2 = new User("liujie",new Address("重庆","北碚区","5108"));
		
		System.out.println(u1.equals(u2));//输出为:false
	}
}

针对上面的问题,需要在地址类中也要定义equals方法:

地址类

package toString;

public 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方法
	// 这里的equals方法判断的是:Address对象和Address对象是否相等
	public boolean equals(Object obj) {

		if (obj == null || !(obj instanceof Address)) {
			return false;
		}
		if (this == obj) {
			return true;
		}

		Address a = (Address) obj;

		return this.city.equals(a.city) && this.street.equals(a.street) && this.zipcode.equals(a.zipcode);
	}
}

测试类

package toString;

public class Test05 {

	public static void main(String[] args) {
		
		User u1 = new User("liujie",new Address("重庆","北碚区","5108"));
		User u2 = new User("liujie",new Address("重庆","北碚区","5108"));
		
		System.out.println(u1.equals(u2));//输出为:true
	}
}

4.Object的finalize方法

  1. 源码:
protected void finalize() throws Throwable{}
  1. finalize()方法只有一个方法体,里面没有代码,而且这个方法是protected修饰的
  2. 这个方法不需要程序员手动调用,JVM的垃圾回收器负责调用这个方法
    如:不像equals、toString,这两个需要写代码调用,但是finalize()方法只需要重写,重写完将来自动会有程序来调用
  3. finalize()方法的执行时机:
    当一个java对象即将被垃圾回收器回收的时候,垃圾回收器负责调用finalize()方法
  4. finalize()方法实际上是SUN公司为java程序员准备的一个时机,垃圾销毁时机。
    如果希望在对象销毁时机执行一段代码的话,这段代码要写到finalize()方法当中
  5. 静态代码块的作用是什么?
    静态代码块在类加载时刻执行,并且只执行一次,这是一个SUN准备的类加载时机
static{
 .....
}
  1. finalize()方法同样也是SUN为程序员准备的一个时机,这个时机是垃圾回收时机
  2. java中的垃圾回收器不是轻易启动的,垃圾太少,或者时间没到,种种条件下,有可能启动,也有可能不启动

人类

package toString;

public class Person {
	
	//重写finalize()方法
	//Person类型的对象被垃圾回收器回收的时候,垃圾回收器负责调用:p.finalize();
	protected void finalize() throws Throwable{
		System.out.println("即将被销毁!!!");
	}
}

测试类

package toString;

import java.util.Iterator;

public class Test06 {

	public static void main(String[] args) {

		// 创建对象
		// Person p = new Person();

		// 怎么将Person对象变成垃圾

		// 第一种方法
		// for (int i = 0; i < 100000000; i++) {
		// Person p = new Person();
		// p = null;
		// }

		// 第二种
		for (int i = 0; i < 100000000; i++) {
			Person p = new Person();
			p = null;
			
			//有一段代码可以建议垃圾回收器启动
			if (i % 2 == 0) {
				System.gc();//建议启动垃圾回收器,但只是建议,可能不启动,也可能启动
			}
		}
	}
}

运行效果

在这里插入图片描述

5.Object的hashCode方法

  1. 源码
    这个方法不是抽象方法,带有native关键字,底层调用C++程序
public native int hashCode();
  1. hashCode()方法返回的是哈希码:实际上就是一个java对象的内存地址,经过哈希算法,得出的一个值
  2. hashCode()方法的执行结果可以等同看做一个java对象的内存地址
package toString;

public class Test07 {

	public static void main(String[] args) {
		
		Object o = new Object();
		int hashCodeValue = o.hashCode();
		
		//对象内存地址经过哈希算法转换的一个数字,可以等同看做内存地址
		System.out.println(hashCodeValue);//输出为:2111991224
		
		MyClass mc = new MyClass();
		int hashCodeValue2 = mc.hashCode();
		
		System.out.println(hashCodeValue2);//输出为:917142466
		//等价于
		System.out.println(mc.hashCode());//输出为:917142466
		
	}

}

class MyClass{
	
}

6.内部类

6.1 内部类概述

  1. 内部类:在类的内部又定义了一个新的类
  2. 内部类分类
内部类备注
静态内部类类似于静态变量
实例内部类类似于实例变量
局部内部类类似于局部变量
  1. 使用内部类编写的代码可读性很差,能不用尽量不用
class Test01 {
	// 静态变量
	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()中不能用
	}
}

6.2 匿名内部类

  1. 匿名内部类:类没有名字
  2. 匿名内部类是局部内部类中的一种
  3. 学习匿名内部类主要是让大家以后在阅读别人代码的时候能理解

示例代码

当不使用匿名内部类的时候

package toString;

public class Test08 {

	public static void main(String[] args) {
		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);
	}
}

当使用匿名内部类的时候,不需要对接口进行实现

package toString;

public class Test08 {

	public static void main(String[] args) {
		MyMath mm = new MyMath();
		//使用匿名内部类
		//表面看上去好像是接口可以直接new,实际上并不是接口可以new,后面的{}代表了对接口的实现
		mm.mySum(new Compute(){
		public int sum(int a, int b) {
		return a + b;
	   }
	}, 100, 200);
	}

}

//负责计算的接口
interface Compute{
	
	//抽象方法
	int sum(int a,int b);
}

//数学类
class MyMath{
	
	//数学求和
	public void mySum(Compute c, int x, int y) {
		int retValue = c.sum(x, y);
		System.out.println(x + "+" + y + "=" + retValue);
	}
}

总结
不建议使用匿名内部类,因为一个类没有名字,没有办法重复使用,另外代码太乱,可读性太差

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值