包装类、日期等常用类型
第十三天
包装类、日期等常用类型
1.包装类简介
java语言是面向对象的语言,但是其中的八大基本数据类型不符合面向对象的特征。因此java为了弥补这样的缺点,为这八种基本数据类型专门设计了八种符合面向对象特征的的类型,这八种具有面向对象特征的类型,统称为包装类。
基本**数据类型** | 包装类型 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
2.装箱与拆箱
装箱boxing : 由基本数据类型转型为包装类型。
调用包装类的构造器,进行装箱
调用valueOf()静态方法,进行装箱
拆箱 unboxing: 由包装类型转型为基本数据类型。
调用xxxValue()非静态方法,进行拆箱
3.自动装箱与自动拆箱
为了方便获取包装类对象,从jdk1.5以后,提供了一个自动装箱的操作,也提供了一个包装类到基本数据类的自动拆箱操作。
自动装箱 : 直接使用一个基本数据类型的变量或字面值给一个包装类型的引用进行赋值即可
自动拆箱 : 直接使用一个包装类型的引用给一个基本数据类型的变量进行赋值即可
4.包装类常量池
对于装箱操作后的包装类的对象,jvm在堆中,维护了一个常量池,该常量池适用于调用了valueOf()方法产生的包装类对象,以及自动装箱的包装类对象。不适用于new关键字创建的包装类对象。
Byte, Short, Integer, Long 这 4 种包装类默认创建了数值 [-128, 127] 的相应类型的缓存数据,
Character 创建了数值在 [0, 127] 范围的缓存数据,
Boolean 直接返回 true 或 false。
Double和Float两个包装类并没有提供常量池
/*** 针对于装箱操作后的包装类的对象的常量池* Integer: -128~127*/ public class _03Box {public static void main(String[] args) {int num = 127;Integer i1 = Integer.valueOf(num); //装箱操作Integer i2 = Integer.valueOf(num);//使用 == 来判断是否是同一个对象System.out.println(i1==i2); //true,同一个对象 // Double和Float两个包装类并没有提供常量池double d1 = 3.14;Double d2 = Double.valueOf(d1);Double d3 = Double.valueOf(d1);System.out.println(d2==d3); boolean f = true;Boolean f1 = Boolean.valueOf(f);Boolean f2 = Boolean.valueOf(f);System.out.println(f1==f2); char c1 = 97; // 'a'Character c2 = Character.valueOf(c1);Character c3 = Character.valueOf(c1);System.out.println(c2==c3); //true} }/** * Integer.parseInt();* Integer.toBinaryString(); 转成二进制* Integer.toHexString(); 转成16进制* Integer.toOctalString() 转成8进制** public static int parseInt(String str):* public static Integer valueOf(String str):* 上述两个方法都用Integer类名调用,* 作用相似:都是将字符串转成整形,一个是转成int类型,一个是转成Integer类型*/
5.BigDecimal/BigInteger
因为使用八大基本数据类型做运算时,尤其是浮点数的运算时,容易精度不准确,所以java语言提供了BigDecimal这个类来完善这类运算,可以非常精确,可以精确到小数点后无数位。BigDecimal 通常支持任意位数的小数部分,用来对超过16位有效位的数进行精确的运算,(float可以精确到7左右,double是15位左右)
BigDecimal(int val) 创建一个具有参数所指定整数值的对象。
BigDecimal(double val) 创建一个具有参数所指定双精度值的对象。不推荐使用,因为存在精度丢失问题
BigDecimal(long val) 创建一个具有参数所指定长整数值的对象。
BigDecimal(String val) 创建一个具有参数所指定以字符串表示的数值的对象。 推荐使用
6.为什么不能用浮点数表示金额?
因为不是所有的小数都能用二进制表示(扩展知识中介绍为啥不能表示),所以,为了解决这个问题,IEEE提出了一种使用近似值表示小数的方式,并且引入了精度的概念。这就是我们所熟知的浮点数。
所以,浮点数只是近似值,并不是精确值,所以不能用来表示金额。否则会有精度丢失。比如0.1+0.2 != 0.3.
7.BigDecimal(double)和BigDecimal(String)有什么区别?
因为double是不精确的,所以使用一个不精确的数字来创建BigDecimal,得到的数字也是不精确的。如0.1这个数字,double只能表示他的近似值。
而对于BigDecimal(String) ,当我们使用new BigDecimal("0.1")创建一个BigDecimal 的时候,其实创建出来的值正好就是等于0.1的。
BigDecimal实际上一个BigDecimal是通过一个"无标度值"和一个"标度"来表示一个数的。
无标度值(Unscaled Value):这是一个整数,表示BigDecimal的实际数值。 标度(Scale):这是一个整数,表示小数点后的位数。 BigDecimal的实际数值计算公式为:unscaledValue × 10^(-scale)。
假设有一个BigDecimal表示的数值是123.45,那么无标度值(Unscaled Value)是12345。标度(Scale)是2。因为123.45 = 12345 × 10^(-2)。
8.为什么不能用BigDecimal的equals方法做等值比较?
因为BigDecimal的equals方法和compareTo并不一样,equals方法会比较两部分内容,分别是值(value)和标度(scale),而对于0.1和0.10这两个数字,他们的值虽然一样,但是精度是不一样的,所以在使用equals比较的时候会返回false。
9.无限精度的坑
BigDecimal 并不代表无限精度,当在两个数除不尽的时候,就会出现无限精度的坑,如下所示:
public static void main(String[] args){BigDecimal b1 = new BigDecimal("1.0");BigDecimal b2 = new BigDecimal("3.0");b1.divide(b2); } Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.at java.math.BigDecimal.divide(BigDecimal.java:1693)at com.demo.controller.Test.main(Test.java:29)
大致意思就是,如果在除法(divide)运算过程中,如果商是一个无限小数(如 0.333…),而操作的结果预期是一个精确的数字,那么将会抛出ArithmeticException异常。
此种情况,只需要在使用 divide方法时指定结果的精度即可:
public static void main(String[] args){BigDecimal b1 = new BigDecimal("1.0");BigDecimal b2 = new BigDecimal("3.0");System.out.println(b1.divide(b2,2, RoundingMode.HALF_UP));//0.33 }
在使用BigDecimal进行(所有)运算时,尽量指定精度和舍入模式。
10.传统的日期处理类型 方法
Date日期类型
1)构造器
Date() :获取当前系统时间
Date(long time):设置一个距离固定点的指定毫秒数的时间点。
2)方法
long getTime()
void setTime(long time)
String toString() 格式: Thu May 24 19:32:14 CST 2018
SimpleDateFormat类型
1)构造器
SimpleDateFormat()
SimpleDateFormat(String pattern):指定一个日期格式符号来构造对象
2)方法
final String format(Date date) DateàString
Date parse(String source) throws ParseException
3)日期模式匹配字符
字符 | 含义 | 案例 |
---|---|---|
y | 年 | yyyy年—2018年;yy-18年 |
M | 月 | MM月—05月;M月—5月 |
d | 日 | dd日—06日;d日—6日 |
E | 星期 | E-星期日(Sun) |
a | 上下午(AM、PM) | a—下午(PM) |
H | 24小时制 | a h时--------下午 10时 HH:mm:ss------12:21:34 hh(a):mm:ss------12(PM):21:34 |
h | 12小时制 | |
m | 分钟 | |
s | 秒 |
Calendar类型
1)getInstance方法
Calendar提供了一个类方法getInstance,以获取此类型的一个通用的对象
此方法返回一个Calendar对象,其日历字段已经由当前日期和时间初始化
2)日期与时间分量方法
Calendar提供的get方法与一些常量合用可以获取日期及时间分量
Calendar提供的set方法与一些常量合用可以设置日期及时间分量
常量
static int YEAR 指定年份的字段数字
static int MONTH 指定月份的字段数字,0为一月
static int DATE 指示一个月份中的第几天
static int DAY_OF_WEEK 指定一个星期中的某天,1为星期日
static int WEEK_OF_MONTH 指定一个月中的第几周
static int WEEK_OF_YEAR 指定一年中的第几周
3)其他方法
getActualMaximum方法
int getActualMaximum(int field)
作用:指定一个时间常量,返回指定日历分量可能拥有的最大值。
注意:要保证年月日的日数字小于等于28
add方法
void add(int field , int mount)
作用:指定一个时间常量,在此时间分量上增加指定的数值,若为负值,则是减去指定的数值。
getTime与setTime
Date getTime()
作用:返回一个Date类型来描述日期
void setTime(Date d)
作用:用Calendar表示Date所描述的日期
Date类型的不足
时区问题:
Date
类不处理时区信息,它只表示一个时间点,通常默认为 GMT(格林威治标准时间)。这导致了很多时区相关的问题,因为日期和时间需要根据时区进行转换和显示。线程不安全性:
Date
类是可变的,这意味着你可以修改它的值。由于 Java 中的日期和时间操作通常需要是线程安全的,这种可变性可能导致并发问题。
LocalDateTime类简介
java.time.LocalDateTime
类是 Java 8 引入的日期时间类,它解决了许多java.util.Date
类存在的问题,并提供了更好的方式来处理日期和时间信息。以下是关于 LocalDateTime
为什么更好的一些原因:
线程安全性:
java.time.LocalDateTime
是不可变的,这意味着一旦创建了对象,它的值不能被修改。这保证了在多线程环境中使用时不会出现并发问题,与java.util.Date
的可变性形成鲜明对比。时区支持:
LocalDateTime
提供了更好的时区支持。它存储日期和时间信息,但不包含时区信息。这允许你在需要时将日期时间信息与特定时区相关联,而不会像java.util.Date
那样受限于默认时区(通常是 GMT)。你可以使用ZoneId
来将LocalDateTime
转换为特定时区的时间更丰富的功能:
java.time
包中提供了一整套用于日期时间操作的类和方法,使日期时间处理更加方便和功能丰富。你可以轻松进行日期的加减、格式化、比较等操作,而无需手动编写复杂的代码。清晰的命名:
java.time
类使用了更直观和清晰的命名,不再存在像java.util.Date
中那样的命名混乱,例如getYear()
和getMonth()
方法的问题。
LocalDateTime常用操作
1.日期加减
使用 Date
进行日期加减操作通常需要使用 Calendar
类,而使用 LocalDateTime
更简单,例如:
// Date 加减 Date currentDate = new Date(); Calendar calendar = Calendar.getInstance(); calendar.setTime(currentDate); calendar.add(Calendar.DAY_OF_MONTH, 1); // 增加一天 Date tomorrow = calendar.getTime(); // LocalDateTime 加减 LocalDateTime currentDateTime = LocalDateTime.now(); LocalDateTime nextDay = currentDateTime.plusDays(1); // 增加一天
2. 格式化和解析
使用 Date
进行格式化和解析通常需要使用 SimpleDateFormat
,而 LocalDateTime
内置了格式化和解析功能:
// Date 格式化和解析 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = dateFormat.format(currentDate); Date parsedDate = dateFormat.parse(dateStr); // LocalDateTime 格式化和解析 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String dateTimeStr = currentDateTime.format(formatter); LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeStr, formatter);
3.转换
你可以在 Date
和 LocalDateTime
之间进行相互转换,例如:
// Date 转换为 LocalDateTime Date date = new Date(); Instant instant = date.toInstant(); LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime(); // LocalDateTime 转换为 Date LocalDateTime localDateTime = LocalDateTime.now(); ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault()); Date date = Date.from(zonedDateTime.toInstant());
这里需要注意的是,转换时需要考虑时区的影响,因为 Date
不包含时区信息,而 LocalDateTime
存储的是本地时间。所以,转换时需要确定时区信息以保持一致性。
总之,LocalDateTime
提供了更简单和直观的日期时间操作,同时也可以与 Date
进行转换,以兼容传统的日期时间处理方式。然而,使用 LocalDateTime
更推荐,尤其在新的 Java 8+ 应用中,因为它更强大、更安全,同时提供了更多的功能。