1. 反射
1.1 认识反射
反射允许对封装类的字段 方法和构造函数进行编程访问 用人话来说 就是用将一个类的成员变量 构造方法 成员方法全部提取出来 例如在idea中 自动提示就是靠反射来实现的
对于字段 构造方法 成员方法这些 我们有可以获得他们一些具体的值 例如修饰词 名字 形参等
如图所示 我们先要获取一个类的class对象 然后再去获取它的一些相关信息
1.2 获取class对象的三种方法
1.2.1Class.forname
第一种方法是class.forname("全类名")全类名就是包名加类名的形式 一般用复制粘贴的形式去获取
我们先定义一个类
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } private Student(String name){ this.name=name; } public void introduce() { System.out.println(name + "-" + age); } }下面是演示
Class clazz=Class.forName("Student"); System.out.println(clazz);1.2.2类名.class
下面是演示
Class clazz=Student.class; System.out.println(clazz);1.2.3对象.getclass
下面是演示
Student s = new Student(); Class clazz = s.getClass(); System.out.println(clazz);但是在一般情况下 我们直接用类名.class来获取Class对象
1.3 反射获取构造方法
1.3.1 获得所有构造方法
常见方法有
第一种方法使用来获取到所有的public构造方法
如果在中间加上Declare那么该类的所有方法都会被获取到 这里我们创建一个Constructor数组来接收 然后使用增强for循环去打印
Class clazz = Student.class; Constructor[] constructors1 = clazz.getConstructors(); Constructor[] constructors2 = clazz.getDeclaredConstructors(); for (Constructor constructor : constructors1) { System.out.println(constructor); } for (Constructor constructor : constructors2) { System.out.println(constructor); }由上图可知 第一第二排是通过.getConstructors()这个方法获取的
第三第四第五排是通过``.getDeclaredConstructors()`这个方法获取的
1.3.2 获得指定的构造方法
通过.getConstructor()和.getDeclaredConstructor(参数)这两种方法 去获取到指定的构造方法
还是和上面一样 不加Decalare关键字获取不到非public的构造方法 而参数的格式为 类型.class 例如String.class,int.class
Constructor cos1=clazz.getConstructor(); Constructor cos2=clazz.getDeclaredConstructor(String.class); System.out.println(cos1); System.out.println(cos2);1.3.3 获取构造方法的权限修饰符
我们可以使用构造器种的.getModifiers()方法去获取到该方法的权限修饰符
Constructor cos1=clazz.getConstructor(); Constructor cos2=clazz.getDeclaredConstructor(String.class); int modifies=cos1.getModifiers(); int modifies2=cos2.getModifiers(); System.out.println(modifies); System.out.println(modifies2);这里.getModifier()返回的是一个整数 不同的整数代表不同的权限修饰符号 例如1代表public 2代表private
1.3.4 parameter相关方法
parameter的意思是参数 相关方法主要有两种
.getParameters() | 获取到所有的参数 |
|---|---|
.getParameterCount() | 获取到参数个数 |
Parameter []parameters=cos3.getParameters(); int num=cos3.getParameterCount(); System.out.println(num); for (Parameter parameter : parameters) { System.out.println(parameter); }1.3.5 利用获取到的构造器创建对象与暴力反射
我们可以利用获取到的构造器 调用.newInstance()方法去new一个对象 但是需要强制类型转换
注意: 如果该构造方法是私有的 我们不能直接创建对象 否则会报错
正确的方法是前面加一个.setAccessible()方法 传递true值进去 表示临时取消权限 这个叫做暴力反射
cos2.setAccessible(true); Student stu2=(Student) cos2.newInstance("leixinrui"); System.out.println(stu2);1.4 反射获取成员变量
示例类
public class Student { public int number; public String name; private int age; public Student(int number, String name) { this.number = number; } public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } private Student(String name) { this.name = name; } public void introduce() { System.out.println(name + "-" + age); } }1.4.1 获取所有的成员变量
与之前获取构造方法一样 成员变量的名字Field 一样的不加Declared代表获取public变量 加上代表获取所有成员变量
Class clazz =Student.class; Field[] fields1 = clazz.getFields(); Field[] fields2 = clazz.getDeclaredFields(); for(Field f:fields1) { System.out.println(f.getName()); } for(Field f:fields2){ System.out.println(f.getName()); }1.4.2 获取到指定的成员变量
我们利用.getField()和.getDeclaredField(变量名)两种方法去获取到指定的变量
Class clazz =Student.class; Field field1=clazz.getField("name"); Field field2=clazz.getDeclaredField("age"); System.out.println(field2.getName()); System.out.println(field1.getName());原理与之前一样
1.4.3 获取权限修饰符
我们利用.getModifiers方法返回一个整数 与之前一样1代表public 2代表private
Class clazz =Student.class; Field field1=clazz.getField("name"); Field field2=clazz.getDeclaredField("age"); System.out.println(field2.getName()); System.out.println(field1.getName()); int modifiers = field1.getModifiers(); int modifiers2 = field2.getModifiers(); System.out.println(modifiers2); System.out.println(modifiers);1.4.4 获取数据类型
我们利用.getType()方法去获取到变量的类型 记得用泛型去接收
Class<?> type = field1.getType(); Class<?> type2 = field2.getType(); System.out.println(type); System.out.println(type2);1.4.5 获取到成员变量值
利用.get(类)方法去获取到成员变量值
Student stu =new Student("leixinrui",19); String value=(String)field1.get(stu); System.out.println(value);1.5 获取方法
1.5.1获取到所有的方法
与之前一样我们 方法的名字叫做Method但与之前不同的是 如果我们不加Declared关键字 我们会获取到该类的父类的方法 加上Declared就不会获取到父类的方法
Class clazz =Student.class; Method[] methods1 = clazz.getMethods(); Method[] methods2 = clazz.getDeclaredMethods(); for (Method m : methods1) { System.out.println(m); } System.out.println(); for (Method m : methods2) { System.out.println(m); }1.5.2 获取到指定的方法
2. 枚举
2.1 什么是枚举
枚举可以理解成装一些固定值的集合 如果一个东西只能从这几个值中选择 那么可以就用枚举
2.2 枚举的使用
定义如下 使用关键字enum
public enum Gender { MALE, FEMALE }使用如下
Gender gender = Gender.MALE; System.out.println(gender); // 输出 MALE使用 枚举名.变量名的方法进行赋值
2.3 为什么需要枚举
在实际工作中 如果直接使用字符串直接命名 那么会出现打错了却不知道的情况 例如
String status = "PAID"; // 正确 String status = "PAYED"; // 拼错了也不会报错,运行时才发现问题如果使用枚举就不会出现这种情况 在编译初期 出现不该有的变量 编译器直接报错
2.4 枚举中的方法
枚举其实也是特殊的一种类 可以有自己的构造方法 与方法 但是有几个注意的点
public enum OrderStatus { CREATED("已创建"), PAID("已支付"), SHIPPED("已发货"), FINISHED("已完成"), CANCELED("已取消"); // 注意结尾分号 private String description; OrderStatus(String description) { this.description = description; } public String getDescription() { return description; } }1.注意逗号与分号
2.注意构造方法前什么修饰词都不用写 默认的是private
枚举这一类自动继承了java.lang.Enum所以自带一下方法
| 方法 | 含义 |
|---|---|
name() | 获取枚举值 |
ordinal() | 获取枚举下标(当前枚举值的下标 从0开始) |
values() | 获取所有枚举值 |
valueOf(String ) | 输入一个字符串 返回一个枚举值 |
下面是应用
OrderStatus status = OrderStatus.PAID; System.out.println(status.name()); // ① 输出:PAID System.out.println(status.ordinal()); // ② 输出:1 for (OrderStatus value : OrderStatus.values()) { // ③ 遍历所有常量 System.out.println(value + " - " + value.getDescription()); } OrderStatus parsed = OrderStatus.valueOf("SHIPPED"); // ④ 按名称解析 System.out.println(parsed.getDescription()); // 输出:已发货3. 正则表达式
3.1 什么是正则表达式
在工作中 我们也能会遇到选哟进行字符串匹配或者说模糊搜索 这时候可以使用正则表达式
例
String phone = "13812345678"; boolean match = phone.matches("1[3-9]\\d{9}");3.2 正则表达式的书写
| 符号 | 含义 |
|---|---|
. | 任意一个字符 |
\\d | 数字 |
\\D | 非数字 |
\\w | 字母、数字、下划线 |
\\W | 非字母、数字、下划线 |
\\s | 空白字符 |
\\S | 非空白字符 |
[abc] | a、b、c 中任意一个 |
[^abc] | 不是 a、b、c 的字符 |
[a-z] | 小写字母范围 |
[A-Z] | 大写字母范围 |
[0-9] | 数字范围 |
值得注意的是 在 Java 字符串里,反斜杠本身需要转义,所以正则里的\d在 Java 代码里要写成"\\d"(两个反斜杠)
用花括号括起来的是代表次数 用方括号括起来的是范围
| 数量词 | 含义 |
|---|---|
? | 0 次或 1 次 |
* | 0 次或多次 |
+ | 1 次或多次 |
{n} | 正好 n 次 |
{n,} | 至少 n 次 |
{n,m} | n 到 m 次 |
3.3 正则表达式的匹配
我们可以在String后面跟上这些方法 用于字符串的匹配
| 方法 | 含义 |
|---|---|
matches(regex) | 判断整个字符串是否匹配 |
replaceAll(regex, replacement) | 替换所有匹配内容 |
split(regex) | 按正则拆分 |
matches方法用于匹配整个字符串 返回一个布尔值
replaceAll方法用与替换匹配部分 第一个参数填正则 第二个参数填替换值
split方法返回一个String[]数组 将正则表达式匹配的内容分开
3.4 Pattern 和 Matcher
3.4.1 Pattern的用途
在实际工作中 我们可能会遇到不是整体匹配的情况 而是我们需要截取一个字符串中的一小部分 我们这个时候就需要用到Pattern
3.4.2 创建正则与匹配器
与之前学习的传统正则不同 我们需要创建一个Pattern正则表达式后 然后再创建一个匹配器 Matcher
String text = "Alice:95, Bob:88, Cindy:76"; Pattern p = Pattern.compile("([A-Za-z]+):(\\d+)"); Matcher m = p.matcher(text); while (m.find()) { String name = m.group(1); String score = m.group(2); System.out.println(name + " 的成绩是 " + score); } // Alice 的成绩是 95 // Bob 的成绩是 88 // Cindy 的成绩是 76我们先创建一个Pattern对象 然后调用compile方法 去创建一个正则表达式
然后创建一个Matcher匹配器 调用Pattern中的matcher方法创建 参数为我们需要匹配的字符串
find()查找下一个匹配(返回boolean)
group()获取当前匹配的内容
4. 日期时间API
Java 早期的Date、Calendar用起来不直观。现代 Java优先使用java.time包。常用类型:
| 类型 | 含义 |
|---|---|
LocalDate | 日期,如 2026-06-22 |
LocalTime | 时间,如 10:30:15 |
LocalDateTime | 日期和时间 |
Instant | 时间戳 |
Duration | 时间间隔(时分秒) |
Period | 日期间隔(年月日) |
DateTimeFormatter | 日期时间格式化工具 |
4.1LocalDate
LocalDate today = LocalDate.now(); // 当前日期 LocalDate birthday = LocalDate.of(2000, 5, 20); // 指定日期 today.getYear(); // 年 today.getMonthValue(); // 月 today.getDayOfMonth(); // 日在LocalDate一般利用两种方法去获取到LocalDate对象 一种是.now()方法 获取到当前时间
一种是.of方法 获取到指定时间的的LocalDate对象 填入三个参数 年,月,日
值得注意的是LocalDate类并不能获取到具体时间 只能获取到
日期
4.2LocalTime和LocalDateTime
4.2.1 LocalTime
与LocalDate一样 我们有两种方法去获取到LocalTime的对象
import java.time.LocalTime; LocalTime now = LocalTime.now(); // 当前时间,如 14:35:20.123 LocalTime fixed = LocalTime.of(10, 30, 15); // 指定 10:30:15 System.out.println(now.getHour()); // 时 System.out.println(now.getMinute()); // 分 System.out.println(now.getSecond()); // 秒一个是.now()一个是.of() 一样的 一个用来获取当前的时间 一个用来获取指定时间 传入的参数格式为 时,分,秒
4.2.2 LocalDateTime
相对于前面两个LocalDateTime结合了之间两个类的邮电 既有日期 又有时间 但是美中不足的时LocalDateTime并不能显示出时区
import java.time.LocalDateTime; LocalDateTime now = LocalDateTime.now(); // 2026-10-12T14:35:20.123 LocalDateTime fixed = LocalDateTime.of(2026, 6, 22, 10, 30); // 指定日期时间 System.out.println(now.getYear()); // 2026 System.out.println(now.getMonthValue()); // 10 System.out.println(now.getDayOfMonth()); // 12方法与之前的无异
| 类型 | 包含内容 | 典型例子 |
|---|---|---|
LocalDate | 只有日期 | 2026-10-12 |
LocalTime | 只有时间 | 14:35:20 |
LocalDateTime | 日期 + 时间 | 2026-10-12T14:35:20 |
值得注意的是 这三个类之间可以相互转换
LocalDateTime dt = LocalDateTime.now(); LocalDate date = dt.toLocalDate(); // 取出日期部分 LocalTime time = dt.toLocalTime(); // 取出时间部分 LocalDateTime combined = date.atTime(time); // 组合成日期时间4.3 日期时间加减
| 方法 | 含义 |
|---|---|
plusDays/minusDays | 增加/减少天数 |
plusMonths/minusMonths | 增加/减少月数 |
plusYears/minusYears | 增加/减少年数 |
plusHours/minusHours | 增加/减少小时(LocalTime/LocalDateTime) |
plusMinutes/minusMinutes | 增加/减少分钟 |
我们可以在之前介绍的三个类中调用这些方法 但值得注意的是 第哦啊用这些方法是发那会一个新的对象 而不是是在原有的对象上进行加减
LocalDate today = LocalDate.now(); today.plusDays(7); // 7 天后 today.minusMonths(1); // 1 个月前 LocalDateTime now = LocalDateTime.now(); now.plusHours(2); // 2 小时后4.4 格式化和解析
4.4.1 出现的原因
格式化和解析的出现本质上是为了解决我们之前介绍的那三类不能转化成字符串的问题 如果我们直接打印之前的对象 那么我们得到是一个ISO格式 不方便观看
System.out.println(LocalDateTime.now()); // 2026-10-12T14:30:05.123456 System.out.println(LocalDateTime.of(2026, 6, 22, 10, 30)); // 2026-06-22T10:30 System.out.println(LocalDateTime.of(2026, 6, 22, 10, 30, 15)); // 2026-06-22T10:30:15所以我们有了格式化和解析 方便转化成我们先要的字符串 便于观看
格式化是将类转化成字符串 解析是把字符串转化成类
而在这之前 我们可以用ofPattern方法去美化时间
4.4.2 使用方法
| 符号 | 含义 |
|---|---|
yyyy | 年 |
MM | 月 |
dd | 日 |
HH | 24 小时制小时 |
mm | 分钟 |
ss | 秒 |
LocalDateTime now = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 1. 格式化:对象 → 字符串 String text = now.format(formatter); System.out.println(text); // 例如:2026-06-22 10:30:00 // 2. 解析:字符串 → 对象 LocalDateTime parsed = LocalDateTime.parse("2026-06-22 10:30:00", formatter); System.out.println(parsed); // 输出:2026-06-22T10:30我们来看看是怎么使用的
先创建一个DateTimeFormatter对象 使用ofPattern方法 传入我们需要的格式(如第一幅图)
formate和parse是我们使用中最重要的两个方法
formate负责把时间类转化为String类 parse方法负责把字符串转化成时间类 传递两个参数 第一个是字符串 第二个是我转化成的格式
4.5Duration和`Period``
这两个时间类是用来计算间隔Duration类用来计算时间间隔Period用来计算日期间隔 他们使用的方法都是调用.between()方法 传递两个时间类 表示开始时间和结束时间
import java.time.LocalDate; import java.time.Period; public class PeriodDemo { public static void main(String[] args) { LocalDate start = LocalDate.of(2020, 1, 1); LocalDate end = LocalDate.of(2026, 6, 22); Period period = Period.between(start, end); System.out.println(period.getYears()); System.out.println(period.getMonths()); System.out.println(period.getDays()); } }import java.time.Duration; import java.time.LocalDateTime; public class DurationDemo { public static void main(String[] args) { LocalDateTime start = LocalDateTime.of(2026, 6, 22, 10, 0); LocalDateTime end = LocalDateTime.of(2026, 6, 22, 12, 30); Duration duration = Duration.between(start, end); System.out.println(duration.toHours()); System.out.println(duration.toMinutes()); } }5. Lambda表达式
5.1 Lambda表达式的介绍
在学习Lambda表达式之前 我们需要认识一下匿名内部类
5.1.1 匿名内部类
在实际工作中 我们可能会遇到一下情况
1.创建了一个接口 但我们只需要使用一次 而又懒得重新创建一个类去实现
2.创建了一个抽象类 但我们懒得去又创建一个新的类去继承 然后再来重写里面的方法
为了解决这些问题 我们发明了匿名内部类
5.1.2 匿名内部类的使用
``
new 父类或接口() { // 这里写方法的具体实现 };我们来理解一下
这里new的是括号里面的方法或者接口相当于new一个类或者接口
// 接口:只有一个方法 interface Greeting { void sayHello(); } public class Test { public static void main(String[] args) { // 匿名内部类:直接 new 接口 + 大括号实现方法 Greeting g = new Greeting() { public void sayHello() { System.out.println("你好!"); } }; g.sayHello(); // 输出:你好! } }我们相当于没有经过我们之前学习的需要创建一个类 然后实现这个接口 而是相当于直接创建了一个接口对象
5.2 Lambda表达式的使用
Lambda表达式的出现就是为了解决匿名内部类太麻烦的问题 相当于更加代码更加简洁 更注重于代码需要实现的功能本身 而不是一个对象 不在是面向对象的思想
// 以前 for (String name : names) { System.out.println(name); } // 现在 names.forEach(name -> System.out.println(name));我们观察以前的代码 我们使用了增强for循环 而现在Lambda表达式的出现 我们更加强调 谁(参数)去做什么(方法)
我们直接写name(谁)->System.out.println(name)(去干什么) 代码更加简单
5.3 函数式接口
Lambda 只能用于函数式接口。
函数式接口是只有一个抽象方法的接口。
@FunctionalInterface public interface Calculator { int calculate(int a, int b); }使用 Lambda:
public class FunctionalInterfaceDemo { public static void main(String[] args) { Calculator add = (a, b) -> a + b; Calculator multiply = (a, b) -> a * b; System.out.println(add.calculate(10, 20)); System.out.println(multiply.calculate(10, 20)); } }6. Stream流
6.1 Stream流介绍
在之前 我们比如说在遍历一个数组的时候 我们需要创建一大堆的临时变量 然后使用if判断等等进行筛选 所以我们发明了Stream流去简化我们的步骤
// 传统写法:筛选出及格分数 List<Integer> passed = new ArrayList<>(); for (Integer score : scores) { if (score >= 60) { passed.add(score); } }使用Stream流
List<Integer> passed = scores.stream() .filter(score -> score >= 60) .toList();6.2 Stream流的使用
我们使用一般使用以下步骤
① 创建 Stream → ② 中间操作(可多个,链式) → ③ 终结操作(只能一个,放在最后)6.2.1 创建Stream流
| 方式 | 代码 |
|---|---|
| 从集合 | list.stream() |
| 从数组 | Arrays.stream(arr)或Stream.of(1, 2, 3) |
| 直接用元素 | Stream.of("a", "b", "c") |
1.Collection 接口创建
List<String> list = new ArrayList<>(); list.add("a"); Stream<String> s1 = list.stream(); // 串行流(默认,最常用) Stream<String> s2 = list.parallelStream(); // 并行流(多线程处理)对于ArrayList这样的数组 我们直接使用.stream()方法去创建Stream对象
2.数组创建
int[] nums = {1, 2, 3, 4}; IntStream intStream = Arrays.stream(nums); // 基本类型数组 → IntStream String[] strs = {"a", "b"}; Stream<String> strStream = Arrays.stream(strs); // 对象数组 → Stream<T>对于数组 我们可以调用Arrays中的stream方法创建 括号中传入目标数组
3.Stream.of(元素...)
Stream<String> s1 = Stream.of("a", "b", "c"); // 直接列举元素 Stream<String> s2 = Stream.of(strs); // 传对象数组值得注意的是tream.of传基本类型数组时,不会拆开,而是把整个数组当成一个元素:
int[] nums = {1, 2, 3}; Stream<int[]> s = Stream.of(nums); // ❌ 得到的是 Stream<int[]>,只有一个元素(整个数组)6.2.2 中间操作
中间操作使得我们可以对Stream中的数据进行筛选 常见方法如下
| 方法 | 含义 |
|---|---|
filter | 筛选 |
map | 转换 |
sorted | 排序 |
distinct | 去重 |
limit | 截取前几个 |
skip | 跳过前几个 |
而我们在实际工作中 会使用链式法则去使用方法 一般最后会跟上foreach方法结合Lambda表达式去遍历
1. filter —— 筛选(保留符合条件的)
.filter(score -> score >= 60) // 只保留 >=60 的参数是一个Predicate<T>(test返回 boolean)。
2. map —— 转换(把一个元素变成另一个元素)
.map(name -> name.length()) // String 转成 Integer .map(Student::getName) // Student 转成 name参数是Function<T, R>,输入类型和输出类型可以不同。
区分:
filter是"留/不留"(元素还是原来那个,个数可能变);map是"变"(元素类型/值变了,个数不变)。
3. sorted —— 排序
.sorted() // 自然排序(要求元素可比较) .sorted((a, b) -> b.getScore() - a.getScore()) // 自定义,按分数降序 .sorted(Comparator.comparing(Student::getScore).reversed()) // 更推荐的可读写法4. distinct —— 去重
.distinct() // 按 equals 去重5. limit —— 截取前几个
.limit(3) // 只保留前 3 个6. skip —— 跳过前几个
.skip(2) // 跳过前 2 个
limit(n)和skip(n)配合可以做"分页"。6.2.3 终结
终结操作会结束 Stream。
方法 含义 forEach遍历 count统计数量 toList收集为列表 anyMatch是否任意元素匹配 allMatch是否所有元素匹配 noneMatch是否没有元素匹配 findFirst查找第一个元素 reduce归约计算
import java.util.ArrayList; public class StreamTerminalDemo { public static void main(String[] args) { ArrayList<Integer> scores = new ArrayList<>(); scores.add(95); scores.add(88); scores.add(58); long passCount = scores.stream() .filter(score -> score >= 60) .count(); boolean hasFullScore = scores.stream() .anyMatch(score -> score == 100); System.out.println(passCount); System.out.println(hasFullScore); } }这些方法不一一介绍