Java-反射

什么是反射?

通过反射可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。
程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
 反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

举例:

我们打掉了敌人的一个侦察机,通过研究他的组成,我们画出了他的图纸,然后自己造出来了一个。这就叫反射。

先创建一个类用于测试

class Father{
	public void funFormA() {
		System.out.println("A类的方法");
	}
}

class Child extends Father{
	String name;
	int age;
	
	
	public Child(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
	private int fun(int a) {
		return a;
	}
	
	public void fun1() {
		System.out.println("fun1");
	}
	
	public void fun2() {
		System.out.println("fun2");
	}
	
	public void fun3() {
		System.out.println("fun3");
	}
}

反射的相关操作:

获取Child类的3种方法:

Class<?> cls = Child.class;
		
Child c = new Child();
Class<?> clz = c.getClass();
		
Class<?> clz1 = Class.forName("类的全限定名");

将Class 对象转换成我们需要的类的两种方法:

1.通过cls.newInstance();注意此种方法,转换的类必须要有无参构造。因为他默认只能调用无参构造方法。

Child per = (Child) cls.newInstance();

2.通过constructor.newInstance();Constructor是一个专用于描述构造器的类。通过他的getConstructor方法可以获取任意个参数的构造方法。

Constructor<?> constructor = cls.getConstructor(String.class, int.class);
Child c = (Child)constructor.newInstance("Tom", 5);

获取类的方法的四种方法:

Method类用来描述一个方法对象。

使用 method.invoke("调用此方法的对象名称", 参数的值,参数的值)来调用方法;

当我们获得了Class对象之后,有以下几种方法供我们来获取一个类中的方法。

// int.class 是函数的参数类型,没有参数可以不写。

Method m = cls.getDeclaredMethod("functionName", int.class);

Method m = cls.getMethod("functionName", int.class);无法获取私有方法,先在本类找,找不到再往父类找。

cls.getDeclaredMethod("functionName", int.class);可以获取到私有方法但是无法调用。

cls.getMethods();获取子类和父类所有public修饰的普通方法

cls.getDeclaredMethods();获取子类的所有的普通方法。

使用getMethods的执行结果:

Method[] methods = cls.getMethods();
for (Method method : methods) {
	if(method != null) {
		System.out.println(method.getName());
		System.out.println("--------------");
	}
}
执行结果:

toString
--------------
fun2
--------------
funFormFather
--------------
wait
--------------
wait
--------------
wait
--------------
equals
--------------
hashCode
--------------
getClass
--------------
notify
--------------
notifyAll
--------------

可以看到getMethods方法获取了自己和父类所有的public方法。

cls.getDeclaredMethods(); 获取当前类所有声明的普通方法。只是获取方法对象,但是无法调用private修饰的方法。

反射中Class类的作用:

Class对象用来描述一个类的信息。

执行Person p = null; 这条语句时,JVM就会创建一个Class的对象用来描述这个类的所有信息。而且每一个类都对应一个唯一的Class对象。所以通过cls.getXXX方法我们就可以获得类的任何信息。

Constructor类的作用:

Constructor类是专用于描述类的构造方法的。通过使用cls.getConstructor(Class<?>...args)等方法就可以获得类的构造方法。

Method类的作用:

Method类是专用于描述类的所有方法的。通过使用cls.getMethod()等方法获取类的方法。

Field类的作用:

Field类是用于描述类的所有属性的。通过使用cls.getField等方法来获取类的属性。使用setField来设置类的属性。

有无Declared的区别:

getDeclaredMethods和getDeclaredFields获得所有本类的。私有的也会获取

getMethods和getFields获得本类和父类所有public修饰的。

getMethod和getField获得本类和父类所有public属性和方法(先在本类找,再去父类找)。

getDeclaredMethod和getDeclaredField获得本类所有方法。


反射的应用:

将一个对象的所有String类型的属性值中包含"a"的替换成"b":

/**
 * POJO类
 * @author Z7M-SL7D2
 *
 */
public class Person {
	private String name;
	private int age;
	private String summery;
	
	public Person(String name, int age, String summery) {
		super();
		this.name = name;
		this.age = age;
		this.summery = summery;
	}
	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 getSummery() {
		return summery;
	}
	public void setSummery(String summery) {
		this.summery = summery;
	}
	
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", summery=" + summery + "]";
	}
}

Person p = new Person("tomas", 20, "tomas is a student, he like playing basketball");

转换前:Person [name=Tomas, age=20, summery=Tomas is a student, he like playing basketball]

转换后:Person [name=Tombs, age=20, summery=Tombs is b student, he like plbying bbsketbbll]


首先获取Person类的Class对象(用来描述Person类的对象,在JVM中只存在一个这样的对象)。

然后通过这个Class对象获取成员变量。判断其类型,使用field.get()方法获取其值,然后调用replaceAll将a替换成b。

使用field.set()方法将替换后的值赋值给对象的相应字段。

完整代码:

import java.lang.reflect.Field;

public class Test {
	public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
		Person p = new Person("Tomas", 20, "Tomas is a student, he like playing basketball");
		System.out.println(p.toString());
		Class<?> cls = p.getClass();
		Field[] fields = cls.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			if(fields[i].getType() == String.class) {
				fields[i].setAccessible(true);
				String newValue = ((String)fields[i].get(p)).replaceAll("a", "b");
				fields[i].set(p, newValue);
			}
		}
		
		System.out.println(p.toString());
	}
}

输出:



使用反射重写简单工厂模式:

使用反射之前:每次新增类都要修改getInstance的代码。

