(1) 继承 Thread
类:
创建一个新类,该类继承自Thread
类,并重写run
方法。然后创建该类的实例,并调用它的start
方法来启动线程。
public class MyThread extends Thread {
public void run() {
System.out.println("Thread using Thread class");
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
(2) 实现 Runnable
接口:
创建一个新类,该类实现Runnable
接口,并重写run
方法。然后创建该类的实例,并将它传递给一个Thread
对象,然后调用Thread
对象的start
方法来启动线程。
public class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread using Runnable interface");
}
}
public class Main {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
(3) 实现 Callable
接口:
创建一个新类,该类实现Callable
接口,并重写call
方法。然后可以使用FutureTask
类来包装Callable
对象,并将FutureTask
对象传递给一个Thread
对象来启动线程。这种方式的优点是可以获取线程的返回值和异常。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class MyCallable implements Callable {
public String call() throws Exception {
return "Thread using Callable interface";
}
}
public class Main {
public static void main(String[] args) {
MyCallable callable = new MyCallable();
FutureTask futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
try {
String result = futureTask.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
(1)线程池参数:
(2)线程池执行流程
创建型设计模式:
结构型设计模式:
行为型设计模式:
(1)饿汉式(Eager Initialization):在这种实现中,实例是在类加载时创建的,这确保了线程安全性。
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
(2) 双重检查锁定(Double-Checked Locking):在这种实现中,我们使用synchronized块和两次null检查来确保线程安全性。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
(3)静态内部类(Static Inner Class):在这种实现中,我们使用一个静态内部类来持有单例的实例,这利用了类加载机制来保证线程安全性。
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
(4)枚举(Enum):使用枚举是创建单例的最简单和最安全的方法,它本身就是线程安全的,并且能防止反序列化创建新的对象。
public enum Singleton {
INSTANCE;
public void doSomething() {
// ...
}
}
(1)单例模式(懒汉式,线程安全)
public class Singleton {
private static volatile Singleton instance;
// 私有化构造器,防止外部实例化
private Singleton() {}
// 提供一个全局的访问点
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
运用场景:
(2) 工厂模式
public interface Product {
void create();
}
public class ProductA implements Product {
@Override
public void create() {
System.out.println("Product A created");
}
}
public class ProductB implements Product {
@Override
public void create() {
System.out.println("Product B created");
}
}
public class ProductFactory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ProductA();
} else if ("B".equals(type)) {
return new ProductB();
} else {
throw new IllegalArgumentException("Unknown product type");
}
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
Product productA = ProductFactory.createProduct("A");
productA.create();
Product productB = ProductFactory.createProduct("B");
productB.create();
}
}
运用场景:
创建库和框架:当创建一个库或框架时,你可能希望提供一种方法给使用者创建某个接口的实例,但不想让他们知道具体的实现类。
UI库:许多UI库使用工厂模式来创建控件。例如,在一个跨平台的UI库中,你可能有一个Button
接口和多个具体的实现类(如WindowsButton
, MacButton
等)。使用工厂模式可以根据运行的操作系统创建正确的按钮类型。
支持多种支付方法:例如,如果你正在开发一个电商平台,你可能有一个PaymentProcessor
接口和多个具体的实现类(如CreditCardProcessor
, PaypalProcessor
等)。使用工厂模式可以根据用户选择创建正确的支付处理器。
加载和注册插件或驱动程序:应用程序可能使用工厂模式动态地加载和注册插件或驱动程序。
数据库访问:应用程序可能需要与多种数据库进行交互。使用工厂模式,可以为不同的数据库创建适当的数据库连接和查询对象
Java多态实现:
(1) GC垃圾回收算法
标记-清除算法(Mark-Sweep)
标记-整理算法(Mark-Compact)
分代收集算法(Generational Collection)
引用计数算法(Reference Counting)
(2) 判断是否应清除一个对象
可达性分析
引用类型
循环引用
finalize 方法
finalize
方法来进行清理操作。这个方法会在对象被GC回收前被调用一次。但是要注意,这个方法不推荐使用,因为它可以拖延对象的回收时间,并且有可能导致对象复活。方法区(Method Area)
堆区(Heap)
栈区(Stack)
程序计数器(Program Counter Register)
本地方法栈(Native Method Stack)
在Java中,内存泄漏通常是由于以下几种原因造成的:
排查Java内存泄漏,通常可以使用以下几种方法:
使用内存分析工具:
这些工具可以帮助你监视Java应用程序的内存使用情况,找出内存泄漏的来源。
查看Heap Dump: 通过获取Heap Dump来分析哪些对象占用了大量的内存和它们的引用链。可以使用jmap
工具或者内存分析工具来获取Heap Dump。
分析GC日志: 通过分析GC日志来查看对象的分配和回收情况。你可以使用-XX:+PrintGCDetails
和-XX:+PrintGCTimeStamps
等JVM选项来启用GC日志。
代码审查: 手动检查代码中可能存在内存泄漏的地方,特别是注意那些长时间持有对象引用的地方。
创建测试用例: 创建特定的测试用例来复现潜在的内存泄漏情况,然后使用内存分析工具来分析内存使用情况。
使用监控系统: 在生产环境中使用监控系统来监控Java应用程序的内存使用情况,这样可以及时发现内存泄漏问题。
(1)synchronized 关键字
public void method() {
synchronized(this) {
// ... 同步代码块
}
}
(2)CountDownLatch
可以用来实现一个或多个线程等待其他线程完成操作。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
private final CountDownLatch latch = new CountDownLatch(1);
public void method() throws InterruptedException {
latch.await();
// ... 受保护的代码块
}
public void release() {
latch.countDown();
}
}
(1)List 接口:
ArrayList
LinkedList
(2)Set 接口
HashSet
TreeSet
(3)Queue 接口
LinkedList
PriorityQueue
Map 接口
HashMap
TreeMap
LinkedHashMap
(1) 强引用(Strong Reference):常见的对象引用就是强引用,即创建一个对象并赋值给一个变量。
Object obj = new Object();
(2) 软引用(Soft Reference)软引用可以让对象在内存不足时被垃圾收集器回收。
SoftReference
(3) 弱引用(Weak Reference):弱引用比软引用更“弱”,垃圾收集器会更积极地回收弱引用指向的对象。
WeakReference
(4)虚引用(Phantom Reference):虚引用是最“弱”的一种引用类型,它不能用来访问对象,只能用来跟踪对象被垃圾收集器回收的状态。
PhantomReference
在Java中,一个对象在没有任何引用指向它时(或者只有弱引用或虚引用指向它时),它就成为垃圾收集的候选对象。具体什么时候被回收是由垃圾收集器决定的,这取决于垃圾收集器的实现和当前的垃圾收集策略。
NullPointerException:这是一个运行时异常,通常发生当你试图访问一个 null 对象的成员时。
ArrayIndexOutOfBoundsException:这也是一个运行时异常,发生于尝试访问数组的一个不存在的索引时。
ClassCastException:这是一个运行时异常,发生于尝试将一个对象强制转换为不兼容的类型时。
IOException:这是一个检查异常,通常发生在 I/O 操作失败或被中断时。需要用 try-catch 语句或者 throws 关键字来处理。
FileNotFoundException:这是 IOException 的一个子类,是一个检查异常,通常发生在尝试访问一个不存在的文件时。
NumberFormatException:这是一个运行时异常,发生在尝试将一个字符串转换为数字,但字符串的格式不正确时
如果在尝试将一个不合适的字符串转换为数字(而不是数字转换为字符串)时,你会遇到 NumberFormatException
。这通常发生在使用 Integer.parseInt()
或Double.parseDouble()
等方法时,如果传递的字符串不能转换为有效的数字,则会抛出此异常。
(1)Java反射机制优点缺点
优点:
缺点:
(2) 动态代理的好处和坏处
好处:
坏处:
(1) 静态变量和非静态变量的区别
(2) 类名调用和实例对象调用静态变量的区别
(3) 在非静态方法中使用静态变量
(4) static 方法是否可以被重写:
局部变量不赋值不能通过编译是因为Java要保证在任何情况下,使用局部变量时它都已经被初始化了。Java编译器会检查局部变量在它被使用之前是否已经被赋值了一个初始值,如果没有,就会报编译错误。这是为了避免未初始化的局部变量导致程序的不确定行为。
索引优化
查询结构优化
SQL写法优化
数据库设计优化
优化事务
幻读(Phantom Read)是指在一个事务内多次查询时,由于其他事务的插入或删除操作,导致后一次查询结果中包含前一次查询中没有的行,或者少了某些行。幻读主要是因为其他事务插入或删除了记录所引起的。
比如:
幻读可以通过使用更高的事务隔离级别(如可重复读或串行化)来避免。
多表查询是在数据库中同时查询多个表的数据。这种查询通常使用以下几种技术:
读未提交(Read Uncommitted)不是异常,而是事务的一种隔离级别。在这种隔离级别下,一个事务可以读取到另一个事务未提交的数据。
如何解决:
(1)三次握手的步骤如下:
客户端向服务器发送一个SYN包,表示请求建立连接。
服务器接收到客户端发来的SYN包后,对该包进行确认后结束LISTEN阶段,并返回一段TCP报文,表示确认客户端的报文序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接。
客户端接收到服务器发来的TCP报文后,再向服务器发送一段确认报文,表示客户端已经准备好发送数据。
(2)四次挥手的步骤如下:
客户端向服务器发送一个FIN包,表示请求断开连接。
服务器接收到客户端发来的FIN包后,对该包进行确认后进入CLOSE_WAIT状态。
服务器向客户端发送一个ACK包,表示已经准备好断开连接。
客户端接收到服务器发来的ACK包后,进入TIME_WAIT状态,并向服务器发送一个FIN包,表示已经准备好断开连接。
由TCP的三次握手和四次断开可以看出,TCP使用面向连接的通信方式, 大大提高了数据通信的可靠性,使发送数据端和接收端在数据正式传输前就有了交互, 为数据正式传输打下了可靠的基础。
(1)OSI 七层模型是一个标准化的网络协议族层次划分,每一层都有特定的功能和责任。从上到下,这些层次是:
应用层(Application Layer)
表示层(Presentation Layer)
会话层(Session Layer)
传输层(Transport Layer)
网络层(Network Layer)
数据链路层(Data Link Layer)
物理层(Physical Layer)
(2)TCP/IP 模型是实际使用最为广泛的网络协议族结构模型,它简化了 OSI 模型层次划分,主要包括以下几层:
应用层(Application Layer)
传输层(Transport Layer)
网络层(Network Layer)
链路层(Link Layer)
(1)TCP (Transmission Control Protocol) 是一个面向连接的、可靠的、基于字节流的传输层通信协议。其报文段结构主要包括以下字段:
(2) HTTP (HyperText Transfer Protocol) 是一个基于TCP/IP协议的应用层协议。它用于传输超文本,如网页。HTTP报文可以分为请求报文和响应报文,结构如下:
请求行/状态行(Request Line/Status Line):
头部字段(Headers):
Content-Type
、User-Agent
、Accept
等。空行(Empty Line):
主体(Body):
协议名称
协议版本
消息结构:每条消息将由以下几部分组成:
Version
(2 bytes):协议版本。Type
(1 byte):消息类型(例如,请求或响应)。Command
(1 byte):命令代码。Length
(4 bytes):消息体的长度。Checksum
(2 bytes):校验和,用于检查消息的完整性。命令集
错误处理
安全性:
(1)RandomLoadBalance:根据权重随机选择(对加权随机算法的实现)。这是Dubbo默认采用的一种负载均衡策略。
(2)LeastActiveLoadBalance:最小活跃数负载均衡。
Dubbo 就认为谁的活跃数越少,谁的处理速度就越快,性能也越好,这样的话,我就优先把请求给活跃数少的服务提供者处理。
(3)ConsistentHashLoadBalance:一致性Hash负载均衡策略。
ConsistentHashLoadBalance 中没有权重的概念,具体是哪个服务提供者处理请求是由你的请求的参数决定的,也就是说相同参数的请求总是发到同一个服务提供者。另外,Dubbo 为了避免数据倾斜问题(节点不够分散,大量请求落到同一节点),还引入了虚拟节点的概念。通过虚拟节点可以让节点更加分散,有效均衡各个节点的请求量。
(4)RoundRobinLoadBalance:加权轮询负载均衡。
轮询就是把请求依次分配给每个服务提供者。加权轮询就是在轮询的基础上,让更多的请求落到权重更大的服务提供者上。
(1)无向图
(2)有向图
为了确保多个生产者和消费者能够正确地共享数据,你需要使用同步机制来控制对共享数据的访问。在Java中,你可以使用以下的同步机制:
(1)冒泡排序
def bubble_sort(arr):
n = len(arr)
for i in range(n):
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
(2)快速排序
def quick_sort(arr):
if len(arr) <= 1:
return arr
else:
pivot = arr[len(arr)//2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
(3)归并排序
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(left, right):
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result
综合起来,这个表达式将检查i
是否可以被2到√i之间的任何数字整除。如果没有任何这样的数字(即,i
不能被2到√i之间的任何数字整除),那么all
函数将返回True
,表示i
是一个素数。如果i
可以被2到√i之间的任何数字整除,all
函数将返回False
,表示i
不是一个素数。
def find_primes(n):
primes = []
for i in range(2, n+1):
is_prime = all(i % j != 0 for j in range(2, int(i ** 0.5) + 1))
if is_prime:
primes.append(i)
return primes
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class DoublyLinkedList:
def __init__(self):
self.head = None
def insert_at_beginning(self, data):
new_node = Node(data)
new_node.next = self.head
if self.head:
self.head.prev = new_node
self.head = new_node
def insert_at_end(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
temp = self.head
while temp.next:
temp = temp.next
temp.next = new_node
new_node.prev = temp
def insert_after_node(self, target_data, data):
new_node = Node(data)
temp = self.head
while temp:
if temp.data == target_data:
break
temp = temp.next
if temp:
new_node.next = temp.next
new_node.prev = temp
if temp.next:
temp.next.prev = new_node
temp.next = new_node
else:
print(f"Node with data {target_data} not found")
class Node:
def __init__(self, data):
self.data = data
self.next = None
def detect_cycle(head):
if not head or not head.next:
return False
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
# 示例代码:
# 创建一个有环链表
head = Node(1)
head.next = Node(2)
head.next.next = Node(3)
head.next.next.next = Node(4)
head.next.next.next.next = head.next
# 检测环
print(detect_cycle(head)) # 输出: True
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
def detectCycle(head):
seen = set()
current = head
while current:
if current in seen:
return current
seen.add(current)
current = current.next
return None
def reverse_string(s):
return s[::-1]
def reverse_string(s):
stack = list(s)
result = []
while stack:
result.append(stack.pop())
return ''.join(result)
(1) 使用加法和减法
public class SwapWithoutThirdVariable {
public static void main(String[] args) {
int a = 5;
int b = 3;
a = a + b; // 此时 a 是 a 和 b 的和
b = a - b; // 减去 b (原始的 b),得到原始的 a
a = a - b; // 减去新的 b (原始的 a),得到原始的 b
System.out.println("a: " + a); // 输出: a: 3
System.out.println("b: " + b); // 输出: b: 5
}
}
(2) 使用俺位异或运算(XOR):如果两个比较位不同,则结果位将为1。如果两者相同,则结果为0。
public class SwapWithoutThirdVariable {
public static void main(String[] args) {
int a = 5;
int b = 3;
a = a ^ b; // XOR 运算
b = a ^ b; // XOR 运算
a = a ^ b; // XOR 运算
System.out.println("a: " + a); // 输出: a: 3
System.out.println("b: " + b); // 输出: b: 5
}
}
min_abs_value = min(min(abs(a), abs(b)), min(abs(c), abs(d)))
初始化 信号量S = 0;
方法A {
上部分 {
P(S); // 会阻塞直到S变成1
// 执行A方法的上部分代码
}
下部分 {
// 执行A方法的下部分代码
}
}
方法B {
上部分 {
// 执行B方法的上部分代码
}
下部分 {
// 执行B方法的下部分代码
V(S); // 使S变成1,这样A方法的P操作将不再阻塞
}
}