反射
Class类
1、概念
Class:这个是一个类的名字叫Class,代表的是一类事物。java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class;java类的作用:java类是描述一类事物的共性,该类有什么属性,没有什么属性,至于这个类是什么,类是不管的,它只管你应该还有什么东西,不应该有什么东西,但是定义一个具体的值是什么,它是不管的;举例:|--众多的人可以用人来表示;人:姓名、年龄、身高;|--众多的java类可以用Class来表示;java类:类的名字,这个类属于哪个包,这个类里面有哪些成员变量,方法、父类是什么?因此通过这个Class可以得到这个类身上的方方面面的信息;
2、Class一些基本的方法:
|--String getName() 以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。|--Package getPackage() 得到自己所属的包|--Method[] getMethods() 得到自己所有的方法列表|--Class<?>[] getInterfaces() 得到自己所有的多个实现接口3、Class类型的变量赋值
|--Class类是没有构造方法的,因此不能直接new对象的;|--Class cls1 = 字节码1;|--Class cls2 = 字节码2;|--字节码:当用到Person类的时候,首先要从硬盘上把这个类的而进制代码(编译成class放在硬盘上以后,就是二进制代码),把这些二进制代码加载到内存里面,在用这个字节码复制出一个个对象来;|--当java的程序里面用到了,Date,Math和Person这三个类,那么内存里面就有三份字节码,每一个字节码都是Class的实例对象;|--Date.class-->表示Date在内存中的那份字节码;Class cls1 = Date.class;|--Person.class-->表示Person在内存中的那份字节码|--获取类的字节码方式:
|--p1.getClass();-->类的字节码已经加载到了内存里面了,想要得到它的字节码,就不要加载了,直接至啊到那个字节码,返回就可以了;
|--Class.forName("java.lang.String");-->这种是在虚拟机里面没有这份字节码,于是就用类加载器去加载,加载进来以后,就把那份字节码给缓存起来,这个方法返回刚才加载进来的那份字节码;4、获取各个字节码对应的实例对象
|--类名.class-->System.class,这个是在写程序的时候就已经把System给写上了;
|--对象.getClass()-->new Data().getClass();
|--Class.forName("类名")-->Class.forName("java.util.Date");这个是反射的时候用的最多的一个,因为在写程序的时候还不知道类的名字,是在运行的时候,别人传给我一个字符串,而这个字符串里面包含一个类的名字;就是在写源程序的时候,可以把"java.util.Date"换成一个变量,就是字符串类型的变量,等到程序运行起来以后,这个变量的值从一个配置文件里面加载进来;就是类的名字在写源程序的时候,不需要知道,而是等到运行的时候,临时送进来;
|--基本的 Java 类型(
boolean、byte、char、short、int、long、float和double)和关键字void也表示为Class对象。
|--以上是9个预定义的Class实例对象;
5、代码举例
<span style="font-family:Microsoft YaHei;">class ReflectTest { public static void main(String[] args) throws Exception { String str1 ="abc"; // 得到String类型的字节码 Class cls1 = str1.getClass(); Class cls2 = String.class; Class cls3 = Class.forName("java.lang.String"); System.out.println(cls1 == cls2); System.out.println(cls1 == cls3); // 看cls1的字节码是否是原始类型,String类型不是基本类型的字节码 System.out.println(cls1.isPrimitive()); // int的字节码是否是基本类型的字节码 System.out.println(int.class.isPrimitive()); // 看看int类型的字节码和Integer类型的字节码是否相同 System.out.println(int.class == Integer.class); // Integer.TYPE代表的是包装类型所包装的那个基本类型的字节码 System.out.println(int.class == Integer.TYPE); // 数组类型的int是不是元素类型,是一个数组 System.out.println(int[].class.isPrimitive()); // 判断是不是数组 System.out.println(int[].class.isArray()); } }</span>6、运行结果
反射
1、概念
反射就是把Java类中的各种成分映射成相应的java类;例如一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等其他信息也用一个个Java类来表示的
2、Class类中的一些常用的方法
|--Package getPackage() 获取方法,返回来的方法又是用一个方法来表示的
|--Method getMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
|--Field getField(String name) 返回的成员变量又是用一个类来表示
Class:代表一份字节码
|--Field:代表的是字节码里面的字段
|--Method:代表的是字节码里面的方法
|--Constructor:代表字节码里面的一个构造方法
|--Package:代表的是字节码里面的包
Constructor类
1、概念
|--Constructor类代表的某个类中的一个构造方法|--Constructor:代表字节码里面的一个构造方法
2、常用方法
|-- Class<T> getDeclaringClass() 得到自己所属的类|-- int getModifiers() 得到前面的修饰符|-- T newInstance(Object... initargs) 获取实例对象|--要想得到某个类所有的构造方法
|--Constructor[] constructor = Class.forName("java.lang.String").getConstructors();|--得到某一个构造方法
|--Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
|--调用方法时要用到类型;
|--创建实例对象:
|--通常方式:new String(new StringBuffer("abc"));
|--反射方式:String str1 = (String)constructor1.newInstance(new StringBuffer("abc"));
|--调用获得的方法时要用到上面相同类型的实例对象;
|--Class.newInstance()方法:
|--创建一个类的实例对象步骤:|--T newInstance() 这个是Class里面的
|--T newInstance(Object... initargs) 这个是Constructor里面的
|--Class 是把中间的环节给省略掉了 class-->new object
|--class-->constructor-->new object 这个是 Constructor 里面的
3、代码体现
<span style="font-family:Microsoft YaHei;font-size:14px;">import java.lang.reflect.Constructor; class ReflectTest { public static void main(String[] args) throws Exception { String str1 ="abc"; // 得到String类型的字节码 Class cls1 = str1.getClass(); Class cls2 = String.class; Class cls3 = Class.forName("java.lang.String"); System.out.println(cls1 == cls2); System.out.println(cls1 == cls3); // 看cls1的字节码是否是原始类型,String类型不是基本类型的字节码 System.out.println(cls1.isPrimitive()); // int的字节码是否是基本类型的字节码 System.out.println(int.class.isPrimitive()); // 看看int类型的字节码和Integer类型的字节码是否相同 System.out.println(int.class == Integer.class); // Integer.TYPE代表的是包装类型所包装的那个基本类型的字节码 System.out.println(int.class == Integer.TYPE); // 数组类型的int是不是元素类型,是一个数组 System.out.println(int[].class.isPrimitive()); // 判断是不是数组 System.out.println(int[].class.isArray()); //要想得到一个构造方法 //String(StringBuffer buffer) //Constructor 对象这个构造方法; //要识别那个构造方法通过参数来识别的,接收的参数类型是StringBuffer //传了两个参数,是StringBuffer的构造方法;JDK1.5的可变参数;class对应一个类型 //Constructor<T> getConstructor(Class<?>... parameterTypes) Constructor constructor1 = String.class.getConstructor(StringBuffer.class);//StringBuffer表示选择哪个构造方法 // T newInstance(Object... initargs) 获取实例对象,newInstance可以调用多次,每调用一次用new了一个对象 String str2 = (String)constructor1.newInstance(new StringBuffer("abc"));//要用一个强制类型转换;StringBuffer用这个构造方法的时候要传一个StringBuffer的对象进去; System.out.println(str2.charAt(2)); } } //new String(new StringBuffer("abc")); // Class<T> getDeclaringClass() 得到自己所属的类 // int getModifiers() 得到前面的修饰符 // T newInstance(Object... initargs) 获取实例对象 // //Package getPackage() 获取方法,返回来的方法又是用一个方法来表示的 //Method getMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 //Field getField(String name) 返回的成员变量又是用一个类来表示 //java类身上的每一个产成分,解析成一个相应的类 //Field //Method //Constructor //Package //System里面有个exit和getProperties() //两个方法 //不管你是什么方法,这些类型都可以用Method方法来表示 //而每一个不同的方法,对应的是methodobj1,和methodobj2 //但是这两个对象属于同一个类型,就是Method,Method代表这个类型,而methodobj1代表的是一个具体的方法;</span>
Field类
1、概念
|--Field:代表的是字节码里面的字段,就是成员变量|--Field代表的是某个字节码身上的变量,不代表某个对象身上的变量2、代码体现
<span style="font-family:Microsoft YaHei;font-size:14px;">package com.itheima; import java.lang.reflect.Constructor; import java.lang.reflect.Field; public class ReflectTest { public static void main(String[] args)throws Exception { String str1 ="abc"; // 得到String类型的字节码 Class cls1 = str1.getClass(); Class cls2 = String.class; Class cls3 = Class.forName("java.lang.String"); System.out.println(cls1 == cls2); System.out.println(cls1 == cls3); // 看cls1的字节码是否是原始类型,String类型不是基本类型的字节码 System.out.println(cls1.isPrimitive()); // int的字节码是否是基本类型的字节码 System.out.println(int.class.isPrimitive()); // 看看int类型的字节码和Integer类型的字节码是否相同 System.out.println(int.class == Integer.class); // Integer.TYPE代表的是包装类型所包装的那个基本类型的字节码 System.out.println(int.class == Integer.TYPE); // 数组类型的int是不是元素类型,是一个数组 System.out.println(int[].class.isPrimitive()); // 判断是不是数组 System.out.println(int[].class.isArray()); //要想得到一个构造方法 //String(StringBuffer buffer) //Constructor 对象这个构造方法; //要识别那个构造方法通过参数来识别的,接收的参数类型是StringBuffer //传了两个参数,是StringBuffer的构造方法;JDK1.5的可变参数;class对应一个类型 //Constructor<T> getConstructor(Class<?>... parameterTypes) Constructor constructor1 = String.class.getConstructor(StringBuffer.class);//StringBuffer表示选择哪个构造方法 // T newInstance(Object... initargs) 获取实例对象,newInstance可以调用多次,每调用一次用new了一个对象 String str2 = (String)constructor1.newInstance(new StringBuffer("abc"));//要用一个强制类型转换;StringBuffer用这个构造方法的时候要传一个StringBuffer的对象进去; System.out.println(str2.charAt(2)); ReflectPoint pt1 = new ReflectPoint(3,5); // 要得到某个类身上的字段,也就是成员变量,要先得到类的字节码,类的字节码里面有成员变量的信息 // 要得到字节码,在然后得到某个字段,要想得到那个字段,就要用变量的名字来区别,变量名就是字符串 Field fieldX = pt1.getClass().getDeclaredField("x");//getDeclaredField可以获取私有的,只要是声明过的都可以获取; fieldX.setAccessible(true);//设置为可以访问,暴力反射; // fieldY 的值是多少?是5 错! fieldY只是代表类字节码身上的变量,没有对应到对象,也就是说fieldY不是代表一个具体的值,而是代表一个变量 // 要想取出某个变量在某个对象身上所对应的值 // fieldY.get(pt1);//在哪个对象身上去取这个y的值;代表的是类身上的东西 Field fieldY= pt1.getClass().getField("y"); System.out.println("y="+fieldY.get(pt1)); System.out.println("x="+fieldX.get(pt1)); } }</span><span style="font-size:18px;"> </span>3、运行结果
4、Field练习
需求:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中能够的"b"改成"a".
就是说把String类型中的b改成a;
该一个对象,可以讲其全部修改了,比如在配置文件里面配置了好多东西,自动扫描配置文件,把里面有的东西有的换掉,一个模拟程序,换掉一个对象里面的字段;
5、字段练习
<span style="font-family:Microsoft YaHei;font-size:14px;">class ReflectPoint { public static void main(String[] args) { private int x; private int y; } } package com.itheima; //ReflectPoint上的String类型所有的b都改成a; public class ReflectPoint { private int x; public int y; public String str1 ="ball"; public String str2 ="basketball"; public String str3 ="itcast"; public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } //为了打印效果好,将toString方法覆盖 @Override public String toString() { return "ReflectPoint [str1=" + str1 + ", str2=" + str2 + ", str3=" + str3 + "]"; } } private static void changeStringVlaue(Object obj) throws Exception{ // 扫描这个ReflectPoint对象上所有的String类型的变量 // 获取对象的字节码,在获取字段 Field[] fields = obj.getClass().getFields(); // 对字段进行迭代 for(Field field : fields){ // 得到字段自己的类型field.getType(),如果field的类型是String类型的,两个都是String类型的,两个不好比 // if(field.getType().equals(String.class)){ // 这里应该用的是==,因为是同一份字节码,只要是字节码用等号比较; if(field.getType() == String.class){ // 在obj对象上拿到一个值,用一个强转, String oldValue = (String)field.get(obj); String newValue = oldValue.replace('b', 'a'); // 要字段身上的值进行设置值;对象身上的值被修改了 field.set(obj, newValue); } } } public class ReflectTest { public static void main(String[] args)throws Exception { changeStringVlaue(pt1); System.out.println(pt1); } }</span><span style="font-size:18px;"> </span>
Method类
1、概念
|--Method:代表某一类中的一个成员方法;|--Method:代表的是字节码里面的方法,不是对象上面的方法,然后拿着这个方法去调用对象;2、代码体现
<span style="font-family:Microsoft YaHei;font-size:14px;">// str1.charAt(1); // 用反射的方式得到字节码里面的方法 Method methodCharAt = String.class.getMethod("charAt", int.class); // 拿着这个方法去作用于某个对象;invoke:调用; System.out.println(methodCharAt.invoke(str1, 1));</span>
面向对象:调用方法,是方法去调用一下,所以是methodCharAt方法去调用一下;谁拥有这个数据,谁就能操作这个数据;
<span style="font-family:Microsoft YaHei;font-size:14px;">// str1.charAt(1); // 用反射的方式得到字节码里面的方法 Method methodCharAt = String.class.getMethod("charAt", int.class); // 拿着这个方法去作用于某个对象;invoke:调用; System.out.println(methodCharAt.invoke(str1, 1)); // 按照1.4的语法来调用的 System.out.println(methodCharAt.invoke(str1, new Object[]{2}));//2是一个数值,该数组的长度是1,数值是2,也就是角标为2的对应值 // new Object(new String("abc"),8); // 这个是正常一般的方法; // TestArguments.main(new String[]{"111","222","333"}); String startingClassName = args[0]; Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class); // 报一个参数的个数不对;这个是三个参数,包成了一包,而java把这个包给拆开了,它认为是一个三个参数 // 现在用 new Object,给了包的东西,现在是一个数组,就是一个东西; // 把数组打包起来 mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}}); // 这个动作是让不要拆包,做了一个强制动作; mainMethod.invoke(null, (Object)new String[]{"111","222","333"}); // 是数组的话,就差包,不是数组的话,就不拆包</span>
数组的反射
<span style="font-family:Microsoft YaHei;font-size:14px;">int [] a1 = new int[]{1,2,3}; int [] a2 = new int[4]; int[] [] a3 = new int[2] [3]; String[] a4 = new String[]{"a","b","c"}; // a1和 System.out.println(a1.getClass() == a2.getClass()); // System.out.println(a1.getClass() == a4.getClass()); false // System.out.println(a1.getClass() == a3.getClass()); false System.out.println(a1.getClass().getName()); //-->[I // 获取父类名 发现父类的名字都是Object System.out.println(a1.getClass().getSuperclass().getName()); // 二位数组; 发现父类的名字都是Object System.out.println(a4.getClass().getSuperclass().getName()); // Object是一个数组 Object aObj1 = a1; Object aObj2 = a4; // int是不能转成Object,因为是int类型的和Integer不同,而一维是int[] 是Object,String类型的也是Object的 // Object[] aObj3 = a1;这个是编译失败 // 表示有一个数组,这个数组里面装的是Object,int类型的数组int[]就是Object Object[] aObj4 = a3; Object[] aObj5 = a4; System.out.println(a1); System.out.println(a4); // 想要打印元素; // 整数按照的是一个参数走的 System.out.println(Arrays.asList(a1));//整数的没有转过来 // 数组等效于多个元素; System.out.println(Arrays.asList(a4));//字符串的转过来了</span>
数组反射的应用
<span style="font-family:Microsoft YaHei;font-size:14px;">// 数组也是Object private static void printObject(Object obj) { Class clazz = obj.getClass(); // 判断是否是数组 if(clazz.isArray()){ // 是数组的话就要取出每一个 int len = Array.getLength(obj); for(int i =0;i<len;i++){ // 打印数组 System.out.println(Array.get(obj, i)); } }else{ System.out.println(obj); } }</span>
我的总结
|--每一份字节码文件都是Class的实例对象;|--有8个基本的数据类型,就对应了8个基本的实例对象|--数组类型的Class实例对象,用的是Class.isArray();|--总之是在源程序中出现的类型,都有各自的Class实例对象,例如int[],void类型;|--反射就是把java类中的各种成分映射成相应的java类;|--要的到各个成分所对应的对象,用这个对象做一些事情;
|--要创建一个类的实例对象步骤;|--反射比较耗时;因为要把功能缓冲起来;导致程序性能严重下降
|--反射构造方法:得到类身上的字节码构造方法,在用构造方法去new一个实例对象|--反射的字段的用途:可以写一个模拟程序,换掉一个对象里面的字段|--字节码里面的方法主要是:先拿到方法,再针对某个对象去调用方法;|--如果传递给Method对象的invoke()方法的第一个参数是null,意味着:该Method方法对象对一个的是有一个静态方法;|--对于main的方法反射如何去调用;|--如何不要当作多个参数:有两种方案;
|--把数组打包成为另外一个数组,拆了一个数组,剩下的就是另外一个数组;|--还有一种就是,你就把它当成object,不要拆包;|--Arrays.asList()方法处理int[]和String[]时的差异。
|--Arrays用于完成对数组的反射操作;
|--数组的反射:给的如果是数组,就把数组拆下来,拆成每一个,一个个的打印,如果不是数组类型的,那么就一起打印;
|--反射的作用主要是用来实现框架。
本文深入探讨Java中的反射机制,解释Class类的概念、方法及其在获取类信息、创建实例对象、访问成员变量和方法等方面的应用。通过代码实例展示了如何利用反射获取类的字节码、字段和方法,并提供了创建类实例、访问成员变量和使用方法的完整流程。



被折叠的 条评论
为什么被折叠?



