SpringBoot内置工具类应有尽有

在java的庞大体系中,其实有很多不错的小工具,也就是我们平常说的:轮子。 如果在我们的日常工作当中,能够将这些轮子用户,再配合一下idea的快捷键,可以极大得提升我们的开发效率。 今天我决定把一些压箱底的小工具,分享给大家,希望对你有所帮助。 首先出场的是java.util包下的Collections类,该类主要用于操作集合或者返回集合,我个人非常喜欢用它。 1.1 排序 在工作中经常有对集合排序的需求。 看看使用Collections工具是如何实现升序和降序的:  List<Integer> list = new ArrayList<>();  list.add(2);  list.add(1);  list.add(3);  Collections.sort(list);//升序  System.out.println(list);  Collections.reverse(list);//降序  System.out.println(list); 执行结果:[1, 2, 3][3, 2, 1] 1.2 获取最大或最小值 有时候需要找出集合中的最大值或者最小值,这时可以使用Collections的max和min方法。例如:List<Integer> list = new ArrayList<>();list.add(2);list.add(1);list.add(3);Integer max = Collections.max(list);//获取最大值Integer min = Collections.min(list);//获取最小值System.out.println(max);System.out.println(min); 执行结果:31 1.3 转换线程安全集合 我们都知道,java中的很多集合,比如:ArrayList、LinkedList、HashMap、HashSet等,都是线程不安全的。 换句话说,这些集合在多线程的环境中,添加数据会出现异常。 这时,可以用Collections的synchronizedxxx方法,将这些线程不安全的集合,直接转换成线程安全集合。例如:  List<Integer> list = new ArrayList<>();  list.add(2);  list.add(1);  list.add(3);   List<Integer> integers = Collections.synchronizedList(list);//将ArrayList转换成线程安全集合  System.out.println(integers); 它的底层会创建SynchronizedRandomAccessList或者SynchronizedList类,这两个类的很多方法都会用synchronized加锁。 1.4 返回空集合 有时,我们在判空之后,需要返回空集合,就可以使用emptyList方法,例如:private List<Integer> fun(List<Integer> list) {    if (list == null || list.size() == 0) {        return Collections.emptyList();    }    //业务处理    return list;} 1.5 二分查找 binarySearch方法提供了一个非常好用的二分查找功能,只用传入指定集合和需要找到的key即可。例如:List<Integer> list = new ArrayList<>();list.add(2);list.add(1);list.add(3); int i = Collections.binarySearch(list, 3);//二分查找System.out.println(i ); 执行结果:2 1.6 转换成不可修改集合 为了防止后续的程序把某个集合的结果修改了,有时候我们需要把某个集合定义成不可修改的,使用Collections的unmodifiablexxx方法就能轻松实现:List<Integer> list = new ArrayList<>();list.add(2);list.add(1);list.add(3); List<Integer> integers = Collections.unmodifiableList(list);integers.add(4);System.out.println(integers); 执行结果:Exception in thread “main” java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableCollection.add(Collections.java:1055) at com.sue.jump.service.test1.UtilTest.main(UtilTest.java:19) 当然Collections工具类中还有很多常用的方法,在这里就不一一介绍了,需要你自己去探索。 对集合操作,除了前面说的Collections工具类之后,CollectionUtils工具类也非常常用。 目前比较主流的是spring的org.springframework.util包下的CollectionUtils工具类。 和apache的org.apache.commons.collections包下的CollectionUtils工具类。 我个人更推荐使用apache的包下的CollectionUtils工具类,因为它的工具更多更全面。 举个简单的例子,spring的CollectionUtils工具类没有判断集合不为空的方法。而apache的CollectionUtils工具类却有。 下面我们以apache的CollectionUtils工具类为例,介绍一下常用方法。 2.1 集合判空 通过CollectionUtils工具类的isEmpty方法可以轻松判断集合是否为空,isNotEmpty方法判断集合不为空。List<Integer> list = new ArrayList<>();list.add(2);list.add(1);list.add(3); if (CollectionUtils.isEmpty(list)) {    System.out.println(“集合为空”);} if (CollectionUtils.isNotEmpty(list)) {    System.out.println(“集合不为空”);} 2.2 对两个集合进行操作 有时候我们需要对已有的两个集合进行操作,比如取交集或者并集等。List<Integer> list = new ArrayList<>();list.add(2);list.add(1);list.add(3); List<Integer> list2 = new ArrayList<>();list2.add(2);list2.add(4); //获取并集Collection<Integer> unionList = CollectionUtils.union(list, list2);System.out.println(unionList); //获取交集Collection<Integer> intersectionList = CollectionUtils.intersection(list, list2);System.out.println(intersectionList); //获取交集的补集Collection<Integer> disjunctionList = CollectionUtils.disjunction(list, list2);System.out.println(disjunctionList); //获取差集Collection<Integer> subtractList = CollectionUtils.subtract(list, list2);System.out.println(subtractList); 执行结果:[1, 2, 3, 4][2][1, 3, 4][1, 3] 说句实话,对两个集合的操作,在实际工作中用得挺多的,特别是很多批量的场景中。以前我们需要写一堆代码,但没想到有现成的轮子。 […]

