【JavaSE】十二、Collection工具类 && 反射 详解

2025博客之星年度评选已开启 10w+人浏览 567人参与


在这里插入图片描述

Ⅰ. Collection工具类

官方文档

CollectionsCollection,名字看起来很相似,但实际上它们是 两个完全不同的东西!不过,它们确实有一些关系,主要体现在 Collections 是为了操作 Collection 接口的集合类提供了很多常用的工具方法

方法作用示例
sort()对 List 排序Collections.sort(list);
reverse()翻转 ListCollections.reverse(list);
shuffle()打乱 ListCollections.shuffle(list);
swap()交换元素Collections.swap(list, i, j);
max() / min()求最大 / 最小元素Collections.max(list);
frequency()某元素出现次数Collections.frequency(list, "apple");
copy()拷贝列表Collections.copy(dest, src);(dest 必须有足够容量)
fill()用一个值填充整个列表Collections.fill(list, 0);
binarySearch()二分查找(需已排序)Collections.binarySearch(list, target);
replaceAll()替换指定值Collections.replaceAll(list, oldVal, newVal);
rotate()旋转列表(整体右移)Collections.rotate(list, k);

其中要倒序排序的话,可以使用:Collections.sort(list, Collections.reverseOrder());

并且排序的类型如果是自定义类型,同样要重写一下特定的比较器,如下所示:

Collections.sort(rec, new Comparator<String>() {
    public int compare(String word1, String word2) {
        return cnt.get(word1) == cnt.get(word2) ? word1.compareTo(word2) : cnt.get(word2) - cnt.get(word1);
    }
});

Ⅱ. 概念与意义

Java 反射是一种机制,允许程序在运行时

  1. 查看类的信息(如属性、方法、构造器等)
  2. 动态创建对象
  3. 访问和修改属性
  4. 调用方法
  5. 操作泛型参数、注解等元信息

反射的意义和应用场景如下所示:

应用场景描述举例
框架设计核心Spring、Hibernate、MyBatis 等全靠它动态注入Bean、解析注解、AOP
通用工具类/库开发不知道具体类时也能处理JSON序列化、ORM字段映射
插件机制 / 动态加载类运行时加载未编译进来的类SPI机制、Class.forName()
动态代理 & AOP 编程实现日志增强、权限控制等JDK动态代理、AspectJ
测试 & Mock 框架访问私有字段,模拟行为JUnit, Mockito 等
简化开发 / 规避重复代码构建泛型代码库动态调用 setter/getter

使用反射的风险和代价如下所示:

风险/代价描述
⏱ 性能开销反射是解释型操作,慢于直接调用
🔒 安全问题可访问私有字段/方法,绕过封装
🧩 可读性差代码不直观,难调试
🧱 编译器检查失效编译时不知道是否写错方法名或类型

Ⅲ. 反射相关的类与操作

类名用途
Class类代表类的实体,在运行的Java应用程序中表示类和接口
Field类代表类的成员变量、属性
Method类代表类的方法
Constructor类代表类的构造方法
Annotation类代表类的注解

一、Class类(一切反射的起点)

官方文档

Java 文件被编译后,生成了 .class 文件,JVM 此时会去解读这个 .class 文件,而 .class 文件也被 JVM 解析为一个对象,这个对象就是 java.lang.Class

通过 Java 的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类。

一旦拿到了 Class 对象,比如:

Class<?> clazz = Person.class; // 具体获取方式看后面

可以做很多事,比如下面的操作:(剩下的查文档)

方法用途
clazz.getName()获取类名
clazz.getDeclaredFields()获取所有字段(所有权限均可访问)
clazz.getDeclaredField(String name)获取指定字段(所有权限均可访问)(下面方法也是类似)
clazz.getDeclaredMethods()获取所有方法(所有权限均可访问)
clazz.getDeclaredConstructors()获取构造器(所有权限均可访问)
clazz.newInstance()创建对象(已废弃,推荐 getConstructor().newInstance())
clazz.getSuperclass()获取父类
clazz.getInterfaces()获取实现的接口
clazz.isInterface()是否是接口
clazz.isAnnotationPresent(XXX.class)是否被某注解标记
clazz.getLoader()获得类的加载器

二、获取Class类的三种方式(Student类见附录)

