JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;
反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
Class反射机制
- 指的是可以于运行时加载,探知和使用编译期间完全未知的类.
- 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已经加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用他的任意一个方法和属性;
- 加载完类之后,在堆内存中会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,而且这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以被称之为:反射。
- 每个类被加载进入内存之后,系统就会为该类生成一个对应的java.lang.Class对象,通过该Class对象就可以访问到JVM中的这个类.
Class对象的获取
- 对象的getClass()方法;
- 类的.class(最安全/性能最好)属性;
- 运用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用).
package com.lwl.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 获取类的所有属性和方法及构造器
* @create 2017-10-11 上午11:42:52
* @version 1.0
*/
public class ReflectAllDemo {
/**
* 获取类的所有属性和方法及构造器
* @param args
* @throws ClassNotFoundException
* @create 2017-10-11 上午9:07:46
*/
public static void main(String[] args) throws ClassNotFoundException {
//根据一个类的全名称获取类的类对象
//如果类的全名称错误或者不存在,则会报类查询不到异常:ClassNotFoundException
Class<?> clazz = Class.forName("java.lang.String");
//获取传递过来的类的所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.toString());
}
System.out.println("------------------------------我是分隔符:以下是类的属性--------------------------------");
//获取传递过来的类的所有属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.toString());
}
System.out.println("------------------------------我是分隔符:以下是构造方法--------------------------------");
//获取传递过来的类的所有构造方法
Constructor<?>[] constructor = clazz.getDeclaredConstructors();
for (Constructor<?> ctt : constructor) {
System.out.println(ctt.toString());
}
}
}
创建对象
通过反射来生成对象的方式有两种:
- 使用Class对象的newInstance()方法来创建该Class对象对应类的实例(这种方式要求该Class对象的对应类有默认构造器).
- 先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例(通过这种方式可以选择指定的构造器来创建实例).
调用方法
当获取到某个类对应的Class对象之后,就可以通过该Class对象的getMethod来获取一个Method数组或Method对象.
每个Method对象对应一个方法,在获得Method对象之后,就可以通过调用invoke方法来调用该Method对象对应的方法.
package com.lwl.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 通过反射,对类中的方法进行操作
* @create 2017-10-11 上午11:45:57
* @version 1.0
*/
public class ReflectMethodDemo {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Class<?> clazz = Class.forName("com.lwl.reflect.Person");
//获取对象
Object instance = clazz.newInstance();
// instance = clazz.getDeclaredConstructor(null).newInstance(null);
//如果已经知道方法的名称,那么调用起来更加方便
Method method = clazz.getDeclaredMethod("setName", new Class[]{String.class});
//设置值
method.invoke(instance, new String[]{"jack"});
System.out.println(instance.toString());
//获取值
Method getMethod = clazz.getDeclaredMethod("getName", new Class[]{});
Object getValue = getMethod.invoke(instance, new String[]{});
System.out.println(getValue);
//对对象中的静态方法进行访问
Method staticMethod = clazz.getDeclaredMethod("printInfo", new Class[]{});
//由于是静态方法,所以这里面的 instance 可以不传,只用用 null 表示
Object staticValue = staticMethod.invoke(instance, new String[]{});
// staticValue = staticMethod.invoke(null, new String[]{});
System.out.println(staticValue);
}
}
访问成员变量
通过Class对象的的getField()方法可以获取该类所包含的全部或指定的成员变量Field,Filed提供了如下两组方法来读取和
设置成员变量值
.
- getXxx(Object obj): 获取obj对象的该成员变量的值, 此处的Xxx对应8中基本类型,如果该成员变量的类型是引用类型, 则取消get后面的Xxx;
- setXxx(Object obj, Xxx val): 将obj对象的该成员变量值设置成val值.此处的Xxx对应8种基本类型, 如果该成员类型是引用类型, 则取消set后面的Xxx;
package com.lwl.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 通过反射,对类中的字段进行操作
* @create 2017-10-11 上午11:45:20
* @version 1.0
*/
public class ReflectFieldDemo {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
//获取类对象
Class<?> clazz = Class.forName("com.lwl.reflect.Person");
//获取对象
Object instance = clazz.newInstance();
// instance = clazz.getDeclaredConstructor(null).newInstance(null);
//根据属性的名称 获取属性对象
Field field = clazz.getDeclaredField("name");
//获取属性的类型
Class<?> type = field.getType();
System.out.println(type); //out print class java.lang.String
//获取属性的名称
String name = field.getName();
System.out.println(name); //out print name
//由于name属性是private,如果不设置setAccessible是无法直接对其进行设置
//setAccessible是暴力破坏,如果是私有方法同理要设置
field.setAccessible(true);
field.set(instance, "张三");
System.out.println(instance.toString()); //out print Person [id=null, name=张三, address=null]
}
}
通过反射赋值
package com.lwl.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 通过反射,对类对象进行赋值操作
* @create 2017-10-11 上午11:44:58
* @version 1.0
*/
public class ReflectSetValueDemo {
/**
* 用一句话说明这个方法做什么
* @param args
* @throws ClassNotFoundException
* @throws SecurityException
* @throws NoSuchFieldException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InstantiationException
* @create 2017-10-11 上午9:41:51
*/
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//获取类的Class的三种方式
/*
* 1. Class.forName(类名称全称:包括包名)
* 2. 类.class 即:Person.class
* 3. 对象.getClass() new Person().getClass();
* */
Class<?> clazz = Class.forName("com.lwl.reflect.Person");
// clazz = new Person().getClass();
// clazz = Person.class;
//获取一个属性
Field field = clazz.getDeclaredField("address");
System.out.println(field.toString());
System.out.println("--------------------------------------------我是分隔符:以下是对方法进行操作--------------------------------------------------------");
//获取一个方法
Method method = clazz.getDeclaredMethod("getName", new Class[]{});
//传递的Class<?>的顺序要和方法的参数类型顺序保持一致,不然就无法匹配抛出异常:java.lang.NoSuchMethodException
Method method1 = clazz.getDeclaredMethod("setName", String.class);
//获取方法的名字
// String name = method1.getName();
Method method2 = clazz.getDeclaredMethod("setInfo", String.class,Integer.class);
//method1 与 method2 还可以使用另外一种方式获取
Method method3 = clazz.getDeclaredMethod("setName", new Class[]{String.class});
Method method4 = clazz.getDeclaredMethod("setInfo", new Class[]{String.class,Integer.class});
System.out.println(method.toString());
System.out.println(method1.toString());
System.out.println(method2.toString());
System.out.println(method3.toString());
System.out.println(method4.toString());
System.out.println("--------------------------------------------我是分隔符:以下是对构造器进行操作--------------------------------------------------------");
//因为默认构造器没有参数,使用可以传入null 或者 new Class[]{}
Constructor<?> cs = clazz.getDeclaredConstructor(new Class[]{});
//带参数的构造器
Constructor<?> cs2 = clazz.getDeclaredConstructor(new Class[]{Integer.class,String.class,String.class});
System.out.println(cs.toString());
System.out.println(cs2.toString());
System.out.println("--------------------------------------------我是分隔符:以下是对反射赋值进行操作--------------------------------------------------------");
//根据类的默认构造器 创建一个类对象
Object object = cs.newInstance(new Object[]{});
// object = cs.newInstance(null);
//根据类的带参数构造器,创建一个类对象
Object object2 = cs2.newInstance(new Object[]{1,"李四","南京"});
// object2 = cs2.newInstance(null);
System.out.println("原始的对象属性情况:"+object);
System.out.println("原始的对象属性情况:"+object2);
System.out.println("--------------------------------------------我是分隔符:以下是对反射赋值进行操作:设置开始--------------------------------------------------------");
//对属性进行设置值,先获取所有的方法
Method[] methods = clazz.getDeclaredMethods();
//获取类的所有属性
Field[] fields = clazz.getDeclaredFields();
for (Method m : methods) {
//获取方法的名称
String name = m.getName();
//如果名称是以set开头的,表示设置值
if(name.startsWith("set")){
//获取set之后的字符串,即属性的名称
String fieldName = name.substring(3);
//因为set之后的首个字符串是大写,所以要截取第一个字符串转化为小写,这里千万别把所有的字符串都转化为小写,防止出现UserName 被转化为username
fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
//这里会对类中的属性进行赋值
//由于Person里面一个特殊的setInfo的方法,且info不是类的属性,则不会进行赋值
setFieldValue(fieldName, fields, m, object);
}
}
System.out.println("赋值后的对象属性情况:"+object);
System.out.println("赋值后的对象属性情况:"+object2);
}
/**
* 给类属性赋值
* @param fieldName
* @param fields
* @param m
* @param object
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
* @create 2017-10-11 上午10:41:53
*/
public static void setFieldValue(String fieldName, Field[] fields, Method m, Object object) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
for (Field field : fields) {
if (field.getName().equals(fieldName)){
//获取字段的属性
Class<?> fieldClass = field.getType();
//如果是Integer 即知道这个是Person的ID
if(fieldClass == Integer.class){
//通过反射设置值:也就是通过object对象,调用当前的 m 方法,后面是 m 方法需要的参数
m.invoke(object, new Integer[]{1});
}
//因为同为String的有2个set方法,所有要通过属性名称判断到底是调用哪个
else if (fieldClass == String.class && "name".equals(fieldName)){
//通过反射设置值:也就是通过object对象,调用当前的 m 方法,后面是 m 方法需要的参数
m.invoke(object, new String[]{"jack"});
}
else if (fieldClass == String.class && "address".equals(fieldName)){
//通过反射设置值:也就是通过object对象,调用当前的 m 方法,后面是 m 方法需要的参数
m.invoke(object, new String[]{"南京"});
}
//不需要再次循环
return;
}
}
}
}
使用到的辅助类:
package com.lwl.reflect;
public class Person {
private Integer id;
private String name;
private String address;
public Person() {
}
public Person(Integer id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public void setInfo(String name,Integer id){
this.id = id;
this.name = name;
}
public static void printInfo(){
System.out.println("我是静态方法,我被调用了");
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", address=" + address
+ "]";
}
}