JAVA技术总结与面试回顾-仅个人学习总结

  1. Java基础

Java语法、数据类型、集合框架、异常处理的详细解释与说明

一、Java语法

Java语法是编写Java程序的基础规则,包括变量声明、方法定义、控制流等。

变量声明:

int age = 25; // 整数类型变量 double salary = 50000.0; // 双精度浮点数 String name = "Alice"; // 字符串

方法定义:

public int add(int a, int b) { return a + b; }

控制流:

  • 条件语句:

if (age > 18) { System.out.println("Adult"); } else { System.out.println("Minor"); }

  • 循环语句:

for (int i = 0; i < 10; i++) { System.out.println(i); } int i = 0; while (i < 10) { System.out.println(i); i++; }

二、数据类型

Java的数据类型分为基本数据类型和引用数据类型。

基本数据类型:

  • 整数类型:byte、short、int、long
  • 浮点类型:float、double
  • 字符类型:char
  • 布尔类型:boolean

int age = 25; double salary = 50000.0; char grade = 'A'; boolean isActive = true;

引用数据类型:

  • 类:如String、ArrayList
  • 接口
  • 数组
  • 枚举

String name = "Alice"; int[] numbers = {1, 2, 3, 4, 5}; List<String> names = new ArrayList<>();

三、集合框架

Java集合框架提供了一组处理集合(如列表、集合、映射等)的接口和类。

List接口:

  • ArrayList:动态数组,查询快,增删慢
  • LinkedList:双向链表,增删快,查询慢

List<String> arrayList = new ArrayList<>(); arrayList.add("Alice"); arrayList.add("Bob"); List<String> linkedList = new LinkedList<>(); linkedList.add("Charlie"); linkedList.add("Dave");

Set接口:

  • HashSet:基于哈希表实现,无序,不允许重复元素
  • TreeSet:基于红黑树实现,有序,不允许重复元素

Set<String> hashSet = new HashSet<>(); hashSet.add("Alice"); hashSet.add("Bob"); Set<String> treeSet = new TreeSet<>(); treeSet.add("Charlie"); treeSet.add("Dave");

Map接口:

  • HashMap:基于哈希表实现,无序,键值对存储
  • TreeMap:基于红黑树实现,有序,键值对存储

Map<String, Integer> hashMap = new HashMap<>(); hashMap.put("Alice", 25); hashMap.put("Bob", 30); Map<String, Integer> treeMap = new TreeMap<>(); treeMap.put("Charlie", 35); treeMap.put("Dave", 40);

四、异常处理

Java异常处理机制用于处理程序运行时可能发生的错误。

基本用法:

  • try:包含可能抛出异常的代码
  • catch:捕获并处理异常
  • finally:无论是否发生异常,都会执行的代码块
  • throw:显式抛出异常
  • throws:声明一个方法可能抛出的异常

public void readFile(String fileName) { try { FileReader file = new FileReader(fileName); BufferedReader reader = new BufferedReader(file); String line = reader.readLine(); while (line != null) { System.out.println(line); line = reader.readLine(); } reader.close(); } catch (FileNotFoundException e) { System.out.println("File not found: " + fileName); } catch (IOException e) { System.out.println("Error reading file: " + fileName); } finally { System.out.println("Finished reading file."); } }

自定义异常:

class CustomException extends Exception { public CustomException(String message) { super(message); } } public class Test { public static void main(String[] args) { try { validateAge(15); } catch (CustomException e) { System.out.println(e.getMessage()); } } public static void validateAge(int age) throws CustomException { if (age < 18) { throw new CustomException("Age must be 18 or older."); } } }

面向对象编程(OOP)概念

面向对象编程(OOP)是Java的核心编程范式,主要包含四个基本概念:封装、继承、多态和抽象。下面详细解释这些概念并提供实例代码。

一、封装(Encapsulation)

封装是将对象的状态(属性)和行为(方法)打包在一起,并对外部隐藏实现细节。通过提供公共的方法来访问和修改属性,保证了对象的完整性和安全性。

示例代码:

public class Person { private String name; // 私有属性 private int age; // 公共的getter方法 public String getName() { return name; } // 公共的setter方法 public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { if (age >= 0) { // 验证年龄 this.age = age; } } } public class Main { public static void main(String[] args) { Person person = new Person(); person.setName("Alice"); person.setAge(25); System.out.println("Name: " + person.getName()); System.out.println("Age: " + person.getAge()); } }

二、继承(Inheritance)

继承是指一个类(子类)从另一个类(父类)继承属性和方法,从而实现代码的重用和扩展。

示例代码:

public class Animal { protected String name; public void eat() { System.out.println(name + " is eating."); } } public class Dog extends Animal { public Dog(String name) { this.name = name; } public void bark() { System.out.println(name + " is barking."); } } public class Main { public static void main(String[] args) { Dog dog = new Dog("Buddy"); dog.eat(); // 继承自Animal类 dog.bark(); } }

三、多态(Polymorphism)

多态是指同一操作在不同对象上有不同的实现。在Java中,多态主要通过方法重载和方法重写实现。

方法重载(Overloading):

public class MathUtil { public int add(int a, int b) { return a + b; } public double add(double a, double b) { return a + b; } } public class Main { public static void main(String[] args) { MathUtil util = new MathUtil(); System.out.println(util.add(1, 2)); // 输出3 System.out.println(util.add(1.0, 2.0)); // 输出3.0 } }

方法重写(Overriding):

public class Animal { public void makeSound() { System.out.println("Animal makes a sound."); } } public class Dog extends Animal { @Override public void makeSound() { System.out.println("Dog barks."); } } public class Main { public static void main(String[] args) { Animal myDog = new Dog(); myDog.makeSound(); // 输出"Dog barks." } }

四、抽象(Abstraction)

抽象是指将一类对象的共同特性提取出来,定义成抽象类或接口,而不关心具体实现细节。抽象类不能实例化,通常包含抽象方法(没有方法体)。

抽象类:

abstract class Animal { protected String name; public Animal(String name) { this.name = name; } public abstract void makeSound(); // 抽象方法 } class Dog extends Animal { public Dog(String name) { super(name); } @Override public void makeSound() { System.out.println(name + " barks."); } } public class Main { public static void main(String[] args) { Animal myDog = new Dog("Buddy"); myDog.makeSound(); // 输出"Buddy barks." } }

接口:

interface Animal { void makeSound(); // 抽象方法 } class Dog implements Animal { private String name; public Dog(String name) { this.name = name; } @Override public void makeSound() { System.out.println(name + " barks."); } } public class Main { public static void main(String[] args) { Animal myDog = new Dog("Buddy"); myDog.makeSound(); // 输出"Buddy barks." } }

总结

通过掌握封装、继承、多态和抽象的概念及其实现方法,可以更好地设计和开发面向对象的软件系统,提高代码的可维护性和重用性。

Java I/O流和NIO

Java I/O流和NIO(New I/O)提供了处理输入输出操作的机制。I/O流用于处理字节和字符数据,而NIO则提供了非阻塞的、高效的I/O操作。

一、Java I/O流

Java I/O流主要分为字节流和字符流。

字节流:

  • 输入流:InputStream及其子类
  • 输出流:OutputStream及其子类

字符流:

