反射的使用

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)

注意:

在获得注解是有一些同学可能第一次发现没有得到注解,这里需要注意一些问题:

  1. 注意注解的生命周期:@Retention(RetentionPolicy.RUNTIME)代表运行时,这样反射才可以读取到,注解的知识将在其他博客中讲解
  2. 如果直接使用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方法写的比较简单,由于时间原因没有写出所有的实现,只是写了一些简单的,便于大家对注解更加理解,只供学习使用,希望大家可以实现更多的功能,

  希望大家多以指点.

 

反射的讲解就写这些把,希望大家可以学习到更多的知识.

 

以上是关于反射的使用的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL片段着色器不照亮场景

反射机制

将 OpenGL 片段着色器设置为仅通过漫反射减少 vec4 色点的 RGB 值,而不是 alpha

反射机制入门

反射机制入门

Golang实践录:反射reflect的一些研究及代码汇总