一.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 String
pathSeparator
依赖于系统的路径分隔符,String类型的表示 static char
pathSeparator
依赖于系统的路径分隔符,char类型的表示 ②构造方法
签名 说明 File(File parent, String child)
根据父目录 + 孩子文件路径,创建一个新的 File
实例File(String pathname)
根据文件路径创建一个新的 File
实例,路径可以是绝对路径或者相对路径File(String parent, String child)
根据父目录 + 孩子文件路径,创建一个新的 File
实例,父目录用路径表示③方法
修饰符及返回值类型 方法签名 说明 String
getParent()
返回 File
对象的父目录文件路径String
getName()
返回 File
对象的文件名称String
getPath()
返回 File
对象的文件路径String
getAbsolutePath()
返回 File
对象的绝对路径String
getCanonicalPath()
返回 File
对象的修饰过的绝对路径boolean
exists()
判断 File
对象描述的文件是否真实存在boolean
isDirectory()
判断 File
对象代表的文件是否是一个目录boolean
isFile()
判断 File
对象代表的文件是否是一个普通文件boolean
createNewFile()
根据 File
对象,自动创建一个空文件。成功创建后返回true
boolean
delete()
根据 File
对象,删除该文件。成功删除后返回true
void
deleteOnExit()
根据 File
对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行String[]
list()
返回 File
对象代表目录下的所有文件名File[]
listFiles()
返回 File
对象代表目录下的所有文件,以File
对象表示boolean
mkdir()
创建 File
对象代表的目录boolean
mkdirs()
创建 File
对象代表的目录,如果必要,会创建中间目录boolean
renameTo(File dest)
进行文件改名,也可以视为我们平时的剪切/粘贴操作 boolean
canRead()
判断用户是否对文件有可读权限 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(字节流)
方法:
修饰符及返回值类型 方法签名 说明 int
read()
读取一个字节的数据,返回 -1 代表已经完全读完了 int
read(byte[] b)
最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1 代表已经读完了 int
read(byte[] b, int off, int len)
最多读取 len 字节的数据到 b 中,放在从 off 开始的位置,返回实际读到的数量;-1 代表已经读完了 void
close()
关闭字节流 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(字节流)
方法:
修饰符及返回值类型 方法签名 说明 void
write(int b)
写入一个字节的数据 void
write(byte[] b)
将 b 这个字节数组中的数据全部写入输出流中 int
write(byte[] b, int off, int len)
将 b 这个字节数组中从 off 开始的数据写入输出流中,一共写 len 个字节 void
close()
关闭字节流 void
flush()
重要:我们知道 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.枚举类和反射
不能通过反射获取枚举类的私有构造方法。