# 学习目标
- 能够使用 System 类获取当前系统毫秒值
- 能够说出 BigDecimal 可以解决的问题
- 能够说出自动装箱、自动拆箱的概念
- 能够将基本类型转换为对应的字符串
- 能够将字符串转换为对应的基本类型
- 能够使用日期类输出当前日期
- 能够使用将日期格式化为字符串的方法
- 能够使用将字符串转换成日期的方法
- 能够使用 Calendar 类的 get、set、add 方法计算日期
# == 和 equals
==
- 基本数据类型比较数据的值是否相等
- 引用数据类型比较对象的地址是否相等
Object 类中有 equals(Object obj),Object 类中 equals 默认比较对象的地址。
我们不想比较对象的地址,我们想比较对象的成员变量怎么办?重写 equals 方法即可
public class Demo {
public static void main(String[] args) {
Teacher t1 = new Teacher("Jerry", 18);
Teacher t2 = new Teacher("Jerry", 18);
System.out.println(t1 == t2); // false
System.out.println(t1.equals(t2)); // true
}
}
public class Teacher {
private int age;
private String name;
public Teacher() {
}
public Teacher(String name, int age) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
// getClass() 方法用于获取对象的运行时对象的类,返回对象的类。
// System.out.println(getClass());
// System.out.println(o.getClass());
if (o == null || getClass() != o.getClass()) return false;
Teacher teacher = (Teacher) o;
if (age != teacher.age) return false;
return name.equals(teacher.name);
}
// 父类的equals方法不是我们想要的,我们想比较对象的属性,重写equals方法
// alt + insert -> equals() and hashCode()
// @Override
// public boolean equals(Object obj) {
// if (this == obj) return true;
//
// if (!(obj instanceof Teacher)) return false;
//
// // 把obj由Object转型Teacher,向下转型,使用子类特有内容
// Teacher t = (Teacher)obj;
//
// if (this.age != t.age || !this.name.equals(t.name)) return false;
//
// return true;
// }
}
# Objects
Objects 是 JDK1.7 提供的类, 他里面有很多静态方法帮助我们操作对象
常用方法:
public static boolean equals(Object a, Object b) 判断a和b是否相同,本质a.equals(b)
public static boolean isNull(Object obj) 判断是否为null,如果为null返回true
示例:
public class Demo {
public static void main(String[] args) {
Teacher t1 = null;
Teacher t2 = new Teacher("jerry", 18);
System.out.println(Objects.equals(t1, t2)); // false
boolean t1Null = Objects.isNull(t1);
boolean t2Null = Objects.isNull(t2);
System.out.println(t1Null); // true
System.out.println(t2Null); // false
}
}
一般来说类名后面带 s 都是工具类,里面会提供一些静态方法帮助我们操作某些对象,Arrays 数组工具类,他里面有很多操作数组的方法。
# Math
我们以前学过的 API 有:
- Scanner 类
- Random 类
- String 类
- ArrayList 类
Math 表示数学类,包含执行基本数字运算的方法。这个类将构造方法私有了,我们不能创建对象,他里面的方法都是 static 方法,直接使用类名调用即可
常用方法:
public static int abs(int a) 获取绝对值 absolute
public static double ceil(double a) 向上取整, 取更大一点的整数
public static double floor(double a) 向下取整, 取小一点的整数
public static double pow(double a, double b) a的b次幂的值
public static long round(double a) 四舍五入
示例:
public class Demo {
public static void main(String[] args) {
System.out.println(Math.abs(-1));
System.out.println(Math.ceil(1.2));
System.out.println(Math.floor(1.2));
System.out.println(Math.pow(2, 3));
System.out.println(Math.round(1.3));
System.out.println(Math.round(1.6));
}
}
# System
System 表示系统类。有和系统相关的方法。
常用方法:
public static void exit(int status) 退出JVM虚拟机,停止程序
public static long currentTimeMillis() 获取当前时间的毫秒值
// Object src: 源数组
// int srcPos: 从源数组哪个位置开始
// Object dest: 目标数组,拷贝后的数组
// int destPos: 放在目标数组哪个位置开始
// int length: 拷贝的数量
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 拷贝数组
示例:
public class Demo {
public static void main(String[] args) {
System.out.println(3);
// System.exit(0);
System.out.println(4);
System.out.println(System.currentTimeMillis());
int[] arr1 = {1, 2, 3, 4};
int[] arr2 = {0, 0, 0, 0, 0, 0, 0};
// public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
// 复制数组
// Object src: 源数组
// int srcPos: 从源数组哪个位置开始
// Object dest: 目标数组,拷贝后的数组
// int destPos: 放在目标数组哪个位置开始
// int length: 拷贝的数量
System.arraycopy(arr1, 2, arr2, 2, 2);
// 打印复制后的数组, 想看数组的内容
// Arrays.toString(arr2): 把数组的内容拼接成一个字符串
System.out.println(Arrays.toString(arr2)); // [0, 0, 3, 4, 0, 0, 0]
}
}
# BigDecimal
在计算机中 double 类型是有精度的小数,不是绝对准确的。
public class Demo07 {
public static void main(String[] args) {
// 小数在计算机中是近似值(浮点型)
double d = 0.1 + 0.2; // 期望 0.3
System.out.println("d = " + d); // 结果 0.30000000000000004
}
}
构造方法:
BigDecimal(double val) 将 double 转换为 BigDecimal
BigDecimal(String val) 将 String 转换为 BigDecimal
常用方法:
BigDecimal add(BigDecimal augend) 相加
BigDecimal subtract(BigDecimal subtrahend) 相减
BigDecimal multiply(BigDecimal multiplicand) 相乘
// 如果遇到除不尽的时候 divide 可以传入两个参数,参数二:要返回的BigDecimal商的保留位数 参数三:应用舍入模式
BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 相除
示例:
public class Demo {
public static void main(String[] args) {
// 小数在计算机中是近似值(浮点型)
double b = 0.1 + 0.2;
System.out.println(b); // 0.30000000000000004
BigDecimal b1 = new BigDecimal(0.4);
System.out.println(b1); // 0.40000000000000002220446049250313080847263336181640625
// 传入字符串时没有误差
BigDecimal b2 = new BigDecimal("0.4");
System.out.println(b2); // 0.4
BigDecimal b3 = new BigDecimal("0.2");
// 加
System.out.println(b3.add(b2)); // 0.6
BigDecimal b4 = new BigDecimal("0.6");
// 减
System.out.println(b4.subtract(b2)); // 0.2
BigDecimal b5 = new BigDecimal("0.1");
// 乘
System.out.println(b5.multiply(b4)); // 0.06
BigDecimal b6 = new BigDecimal("0.8");
// 除
System.out.println(b6.divide(b3)); // 4
BigDecimal b7 = new BigDecimal("20");
BigDecimal b8 = new BigDecimal("3");
// 报错,除不尽
// System.out.println(b7.divide(b8));
// 如果遇到除不尽的时候 divide 可以传入两个参数,参数二:要返回的BigDecimal商的比例 参数三:应用舍入模式
System.out.println(b7.divide(b8, 2, BigDecimal.ROUND_HALF_UP)); // 6.67
}
}
注意:
# 包装类
我们之前学过基本数据类型有 8 种,基本数据类型效率高,但是功能及其有限,只能做加减乘除运算。为了对基本数据类型进行更多的操作,Java 为每种基本数据类型提供了对应的类(包装类)
包装类不仅可以操作基本类型数据,还提供了方法,功能强大。
| 基本数据类型(关键字) | 包装类(类) |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
包装类的规律: 首字母大写,特殊的两个 int -> Integer, char -> Character
# int 转换为 String
- 方式一:加双引号即可
- 方式二:public static String valueOf(int i):返回 int 参数的字符串表示形式。该方法是 String 类中的方法
public class Demo1 {
public static void main(String[] args) {
// int转成String 5 -> "5"
String s1 = 5 + "";
String s2 = String.valueOf(5);
}
}
# String 转换为 int
调用 Integer.parseInt(String s) 方法
public class Demo1 {
public static void main(String[] args) {
// String转成int "5" -> 5 重点
int i = Integer.parseInt(s1);
// "6.66" -> double的 6.66
double d = Double.parseDouble("6.66");
}
}
# 自动装箱和自动拆箱
装箱:把基本数据类型转换为对应的包装类类型
拆箱:把包装类类型转换为对应的基本数据类型
Integer ig1 = Integer.valueOf(5); // 手动装箱
int i = ig1.intValue(); // 手动拆箱
// 自动装箱: 将基本数据类型转成包装类
Integer it3 = 6;
// 自动拆箱: 将包装类转成基本数据类型
int x = it3;
自动装箱和自动拆箱目的是简化代码
public class Demo2 {
public static void main(String[] args) {
Integer i1 = Integer.valueOf(1);
System.out.println(i1); // 1
int i2 = i1.intValue();
System.out.println(i2); // 1
// 自动装箱: 自动将基本数据类型转成包装类
Integer i3 = 3;
// 自动拆箱: 将包装类转成基本数据类型
int i4 = i3;
// 自动装箱和自动拆箱的原理是什么?
// 可以看到自动装箱底层使用: Integer i3 = Integer.valueOf(5);
// 自动拆箱: int i4 = i3.intValue();
Integer i5 = 2;
i5 += 2; // 这句代码做了什么操作
// i5 = i5 + 2;
// i5 + 2: i5.intValue() + 2
// i5 = i5 + 2: Integer.valueOf(i5.intValue() + 2)
System.out.println("i5 = " + i5);
}
}
# Date
Date 类代表了一个特定的时间,精确到毫秒。
Date 类构造方法:
Date() 创建Date对象,时间为执行这行代码的时间(当前时间)
Date(long date) 创建Date对象,时间是在 1970年1月1日 0时0分0秒 基础上增加参数指定的毫秒值
常用方法:
- long getTime() 得到当前对象的时间 和 1970 年 1 月 1 日 0 时 0 分 0 秒 相差的毫秒值
- void setTime(long time) 修改时间, 时间是在 1970 年 1 月 1 日 0 时 0 分 0 秒 基础上增加参数指定的毫秒值
示例:
public class Demo {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date); // Wed Apr 12 01:10:00 CST 2023
// 在标准时间 1970年1月1日 0时0分0秒基础上加上指定的毫秒值
Date date1 = new Date(5000);
System.out.println(date1); // Thu Jan 01 08:00:05 CST 1970
// long getTime() 获取日期和 1970年1月1日 0时0分0秒之间毫秒值
System.out.println(date.getTime()); // 1681233244101
// void setTime(long time) 了解, 在标准时间 1970年1月1日 0时0分0秒基础上加上指定的毫秒值
date.setTime(5000);
System.out.println(date); // Thu Jan 01 08:00:05 CST 1970
}
}
# DateFormat
为什么要 DateFormat 类?
Date主要表示1970年到某个时间的毫秒值,如果输出给用户看,用户是看不太懂的:
"Thu Oct 25 21:41:33 CST 2018"
用户实际上比较喜欢"2018年10月25日 21时41分33秒"
创建 DateFormat 对象: 是抽象类,使用子类 SimpleDateFormat
SimpleDateFormat 可以对 Date 对象,进行格式化和解析。
- 格式化(即日期文本) 将 Date 对象转成文字,调用 format 方法
Date对象 转为 xxxx年xx月xx日 xx:xx:xx
- 解析(文本日期) 将文字转成 Date 对象,调用 parse 方法
xxxx年xx月xx日 xx:xx:xx 转为 Date对象
为什么要将字符串转成 Date 对象?例如网站注册用户选择的年月日是一个字符串.需要将他转成 Date 保存到数据库中。
SimpleDateFormat 的构造方法:
public SimpleDateFormat() 构造一个SimpleDateFormat,使用默认格式
public SimpleDateFormat(String pattern) 构造一个SimpleDateFormat,使用指定的格式
SimpleDateFormat 格式化和解析日期:
public final String format(Date date) 将日期格式化成时间字符串
public Date parse(String source) 从给定字符串的开始解析文本以生成日期
# 练习
将 当前时间的 Date 对象 转成 "xxxx 年 xx 月 xx 日 xx 时 xx 分 xx 秒"
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
String formatDate = simpleDateFormat.format(date);
System.out.println(formatDate); // 2023年05月02日 00时36分16秒
}
将字符串 "2007-12-26 10:13:31" 转成 Date 对象
import java.text.ParseException;
public static void main(String[] args) throws ParseException {
String str = "2023-05-02 00:40:31";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parse = simpleDateFormat.parse(str); // parse方法会有红色的波浪线, alt + 回车 -> 第一个
System.out.println(parse); // Tue May 02 00:40:31 CST 2023
}
# Calendar
Date 类的缺点:Date 主要是表示 1970 的毫秒值,Date 类对单独获取年、月、日、时、分、秒、昨天、明天、上个星期、加上或减去一些时间不好处理。
Calendar 的作用:方便调整时间
如何创建 Calendar 对象
Calendar rightNow = Calendar.getInstance();
Calendar 类常用方法:
- int get(int field) 返回给定日历字段的值。
- void set(int field, int value) 将给定的日历字段设置为给定的值。
- void add(int field, int amount) 根据日历的规则,将指定的时间量添加或减去给定的日历字段。
Calendar 注意事项:月份的索引是从零开始的。
import java.util.Calendar;
public class Demo {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar); // 可以看到是可以得到很多信息的: java.util.GregorianCalendar[time=1682959695014,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2023,MONTH=4,WEEK_OF_YEAR=18,WEEK_OF_MONTH=1,DAY_OF_MONTH=2,DAY_OF_YEAR=122,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=48,SECOND=15,MILLISECOND=14,ZONE_OFFSET=28800000,DST_OFFSET=0]
test1();
test2();
test3();
}
// 获取某个时间字段的值
public static void test1() {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(Calendar.YEAR)); // 2023
System.out.println(calendar.get(Calendar.MONTH)); // 4 月份少一
System.out.println(calendar.get(Calendar.HOUR)); // 1
}
// 设置时间
public static void test2() {
Calendar calendar = Calendar.getInstance();
// 将年份设置成 2020 年
calendar.set(Calendar.YEAR, 2020);
System.out.println(calendar); // YEAR=2020 java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2020,MONTH=4,WEEK_OF_YEAR=18,WEEK_OF_MONTH=1,DAY_OF_MONTH=2,DAY_OF_YEAR=122,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=57,SECOND=25,MILLISECOND=947,ZONE_OFFSET=28800000,DST_OFFSET=0]
}
// 在现有基础上对时间进行相对的增、减
public static void test3() {
Calendar calendar = Calendar.getInstance();
// 在当前年份上加 10 年
calendar.add(Calendar.YEAR, 10);
System.out.println("调整后的日期" + calendar); // 调整后的日期java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2033,MONTH=4,WEEK_OF_YEAR=18,WEEK_OF_MONTH=1,DAY_OF_MONTH=2,DAY_OF_YEAR=122,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=55,SECOND=27,MILLISECOND=596,ZONE_OFFSET=28800000,DST_OFFSET=0]
}
}
# 练习
# 活了多少天
小贾出生日期为 2000 年 11 月 11 日,请计算小贾活了多少天了!
package demo09日期类练习2_计算活了多少天;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo {
public static void main(String[] args) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
Date birthday = simpleDateFormat.parse("1992年05月15日");
long birthdayTime = birthday.getTime();
Date nowDate = new Date();
long nowTime = nowDate.getTime();
long time = nowTime - birthdayTime;
long day = time / 1000 / 60 / 60 / 24;
System.out.println("小贾活了" + day + "天");
}
}
# 秒杀
秒杀开始时间: 2020 年 11 月 11 日 00:00:00
秒杀结束时间: 2020 年 11 月 11 日 00:10:00
小贾下单并付款的时间为:
2020 年 11 月 11 日 0:03:47
小皮单并付款的时间为:
2020 年 11 月 11 日 0:10:11
用代码说明这两位同学有没有参加上秒杀活动?
package demo08日期类练习1_秒杀案例;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
把秒杀开始时间转成毫秒值
把秒杀结束时间转成毫秒值
把小贾下单时间转成毫秒值
如果小贾下单时间毫秒值在开始和结束秒杀时间内,说明秒杀成功
核心思路就是将时间字符串转成毫秒值,对比下单的毫秒值是否在秒杀开始和结束的毫秒值范围内
将时间字符串 "2020年11月11日 00:00:00" -> 毫秒值,不能一步到位
"2020年11月11日 00:00:00" 解析为 Date对象 -> Date对象调用getTime() 得到毫秒值
*/
public class Demo {
public static void main(String[] args) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date startDate = simpleDateFormat.parse("2020年11月11日 00:00:00");
long startTime = startDate.getTime();
Date endDate = simpleDateFormat.parse("2020年11月11日 00:10:00");
long endTime = endDate.getTime();
Date xiaoJiaOrderDate = simpleDateFormat.parse("2020年11月11日 0:03:47");
long xiaojiaOrderTime = xiaoJiaOrderDate.getTime();
Date xiaoPiOrderDate = simpleDateFormat.parse("2020年11月11日 0:10:11");
long xiaoPiOrderTime = xiaoPiOrderDate.getTime();
if (xiaojiaOrderTime >= startTime && xiaojiaOrderTime <= endTime) {
System.out.println("小贾秒杀成功");
} else {
System.out.println("小贾秒杀失败");
}
if (xiaoPiOrderTime >= startTime && xiaoPiOrderTime <= endTime) {
System.out.println("小皮秒杀成功");
} else {
System.out.println("小皮秒杀失败");
}
}
}