Java基础知识

Java 函数式接口

Java 函数式接口是指只包含一个抽象方法的接口。Java 8 引入了函数式接口的概念,以支持函数式编程风格和Lambda 表达式的使用。

函数式接口可以用作 Lambda 表达式的目标类型,也可以通过匿名类或方法引用来实现。

Java 提供了 java.util.function 包,其中包含了一些常用的函数式接口,例如:

  1. Supplier<T>:无参数,返回一个结果。
  2. Consumer<T>:接受一个参数,不返回结果。
  3. Predicate<T>:接受一个参数,返回一个布尔值结果。
  4. Function<T, R>:接受一个参数,返回一个结果。

除了上述常用的函数式接口,还有一些其他的函数式接口,如 BiConsumer<T, U>BiPredicate<T, U>BiFunction<T, U, R> 等,用于接受多个参数的情况。

以下是一个示例,展示如何使用函数式接口和 Lambda 表达式:

1
2
3
4
5
6
7
8
9
import java.util.function.Function;

public class FunctionalInterfaceExample {
public static void main(String[] args) {
// 使用 Function 接口和 Lambda 表达式
Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 输出 25
}
}

在上面的示例中,我们使用 Function<Integer, Integer> 接口来定义一个函数,该函数接受一个整数参数并返回其平方。通过 Lambda 表达式实现了函数的具体逻辑。

请注意,函数式接口只能包含一个抽象方法,但可以包含默认方法和静态方法。

Lamda表达式

要将 List<CbSampleInBoxDTO> 中的多个 sampleInDetailDTOList 合并为一个 List<CbSampleInDetailDTO>,您可以使用 Java 8 的 Lambda 表达式和流操作来实现。以下是一种可能的实现方式:

1
2
3
4
List<CbSampleInBoxDTO> cbSampleInBoxDTOList = ...; // 原始的 List<CbSampleInBoxDTO>
List<CbSampleInDetailDTO> mergedList = cbSampleInBoxDTOList.stream()
.flatMap(box -> box.getSampleInDetailDTOList().stream()) // 将每个 CbSampleInBoxDTO 中的 sampleInDetailDTOList 转换为流
.collect(Collectors.toList()); // 将所有的 CbSampleInDetailDTO 收集到一个新的 List 中

在上面的代码中,我们使用 stream() 方法将 cbSampleInBoxDTOList 转换为流,然后使用 flatMap() 方法将每个 CbSampleInBoxDTO 中的 sampleInDetailDTOList 转换为一个流。最后,使用 collect() 方法将所有的 CbSampleInDetailDTO 收集到一个新的 List 中。

请确保 CbSampleInBoxDTOCbSampleInDetailDTO 类型的定义与您的实际情况相匹配。

Optional.ofNullable

Optional.ofNullable 是 Java 中 Optional 类的一个静态方法。它的作用是将一个可能为 null 的对象包装成一个 Optional 对象。

Optional 类是 Java 8 引入的一个用于处理可能为空的值的容器类。它的设计目的是为了避免空指针异常,并提供一种更优雅的方式来处理可能为空的情况。

Optional.ofNullable 方法接受一个参数,该参数可以是任意类型的对象,包括 null。如果传入的参数为非 null 值,则会将该值包装在一个 Optional 对象中;如果传入的参数为 null,则会返回一个空的 Optional 对象。

下面是一个示例,演示如何使用 Optional.ofNullable 方法:

1
2
3
4
5
6
7
8
String name = "John";
Optional<String> optionalName = Optional.ofNullable(name);

if (optionalName.isPresent()) {
System.out.println("Name: " + optionalName.get());
} else {
System.out.println("Name is null");
}

在上面的示例中,我们将一个可能为 null 的字符串 name 使用 Optional.ofNullable 方法进行包装。如果 name 不为 null,则可以通过 optionalName.get() 方法获取到该值,并进行相应的操作。如果 name 为 null,则可以根据需要处理空值的情况。

使用 Optional 类可以帮助我们编写更加健壮和可读性更高的代码,避免了繁琐的空值检查和空指针异常。

服务加载器

在 Java 中,服务加载器(Service Loader)是一种机制,用于动态加载和发现服务提供者(Service Provider)。服务提供者是指实现特定接口或抽象类的类,它们提供了特定功能或服务的实现。

Java 的服务加载器机制允许开发者编写服务接口,然后通过服务提供者(实现了这些接口的类)来扩展和实现这些接口。服务加载器负责在运行时动态加载这些服务提供者,使得应用程序可以在不修改代码的情况下添加或更换服务提供者。

在 Java 中,服务加载器的主要类是 java.util.ServiceLoader。下面是使用服务加载器的基本步骤:

  1. 定义服务接口: 首先,定义一个服务接口,描述服务提供者应该实现的方法。

  2. 实现服务提供者: 创建实现服务接口的类,这些类是服务提供者。每个服务提供者类都应该在 META-INF/services 目录下创建一个以服务接口全限定名命名的文件,文件内容为实现类的全限定名。

  3. 使用服务加载器: 使用 ServiceLoader 类来加载服务提供者。通过 ServiceLoader.load() 方法加载服务接口的实现类。

下面是一个简单的示例,演示了如何使用服务加载器加载并调用服务提供者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 定义服务接口
public interface GreetingService {
void greet();
}

// 实现服务提供者
public class EnglishGreetingService implements GreetingService {
@Override
public void greet() {
System.out.println("Hello!");
}
}

