目录
- 值传递&引用传递
- WeakHashMap-弱引用Map
- HashMap
- ResponseEntity
- MessageFormat.format-代码里直接打印输出
- 如何将 java 项目的依赖打成一个大的 jar 包
- 加载证书报错:Could not parse certificate: java.io.IOException: Incomplete data
- powermock使用注意
- jpa的使用注意
- AOP切面
- java中的流是否需要关闭
- 通过查询数据库的日志,来查看每个查询的执行次数以及执行时长等关键的性能数据
- Java SSH库
- WARNING! Using --password via the CLI is insecure. Use --password-stdin.
- windows 上生成指定大小的空文件,用于测试上传等
- Http状态码401和403的区别
- MultipartFile 配置临时存储目录
- 控制台执行jar程序的输入参数
- 运行 jar 包报 “XXX中没有主清单属性”
- java 的 properties 的基本操作
- 关于 try catch finally 语句异常被捕获和未被捕获发生的结果
- JPA 中使用了悲观锁,同时使用了事务的话,需要注意下面:
- 执行测试用例报错:No tests were found
值传递&引用传递
Java 中的传递,是值传递,而这个值,实际上是对象的引用。
1、传递的值在栈中,直接拷贝一份值传递,改变的形参不会对实参造成影响。
2、传递的值在栈中存放的是地址(引用),先根据栈中的地址找到在堆上的值,然后把地址拷贝一份(拷贝的地址是一个值),此时形参和实参指向堆上同一个地址,形参的修改导致了实参的改变。
注:java只有值传递,没有引用传递。
WeakHashMap-弱引用Map
我们都知道Java中内存是通过GC自动管理的,GC会在程序运行过程中自动判断哪些对象是可以被回收的,并在合适的时机进行内存释放。GC判断某个对象是否可被回收的依据是,是否有有效的引用指向该对象。如果没有有效引用指向该对象(基本意味着不存在访问该对象的方式),那么该对象就是可回收的。这里的 有效引用 并不包括 弱引用。也就是说,虽然弱引用可以用来访问对象,但进行垃圾回收时弱引用并不会被考虑在内,仅有弱引用指向的对象仍然会被GC回收。
WeakHashMap 内部是通过弱引用来管理 entry 的,弱引用的特性对应到 WeakHashMap 上意味着什么呢?
WeakHashMap 里的 entry 可能会被GC自动删除,即使程序员没有调用remove()
或者clear()
方法。
WeakHashMap 的这个特点特别适用于需要缓存的场景。在缓存场景下,由于内存是有限的,不能缓存所有对象;对象缓存命中可以提高系统效率,但缓存MISS也不会造成错误,因为可以通过计算重新得到。
HashMap
java1.7的 hashmap 基于哈希表实现,采用冲突链表方式,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)。
java1.8的 hashmap 基于数组+链表+红黑树,就是在上面的链表的元素达到了 8 个时,会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。
ResponseEntity
ResponseEntity是一种泛型类型。因此,我们可以使用任何类型作为响应主体,还可以设置返回的状态码、响应头等信息,比较方便。
因此建议将所有的 rest 接口的返回值外面包一层 ResponseEntity,如:ResponseEntity<String>
MessageFormat.format-代码里直接打印输出
在代码里面打印输出时,使用MessageFormat.format
,而不是String.format
public static void main(String[] args)throws Exception{
System.out.println(MessageFormat.format("{0}-{1}",11,22));
}
说明:感觉这种打印方式,比String.fromat
的要好,跟slf4j
日志框架的一致,更习惯。
如何将 java 项目的依赖打成一个大的 jar 包
Caused by: java.lang.ClassNotFoundException: org.apache.thrift.TException
报错,是因为打jar包的时候,没有将依赖一起打包,导致找不到相关的包,只是在pom.xml中引用不够的~
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
上面的插件,将依赖一起打成一个大的jar包,descriptorRef是jar包后面的描述信息,是jar包名称的一部分。
加载证书报错:Could not parse certificate: java.io.IOException: Incomplete data
ca 证书需要有换行符\n
,不是一连串的字符串,可以手动给每行后面补一个\n
,然后去 BeJson 上面进行压缩,注意开头和结尾的证书单词不要被压缩的合在一起了。
powermock使用注意
被测试类:
class A {
public void handle() {
B b = new B();
}
}
测试handle方法,而我不想真正去执行new B()操作,那么可以使用PowerMockito.whenNew:
@RunWith(PowerMockRunner.class)
@PrepareForTest({A.class})//此处写被测试类,而不是whenNew的类
public class ATest extends PowerMockTestCase {
@Test
public void testHandle() {
B b = PowerMockito.mock(B.class);
PowerMockito.whenNew(B.class).withNoArguments().thenReturn(b);
}
}
@PrepareForTest后面应该加上被测试的类,而不是要whenNew的类。
jpa的使用注意
JPA 的 save 方法被事务注解包裹,如果在 save 之后对 entity 的数据又做了操作,则会影响保存到数据库中的数据。
解决方案:可以使用深拷贝 BeanUtils.copyProperties(dbEntity, voEntity)
,对拷贝出来的对象进行操作,就不会影响 save 的数据了。
AOP切面
1、正常的执行顺序是:
@Around ->@Before->主方法体->@Around中pjp.proceed()->@After->@AfterReturning
2、异常的执行顺序是:
如果异常在 Around 中 pjp.proceed() 之前,则执行顺序为:@Around -> @After -> @AfterThrowing
如果异常在 Around 中 pjp.proceed() 之后,则执行顺序为:@Around -> @Before->主方法体->@Around中pjp.proceed()->@After->@AfterThrowing
java中的流是否需要关闭
通过查询数据库的日志,来查看每个查询的执行次数以及执行时长等关键的性能数据
1、gaussdb的日志: /var/log/operationlog/gaussdb
2、脚本:
#!/bin/bash
cd /var/log/operationlog/gaussdb
listfiles=$(ls /var/log/operationlog/gaussdb | grep -v current | grep -v syslog_label )
for file in $listfiles
do
cat $file |grep -w "duration"| grep execute | awk '{out=$1" "$2"|"$4"|"$5"|"$8"|"; for(i=12;i<=NF-1;i++){out=out" "$i}; print out}'| cut -d'[' -f 2 >> /home/GalaX8800/sqlduration2.log
done
3、查询某一段时间内的sql语句执行情况,将相同的查询统计在一起,记录计数项字段。
Java SSH库
1、Apache sshd
2、JSch(Java Secure Channel)
注:第二个没有人维护了,优先选择第一个,支持交互式命令行和非交互式命令行。
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
当使用 docker login -u xxx -p xx 时,报下面的警告:WARNING! Using --password via the CLI is insecure. Use --password-stdin.
解决方案:
1、只是警告而已啦,直接忽略不理就行。
2、将密码写入到一个文件中,例如 /etc/docker_passwd
文件,并使用以下命令执行登录:
cat /etc/docker_passwd | docker login --username 用户名 --password-stdin
windows 上生成指定大小的空文件,用于测试上传等
在 Linux 下可以通过 dd 命令创建一个固定大小的文件,而在 windows 下也同样可以,不过命令就不是 dd 了。
比如在 Windows 系统中瞬间生成一个 100M 的文件,如下操作:
进入命令提示符,然后输入:
fsutil file createnew test.txt 104857600
之后就会在当前命令提示符所在工作目录下生成了一个 100M 的 test.txt 文件了
其中 104857600=10010241024字节(Byte)
Http状态码401和403的区别
401 Unauthorized
状态码401标识认证失败,表示请求没有被认证或者认证失败。
通常由web服务器返回,而不是web应用。
场景:token失效、token缺失、token伪造,导致服务端无法识别身份。
403 Forbidden
状态码403表示授权失败,通常表示用户通过了身份验证,但缺少权限对给定的资源进行访问或者操作。
通常由web应用返回。
场景:用户登录成功,但是无权进行读写操作。
总结:401和403的主要区别在于
重点不同:401着重于认证,403着重于授权
返回对象不同:401通常由web服务器返回,403由web应用返回
场景不同:401表示用户未通过身份授权、验证,403表示用户可能通过了身份验证,但缺少指定权限
MultipartFile 配置临时存储目录
MultipartFile 其实只是一个接口,真正实现在org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.StandardMultipartFile
,
StandardMultipartFile 里面有个 javax.servlet.http.Part 对象,Part 也是一个接口,真正实现在org.apache.catalina.core.ApplicationPart
,
ApplicationPart 里有个org.apache.tomcat.util.http.fileupload.FileItem
接口和 File,FileItem 实现是org.apache.tomcat.util.http.fileupload.disk.DiskFileItem
,File 表示上传的文件(是一个临时存放文件)
DiskFileItem 是由org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory
创建出来的,
从 DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD = 10240 可以得知:
如果文件大小在 10KB 以内,则直接放在内存,如果大于 10KB (这个是可以配置的),则存储在本地磁盘中(注意是存在临时目录中,意味着这个文件如果你不持久化那这个文件随时会被系统清除)
解决方案:
1、写代码自定义临时文件目录。@Baen MultipartConfigElement
2、在配置文件中增加:server.tomcat.basedir:/your_dir
后面的 transferTo 动作,如果不超过 10KB,则是从内存中直接保存到目标位置,否则就是走文件系统的 mv 到目标位置,不过内存。
控制台执行jar程序的输入参数
> java -jar test.jar mama baba son
public static void main(String[] args)
args数组,长度为3,args[0] = mama, args[1] = baba, args[2] = son
运行 jar 包报 “XXX中没有主清单属性”
D:\hu-git\spring-xxx-xxx\target>java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar
spring-xxx-xxx-0.0.1-SNAPSHOT.jar中没有主清单属性
在这里有一个问题就是主清单属性是什么?
以 SpringBoot 为例,jar 包中包含了三个文件夹:BOOT-INF,META-INF,org,可以把 jar 包解压到文件夹下查看,其中 META-INF 文件夹下有一个MANIFEST.MF 文件,该文件指明了程序的入口以及版本信息等内容,如下:
Manifest-Version: 1.0
Implementation-Title: spring-xxx-xxx
Implementation-Version: 0.0.1-SNAPSHOT
Archiver-Version: Plexus Archiver
Built-By: XXXX
Implementation-Vendor-Id: com.huyikang.practice
Spring-Boot-Version: 1.5.9.RELEASE
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.JarLauncher // 代表了Spring Boot 中启动 jar 包的程序
Start-Class: com.huyikang.practice.eureka.Application // 代表了Spring Boot 程序的入口类,这个类中应该有一个 main 方法
Spring-Boot-Classes: BOOT-INF/classes/ // 代表了类的路径,所有编译后的class文件,以及配置文件,都存储在该路径下
Spring-Boot-Lib: BOOT-INF/lib/ // 表示依赖的jar包存储的位置
Created-By: Apache Maven 3.5.2
Build-Jdk: 1.8.0_151
Implementation-URL: http://maven.apache.org
这些值都是 SpringBoot 打包插件会默认生成的,如果没有这些属性,SpringBoot 程序自然不能运行,就会报错:jar 中没有主清单属性,也就是说没有按照SpringBoot 的要求,生成这些必须的属性。
解决办法:
在pom中添加一个 SpringBoot 的构建的插件,然后重新运行 mvn install 即可。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
在运行 mvn install 的时候,自动生成这些主清单属性,运行 java -jar xxx.jar 时会根据主清单属性找到启动类,从而启动程序。
PS:也可以手动的改一下上面的 MANIFEST.MF 文件,注明启动类之类的,但最好还是利用插件生成,保证正确性。
java 的 properties 的基本操作
1、默认值
@Value("${external.image.repository.num.limit:10}")
privateintexternalImageRepositoryNumLimit;
2、拆分数组
@Value("#{'${external.image.repository.forbidden.ports}'.split(',')}")
privateList<String>externalImageRepositoryForbiddenPortList;
3、读取配置
@Value("${external.image.repository.cert.check.cron}")
privateStringexternalImageRepositoryCertCheckCron;
关于 try catch finally 语句异常被捕获和未被捕获发生的结果
包括以下几种情况:
1、不抛出异常,try 里面的代码、finally 里面的代码、finally 以后的代码都将正常执行,而 catch 里面的代码不会执行。
2、抛出异常且被 catch 捕获,try 里面的代码部分执行,catch 里面的代码、finally 里面的代码、finally 以后的代码都将正常执行。
3、抛出异常,但未被 catch 捕获,抛出异常的语句将会报错,在 try 中的抛出异常的语句后的语句将不会执行,由于异常未被捕获,故 catch 语句不执行,但是finally 仍然会执行,在 finally 后面的语句由于程序已经报错停止,故将不会执行。
注意:finally 在这三种情况中,都正常执行,finally 永远会执行,除非 try 前面的语句报错。
JPA 中使用了悲观锁,同时使用了事务的话,需要注意下面:
当我们在事务中使用悲观锁并访问实体时,它将立即锁定,通过提交或回滚事务来释放锁。如果不提交或者不回滚的话,锁会一直在,除非重启程序,或者手动删除锁。
select pid,
state,
usename,
query,
query_start
from pg_stat_activity
where pid in (
select pid
from pg_locks l
join pg_class t on l.relation = t.oid
and t.relkind = 'r'
);
手动删除锁的方法(PostgreSql 适用):
删除锁:(第一步查出来的结果有 pid
// (只kill掉select)
SELECT pg_cancel_backend(pid)
// (kill掉select、update、insert、delete)
SELECT pg_terminate_backend(pid)
执行测试用例报错:No tests were found
IDEA中,右键选中某个测试类,可以 Run | Debug | Run with coverage(带覆盖率统计的),但是选中整个 package 再 Run 的时候,提示 “No tests were found”。
说明:
-
确定 junit 的版本,有 junit4 和 junit5 的区别,注意分析 pom 的依赖。
-
确定 class 和 functions 都是 public 的。
-
确定没有其他的测试框架影响,比如我在 krm/base-common 中就遇到了groovy 框架和 testng 两种,前一个导致无法右键整个包执行用例,后一个导致到流水线上执行未发生任何测试用例。去掉这两个框架的依赖就好了。