您的位置:首页 > 其它

反射

2015-08-27 09:23 253 查看
一、Class类与反射

1.反射:反射就是把Java类中的各种成分映射成相应的Java类。

例如,一个Java类中用一个Class的对象来表示。一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示。表示Java类的Class类显然要提供一系列的方法来获得其中的变量、方法、构造方法、修饰符、包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等

注意:反射会导致程序性能严重下降。

2.反射的基础-Class类:

Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。Java程序中的各个Java类也属于同一种事物,也可以用一个类描述这类事物,这个类的名字就是Class。Class类描述了以下方面的信息:类的名字、类的访问属性、类所属于的包名、字段名称的列表、方法名称的列表等。

Class类的对象对应各个类在内存中的字节码,就是编译之后的那份类所对应的字节码。一个类在被类加载器加载到内存中,占用一片存储空间,这个空间里的内容就是类的字节码,不同类的字节码是不同的,所以它们在内存中的内容是不同的。这一个个的空间可分别用一个个的对象来表示,这些对象具有相同的类型就是Class。

注意:字节码用“==”比较,因为是同一份字节码!

如何得到各个字节码对应的实例对象:

(1)类名.class;

(2)对象.getClass();

(3)Class.forName("类名"),返回字节码,曾经被类加载器加载到java虚拟机中就直接返回,否则就先用类加载器加载到虚拟机中再返回。

九个预定义Class实例对象: 八种基本类型(boolean、byte、char、short、int、long、float 和double )和void 。基本类型的字节码获取方式只有一种就是类名.class。例如int.class,void.class。

数组也是一种Class的实例对象。

总之,只要在源程序出现的类型,都有各自的Class实例对象,例如,int[],void。

二、反射的应用

1.构造方法的反射

Constructor类代表某个类中的一个构造方法。每个Constructor对象代表一个构造方法,利用Constructor对象可以操纵相应的构造方法。

在通过下面一组方法访问构造方法时,将返回Constructor类型的对象或数组。

getConstructors():得到某个类的所有构造方法,返回Constructor类型的数组;

getConstructor(Class<?>...parameterTypes):得到某个类的指定构造方法,返回Constructor类型的对象。

(1)得到某个类的构造方法,获得方法时要用到参数的类型 (StringBuffer.class)。

Constructor constructor=字节码.getConstructor(参数的类型的字节码):

Constructor constructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);

(2)用newInstance创建实例对象,调用获得的方法时要用到上面相同类型的实例对象。

String str=(String)constructor.newInstance(new StringBuffer("abc"));

(3)Class.newInstance()方法: 该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。

例:String obj=(String)Class.forName("java.lang.String").newInstance();

2.成员变量的反射

Field类代表某个类中的一个成员变量。

(1)获取字段

Field field=对象名.getClass().getField("变量名");

(2)field不代表具体的值,只代表类字节码上的一个变量,并没有对应到对象身上要用它去取某个对象上对应的值要调用field的get(Object obj)方法:field.get(Object obj)。

3.成员方法的反射

Method类代表某个类中的一个成员方法

(1)得到某个类中的一个成员方法,第一个参数为方法名,第二个参数为方法的参数类型

Method methodCharAt=Class.forName("java.lang.String").getMethod("charAt",int.class);

(2)调用方法,要调用invoke方法,第一个参数为要操作的对象,第二个参数为方法的参数

System.out.println(methodCharAt.invoke(str,1));

注意:静态方法调用时不需要对象,invoke中的第一个参数应该为null。

4.暴力反射

能够获取类中所有被声明的过的内容,包括私有内容。

getDeclaredConstructor(Class<?>...parameterTypes):构造方法

getDeclaredField(String name):成员变量

getDeclaredMethod(String name,Class<?>...parameterTypes):成员方法

之后在调用setAccessible(true)即可。

三、反射的应用

package com.itheima.reflect;

import java.lang.reflect.*;

public class ReflectTest {

public static void main(String[] args) throws Exception {
String str1="abc";
//用三种方式得到Class类,即获取字节码
Class cls1=str1.getClass();//对象名.getClass()方法
Class cls2=String.class;//类名.class方式
Class cls3=Class.forName("java.lang.String");//Class类的forName(String clssName)静态方法
//判断是否为同一份字节码,由于都是String类的字节码,所以返回的结果都为true
System.out.println(cls1==cls2);
System.out.println(cls1==cls3);

//判断是否为基本类型的字节码对象,由于String类型不是基本类型,所以返回结果为false
System.out.println(cls1.isPrimitive());
//返回结果为true,可以推断出void在Java中也被认为是一种基本数据类型
System.out.println(void.class.isPrimitive());
//int是基本数据类型,Integer是包装类,不是同一种类型,所以结果为false
System.out.println(int.class==Integer.class);
//包装类名.TYPE包装了它所对应的基本类型的字节码,因此为true
System.out.println(int.class==Integer.TYPE);
//判断是否是数组类型,结果为true,但其不是基本数据类型
System.out.println(int[].class.isArray());
System.out.println(int[].class.isPrimitive());

//构造方法的反射
//得到参数类型为StringBuffer的String的构造方法
Constructor constructor1=String.class.getConstructor(StringBuffer.class);
//因为构造方法的参数类型为StringBuffer,所以当创建String对象时应往里面传递StringBuffer对象
String str=(String)constructor1.newInstance(new StringBuffer("abc"));
//打印结果为“c”
System.out.println(str.charAt(2));

ReflectPoint pt1=new ReflectPoint(3,5);
//成员变量的反射
//获取字段,fieldY不代表具体的值,只代表类字节码上的一个变量,并没有对应到对象身上
Field fieldY=pt1.getClass().getField("y");
//fieldY不是对象身上的变量,而是类上的,要用它去取某个对象上对应的值要调用fieldY的get(Object obj)方法
System.out.println(fieldY.get(pt1));

//暴力反射,只要在类中被声明过都可以被获取,可以用来获取私有成员变量
//因为ReflectPoint中的x是private修饰的,无法用getField("x")获取,这时需要用getDeclaredField("x")
Field fieldX=pt1.getClass().getDeclaredField("x");
//但是由于是私有的,只允许自己访问,所以还要设置它的访问权限
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));

//将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”
changeStringValue(pt1);
System.out.println(pt1);

//成员方法的反射
//得到某个类中的一个成员方法,第一个参数为方法名,第二个参数为方法的参数类型
Method methodCharAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
//调用方法,输出结果为c
System.out.println(methodCharAt.invoke(str1,1));
}

//将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”
private static void changeStringValue(Object obj)throws Exception {
Field[] fields=obj.getClass().getDeclaredFields();
for(Field field:fields){
//先判断变量的类型是不是String字节码,此处用等号,因为是同一份字节码
if(field.getType()==String.class){
String oldValue=(String)field.get(obj);
String newValue=oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}

}

}

public class ReflectPoint {
private int x;
public int y;
public String str1 = "ball";
public String str2 = "ballball";
public String str3 = "itcase";

public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString(){
return str1+" "+str2+" "+str3;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: