一.lambda表达式
作用:Lambda 表达式在 Java 8 引入,主要用于简化匿名内部类的写法,特别是在函数式编程场景中,比如 函数式接口、流式 API(Streams)、并发编程等。它让 Java 代码更简洁、可读性更强,同时提升开发效率。
1.Lambda表达式的语法
// 基本语法
(parameters) ->{ statements; }
// 1. paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
// 2. ->:可理解为“被用于”的意思
// 3. 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。
2.函数式接口
函数式接口定义:一个接口有且只有一个抽象方法。然后可以在接口上使用一个注解@FunctionalInterface,添加了这个注解之后,编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的,就相当于自动检测了。
注意:1.函数式接口是指只有一个抽象方法的接口,但继承自 Object 类的方法不算抽象方法。
2.默认方法(default 方法),不是抽象方法。
3.静态方法(static 方法),也不是抽象方法。
3.使用案例
// 案例一
@FunctionalInterface
interface Rhm {
void test();
}
public class Demo1 {
public static void main(String[] args) {
// 正常写法使用匿名内部类
Rhm rhm1 = new Rhm(){
public void test() {
System.out.println("hello");
}};
// 使用Lambda表达式
Rhm rhm2 = ()->{System.out.println("无参数无返回值");};
// 使用
rhm.test();
}
}
// 案例二:创建一个Thread 类实例
public class Demo1 {
public static void main(String[] args) {
// 匿名内部类创建 Runnable ⼦类对象
Thread t1 = new Thread(new Runnable() {
public void run() {
System.out.println("使⽤匿名类创建 Runnable ⼦类对象");
}
});
// 使用Lambda表达式
Thread t2 = new Thread(() -> System.out.println("使⽤匿名类创建 Thread ⼦类对象"));
}
}
4.变量捕获
Lambda 可以使用表达式外的变量但是只能使用没有改变过的变量或者被final关键字修饰的变量。
所以Lambda表达式只能捕获被final修饰的变量,如果不是被final修饰的,也要保证在使用之前,没有修改。
5.Lambda在集合框架中的使用

