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 调用方法