在运行状态中,对任意一个类(class文件),能够知道这个类的所有属性和方法,对任意一个对象都可以调用其属性和方法,这种动态获取信息以及调用对象的方法的功能称之为java反射机制。
好处:提高了程序扩展性;
类加载器如何工作的,参加文章:
http://blog.csdn.net/cdw8131197/article/details/67056507
准备类:
import java.util.Date;
public class UserTest {
private String name;
private int age;
@Deprecated
public Date time = new Date();
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 Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public UserTest(String name, int age, Date time) {
super();
this.name = name;
this.age = age;
this.time = time;
}
static{
System.out.println("UserTest的静态代码块");
}
public UserTest() {
super();
}
}
一、获取类字节码的方式
**方式一:**Object类中的getClass方法获取字节码;
缺点:必须明确类,并且创建类的对象;
方式二:使用类中的静态属性.class获取类字节码;
缺点:必须明确类,不利于扩展;
注意,有一点很有趣,当使用“.class”来创建Class对象的引用时,不会自动地初始化该class对象。我们知道为使用类而做的三个步骤是:加载、链接、初始化,初始化主要做的工作为:执行类构造器<clinit>
、static变量 、static{}语句被赋值等。所以当使用”.class”创建的对象的初始化被延迟到了对静态方法(构造器隐式地是静态的)或者非常数静态域首次引用时才执行。
常数静态域如:static final int staticFinal = 47;
非常数静态域如:static final int staticFinal = new Random(47);
方式三:使用Class类中的forName(类的全限定名称)方法;(推荐使用)
示例代码:
public class RefectMain {
public static void main(String[] args) throws Exception {
//方式一:Object类中的getClass方法获取字节码。
//缺点:必须明确类,并且创建类的对象
// UserTest ut = new UserTest();
// Class clazz_1 = ut.getClass();
System.out.println("--------1---------");
//方式二:使用类中的静态属性.class获取类字节码
//缺点:必须明确类,不利于扩展
Class clazz_2 = UserTest.class;
System.out.println("--------2---------");
//方式三:使用Class类中的forName(类的全限定名称)方法
Class clazz_3 = null;
clazz_3 = Class.forName("com.cn.oneday.UserTest");
System.out.println("--------3---------");
System.out.println(clazz_2==clazz_3);
}
}
执行结果:
——–1———
——–2———
UserTest的静态代码块
——–3———
true
从执行结果中可以看,当.class的时候没有执行UserTest中的静态代码块,当用class.forName的时候执行了静态代码块。之所以将方式一注释,是由于方式一在创建对象是就会加载类,初始化类,从而执行静态代码块,而我们知道,静态代码块只执行一次。这样就看不出.class会不会执行静态代码块。
二、利用反射机制创建对象
方式一:使用类的newInstance()方法
通过jdk api
Creates a new instance of the class represented by this 类 object. The class is instantiated as if by a new expression with an empty argument list. The class is initialized if it has not already been initialized.
可以看出:
newInstance()方法是使用了该类的无参构造函数,若是该类没有初始化则会进行初始化。即当使用”.class”方式获取的字节码,则在调用newInstance方法时,会进行初始化动作,即调用静态代码块;
示例代码:
Class clazz_2 = UserTest.class;
Object o2 = clazz_2.newInstance();
执行结果:
UserTest的静态代码块
UserTest的无参构造函数
若是类中无无参构造方法会怎样???运行程序报错如下:
Exception in thread “main” java.lang.InstantiationException: com.cn.oneday.UserTest
at java.lang.Class.newInstance(Class.java:427)
at com.cn.oneday.RefectMain.main(RefectMain.java:14)
Caused by: java.lang.NoSuchMethodException: com.cn.oneday.UserTest.()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
… 1 more
若是我们使用的类没有无参构造方法该怎么办???
查看jdkapi,发现以下方法:
getConstructor(类<?>... parameterTypes)
—获取该类的执行参数构造器,入参是可变参数,传递的是class类;返回参数:Constructor<T>
getConstructors()
获取类的所有构造函数,返回类型是构造函数数组Constructor<?>[]
类 Constructor<T>
中有方法newInstance(Object... initargs)
创建对象
示例代码:
import java.lang.reflect.Constructor;
import java.util.Date;
public class RefectMain {
public static void main(String[] args) throws Exception {
Class clazz_3 = Class.forName("com.cn.oneday.UserTest");
Constructor<UserTest> c = clazz_3.getConstructor(String.class,int.class,Date.class);
c.newInstance("小明",29,new Date(System.currentTimeMillis()));
}
}
运行结果:
UserTest的静态代码块
三、利用反射机制操作field
1、操作普通的public修饰的field,使用方法:
getField(String name) 获取某个成员变量,返回值:java.lang.reflect
类 Field
getFields() 获取所有的成员变量 返回值:Field数组
示例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Date;
public class RefectMain {
public static void main(String[] args) throws Exception {
Class clazz_3 = Class.forName("com.cn.oneday.UserTest");
Constructor<UserTest> c = clazz_3.getConstructor(String.class,int.class,Date.class);
UserTest ut = c.newInstance("小明",29,new Date(System.currentTimeMillis()));
Field f = clazz_3.getField("time");
System.out.println(f.get(ut));
}
}
执行结果:
UserTest的静态代码块
Wed Mar 29 22:30:48 CST 2017
可以用得到的Field对象get或者set field的值
此方法访问私用变量是报错:
Exception in thread “main” java.lang.NoSuchFieldException: xxx
at java.lang.Class.getField(Class.java:1703)
at com.cn.oneday.RefectMain.main(RefectMain.java:13)
2、或是访问私用变量则使用方法:
getDeclaredField
getDeclaredFields()
得到的Field,直接进行get set时报错:
Exception in thread “main”
java.lang.IllegalAccessException:
Class com.cn.oneday.RefectMain can not access a member of class com.cn.oneday.UserTest with modifiers “private”
我们要对得到的field,进行权限处理,语句:
f.setAccessible(true);
表示可以对filed进行操作
示例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Date;
public class RefectMain {
public static void main(String[] args) throws Exception {
Class clazz_3 = Class.forName("com.cn.oneday.UserTest");
Constructor<UserTest> c = clazz_3.getConstructor(String.class,int.class,Date.class);
UserTest ut = c.newInstance("小明",29,new Date(System.currentTimeMillis()));
Field f = clazz_3.getDeclaredField("age");
f.setAccessible(true);
System.out.println(f.get(ut));
}
}
执行结果:
UserTest的静态代码块
29
四、利用java反射机制操作method
同理有方法:
//获取指定名称和参数类型方法,无论方法是否为私有
getDeclaredMethod(String name, 类<?>... parameterTypes)
//获取所有方法,无论方法是否为私有,包括其父类方法
getDeclaredMethods()
//获取可以访问的指定名称和参数类型方法
getMethod(String name, 类<?>... parameterTypes)
//获取所有可以访问的方法,包括其父类方法
getMethods()
调用方法使用:
invoke(Object obj, Object… args)
Object obj—-调用那个对象的方法,传递的是那个对象。即newInstance得到的
Object…args —–可变参数,即方法需要的参数具体指。
调用私有方法之前需要设置权限:setAccessible(true);
更多方法参加JDK API
工作中使用案例–拷贝对象,用了反射和json转化方式:
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import com.alibaba.dubbo.common.utils.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
/**
*
* @ClassName: BeanCopy
* @Description: 实体类间相互转化工具
* @author admin
* @date Oct 11, 2016
*
*/
public class BeanCopy extends BeanUtils{
/**
*
* @Title: bean2AppBean
* @Description: 普通Bean对象转化为AppBean对象--效率不如反射高
* @param sourceObject
* @param targetClazz
* @return
* @throws InstantiationException
* @throws IllegalAccessException 参数
* @return E 返回类型
* @throws
*/
public static <E> E copyBeanByJson(Object sourceObject,Class<E> targetClazz) throws InstantiationException, IllegalAccessException{
E appBean = targetClazz.newInstance();
appBean = JSONObject.parseObject(JSON.toJSONString(sourceObject),targetClazz);
return appBean;
}
/**
*
* @Title: beans2ListAppBean
* @Description: beans转化为AppBean列表--效率不如反射高
* @param sourceObject
* @param targetClazz
* @return
* @throws InstantiationException
* @throws IllegalAccessException 参数
* @return List<E> 返回类型
* @throws
*/
@SuppressWarnings("unchecked")
public static <E> List<E> copyListByJson(Object sourceObject,Class<E> targetClazz) throws InstantiationException, IllegalAccessException{
if(null==sourceObject || !(sourceObject instanceof ArrayList<?>)){
return null;
}
List<?> sourceList = null;
if (sourceObject instanceof ArrayList<?>){
sourceList = (ArrayList<?>)sourceObject;
}
List<E> result = new ArrayList<E>();
result = JSONObject.parseObject(JSON.toJSONString(sourceList),result.getClass());
return result;
}
/**
*
* @Title: beans2ListAppBean
* @Description: beans转化为AppBean列表--效率不如反射高
* @param sourceObject
* @param targetClazz
* @return
* @throws InstantiationException
* @throws IllegalAccessException 参数
* @return List<E> 返回类型
* @throws
*/
public static <E> List<E> copyListOneByOneByJson(Object sourceObject,Class<E> targetClazz) throws InstantiationException, IllegalAccessException{
if(null==sourceObject || !(sourceObject instanceof ArrayList<?>)){
return null;
}
List<E> result = new ArrayList<E>();
List<?> sourceList = null;
if (sourceObject instanceof ArrayList<?>){
sourceList = (ArrayList<?>)sourceObject;
}
for(int i = 0;null!=sourceList && i < sourceList.size(); i++){
E appBean = targetClazz.newInstance();
JSONObject.parseObject(JSON.toJSONString(sourceList.get(i)),targetClazz);
result.add(appBean);
}
return result;
}
/**
*
*
* @Title: copyBeanByReflection
* @Description: 通过Java反射方式copy实例
* @param sourceObject
* @param targetClazz
* @return
* @throws InstantiationException
* @throws IllegalAccessException 参数
* @return E 返回类型
* @throws Exception
*/
public static <E> E copyBeanByReflection(Object sourceObject,Class<E> targetClazz) throws Exception{
E appBean = targetClazz.newInstance();
appBean = replaceProperties(sourceObject,appBean);
return appBean;
}
/**
*
* @Title: copyListOneByReflection
* @Description: java反射方式copy列表
* @param sourceObject
* @param targetClazz
* @return
* @throws InstantiationException
* @throws IllegalAccessException 参数
* @return List<E> 返回类型
* @throws Exception
*/
public static <E> List<E> copyListOneByReflection(Object sourceObject,Class<E> targetClazz) throws Exception{
if(null==sourceObject || !(sourceObject instanceof ArrayList<?>)){
return null;
}
List<E> result = new ArrayList<E>();
List<?> sourceList = null;
if (sourceObject instanceof ArrayList<?>){
sourceList = (ArrayList<?>)sourceObject;
}
for(int i = 0;null!=sourceList && i < sourceList.size(); i++){
E appBean = targetClazz.newInstance();
appBean = replaceProperties(sourceList.get(i),appBean);
result.add(appBean);
}
return result;
}
/**
*
* @Title: replaceProperties
* @Description: 描述:将源非默认值的数据复制到目标相应数据中
* @param source
* @param target 参数
* @return void 返回类型
* @throws
*/
@SuppressWarnings({"rawtypes"})
private static <E> E replaceProperties(Object source, E target) throws Exception{
if(null==source || null==target){
return null;
}
Class actualEditable = target.getClass();
// 获取copy的属性
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
for(int i = 0; i < targetPds.length; i++){
PropertyDescriptor targetPd = targetPds[i];
if(targetPd.getWriteMethod() != null){
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());// 从类中获取属性
if(sourcePd != null && sourcePd.getReadMethod() != null){
try{
Method readMethod = sourcePd.getReadMethod();
Class sourceClass = sourcePd.getPropertyType();
if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())){
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source, new Object[0]);
if(null != value && !(sourceClass.equals(int.class) && (int) value == 0)){
Method writeMethod = targetPd.getWriteMethod();
if(!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())){
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, new Object[] {value});
}
}catch (Exception ex){
throw new FatalBeanException("Could not copy properties from source to target", ex);
}
}
}
}
return target;
}
/**
*
* @Title: copyPropertiesAccept
* @Description: 描述:copy目标中允许的字段
* @param source
* @param target
* @param acceptStrs 参数
* @return void 返回类型
* @throws
*/
@SuppressWarnings("rawtypes")
public static void copyPropertiesAccept(Object source, Object target, String[] acceptStrs) throws Exception{
Assert.notNull(source, "copyPropertiesAccept source must not be null");
Assert.notNull(target, "copyPropertiesAccept target must not be null");
Class actualEditable = target.getClass();
// 获取copy的属性
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List acceptList = (acceptStrs != null) ? Arrays.asList(acceptStrs) : null;
for(int i = 0; i < targetPds.length; i++){
PropertyDescriptor targetPd = targetPds[i];
if(targetPd.getWriteMethod() != null){
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());// 从类中获取属性
if(sourcePd != null && sourcePd.getReadMethod() != null && acceptList.contains(targetPd.getName())){
try{
Method readMethod = sourcePd.getReadMethod();
if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())){
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source, new Object[0]);
Method writeMethod = targetPd.getWriteMethod();
if(!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())){
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, new Object[] {value});
}catch (Exception ex){
throw new FatalBeanException("Could not copy properties from source to target", ex);
}
}
}
}
}
/**
*
* @Title: getProperties
* @Description: 递归的方法获取属性值
* @param source
* @param propertiesName
* @return 参数
* @return Object 返回类型
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static Object getProperties(Object source, String propertiesName) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
Assert.notNull(source, "getProperties source must not be null");
Object value = null;
if (propertiesName.contains(".")){
String frontProperName = propertiesName.substring(0,propertiesName.indexOf("."));
String backProperName = propertiesName.substring(propertiesName.indexOf(".")+1);
value = getProperties(getProperties(source, frontProperName), backProperName);
}else {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), propertiesName);
if (sourcePd != null && sourcePd.getReadMethod() != null){
Method readMethod = sourcePd.getReadMethod();
if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())){
readMethod.setAccessible(true);
}
value = readMethod.invoke(source, new Object[0]);
}
}
return value;
}
}