Java 黑马程序员学习笔记(进阶篇8)
1. 包装类
① 包装类的作用
Java 中的基本数据类型(如 int
、double
、char
、boolean
等)本身不是 “对象”,但很多场景需要以 “对象形式” 操作(比如集合 List
只能存储对象)。因此 Java 提供了包装类,用于将基本数据类型转换为对象,让基本类型能参与 “面向对象” 的操作(如泛型、对象方法调用等)。
② 基本类型与包装类的对应关系:
基本类型 | 包装类 |
---|---|
int | Integer |
double | Double |
char | Character |
boolean | Boolean |
③ 自动装箱与自动拆箱
Java 提供自动转换机制,让基本类型和包装类能 “无缝转换”:
自动装箱:基本类型 → 包装类对象
示例:Integer a = 10;
(将int
类型的10
自动包装为Integer
对象)。自动拆箱:包装类对象 → 基本类型
示例:int b = a;
(将Integer
对象a
自动拆分为int
类型的值)。
④ 代码现象:
观察以下代码的输出差异:
public class IntegerDemo {public static void main(String[] args) {// 情况1:值为127(-128~127范围内)Integer i1 = 127;Integer i2 = 127;System.out.println(i1 == i2); // 输出:true// 情况2:值为129(超出-128~127范围)Integer i3 = 129;Integer i4 = 129;System.out.println(i3 == i4); // 输出:false}
}
底层原因:Integer 缓存池机制:
Java 对 Integer
类型做了缓存优化,核心逻辑在 Integer.valueOf()
方法中:
- 当自动装箱(把
int
转Integer
)时,会调用Integer.valueOf(int i)
。 - 该方法内部维护了一个缓存池,存储了
-128 ~ 127
范围内的Integer
对象。 - 如果数值在
-128 ~ 127
之间,直接从缓存池获取已存在的对象;如果超出范围,则新建Integer
对象。
⑤ Integer 类成员方法
方法签名 | 功能说明 |
---|---|
public static String toBinaryString(int i) | 将 int 类型整数 i 转换为二进制字符串(如 i=5 → 返回 "101" )。 |
public static String toOctalString(int i) | 将 int 类型整数 i 转换为八进制字符串(如 i=9 → 返回 "11" )。 |
public static String toHexString(int i) | 将 int 类型整数 i 转换为十六进制字符串(如 i=17 → 返回 "11" ;十六进制中 10~15 用 a~f 表示,默认小写)。 |
public static int parseInt(String s) | 将字符串形式的整数(如 "123" )转换为 int 类型(若字符串非合法整数,会抛出 NumberFormatException )。 |
⑥ 键盘录入建议:优先使用 nextLine()
(1) 使用 Scanner
进行键盘输入时,建议统一用 nextLine()
方法,原因是:
nextLine()
的特点是 **“遇到回车才结束输入”,能完整接收一行内容 **(包括空格、特殊字符等)。- 对比
next()
:next()
遇到 “空格、制表符、回车” 等分隔符就会停止,无法接收带空格的内容(如输入"hello world"
会被截断为"hello"
),灵活性不足。
(2) 接收内容的 “形式”:始终为字符串
nextLine()
的返回值是 String
类型,无论你输入的是:
- 纯数字(如
123
); - 带空格的文本(如
hello world
); - 仅包含特殊字符(如
abc
,前后带空格);
都会被以字符串的形式完整接收。
如果需要将输入的 “数字字符串” 转为 int
/double
等类型,需额外调用 Integer.parseInt()
、Double.parseDouble()
等方法。
2. 综合练习
① 题目 1:实现十进制转二进制字符串(自定义方法)
请编写一个 Java 程序,包含一个方法toBinaryString
,该方法接收一个正整数作为参数,通过 “除 2 取余” 的方式手动将其转换为对应的二进制字符串(要求不使用Integer
类的toBinaryString
内置方法)
示例:
- 输入:123 → 输出:"1111011"
- 输入:5 → 输出:"101"
package demo3;public class test6 {public static void main(String[] args) {toBinaryString(123);}public static String toBinaryString (int number) {StringBuilder sb = new StringBuilder();while (true) {if (number == 0) {break;}int remaindar = number % 2;sb.insert(0, remaindar); //不太理解System.out.print(remaindar);number = number / 2;}return sb.toString();}
}
关键逻辑: sb.insert(0, remaindar);
(1) StringBuilder
的 insert(0, 内容)
方法表示:在字符串的最前面(索引 0 的位置)插入内容。
(2) 结合上面的例子(转换 5 的过程):
- 第一次得到余数
1
,执行sb.insert(0, 1)
→sb
中是"1"
(此时是最低位,暂时放在前面); - 第二次得到余数
0
,执行sb.insert(0, 0)
→ 在"1"
的前面插入0
,sb
变成"01"
; - 第三次得到余数
1
,执行sb.insert(0, 1)
→ 在"01"
的前面插入1
,sb
变成"101"
。
(3) 最终 sb
中的内容就是正确的二进制 101
。
② 题目 2:计算从出生日期到今天的总天数
请编写一个 Java 程序,实现以下功能:
(1) 定义一个字符串表示出生日期(格式为 "yyyy 年 MM 月 dd 日",例如 "2000 年 1 月 1 日");
(2) 将该字符串日期转换为对应的毫秒值(时间戳);
(3) 获取当前系统时间的毫秒值;
(4) 计算两个时间戳的差值,并转换为总天数(忽略时分秒差异,只按日期计算);
(5) 输出计算得到的总天数。
要求:使用SimpleDateFormat
进行日期解析,注意处理可能的解析异常。
package demo3;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class test8 {public static void main(String[] args) throws ParseException {//1.计算出生年月日的毫秒值,第一部分都不太了解String birthday = "2000年1月1日";SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");Date date = sdf.parse(birthday);long birthdayTime = date.getTime();//获取当前时间的毫秒值long todayTime = System.currentTimeMillis();long time = todayTime - birthdayTime;System.out.println(time / 1000 / 60 / 60 / 24);}
}
关键逻辑 1: String birthday = "2000年1月1日";
- 作用:定义一个字符串类型的日期,表示要计算的出生日期。
- 注意:字符串的格式必须明确(这里是 “年 - 月 - 日” 带中文单位的格式),后续解析时需要严格匹配这个格式。
关键逻辑 2: SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
- 作用:创建一个日期格式化工具,用于 “字符串日期 ↔ Date 对象” 的转换。
- 关键:构造参数
"yyyy年MM月dd日"
是模式字符串,用于指定日期的格式:yyyy
:4 位年份(如2000
);MM
:2 位月份(如01
代表 1 月,注意月份必须用MM
,小写mm
代表分钟);dd
:2 位日期(如01
代表 1 日);- 中间的
"年"
、"月"
、"日"
是固定分隔符,必须和字符串birthday
中的分隔符完全一致(否则解析会报错)。
关键逻辑 3: Date date = sdf.parse(birthday);
- 作用:将字符串类型的日期(
birthday
)解析为Date
类型的对象(date
)。 - 原理:
SimpleDateFormat
的parse
方法会按照之前定义的模式("yyyy年MM月dd日"
),把字符串中的 “年、月、日” 提取出来,转换成一个包含该日期信息的Date
对象。 - 异常:如果字符串格式和模式不匹配(比如字符串是
"2000-1-1"
而模式是"yyyy年MM月dd日"
),会抛出ParseException
,因此代码在main
方法上声明了throws ParseException
来处理这个异常。
关键逻辑 4: long birthdayTime = date.getTime();
- 作用:获取
Date
对象对应的毫秒值(时间戳)。 - 时间戳概念:Java 中时间戳的基准是 1970 年 1 月 1 日 00:00:00 GMT(格林威治标准时间),
getTime()
方法返回的是 “从基准时间到当前Date
对象表示的时间” 所经过的毫秒数(1 秒 = 1000 毫秒)。 - 举例:如果
date
是 2000 年 1 月 1 日,birthdayTime
就是从 1970 年 1 月 1 日到 2000 年 1 月 1 日的总毫秒数。
方法二:
package demo4;import java.time.LocalDate;
import java.time.temporal.ChronoUnit;public class test1 {public static void main(String[] args) {LocalDate ld1 = LocalDate.of(2000,1,1);LocalDate ld2 = LocalDate.now();long days = ChronoUnit.DAYS.between(ld1,ld2);System.out.println(days);}
}