基于反射的动态调用-不止是code和oop,还有类加载方案

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于反射的动态调用-不止是code和oop,还有类加载方案相关的知识,希望对你有一定的参考价值。

先给出基础类资源的代码,很容易理解,不做过多介绍:

 1 package oop;
 2 interface t {
 3     public static final String name="zhangph";
 4 }
 5 public class Father implements t{
 6     private static int mem;
 7     static {
 8         System.out.println("这是父类");//因为你不曾常用反射,所以加上这段代码显示区别
 9     }
10     public Father() {
11         this.mem=258;
12     }
13     public String toString() {
14         return "there is the Object named father and the mem="+this.mem;
15     }
16     public void outPut() {
17         System.out.println("father function"+mem);
18     }
19     public void setMem(int m) {
20         this.mem=m;
21     }
22     public void test(int x) {
23         System.out.println("参数为"+x);
24     }
25 }
 1 package oop;
 2 import java.lang.reflect.Field;
 3 public class Child extends Father{
 4    private static int mem;
 5     static {
 6         System.out.println("class is here");
 7         Static();
 8         System.out.println(mem);
 9     }
10    public Child() {
11        System.out.println("子类版本构造函数");
12        System.out.println("子类的mem初始值"+mem);
13        this.mem=2;
14        System.out.println("子类的mem最终值"+mem);
15    }
16    public void Cg() throws Exception {
17        this.mem=6;
18         System.out.println("子类的Cg方法"+mem);
19    }
20    public  String toString() {
21         return "child"+super.toString();
22     }
23    public static void Static () {
24        System.out.println("this is a static function");
25    }
26 }

然后再介绍主函数:

 1 package oop;
 2 import java.lang.reflect.Field;
 3 import java.lang.reflect.Method;
 4 import java.util.Random;
 5 public class oop {
 6     private static void Print( Object s) {
 7         System.out.println(s.toString());
 8     }
 9     public static void main(String []args) {
10         Class<?> cl1 = null,cl2=null,cl3=null;
11         try {
12            Print("1");
13            cl1=Class.forName("oop.Father");
14         } catch (ClassNotFoundException e) {
15             // TODO Auto-generated catch block
16             e.printStackTrace();
17         }
18         Print("2");
19         cl2=Father.class;
20         Print("3");
21         cl3=new Father().getClass();
22         Print("ls");
23         Print(cl1.getName());
24         Print(cl2.getName());
25         Print(cl3.getName());
26         Print("***********************");
27         try {
28             Father f=(Father)cl1.newInstance();
29             Field field=cl1.getDeclaredField("mem");
30             field.setAccessible(true);
31             field.set(f,1234);
32             f.outPut();
33             Method method1=cl1.getMethod("test",int.class);
34             method1.invoke(cl1.newInstance(),5);
35             Method method2=cl1.getMethod("outPut" );
36             method2.invoke(cl1.newInstance());
37             Method method3=cl1.getMethod("toString");
38             String ss=(String) method3.invoke(cl1.newInstance());
39             Print(ss);
40         } catch (InstantiationException | IllegalAccessException e) {
41             // TODO Auto-generated catch block
42             e.printStackTrace();
43         }catch (NoSuchFieldException | SecurityException e) {
44             // TODO Auto-generated catch block
45             e.printStackTrace();
46         }catch (Exception e) {
47             System.out.println("error");
48         }
49          
50          
51     }
52         
53 }

执行效果:

1
这是父类
2
3
ls
oop.Father
oop.Father
oop.Father
***********************
father function1234
参数为5
father function258

there is the Object named father and the mem=258

下面着重分析第三个程序:

在第15、21、23行,你分别看到三种加载Class对象引用的方法。

forName()方法在编译时不会被检查,是完全的运行时调用,所以需要异常检查。对于Class对象引用,你可能之前接触很少(也许只是使用mysql包的时候接触过),但是它本身不难理解,类是程序的一部分,java中一切皆对象,所以自然而然每个类都对应着一个Class对象(泛型的),如你所知,每编译一个类,都会产生对应的.class文件。

我们知道,java程序在它开始运行之前并非完全被加载。

JVM的类加载系统会首先检查是否加载了.class文件的字节码,而JVM加载一个类的开始,一定是创建了对这个类的静态引用。

基于上面这句话,我们可以推断,构造方法也是静态方法

我们回到15、21、23行,此时可以很明白的理解15和23行,23行是调用构造方法导入class类,forName方法直接加载.class对象。在forName方法中,我们也可以看到类体的静态语句被执行。

然后介绍21行,21行是使用一种叫做类字面常量的方法来生成的对Class对象的引用。这样做和使用forName的区别是它是在编译时检查的,所以不需要放在try块里面。同时,类字面常量的加载实现了足够的惰性,你可以看出,类字面常量并未加载类中的静态语句。

 

在25、26、27行中,我们可以看到Class对象引用的使用方法。实际上,Class对象引用有各种各样的get方法,例如

//getFields() 获取所有数据域
//getInterfaces() 获取实现的接口名
//getMethods() 获取所有的非构造函数
//getConstructors()获取构造函数
//getSuperclass()获取直接超类

 

下面介绍反射的调用规则。

在上面代码的30-41行,形象介绍了反射的使用方法。通过newInstance()生成对应类的对象引用,实际上这个方法的作用就类似于构造方法。同样,也通过getDeclaredField()方法修改静态域等。

在35到41行中,介绍了反射的普通方法调用,我分别举例了一个传参的和无参以及带返回值的方法,你可以类比其它。

以上是反射的核心部分内容,除了java编程思想,http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html这个博客对我理解以上内容有很大帮助。

 

关于反射的意义,反射的效率是低于普通构造方法的,但是它作为一种理念,即动态加载和自控制,是很值得学习的,同时,反射机制也是在处理大型程序编码中可能用到的,因为那个时候你对整个项目也许就不会了解的面面俱到了。

以上。

以上是关于基于反射的动态调用-不止是code和oop,还有类加载方案的主要内容,如果未能解决你的问题,请参考以下文章

java反射——构造方法

C#反射,性能优化,不止于优化

OOP1(定义基类和派生类)

设计模式之动态代理

http请求POST和GET调用接口以及反射动态调用Webservices类

Java -- 每日一问:动态代理是基于什么原理?