1、首先一個問題,什麼是類,類是不是物件?
我們總是說我們new一個物件出來
那麼我們還沒有new的時候,建造的那個java類是不是物件呢?
是
它是java.lang.Class的物件
對於反射我們首先要知道的就是什麼是Class類
java.lang.Class到底是什麼,有啥用。
首先我們要得到Class,我們有三種可以得到的方法,程式碼如下,c1,c2,c3就是我們需要的Class
//任何一個類都有一個隱含的成員變數叫做calss Class c1 = Man.class; //通過物件的getClass方法獲取class Class c2 = man1.getClass(); //c1和c2表示了Man的類型別,也就是class type Class c3 = null; c3 = Class.forName("com.clazz.test.Man");
需要知道的是類的類型別是一樣的,都指的是一個,所以c1,c2,c3都是一樣的。都是相等的。
那我們現在已經拿到了Class,可以用它來幹嘛呢?
我們可以通過類的類型別直接建立一個物件,也就是說,沒有new了。
需要注意的是下面的程式碼中newInstance方法會呼叫類的無參構造方法
//通過類的類型別建立物件。 Man man2 = (Man) c1.newInstance();
下面是所有的測試程式碼
package com.clazz.test; /** * Class類的描述測試 * @author XX * */ public class ClazzDemo1 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { //Man是java.lang.Class的物件 //任何一個類都是class類的例項物件 Man man1 = new Man(); //man1是Man類的例項物件 //任何一個類都有一個隱含的成員變數叫做calss Class c1 = Man.class; //通過物件的getClass方法獲取class Class c2 = man1.getClass(); //c1和c2表示了Man的類型別,也就是class type Class c3 = null; c3 = Class.forName("com.clazz.test.Man"); //通過類的類型別建立物件。 Man man2 = (Man) c1.newInstance(); } } class Man{ public Man(){ System.out.println("建立了這個類"); } }
2、靜態載入類和動態載入類
new建立物件是靜態載入,在編譯時刻就需要載入所有可能使用到的類。
通過動態載入類,可以在執行時刻載入使用到的類
也就是我們的c3使用的方法
package com.clazz.test; public class DynamicLoadClass { public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException { //靜態載入類,直接new出物件,呼叫方法,一般都是使用這個,但是一旦沒有Student類,編譯就會報錯 //而且如果需要使用老師物件那麼程式碼需要重新寫過很多,要把new物件重新寫過, Student s1 = new Student(); s1.speck(); //通過動態載入類,編譯時刻,無論學生還是老師類存不存在,都可以編譯通過,只有當執行時刻才進行載入 //好處是如果你需要新增一個類,直接實現IPeople介面,只要forName的名字正確即可,而且這個名字可以 //作為一個引數傳進來然後進行修改 Class c1 = Class.forName("com.clazz.test.Student"); IPeople p1 = (IPeople) c1.newInstance(); p1.speck(); } }
package com.clazz.test; public class Student implements IPeople{ public void speck() { System.out.println("我是學生"); } }
package com.clazz.test; public interface IPeople { public void speck(); }
3通過反射拿到一個類的所有的資訊
下面這個就是反射最基本的用法,也是反射最神奇的地方
package com.clazz.util; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import com.clazz.test.Student; /** * calss類的測試工具類 * @author XX * */ public class ClazzUtil { public static void main(String args[]) { //通過反射能拿到類的所有方法資訊 printAll(new Student()); } /** * 獲取這個物件的資訊並列印出來 */ public static void printAll(Object object) { Class c = object.getClass(); //列印該類的名字 System.out.println("類的名字" + c.getName()); //列印該類的所有方法(包含從父類繼承來的)(不包含私有的方法) Method[] ms = c.getMethods(); for (Method method : ms) { System.out.println("方法:" + method.getName() + " 方法的返回值:" + method.getReturnType().getName()); //獲取方法的引數 Class<?>[] par = method.getParameterTypes(); for (Class<?> class1 : par) { System.out.println("方法的入參:" + class1.getName()); } } //列印該類的方法(不包含從父類繼承來的)包含私有方法 Method[] ms2 = c.getDeclaredMethods(); for (Method method : ms2) { System.out.println("方法1:" + method.getName() + " 方法的返回值:" + method.getReturnType().getName()); } //獲取成員變數(不包含私有變數) Field[] fs = c.getFields(); for (Field field : fs) { System.out.println("成員變數: " + field.getName()); } //獲取成員變數(包含私有變數) Field[] fs1 = c.getDeclaredFields(); for (Field field : fs1) { System.out.println("成員變數: " + field.getName()); } //獲取構造方法 Constructor[] con = c.getDeclaredConstructors(); for (Constructor constructor : con) { System.out.println("構造方法:" + constructor.getName()); } } }
package com.clazz.test; public class Student implements IPeople{ private int a1; public int a12; public void speck() { System.out.println("我是學生"); } private void speck2() { System.out.println("我是學生"); } }
4通過反射呼叫類的方法
這個是反射的應用,通過反射呼叫一個類的方法
package com.clazz.test; import java.lang.reflect.Method; /** * @author XX * */ public class ReflectMethod { public static void main(String args[]) throws Exception { A a = new A(); Class c = a.getClass(); Method m = c.getMethod("add", int.class,int.class); System.out.println(m.invoke(a, 1,2)); } } class A{ public int add(int a,int b) { return a+b; } private int minus(int a,int b) { return a-b; } }
5利用反射解釋ArrayList的泛型
package com.clazz.test; import java.lang.reflect.Method; import java.util.ArrayList; public class ArrayListRflect { public static void main(String args[]) throws Exception { /** * 下面的程式碼用於證明,在ArrayList中,泛型只在編譯之前有效,編譯之後泛型對於list的限制 * 是無效的。利用反射就能繞過編譯這個階段在ArrayList新增本不屬於該泛型的值 */ ArrayList<String> list = new ArrayList<String>(); list.add("哈哈"); //list.add(1);如果原來這麼寫,編譯會報錯的 //那麼怎麼在list放個int的1呢? //獲取list的類型別 Class c = list.getClass(); //通過類型別拿到方法 Method m = c.getMethod("add",Object.class); //通過反射呼叫方法 m.invoke(list, 1); System.out.println(list); //結果為:[哈哈, 1] //所以證明了上面的觀點,也同時很好的證明了反射多神奇 } }