问题背景及代码示例
一个SDK,想通过java -jar
命令动态设置后台服务的地址,开发完成后,再本地测试时一路绿灯,非常丝滑,但是将后端服务部署到服务器后,SDK死活都访问不到服务端,最终一顿调试定位给到问题:启动时传入的参数根本没生效,而代码里面我默认写了本地地址,所以只可以访问到本地。废话不多说,先看当时出现问题的示例代码。
代码示例
public class ConfigHolder {
private static final Logger log = org.slf4j.LoggerFactory.getLogger(ConfigHolder.class);
public String BASE_URL = "http://127.0.0.1:8888";
public String API_VERSION = "/api/v1";
public final String HEART_BEAT_URL = BASE_URL + API_VERSION + "/heartBeat";
// 其他接口地址和参数...
private static class InstanceHolder {
private final static ConfigHolder INSTANCE = new ConfigHolder();
}
private ConfigHolder() {
}
public static ConfigHolder getInstance() {
return InstanceHolder.INSTANCE;
}
public void parseArgs(String[] args) {
for (int i = 0; i + 1 < args.length; i += 2) {
if ("--api".equals(args[i])) {
this.BASE_URL = args[i + 1];
log.info("Api address: {}", this.BASE_URL);
}
}
}
}
// 启动类
public class Main {
public static void main(String[] args) {
ConfigHolder.getInstance().parseArgs(args);
// 其他操作...
}
}
代码并不复杂,就是一个单例类,然后一个parseArgs
方法解析启动类中mian
函数传过来的启动参数,然后赋值给相应的成员变量。
看完代码不知各位是否已经发现参数不生效的问题,如果没有发现,说明准备面试“背诵八股文”的时候,单纯是为了背诵。(手动狗头)
问题分析
我们先来回顾下一道著名的“八股”——Java类实例化的顺序。在Java中,如果一个类不涉及继承,那它实例化的时候,顺序时这样的:静态变量 -> 静态代码块 -> 静态方法 -> 普通成员变量 -> 普通代码块 -> 构造方法。
在上面的代码中,变量BASE_URL
是在类实例化完成后就已经被赋值http://127.0.0.1:8888
,因为JVM对字符串拼接的优化,变量HEART_BEAT_URL
在类实例化时就被赋值为http://127.0.0.1:8888/api/v1/heartBeat
,而参数解析是在类实例化之后,也就是说,我成功将BASE_URL
改成了我预期的地址,而HEART_BEAT_URL
在初始化的时候,取的是旧的BASE_URL
,那么实例化完成后,无论我怎么修改BASE_URL
,HEART_BEAT_URL
都不会变,永远是http://127.0.0.1:8888/api/v1/heartBeat
,所以我怎么可能用访问得到服务端。
解决方法
当然解决方式不止这一种,这里给出一个示例。
import java.util.function.Supplier;
public class ConfigHolder {
private static final Logger log = org.slf4j.LoggerFactory.getLogger(ConfigHolder.class);
public String BASE_URL = "http://127.0.0.1:8888";
public String API_VERSION = "/api/v1";
// 使用 Supplier 做包装,用到 HEART_BEAT_URL 的时候再实例化
public final Supplier<String> HEART_BEAT_URL = () -> BASE_URL + API_VERSION + "/heartBeat";
// 其他接口地址和参数...
private static class InstanceHolder {
private final static ConfigHolder INSTANCE = new ConfigHolder();
}
private ConfigHolder() {
}
public static ConfigHolder getInstance() {
return InstanceHolder.INSTANCE;
}
public void parseArgs(String[] args) {
for (int i = 0; i + 1 < args.length; i += 2) {
if ("--api".equals(args[i])) {
this.BASE_URL = args[i + 1];
AnsiLog.info("Api address: " + this.BASE_URL);
}
}
}
}
用Supplier
(Java 8 的新特性)对地址做了一层包装,只有在调用HEART_BEAT_URL.get()
的时候才会实例化该字符串,在启动的时候肯定是先解析参数,再获取接口地址,获取接口地址的时候BASE_URL
已经更新为最新值,所以获取到的接口地址也就是最新的了。
总结
回到文章的题目,“八股文”到底有没有用,如果是面向面试学习,简单的背诵,显然意义不大,但如果看完了可以自己理解,对我们排查、定位及分析问题,还是有用的。而且“八股文”相当于是一份精华笔记,里面很少有废话,比起系统学习,虽然深度欠缺一些,但可以快速掌握一些知识,所以以学习为目的去看“八股文”,我认为还是有意义的。所以,“八股文”并不“八股”(狗头)。
zmezxalcnk
《鲍勃罗斯:那些美与丑的风景》记录片高清在线免费观看:https://www.jgz518.com/xingkong/7301.html
ccbbp
诞生之初应该是有用的
流量卡知识网
你好,看完你的博客文章,感觉很不错!希望与你网站首页友情链接
流量卡知识网
http://53go.cn/
专注于移动/联通/电信推出的大流量多语音活动长短期套餐手机卡的相关知识的介绍普及听说互换友情链接可以增加网站的收录量,特此来换,如果同意的话就给internetyewu@163.com[微信ganenboy]发信息或者就在此回复下吧!【建站问题也可以一起讨论!】