java反射深入剖析改变private方法的简单实例
classDemoTest{privateStringname="123";publicgetName(){system.out.println("publicgetName"+name);returnname;}privategetName2(){system.out.println("privategetName2"+name);returnname;}}
如这个,要改变name的值。如何改变。通过java反射如何改变
先来看看如何用反射来调用getName方法
classcalzz=DemoTest.class;Constructorcons=clazz.getConstructor(newclass[]{});//这个是得到class对象的构造方法。参数为构造方法的参数的类的对象。
如:DemoTest有一个构造方法为publicDemoTest(Stringarg0,Stringarg1){......}
这时,Constructorcons=clazz.getConstructor(newclass[]{String.class,String.class});两个String.calss分别对应arg0,arg1.
接下来是:
DemoTesttest=(DemoTest)cons.newInstance(newObject[]{});//生成相应的对象。参数newObject[]{}就是构造方法对应的参数的具体值。如刚才的:DemoTesttest=(DemoTest)cons.newInstance(newObject[]{“李车”,"张三"});
接下来:
Methodmethod=clazz.getMethod("getName",newClass[]{String.class});method.invoke(test,newObject[]{"王五"});
这时就可以打印出王五了。
那要如何把name为private的值进行改变呢。
1。先得到field,
Fieldfield=clazz.getDeclaredField("name");
2.将此对象的accessible标志设置为指示的布尔值。值为true则指示反射的对象在使用时应该取消Java语言访问检查。值为false则指示反射的对象应该实施Java语言访问检查。
field.setAccessible(true);
3.对变量值进行修改
field.set("name","王五");
OK;
要访问私有的方法也是一样。
Methodmethod=clazz.getDeclaredMethod("getName2");method.setAccessible(true);method.invoke(test,newObject[]{"王五"});
用getDeclaredFields就能得到private和publicprotendprotected的字段
如果用c.getFields();只能得到public类型的属性
我们知道,Java应用程序不能访问持久化类的private方法,但Hibernate没有这个限制,它能够访问各种级别的方法,如private,default,protected,public.Hibernate是如何实现该功能的呢?答案是利用JAVA的反射机制,如下:
importjava.lang.reflect.InvocationTargetException;importjava.lang.reflect.Method;publicclassReflectDemo{publicstaticvoidmain(String[]args)throwsException{Methodmethod=PackageClazz.class.getDeclaredMethod("privilegedMethod",newClass[]{String.class,String.class});method.setAccessible(true);method.invoke(newPackageClazz(),"452345234","q31234132");}}classPackageClazz{privatevoidprivilegedMethod(StringinvokerName,Stringadb){System.out.println("---"+invokerName+"----"+adb);}}
输出结果为:---452345234----q31234132
我们看到,利用java的反射机制,即使是private方法,也可以被调用使用.不过我到产生一个疑问--即然private方法都可以被调用,那么封装又有何意义?
【案例1】通过一个对象获得完整的包名和类名
packageReflect;/通过一个对象获得完整的包名和类名/classDemo{//othercodes...}classhello{publicstaticvoidmain(String[]args){Demodemo=newDemo();System.out.println(demo.getClass().getName());}}
【运行结果】:Reflect.Demo
添加一句:所有类的对象其实都是Class的实例。
【案例2】实例化Class类对象
packageReflect;classDemo{//othercodes...}classhello{publicstaticvoidmain(String[]args){Class>demo1=null;Class>demo2=null;Class>demo3=null;try{//一般尽量采用这种形式demo1=Class.forName("Reflect.Demo");}catch(Exceptione){e.printStackTrace();}demo2=newDemo().getClass();demo3=Demo.class;System.out.println("类名称"+demo1.getName());System.out.println("类名称"+demo2.getName());System.out.println("类名称"+demo3.getName());}}
【运行结果】:
类名称Reflect.Demo
类名称Reflect.Demo
类名称Reflect.Demo
【案例3】通过Class实例化其他类的对象
通过无参构造实例化对象
publicPerson(Stringname,intage){this.age=age;this.name=name;}
然后继续运行上面的程序,会出现:
所以大家以后再编写使用Class实例化其他类的对象的时候,一定要自己定义无参的构造函数
【案例】通过Class调用其他类中的构造函数(也可以通过这种方式通过Class创建其他类的对象)
packageReflect;importjava.lang.reflect.Constructor;classPerson{publicPerson(){}publicPerson(Stringname){this.name=name;}publicPerson(intage){this.age=age;}publicPerson(Stringname,intage){this.age=age;this.name=name;}publicStringgetName(){returnname;}publicintgetAge(){returnage;}@OverridepublicStringtoString(){return"["+this.name+""+this.age+"]";}privateStringname;privateintage;}classhello{publicstaticvoidmain(String[]args){Class>demo=null;try{demo=Class.forName("Reflect.Person");}catch(Exceptione){e.printStackTrace();}Personper1=null;Personper2=null;Personper3=null;Personper4=null;//取得全部的构造函数Constructor>cons[]=demo.getConstructors();try{per1=(Person)cons[0].newInstance();per2=(Person)cons[1].newInstance("Rollen");per3=(Person)cons[2].newInstance(20);per4=(Person)cons[3].newInstance("Rollen",20);}catch(Exceptione){e.printStackTrace();}System.out.println(per1);System.out.println(per2);System.out.println(per3);System.out.println(per4);}}
【运行结果】:
[null0]
[Rollen0]
[null20]
[Rollen20]
【案例】
返回一个类实现的接口:
packageReflect;interfaceChina{publicstaticfinalStringname="Rollen";publicstaticintage=20;publicvoidsayChina();publicvoidsayHello(Stringname,intage);}classPersonimplementsChina{publicPerson(){}publicPerson(Stringsex){this.sex=sex;}publicStringgetSex(){returnsex;}publicvoidsetSex(Stringsex){this.sex=sex;}@OverridepublicvoidsayChina(){System.out.println("hello,china");}@OverridepublicvoidsayHello(Stringname,intage){System.out.println(name+""+age);}privateStringsex;}classhello{publicstaticvoidmain(String[]args){Class>demo=null;try{demo=Class.forName("Reflect.Person");}catch(Exceptione){e.printStackTrace();}//保存所有的接口Class>intes[]=demo.getInterfaces();for(inti=0;i
【运行结果】:
实现的接口Reflect.China
(注意,以下几个例子,都会用到这个例子的Person类,所以为节省篇幅,此处不再粘贴Person的代码部分,只粘贴主类hello的代码)
【案例】:取得其他类中的父类
classhello{publicstaticvoidmain(String[]args){Class>demo=null;try{demo=Class.forName("Reflect.Person");}catch(Exceptione){e.printStackTrace();}//取得父类Class>temp=demo.getwww.hunanwang.netSuperclass();System.out.println("继承的父类为:"+temp.getName());}}
【运行结果】
继承的父类为:java.lang.Object
【案例】:获得其他类中的全部构造函数
这个例子需要在程序开头添加importjava.lang.reflect.;
然后将主类编写为:
classhello{publicstaticvoidmain(String[]args){Class>demo=null;try{demo=Class.forName("Reflect.Person");}catch(Exceptione){e.printStackTrace();}Constructor>cons[]=demo.getConstructors();for(inti=0;i
【运行结果】:
构造方法:publicReflect.Person()
构造方法:publicReflect.Person(java.lang.String)
但是细心的读者会发现,上面的构造函数没有public或者private这一类的修饰符
下面这个例子我们就来获取修饰符
classhello{publicstaticvoidmain(String[]args){Class>demo=null;try{demo=Class.forName("Reflect.Person");}catch(Exceptione){e.printStackTrace();}Constructor>cons[]=demo.getwww.visa158.comConstructors();for(inti=0;ip[]=cons[i].getParameterTypes();System.out.print("构造方法:");intmo=cons[i].getModifiers();System.out.print(Modifier.toString(mo)+"");System.out.print(cons[i].getName());System.out.print("(");for(intj=0;j
【运行结果】:
构造方法:publicReflect.Person(){}
构造方法:publicReflect.Person(java.lang.Stringarg1){}
有时候一个方法可能还有异常,呵呵。下面看看:
classhello{publicstaticvoidmain(String[]args){Class>demo=null;try{demo=Class.forName("Reflect.Person");}catch(Exceptione){e.printStackTrace();}Methodmethod[]=demo.getMethods();for(inti=0;ireturnType=method[i].getReturnType();Class>para[]=method[i].getParameterTypes();inttemp=method[i].getModifiers();System.out.print(Modifier.toString(temp)+"");System.out.print(returnType.getName()+"");System.out.print(method[i].getName()+"");System.out.print("(");for(intj=0;jexce[]=method[i].getExceptionTypes();if(exce.length>0){System.out.print(")throws");for(intk=0;k
【运行结果】:
【案例】接下来让我们取得其他类的全部属性吧,最后我讲这些整理在一起,也就是通过class取得一个类的全部框架
classhello{publicstaticvoidmain(String[]args){Class>demo=null;try{demo=Class.forName("Reflect.Person");}catch(Exceptione){e.printStackTrace();}System.out.println("===============本类属性========================");//取得本类的全部属性Field[]field=demo.getDeclaredFields();for(inti=0;itype=field[i].getType();System.out.println(priv+""+type.getName()+""+field[i].getName()+";");}System.out.println("===============实现的接口或者父类的属性========================");//取得实现的接口或者父类的属性Field[]filed1=demo.getFields();for(intj=0;jtype=filed1[j].getType();System.out.println(priv+""+type.getName()+""+filed1[j].getName()+";");}}}
【运行结果】:
===============本类属性========================
privatejava.lang.Stringsex;
===============实现的接口或者父类的属性========================
publicstaticfinaljava.lang.Stringname;
publicstaticfinalintage;
【案例】其实还可以通过反射调用其他类中的方法:
classhello{publicstaticvoidmain(String[]args){Class>demo=null;try{demo=Class.forName("Reflect.Person");}catch(Exceptione){e.printStackTrace();}try{//调用Person类中的sayChina方法Methodmethod=demo.getMethod("sayChina");method.invoke(demo.newInstance());//调用Person的sayHello方法method=demo.getMethod("sayHello",String.class,int.class);method.invoke(demo.newInstance(),"Rollen",20);}catch(Exceptione){e.printStackTrace();}}}
【运行结果】:
hello,china
Rollen20
【案例】调用其他类的set和get方法
classhello{publicstaticvoidmain(String[]args){Class>demo=null;Objectobj=null;try{demo=Class.forName("Reflect.Person");}catch(Exceptione){e.printStackTrace();}try{obj=demo.newInstance();}catch(Exceptione){e.printStackTrace();}setter(obj,"Sex","男",String.class);getter(obj,"Sex");}/@paramobj操作的对象@paramatt操作的属性/publicstaticvoidgetter(Objectobj,Stringatt){try{Methodmethod=obj.getClass().getMethod("get"+att);System.out.println(method.invoke(obj));}catch(Exceptione){e.printStackTrace();}}/@paramobj操作的对象@paramatt操作的属性@paramvalue设置的值@paramtype参数的属性/publicstaticvoidsetter(Objectobj,Stringatt,Objectvalue,Class>type){try{Methodmethod=obj.getClass().getMethod("set"+att,type);method.invoke(obj,value);}catch(Exceptione){e.printStackTrace();}}}//endclass
【运行结果】:
男
【案例】通过反射操作属性
classhello{publicstaticvoidmain(String[]args)throwsException{Class>demo=null;Objectobj=null;demo=Class.forName("Reflect.Person");obj=demo.newInstance();Fieldfield=demo.getDeclaredField("sex");field.setAccessible(true);field.set(obj,"男");System.out.println(field.get(obj));}}//endclass
【案例】通过反射取得并修改数组的信息:
importjava.lang.reflect.;classhello{publicstaticvoidmain(String[]args){int[]temp={1,2,3,4,5};Class>demo=temp.getClass().getComponentType();System.out.println("数组类型:"+demo.getName());System.out.println("数组长度"+Array.getLength(temp));System.out.println("数组的第一个元素:"+Array.get(temp,0));Array.set(temp,0,100);System.out.println("修改之后数组第一个元素为:"+Array.get(temp,0));}}
【运行结果】:
数组类型:int
数组长度5
数组的第一个元素:1
修改之后数组第一个元素为:100
【案例】通过反射修改数组大小
classhello{publicstaticvoidmain(String[]args){int[]temp={1,2,3,4,5,6,7,8,9};int[]newTemp=(int[])arrayInc(temp,15);print(newTemp);System.out.println("=====================");String[]atr={"a","b","c"};String[]str1=(String[])arrayInc(atr,8);print(str1);}/修改数组大小/publicstaticObjectarrayInc(Objectobj,intlen){Class>arr=obj.getClass().getComponentType();ObjectnewArr=Array.newInstance(arr,len);intco=Array.getLength(obj);System.arraycopy(obj,0,newArr,0,co);returnnewArr;}/打印/publicstaticvoidprint(Objectobj){Class>c=obj.getClass();if(!c.isArray()){return;}System.out.println("数组长度为:"+Array.getLength(obj));for(inti=0;i
【运行结果】:
数组长度为:15
123456789000000=====================
数组长度为:8
abcnullnullnullnullnull
动态代理
【案例】首先来看看如何获得类加载器:
classtest{}classhello{publicstaticvoidmain(String[]args){testt=newtest();System.out.println("类加载器"+t.getClass().getClassLoader().getClass().getName());}}
【程序输出】:
类加载器sun.misc.Launcher$AppClassLoader
其实在java中有三种类类加载器。
1)BootstrapClassLoader此加载器采用c++编写,一般开发中很少见。
2)ExtensionClassLoader用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类
3)AppClassLoader加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
如果想要完成动态代理,首先需要定义一个InvocationHandler接口的子类,已完成代理的具体操作。
packageReflect;importjava.lang.reflect.;//定义项目接口interfaceSubject{publicStringsay(Stringname,intage);}//定义真实项目classRealSubjectimplementsSubject{@OverridepublicStringsay(Stringname,intage){returnname+""+age;}}classMyInvocationHandlerimplementsInvocationHandler{privateObjectobj=null;publicObjectbind(Objectobj){this.obj=obj;returnProxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{Objecttemp=method.invoke(this.obj,args);returntemp;}}classhello{publicstaticvoidmain(String[]args){MyInvocationHandlerdemo=newMyInvocationHandler();Subjectsub=(Subject)demo.bind(newRealSubject());Stringinfo=sub.say("Rollen",20);System.out.println(info);}}
【运行结果】:
Rollen20
类的生命周期
在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载,链接,初始化这3个步骤完成。
类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。但是同一个类只会被类装载器装载以前
链接就是把二进制数据组装为可以运行的状态。
链接分为校验,准备,解析这3个阶段
校验一般用来确认此二进制文件是否适合当前的JVM(版本),
准备就是为静态成员分配内存空间,。并设置默认值
解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)
完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。
当没有任何引用指向Class对象时就会被卸载,结束类的生命周期
将反射用于工厂模式
先来看看,如果不用反射的时候,的工厂模式吧:
/@authorRollen-Holt设计模式之工厂模式/interfacefruit{publicabstractvoideat();}classAppleimplementsfruit{publicvoideat(){System.out.println("Apple");}}classOrangeimplementsfruit{publicvoideat(){System.out.println("Orange");}}//构造工厂类//也就是说以后如果我们在添加其他的实例的时候只需要修改工厂类就行了classFactory{publicstaticfruitgetInstance(StringfruitName){fruitf=null;if("Apple".equals(fruitName)){f=newApple();}if("Orange".equals(fruitName)){f=newOrange();}returnf;}}classhello{publicstaticvoidmain(String[]a){fruitf=Factory.getInstance("Orange");f.eat();}}
这样,当我们在添加一个子类的时候,就需要修改工厂类了。如果我们添加太多的子类的时候,改的就会很多。
现在我们看看利用反射机制:
packageReflect;interfacefruit{publicabstractvoideat();}classAppleimplementsfruit{publicvoideat(){System.out.println("Apple");}}classOrangeimplementsfruit{publicvoideat(){System.out.println("Orange");}}classFactory{publicstaticfruitgetInstance(StringClassName){fruitf=null;try{f=(fruit)Class.forName(ClassName).newInstance();}catch(Exceptione){e.printStackTrace();}returnf;}}classhello{publicstaticvoidmain(String[]a){fruitf=Factory.getInstance("Reflect.Apple");if(f!=null){f.eat();}}}
现在就算我们添加任意多个子类的时候,工厂类就不需要修改。
上面的爱吗虽然可以通过反射取得接口的实例,但是需要传入完整的包和类名。而且用户也无法知道一个接口有多少个可以使用的子类,所以我们通过属性文件的形式配置所需要的子类。
下面我们来看看:结合属性文件的工厂模式
首先创建一个fruit.properties的资源文件,
内容为:
apple=Reflect.Appleorange=Reflect.Orange
然后编写主类代码:
packageReflect;importjava.io.;importjava.util.;interfacefruit{publicabstractvoideat();}classAppleimplementsfruit{publicvoideat(){System.out.println("Apple");}}classOrangeimplementsfruit{publicvoideat(){System.out.println("Orange");}}//操作属性文件类classinit{publicstaticPropertiesgetPro()throwsFileNotFoundException,IOException{Propertiespro=newProperties();Filef=newFile("fruit.properties");if(f.exists()){pro.load(newFileInputStream(f));}else{pro.setProperty("apple","Reflect.Apple");pro.setProperty("orange","Reflect.Orange");pro.store(newFileOutputStream(f),"FRUITCLASS");}returnpro;}}classFactory{publicstaticfruitgetInstance(StringClassName){fruitf=null;try{f=(fruit)Class.forName(ClassName).newInstance();}catch(Exceptione){e.printStackTrace();}returnf;}}classhello{publicstaticvoidmain(String[]a)throwsFileNotFoundException,IOException{Propertiespro=init.getPro();fruitf=Factory.getInstance(pro.getProperty("apple"));if(f!=null){f.eat();}}}
【运行结果】:Apple
|
|