大家都知道,要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载。Java类如果不被Java虚拟机加载,是不能正常运行的。现在我们运行的所有的程序都是在编译期的时候就已经知道了你所需要的那个类的已经被加载了。
Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类。这样的特点就是反射。
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。
Java的反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。
大家都用过IDEA和eclipse。当我们构建出一个对象的时候,去调用该对象的方法和属性的时候。一按点,编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。(反射是一种功能强大且复杂的机制。使用它的主要人员是工具构造者,而不是应用程序员。)
要正确使用Java反射机制就得使用java.lang.Class这个类。它是Java反射机制的起源。当一个类被加载以后,Java虚拟机就会自动产生一个Class对象。通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的方法、成员变量以及构造方法的声明和定义等信息。
既然class对象如此重要,那么我们如何获取class对象呢?这里有三种方法:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
下面介绍一下 Field、Method 和 Constructor三个类的常用方法
for (Method m : methods) {
String modifiers = Modifier.toString(m.getModifiers());
}
该修饰符是java.lang.reflect.Modifier的静态属性。这里是用十进制表示的,源码里面是十六进制表示的。
对应表如下:
PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048
包括超类的公有成员
。不包括超类的成员
。下面是一个代码案例,显示了如何打印一个类的全部信息的方法。
这个程序提醒用户输入一个类名,然后输出类中所有的属性、方法、构造器。里面有一些数字是我用来测试,类似-m8-
package JavaSE.Chapter5.Section57.cs573;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Scanner;
/**
* This program uses reflection to print all features of a class.
*
* @author Cay Horstmann
* @version 1.1 2004-02-21
* 利用反射分析类的能力,查看属性、构造器、方法的结构
*/
public class ReflectionTest {
public static void main(String[] args) {
String name = "JavaSE.Chapter5.Section57.cs571.Employee";
try {
//获取输入字符串的类对象
Class cl = Class.forName(name);
System.out.println(cl + "-1-");
//获取父类对象
Class supercl = cl.getSuperclass();
System.out.println(supercl + "-2-");
//获取类的访问修饰符和属于public、private、还是final
String modifiers = Modifier.toString(cl.getModifiers());
System.out.println(cl.getModifiers() + "-----cl.getModifiers");
System.out.println(modifiers + "-4-");
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print("class " + name);
if (supercl != null && supercl != Object.class) System.out.print(" extends "
+ supercl.getName());
System.out.print("\n{\n");
System.out.println("------------打印构造器方法-----------");
printConstructors(cl);
System.out.println("------------打印非构造器方法-----------");
printMethods(cl);
System.out.println("------------打印属性信息-----------");
printFields(cl);
System.out.println("}");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.exit(0);
}
/**
* Prints all constructors of a class
*
* @param cl a class
*/
public static void printConstructors(Class cl) {
/*返回反映Constructor对象表示的类声明的所有Constructor对象的数组类 。
这些是public,protected,default(package)访问和私有构造函数。
返回的数组中的元素不会排序,并且不是任何特定的顺序。
如果类有一个默认构造函数,它将包含在返回的数组中。
如果类对象表示接口,原始类型,数组类或空值,则此方法返回长度为0的数组。
*/
Constructor[] constructors = cl.getDeclaredConstructors();
System.out.println(Arrays.toString(constructors) + "-c5-");
for (Constructor c : constructors) {
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());
System.out.println(c.getModifiers() + "-----Counstructor.getModifiers");
//打印构造方法的访问修饰符
if (modifiers.length() > 0) System.out.print(modifiers + " ");
//打印构造方法的名字
System.out.print(name + "(");
//获取类构造器的参数类型数组
Class[] paramTypes = c.getParameterTypes();
System.out.println("-6-" + Arrays.toString(paramTypes) + "-c6-");
for (int j = 0; j < paramTypes.length; j++) {
if (j > 0) System.out.print(", ");
//打印参数类型名字
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
* Prints all methods of a class
*
* @param cl a class
*/
public static void printMethods(Class cl) {
Method[] methods = cl.getDeclaredMethods();
System.out.println(Arrays.toString(methods) + "-m7-");
for (Method m : methods) {
Class retType = m.getReturnType();
System.out.println(retType + "-m8-");
String name = m.getName();
System.out.print(" ");
// print modifiers, return type and method name
String modifiers = Modifier.toString(m.getModifiers());
System.out.println(m.getModifiers() + "-----Method.getModifiers");
//打印方法的访问修饰符
if (modifiers.length() > 0) System.out.print(modifiers + " ");
//打印方法返回类型和方法名
System.out.print(retType.getName() + " " + name + "(");
// print parameter types
Class[] paramTypes = m.getParameterTypes();
System.out.println("-m9-" + Arrays.toString(paramTypes) + "-m9-");
for (int j = 0; j < paramTypes.length; j++) {
if (j > 0) System.out.print(", ");
//打印方法参数类型
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
* Prints all fields of a class
*
* @param cl a class
*/
public static void printFields(Class cl) {
Field[] fields = cl.getDeclaredFields();
for (Field f : fields) {
Class type = f.getType();//返回属性所属类型的 Class 对象
System.out.println(type + "-f10-");
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
System.out.println(f.getModifiers() + "-----Field.getModifiers");
//打印属性的访问修饰符
if (modifiers.length() > 0) System.out.print(modifiers + " ");
//打印属性的类型名和属性名字
System.out.println(type.getName() + " " + name + ";");
}
}
}
Class t = f.getType();
//获取属性类型,f为Field对象。f.get(obj)
获取 obj 属性的当前值。f为Field对象,obj是一个Object对象。f.set(obj,value)
可以将 obj 对象的 f 属性设置成新值。f为Field对象。x. setAccessible(true);
,x为Field、Method 或 Constructor的对象。AccessibleObject.setAccessible(x, true);
来设置private值的可访问性,它是 Field、 Method 和 Constructor 类的公共超类,x为Field、Method 或 Constructor 对象的数组引用。接下来的一个例子将使用上面所说的方法,来查看访问对象的属性值
如下一个可供任意类使用的通用 toString方法。 其中使用 getDeclaredFileds 获得所有的数据属性, 然后使用 setAccessible 将所有的属性设置为可访问的。 对于每个属性,获得了名字和值。递归调用 toString方法,将每个值转换成字符串。(这个例子是java核心技术卷一里面的,这个例子看懂我感觉还是需要花时间的,有的地方我还没看懂……)
package JavaSE.Chapter5.Section57.cs574;
import JavaSE.Chapter5.Section57.cs571.Employee;
import java.lang.reflect.Field;
/**
* 在运行时使用反射分析对象,查看对象当前的各个属性值
*/
public class ObjectAnalyzerTest {
public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
Employee s = new Employee();
System.out.println(new ObjectAnalyzer().toString(s));
System.out.println("--------------------------------");
String[] str = {"str11", "str22", "str33"};
System.out.println(new ObjectAnalyzer().toString(str));
System.out.println("--------------------------------");
Class em = s.getClass();
Object obj = em.newInstance();
Field f = em.getDeclaredField("name");
f.setAccessible(true);
Object val = f.get(obj);//获取属性的值
System.out.println(val);
f.set(obj, "BitHachi");
Employee em2 = (Employee) obj;
System.out.println(em2.getName());
}
}
package JavaSE.Chapter5.Section57.cs574;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
public class ObjectAnalyzer {
private ArrayList<Object> visited = new ArrayList<>();
public String toString(Object obj) {
if (obj == null) return "null";
if (visited.contains(obj)) return "...";
visited.add(obj);
Class cl = obj.getClass();
//如果对象是一个字符串对象,则直接打印其值
if (cl == String.class) return (String) obj;
//判断类对象是否是一个数组
if (cl.isArray()) {
//getComponentType返回对象数组的的Class类对象。 如果此类不表示数组类,则此方法返回null。
String r = cl.getComponentType() + "[]{";
System.out.println(r + "-1-");
//返回指定数组对象的长度Array.getLength(obj)
for (int i = 0; i < Array.getLength(obj); i++) {
if (i > 0) r += ",";
//返回指定数组对象中的索引组件的值。
Object val = Array.get(obj, i);
System.out.println(val + " -val -2-");
/*确定指定类对象表示一个基本类型。
有九个预定类对象代表八个原始类型和void。
这些是由Java虚拟机创建,并且具有相同的名称为他们所代表的基本类型,
即boolean , byte , char , short , int , long , float和double 。
isPrimitive返回一个boolean值*/
if (cl.getComponentType().isPrimitive()) r += "@" + val + "@";
else r += toString(val);
System.out.println(r + "-3-");
}
return r + "}";
}
String r = cl.getName();
System.out.println(r + "-4-");
// 检查此类和所有超类的字段
do {
r += "[";
//获取类的所有属性得一个数组
Field[] fields = cl.getDeclaredFields();
/*setAccessible()为反射对象设置可访问标志。 true 表明屏蔽 Java语言的访问检查,
使得对象的 private私有属性也可以被査询和设置。 */
AccessibleObject.setAccessible(fields, true);
//获取所有属性的名字和值
for (Field f : fields) {
if (!Modifier.isStatic(f.getModifiers())) {
if (!r.endsWith("[")) r += ",";
r += f.getName() + "=";
System.out.println(r + "-5-");
try {
Class t = f.getType();//获取属性类型
Object val = f.get(obj);//获取属性的值
System.out.println(val + " -val -6-");
if (t.isPrimitive()) {
r += val;
System.out.println(r + "-7-");
} else {
r += toString(val);
System.out.println(r + "-7-");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
r += "]";
cl = cl.getSuperclass();
}
while (cl != null);
return r;
}
}
下面是一个代码示例:
package JavaSE.Chapter5.Section57.cs576;
import java.lang.reflect.Method;
public class MethodTableTest {
public static void main(String[] args) throws Exception {
// 获取相应方法的Method对象,通过类对象来获取方法对象
Method square = MethodTableTest.class.getMethod("square", double.class);
Method sqrt = Math.class.getMethod("sqrt", double.class);
// 打印x和y值表
printTable(1, 10, 10, square);
printTable(1, 10, 10, sqrt);
}
public static double square(double x) {
return x * x;
}
/**
* Prints a table with x- and y-values for a method
*
* @param from the lower bound for the x-values 上限
* @param to the upper bound for the x-values 下限
* @param n the number of rows in the table 个数
* @param f a method with a double parameter and double return value
*/
public static void printTable(double from, double to, int n, Method f) {
// print out the method as table header
System.out.println("方法: " + f);
double dx = (to - from) / (n - 1);//按上下限设置每次加的值
for (double x = from; x <= to; x += dx) {
try {
double y = (Double) f.invoke(null, x);//调用这个方法对象进行计算
System.out.printf("%10.4f | %10.4f%n", x, y);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package JavaSE.Chapter5.Section57.cs575;
import java.lang.reflect.Array;
import java.util.Arrays;
public class CopyOfTest {
public static void main(String[] args) {
int[] a = {1, 2, 3};
a = (int[]) goodCopyOf(a, 10);
System.out.println(Arrays.toString(a));
System.out.println("--------------------");
String[] b = {"Tom", "Dick", "Harry"};
b = (String[]) goodCopyOf(b, 10);
System.out.println(Arrays.toString(b));
}
public static Object goodCopyOf(Object a, int newLength) {
//第一步:获取a数组的类对象
Class cl = a.getClass();
System.out.println(cl + "---1");
//第二步:判断a数组的类对象是否是一个数组
if (!cl.isArray()) return null;
//第三步:使用Class类的getComponentType方法确定数组对应的类型
Class componentType = cl.getComponentType();
System.out.println(componentType + "---2");
//获取数组的长度
int length = Array.getLength(a);
System.out.println(length + "---3");
//构造新数组newInstance方法
//返回一个具有给定类型、给定长度的新数组
Object newArray = Array.newInstance(componentType, newLength);
/*arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
将指定源数组中的数组从指定位置复制到目标数组的指定位置。*/
System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));
return newArray;
}
}