聊聊写代码的20个反面教材
前言
今天跟大家聊一个有趣的话题:如何写出让人抓狂的代码?
大家看到这个标题,第一印象觉得这篇文章可能是一篇水文。但我很负责的告诉你,它是一篇有很多干货的技术文。
曾几何时,你在阅读别人代码的时候,有没有抓狂,想生气,想发火的时候?
今天就跟大家一起聊聊,这20种我看了会抓狂的代码,看看你中招了没?
1.不注重代码格式
代码格式说起来很虚,下面我用几个案例演示一下,不注重代码格式的效果。作为这篇文章的开胃小菜吧。
1.1 空格
有时候必要的空格没有加,比如:
@Service
@Slf4j
public class TestService1{
public void test1(){
addLog("test1");if (condition1){if (condition2){if (condition3){log.info("info:{}",info);}}}
}
}
复制代码
你看了这段代码有何感想,有没有血压飙升的感觉?
代码好像揉到一起去了。
那么,如何把血压降下来呢?
答:加上空格即可。
正解:
@Service
@Slf4j
public class TestService1 {public void test1() {addLog("test1");if (condition1) {if (condition2) {if (condition3) {log.info("info:{}", info);}}}}
}
复制代码
只加了一些空格,稍微调整了一下,这段代码的层次结构一下子变得非常清晰了。
好吧,我又冷静下来了。
1.2 换行
写代码时,如果有些必要的换行没有加,可能会出现这样的代码:
public void update(User user) {if (null != user.getId()) {User oldUser = userMapper.findUserById(user.getId());if(null == oldUser)throw new RuntimeException("用户id不存在");oldUser.setName(user.getName());oldUser.setAge(user.getAge());oldUser.setAddress(user.getAddress());userMapper.updateUser(oldUser);} else { userMapper.insertUser(user);}
}
复制代码
看了这段代码,是不是有点生无可恋的感觉?
简单的加点空格优化一下:
public void update(User user) {if (null != user.getId()) {User oldUser = userMapper.findUserById(user.getId());if(null == oldUser) {throw new RuntimeException("用户id不存在");}oldUser.setName(user.getName());oldUser.setAge(user.getAge());oldUser.setAddress(user.getAddress());userMapper.updateUser(oldUser);} else {userMapper.insertUser(user);}
}
复制代码
代码逻辑一下子变得清晰了许多。
2.随意的命名
java中没有强制规定参数、方法、类或者包名该怎么起名。但如果我们没有养成良好的起名习惯,随意起名的话,可能会出现很多奇怪的代码。
2.1 有意义的参数名
有时候,我们写代码时为了省事(可以少敲几个字母),参数名起得越简单越好。假如同事A写的代码如下:
int a = 1;
int b = 2;
String c = "abc";
boolean b = false;
复制代码
一段时间之后,同事A离职了,同事B接手了这段代码。
他此时一脸懵逼,a是什么意思,b又是什么意思,还有c...然后心里一万个草泥马。
给参数起一个有意义的名字,是非常重要的事情,避免给自己或者别人埋坑。
正解:
int supplierCount = 1;
int purchaserCount = 2;
String userName = "abc";
boolean hasSuccess = false;
复制代码
2.2 见名知意
光起有意义的参数名还不够,我们不能就这点追求。我们起的参数名称最好能够见名知意
,不然就会出现这样的情况:
String yongHuMing = "苏三";
String 用户Name = "苏三";
String su3 = "苏三";
String suThree = "苏三";
复制代码
这几种参数名看起来是不是有点怪怪的?
为啥不定义成国际上通用的(地球人都能看懂)英文单词呢?
String userName = "苏三";
String susan = "苏三";
复制代码
上面的这两个参数名,基本上大家都能看懂,减少了好多沟通成本。
所以建议在定义不管是参数名、方法名、类名时,优先使用国际上通用的英文单词,更简单直观,减少沟通成本。少用汉子、拼音,或者数字定义名称。
2.3 参数名风格一致
参数名其实有多种风格,列如:
//字母全小写
int suppliercount = 1;//字母全大写
int SUPPLIERCOUNT = 1;//小写字母 + 下划线
int supplier_count = 1;//大写字母 + 下划线
int SUPPLIER_COUNT = 1;//驼峰标识
int supplierCount = 1;
复制代码
如果某个类中定义了多种风格的参数名称,看起来是不是有点杂乱无章?
所以建议类的成员变量、局部变量和方法参数使用supplierCount,这种驼峰风格
,即:第一个字母小写,后面的每个单词首字母大写。例如:
int supplierCount = 1;
复制代码
此外,为了好做区分,静态常量建议使用SUPPLIER_COUNT,即:大写字母
+
下划线
分隔的参数名。例如:
private static final int SUPPLIER_COUNT = 1;
复制代码
3.出现大量重复代码
ctrl + c
和 ctrl + v
可能是程序员使用最多的快捷键了。
没错,我们是大自然的搬运工。哈哈哈。
在项目初期,我们使用这种工作模式,确实可以提高一些工作效率,可以少写(实际上是少敲)很多代码。
但它带来的问题是:会出现大量的代码重复。例如:
@Service
@Slf4j
public class TestService1 {public void test1() {addLog("test1");}private void addLog(String info) {if (log.isInfoEnabled()) {log.info("info:{}", info);}}
}
复制代码
@Service
@Slf4j
public class TestService2 {public void test2() {addLog("test2");}private void addLog(String info) {if (log.isInfoEnabled()) {log.info("info:{}", info);}}
}
复制代码
@Service
@Slf4j
public class TestService3 {public void test3() {addLog("test3");}private void addLog(String info) {if (log.isInfoEnabled()) {log.info("info:{}", info);}}
}
复制代码
在TestService1、TestService2、TestService3类中,都有一个addLog方法用于添加日志。
本来该功能用得好好的,直到有一天,线上出现了一个事故:服务器磁盘满了。
原因是打印的日志太多,记了很多没必要的日志,比如:查询接口的所有返回值,大对象的具体打印等。
没办法,只能将addLog方法改成只记录debug日志。
于是乎,你需要全文搜索,addLog方法去修改,改成如下代码:
private void addLog(String info) {if (log.isDebugEnabled()) {log.debug("debug:{}", info);}
}
复制代码
这里是有三个类中需要修改这段代码,但如果实际工作中有三十个、三百个类需要修改,会让你非常痛苦。改错了,或者改漏了,都会埋下隐患,把自己坑了。
为何不把这种功能的代码提取出来,放到某个工具类中呢?
@Slf4j
public class LogUtil {private LogUtil() {throw new RuntimeException("初始化失败");}public static void addLog(String info) {if (log.isDebugEnabled()) {log.debug("debug:{}", info);}}
}
复制代码
然后,在其他的地方,只需要调用。
@Service
@Slf4j
public class TestService1 {public void test1() {LogUtil.addLog("test1");}
}
复制代码
如果哪天addLog的逻辑又要改了,只需要修改LogUtil类的addLog方法即可。你可以自信满满的修改,不需要再小心翼翼了。
我们写的代码,绝大多数是可维护性的代码,而非一次性的。所以,建议在写代码的过程中,如果出现重复的代码,尽量提取成公共方法。千万别因为项目初期一时的爽快,而给项目埋下隐患,后面的维护成本可能会非常高。
4.从不写注释
有时候,在项目时间比较紧张时,很多人为了快速开发完功能,在写代码时,经常不喜欢写注释。
此外,还有些技术书中说过:好的代码,不用写注释,因为代码即注释
。这也给那些不喜欢写代码注释的人,找了一个合理的理由。
但我个人觉得,在国内每个程序员的英文水平都不一样,思维方式和编码习惯也有很大区别。你要把前人某些复杂的代码逻辑真正搞懂,可能需要花费大量的时间。
我们看到spring
的核心方法refresh
,也是加了很多注释的:
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}}
复制代码
如果你写的代码完全不写注释,可能最近一个月、三个月、半年还记得其中的逻辑。但一年、两年,甚至更久的时间之后,你确定还能想起当初的逻辑,而不需要花费大量的时间去重新看自己的代码梳理逻辑?
说实话,不写注释,到了项目后期,不光是把自己坑了,还会坑队友。
为什么把这一条单独拿出来?
因为我遇到过,接过锅,被坑惨了。
5.方法过长
我们平时在写代码时,有时候思路来了,一气呵成,很快就把功能开发完了。但也可能会带来一个小问题,就是方法过长。
伪代码如下:
public void run() {List<User> userList = userMapper.getAll();//经过一系列的数据过滤//此处省略了50行代码List<User> updateList = //最终获取到user集合if(CollectionUtils.isEmpty(updateList)) {return;}for(User user: updateList) {//经过一些复杂的过期时间计算//此处省略30行代码}//分页更新用户的过期时间//此处省略20行代码//发mq消息通知用户//此处省略30行代码
}
复制代码
上面的run方法中包含了多种业务逻辑,虽说确实能够实现完整的业务功能,但却不能称之为好。
为什么呢?
答:该方法总长度超过150行,里面的代码逻辑很杂乱,包含了很多关联性不大的代码块。该方法的职责太不单一了,非常不利于代码复用和后期的维护。
那么,如何优化呢?
答:做方法拆分
,即把一个大方法拆分成多个小方法。
例如:
public void run() {List<User> userList = userMapper.getAll();List<User> updateList = filterUser(userList);if(CollectionUtils.isEmpty(updateList)) {return;}for(User user: updateList) {clacExpireDay(user);}updateUser(updateList);sendMq(updateList);
}private List<User> filterUser(List<User> userList) {//经过一系列的数据过滤//此处省略了50行代码List<User> updateList = //最终获取到user集合return updateList;
}private void clacExpireDay(User user) {//经过一些复杂的过期时间计算//此处省略30行代码
}private void updateUser(List<User> updateList) {//分页更新用户的过期时间//此处省略20行代码
}private void sendMq(List<User> updateList) {//发mq消息通知用户//此处省略30行代码
}
复制代码
这样简单的优化之后,run方法的代码逻辑一下子变得清晰了许多,光看它调用的子方法的名字,都能猜到这些字方法是干什么的。
每个子方法只专注于自己的事情,别的事情交给其他方法处理,职责更单一了。
此外,如果此时业务上有一个新功能,也需要给用户发消息,那么上面定义的sendMq方法就能被直接调用了。岂不是爽歪歪?
换句话说,把大方法按功能模块拆分成N个小方法,更有利于代码的复用。
顺便说一句,Hotspot对字节码超过8000字节的大方法有JIT编译限制,超过了限制不会被编译。
6.参数过多
我们平常在定义某个方法时,可能并没注意参数个数的问题(其实是我猜的)。我的建议是方法的参数不要超过5
个。
先一起看看下面的例子:
public void fun(String a,String b,String c,String d,String e,String f) {...
}public void client() {fun("a","b","c","d",null,"f");
}
复制代码
上面的fun方法中定义了6个参数,这样在调用该方面的所有地方都需要思考一下,这些参数该怎么传值,哪些参数可以为空,哪些参数不能为空。
方法的入参太多,也会导致该方法的职责不单一,方法存在风险的概率更大。
那么,如何优化参数过多问题呢?
答:可以将一部分参数迁移到新方法中。
这个例子中,可以把参数d,e,f迁移到otherFun方法。例如:
public Result fun(String a,String b,String c) {...return result;
}public void otherFun(Result result,String d,String e,String f) {...
}public void client() {Result result = fun("a","b","c");otherFun(result, "d", null, "f");
}
复制代码
这样优化之后,每个方法的逻辑更单一一些,更有利于方法的复用。
如果fun中还需要返回参数a、b、c,给下个方法继续使用,那么代码可以改为:
public Result fun(String a,String b,String c) {...Result result = new Result();result.setA(a);result.setB(b);result.setC(c);return result;
}
复制代码
在给Result对象赋值时,这里有个小技巧,可以使用lombok
的@Builder
注解,做成链式调用。例如:
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
public class Result {private String a;private String b;private String c;
}
复制代码
这样在调用的地方,可以这样赋值:
Result result = Result.builder()
.a("a").b("b").c("c")
.build();
复制代码
非常直观明了。
此时,有人可能会说,ThreadPoolExecutor
不也提供了7个参数的方法?
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {...
}
复制代码
没错,不过它是构造方法
,我们这里主要讨论的是普通方法
。
7.代码层级太深
不知道你有没有见过类似这样的代码:
if (a == 1) {if(b == 2) {if(c == 3) {if(d == 4) {if(e == 5) {...}...}...}...}...
}
复制代码
这段代码中有很多层if判断,是不是看得人有点眼花缭乱?
有同感的同学,请举个手。
如果你没啥感觉,那么接着往下看:
for(int i=0; i<100;i++) {for(int j=0; j<50;j++) {for(int m=0; m<200;m++) {for(int n=0; n<100;n++) {for(int k=0; k<50; k++) {...}}}}
}
复制代码
看了这段代码,你心中可能会一紧。这么多循环,代码的性能真的好吗?
这两个例子中的代码都犯了同一个错误,即:代码层级太深
。
代码层级太深导致的问题是代码变得非常不好维护,不容易理清逻辑,有时候代码的性能也可能因此变差。
那么关键问题来了,如何解决代码层级较深的问题呢?
对于if判断层级比较多的情况:
if(a!=1) {...return;
}doConditionB();
复制代码
private void doConditionB() {if(b!=2) {...return;}doConditionC();
}
复制代码
把不满足条件(a==1)的逻辑先执行,先返回。再把满足条件(a==1)的逻辑单独抽取到一个方法(doConditionB)中。该doConditionB中也会把不满足条件(b==2)的逻辑先执行,先返回。再把满足条件(b==2)的逻辑单独抽取到一个方法(doConditionC)中。后面逻辑以此类推。
这种做法是面向防御式编程
的一种,即先把不满足条件的代码先执行,然后才执行满足条件的代码。此外别忘了,把满足条件的代码抽取到一个新的方法中喔。
对于for循环层级太深的优化方案,一般推荐使用map
。
例如:
for(Order order:orderList) {for(OrderDetail detail: detailList) {if(order.getId().equals(detail.getOrderId())) {doSamething();}}
}
复制代码
使用map优化之后:
Map<Long, List<OrderDetail>> detailMap = detailList.stream().collect(Collectors.groupingBy(OrderDetail::getOrderId));for(Order order:orderList) {List<OrderDetail> detailList = detailMap.get(order.getId());if(CollectionUtils.isNotEmpty) {doSamething();}
}
复制代码
这个例子中使用map,少了一层循环,代码效率提升一些。但不是所有的for循环都能用map替代,要根据自己实际情况选择。
代码层级太深,还有其他的场景,比如:方法中return的次数太多,也会降低代码的可读性。
这种情况,其实也可能通过面向防御式编程进行代码优化。
8.判断条件太多
我们在写代码的时候,判断条件是必不可少的。不同的判断条件,走的代码逻辑通常会不一样。
废话不多说,先看看下面的代码。
public interface IPay { void pay();
} @Service
public class AliaPay implements IPay { @Overridepublic void pay() { System.out.println("===发起支付宝支付==="); }
} @Service
public class WeixinPay implements IPay { @Overridepublic void pay() { System.out.println("===发起微信支付==="); }
} @Service
public class JingDongPay implements IPay { @Overridepublic void pay() { System.out.println("===发起京东支付==="); }
} @Service
public class PayService { @Autowiredprivate AliaPay aliaPay; @Autowiredprivate WeixinPay weixinPay; @Autowiredprivate JingDongPay jingDongPay; public void toPay(String code) { if ("alia".equals(code)) { aliaPay.pay(); } elseif ("weixin".equals(code)) { weixinPay.pay(); } elseif ("jingdong".equals(code)) { jingDongPay.pay(); } else { System.out.println("找不到支付方式"); } }
}
复制代码
PayService类的toPay方法主要是为了发起支付,根据不同的code,决定调用用不同的支付类(比如:aliaPay)的pay方法进行支付。
这段代码有什么问题呢?也许有些人就是这么干的。
试想一下,如果支付方式越来越多,比如:又加了百度支付、美团支付、银联支付等等,就需要改toPay方法的代码,增加新的else...if判断,判断多了就会导致逻辑越来越多?
很明显,这里违法了设计模式六大原则的:开闭原则 和 单一职责原则。
开闭原则:对扩展开放,对修改关闭。就是说增加新功能要尽量少改动已有代码。
单一职责原则:顾名思义,要求逻辑尽量单一,不要太复杂,便于复用。
那么,如何优化if...else判断呢?
答:使用 策略模式
+工厂模式
。
策略模式定义了一组算法,把它们一个个封装起来, 并且使它们可相互替换。 工厂模式用于封装和管理对象的创建,是一种创建型模式。
public interface IPay {void pay();
}@Service
public class AliaPay implements IPay {@PostConstructpublic void init() {PayStrategyFactory.register("aliaPay", this);}@Overridepublic void pay() {System.out.println("===发起支付宝支付===");}
}@Service
public class WeixinPay implements IPay {@PostConstructpublic void init() {PayStrategyFactory.register("weixinPay", this);}@Overridepublic void pay() {System.out.println("===发起微信支付===");}
}@Service
public class JingDongPay implements IPay {@PostConstructpublic void init() {PayStrategyFactory.register("jingDongPay", this);}@Overridepublic void pay() {System.out.println("===发起京东支付===");}
}public class PayStrategyFactory {private static Map<String, IPay> PAY_REGISTERS = new HashMap<>();public static void register(String code, IPay iPay) {if (null != code && !"".equals(code)) {PAY_REGISTERS.put(code, iPay);}}public static IPay get(String code) {return PAY_REGISTERS.get(code);}
}@Service
public class PayService3 {public void toPay(String code) {PayStrategyFactory.get(code).pay();}
}
复制代码
这段代码的关键是PayStrategyFactory类,它是一个策略工厂,里面定义了一个全局的map,在所有IPay的实现类中注册当前实例到map中,然后在调用的地方通过PayStrategyFactory类根据code从map获取支付类实例即可。
如果加了一个新的支付方式,只需新加一个类实现IPay接口,定义init方法,并且重写pay方法即可,其他代码基本上可以不用动。
当然,消除又臭又长的if...else判断,还有很多方法,比如:使用注解、动态拼接类名称、模板方法、枚举等等。由于篇幅有限,在这里我就不过多介绍了,更详细的内容可以看看我的另一篇文章《消除if...else是9条锦囊妙计》
9.硬编码
不知道你有没有遇到过这类需求:
- 限制批量订单上传接口,一次性只能上传200条数据。
- 在job中分页查询用户,一页查询100个用户,然后计算用户的等级。
上面例子中的200条数据和100个用户,很容易硬编码,即在代码中把参数写死了。
我们以上传200条数据为例:
private static final int MAX_LIMIT = 200;public void upload(List<Order> orderList) {if(CollectionUtils.isEmpty(orderList)) {throw new BusinessException("订单不能为空");} if(orderList.size() > MAX_LIMIT) {throw new BusinessException("超过单次请求的数量限制");}
}
复制代码
其中MAX_LIMIT被定义成了静态常量
。
上线之后,你发现上传历史数据时速度太慢了,需要把限制调大一点。
我擦。。。这种小小的参数改动,还需要改源代码,重新编译,重新打包,重新部署。。。
但如果你当初把这些公共参数,设置成可配置的,例如:
@Value("${com.susan.maxLimit:200}")
private int maxLimit = 200;public void upload(List<Order> orderList) {if(CollectionUtils.isEmpty(orderList)) {throw new BusinessException("订单不能为空");} if(orderList.size() > maxLimit) {throw new BusinessException("超过单次请求的数量限制");}
}
复制代码
这样只需在配置中心(比如:apollo、nocas等)中修改一下配置即可,不用修改源代码,不用重新编译,不用重新打包,不用重新部署。
一个字:爽。
我们在前期开发的时候,宁可多花一分钟思考一下,这个参数后面是否会被修改,是否可以定义成可配置的参数。也比后期修改代码,重新编译,重新打包,重新上线花的时间少得多。
10.事务过大
我们平时在使用spring框架开发项目时,喜欢用@Transactional
注解声明事务。例如:
@Transactional(rollbackFor = Throwable.class)
public void updateUser(User user) {System.out.println("update");
}复制代码
只需在需要使用事务的方法上,使用@Transactional
注解声明一下,该方法通过AOP就自动拥有了事务的功能。
没错,这种做法给我们带来了极大的便利,开发效率更高了。
但也给我们带来了很多隐患,比如大事务的问题。我们一起看看下面的这段代码:
@Transactional(rollbackFor = Throwable.class)
public void updateUser(User user) {User oldUser = userMapper.getUserById(user.getId());if(null != oldUser) {userMapper.update(user);} else {userMapper.insert(user);}sendMq(user);
}复制代码
这段代码中getUserById方法和sendMq方法,在这个案例中无需使用事务,只有update或insert方法才需要事务。
所以上面这段代码的事务太大了,是整个方法级别的事务。假如sendMq方法是一个非常耗时的操作,则可能会导致整个updateUser方法的事务超时,从而出现大事务问题。
那么,如何解决这个问题呢?
答:可以使用TransactionTemplate
的编程式事务优化代码。
@Autowired
private TransactionTemplate transactionTemplate;....public void updateUser(User user) {User oldUser = userMapper.getUserById(user.getId());transactionTemplate.execute((status) => {if(null != oldUser) {userMapper.update(user);} else {userMapper.insert(user);}return Boolean.TRUE;})sendMq(user);
}
复制代码
只有在execute
方法中的代码块才真正需要事务,其余的方法,可以非事务执行,这样就能缩小事务的范围,避免大事务。
当然使用TransactionTemplate
这种编程式事务,缩小事务范围,来解决大事务问题,只是其中一种手段。
如果你想对大事务问题,有更深入的了解,可以看看我的另一篇文章《让人头痛的大事务问题到底要如何解决?》
11.在循环中远程调用
有时候,我们需要在某个接口中,远程调用第三方的某个接口。
比如:在注册企业时,需要调用天眼查接口,查一下该企业的名称和统一社会信用代码是否正确。
这时候在企业注册接口中,不得不先调用天眼查接口校验数据。如果校验失败,则直接返回。如果校验成功,才允许注册。
如果只是一个企业还好,但如果某个请求有10个企业需要注册,是不是要在企业注册接口中,循环调用10次天眼查接口才能判断所有企业是否正常呢?
public void register(List<Corp> corpList) {for(Corp corp: corpList) {CorpInfo info = tianyanchaService.query(corp); if(null == info) {throw new RuntimeException("企业名称或统一社会信用代码不正确");}}doRegister(corpList);
}
复制代码
这样做可以,但会导致整个企业注册接口性能很差,极容易出现接口超时问题。
那么,如何解决这类在循环中调用远程接口的问题呢?
11.1 批量操作
远程接口支持批量操作,比如天眼查支持一次性查询多个企业的数据,这样就无需在循环中查询该接口了。
但实际场景中,有些第三方不愿意提供第三方接口。
11.2 并发操作
java8以后通过CompleteFuture
类,实现多个线程查天眼查接口,并且把查询结果统一汇总到一起。
具体用法我就不展开了,有兴趣的朋友可以看看我的另一篇文章《聊聊接口性能优化的11个小技巧》
12.频繁捕获异常
通常情况下,为了在程序中抛出异常时,任然能够继续运行,不至于中断整个程序,我们可以选择手动捕获异常
。例如:
public void run() {try {doSameThing();} catch (Exception e) {//ignore}doOtherThing();
}
复制代码
这段代码可以手动捕获异常,保证即使doSameThing方法出现了异常,run方法也能继续执行完。
但有些场景下,手动捕获异常被滥用了。
12.1 滥用场景1
不知道你在打印异常日志时,有没有写过类似这样的代码:
public void run() throws Exception {try {doSameThing();} catch (Exception e) {log.error(e.getMessage(), e);throw e;}doOtherThing();
}
复制代码
通过try/catch关键字,手动捕获异常的目的,仅仅是为了记录错误日志,在接下来的代码中,还是会把该异常抛出。
在每个抛出异常的地方,都捕获一下异常,打印日志。
12.2 滥用场景2
在写controller层接口方法时,为了保证接口有统一的返回值,你有没有写过类似这样的代码:
@PostMapping("/query")
public List<User> query(@RequestBody List<Long> ids) {try {List<User> userList = userService.query(ids);return Result.ok(userList);} catch (Exception e) {log.error(e.getMessage(), e);return Result.fature(500, "服务器内部错误");}
}
复制代码
在每个controller层的接口方法中,都加上了上面这种捕获异常的逻辑。
上述两种场景中,频繁的捕获异常,会让代码性能降低,因为捕获异常是会消耗性能的。
此外,这么多重复的捕获异常代码,看得让人头疼。
其实,我们还有更好的选择。在网关层(比如:zuul或gateway),有个统一的异常处理代码,既可以打印异常日志,也能统一封装接口返回值,这样可以减少很多异常被滥用的情况。
13.不正确的日志打印
在我们写代码的时候,打印日志是必不可少的工作之一。
因为日志可以帮我们快速定位问题,判断代码当时真正的执行逻辑。
但打印日志的时候也需要注意,不是说任何时候都要打印日志,比如:
@PostMapping("/query")
public List<User> query(@RequestBody List<Long> ids) {log.info("request params:{}", ids);List<User> userList = userService.query(ids);log.info("response:{}", userList);return userList;
}
复制代码
对于有些查询接口,在日志中打印出了请求参数和接口返回值。
咋一看没啥问题。
但如果ids中传入值非常多,比如有1000个。而该接口被调用的频次又很高,一下子就会打印大量的日志,用不了多久就可能把磁盘空间打满。
如果真的想打印这些日志该怎么办?
@PostMapping("/query")
public List<User> query(@RequestBody List<Long> ids) {if (log.isDebugEnabled()) {log.debug("request params:{}", ids);}List<User> userList = userService.query(ids);if (log.isDebugEnabled()) {log.debug("response:{}", userList);}return userList;
}
复制代码
使用isDebugEnabled
判断一下,如果当前的日志级别是debug才打印日志。生产环境默认日志级别是info,在有些紧急情况下,把某个接口或者方法的日志级别改成debug,打印完我们需要的日志后,又调整回去。
方便我们定位问题,又不会产生大量的垃圾日志,一举两得。
14.没校验入参
参数校验是接口必不可少的功能之一,一般情况下,提供给第三方调用的接口,需要做严格的参数校验。
以前我们是这样校验参数的:
@PostMapping("/add")
public void add(@RequestBody User user) {if(StringUtils.isEmpty(user.getName())) {throw new RuntimeException("name不能为空");}if(null != user.getAge()) {throw new RuntimeException("age不能为空");}if(StringUtils.isEmpty(user.getAddress())) {throw new RuntimeException("address不能为空");}userService.add(user);
}
复制代码
需要手动写校验的代码,如果作为入参的实体中字段非常多,光是写校验的代码,都需要花费大量的时间。而且这些校验代码,很多都是重复的,会让人觉得恶心。
好消息是使用了hibernate
的参数校验框架validate
之后,参数校验一下子变得简单多了。
我们只需要校验的实体类User中使用validation框架的相关注解,比如:@NotEmpty、@NotNull等,定义需要校验的字段即可。
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {private Long id;@NotEmptyprivate String name;@NotNullprivate Integer age;@NotEmptyprivate String address;
}
复制代码
然后在controller类上加上@Validated
注解,在接口方法上加上@Valid
注解。
@Slf4j
@Validated
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/add")public void add(@RequestBody @Valid User user) {userService.add(user);}
}
复制代码
这样就能自动实现参数校验的功能。
然而,现在需求改了,需要在User类上增加了一个参数Role,它也是必填字段,并且它的roleName和tag字段都不能为空。
但如果我们在校验参数时,不小心把代码写成这样:
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {private Long id;@NotEmptyprivate String name;@NotNullprivate Integer age;@NotEmptyprivate String address;@NotNullprivate Role role;
}
复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {@NotEmptyprivate String roleName;@NotEmptyprivate String tag;
}
复制代码
结果就悲剧了。
你心里可能还乐呵呵的认为写的代码不错,但实际情况是,roleName和tag字段根本不会被校验到。
如果传入参数:
{"name": "tom","age":1,"address":"123","role":{}
}
复制代码
即使role字段传入的是空对象,但该接口也会返回成功。
那么如何解决这个问题呢?
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {private Long id;@NotEmptyprivate String name;@NotNullprivate Integer age;@NotEmptyprivate String address;@NotNull@Validprivate Role role;
}
复制代码
需要在Role字段上也加上@Valid
注解。
温馨的提醒一声,使用validate框架校验参数一定要自测,因为很容易踩坑。
15.返回值格式不统一
我之前对接某个第三方时,他们有部分接口的返回值结构是这样的:
{"ret":0,"message":null,"data":[]
}
复制代码
另一部分接口的返回值结构是这样的:
{"code":0,"msg":null,"success":true,"result":[]
}
复制代码
整得我有点懵逼。
为啥没有一个统一的返回值?
我需要给他们的接口写两套返回值解析的代码,后面其他人看到了这些代码,可能也会心生疑问,为什么有两种不同的返回值解析?
唯一的解释是一些接口是新项目的,另外一些接口是老项目的。
但如果不管是新项目,还是老项目,如果都有一个统一的对外网关服务,由这个服务进行鉴权和统一封装返回值。
{"code":0,"message":null,"data":[]
}
复制代码
就不会有返回值结构不一致的问题。
温馨的提醒一下,业务服务不要捕获异常,直接把异常抛给网关服务,由它来统一全局捕获异常,这样就能统一异常的返回值结构。
16.提交到git的代码不完整
我们写完代码之后,把代码提交到gitlab
上,也有一些讲究。
最最忌讳的是代码还没有写完,因为赶时间(着急下班),就用git
把代码提交了。例如:
public void test() {String userName="苏三";String password=
}
复制代码
这段代码中的password变量都没有定义好,项目一运行起来必定报错。
这种错误的代码提交方式,一般是新手会犯。但还有另一种情况,就是在多个分支merge代码的时候,有时候会出问题,merge之后的代码不能正常运行,就被提交了。
好的习惯是:用git提交代码之前,一定要在本地运行一下,确保项目能正常启动才能提交。
宁可不提交代码到远程仓库,切勿因为一时赶时间,提交了不完整的代码,导致团队的队友们项目都启动不了。
17.不处理没用的代码
有些时候,我们为了偷懒,对有些没用的代码不做任何处理。
比如:
@Slf4j
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public void add(User user) {System.out.println("add");}public void update(User user) {System.out.println("update");}public void query(User user) {System.out.println("query");}
}
复制代码
本来UserService类中的add、update、query方法都在用的。后来,某些功能砍掉了,现在只有add方法真正在用。
某一天,项目组来了一个新人,接到需求需要在user表加一个字段,这时候他是不是要把add、update、query方法都仔细看一遍,评估一下影响范围?
后来发现只有add方法需要改,他心想前面的开发者为什么不把没用的代码删掉,或者标记出来呢?
在java中可以使用@Deprecated
表示这个类或者方法没在使用了,例如:
@Slf4j
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public void add(User user) {System.out.println("add");}@Deprecatedpublic void update(User user) {System.out.println("update");}@Deprecatedpublic void query(User user) {System.out.println("query");}
}
复制代码
我们在阅读代码时,可以先忽略标记了@Deprecated
注解的方法。这样一个看似简单的举手之劳,可以给自己,或者接手该代码的人,节省很多重复查代码的时间。
建议我们把没用的代码优先删除掉,因为gitlab中是有历史记录的,可以找回。但如果有些为了兼容调用方老版本的代码,不能删除的情况,建议使用
@Deprecated
注解相关类或者接口。
18.随意修改接口名和参数名
不知道你有没有遇到过这种场景:你写了一个接口,本来以为没人使用,后来觉得接口名或参数名不对,偷偷把它们改了。比如:
@PostMapping("/query")
public List<User> query(@RequestBody List<Long> ids) {return userService.query(ids);
}
复制代码
接口名改了:
@PostMapping("/queryUser")
public List<User> queryUser(@RequestBody List<Long> ids) {return userService.query(ids);
}
复制代码
结果导致其他人的功能报错,原来他已经在调用该接口了。
大意了。。。
所以在修改接口名、参数名、修改参数类型、修改参数个数时,一定要先询问一下相关同事,有没有使用该接口,免得以后出现不必要的麻烦。
对于已经在线上使用的接口,尽量不要修改接口名、参数名、修改参数类型、修改参数个数,还有请求方式,比如:get改成post等。宁可新加一个接口,也尽量不要影响线上功能。
19.使用map接收参数
我之前见过有些小伙伴,在代码中使用map接收参数的。例如:
@PostMapping("/map")
public void map(@RequestBody Map<String, Object> mapParam){System.out.println(mapParam);
}
复制代码
在map方法中使用mapParam对象接收参数,这种做法确实很方便,可以接收多种json格式的数据。
例如:
{"id":123,"name":"苏三","age":18,"address":"成都"
}
复制代码
或者:
{"id":123,"name":"苏三","age":18,"address":"成都","role": {"roleName":"角色","tag":"t1"}
}
复制代码
这段代码可以毫不费劲的接收这两种格式的参数,so cool。
但同时也带来了一个问题,那就是:参数的数据结构你没法控制,有可能你知道调用者传的json数据格式是第一种,还是第二种。但如果你没有写好注释,其他的同事看到这段代码,可能会一脸懵逼,map接收的参数到底是什么东东?
项目后期,这样的代码变得非常不好维护。有些同学接手前人的代码,时不时吐槽一下,是有原因的。
那么,如果优化这种代码呢?
我们应该使用有明确含义的对象去接收参数,例如:
@PostMapping("/add")
public void add(@RequestBody @Valid User user){System.out.println(user);
}
复制代码
其中的User对象是我们已经定义好的对象,就不会存在什么歧义了。
20.从不写单元测试
因为项目时间实在太紧了,系统功能都开发不完,更何况是单元测试呢?
大部分人不写单元测试的原因,可能也是这个吧。
但我想告诉你的是,不写单元测试并不是个好习惯。
我见过有些编程高手是测试驱动开发
,他们会先把单元测试写好,再写具体的业务逻辑。
那么,我们为什么要写单元测试呢?
-
我们写的代码大多数是可维护的代码,很有可能在未来的某一天需要被重构。试想一下,如果有些业务逻辑非常复杂,你敢轻易重构不?如果有单元测试就不一样了,每次重构完,跑一次单元测试,就知道新写的代码有没有问题。
-
我们新写的对外接口,测试同学不可能完全知道逻辑,只有开发自己最清楚。不像页面功能,可以在页面上操作。他们在测试接口时,很有可能覆盖不到位,很多bug测不出来。
建议由于项目时间非常紧张,在开发时确实没有写单元测试,但在项目后期的空闲时间也建议补上。
本文结合自己的实际工作经验,用调侃的方式,介绍了在编写代码的过程中,不太好的地方和一些优化技巧,给用需要的朋友们一个参考。
最后说一句(求关注,别白嫖我)
如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- npm和yarn的区别
周一入职,同事JJ让我熟悉一下基于React的新项目。 按照以往,我的步骤都是: git clone xxx npm install npm run dev这时,JJ给我来了下面一段 git clone xxx yarn yarn start“咦,yarn是什么鬼?难道npm更高级…...
2024/5/5 22:35:09 - tensorboard安装错误史
tensorboard安装错误 1尝试阶段 Dell 32位电脑,原有安装Anaconda, conda create -n tensorflow python=3.6 activate tensorflow pip install --ignore-installed --upgrade tensorflow 结果显示找不到符合匹配的tensorflow版本 2查找资料 发现tensorflow没有32位版本 lenovo…...
2024/4/17 21:23:17 - 力扣Day27
459、重复的子字符串 给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。 法一: class Solution {public boolean repeatedSubstringPattern(String s) {String str s s;return str.substring(1, str.length() - 1).contains(s)…...
2024/5/6 4:23:54 - 本地图文直接复制到CKEditor编辑器中
1.4.2之后官方并没有做功能的改动,1.4.2在word复制这块没有bug,其他版本会出现手动无法转存的情况 本文使用的后台是Java。前端为Jsp(前端都一样,后台如果语言不通得自己做 Base64编码解码) 因为公司业务需要支持IE8…...
2024/5/5 17:55:19 - C++构造函数
构造函数按参数为为:有参构造函数和无参构造函数 按类型分为:普通构造函数和拷贝构造函数 构造函数的三种调用方法:括号法,显示法,隐式转换法; //括号法 Person p1; //默认构造 无参构造 Person p2(1…...
2024/5/5 18:56:51 - linux虚拟机运行打包的jar包访问本地宿主机的mysql数据库
linux虚拟机运行打包的jar包访问本地宿主机的mysql数据库 1.打包jar包,如果要把java项目和依赖一起打包,推荐使用maven的packge打包 2.打包jar包后,在宿主机上,创建一个可以远程访问的mysql用户,并开通权限 3.配置s…...
2024/4/13 10:08:38 - 四川玖益科技:店铺搜索排名的几大误区
拼多多的免费流量是每个卖家都想要的,因为有流量不用自己花钱,对自己很好。然后,为了获得免费流量,卖家要优化自然搜索排名。然而,大多数卖家对拼多多搜索排名的优化存在一些误解。 拼多多搜索排名有五个常见误区&…...
2024/5/6 4:33:09 - 17 其他内容
17.导航守卫 导航守卫 | Vue Router 全局的导航守卫 场景: 大多数的路由都需要执行的业务时,可以使用全局导航守卫 比如说。后台管理系统,app首先必须先登录后使用 import Vue from vue import VueRouter from vue-router import store from…...
2024/5/5 17:58:21 - 初学网页前端笔记(5)
学习资源:HTML 教程 | 菜鸟教程 (runoob.com)【优极限】 HTMLCSSJavaScriptjQuery前端必学教程,小白教学,前端基础全套完成版_哔哩哔哩_bilibiliHTML 教程 | 菜鸟教程 (runoob.com) 通过观看p13-p19学习了javascript,主要内容&…...
2024/5/6 0:28:50 - 安装算量软件哪个速度快效率高?
鹏业安装算量软件在2010年就已推出,到目前已经有12年的时间(2022年),算是国内首批推出的安装算量软件之一。 1.运行速度快,性能稳定。对电脑的配置要求不会太高,一般的电脑运行安装算量软件都不会卡顿。 …...
2024/4/15 3:10:27 - [ABAP]--SOAMANAGER创建WEB服务
一、创建SOAMANAGER首先需要激活对应的服务 激活步骤如下: T-CODE:SICF 输入:SERVICE SAP是通过SOAMANAGER来对外发布指定服务器的,SOAMANAGER本身其实也是一个基于Netweaver的Web Dynpro程序,当你在SAP GUI客户端执行T-Code&…...
2024/4/13 10:09:24 - 史上最全Windows安全工具锦集
PE工具篇 PEiD 一款著名的PE侦壳工具,可以检测PE常见的一些壳,但是目前已经无法从官网获得: EXEInfoPE PE侦壳工具,PEiD的加强版,可以查看EXE/DLL文件编译器信息、是否加壳、入口点地址、输出表/输入表等等PE信息&a…...
2024/4/14 4:57:37 - 智能额温枪软件设计红外测温仪方案开发
返岗生产很重要,防控措施不能少。除了戴口罩之外,体温筛查也成为疫情控制的一道重要防线。通过发现体温异常人员,采取及时发现、及早隔离等措施,能够有效防止疫情的扩散。额温枪这一非接触式测温仪器,成为疫情防控的重…...
2024/4/15 16:58:41 - 中国轴承温度传感器市场现状研究分析与发展前景预测报告
【报告篇幅】:101 【报告图表数】:151 【报告出版时间】:2022年1月 报告摘要 2021年中国轴承温度传感器市场销售收入达到了 万元,预计2028年可以达到 万元,2022-2028期间年复合增长率(CAGR)为 %。中国市场核心厂商包…...
2024/4/26 20:48:29 - C++实现一个简单的数组队列
C实现一个简单的数组队列 /**--------------------------------------- * 文件名称: CSeqQueue.h * 功能描述: 数组实现队列 * 创建标识: xad 2022/2/9 * * 修改标识: * 修改描述: ---------------------------------…...
2024/5/4 4:57:19 - 第20篇 进阶(二十)模型—视图—代理 之 基本概念、基础模型
1、概念 数据通过(model—view)(模型—视图)分离。 对于每个视图(view),每个数据元素的可视化都分给一个代理。 对于用户界面的开发,最重要的是把数据与视图可视化分离。 举个简单的例子: 现在要完成一个电话簿的界面? 视图排列可以使用垂直方式或者水平方式,…...
2024/4/15 5:15:26 - 在微信小程序中使用条形码生成器
此功能是基于插件实现的,具体插件可以自行在插件市场下载使用,下载地址:条形码生成器 - DCloud 插件市场 最终实现效果: 微信APP能够扫码识别的条形码类型有code128\code39\ean13\ean8\upc\itf14,这里使用的是code128…...
2024/4/19 10:56:50 - Centos 7.9 install Mysql 5.7
访问mysql官方网站下载需要的对应版本,速度很快! MySQL :: Download MySQL Community Server (Archived Versions)https://downloads.mysql.com/archives/community/本篇文章安装的版本为mysql-5.7.21-1.el7.x86_64.rpm-bundle.tar 一、下载后解压 # …...
2024/4/18 0:38:53 - C++实现一个简单的数组栈
C实现一个简单的数组栈 /**--------------------------------------- * 文件名称: CSeqStack.h * 功能描述: 数组实现栈 * 创建标识: xad 2022/2/9 * * 修改标识: * 修改描述: -------------------------------------…...
2024/4/7 22:37:20 - 为什么要读书?
今日,正月初九,想必大部分上班族都已外出奋斗,开启了新的一年;某些省份的高三学子,也已正式开学,校园里开始熙熙攘攘。今日,我们就谈谈”为什么要读书”。当然,我自己不够资格去谈论…...
2024/4/16 20:54:35
最新文章
- STM32和OpenMV在通信时,如何避免数据包丢失或错误?
在STM32和OpenMV之间的通信中,数据包丢失或错误可能会导致整个系统不稳定或失效。为了避免这些问题,可以采取以下措施: 1. 确保硬件连接正确 首先,检查物理连接是否正确。STM32的TX(发送)应连接到OpenMV的…...
2024/5/6 4:33:24 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/3/20 10:50:27 - 一个浮动绝对居中的tailwindcss
今天改进图片组件,遇到个SVG绝对居中的问题。想起之前大概是通过top left来实现,由于组件的宽高需要动态输入。不能定死宽高,于是想起来问GPT。刚开始老是给一些很菜的代码,不是我想要的 气不打一处来,索性给他限死框框…...
2024/5/5 8:46:53 - VSCode上搭建C/C++开发环境(vscode配置c/c++环境)Windows系统---保姆级教程
引言劝退 VSCode,全称为Visual Studio Code,是由微软开发的一款轻量级,跨平台的代码编辑器。大家能来搜用VSCode配置c/c,想必也知道VSCode的强大,可以手握一个VSCode同时编写如C,C,C#ÿ…...
2024/5/5 2:49:41 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/5/4 23:54:56 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/5/4 23:54:56 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/5/4 23:54:56 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/5/4 23:55:17 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/5/4 23:54:56 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/5/4 23:55:05 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/5/4 23:54:56 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/5/4 23:55:16 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/5/4 23:54:56 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/5/6 1:40:42 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/5/4 23:54:56 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/5/4 23:55:17 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/5/4 23:55:06 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/5/4 23:54:56 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/5/4 23:55:06 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/5/5 8:13:33 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/5/4 23:55:16 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/5/4 23:54:58 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/5/4 23:55:01 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/5/4 23:54:56 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57