简单的删除controller如下:
@PostMapping("/delete")
public Map delete(long id, String lang) {
Map data = new HashMap();
boolean result = false;
try {
// 语言(中英文提示不同)
Locale local = "zh".equalsIgnoreCase(lang) ? Locale.CHINESE : Locale.ENGLISH;
result = configService.delete(id, local);
data.put("code", 0);
} catch (CheckException e) {
// 参数等校验出错,这类异常属于已知异常,不需要打印堆栈,返回码为-1
data.put("code", -1);
data.put("msg", e.getMessage());
} catch (Exception e) {
// 其他未知异常,需要打印堆栈分析用,返回码为99
log.error(e);
data.put("code", 99);
data.put("msg", e.toString());
}
data.put("result", result);
return data;
}
可以看到上面的代码写了很多和业务逻辑无关的内容,如返回值时Map,兼容了正常和错误的情况,处理了语言问题。如语言和异常处理的封装,完全可以抽出公共模块来做
大佬改成如下:
@PostMapping("/delete")
public ResultBean delete(long id) {
return new ResultBean(configService.delete(id));
}
很简单吧,用到的技术是AOP,也不高深,但是人家就能想到。技术无所谓高低,看你怎么用。
使用父类接口
举个例子,假设我们需要判断一个arrayList是否为空的函数,一开始是这样的。
public static boolean isEmpty(ArrayList> list) {
return list == null || list.size() == 0;
}
这个时候需要考虑一下,参数的类型能不能使用父类,我们看到只使用了size方法,我们可以知道size方法在list接口上有,于是我们修改成这样。
public static boolean isEmpty(List> list) {
return list == null || list.size() == 0;
}
后面发现,size方法在list的父类/接口Collection上也有,那么我们可以修改为最终这样
public static boolean isEmpty(Collection> list) {
return list == null || list.size() == 0;
}
到了这步,Collection没有父类/接口有size方法了,修改就可以结束了。最后我们需要把参数名字改一下,不要再使用list。改完后,所有实现了Collection的对象都可使用,最终版本如下:
public static boolean isEmpty(Collection> collection) {
return collection == null || collection.size() == 0;
}
使用重载编写衍生接口
举个例子:现在需要编写一个方法,输入是一个utf-8格式的文件的文件名,把里面内容输出到一个list《 String》。我们刚刚开始编写的时候,是这个样子的
public static List readFile2List(String filename) throws IOException {
List list = new ArrayList();
File file = new File(filename);
FileInputStream fileInputStream = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fileInputStream,
"UTF-8"));
// XXX操作
return list;
}
很显然,编码格式有可能被修改,可传的参数有File,FileInputStream等类型,所以我们需要都支持
package plm.common.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
/**
* 工具类编写范例,使用重载编写不同参数类型的函数组
*
* @author 晓风轻 https://github.com/xwjie/PLMCodeTemplate
*
*/
public class FileUtil {
private static final String DEFAULT_CHARSET = "UTF-8";
public static List readFile2List(String filename) throws IOException {
return readFile2List(filename, DEFAULT_CHARSET);
}
public static List readFile2List(String filename, String charset)
throws IOException {
FileInputStream fileInputStream = new FileInputStream(filename);
return readFile2List(fileInputStream, charset);
}
public static List readFile2List(File file) throws IOException {
return readFile2List(file, DEFAULT_CHARSET);
}
public static List readFile2List(File file, String charset)
throws IOException {
FileInputStream fileInputStream = new FileInputStream(file);
return readFile2List(fileInputStream, charset);
}
public static List readFile2List(InputStream fileInputStream)
throws IOException {
return readFile2List(fileInputStream, DEFAULT_CHARSET);
}
public static List readFile2List(InputStream inputStream, String charset)
throws IOException {
List list = new ArrayList();
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(inputStream, charset));
String s = null;
while ((s = br.readLine()) != null) {
list.add(s);
}
} finally {
IOUtils.closeQuietly(br);
}
return list;
}
}
多想一步,根据参数变化编写各种类型的入参函数,需要保证函数主要代码只有一份。
最开始我们写的代码如下
User user = new User();
BeanUtils.copyProperties(userInputDTO,user);
上面的代码很好的简化和优化了代码,但是他的语义是有问题的,我们需要体现一个转化过程
public interface DTOConvert {
T convert(S s);
}
public class UserInputDTO {
private String username;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User convertToUser(){
UserInputDTOConvert userInputDTOConvert = new UserInputDTOConvert();
User convert = userInputDTOConvert.convert(this);
return convert;
}
private static class UserInputDTOConvert implements DTOConvert {
@Override
public User convert(UserInputDTO userInputDTO) {
User user = new User();
BeanUtils.copyProperties(userInputDTO,user);
return user;
}
}
}
调用方法如下:
User user = userInputDTO.convertToUser();
User saveUserResult = userService.addUser(user);
比之前简洁了不少,再查工具类后,GUAUA实现了我们自定义的DTOConver接口
public abstract class Converter implements Function {
protected abstract B doForward(A a);
protected abstract A doBackward(B b);
//其他略
}
他可以支持正向转化和逆向转化,继续修改我们的转换代码
public class UserDTO {
private String username;
private int age;
//setget方法省略
public User convertToUser(){
UserDTOConvert userDTOConvert = new UserDTOConvert();
User convert = userDTOConvert.convert(this);
return convert;
}
public UserDTO convertFor(User user){
UserDTOConvert userDTOConvert = new UserDTOConvert();
UserDTO convert = userDTOConvert.reverse().convert(user);
return convert;
}
private static class UserDTOConvert extends Converter {
@Override
protected User doForward(UserDTO userDTO) {
User user = new User();
BeanUtils.copyProperties(userDTO,user);
return user;
}
@Override
protected UserDTO doBackward(User user) {
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(user,userDTO);
return userDTO;
}
}
}
最终调用方式如下:
@PostMapping
public UserDTO addUser(UserDTO userDTO){
User user = userDTO.convertToUser();
User saveResultUser = userService.addUser(user);
UserDTO result = userDTO.convertFor(saveResultUser);
return result;
}
bean中的链式风格
@Accessors(chain = true)
@Setter
@Getter
public class Student {
private String name;
private int age;
}
调用方式(但是仅限在new的时候可以用,如果给实例赋值则不能使用)
Student student = new Student()
.setAge(24)
.setName(“zs”);
bean的必输字段如name,一般方法是将name字段包装成一个构造方法,只有传入name这样的构造方法,才能创建一个Student对象。lombok可以用@RequiredArgsConstructor解决
@Accessors(chain = true)
@Setter
@Getter
@RequiredArgsConstructor(staticName = “ofName”)
public class Student {
@NonNull private String name;
private int age;
}
测试代码:
Student student = Student.ofName(“zs”);
使用builder,builder模式大家都知道,lombok中有一个注解可以替代@Builder
一般的builder模式如下:
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static Builder builder(){
return new Builder();
}
public static class Builder{
private String name;
private int age;
public Builder name(String name){
this.name = name;
return this;
}
public Builder age(int age){
this.age = age;
return this;
}
public Student build(){
Student student = new Student();
student.setAge(age);
student.setName(name);
return student;
}
}
}
调用方式:
Student student = Student.builder().name(“zs”).age(24).build();
这样的builder代码,实在是恶心难受,于是可以用lombok替代
@Builder
public class Student {
private String name;
private int age;
}
调用方式:
Student student = Student.builder().name(“zs”).age(24).build();
JDK7之后,Java多了个新的语法:try-with-resources语句,
可以理解为是一个声明一个或多个资源的 try语句(用分号隔开),
一个资源作为一个对象,并且这个资源必须要在执行完关闭的,
try-with-resources语句确保在语句执行完毕后,每个资源都被自动关闭 。
任何实现了** java.lang.AutoCloseable的对象, 包括所有实现了 java.io.Closeable** 的对象
, 都可以用作一个资源。作者:程序鱼链接:https://www.jianshu.com/p/258c5ce1a2bd来源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
private void handle(String fileName) {
//括号里面的流会被自动关闭
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
while ((line = reader.readLine()) != null) {
...
}
} catch (Exception e) {
...
}
}
尽所周知,好多变量都有可能为null,所有就写了满屏的null != xxx 的判断,这种判断多了,就很碍眼了,怎么能够减少这种写法,还能不影响业务逻辑
返回值为null有两种含义
如上两种情况,第一种情况,后续代码就是要兼容为null的情况,第二种情况就要想办法避免返回null值,或者是否需要抛出一个异常。
如
public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
public void doSomething() { /* do nothing */ }
};
public Action findAction(String userInput) {
// ...
if ( /* we can't find any actions */ ) { //再找不到匹配的action的时候,我们返回默认的action,防止获取action的后续代码出现null的情况
return DO_NOTHING;
}
}}
Objects的equals的方法经常报空指针异常,所以我们都知道需要使用常量或者确定的对象来调用equals方法。举个例子
// 不能使用一个值为null的引用类型变量来调用非静态方法,否则会抛出异常
String str = null;
if (str.equals("SnailClimb")) {
...
} else {
..
}
运行上面的程序会抛出空指针异常,但是我们把第二行的条件判断语句改为下面这样的话,就不会抛出空指针异常,else 语句块得到执行。:
"SnailClimb".equals(str);// false
不过更推荐使用 java.util.Objects#equals(JDK7 引入的工具类)。
Objects.equals(null,"SnailClimb");// false
我们看一下java.util.Objects#equals的源码就知道原因了。
public static boolean equals(Object a, Object b) {
// 可以避免空指针异常。如果a==null的话此时a.equals(b)就不会得到执行,避免出现空指针异常。
return (a == b) || (a != null && a.equals(b));
}
注意: