跳到主要内容Java 反射、枚举与 Lambda 表达式 | 极客日志Javajava
Java 反射、枚举与 Lambda 表达式
Java 反射机制的核心类(Class、Field、Method、Constructor)及获取 Class 对象的方式。阐述了枚举的定义、使用场景及构造方法限制,指出枚举无法通过反射实例化。最后讲解了 Lambda 表达式的语法、函数式接口定义及变量捕获规则。
战神4 浏览 1. 反射
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 java 语言的反射机制。
1.1 反射相关的类
| 类名 | 用途 |
|---|
| Class 类 | 代表类的实体,在运行的 Java 应用程序中表示类和接口 |
| Field 类 | 代表类的成员变量 (类的属性) |
| Method 类 | 代表类的方法 |
| Constructor | 代表类的构造方法 |
1.2 Class 类中的相关方法
| 方法 | 用途 |
|---|
| getClassLoader() | 获得类的加载器 |
| getDeclaredClasses() | 返回一个数组,数组中包含该类中所有的类和接口的对象 (包含私有的) |
| forName(String className) | 根据类名返回类的对象 |
| newInstance() | 创建类的实例 |
| getName() | 获得类的完整路径名字 |
1.3 Field 类中的相关方法
| 方法 | 用途 |
|---|
| getField(String name) | 获得某个共有的属性对象 |
| getFields() | 获得所有共有的属性对象 |
| getDeclaredField(String name) | 获得某个属性对象 |
| getDeclaredFields() | 获得所有属性对象 |
1.4 Method 类中的相关方法
| 方法 | 用途 |
|---|
| getDeclaredMethod(String name, Class<?>... parameterTypes) | 获得该类某个方法 |
| getDeclaredMethods() | 获得该类的所有方法 |
| getMethod(String name, Class<?>... parameterTypes) | 获得该类某个公有方法 |
| getMethods() | 获得该类所有公有方法 |
| invoke(对象名,要修改的参数) | 触发方法执行的方法 |
1.5 Constructor 类中的相关方法
| 方法 | 用途 |
|---|
| getConstructor(Class<?>... parameterTypes) |
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
| getConstructors() | 获得该类中所有的公有构造方法 |
| getDeclaredConstructor(Class<?>... parameterTypes) | 获得该类中与参数类型匹配的某个构造方法 |
| getDeclaredConstructors() | 获得该类中所有的构造方法 |
1.6 获取 Class 对象的三种方式
在反射之前,我们需要做的第一步就是先拿到当前需要反射的类的 Class 对象,然后通过 Class 对象的核心方法,达到反射的目的
class Student {
private String name = "xiaozhuxiaozhu";
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);
}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
- 使用 Class.forName('类的全路径名'); 静态方法 (前提:已明确类的全路径名)。
public class test1 {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("reflection.Student");
}
}
- 使用 .class 方法 (仅适合在编译前就已经明确要操作的 Class)。
public class test2 {
public static void main(String[] args) throws ClassNotFoundException {
Class c2 = Student.class;
}
}
public class test3 {
public static void main(String[] args) throws ClassNotFoundException {
Student s1 = new Student();
Class c3 = s1.getClass();
}
}
1.7 反射的使用
接下来我们开始使用反射,我们依旧反射上面的 Student 类
注意:所有和反射相关的包都在 import java.lang.reflect 包下面。
package reflection;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class demo_test {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
Class<?> c1 = Class.forName("reflection.Student");
Object newInstance = c1.newInstance();
Student student = (Student) newInstance;
System.out.println(student);
Constructor<?> constructors = c1.getDeclaredConstructor(String.class, int.class);
constructors.setAccessible(true);
Object newInstance1 = constructors.newInstance("zhangsan", 20);
Student student1 = (Student) newInstance1;
System.out.println(student1);
Method method = c1.getDeclaredMethod("function", String.class);
method.setAccessible(true);
method.invoke(student1, "hello");
Field field = c1.getDeclaredField("name");
field.setAccessible(true);
field.set(student1, "lisi");
System.out.println(student1);
}
}
2. 枚举
枚举是在 JDK1.5 以后引入的。本质是一个特殊的类,是 java.lang.Enum 的子类,自己写的枚举类,就算没有显示的继承 Enum,但是其默认继承了这个类。枚举的关键字为 enum,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等。
2.1 枚举的定义
限定符 enum 枚举名称 {
常量 1, 常量 2, 常量 3....;
}
2.2 枚举的使用
public enum testenum {
RED, BLACK, WHITE;
public static void main(String[] args) {
testenum t1 = testenum.BLACK;
switch (t1) {
case RED:
System.out.println("红色");
break;
case BLACK:
System.out.println("黑色");
break;
case WHITE:
System.out.println("白色");
break;
default:
break;
}
}
}
| 方法名称 | 描述 |
|---|
| values() | 以数组形式返回枚举类型的所有成员 |
| ordinal() | 获取枚举成员的索引位置 |
| valueOf() | 将普通字符串转换为枚举类型 |
| compareTo() | 比较两个枚举成员在定义时的顺序 |
testenum[] values = testenum.values();
for (int i = 0; i < values.length; i++) {
System.out.println(values[i]);
}
2.3 枚举的构造方法
💡💡💡💡💡注意:当枚举对象有参数后,需要提供相应的构造函数
💡💡💡💡💡注意:枚举的构造函数默认是私有的 这个一定要记住
public enum testenum {
RED("红色", 1), BLACK("黑色", 2), WHITE("白色", 3);
private String color;
private int key;
private testenum(String color, int key) {
this.color = color;
this.key = key;
}
}
2.4 枚举与反射
关于枚举与反射,请记住一个结论:你不能通过反射获取枚举类的实例!
这也是<为什么枚举实现单例模式是安全的?>问题的答案。至于为什么呢,待后续学习单例模式后补充…
3. Lambda 表达式
Lambda 表达式(Lambda expression),基于数学中的λ演算得名,也可称为闭包(Closure),是 Java SE 8 中的一个重要新特性。Lambda 表达式允许你通过表达式来代替功能接口。lambda 表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体 (主体可以是一个表达式或一个代码块)。
3.1 Lambda 表达式语法
基本语法:(parameters) -> expression 或 (parameters) -> {statement;}
- parameters:类似方法中的形参列表,这里的参数是函数式接口里的参数。
- ->:可理解为'被用于'的意思
- 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块等同于方法的方法体。***
() -> 2
x -> 2 * x
(x, y) -> x + y
(int x, int y) -> x * y
(String s) -> System.out.print(s)
3.2 函数式接口
函数式接口定义:一个接口有且只有一个抽象方法。
💡💡注意:
- 如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
- 如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
3.3 变量捕获
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
public static void main(String[] args) {
int a = 10;
NoParameterNoReturn noParameterNoReturn = () -> {
System.out.println("捕获变量:" + a);
};
noParameterNoReturn.test();
}