Java 高级特性” 体系(反射 + 枚举 + Lambda)
1.反射
1.1 定义
Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 不用 new,不用知道类名,也能操作类。
1.2 用途
- 框架底层核心(Spring、MyBatis 全靠反射)
- 动态加载类、动态调用方法
- 突破私有访问限制
1.3 反射基本信息
Java程序中许多对象在运行时会出现两种类型:运行时类型 和 编译时类型,例如 Person p = new Student();这句代码中p在编译时类型为Person,运行时类型为Student。程序需要在运行时发现对象和类的真实信息。而通过使用反射程序就能判断出该对象和类属于哪些类。
1.4 反射相关的类
| 类名 | 用途 | 链接文档 |
|---|---|---|
| Class类 | 类的实体,在运行的Java应用程序中表示类和接口 | Class | API reference | Android Developers |
| Field类 | 类的成员变量/类的属性 | Field | API reference | Android Developers |
| Method类 | 类的方法 | Method | API reference | Android Developers |
| Constructor类 | 类的构造方法 | Constructor | API reference | Android Developers |
1.5 反射示例
1.51获得Class对象的三种方式
第一种,使用 Class.forName(“类的全路径名”); 静态方法。前提:已明确类的全路径名。
第二种,使用 .class 方法。说明:仅适合在编译前就已经明确要操作的 Class
第三种,使用类对象的 getClass() 方法
package test;
public class demo5 {
static class Student{
}
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 异常
Class c3 = null;
try {
//注意这里是类的全路径,如果有包需要加包的路径
//c3 = Class.forName("test.demo5.Student"); ❌
c3 = Class.forName("test.demo5$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));
}
}
| 获取方式 | 语法 | 特点 | 适用场景 |
|---|---|---|---|
对象.getClass() | s1.getClass() | 必须先创建对象,运行时获取 | 已经有对象,需要获取类信息 |
类名.class | Student.class | 不需要创建对象,编译期获取,性能最高、最安全 | 编译期就知道类名,静态获取 |
Class.forName() | Class.forName("全类名") | 动态加载,只需要类名字符串,可配置 | 框架动态加载类、配置文件指定类名 |
🎯 为什么test.demo5.Student是错的,test.demo5$Student是对的?
这是静态内部类的全限定名规则:
-
.符号:只代表「包层级」test.demo5.Student会被 JVM 理解为:test包 →demo5包 →Student类- 但你的
demo5不是包,是外部类,Student是demo5里的静态内部类,所以 JVM 根本找不到这个类,直接抛异常ClassNotFoundException。
-
$符号:代表「内部类」- Java 规定:内部类(包括静态内部类)的全限定名,必须用
$分隔外部类和内部类 - 编译后,静态内部类会生成独立的字节码文件:
demo5$Student.class,forName就是根据这个文件名加载类的 - 所以正确写法是
test.demo5$Student,表示test包下,demo5类的内部类Student.
- Java 规定:内部类(包括静态内部类)的全限定名,必须用
1.6反射优点和缺点
优点:
- 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
- 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
- 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。
缺点:
1.使用反射会有效率问题。会导致程序效率降低.
2.反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
2 枚举
2.1 定义
枚举是一组固定不变的常量,用来代替大量 public static final 常量,代码更简洁、更安全。本质:是 java.lang.Enum 的子类,也就是说,自己写的枚举类,就算没有显示的继承 Enum ,但是其默认继承了这个类。
2.2 使用
2.2.1.sitch语句
package test;
public class demo6 {
public enum TestEnum {
RED,BLACK,GREEN,WHITE,hhh;
public static void main(String[] args) {
TestEnum testEnum2 = TestEnum.hhh;
switch (testEnum2) {
case RED:
System.out.println("red");
break;
case BLACK:
System.out.println("black");
break;
case WHITE:
System.out.println("WHITE");
break;
case GREEN:
System.out.println("black");
break;
default:
System.out.println("不是颜色");
}
}
}
}
2.2.2、常用方法
Enum 类的常用方法
| 方法名称 | 描述 | 遍历方法 |
|---|---|---|
| values() | 以数组形式返回枚举类型的所有成员 | 遍历所有枚举 |
| ordinal() | 获取枚举成员的索引位置 | 获取枚举序号 |
| valueOf() | 将普通字符串转换为枚举实例 | 字符串转枚举 |
| compareTo() | 比较两个枚举成员在定义时的顺序 | 枚举比较 |
package test;
public class demo7{
public enum TestEnum {
RED, BLACK, GREEN, WHITE;
public static void main(String[] args) {
TestEnum[] testEnum2 = TestEnum.values();
for (int i = 0; i < testEnum2.length; i++) {
System.out.println(testEnum2[i] + " " + testEnum2[i].ordinal());
}
System.out.println("=========================");
System.out.println(TestEnum.valueOf("GREEN"));
System.out.println("=========================");
TestEnum testEnum = TestEnum.BLACK;
TestEnum testEnum21 = TestEnum.RED;
System.out.println(testEnum.compareTo(testEnum21));
System.out.println(BLACK.compareTo(RED));
System.out.println(RED.compareTo(BLACK));
}
}
}
2.3 枚举优点缺点
优点:
- 枚举常量更简单安全 。
- 枚举具有内置方法 ,代码更优雅
缺点:
1.不可继承,无法扩展
💡为什么枚举实现单例模式是安全的?与他们三个有关系吗?
枚举天生就是单例,而且是 Java 中最安全、最优雅的单例实现,没有之一。
-
枚举实例在 JVM 中天然是单例且全局唯一
枚举里的每个常量,本质都是
public static final的,在类加载阶段就初始化,由 JVM 保证只会存在一个实例。 -
构造方法天然私有,外部无法通过 new 创建对象
枚举的构造器默认就是 private,而且编译器不允许我们把它改成 public,从根源上杜绝了手动实例化。
-
可以防止反射攻击
普通单例可以通过反射暴力调用私有构造方法,从而破坏单例;
但 JVM 对枚举做了特殊处理,反射无法通过 new Instance 创建枚举对象,会直接抛出异常,因此单例不会被破坏。
-
可以防止序列化破坏单例
普通 Java 对象序列化再反序列化时,会创建一个新对象;
而枚举序列化时只传输名字,反序列化时通过
valueOf()找原有常量,不会产生新实例,依然保持单例。 -
天然线程安全
因为是在类加载阶段初始化,不存在多线程并发问题,不需要加锁,也不需要做双重校验。
-
和枚举直接相关:它本身就是用枚举特性实现的。
-
和反射强相关:普通单例会被反射破坏,枚举不会。
-
和 Lambda 基本无关,Lambda 只是函数式写法,不影响单例安全。
3.Lambda表达式
3.1 是什么?
简化的匿名内部类写法,专门用于函数式接口(只有一个抽象方法的接口)。
(参数) -> { 代码体 }
//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
void test(int a);
}
//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
void test(int a,int b);
}
//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
int test();
}
//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
int test(int a);
}
//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {
int test(int a,int b);
}
3.2 Lambda表达式的基本使用
package test;
// 1. 无参数无返回值 函数式接口
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
// 2. 一个参数无返回值 函数式接口
@FunctionalInterface
interface OneParameterNoReturn {
void test(int a);
}
// 3. 多个参数无返回值 函数式接口
@FunctionalInterface
interface MoreParameterNoReturn {
void test(int a, int b);
}
// 4. 无参数有返回值 函数式接口
@FunctionalInterface
interface NoParameterReturn {
int test();
}
// 5. 一个参数有返回值 函数式接口
@FunctionalInterface
interface OneParameterReturn {
int test(int a);
}
// 6. 多个参数有返回值 函数式接口
@FunctionalInterface
interface MoreParameterReturn {
int test(int a, int b);
}
public class demo8 {
public static void main(String[] args) {
// 1. 无参无返回
NoParameterNoReturn noParameterNoReturn = () -> {
System.out.println("无参数无返回值");
};
noParameterNoReturn.test();
// 2. 单参无返回
OneParameterNoReturn oneParameterNoReturn = (int a) -> {
System.out.println("一个参数无返回值:" + a);
};
oneParameterNoReturn.test(10);
// 3. 多参无返回
MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> {
System.out.println("多个参数无返回值:" + a + " " + b);
};
moreParameterNoReturn.test(20, 30);
// 4. 无参有返回
NoParameterReturn noParameterReturn = () -> {
System.out.println("有返回值无参数!");
return 40;
};
int ret = noParameterReturn.test();
System.out.println(ret);
// 5. 单参有返回
OneParameterReturn oneParameterReturn = (int a) -> {
System.out.println("有返回值有一个参数!");
return a;
};
ret = oneParameterReturn.test(50);
System.out.println(ret);
// 6. 多参有返回
MoreParameterReturn moreParameterReturn = (int a, int b) -> {
System.out.println("有返回值多个参数!");
return a + b;
};
ret = moreParameterReturn.test(60, 70);
System.out.println(ret);
}
}
3.3. 语法精简
public static void main(String[] args) {
MoreParameterNoReturn moreParameterNoReturn = ( a, b)->{
System.out.println("无返回值多个参数,省略参数类型:"+a+" "+b);
};
moreParameterNoReturn.test(20,30);
OneParameterNoReturn oneParameterNoReturn = a ->{
System.out.println("无参数一个返回值,小括号可以胜率:"+ a);
};
oneParameterNoReturn.test(10);
NoParameterNoReturn noParameterNoReturn = ()->System.out.println("无参数无返回值,方法体中只有一行代码");
noParameterNoReturn.test();
//方法体中只有一条语句,且是return语句
NoParameterReturn noParameterReturn = ()-> 40;
int ret = noParameterReturn.test();
System.out.println(ret);
}
3.4 Lambda在集合当中的使用
| 接口名称 | 新增方法 | 核心作用 |
|---|---|---|
| Collection | forEach() | 遍历集合,支持 Lambda 表达式 |
removeIf(Predicate<? super E> filter) | 根据条件批量删除元素 | |
stream() | 获取集合的顺序流(Stream),用于流式操作 | |
parallelStream() | 获取集合的并行流,提升大数据量处理效率 | |
spliterator() | 获取可分割迭代器,用于并行遍历 | |
| List | replaceAll(UnaryOperator<E> operator) | 对集合中所有元素执行统一操作,批量替换 |
sort(Comparator<? super E> c) | 直接在 List 上排序,无需额外创建数组 | |
| Map<K,V> | getOrDefault(Object key, V defaultValue) | 键不存在时返回默认值,避免空指针 |
forEach(BiConsumer<? super K,? super V> action) | 遍历 Map,支持 Lambda 处理键值对 | |
replaceAll(BiFunction<? super K,? super V,? extends V> function) | 批量更新 Map 中所有值 | |
putIfAbsent(K key, V value) | 键不存在时才插入,存在则不覆盖 | |
remove(Object key, Object value) | 仅当键值对完全匹配时才删除 | |
replace(K key, V value) / replace(K key, V oldValue, V newValue) | 替换指定键的值,支持新旧值校验 | |
computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) | 键不存在时,计算并插入新值 | |
computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) | 键存在时,计算并更新值 | |
compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) | 无论键是否存在,直接计算并更新值 | |
merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) | 合并新旧值,灵活处理键值对 |
3.5 代码展示
Collection 中的 forEach() 方法演示
package test;
import java.util.ArrayList;
import java.util.function.Consumer;
public class demo10 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("西安");
list.add("hello");
list.add("中国");
list.forEach(new Consumer<String>(){
@Override
public void accept(String str){
//简单遍历集合中的元素。
System.out.print(str+" ");
}
});
}
}
修改为lambda表达式:
package test;
import java.util.ArrayList;
import java.util.function.Consumer;
public class demo10 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("西安");
list.add("hello");
list.add("中国");
//表示调用一个,不带有参数的方法,其执行花括号内的语句,为原来的函数体内容。
list.forEach(s -> {
System.out.print(s);
});
}
}
List 中的 sort()方法的演示
package test;
import java.util.ArrayList;
import java.util.Comparator;
public class demo12 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("西安");
list.add("hello");
list.add("中国");
list.sort(new Comparator<String>() {
@Override
public int compare(String str1, String str2){
//注意这里比较长度
return str1.length()-str2.length();
}
});
System.out.println(list);
}
}
修改为lambda表达式:
package test;
import java.util.ArrayList;
import java.util.Comparator;
public class demo12 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("西安");
list.add("hello");
list.add("中国");
//调用带有2个参数的方法,且返回长度的差值
list.sort((str1,str2)-> str1.length()-str2.length());
System.out.println(list);
}
}
Map 中的 forEach() 方法演示
package test;
import java.util.HashMap;
import java.util.function.BiConsumer;
public class demo13 {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "hello");
map.put(2, "西安");
map.put(3, "hello");
map.put(4, "中国");
map.forEach(new BiConsumer<Integer, String>(){
@Override
public void accept(Integer k, String v){
System.out.println(k + "=" + v);
}
});
}
}
修改为lambda表达式:
package test;
import java.util.HashMap;
import java.util.function.BiConsumer;
public class demo13 {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "hello");
map.put(2, "西安");
map.put(3, "hello");
map.put(4, "中国");
map.forEach((k,v)-> System.out.println(k + "=" + v));
}
}
3.6Lambda 优缺点
Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。
优点:
- 代码简洁,开发迅速
- 方便函数式编程
- 非常容易进行并行计算
- Java 引入 Lambda,改善了集合操作
缺点:
1.代码可读性变差
2.在非并行计算中,很多计算未必有传统的 for 性能要高
3.不容易进行调试
更多推荐

所有评论(0)