  • 输入流:Reader及其子类
  • 输出流:Writer及其子类

字节流示例:

import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ByteStreamExample { public static void main(String[] args) { try (FileInputStream fis = new FileInputStream("input.txt"); FileOutputStream fos = new FileOutputStream("output.txt")) { int data; while ((data = fis.read()) != -1) { fos.write(data); } } catch (IOException e) { e.printStackTrace(); } } }

字符流示例:

import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CharStreamExample { public static void main(String[] args) { try (FileReader fr = new FileReader("input.txt"); FileWriter fw = new FileWriter("output.txt")) { int data; while ((data = fr.read()) != -1) { fw.write(data); } } catch (IOException e) { e.printStackTrace(); } } }

缓冲流:缓冲流提供了缓冲机制,提高了读写效率。

缓冲字节流:

import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class BufferedByteStreamExample { public static void main(String[] args) { try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))) { int data; while ((data = bis.read()) != -1) { bos.write(data); } } catch (IOException e) { e.printStackTrace(); } } }

缓冲字符流:

import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class BufferedCharStreamExample { public static void main(String[] args) { try (BufferedReader br = new BufferedReader(new FileReader("input.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) { String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); } } catch (IOException e) { e.printStackTrace(); } } }

二、Java NIO

Java NIO提供了非阻塞的、高效的I/O操作,主要包括缓冲区(Buffer)、通道(Channel)和选择器(Selector)。

缓冲区(Buffer):缓冲区是一个用于存储数据的容器。

缓冲区示例:

import java.nio.ByteBuffer; public class BufferExample { public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocate(1024); // 分配一个容量为1024字节的缓冲区 String data = "Hello, NIO!"; buffer.put(data.getBytes()); // 写入数据 buffer.flip(); // 切换为读取模式 byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); // 读取数据 System.out.println(new String(bytes)); } }

通道(Channel):通道是用于读写数据的对象。

通道示例:

import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class ChannelExample { public static void main(String[] args) { try (FileChannel inputChannel = new FileInputStream("input.txt").getChannel(); FileChannel outputChannel = new FileOutputStream("output.txt").getChannel()) { ByteBuffer buffer = ByteBuffer.allocate(1024); while (inputChannel.read(buffer) > 0) { buffer.flip(); outputChannel.write(buffer); buffer.clear(); } } catch (IOException e) { e.printStackTrace(); } } }

选择器(Selector):选择器用于监控多个通道的事件(如读、写、连接等)。

选择器示例:

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; public class SelectorExample { public static void main(String[] args) { try { Selector selector = Selector.open(); ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.bind(new InetSocketAddress(8080)); serverChannel.configureBlocking(false); serverChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { selector.select(); Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { SocketChannel client = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); client.read(buffer); buffer.flip(); client.write(buffer); buffer.clear(); } } } } catch (IOException e) { e.printStackTrace(); } } }

总结

Java I/O流适合处理简单的、阻塞的I/O操作,而NIO则提供了更高效、非阻塞的I/O操作。根据应用场景的不同,选择合适的I/O技术可以提高程序的性能和可扩展性。

多线程与并发编程

Java 提供了多种工具和机制来支持多线程与并发编程,包括线程池、同步机制、并发集合和锁。

一、线程池

线程池是一组可重用的线程,减少了线程创建和销毁的开销,提升了性能和资源利用率。

使用ExecutorService创建线程池:

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { // 创建一个固定大小为5的线程池 ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { executorService.execute(new Task(i)); } // 关闭线程池 executorService.shutdown(); } } class Task implements Runnable { private int taskId; public Task(int taskId) { this.taskId = taskId; } @Override public void run() { System.out.println("Task ID: " + this.taskId + " performed by " + Thread.currentThread().getName()); } }

注释:

  1. ExecutorService 是 Java 提供的用于管理线程池的接口。
  2. Executors.newFixedThreadPool(5) 创建一个固定大小为 5 的线程池。
  3. executorService.execute(new Task(i)) 提交任务给线程池执行。
  4. executorService.shutdown() 关闭线程池。

二、同步机制

同步机制确保多个线程在访问共享资源时保持一致性。

使用 synchronized 关键字:

public class SynchronizedExample { private int counter = 0; // 增加计数器的方法,使用synchronized关键字进行同步 public synchronized void increment() { counter++; } public static void main(String[] args) { SynchronizedExample example = new SynchronizedExample(); Runnable task = example::increment; Thread thread1 = new Thread(task); Thread thread2 = new Thread(task); thread1.start(); thread2.start(); try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final Counter: " + example.counter); } }

注释:

  1. synchronized 关键字用于同步方法,确保同一时间只有一个线程可以访问该方法。
  2. thread1.join() 和 thread2.join() 确保主线程等待子线程执行完毕。

三、并发集合

Java 提供了一些线程安全的并发集合,如 ConcurrentHashMap、CopyOnWriteArrayList 等。

使用 ConcurrentHashMap:

import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ConcurrentCollectionExample { public static void main(String[] args) { ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { final int index = i; executorService.execute(() -> map.put("key" + index, index)); } executorService.shutdown(); while (!executorService.isTerminated()) { } map.forEach((key, value) -> System.out.println(key + ": " + value)); } }

注释:

  1. ConcurrentHashMap 是线程安全的哈希表,适用于高并发环境。
  2. 多线程同时往 map 中添加数据,确保线程安全。

四、锁

锁机制提供了更细粒度的控制来实现同步操作,Java 提供了 ReentrantLock 类。

使用 ReentrantLock:

import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockExample { private int counter = 0; private final Lock lock = new ReentrantLock(); public void increment() { lock.lock(); try { counter++; } finally { lock.unlock(); } } public static void main(String[] args) { LockExample example = new LockExample(); Runnable task = example::increment; Thread thread1 = new Thread(task); Thread thread2 = new Thread(task); thread1.start(); thread2.start(); try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final Counter: " + example.counter); } }

注释:

  1. ReentrantLock 是可重入锁,提供了显式的加锁和解锁操作。
  2. lock.lock() 加锁,lock.unlock() 解锁,确保 counter 变量的线程安全性。

总结

通过线程池、同步机制、并发集合和锁,Java 提供了强大的多线程和并发编程能力,能够有效地处理并发任务,提升程序的性能和可靠性。理解并合理使用这些工具和机制是编写高效并发程序的关键。

  1. Java高级特性

泛型、注解、反射

泛型(Generics)

泛型是Java的一种机制,允许类、接口和方法操作类型参数,从而实现类型安全和代码重用。

泛型类:

public class Box<T> { private T item; public void set(T item) { this.item = item; } public T get() { return item; } public static void main(String[] args) { Box<String> stringBox = new Box<>(); stringBox.set("Hello"); System.out.println(stringBox.get()); Box<Integer> intBox = new Box<>(); intBox.set(123); System.out.println(intBox.get()); } }

注释:

  1. Box 是一个泛型类,其中 T 是类型参数。
  2. 在实例化 Box 时,指定具体类型,如 Box 和 Box。

泛型方法:

public class GenericMethodExample { public static <T> void printArray(T[] array) { for (T element : array) { System.out.print(element + " "); } System.out.println(); } public static void main(String[] args) { Integer[] intArray = {1, 2, 3, 4, 5}; String[] stringArray = {"A", "B", "C", "D"}; printArray(intArray); printArray(stringArray); } }

注释:

  1. 声明了一个类型参数 T,用于定义泛型方法 printArray。
  2. 该方法可以接受任何类型的数组,并打印其元素。

泛型接口:

public interface Pair<K, V> { K getKey(); V getValue(); } public class OrderedPair<K, V> implements Pair<K, V> { private K key; private V value; public OrderedPair(K key, V value) { this.key = key; this.value = value; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } public static void main(String[] args) { Pair<String, Integer> pair = new OrderedPair<>("One", 1); System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue()); } }

注释:

  1. Pair 是一个泛型接口,定义了 getKey 和 getValue 方法。
  2. OrderedPair 实现了 Pair 接口。

注解(Annotations)

注解是元数据,用于为代码提供信息,编译器和工具可以使用这些信息来生成代码、文档和进行编译时检查。

自定义注解:

import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value(); }

注释:

  1. @Retention(RetentionPolicy.RUNTIME) 指定注解的保留策略,在运行时可通过反射访问。

使用自定义注解:

public class AnnotationExample { @MyAnnotation(value = "Hello, Annotation!") public void myMethod() { System.out.println("This is a method with MyAnnotation."); } public static void main(String[] args) throws Exception { AnnotationExample example = new AnnotationExample(); example.myMethod(); // 通过反射获取注解信息 MyAnnotation annotation = example.getClass() .getMethod("myMethod") .getAnnotation(MyAnnotation.class); System.out.println("Annotation value: " + annotation.value()); } }

注释:

  1. @MyAnnotation(value = "Hello, Annotation!") 为 myMethod 方法添加了自定义注解。
  2. 通过反射获取并打印注解的值。

反射(Reflection)

反射是Java的一个强大特性,允许在运行时查看和操作类的结构和行为。

获取类的结构信息:

public class ReflectionExample { public static void main(String[] args) throws Exception { // 获取类对象 Class<?> clazz = Class.forName("java.util.ArrayList"); // 获取类名 System.out.println("Class Name: " + clazz.getName()); // 获取类的所有方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { System.out.println("Method Name: " + method.getName()); } // 获取类的所有构造方法 Constructor<?>[] constructors = clazz.getDeclaredConstructors(); for (Constructor<?> constructor : constructors) { System.out.println("Constructor: " + constructor); } } }

注释:

  1. Class.forName("java.util.ArrayList") 获取 ArrayList 类的 Class 对象。
  2. getDeclaredMethods() 获取类的所有方法。
  3. getDeclaredConstructors() 获取类的所有构造方法。

调用方法和访问字段:

import java.lang.reflect.Method; import java.lang.reflect.Field; public class ReflectiveOperation { private String hidden = "secret"; public void printMessage(String message) { System.out.println(message); } public static void main(String[] args) throws Exception { ReflectiveOperation obj = new ReflectiveOperation(); Class<?> clazz = obj.getClass(); // 调用方法 Method method = clazz.getMethod("printMessage", String.class); method.invoke(obj, "Hello, Reflection!"); // 访问字段 Field field = clazz.getDeclaredField("hidden"); field.setAccessible(true); String fieldValue = (String) field.get(obj); System.out.println("Hidden Field Value: " + fieldValue); } }

注释:

  1. getMethod("printMessage", String.class) 获取 printMessage 方法。
  2. method.invoke(obj, "Hello, Reflection!") 调用方法。
  3. getDeclaredField("hidden") 获取 hidden 字段,并设置为可访问。
  4. field.get(obj) 读取字段值。

总结

泛型提供了类型安全和代码重用,注解为代码添加元数据,反射允许在运行时操作类和对象。这些特性使Java成为强大且灵活的编程语言。理解和熟练运用这些特性是高级Java编程的关键。

Lambda表达式和Stream API

Lambda表达式和Stream API是Java 8引入的重要特性,简化了集合操作和并发编程,提升了代码的简洁性和可读性。

一、Lambda表达式

Lambda表达式提供了一种简洁的语法来定义匿名函数,主要用于替代匿名内部类。基本语法如下:

rust复制代码(parameters) -> expression 或 (parameters) -> { statements }

基本用法:

import java.util.Arrays; import java.util.List; public class LambdaExample { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); // 使用Lambda表达式打印列表中的每个元素 names.forEach(name -> System.out.println(name)); // 使用Lambda表达式实现Runnable接口 Runnable task = () -> System.out.println("Hello from a thread!"); new Thread(task).start(); } }

注释:

  1. names.forEach(name -> System.out.println(name)); 使用Lambda表达式遍历并打印列表中的元素。
  2. Runnable task = () -> System.out.println("Hello from a thread!"); 使用Lambda表达式实现 Runnable 接口。

方法引用:方法引用是Lambda表达式的简写形式,使用::操作符引用方法。

import java.util.Arrays; import java.util.List; public class MethodReferenceExample { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); // 使用方法引用打印列表中的每个元素 names.forEach(System.out::println); } }

注释:

  1. names.forEach(System.out::println); 使用方法引用代替Lambda表达式。

二、Stream API

Stream API 提供了一种高效且易于使用的方式来处理集合数据,通过链式调用对数据进行过滤、排序、映射和聚合操作。

创建流:

import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class StreamCreationExample { public static void main(String[] args) { // 从集合创建流 List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); Stream<String> stream1 = names.stream(); // 从数组创建流 String[] nameArray = {"Alice", "Bob", "Charlie"}; Stream<String> stream2 = Arrays.stream(nameArray); // 使用Stream.of()创建流 Stream<String> stream3 = Stream.of("Alice", "Bob", "Charlie"); } }

注释:

  1. names.stream() 从集合创建流。
  2. Arrays.stream(nameArray) 从数组创建流。
  3. Stream.of("Alice", "Bob", "Charlie") 使用 Stream.of() 创建流。

Stream操作:

中间操作:

  • filter:筛选符合条件的元素。
  • map:将元素转换为另一种形式。
  • sorted:对元素进行排序。

终端操作:

  • forEach:遍历并处理每个元素。
  • collect:将流转换为集合或其他形式。
  • reduce:将元素组合成一个值。

示例代码:

import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class StreamExample { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve"); // 筛选以'A'开头的名字并转换为大写 List<String> filteredNames = names.stream() .filter(name -> name.startsWith("A")) .map(String::toUpperCase) .collect(Collectors.toList()); System.out.println(filteredNames); // 输出:[ALICE] // 计算名字的总长度 int totalLength = names.stream() .mapToInt(String::length) .sum(); System.out.println("Total Length: " + totalLength); // 输出:21 } }

注释:

  1. filter(name -> name.startsWith("A")) 筛选以 'A' 开头的名字。
  2. map(String::toUpperCase) 将名字转换为大写。
  3. collect(Collectors.toList()) 将结果收集为列表。
  4. mapToInt(String::length) 将名字转换为长度并求和。

并行流:并行流利用多核处理器并行处理数据,提升性能。

import java.util.Arrays; import java.util.List; public class ParallelStreamExample { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve"); // 使用并行流打印每个名字 names.parallelStream().forEach(name -> System.out.println(Thread.currentThread().getName() + ": " + name)); } }

注释:

  1. names.parallelStream().forEach(...) 使用并行流处理数据。
  2. Thread.currentThread().getName() 打印当前线程的名称,验证并行处理。

总结

Lambda表达式简化了匿名内部类的使用,提高了代码的简洁性。Stream API 提供了强大的集合处理能力,通过链式调用轻松实现复杂的数据操作。理解和熟练运用这些特性可以显著提升Java编程的效率和可读性。

JVM原理:内存模型、垃圾回收机制、类加载机制

Java虚拟机(JVM)是Java程序运行的基础,理解其内存模型、垃圾回收机制和类加载机制对于编写高效、稳定的Java程序至关重要。

一、JVM内存模型

JVM内存模型(Java Memory Model, JMM)定义了Java程序在运行时使用的内存结构,主要包括以下几个部分:

  1. 方法区(Method Area)

    • 存储类信息、常量、静态变量和JIT编译后的代码。
    • 在JDK 8之前,称为永久代(PermGen),在JDK 8及以后称为元空间(Metaspace)。
  1. 堆(Heap)

    • 用于存储对象实例和数组。
    • 是GC管理的主要区域,堆被所有线程共享。
  1. 栈(Stack)

    • 每个线程有一个独立的栈,存储局部变量和部分结果。
    • 每个方法调用都会创建一个栈帧,包含局部变量表、操作数栈和方法出口。
  1. 程序计数器(Program Counter, PC)

    • 是当前线程执行的字节码指令的地址。
    • 每个线程都有一个独立的程序计数器。
  1. 本地方法栈(Native Method Stack)

    • 为执行本地方法(如使用JNI调用C/C++代码)服务。

示例图:

mathematica复制代码┌─────────────────────┐ │ Method Area │ ├─────────────────────┤ │ Heap │ ├─────────────────────┤ │ Java Stack (Thread1) │ ├─────────────────────┤ │ Java Stack (Thread2) │ ├─────────────────────┤ │ Program Counter (Thread1) │ ├─────────────────────┤ │ Program Counter (Thread2) │ ├─────────────────────┤ │ Native Method Stack(Thread1) │ ├─────────────────────┤ │ Native Method Stack(Thread2) │ └─────────────────────┘

二、垃圾回收(GC)机制

垃圾回收(GC)是JVM自动管理内存的机制,用于回收不再使用的对象以释放内存。主要的GC算法包括:

  1. 标记-清除(Mark-Sweep)

    • 标记阶段:标记所有可达对象。
    • 清除阶段:清除所有未标记的对象。
  1. 复制算法(Copying)

    • 将对象从一个区域复制到另一个区域,适用于年轻代(Young Generation)。
    • 分为Eden区和两个Survivor区(S0, S1)。
  1. 标记-压缩(Mark-Compact)

    • 标记阶段:标记所有可达对象。
    • 压缩阶段:移动所有存活对象,压缩空间。
  1. 分代收集(Generational Collection)

    • 根据对象的生命周期将内存划分为年轻代(Young Generation)和老年代(Old Generation)。
    • 年轻代使用复制算法,老年代使用标记-压缩算法。

GC示例代码:

public class GCDemo { public static void main(String[] args) { for (int i = 0; i < 1000000; i++) { new Object(); // 创建大量对象 } System.gc(); // 手动请求垃圾回收 } }

注释:

  1. System.gc() 手动请求垃圾回收,具体执行由JVM决定。

主要GC收集器:

  • Serial GC:单线程的GC。
  • Parallel GC:多线程的GC,适合多核处理器。
  • CMS GC(Concurrent Mark-Sweep GC):并发标记-清除GC,减少停顿时间。
  • G1 GC(Garbage-First GC):面向低停顿时间的GC,适合大堆内存。

三、类加载机制

类加载机制负责将类文件加载到JVM中,并将其转换为Class对象。主要过程包括加载、链接和初始化:

  1. 加载(Loading)

    • 将字节码文件读入内存,创建Class对象。
  1. 链接(Linking)

    • 验证(Verification):确保字节码符合JVM规范。
    • 准备(Preparation):为类的静态变量分配内存,并初始化默认值。
    • 解析(Resolution):将符号引用转换为直接引用。
  1. 初始化(Initialization)

    • 执行类的静态初始化块和静态变量的初始化。

示例代码:

public class ClassLoaderDemo { public static void main(String[] args) { try { // 使用默认的类加载器加载类 Class<?> clazz = Class.forName("java.util.ArrayList"); // 打印类加载器 System.out.println("ClassLoader: " + clazz.getClassLoader()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }

注释:

  1. Class.forName("java.util.ArrayList") 使用默认类加载器加载 ArrayList 类。
  2. clazz.getClassLoader() 获取类的类加载器。

类加载器的类型:

  • 引导类加载器(Bootstrap ClassLoader):加载核心类库(如rt.jar)。
  • 扩展类加载器(Extension ClassLoader):加载扩展类库(如ext目录下的类)。
  • 应用类加载器(Application ClassLoader):加载应用程序类和库。

总结

理解JVM的内存模型、垃圾回收机制和类加载机制对于编写高效、稳定的Java应用程序至关重要。内存模型定义了JVM管理内存的方式,垃圾回收机制自动回收不再使用的对象,类加载机制则负责将类文件加载到内存中并进行初始化。掌握这些原理有助于优化Java程序的性能和资源使用。

性能调优与故障排查

Java应用的性能调优和故障排查是确保系统高效运行和稳定性的关键。以下是一些常用的性能调优和故障排查方法和工具。

一、性能调优

  1. JVM参数调优

    • 堆内存设置:调整堆内存大小以适应应用的需求。

java -Xms512m -Xmx2048m -jar MyApp.jar

      • -Xms:设置初始堆大小。
      • -Xmx:设置最大堆大小。
    • 垃圾回收器设置:选择合适的GC收集器。

java -XX:+UseG1GC -jar MyApp.jar

      • -XX:+UseG1GC:使用G1垃圾收集器。
      • -XX:+UseParallelGC:使用并行垃圾收集器。
      • -XX:+UseConcMarkSweepGC:使用CMS垃圾收集器。
  1. 代码优化

    • 避免创建不必要的对象:减少对象创建,避免频繁的垃圾回收。
    • 使用高效的数据结构:选择合适的数据结构,如ArrayList比LinkedList更适合随机访问。
    • 优化算法:优化代码中的算法,提高执行效率。
    • 并发优化:合理使用线程池,避免创建过多的线程。
  1. 数据库调优

    • 索引优化:确保查询使用索引,避免全表扫描。
    • 缓存使用:使用缓存减少数据库访问频率。
    • 连接池优化:配置合理的数据库连接池大小,避免频繁创建和销毁连接。
  1. 使用性能分析工具

    • JProfiler:用于CPU、内存和线程分析。
    • VisualVM:JDK自带的性能监控工具,可以进行内存、线程、GC监控。
    • YourKit:强大的Java性能分析工具,支持内存和CPU分析。

二、故障排查

  1. 日志分析

    • 记录充分的日志:在关键位置记录日志,包含错误信息、参数和状态。
    • 使用日志级别:根据需求调整日志级别,避免生产环境记录过多无用日志。
    • 日志分析工具:如ELK(Elasticsearch, Logstash, Kibana)用于集中式日志分析。
  1. 监控和报警

    • 应用监控:使用监控工具(如Prometheus、Grafana)监控应用的CPU、内存、响应时间等指标。
    • 日志报警:设置关键日志的报警规则,当出现错误日志时及时通知。
  1. 线程和内存分析

    • 线程转储分析:获取和分析线程转储(Thread Dump),定位线程死锁和阻塞问题。

jstack <pid> > thread_dump.txt

    • 内存转储分析:获取和分析内存转储(Heap Dump),定位内存泄漏和对象分配问题。

jmap -dump:format=b,file=heap_dump.hprof <pid>

  1. CPU使用分析

    • top命令:监控系统的CPU和内存使用情况。
    • jstack命令:分析高CPU占用时的线程状态,定位热点代码。
  1. GC日志分析

    • 启用GC日志:记录GC日志,分析垃圾回收行为和频率。

java -Xlog:gc*:file=gc.log -jar MyApp.jar

    • GC日志分析工具:使用GCViewer、GCEasy等工具分析GC日志。
  1. 网络问题排查

    • 网络监控:使用网络监控工具(如Wireshark)分析网络流量和延迟。
    • 连接超时和失败:检查网络配置和防火墙设置,确保网络连接正常。

三、示例代码

代码优化示例:

import java.util.ArrayList; import java.util.List; public class CodeOptimizationExample { public static void main(String[] args) { // 避免创建不必要的对象 StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.append("Number: ").append(i).append("\n"); } System.out.println(sb.toString()); // 使用高效的数据结构 List<Integer> list = new ArrayList<>(); for (int i = 0; i < 1000; i++) { list.add(i); } System.out.println("List size: " + list.size()); } }

注释:

  1. 使用StringBuilder替代字符串拼接,避免创建大量临时对象。
  2. 使用ArrayList存储数据,提高访问效率。

多线程优化示例:

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { // 创建固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 100; i++) { executor.submit(() -> { System.out.println(Thread.currentThread().getName() + " is executing task."); }); } // 关闭线程池 executor.shutdown(); } }

注释:

  1. 创建固定大小的线程池,避免频繁创建和销毁线程。
  2. 使用线程池提交任务,提升并发性能。

总结

性能调优和故障排查是保障Java应用高效、稳定运行的关键环节。通过合理配置JVM参数、优化代码、使用性能分析工具和监控系统,可以有效提升应用性能。同时,通过日志分析、监控和分析线程、内存、GC日志等手段,可以快速定位和解决故障,确保系统稳定运行。

  1. 设计模式
    • 了解常见的设计模式(如单例模式、工厂模式、观察者模式等)
    • 掌握设计模式的应用场景和实现方法

一、创建型模式

创建型模式关注对象的创建过程,旨在将对象的创建和使用分离,从而提高系统的灵活性和可扩展性。

  1. 单例模式(Singleton)

应用场景:

    • 确保一个类只有一个实例,并提供全局访问点。
    • 用于配置管理、日志记录等需要全局控制的场景。

实现方法:

public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

注释:

    • 使用 synchronized 确保线程安全。
    • getInstance() 方法提供全局访问点。
  1. 工厂方法模式(Factory Method)

应用场景:

    • 定义一个创建对象的接口,但让子类决定实例化哪个类。
    • 用于需要创建复杂对象或在多个子类中创建不同对象的场景。

实现方法:

// 产品接口 public interface Product { void use(); } // 具体产品 public class ConcreteProductA implements Product { @Override public void use() { System.out.println("Using ConcreteProductA"); } } public class ConcreteProductB implements Product { @Override public void use() { System.out.println("Using ConcreteProductB"); } } // 工厂接口 public abstract class Creator { public abstract Product factoryMethod(); } // 具体工厂 public class ConcreteCreatorA extends Creator { @Override public Product factoryMethod() { return new ConcreteProductA(); } } public class ConcreteCreatorB extends Creator { @Override public Product factoryMethod() { return new ConcreteProductB(); } }

注释:

    • Creator 定义了工厂方法 factoryMethod。
    • ConcreteCreatorA 和 ConcreteCreatorB 实现具体的产品创建逻辑。
  1. 抽象工厂模式(Abstract Factory)

应用场景:

    • 创建一系列相关或相互依赖的对象。
    • 用于需要创建一组相关对象的场景,如UI组件库。

实现方法:

// 抽象产品A public interface AbstractProductA { void use(); } // 具体产品A1 public class ConcreteProductA1 implements AbstractProductA { @Override public void use() { System.out.println("Using ConcreteProductA1"); } } // 具体产品A2 public class ConcreteProductA2 implements AbstractProductA { @Override public void use() { System.out.println("Using ConcreteProductA2"); } } // 抽象产品B public interface AbstractProductB { void use(); } // 具体产品B1 public class ConcreteProductB1 implements AbstractProductB { @Override public void use() { System.out.println("Using ConcreteProductB1"); } } // 具体产品B2 public class ConcreteProductB2 implements AbstractProductB { @Override public void use() { System.out.println("Using ConcreteProductB2"); } } // 抽象工厂 public interface AbstractFactory { AbstractProductA createProductA(); AbstractProductB createProductB(); } // 具体工厂1 public class ConcreteFactory1 implements AbstractFactory { @Override public AbstractProductA createProductA() { return new ConcreteProductA1(); } @Override public AbstractProductB createProductB() { return new ConcreteProductB1(); } } // 具体工厂2 public class ConcreteFactory2 implements AbstractFactory { @Override public AbstractProductA createProductA() { return new ConcreteProductA2(); } @Override public AbstractProductB createProductB() { return new ConcreteProductB2(); } }

注释:

    • AbstractFactory 定义创建相关产品的方法。
    • ConcreteFactory1 和 ConcreteFactory2 实现不同的产品组合。
  1. 建造者模式(Builder)

应用场景:

    • 复杂对象的创建过程可以被分步骤进行。
    • 用于需要创建复杂对象,且创建过程不唯一的场景。

实现方法:

// 产品类 public class Product { private String partA; private String partB; public void setPartA(String partA) { this.partA = partA; } public void setPartB(String partB) { this.partB = partB; } @Override public String toString() { return "Product [partA=" + partA + ", partB=" + partB + "]"; } } // 抽象建造者 public abstract class Builder { protected Product product = new Product(); public abstract void buildPartA(); public abstract void buildPartB(); public Product getResult() { return product; } } // 具体建造者 public class ConcreteBuilder extends Builder { @Override public void buildPartA() { product.setPartA("PartA"); } @Override public void buildPartB() { product.setPartB("PartB"); } } // 指导者 public class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } public void construct() { builder.buildPartA(); builder.buildPartB(); } }

注释:

    • Builder 定义了建造过程。
    • ConcreteBuilder 实现具体的建造逻辑。
    • Director 使用 Builder 完成构建。
  1. 原型模式(Prototype)

应用场景:

    • 通过复制现有对象创建新对象。
    • 用于需要大量相似对象且对象创建成本高的场景。

实现方法:

public class Prototype implements Cloneable { private String name; public Prototype(String name) { this.name = name; } public String getName() { return name; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }

注释:

    • Prototype 实现 Cloneable 接口。
    • clone() 方法用于复制对象。

二、结构型模式

结构型模式关注类和对象的组成,通过组合类或对象来实现更复杂的功能。

  1. 适配器模式(Adapter)

应用场景:

    • 使一个不兼容的接口与客户端代码兼容。
    • 用于已有接口不符合需求的场景。

实现方法:

// 目标接口 public interface Target { void request(); } // 适配者 public class Adaptee { public void specificRequest() { System.out.println("Specific request"); } } // 适配器 public class Adapter implements Target { private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void request() { adaptee.specificRequest(); } }

注释:

    • Adapter 将 Adaptee 的接口适配到 Target 接口。
  1. 桥接模式(Bridge)

应用场景:

    • 分离抽象和实现,使它们可以独立变化。
    • 用于需要独立变化的抽象和实现的场景。

实现方法:

// 实现接口 public interface Implementor { void operation(); } // 具体实现 public class ConcreteImplementorA implements Implementor { @Override public void operation() { System.out.println("Operation in ConcreteImplementorA"); } } // 抽象类 public abstract class Abstraction { protected Implementor implementor; public Abstraction(Implementor implementor) { this.implementor = implementor; } public abstract void operation(); } // 扩展抽象类 public class RefinedAbstraction extends Abstraction { public RefinedAbstraction(Implementor implementor) { super(implementor); } @Override public void operation() { implementor.operation(); } }

注释:

    • Abstraction 持有 Implementor 的引用,并将操作委托给实现类。
    • RefinedAbstraction 实现具体操作。
  1. 组合模式(Composite)

应用场景:

    • 允许将对象组合成树形结构,以表示部分和整体的层次结构。
    • 用于表示树形结构的数据,如文件系统。

实现方法:

import java.util.ArrayList; import java.util.List; // 组件接口 public interface Component { void operation(); } // 叶子类 public class Leaf implements Component { @Override public void operation() { System.out.println("Leaf operation"); } } // 组合类 public class Composite implements Component { private List<Component> children = new ArrayList<>(); public void add(Component component) { children.add(component); } public void remove(Component component) { children.remove(component); } @Override public void operation() { for (Component child : children) { child.operation(); } } }

注释:

    • Composite 允许添加和删除子组件,统一处理子组件的操作。
  1. 装饰器模式(Decorator)

应用场景:

    • 动态地给对象添加额外的职责。
    • 用于需要扩展对象功能而不影响其他对象的场景。

实现方法:

// 组件接口 public interface Component { void operation(); } // 具体组件 public class ConcreteComponent implements Component { @Override public void operation() { System.out.println("ConcreteComponent operation"); } } // 装饰器基类 public abstract class Decorator implements Component { protected Component component; public Decorator(Component component) { this.component = component; } @Override public void operation() { component.operation(); } } // 具体装饰器 public class ConcreteDecorator extends Decorator { public ConcreteDecorator(Component component) { super(component); } @Override public void operation() { super.operation(); System.out.println("ConcreteDecorator operation"); } }

注释:

    • Decorator 通过组合 Component 对象来扩展功能。
    • ConcreteDecorator 实现具体的装饰逻辑。
  1. 外观模式(Facade)

应用场景:

    • 提供一个统一的接口,简化对复杂子系统的访问。
    • 用于需要简化复杂子系统的使用的场景。

实现方法:

// 子系统类1 public class Subsystem1 { public void operation1() { System.out.println("Subsystem1 operation1"); } } // 子系统类2 public class Subsystem2 { public void operation2() { System.out.println("Subsystem2 operation2"); } } // 外观类 public class Facade { private Subsystem1 subsystem1 = new Subsystem1(); private Subsystem2 subsystem2 = new Subsystem2(); public void operation() { subsystem1.operation1(); subsystem2.operation2(); } }

注释:

    • Facade 提供简化接口来访问复杂的子系统。
  1. 享元模式(Flyweight)

应用场景:

    • 用于减少创建对象的数量,通过共享已有对象来提高效率。
    • 用于对象数量众多且占用内存较大的场景。

实现方法:

import java.util.HashMap; import java.util.Map; // 享元接口 public interface Flyweight { void operation(String extrinsicState); } // 具体享元 public class ConcreteFlyweight implements Flyweight { private String intrinsicState; public ConcreteFlyweight(String intrinsicState) { this.intrinsicState = intrinsicState; } @Override public void operation(String extrinsicState) { System.out.println("Intrinsic State: " + intrinsicState + ", Extrinsic State: " + extrinsicState); } } // 享元工厂 public class FlyweightFactory { private Map<String, Flyweight> flyweights = new HashMap<>(); public Flyweight getFlyweight(String key) { if (!flyweights.containsKey(key)) { flyweights.put(key, new ConcreteFlyweight(key)); } return flyweights.get(key); } }

注释:

    • FlyweightFactory 管理和共享享元对象,减少内存使用。

三、行为型模式

行为型模式关注对象之间的协作和职责分配,旨在提高对象之间的互动灵活性。

  1. 责任链模式(Chain of Responsibility)

应用场景:

    • 允许多个对象处理请求,形成一个处理链。
    • 用于请求处理需要多个步骤的场景,如日志记录、权限验证。

实现方法:

// 处理者接口 public abstract class Handler { protected Handler successor; public void setSuccessor(Handler successor) { this.successor = successor; } public abstract void handleRequest(String request); } // 具体处理者A public class ConcreteHandlerA extends Handler { @Override public void handleRequest(String request) { if (request.equals("A")) { System.out.println("Handled by ConcreteHandlerA"); } else if (successor != null) { successor.handleRequest(request); } } } // 具体处理者B public class ConcreteHandlerB extends Handler { @Override public void handleRequest(String request) { if (request.equals("B")) { System.out.println("Handled by ConcreteHandlerB"); } else if (successor != null) { successor.handleRequest(request); } } }

注释:

    • Handler 处理请求并将其传递给下一个处理者。
    • ConcreteHandlerA 和 ConcreteHandlerB 处理具体的请求。
  1. 命令模式(Command)

应用场景:

    • 将请求封装为对象,从而可以使用不同的请求、队列或日志请求。
    • 用于需要将操作请求对象化以支持撤销和重做的场景。

实现方法:

// 命令接口 public interface Command { void execute(); } // 具体命令 public class ConcreteCommand implements Command { private Receiver receiver; public ConcreteCommand(Receiver receiver) { this.receiver = receiver; } @Override public void execute() { receiver.action(); } } // 接收者 public class Receiver { public void action() { System.out.println("Receiver action"); } } // 调用者 public class Invoker { private Command command; public void setCommand(Command command) { this.command = command; } public void invoke() { command.execute(); } }

注释:

    • Command 定义执行操作的方法。
    • ConcreteCommand 实现具体的操作,并调用 Receiver 执行。
  1. 解释器模式(Interpreter)

应用场景:

    • 用于定义语言的语法,并解释语言中的句子。
    • 用于需要解释和处理特定语言或表达式的场景,如编程语言解析。

实现方法:

// 抽象表达式 public interface Expression { boolean interpret(String context); } // 终结符表达式 public class TerminalExpression implements Expression { private String data; public TerminalExpression(String data) { this.data = data; } @Override public boolean interpret(String context) { return context.contains(data); } } // 非终结符表达式 public class OrExpression implements Expression { private Expression expr1; private Expression expr2; public OrExpression(Expression expr1, Expression expr2) { this.expr1 = expr1; this.expr2 = expr2; } @Override public boolean interpret(String context) { return expr1.interpret(context) || expr2.interpret(context); } }

注释:

    • Expression 定义解释方法。
    • TerminalExpression 和 OrExpression 实现具体的解释逻辑。
  1. 迭代器模式(Iterator)

应用场景:

    • 提供一种方法顺序访问集合对象中的元素,而无需暴露集合的内部表示。
    • 用于需要访问集合中的元素而不暴露集合内部结构的场景。

实现方法:

import java.util.ArrayList; import java.util.List; // 迭代器接口 public interface Iterator<T> { boolean hasNext(); T next(); } // 具体迭代器 public class ConcreteIterator<T> implements Iterator<T> { private List<T> items; private int position; public ConcreteIterator(List<T> items) { this.items = items; this.position = 0; } @Override public boolean hasNext() { return position < items.size(); } @Override public T next() { return items.get(position++); } } // 聚合类 public class Aggregate<T> { private List<T> items = new ArrayList<>(); public void add(T item) { items.add(item); } public Iterator<T> iterator() { return new ConcreteIterator<>(items); } }

注释:

    • Iterator 定义遍历集合的接口。
    • ConcreteIterator 实现具体的遍历逻辑。
  1. 中介者模式(Mediator)

应用场景:

    • 定义一个中介对象来封装一系列对象之间的交互。
    • 用于减少对象之间的直接耦合,集中控制交互逻辑的场景。

实现方法:

// 中介者接口 public interface Mediator { void notify(Object sender, String event); } // 具体中介者 public class ConcreteMediator implements Mediator { private Component1 component1; private Component2 component2; public void setComponent1(Component1 component1) { this.component1 = component1; } public void setComponent2(Component2 component2) { this.component2 = component2; } @Override public void notify(Object sender, String event) { if (sender == component1) { component2.handleEvent(event); } else if (sender == component2) { component1.handleEvent(event); } } } // 组件1 public class Component1 { private Mediator mediator; public Component1(Mediator mediator) { this.mediator = mediator; } public void doSomething() { mediator.notify(this, "Event from Component1"); } public void handleEvent(String event) { System.out.println("Component1 handles: " + event); } } // 组件2 public class Component2 { private Mediator mediator; public Component2(Mediator mediator) { this.mediator = mediator; } public void doSomething() { mediator.notify(this, "Event from Component2"); } public void handleEvent(String event) { System.out.println("Component2 handles: " + event); } }

注释:

    • Mediator 定义中介者接口。
    • ConcreteMediator 实现对象间的交互逻辑。
    • Component1 和 Component2 通过中介者进行通信。
  1. 备忘录模式(Memento)

应用场景:

    • 保存和恢复对象的状态,而不暴露对象的实现细节。
    • 用于需要撤销操作或恢复先前状态的场景。

实现方法:

// 备忘录 public class Memento { private String state; public Memento(String state) { this.state = state; } public String getState() { return state; } } // 发起人 public class Originator { private String state; public void setState(String state) { this.state = state; } public String getState() { return state; } public Memento saveStateToMemento() { return new Memento(state); } public void getStateFromMemento(Memento memento) { state = memento.getState(); } } // 管理者 public class Caretaker { private Memento memento; public void saveState(Originator originator) { memento = originator.saveStateToMemento(); } public void restoreState(Originator originator) { originator.getStateFromMemento(memento); } }

注释:

    • Memento 保存对象的状态。
    • Originator 管理对象的状态并提供保存和恢复功能。
    • Caretaker 负责存储和恢复 Memento。
  1. 状态模式(State)

应用场景:

    • 允许一个对象在其内部状态改变时改变其行为。
    • 用于对象状态转换复杂且行为依赖于状态的场景。

实现方法:

// 状态接口 public interface State { void handle(); } // 具体状态A public class ConcreteStateA implements State { @Override public void handle() { System.out.println("Handling State A"); } } // 具体状态B public class ConcreteStateB implements State { @Override public void handle() { System.out.println("Handling State B"); } } // 上下文 public class Context { private State state; public void setState(State state) { this.state = state; } public void request() { state.handle(); } }

注释:

    • Context 根据内部状态决定具体行为。
    • State 接口定义状态的行为。
    • ConcreteStateA 和 ConcreteStateB 实现具体的状态逻辑。
  1. 策略模式(Strategy)

应用场景:

    • 定义一系列算法,将每一个算法封装起来,并使它们可以互换。
    • 用于需要多种算法或策略可选的场景。

实现方法:

// 策略接口 public interface Strategy { void execute(); } // 具体策略A public class ConcreteStrategyA implements Strategy { @Override public void execute() { System.out.println("Executing Strategy A"); } } // 具体策略B public class ConcreteStrategyB implements Strategy { @Override public void execute() { System.out.println("Executing Strategy B"); } } // 上下文 public class Context { private Strategy strategy; public void setStrategy(Strategy strategy) { this.strategy = strategy; } public void executeStrategy() { strategy.execute(); } }

注释:

    • Strategy 定义算法接口。
    • ConcreteStrategyA 和 ConcreteStrategyB 实现不同的算法。
    • Context 使用 Strategy 执行策略。
  1. 模板方法模式(Template Method)

应用场景:

    • 定义一个操作的算法框架,将某些步骤延迟到子类中。
    • 用于需要统一流程并允许子类实现某些步骤的场景。

实现方法:

public abstract class AbstractClass { public final void templateMethod() { step1(); step2(); step3(); } protected abstract void step1(); protected abstract void step2(); private void step3() { System.out.println("Common Step 3"); } } public class ConcreteClass extends AbstractClass { @Override protected void step1() { System.out.println("ConcreteClass Step 1"); } @Override protected void step2() { System.out.println("ConcreteClass Step 2"); } }

注释:

    • templateMethod() 定义了算法的框架。
    • step1() 和 step2() 由子类实现,step3() 是固定的步骤。
  1. 访问者模式(Visitor)

应用场景:

    • 允许在不修改元素类的前提下定义作用于元素的新操作。
    • 用于需要在元素类上添加新的操作而不改变元素类的场景。

实现方法:

// 元素接口 public interface Element { void accept(Visitor visitor); } // 具体元素A public class ConcreteElementA implements Element { @Override public void accept(Visitor visitor) { visitor.visit(this); } } // 具体元素B public class ConcreteElementB implements Element { @Override public void accept(Visitor visitor) { visitor.visit(this); } } // 访问者接口 public interface Visitor { void visit(ConcreteElementA elementA); void visit(ConcreteElementB elementB); } // 具体访问者 public class ConcreteVisitor implements Visitor { @Override public void visit(ConcreteElementA elementA) { System.out.println("Visiting ConcreteElementA"); } @Override public void visit(ConcreteElementB elementB) { System.out.println("Visiting ConcreteElementB"); } }

注释:

    • Visitor 定义访问元素的方法。
    • ConcreteVisitor 实现具体的访问逻辑。

总结

设计模式提供了一些常见问题的解决方案,能够帮助设计更灵活、可扩展的系统。每种模式都有其特定的应用场景和实现方式,选择合适的模式可以显著提升代码的可维护性和复用性。

  1. 数据结构与算法
    • 常见的数据结构:数组、链表、栈、队列、哈希表、树、图
  • 数据结构与算法

1. 数组(Array)

说明:

  • 数组是一种线性数据结构,其中元素在内存中连续存储。可以通过索引(下标)快速访问元素。
  • 优点:支持快速随机访问。
  • 缺点:大小固定,插入和删除操作效率低。

示例:

public class ArrayExample { public static void main(String[] args) { int[] array = {1, 2, 3, 4, 5}; // 访问元素 System.out.println("Element at index 2: " + array[2]); // 修改元素 array[2] = 10; System.out.println("Element at index 2 after modification: " + array[2]); } }

2. 链表(Linked List)

说明:

  • 链表是一种线性数据结构,其中元素通过指针连接,存储在不连续的内存位置。
  • 节点包含数据和指向下一个节点的指针(对于单向链表)。

示例:

public class LinkedListExample { static class Node { int data; Node next; Node(int data) { this.data = data; this.next = null; } } public static void main(String[] args) { // 创建节点 Node head = new Node(1); head.next = new Node(2); head.next.next = new Node(3); // 遍历链表 Node current = head; while (current != null) { System.out.println("Node data: " + current.data); current = current.next; } } }

3. 栈(Stack)

说明:

  • 栈是一种后进先出(LIFO)的数据结构。元素只在一端(栈顶)插入和删除。
  • 操作包括 push(入栈)和 pop(出栈)。

示例:

import java.util.Stack; public class StackExample { public static void main(String[] args) { Stack<Integer> stack = new Stack<>(); // 入栈 stack.push(1); stack.push(2); stack.push(3); // 出栈 System.out.println("Popped element: " + stack.pop()); System.out.println("Top element after pop: " + stack.peek()); } }

4. 队列(Queue)

说明:

  • 队列是一种先进先出(FIFO)的数据结构。元素在一端(队列尾)插入,在另一端(队列头)删除。
  • 操作包括 enqueue(入队)和 dequeue(出队)。

示例:

import java.util.LinkedList; import java.util.Queue; public class QueueExample { public static void main(String[] args) { Queue<Integer> queue = new LinkedList<>(); // 入队 queue.add(1); queue.add(2); queue.add(3); // 出队 System.out.println("Dequeued element: " + queue.poll()); System.out.println("Front element after dequeue: " + queue.peek()); } }

5. 哈希表(Hash Table)

说明:

  • 哈希表是一种基于哈希函数的数据结构,用于实现快速的查找、插入和删除操作。
  • 键值对存储,通过哈希函数计算键的哈希值来访问对应的值。

示例:

import java.util.HashMap; import java.util.Map; public class HashTableExample { public static void main(String[] args) { Map<String, Integer> hashMap = new HashMap<>(); // 插入键值对 hashMap.put("A", 1); hashMap.put("B", 2); hashMap.put("C", 3); // 查找 System.out.println("Value for key 'B': " + hashMap.get("B")); // 删除 hashMap.remove("B"); System.out.println("Value for key 'B' after removal: " + hashMap.get("B")); } }

6. 树(Tree)

说明:

  • 树是一种层次数据结构,由节点组成。每个节点有零个或多个子节点。
  • 二叉树是树的一种特殊形式,每个节点最多有两个子节点。

示例:

public class TreeExample { static class TreeNode { int data; TreeNode left, right; TreeNode(int data) { this.data = data; this.left = null; this.right = null; } } public static void main(String[] args) { // 创建树节点 TreeNode root = new TreeNode(1); root.left = new TreeNode(2); root.right = new TreeNode(3); root.left.left = new TreeNode(4); root.left.right = new TreeNode(5); // 遍历树(前序遍历) preOrderTraversal(root); } public static void preOrderTraversal(TreeNode node) { if (node != null) { System.out.println("Node data: " + node.data); preOrderTraversal(node.left); preOrderTraversal(node.right); } } }

7. 图(Graph)

说明:

  • 图是一种非线性数据结构,由节点(顶点)和边组成。边连接两个节点,表示它们之间的关系。
  • 图可以是有向图或无向图,边可以有权重或没有权重。

示例:

import java.util.*; public class GraphExample { static class Graph { private Map<Integer, List<Integer>> adjList = new HashMap<>(); public void addEdge(int src, int dest) { adjList.computeIfAbsent(src, k -> new ArrayList<>()).add(dest); adjList.computeIfAbsent(dest, k -> new ArrayList<>()).add(src); } public void printGraph() { for (Map.Entry<Integer, List<Integer>> entry : adjList.entrySet()) { System.out.println("Vertex " + entry.getKey() + " is connected to " + entry.getValue()); } } } public static void main(String[] args) { Graph graph = new Graph(); graph.addEdge(1, 2); graph.addEdge(1, 3); graph.addEdge(2, 4); graph.addEdge(3, 4); graph.printGraph(); } }

总结

  • 数组:适用于需要快速随机访问的数据场景。
  • 链表:适用于频繁插入和删除操作的场景。
  • 栈:适用于需要后进先出操作的场景,如递归。
  • 队列:适用于需要先进先出操作的场景,如任务调度。
  • 哈希表:适用于需要快速查找、插入和删除的数据场景。
  • 树:适用于需要层次结构的数据场景,如文件系统。
  • 图:适用于需要表示复杂关系的数据场景,如社交网络。

算法基础

1. 排序(Sorting)

说明:

  • 排序算法将数据元素按照某种顺序(升序或降序)排列。
  • 常见的排序算法包括冒泡排序、选择排序、插入排序、归并排序和快速排序。

示例:

  • 冒泡排序:逐对比较相邻元素,并交换它们的顺序,直到整个数组有序。

public class BubbleSortExample { public static void main(String[] args) { int[] array = {64, 25, 12, 22, 11}; // 冒泡排序 bubbleSort(array); // 打印排序结果 System.out.println("Sorted array:"); for (int num : array) { System.out.print(num + " "); } } public static void bubbleSort(int[] arr) { int n = arr.length; for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (arr[j] > arr[j + 1]) { // 交换 int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } }

2. 查找(Searching)

说明:

  • 查找算法用于在数据集合中寻找特定的元素。
  • 常见的查找算法包括线性查找和二分查找。

示例:

  • 二分查找:在一个已排序的数组中,利用折半策略查找元素。

public class BinarySearchExample { public static void main(String[] args) { int[] sortedArray = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int target = 5; // 二分查找 int result = binarySearch(sortedArray, target); // 打印查找结果 if (result != -1) { System.out.println("Element found at index: " + result); } else { System.out.println("Element not found"); } } public static int binarySearch(int[] arr, int target) { int left = 0; int right = arr.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == target) { return mid; } if (arr[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return -1; // 元素未找到 } }

3. 递归(Recursion)

说明:

  • 递归是指一个函数直接或间接地调用自身。
  • 常用于分治算法,例如计算阶乘、斐波那契数列等。

示例:

  • 斐波那契数列:第 n 项等于前两项之和。

public class FibonacciExample { public static void main(String[] args) { int n = 6; // 计算第n项斐波那契数 System.out.println("Fibonacci number at position " + n + " is " + fibonacci(n)); } public static int fibonacci(int n) { if (n <= 1) { return n; } return fibonacci(n - 1) + fibonacci(n - 2); } }

4. 动态规划(Dynamic Programming)

说明:

  • 动态规划用于解决具有重叠子问题和最优子结构性质的问题。
  • 常用于最短路径、背包问题等。

示例:

  • 背包问题:在限制重量的情况下,最大化背包的总价值。

public class KnapsackExample { public static void main(String[] args) { int[] weights = {1, 2, 3, 8, 7, 4}; int[] values = {20, 5, 10, 40, 15, 25}; int capacity = 10; // 计算最大价值 System.out.println("Maximum value in knapsack = " + knapsack(weights, values, capacity)); } public static int knapsack(int[] weights, int[] values, int capacity) { int n = values.length; int[][] dp = new int[n + 1][capacity + 1]; for (int i = 0; i <= n; i++) { for (int w = 0; w <= capacity; w++) { if (i == 0 || w == 0) { dp[i][w] = 0; } else if (weights[i - 1] <= w) { dp[i][w] = Math.max(values[i - 1] + dp[i - 1][w - weights[i - 1]], dp[i - 1][w]); } else { dp[i][w] = dp[i - 1][w]; } } } return dp[n][capacity]; } }

5. 贪心算法(Greedy Algorithm)

说明:

  • 贪心算法通过选择当前最优解来期望达到全局最优解。
  • 常用于最小生成树、哈夫曼编码等问题。

示例:

  • 活动选择问题:选择最多的互不重叠的活动。

import java.util.*; public class ActivitySelectionExample { static class Activity { int start, end; Activity(int start, int end) { this.start = start; this.end = end; } } public static void main(String[] args) { Activity[] activities = { new Activity(1, 2), new Activity(3, 4), new Activity(0, 6), new Activity(5, 7), new Activity(8, 9) }; // 活动选择 List<Activity> selectedActivities = selectActivities(activities); // 打印选择的活动 System.out.println("Selected activities:"); for (Activity activity : selectedActivities) { System.out.println("Activity: start = " + activity.start + ", end = " + activity.end); } } public static List<Activity> selectActivities(Activity[] activities) { Arrays.sort(activities, Comparator.comparingInt(a -> a.end)); List<Activity> result = new ArrayList<>(); int lastEndTime = 0; for (Activity activity : activities) { if (activity.start >= lastEndTime) { result.add(activity); lastEndTime = activity.end; } } return result; } }

总结

  • 排序:用于将数据按照指定顺序排列。
  • 查找:用于在数据集合中寻找特定元素。
  • 递归:解决问题的基本方法,通常适用于可以分解成子问题的情况。
  • 动态规划:通过保存中间结果来避免重复计算,适用于具有重叠子问题的问题。
  • 贪心算法:通过选择当前最优解来期望达到全局最优解,适用于某些可以通过局部最优解达到全局最优解的问题。
  1. 数据库

数据库面试常问问题及答案

1. 什么是关系型数据库?

回答:关系型数据库是一种通过表(关系)来存储数据的数据库系统。在关系型数据库中,数据以行和列的形式存储在表中,表之间可以通过外键进行关联。常见的关系型数据库管理系统(RDBMS)包括 MySQL、PostgreSQL、Oracle 和 SQL Server。

2. 什么是 SQL?

回答:SQL(Structured Query Language)是用于操作关系型数据库的标准语言。它用于查询、插入、更新和删除数据,并管理数据库结构。SQL 包括数据定义语言(DDL)、数据操作语言(DML)和数据控制语言(DCL)。

  • DDL:创建、修改和删除数据库对象(如表、索引)。例:CREATE TABLE, ALTER TABLE, DROP TABLE。
  • DML:查询和操作数据。例:SELECT, INSERT, UPDATE, DELETE。
  • DCL:管理权限和访问控制。例:GRANT, REVOKE。

3. 什么是主键(Primary Key)?

回答:主键是表中的一个或多个列,其值唯一标识表中的每一行。主键的主要特点是唯一性和非空性(不能有重复或空值)。每个表只能有一个主键。

4. 什么是外键(Foreign Key)?

回答:外键是表中的一列或多列,其值引用另一表的主键。外键用于建立表之间的关系,确保数据的一致性和完整性。外键约束用于防止在主表中没有对应记录的情况下插入或更新数据。

5. 什么是索引(Index)?

回答:索引是数据库表的一种数据结构,用于加快数据检索速度。索引通过为表的一个或多个列创建一个结构,能够快速查找数据,从而提高查询性能。常见的索引类型包括单列索引、多列索引、唯一索引和全文索引。

6. 什么是规范化(Normalization)?

回答:规范化是数据库设计中的一种过程,用于减少数据冗余和提高数据一致性。规范化通过将数据分解到多个表中,并使用主键和外键建立表之间的关系来实现。常见的规范化范式包括:

  • 第一范式(1NF):每列都包含原子值。
  • 第二范式(2NF):满足1NF,且所有非键列完全依赖于主键。
  • 第三范式(3NF):满足2NF,且所有非键列不传递依赖于主键。

7. 什么是反规范化(Denormalization)?

回答:反规范化是对数据库进行调整以提高查询性能的过程。反规范化通常涉及将多个表合并为一个表或减少表之间的连接,以减少复杂的查询操作。这可能会增加数据冗余,但可以提高读取操作的性能。

8. 什么是事务(Transaction)?

回答:事务是一组操作的集合,这些操作作为一个整体执行,要么全部成功,要么全部失败。事务具有以下四个ACID特性:

  • 原子性(Atomicity):事务中的操作要么全部执行,要么全部不执行。
  • 一致性(Consistency):事务执行前后数据库的一致性得以维护。
  • 隔离性(Isolation):事务之间的操作不会互相干扰。
  • 持久性(Durability):事务一旦提交,其结果将永久保存,即使系统崩溃也不会丢失。

9. 什么是 SQL 注入(SQL Injection)?

回答:SQL 注入是一种安全漏洞,攻击者通过在输入数据中插入恶意 SQL 代码来操控数据库。这种攻击可以导致数据泄露、数据丢失、系统破坏等严重问题。防止 SQL 注入的方法包括使用预编译的语句和参数化查询,避免直接拼接 SQL 字符串。

10. 什么是视图(View)?

回答:视图是一个虚拟表,它基于一个或多个表的查询结果。视图不实际存储数据,而是通过定义的查询动态生成数据。视图用于简化复杂的查询、限制数据访问、以及提供数据的不同视图。

11. 什么是存储过程(Stored Procedure)?

回答:存储过程是一组预编译的 SQL 语句和控制流逻辑,被存储在数据库中并可以被应用程序调用。存储过程可以封装业务逻辑、提高性能、以及简化复杂的数据库操作。

12. 什么是触发器(Trigger)?

回答:触发器是数据库中的一种特殊类型的存储过程,它在指定的事件(如插入、更新或删除)发生时自动执行。触发器用于实现自动数据验证、数据审计和维护数据完整性。

13. 什么是联合查询(JOIN)?

回答:联合查询用于从两个或多个表中检索相关数据。常见的联合查询类型包括:

  • INNER JOIN:只返回两个表中匹配的记录。
  • LEFT JOIN(或 LEFT OUTER JOIN):返回左表中所有记录和右表中匹配的记录,右表中没有匹配的记录用NULL填充。
  • RIGHT JOIN(或 RIGHT OUTER JOIN):返回右表中所有记录和左表中匹配的记录,左表中没有匹配的记录用NULL填充。
  • FULL JOIN(或 FULL OUTER JOIN):返回两个表中所有记录,匹配的记录填充在一起,不匹配的记录用NULL填充。

示例:

sql复制代码SELECT employees.name, departments.name FROM employees INNER JOIN departments ON employees.department_id = departments.id;

14. 什么是分区(Partitioning)?

回答:分区是将大表或索引分解成更小、更易管理的部分的过程。分区可以提高查询性能和维护效率。常见的分区方式包括范围分区、列表分区、哈希分区和复合分区。

15. 如何优化数据库性能?

回答:优化数据库性能的方法包括:

  • 索引:为常用的查询条件创建索引,以提高查询速度。
  • 查询优化:编写高效的 SQL 查询,避免不必要的计算和数据扫描。
  • 数据库设计:使用规范化设计减少冗余数据,同时根据需要进行反规范化以提高性能。
  • 缓存:使用缓存技术减少对数据库的直接访问。
  • 数据库配置:调整数据库的配置参数,如内存、缓存大小、并发连接数等,以提高性能。

16. 如何处理数据库的并发问题?

回答:处理数据库并发问题的方法包括:

  • 事务隔离级别:调整事务的隔离级别(如 READ COMMITTED、SERIALIZABLE)以控制并发访问。
  • 锁机制:使用行锁、表锁等机制来控制对数据的访问。
  • 乐观锁和悲观锁:乐观锁假设并发冲突较少,通过版本号或时间戳检测冲突;悲观锁则假设冲突频繁,通过锁定资源来避免冲突。

17. 什么是数据库范式?

回答:数据库范式是设计规范,用于减少数据冗余和提高数据一致性。常见的范式包括:

  • 第一范式(1NF):确保表中的每一列都是原子的,即每列只包含一个值。
  • 第二范式(2NF):在1NF的基础上,确保所有非键列完全依赖于主键。
  • 第三范式(3NF):在2NF的基础上,确保所有非键列不传递依赖于主键。
  • BCNF:在3NF的基础上,确保所有决定因素都是候选键。

18. 什么是数据冗余?

回答:数据冗余是指在数据库中存储了重复的数据。这种重复数据会导致数据不一致、增加存储空间需求以及降低数据更新的效率。通过规范化设计可以减少数据冗余。

19. 什么是数据备份和恢复?

回答:数据备份是将数据库中的数据复制到其他存储介质,以防止数据丢失或损坏。数据恢复是将备份的数据恢复到数据库中的过程。备份可以是完整备份、增量备份或差异备份,恢复可以包括完整恢复、点时间恢复等。

20. 什么是大数据?

回答:大数据是指数据量巨大、增长迅速,且多样化的数据集合,超出了传统数据库管理系统处理的能力。大数据技术包括分布式存储、分布式计算(如 Hadoop、Spark)以及 NoSQL 数据库(如 MongoDB、Cassandra)等。

SQL 优化

1. 使用索引

优化前:

sql复制代码SELECT * FROM employees WHERE last_name = 'Smith';

优化后:

创建索引以加速查询 CREATE INDEX idx_last_name ON employees(last_name); -- 使用优化后的查询 SELECT * FROM employees WHERE last_name = 'Smith';

说明:创建索引可以加速对 last_name 列的查找操作,特别是对于大表。

2. 避免 SELECT *

优化前:

sql复制代码SELECT * FROM orders WHERE order_id = 1001;

优化后:

sql复制代码SELECT order_id, order_date, total_amount FROM orders WHERE order_id = 1001;

说明:选择具体的列而不是 * 可以减少不必要的数据传输,提升性能。

3. 使用合适的 JOIN 类型

优化前:

sql复制代码SELECT employees.name, departments.name FROM employees LEFT JOIN departments ON employees.department_id = departments.id WHERE departments.name = 'Sales';

优化后:

sql复制代码SELECT employees.name, departments.name FROM employees INNER JOIN departments ON employees.department_id = departments.id WHERE departments.name = 'Sales';

说明:如果你只关心匹配的记录,使用 INNER JOIN 替代 LEFT JOIN 可以提升性能。

4. 使用 WHERE 子句限制结果集

优化前:

sql复制代码SELECT * FROM sales WHERE sale_date BETWEEN '2023-01-01' AND '2023-12-31';

优化后:

sql复制代码SELECT sale_id, product_id, sale_date, amount FROM sales WHERE sale_date BETWEEN '2023-01-01' AND '2023-12-31';

说明:选择需要的列而不是所有列可以减少扫描的数据量和结果集大小。

5. 避免在 WHERE 子句中使用函数

优化前:

sql复制代码SELECT * FROM orders WHERE YEAR(order_date) = 2023;

优化后:

sql复制代码SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31';

说明:在 WHERE 子句中使用函数会导致索引失效。直接使用范围查询可以更好地利用索引。

6. 使用 EXISTS 替代 IN

优化前:

sql复制代码SELECT * FROM employees WHERE department_id IN (SELECT id FROM departments WHERE department_name = 'Sales');

优化后:

sql复制代码SELECT * FROM employees WHERE EXISTS ( SELECT 1 FROM departments WHERE department_name = 'Sales' AND departments.id = employees.department_id );

说明:使用 EXISTS 可以在子查询中找到匹配的记录时更早地停止扫描,提高效率。

7. 适当使用 LIMIT 和 OFFSET

优化前:

sql复制代码SELECT * FROM products ORDER BY created_at DESC;

优化后:

仅获取前10条记录 SELECT * FROM products ORDER BY created_at DESC LIMIT 10;

说明:在分页查询中使用 LIMIT 可以减少返回的数据量,提高查询性能。

8. 预先计算聚合结果

优化前:

sql复制代码SELECT department_id, COUNT(*) AS employee_count FROM employees GROUP BY department_id;

优化后:

创建物化视图(某些数据库支持) CREATE MATERIALIZED VIEW department_employee_counts AS SELECT department_id, COUNT(*) AS employee_count FROM employees GROUP BY department_id; -- 查询物化视图 SELECT * FROM department_employee_counts;

说明:对于频繁查询的聚合数据,可以使用物化视图或缓存来提高查询性能。

9. 使用批量操作减少事务数量

优化前:

多次单独插入 INSERT INTO orders (order_id, order_date) VALUES (1, '2023-08-01'); INSERT INTO orders (order_id, order_date) VALUES (2, '2023-08-02'); INSERT INTO orders (order_id, order_date) VALUES (3, '2023-08-03');

优化后:

批量插入 INSERT INTO orders (order_id, order_date) VALUES (1, '2023-08-01'), (2, '2023-08-02'), (3, '2023-08-03');

说明:批量操作减少了数据库的事务数量和通信开销,提高了性能。

10. 使用合适的存储引擎(MySQL 特有)

优化前:

使用默认的存储引擎 CREATE TABLE orders ( order_id INT PRIMARY KEY, order_date DATE );

优化后:

根据需求选择合适的存储引擎 CREATE TABLE orders ( order_id INT PRIMARY KEY, order_date DATE ) ENGINE=InnoDB;

说明:选择适合的存储引擎(如 InnoDB 或 MyISAM)可以根据应用场景优化性能和事务支持。

11. 避免过多的索引

优化前:

sql复制代码CREATE INDEX idx_name ON employees(name); CREATE INDEX idx_department ON employees(department_id); CREATE INDEX idx_salary ON employees(salary);

优化后:

仅创建必要的索引 CREATE INDEX idx_name_department ON employees(name, department_id);

说明:虽然索引可以加速查询,但过多的索引会影响插入、更新和删除性能。应根据查询需求创建必要的索引。

12. 使用连接池

优化前:

sql复制代码// 未使用连接池的数据库操作 Connection conn = DriverManager.getConnection(url, user, password); // 执行数据库操作

优化后:

// 使用连接池 DataSource dataSource = new DataSource(); Connection conn = dataSource.getConnection(); // 执行数据库操作

说明:使用数据库连接池可以减少连接创建和销毁的开销,提高应用的性能和响应速度。

总结

优化 SQL 查询的关键在于:

  • 减少扫描的数据量:通过选择必要的列、限制结果集。
  • 利用索引:确保适当的索引以加速检索。
  • 避免不必要的计算和操作:如在 WHERE 子句中避免使用函数。
  • 提高查询效率:如使用批量操作、合适的 JOIN 类型。
    • SQL语言:数据查询、更新、删除、表结构设计
    • 数据库设计范式
    • 常见关系型数据库:MySQL、PostgreSQL
    • NoSQL数据库:MongoDB、Redis

中间件

  1. Spring Framework
    • Spring Core:依赖注入(DI)、面向切面编程(AOP)
    • Spring Boot:快速开发与配置
    • Spring Data:与数据库的整合
    • Spring Security:安全框架
    • Spring Cloud:微服务架构

在 Spring 框架的面试中,通常会涉及到以下几个方面的问题,包括基本概念、核心特性、以及实际使用中的最佳实践。以下是一些常见的 Spring 面试问题及其详细解答和示例:

1. 什么是 Spring 框架?

回答:Spring 是一个开源的 Java 框架,用于简化企业级应用的开发。它提供了全面的基础设施支持,包括依赖注入(DI)、面向切面编程(AOP)、数据访问、事务管理、Web 应用支持等。Spring 旨在帮助开发人员编写松耦合的、易于测试的代码,并简化企业级应用的开发。

示例:

  • 使用 Spring IoC 容器来管理对象的生命周期和依赖关系。
  • 利用 Spring AOP 来处理跨切关注点,如日志记录和事务管理。

2. 什么是依赖注入(DI)?

回答:依赖注入(Dependency Injection)是一种设计模式,用于实现控制反转(IoC)。通过 DI,Spring 容器在运行时将所需的依赖注入到对象中,而不是由对象自己创建依赖。这样可以减少对象间的耦合,提高代码的灵活性和可测试性。

示例:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public void saveUser(User user) { userRepository.save(user); } }

在这个例子中,UserService 依赖于 UserRepository,Spring 容器通过构造函数注入将 UserRepository 实例注入到 UserService 中。

3. 什么是面向切面编程(AOP)?

回答:面向切面编程(Aspect-Oriented Programming, AOP)是一种编程范式,用于处理横切关注点(如日志、安全、事务)。AOP 通过切面(Aspect)将这些横切关注点从业务逻辑中分离出来,使代码更清晰、更模块化。

示例:

  • 切面类:

import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { @After("execution(* com.example.service.*.*(..))") public void logAfterMethod() { System.out.println("Method executed."); } }

  • 业务逻辑类:

import org.springframework.stereotype.Service; @Service public class UserService { public void addUser() { // 添加用户逻辑 } }

LoggingAspect 切面会在 UserService 类中的所有方法执行后打印日志。

4. Spring Boot 是什么?

回答:Spring Boot 是 Spring 框架的一个扩展,用于简化 Spring 应用的配置和部署。它通过自动配置、起始器(Starter)依赖和内嵌服务器(如 Tomcat、Jetty)等特性,简化了 Spring 应用的设置过程,使开发者能够更快速地创建和部署应用。

示例:

  • Spring Boot 主应用类:

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

@SpringBootApplication 注解自动配置 Spring 环境并启动嵌入式服务器。

5. 什么是 Spring 的事务管理?

回答:Spring 的事务管理是 Spring 框架提供的一项功能,用于确保数据库操作的一致性和完整性。Spring 提供了声明式事务管理和编程式事务管理两种方式。声明式事务管理通过配置文件或注解来管理事务,而编程式事务管理则通过代码显式地控制事务的开始、提交和回滚。

示例:

  • 声明式事务管理:

import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserService { @Transactional public void registerUser(User user) { // 注册用户逻辑 } }

@Transactional 注解表示 registerUser 方法应该在一个事务中执行。

6. Spring 中的 Bean 生命周期是什么?

回答:Spring Bean 的生命周期包括以下几个阶段:

  1. 实例化:Spring 容器创建 Bean 实例。
  2. 依赖注入:Spring 容器注入 Bean 的依赖。
  3. 初始化:调用 Bean 的初始化方法(如 @PostConstruct 注解的方法或实现 InitializingBean 接口的方法)。
  4. 使用: Bean 处于可用状态,可以被应用程序使用。
  5. 销毁:容器关闭时,调用 Bean 的销毁方法(如 @PreDestroy 注解的方法或实现 DisposableBean 接口的方法)。

示例:

  • 初始化和销毁方法:

import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.springframework.stereotype.Component; @Component public class MyBean { @PostConstruct public void init() { System.out.println("Bean 初始化"); } @PreDestroy public void destroy() { System.out.println("Bean 销毁"); } }

7. Spring 中的 @Autowired 注解的作用是什么?

回答:@Autowired 注解用于自动注入依赖。当 Spring 容器创建一个 Bean 时,@Autowired 注解会自动将依赖的 Bean 注入到目标 Bean 中。可以应用于构造函数、字段和 setter 方法。

示例:

  • 构造函数注入:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class MyService { private final DependencyService dependencyService; @Autowired public MyService(DependencyService dependencyService) { this.dependencyService = dependencyService; } }

8. 如何使用 Spring 配置文件配置 Bean?

回答:Spring 支持通过 XML 配置文件来定义和配置 Bean。配置文件包含 Bean 的定义和依赖关系。

示例:

  • 配置文件:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userService" class="com.example.UserService"> <property name="userRepository" ref="userRepository"/> </bean> <bean id="userRepository" class="com.example.UserRepository"/> </beans>

9. 什么是 Spring 的 Bean 后处理器(BeanPostProcessor)?

回答:BeanPostProcessor 是 Spring 提供的一种接口,用于在 Bean 实例化和依赖注入之后,对 Bean 进行自定义处理。通过实现 BeanPostProcessor 接口,可以在 Bean 初始化之前和之后执行自定义逻辑。

示例:

  • 实现 BeanPostProcessor:

import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class CustomBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 在 Bean 初始化之前执行 System.out.println("Before Initialization : " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 在 Bean 初始化之后执行 System.out.println("After Initialization : " + beanName); return bean; } }

10. 如何使用 Spring Boot 的 Actuator 进行应用监控?

回答:Spring Boot Actuator 提供了监控和管理 Spring Boot 应用的功能,如健康检查、应用信息、度量指标等。Actuator 提供了多个内置端点,允许你查看和管理应用的各个方面。

示例:

  • 添加 Actuator 依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

  • 配置 Actuator:

properties复制代码management.endpoints.web.exposure.include=health,info

  • 使用 Actuator 端点: 访问 /actuator/health 查看应用健康状态,访问 /actuator/info 查看应用信息。
  1. MyBatis

1. MyBatis 是什么?

回答:MyBatis 是一个持久层框架,它简化了数据库操作的过程,允许开发人员通过映射 SQL 语句和 Java 对象之间的关系,来实现数据库 CRUD(增删改查)操作。MyBatis 提供了对 SQL 语句的精细控制,并支持动态 SQL。

示例:

  • MyBatis 允许直接在 XML 或注解中定义 SQL 语句,并将结果映射到 Java 对象。

2. 如何配置 MyBatis?

回答:MyBatis 的配置通常涉及 SqlSessionFactory 和 SqlSession 的配置。可以使用 XML 配置文件或 Java 配置类。

示例:

  • XML 配置文件(mybatis-config.xml):

<configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="root"/> <property name="password" value="password"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/example/mapper/UserMapper.xml"/> </mappers> </configuration>

  • Java 配置:

import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import javax.sql.DataSource; @Configuration public class MyBatisConfig { @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml")); return factoryBean.getObject(); } }

3. MyBatis 的映射器(Mapper)是什么?

回答:MyBatis 的映射器(Mapper)用于定义 SQL 语句和 Java 方法之间的映射关系。映射器可以通过 XML 文件或注解方式定义,并将 SQL 查询结果映射到 Java 对象。

示例:

  • XML 映射器(UserMapper.xml):

<mapper namespace="com.example.mapper.UserMapper"> <select id="findById" parameterType="int" resultType="com.example.model.User"> SELECT * FROM users WHERE id = #{id} </select> <insert id="insertUser" parameterType="com.example.model.User"> INSERT INTO users (name, age) VALUES (#{name}, #{age}) </insert> </mapper>

  • 注解映射器:

import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import com.example.model.User; public interface UserMapper { @Select("SELECT * FROM users WHERE id = #{id}") User findById(int id); @Insert("INSERT INTO users (name, age) VALUES (#{name}, #{age})") void insertUser(User user); }

4. 如何执行复杂的查询?

回答:MyBatis 支持动态 SQL 和复杂查询,通过使用 , , 等标签,可以构建复杂的 SQL 语句。

示例:

  • 动态 SQL:

<select id="findUsers" parameterType="map" resultType="com.example.model.User"> SELECT * FROM users WHERE 1=1 <if test="name != null"> AND name = #{name} </if> <if test="age != null"> AND age = #{age} </if> </select>

5. 如何处理事务?

回答:MyBatis 支持通过 Spring 事务管理器进行事务管理。可以通过 Spring 配置文件或注解来声明事务管理。

示例:

  • Spring 配置(基于注解):

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.jta.JtaTransactionManager; @Configuration @EnableTransactionManagement public class AppConfig { @Bean public PlatformTransactionManager transactionManager() { return new JtaTransactionManager(); } }

6. MyBatis 的缓存机制是什么?

回答:MyBatis 提供了一级缓存和二级缓存:

  • 一级缓存:在 SqlSession 生命周期内有效,默认开启。即同一 SqlSession 中的查询会使用缓存,避免重复查询。
  • 二级缓存:跨 SqlSession 共享的缓存,需要额外配置。适用于缓存查询结果,提高性能。

示例:

  • 开启二级缓存:
    • Mapper XML 配置:

<mapper namespace="com.example.mapper.UserMapper"> <cache/> </mapper>

  • MyBatis 配置:

<configuration> <settings> <setting name="cacheEnabled" value="true"/> </settings> </configuration>

7. 如何处理 MyBatis 中的结果映射?

回答:结果映射用于将 SQL 查询结果映射到 Java 对象。可以通过 resultMap 标签进行自定义映射。

示例:

  • XML 配置:

<resultMap id="userResultMap" type="com.example.model.User"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="age" column="age"/> </resultMap> <select id="findAll" resultMap="userResultMap"> SELECT * FROM users </select>

8. 如何处理 MyBatis 中的分页?

回答:分页通常通过在 SQL 查询中使用 LIMIT 和 OFFSET 来实现。也可以使用分页插件来简化分页操作。

示例:

  • 使用 MyBatis 插件分页(如 PageHelper):

<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>5.2.0</version> </dependency>

  • Mapper XML 配置:

<select id="findUsers" resultType="com.example.model.User"> SELECT * FROM users LIMIT #{offset}, #{limit} </select>

  • 使用 PageHelper:

import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserService { @Autowired private UserMapper userMapper; public PageInfo<User> getUsers(int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); List<User> users = userMapper.findAll(); return new PageInfo<>(users); } }

MyBatis-Plus

MyBatis-Plus 是 MyBatis 的增强工具,旨在简化 MyBatis 的使用,提高开发效率。它提供了一些额外的功能,如自动生成 SQL、内置分页插件、代码生成器等。以下是关于 MyBatis-Plus 的面试技术点及示例说明:

1. MyBatis-Plus 是什么?

回答:MyBatis-Plus 是一个 MyBatis 的增强工具包,旨在简化 MyBatis 的配置和使用。它提供了很多开箱即用的功能,如通用 CRUD 操作、分页查询、条件构造器等,减少了重复代码,提高了开发效率。

示例:

  • 使用 MyBatis-Plus 可以快速实现对数据库表的 CRUD 操作,而无需编写大量的 SQL 语句。

2. 如何配置 MyBatis-Plus?

回答:MyBatis-Plus 的配置涉及到添加相关依赖和配置文件。配置过程通常包括添加 Maven 依赖和配置 MybatisPlus 的 MetaObjectHandler、GlobalConfig 等。

示例:

  • 添加 Maven 依赖:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency>

  • 配置 MyBatis-Plus:
    • Java 配置:

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyBatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }

3. 如何使用 MyBatis-Plus 进行 CRUD 操作?

回答:MyBatis-Plus 提供了通用的 Mapper 接口,可以直接继承并使用其提供的方法进行 CRUD 操作。

示例:

  • 实体类:

import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; @Data @TableName("user") public class User extends Model<User> { private Long id; private String name; private Integer age; }

  • Mapper 接口:

import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.stereotype.Repository; @Repository public interface UserMapper extends BaseMapper<User> { }

  • Service 层使用:

import com.baomidou.mybatisplus.extension.service.IService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService implements IService<User> { @Autowired private UserMapper userMapper; public void saveUser(User user) { userMapper.insert(user); } public User getUserById(Long id) { return userMapper.selectById(id); } }

4. 如何进行分页查询?

回答:MyBatis-Plus 提供了分页插件,简化了分页查询的过程。

示例:

  • 分页查询:

import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserMapper userMapper; public IPage<User> getUsers(int pageNum, int pageSize) { Page<User> page = new Page<>(pageNum, pageSize); return userMapper.selectPage(page, null); } }

5. 如何使用条件构造器(QueryWrapper)?

回答:MyBatis-Plus 提供了 QueryWrapper 类,用于构建复杂的查询条件。

示例:

  • 使用 QueryWrapper:

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserMapper userMapper; public List<User> findUsersByName(String name) { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name", name); return userMapper.selectList(queryWrapper); } }

6. 如何处理逻辑删除?

回答:MyBatis-Plus 支持逻辑删除,通过使用 @TableLogic 注解来标记逻辑删除字段,并在配置中启用逻辑删除功能。

示例:

  • 实体类:

import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; @Data @TableName("user") public class User extends Model<User> { private Long id; private String name; private Integer age; @TableLogic private Integer deleted; // 逻辑删除标记字段 }

  • 配置:

import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusProperties mybatisPlusProperties() { MybatisPlusProperties properties = new MybatisPlusProperties(); properties.getGlobalConfig().setLogicDeleteValue("1"); properties.getGlobalConfig().setLogicNotDeleteValue("0"); return properties; } }

7. 如何使用 MyBatis-Plus 的代码生成器?

回答:MyBatis-Plus 提供了代码生成器,用于自动生成实体类、Mapper 接口、XML 文件等。

示例:

  • 代码生成器配置:

import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.*; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; public class CodeGenerator { public static void main(String[] args) { AutoGenerator generator = new AutoGenerator(); // 全局配置 GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setOutputDir("src/main/java"); globalConfig.setAuthor("author"); globalConfig.setOpen(false); generator.setGlobalConfig(globalConfig); // 数据源配置 DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mydb"); dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver"); dataSourceConfig.setUsername("root"); dataSourceConfig.setPassword("password"); generator.setDataSource(dataSourceConfig); // 包配置 PackageConfig packageConfig = new PackageConfig(); packageConfig.setParent("com.example"); generator.setPackageInfo(packageConfig); // 策略配置 StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setNaming(NamingStrategy.underline_to_camel); strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); generator.setStrategy(strategyConfig); // 执行生成 generator.execute(); } }

8. 如何处理 MyBatis-Plus 的性能优化?

回答:MyBatis-Plus 提供了多种性能优化措施,如分页插件、缓存机制等。使用分页插件可以避免一次性加载大量数据,提高性能。

示例:

  • 分页插件配置:

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyBatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }

Kafka

  • Kafka 主要用于高吞吐量的流处理,支持分布式、持久化存储,适合实时数据流和大数据场景。

1. Kafka 架构与组件

  • Broker:Kafka 的服务器节点,负责存储和传递消息。
  • Topic:消息的分类标准,Kafka 中的每个消息都有一个主题(topic)。
  • Partition:每个 topic 被划分为多个分区(partition),实现数据的分片和并行处理。
  • Producer:生产者,将消息发送到 Kafka 的 topic。
  • Consumer:消费者,从 Kafka 的 topic 中读取消息。
  • Zookeeper:用于协调 Kafka 集群的元数据,跟踪 Kafka 的节点状态。

2. 消息存储与管理

  • 持久化:Kafka 将消息持久化到磁盘,并根据配置保留一定时间或大小。
  • 副本:每个 partition 可以有多个副本(replica),增加系统的可靠性。

3. 生产者与消费者

  • 生产者:将消息发送到 topic 的分区。可以指定分区,也可以使用默认的分区策略。
  • 消费者:订阅一个或多个 topic,从中消费消息。消费者可以组成消费者组,进行负载均衡。

4. 配置与使用

  • 配置:Kafka 的配置通常包括 brokers 的地址、序列化器、反序列化器等。

示例代码:

  • Kafka 生产者:

import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.common.serialization.StringSerializer; import java.util.Properties; public class KafkaProducerExample { public static void main(String[] args) { Properties props = new Properties(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); KafkaProducer<String, String> producer = new KafkaProducer<>(props); ProducerRecord<String, String> record = new ProducerRecord<>("my-topic", "key", "value"); producer.send(record); producer.close(); } }

  • Kafka 消费者:

import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.common.serialization.StringDeserializer; import java.util.Collections; import java.util.Properties; public class KafkaConsumerExample { public static void main(String[] args) { Properties props = new Properties(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group"); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Collections.singletonList("my-topic")); while (true) { for (ConsumerRecord<String, String> record : consumer.poll(100)) { System.out.printf("Consumed record with key %s and value %s%n", record.key(), record.value()); } } } }


RabbitMQ

  • RabbitMQ 主要用于企业级消息传递,支持多种消息路由模式,适合任务队列和消息队列场景。

1. RabbitMQ 架构与组件

  • Broker:消息代理服务器,处理消息的接收、存储和转发。
  • Exchange:消息交换机,根据 routing key 将消息路由到一个或多个队列。
  • Queue:消息队列,存储待处理的消息。
  • Binding:交换机与队列之间的路由关系。
  • Producer:生产者,将消息发送到交换机。
  • Consumer:消费者,从队列中消费消息。

2. 消息路由与传递模式

  • Direct Exchange:通过 routing key 将消息路由到特定的队列。
  • Fanout Exchange:将消息广播到所有绑定的队列。
  • Topic Exchange:根据 routing key 的模式匹配将消息路由到队列。
  • Headers Exchange:根据消息的头信息路由消息。

3. 持久化与可靠性

  • 消息持久化:RabbitMQ 支持将消息持久化到磁盘,以防止服务器重启后丢失。
  • 事务:支持消息事务,确保消息的可靠传递。

4. 配置与使用

  • 配置:RabbitMQ 的配置通常包括交换机、队列、绑定关系等。

示例代码:

  • RabbitMQ 生产者:

import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; public class RabbitMQProducer { private final static String QUEUE_NAME = "hello"; public static void main(String[] argv) throws Exception { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); try (Connection connection = factory.newConnection(); Channel channel = connection.createChannel()) { channel.queueDeclare(QUEUE_NAME, false, false, false, null); String message = "Hello World!"; channel.basicPublish("", QUEUE_NAME, null, message.getBytes()); System.out.println(" [x] Sent '" + message + "'"); } } }

  • RabbitMQ 消费者:

import com.rabbitmq.client.*; public class RabbitMQConsumer { private final static String QUEUE_NAME = "hello"; public static void main(String[] argv) throws Exception { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); try (Connection connection = factory.newConnection(); Channel channel = connection.createChannel()) { channel.queueDeclare(QUEUE_NAME, false, false, false, null); DeliverCallback deliverCallback = (consumerTag, delivery) -> { String message = new String(delivery.getBody(), "UTF-8"); System.out.println(" [x] Received '" + message + "'"); }; channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { }); } } }


Redis

  • Redis 是一个开源的内存数据结构存储系统,广泛用于缓存、消息传递、会话管理等场景。以下是 Redis 的面试技术点、详细说明以及示例代码。

1. Redis 基本概念

  • 数据结构

    • String:简单的键值对。
    • Hash:键值对的集合,适用于存储对象。
    • List:有序的字符串列表。
    • Set:不允许重复的字符串集合。
    • Sorted Set:带分数的有序字符串集合。
    • Bitmap:用于位操作的特殊数据结构。
    • HyperLogLog:用于估算唯一元素数量的数据结构。
    • Geospatial Indexes:用于存储地理位置数据和执行地理位置查询的数据结构。
    • Stream:用于处理消息队列的日志数据结构。
  • 持久化

    • RDB(快照):在指定时间间隔内生成数据的快照。
    • AOF(追加文件):记录每个写操作,允许数据恢复到最新状态。
  • 缓存策略

    • 过期时间:设置键的生存时间。
    • LRU(最近最少使用):用于淘汰不常用的缓存项。
  • 事务

    • Redis 支持通过 MULTI、EXEC、DISCARD 和 WATCH 命令来实现事务。

2. Redis 安装与配置

  • 安装

    • 可以通过下载 Redis 源代码进行编译安装,或使用包管理工具安装(如 apt-get、yum、brew)。
  • 配置

    • Redis 配置文件通常是 redis.conf,可以通过修改配置文件来调整 Redis 的参数(如端口、持久化方式、内存限制等)。

3. Redis 常用命令

  • String 操作

baSET key value GET key DEL key

  • Hash 操作

baHSET hash field value HGET hash field HDEL hash field

  • List 操作

baLPUSH list value RPUSH list value LPOP list RPOP list

  • Set 操作

baSADD set member SREM set member SMEMBERS set

  • Sorted Set 操作

baZADD zset score member ZRANGE zset start stop ZREM zset member

  • 事务操作

baMULTI SET key value GET key EXEC

4. Redis 连接与操作示例

使用 Jedis 客户端库来连接和操作 Redis。

  • 添加依赖:

<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>4.3.1</version> </dependency>

  • 连接 Redis 并执行操作:

import redis.clients.jedis.Jedis; public class RedisExample { public static void main(String[] args) { try (Jedis jedis = new Jedis("localhost", 6379)) { // String 操作 jedis.set("key", "value"); String value = jedis.get("key"); System.out.println("Value: " + value); // Hash 操作 jedis.hset("myhash", "field1", "value1"); String hashValue = jedis.hget("myhash", "field1"); System.out.println("Hash Value: " + hashValue); // List 操作 jedis.lpush("mylist", "value1"); jedis.lpush("mylist", "value2"); System.out.println("List: " + jedis.lrange("mylist", 0, -1)); // Set 操作 jedis.sadd("myset", "member1"); jedis.sadd("myset", "member2"); System.out.println("Set: " + jedis.smembers("myset")); // Sorted Set 操作 jedis.zadd("myzset", 1, "member1"); jedis.zadd("myzset", 2, "member2"); System.out.println("Sorted Set: " + jedis.zrange("myzset", 0, -1)); // 事务操作 jedis.multi() .set("transaction_key", "transaction_value") .get("transaction_key") .exec(); } } }

5. Redis 高级特性

  • 发布/订阅:用于消息传递。可以通过 PUBLISH、SUBSCRIBE、UNSUBSCRIBE 命令实现。
  • Lua 脚本:通过 EVAL 命令在 Redis 中执行 Lua 脚本,实现原子操作。
  • 分布式 Redis:通过 Redis Cluster 支持数据分片,提供高可用性和横向扩展能力。

6. Redis 性能优化

  • 内存管理:合理设置内存限制和淘汰策略。
  • 持久化策略:根据需求选择 RDB 或 AOF,或两者结合使用。
  • 监控和调优:使用 MONITOR、INFO 等命令监控 Redis 性能,调整配置以优化性能。

Eureka

Eureka 是一个由 Netflix 开发的服务发现工具,主要用于微服务架构中,实现服务的注册与发现。Eureka 主要有两个组件:Eureka Server 和 Eureka Client。以下是 Eureka 的面试技术点、详细说明和示例代码。

1. Eureka 基本概念

  • Eureka Server

    • 作为服务注册中心,提供服务注册和服务发现的功能。
    • 服务提供者将自己的服务实例注册到 Eureka Server。
    • 服务消费者从 Eureka Server 获取服务实例的信息进行调用。
  • Eureka Client

    • 服务提供者和消费者使用的客户端,负责将服务注册到 Eureka Server,并从 Eureka Server 获取其他服务的信息。
  • 服务注册

    • 服务实例启动时,向 Eureka Server 注册自己,包括服务名、实例 ID、地址、端口等信息。
  • 服务发现

    • 服务消费者通过 Eureka Client 从 Eureka Server 获取服务实例列表,以便进行调用。
  • 健康检查

    • Eureka 定期检查注册的服务实例的健康状态,并在服务实例不可用时将其从注册中心移除。

2. Eureka 架构

  • Eureka Server

    • 存储服务注册信息,并提供 REST API 接口供客户端查询和注册服务。
    • 支持高可用性配置,通过集群模式实现容错。
  • Eureka Client

    • 通过配置连接到 Eureka Server。
    • 定期向 Eureka Server 发送心跳以维持服务注册状态。
    • 从 Eureka Server 获取服务列表并进行负载均衡。

3. 配置 Eureka

  • Eureka Server 配置

    • 配置 Eureka Server 的端口、注册地址、集群配置等。

application.yml 示例:

eureka: client: registerWithEureka: false fetchRegistry: false server: enableSelfPreservation: false spring: application: name: eureka-server

  • Eureka Client 配置

    • 配置 Eureka Client 的服务名、Eureka Server 地址等。

application.yml 示例:

eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: hostname: localhost spring: application: name: my-service

4. 示例代码

Eureka Server 示例:

  • 主应用类:

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }

Eureka Client 示例:

  • 主应用类

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class MyServiceApplication { public static void main(String[] args) { SpringApplication.run(MyServiceApplication.class, args); } }

  • Controller 示例

import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api") public class MyController { @GetMapping("/hello") public String hello() { return "Hello from Eureka Client!"; } }

5. Eureka 高级特性

  • 自我保护模式
    • Eureka 的自我保护模式用于防止由于网络分区导致的服务不可用。在自我保护模式下,Eureka Server 将保留服务实例,即使这些实例未能发送心跳。
  • 服务的区域分布
    • Eureka 支持多区域部署,可以在不同的区域部署多个 Eureka Server,实现跨区域的服务发现。
  • 动态实例管理
    • 支持动态的服务实例管理,包括服务实例的注册、注销和更新。

6. Eureka 的替代方案

  • Consul:HashiCorp 提供的服务发现和配置管理工具。
  • Zookeeper:Apache 提供的分布式协调服务,可以用于服务发现。
  • Nacos:阿里巴巴提供的动态服务发现、配置和服务管理平台。

Zookeeper

Zookeeper 是一个开源的分布式协调服务,主要用于分布式系统中的协调、配置管理和命名服务。以下是 Zookeeper 的面试技术点、详细说明和示例代码。

1. Zookeeper 基本概念

  • Zookeeper 的主要功能
    • 分布式协调:协调分布式应用程序中的各个节点。
    • 配置管理:集中管理和共享配置数据。
    • 命名服务:提供分布式系统中的命名功能。
    • 分布式锁:用于控制对共享资源的访问。
  • Zookeeper 架构
    • Zookeeper 集群:由多个 Zookeeper 服务器组成,其中一个为领导者(Leader),其他为跟随者(Follower)和观察者(Observer)。
    • ZNode:Zookeeper 中的节点,类似于文件系统中的节点。每个 ZNode 存储数据,并且可以有子节点。
  • Zookeeper 数据模型
    • 树形结构:Zookeeper 的数据以树形结构组织,每个节点称为 ZNode。
    • 临时节点:客户端会话结束后,临时节点会被自动删除。
    • 持久节点:即使客户端会话结束,持久节点也会继续存在。

2. Zookeeper 主要功能

  • 节点操作
    • 创建节点:create
    • 读取节点数据:get
    • 更新节点数据:set
    • 删除节点:delete
  • 节点监视
    • 设置监听器:可以设置节点的监听器,监听节点的变化(如数据变化、子节点变化等)。
  • 分布式锁
    • 创建锁节点:在特定路径下创建节点,作为锁的标识。
    • 获取锁:检查锁节点是否存在,若不存在,则创建锁节点。
    • 释放锁:删除锁节点。

3. Zookeeper 配置

  • 配置文件

:通常为 zoo.cfg,包括以下常用配置项:

    • dataDir:Zookeeper 存储数据的目录。
    • clientPort:客户端连接 Zookeeper 的端口。
    • initLimit:初始化期间的最长时间。
    • syncLimit:同步期间的最长时间。
    • server.X:Zookeeper 节点的配置(包括主机名和端口)。

zoo.cfg 示例:

properties复制代码dataDir=/var/lib/zookeeper clientPort=2181 initLimit=5 syncLimit=2 server.1=localhost:2888:3888 server.2=localhost:2889:3888 server.3=localhost:2890:3888

4. Zookeeper 示例代码

Java 示例:

使用 Apache Curator 框架来简化 Zookeeper 的操作。Curator 是一个 Zookeeper 客户端库,提供了更高级的 API 和工具。

  • 添加 Maven 依赖

<dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>5.3.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>5.3.0</version> </dependency>

  • Zookeeper 连接和操作示例

import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; public class ZookeeperExample { public static void main(String[] args) { String zkConnectString = "localhost:2181"; CuratorFramework client = CuratorFrameworkFactory.newClient( zkConnectString, new ExponentialBackoffRetry(1000, 3) ); client.start(); try { // 创建节点 client.create().withMode(CreateMode.PERSISTENT).forPath("/my-node", "my-data".getBytes()); // 读取节点数据 byte[] data = client.getData().forPath("/my-node"); System.out.println("Data: " + new String(data)); // 更新节点数据 client.setData().forPath("/my-node", "new-data".getBytes()); // 读取更新后的节点数据 data = client.getData().forPath("/my-node"); System.out.println("Updated Data: " + new String(data)); // 删除节点 client.delete().forPath("/my-node"); } catch (KeeperException | InterruptedException e) { e.printStackTrace(); } finally { client.close(); } } }

  • 分布式锁示例

import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import org.apache.curator.retry.ExponentialBackoffRetry; public class ZookeeperLockExample { public static void main(String[] args) { String zkConnectString = "localhost:2181"; CuratorFramework client = CuratorFrameworkFactory.newClient( zkConnectString, new ExponentialBackoffRetry(1000, 3) ); client.start(); InterProcessMutex lock = new InterProcessMutex(client, "/my-lock"); try { lock.acquire(); System.out.println("Lock acquired!"); // 执行需要锁的操作 Thread.sleep(5000); // Simulate some work } catch (Exception e) { e.printStackTrace(); } finally { try { lock.release(); System.out.println("Lock released!"); } catch (Exception e) { e.printStackTrace(); } client.close(); } } }

5. Zookeeper 高级特性

  • ZooKeeper 事务日志:记录所有对 Zookeeper 数据的修改操作,保证数据的一致性。
  • 观察者模式:通过 watch 机制,可以监视节点的数据变化和子节点的变化。
  • 分布式配置管理:集中管理分布式系统的配置信息,支持动态更新。

6. Zookeeper 的替代方案

  • Consul:由 HashiCorp 提供的服务发现和配置管理工具。
  • Eureka:Netflix 提供的服务发现工具。
  • Etcd:CoreOS 提供的分布式键值存储系统,常用于配置管理和服务发现。

Gateway

Gateway 网关在现代微服务架构中扮演了重要角色,它主要用于处理客户端请求,并将其路由到适当的服务。以下是 Gateway 的面试技术点、详细说明和示例代码。

1. Gateway 基本概念

  • 网关的功能

    • 请求路由:将客户端请求路由到合适的服务实例。
    • 负载均衡:在多个服务实例之间分配请求。
    • 请求过滤:对请求进行过滤和处理,例如身份验证、日志记录等。
    • 协议转换:将不同的协议(如 HTTP 到 HTTPS)进行转换。
    • API 聚合:将多个服务的响应聚合成一个响应返回给客户端。
  • 常见网关

    • Spring Cloud Gateway:基于 Spring WebFlux 的非阻塞 API 网关。
    • Zuul:由 Netflix 开发的网关,主要用于路由和负载均衡。
    • Nginx:高性能的反向代理和负载均衡器,也可以作为网关使用。
    • Kong:开源的 API 网关和微服务管理层。

2. Spring Cloud Gateway

Spring Cloud Gateway 是 Spring Cloud 生态系统中的一个组件,专注于提供 API 网关的功能。

  • 主要特性

    • 动态路由:根据请求的 URL、请求头等动态路由请求。
    • 过滤器:可以在请求和响应的生命周期中添加过滤器进行处理。
    • 负载均衡:集成了 Ribbon 进行负载均衡。
  • 配置

    • 路由配置:通过 application.yml 文件或 Java 配置类定义路由。
    • 过滤器配置:通过路由配置文件或代码定义过滤器。

3. Spring Cloud Gateway 配置示例

  • 添加依赖

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>

  • application.yml

示例:

spring: cloud: gateway: routes: - id: service_route uri: http://localhost:8081 predicates: - Path=/service/** filters: - StripPrefix=1

    • 路由配置说明:
      • id:路由的唯一标识符。
      • uri:请求将被转发到的服务地址。
      • predicates:路由条件,例如 Path 表示匹配请求路径。
      • filters:对请求进行处理的过滤器,例如 StripPrefix 用于去除路径前缀。
  • Java 配置示例

java复制代码import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class GatewayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("service_route", r -> r.path("/service/**") .uri("http://localhost:8081") .filter((exchange, chain) -> { exchange.getRequest().mutate().header("X-Custom-Header", "Value"); return chain.filter(exchange); }) .stripPrefix(1)) .build(); } }

    • 配置说明:
      • route:定义一个路由规则。
      • path:匹配请求路径。
      • uri:请求转发到的服务地址。
      • filter:对请求进行处理的过滤器。
      • stripPrefix:去除路径前缀。

4. 网关的常见问题

  • 性能:网关需要处理大量的请求和转发,需优化性能和配置。
  • 安全性:网关可能成为攻击目标,需要确保安全配置,如身份验证和授权。
  • 故障恢复:网关的高可用性和故障恢复策略需要考虑,以避免单点故障。

部署安装

Docker 基础命令

  • 查看 Docker 版本

docker --version # 输出:Docker version 20.10.7, build f0df350

  • 查看 Docker 当前状态

docker info # 输出:显示 Docker 系统的信息,如容器、镜像、存储驱动等

镜像操作

  • 拉取镜像

docker pull <image-name>:<tag> # 例如:docker pull ubuntu:latest

  • 查看本地镜像

docker images # 或 docker image ls # 输出:列出所有本地镜像

  • 删除镜像

docker rmi <image-id> # 例如:docker rmi 7e8f6d0d8e12

  • 查看镜像详细信息

docker inspect <image-id> # 例如:docker inspect ubuntu:latest

  • 标记镜像

docker tag <image-id> <repository>:<tag> # 例如:docker tag 7e8f6d0d8e12 myrepo/myimage:latest

  • 推送镜像到 Docker Hub

docker push <repository>:<tag> # 例如:docker push myrepo/myimage:latest

容器操作

  • 运行容器

docker run -d --name <container-name> <image-name>:<tag> # 例如:docker run -d --name my-container nginx:latest

  • 运行容器并进入交互模式

docker run -it <image-name>:<tag> /bin/bash # 例如:docker run -it ubuntu:latest /bin/bash

  • 查看正在运行的容器

docker ps # 或 docker container ls

  • 查看所有容器(包括停止的容器)

docker ps -a # 或 docker container ls -a

  • 停止容器

docker stop <container-id> # 例如:docker stop my-container

  • 启动容器

docker start <container-id> # 例如:docker start my-container

  • 删除容器

docker rm <container-id> # 例如:docker rm my-container

  • 进入容器

docker exec -it <container-id> /bin/bash # 例如:docker exec -it my-container /bin/bash

  • 查看容器日志

docker logs <container-id> # 例如:docker logs my-container

  • 查看容器的资源使用情况

docker stats # 输出:显示正在运行的容器的资源使用情况

网络操作

  • 查看网络列表

docker network ls # 输出:列出所有 Docker 网络

  • 创建网络

docker network create <network-name> # 例如:docker network create my-network

  • 连接容器到网络

docker network connect <network-name> <container-id> # 例如:docker network connect my-network my-container

  • 断开容器与网络

docker network disconnect <network-name> <container-id> # 例如:docker network disconnect my-network my-container

  • 查看网络详细信息

docker network inspect <network-name> # 例如:docker network inspect my-network

卷操作

  • 查看卷列表

docker volume ls # 输出:列出所有 Docker 卷

  • 创建卷

docker volume create <volume-name> # 例如:docker volume create my-volume

  • 删除卷

docker volume rm <volume-name> # 例如:docker volume rm my-volume

  • 查看卷详细信息

docker volume inspect <volume-name> # 例如:docker volume inspect my-volume

构建和管理镜像

  • 构建镜像

docker build -t <image-name>:<tag> <path> # 例如:docker build -t my-image:latest .

  • 清理未使用的镜像

docker image prune # 或 docker system prune --all

其他命令

  • Docker 进程管理(如 Docker 服务)

docker service ls # 输出:列出所有 Docker 服务

  • Docker Compose 操作
    • 启动服务

docker-compose up

    • 停止服务

docker-compose down

    • 查看服务状态

docker-compose ps

  • 查看 Docker 文件系统

sh已复制!docker df # 输出:显示 Docker 文件系统的使用情况

  • 查看 Docker 版本

docker --version # 输出:Docker version 20.10.7, build f0df350

  • 查看 Docker 当前状态

docker info # 输出:显示 Docker 系统的信息,如容器、镜像、存储驱动等

镜像操作

  • 拉取镜像

docker pull <image-name>:<tag> # 例如:docker pull ubuntu:latest

  • 查看本地镜像

docker images # 或 docker image ls # 输出:列出所有本地镜像

  • 删除镜像

docker rmi <image-id> # 例如:docker rmi 7e8f6d0d8e12

  • 查看镜像详细信息

docker inspect <image-id> # 例如:docker inspect ubuntu:latest

  • 标记镜像

docker tag <image-id> <repository>:<tag> # 例如:docker tag 7e8f6d0d8e12 myrepo/myimage:latest

  • 推送镜像到 Docker Hub

docker push <repository>:<tag> # 例如:docker push myrepo/myimage:latest

容器操作

  • 运行容器

docker run -d --name <container-name> <image-name>:<tag> # 例如:docker run -d --name my-container nginx:latest

  • 运行容器并进入交互模式

docker run -it <image-name>:<tag> /bin/bash # 例如:docker run -it ubuntu:latest /bin/bash

  • 查看正在运行的容器

docker ps # 或 docker container ls

  • 查看所有容器(包括停止的容器)

docker ps -a # 或 docker container ls -a

  • 停止容器

docker stop <container-id> # 例如:docker stop my-container

  • 启动容器

docker start <container-id> # 例如:docker start my-container

  • 删除容器

docker rm <container-id> # 例如:docker rm my-container

  • 进入容器

docker exec -it <container-id> /bin/bash # 例如:docker exec -it my-container /bin/bash

  • 查看容器日志

docker logs <container-id> # 例如:docker logs my-container

  • 查看容器的资源使用情况

docker stats # 输出:显示正在运行的容器的资源使用情况

网络操作

  • 查看网络列表

docker network ls # 输出:列出所有 Docker 网络

  • 创建网络

docker network create <network-name> # 例如:docker network create my-network

  • 连接容器到网络

docker network connect <network-name> <container-id> # 例如:docker network connect my-network my-container

  • 断开容器与网络

docker network disconnect <network-name> <container-id> # 例如:docker network disconnect my-network my-container

  • 查看网络详细信息

docker network inspect <network-name> # 例如:docker network inspect my-network

卷操作

  • 查看卷列表

docker volume ls # 输出:列出所有 Docker 卷

  • 创建卷

docker volume create <volume-name> # 例如:docker volume create my-volume

  • 删除卷

docker volume rm <volume-name> # 例如:docker volume rm my-volume

  • 查看卷详细信息

docker volume inspect <volume-name> # 例如:docker volume inspect my-volume

构建和管理镜像

  • 构建镜像

docker build -t <image-name>:<tag> <path> # 例如:docker build -t my-image:latest .

  • 清理未使用的镜像

docker image prune # 或 docker system prune --all

其他命令

  • Docker 进程管理(如 Docker 服务)

docker service ls # 输出:列出所有 Docker 服务

  • Docker Compose 操作
    • 启动服务

docker-compose up

    • 停止服务

docker-compose down

    • 查看服务状态

docker-compose ps

  • 查看 Docker 文件系统

sh已复制!docker df # 输出:显示 Docker 文件系统的使用情况

Kubernetes

Kubernetes 是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。它提供了一组工具和功能,使得在容器化环境中运行应用程序变得更加高效和可靠。以下是 Kubernetes 的常用命令、面试技术点及举例说明。

1. Kubernetes 常用命令

kubectl 基本命令

  • 查看 Kubernetes 版本

kubectl version

  • 查看集群状态

kubectl cluster-info

  • 查看当前上下文

kubectl config current-context

  • 列出所有命名空间

kubectl get namespaces # 或 kubectl get ns

  • 切换命名空间

kubectl config set-context --current --namespace=<namespace> # 例如:kubectl config set-context --current --namespace=default

Pod 操作

  • 列出所有 Pods

kubectl get pods # 或 kubectl get po

  • 查看 Pod 详细信息

kubectl describe pod <pod-name>

  • 查看 Pod 日志

kubectl logs <pod-name>

  • 进入 Pod

kubectl exec -it <pod-name> -- /bin/bash

  • 删除 Pod

kubectl delete pod <pod-name>

Deployment 操作

  • 创建 Deployment

kubectl create deployment <deployment-name> --image=<image-name> # 例如:kubectl create deployment my-deployment --image=nginx:latest

  • 查看 Deployment 列表

kubectl get deployments # 或 kubectl get deploy

  • 更新 Deployment

kubectl set image deployment/<deployment-name> <container-name>=<new-image> # 例如:kubectl set image deployment/my-deployment nginx=nginx:latest

  • 删除 Deployment

kubectl delete deployment <deployment-name>

Service 操作

  • 创建 Service

kubectl expose deployment <deployment-name> --port=<port> --target-port=<target-port> # 例如:kubectl expose deployment my-deployment --port=80 --target-port=80

  • 查看 Service 列表

kubectl get services # 或 kubectl get svc

  • 删除 Service

kubectl delete service <service-name>

ConfigMap 和 Secret 操作

  • 创建 ConfigMap

kubectl create configmap <configmap-name> --from-literal=<key>=<value> # 例如:kubectl create configmap my-config --from-literal=key1=value1

  • 查看 ConfigMap 列表

kubectl get configmaps # 或 kubectl get cm

  • 删除 ConfigMap

kubectl delete configmap <configmap-name>

  • 创建 Secret

kubectl create secret generic <secret-name> --from-literal=<key>=<value> # 例如:kubectl create secret generic my-secret --from-literal=password=secret

  • 查看 Secret 列表

kubectl get secrets # 或 kubectl get secret

  • 删除 Secret

kubectl delete secret <secret-name>

Volume 操作

  • 查看 PersistentVolume 列表

kubectl get persistentvolumes # 或 kubectl get pv

  • 查看 PersistentVolumeClaim 列表

kubectl get persistentvolumeclaims # 或 kubectl get pvc

2. Kubernetes 面试常见问题

基本概念

  1. 什么是 Kubernetes?
    • 回答:Kubernetes 是一个开源的容器编排平台,用于自动化容器化应用程序的部署、扩展和管理。它提供了一个高度可扩展的系统,用于处理容器化应用的生命周期。
  1. 什么是 Pod?
    • 回答:Pod 是 Kubernetes 中最小的可部署单元,它可以包含一个或多个容器,这些容器共享网络和存储资源。Pod 内的容器通常一起部署、缩放和管理。
  1. 什么是 Deployment?
    • 回答:Deployment 是一种 Kubernetes 控制器,用于管理和更新 Pod 的副本。它确保指定数量的 Pod 副本在集群中运行,并可以进行滚动更新和回滚操作。
  1. 什么是 Service?
    • 回答:Service 是 Kubernetes 中的一种资源,用于定义一组 Pods 的网络访问规则。它提供了负载均衡和服务发现功能,使得 Pod 的访问变得更加稳定和可靠。

高级概念

  1. Kubernetes 的控制平面包括哪些组件?
    • 回答:Kubernetes 的控制平面包括以下组件:
      • kube-apiserver:处理 API 请求,作为集群的入口点。
      • etcd:分布式键值存储,用于保存集群状态和配置。
      • kube-scheduler:负责将未调度的 Pod 调度到合适的节点上。
      • kube-controller-manager:运行控制器以维护集群的状态。
      • cloud-controller-manager:处理与云服务相关的控制逻辑。
  1. 什么是 ConfigMap 和 Secret?
    • 回答:ConfigMap 和 Secret 是 Kubernetes 中用于存储配置信息的对象。ConfigMap 用于存储非敏感的配置数据,而 Secret 用于存储敏感数据,如密码和密钥。它们可以被 Pods 使用,以便在应用程序运行时读取这些配置或密钥。
  1. 什么是 StatefulSet?
    • 回答:StatefulSet 是 Kubernetes 中用于管理有状态应用的控制器。与 Deployment 不同,StatefulSet 确保每个 Pod 都具有稳定的网络标识和持久化的存储。它适用于需要稳定网络身份和持久存储的应用程序,如数据库。
  1. 什么是 Helm?
    • 回答:Helm 是 Kubernetes 的包管理工具,用于简化应用程序的部署和管理。它通过 Helm Chart 组织和打包 Kubernetes 配置文件,使得部署、升级和回滚应用变得更加简单和高效。
  1. 如何实现 Kubernetes 集群的高可用性?
    • 回答:实现 Kubernetes 集群的高可用性可以通过以下措施:
      • 多节点控制平面:部署多个 kube-apiserver、etcd 实例以避免单点故障。
      • 负载均衡:在控制平面节点之间配置负载均衡器,以分发 API 请求。
      • 多数据中心部署:在多个数据中心部署节点,以提高容错能力和灾难恢复能力。
      • Pod 副本:使用 Deployment、ReplicaSet 等资源定义 Pod 副本,确保服务的高可用性。

3. Kubernetes YAML 配置示例

Pod 配置

apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: my-container image: nginx:latest ports: - containerPort: 80

Deployment 配置

apiVersion: apps/v1 kind: Deployment metadata: name: my-deployment spec: replicas: 3 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-container image: nginx:latest ports: - containerPort: 80

Service 配置

apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: my-app ports: - protocol: TCP port: 80 targetPort: 80 type: LoadBalancer

ConfigMap 配置

apiVersion: v1 kind: ConfigMap metadata: name: my-config data: config.key1: value1 config.key2: value2

Secret 配置

apiVersion: v1 kind: Secret metadata: name: my-secret type: Opaque data: password: c2VjcmV0 # password: secret (base64 encoded value)

4. 常见问题与解决方案

  • 如何调试 Kubernetes 集群中的问题?
    • 回答:
      • 查看 Pod 日志:使用 kubectl logs 命令查看 Pod 的日志信息。
      • 描述资源:使用 kubectl describe 命令查看资源的详细信息,以诊断问题。
      • 检查事件:使用 kubectl get events 查看集群事件,了解最近发生的事件和错误。
      • 使用 kubectl exec:进入容器内部进行手动检查和调试。
  • 如何进行 Kubernetes 升级?
    • 回答:
      • 备份数据:在进行任何升级之前备份 etcd 数据和配置文件。
      • 升级控制平面:逐步升级控制平面组件,确保新版本的兼容性。
      • 升级节点:逐步升级集群节点的 Kubernetes 版本,并重启节点。
      • 升级应用程序:使用 Deployment 进行应用程序的滚动更新,以最小化服务中断。

其他

  1. 版本控制
    • Git:分布式版本控制系统
    • GitHub/GitLab:代码托管平台
  1. 测试
    • 单元测试:JUnit、Mockito
    • 集成测试:Spring Test
    • 性能测试:JMeter
  1. 监控与日志
    • ELK Stack(Elasticsearch、Logstash、Kibana)
    • Prometheus + Grafana
    • Zipkin/Sleuth:分布式追踪
  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值