方式语法说明
Class.forName("全限定名")Class.forName("com.demo.Person")常用于配置、动态加载
类静态变量 classPerson.class编译期就能获得类信息
对象.getClass()obj.getClass()运行时获得对象的真实类型
public static void main(String[] args){
    // 1. 使用Class.forName(),注意要指定包名,并且最好捕捉异常
    Class<?> c1 = null;
    try {
        c1 = Class.forName("Reflection.Student");
    } catch(ClassNotFoundException e) {
        e.printStackTrace();
    }

    // 2. 使用指定类的class静态变量
    Class<?> c2 = Student.class;

    // 3. 使用对象实例的getClass()方法
    Student st = new Student();
    Class<?> c3 = st.getClass();

    // 一个类在JVM中只会有一个Class实例,因此c1、c2、c3都指向同一个Class实例
    System.out.println(c1 == c2); // true
    System.out.println(c2 == c3); // true
    System.out.println(c3 == c1); // true
}

☠注意事项:

  1. 一个类在 JVM 中只会有一个 Class 实例。不管你创建多少对象,它们的 .getClass() 是同一个。
  2. 泛型信息在运行时会被擦除,可以参考泛型笔记。
  3. Class.forName() 会触发类初始化,会执行静态代码块,慎用

三、创建对象的两种方式

① 直接调用 Class 中的 newInstance() 方法

第一种方式就是调用 Class 对象中的 newInstance() 方法,这种方法现在已经被淘汰了,原因如下所示:

  1. 只能调用 无参构造方法
  2. 如果构造方法不是 public,会抛异常
  3. 已被标记为 过时方法(从 JDK 9 起)
public static void main(String[] args) {
    try {
        // 1. 先获取Class对象
        Class c = Class.forName("Reflection.Student");

        // 2. 然后调用newInstance()方法创建对象,注意要强转成Student类型
        Student p = (Student)c.newInstance();
        System.out.println(p);

    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

② 生成 Constructor 构造方法对象

官方文档

第二种方式先调用 Class 对象中的 getConstructor() 系列的方法,得到 Constructor 对象,然后调用该对象中的 newInstance() 方法,优势如下所示:

  1. 可调用有参构造方法
  2. 可修改构造器可见性(setAccessible(true)
public static void main(String[] args) {
    try {
        // 1. 获取Class对象
        Class<?> cs = Class.forName("Reflection.Student");

        // 2. 获取无参的构造方法来创建对象
        Constructor<?> non_arg_constructor = cs.getDeclaredConstructor();
        Student s1 = (Student)non_arg_constructor.newInstance();
        System.out.println(s1);

        // 3. 获取带参的构造方法来创建对象,因为带参构造方法是私有的,所以要设置权限为true
        Constructor<?> arg_constructor = cs.getDeclaredConstructor(String.class, int.class);
        arg_constructor.setAccessible(true);
        Student s2 = (Student)arg_constructor.newInstance("liren", 18);
        System.out.println(s2);
        
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

// 运行结果:
Student()
Student{name='bit', age=18}
Student(String,name)
Student{name='liren', age=18}

四、操作类的字段(例子)

官方文档

public static void main(String[] args) {
    // 使用反射操作字段
    try {
        Class<?> cs = Class.forName("Reflection.Student");

        // 1. 输出类名,打印字段信息
        System.out.println("ClassName: " + cs.getName());
        for(Field e : cs.getDeclaredFields()) {
            System.out.println(e.getName() + " " + e.getType().getName() + " " + Modifier.toString(e.getModifiers()));
        }

        // 2.1 获取私有属性name
        Field namefield = cs.getDeclaredField("name");
        namefield.setAccessible(true); // 对于私有属性,需要设置Accessible为true

        // 2.2 获取该类对象,并设置name属性的值,然后用namefield.get()获取这个私有值
        Student st = (Student)cs.newInstance();
        namefield.set(st, "liren");
        System.out.println("反射私有属性修改了name:" + namefield.get(st));

    } catch (ClassNotFoundException | NoSuchFieldException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

// 运行结果:
ClassName: Reflection.Student
name java.lang.String private
age int public
Student()
反射私有属性修改了name:liren

五、操作类的方法(例子)

官方文档

public static void main(String[] args) {
    try {
        Class<Student> cs = Student.class;

        // 获取带参的私有方法,然后设置权限,最后使用invoke()方法进行调用
        Method func_method = cs.getDeclaredMethod("function", String.class);
        func_method.setAccessible(true);
        func_method.invoke((Student)cs.newInstance(), "我是给私有的function函数传的参数");

        // 还可以输出方法的指定信息
        System.out.println("方法名:" + func_method.getName());
        System.out.println("修饰符为:" + Modifier.toString(func_method.getModifiers()));

    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        throw new RuntimeException(e);
    }
}

// 运行结果:
Student()
我是给私有的function函数传的参数
方法名:function
修饰符为:private

附录

class Student {
    // 私有属性name
    private String name = "bit";
    // 公有属性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");
    }

    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 +
                '}';
    }
}

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

利刃大大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值