目录
常见代码优化:
if/if-else语句优化
使用提前判断
使用枚举或映射
使用函数式编程
将判断逻辑封装
其他
循环类型代码优化
Stream API
集合操作
数组操作
异常类型代码优化
tyr-with-resource
使用Java 8的函数式接口简化回调操作
字符串操作代码优化
字符格式化
字符串拼接
Java 11的字符串方法
Java 14的新特性:多行文本块
多线程代码优化
使用并发集合简化
Java 8的CompletableFuture实现异步编程
其它类别代码优化
延迟计算
对象类型转换
Java 15的新特性:记录(Records)
Java 16的新特性:模式匹配的实例of操作符
Java 9的模块化系统
Java15引入特性:密封类型
Java14:Switch表达式
Java 11:嵌套的Lambda表达式
Java8新特性:自定义注解
Java代码规范
如果某些条件更常见或更容易满足,将其放在前面判断,以减少不必要的判断。这样可以更快地结束整个条件分支。
eg:
// 修改后:
@SneakyThrows
@PostMapping("/saveFeedBackT")
@ApiOperation("获用户取评价内容")
public AjaxResult saveFeedBackT(@ApiParam("教师名称") @RequestParam String teacher,
@ApiParam("评分") @RequestParam Integer rating,
@ApiParam("评价内容") @RequestParam String comment) {
Preconditions.checkArgument(rating!=null&&teacher!=null,"信息填写不完整");
//或者 一个参数或者对象可以 Assert.notNull(rating,"信息填写不完整");
return feedbackTeacherService.saveFeedBackT(teacher,rating,comment);
}
机制 | 优点 | 缺点 |
---|---|---|
Alerrt :断言/警告机制 | 可读性高,可通过异常处理机制传播栈堆错误,方便找出原因 |
1)本身会消耗一些性能,开启Assert会产生额外能消耗(idea默认开启) 2)敏感信息被放入栈堆可能会被攻击者利用 3)处理的能力比较弱 |
PreCondition:前置条件机制 | 可读性高,内置方法多,处理能力较强 | 1)本身会消耗一些性能 2)不同方法使用了很多相同的判断,会导致代码冗余(Assert相同,但是Assert一般在生产环境要被禁用或移除) |
if语句结合抛出异常 | 灵活性高,可适应特殊的处理 | 1)可读性较弱 2)需要手动实现异常处理机制,比较复杂(实现可能出错,如抛出了不相关异常),可维护性较弱 |
如果有大量的条件和对应的操作,可以使用枚举或映射来简化代码。将条件作为枚举的值或映射的键,将对应的操作封装在枚举常量或映射值中。
//创建测试数组
List list = Arrays.asList(1, 2, 3, 4, 5);
//列表元素中选择出1,2,5并进行打印
list.forEach(f->{ // 不好的写法
if (f==1){
System.out.println("一");
}
if (f==2){
System.out.println("二");
}
if (f==5){
System.out.println("五");
}
});
list.forEach(f->{ // 使用if-else减少判断次数
if (f==1){
System.out.println("一");
}else if(f==2){
System.out.println("二");
}else if (f==5){
System.out.println("五");
}
});
list.forEach(f -> { //使用枚举简化(需要创建枚举类)
Test t = Test.valueOf(String.valueOf(f));
System.out.println(t.getI_CN());
});
list.forEach(f->{ //使用switch表达式 java14引入
System.out.println(switch (f) {
case 1 -> "一";
case 2 -> "二";
case 5 -> "五";
default -> "";
});
});
Java 8 引入的函数式编程可以使代码更简洁和可读。可以使用Lambda表达式、函数接口以及Stream API等功能来处理条件判断。
eg:
//用于存储大于3的元素
List A_gt3 = new ArrayList<>(5); //一般的写法
for (Integer i : list) {
if (i>3){
A_gt3.add(i);
}
}
List B_gt3 = new ArrayList<>(5); //使用lambda表达式
list.forEach(i->{
if (i>3){
A_gt3.add(i);
}
});
List C_gt3 = list.stream() //使用stream()流
.filter(i -> i > 3)
.toList();
}
for、foreach、stream效率比较:
如果数据在1万以内的话,for循环效率高于foreach和stream;如果数据量在10万的时候,stream效率最高,其次是foreach,最后是for。另外需要注意的是如果数据达到100万的话,parallelStream异步并行处理效率最高,高于foreach和for。
//如果有很多给相同的判断 可以使用Predicate接口将判断逻辑封装成可复用的谓词
// 将一个是否为偶数的判断封装
Predicate isEven = number -> number % 2 == 0;
boolean result = isEven.test(4);
此外,还有的常用做法还有,使用三元表达式,对if判断进行逻辑优化,提出if语句作为公共方法等。
Map map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
List list = Arrays.asList(1, 2, 3, 4, 5, 5);
// 相加
int sum = list.stream().mapToInt(Integer::intValue).sum();
int sum2 = list.stream().reduce(0, Integer::sum); //从基础值0的基础上累加
int sum3 = list.parallelStream().reduce(0, Integer::sum); //使用并行流相加
// 获取最大值 //Optional类型可以包装结果为null的值
Optional max = list.stream().max(Integer::compareTo);
// 获取最小值
Optional min = list.stream().min(Integer::compareTo);
min.ifPresent(System.out::println);
//直接使用Java 8的Stream API对集合进行统计操作
IntSummaryStatistics i = list.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
System.out.println("最大值: " + i.getMax());
System.out.println("最小值: " + i.getMin());
System.out.println("总和: " + i.getSum());
System.out.println("平均值: " + i.getAverage());
// 去重
List distinctNumbers = list.stream()
.distinct()
.toList();
//利用lambda表达式 使用方法引用 打印输出
list.forEach(System.out::println);
// 遍历所有的键值对 使用Lambda表达式
map.forEach((key, value) -> System.out.println(key + ": " + value));
//筛选和映射
List names = Arrays.asList("aa", "bbb", "ccccc");
List filteredNames = names.stream() // 遍历输出的结果为 CCCCC
.filter(name -> name.length() > 4)
.map(String::toUpperCase)
.toList();
// 分组和聚合
List people = Arrays.asList(
new Person("A", 10),
new Person("B", 15),
new Person("C", 20)
);
Map> personList = people.stream() //会创建一个新的map
.collect(Collectors.groupingBy(Person::getAge));
int totalAge = people.stream() //累加每个人的年龄
.mapToInt(Person::getAge)
.sum();
// 分组和聚合
List peoples = Arrays.asList(
new Person("A", 10),
new Person("B", 15),
new Person("C", 20),
new Person("D", 25)
);
// 使用Optional和Stream API链式操作处理嵌套对象
Optional company = getCompany();
String companyName = company.flatMap(Company::getDepartment)
.flatMap(Department::getManager)
.map(Manager::getName)
.orElse("Unknown");
int[] numbers = {1, 2, 3, 4, 5};
// 数组元素求和
int sum = Arrays.stream(numbers).sum();
// 数组排序
int[] sortedNumbers = Arrays.stream(numbers).sorted().toArray();
// 可同时处理多个资源
try (BufferedReader br = new BufferedReader(new FileReader("file1.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("file2.txt"))) {
// 执行操作
} catch (IOException e) {
// 处理异常
}
//不使用lambda表达式
public interface Callback {
void onSuccess(String result);
void onFailure(Exception e);
}
public void fetchDataAsync(Callback callback) {
// 异步获取数据
try {
String data = fetchData();
callback.onSuccess(data);
} catch (Exception e) {
callback.onFailure(e);
}
}
// 使用匿名内部类调用
fetchDataAsync(new Callback() {
@Override
public void onSuccess(String result) {
System.out.println("Success: " + result);
}
@Override
public void onFailure(Exception e) {
System.err.println("Error: " + e.getMessage());
}
});
// 使用lambda表达式后
@FunctionalInterface //Java8引入的一个注解,用于标记该接口为函数式接口
public interface Callback {
void onSuccess(String result);
void onFailure(Exception e);
}
public void fetchDataAsync(Callback callback) {
// 异步获取数据
try {
String data = fetchData();
callback.onSuccess(data);
} catch (Exception e) {
callback.onFailure(e);
}
}
// 使用Lambda表达式调用
fetchDataAsync(result -> System.out.println("Success: " + result),
error -> System.err.println("Error: " + error.getMessage()));
// 类似于c语言的输出语句 阅读时可以知道传入值的类型(和'+'拼接相比)
String name = "LiMing";
int age = 18;
String message = String.format("My name is %s and I'm %d years old.", name, age);
使用'+'进行拼接:每执行完一条拼接语句会创建一个新的String对象,在产生对象少时推荐使用
需要多次拼接时:
StringBuilder:线程不安全,效率高,不考虑线程安全时使用(如单线程环境),jdk1.5引入StringBuffer:方法都是同步,线程安全,效率比StringBuilder低,jdk1.0引入
StringJoiner:适用于两字符串间要拼接相同的字符串,jdk8引入
//demo
StringBuilder s = new StringBuilder();
s.append("A");
s.append(", ");
s.append("B");
s.append(", ");
s.append("C");
String result = s.toString();
StringBuffer s = new StringBuffer();
s.append("A");
s.append(", ");
s.append("B");
s.append(", ");
s.append("C");
String result = s.toString();
StringJoiner s = new StringJoiner(", ");
s.add("A");
s.add("B");
s.add("C");
String result = s.toString();
// isBlank()和lines()
String text = "Hello\nWorld";
System.out.println(text.isBlank()); // 检查字符串是否为空白
System.out.println(text.lines().count()); // 统计字符串中行的数量
String html = """
Hello, World!
""";
System.out.println(html);
// 简化前代码 并发时要保证线程安全 所以使用hashtable
// 这个代码是给一个key,key存在,它的值进行加一操作
Hashtable hashtable = new Hashtable<>();
synchronized (hashtable) {
if (!hashtable.containsKey("key")) {
hashtable.put("key", 0);
} else {
hashtable.put("key", hashtable.get("key") + 1);
}
}
//简化后
ConcurrentMap concurrentMap = new ConcurrentHashMap<>();
concurrentMap.putIfAbsent("key", 0);
concurrentMap.computeIfPresent("key", (k, v) -> v + 1);
//直接使用这个类 目的是为了不用手动创建、启动和管理线程
//实现多个任务并行执行
CompletableFuture future1 = CompletableFuture.supplyAsync(() -> task1());
CompletableFuture future2 = CompletableFuture.supplyAsync(() -> task2());
CompletableFuture combinedFuture = future1.thenCombine(future2, (result1, result2) -> result1 + ", " + result2);
//实现超时处理
CompletableFuture future = CompletableFuture.supplyAsync(() -> task());
try {
String result = future.get(1, TimeUnit.SECONDS);
} catch (TimeoutException e) {
// 处理超时情况
}
它能够节省资源,避免重复计算
如在电商网站推迟商品推荐的计算,直到用户进入推荐页面时才进行
Supplier lazyValue = () -> {
// 执行一些复杂的计算
return "Hello World!";
};
String value = lazyValue.get(); // 在需要时计算值
class Student {
private String name;
private int age;
private double score;
// 省略构造方法和其他方法
// ...
}
class StudentDTO {
private String name;
private int age;
// 省略构造方法和其他方法
// ...
}
// 1、可以使用Function类进行类型装换 可适用于复杂场景,比较符合函数式编程代码风格
Function studentToDtoConverter = student -> new StudentDTO(student.getName(), student.getAge());
StudentDTO studentDTO = studentToDtoConverter.apply(student);
// 2、使用BeanUtils.copyProperties(source, target)方法进行类型转换 使用简单明了
StudentDTO studentDTO = new StudentDTO();
BeanUtils.copyProperties(student, studentDTO)
Records对象:不可变(相当于final修饰),会自动为数据成员生成字段、构造函数、equals()、hashCode() 和 toString() 方法
使用目的:简洁、可减少代码书写 且Records支持模式匹配(instanceof 或 switch检查对象是否与某个类型或模式匹配,并且可以直接获取匹配后的对象属性)
//demo
record Person(String name, int age) {}
Person person = new Person("LiMing", 18);
System.out.println(person.name());
System.out.println(person.age());
//模式匹配demo
public void processPerson(Person person) {
if (person instanceof Person adult && adult.age() >= 18) {
System.out.println(adult.name() + " 是成人,");
System.out.println("年龄: " + adult.age());
}
}
Object obj = 10;
if (obj instanceof Integer integer) {
System.out.println("Integer: " + integer);
} else if (obj instanceof String str) {
System.out.println("String: " + str);
} else {
System.out.println("Unknown");
}
它的目的是提供一种更好的方式来组织和管理 Java 应用程序的代码。通过模块化,可以显式地声明模块之间的依赖关系,增强了代码的可维护性和可扩展性
// module-info.java
//这个文件要放到项目中的模块根目录中
module com.example.myapp {
requires java.base;
requires org.apache.commons.lang3;
}
sealed 关键字:用于修饰类、接口或枚举,将其声明为密封类型。密封类型表示该类型只能被指定的子类继承或实现。使用 sealed 可以限制对密封类型的扩展,从而更好地控制类型的继承结构
permits 关键字:用于在密封类型的声明中列出允许继承或实现的具体子类。只有在 permits 子句中明确列出的类才能继承或实现密封类型。这样,您可以明确指定哪些类允许扩展或实现密封类型,提供更加可控的类型继承关系
//demo
public sealed interface Shape permits Circle, Rectangle {
double calculateArea();
}
public final class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
public final class Rectangle implements Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
}
//传统写法
int day = 2;
String dayOfWeek;
switch (day) {
case 1:
dayOfWeek = "星期一";
break;
case 2:
dayOfWeek = "星期二";
break;
case 3:
dayOfWeek = "星期三";
break;
// 其他...
default:
dayOfWeek = "无法识别";
break;
}
//Switch表达式 switch表达式不需要break关键字
int day = 2;
String dayOfWeek = switch (day) {
case 1 -> "星期一";
case 2 -> "星期二";
case 3 -> "星期三";
// 其他箭头标签...
default -> "无法识别";
};
在Java 8中,Lambda表达式不能直接包含嵌套的Lambda表达式。只能通过传递已存在的Lambda表达式作为参数来实现类似的效果。
但是从Java 11开始,嵌套的Lambda表达式被引入,允许在Lambda表达式的主体中包含其他Lambda表达式。这种语法使得编写更复杂的函数式代码变得更加方便。
//demo
Function> add = x -> y -> x + y;
int result = add.apply(1).apply(2);
System.out.println(result); // 输出 3
//demo
// 自定义的注解 @Color 和 @Colors
@Repeatable(Colors.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Color {
String value();
}
public @interface Colors {
Color[] value();
}
//调用
@Color("Red")
@Color("Blue")
public class MyClass {
public static void main(String[] args) {
// 获取 MyClass 类上的 @Color 注解
Color[] colors = MyClass.class.getAnnotationsByType(Color.class);
for (Color color : colors) {
System.out.print(color.value()); //输出RedBlue
}
}
}
阿里巴巴java开发手册2020.pdf
链接:
百度网盘 请输入提取码
提取码:8700
最后,
希望文章对你有所帮助!