黑马程序员_反射

来源:互联网 发布:日语网络流行语2017 编辑:程序博客网 时间:2024/05/06 18:32
------- android培训、java培训、期待与您交流! ----------

反射

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 类型(booleanbytecharshortintlongfloatdouble)和关键字 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方法覆盖@Overridepublic 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());  falseSystem.out.println(a1.getClass().getName());   //-->[I//获取父类名  发现父类的名字都是ObjectSystem.out.println(a1.getClass().getSuperclass().getName()); //二位数组;  发现父类的名字都是ObjectSystem.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[]就是ObjectObject[] 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;">//数组也是Objectprivate 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用于完成对数组的反射操作;

|--数组的反射:给的如果是数组,就把数组拆下来,拆成每一个,一个个的打印,如果不是数组类型的,那么就一起打印;

|--反射的作用主要是用来实现框架。

0 0
原创粉丝点击