为什么要使用反射:
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方法写的比较简单,由于时间原因没有写出所有的实现,只是写了一些简单的,便于大家对注解更加理解,只供学习使用,希望大家可以实现更多的功能,
希望大家多以指点.
反射的讲解就写这些把,希望大家可以学习到更多的知识.