02-Java反射

举报
kwan的解忧杂货铺 发表于 2024/05/27 22:12:54 2024/05/27
【摘要】 一.反射概念反射技术通常被用来检测和改变应用程序在 Java 虚拟机中的行为表现。它是一个相对而言比较高级的技术,通常它应用的前提是开发者本身对于 Java 语言特性有很强的理解的基础上。值得说明的是,反射是一种强有力的技术特性,因此可以使得应用程序执行一些常规手段无法企及的目的。Java 的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个...

一.反射概念

反射技术通常被用来检测和改变应用程序在 Java 虚拟机中的行为表现。它是一个相对而言比较高级的技术,通常它应用的前提是开发者本身对于 Java 语言特性有很强的理解的基础上。值得说明的是,反射是一种强有力的技术特性,因此可以使得应用程序执行一些常规手段无法企及的目的。

Java 的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为 Java 语言的反射机制。反射被视为动态语言的关键。

  • java.lang.reflect AccessibleObject
  • java.lang.reflect Array
  • java.lang.reflect Constructor
  • java.lang.reflect Field
  • java.lang.reflect Method
  • java.lang.reflect Modifier
  • java.lang.reflect Proxy

Class 类实例表示正在运行的 Java 应用程序中的类和接口。Class 是普通类、接口、枚举类、数组等的抽象,即它们的类型就是 Class,它们是 Class 的实例。

二.使用反射

1.常用方法

  • getName():返回 String 形式的该类的名称。
  • newInstance():根据某个 Class 对象产生其对应类的实例,它调用的是此类的默认构造方法(没有默认无参构造器会报错)
  • getClassLoader():返回该 Class 对象对应的类的类加载器。
  • getSuperClass():返回某子类所对应的直接父类所对应的 Class 对象
  • getConstructor(Class[]) :返回当前 Class 对象表示的类的指定的公有构造子对象。
  • getConstructors() :返回当前 Class 对象表示的类的所有公有构造子对象数组。
  • getDeclaredConstructor(Class[]):返回当前 Class 对象表示的类的指定已说明的一个构造子对象。
  • getDeclaredConstructors() :返回当前 Class 对象表示的类的所有已说明的构造子对象数组。
  • getDeclaredField(String) :返回当前 Class 对象表示的类或接口的指定已说明的一个域对象。
  • getDeclaredFields() :返回当前 Class 对象表示的类或接口的所有已说明的域对象数组。
  • getDeclaredMethod(String, Class[]) :返回当前 Class 对象表示的类或接口的指定已说明的一个方法对象。
  • getDeclaredMethods() :返回 Class 对象表示的类或接口的所有已说明的方法数组。
  • getField(String) :返回当前 Class 对象表示的类或接口的指定的公有成员域对象。
  • getFields() :返回当前 Class 对象表示的类或接口的所有可访问的公有域对象数组。
  • getInterfaces() :返回当前对象表示的类或接口实现的接口。
  • getMethod(String, Class[]) :返回当前 Class 对象表示的类或接口的指定的公有成员方法对象。
  • getMethods() :返回当前 Class 对象表示的类或接口的所有公有成员方法对象数组,包括已声明的和从父类继承的方法。
  • isInstance(Object) :此方法是 Java 语言 instanceof 操作的动态等价方法。
  • isInterface() :判定指定的 Class 对象是否表示一个接口类型
  • isPrimitive() :判定指定的 Class 对象是否表示一个 Java 的基类型。

2.获取 Class

//第一种
public class Basic_Reflect_01_getName {
    public static void main(String[] args) {
        //1.获取并输出类的名称
        Class mClass = SonClass.class;
        System.out.println("类的名称:" + mClass.getName());
    }
}
public class Basic_Reflect_01_getName_02 {
    public static void main(String[] args) {
        //1.获取并输出类的名称
        SonClass x = new SonClass();
        Class mClass = x.getClass();
        System.out.println("类的名称:" + mClass.getName());
    }
}
public class Basic_Reflect_01_getName_03 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.获取并输出类的名称
        Class mClass = Class.forName("com.xiaofei.antbasic.basic_reflect.SonClass");
        System.out.println("类的名称:" + mClass.getName());
    }
}
public class Basic_Reflect_01_getName_04 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.获取并输出类的名称
        Class<?> class4 =SonClass.class.getClassLoader().loadClass("com.xiaofei.antbasic.basic_reflect.SonClass");
        System.out.println("类的名称:" + mClass.getName());
    }
}

3.getFields 方法

getFields 和 getDeclaredFields 的区别?

getFields:通过反射获取类的所有变量 获取所有 public 访问权限的变量

getDeclaredFields:获取所有本类声明的变量(不限制访问权限)

getMethods 和 getDeclaredMethods 同理

//变量
//访问权限
//类型
//名称
public class Basic_Reflect_02_getFields_01 {
    public static void main(String[] args) {
        Class mClass = SonClass.class;
        //获取所有 public 访问权限的变量
        // 包括本类声明的和从父类继承的
        Field[] fields = mClass.getFields();
        //3. 遍历变量并输出变量信息
        for (Field field : fields) {
            //获取访问权限并输出
            int modifiers = field.getModifiers();
            System.out.print(Modifier.toString(modifiers) + " ");
            //输出变量的类型及变量名
            System.out.println(field.getType().getName()
                    + " " + field.getName());
        }
    }
}