// 在 META-INF/services 目录下创建文件:com.example.GreetingService
// 文件内容为实现类的全限定名:com.example.EnglishGreetingService

// 使用服务加载器加载服务提供者
public class Main {
public static void main(String[] args) {
ServiceLoader<GreetingService> loader = ServiceLoader.load(GreetingService.class);
for (GreetingService service : loader) {
service.greet();
}
}
}

在这个示例中,我们定义了一个 GreetingService 接口和一个 EnglishGreetingService 类作为服务提供者。通过服务加载器,我们可以加载并调用 GreetingService 接口的实现类。

通过服务加载器,Java 应用程序可以更加灵活地扩展和替换功能,实现了松耦合和可插拔的设计。希望这个简单的示例能帮助你理解 Java 中的服务加载器机制。

代理

在 Java 中,代理(Proxy)是一种设计模式,允许你提供一个替代或占位对象,以控制对另一个对象的访问。代理通常充当客户端和实际对象之间的中介,可以在访问实际对象之前或之后执行额外的操作。

Java 中的代理通常有以下几种类型:

  1. 静态代理: 静态代理是在编译时就已经确定的代理类。在静态代理中,代理类需要显式地实现与目标对象相同的接口,并在代理类中调用目标对象的方法。

  2. 动态代理: 动态代理是在运行时创建的代理类,无需显式实现接口。Java 中的动态代理通常使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现。

下面是一个简单的动态代理示例,演示了如何使用 Java 的动态代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义接口
interface Hello {
void sayHello();
}

// 实现类
class HelloImpl implements Hello {
public void sayHello() {
System.out.println("Hello, World!");
}
}

// InvocationHandler 实现
class DynamicProxy implements InvocationHandler {
private Object target;

public DynamicProxy(Object target) {
this.target = target;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method execution");
Object result = method.invoke(target, args);
System.out.println("After method execution");
return result;
}
}

// 使用动态代理
public class Main {
public static void main(String[] args) {
Hello hello = new HelloImpl();
Hello proxyHello = (Hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
new DynamicProxy(hello)
);

proxyHello.sayHello();
}
}

在这个示例中,我们定义了一个 Hello 接口和一个 HelloImpl 类作为实现类。然后,我们创建了一个 DynamicProxy 类来实现 InvocationHandler 接口,用于在方法调用前后执行额外的操作。

Main 类中,我们使用 Proxy.newProxyInstance() 方法创建了一个动态代理对象,并在代理对象的方法调用前后输出了额外的信息。

通过代理模式,你可以在不修改原始类的情况下对其进行控制和扩展,实现了更加灵活的代码结构。希望这个示例能帮助你理解 Java 中的动态代理机制。

MyBatis Plus 中的saveOrUpdate 会返回id吗

在 MyBatis Plus 中,saveOrUpdate 方法用于保存或更新实体对象。根据实体对象的状态,它将执行插入或更新操作。但是,saveOrUpdate 方法不会返回生成的主键 ID。

如果您需要获取插入后的主键 ID,可以使用以下方法之一:

  1. 使用 insert 方法:如果您想要明确地执行插入操作并获取生成的主键 ID,可以使用 insert 方法。示例如下:

    1
    2
    3
    4
    5
    YourEntity entity = new YourEntity();
    // 设置实体对象的属性
    // ...
    yourMapper.insert(entity);
    Long id = entity.getId(); // 获取插入后的主键 ID

    在这种情况下,您需要确保实体对象的 id 属性是正确映射到数据库表的主键字段。

  2. 使用数据库的自增主键:如果您在数据库表中使用了自增主键,并且将其正确映射到实体对象的属性上,MyBatis Plus 在插入后会自动将生成的主键 ID 设置到实体对象中。示例如下:

    1
    2
    3
    4
    5
    YourEntity entity = new YourEntity();
    // 设置实体对象的属性
    // ...
    yourMapper.save(entity); // 使用 save 方法执行插入操作
    Long id = entity.getId(); // 获取插入后的主键 ID

    在这种情况下,您需要确保实体对象的主键属性与数据库表的自增主键字段正确映射。

请注意,saveOrUpdate 方法的返回值是一个布尔类型,表示操作是否成功。如果插入或更新成功,它将返回 true,否则返回 false。要获取主键 ID,您需要使用上述方法之一。

AutoCloseable 的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.FileWriter;
import java.io.IOException;

class MyFileWriter implements AutoCloseable {
private FileWriter fileWriter;

public MyFileWriter(String fileName) throws IOException {
fileWriter = new FileWriter(fileName);
}

public void write(String content) throws IOException {
fileWriter.write(content);
}

@Override
public void close() throws IOException {
fileWriter.close();
}
}

public class AutoCloseableExample {
public static void main(String[] args) {
String fileName = "example.txt";

try (MyFileWriter writer = new MyFileWriter(fileName)) {
writer.write("Hello, this is an example of using AutoCloseable in Java.");
} catch (IOException e) {
e.printStackTrace();
}
}
}

在这个示例中,我们创建了一个 MyFileWriter 类,实现了 AutoCloseable 接口。它包含了一个 FileWriter 实例来写入文件。在 close() 方法中,我们关闭了 FileWriter

main 方法中,我们使用 try-with-resources 语句来实例化 MyFileWriter 类并写入内容到文件。当 try 块结束时,无论是否发生异常,MyFileWriter 类的 close() 方法都会被调用来关闭文件。

-------------本文结束感谢您的阅读-------------