反射概述
概念:Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class(其实反射就是把Java类中的各种成分映射成相应的java类)
所有的人,都可以定义一个Person类,那么所有的类也是同样的道理,用一个Class类来描述。
Class类型实例对象的三种方式
类名.class
对象.getClass()
static Class forName("类名") :此方法,有返回Class对象有两种方式,一种是字节码已经被加载进虚拟机,直接返回对象;第二种是虚拟机里面没有字节码,则用类加载器去加载,把加载进来的字节码缓存在虚拟机。
九个预定义的Class实例对象:八个基本数据类型+void类型(除这9个外,还有数组型)
class Demo1Reflect {
public static void main(String[] args) throws Exception {
String str = "反射";
Class cls1 = String.class;
Class cls2 = str.getClass();
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls3); //三种方式创建出来的字节码都相同
System.out.println(cls1.isPrimitive()); //isPrimitive()判断是否是基本数据类型
System.out.println(int.class.isPrimitive());
System.out.println(int.class == Integer.class); //这个返回false,因为int是预定义的实例对象,Integer是一个类
System.out.println(int[].class.isArray()); //isArray()判断是否是数组类型
}
}
一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个Java的Java类来表示,就像汽车是一个类,汽车中的发动机 ,变速箱等等也是一个个的类。表示Java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息 ,这些信息就是用相应类的 实例对象来表示,它们是Contuctor,Method,Field,Array等等。
Constructor类
Constructor类代表某一个类中的一个构造函数
得到某个类所有的构造方法:
Constructor[] constructors = Class.forName("java.lang.String")
.getConstructor()
得到某一个构造方法:
Constructor constructor = Class.forName("java.lang.String")
.getConstructor(StringBuffer.class)
创建实例对象:
String str = (String)constructor.
newInstatce(new StringBuffer("abc"))
import java.lang.reflect.Constructor;
class Demo2Construtor {
public static void main(String[] args) throws Exception {
Constructor<String> constructor = String.class.getConstructor(StringBuffer.class); //得到String类中的一个构造方法,并且该构造方法只能传递//StringBuffer型参数
String str = (String)constructor.newInstance(new StringBuffer("反射")); //创建实例对象,newInstance返回的Object类型,所以要向下转型
System.out.println(str);
}
}
Class.newInstance():该方法内部先得到默认的空参数构造方法,然后用该构造方法创建实例对象
String obj = (String)Class.forName("java.lang.String").newInstance();
Field类
Field类代表某个 类中的一个成员
import java.lang.reflect.Field;
class Demo3Field {
public static void main(String[] args) throws Exception {
FieldDemo fd = new FieldDemo(4,6);
Field fieldY = fd.getClass().getField("y"); //获取一个Field类实例
System.out.println(fieldY.get(fd)); //得到变量 y的值
Field fieldX = fd.getClass().getDeclaredField("x"); //因为变量x被私有,所以通过getDeclaredField()方法来获取
fieldX.setAccessible(true); //通过setAccessible()方法是否允许得到变量x的值
System.out.println(fieldX.get(fd));
}
}
class FieldDemo {
private int x;
public int y;
FieldDemo(int x,int y) {
this.x = x;
this.y = y;
}
}
思路:
1、遍历对象中的所有字段,取出String类型的字段。
2、对字段进行内容的替换,把b替换成a
import java.lang.reflect.Field;
class Demo3Field {
public static void main(String[] args) throws Exception {
FieldDemo fd = new FieldDemo();
replaceStr(fd)
}
public static void replaceStr(Object fd) throws Exception{
Field[] fields = fd.getClass().getFields(); //getFields得到对象中的所有字段
//遍历Field[]数组
for(Field field : fields) {
//判断字段类型是否是String类型,(因为都是一个字节码,所以用==比较)是的话就开始替换
if(field.getType() == String.class) {
String oldValue = (String)field.get(fd); //得到原来的字符串
String newValue = oldValue.replace('b', 'a'); //把字符串里面的b替换成a
field.set(fd, newValue); //再用set方法对字段进行修改
}
}
}
}
class FieldDemo {
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "sport";
public String toString() { //重写toString方法,一遍打印此雕像是能够直接显示字符串
return str1 + "," + str2 + "," + str3;
}
}
Method类
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
Metho charAt = Class forName("java.lang.String") .getMethod("charAt",int.calss);
调用方法:
通常方法:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1));
注意:如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法
用反射方式执行某个类中的main方法
Metho charAt = Class forName("java.lang.String") .getMethod("charAt",int.calss);
调用方法:
通常方法:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1));
注意:如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法
import java.lang.reflect.*;
class Demo4Method {
public static void main(String[] args) throws Exception {
String str = "反射Method";
Method charAt = str.getClass().getMethod("charAt", int.class); //通过调用String字节码,获取Method类的实例对象
System.out.println(charAt.invoke(str, 3));//invoke()调用String类中charAt()方法
}
}
用反射方式执行某个类中的main方法
import java.lang.reflect.*;
class Demo4Method {
public static void main(String[] args) throws Exception {
String className = args[0]; //主函数传参,假设数组args第一个元素为calssName
Method mainMethod = Class.forName(className).getMethod("main", String[].class); //得到TestAgruments类里面的main方法
mainMethod.invoke(null, new Object[]{new String[]{"反射","字段","方法"}});
/*
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射的方式来调用这个
main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对
应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容
jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散称为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码
mainMethod.invoke(null, new String[]{"反射","字段","方法"});
*/
}
}
class TestArguments {
public static void main(String[] args) {
for(String str : args) {
System.out.println(str);
}
}
}
Jdk1.5:public Object invoke(Object obj,Object...args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按Jdk1.4的语法需要将一个数组作为参数
传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法
的代码也可以用Jdk1.4改写为charAt.inboke("str",new Object[]{1})形式
数组的反射
特点:
1、具有相同的维度和元素类型的数组属于同一个类型,即具有相同的Class实例对象
2、代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class(字节码)Object.class
3、基本类型的一维数组可以被当做Object使用,不能当做Object[]类型使用。非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用
import java.util.*;
import java.lang.reflect.*;
class Demo5Array {
public static void main(String[] args) {
int[] arr1 = new int[3];
String[] arr2 = new String[]{"a","b","c"};
System.out.println(arr1.getClass().getSuperclass().getName());
Object obj1 = arr1;
Object obj2 = arr2;
// Object[] obj3 = arr1; 如果把arr1赋值给Object[]型则编译失败
Object[] obj4 = arr2;
/*
arr1的打印结果为一个对象的哈希值,这时因为1.4版本接收的是Object[]类型,基本类型不能当做Object[]类型使用,
只能当做Object使用,而1.5版本的jdk接收的是Object类型。
*/
System.out.println(Arrays.asList(arr1));
System.out.println(Arrays.asList(arr2));
printObject(arr2);
}
//用反射的方式遍历数组
public static void printObject(Object obj) {
Class cls = obj.getClass(); //获取字节码
if(cls.isArray()) { //判断该字节码是不是数组类型
int len = Array.getLength(obj); //使用静态方法getLength()获取数组长度
for(int x = 0 ; x < len ; x++) {
System.out.println(Array.get(obj, x)); //得到值
}
}else {
System.out.println(obj);
}
}
}