一、注解与反射
1. 注解
注解是 Java 提供的一种元数据机制,它为程序元素(类、方法、变量等)添加额外信息,这些信息可在编译、运行时被读取和处理,以实现代码检查、配置管理、框架扩展等功能。
自定义注解
自定义注解需使用 @interface
关键字,同时借助元注解指定注解的保留策略、作用目标等。常见元注解有 @Retention
、@Target
、@Documented
和 @Inherited
。
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// @Retention 用于指定注解的保留策略,这里设置为 RUNTIME 表示在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// @Target 用于指定注解可以应用的程序元素类型,这里表示注解可以应用于方法上
@Target(ElementType.METHOD)
@interface MyAnnotation {
// 定义注解的属性,类似于方法声明,可以有默认值
String value() default "default value";
int count() default 1;
}
使用注解
在类中使用自定义注解,并在方法上添加注解属性值。
java
class MyClass {
@MyAnnotation(value = "Hello Annotation", count = 3)
public void myMethod() {
System.out.println("This is a method with annotation.");
}
}
注解处理器
注解处理器可以在编译时处理注解,执行代码生成、验证等操作。例如,使用 javax.annotation.processing
包中的类创建一个简单的注解处理器。
java
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;
@SupportedAnnotationTypes("MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (TypeElement annotation : annotations) {
Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);
for (Element element : annotatedElements) {
MyAnnotation myAnnotation = element.getAnnotation(MyAnnotation.class);
System.out.println("Annotation value: " + myAnnotation.value());
System.out.println("Annotation count: " + myAnnotation.count());
}
}
return true;
}
}
2. 反射
反射允许程序在运行时获取类的信息并操作类的属性、方法和构造函数。通过反射,可在运行时动态地创建对象、调用方法、访问和修改属性等。
获取类的信息
通过 Class
对象可以获取类的各种信息,如类名、父类、接口、方法、属性等。
java
import java.lang.reflect.Method;
public class ReflectionExample {
public static void main(String[] args) throws Exception {
MyClass obj = new MyClass();
// 获取对象的 Class 对象
Class<?> cls = obj.getClass();
// 获取类的名称
System.out.println("Class name: " + cls.getName());
// 获取类的所有方法
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println("Method name: " + method.getName());
// 检查方法上是否有 MyAnnotation 注解
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println("Annotation value: " + annotation.value());
System.out.println("Annotation count: " + annotation.count());
}
}
}
}
动态创建对象和调用方法
通过反射可以在运行时动态地创建对象并调用方法。
java
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectionObjectCreation {
public static void main(String[] args) throws Exception {
// 获取类的 Class 对象
Class<?> cls = MyClass.class;
// 获取类的无参构造函数
Constructor<?> constructor = cls.getConstructor();
// 通过构造函数创建对象
Object obj = constructor.newInstance();
// 获取指定名称和参数类型的方法
Method method = cls.getMethod("myMethod");
// 调用方法
method.invoke(obj);
}
}
反射与泛型
反射可以处理泛型信息,但由于 Java 的泛型擦除机制,在运行时需要特殊处理。
java
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
class GenericClass<T> {
private List<T> list;
public GenericClass() {
list = new ArrayList<>();
}
public List<T> getList() {
return list;
}
}
public class ReflectionWithGeneric {
public static void main(String[] args) throws NoSuchFieldException {
GenericClass<String> genericClass = new GenericClass<>();
Class<?> cls = genericClass.getClass();
java.lang.reflect.Field field = cls.getDeclaredField("list");
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type type : actualTypeArguments) {
System.out.println("Generic type: " + type.getTypeName());
}
}
}
}
二、多线程高级应用
1. 线程池
线程池可以管理和复用线程,避免频繁创建和销毁线程带来的性能开销,提高程序的性能和稳定性。Java 提供了 ExecutorService
接口和 Executors
工具类来创建和管理线程池。
创建线程池
常见的线程池类型有固定大小线程池、缓存线程池、单线程线程池和定时任务线程池。
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Running task in thread: " + Thread.currentThread().getName());
try {
// 模拟任务执行时间
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建固定大小为 3 的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
// 向线程池提交任务
executor.submit(new MyRunnable());
}
// 关闭线程池,不再接受新任务,但会等待已提交的任务执行完毕
executor.shutdown();
}
}
线程池的生命周期管理
线程池有四种状态:运行、关闭、停止和终止。可以通过 shutdown()
方法关闭线程池,不再接受新任务,但会等待已提交的任务执行完毕;通过 shutdownNow()
方法立即停止线程池,尝试中断正在执行的任务,并返回未执行的任务列表。
java
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolLifecycle {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executor.submit(new MyRunnable());
}
// 关闭线程池
executor.shutdown();
// 等待线程池中的任务执行完毕,最多等待 5 秒
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
// 如果 5 秒后任务还未执行完毕,强制停止线程池
List<Runnable> remainingTasks = executor.shutdownNow();
System.out.println("Remaining tasks: " + remainingTasks.size());
}
}
}
自定义线程池
除了使用 Executors
工具类创建线程池,还可以通过 ThreadPoolExecutor
类自定义线程池。
java
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomThreadPoolExample {
public static void main(String[] args) {
int corePoolSize = 2;
int maximumPoolSize = 5;
long keepAliveTime = 60;
TimeUnit unit = TimeUnit.SECONDS;
ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue
);
for (int i = 0; i < 5; i++) {
executor.submit(new MyRunnable());
}
executor.shutdown();
}
}
2. 并发集合
Java 提供了并发集合,如 ConcurrentHashMap
、ConcurrentLinkedQueue
等,这些集合在多线程环境下可以安全地进行读写操作,避免了使用传统集合时可能出现的线程安全问题。
ConcurrentHashMap
ConcurrentHashMap
是线程安全的哈希表,采用分段锁或 CAS(Compare-And-Swap)机制来实现并发操作。
java
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 向 ConcurrentHashMap 中添加元素
map.put("key1", 1);
map.put("key2", 2);
// 获取元素
Integer value = map.get("key1");
System.out.println("Value of key1: " + value);
// 替换元素
map.replace("key2", 3);
System.out.println("Value of key2 after replacement: " + map.get("key2"));
}
}
ConcurrentLinkedQueue
ConcurrentLinkedQueue
是线程安全的无界队列,基于链表实现,适用于多线程环境下的生产者 - 消费者模型。
java
import java.util.concurrent.ConcurrentLinkedQueue;
public class ConcurrentQueueExample {
public static void main(String[] args) {
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
// 向队列中添加元素
queue.add("element1");
queue.add("element2");
// 从队列中取出元素
String element = queue.poll();
System.out.println("Polled element: " + element);
}
}
CopyOnWriteArrayList
CopyOnWriteArrayList
是线程安全的列表,它在进行写操作时会创建一个新的数组副本,适用于读多写少的场景。
java
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("item1");
list.add("item2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
list.add("item3");
iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
3. 锁机制
Java 提供了多种锁机制,如 synchronized
关键字、ReentrantLock
、ReadWriteLock
等,用于实现线程同步和并发控制。
synchronized 关键字
synchronized
关键字可以修饰方法或代码块,确保同一时间只有一个线程可以访问被修饰的方法或代码块。
java
class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
ReentrantLock
ReentrantLock
是一个可重入锁,它提供了比 synchronized
更灵活的锁机制,如可中断锁、定时锁等。
java
import java.util.concurrent.locks.ReentrantLock;
class ReentrantLockExample {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
ReadWriteLock
ReadWriteLock
是一个读写锁,它允许多个线程同时进行读操作,但在写操作时会独占锁,适用于读多写少的场景。
java
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class ReadWriteLockExample {
private int data = 0;
private ReadWriteLock lock = new ReentrantReadWriteLock();
public int readData() {
lock.readLock().lock();
try {
return data;
} finally {
lock.readLock().unlock();
}
}
public void writeData(int newData) {
lock.writeLock().lock();
try {
data = newData;
} finally {
lock.writeLock().unlock();
}
}
}
三、网络编程
1. Socket 编程
Socket 编程用于实现网络通信,Java 提供了 Socket
和 ServerSocket
类来实现客户端 - 服务器模型的网络通信。
服务器端代码
服务器端使用 ServerSocket
监听指定端口,等待客户端连接。当有客户端连接时,会返回一个 Socket
对象,用于与客户端进行通信。
java
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("Server is listening on port 8888");
while (true) {
// 等待客户端连接
Socket socket = serverSocket.accept();
System.out.println("Client connected: " + socket.getInetAddress());
try (Scanner in = new Scanner(socket.getInputStream());
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
String inputLine;
while ((inputLine = in.nextLine()) != null) {
System.out.println("Received from client: " + inputLine);
// 向客户端发送响应
out.println("Server received: " + inputLine);
}
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端代码
客户端使用 Socket
连接到服务器指定的地址和端口,然后可以通过输入输出流与服务器进行通信。
java
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8888);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
Scanner in = new Scanner(socket.getInputStream());
Scanner scanner = new Scanner(System.in)) {
System.out.println("Connected to server. Enter message:");
while (true) {
String message = scanner.nextLine();
// 向服务器发送消息
out.println(message);
if ("quit".equalsIgnoreCase(message)) {
break;
}
// 接收服务器的响应
String response = in.nextLine();
System.out.println("Server response: " + response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
基于 UDP 的 Socket 编程
UDP(User Datagram Protocol)是一种无连接的传输协议,它不保证数据的可靠传输,但具有传输速度快的特点。Java 提供了 DatagramSocket
和 DatagramPacket
类来实现 UDP 通信。
java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
// UDP 服务器
class UDPServer {
public static void main(String[] args) {
try (DatagramSocket socket = new DatagramSocket(8888)) {
byte[] receiveBuffer = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
socket.receive(receivePacket);
String message = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("Received from client: " + message);
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
String responseMessage = "Server received: " + message;
byte[] sendBuffer = responseMessage.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, clientAddress, clientPort);
socket.send(sendPacket);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// UDP 客户端
class UDPClient {
public static void main(String[] args) {
try (DatagramSocket socket = new DatagramSocket()) {
InetAddress serverAddress = InetAddress.getByName("localhost");
String message = "Hello, server!";
byte[] sendBuffer = message.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, serverAddress, 8888);
socket.send(sendPacket);
byte[] receiveBuffer = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
socket.receive(receivePacket);
String responseMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("Received from server: " + responseMessage);
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. NIO 编程
Java NIO(New I/O)是 Java 1.4 引入的新的 I/O 库,提供了非阻塞 I/O 操作,适用于高并发场景。NIO 主要基于通道(Channel)和缓冲区(Buffer)进行数据传输,通过选择器(Selector)实现多路复用。
java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NioServer {
public static void main(String[] args) throws IOException {
// 创建选择器
Selector selector = Selector.open();
// 创建服务器套接字通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8888));
// 设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
// 注册选择器,监听连接事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO Server is listening on port 8888");
while (true) {
// 等待事件发生
selector.select();
// 获取所有已就绪的选择键
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// 处理连接事件
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = ssc.accept();
socketChannel.configureBlocking(false);
// 注册选择器,监听读事件
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
// 处理读事件
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = socketChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data);
System.out.println("Received from client: " + message);
// 向客户端发送响应
ByteBuffer responseBuffer = ByteBuffer.wrap(("Server received: " + message).getBytes());
socketChannel.write(responseBuffer);
}
}
// 移除已处理的选择键
keyIterator.remove();
}
}
}
}
NIO.2 编程
Java 7 引入了 NIO.2,它在 NIO 的基础上进行了扩展,提供了更强大的文件和网络编程功能。例如,使用 AsynchronousFileChannel
进行异步文件操作。
java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Future;
public class Nio2Example {
public static void main(String[] args) {
Path path = Paths.get("test.txt");
try (AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> result = fileChannel.read(buffer, 0);
while (!result.isDone()) {
System.out.println("Reading file...");
}
Integer bytesRead = result.get();
System.out.println("Bytes read: " + bytesRead);
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String content = new String(data);
System.out.println("File content: " + content);
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、数据库操作
1. JDBC 连接数据库
使用 JDBC(Java Database Connectivity)可以连接数据库并执行 SQL 语句。JDBC 提供了一套标准的 API,使得 Java 程序可以与各种数据库进行交互。
连接 MySQL 数据库
首先需要下载并添加 MySQL JDBC 驱动到项目中,然后使用 DriverManager
类来获取数据库连接。
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement()) {
// 执行查询语句
String sql = "SELECT * FROM users";
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
// 获取查询结果
int id = rs.getInt("id");
String username = rs.getString("username");
String email = rs.getString("email");
System.out.println("ID: " + id + ", Username: " + username + ", Email: " + email);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
预处理语句
使用预处理语句(PreparedStatement
)可以提高 SQL 语句的执行效率,同时避免 SQL 注入攻击。
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class PreparedStatementExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// 定义预处理语句
String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置参数
pstmt.setString(1, "john");
// 执行查询
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
String email = rs.getString("email");
System.out.println("ID: " + id + ", Username: " + username + ", Email: " + email);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
批量操作
使用 Statement
或 PreparedStatement
可以进行批量操作,提高数据库操作的效率。
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class BatchOperationExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
String sql = "INSERT INTO users (username, email) VALUES (?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "user1");
pstmt.setString(2, "user1@example.com");
pstmt.addBatch();
pstmt.setString(1, "user2");
pstmt.setString(2, "user2@example.com");
pstmt.addBatch();
int[] results = pstmt.executeBatch();
for (int result : results) {
System.out.println("Insert result: " + result);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2. 事务管理
在数据库操作中,事务是一组不可分割的操作序列,要么全部执行成功,要么全部失败回滚。JDBC 提供了事务管理的功能,可以通过设置自动提交模式和手动提交或回滚来实现事务控制。
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class TransactionExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// 关闭自动提交模式
conn.setAutoCommit(false);
try (Statement stmt = conn.createStatement()) {
// 执行插入操作
String sql1 = "INSERT INTO users (username, email) VALUES ('jane', 'jane@example.com')";
stmt.executeUpdate(sql1);
// 模拟异常
int result = 1 / 0;
// 执行更新操作
String sql2 = "UPDATE users SET email = 'newemail@example.com' WHERE username = 'john'";
stmt.executeUpdate(sql2);
// 提交事务
conn.commit();
System.out.println("Transaction committed successfully");
} catch (Exception e) {
// 回滚事务
conn.rollback();
System.out.println("Transaction rolled back due to exception: " + e.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
分布式事务
在分布式系统中,可能涉及多个数据库或服务的事务操作,需要使用分布式事务管理。常见的分布式事务解决方案有两阶段提交(2PC)、三阶段提交(3PC)和事务补偿机制等。以 JTA(Java Transaction API)为例,它提供了一种标准的方式来管理分布式事务。
java
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class DistributedTransactionExample {
public static void main(String[] args) {
try {
InitialContext ctx = new InitialContext();
UserTransaction ut = (UserTransaction) ctx.lookup("java:comp/UserTransaction");
ut.begin();
Connection conn1 = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "user1", "password1");
Connection conn2 = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "user2", "password2");
Statement stmt1 = conn1.createStatement();
stmt1.executeUpdate("INSERT INTO table1 (column1) VALUES ('value1')");
Statement stmt2 = conn2.createStatement();
stmt2.executeUpdate("INSERT INTO table2 (column2) VALUES ('value2')");
ut.commit();
System.out.println("Distributed transaction committed successfully");
} catch (NamingException | NotSupportedException | SystemException | RollbackException | HeuristicMixedException | HeuristicRollbackException | SQLException e) {
e.printStackTrace();
}
}
}
五、设计模式应用
1. 单例模式
单例模式确保一个类只有一个实例,并提供一个全局访问点。常见的单例模式实现方式有饿汉式、懒汉式、双重检查锁定和静态内部类。
饿汉式单例
饿汉式单例在类加载时就创建实例,线程安全,但可能会造成资源浪费。
java
class SingletonEager {
// 在类加载时就创建实例
private static final SingletonEager instance = new SingletonEager();
private SingletonEager() {}
public static SingletonEager getInstance() {
return instance;
}
}
懒汉式单例
懒汉式单例在第一次使用时才创建实例,但在多线程环境下可能会出现线程安全问题。
java
class SingletonLazy {
private static SingletonLazy instance;
private SingletonLazy() {}
public static synchronized SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
双重检查锁定单例
双重检查锁定单例结合了懒汉式和线程安全的特点,在多线程环境下高效地创建单例实例。
java
class SingletonDoubleCheck {
private static volatile SingletonDoubleCheck instance;
private SingletonDoubleCheck() {}
public static SingletonDoubleCheck getInstance() {
if (instance == null) {
synchronized (SingletonDoubleCheck.class) {
if (instance == null) {
instance = new SingletonDoubleCheck();
}
}
}
return instance;
}
}
静态内部类单例
静态内部类单例利用 Java 静态内部类的特性,实现了延迟加载和线程安全。
java
class SingletonStaticInnerClass {
private SingletonStaticInnerClass() {}
private static class SingletonHolder {
private static final SingletonStaticInnerClass instance = new SingletonStaticInnerClass();
}
public static SingletonStaticInnerClass getInstance() {
return SingletonHolder.instance;
}
}
枚举单例
枚举单例是一种简洁、线程安全且可以防止反序列化重新创建新对象的单例实现方式。
java
enum SingletonEnum {
INSTANCE;
public void doSomething() {
System.out.println("Doing something...");
}
}
2. 工厂模式
工厂模式是一种创建对象的设计模式,将对象的创建和使用分离,提高了代码的可维护性和可扩展性。常见的工厂模式有简单工厂模式、工厂方法模式和抽象工厂模式。
简单工厂模式
简单工厂模式定义一个工厂类,根据传入的参数创建不同类型的对象。
java
// 产品接口
interface Product {
void operation();
}
// 具体产品类
class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductA operation");
}
}
class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductB operation");
}
}
// 简单工厂类
class SimpleFactory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ConcreteProductA();
} else if ("B".equals(type)) {
return new ConcreteProductB();
}
return null;
}
}
public class SimpleFactoryExample {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("A");
productA.operation();
Product productB = SimpleFactory.createProduct("B");
productB.operation();
}
}
工厂方法模式
工厂方法模式将对象的创建延迟到子类中实现,每个具体工厂类负责创建一种具体产品。
java
// 产品接口
interface Product {
void operation();
}
// 具体产品类
class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductA operation");
}
}
class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductB operation");
}
}
// 抽象工厂类
abstract class Factory {
public abstract Product createProduct();
}
// 具体工厂类
class ConcreteFactoryA extends Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
class ConcreteFactoryB extends Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
public class FactoryMethodExample {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.createProduct();
productA.operation();
Factory factoryB = new ConcreteFactoryB();
Product productB = factoryB.createProduct();
productB.operation();
}
}
工厂方法模式的优势在于符合开闭原则,当需要新增产品时,只需创建新的具体产品类和对应的具体工厂类,而无需修改原有的代码。不过,它也有一定的缺点,随着产品种类的增多,具体工厂类的数量会不断增加,导致类的数量膨胀。
抽象工厂模式
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。它与工厂方法模式的区别在于,工厂方法模式针对单一产品等级结构,而抽象工厂模式针对多个产品等级结构。
java
// 抽象产品A
interface AbstractProductA {
void use();
}
// 具体产品A1
class ConcreteProductA1 implements AbstractProductA {
@Override
public void use() {
System.out.println("Using ConcreteProductA1");
}
}
// 具体产品A2
class ConcreteProductA2 implements AbstractProductA {
@Override
public void use() {
System.out.println("Using ConcreteProductA2");
}
}
// 抽象产品B
interface AbstractProductB {
void work();
}
// 具体产品B1
class ConcreteProductB1 implements AbstractProductB {
@Override
public void work() {
System.out.println("ConcreteProductB1 is working");
}
}
// 具体产品B2
class ConcreteProductB2 implements AbstractProductB {
@Override
public void work() {
System.out.println("ConcreteProductB2 is working");
}
}
// 抽象工厂
interface AbstractFactory {
AbstractProductA createProductA();
AbstractProductB createProductB();
}
// 具体工厂1
class ConcreteFactory1 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
}
// 具体工厂2
class ConcreteFactory2 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public AbstractProductB createProductB() {
return new ConcreteProductB2();
}
}
public class AbstractFactoryExample {
public static void main(String[] args) {
AbstractFactory factory1 = new ConcreteFactory1();
AbstractProductA productA1 = factory1.createProductA();
AbstractProductB productB1 = factory1.createProductB();
productA1.use();
productB1.work();
AbstractFactory factory2 = new ConcreteFactory2();
AbstractProductA productA2 = factory2.createProductA();
AbstractProductB productB2 = factory2.createProductB();
productA2.use();
productB2.work();
}
}
抽象工厂模式的优点是将一系列产品的创建封装在一个工厂中,便于维护和管理。缺点是当产品等级结构发生变化时,需要修改抽象工厂接口及其所有具体工厂类,违反了开闭原则。
3. 观察者模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己的状态。
java
import java.util.ArrayList;
import java.util.List;
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 观察者接口
interface Observer {
void update(String message);
}
// 具体主题
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String message;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(message);
}
}
public void setMessage(String message) {
this.message = message;
notifyObservers();
}
}
// 具体观察者
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
public class ObserverPatternExample {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver("Observer1");
ConcreteObserver observer2 = new ConcreteObserver("Observer2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.setMessage("New message from the subject!");
subject.removeObserver(observer2);
subject.setMessage("Another message after removing an observer.");
}
}
观察者模式在很多场景中都有应用,如 GUI 编程中的事件处理、消息通知系统等。它的优点是实现了对象之间的解耦,提高了系统的可维护性和可扩展性。但如果观察者过多,通知所有观察者可能会带来性能问题。
4. 装饰器模式
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。它通过创建一个包装对象,也就是装饰器,来包裹真实的对象。
java
// 组件接口
interface Component {
void operation();
}
// 具体组件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
// 抽象装饰器
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器A
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehaviorA();
}
private void addedBehaviorA() {
System.out.println("Added behavior A");
}
}
// 具体装饰器B
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehaviorB();
}
private void addedBehaviorB() {
System.out.println("Added behavior B");
}
}
public class DecoratorPatternExample {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decoratedComponentA = new ConcreteDecoratorA(component);
Component decoratedComponentAB = new ConcreteDecoratorB(decoratedComponentA);
decoratedComponentAB.operation();
}
}
装饰器模式的优点是可以动态地为对象添加功能,避免了使用继承带来的类爆炸问题。但它也会导致代码中对象嵌套较多,增加了代码的复杂度。
5. 代理模式
代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用。
静态代理
java
// 主题接口
interface Subject {
void request();
}
// 真实主题
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject handling request");
}
}
// 代理主题
class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
preRequest();
realSubject.request();
postRequest();
}
private void preRequest() {
System.out.println("ProxySubject: Preparing to handle request");
}
private void postRequest() {
System.out.println("ProxySubject: Finished handling request");
}
}
public class StaticProxyExample {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.request();
}
}
静态代理的缺点是代理类和委托类的接口方法必须一致,并且每一个委托类都需要创建一个对应的代理类,当接口方法发生变化时,需要同时修改委托类和代理类。
动态代理
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 主题接口
interface Subject {
void request();
}
// 真实主题
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject handling request");
}
}
// 调用处理器
class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
preRequest();
Object result = method.invoke(target, args);
postRequest();
return result;
}
private void preRequest() {
System.out.println("Proxy: Preparing to handle request");
}
private void postRequest() {
System.out.println("Proxy: Finished handling request");
}
}
public class DynamicProxyExample {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxyHandler handler = new ProxyHandler(realSubject);
Subject proxySubject = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class<?>[]{Subject.class},
handler
);
proxySubject.request();
}
}
动态代理通过反射机制在运行时创建代理对象,避免了静态代理的缺点。它在 AOP(面向切面编程)中有着广泛的应用。
六、序列化与反序列化
1. 基本概念
序列化是将对象的状态信息转换为可以存储或传输的形式(如字节序列)的过程,反序列化则是将字节序列恢复为对象的过程。在 Java 中,对象要实现序列化,需要实现 java.io.Serializable
接口。
java
import java.io.*;
// 可序列化的类
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class SerializationExample {
public static void main(String[] args) {
Person person = new Person("John", 30);
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
oos.writeObject(person);
System.out.println("Object serialized");
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person deserializedPerson = (Person) ois.readObject();
System.out.println("Deserialized person: " + deserializedPerson.getName() + ", " + deserializedPerson.getAge());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
2. 注意事项
serialVersionUID
:它是一个序列化版本号,用于在反序列化时验证序列化对象和当前类是否兼容。如果没有显式定义,Java 会根据类的结构自动生成一个,当类的结构发生变化时,可能会导致反序列化失败。因此,建议显式定义serialVersionUID
。transient
关键字:如果某些字段不需要序列化,可以使用transient
关键字修饰。例如:
java
import java.io.*;
class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private transient int salary;
public Employee(String name, int salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public int getSalary() {
return salary;
}
}
public class TransientExample {
public static void main(String[] args) {
Employee employee = new Employee("Alice", 5000);
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employee.ser"))) {
oos.writeObject(employee);
System.out.println("Employee object serialized");
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("employee.ser"))) {
Employee deserializedEmployee = (Employee) ois.readObject();
System.out.println("Deserialized employee: " + deserializedEmployee.getName() + ", Salary: " + deserializedEmployee.getSalary());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
在上述例子中,salary
字段被 transient
修饰,反序列化后该字段的值为默认值(对于 int
类型是 0)。
七、性能优化与调优
1. 代码层面优化
减少对象创建
频繁创建对象会增加垃圾回收的压力,尽量复用对象。例如,使用 StringBuilder
代替 String
进行字符串拼接。
java
public class StringConcatenationExample {
public static void main(String[] args) {
// 使用 StringBuilder 进行字符串拼接
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
String result = sb.toString();
}
}
避免使用全局变量
全局变量会增加对象的生命周期,不利于垃圾回收。尽量使用局部变量。
合理使用数据结构
根据不同的业务场景选择合适的数据结构。例如,需要频繁查找元素时,使用 HashMap
;需要保持插入顺序时,使用 LinkedHashMap
。
2. 内存优化
垃圾回收机制
了解 Java 的垃圾回收机制,合理设置堆内存大小。可以通过 -Xmx
和 -Xms
参数来设置最大堆内存和初始堆内存。例如:
plaintext
java -Xmx512m -Xms256m YourMainClass
弱引用和软引用
使用 WeakReference
和 SoftReference
可以在内存不足时让对象更容易被垃圾回收。例如:
java
import java.lang.ref.SoftReference;
public class SoftReferenceExample {
public static void main(String[] args) {
byte[] largeObject = new byte[1024 * 1024];
SoftReference<byte[]> softReference = new SoftReference<>(largeObject);
largeObject = null; // 释放强引用
byte[] retrievedObject = softReference.get();
if (retrievedObject != null) {
System.out.println("Object is still in memory");
} else {
System.out.println("Object has been garbage collected");
}
}
}
3. 性能监测工具
VisualVM
VisualVM 是一个可视化的性能监测工具,可以实时监控 Java 应用程序的内存使用、线程状态、CPU 使用率等。通过它可以分析应用程序的性能瓶颈。
YourKit
YourKit 是一款功能强大的 Java 性能分析工具,它可以深入分析应用程序的内存泄漏、方法调用时间等问题。
通过深入学习这些 Java 高级知识,你能更全面、更深入地理解 Java 语言的特性和机制,编写出更高效、更复杂、更具可维护性和可扩展性的 Java 程序。同时,不断实践和应用这些知识,将有助于你在实际项目中更好地解决问题,提升自己的编程能力和技术水平。