javareflection 反射机制改变私有private属性值.docx
《javareflection 反射机制改变私有private属性值.docx》由会员分享,可在线阅读,更多相关《javareflection 反射机制改变私有private属性值.docx(12页珍藏版)》请在冰豆网上搜索。
javareflection反射机制改变私有private属性值
java(reflection)反射机制改变私有(private)属性值
关键字:
javareflectionprivate
publicclassStudent{
privateintid=0;
publicintgetId(){
return id;
}
}
问题:
能否将ReadOnlyClass类的一个对象,把它的name属性的值由hello改为world?
如果能,请写出实现代码。
如果不能请说明理由。
答案:
可以。
利用java的反射
分析:
任何一个类,我们可以得到它运行时的Class实例,对于Student类,我们可以通过Student.class得到它运行时的Class实例,接着我们可以通过该类的Class实例去获得这个id属性所对应的Field对象,我们知道对应一个类的属性都一个和它相关的Field对象存在,对于构造方法来说有一个Constructor对象存在,对于一个方法来说有一个对应的Method对象存在。
通过这些我们可以利用反射来给这些属性动态的赋值。
首先我们看看JDKAPI中Class类的两个方法的描述:
publicFieldgetField(Stringname)throwsNoSuchFieldException,SecurityException
ReturnsaFieldobjectthatreflectsthespecifiedpublicmemberfieldoftheclassorinterfacerepresentedbythisClassobject.ThenameparameterisaStringspecifyingthesimplenameofthedesiredfield.Thefieldtobereflectedisdeterminedbythealgorithmthatfollows.LetCbetheclassrepresentedbythisobject:
IfCdeclaresapublicfieldwiththenamespecified,thatisthefieldtobereflected.Ifnofieldwasfoundinstep1above,thisalgorithmisappliedrecursivelytoeachdirectsuperinterfaceofC.Thedirectsuperinterfacesaresearchedintheordertheyweredeclared.Ifnofieldwasfoundinsteps1and2above,andChasasuperclassS,thenthisalgorithmisinvokedrecursivelyuponS.IfChasnosuperclass,thenaNoSuchFieldExceptionisthrown.(翻译:
返回一个Field对象,它返回此Class对象所表示的类或接口的指定public属性字段,id参数是一个int,用于指定所需字段的简称。
要反映的字段由下面的算法确定。
设C为次对象所表示的类:
如果C声明一个带有指定名的公共字段,则它就是要反映的字段。
如果在第1步中没有找到任何字段,则该算法被递归的应用与C的每一个直接超接口。
直接超接口按其声明的顺序进行搜索,如果在第1,2两步没有找到任何字段,且C有一个超类S,则在S上递归调用该算法,如果C没有超类则抛出NoSuchFieldException.)
publicFieldgetDeclaredField(Stringname)throwsNoSuchFieldException,SecurityException
ReturnsaFieldobjectthatreflectsthespecifieddeclaredfieldoftheclassorinterfacerepresentedbythisClassobject.ThenameparameterisaStringthatspecifiesthesimplenameofthedesiredfield.Notethatthismethodwillnotreflectthelengthfieldofanarrayclass.(翻译:
返回一个Field对象,该对象反映次Class对象所表示的类或接口的指定以声明字段,id参数是一个int,它指定所需字段的简称,次方法不反映数组类的length字段。
)
从上面的JDKAPI我们知道,要得到id属性对应的Filed对象,我们只能调用class的getDeclaredField方法,因为getField方法只能得到一个类的public的属性对应的Field对象,而这里的name属性是private的,我们通过Class的getDeclaredField方法得到id属性对应的Field对象后,我们就可以调用Filed对象的set方法给id赋值。
由于id是私有属性,我们要想知道我们到底改变了id的值没有,我们可以通过Filed类的父类的setAccessible(booleanflag)方法类限制java语言的访问限制。
对于setAccessible方法我们看JDK文档:
publicvoidsetAccessible(booleanflag)throwsSecurityException
Settheaccessibleflagforthisobjecttotheindicatedbooleanvalue.AvalueoftrueindicatesthatthereflectedobjectshouldsuppressJavalanguageaccesscheckingwhenitisused.AvalueoffalseindicatesthatthereflectedobjectshouldenforceJavalanguageaccesschecks.First,ifthereisasecuritymanager,itscheckPermissionmethodiscalledwithaReflectPermission("suppressAccessChecks")permission.ASecurityExceptionisraisedifflagistruebutaccessibilityofthisobjectmaynotbechanged(forexample,ifthiselementobjectisaConstructorobjectfortheclassClass).(翻译:
将此对象的accessible标志设置为指示的布尔值,值为true则指示反射的对象在使用时应该取消java语言访问检查,值为False则指示反射的对象应该事实java语言访问检查。
首先,如果存在安全管理器,则在ReflectPermission("suppressAccessChecks"))权限下调用checkPermission方法。
如果falg为true,并且不能更改次对象的可访问性(例如,如果次元素对象是Class类的Constructor对象),。
则会引发SecurityException。
如果次对象是java.lang.Class类的Constructor对象,并且flag为true,则会引发SecurityException
所以:
publicclassStudentRef{
publicstaticvoidmain(String[]args){
Studentstu=newStudent();
Class
> cla=Student.class;
Fieldfiled= cla.getDeclaredField("id");
field.setAccessible(true);
field.set(stu,2);
System.out.println(stu.getId());
}
}
总结:
对于一个类,它只有唯一个Class对象,它来标识这个对象,这个Class对象就能够获得这个类的结构上的特征。
那那么通过Class对象就可以来获得这个类相应的构造方法,属性等。
获得某一个类它的Class对应有4种方法:
*使用类的.class语法
*使用类的对象的getClass()方法,getClass()方法在Object类里定义的
*通过Class对象的forName()方法
*对于包装类,可以通过.TYPE方法
通过类的反射机制,我们可以改变只读的私有(private)属性值
Xukui:
packagecom;
publicclassStudent{
privateintid=0;
publicintgetId(){
returnid;
}
privatevoidsetId(Integerid){
this.id=id;
}
privatevoidsetId(){
this.id=100000;
}
}
packagecom;
importjava.lang.reflect.Field;
importjava.lang.reflect.Method;
publicclassStudentRefextendsStudent{
/**
*@paramargs
*@throwsException
*/
publicstaticvoidmain(String[]args)throwsException{
Studentstu=newStudent();
//Class
>cla=Student.class;
Classcla=Class.forName("com.Student");
Fieldfiled=cla.getDeclaredField("id");
filed.setAccessible(true);
filed.set(stu,2);
System.out.println(stu.getId());
Methodm=cla.getDeclaredMethod("setId",newClass[]{Class.forName("java.lang.Integer")});
m.setAccessible(true);
m.invoke(stu,100);
System.out.println(stu.getId());
m=cla.getDeclaredMethod("setId");
m.setAccessible(true);
m.invoke(stu);
System.out.println(stu.getId());
}
}
setAccessible()方法是否破坏了Java的访问规则呢?
收藏
看下面的代码:
publicclassA
{
privateintdata=0;
}
importjava.lang.reflect.*;
publicclassB
{
publicstaticvoidmain(String[]args)
{
Aa1=newA();
Field[]fields=a1.getClass().getDeclaredFields();
AccessibleObject.setAccessible(fields,true);
try
{
System.out.println(fields[0].toString()+"="+fields[0].get(a1));
fields[0].setInt(a1,150);
System.out.print(fields[0].toString()+"="+fields[0].get(a1));
}catch(IllegalAccessExceptionex1)
{
}catch(IllegalArgumentExceptionex1)
{
}
}
}
以上代码的输出结果为:
privateintreflectiontest.A.data=0
privateintreflectiontest.A.data=150
在这个过程中对象a1的private类型字段值被修改了,这是否算是破坏了Java的访问规则呢?
一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外,但有的时候,例如要序列化的时候,我们又必须有能力去处理这些字段,这时候,我们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问,而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在这些类上调用setAccessible()方法,我们可以实现对这些字段的操作。
但有的时候这将会成为一个安全隐患,为此,我们可以启用java.security.manager来判断程序是否具有调用setAccessible()的权限。
默认情况下,内核API和扩展目录的代码具有该权限,而类路径或通过URLClassLoader加载的应用程序不拥有此权限。
例如:
当我们以这种方式来执行上述程序时将会抛出异常
>java-Djava.security.managerExampleExplorer
Exceptioninthread"main"java.security.AccessControlException:
accessdenied(
java.lang.reflect.ReflectPermissionsuppressAccessChecks)
atjava.security.AccessControlContext.checkPermission(UnknownSource)
本文来自CSDN博客,转载请标明出处:
packagejavabean.newcredit.quota.myconcern.dao.impl;
importjavabean.domesticbiz.quota.concern.model.MyConcernDAO;
importjava.lang.reflect.Method;
importjava.util.*;
importjavabean.function.database.PageViewer;
importmon.log.LogUtils;
importsinosure.database.DatabaseException;
publicclassMyConcernExtendDaoextendsMyConcernDAO{
//日志记录对象
privateLogUtilslogger=newLogUtils(MyConcernDAO.class);
/**
*按给定的条件获得所有的我的关注记录
*@paramConnection对象,where条件
*@returnMyConcern的List对象
*@exceptionDatabaseExceptionifadatabaseaccesserroroccurs
*/
publicListgetMyConcernList(HashMapconditionMap)throwsDatabaseException{
logger.logDebug("执行getMyConcernList按给定的条件获得所有的用户登陆信息表记录!
");
ListmyConcernList=newArrayList();
try{
PageViewerMyConcernPageViewer=newPageViewer();
//Classc=Class.forName("javabean.domesticbiz.quota.concern.model.MyConcernDAO");
Classc=javabean.domesticbiz.quota.concern.model.MyConcernDAO.class;
//Objectobj=c.newInstance();
MyConcernDAOobj=newMyConcernDAO();
//这里是里设置属性的值
Method[]mm=c.getDeclaredMethods();
Methodm=c.getDeclaredMethod("getLoadMyConcernSelectSql");
//m.setAccessible(newObject[]{"getLoadMyConcernSelectSql","getLoadMyConcernFromSql","getLoadMyConcernWhereSql","getLoadInitialMyConcernWhereSql","getLoadMyConcernOrderBySql"},true);
//m.setAccessible(true);
StringselectSql="";//(String)m.invoke(obj);
m=c.getDeclaredMethod("getLoadMyConcernFromSql");
m.setAccessible(true);
StringfromSql=(String)m.invoke(obj,newObject[]{});
//StringselectSql=super.getLoadMyConcernSelectSql();
//StringfromSql=super.getLoadMyConcernFromSql();
StringwhereSql="";
ObjectuserId=conditionMap.get("userId");
if(null==userId){
m=c.getDeclaredMethod("getLoadMyConcernWhereSql",newClass[]{Class.forName("java.util.HashMap")});
m.setAccessible(true);
whereSql=(String)m.invoke(obj,conditionMap);
//whereSql=super.getLoadMyConcernWhereSql(conditionMap);
}else{
m=c.getDeclaredMethod("getLoadInitialMyConcernWhereSql",newClass[]{Class.forName("java.lang.String")});
m.setAccessible(true);
whereSql=(String)m.invoke(obj,newObject[]{(String)userId});
//whereSql=super.getLoadInitialMyConcernWhereSql((String)userId);
}
m=c.getDeclaredMethod("getLoadMyConcernOrderBySql");
m.setAccessible(true);
StringorderBySql=(String)m.invoke(obj,newObject[]{});
//StringorderBySql=super.getLoadMyConcernOrderBySql();
logger.logInfo("ExecutingSQL["+selectSql+"]");
logger.logInfo("ExecutingSQL["+fromSql+"]");
logger.logInfo("ExecutingSQL["+whereSql+"]");
MyConcernPageViewer.setSelectSql(selectSql);
MyConcernPageViewer.setFromSql(fromSql);
MyConcernPageViewer.setWhereSql(whereSql);
MyConcernPageViewer.setOrderBySql(orderBySql);
MyConcernPageViewer.setPageSize(5000);//最多输出5000条记录
MyConcernPageViewer.setPageNum
(1);
myConcernList=MyConcernPageViewer.query(PageViewer.WHOLE_ORDER);
logger.logDebug("按给定的条件获得所有的用户登陆信息表记录完成!
查询记录条数:
"+myConcernList.size());
returnmyConcernList;
}
catch(Exceptione){
//执行数据库操作异常
e.printStackTrace();
logger.logError("按给定的条件获得所有的我的关注记录失败!
",e);
thrownewDatabaseException("按