反射的使用
Posted poorguy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反射的使用相关的知识,希望对你有一定的参考价值。
为什么要使用反射:
java的反射机制就是增加程序的灵活性,避免将程序写死到代码里,
例如:实例化一个 person()对象, 不使用反射, new person(); 如果想变成 实例化 其他类, 那么必须修改源代码,并重新编译。
使用反射:
class.forName("person").newInstance();
而且这个类描述可以写到配置文件中,如 **.xml, 这样如果想实例化其他类,
只要修改配置文件的"类描述"就可以了,不需要重新修改代码并编译。
上面提到了加载配置文件:(这里给出加载配置文件的方法)
可以使用Properties.load()来加载配置文件,加载Json或者xml
Properties properties = new Properties(); properties.load(new FileInputStream("xxx.xml")); properties.get("key");
正式进入反射:(下面的例子中均使用Person类做实例)
Person类
package cn.csuft.poorguy.reflet.text; public class Person { @Name(name = "name") private String name; @Name(name = "id") private int id; @Name(name = "address") private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
Name注解:
package cn.csuft.poorguy.reflet.text; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Name { String name() default ""; }
1.反射的介绍
反射机制是在[运行状态]中,(所谓运行状态中就是说在编译时程序是不知道加载内容的,在运行过程中根据配置文件得到加载信息的)
1.对于任意一个类,都能知道这个类的属性和方法
2.对于任意一个对象都能调用它的属性和方法
反射提供的功能:
在运行过程中判断任何一个对象所属的类
在运行时构造任意一个类和对象
在运行时判断一个类所具有的成员变量和方法
在运行时调用任何一个类的方法
生成动态代理
获取反射入口的方法有三种方法:
- Class clazz = 对象.getClass();
- Class clazz = 类名.class;
- Class clazz = Class.forName(类的全名);
- 三者的返回值都是Class类型
注意:使用反射的第一步必须是获取反射入口
Class clazz1 = Class.forName("cn.csuft.poorguy.reflet.text.Person"); Class clazz2 = person.getClass(); Class clazz3 = Person.class; System.out.println(clazz1.getName()); System.out.println(clazz2.getName()); System.out.println(clazz3.getName());
运行结果:
cn.csuft.poorguy.reflet.text.Person cn.csuft.poorguy.reflet.text.Person cn.csuft.poorguy.reflet.text.Person
动态获取方法和属性(方法,注解,属性...)
1.获取公共方法或属性:
Person person = new Person(); Class clazz2 = person.getClass(); Method []methods = clazz2.getMethods(); for (Method method : methods) { System.out.println(method.getName()); } System.out.println("======================>"); Field []fields = clazz2.getFields(); for (Field field : fields) { System.out.println(field.getName()); }
运行结果:
getAddress getName getId setName setId setAddress wait wait wait equals toString hashCode getClass notify notifyAll ======================>
分析:我们看上面的输出结果会发现其中没有输出字段的名字,这个原因其实很简单,由于字段都是private的,所以getMethods和GetFields方法遵循访问限定符的要求
所以private类型的字段没有办法显示出来.
同时我们可以发现,多出来很多例如:wait,toString方法,并不是我们定义的,其实这些是object父类中的,所以这两个方法是得到所有的公共的方法或字段(所以父类中的也会出现)
可以使用getDeclaredFields和getDeclaredMethods方法得到当前类的所有字段或方法,其中包含了访问限定符所限制的字段和方法
Person person = new Person(); Class clazz2 = person.getClass(); Method []methods = clazz2.getDeclaredMethods(); for (Method method : methods) { System.out.println(method.getName()); } System.out.println("======================>"); Field []fields = clazz2.getDeclaredFields(); for (Field field : fields) { System.out.println(field.getName()); }
运行结果:
getAddress getName getId setName setId setAddress ======================> name id address
分析:
我们可以看到使用getDeclared...得到的字段或者方法等,都是本类所自己定义的,包含了访问限定符所限制的
创建一个对象实例:
- clazz2.getConstructor().newInstance();
- clazz2.newInstance();
推荐使用第一种方法创建(构造方法),因为第二种方法在JDK1.9开始已经不推荐使用
Class clazz = Person.class; Person p = (Person)clazz.getConstructor().newInstance(); Method m = clazz.getMethod("setName",String.class); m.invoke(p,"PoorGuy"); System.out.println(p.getName());
运行结果:
PoorGuy
分析:
对上述代码中使用的方法做一个介绍
- getMethod:第一个参数是一个方法名,第二个参数是方法的参数类型,因为第二个是可变数组类型,所以可以根据实际情况填写,如果没有参数可以不写
- invoke:这个方法两个参数,第一个参数是一个对象,标识谁调用了我,第二个参数是参数值,也就是要传递的参数的值
- 也可以使用set直接为字段赋值,但是不推荐使用,违反了java封装原则,会使代码看上去比较乱.
常见的方法还有:
- getSuperClass:得到父类
- getInterfaces():得到所有的接口(公共的)
- getConstructors():获得所有的构造方法
- getFields():获得所有的字段(受访问限定符约束,包含父类中的字段)
- getDeclaredFields():获得一个类所有的字段(不受访问限定符约束,不包含父类中的字段)
- Field.setAccessible(true):属性类型为private,可以通过setAccessible(true)打开访问权限
- Method.setAccessible(true):将访问限定符临时打开
setAccessible(true)方法的使用:
- 可以打开访问限定符,讲private改成public
- 使用这个方法可以打开访问限定符,然后直接为私有属性或方法赋值
- 使用这个方法修改访问限定符的限制后,可以忽略泛型的检查
注意:这里只是说明可以这样使用,可以实现这样的功能,但是不推荐这样使用,因为破环了java的封装机制,会使得代码变的凌乱,所以没有给出这些代码的实现,推荐调用set和get方法操作字段.
通过反射获得注解:
Class clazz = Person.class; Annotation []ann1 = clazz.getDeclaredAnnotations(); System.out.println(ann1.length); System.out.println("=================="); Field []fields = clazz.getDeclaredFields(); for (Field field : fields) { Annotation []anno = field.getAnnotations(); System.out.println(anno[0]); }
运行结果:
0 ================== @cn.csuft.poorguy.reflet.text.Name(name=name) @cn.csuft.poorguy.reflet.text.Name(name=id) @cn.csuft.poorguy.reflet.text.Name(name=address)
注意:
在获得注解是有一些同学可能第一次发现没有得到注解,这里需要注意一些问题:
- 注意注解的生命周期:@Retention(RetentionPolicy.RUNTIME)代表运行时,这样反射才可以读取到,注解的知识将在其他博客中讲解
- 如果直接使用clazz.getAnnotations方法,得到的将是类的注解而不是字段和方法的,如果是字段要使用field.getAnnotations,方法使用method.getAnnotations这里需要注意一下
使用反射完成Json解析:
Address类:
public class Address { public String privince; public String city; public String building; public Address(String privince, String city, String building) { this.privince = privince; this.city = city; this.building = building; } public Address() { super(); // TODO Auto-generated constructor stub } @Override public String toString() { return "Address [privince=" + privince + ", city=" + city + ", building=" + building + "]"; } public String getPrivince() { return privince; } public void setPrivince(String privince) { this.privince = privince; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getBuilding() { return building; } public void setBuilding(String building) { this.building = building; } }
Person类:
public class Person { @MyElement private String name; private int age; private String sex; private ArrayList<String>friendList = new ArrayList<String>(); private Address address; public Person(String name, int age, String sex, ArrayList<String> friendList, Address address) { super(); this.name = name; this.age = age; this.sex = sex; this.friendList = friendList; this.address = address; } public Person() { super(); // TODO Auto-generated constructor stub } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public ArrayList<String> getFriendList() { return friendList; } public void setFriendList(ArrayList<String> friendList) { this.friendList = friendList; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public void addFriend(String name) { friendList.add(name); } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", sex=" + sex + ", friendList=" + friendList + ", address=" + address + "]"; } }
主方法:
public static void main(String[] args) { Person person = new Person(); person.setName("PoorGuy"); person.setAge(18); person.setSex("男"); person.addFriend("Bob"); person.addFriend("Tom"); person.addFriend("jick"); Address address = new Address("黑龙江", "牡丹江", "幸福小区"); person.setAddress(address); System.out.println(toJson(person)); }
toJson方法:
private static String toJson(Object o) { Class clazz = o.getClass(); StringBuilder s = new StringBuilder("{"); Method []method = clazz.getDeclaredMethods(); for (Method m : method) { if(m.getName().startsWith("get")) { try { Object value = m.invoke(o); String key = m.getName().substring(3).toLowerCase(); //判断是否为整型 if(value instanceof Integer) { s.append(String.format(""%s":%s,",key,value)); continue; } //判断是否为字符串类型 if(value instanceof String) { s.append(String.format(""%s":"%s",",key,value)); continue; } if(value instanceof Double) { s.append(String.format(""%s":%s,",key,value)); continue; } if(value instanceof Float) { s.append(String.format(""%s":%s,",key,value)); continue; } if(value instanceof Long) { s.append(String.format(""%s":%s,",key,value)); continue; } //判断是否为字符串数组 if(value instanceof List) { s.append(String.format(""%s":[",key)); List list = (List)value; for(int i=0;i<list.size();i++) { s.append(String.format(""%s",",list.get(i))); } if(s.toString().endsWith(",")) { s.deleteCharAt(s.length()-1); } s.append("],"); continue; } //判断是否为字符串数组 if(value instanceof String[]) { s.append(String.format(""%s":[",key)); String string[] = (String[])value; for(int i=0;i<string.length;i++) { s.append(String.format(""%s",",string)); } if(s.toString().endsWith(",")) { s.deleteCharAt(s.length()-1); } s.append("],"); continue; } if(value instanceof Object) { s.append(String.format(""%s":%s,",key,toJson(value))); continue; } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } } } //处理最后多余的逗号 if(s.toString().endsWith(",")) { s.deleteCharAt(s.length()-1); } s.append("}"); return s.toString(); }
运行结果:
{"address":{"building":"幸福小区","privince":"黑龙江","city":"牡丹江"},"name":"PoorGuy","friendlist":["Bob","Tom","jick"],"age":18,"sex":"男"}
注意:上述的toJson方法写的比较简单,由于时间原因没有写出所有的实现,只是写了一些简单的,便于大家对注解更加理解,只供学习使用,希望大家可以实现更多的功能,
希望大家多以指点.
反射的讲解就写这些把,希望大家可以学习到更多的知识.
以上是关于反射的使用的主要内容,如果未能解决你的问题,请参考以下文章