4.getMethods 包含信息

//修饰符
//返回值类型
//方法参数
//异常
public class Basic_Reflect_03_getMethods_01 {
    public static void main(String[] args) {
        Class mClass = SonClass.class;
        //获取所有 public 访问权限的方法
        //包括自己声明和从父类继承的
        Method[] mMethods = mClass.getMethods();
        //3.遍历所有方法
        for (Method method : mMethods) {
            //获取并输出方法的访问权限(Modifiers:修饰符)
            int modifiers = method.getModifiers();
            System.out.print(Modifier.toString(modifiers) + " ");
            //获取并输出方法的返回值类型
            Class returnType = method.getReturnType();
            System.out.print(returnType.getName() + " "
                    + method.getName() + "( ");
            //获取并输出方法的所有参数
            Parameter[] parameters = method.getParameters();
            for (Parameter parameter :
                    parameters) {
                System.out.print(parameter.getType().getName()
                        + " " + parameter.getName() + ",");
            }
            //获取并输出方法抛出的异常
            Class[] exceptionTypes = method.getExceptionTypes();
            if (exceptionTypes.length == 0) {
                System.out.println(" )");
            } else {
                for (Class c : exceptionTypes) {
                    System.out.println(" ) throws "
                            + c.getName());
                }
            }
        }
    }
}

5.访问私有方法?

privateMethod.setAccessible(true);

public class Basic_Reflect_04_getPrivateMethod {
    public static void main(String[] args) throws Exception {
        //1. 获取 Class 类实例
        TestClass testClass = new TestClass();
        Class mClass = testClass.getClass();
        //2. 获取私有方法
        //第一个参数为要获取的私有方法的名称
        //第二个为要获取方法的参数的类型,参数为 Class...,没有参数就是null
        //方法参数也可这么写 :new Class[]{String.class , int.class}
        Method privateMethod = mClass.getDeclaredMethod("privateMethod", String.class, int.class);
        //3. 开始操作方法
        if (privateMethod != null) {
            //获取私有方法的访问权
            //只是获取访问权,并不是修改实际权限
            privateMethod.setAccessible(true);
            //使用 invoke 反射调用私有方法
            //privateMethod 是获取到的私有方法
            //testClass 要操作的对象
            //后面两个参数传实参
            privateMethod.invoke(testClass, "Java Reflect ", 666);
        }
    }
}

6.修改私有变量

public class Basic_Reflect_05_modifyPrivateFiled {
    public static void main(String[] args) throws Exception {
        //1. 获取 Class 类实例
        TestClass testClass = new TestClass();
        Class mClass = testClass.getClass();
        //2. 获取私有变量
        Field privateField = mClass.getDeclaredField("MSG");
        //3. 操作私有变量
        if (privateField != null) {
            //获取私有变量的访问权
            privateField.setAccessible(true);
            //修改私有变量,并输出以测试
            System.out.println("Before Modify:MSG = " + testClass.getMsg());
            //调用 set(object , value) 修改变量的值
            //privateField 是获取到的私有变量
            //testClass 要操作的对象
            //"Modified" 为要修改成的值
            privateField.set(testClass, "Modified");
            System.out.println("After Modify:MSG = " + testClass.getMsg());
        }
    }
}

7.修改常量 final

  • 第一句打印修改前 FINAL_VALUE 的值,没有异议;
  • 第二句打印修改后常量的值,说明 FINAL_VALUE 确实通过反射修改了;
  • 第三句打印通过 getFinalValue() 方法获取的 FINAL_VALUE 的值,但还是初始值,导致修改无效!

原因 :程序运行时是根据编译后的 .class 来执行的。

public class Basic_Reflect_06_modifyFinalFiled {
    public static void main(String[] args) throws Exception {
        //1. 获取 Class 类实例
        TestClass testClass = new TestClass();
        Class mClass = testClass.getClass();
        //2. 获取私有常量
        Field finalField = mClass.getDeclaredField("FINAL_VALUE");
        //3. 修改常量的值
        if (finalField != null) {
            //获取私有常量的访问权
            finalField.setAccessible(true);
            //调用 finalField 的 getter 方法
            //输出 FINAL_VALUE 修改前的值
            System.out.println("Before Modify:FINAL_VALUE = "
                    + finalField.get(testClass));
            //修改私有常量
            finalField.set(testClass, "Modified");
            //调用 finalField 的 getter 方法
            //输出 FINAL_VALUE 修改后的值
            System.out.println("After Modify:FINAL_VALUE = "
                    + finalField.get(testClass));
            //使用对象调用类的 getter 方法
            //获取值并输出
            System.out.println("Actually :FINAL_VALUE = "
                    + testClass.getFinalValue());
        }
    }
}
private final String FINAL_VALUE_000 = null == null ? "FINAL" : null;

8.构造函数

  • 先获取 getConstructor
  • 再调用 newInstance,获取实例
