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()必须先创建对象,运行时获取已经有对象,需要获取类信息
类名.classStudent.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.

1.6反射优点和缺点

优点:

  1. 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
  2. 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
  3. 反射已经运用在了很多流行框架如: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. 枚举常量更简单安全 。
  2. 枚举具有内置方法 ,代码更优雅
    缺点:
    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在集合当中的使用

接口名称新增方法核心作用
CollectionforEach()遍历集合,支持 Lambda 表达式
removeIf(Predicate<? super E> filter)根据条件批量删除元素
stream()获取集合的顺序流(Stream),用于流式操作
parallelStream()获取集合的并行流,提升大数据量处理效率
spliterator()获取可分割迭代器,用于并行遍历
ListreplaceAll(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表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。
优点:

  1. 代码简洁,开发迅速
  2. 方便函数式编程
  3. 非常容易进行并行计算
  4. Java 引入 Lambda,改善了集合操作
    缺点:
    1.代码可读性变差
    2.在非并行计算中,很多计算未必有传统的 for 性能要高
    3.不容易进行调试

更多推荐