JavaSE学习笔记(十四)反射

本文详细介绍了Java反射机制,包括通过Class对象获取类的构造器、成员变量和方法,并展示了如何使用反射创建对象、设置和获取成员变量的值以及调用方法。此外,还探讨了反射在绕过编译阶段泛型约束和通用框架底层原理中的应用。
摘要由CSDN通过智能技术生成

1 反射

概述

  • 反射是在运行时获取类的字节码文件对象:然后可以解析类中的全部成分
  • 在运行时,可以直接得到这个类的构造器对象:Constructor
  • 在运行时,可以直接得到这个类的成员变量对象:Field
  • 在运行时,可以直接得到这个类的成员方法对象:Method
  • 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制

关键

  • 反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分

2 获取Class类的对象

三种方式

  • Class c1 = Class.forName(“全限名”);
  • Class c2 = 类名.class
  • Class c3 = 对象.getClass();
public class Test {
    public static void main(String[] args) throws Exception {
        // 1、Class类中的一个静态方法:forName(全限名:包名 + 类名)
        Class c = Class.forName("com.fanglaosi.Student");
        System.out.println(c); // class com.fanglaosi.Student

        // 2、类名.class
        Class c1 = Student.class;
        System.out.println(c1);// class com.fanglaosi.Student

        // 3、对象.getClass() 获取对象对应类的Class对象。
        Student s = new Student();
        Class c2 = s.getClass();
        System.out.println(c2);// class com.fanglaosi.Student
    }
}

3 获取构造器对象并创建对象

获取构造器对象

方法说明
Constructor<?>[] getConstructors​()返回所有构造器对象的数组(只能拿public的)
Constructor<?>[] getDeclaredConstructors​()返回所有构造器对象的数组,存在就能拿到
Constructor< T> getConstructor​(Class<?>… parameterTypes)返回单个构造器对象(只能拿public的)
Constructor< T> getDeclaredConstructor​(Class<?>… parameterTypes)返回单个构造器对象,存在就能拿到
// 1. getConstructors:
// 获取全部的构造器:只能获取public修饰的构造器。
// Constructor[] getConstructors()
@Test
public void getConstructors(){
    // a.第一步:获取类对象
    Class c = Student.class;
    // b.提取类中的全部的构造器对象(这里只能拿public修饰)
    Constructor[] constructors = c.getConstructors();
    // c.遍历构造器
    for (Constructor constructor : constructors) {
        System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
    }
}


// 2.getDeclaredConstructors():
// 获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。
@Test
public void getDeclaredConstructors(){
    // a.第一步:获取类对象
    Class c = Student.class;
    // b.提取类中的全部的构造器对象
    Constructor[] constructors = c.getDeclaredConstructors();
    // c.遍历构造器
    for (Constructor constructor : constructors) {
        System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
    }
}

// 3.getConstructor(Class... parameterTypes)
// 获取某个构造器:只能拿public修饰的某个构造器
@Test
public void getConstructor() throws Exception {
    // a.第一步:获取类对象
    Class c = Student.class;
    // b.定位单个构造器对象 (按照参数定位无参数构造器 只能拿public修饰的某个构造器)
    Constructor cons = c.getConstructor();
    System.out.println(cons.getName() + "===>" + cons.getParameterCount());
}


// 4.getConstructor(Class... parameterTypes)
// 获取某个构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。
@Test
public void getDeclaredConstructor() throws Exception {
    // a.第一步:获取类对象
    Class c = Student.class;
    // b.定位单个构造器对象 (按照参数定位无参数构造器)
    Constructor cons = c.getDeclaredConstructor();
    System.out.println(cons.getName() + "===>" + cons.getParameterCount());

    // c.定位某个有参构造器
    Constructor cons1 = c.getDeclaredConstructor(String.class, int.class);
    System.out.println(cons1.getName() + "===>" + cons1.getParameterCount());

}

根据获得的构造器对象创建对象

方法说明
T newInstance​(Object… initargs)根据指定的构造器创建对象
public void setAccessible(boolean flag)设置为true,表示取消访问检查,进行暴力反射
// 1.调用构造器得到一个类的对象返回。
@Test
public void getDeclaredConstructor() throws Exception {
    // a.第一步:获取类对象
    Class c = Student.class;
    // b.定位单个构造器对象 (按照参数定位无参数构造器)
    Constructor cons = c.getDeclaredConstructor();
    System.out.println(cons.getName() + "===>" + cons.getParameterCount());

    // 如果遇到了私有的构造器,可以暴力反射
    cons.setAccessible(true); // 权限被打开

    Student s = (Student) cons.newInstance();
    System.out.println(s);

    System.out.println("-------------------");

    // c.定位某个有参构造器
    Constructor cons1 = c.getDeclaredConstructor(String.class, int.class);
    System.out.println(cons1.getName() + "===>" + cons1.getParameterCount());

    Student s1 = (Student) cons1.newInstance("孙悟空", 1000);
    System.out.println(s1);
}

