1. 概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
2. 获取Class对象
public class ClassDemo1 {
public static void main(String[] args) {
try {
Entity foo1 = new Entity();
// 获得Class对象的两种方式:
Class c1 = Entity.class;
Class c2 = foo1.getClass();
Class c3 = Class.forName("com.thr.reflect.Entity");
// 不管c1还是c2,都代表了Entity的class的对象
System.out.println(c1 == c2);
// c3也是Entity的class对象
System.out.println(c1 == c3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Entity {
}
三种方式获取的Class对象是一致的。通过Class对象我们还可以创建类:
Entity en = (Entity) c1.newInstance();
通过获取的这个对象就可以像用new创建出来的对象一样使用Entity内部的方法等。
3. 动态加载类
首先创建一个接口:
public interface Singable {
void sing();
}
然后写两个实现类:public class Bird implements Singable {
@Override
public void sing() {
System.out.println("Bird sing...");
}
}
public class Cat implements Singable {
@Override
public void sing() {
System.out.println("Cat sing...");
}
}
最后再主程序中动态加载类:
public class World {
public static void main(String[] args) {
try {
Class c = Class.forName(args[0]);
Singable singable = (Singable) c.newInstance();
singable.sing();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
运行时要传入类的全名参数。
4. 获取方法信息
首先来看基本数据类型的Class对象:
public class ClassDemo2 {
public static void main(String[] args) {
Class[] cs = new Class[] { int.class, String.class, double.class,
Double.class, void.class, Class.class };
for (Class c : cs) {
System.out.println(c.getName());
System.out.println(c.getSimpleName());
}
}
}
我们编写一个获取类的方法的工具方法: public static void printClassMethods(Object obj) {
// 首先获取Class对象
Class c = obj.getClass();
// 获取类的名称
System.out.println("类的名称是:" + c.getName());
// 获取类的方法,一个Method的对象就是一个成员方法,通过getMethods()获取的是所有public的方法,包括从父类继承而来的
// getDeclaredMethods()获取的是所有该类自己生命的方法,不问访问权限
Method[] ms = c.getMethods();
for (Method m : ms) {
// 获取返回值类型
Class returnType = m.getReturnType();
System.out.print(returnType.getName() + " ");
// 获取方法名
System.out.print(m.getName() + "(");
// 获取参数类型
Class[] paramTypes = m.getParameterTypes();
for (Class type : paramTypes) {
System.out.print(type.getName() + ",");
}
System.out.println(")");
}
}
使用它: public static void main(String[] args) {
String s = new String();
ClassUtil.printClassMethods(s);
Integer i = 1;
ClassUtil.printClassMethods(i);
}
5. 获取成员变量和构造函数信息
获取成员变量信息:
public static void printClassFields(Object obj) {
// 首先获取Class对象
Class c = obj.getClass();
// 使用getFields()获取的是所有public的成员变量信息,使用getDeclaredFields获取的是该类自己声明的成员变量信息,不问访问权限
Field[] fs = c.getDeclaredFields();
for (Field f : fs) {
// 获取成员变量的类型
Class fieldType = f.getType();
String typeName = fieldType.getName();
// 获取成员变量的名字
String filedName = f.getName();
System.out.println(typeName + " " + filedName);
}
}
获取构造方法信息: public static void printClassConstructor(Object obj) {
// 首先获取Class对象
Class c = obj.getClass();
Constructor[] cons = c.getDeclaredConstructors();
for (Constructor con : cons) {
// 获取方法名
System.out.print(con.getName() + "(");
// 获取参数类型
Class[] paramTypes = con.getParameterTypes();
for (Class type : paramTypes) {
System.out.print(type.getName() + ",");
}
System.out.println(")");
}
}
6. 方法反射的基本操作
所有的方法对象都有一个invoke方法我们调用method.invoke(对象, 参数列表)就可以执行方法:
public class MethodDemo1 {
public static void main(String[] args) {
try {
// 获取print(int, int)方法
A a = new A();
Class c = a.getClass();
Method m1 = c.getMethod("print", int.class, int.class);
// 执行方法,使用o来接受返回值
// 如果有返回值,那么Object就是返回值的类型;没有返回值就返回null
Object o1 = m1.invoke(a, 10, 10);
// 获取print(String, String)方法
Method m2 = c.getMethod("print", new Class[] { String.class,
String.class });
Object o2 = m2.invoke(a, "s1", "s2");
// 获取print()方法
Method m3 = c.getMethod("print");
Object o3 = m3.invoke(a);
System.out.println(o1);
System.out.println(o2);
System.out.println(o3);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
class A {
public void print() {
System.out.println("hello");
}
public int print(int a, int b) {
System.out.println(a + b);
return a + b;
}
public String print(String a, String b) {
System.out.println(a.toUpperCase() + "," + b.toLowerCase());
return a + b;
}
}
7. 泛型的本质
public static void main(String[] args) {
List list1 = new ArrayList();
List<String> list2 = new ArrayList<String>();
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1 == c2);
// 反射的操作都是编译之后的操作
// c1==c2是true说明编译之后集合的泛型是去泛型化的
// Java中集合的泛型是防止错误输入的,只在编译阶段有效,绕过编译就无效了
// 我们可以通过方法的反射来绕过编译
Method m;
try {
m = c1.getMethod("add", Object.class);
m.invoke(list1, 100);
System.out.println(list1.size());
System.out.println(list1);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
通过这个例子可以看到通过Java的反射机制可以绕过编译,是在运行时刻执行的,自然就可以绕过集合的泛型。