欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java高级篇(四)——反射

程序员文章站 2022-06-08 20:21:30
之前写到了设计模式的代理模式,因为下一篇动态代理等内容需要用到反射的知识,所以在之前Java篇的基础上再写一篇有关反射的内容,还是以实际的程序为主,了解反射是做什么的、应该怎么用。 一、什么是反射 反射就是把Java类中的各个成分映射成一个个的Java对象。即在运行状态中,对于任意一个类,都能够知道 ......

  之前写到了设计模式的代理模式,因为下一篇动态代理等内容需要用到反射的知识,所以在之前java篇的基础上再写一篇有关反射的内容,还是以实际的程序为主,了解反射是做什么的、应该怎么用。

一、什么是反射

  反射就是把java类中的各个成分映射成一个个的java对象。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫java的反射机制。

  1. 反射机制的功能

  java反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法。
  • 生成动态代理。

  2. 实现反射机制的类

   java中主要由以下的类来实现java反射机制(这些类都位于java.lang.reflect包中):

  • class类:代表一个类。 field类:代表类的成员变量(成员变量也称为类的属性)。

  • method类:代表类的方法。

  • constructor类:代表类的构造方法。

  • array类:提供了动态创建数组,以及访问数组的元素的静态方法。

二、反射的使用

  下面分步说明以下如何通过反射获取我们需要的内容。

  我们先随意写一个customer类(就是一个po类),然后看看如何通过反射对这个类进行操作。

  1. customer类

 1 public class customer {
 2     
 3     private long id;
 4     private string name;
 5     private int age;
 6       
 7     public customer() {}
 8     
 9     public customer(string name,int age) {
10         this.name = name;
11         this.age = age;
12     }
13       
14     public long getid() {
15         return id;
16     }
17     public void setid(long id) {
18         this.id=id;
19     }
20     public string getname() {
21         return name;
22     }
23     public void setname(string name) {
24         this.name=name;
25     }
26     public int getage() {
27         return age;
28     }
29     public void setage(int age) {
30         this.age=age;
31     }
32 
33 }

   2. reflecttester类

  这个类用来演示reflection api的基本使用方法。这里自定义的copy方法是用来创建一个和参数objcet同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将其返回。

 1 import java.lang.reflect.field;
 2 import java.lang.reflect.method;
 3 
 4 public class reflecttester {
 5     
 6     public object copy(object object) throws exception{
 7         //获得对象的类型
 8         class classtype=object.getclass();
 9         system.out.println("class:"+classtype.getname());
10 
11         //通过默认构造方法创建一个新的对象
12         object objectcopy=classtype.getconstructor(new class[]{}).newinstance(new object[]{});
13 
14         //获得对象的所有属性
15         field fields[]=classtype.getdeclaredfields();
16 
17         for(int i=0; i<fields.length;i++){
18               field field=fields[i];
19 
20               string fieldname=field.getname();
21               string firstletter=fieldname.substring(0,1).touppercase();
22               //获得和属性对应的getxxx()方法的名字
23               string getmethodname="get"+firstletter+fieldname.substring(1);
24               //获得和属性对应的setxxx()方法的名字
25               string setmethodname="set"+firstletter+fieldname.substring(1);
26 
27               //获得和属性对应的getxxx()方法
28               method getmethod=classtype.getmethod(getmethodname,new class[]{});
29               //获得和属性对应的setxxx()方法
30               method setmethod=classtype.getmethod(setmethodname,new class[]{field.gettype()});
31 
32               //调用原对象的getxxx()方法
33               object value=getmethod.invoke(object,new object[]{});
34               system.out.println(fieldname+":"+value);
35               //调用拷贝对象的setxxx()方法
36              setmethod.invoke(objectcopy,new object[]{value});
37         }
38         return objectcopy;
39      }
40 
41 }

  下面分析一下上述代码。

  首先,通过object类中的getclass()方法获取对象的类型。

class classtype=object.getclass();

  而class类是reflection api中的核心类,主要方法如下:

  • getname():获得类的完整名字。 getfields():获得类的public类型的属性。

  • getdeclaredfields():获得类的所有属性。

  • getmethods():获得类的public类型的方法。

  • getdeclaredmethods():获得类的所有方法。

  • getmethod(string name, class[] parametertypes):获得类的特定方法,name参数指定方法的名字,parametertypes参数指定方法的参数类型。

  • getconstrutors():获得类的public类型的构造方法。

  • getconstrutor(class[] parametertypes):获得类的特定构造方法,parametertypes参数指定构造方法的参数类型。

  • newinstance():通过类的不带参数的构造方法创建这个类的一个对象。

  第二步,通过默认构造方法创建一个新的对象,即先调用class类的getconstructor()方法获得一个constructor对象,它代表默认的构造方法,然后调用constructor对象的newinstance()方法构造一个实例。

object objectcopy=classtype.getconstructor(new class[]{}).newinstance(new object[]{});

  第三步,获得对象的所有属性,即通过class类的getdeclaredfields()方法返回类的所有属性,包括public、protected、default和private访问级别的属性,

field fields[]=classtype.getdeclaredfields();

  第四步,获得每个属性相应的get/set方法,然后执行这些方法,把原来的对象属性拷贝到新的对象中。

  这里我们可以写一个invoketester的类,然后运用反射机制调用一个invoketester对象的add()方法(自定义方法),如add()方法的两个参数为int类型,那么获取表示add()方法的method对象代码如下:

