18.4.2 调用方法
当获得某个类对应的Class
对象后,就可以通过该Class
对象的getMethods()
方法或者getMethod()
方法来获取全部方法或指定方法—这两个方法的返回值是Method
数组,或者Method
对象。
每个Method
对象对应一个方法,获得Method
对象后,程序就可通过该Method
来调用它对应的方法。在Method
里包含一个invoke()
方法,该方法的签名如下:
Object invoke(Object object,Object.args)
:该方法中的object
是执行该方法的类的实例,后面的args
是执行该方法时传入该方法的实参。
实例 对象池工厂
下面程序对前面的对象池工厂进行加强,允许在配置文件中增加配置对象的成员变量的值,对象池工厂会读取为该对象配置的成员变量值,并利用该对象对应的setter
法设置成员变量的值。
ExtendedObjectPoolFactory.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| import java.util.*; import java.io.*; import java.lang.reflect.*; public class ExtendedObjectPoolFactory { private Map<String ,Object> objectPool = new HashMap<>(); private Properties config = new Properties(); public void init(String fileName) { try( FileInputStream fis = new FileInputStream(fileName)) { config.load(fis); } catch (IOException ex) { System.out.println("读取" + fileName + "异常"); } } private Object createObject(String clazzName) throws InstantiationException , IllegalAccessException , ClassNotFoundException { Class<?> clazz =Class.forName(clazzName); return clazz.newInstance(); } public void initPool()throws InstantiationException ,IllegalAccessException , ClassNotFoundException { for (String name : config.stringPropertyNames()) { if (!name.contains("%")) { objectPool.put(name , createObject(config.getProperty(name))); } } } public void initProperty()throws InvocationTargetException ,IllegalAccessException,NoSuchMethodException { for (String name : config.stringPropertyNames()) { if (name.contains("%")) { String[] objAndProp = name.split("%"); Object target = getObject(objAndProp[0]); String mtdName = "set" + objAndProp[1].substring(0 , 1).toUpperCase() + objAndProp[1].substring(1); Class<?> targetClass = target.getClass(); Method mtd = targetClass.getMethod(mtdName , String.class); mtd.invoke(target , config.getProperty(name)); } } } public Object getObject(String name) { return objectPool.get(name); } public static void main(String[] args) throws Exception { ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory(); epf.init("extObj.txt"); epf.initPool(); epf.initProperty(); System.out.println(epf.getObject("a")); } }
|
上面程序中initProperty()
方法里的第一行粗体字代码获取目标类中包含一个String
参数的setter
方法,第二行粗体字代码通过调用Method
的invoke()
方法来执行该setr
方法,该方法执行完成后,就相当于执行了目标对象的setter
方法。为上面程序提供如下配置文件:
extObj.txt
1 2 3 4
| a=javax.swing.JFrame b=javax.swing.JLabel #set the title of a a%title=Test Title
|
上面配置文件中的a%title
行表明希望调用a
对象的setTitle()
方法,调用该方法的参数值为Test Title
编译、运行上面的ExtendedObjectPoolFactory.java
程序,输出如下:
1
| javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=Test Title,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
|
可以看到输出一个JFrame
窗口,该窗口的标题为Test Title
.
应用
Spring
框架就是通过这种方式将成员变量值以及依赖对象等都放在配置文件中进行管理的,从而实现了较好的解耦。这也是Spring
框架的loC
的秘密。
setAccessible取消权限检查
当通过Method
的invoke()
方法来调用对应的方法时,Java
会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的private
方法,则可以先调用Method
对象的如下方法。
setAccessible(boolean flag)
:将Method
对象的accessible
设置为指定的布尔值。
- 如果值为
true
,指示该Method
在使用时取消Java
语言的访问权限检查;
- 如果值为
false
,则指示该Method
在使用时要实施Java
语言的访问权限检查
实际上,setAccessible()
方法并不属于Method
,而是属于它的父类AccessibleObject
因此Method
、Constructor
、Field
都可调用该方法,从而实现通过反射来调用private
方法、private
构造器和private
成员变量,下一节将会让读者看到这种示例。也就是说,它们可以通过调用setAccessible()
方法来取消访问权限检查,从而可以通过反射来访问private
成员。
原文链接: 18.4 使用反射生成并操作对象 18.4.2 调用方法