JAVA-总结
- (1)总结
- 1.所有的int都用Integer类型,包括出参和入参
- 2.写完代码git提交记得格式化
- 3.对于所有的校验,如果不通过,一定要返回错误的Result
- 4.代码不要重复调用,应该加一个变量接收,命名需规范-驼峰命名
- 5.只有一个参数的情况下,就不要用对象传递,一般参数 >3个才用对象,直接用参数Date或String即可
- 6.所有的类型字段,状态字段,如果值超过2个,需要加枚举对象
- 7.多个写操作的语句,需要加事务
- 8.Mapper批量操作规范
- 9.XML中Date类型一定要加jdbcType=DATE
- 10.引用yml中配置的值
- 11.什么时候定义code?
- 12.Pattern(正则表达式)
- 13.对于接口传入的参数都要进行校验
- 14.分页排序时,排序不要有二义性,二义性情况下可能会导致分页结果乱序,可以在后面追加一个主键排序
- 15.判断相等或不等时尽量使用equals
- 16.循环里面不要涉及到与其他控件(数据库,mq(消息队列),外部应用之类的)交互,尽量一次性批量查询
- 17.@JsonProperty和@JSONField
- 18.Redis - increment 递增方法 | 处理防重复和并发问题
- 19. MySQL for update使用详解
- 20. args用来匹配方法参数。
- 21. MDC(Mapped Diagnostic Context,映射调试上下文)
- (2)问题
(1)总结
1.所有的int都用Integer类型,包括出参和入参
2.写完代码git提交记得格式化
3.对于所有的校验,如果不通过,一定要返回错误的Result
4.代码不要重复调用,应该加一个变量接收,命名需规范-驼峰命名
5.只有一个参数的情况下,就不要用对象传递,一般参数 >3个才用对象,直接用参数Date或String即可
对象传递需进行转换
传入:req->dto
传出:bo->resp
6.所有的类型字段,状态字段,如果值超过2个,需要加枚举对象
枚举遍历
StateEnum[] stateEnums = StateEnum.values();
for (StateEnum stateEnum : stateEnums) {
if(stateEnum.getCode()) {...}
}
7.多个写操作的语句,需要加事务
@Transactional 事务注解
throw new BusinessException(“msg”);
手动抛出自定义异常;
异常拦截器;
@AdminOptLogTitle(“预约操作接口”)
查询类的不需要加上这个注解,只有写操作,或者有敏感操作才加,加了这个操作就代表要存入数据库记录;
8.Mapper批量操作规范
批量增加,删除,更新
<insert id="insertList" parameterType="java.util.List">
insert into reserve_rule_config (rule_date, rule_start_tm, rule_end_tm, rule_number) values
<foreach collection="list" separator="," item="item">
(#{item.ruleDate,jdbcType=DATE}, #{item.ruleStartTm,jdbcType=VARCHAR},
#{item.ruleEndTm,jdbcType=VARCHAR},#{item.ruleNumber,jdbcType=INTEGER})
</foreach>
</insert>
<delete id="deleteList" parameterType="java.util.List">
delete from reserve_rule_config
where reserve_rule_config_id in (
<foreach collection="list" separator="," item="item">
#{item}
</foreach>
)
</delete>
<update id="updateRecords" parameterType="java.util.List">
<foreach collection="list" item="item" separator=";">
update reserve_rule_config
<set>
<if test="item.ruleStartTm != null">
rule_start_tm = #{item.ruleStartTm,jdbcType=VARCHAR},
</if>
<if test="item.ruleEndTm != null">
rule_end_tm = #{item.ruleEndTm,jdbcType=VARCHAR},
</if>
</set>
where reserve_rule_config_id = #{item.reserveRuleConfigId}
</foreach>
</update>
9.XML中Date类型一定要加jdbcType=DATE
rule_date = #{ruleDate,jdbcType=DATE}
10.引用yml中配置的值
@Value("${spring.profiles.active}")
private String profiles;
11.什么时候定义code?
1.前端需要这个code做一些交互,或者提示,或者跳转,前端需要通过code写逻辑代码,比如code=401,前端要跳转到登陆页面
2.这个code很特殊,通过code方便直观的定位问题,比如通过code直接查询文档,知道报错原因,或者通过code的规则知道是哪个模块的报错,去找对应模块开发人员等
3.通用系统类code(如系统异常,服务器异常请重试),通用业务类code(用户不存在,状态已更新请刷新)
12.Pattern(正则表达式)
指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建 Matcher 对象,依照正则表达式,该对象可以与任意字符序列匹配。
典型的调用顺序
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();
在仅使用一次正则表达式时,可以方便地通过此类定义 matches方法。此方法编译表达式并在单个调用中将输入序列与其匹配。
boolean b = Pattern.matches("a*b", "aaaaab");
等效于上面的三个语句,尽管对于重复的匹配而言它效率不高,因为它不允许重用已编译的模式。
13.对于接口传入的参数都要进行校验
为空校验、匹配校验、重合校验等,避免恶意调接口。
14.分页排序时,排序不要有二义性,二义性情况下可能会导致分页结果乱序,可以在后面追加一个主键排序
order by reserve_date desc,reserve_record_id
15.判断相等或不等时尽量使用equals
如果使用==或!=判断interger类型,当interger 类型的数据超过128的时候,此时保存的是引用地址,这时候相同的值比较也会报false,用equals就不会有问题。
16.循环里面不要涉及到与其他控件(数据库,mq(消息队列),外部应用之类的)交互,尽量一次性批量查询
网络通信是很耗时的,单个查询可能会忽略不计,在循环里面查询就会很慢
17.@JsonProperty和@JSONField
2个注解都是为了解决json字符串的某些属性名和JavaBean中的属性名匹配不上的问题。
- @JsonProperty
该注解为jackson包下的,在starter-web启动器下已经存在。
使用方法,在bean属性或方法上加上该注解, 出现重复字段解决办法,@JsonProperty改为加在get方法上。 - @JSONField
该注解为fastjson包下的,启动类配置fastjson转换(如果仅仅是内部使用转换json字符串则不需要配置) - 需要注意的是在使用这2个注解进行转换时必须使用相应的方法否则不起作用(fastjson、jackson)
fastjson忽略属性注解为@JSONField(serialize = false)
jackson忽略属性注解为@JsonIgnore
18.Redis - increment 递增方法 | 处理防重复和并发问题
什么是increment?
Redis 的 INCR 命令将key中存储的数字值递增。如果key不存在,那么key的值会先被初始化为0,然后在执行 INCR 操作。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。本操作的值限制在 64 位(bit)有符号数字表示之内。
问题:redis是单线程操作,上图获取和插入是两个原子操作,并发可能会获取到相同的值。
解决:使用redis自带的自增函数increment()。
19. MySQL for update使用详解
- 排他锁(S):select * from table_name where … for update;
for update是在数据库中上锁用的,可以为数据库中的行上一个排它锁。当一个事务的操作未完成时候,其他事务可以读取但是不能写入或更新。
for update 和 for update nowait区别(前者阻塞其他事务,后者拒绝其他事务)
for update锁住表或者锁住行,只允许当前事务进行操作(读写),其他事务被阻塞,直到当前事务提交或者回滚,被阻塞的事务自动执行
for update nowait 锁住表或者锁住行,只允许当前事务进行操作(读写),其他事务被拒绝,事务占据的statement连接也会被断开
20. args用来匹配方法参数。
SpringBoot使用AOP,PointCut详解 @Around(“paramCheckPointCut() && args(…, param)”)
- args() : 匹配不带参数的方法。
- args(java.lang.String) : 匹配方法参数是String类型的。
- args(…) :带任意参数的方法。
- args(java.lang.String,…) :匹配第一个参数是String类型的,其他参数任意。
21. MDC(Mapped Diagnostic Context,映射调试上下文)
MDC 是 log4j 、logback及log4j2 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。
当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。
(2)问题
- 所以的实体类都必须实现序列化吗?
序列化 - 系统部署到linux服务器(非分布式)定时任务会重复执行两次?
分布式锁 - 分布式是否属于微服务?
答案是属于。微服务的意思也就是将模块拆分成一个独立的服务单元通过接口来实现数据的交互。但是微服务不一定是分布式,因为微服务的应用不一定是分散在多个服务器上,他也可以是同一个服务器。这也是分布式和微服务的一个细微差别。 - group_concat函数详解?
SELECT parking_order_id,GROUP_CONCAT(DISTINCT `job_number`) as `job_number`
FROM `order_peer` GROUP BY parking_order_id;
- mysql锁表原因及解决方法?
锁表的原理:数据库使用独占式封锁机制,当执行insert,update,delete语句时,对表进行锁住,直到发生commite 或者 回滚 或者退出数据库用户;
解决方法:
show full processlist;
查看进程id,然后用kill id杀掉进程
- 聚簇索引的概念,回表操作?
- 惰性删除方案?
- 接口返回数据,为什么有一个或多个?
带宽限制,高并发导致服务器崩; - 面向过程编程—>面向对象编程?
校验类的封装一个方法,通用类的封装public方法,独立逻辑的封装private方法。 - SpringBoot配置LocalDateTime自动返回前端时间戳
直接在yml配置,前后端统一使用13位毫秒级时间戳进行交互。
spring:
jackson:
serialization:
# Date返回前端转时间戳
write-dates-as-timestamps: true
- feign远程调用失败
okhttp 和 HttpURLConnection 不支持 @RequestBody + RequestMethod.GET,只有 httpclient 支持,默认使用 HttpURLConnection。
解决方法:更改为Post请求,使用 httpclient。=
feign:
hystrix:
enabled: true
okhttp:
enabled: false
httpclient:
enabled: true
- 为什么使用RequestBody只能读取一遍请求数据流?
那是因为流对应的是数据,数据放在内存中,有的是部分放在内存中。read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。 所以这就是为什么读了一次第二次是空了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。javaAPI中有一个方法public void reset() 这个方法就是可以重置pos为起始位置,但是不是所有的IO读取流都可以调用该方法!ServletInputStream是不能调用reset方法,这就导致了只能调用一次getInputStream()。
解决办法:重写HttpServletRequestWrapper方法
这种方法就是通过重写HttpServletRequestWrapper把request的保存下来,然后通过过滤器保存下来的request在填充进去,这样就可以多次读取request了。 - 为什么表单Content-Type为multipart/form-data时上传文件,后台数据的接收为空?
request 特性
1、在form表单提交是默认类型的时候可以通过request.getParameter()获取请求参数。但是当请求类型不是默认类型(例如enctype="multipart/form-data"类型,上传一个文件)的时候,需调用request.getInputStream()或request.getReader()方法来获取请求内容值。
2、 request.getParameter(),request.getInputStream(),request.getReader()三个方法是不能共存的。调用其中一个就会影响剩下两个方法的使用,会导致request中的字节流为空。导致后面再次调用的时候读取不到。
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
// 读取输入流里的请求参数,并保存到bytes里
bytes = IOUtils.toByteArray(request.getInputStream());
}
- Copy ‘xxx‘ to effectively final temp variable 问题解决
- 便用if函数对变量进行了校验,编译后发现报错了。筛查后发现,在这块代码下有一处匿名内部类调用了该变量。
出错的原因是:内部类对象的生命周期会超过局部变量的生命周期。
所以在匿名内部类中,只能调用被final修饰的局部变量,而在java 8中,如果局部变量被匿名内部类访问,那么该变量会自动被final隐性修饰。Lambda大致同理。 - SpringCloud Feign 的 @RequestParam 长度过长,导致服务降级
@RequestParam会经过Query String的方式,即便设置了提交方式是post也会将参数拼接在url的后面,然而url传参是有长度限制的(类似于Get请求),会截断后面的参数,致使请求失败 。
解决方法:把@RequestParam 的参数全部封装成一个对象,用@RequestBody 方式对该对象进行请求,这样就能解决长度过长的问题。 - maven依赖死活都下载不了
- 解决方法:项目想下载一个依赖,在idea中死都下不了,查看网上各种解决方案都没有效果,使用命令下载导入到maven仓库。先去中央仓库,下好包,使用cmd打开命令行输入。
mvn install:install-file -Dfile=d:\env\logback-kafka-appender-0.2.0-RC2.jar -DgroupId=com.github.danielwegener -DartifactId=logback-kafka-appender -Dversion=0.2.0-RC2 -Dpackaging=jar