SpringBoot 内置工具类

断言 断言是一个逻辑判断,用于检查不应该发生的情况 Assert 关键字在 JDK1.4 中引入,可通过 JVM 参数-enableassertions开启 SpringBoot 中提供了 Assert 断言工具类,通常用于数据合法性检查 // 要求参数 object 必须为非空(Not Null),否则抛出异常,不予放行  // 参数 message 参数用于定制异常信息。  void notNull(Object object, String message)  // 要求参数必须空(Null),否则抛出异常,不予『放行』。  // 和 notNull() 方法断言规则相反  void isNull(Object object, String message)  // 要求参数必须为真(True),否则抛出异常,不予『放行』。  void isTrue(boolean expression, String message)  // 要求参数(List/Set)必须非空(Not Empty),否则抛出异常,不予放行  void notEmpty(Collection collection, String message)  // 要求参数(String)必须有长度(即,Not Empty),否则抛出异常,不予放行  void hasLength(String text, String message)  // 要求参数(String)必须有内容(即,Not Blank),否则抛出异常,不予放行  void hasText(String text, String message)  // 要求参数是指定类型的实例,否则抛出异常,不予放行  void isInstanceOf(Class type, Object obj, String message)  // 要求参数 `subType` 必须是参数 superType 的子类或实现类,否则抛出异常,不予放行  void isAssignable(Class superType, Class subType, String message)   对象、数组、集合 ObjectUtils 获取对象的基本信息 // 获取对象的类名。参数为 null 时,返回字符串:”null” String nullSafeClassName(Object obj) // 参数为 null 时,返回 0 int nullSafeHashCode(Object object) // 参数为 null 时,返回字符串:”null” String nullSafeToString(boolean[] array) // 获取对象 HashCode(十六进制形式字符串)。参数为 null 时,返回 0 String getIdentityHexString(Object obj) // 获取对象的类名和 HashCode。 参数为 null 时,返回字符串:”” […]

每天 100 万次登陆请求,8G 内存该如何设置 JVM 参数?

在阿里云技术面终面的时候被问到这么一个问题:假设一个每天 100 万次登陆请求的平台,一个服务节点 8G 内存,该如何设置JVM参数?下面以面试题的形式给大家梳理出来,做到一箭双雕:既供大家实操参考,又供大家面试参考。大家要学习的,除了 JVM 配置方案之外,是其分析问题的思路、思考问题的视角。这些思路和视角,能帮助大家走更远、更远。接下来,进入正题。每天 100 万次登陆请求,8G 内存该如何设置 JVM 参数,大概可以分为以下 8 个步骤 。 第一步、新系统上线如何规划容量? 1. 套路总结任何新的业务系统在上线以前都需要去估算服务器配置和 JVM 的内存参数,这个容量与资源规划并不仅仅是系统架构师的随意估算的,需要根据系统所在业务场景去估算,推断出来一个系统运行模型,评估 JVM 性能和 GC 频率等等指标。以下是我结合大牛经验以及自身实践来总结出来的一个建模步骤: 计算业务系统每秒钟创建的对象会佔用多大的内存空间,然后计算集群下的每个系统每秒的内存佔用空间(对象创建速度); 设置一个机器配置,估算新生代的空间,比较不同新生代大小之下,多久触发一次 MinorGC; 为了避免频繁 GC,就可以重新估算需要多少机器配置,部署多少台机器,给 JVM 多大内存空间,新生代多大空间; 根据这套配置,基本可以推算出整个系统的运行模型,每秒创建多少对象,1 秒以后成为垃圾,系统运行多久新生代会触发一次 GC,频率多高。 2. 套路实战:以登录系统为例有些同学看到这些步骤还是发憷,说的好像是那么回事,一到实际项目中到底怎么做我还是不知道。光说不练假把式,以登录系统为例模拟一下推演过程: 假设每天 100 万次登陆请求,登陆峰值在早上,预估峰值时期每秒 100 次登陆请求; 假设部署 3 台服务器,每台机器每秒处理 30 次登陆请求。假设一个登陆请求需要处理 1 秒钟,JVM 新生代里每秒就要生成 30 个登陆对象,1 秒之后请求完毕这些对象成为了垃圾; 一个登陆请求对象假设 20 个字段,一个对象估算 500 […]

Java对象为啥要实现Serializable接口?

