Java 反射

不該相遇在秋天發表於2018-10-20

一、什麼是反射?

能夠分析類能力的程式 稱之為反射

 

二、反射能做什麼?

反射機制的功能極其強大,打個比方,假如你有反射功能,那麼你不管看到誰,都可以看透對方的一切,並且還可以呼叫對方的肢體做一些你想做的事情。

 

三、怎麼反射?

1. 利用Class物件來反射

每當編譯一個類,便會生成一個同名的.class檔案,此檔案中儲存了類相關的資訊,稱之為Class物件。
每當程式需要例項化一個物件時(或者是建立靜態成員的引用時),JVM將使用類載入器來載入這個類的Class物件,再用Class物件來建立這個物件
可以這麼理解:每一個類都有自己的class物件,class物件是儲存在檔案中的,在使用的時候會被載入到JVM中,class物件是用來建立物件的模板。

 

2. 得到Class

第一種方式:

        //一般來說 這種方式幾乎不會使用
        User user = new User();
        Class c1 = user.getClass();

第二種方式:

        //在程式中反射某個類的資訊時一般會使用這種方式
        Class c2 = User.class;

第三種方式:

        //當類路徑從配置檔案載入的時候會使用此種方式
        try{
            //無論何時使用這種方式必須提供一個錯誤處理器
            Class c3 = Class.forName("model.User");
        }catch (ClassNotFoundException e){
            System.out.println("類不存在");
        }

3. 根據Class獲取物件例項

        Class c1 = User.class;
        try{
            Object object = c1.newInstance();//呼叫無參構造器  必須是顯式構造器
            User user = (User) object;
            System.out.println(user.toString());
        }catch (Exception e) {
            e.printStackTrace();
        }

 

四、實現反射

1. 反射建構函式

        Class cla = User.class;
        Constructor[] constructors =  cla.getConstructors();//獲取當前類的所有public建構函式
        //Constructor[] constructors =  cla.getDeclaredConstructors();//獲取當前類的所有建構函式
        Arrays.stream(constructors).forEach((v)->{
            System.out.print(Modifier.toString(v.getModifiers()) + " " + v.getName() + "(");
            String string = Arrays.stream(v.getParameterTypes()).map(Class::getName).collect(Collectors.joining(","));
            System.out.println(string + ")");
        });

 

2. 反射屬性

        Class cla = User.class;
        //Field[] fields = cla.getFields();//獲取當前類包括超類的public屬性
        Field[] fields = cla.getDeclaredFields();//獲取當前類的所有屬性 包括私有屬性

        Arrays.stream(fields).forEach((v)->{
            String s = Modifier.toString(v.getModifiers()) + " " + v.getType().getName() + " " + v.getName();
            try{
                s += " = " + v.get(cla.newInstance());
            }catch (Exception e){

            }finally {
                System.out.println(s);
            }
        });

 

3. 反射方法

        Class cla = User.class;
        //Method[] methods = cla.getMethods();//獲取當前物件包括超類物件包括Object物件的所有public方法
        Method[] methods = cla.getDeclaredMethods();//獲取當前物件的所有方法 包括私有方法
        Arrays.stream(methods).forEach((v)->{
            //Type type = v.getGenericReturnType();//與getReturnType一樣
            Type type = v.getReturnType();
            System.out.print(type.getTypeName()+" "+v.getName() + "(");

            Class[] params = v.getParameterTypes();
            String string = Arrays.stream(params).map(Class::getName).collect(Collectors.joining(","));
            System.out.println(string + ")");
        });

 

4. 反射註解

        Class cla = User.class;

        //獲取類上的註解
        Annotation[] annotations = cla.getAnnotations();
        Arrays.stream(annotations).forEach(System.out::println);

        if(cla.isAnnotationPresent(Deprecated.class)){
            System.out.println(cla.getName()+"類含有Deprecated這個註解");
        }

        System.out.println("-----------------");

        //獲取方法上的註解
        Method[] methods = cla.getDeclaredMethods();
        Arrays.stream(methods).filter((v)->v.getDeclaredAnnotations().length > 0).forEach((vvv)-> {
            Annotation[] a2 = vvv.getDeclaredAnnotations();
            System.out.print(vvv.toString() + ": ");
            Arrays.stream(a2).forEach(System.out::println);

            if(vvv.isAnnotationPresent(Transient.class)){
                System.out.println(vvv.getName()+"方法含有Transient這個註解");
            }
        });

我們在spring框架中時常用到@Controller @Service @Autowrite 等註解 實際上框架就是在啟動的時候利用反射技術將所有類所有方法遍歷了一遍  校驗是否含有註解 根據不同的註解初始化不同的事情 

關於註解的內容會另外開一篇部落格系統闡述,在此只是說明註解存在的意義是被反射發現,這也是註解唯一的用處。

 

5. 呼叫方法

        Class cla = User.class;

        try{
            //呼叫某個類的某個方法
            User user = (User)cla.newInstance();

            Method method1 = cla.getMethod("setId", Long.class);
            method1.invoke(user,2L);

            Method method11 = cla.getMethod("getId");
            System.out.println(method11.invoke(user));


            //靜態方法可傳Null
            Method method2 = cla.getMethod("setSex", String.class);
            method2.invoke(null,"不男不女");

            Method method22 = cla.getMethod("getSex");
            System.out.println(method22.invoke(null));

        }catch (Exception e){
            e.printStackTrace();
        }

 

 五、原始碼分享

Fork me on Gitee

 

相關文章