public IFruit getInstance(String title){
	if(title == null)
		return null;
	if("apple".equals(title))
		return new Apple();
	else if("banana".equals(title))
		return new Banana();
	return null;
}

使用反射之后:一劳永逸,不用修改代码。

Factory类:

public class Factory {
	
	// 取得IFruit实例
	public IFruit getInstance(String title) throws Exception {
		String className = toQualifiedName(title);
		if(className == null)
			return null;
		Class<?> cls = Class.forName(className);
		return (IFruit) cls.newInstance();
	}
	
	// 获得类的全限定名
	public String toQualifiedName(String title) {
		if("".equals(title))
			return null;
		return "反射与工厂模式."+title.substring(0, 1).toUpperCase()+title.substring(1);
	}
}


反射与POJO(普通Java类):

给POJO类属性字段赋值的几种方法:

1.构造函数

    形式:Person p = new Person("tom", 20, "student", ........);当有很多字段的时候赋值就变得很麻烦。

2.set方法

    不够简单。当修改多个属性字段时会调用很多set方法

以上两种方法的缺点是:对字段的修改和初始化都很麻烦。

所以我们使用字符串存储key:value,key:value的形式来修改或者赋值类的字段,

非常简单而且可读性很好:

emp.id:1001,emp.name:tom,emp.age:20,emp.salary:199999

首先定义一个POJO类:

package 反射与POJO类;
// POJO类
public class Person {
	private int id;
	private String name;
	private int age;
	private float salary;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	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 float getSalary() {
		return salary;
	}
	public void setSalary(float salary) {
		this.salary = salary;
	}
	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
	}
}
现在我们要使用  "person.id:1001,person.name:tom,person.age:20,person.salary:199999" 这个字符串来初始化这个类。

然后调用toString方法检验是否初始化成功。

要使用字符串初始化这个类,首先想到的就是将字符串拆分。通过使用反射获取到类的成员变量,然后给其赋值。

但是POJO类只能有除get,set和一些简单的计算方法。所以新建了一个BeanOperation来操作。


BeanOperation类:用来将传入的字符串进行拆分,并复制给每一个对象的属性。

PersonAction是用来产生Person对象的,通过构造函数PersonAction(String value)传入用于赋值或者修改的字符串。

PersonAction类代码如下:

public class PersonAction {
	private Person person = new Person();
	
	public PersonAction(String value) throws Exception {
		BeanOperation.setValue(this, value);
	}
	
	public Person getPerson() {
		return person;
	}
}

BeanOpetation的setValue方法通过传入的this对象获取PersonAction类,然后获取到Person成员变量。

然后給获取到的成员变量(Person对象)的相应字段赋值。

Test类代码如下:

public class Test {
	public static void main(String[] args) throws Exception {
		PersonAction personAction = new PersonAction("person.id:1001,person.name:tom,person.age:20,person.salary:199999");
		Person person= personAction.getPerson();
		System.out.println(person);
	}
}

BeanOperation类代码:

package 反射与POJO类;

import java.lang.reflect.Method;

/**
 * 通过字符串初始化传入的类
 * 字符串格式"key:value,key:value"
 * @author Z7M-SL7D2
 */
public class BeanOperation {
	/**
	 * 拆分字符串
	 * @param objAction
	 * @param value
	 * @throws Exception
	 */
	public static void setValue(Object objAction, String value) throws Exception {
		if(value == null)
			return;
		String[] values = value.split(",");
		int count = values.length;
		for(int i = 0; i < count; i++) {
			
			// 获取值
			String fieldData = values[i].split("\\:")[1];
			
			// 获取类名
			String clsName = values[i].split("\\.")[0];
			
			// 获取成员变量名称
			String fieldName = values[i].split("\\:")[0].split("\\.")[1];
			
			// 通过传入的PersonAction对象获取其成员变量(Person对象)realObject,用来调用其set方法。
			Object realObject = getRealObject(objAction, clsName);
			
			String setMethod = "set" + castFirst(fieldName);
			
			Method method = null;
			// 判断成员变量的类型。
			Class<?> cls = realObject.getClass();
			if((cls.getDeclaredField(fieldName)).getType() == int.class) {
				method = cls.getDeclaredMethod(setMethod, int.class);
				method.invoke(realObject, Integer.parseInt(fieldData));
			}else if(cls.getDeclaredField(fieldName).getType() == String.class) {
				method = cls.getDeclaredMethod(setMethod, String.class);
				method.invoke(realObject, fieldData);
			}else if(cls.getDeclaredField(fieldName).getType() == double.class) {
				method = cls.getDeclaredMethod(setMethod, double.class);
				method.invoke(realObject, Double.parseDouble(fieldData));
			}else if(cls.getDeclaredField(fieldName).getType() == float.class) {
				method = cls.getDeclaredMethod(setMethod, float.class);
				method.invoke(realObject, Float.parseFloat(fieldData));
			}
			
		}
	}
	
	/**
	 * 首字母大写
	 * @param str
	 * @return
	 */
	public static String castFirst(String str) {
		return str.substring(0, 1).toUpperCase() + str.substring(1);
	}
	
	/**
	 * @param objAction
	 * @param className 是EmpAction的成员变量
	 * @return 
	 * @throws Exception 
	 */
	public static Object getRealObject(Object objAction, String className) throws Exception{
		Class<?> fieldCls = objAction.getClass();
		Method getMethod = fieldCls.getMethod("getPerson");
		return getMethod.invoke(objAction);
	}
}

运行结果:







  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值