JAVA反射
一、知识准备
现在我们首先来看三个问题。
1.什么是Class 类
类描述:描述JAVA类,众多的人可以用person类来表示,众多的JAVA类可以用一个类来描述,这个类就是Class。
2.怎么得到Class的对象。
如得到Person对象,可以这样做: Person p=new Person();
现在我们要知道一个概念:Class的实例对象就是内存中的一份字节码,或者说内存中的一份字节码对应Class的一个实例对象。现在解释一下什么是字节码?
我们常说JAVA编译后,会变成.class文件,而这里所说的字节码就是一个类编译而成的二进制代码,比如:
要得到Person对象时,是要先得到Person类的一份字节码(如果JVM中没有,需要先加载,如果有,可以直接返回);
要得到Set对象时,是要先得到Set类的一份字节码;
要得到Math对象时,是要先得到Math类的一份字节码。
注意:每个类的字节码,在内存中只有一份,每一份字节码就是一个Class的实例对象,比如要得到Person的字节码,可以有下面三种写法
A. Class p=Person.class; “Person.class”就代表Person的字节码,“Person.class”的所属类型的Class
B. Class.forName("类的全路径名");
C. 对象名.getClass();
例子:
public class ReflectTest1 {
public static void main(String[] args) throws ClassNotFoundException {
String str1="abc";
//第一种方式:用类名.class
Class class1=String.class;
//第二种方式:用String的对象获取String的字节码
Class class2=str1.getClass();
//第二种方式:用String的对象获取String的字节码
Class class3=Class.forName("java.lang.String");
System.out.println("结果如下");
System.out.println(class1==class2);
System.out.println(class1==class3);
}
}
这个类的结果输出是两个true,说明每个类的字节码,在内存中只有一份,无论你用三种方式的哪一种方式去取,得到的都是同一份字节码。</span>
3.基本类型的字节码
Java中有八种基本类型,所对应的字节码是基本数据类型的字节码,在程序中,可以种Class的isPrimitive()方法来判断它是不是基本数据类型的字节码。
值得一提的是,int[].class这不是基本数据类型的字节码,因为这是数组类型,当调用isArray的时候返回true。
二、反射深入分析
1.什么是反射?
定义:反射就是把JAVA类中的各种成分映射成映射成相应的JAVA类。
这句话怎么理解呢?
我们知道,一个类中可以有成员变量,成员方法,构造方法等信息,这些信息就用相应的类的实例对象来表示。
在反射中,有一些类用来表示反射以后类中的成分,比如:Filed,Method,Constructor,Package。
比如:System类中,有System.exit(),System.getProperties(),不管你的类中有什么方法,都可以用反射中Method来表示。
我们知道,一个类中可以有成员变量,成员方法,构造方法等信息,这些信息就用相应的类的实例对象来表示。
在反射中,有一些类用来表示反射以后类中的成分,比如:Filed,Method,Constructor,Package。
2.类构造函数的反射 Constructor
Constructor代表某一个类的构造方法
那么怎么得到一个类的构造方法?
可以先拿到这个类的CLass实例对象(这个类的字节码),实例对象中有两个方法。
得到类的一个构造函数的方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException
通过参数类型,得到想要的构造函数,因为接收的是可变参数,所以可以传多个,比如:想得到String的String(StringBuffer buf)的构造方法,可以这么写:StringString.class.getConstructor(StringBuffer.class);
例子:
public class ConstructorReflect {
public static void main(String[] args) throws Exception {
//通过字节码的方法得到Constructor对象
Constructor<String> constructor = String.class.getConstructor(StringBuffer.class);
String instance = constructor.newInstance(new StringBuffer("abc"));
System.out.println(instance);
}
}
注意在上面程序有两个地方用到StringBuffer,第一个StringBuffer指定得到哪个构造方法,第二个StringBuffer表示在用constructor实例化对象的时候
还要传递一个StringBuffer的对象,两个必须完全一致。
如果你这么写 String.class.newInstance();那就是得到这个类中不带任何参数的构造方法。
得到一个类的所有构造函数的方法
public Constructor<?>[] getConstructors() throws SecurityException
3.类成员变量的反射 Filed
怎么通过反射得到类中字段的值呢?
例子1:下面的程序可以通过反射得到公有成员y,和私有成员x。
class Point{
private int x;
public int y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
public class FiledReflect {public static void main(String[] args) throws Exception {Point p1=new Point(5, 39);Point p2=new Point(3, 9);</span><span style="font-size:14px;">//对公有成员变量Field fieldY = p1.getClass().getField("y");int y = (int) fieldY.get(p1);//取对应p1对象的y字段的值,必须要有对象。System.out.println(y);//对私有成员变量,可以进行暴力反射Field fieldX = p1.getClass().getDeclaredField("x");fieldX.setAccessible(true);//暴力反射int x = (int) fieldX.get(p1);//取对应p1对象的x字段的值System.out.println(x);}}
例子2:需求:对一个Person类中的成员变量,把所有String类型的变量值字母a改成A;
import java.lang.reflect.Field;
class Person{
private String name;
private int age ;
private String nickName;
public Person(String name, int age, String nickName) {
super();
this.name = name;
this.age = age;
this.nickName = nickName;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", nickName="+ nickName + "]";
}
}
public class FiledReflect2 {public static void main(String[] args) throws Exception {Person p=new Person("zhangsan", 25, "Amao");//得到字节码,通过字节码得到这个类的所有方法Field[] fields = p.getClass().getDeclaredFields();for(Field field:fields){//因为一个类的字节码在内存中只有一份,所以用==比较更专业,此处用==,不用equals()if(field.getType()==String.class){field.setAccessible(true);//进行暴力反射String oldValue = (String) field.get(p);//获得字段的值String newValue =oldValue.replace("a", "A");field.set(p, newValue);System.out.println(p);}}}}
执行结果是:Person [name=zhAngsAn, age=25, nickName=AmAo]
4.类成员方法的反射 Method
比如:想调用String类中的chatAt(int i)这个方法,该怎么么办呢?
public Method getMethod(String name, Class<?>... parameterTypes)hrows NoSuchMethodException,SecurityException参数说明:
name:这个表示方法的名字
parameterTypes:这个参数的作用表示调用哪个方法,因为重载的原因,一个类中同名的方法可能不止一个
对于上面的问题,我们可以这样做。
Method myStrCharAt=String.class.getMethod("charAt",int.class);
例子:
public class MethodReflect {
public static void main(String[] args) throws Exception{
String str="abcdef";
Method strMethod = str.getClass().getMethod("charAt", int.class);
//得到方法之后,调用对象str的chatAt方法;
char result = (char) strMethod.invoke(str, 1);
System.out.println(result);//结果是b
}
}
当一个类XXX,我们已经通过反射得到它的方法xxxMethod,那么看下面一行代码
xxxMethod.invoke(null, 1);这表示不知道谁的xxxMethod方法,因为传递的是null,说明调用的这个方法是属于类的,就是静态方法,所以就不需要传递参数了。
5.数组与反射
数组在 Java 语言中是一种特殊的类类型,一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的:
import java.lang.reflect.*;
public class Array1 {
public static void main(String args[]) {
try {
Class cls = Class.forName("java.lang.String");
Object arr = Array.newInstance(cls, 10);
Array.set(arr, 5, "this is a test");
String s = (String) Array.get(arr, 5);
System.out.println(s);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,最后将这个字符串从数组中取得并打印了出来。
这篇博客上的所写,写的不对不好的地方,还请大家指正,互相学习,共同进步。