//无参构造函数
public class Basic_Reflect_07_getConstructor_01 {
    public static void main(String[] args) throws Exception {
        //第一步:获取到Class对象
        Class personClazz = TestClass.class;
        // 第二步:获取构造方法
        Constructor<TestClass> constructor = personClazz.getConstructor();
        // 第三步:创建对象
        TestClass testClass = constructor.newInstance();
        System.out.println(testClass);
    }
}
//有参构造函数
public class Basic_Reflect_07_getConstructor_02 {
    public static void main(String[] args) throws Exception {
        // 第一步:获取到Class对象
        Class personClazz = Person.class;
        // 第二步:获取构造方法
        Constructor<Person> constructor = personClazz.getConstructor(String.class, Integer.class);
        // 第三步:创建对象
        Person person = constructor.newInstance("张三", 10);
        System.out.println(person);
        System.out.println(person.getName() + ":" + person.getAge());
    }
}

三.常见方法

1.getName()

一个 Class 对象描述了一个特定类的属性,Class 类中最常用的方法 getName 以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类.基本类型或 void)名称。

2.newInstance()

Class 还有一个有用的方法可以为类创建一个实例,这个方法叫做 newInstance()。例如:
x.getClass.newInstance(),创建了一个同 x 一样类型的新实例。newInstance()方法调用默认构造器(无参数构造器)初始化新建对象。

3.getClassLoader()

返回该类的类加载器。

4.getComponentType()

返回表示数组组件类型的 Class。

5.getSuperclass()

返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。

6.isArray()

判定此 Class 对象是否表示一个数组类。

四.常见问题

1.利用反射创建类实例?

方法一:newInstance

//首先获取Class对象
Class clazz=Class.forClass("test.Student");
//创建对象
Student stu=(Student)clazz.newInstance();

方法二:getConstructor

//首先创建Class对象
Class clazz=Class.forClass("test.Student");
//获取想调用的构造函数
Constructor constructor=clazz.getConstructor(String.class, int.class);
//调用Constructor的newInstance()方法
Student stu=(Student)constructor.newInstance("大王"20);

2.调用方法的方式?

反射要调用类中的方法,需要通过关键方法“invoke()”实现的,方法调用也分为三种:

  • 静态(static)方法调用
  • 普通方法调用
  • 私有方法调用
//privateMethod 是获取到的方法
//testClass 要操作的对象
//后面两个参数传实参
privateMethod.invoke(testClass, "Java Reflect ", 666);

3.newInstance 使用

new 关键字和 newInstance()方法的区别?

  • newInstance: 弱类型。低效率。只能调用无参构造。 构造器的 newInstance 可以是有参的
  • new: 强类型。相对高效。能调用任何 public 构造。

4.反射创建数组?

数组本质上是一个 Class,而在 Class 中存在一个方法用来识别它是否为一个数组。

反射创建数组是通过 Array.newInstance(T.class,维数) 这个方法。

第一个参数指定的是数组内的元素类型,后面的是可变参数,表示的是相应维度的数组长度限制。

//比如,我要创建一个 int [2][3]的数组。
Int[][]  a=Array.newInstance(Integer.TYPE, 2, 3);

5.Spring 创建 bean

Spring 组件即通过反射使用无参构造方法创建 bean:

<bean id="person" class="com.company.demo2.Person"></bean>

Spring 组件即通过反射使用有参构造方法创建 bean:

<bean id="person" class="com.company.demo2.Person">
 	<constructor-arg index="0" type="java.lang.String" value="张三"></constructor-arg>
	<constructor-arg index="1" type="java.lang.Integer" value="10"></constructor-arg>
</bean>

6.判断属性存在

获取所有属性

 BrandStoreSkuInvSalRateWeekDTO.class.getDeclaredField(sortname);

7.forName 和 loadClass 区别?

Class.forName()和ClassLoader.loadClass()的区别:

  • foraNme 在类加载的时候会执行静志代码块
  • loadClass 只有在调用 newInstance 方法的时候才会执行静态代码块
  • 初始化不同:Class.forName()会对类初始化,而 loadClass(只会装在或链接。可见的效果就是类中静态初始化段及字节码中对所有静态成员的初始工作的执行(这个过程在类的所有父类中递归地调用).这点就与 ClassLoader.loadClass()不同. ClassLoader.loadClass(加载的类对象是在第一次被调用时才进行初始化的。你可以利用上述的差异.比如,要加载一个静态初始化开销很大的类,你就可以选择提前加载该类(以确保它在 classpath 下),但不进行初始化,直到第一次使用该类的域或方法时才进行初始化
  • 类加载器不用:Class.forName(String)方法(只有一个参数),使用调用者的类加载器来加载,也就是用加载了调用 forName 方法的代码的那个类加载器。当然,它也有个重载的方法,可以指定加载器。相应的,ClassLoader.loadClass()方法是一个实例方法(非静态方法),调用时需要自己指定类加载器,那么这个类加载器就可能是也可能不是加载调用代码的类加载器(调用代用代码类加载器通 getClassLoaderoQ 获得)
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。