一、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;
}
}