常用工具类方法
背景:在cr代码的时候自己写的一串代码有一些问题。
String xxx=remoteClient.xxxservice();
if(xxx.isEmpty()){
xxxx;
}
主要有两个问题:
- 在远程方法调用的时候无法确定各种异常情况,可能由于网络原因或者超时等原因方法调用失败,没有捕获RpcException这个异常或者ioException 导致这个方法整体抛出异常而失败 。
- 其次对于String类等isEmpty()方法默认调用者不为null 会出现npe问题。对于这些判断的操作推荐使用工具类完成,不用自己造轮子
解决方案:
- 远程方法调用封装一层中间层,在中间层进行异常捕获,对于业务的重要程度(强依赖/弱依赖)来划分为抛出异常给调用方处理 还是返回统一默认值。若有业务异常提前抛出业务异常,并在最外层使用Exception进行兜底。
- 使用StringUtils.isBlank()这个工具类实现判断 内部实现 自动判断null 问题。在使用方法类方法的同时要考虑调用方法的含义以及是否便于理解。
后续总结:
对于常用的工具类需要有一定的理解,比如分页数量判断使用Lists.partition() 就可以避免自己代码计算。所以了解一些常用工具类很有必要。目前看到项目中常用的工具类有commons、utils、Guava。列出下面的一些常用方法。
Guava
Google Guava 概述
1、Guava 是一组来自 Google 的核心 Java 库,包括新的集合类型(如 multimap 和 multiset)、不可变集合、图形库以及用于并发、I/O、散列、缓存、原语、字符串等的实用程序!被广泛应用于 Google 的大多数 Java 项目中,也被许多其他公司广泛使用。
2、guava github 开源地址:github.com/google/guav…
3、官网用户手册:github.com/google/guav…
4、com.google.guava 依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
不可变集合与对象
1、制作对象的不可变副本是一种很好的防御性编程技术,不可变对象有许多优点,包括:
可供不受信任的库安全使用。线程安全:可由多个线程使用,无争用风险。不需要支持突变,并且可以节省时间和空间,所有不可变的集合实现都比它们的可变同级更节省内存。可以用作常数,并期望它将保持不变。
2、要点:每个 Guava 不可变集合实现都拒绝 null 值。Guava 的设计上推荐使用 null 值,大多数情况下,遇到 null 值会抛异常.
3、一个不可变的 ImmutableXxx 集合可以通过以下几种方式创建:
使用 copyOf 方法,如 ImmutableSet.copyOf(set)使用 of 方法, 如 ImmutableSet.of("a", "b", "c") 或 ImmutableMap.of("a", 1, "b", 2)使用 Builder 方法,。
4、Guava 为 java jdk 每种标准集合类型提供了简单易用的不可变版本,包括 Guava 自己的集合变体,为 java 提供的不可变版本都是继承 java jdk 的接口而来,所以操作上基本无异。下面接口的实现类也都有对应的不可变版本。
| 接口 | JDK 或者 Guava | 不可变版本 |
|---|---|---|
Collection | JDK | ImmutableCollection |
List | JDK | ImmutableList |
Set | JDK | ImmutableSet |
SortedSet/NavigableSet | JDK | ImmutableSortedSet |
Map | JDK | ImmutableMap |
SortedMap | JDK | ImmutableSortedMap |
Multiset | Guava | ImmutableMultiset |
SortedMultiset | Guava | ImmutableSortedMultiset |
Multimap | Guava | ImmutableMultimap |
ListMultimap | Guava | ImmutableListMultimap |
SetMultimap | Guava | ImmutableSetMultimap |
BiMap | Guava | ImmutableBiMap |
ClassToInstanceMap | Guava | ImmutableClassToInstanceMap |
Table | Guava | ImmutableTable |
在线演示源码:github.com/main/java/c…
Guava 新集合类型
1、Guava 引入了许多新的集合类型,这些类型不在 Java JDK 中,但却非常有用,这些都是为了与 JDK 集合框架愉快地共存而设计的,而不是将东西塞进 JDK 集合抽象中。
Multiset 可重复集合
1、Guava 提供了一个新的集合类型 Multiset,它支持添加多个相同的元素,其中成员可以出现不止一次。
2、Multiset 相当于 Set,区别在于 Multiset 可添加相同的元素,它的内部使用一个 HashMap 来维护,
3、Multiset 也有自己的实现类,常用的有 HashMultiset、LinkedHashMultiset、TreeMultiset 等,HashMultiset 、TreeMultiset 是无序的,LinkedHashMultiset 是有序的,操作完全同理 JDK 的 HashSet、TreeSet、LinkedHashSet。
在线演示源码:github.com/wangmaoxion…
Multimap 多重映射
1、每个有经验的 Java 程序员都曾在某个地方实现过 Map<K、List> 或 Map<K、Set>,Guava 的 Multimap 框架使处理从键到多个值的映射变得容易,多重映射是将键与任意多个值关联的一种通用方法。
2、从概念上讲,有两种方法可以将多重映射视为从单个键到单个值的映射的集合:
a -> 1 a -> [1, 2, 4]
a -> 2 b -> [3]
方式1 a -> 4 方式2 c -> [5]
b -> 3
c -> 5
3、Multimap 提供了多种实现:
| Multimap 实现 | key 使用的是 | value 使用的是 |
|---|---|---|
ArrayListMultimap | HashMap | ArrayList |
HashMultimap | HashMap | HashSet |
LinkedListMultimap * | LinkedHashMap``* | LinkedList``* |
LinkedHashMultimap** | LinkedHashMap | LinkedHashSet |
TreeMultimap | TreeMap | TreeSet |
ImmutableListMultimap | ImmutableMap | ImmutableList |
ImmutableSetMultimap | ImmutableMap | ImmutableSet |
4、除了不可变的实现之外,每个实现都支持空键和值。并不是所有的实现都是作为一个Map<K,Collection>实现的(特别是一些Multimap实现使用自定义哈希表来最小化开销。)
在线演示源码:github.com/wangmaoxion…
BiMap 双向映射
1、将值映射回键的传统方法是维护两个独立的映射,并使它们保持同步,但这很容易产生错误,并且当映射中已经存在一个值时,可能会非常混乱。例如:
Map<String, Integer> nameToId = Maps.newHashMap();
Map<Integer, String> idToName = Maps.newHashMap();
nameToId.put("Bob", 42);
idToName.put(42, "Bob");
2、BiMap 提供了多种实现:
| 键值映射实现 | 值键映射实现 | 对应BiMap |
|---|---|---|
HashMap | HashMap | HashBiMap |
ImmutableMap | ImmutableMap | ImmutableBiMap |
EnumMap | EnumMap | EnumBiMap |
EnumMap | HashMap | EnumHashBiMap |
在线演示源码:github.com/wangmaoxion…
Table 表结构数据
1、当试图一次在多个键上建立索引时,您将得到类似 Map<FirstName,Map<LastName,Person>> 的代码,这很难看,而且使用起来很尴尬。Guava 提供了一个新的集合类型 Table,它支持任何“row”类型和“column”类型的这个用例。
2、Table 提供了多种实现:
HashBasedTable:基本上是由 HashMap<R,HashMap<C,V>> 支持的。TreeBasedTable:基本上是由 TreeMap<R,TreeMap<C,V>> 支撑的。ImmutableTableArrayTable:要求在构造时指定行和列的完整范围,但在表密集时由二维数组支持以提高速度和内存效率,ArrayTable的工作原理与其他实现有些不同
在线演示源码:github.com/wangmaoxion…
ClassToInstanceMap 类型映射到实例
1、有时 key 并不是单一的类型,而是多种类型,Guava 为此提供了 ClassToInstanceMap,key 可以是多种类型,value 是此类型的实例。
2、ClassToInstanceMap 的实现有: MutableClassToInstanceMap 和 ImmutableClassToInstanceMap 的实现。
在线演示源码:github.com/wangmaoxion…
JDK 集合辅助工具类
1、任何有 JDK 集合框架经验的程序员都知道并喜欢其中提供的实用程序 java.util.Collections,Guava 提供了许多适用于集合的静态方法实用程序。
| 接口 | 属于 JDK 还是 Guava | 对应 Guava API |
|---|---|---|
Collection | JDK | Collections2 |
List | JDK | Lists |
Set | JDK | Sets |
SortedSet | JDK | Sets |
Map | JDK | Maps |
SortedMap | JDK | Maps |
Queue | JDK | Queues |
Multiset | Guava | Multisets |
Multimap | Guava | Multimaps |
BiMap | Guava | Maps |
Table | Guava | Tables |
Lists 在线演示:github.com/wangmaoxion…
Sets 在线演示:github.com/wangmaoxion…
JDK 基本类型辅助工具类
1、Guava 为 Java JDK 的基本类型提供了实用程序类:
| 基本类型 | Guava 辅助工具类 |
|---|---|
byte | Bytes, SignedBytes, UnsignedBytes |
short | Shorts |
int | Ints, UnsignedInteger, UnsignedInts |
long | Longs, UnsignedLong, UnsignedLongs |
float | Floats |
double | Doubles |
char | Chars |
boolean | Booleans |
Ints 在线演示源码:github.com/wangmaoxion…
doubles 在线演示源码:github.com/src/main/ja…
booleans 在线演示源码:github.com/src/main/ja…
其它类型同理。
JDK 字符串辅助工具类
1、Strings 类中提供了少数几个常用的符串实用程序。在线演示源码。
2、Joiner 是连接器,用于连接 java.lang.Iterable、java.util.Iterator、java.lang.Object[] 中的元素。在线演示源码。
3、Splitter 是分割器,用于分割字符序列 java.lang.CharSequence,在线演示源码。
4、CharMatcher 字符匹配器,用于匹配字符,可以将 CharMatcher 视为代表一类特定的字符,如数字或空白。注意:CharMatcher 只处理 char 值。在线演示源码。
Objects类
Java 7 引入了 java.util.Objects 类。
就像 Arrays 是 Array 的工具类,Collections 是 Collection 的工具类,Objects 即为 Object 的工具类,包含 对象操作 的方法,其中大部分方法是 空指针安全 的。
requireNonNull 方法
requireNonNull 最初用于方法中的 参数校验,其定义如下:
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
public static <T> T requireNonNull(T obj, String message) {
if (obj == null)
throw new NullPointerException(message);
return obj;
}
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
if (obj == null)
throw new NullPointerException(messageSupplier.get());
return obj;
}
null 判断
有两个方法用于空指针判断,他们语义恰好相反:
isNullnonNull
它们实现如下:
public static boolean isNull(Object obj) {
return obj == null;
}
public static boolean nonNull(Object obj) {
return obj != null;
}
isNull和nonNull是被设计用做filter的谓语:filter(Objects::nonNull)。
toString 方法
public static String toString(Object o) {
return String.valueOf(o);
}
而 String.valueOf 实现如下:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
因此 Objects.toString 对于 null 参数,返回 null 字符串。
还可以指定参数为 null 时的返回值:
public static String toString(Object o, String nullDefault) {
return (o != null) ? o.toString() : nullDefault;
}
equals 方法
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
- 若
ab都是null,则为true;若只有一个为null,则为false
其余函数
Objects 中还有 compare hash hashCode 等其他函数,暂时没有用到。
常用工具类介绍
下面每个块的工具都是按我的使用频率排序的。
字符串处理
import org.apache.commons.lang3.StringUtils;
boolean StringUtils.isBlank(str); // " " 为 true
boolean StringUtils.isEmpty(str); // " " 为 false
boolean StringUtils.isNotBlank(str);
boolean StringUtils.isNotEmpty(str);
String[] StringUtils.split(str,char/String,max);//切割字符串,使用给定字符 ; max 主要是用来处理空内容的
String StringUtils.capitalize(str); //首字母大写
String StringUtils.uncapitalize(str); //首字母小写
boolean StringUtils.isNumeric(str);
boolean StringUtils.isNumericSpace(str); // "" 为true "12 3" 为 true
String StringUtils.join(array/Iterable,chat/String); // 类似于 js 的 join ,使用给定字符拼接数组或集合中的元素
String StringUtils.leftPad(str,size,padChar); //给左边拼接固定长度的字符
String StringUtils.rightPad(str,size,padChar); //右边拼接固定长度的字符
格式化信息
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
DateFormatUtils.format(Date/Calendar/long,pattern);
DateFormatUtils.ISO_DATE_FORMAT.format(Date/Calendar/long); // yyyy-MM-dd
DateFormatUtils.ISO_TIME_NO_T_FORMAT.format(Date/Calendar/long);// HH:mm:ss
DurationFormatUtils.formatDuration(long,format); //格式化毫秒值为指定格式
// 用于实体类 param,po,dto,vo
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this,ToStringStyle.SHORT_PREFIX_STYLE);
}
集合和数组的处理
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.collections.CollectionUtils;
boolean ArrayUtils.isEmpty(Array);
boolean ArrayUtils.isNotEmpty(Array);
boolean CollectionUtils.isEmpty(Collection);
boolean CollectionUtils.isNotEmpty(Collection);
Collection CollectionUtils.union(a,b); //并集
Collection CollectionUtils.intersection(a,b); //交集
Collection CollectionUtils.disjunction(a,b); //补集
Collection CollectionUtils.subtract(a,b); //差集
日期处理
import org.apache.commons.lang3.time.DateUtils;
Date DateUtils.parseDate(str,parsePatterns);
boolean DateUtils.isSameDay(Date,Date); //判断两个日期是否是同一天
Date DateUtils.truncate(Date,field); //日期截取,只取指定日期字段的值
Date DateUtils.round(Date,field); // 日期四舍五入,和 truncate 的区别是 如果用 YEAR 当前时间是 2019-08-18 truncate 的值还是 2019 ,但 round 会得到 2020
Date DateUtils.addDays(Date,amount); //增加一天,这个不会修改原来的日期值
还有 addYears,addMonths,addWeeks,addHours,addMinutes,addSeconds,addMilliseconds
Date DateUtils.setDays(Date,amount); //设置为本月的第几天
还有 setYears,setMonths,setHours,setMinutes,setSeconds,setMilliseconds
/*
计算已过去的天数,从哪儿开始算呢,根据第2个参数fragment来确定,
如现在是2014-10-23 13:27:00,那么
DateUtils.getFragmentInDays(new Date(), Calendar.MONTH)返回23,表示从当月起已经过去23天了,
DateUtils.getFragmentInDays(new Date(), Calendar.YEAR)返回296,表示从当年起已经过去296天了,
DateUtils.getFragmentInHours(new Date(), Calendar.DATE)返回13,表示从今天起已经过去13个小时了
*/
long DateUtils.getFragmentInDays(Date,fragment); //计算已经过去的天数
数字的一些处理
import org.apache.commons.lang3.math.NumberUtils;
int NumberUtils.toInt(String);
还有 toLong,toDouble,toFloat,toShort 等
boolean NumberUtils.isDigits(String); //判断是否全由数字组成 "" 为 false
boolean NumberUtils.isNumber(String); // 支持 0x 类的表达形式
增强反射处理
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.util.ReflectionUtils;
Class<?> ClassUtils.getClass(className);
boolean ClassUtils.isInnerClass(Class<?>); //是否是内部类
boolean ClassUtils.isPrimitiveOrWrapper(Class<?>); //判断是否是原始类型或原始类型的包装类
List<Method> MethodUtils.getMethodsListWithAnnotation( Class<?>, Class<? extends Annotation>);
Method ReflectionUtils.findMethod(Class<?> clazz,methodName, Class<?>...);
PropertyDescriptor[] ReflectUtils.getBeanGetters(Class type);
Object MethodUtils.invokeMethod(object,methodName,args[],values[]); //调用方法
IO 流,文件相关方法
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
String IOUtils.toString(InputStream,encoding); //文本文件流读出字符串
List<String> IOUtils.readLines(InputStream,encoding); //文本文件读出 List<String>
int IOUtils.copy(InputStream, OutputStream);
void FileUtils.writeStringToFile(File, String, Charset);
void FileUtils.deleteDirectory(File);
String FilenameUtils.getBaseName(filename); //获取文件名
String FilenameUtils.getExtension(filename); //获取扩展名
String FilenameUtils.separatorsToUnix(String path); //转成 linux 路径分隔符
加解密相关
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
byte[] Base64.encodeBase64(byte []);
byte[] Base64.decodeBase64(byte[]);
char [] Hex.encodeHex(byte []);
byte [] Hex.decodeHex(char []);
String DigestUtils.md5Hex(String);
String DigestUtils.shaHex(String);
随机数生成
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.RandomStringUtils;
int RandomUtils.nextInt(start,end);
long RandomUtils.nextLong(start,end);
String RandomStringUtils.randomNumeric(count);
String RandomStringUtils.randomAlphabetic(count);
其它可以用到的工具
// 秒表
import org.apache.commons.lang3.time.StopWatch;
StopWatch stopWatch = new StopWatch();
stopWatch.start();
stopWatch.getTime();
stopWatch.stop();
//spring 获取方法参数名称
import org.springframework.core.ParameterNameDiscoverer;
ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
//spring 用于处理逗号分隔符的字符串列表方法
import org.springframework.util.StringUtils;
String [] StringUtils.tokenizeToStringArray(String str, String delimiters);