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的反射機制可以繞過編譯,是在運行時刻執行的,自然就可以繞過集合的泛型。