意义

  • 依然是创建对象的
    • public newInstance(Object… initargs)
  • 如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象
    • setAccessible(boolean)
    • 反射可以破坏封装性,私有的也可以执行了

4 获取成员变量对象并赋值

获取成员变量对象

方法说明
Field[] getFields​()返回所有成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields​()返回所有成员变量对象的数组,存在就能拿到
Field getField​(String name)返回单个成员变量对象(只能拿public的)
Field getDeclaredField​(String name)返回单个成员变量对象,存在就能拿到
@Test
public void getDeclaredFields(){
    // a.定位Class对象
    Class c = Student.class;
    // b.定位全部成员变量
    Field[] fields = c.getDeclaredFields();
    // c.遍历一下
    for (Field field : fields) {
        System.out.println(field.getName() + "==>" + field.getType());
    }
}

/**
    2.获取某个成员变量对象 Field getDeclaredField(String name);
 */
@Test
public void getDeclaredField() throws Exception {
    // a.定位Class对象
    Class c = Student.class;
    // b.根据名称定位某个成员变量
    Field f = c.getDeclaredField("age");
    System.out.println(f.getName() +"===>" + f.getType());
}

为成员变量对象赋值

方法说明
void set​(Object obj, Object value)赋值
Object get​(Object obj)获取值
@Test
public void setField() throws Exception {
    // a.反射第一步,获取类对象
    Class c = Student.class;
    // b.提取某个成员变量
    Field ageF = c.getDeclaredField("age");

    ageF.setAccessible(true); // 暴力打开权限

    // c.赋值
    Student s = new Student();
    ageF.set(s , 18);  // s.setAge(18);
    System.out.println(s);

    // d、取值
    int age = (int) ageF.get(s);
    System.out.println(age);
}

5 获取方法对象并使用

获取方法对象

方法说明
Method[] getMethods​()返回所有成员方法对象的数组(只能拿public的)
Field[] getDeclaredFields​()返回所有成员方法对象的数组,存在就能拿到
Method getMethod​(String name, Class<?>… parameterTypes)返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod​(String name, Class<?>… parameterTypes)返回单个成员方法对象,存在就能拿到

使用方法对象

方法说明
bject invoke​(Object obj, Object… args)运行方法
-参数一:用obj对象调用该方法
-参数二:调用方法的传递的参数(如果没有就不写)
-返回值:方法的返回值(如果没有就不写)

6 反射的作用

6.1 绕过编译阶段为集合添加数据

  • 泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了
  • 反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的
public static void main(String[] args) throws Exception {
    // 需求:反射实现泛型擦除后,加入其他类型的元素
    ArrayList<String> lists1 = new ArrayList<>();
    ArrayList<Integer> lists2 = new ArrayList<>();

    System.out.println(lists1.getClass()); // class java.util.ArrayList
    System.out.println(lists2.getClass()); // class java.util.ArrayList

    System.out.println(lists1.getClass() ==  lists2.getClass());  // true

    System.out.println("---------------------------");
    ArrayList<Integer> lists3 = new ArrayList<>();
    lists3.add(23);
    lists3.add(22);
    // lists3.add("黑马");

    Class c = lists3.getClass(); // ArrayList.class  ===> public boolean add(E e)
    // 定位c类中的add方法
    Method add = c.getDeclaredMethod("add", Object.class);
    boolean rs = (boolean) add.invoke(lists3, "方佬肆");
    System.out.println(rs); // true

    System.out.println(lists3); // [23, 22, 方佬肆]

    ArrayList list4 = lists3;
    list4.add("李四");
    list4.add(false);
    System.out.println(lists3); // [23, 22, 方佬肆, 李四, false]
}

tip:擦除泛型的另一种方式

ArrayList<String> list = new ArrayList<>();
ArrayList list2 = list;
list2.add(false);
System.out.println(list); // false

6.2 通用框架的底层原理

  • 需求:给你任意一个对象,在不清楚对象字段的情况下,可以把对象的字段名称和对应值存储到文件中去
public static void save(Object obj){
    try (
            PrintStream ps = new PrintStream(new FileOutputStream("fanglaosi/src/data.txt", true));
    ){
        // 1、提取这个对象的全部成员变量:只有反射可以解决
        Class c = obj.getClass();  //   c.getSimpleName()获取当前类名   c.getName获取全限名:包名+类名
        ps.println("================" + c.getSimpleName() + "================");

        // 2、提取它的全部成员变量
        Field[] fields = c.getDeclaredFields();
        // 3、获取成员变量的信息
        for (Field field : fields) {
            String name = field.getName();
            // 提取本成员变量在obj对象中的值(取值)
            field.setAccessible(true);
            String value = field.get(obj) + "";
            ps.println(name  + "=" + value);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

作用:

  • 可以在运行时得到一个类的全部成分然后操作
  • 可以破坏封装性。(很突出)
  • 也可以破坏泛型的约束性。(很突出)
  • 更重要的用途是适合:做Java高级框架
  • 基本上主流框架都会基于反射设计一些通用技术功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值