Object
Object 类属于java.lang 包,其方法被所有类继承,其中常用的方法如下:
- equals:判断当前对象是否与指定对象相同
- finalize:当对象不存在对象引用时,由对象的垃圾回收器调用该方法
- getClass:返回该对对象的运行时类
- hashCode:返回该对象的哈希值
- toString:返回该对对象的字符串表示
equals
之前提到的关系运算符中通过双等于 == 可以实现等值判断,其与equals 的关联如下:
- 在Object 的equals 方法中,通过 == 实现相等判断
public boolean equals(Object obj) {
return (this == obj);
}
- 由于Java 中只存在值传递,因此在对引用类型直接进行相等判断的时候其实是在判断是否为同一个对象
在实际使用中,通常是判断两个对象的内容是否相同,因此子类通常会重写equals 方法。比如Integer 类对equals 进行的重写如下:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
// 比较实际值是否相等
return value == ((Integer)obj).intValue();
}
return false;
}
hashCode
用于返回对象的哈希码值,支持此方法是为了提高哈希表的性能
哈希值通过对象的地址计算获得,简单地说:指向相同对象的两个引用,其哈希值相同;反过来,如果哈希值不同则说明两个引用指向两个不同的对象
equals 和hashCode 方法要满足一致性原则:
- 如果两个对象根据 equals 方法是相等的,那么它们的 hashCode 必须相同。
- 反之,如果两个对象的 hashCode 相同,它们不一定相等。但是,hashCode 相同是 equals 方法进一步比较的前提条件。
toString
默认返回全类名 + @ + 哈希值的十六进制,源码如下:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
与equals 方法类似,实际使用时通常会输出对象的部分或全部内容,所以会对其进行重写
当需要打印对象或者使用+ 进行拼接运算时都会自动调用对象的toString() 方法
finalize
当对象没有被引用时,JVM 就会认为这是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,而该方法会在销毁前被调用。子类可以重写该方法,以在对象被销毁前完成一些资源释放的操作
public class Test {
public static void main(String[] args) {
Test test = new Test();
// 手动置null,使Test 对象成为不具任何引用的垃圾对象
test = null;
// 手动触发垃圾回收机制,在这之前会调用 finalize() 方法
System.gc();
}
@Override
protected void finalize() throws Throwable {
System.out.println("释放资源中....");
}
}
断点调试
可以查看程序的执行过程,从而发现问题所在,实现排错。并且在调试过程中,程序是属于运行状态的,期间是以对象的运行类型进行的
调试步骤如下:
1、设置断点(可以在调试过程中动态的设置断点)
2、启动调试
3、观察调试信息
步过
快捷键为F8,对应位置如下: - > 其作用为逐行执行代码
步入
快捷键为F7,对应位置如下: - > 其作用为进入方法(用于查看方法源码,如果正常进不去就可以使用alt + shift + F7 强制步入)
步出
快捷键为shift + F8,对应位置如下: - > 其作用为退出方法
恢复程序
快捷键为F9,位置如下: - >其作用为恢复程序运行,直到下一个断点
练习
对象创建流程
1、加载类信息
源代码
加载类前
加载类后
2、初始化(默认 - 显式 - 构造器)
3、返回内存地址
动态绑定分析
零钱通
基础版本
package com.liu.coinPass;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/**
* 零钱通系统
*/
public class CoinPassSys {
public static void main(String[] args) {
// 用于循环判断是否退出系统
boolean loop = true;
// 用于拼接收益入账信息
String details = "";
// 循环展示零钱通菜单
menuDisplay(details, loop);
}
/**
* 显示零钱通菜单并处理用户选择
*
* @param details 账户的详细信息,包括账单明细等
* @param loop 控制菜单是否继续显示的标志,通常初始化为true
*/
private static void menuDisplay(String details, boolean loop) {
// 用于获取用户输入,判断操作类型
Scanner scanner = new Scanner(System.in);
// 获取当前时间,用于拼接入账信息
Date date = new Date();
// 用于格式化日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 获取余额,用于拼接入账信息
double money = 0;
// 获取入账金额,用于拼接入账信息
double moneyIn = 0;
// 获取消费金额,用于拼接消费信息
double moneyOut = 0;
// 获取消费地址,用于拼接消费信息
String address = "";
// 获取用户退出操作数,用于判断是否退出系统、
char choice = ' ';
//
do {
System.out.println("-----------------零钱通菜单-----------------");
System.out.println("\t\t\t1. 账单明细");
System.out.println("\t\t\t2. 收益入账");
System.out.println("\t\t\t3. 消费情况");
System.out.println("\t\t\t4. 退 出");
// 接收用户输入,判断操作类型
System.out.print("请选择(1-4):");
int key = scanner.nextInt();
switch (key) {
case 1:
System.out.println("----------账单明细----------");
System.out.println(details);
break;
case 2:
System.out.print("请输入收益金额:");
moneyIn = scanner.nextDouble();
if (moneyIn <= 0) {
System.out.println("收益金额必须大于0!");
break;
}
money += moneyIn;
details += "\n" + sdf.format(date) + " 收益入账:" + moneyIn + ",余额:" + money;
System.out.println("收益入账成功!");
break;
case 3:
System.out.print("请输入消费地址:");
address = scanner.next();
System.out.print("请输入消费金额:");
moneyOut = scanner.nextDouble();
if (moneyOut > money) {
System.out.println("余额不足!");
break;
} else if (moneyOut < 0) {
System.out.println("消费金额必须大于0!");
}
money += moneyOut;
details += "\n" + sdf.format(date) + " 消费:" + moneyOut + ",消费地址:" + address + ",余额:" + money;
break;
case 4:
do {
System.out.print("确定要退出吗?(y/n):");
choice = scanner.next().charAt(0);
if (choice == 'y' || choice == 'Y') {
loop = false;
System.out.println("准备退出系统!");
break;
} else if (choice == 'n' || choice == 'N') {
System.out.println("继续使用系统!");
break;
} else {
System.out.println("输入有误,请重新输入!");
}
} while (true);
}
} while (loop);
}
}
OOP 版本
package com.liu.coinPass;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/**
* 零钱通系统
*/
public class CoinPassSys {
// 获取当前时间,用于拼接入账信息
Date date = new Date();
// 用于格式化日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 退出标志,用于循环判断是否退出系统
boolean loop = true;
// 用于拼接收益入账信息
String details = "";
// 获取余额,用于拼接入账信息
double money = 0;
// 获取入账金额,用于拼接入账信息
double moneyIn = 0;
// 获取消费金额,用于拼接消费信息
double moneyOut = 0;
// 获取消费地址,用于拼接消费信息
String address = "";
// 获取用户退出操作数,用于判断是否退出系统、
char choice = ' ';
// 用于获取用户输入,判断操作类型
Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
// 启动系统
new CoinPassSys().menuDisplay();
}
/**
* 打印账单信息
*
* @param details 账单明细字符串
*/
public void billingDetails(String details) {
System.out.println(details);
}
/**
* 计算收益信息
*/
public void income() {
System.out.print("请输入收益金额:");
moneyIn = scanner.nextDouble();
if (moneyIn <= 0) {
System.out.println("收益金额必须大于0!");
return;
}
// 更新余额
money += moneyIn;
details += "\n" + sdf.format(date) + " 收益入账:" + moneyIn + ",余额:" + money;
System.out.println("收益入账成功!");
}
/**
* 计算消费信息
*/
public void consume() {
System.out.print("请输入消费地址:");
address = scanner.next();
System.out.print("请输入消费金额:");
moneyOut = scanner.nextDouble();
if (moneyOut > money) {
System.out.println("余额不足!");
return;
} else if (moneyOut < 0) {
System.out.println("消费金额必须大于0!");
}
// 更新余额
money -= moneyOut;
details += "\n" + sdf.format(date) + " 消费:" + moneyOut + ",消费地址:" + address + ",余额:" + money;
}
/**
* 退出系统
*/
public void exit() {
do {
System.out.print("确定要退出吗?(y/n):");
choice = scanner.next().charAt(0);
if (choice == 'y' || choice == 'Y') {
// 更新退出标志
loop = false;
System.out.println("准备退出系统!");
break;
} else if (choice == 'n' || choice == 'N') {
System.out.println("继续使用系统!");
break;
} else {
System.out.println("输入有误,请重新输入!");
}
} while (true);
}
/**
* 显示零钱通菜单并处理用户选择
*/
private void menuDisplay() {
do {
System.out.println("-----------------零钱通菜单-----------------");
System.out.println("\t\t\t1. 账单明细");
System.out.println("\t\t\t2. 收益入账");
System.out.println("\t\t\t3. 消费情况");
System.out.println("\t\t\t4. 退 出");
// 接收用户输入,判断操作类型
System.out.print("请选择(1-4):");
int key = scanner.nextInt();
switch (key) {
case 1:
System.out.println("----------账单明细----------");
billingDetails(details);
break;
case 2:
income();
break;
case 3:
consume();
break;
case 4:
exit();
break;
default:
System.out.println("输入有误,请重新输入!");
}
} while (loop);
}
}