最近这段时间一直在忙着编写Java业务代码,麻木地搬着Ctrl-C、Ctrl-V的砖,在不知道重复了多少次定义Java实体对象时“implements Serializable”的C/V大法后,脑海中突然冒出一个思维(A):问了自己一句“Java实体对象为什么一定要实现Serializable接口呢?”,关于这个问题,脑海中的另一个思维(B)立马给出了回复“居然问这么幼稚和基础的问题,实现Serilizable接口是为了序列化啊!”,思维(A):“哦,好吧!然而,然后呢?” 此时思维(B)陷入了沉默,突然感觉自己有点浅薄了,好像写了这么多年Java还真是没有太关注过Serializable这个接口!为什么一定要实现Serializable接口?它的底层原理是什么?为什么一定要序列化,序列化又是什么?关于这些问题,不知道各位读者朋友有没有过类似的问题,如果有那么我们就在这篇文章中一起寻找答案吧!当然,如果你对这些问题都很清楚,也欢迎表达看法! Serializable接口概述 Serializable是http://java.io包中定义的、用于实现Java类的序列化操作而提供的一个语义级别的接口。Serializable序列化接口没有任何方法或者字段,只是用于标识可序列化的语义。实现了Serializable接口的类可以被ObjectOutputStream转换为字节流,同时也可以通过ObjectInputStream再将其解析为对象。例如,我们可以将序列化对象写入文件后,再次从文件中读取它并反序列化成对象,也就是说,可以使用表示对象及其数据的类型信息和字节在内存中重新创建对象。 而这一点对于面向对象的编程语言来说是非常重要的,因为无论什么编程语言,其底层涉及IO操作的部分还是由操作系统其帮其完成的,而底层IO操作都是以字节流的方式进行的,所以写操作都涉及将编程语言数据类型转换为字节流,而读操作则又涉及将字节流转化为编程语言类型的特定数据类型。而Java作为一门面向对象的编程语言,对象作为其主要数据的类型载体,为了完成对象数据的读写操作,也就需要一种方式来让JVM知道在进行IO操作时如何将对象数据转换为字节流,以及如何将字节流数据转换为特定的对象,而Serializable接口就承担了这样一个角色。 下面我们可以通过例子来实现将序列化的对象存储到文件,然后再将其从文件中反序列化为对象,代码示例如下: 先定义一个序列化对象User: public class User implements Serializable { private static final long serialVersionUID = 1L; private String userId; private String userName; public User(String userId, String userName) { this.userId = userId; this.userName = userName; } } 然后我们编写测试类,来对该对象进行读写操作,我们先测试将该对象写入一个文件: public class SerializableTest { /** * 将User对象作为文本写入磁盘 */ public static void writeObj() { […]

Spring Boot中使用AOP统一处理Web请求日志

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是Spring框架中的一个重要内容,它通过对既有程序定义一个切入点,然后在其前后切入不同的执行内容,比如常见的有:打开数据库连接/关闭数据库连接、打开事务/关闭事务、记录日志等。基于AOP不会破坏原来程序逻辑,因此它可以很好的对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。 下面主要讲两个内容,一个是如何在Spring Boot中引入Aop功能,二是如何使用Aop做切面去统一处理Web请求的日志。 准备工作 因为需要对web请求做切面来记录日志,所以先引入web模块,并创建一个简单的hello请求的处理。 pom.xml中引入web模块 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 实现一个简单请求处理:通过传入name参数,返回“hello xxx”的功能。 @RestController public class HelloController { @RequestMapping(value = “/hello”, method = RequestMethod.GET) @ResponseBody public String hello(@RequestParam String name) { return “Hello ” + name; } } 下面,我们可以对上面的/hello请求,进行切面日志记录。 引入AOP依赖 在Spring Boot中引入AOP就跟引入其他模块一样,非常简单,只需要在pom.xml中加入如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 在完成了引入AOP依赖包后,一般来说并不需要去做其他配置。也许在Spring中使用过注解配置方式的人会问是否需要在程序主类中增加@EnableAspectJAutoProxy来启用,实际并不需要。 可以看下面关于AOP的默认配置属性,其中spring.aop.auto属性默认是开启的,也就是说只要引入了AOP依赖后,默认已经增加了@EnableAspectJAutoProxy。 # AOP spring.aop.auto=true # Add @EnableAspectJAutoProxy. […]

java 泛型详解

对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。 本文参考java 泛型详解、Java中的泛型方法、 java泛型详解 1. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。 2. 一个栗子 一个被举了无数次的例子: List arrayList = new ArrayList(); arrayList.add(“aaaa”); arrayList.add(100); for(int i = 0; i< arrayList.size();i++){ String item = (String)arrayList.get(i); Log.d(“泛型测试”,”item = ” + item); } 毫无疑问,程序的运行结果会以崩溃结束: ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。 我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。 List arrayList = new ArrayList(); … //arrayList.add(100); 在编译阶段,编译器就会报错 3. 特性 泛型只在编译阶段有效。看下面的代码: List stringArrayList = new ArrayList(); List integerArrayList […]