反射——Java学习笔记

    科技2026-04-07  13

    反射

    反射概述哪些类有Class对象获得Class从Class中获取信息获取包名和类型名获取属性获取方法获取构造器获取类的加载器获取类型修饰符获取父类或父接口获取内部类或外部类信息获取泛型父类获取注解信息 使用反射生成对象并操作使用反射创建对象获取、设置属性值调用方法操作数组

    反射概述

    Java反射机制是运行状态中,对于任意一个类,都能够知道这个类的所有的属性和方法,对于任意一个对象,都能够调用它的任意方法和属性,这种动态获取信息以及动态调用对象的方法就被称为Java语言的反射机制

    java程序中,所有对象都有两种类型,编译时类型和运行时类型,而很多时候对象的编译时类型和运行时类型不同

    例如我们某些变量或形参的类型是Object类型,但是程序却需要调用该对象运行时类型的方法,该方法不是Object中的方法,那么如果解决呢?

    在编译和运行时,多万千知道类型的具体信息,这种情况下,我们可以直接先使用instanceof运算符进行判断,再利用强制类型转换,将其转换为运行时类型的变量编译时根本无法预知该对象和类的真实信息,程序只能依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射

    Class类:

    ​ 因为加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象,(一个类只有一个Class对象)这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构,这个对象就是一面镜子,通过这个镜子就能看到这个类的结构,所以我们形象的称之为反射所以最终如果想要解刨一个类,必须先获取该类的字节码对象,而解刨使用的就是Class类中的方法,所以先要获取每个类对应的Class对象。反射就是把java类中的各个组成部分映射成一个个的java对象。

    哪些类有Class对象

    Class类的实例表示正在运行的Java应用程序的类和接口,事实上,所有类型都可以表示为Class的实例对象

    class:外部类,内部类interface:接口[]:数组,所具有相同元素类型和维数的数组共享同一个Class对象enum:枚举annotation:注解@interfaceprimitive type:8种基本数据类型void:是一种特殊的类型,如果要获取void表示的类型,则返回null

    获得Class

    java中有四种方法获取Class对象:

    类名.class,适用于编译期间已知的任意类型调用任意对象的getClass()方法,可以获取该对象运行时类型的Class对象使用Class类的forName(String name)静态方法,该方法需要传入一个类的全限定名调用类加载对象的loadClass(String name)该方法需要传入一个类的全限定名 Class<?> clazz = String.class; Class<?> c2 = clazz.getClass(); Class<?> c3 = Class.forName("java.lang.String"); Class<?> c4 = ClassLoader.getSystemClassLoader().loadClass("java.lang.String");

    如果某个类型编译期间是已知的,优先考虑使用方式1,代码更安全,效率更高。另外基本数据类型和数组类型也只能通过该方法获得Class对象

    如果某个类型编译期间是未知的,我们只能通过某种方式获取个该类型的全限定名的字符串形式,使用方式3、4,但是该方法运行期间仍然无法加载该类的话,会报ClassNotFoundExcrption。这两种方式不适用与基本数据类型和数组类型

    Class<?> intC = int.class;//基本数据类型 Class<?> intsC1 = int[].class;//数组 Class<?> intsC2 = int[][].class; //维度不同的数组Class不同 System.out.println(intsC1==intsC2); Class<?> c5 = Override.class;//注解 Class<?> c6 = ElementType.class;//枚举 Class<?> c7 = Comparable.class;//接口

    从Class中获取信息

    Class类提供了大量的实例方法来获取该Class对象所对应类的详细信息

    包、修饰符、类名、父类、父接口、注解、成员(属性、方法、构造器)反射相关的API主要是java.lang.Class和java.lang.reflect包的内容

    获取包名和类型名

    public Package getPackage();获取此类的包名public String getName();获取此类的包名和类名public String getSimpleName();获取此类名 Class<?> clazz = String.class; System.out.println("获取包名:"+clazz.getPackage()); System.out.println("获取包名和类名:"+clazz.getName()); System.out.println("获取类名:"+clazz.getSimpleName());

    获取属性

    public Field getField(String name);根据指定name获得public修饰的Fieldpublic Field[] getFIelds();获得所有public修饰的Fieldpublic Field getDeclaredField(String name);根据指定name获取Fieldpublic Field[] getDeclaredFields();获得所有Field Class<?> clazz = String.class; //获取String类中的hash属性(即使时private修饰) System.out.println(clazz.getDeclaredField("hash")); //获取String类中的全部属性 Field[] fields = clazz.getDeclaredFields(); for (Field f :fields){ System.out.println("属性名:"+f.getName()); }

    获取方法

    public Method getMethod(String name,Class<?>…parameterTypes);根据指定的name和参数的类型,获得public修饰的Methodpublic Method[] getMethods();返回所有public修饰的Methodpublic Method getDeclaredMethod(String name,Class<?>… parameterYypes);根据指定的name和参数类型,获得Methodpublic Method[] getDeclaredMethods();获取所有的Method获得所有的Method Class<?> clazz = String.class; //获取String类中参数列表为int的valueOf方法 Method method1 = clazz.getDeclaredMethod("valueOf",int.class); System.out.println(method1); //获取全部方法 Method[] methods = clazz.getMethods(); for (Method method : methods) { System.out.println(method); }

    获取构造器

    public Constructor getConstructor(Class<?> …parameterTypes);根据指定的参数类型,获取public修饰的Constructorpublic Constructor<?>[] getConstructors();获得所有public修饰的Constructorpublic Constructor getDeclaredConstructor(Class<?> …parameterTypes);根据指定的参数类型,获得Constructorpublic Constructor<?>[] getDeclaredConstructors();获得所有的Constructor Class<?> clazz = String.class; Constructor<?> constructor = clazz.getConstructor(); System.out.println("无参的构造方法:"+constructor); //全部的构造方法 Constructor<?>[] constructors = clazz.getConstructors(); for (Constructor<?> c : constructors){ System.out.println(c); }

    --------------------------------------------------了解:------------------------------------------------

    获取类的加载器

    public ClassLoader getClassLoader();返回该类的加载器,有些实现可能使用null来表示引导类加载器,如果此对象表示一个基本数据类型或void,则返回null。

    获取类型修饰符

    public int getModifiers();返回此类(接口)以整数编码的Java语言修饰符 修饰符由Java虚拟机的public、protected、private、final、static、abstract和interface对应的常量组成,它们应当使用Modifier类的方法来解码。几种情况: 如果底层类是数组类,则其public、private和protected修饰符与其组件类型的修饰符相同。如果此Class表示一个基本类型或void,则其public修饰符始终为true,protected和private修饰符始终为false。如果此对象表示一个数组类,一个基本数据类型或void,则其final修饰符始终为true,其接口修饰符始终为false。该规范没有给定其他修饰符的值。

    获取父类或父接口

    public Class<? super T> getSuperClass();返回表示此Class所表示的实体(类、接口、基本类型或void)的超类的Class。 如果此Class表示Object类、一个接口、一个基本类型或void,则返回null如果此Class表示一个数组类,则返回表示该Object类的Class对象 public Class<?>[] getInterfaces();确定此对象所表示的类或接口实现的接口, 如果此对象表示一个类,则返回值是一个数组它包含了表示该类所实现的所有接口的对象,数组中接口对象顺序于此对象所表示的类声明的implements子句中接口名顺序一致如果此对象表示一个接口,则该数组包含表示该接口扩展的所有接口的对象,数组中接口对象顺序与此对象所表示的接口声明的extends子句中接口名顺序一致。如果此对象表示一个不实现任何接口的类或接口,则此方法返回一个长度为0的数组如果此对象表示一个基本数据类型或void,则此方法返回一个长度为0的数组

    获取内部类或外部类信息

    public Class<?>[] getClasses();返回所有公共内部类和内部接口。包括从超类继承的公共类和接口成员以及该类声明的公共类和接口成员public Class<?>[] getDeclaredClasses();返回Class对象的一个数组,这些对象反映声明为此Class对象所表示的类的成员和所有类和接口(包括该类所声明的公共、保护、默认、访问及私有类和接口),但是不包括继承的类和接口public Class<?> getDeclaringClass();返回它的外部类或外部接口,如果此对象不是一个内部类或内部接口(没有外部类或外部接口),则返回null

    获取泛型父类

    JDK1.5引入泛型,为了通过反射操作这些泛型,新增加了ParameterizedType、GenericArrayType、TypeVariable、WildcardType几种类型来代表不能被归到Class中的类型但是又和原始类型齐名的类型

    而在Class类、Field类、Method类等API中增加了很多关于获取泛型信息的方法,例如在Class类中就有很多,其中有一个获取泛型父类的方法: public Type getGenericSuperclass():返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type

    获取注解信息

    public Annotation[] getAnnotations():返回此元素上存在的所有注解public Annotation[] getDeclaredAnnotations();获取某个元素上存在的所有注解。(该方法会忽略继承的注解)publicT getAnnotation(Class annotationClass);如果存在该元素的指定类型的注解,则返回这些注解,否则返回null

    (想要通过反射获取到某个注解的信息,该注解声明时必须加@Retention(RetentionPolicy.RUNTIME)元注解,表名滞留注解信息到运行时)

    使用反射生成对象并操作

    使用反射创建对象

    使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方法要求该Class对象的对应类有默认构造器,而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例。(所以说我们在自定义javabean时都要提供一个公共无参的构造方法)使用Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance(Object… args)方法来创建该Class对象对应类的实例,通过这种方式可以选择使用某个类的指定构造器来创建实例 //获取自定义类 Class<?> clazz = Class.forName("com.bdit.Student"); //通过反射创建对象 Object obj1 = clazz.newInstance(); System.out.println(obj1); //指定构造器创建对象 Object obj2 = clazz.getConstructor(String.class,int.class,int.class).newInstance("张三",1001,20); System.out.println(obj2);

    获取、设置属性值

    通过Class对象的getFields()等方法可以获取该类所包含的Field或指定Field。而Field类除了提供了获取属性的修饰符、属性类型、属性名的方法外,我们还可以获取、设置属性值

    public Object get(Object obj);返回由该Field表示的字段在指定对象obj上的值 如果是基本数据类型使用getXxx(Object obj)获取属性值 public void set(Object obj,Object value);将指定的对象参数obj由此Field对象表示的字段设置为指定的新值value 如果是基本数据类型使用setXxx(Object obj,Xxx value)设置属性值 public void setAccessible(boolean flag);启动或禁用访问安全检查的开关 true,反射对象在使用时,取消Java语言的访问检查 1、能使原本无法访问的私有成员也可以访问2、能提高反射的效率,代码需要频繁的被调用建议使用 false,反射对象在使用时,进行Java语言的访问检查(默认就是进行检查) Class<?> clazz = Class.forName("com.bdit.Student"); Object obj1 = clazz.newInstance(); System.out.println(obj1); //获取自定义类中的私有属性 Field field = clazz.getDeclaredField("name"); System.out.println(field); //不进行安全检查 field.setAccessible(true); //将对象的name属性设置值为 张三 field.set(obj1,"张三"); System.out.println(obj1);

    调用方法

    通过Class对象的getMethod()等方法获取全部Method或者指定Method后,就可以使用Method中的方法调用

    public Object invoke(Object obj,Object… args);在具有指定参数args的指定对象obj上调用此对象表示的方法。 Class<?> clazz = Class.forName("com.bdit.Student"); Object obj1 = clazz.newInstance(); System.out.println(obj1); //获取Student类中的setName方法 Method method = clazz.getMethod("setName",String.class); //使用获取到的方法修改obj1中的name值 method.invoke(obj1,"李四"); System.out.println(obj1);

    操作数组

    在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array类来动态的创建数组,操作数组元素等。

    Array类提供的方法:

    public static Object newInstance(Class<?> componentType, int… dimensions):创建一个具有指定的组件类型和维度的新数组public static void setXxx(Object array,int index,xxx value):将指定array数组中[index]元素的值修改为value。此处的Xxx对应8种基本数据类型,如果该属性的类型是引用数据类型,则直接使用set(Object array,int index, Object value)方法public static xxx getXxx(Object array,int index):将指定array数组中[index]元素的值返回。此处的Xxx对应8种基本数据类型,如果该属性的类型是引用数据类型,则直接使用get(Object array,int index)方法 Object arr = Array.newInstance(String.class,5); Array.set(arr,0,"java"); Array.set(arr,1,"array"); System.out.println(Array.get(arr,0)); System.out.println(Array.get(arr,1));
    Processed: 0.022, SQL: 9