开发技巧 equals() 方法的使用
- null.equals()会出报空指针,因该是非null的值.equals()
- 可以使用Objects的equals()方法避免空值,完美
String strOne = null; String strTwo = null; boolean oneFlag = Objects.equals(strOne, strTwo);
- 忽略大小写:equalsIgnoreCase()
创建 HashMap 指定初始化大小
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
-
initialCapacity = (需要存储的元素个数/负载因子)+1。负载因子默认为0.75
-
当不指定HashMap的大小会发生多次扩容会影响效率。比如map中有1000个元素,至少要将容量设置为1000/0.75=1333+1=1334,如果不设置大小那么就会多次扩容,比较影响效率。
-
当无法估计大小的时候,请设置为HashMap的默认大小16
Optional.ofNullable().orElse() 避免空指针
通过Optional.ofNullable().orElse()避免空指针,例如遍历从map中拿到的某个list,原始代码如下:
Mapmap = new HashMap<>(16); map.put("list",null); List
- 使用Optional 完美解决
Mapmap = new HashMap<>(16); map.put("list",null); List > list = Optional.ofNullable((List < Map < String, Object >> ) map.get("list")).orElse(new ArrayList<>()); for (Map item : list) { logger.info(String.valueOf(item)); }
- 使用Optional选择默认数据
User user1 = new User(1, "张三", "西安"); User user2 = new User(2, "李四", "新疆"); user1 = null; User user = Optional.ofNullable(user1).orElseGet(() -> user2); logger.info(user.toString());
Stream 求和
- 使用 Stream流遍历集合中的元素进行求和操作
ListdoubleList = Arrays.asList(12.2, 13.45, 12.4); double sumOne = doubleList.stream().mapToDouble(x -> x).sum(); logger.info("sumOne:{}", sumOne); List decimalList = new ArrayList<>(); decimalList.add(new BigDecimal("12.3")); decimalList.add(new BigDecimal("12.3")); decimalList.add(new BigDecimal("12.3")); BigDecimal bigDecimal = decimalList.stream().reduce(BigDecimal.ZERO, BigDecimal::add); logger.info("bigDecimal:{}", bigDecimal.floatValue()); logger.info("bigDecimal:{}", bigDecimal.doublevalue()); List
List 切割工具
- Lists.partition()
for (ListdataDtos : Lists.partition(list, 1000)) { dataDtoMapper.insertBatch(dataDtos); }
- 依赖
com.google.guava guava 31.0.1-jre log4j log4j
单例类或者工具类 添加私有构造方法
- 工具类或者单例类提供私有的构造函数,没有过多的功能,提供私有构造函数防止外部使用污染。
类变量(集合)应该在使用完成之后清空,避免内存泄漏
- 应该及时销毁没用的对象,如果对象过大,虚拟机没办法垃圾回收的时候,有可能造成内存泄漏,所以使用完就清空.
private static final MapTABLE_COLUMN_MAP = new HashMap<>(); // 清空集合 TABLE_COLUMN_MAP.clear();
使用 ThreaadLocal 后需要释放资源防止内存泄漏
private ThreadLocal> threadLocalMap = new ThreadLocal<>(); @Test public void test1() { Map map = new HashMap<>(); map.put("test1", "张三"); map.put("test2", "李四"); threadLocalMap.set(map); getThreadLocalMap(); threadLocalMap.remove(); }
巧妙使用设计模式
-
巧妙使用接口、抽象类
-
Java 设计模式请看:https://blog.csdn.net/qq_37248504/article/details/117753021
Spring 工厂模式的结合使用
- Java 工厂模式请看:https://blog.csdn.net/qq_37248504/article/details/117753021
- 使用Spring 收集Bean对象放到一个Map中,从这个Map中拿到相应的对象的实例,直接上代码
- 接口 CustomerService.java
public interface CustomerService {
String getUserName();
void registered();
void login();
}
- 接口的实现类:需要收集的对象实例可以有很多个实例
@Service
public class CustomerServiceOneImpl implements CustomerService {
@Override
public String getUserName() {
return "CustomerOne";
}
@Override
public void registered() {
}
@Override
public void login() {
}
}
@Service
public class CustomerServiceTwoImpl implements CustomerService {
@Override
public String getUserName() {
return "CustomerTwo";
}
@Override
public void registered() {
}
@Override
public void login() {
}
}
- 工厂方法:可以使用set注入、或者构造函数方式、或者从当前上下文中拿到接口的实例对象
@Component
public class CustomerFactory {
private static final Map OBJECT_MAP = new HashMap<>();
@Autowired
private void setObjectMap(List customerService) {
for (CustomerService service : customerService) {
OBJECT_MAP.put(service.getUserName(), service);
}
}
public static CustomerService getInstance(String type) {
return OBJECT_MAP.get(type);
}
}
- 测试
@Override
public void testFive() {
CustomerFactory.getInstance("CustomerOne").registered();
CustomerFactory.getInstance("CustomerTwo").registered();
}
- 完整代码请看:https://gitee.com/Marlon_Brando/back/commit/d9ad24564dfe1459ec9e0728822b709f1ea6fa41
Spring、SpringBoot 经常使用技巧
-
获取容器中的Bean、Bean的初始化
-
更多详情请看:https://blog.csdn.net/qq_37248504/article/details/113896952
Stream 流等操作时候使用方法的引用
- Stream
Listcollect = list.stream().filter("lisi"::equals).map(String::toUpperCase).collect(Collectors.toList()); collect.forEach(System.out::println); list.removeIf("李四"::equals);
- HashMap
Mapmap = new HashMap () {{ put("one", "one"); put("two", "one"); put("three", "one"); }}; Map mapOne = new HashMap<>(); map.forEach(mapOne::put); logger.info(String.valueOf(mapOne));
Java 创建线程池
- 使用 ThreadPoolExecutor创建线程池,使用线程,到处 new Thread() 没有回收造成资源浪费,因该交给线程池去管理线程。
public class ThreadPooTest {
private static final Logger logger = LoggerFactory.getLogger(ThreadPooTest.class);
private static final long THREAD_TIME_OUT = 60;
@Test
public void test() {
// Cpu 核数
int cpuNum = Runtime.getRuntime().availableProcessors();
// 最大数
int maxSize = 2 * cpuNum + 1;
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(cpuNum,
maxSize,
THREAD_TIME_OUT,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(20),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
poolExecutor.execute(new Thread(() -> {
logger.info(Thread.currentThread().toString());
}));
}
}
Spring Boot 配置全局的线程池
@Configuration
@EnableAsync
public class ThreadPoolConfig {
private static final Logger logger = LoggerFactory.getLogger(ThreadPoolConfig.class);
@Bean
public Executor globalExecutor() {
// 获取当前cpu核数
int cpuNum = Runtime.getRuntime().availableProcessors();
ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
poolExecutor.setCorePoolSize(cpuNum);
//配置最大线程数
poolExecutor.setMaxPoolSize(cpuNum * 2);
//配置队列大小
poolExecutor.setQueueCapacity(300);
//线程存活时间
poolExecutor.setKeepAliveSeconds(60);
//配置线程池中的线程的名称前缀
poolExecutor.setThreadNamePrefix("globalExecutor");
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
poolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
poolExecutor.initialize();
logger.info(LogConstant.FLAG);
logger.info(LogConstant.LOG_SUCCESS_PREFIX + "Spring 全局线程池初始化完成......");
logger.info(JSON.toJSONString(poolExecutor));
logger.info(LogConstant.FLAG);
return poolExecutor;
}
}
- 使用
@Async("globalExecutor")
@Override
public void globalExecutorTest() {
logger.info("test thread!");
}
Idea 热部署插件:Jrebel
-
Idea社区版能满足常用的开发,支持maven、gradle项目,真的没有必要破解Idea,哈哈哈哈哈哈哈哈。
-
使用特别Nice基本上不用重启服务,网上都有注册码。
初始化 Map
- 惯例
MapmapTwo = new HashMap<>(); mapTwo.put("a", "b"); mapTwo.put("c", "d");
- java8新特性:双括号初始化
MapmapOne = new HashMap () { { put("one", "testOne"); put("two", "testTwo"); put("three", "testThree"); } };
- com.google.guava中的方法
ImmutableMapmap = ImmutableMap.of("a", 1, "b", 2, "c", 3); logger.info(String.valueOf(map));
List 初始化
- 构造List,add()
ListlistOne = new ArrayList<>(); listOne.add("test"); listOne.add("test");
- 双括号语法
ListlistTwo = new ArrayList (){{ add("one"); add("one"); add("one"); }};
- Arrays.asList()
ListlistThree = Arrays.asList("one", "two", "three");
- Stream.of()
ListlistFour = Stream.of("testone", "testtwo", "testthree").collect(Collectors.toList());
- com.google.guava中的方法
ArrayListlistFive = Lists.newArrayList("one", "two", "three");
后端统一返回对象(泛型使用)
- 静态方法 success、error
public class ResponseInfo{ private Integer code; private String message; private T data; public ResponseInfo() { } public ResponseInfo(Integer code, String message) { this.code = code; this.message = message; } public ResponseInfo(Integer code, String message, T data) { this.code = code; this.message = message; this.data = data; } public static ResponseInfo error(Integer code, String message) { return new ResponseInfo<>(code, message); } public static ResponseInfo success(Integer code, String message) { return new ResponseInfo<>(code, message); } public static ResponseInfo success(Integer code, String message, T object) { return new ResponseInfo<>(code, message, object); } }
return 空的集合代替 return null
- 避免空指针问题
return Collections.emptyList(); return Collections.emptyMap(); return Collections.emptySet();
使用 String.valueof() 防止强制转换报错
- valueOf()方式可以避免类型转换异常
@Test
public void testOne() {
Integer one = 24;
BigDecimal two = new BigDecimal(23);
Map map = new HashMap<>();
map.put("one", one);
map.put("two", two);
// 报错
String strOne = (String) map.get("one");
String strTwo = (String) map.get("two");
// 正常使用
String strThree = String.valueOf(map.get("one"));
String strFour = String.valueOf(map.get("two"));
}
- 尽量使用valueof()方法避免转换异常
String.join()拼接集合中的元素
// 拼接迭代器中的元素 Listlist = Arrays.asList("zhangsan", "lisi", "wangwu"); String joinStr = String.join("','", list); logger.info(joinStr); // zhangsan','lisi','wangwu
TreeSet 排序
@Test
public void treeSetTest() {
TreeSet set = new TreeSet((Comparator) (o1, o2) -> {
int length1 = String.valueOf(o1).length();
int length2 = String.valueOf(o2).length();
if (length1 == length2) return 1;
return Integer.compare(length1, length2);
});
set.add("zhangsan");
set.add("test");
set.add("lisi");
set.add("1234");
set.add("wangwu");
logger.info(String.valueOf(set));
}
Map 做缓存
- 使用Map可以做本地的部分缓存,将常用的对象例如工具类等可以放在缓存当中,从缓存中取值,
- 注意Map作为缓存的时候,确定使用恰当。
final 的使用场景
- 不允许被继承的类,如:String 类
- 不允许修改引用的域对象,如:POJO 类的域变量
- 不允许被重写的方法
- 不允许运行过程中重新赋值的局部变量
for 循环移除元素
- 不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator
方式,如果并发操作,需要对 Iterator 对象加锁。
Iteratoriterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if (删除元素的条件) { iterator.remove(); } }
遍历Map
- 使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。
for (Map.Entryentry : map.entrySet()) { logger.info("键:{}", entry.getKey()); logger.info("值:{}", entry.getValue()); }
集合空值说明
| 集合类 | key | value | super | 说明 |
|---|---|---|---|---|
| HashMap | 允许为null | 允许为null | AbstractMap | 线程不安全 |
| TreeMap | 不允许为null | 允许为null | AbstractMap | 线程不安全 |
| ConcurrentHashMap | 不允许为null | 不允许为null | AbstractMap | 线程安全 |
| Hashtable | 不允许为null | 不允许为null | Dictionary | 锁分段技术(JDK8:CAS) |
Git删除add的文件
git rm --cached 文件名
Git重新 commit
- 使用Idea的undo commit撤销上次的commit重新来。
Git重新 commit(还没有push到远端)
-
reset Mixed 之前 Commit 修改的内容会保留。
-
reset Hard 之前Commit修改的内容不会保留。
-
reset Soft 类似于 reset Mixed,但是又区别。比如上次的commit中有个新建的文件,当使用reset Mixed命令时候,撤销commit后这个新建的文件是没有进行add操作的,文件的颜色是红色的。使用reset Soft命令这个文件是进行了add操作了的。
利用Set去重
-
注意重写equals()和hash(),可以根据实际业务场景进行去重操作,例如如果商品编号相等那么断定这个商品是同一件商品。
-
重写对象的equals()
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (Objects.isNull(object) || getClass() != object.getClass()) return false;
Shop shop = (Shop) object;
return (Objects.nonNull(shop) ? shopCode.equals(shop.getShopCode()) : shop == null);
}
- 重写对象的hash()
@Override
public int hashCode() {
return Objects.isNull(shopCode) ? 0 : shopCode.hashCode();
}
异常处理
- java异常
class Throwable; class Exception; class Error;
- 处理方法:
向上抛异常:最底层的代码例如工具类,直接向上抛原始异常就行了。
输出原始异常:如果是捕获到了异常,那么一定要输出原始异常的信息,如果只是输出e.getMessage无法定位到代码出错的行数,如果是空指针错误信息都没有。
使用日志框架层:例如log4j
优雅的抛出异常
- 使用Objects
@Test
public void testFive() {
String str = null;
Objects.requireNonNull(str, "str is null!");
}
- 使用Assets
@Test
public void testSix() {
String str = null;
Assert.notNull(str, "str is null!");
}
Mybatis 使用技巧
- 详见:https://blog.csdn.net/qq_37248504/article/details/106932085
常用规范
其实使用Idea开发的时候,开启Idea自带的代码检测,处理掉所有的告警大部分代码问题会解决,代码命名规范等可以使用阿里巴巴开发规范插件,使用SonarLint插件可以处理一些垃圾代码,可以扫描一些静态的错误,从而提高代码质量。
SonarLint 检查提示和解决方法
- 详细请看:https://blog.csdn.net/qq_37248504/article/details/115266913
类的命名
- 使用驼峰式命名的规范。DO、 DTO 、 VO等除外
UserDO UserDTO UserVO
- 在模块或者接口,类,方法中使用了设计模式,那么请在命名的时候体现出来。
CompomentFactory AbstractPrint
pojo 类中的属性使用包装类
-
基本类型会有默认值,使用包装内可以设置为null
-
pojo的getter和setter方法里面不要增加业务逻辑。
抛出异常的时候确定异常的类型,可以定义自己的异常抛出
public class MyException extends RuntimeException {
public MyException() {
}
public MyException(String message) {
super(message);
}
public MyException(String message, Throwable cause) {
super(message, cause);
}
public MyException(Throwable cause) {
super(cause);
}
public MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
- 使用
@GetMapping("/myexception")
public String createMyException() throws MyException {
throw new MyException("MyException 自定义异常信息");
}
在 for 循环中拼接字符串使用 StringBuilder
StringBuilder strOne = new StringBuilder("test");
for (int i = 0; i < 10000000; i++) {
strOne.append(strOne);
}
不要使用魔法值(常量)
- 使用接口在接口中定义常量
- 维护常量类
- 使用枚举类
final 的使用
- 类、方法、变量能用就可以加上final防止重写、继承。
避免创建不必要的对象
- 不要随便创建对象
- 例如在for循环中尝试使用同一对象处理等。
- 可以使用Map做常用对象的缓存
组合优先于继承
- 继承有利于代码复用,但是尽可能不要进行跨包的继承。
- 组合,即不扩展已有的类,而是在的类中新增一个现有类的。
用枚举代替常量值
- Color.java
public enum Color {
RED("red", "红色"), GREEN("green", "绿色");
private String name;
private String title;
Color(String name, String title) {
this.name = name;
this.title = title;
}
public String getName() {
return name;
}
public String getTitle() {
return title;
}
}
接口优先于反射机制
使用反射机制会带来以下的问题:
- 丧失了编译期类型检查
- 代码笨拙冗长
- 性能损失
反射基本上只适合用在编写组件时、代码分析器、RPC等场景下使用。在使用反射机制时,如果可能,尽可能只通过反射机制实例化对象,而访问方法时,使用已知的接口或者超类。
命名风格(阿里巴巴规范)
-
代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束
-
类名使用驼峰规则,DO、BO、DTO、VO等除外
-
方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从
驼峰形式。
-
抽象类命名使用 Abstract 或 base 开头;异常类命名使用 Exception 结尾;测试类
命名以它要测试的类的名称开始,以 Test 结尾。
-
中括号是数组类型的一部分,数组定义如下:String[] args
-
为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词
组合来表达其意。
-
对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。
-
类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter方法
各层命名规范(阿里巴巴规范)
- 获取单个对象的方法用 get 做前缀。
- 获取多个对象的方法用 list 做前缀。
- 获取统计值的方法用 count 做前缀。
- 插入的方法用 save/insert 做前缀。
- 删除的方法用 remove/delete 做前缀。
- 修改的方法用 update 做前缀。
类成员与方法访问控制(阿里巴巴规范)
- 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。
- 工具类不允许有 public 或 default 构造方法
- 类非 static 成员变量并且与子类共享,必须是 protected
- 类非 static 成员变量并且仅在本类使用,必须是 private
- 类 static 成员变量如果仅在本类使用,必须是 private。
- 若是 static 成员变量,必须考虑是否为 final
- 类成员方法只供类内部调用,必须是 private
- 类成员方法只对继承类公开,那么限制为 protected
工具类 镜像源、常用工具
- 详见:https://blog.csdn.net/qq_37248504/article/details/118605663
Markdown
- 详见:https://blog.csdn.net/qq_37248504/article/details/109085445
Linux、DOS常用命令
- 详见:https://blog.csdn.net/qq_37248504/article/details/106866545
好用工具推荐
- 数据库连接工具:免费的Dbeaver(支持各种数据库):https://dbeaver.io/
- Markdown语法工具:Typora:https://typora.io/
- Redis客户端: https://gitee.com/qishibo/AnotherRedisDesktopManager



