Java反射
反射技术
-
其实就是动态加载一个指定的类,并获取该类中的所有的内容。
-
而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,它允许程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。
-
简单说:反射技术可以对一个类进行解剖。
反射的好处:大大的增强了程序的扩展性。
反射的基本步骤:
-
获得Class对象,就是获取到指定的名称的字节码文件对象。
-
实例化对象,获得类的属性、方法或构造函数。
-
访问属性、调用方法、调用构造函数创建对象。
类类型 class Class
用于描述程序中的各个类属于同一类事物的Java类,它封装了类的很多信息。
查看JDK中的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public final class Class<T> implements java.io.Serializable, java.lang.reflect.GenericDeclaration, java.lang.reflect.Type, java.lang.reflect.AnnotatedElement { private static final int ANNOTATION= 0x00002000 ; private static final int ENUM = 0x00004000 ; private static final int SYNTHETIC = 0x00001000 ; private static native void registerNatives(); static { registerNatives(); } /* * Constructor. Only the Java Virtual Machine creates Class * objects. */ private Class() {} |
发现:Class类有构造器,并且它的构造方法是private的(可能是为了禁止开发者去自己创建Class类的实例)。
看到注释我们知道,这个类是JVM来创建的。如果我们拿到一个类的类型信息,就可以利用反射获取其各种成员以及方法了。
那么我们怎么拿到一个类的信息呢?【获取Class对象可以有3种方式】
如果没有对象实例的时候,主要有两种办法可以获取类类型:
1 2 | Class cls1 = Show. class ; //每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。 Class cls2 = Class.forName( "Show" ); //【推荐这种方法】这种方式的扩展性最强,只要将类名的字符串传入即可。如果类是在某个包中,要带上包名。 |
1 2 3 4 5 6 7 8 9 | public static void main(String[] args) throws Exception { Class<Show> cls1 = Show. class ; Object obj1 = cls1.newInstance(); System.out.println(obj1); Class<?> cls2 = Class.forName( "Show" ); Object obj2 = cls2.newInstance(); System.out.println(obj2); } |
这样就创建了一个对象,缺点是:
第一,我们只能利用默认构造函数,因为Class的newInstance是不接受参数的;
第二,如果类的构造函数是private的,比如Class,我们仍旧不能实例化其对象。
如果有对象实例的话,除了上面的两种方法来获取类的信息外,还有第三种方法:对象.getClass()。弊端:必须要创建该类对象,才可以调用getClass方法。
反射就是把Java类中的各种成分映射成相应的Java类:
例如,一个Java类用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示【就像汽车是一个类,汽车中的发动机,变速箱也是一个个的类】。表示Java类的Class类中提供了一系列的方法来获取其中的变量(Field),方法(Method),构造方法(Contructor),修饰符(Modifiers),包(Package)等信息。
反射的用法
1.需要获得java类的各个组成部分,首先需要获得类的Class对象,获得Class对象的三种方式:
- Class.forName(classname) 用于做类加载
- obj.getClass() 用于获得对象的类型
- 类名.class 用于获得指定的类型,传参用
2.反射类的成员方法:
- public Method getMethod(String name,Class<?>... parameterTypes)//返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
- public Method[] getMethods()//返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口的公共 member 方法。
3.反射类的构造函数:
- public Constructor<?>[] getConstructors() 返回类中所有的public构造器集合,默认构造器的下标为0
- public Constructor<T> getConstructor(Class<?>... parameterTypes) 返回指定public构造器,参数为构造器参数类型集合
- public Constructor<?>[] getDeclaredConstructors() 返回类中所有的构造器,包括私有
- public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回任意指定的构造器
发现带上Declared的都是获得所有的构造方法,包括私有,这下我们就可以调用原本不允许调用的私有构造器了^_^
1 2 3 4 5 6 7 8 9 10 11 12 13 | eg: Class cls1 = Show. class ; // 获取所有的构造方法集合 Constructor[] con1 = cls1.getDeclaredConstructors(); con1[ 1 ].setAccessible( true ); // 设置可访问的权限 Object obj1 = con1[ 1 ].newInstance( new Object[] { "adanac" }); System.out.println(obj1); // 指定参数列表获取特定的方法 Constructor con = cls1.getDeclaredConstructor( new Class[] { String. class }); con.setAccessible( true ); Object obj2 = con.newInstance( new Object[] { "lfz" }); System.out.println(obj2); |
4.获取类的成员变量:
- public Field getDeclaredField(String name) 获取任意指定名字的成员
- public Field[] getDeclaredFields() 获取所有的成员变量
- public Field getField(String name) 获取任意public成员变量
- public Field[] getFields() 获取所有的public成员变量
1 2 3 4 5 6 7 8 9 10 | Class cls1 = Show. class ; // 获取所有的构造方法集合 Constructor[] con1 = cls1.getDeclaredConstructors(); con1[ 1 ].setAccessible( true ); // 设置可访问的权限 Object obj1 = con1[ 1 ].newInstance( new Object[] { "adanac" }); Field nameField = cls1.getDeclaredField( "name" ); nameField.setAccessible( true ); System.out.println(nameField.get(obj1)); // 打印结果:adanac |
现在私有变量也可以访问到了哈~~
获取了字节码文件对象后,最终都需要创建指定类的对象,创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):
- 调用空参数的构造函数:使用了Class类中的newInstance()方法。
- 调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过该构造函数的对象的newInstance(实际参数) 进行对象的初始化。
注意:第二种方式必须要先明确具体的构造函数的参数类型,不便于扩展。所以一般情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数。
Object obj = clazz.newInstance();//该实例化对象的方法调用就是指定类中的空参数构造函数,给创建对象进行初始化。
当指定类中没有空参数构造函数时,该如何创建该类对象呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Class<?> clazz = Class.forName( "heimablog.Person" ); // 既然类中没有空参数的构造函数,那么只有获取指定参数的构造函数,用该函数来进行实例化。 // 获取一个带参数的构造器。 Constructor<?> constructor = clazz.getConstructor(String. class , int . class ); // 想要对对象进行初始化,使用构造器的方法newInstance(); Object obj = constructor.newInstance( "zhagnsan" , 30 ); // 获取所有构造器。 Constructor[] constructors = clazz.getConstructors(); // 只包含公共的 constructors = clazz.getDeclaredConstructors(); // 包含私有的 for (Constructor<?> con : constructors) { System.out.println(con); } |
【案例】
通过class取得一个类的全部框架:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | package heimablog; import java.lang.reflect.Field; import java.lang.reflect.Modifier; class hello { public static void main(String[] args) { Class<?> demo = null ; try { demo = Class.forName( "heimablog.Person" ); } catch (Exception e) { e.printStackTrace(); } System.out.println( "===============本类属性========================" ); // 取得本类的全部属性 Field[] field = demo.getDeclaredFields(); for ( int i = 0 ; i < field.length; i++) { // 权限修饰符 int mo = field[i].getModifiers(); //返回此类或接口以整数编码的 Java 语言修饰符。 String priv = Modifier.toString(mo); // 属性类型 Class<?> type = field[i].getType(); System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";" ); } System.out.println( "===============实现的接口或者父类的属性========================" ); // 取得实现的接口或者父类的属性 Field[] filed1 = demo.getFields(); for ( int j = 0 ; j < filed1.length; j++) { // 权限修饰符 int mo = filed1[j].getModifiers(); String priv = Modifier.toString(mo); // 属性类型 Class<?> type = filed1[j].getType(); System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";" ); } } } |
通过反射操作属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package heimablog; import java.lang.reflect.Field; class hello { public static void main(String[] args) throws Exception { Class<?> cls1 = Class.forName( "heimablog.Person" ); Object object = cls1.newInstance(); Field field = cls1.getDeclaredField( "name" ); field.setAccessible( true ); field.set(object, "男" ); System.out.println(field.get(object)); //打印结果:男 } } |
通过反射取得并修改数组的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package heimablog; import java.lang.reflect.Array; class hello { public static void main(String[] args) throws Exception { int [] temp={ 1 , 2 , 3 , 4 , 5 }; Class<?>demo=temp.getClass().getComponentType(); System.out.println( "数组类型: " +demo.getName()); System.out.println( "数组长度 " +Array.getLength(temp)); System.out.println( "数组的第一个元素: " +Array.get(temp, 0 )); Array.set(temp, 0 , 100 ); System.out.println( "修改之后数组第一个元素为: " +Array.get(temp, 0 )); } } /** * 数组类型: int *数组长度 5 *数组的第一个元素: 1 *修改之后数组第一个元素为: 100 */ |
通过反射修改数组大小:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package heimablog; import java.lang.reflect.Array; class hello{ public static void main(String[] args) { int [] temp={ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 }; int [] newTemp=( int [])arrayInc(temp, 12 ); print(newTemp); System.out.println( "^^^^^" ); String[] atr={ "a" , "b" , "c" }; String[] str1=(String[])arrayInc(atr, 5 ); print(str1); } /** * 修改数组大小 * */ public static Object arrayInc(Object obj, int len){ Class<?>arr=obj.getClass().getComponentType(); Object newArr=Array.newInstance(arr, len); int co=Array.getLength(obj); System.arraycopy(obj, 0 , newArr, 0 , co); return newArr; } /** * 打印 * */ public static void print(Object obj){ Class<?>c=obj.getClass(); if (!c.isArray()){ return ; } System.out.println( "数组长度为: " +Array.getLength(obj)); for ( int i = 0 ; i < Array.getLength(obj); i++) { System.out.print(Array.get(obj, i)+ " " ); } } } /** *数组长度为: 10 *1 2 3 4 5 6 7 8 9 0 ^^^^^ *数组长度为: 5 *a b c null null */ |
获得类加载器:
1 2 3 4 5 6 7 8 9 10 11 12 | package heimablog; class test{ } class hello{ public static void main(String[] args) { test t= new test(); System.out.println( "类加载器 " +t.getClass().getClassLoader().getClass().getName()); } } //类加载器 sun.misc.Launcher$AppClassLoader |
反射小例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | package heimablog; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.sql.Timestamp; import java.util.Calendar; public class ClassTest { public static void main(String[] args) { Calendar birthday = Calendar.getInstance(); birthday.set( 1966 , 9 , 11 , 0 , 0 , 0 ); Student s1 = new Student( "007" , "Jack Brawer" , true , new Timestamp( birthday.getTimeInMillis()), 1.80 ); ClassInfo classInfo = new ClassInfo(); System.out.println(classInfo.getClassInfo(s1)); } } // 该类可以获取任何类的元数据信息 class ClassInfo { public String getClassInfo(Object obj) { StringBuffer result = new StringBuffer(); // 得到参数类变量的Class引用变量 Class cls = obj.getClass(); // 得到参数类变量的属性信息 Field[] fields = cls.getDeclaredFields(); // 得到参数类变量的完整类名(含有包的名称) String fullName = cls.getName(); // 得到去除包名称的类名 String className = fullName.substring(fullName.lastIndexOf( '.' ) + 1 ); // 如果有包的定义,可以得到包的名称 int packagePosition = fullName.lastIndexOf( '.' ); String packageName = null ; if (packagePosition < 0 ) packageName = "" ; else packageName = fullName.substring( 0 , fullName.lastIndexOf( '.' )); // 输出包名和类名 result.append( "包的名称为:" + packageName + "\r\n" ); result.append( "类的名称为:" + className + "\r\n" ); // 输出类中所有的属性信息 for (Field field : fields) { // 允许访问私有成员 field.setAccessible( true ); try { // 输出私有属性信息 if (field.getModifiers() == Modifier.PRIVATE) result.append( "私有属性" + field.getName() + ":值为" + field.get(obj) + "\n" ); // 输出受保护属性信息 if (field.getModifiers() == Modifier.PROTECTED) result.append( "受保护属性" + field.getName() + ":值为" + field.get(obj) + "\n" ); } catch (Exception e) { System.out.println(e.getMessage()); } } return result.toString(); } } // 学生类 class Student { // 学号 private String number = null ; // 姓名 private String name = null ; // 性别 private boolean sex = false ; // 生日 private Timestamp birthday = null ; // 身高 private double height = 0 ; // 默认构造函数 public Student() { } // 该构造函数可以对学生的属性进行初始化 public Student(String number, String name, boolean sex, Timestamp birthday, double height) { setNumber(number); setName(name); setSex(sex); setBirthday(birthday); setHeight(height); } // 学号的读取函数 public String getNumber() { return number; } // 学号的设置函数 public void setNumber(String number) { this .number = number; } // 姓名的读取函数 public String getName() { return name; } // 姓名的设置函数 public void setName(String name) { this .name = name; } // 性别的读取函数 public boolean isSex() { return sex; } // 性别的设置函数 public void setSex( boolean sex) { this .sex = sex; } // 生日的读取函数 public Timestamp getBirthday() { return birthday; } // 生日的设置函数 public void setBirthday(Timestamp birthday) { this .birthday = birthday; } // 身高的读取函数 public double getHeight() { return height; } // 身高的设置函数 public void setHeight( double height) { this .height = height; } // 格式化字符信息输出 public String toString() { return "学号:" + number + "/n姓名:" + name + "/n是否为男生:" + sex + "/n生日:" + birthday + "/n身高:" + height; } } /* 包的名称为:heimablog 类的名称为:Student 私有属性number:值为007 私有属性name:值为Jack Brawer 私有属性sex:值为true 私有属性birthday:值为1966-10-11 00:00:00.267 私有属性height:值为1.8 */ |
在java中有三种类类加载器:
-
Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
-
Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类
-
AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步