打包后的工具类 God+BlueJ+ClassLoader
来源:互联网 发布:twitter第三方登录js 编辑:程序博客网 时间:2024/06/05 03:20
工具类 God从开始的一个类,变成了使用策略模式的几个类;从项目中的一部分,变成了一个单独jar;
打包的yqj2065.jar在NetBeans环境中运行良好,有一天我在BlueJ中使用该yqj2065.jar,总是抛出NullPointerException。
我知道是路径问题,但是为什么NetBeans环境可以但是BlueJ中不行?虽然我一般用NetBeans,这个问题总在心里放着,真TM不舒服,而且一下两下还搞不定它。
近期有时间研究一下这个问题,发现是BlueJ的ClassLoader问题。ClassLoader问题其实在《编程导论(Java)·7.1类载入》中提到过,但是我已经忘记了。,或者说,当时我就没有太在意这个问题。p220写到:
注意:在BlueJ和控制台中运行,结果不同。在BlueJ中输出:java.net.URLClassLoader,而控制台中为sun.misc.Launcher$AppClassLoader。开发环境通常提供自己的装载器(注:选择某种装载器使用策略),而在控制台中myLoader 与ClassLoader loader = ClassLoader.getSystemClassLoader()的loader指向同一个对象。(这点差异不影响后面的介绍)下面介绍一下我的探索过程,虽然走了一些弯路,但是走些弯路,可以看到一些也有意义的东西。
1.选择ClassLoader
第一阶段,在NetBeans和BlueJ项目中测试和选择ClassLoader。目标是访问 默认文件"my.properties"。try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(path))
是关键所在,因此将该语句分开
public void testPropertiesLocator() { ClassLoader[] cls = new ClassLoader[]{ ClassLoader.getSystemClassLoader(), this.getClass().getClassLoader(), Main.class.getClassLoader(), Thread.currentThread().getContextClassLoader()}; for(ClassLoader x : cls ){ URL resource = x.getResource("my.properties"); pln(resource); } String cwd = System.getProperty("user.dir"); pln(cwd); }
NetBeans输出:
file:/D:/yqj2065/yqj2065/build/classes/my.properties
file:/D:/yqj2065/yqj2065/build/classes/my.properties
file:/D:/yqj2065/yqj2065/build/classes/my.properties
D:\yqj2065\yqj2065
BlueJ项目输出:
null
file:/D:/path/my.properties
file:/D:/path/my.properties
file:/D:/path/my.properties
D:\path
基于classpath的相对路径,在BlueJ中需要排除ClassLoader.getSystemClassLoader()。
剩下的2个(2和3其实是一个东西),在BlueJ项目中都可以工作,
public static void testGetValue() { String str = new PropertiesLocator6().getValue("2065") ; pln(str); }但是打包后,仅仅测试getValue("2065"),发现this.getClass().getClassLoader()不行了,它不能够正确访问测试项目的my.properties(它能够访问jar中的资源),
在测试项目中调用jar中testPropertiesLocator()和直接执行testPropertiesLocator()代码,可以明显发现,
public class Test{ public static void testGetValue() { String str = new PropertiesLocator6().getValue("2065") ; pln(str); new Main().testPropertiesLocator(); pln("-------------------------------" ); ClassLoader[] cls = new ClassLoader[]{ Test.class.getClassLoader(), Thread.currentThread().getContextClassLoader()}; for(ClassLoader x : cls ){ URL resource = x.getResource("my.properties"); pln(resource); } }}
输出:
a.DDD
null
null
null
file:/D:/test/my.properties
-------------------------------
file:/D:/test/my.properties
file:/D:/test/my.properties
当我以为问题搞定了,测试create,又出现NullPointerException。既然已经将类全名找到——a.DDD,用它创建对象还有问题?
这时,我才想起来翻书,我意识到是ClassLoader问题。
2.WhoLoadMe
类装载器系统采用托付模型,每个类装载器都有一个父加载器(与类层次无关)。细节请看《编程导论(Java)·7.1类载入》和随书代码。
在NetBeans使用yqj2065.jar的某个项目中,随便找一个类如Client,打印类装载器
pln(Client.class.getClassLoader()); pln(ClassLoader.getSystemClassLoader()); pln(Thread.currentThread().getContextClassLoader());输出:
sun.misc.Launcher$AppClassLoader@55f96302
sun.misc.Launcher$AppClassLoader@55f96302
sun.misc.Launcher$AppClassLoader@55f96302
在BlueJ使用yqj2065.jar的项目中,随便找一个类如Test,打印类装载器
public static void testWhoLoadMe(){ pln(Test.class.getClassLoader() ); pln( God.class.getClassLoader() ); pln( Locator.class.getClassLoader() ); pln(Thread.currentThread().getContextClassLoader()); }输出:
java.net.URLClassLoader@50ce9f3e
sun.misc.Launcher$AppClassLoader@14dad5dc
sun.misc.Launcher$AppClassLoader@14dad5dc
java.net.URLClassLoader@50ce9f3e
虽然访问 默认文件"my.properties"时,我们指定了Thread.currentThread().getContextClassLoader()——java.net.URLClassLoader,但是反射创建对象时God的类装载器,却是AppClassLoader。真tm烦人。最简单地Class.forName(typeName).newInstance()不能够用了。
新版本如下:
package yqj2065.util;public class God { public static final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); private static final Locator locator =LocatorFactory.getLocator(); public interface Locator{ public String getValue(String path, String key); default public String getValue(String key){ return getValue("my.properties", key); } public static String getPath() { return System.getProperty("user.dir"); } } private static Object _create(String typeName){ Object obj = null; if (typeName != null) { try { obj =classLoader.loadClass(typeName).newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { } } return obj; } public static Object create(String path, String key) { String typeName = locator.getValue(path, key); return _create( typeName); } public static Object create(String key) { String typeName = locator.getValue(key); return _create( typeName); }}而其他类没有什么变化,如
package yqj2065.util;/** * * @author yqj2065 */import java.io.InputStream;import java.io.InputStreamReader;import java.util.Properties;import static yqj2065.util.Print.pln;public class PropertiesLocator implements God.Locator{ @Override public String getValue(String path, String key) { Properties props = new Properties(); try (InputStream is = God.classLoader.getResourceAsStream(path)) { try (InputStreamReader isr = new InputStreamReader(is, "UTF-8")) { props.load(isr);//从流中加载properties文件信息 } } catch (java.io.FileNotFoundException e) { pln(e.getClass().getName()); } catch (java.io.IOException e) { pln(e.getClass().getName()); } return props.getProperty(key); }}打包后可以使用了。
但是,...................
①我有意留下的小尾巴:
private static final Locator locator =LocatorFactory.getLocator();②我实在没有兴趣搞的东西,BlueJ的静态数据加载问题。在测试项目中使用了yqj2065.jar,打开BlueJ运行ok。
public class DDD{ public void foo(){ pln("okjhkkkkkkkjhlfkkdffddkk"); }}
public static void testCreate(){ DDD dd = (DDD)God.create("2065"); dd.foo(); }修改DDD,编译,再次运行:
java.lang.ClassCastException: a.DDD cannot be cast to a.DDD
(所以,运行前,请在BlueJ中重启JVM)
- 打包后的工具类 God+BlueJ+ClassLoader
- 工具类 tool.God
- BlueJ的code pad
- bluej
- God--Ruby版的进程监控工具
- ClassLoader(包括一个工具类)
- AndroidStudio打包后的apk,使用zipalign工具优化方法
- 渠道打包工具打包后\n变成换行符的问题
- ClassLoader类的原理
- GOD
- apk的打包和反编译八、ClassLoader防反编译
- 关于ClassLoader.getResouce打包配置文件放外面的方法
- 对序列化后的buffer进行strlen操作, oh my god
- BlueJ & MenuetOS
- 打包工具类
- 压缩打包工具类
- zip打包工具类
- 登峰造极的人物 God Like
- HDU 1180 诡异的楼梯
- DOS命令初次接触
- 暂时保存代码
- 流程的Python 第五章:一等函数
- ARM初体验
- 打包后的工具类 God+BlueJ+ClassLoader
- jvm 内存区域划分
- Python中变量
- ASP.NET基础篇--2、学习笔记
- 当数据迁移遇到MySql表统计分析(Cardinality)不准确的坑
- hdu2256
- 暑假测试 Day 1
- hdu5775 sum
- 如何反射SubscriptionManager和TelephonyManager获取其方法