1. 反射
1. Java 的反射机制
Java 的反射机制是在运行状态,对于任意一个类,都能够直到它所有的属性和方法;
对于任意一个对象,都能调用它的方法和属性;
这种动态获取信息及调用对象方法的功能,称为Java的反射机制;
2. 反射相关的类与方法
Class:代表类的实体,在运行的 Java 程序中表示类和接口;
Field:代表类的成员变量和类的属性;
Method:代表类的方法;
Constructor:代表类的构造方法;
获得类的相关方法:
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. 反射的使用
创建 Student 类用于测试:
class Student{
//私有属性name
private String name = "张三";
//公有属性age
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 +
'}';
}
}
利用反射创建对象,反射私有构造方法,反射私有属性,私有方法:
public class ReflectDemo {
public static void testReflect(){
try {
Class<?> c = Class.forName("Student");
// 利用反射创建对象
Student student = (Student)c.newInstance();
// 利用反射调用私有构造方法
Constructor constructor = c.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
Student student1 = (Student) constructor.newInstance("lisi", 22);
System.out.println(student1);
// 利用反射调用私有属性
Field field = c.getDeclaredField("name");
field.setAccessible(true);
field.set(student1, "libai");
System.out.println(student1);
// 利用反射调用私有方法
Method method = c.getDeclaredMethod("eat");
method.setAccessible(true);
method.invoke(student1);
Method function = c.getDeclaredMethod("function", String.class);
function.setAccessible(true);
function.invoke(student1, "Hello, nice to meet you!");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
ReflectDemo.testReflect();
}
}
4. 反射的优缺点
优点:
- 对于任意一个类,都能知道它的属性和方法;
- 对于任意一个对象,都能调用它的任意属性和方法;
- 增加程序的灵活性和扩展性,降低代码的耦合性,提高自适应能力;
- 反射已经应用在了一些流行框架上,例如 Spring;
缺点:
- 使用反射会导致程序的效率降低;
- 反射机制绕过了源代码,反射代码比相应的代码更复杂,因而会有维护问题;
2. 枚举
枚举是将常量组织起来,统一进行管理,常用于表示错误状态码,消息类型,颜色划分,状态机等;
1. 枚举的常用方法
values():以数组形式返回所有成员;
ordinal():获取枚举成员的索引位置;
valueOf():将普通字符转换为枚举实例;
compareTo():比较两个成员在定义时的顺序;
2. 枚举的定义
枚举在 Java 当中实际上就是一个类,在定义枚举的时候,也可以使用构造方法的方式,如下:
public enum TestEnum {
RED("red", 0),
BLACK("black", 1),
GREEN("green", 2),
WHITE("white", 3);
private String name;
private int val;
TestEnum(String color, int val){
this.name = color;
this.val = val;
}
}
注意:枚举中的构造方法是私有的;
使用枚举的优缺点:
优点:枚举常量更简单安全,枚举有内置方法,使用枚举,代码更优雅;
缺点:不可继承,无法扩展;
3. 枚举和反射
所有的枚举类都默认继承 java.lang.Enum;
反射包中的 Contructor 类 newInstance() 方法源码如下:
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
注意到使用反射调用枚举的构造方法就会抛出异常 “Cannot refletively create enum objects ”,因此不能使用反射获取枚举类的实例。
3. Lambda 表达式
1. 函数式接口
一个接口有且只有一个抽象方法,这个接口就是一个函数式接口;
如果接口使用 @FunctionalInterface 注解修饰,编译器就会按照函数式接口的格式进行检查,如果里面有多个抽象方法,编译器就会报错;
2. Lambda 表达式的基本使用
Lambda 表达式是匿名内部类的简化,实际上是创建了一个类,实现了接口,重写了接口的方法;
基本语法:(parameters) -> expression 或 (parameters)-> {statements;}
语法精简:
1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
2. 参数的小括号里面只有一个参数,那么小括号可以省略
3. 如果方法体当中只有一句代码,那么大括号可以省略
4. 如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字。
3. 变量的捕获
在匿名内部类中使用类外的变量的时候,要保证变量不会被修改,如果变量被修改了就会报错;
同理,在 lambda 表达式中使用外部的变量也要确保变量不会被修改,如果被修改了也会报错;
4. lambda 表达式的优缺点
优点:
1. 代码简洁,开发迅速
2. 方便函数式编程
3. 非常容易进行并行计算
4. Java 引入 Lambda,改善了集合操作
缺点:
1. 代码可读性变差
2. 在非并行计算中,很多计算未必有传统的 for 性能要高
3. 不容易进行调试