基础加强
思维导图概览内容
异常机制
异常机制的本质
当程序出现错误,程序安全的、继续执行的机制。
案例代码
try {
copyFile("d:/a.txt", "e:/a.txt")
} catch (Exception e) {
e.printStackTrace();
}Exception 概念
所谓异常处理,就是指程序在出现问题时依然可以正确的执行完。
异常的分析
public class Test {
public static void main(String[] args) {
System.out.println("111");
int a = 1 / 0;
System.out.println("222");
}
}Exception in thread "main" java.lang.ArithmeticException: / by zero # 算数异常当前代码只能执行到输出"111",如果添加try catch,则能继续往前走,输出"222"
public class Test {
public static void main(String[] args) {
System.out.println("111");
try {
int a = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("222");
}
}**捕获异常:**JRE 得到该 异常之后,寻找相应的代码来处理该异常。JRE 在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。
异常分类
所有的异常对象都是派生于Throwable类的一个实例。如果内置的异常类不能够满足需要,还可以创建自己的异常类。
所有的异常的根类是java.lang.Throwable,它下面又派生了 2 个子类:Error和Exception,我们通常只能管Exception。
布尔型(boolean)
- boolean 类型有 2 个常量值,true 和 false
- 在内存中占用一个字节【布尔数组】或 4 个字节【一般时候都是 4 个字节】,不可以使用 0 或非 0的整数替代 true 和 false,这点和 C 语言不一样
public class TestBoolean {
public static void main(String[] args) {
boolean b1 = true;
boolean b2 = false;
if (b1) {
System.out.println("b1是true!");
} else {
System.out.println("b1是false!");
}
}
}尽量避免写
b1 == true来判断,虽然是正确的,但是不推荐。
方法的重载
一个类中可以定义多个名称相同,但参数列表不同的方法。
构成方法重载的条件
- 形参列表不同的含义
- 形参类型不同
- 形参个数不同
- 形参顺序不同
- 只有返回值不同,不构成方法的重载
- 只有形参的名称不同,不构成方法的重载
static
静态变量/静态方法生命周期和类相同,在整个程序执行期间都有效。
- 为该类的公共变量,属于类,被该类的所有实例共享,在类载入的时候被初始化
static成员变量只有一份- 一般用“类名.类变量/方法” 来调用
- 在
static方法中不可直接访问非static的成员
静态初始化块
构造方法用于对象的普通属性初始化
静态初始化块,用于类的初始化操作,初始化静态属性
在静态初始化块中不能直接访问非static成员
注意事项
静态初始化块执行顺序:上溯到Object类,先执行Object的静态初始化块,再向下执行子类的静态初始化块,直到类的静态初始化块为止。构造方法执行顺序和上面顺序一样
try-with-resource 自动关闭 Closable 接口的资源
JVM 的垃圾回收机制可以对内部资源实现自动回收,给开发者带来了极大的便利。但是 JVM 对外部资源(调用了底层操作系统的资源)的引用却无法自动回收,例如数据库连接,网络连接以及输入输出 IO 流等。这些连接就需要我们手动去关闭,不然会导致外部资源泄露,连接池溢出以及文件被异常占用等。
JDK7 之后新增了try-with-resource,它可以自动关闭实现了Closable接口的类。
public class Test01 {
public static void main(String[] args) {
try (FileReader reader = new FileReader("d:/a.txt")) // 这个里面的代码会自动关闭连接 {
// ...
// ...
} catch (Exception e) {
e.printStackTrace();
}
}
}在编译时,仍然会编译成try-catch-finally,只是代码变简单了。
基本数据类型的包装类
Java 为每一个基本数据类型提供了相应的包装类。我们经常用到的基本数据类型就不是对象,但是我们在实际应用中经常需要将基本数据类型转化为对象,以便于操作。为了解决这个不足,Java 在设计类时为每个基本数据类型设计了一个对应的类进行代表。包装类位于java.lang包
| 基本数据类型 | 包装类 |
|---|---|
| byte | Byte |
| boolean | Boolean |
| short | Short |
| char | Character |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
用的最多的是 Integer和Long
Number 类继承体系
说明:
Number是一个抽象类,位于java.lang包,是数字包装类的父类Number提供了将数值转换为其基本类型的常用方法Byte,Short,Integer,Long,Float,Double都继承自Number类
初识包装类
public class Test {
public static void main(String[] args) {
Integer i = new Integer(50); // 从 jdk9 开始被废弃
Integer j = Integer.valueOf(10); // 官方推荐
}
}内存分析(栈与堆)
或者使用类图形式展示内存引用关系:
valueOf源码里有对应的缓存的过程。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache[i + (-IntegerCache.low)];
return new Integer(i);
}public class Test {
public static void main(String[] args) {
Integer i = new Integer(50); // 从 jdk9 开始被废弃
Integer j = Integer.valueOf(10); // 官方推荐
int a = j.intValue(); // 把包装类对象转换成基本数据类型
double b = j.doubleValue();
// 把字符串转换成数字 不能包含非数字的字符串
Integer m = Integer.valueOf("123");
}
}主要用途
- 作为和基本数据类型对应的类型存在,方便涉及到对象的操作,如
Object[]、集合等的操作 - 包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法(这些操作方法的作用是在基本数据类型、包装类对象、字符串对象、字符串之间提供相互之间的转化!)
自动装箱和拆箱
将基本数据类型和包装类型自动转化。
自动装箱
Integer i = 100; // 自动装箱,相当于编译器自动为您作以下的语法编译:
Integer i = Integer.valueOf(100);自动拆箱
Integer x = 100;
int y = x; // 编译器:int y = x.intValue();注意:空指针异常
Integer z = null;
int z2 = z; // 编译器: int z2 = z.intValue();包装类的缓存问题
Integer x1 = 100; // Integer x1 = Integer.valueOf(100);
Integer x2 = 100;
Integer x3 = 1000;
Integer x4 = 1000;
System.out.println(x1 == x2); // true
System.out.println(x3 == x4); // false
System.out.println(x1.equals(x2)); // true 值相等
System.out.println(x3.equals(x4)); // true 值相等包装类缓存机制详解
现象解释:
x1 == x2返回true:因为100在缓存范围-128~127内,valueOf()返回的是缓存池中的同一个对象引用x3 == x4返回false:因为1000超出缓存范围,每次valueOf()都会创建新的对象equals()比较的是值,所以无论是否缓存都返回true
Integer Cache 工作原理:
Java 源码实现(JDK 8):
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// 初始化缓存数组,加载 -128 到 high 之间的所有整数对象
high = sun.misc.VM.getIntProperty("sun.java.lang.Integer.cache.high", 127);
cache = new Integer[high - low + 1];
for (int i = 0; i < cache.length; i++) {
cache[i] = new Integer(i + low);
}
}
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache[i + (-IntegerCache.low)]; // 直接返回缓存对象
return new Integer(i); // 创建新对象
}关键要点:
- 缓存范围:默认为
-128 ~ 127,可通过 JVM 参数sun.java.lang.Integer.cache.high调整 - == 运算符:比较的是引用地址(内存地址),不是值
- equals() 方法:
Integer重写了equals(),比较的是数值大小 - 最佳实践:比较数值用
equals(),不要使用==
扩展思考:
boolean、byte、short、char等包装类也有类似的小数值缓存机制- Java 通过缓存减少内存占用,提高性能
字符串相关类
String类代表的是不可变的字符序列
StringBuilder和StringBuffer类代表的是可变的字符序列
**可变还是不可变都是final**来决定的
**底层都是unicode**字符集
public final class String {
/** The value is used for character storage. */
private final char value[];
}StringBuilder 和 StringBuffer
- StringBuffer:线程安全,做同步检查,效率低
- StringBuilder:线程不安全,不做线程同步检查,效率高,一般使用它
public class Test {
public static void main(String[] args) {
String str = "abc";
StringBuilder sb = new StringBuilder("abc");
StringBuffer sb2 = new StringBuffer("abc");
}
}常用方法列表
- 重载的
public StringBuilder append(...)方法,append方法,可以为该StringBuilder对象添加字符序列,仍然返回自身对象 - 方法
public StringBuilder delete(int start, int end)可以删除从start开始到end-1为止的一段字符序列,仍然返回自身对象 public StringBuilder deleteCharAt(int index),移除此序列指定位置上的char,仍然返回自身对象public StringBuilder insert(...),可以为该StringBuilder对象在指定位置插入字符序列,仍然返回自身对象public StringBuilder reverse(),用于将字符序列逆序,仍然返回自身对象
不可变字符序列陷阱
String str = "";
long num1 = Runtime.getRuntime().freeMemory(); // 获取系统剩余内存空间
long time1 = System.currentTimeMillis(); // 获取系统的当前时间
for (int i = 0; i < 5000; i++) {
str = str + i; // 相当于产生了 5000 个对象
}StringBuilder sb = new StringBuilder("");
for (int i = 0; i < 5000; i++) {
sb.append(i);
}遇到拼接的情况就用StringBuilder。上面会占用很大的内存空间。
时间处理类
我们把1970年 1 月 1 日 00:00:00 作为基准时间,每个度量单位是毫秒,1 秒的千分之一。
我们用long类型的变量来表示时间,从基准时间前后几亿年都能表示。
这个“时刻数值”是所有时间类的核心值,年月日都是根据这个“数值”计算出来的。
java.util.Datejava.sql.Datejava.sql.Timejava.sql.TimeStamp
java.util.Calendarjava.util.GregorianCalendar
java.text.DateFormatjava.text.SimpleDateFormat
Date分配一个Date对象,并初始化次对象为系统当前的日期和时间,可以精确到毫秒boolean equals(Object obj)比较 2 个日期的想等性,比较的是毫秒数DateFormat 是抽象类
SimpleDateFormat 是子类
DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); // 格式化,格式可变,字母一般不变
String str = "2026-10-1 10:10:10"; // 真实的字符串
Date guoqing = format.parse(str); // 这里需要抛出异常 ParseException如果字符串str里的格式和format的格式不匹配,比如2026,10,1 10:10:10,这样就会出现异常
DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = new Date();
String str = format.format(date); // 将时间转换为格式化后的字符串SimpleDateFormat 模式字符含义
SimpleDateFormat使用特定的字母来表示不同的时间单位:
| 模式字符 | 含义 | 示例值 |
|---|---|---|
y | 年 | 2026 |
M | 月 | 10 |
d | 日 | 1 |
h | 小时 (1~12) | 10 |
H | 小时 (0~23) | 22 |
m | 分钟 | 30 |
s | 秒 | 45 |
S | 毫秒 | 123 |
E | 星期 | 星期二 |
a | 上午/下午标记 | 上午 |
格式化示例:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS E");
Date date = new Date(); // 假设当前时间是 2026 年 10 月 1 日 22:30:45.123,星期二
String str = format.format(date);
// 输出:2026-10-01 22:30:45.123 星期二注意事项:
- 区分大小写:
HH表示 24 小时制(0~23),hh表示 12 小时制(1~12) - 不可变字符:大写字母如
Y,M,d,H,m,s等代表实际的时间单位 - 特殊字符转义:需要用单引号包裹的字符,如
'年'月'日 ` - 线程不安全:
SimpleDateFormat不是线程安全的,多线程环境下应使用局部变量或synchronized - Java 8+ 新 API:推荐使用
java.time包中的DateTimeFormatter替代SimpleDateFormat
带转义字符的示例:
SimpleDateFormat format = new SimpleDateFormat("'当前时间:'yyyy 年 MM 月 dd 日 HH:mm:ss");
Date date = new Date();
String str = format.format(date);
// 输出:当前时间:2026 年 10 月 01 日 22:30:45Date now = new Data();
DateFormat f1 = new SimpleDateFormat("今年的第 D 天,第w周"); // 小写的 w
String nowStr = f1.format(now);
System.out.println(nowStr); // 公历来计算的Calendar类是一个抽象类,为我们提供了关于日期计算的功能,比如:年、月、日、时、分、秒的展示和计算。
注意月份的表示是从 0 到 11 表示的,0 代表 1 月,12 月是 11,因此大多数人习惯于使用单词而不是使用数字来表示月份,这样程序也容易阅读
GregorianCalendar calendar = new GregorianCalendar(2049, 9, 1, 22, 10, 50);
int year = calendar.get(Calendar.YEAR); // 2049
int month = calendar.get(Calendar.MONTH) + 1; // 月 10 月
int day = calendar.get(Calendar.DAY_OF_MONTH);
int week = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 星期几,需要-1