// 这里只演示List的foreach
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("rhm");
list.add("hello");
list.add("lambda");
// 使用匿名内部类
list.forEach(new Consumer<String>(){
public void accept(String str){
System.out.print(str+" ");
}
});
// 使用Lambda表达式
list.forEach(s -> { System.out.println(s); });
}
二.文件操作和IO
1.file类
①属性
修饰符及类型 属性 说明 static StringpathSeparator依赖于系统的路径分隔符,String类型的表示 static charpathSeparator依赖于系统的路径分隔符,char类型的表示 ②构造方法
签名 说明 File(File parent, String child)根据父目录 + 孩子文件路径,创建一个新的 File实例File(String pathname)根据文件路径创建一个新的 File实例,路径可以是绝对路径或者相对路径File(String parent, String child)根据父目录 + 孩子文件路径,创建一个新的 File实例,父目录用路径表示③方法
修饰符及返回值类型 方法签名 说明 StringgetParent()返回 File对象的父目录文件路径StringgetName()返回 File对象的文件名称StringgetPath()返回 File对象的文件路径StringgetAbsolutePath()返回 File对象的绝对路径StringgetCanonicalPath()返回 File对象的修饰过的绝对路径booleanexists()判断 File对象描述的文件是否真实存在booleanisDirectory()判断 File对象代表的文件是否是一个目录booleanisFile()判断 File对象代表的文件是否是一个普通文件booleancreateNewFile()根据 File对象,自动创建一个空文件。成功创建后返回truebooleandelete()根据 File对象,删除该文件。成功删除后返回truevoiddeleteOnExit()根据 File对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行String[]list()返回 File对象代表目录下的所有文件名File[]listFiles()返回 File对象代表目录下的所有文件,以File对象表示booleanmkdir()创建 File对象代表的目录booleanmkdirs()创建 File对象代表的目录,如果必要,会创建中间目录booleanrenameTo(File dest)进行文件改名,也可以视为我们平时的剪切/粘贴操作 booleancanRead()判断用户是否对文件有可读权限 boolean canWrite() 判断用户是否对文件有可写的权限
代码案例:
public class Main {
public static void main(String[] args) throws IOException {
File file = new File("..\\hello-world.txt"); // 并不要求该⽂件真实存
System.out.println(file.getParent());
System.out.println(file.getName());
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
}
}
// 其他代码就不多演示了
2.文件的读写
①Inputstream(字节流)
方法:
修饰符及返回值类型 方法签名 说明 intread()读取一个字节的数据,返回 -1 代表已经完全读完了 intread(byte[] b)最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1 代表已经读完了 intread(byte[] b, int off, int len)最多读取 len 字节的数据到 b 中,放在从 off 开始的位置,返回实际读到的数量;-1 代表已经读完了 voidclose()关闭字节流 InputStream 只是⼀个抽象类,要使⽤还需要具体的实现类。关于 InputStream 的实现类有很多,基本可以认为不同的输⼊设备都可以对应⼀个 InputStream 类,我们现在只关⼼从⽂件中读取,所以使⽤ FileInputStream。
FileInputStream介绍:
构造方法:
代码案例:
签名 说明 FileInputStream(File file)利用 File 对象构造文件输入流 FileInputStream(String name)利用文件路径构造文件输入流 // 字节流读取操作 // 使用FileInputStream读取文件 public class fileinputstreamUser { public static void main(String[] args) throws IOException { try (InputStream inputStream = new FileInputStream("./test.txt")) { // 读文件操作. while (true) { // 一次读一个字节 // int data = inputStream.read(); // if (data == -1) { // // 文件读完. // break; // } // System.out.printf("0x%x\n", data); // 一次读多个字节, 数组的长度, 自行定义. byte[] data = new byte[3]; // 读操作, 就会尽可能把字节数组给填满. // 填不满的话, 能填几个就是几个! // 此处的 n 就表示实际读了几个字节. int n = inputStream.read(data); System.out.println("n = " + n); if (n == -1) { // 文件读完. break; } for (int i = 0; i < n; i++) { System.out.printf("0x%x\n", data[i]); } System.out.println("========================"); } } } }②Outputstream(字节流)
方法:
修饰符及返回值类型 方法签名 说明 voidwrite(int b)写入一个字节的数据 voidwrite(byte[] b)将 b 这个字节数组中的数据全部写入输出流中 intwrite(byte[] b, int off, int len)将 b 这个字节数组中从 off 开始的数据写入输出流中,一共写 len 个字节 voidclose()关闭字节流 voidflush()重要:我们知道 I/O 的速度是很慢的,所以,⼤多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写⼊内存的⼀个指定区域⾥,直到该区域满了或者其他指定条件时才真正将数据写⼊设备中,这个区域⼀般称为缓冲区。但造成⼀个结果,就是我们写的数据,很可能会遗留⼀部分在缓冲区中。需要在最后或者合适的位置,调⽤ flush(刷新)操作,将数据刷到设备中。 OutputStream 同样只是⼀个抽象类,要使⽤还需要具体的实现类。我们现在还是只关⼼写⼊⽂件中,所以使⽤ FileOutputStream。
代码案例:// 字节流写入操作 // 使用FileOutputStream像文件中写内容 public class fileoutputstreamUser { public static void main(String[] args) { // append是从后面接着加入 try (OutputStream outputStream = new FileOutputStream("./output.txt", true)) { // outputStream.write(97); // 97 在 ascii 中就是 'a' // outputStream.write(98); // outputStream.write(99); byte[] bytes = {99}; outputStream.write(bytes); } catch (IOException e) { // 此处是需要处理两个异常. 由于此处并没有针对这两个异常提供不同的处理, 就直接合并了. throw new RuntimeException(e); } } }③Reader
Reader是抽象方法,需要使用FileReader。
代码案例:
// 使用FileReader // 字符流读取 public class filereaderUser { public static void main(String[] args) { try (Reader reader = new FileReader("./test.txt")) { while (true) { char[] buf = new char[1024]; int n = reader.read(buf); if (n == -1) { break; } for (int i = 0; i < n; i++) { System.out.println(buf[i]); } } } catch (IOException e) { throw new RuntimeException(e); } } }④Writer
Writer是抽象方法,需要使用FileWriter
代码案例:
// 使用 FileWriter // 字符流写入 public class filewriterUser { public static void main(String[] args) { try (Writer writer = new FileWriter("./output.txt", true)) { // writer.write("This is a test"); BufferedWriter bufferedWriter = new BufferedWriter(writer); bufferedWriter.write("Hello World"); } catch (IOException e) { throw new RuntimeException(e); } } }
三.反射
作用:Java 反射(Reflection)允许在运行时获取类的元信息(如类名、方法、字段、构造器),并动态调用方法、修改字段或创建实例。
详细解析:Java文件被编译后,生成了.class文件,JVM此时就要去解读.class文件 ,被编译后的Java文件.class也被JVM解析为一个对象,这个对象就是 java.lang.Class .这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例。我们通过Java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类 .
1.反射相关的类:
| 类名 | 用途 |
|---|---|
| Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
| Field类 | 代表类的成员变量/类的属性 |
| Method类 | 代表类的方法 |
| Constructor类 | 代表类的构造方法 |
2.Class类中的相关方法:
①常用获得类相关的方法
| 方法 | 用途 |
|---|---|
| getClassLoader() | 获得类的加载器 |
| getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有的) |
| forName(String className) | 根据类名返回类的对象 |
| newInstance() | 创建类的实例 |
| getName() | 获得类的完整路径名字 |
②常用获得类中属性相关的方法
| 方法 | 用途 |
|---|---|
| getField(String name) | 获得某个公有的属性对象 |
| getFields() | 获得所有公有的属性对象 |
| getDeclaredField(String name) | 获得某个属性对象 |
| getDeclaredFields() | 获得所有属性对象 |
③获得类中构造器相关的方法
| 方法 | 用途 |
|---|---|
| getConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
| getConstructors() | 获得该类的所有公有构造方法 |
| getDeclaredConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
| getDeclaredConstructors() | 获得该类所有构造方法 |
④获得类中方法相关的方法
| 方法 | 用途 |
|---|---|
| getMethod(String name, Class...<?> parameterTypes) | 获得该类某个公有的方法 |
| getMethods() | 获得该类所有公有的方法 |
| getDeclaredMethod(String name, Class...<?> parameterTypes) | 获得该类某个方法 |
| getDeclaredMethods() | 获得该类所有方法 |
3.反射的代码案例
①先创建一个类:
class Student{ private String name = "rhm"; public int age = 18; public Student(){ System.out.println("Student()"); } private Student(String name,int age) { this.name = name; this.age = age; System.out.println("Student(String,name)"); } private void eat(){ System.out.println("i am eat"); } public void sleep(){ System.out.println("i am pig"); } private void function(String str) { System.out.println(str); } public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }②获取Class对象:三种方法
public class TestDemo { public static void main(String[] args) { // 1.通过getClass获取Class对象 Student s1 = new Student(); Class c1 = s1.getClass(); // 2.直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高这说明任何一个类都有一个隐含的静态成员变量 class Class c2 = Student.class; // 3、通过 Class 对象的 forName() 静态方法来获取,用的最多,但可能抛出ClassNotFoundException 异常 try { //注意这里是类的全路径,如果有包需要加包的路径 Class c3 = Class.forName("Student"); } catch (ClassNotFoundException e) { e.printStackTrace(); } //一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 //c1,c2,c3进行 equals 比较,发现都是true System.out.println(c1.equals(c2)); System.out.println(c1.equals(c3)); System.out.println(c2.equals(c3)); } }③反射的使用:
public class ReflectClassDemo { // 反射私有的构造方法 屏蔽内容为获得公有的构造方法 public static void reflectPrivateConstructor() { try { Class<?> classStudent = Class.forName("Student"); //注意传入对应的参数 Constructor<?> declaredConstructorStudent = classStudent.getDeclaredConstructor(String.class,int.class); declaredConstructorStudent.setAccessible(true); //设置为true后可修改访问权限 Student student = (Student) declaredConstructorStudent.newInstance("冉汉梦",15); System.out.println("获得私有构造哈数且修改姓名和年龄:"+student); } catch (Exception ex) { ex.printStackTrace(); } } // 反射私有属性 public static void reflectPrivateField() { try { Class<?> classStudent = Class.forName("Student"); Field field = classStudent.getDeclaredField("name"); field.setAccessible(true); //可以修改该属性的值 Student student = (Student) classStudent.newInstance(); field.set(student,"小明"); String name = (String) field.get(student); System.out.println("反射私有属性修改了name:"+ name); } catch (Exception ex) { ex.printStackTrace(); } } // 反射私有方法 public static void reflectPrivateMethod() { try { Class<?> classStudent = Class.forName("Student"); Method methodStudent = classStudent.getDeclaredMethod("function",String.class); System.out.println("私有方法的方法名为:"+methodStudent.getName()); methodStudent.setAccessible(true); //私有的一般都要加 Student student = (Student) classStudent.newInstance(); methodStudent.invoke(student,"我是给私有的function函数传的参数"); } catch (Exception ex) { ex.printStackTrace(); } } public static void main(String[] args) { //reflectPrivateConstructor(); //reflectPrivateField(); reflectPrivateMethod(); } }
四.枚举类
定义:
枚举类型本质上也是一种类,只不过这个类的对象是有限的,只有固定的几个,不能让用户随意创建。
注意:
①枚举类的构造方法是私有的。
②是 java.lang.Enum 的子类,也就是说,自己写的枚举类,就算没有显示的继承Enum,但是其默认继承了这个类。
1.常用方法
| 方法名称 | 描述 |
|---|---|
| values() | 以数组形式返回枚举类型的所有成员 |
| ordinal() | 获取枚举成员的索引位置 |
| valueOf() | 将普通字符串转换为枚举实例 |
| compareTo() | 比较两个枚举成员在定义时的顺序 |
2.代码案例
// 使用案例一
public enum TestEnum {
RED,BLACK,GREEN,WHITE; // 定义的枚举类的对象
public static void main(String[] args) {
TestEnum[] testEnum = TestEnum.values(); // 获得枚举类的所用成员
for (int i = 0; i < testEnum2.length; i++) {
System.out.println(testEnum[i] + " " + testEnum[i].ordinal());
}
System.out.println(TestEnum.valueOf("GREEN")); // 字符串转换成枚举实例
//拿到枚举实例BLACK和RED
TestEnum testEnum1 = TestEnum.BLACK;
TestEnum testEnum2 = TestEnum.RED;
System.out.println(testEnum1.compareTo(testEnum2));
System.out.println(RED.compareTo(BLACK));
}
}
// 使用案例二
public enum TestEnum {
RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
private String name;
private int key;
private TestEnum (String name,int key) {
this.name = name;
this.key = key;
}
public static TestEnum getEnumKey (int key) {
for (TestEnum t: TestEnum.values()) {
if(t.key == key) {
return t;
}
}
return null;
}
public static void main(String[] args) {
System.out.println(getEnumKey(2));
}
}
3.枚举类和反射
不能通过反射获取枚举类的私有构造方法。



