method addmethod=classtype.getmethod("add",new class[]{int.class,int.class});

   上述代码中也有用到method的invoke方法,其接收参数必须为对象,如果参数为基本数据类型,必须转换为相应的包装类型的对象,如int要转换为integer。而invoke方法的返回值总是对象,如果实际被调用的方法的返回类型是基本数据类型,那么invoke方法会将其转换为相应的包装类型的对象,再将其返回。

  下面简单测试一下,具体的方法调用如上面提到的add方法,可自行编写(具体实例见下篇):

1 public static void main(string[] args) throws exception {
2   customer customer = new customer();
3   customer.setid(10l);
4   customer.setname("adam");
5   customer.setage(3);
6         
7   new reflecttester().copy(customer);
8 }   

   运行结果如下:

  Java高级篇(四)——反射

三、具体实例

  下面我们尝试着通过反射机制对一个jar包中的类进行分析,把类中所有的属性和方法提取出来,并写入到一个文件里中。

  目录结构如下:

  Java高级篇(四)——反射

  1. reflexdemo类

  主要代码部分,通过反射获取类、属性及方法。

 1 import java.io.file;
 2 import java.lang.reflect.field;
 3 import java.lang.reflect.method;
 4 import java.net.url;
 5 import java.net.urlclassloader;
 6 import java.util.enumeration;
 7 import java.util.jar.jarentry;
 8 import java.util.jar.jarfile;
 9 
10 /**
11  * @classname: reflexdemo
12  * @description: 通过反射获取类、属性及方法
13  * @author adamjwh
14  * @date 2018年5月28日
15  *
16  */
17 public class reflexdemo {
18 
19     private static stringbuffer sbuffer;
20     
21     public static void getjar(string jar) throws exception {
22         try {
23             file file = new file(jar);
24             url url = file.touri().tourl();
25             urlclassloader classloader = new urlclassloader(new url[] { url },
26                     thread.currentthread().getcontextclassloader());
27 
28             jarfile jarfile = new jarfile(jar);
29             enumeration<jarentry> enumeration = jarfile.entries();
30             jarentry jarentry;
31             
32             sbuffer = new stringbuffer();    //存数据
33 
34             while (enumeration.hasmoreelements()) {
35                 jarentry = enumeration.nextelement();
36 
37                 if (jarentry.getname().indexof("meta-inf") < 0) {
38                     string classfullname = jarentry.getname();
39                     if (classfullname.indexof(".class") < 0) {
40                         classfullname = classfullname.substring(0, classfullname.length() - 1);
41                     } else {
42                         // 去除后缀.class,获得类名
43                         string classname = classfullname.substring(0, classfullname.length() - 6).replace("/", ".");
44                         class<?> myclass = classloader.loadclass(classname);
45                         sbuffer.append("类名\t:" + classname);
46                         system.out.println("类名\t:" + classname);
47 
48                         // 获得属性名
49                         class<?> clazz = class.forname(classname);
50                         field[] fields = clazz.getdeclaredfields();
51                         for (field field : fields) {
52                             sbuffer.append("属性名\t:" + field.getname() + "\n");
53                             system.out.println("属性名\t:" + field.getname());
54                             sbuffer.append("-属性类型\t:" + field.gettype() + "\n");
55                             system.out.println("-属性类型\t:" + field.gettype());
56                         }
57 
58                         // 获得方法名
59                         method[] methods = myclass.getmethods();
60                         for (method method : methods) {
61                             if (method.tostring().indexof(classname) > 0) {
62                                 sbuffer.append("方法名\t:" + method.tostring().substring(method.tostring().indexof(classname)) + "\n");
63                                 system.out.println("方法名\t:" + method.tostring().substring(method.tostring().indexof(classname)));
64                             }
65                         }
66                         sbuffer.append("--------------------------------------------------------------------------------" + "\n");
67                         system.out.println("--------------------------------------------------------------------------------");
68                     }
69                 }
70             }
71         } catch (exception e) {
72             e.printstacktrace();
73         } finally {
74             sbuffer.append("end");
75             system.out.println("end");
76             
77             writefile.write(sbuffer);    //写文件
78         }
79     }
80 
81 }

  2. writefile类

  进行写文件操作。

 1 import java.io.bufferedwriter;
 2 import java.io.file;
 3 import java.io.filewriter;
 4 
 5 /**
 6  * @classname: writefile
 7  * @description: 写文件操作
 8  * @author adamjwh
 9  * @date 2018年5月28日
10  *
11  */
12 public class writefile {
13 
14     private static string pathname = "src/com/adamjwh/jnp/ex14/out.txt";
15     
16     public static void write(stringbuffer sbuffer) throws exception {
17         file file = new file(pathname);
18         bufferedwriter bw = new bufferedwriter(new filewriter(file));
19         
20         bw.write(sbuffer.tostring());
21         bw.close();
22     }
23     
24 }

  3. main类

  这里我们需要在项目下新建一个lib文件夹,然后将要解析的jar包放入其中,比如这里我们放入jdk的dt.jar。目录结构如下:

  Java高级篇(四)——反射

  执行程序:

 1 /**
 2  * @classname: main
 3  * @description: 
 4  * @author adamjwh
 5  * @date 2018年5月28日
 6  *
 7  */
 8 public class main {
 9     
10     private static string jar = "lib/dt.jar";
11     
12     public static void main(string[] args) throws exception {
13         reflexdemo.getjar(jar);
14     }
15 
16 }

  运行结果如下:

  Java高级篇(四)——反射