------- android培训、java培训、期待与您交流! ----------
Class类(反射的基石)
Java中的类是用来描述同一类事物的,比如, Person,Animal等等,而java中的类也同属于一类事物,对于这些类就用Class类来描述, Class对象又称作字节码对象,数组和void也是数据类型
得到一个类的Class对象又三种方式
1. 类名.class; 例如: Class cla= Date.class;
2. 类对象的引用.getClass() ; 例如: Date date = newDate(); Class cla = date.getClass();
3. Class.forName(包名.类名); 例如Class.forName(Java.util.Date);
Java中预定义了9个Class实例对象
1. 分别是8个基本数据类型和void,它们也可以用类型.class获得相应的Class对象
2. 基本数据类型的包装类可以通过包装类.Type获得对应基本数据类型的Class对象,例如 int.class == Integer.Class 返回的是true
3. public boolean isPrimitive()判断是否为基本数据类型的字节码文件对象
数组类型的Class实例对象
public boolean isArray() 判断是否为数组类型的字节码文件对象
代码示例
package com.itcast.reflect;
public class ClassTest {
public static void main(String[] args) throws Exception {
// 得到Class对象的三种方式
String str = "1234";
Class cls1 = Class.forName("java.lang.String"); // 这里需要抛出异常
Class cls2 = String.class;
Class cls3 = str.getClass();
// 比较这三个Class对象是否为同一个, 返回的都是true
System.out.println(cls1 == cls2);
System.out.println(cls1 == cls3);
// 判断cls1是否为基本数据类型的Class对象, 返回false
System.out.println(cls1.isPrimitive());
// 判断int.class是否为基本数据类型的Class对象, 返回true
System.out.println(int.class.isPrimitive());
// 判断Integer.Type是否与int.class是同一对象, 返回true
System.out.println(Integer.TYPE == int.class);
// 判断int[]是否为数组类型的Class对象, 返回true
System.out.println(int[].class.isArray());
}
}
反射
反射是将Java类中的各个组成成分映射成相应的类,例如类可以用Class类来表示,那么类中的属性, 方法,构造方法, 修饰符,包等信息也可以用一个类来表示, 相对应的分别是Filed, Method, Constructor, Package等等,这些类需要通过Class类中的方法获得
Constructor类
通过Class类中的getConstructors可以获得一个构造方法的数组Constructors[],如果想获得其中一个构造方法可以用Class类中的getConstructor(Class… paras),这个方法接收一个可变参数, 可以根据数据类型的Class对象获得相应的构造方法
Constructor对象后,可以通过Constructor中的newInstance(Object…objs)方法构建一个使用该构造方法创建的实例对象.
通过Class类中newInstance()方法也可以获得该Class对象的类的实例,是通过无参构造方法获得的, 一般不建议使用这个方法
代码示例
package com.itcast.reflect;
import java.lang.reflect.Constructor;
import java.util.Date;
public class ConstructorTest {
public static void main(String[] args) throws Exception {
// 得到String类的Class对象
Class clazz1 = String.class;
// 得到指定的String对象中的某个构造方法的Constructor对象, 这里是String(StringBuffer sbuff)
Constructor cons = clazz1.getConstructor(StringBuffer.class); // 这里需要抛出异常
// 通过Constructor对象构建一个String的对象
String str = (String) cons.newInstance(new StringBuffer("abc"));
System.out.println(str);
// 利用Class中的newInstance()方法调用String的无参构造方法构建一个Date对象
Class clazz2 = Date.class;
Date date = (Date) clazz2.newInstance();
System.out.println(date);
}
}
Filed类
Filed类对象表示的是类中的属性,可以通过Class类中的,getFields()和getDeclaredFileds()获得类中所有的属性,返回Filed[], 如果想要获得某个指定的属性,需要用getFiled(“属性名”),或者getDeclaredFiled(“属性名”)获得.
Field类中的getFiled()只能获得显示的属性Field对象,不能获得私有的属性Field对象,而getDeclareFile()可以获得声明的属性对象,但是不能直接对属性内容进行修改, 需要通过Filed类中的setAccessAble(true)来更改访问权限来修改属性的值, getFields()和getDeclareFields()也是一样
要想通过Filed的对象获取属性值,需要使用Field中的get(类对象)来获取,要设置属性的值, 需要通过set(类对象, newValue)来重新设置
代码示例
package com.itcast.reflect;
import java.lang.reflect.Field;
public class FiledTest {
public static void main(String[] args) throws Exception {
// 获得Person的Class对象
Class cls = Person.class;
// 获得Person的对象
Object obj = cls.getConstructor().newInstance();
// 获取所有的Field对象
Field[] fields = cls.getDeclaredFields();
// 遍历获取每个属性的Field对象
for (Field field : fields) {
// 将所有field权限设置为可操作的
field.setAccessible(true);
// 获取属性原来的值
String oldValue = (String) field.get(obj); // 这里会抛出异常
// 将原来的值中的字符'b'全部替换成'a'
String newValue = oldValue.replace('b', 'a');
// 将原来的属性值设置为新的值
field.set(obj, newValue);
}
// 打印Person对象的toString()
System.out.println(obj);
}
}
class Person {
private String str1 = "abcb";
private String str2 = "basketball";
private String str3 = "hello";
public Person() {
}
@Override
public String toString() {
return str1 + ":" + str2 + ":" + str3;
}
}
Method类
Method类对象表示的是类中的方法,可以通过Class类中的getMethods()和getDeclaredMethods()获得全部的方法,返回Method[],如果要获取某个方法, 需要用Class类中的getDeclaredMethod(“方法名”,参数类型.class…),或者getDeclaredMehod(“方法名”, 参数类型.class…),同Filed一样, getDeclaredMethod()和getMethod()可以得到声明的方法,要使用限制的方法需要使用setAccessable(true)来设置
Method类中提供了invoke(对象,参数…)来调用某个对象的方法,前提是先获取到相对应的Method对象,如果调用的是静态方法, 那么对象的位置写成null也可以
如果要被调用的方法需要接收一个数组类型的数据,例如 Method method = clazz.getMethod(“main”, String[].class),那么使用invoke方法的时候需要将传入的数组参数转换为Object,因为Java语法为了兼容jdk1.5之前的语法,会将数组内的内容先拆开, 视为多个参数传入方法,数组也是Object对象,因此, 将数组转换为Object就不会出现错误,或者还有一种方式是将要传入的数组作为Object[]的第一个参数,将Object[]传入也可以
代码示例
package com.itcast.reflect;
import java.lang.reflect.Method;
public class MethodTest {
public static void main(String[] args) throws Exception {
// 获得Test类的Class对象
Class cls = Test.class;
// 获得Test类中的main方法的Method对象
Method mth = cls.getMethod("main", String[].class);
// 调用main方法, 因为是静态的方法, 所以对象出入null即可, 将传入的数组参数转换为Object
mth.invoke(null, (Object) new String[] { "张三", "李四", "王五" });
}
}
class Test {
// 直接这行这个主方法就会爆出数组下标越界异常
public static void main(String[] args) {
System.out.println(args[0] + ":" + args[1] + ":" + args[2]);
}
}
数组的反射
数组也是Object类的子类,一个一位数组就可以表示为一个Object, 多维数组同样也是Object,比较两个数组的Class对象是否相等,比较的是数组的数据类型和纬度, 如果是相同类型,并且纬度相同的两个数组, 那么它们的Class对象时同一个
Array类是用来操作数组的反射的,可以通过Array.getLenth(“数组对象”)来获取数组的长度,通过Array.get(“数组对象”, “数组下标”)来获取数组的元素,数组的数据类型是获取不到的,但是可以获取到数组元素的类型,使用getClass().getName()获得
代码示例
package com.itcast.reflect;
import java.lang.reflect.Array;
public class ArrayTest {
public static void main(String[] args) {
// 定义4个数组
int[] arr1 = new int[] { 1, 2, 3 };
int[] arr2 = new int[5];
int[][] arr3 = new int[2][3];
String[] arr4 = new String[3];
// 分别比较每个数组的Class对象, 第一个结果为true, 其余都false
System.out.println(arr1.getClass() == arr2.getClass());
// 两个不同维度和不同类型的数组比较会编译错误
// System.out.println(arr1.getClass()== arr3.getClass());
// System.out.println(arr1.getClass() == arr4.getClass());
// 先得到每个数组的Class对象, 再进行比较
Class a1 = arr1.getClass();
Class a3 = arr3.getClass();
Class a4 = arr4.getClass();
System.out.println(a1 == a3);
System.out.println(a1 == a4);
// 调用反射打印数组方法, 打印结果 1 2 3
printArr(arr1);
}
// 遍历数组, 利用反射打印数组元素
public static void printArr(Object obj) {
// 得到参数的Class对象
Class clazz = obj.getClass();
// 判断clazz是否为数组的字节码文件
if (clazz.isArray()) {
// Array.getLength()可以通过反射获得数组的长度
for (int i = 0; i < Array.getLength(obj); i++) {
// Array.get()可以获取指定的元素
System.out.println(Array.get(obj, i));
}
} else {
System.out.println(obj);
}
}
}