【Java】(2)Java反射

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


發佈了80 篇原創文章 · 獲贊 0 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章