目录
1、反射
1.1 反射的定义
1.2 反射机制的原理
1.3 反射相关类
1.4 Class类
1.4.1 相关方法
1.4.1.1 常用获得类相关的方法
编辑
1.4.1.2 常用获得类中属性相关的方法
1.4.1.3 获得类中构造器相关的方法
1.4.1.4 获得类中方法相关的方法
1.4.2 获取Class对象
1.5 总结
1.6 代码实例
2、枚举
2.1 定义
2.1.1 简单使用
2.2 常用方法
2.3 构造方法
2.4 枚举和反射(阿里面试:为什么枚举实现单例模式是安全的?)
2.5 总结
3、Lambda表达式
3.1 定义
3.2 函数式接口
3.3 省略规则
3.4 变量捕获
3.5 Lambda表达式在集合中的使用
3.5.1 forEach循环
3.5.1.1 Collection集合
3.5.1.2 Map集合
3.5.2 sort排序-forEach循环
3.5.2.1 List集合
1、反射
1.1 反射的定义
Java的反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任 意一个对象,都能够调用它的任意方法和属性,也能够修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
简单来说,反射就是在运行的状态下,能够看清类的基本信息。
我们可以这样理解:在安检时,我们把行李箱放在安检的机器上,这时,不管行李箱里面有什么东西,哪怕是私密的东西,也可以被看的一清二楚。
1.2 反射机制的原理
一个.java文件经过编译会生成.class字节码文件,在运行时,这个字节码文件又会被JVM解析成一个Class类的对象,程序在运行时,每个java文件最终就会变成Class类对象的一个实例,我们就可以通过这个Class类来获取或修改类的基本信息。
也就是说,在反射之前,我们需要做的第一步就是先拿到当前需要反射的类的Class对象(见下文),然后通过Class对象的核心方法,达到反射的目的,
1.3 反射相关类
要想实现反射,我们必须了解以下类:
- Class类:代表类的实体,在运行的Java应用程序中表示类和接口
- Field类:代表类的成员变量/类的属性
- Method类:代表类的方法
- Constructor类:代表类的构造方法
1.4 Class类
1.4.1 相关方法
1.4.1.1 常用获得类相关的方法
1.4.1.2 常用获得类中属性相关的方法
1.4.1.3 获得类中构造器相关的方法
1.4.1.4 获得类中方法相关的方法
1.4.2 获取Class对象
-
第一种,使用 Class.forName("类的全路径名"); (静态方法)(常用) 例:Class<?> c1 = Class.forName("reflectdemo.Student");
-
使用 .class 方法。 例:Class c2 = Student.class;
-
使用类对象的 getClass() 方法。 例:Student student = new Student();Class<?> c3 = student.getClass();
1.5 总结
反射优点:
- 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法。
- 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力。
- 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。
反射缺点:
- 调用大量方法,导致程序效率降低 。
- 会带来维护问题。
- 反射代码比相应的直接代码更复杂 。
1.6 代码实例
public static void reflectPrivateField() {
try {
Class<?> c1 = Class.forName("reflectdemo.Student");
Field field = c1.getDeclaredField("name");
field.setAccessible(true);//修改private修饰的成员,需要添加这条语句
//获取Student对象
Student student = (Student)c1.newInstance();
field.set(student,"poll");
System.out.println(student);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
2、枚举
2.1 定义
枚举的使用语法:用enum代替class关键字,定义一个枚举类。枚举类实际上就是一个类。
其中的RE、 BLACK、GREEN ,叫做枚举对象。
能够将常量组织起来统一进行管理,不具备整形等值的概念,是枚举类型。
本质:是 java.lang.Enum 的子类,也就是说,自己写的枚举类,就算没有显示的继承 Enum ,但是其默认继承了Enum类。
2.1.1 简单使用
public enum Color {
RED,PICK,BLACK,BLUE;//枚举对象
public static void main(String[] args) {
Color color = RED;
switch (color) {
case RED :
System.out.println(RED);
break;
case BLACK :
System.out.println(BLACK);
break;
case PICK:
System.out.println(PICK);
break;
case BLUE:
System.out.println(BLUE);
break;
default:
System.out.println("null");
break;
}
}
}
2.2 常用方法
因为任何的自定义枚举类都继承自Enum类,故继承了Enum类的方法:
其中 valueOf方法,只能转化成已有的枚举对象。
方法使用:
public enum Color {
RED,PICK,BLACK,BLUE;//枚举对象
public static void main(String[] args) {
Color[] colors = Color.values();//将枚举对象转化为数组
for (Color color : colors) {
System.out.println(color+" "+color.ordinal());//获取索引
}
System.out.println("====");
Color color = Color.valueOf("RED");//将字符串转化为枚举对象(只能转化成已有的枚举对象)
System.out.println(color);
System.out.println("===");
System.out.println(RED.compareTo(PICK));//比较枚举对象的定义顺序(下标差)
}
}
2.3 构造方法
因为枚举本身就是一个类,所以也有普通类所具备的构造方法。
当我们没有写出任何构造方法时,Java会帮我们默认提供不带参数的构造方法,所以我们实例出的枚举对象不用传入参数。但是当我们写出带参构造时,我们就需要自主给枚举对象提供相应参数。
但是这里重点要说的是:枚举的构造方法默认是私有的。
我们发现,当private修饰构造方法时,private显示为灰色(上图),说明构造方法默认是私有的,当我们使用public修饰时,会编译报错:
2.4 枚举和反射(阿里面试:为什么枚举实现单例模式是安全的?)
我们上文讲到了反射可以拿到类中所有的属性和方法,包括private私有的,那么枚举的构造方法也是私有的,我们是否可以拿到呢?
答案是:反射不可以拿到枚举的构造方法。
当我们通过反射(newInstance方法中)去获取枚举对象时,直接抛出了异常,也就是说,在反射中,枚举被过滤掉了,所以我们不能通过反射获取枚举类的实例!!!
总结:枚举是非常安全的,甚至不可以用反射获取枚举的实例。
2.5 总结
- 枚举本身就是一个类,其构造方法默认为私有的,且都是默认继承与 java.lang.Enum
- 枚举的构造方法默认是私有的
- 枚举可以避免反射和序列化问题
- 枚举常量更简单安全
- 枚举不可继承,无法扩展
3、Lambda表达式
3.1 定义
Lambda表达式是JDK8引入的一种语法形式,主要用来简化代码,语法为:
3.2 函数式接口
Lambda表达式主要用来简化匿名内部类的书写,而且只能简化函数式接口的匿名内部类的书写。
函数式接口:
- 有且仅有一个抽象方法的接口
- 我们可以为函数式接口添加@FunctionalInterface 注解,当其不是函数式接口时,编译器会报错。
所以我们可以这样理解Lambda表达式:Lambda就是匿名内部类的简化,实际上是创建了一个匿名类的对象,实现了函数式接口,重写了接口的方法 。
3.3 省略规则
-
省略所实现接口的表示,省略方法名、返回值,只保留重写方法的参数列表和方法体
-
参数类型可省略,若要省略则全部参数的类型都要省略
-
若只有一个参数,那么参数的小括号可省略;若有两个参数,则小括号不可省略。
-
参数和方法体间使用 -> 连接
-
若方法体当中只有一句代码,那么花括号可以省略,分号可以省略
-
如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字,去掉分号
代码演示:
public static void main(String[] args) {
//原匿名内部类形式
PriorityQueue<String> queue1 = new PriorityQueue<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
//Lambda表达式形式
PriorityQueue<String> queue2 = new PriorityQueue<>((o1,o2) -> o2.compareTo(o1));
}
3.4 变量捕获
变量捕获就是在匿名内部类中使用到了一个变量,那这个变量在使用前或者使用后都不能做出修改,否则会编译报错(这个变量就叫做被捕获的变量):
在匿名内部类中存在变量捕获,那么在Lambda表达式中也必然存在:
上述代码中变量aaaa就叫做:被捕获的变量
所以我们得出结论:
- 这个变量要么是被final修饰为常量(常量不可修改)。
- 如果不是被final修饰的,我们要保证在使用之前和使用之后,没有修改。
3.5 Lambda表达式在集合中的使用
3.5.1 forEach循环
forEach循环需要使用匿名内部类重写方法,我们可以使用Lambda表达式来简化代码。
3.5.1.1 Collection集合
public static void main1(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(23);
list.add(29);
list.add(13);
//原形式
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.print(integer+" ");
}
});
System.out.println();
//Lambda形式
list.forEach(integer -> System.out.print(integer+" "));
}
3.5.1.2 Map集合
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
map.put("hello",10);
map.put("world",13);
map.put("abc",20);
map.forEach(new BiConsumer<String, Integer>() {
@Override
public void accept(String s, Integer integer) {
System.out.println(s+" "+integer+" ");
}
});
//Lambda形式
map.forEach((key,value) -> System.out.println(key+" "+value+" "));
3.5.2 sort排序-forEach循环
3.5.2.1 List集合
List集合的sort方法,也可以使用匿名内部类重写方法来排序,我们可以使用Lambda形式:
public static void main2(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(23);
list.add(29);
list.add(13);
//原匿名内部类排序形式
/*list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});*/
//Lambda形式 ->排序
list.sort((o1, o2) -> o1.compareTo(o2));
//Lambda形式 ->forEach循环
list.forEach(integer -> System.out.print(integer+" "));
}
END