Java核心知识点与面试总结
本文档旨在总结Java的核心知识点,并提供常见的面试问题与解答,帮助学习者巩固知识和准备面试。
目录
零基础学Java——大纲合集
Java基础
1. Java概述
- JDK (Java Development Kit): Java开发工具包,包含Java的开发和运行环境,即Java开发工具和JRE。
- JRE (Java Runtime Environment): Java运行环境,包含Java程序运行所需的类库和JVM(Java虚拟机)。
- JVM (Java Virtual Machine): Java虚拟机,执行Java字节码。
- javac命令: 编译Java源文件 (.java) 为字节码文件 (.class)。
- java命令: 运行Java字节码文件,启动JVM。
- 环境变量:
JAVA_HOME
: 指向JDK的安装目录。PATH
: 包含JDK的bin目录,使得javac和java等命令可以在任何目录下执行。CLASSPATH
: 指定类搜索路径(在较新的Java版本中,通常不需要手动配置)。
2. 基本语法
- 标识符:
- 可以由字母、数字、下划线(_)、美元符号($)组成。
- 不能以数字开头。
- 不能是Java关键字。
- 区分大小写。
- 关键字: Java语言中预定义的、具有特殊含义的单词,如
class
,public
,static
,void
,if
,else
,for
,while
等。 - 保留字: Java预留的关键字,当前版本未使用,但未来版本可能会使用,如
goto
,const
。 - 变量:
- 作用域: 变量定义的位置开始到其所在代码块结束。
- 生命周期: 从定义开始到作用域结束。
- 数据类型:
- 基本数据类型:
- 整数类型:
byte
(1字节),short
(2字节),int
(4字节),long
(8字节) - 浮点类型:
float
(4字节),double
(8字节) - 字符类型:
char
(2字节, Unicode字符) - 布尔类型:
boolean
(true/false)
- 整数类型:
- 引用数据类型: 类 (Class), 接口 (Interface), 数组 (Array), 枚举 (Enum), String。
- 基本数据类型:
- 运算符:
- 算术运算符:
+
,-
,*
,/
,%
,++
,--
- 赋值运算符:
=
,+=
,-=
,*=
,/=
,%=
- 比较运算符:
==
,!=
,>
,<
,>=
,<=
- 逻辑运算符:
&&
(与),||
(或),!
(非),&
,|
,^
- 位运算符:
&
,|
,^
,~
,<<
,>>
,>>>
- 三元运算符:
condition ? expression1 : expression2
- 算术运算符:
- 流程控制:
- 条件语句:
if
,if-else
,if-else if-else
,switch
- 循环语句:
for
,while
,do-while
- 跳转语句:
break
,continue
,return
- 条件语句:
面向对象
1. 基本概念
- 面向对象编程 (OOP): 一种编程范式,以“对象”为核心,将现实世界的事物抽象为程序中的对象。
- 类 (Class): 对象的模板,描述具有相同属性和行为的一类事物。类是抽象的。
- 对象 (Object/Instance): 类的具体实例。对象是具体的。
2. 三大特性
- 封装 (Encapsulation):
- 将对象的属性(数据)和方法(行为)结合为一个独立的整体。
- 信息隐藏:隐藏对象内部的实现细节,只对外提供公共的访问方式(通常是getter/setter方法)。
- 访问修饰符:
private
,default
(包级私有),protected
,public
用于控制成员的可见性。
- 继承 (Inheritance):
- 子类可以继承父类的属性和方法,并可以添加自己特有的属性和方法,或重写父类的方法。
- Java中类是单继承的(一个类只能有一个直接父类),但接口可以多继承。
extends
关键字用于类的继承,implements
关键字用于接口的实现。super
关键字:用于在子类中调用父类的构造方法或成员。
- 多态 (Polymorphism):
- 同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
- 实现方式:
- 方法重写 (Override): 子类重新实现父类的方法。
- 方法重载 (Overload): 同一个类中,方法名相同,但参数列表(个数、类型、顺序)不同。
- 父类引用指向子类对象:
Parent p = new Child();
3. 类与对象的使用
- 定义类: 使用
class
关键字。[修饰符] class ClassName { // 成员变量 (属性) [修饰符] dataType variableName; // 成员方法 (行为) [修饰符] returnType methodName(parameters) { // 方法体 } // 构造方法 [修饰符] ClassName(parameters) { // 构造方法体 } }
- 创建对象 (实例化): 使用
new
关键字。
ClassName objectName = new ClassName();
- 构造方法 (Constructor):
- 方法名与类名相同。
- 没有返回值类型 (连
void
也没有)。 - 用于初始化对象。
- 如果不显式定义构造方法,Java会提供一个默认的无参构造方法。
- 可以重载。
this
关键字:- 指代当前对象。
- 用于区分同名的成员变量和局部变量。
- 调用本类的其他构造方法 (必须放在构造方法的第一行)。
- 调用本类的成员方法。
static
关键字:- 修饰成员变量 (类变量/静态变量): 被类的所有对象共享,可以通过类名直接访问。
- 修饰成员方法 (类方法/静态方法): 可以通过类名直接调用,静态方法不能直接访问非静态成员。
- 静态代码块: 类加载时执行,只执行一次,用于初始化静态资源。
4. 抽象类与接口
- 抽象类 (Abstract Class):
- 使用
abstract
关键字修饰的类。 - 不能被实例化。
- 可以包含抽象方法 (没有方法体,使用
abstract
修饰) 和具体方法。 - 子类继承抽象类,必须实现所有抽象方法,除非子类也是抽象类。
- 使用
- 接口 (Interface):
- 使用
interface
关键字定义。 - 是完全抽象的,所有方法默认都是
public abstract
(Java 8+可以有默认方法和静态方法)。 - 所有属性默认都是
public static final
。 - 类通过
implements
关键字实现接口,一个类可以实现多个接口。 - 接口主要用于定义规范和实现解耦。
- 使用
集合框架
Java集合框架主要包含两大接口:Collection
和 Map
。
1. Collection 接口
根接口,用于存储一组对象(单一元素)。
1.1. List 接口
- 特点: 有序集合,允许元素重复,可以通过索引访问元素。
- 常用实现类:
ArrayList
:- 基于动态数组实现。
- 查询快(随机访问快)。
- 增删慢(需要移动元素)。
- 线程不安全。
LinkedList
:- 基于双向链表实现。
- 增删快。
- 查询慢。
- 线程不安全。
Vector
:- 基于动态数组实现。
- 线程安全(方法使用
synchronized
修饰),但效率较低。 - 早期版本,已不推荐使用,可使用
Collections.synchronizedList(new ArrayList<>())
替代。
1.2. Set 接口
- 特点: 不允许元素重复,通常无序(
TreeSet
除外)。 - 常用实现类:
HashSet
:- 基于哈希表 (内部使用
HashMap
实现)。 - 元素无序。
- 允许存储
null
值。 - 线程不安全。
- 基于哈希表 (内部使用
LinkedHashSet
:HashSet
的子类,基于链表和哈希表 (内部使用LinkedHashMap
实现)。- 保持元素插入顺序。
- 线程不安全。
TreeSet
:- 基于红黑树实现。
- 元素有序 (自然排序或自定义比较器排序)。
- 不允许存储
null
值 (如果使用自然排序且未处理null)。 - 线程不安全。
1.3. Queue 接口
- 特点: 队列,通常遵循先进先出 (FIFO) 原则。
- 常用实现类:
LinkedList
: 也可以作为队列使用,实现了Deque
接口 (双端队列)。PriorityQueue
:- 优先队列,元素按优先级排序 (自然排序或自定义比较器)。
- 底层是堆 (通常是小顶堆)。
ArrayDeque
:- 基于可变数组的双端队列。
2. Map 接口
- 特点: 存储键值对 (key-value),键唯一,值可重复。
- 常用实现类:
HashMap
:- 基于哈希表 (数组 + 链表/红黑树)。
- 键值对无序。
- 允许
null
键和null
值。 - 线程不安全。
LinkedHashMap
:HashMap
的子类,额外维护了一个双向链表。- 保持键值对的插入顺序或访问顺序。
- 线程不安全。
TreeMap
:- 基于红黑树实现。
- 键值对按键排序 (自然排序或自定义比较器)。
- 线程不安全。
Hashtable
:- 基于哈希表。
- 线程安全 (方法使用
synchronized
修饰),但效率较低。 - 不允许
null
键和null
值。 - 早期版本,已不推荐使用,可使用
ConcurrentHashMap
替代。
3. Iterator (迭代器)
- 用于遍历
Collection
集合中的元素。 - 主要方法:
hasNext()
,next()
,remove()
。 ListIterator
:List
接口特有的迭代器,支持双向遍历、添加元素、修改元素。
4. Collections 工具类
- 提供了操作集合的静态方法,如排序 (
sort()
)、查找 (binarySearch()
)、线程安全化 (synchronizedList()
,synchronizedMap()
,synchronizedSet()
) 等。
5. Arrays 工具类
- 提供了操作数组的静态方法,如排序 (
sort()
)、查找 (binarySearch()
)、转换为列表 (asList()
) 等。
6. 泛型 (Generics)
- 在编译时进行类型检查,避免运行时
ClassCastException
。 - 提高代码的可读性和安全性。
7. 比较器 (Comparator & Comparable)
Comparable
: 内比较器,类实现此接口的compareTo()
方法,定义对象的自然排序。Comparator
:外比较器,创建一个比较器类实现此接口的compare()
方法,用于自定义排序规则,或者对没有实现Comparable
接口的类进行排序。
多线程与并发
1. 基本概念
- 进程 (Process): 程序的一次执行过程,是系统分配资源的独立单位。每个进程都有自己独立的内存空间。
- 线程 (Thread): 进程中的一个执行单元,是CPU调度的最小单位。一个进程可以包含多个线程,它们共享进程的资源(如内存空间),但每个线程有自己的程序计数器、栈等。
- 并发 (Concurrency): 多个任务在单个CPU上通过时间片轮转的方式交替执行,看起来像同时执行。
- 并行 (Parallelism): 多个任务在多个CPU核心上真正同时执行。
- 协程 (Coroutine): 用户态的轻量级线程,由用户程序控制调度,切换开销小。
2. 创建线程的方式
- 继承
Thread
类: 重写run()
方法。class MyThread extends Thread { @Override public void run() { // 线程执行的任务 } } MyThread t = new MyThread(); t.start(); // 启动线程
- 实现
Runnable
接口: 实现run()
方法,并将Runnable
对象传递给Thread
类的构造器。class MyRunnable implements Runnable { @Override public void run() { // 线程执行的任务 } } Thread t = new Thread(new MyRunnable()); t.start();
- 实现
Callable
接口 (配合FutureTask
): 实现call()
方法 (可以有返回值,可以抛出异常),通常与线程池一起使用。import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class MyCallable implements Callable<String> { @Override public String call() throws Exception { // 线程执行的任务 return "Result"; } } FutureTask<String> futureTask = new FutureTask<>(new MyCallable()); Thread t = new Thread(futureTask); t.start(); // String result = futureTask.get(); // 获取结果
- 使用线程池 (
ExecutorService
): 推荐的方式,可以更好地管理线程资源。
3. 线程生命周期与状态
- NEW (新建): 线程对象被创建,但尚未调用
start()
方法。 - RUNNABLE (可运行): 调用
start()
方法后,线程进入可运行状态,等待CPU调度执行。包括READY
(就绪) 和RUNNING
(运行中) 两种细分状态。 - BLOCKED (阻塞): 线程等待获取一个监视器锁 (例如进入
synchronized
同步块/方法)。 - WAITING (等待): 线程无限期等待另一个线程执行特定操作。例如调用
Object.wait()
,Thread.join()
,LockSupport.park()
。 - TIMED_WAITING (计时等待): 线程在指定时间内等待另一个线程执行特定操作。例如调用
Thread.sleep(long)
,Object.wait(long)
,Thread.join(long)
,LockSupport.parkNanos()
,LockSupport.parkUntil()
。 - TERMINATED (终止): 线程执行完毕或因异常退出。
4. 线程同步与锁
synchronized
关键字:- 修饰实例方法: 锁是当前实例对象。
- 修饰静态方法: 锁是当前类的
Class
对象。 - 修饰代码块: 指定锁对象。
- 是一种悲观锁、可重入锁、非公平锁。
volatile
关键字:- 保证共享变量的可见性 (当一个线程修改了变量的值,其他线程能立即看到)。
- 禁止指令重排序。
- 不保证原子性。
Lock
接口 (JUC):ReentrantLock
: 可重入锁,比synchronized
更灵活,可以实现公平锁和非公平锁,需要手动释放锁 (lock()
和unlock()
)。ReentrantReadWriteLock
: 读写锁,允许多个线程同时读,但只允许一个线程写。读锁是共享锁,写锁是排他锁。
- 原子类 (Atomic):
java.util.concurrent.atomic
包下的类,如AtomicInteger
,AtomicLong
,利用CAS (Compare-And-Swap) 机制实现无锁的原子操作。
5. 线程通信
wait()
,notify()
,notifyAll()
:Object
类的方法,必须在synchronized
代码块或方法中使用,用于线程间的协作。wait()
: 使当前线程等待,并释放锁。notify()
: 唤醒在此对象监视器上等待的单个线程。notifyAll()
: 唤醒在此对象监视器上等待的所有线程。
Condition
接口 (配合Lock
):await()
: 相当于wait()
。signal()
: 相当于notify()
。signalAll()
: 相当于notifyAll()
。- 提供了更精细的线程等待和唤醒控制,一个
Lock
可以关联多个Condition
对象。
join()
方法:Thread
类的方法,一个线程等待另一个线程执行完毕。CountDownLatch
: 允许一个或多个线程等待其他一组线程完成操作。CyclicBarrier
: 让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会打开,所有被屏障拦截的线程才会继续执行。Semaphore
: 控制同时访问特定资源的线程数量。
6. 线程池 (ExecutorService
)
- 作用: 管理线程的创建、复用和销毁,提高性能,避免资源耗尽。
Executors
工具类: 提供创建不同类型线程池的静态方法 (不推荐在生产环境直接使用,建议通过ThreadPoolExecutor
自定义参数)。newFixedThreadPool()
: 固定大小线程池。newCachedThreadPool()
: 可缓存线程池,线程数根据需要动态调整。newSingleThreadExecutor()
: 单线程线程池。newScheduledThreadPool()
: 定时及周期性任务执行的线程池。
ThreadPoolExecutor
核心参数:corePoolSize
: 核心线程数。maximumPoolSize
: 最大线程数。keepAliveTime
: 非核心线程空闲存活时间。unit
:keepAliveTime
的时间单位。workQueue
: 任务队列 (阻塞队列)。threadFactory
: 线程工厂。handler
: 拒绝策略。
- 拒绝策略:
AbortPolicy
(默认): 抛出RejectedExecutionException
。CallerRunsPolicy
: 由提交任务的线程执行。DiscardPolicy
: 直接丢弃任务。DiscardOldestPolicy
: 丢弃队列中最旧的任务,然后尝试提交当前任务。
7. ThreadLocal
- 为每个线程提供一个独立的变量副本,从而实现线程间的数据隔离。
- 常用于存储线程上下文信息,如数据库连接、Session等。
- 注意内存泄漏: 如果
ThreadLocal
的生命周期比线程长,且线程池中的线程不会被销毁,可能导致ThreadLocalMap
中的 Entry 无法被回收。
8. 并发容器 (JUC - java.util.concurrent
)
ConcurrentHashMap
: 线程安全的HashMap
,分段锁 (JDK 1.7) 或 CAS +synchronized
(JDK 1.8+)。CopyOnWriteArrayList
: 线程安全的ArrayList
,写操作时复制底层数组,读写分离,适用于读多写少的场景。CopyOnWriteArraySet
: 线程安全的Set
,内部使用CopyOnWriteArrayList
实现。- 阻塞队列 (
BlockingQueue
):ArrayBlockingQueue
: 基于数组的有界阻塞队列。LinkedBlockingQueue
: 基于链表的有界/无界阻塞队列。PriorityBlockingQueue
: 支持优先级的无界阻塞队列。DelayQueue
: 延迟队列,元素只有在其指定的延迟时间到了之后才能被取出。SynchronousQueue
: 不存储元素的阻塞队列,每个插入操作必须等待一个相应的删除操作。
9. 死锁 (Deadlock)
- 定义: 两个或多个线程无限期地互相等待对方释放资源。
- 产生条件 (四个必要条件):
- 互斥条件。
- 请求与保持条件。
- 不剥夺条件。
- 循环等待条件。
- 避免与解决: 破坏四个必要条件之一、加锁顺序、加锁时限、死锁检测。
IO与NIO
1. 基本概念与区别
- Java IO (BIO - Blocking I/O):
- 面向流 (Stream Oriented): 数据按顺序从流中读取或写入,一次一个或多个字节。数据没有被缓存,不能在流中前后移动。
- 阻塞IO (Blocking I/O): 当线程调用
read()
或write()
时,线程会被阻塞,直到数据被读取或完全写入。在此期间线程不能做其他事情。 - 通常一个连接对应一个线程处理。
- Java NIO (New I/O / Non-blocking I/O):
- 面向缓冲区 (Buffer Oriented): 数据被读取到缓冲区中进行处理,可以在缓冲区中前后移动数据,增加了处理的灵活性。
- 非阻塞IO (Non-blocking I/O): 线程请求读取数据时,如果数据未准备好,不会阻塞线程,线程可以继续做其他事情。写操作也类似。
- 选择器 (Selectors): 允许单个线程监视多个通道 (Channel) 的IO事件(如连接就绪、数据可读/可写),实现IO多路复用。 3 5
- 通常一个线程可以管理多个连接。
2. Java IO 核心类
- 字节流:
InputStream
和OutputStream
是所有字节输入/输出流的抽象基类。- 文件流:
FileInputStream
,FileOutputStream
- 缓冲流:
BufferedInputStream
,BufferedOutputStream
(提高读写效率) - 对象流:
ObjectInputStream
,ObjectOutputStream
(用于对象的序列化和反序列化) - 数据流:
DataInputStream
,DataOutputStream
(处理基本数据类型)
- 文件流:
- 字符流:
Reader
和Writer
是所有字符输入/输出流的抽象基类。- 文件流:
FileReader
,FileWriter
- 缓冲流:
BufferedReader
,BufferedWriter
(提供readLine()
等方法) - 转换流:
InputStreamReader
(字节流 -> 字符流),OutputStreamWriter
(字符流 -> 字节流)
- 文件流:
3. Java NIO 核心组件
- 通道 (Channel):
- 类似于传统IO中的流,但通道是双向的(可以同时进行读写操作,而流是单向的)。
- 数据总是从通道读入缓冲区,或从缓冲区写入通道。
- 主要实现类:
FileChannel
,SocketChannel
,ServerSocketChannel
,DatagramChannel
.
- 缓冲区 (Buffer):
- 本质上是一个数组,用于存储数据。所有数据都通过缓冲区处理。
- 核心属性:
capacity
: 缓冲区的总容量,一旦设定不可改变。position
: 下一个要读或写的数据的索引。limit
: 缓冲区中有效数据的末尾位置(读模式下)或最大可写位置(写模式下)。mark
: 用于临时记录当前position
。
- 核心方法:
allocate()
: 分配一个新的缓冲区。put()
: 向缓冲区写入数据。get()
: 从缓冲区读取数据。flip()
: 切换缓冲区从写模式到读模式 (limit=position, position=0)。clear()
: 清空缓冲区,切换到写模式 (position=0, limit=capacity)。compact()
: 压缩缓冲区,将未读数据移到开头,切换到写模式。
- 主要实现类:
ByteBuffer
,CharBuffer
,IntBuffer
等。
- 选择器 (Selector):
- 允许单个线程管理多个通道的IO事件。
- 通道可以向选择器注册感兴趣的事件 (如
OP_READ
,OP_WRITE
,OP_CONNECT
,OP_ACCEPT
)。 - 当注册的事件发生时,选择器会通知应用程序。
- 实现IO多路复用的关键。
4. NIO 的优势
- 非阻塞: 避免了线程因IO操作而长时间阻塞,提高了线程利用率。
- 高并发: 使用少量线程即可处理大量连接,减少了线程创建和上下文切换的开销。
- 缓冲区: 提供了更灵活的数据处理方式。
5. 使用场景
- Java IO (BIO): 适用于连接数较少且固定的架构,编程模型简单。
- Java NIO: 适用于高并发、高连接数的场景,如网络服务器、聊天应用等。
网络编程
1. 网络编程基础
- 计算机网络: 将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
- 协议 (Protocol): 网络通信中,通信双方必须遵守的规则和约定。
- OSI七层模型: 应用层, 表示层, 会话层, 传输层, 网络层, 数据链路层, 物理层。
- TCP/IP四层模型 (或五层): 应用层, 传输层, 网络层 (IP层), 数据链路层 (有些也加上物理层)。
- IP地址 (IP Address): 网络中设备的唯一标识。
- 端口号 (Port Number): 用于区分一台主机上不同的应用程序。范围 0-65535,其中 0-1023 为熟知端口。
- Socket: 网络通信的端点,封装了IP地址和端口号,是应用层与TCP/IP协议族通信的中间软件抽象层。
2. TCP (Transmission Control Protocol)
- 特点: 面向连接、可靠的、基于字节流的传输层通信协议。
- 三次握手 (建立连接):
- 客户端发送SYN包到服务器,并进入SYN_SEND状态。
- 服务器收到SYN包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
- 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
- 四次挥手 (断开连接):
- 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。
- 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。
- 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
- Java中的TCP编程: 使用
java.net.Socket
(客户端) 和java.net.ServerSocket
(服务器端)。
3. UDP (User Datagram Protocol)
- 特点: 无连接、不可靠、面向数据报的传输层协议。传输效率高,但不保证数据顺序和可靠性。
- 适用场景: 实时性要求高、对少量数据丢失不敏感的场景,如视频会议、DNS查询。
- Java中的UDP编程: 使用
java.net.DatagramSocket
和java.net.DatagramPacket
。
4. Java Socket编程
InetAddress
类: 表示IP地址。Socket
类 (客户端):- 构造方法:
Socket(String host, int port)
或Socket(InetAddress address, int port)
。 - 获取输入/输出流:
getInputStream()
,getOutputStream()
。 - 关闭Socket:
close()
。
- 构造方法:
ServerSocket
类 (服务器端):- 构造方法:
ServerSocket(int port)
。 - 监听连接请求:
accept()
(阻塞方法,返回一个与客户端连接的Socket
对象)。 - 关闭ServerSocket:
close()
。
- 构造方法:
- 多线程服务器: 为每个客户端连接创建一个新的线程来处理,以支持并发。
5. URL与URLConnection
URL
类: 表示统一资源定位符,指向网络上的资源。- 构造方法:
URL(String spec)
。 - 打开连接:
openConnection()
返回一个URLConnection
对象。 - 获取输入流:
openStream()
。
- 构造方法:
URLConnection
类: 用于与URL指定的资源建立连接并进行通信的抽象类。HttpURLConnection
:URLConnection
的子类,专门用于HTTP协议。- 设置请求属性:
setRequestProperty(String key, String value)
。 - 获取响应码:
getResponseCode()
。 - 获取输入/输出流。
6. HTTP协议基础
- 请求 (Request): 客户端向服务器发送的请求信息。
- 请求行: 方法 (GET, POST, PUT, DELETE等), URL, HTTP版本。
- 请求头: 包含客户端环境信息、认证信息等。
- 请求体: POST请求时携带的数据。
- 响应 (Response): 服务器对客户端请求的应答。
- 状态行: HTTP版本, 状态码 (200 OK, 404 Not Found, 500 Internal Server Error等), 状态描述。
- 响应头: 包含服务器信息、内容类型、编码等。
- 响应体: 服务器返回的实际数据。
7. NIO在网络编程中的应用 (回顾IO与NIO部分)
SocketChannel
: 用于TCP连接的通道。ServerSocketChannel
: 用于监听TCP连接的通道。DatagramChannel
: 用于UDP连接的通道。- 结合
Selector
实现非阻塞、高并发的网络服务器。
JVM相关
1. JVM概述
- JVM (Java Virtual Machine): Java虚拟机,是一个虚构出来的计算机,通过在实际的计算机上仿真模拟各类计算机功能来实现。Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。
- 作用: JVM负责将Java字节码(.class文件)解释或编译成特定平台的机器码并执行。它提供了内存管理、垃圾回收、安全性等功能。
- 跨平台性原理: Java源代码(.java)被编译成平台无关的字节码(.class),JVM针对不同操作系统有不同实现,从而使得同一份字节码可以在不同平台上运行。
- JDK, JRE, JVM 的关系:
JDK (Java Development Kit)
: Java开发工具包,包含JRE以及编译器(javac)、调试器等开发工具。JRE (Java Runtime Environment)
: Java运行环境,包含JVM和Java核心类库。JVM
: Java虚拟机,是JRE的一部分。
2. JVM内存结构 (运行时数据区)
JVM在执行Java程序时,会把它管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间。
2.1. 线程私有区域:
- 程序计数器 (Program Counter Register):
- 一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。
- 如果线程正在执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)。
- 此内存区域是唯一一个在Java虚拟机规范中没有规定任何
OutOfMemoryError
情况的区域。
- Java虚拟机栈 (Java Virtual Machine Stacks):
- 每个方法在执行的同时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
- 每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
- 局部变量表存放了编译期可知的各种基本数据类型、对象引用和returnAddress类型。
- 可能抛出
StackOverflowError
(线程请求的栈深度大于虚拟机所允许的深度) 和OutOfMemoryError
(如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存)。
- 本地方法栈 (Native Method Stacks):
- 与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。
- 也可能抛出
StackOverflowError
和OutOfMemoryError
。
2.2. 线程共享区域:
- Java堆 (Java Heap):
- Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。
- 此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
- 是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。
- 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出
OutOfMemoryError
异常。 - 堆内存的划分 (常见的分代模型,如HotSpot):
- 新生代 (Young Generation): Eden区, Survivor From区 (S0), Survivor To区 (S1)。大部分对象在此分配和回收。
- 老年代 (Old Generation / Tenured Generation): 存放生命周期较长的对象。
- 方法区 (Method Area):
- 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 运行时常量池 (Runtime Constant Pool) 是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。 3
- JDK 8 以前,HotSpot虚拟机使用“永久代(Permanent Generation)”来实现方法区。JDK 8 及以后,永久代被移除,取而代之的是在本地内存中实现的元空间(Metaspace)。
- 当方法区无法满足内存分配需求时,将抛出
OutOfMemoryError
异常。
3. 类加载机制
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
- 生命周期: 加载 (Loading)、验证 (Verification)、准备 (Preparation)、解析 (Resolution)、初始化 (Initialization)、使用 (Using)、卸载 (Unloading)。其中验证、准备、解析三个部分统称为连接(Linking)。
- 类加载器 (Class Loader):
- 启动类加载器 (Bootstrap ClassLoader): 负责加载
<JAVA_HOME>\lib
目录中的,或者被-Xbootclasspath
参数所指定的路径中的,并且是虚拟机识别的类库。 - 扩展类加载器 (Extension ClassLoader): 负责加载
<JAVA_HOME>\lib\ext
目录中的,或者被java.ext.dirs
系统变量所指定的路径中的所有类库。 - 应用程序类加载器 (Application ClassLoader / System ClassLoader): 负责加载用户类路径 (Classpath)上所指定的类库。开发者可以直接使用这个类加载器。
- 自定义类加载器 (User-Defined ClassLoader): 继承
java.lang.ClassLoader
实现。
- 启动类加载器 (Bootstrap ClassLoader): 负责加载
- 双亲委派模型 (Parents Delegation Model):
- 工作过程: 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
- 好处: 避免类的重复加载;保证Java核心库的类型安全。
4. 垃圾回收 (Garbage Collection, GC)
- 目的: 自动回收不再使用的对象所占用的内存。
- 判断对象是否存活:
- 引用计数法: 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1。任何时刻计数器为0的对象就是不可能再被使用的。 (Java未使用,因为难以解决对象之间循环引用的问题)
- 可达性分析算法 (Reachability Analysis): 通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。 (Java使用此方法)
- GC Roots 对象包括:虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象等。
- 常见的垃圾回收算法:
- 标记-清除 (Mark-Sweep): 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。缺点是效率不高,产生内存碎片。
- 标记-复制 (Mark-Copy / Copying): 将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。优点是实现简单,运行高效,不易产生碎片。缺点是内存使用率低 (只有一半)。新生代多采用此算法。
- 标记-整理 (Mark-Compact): 标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。老年代多采用此算法。
- 分代收集 (Generational Collection): 根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
- 常见的垃圾收集器 (HotSpot):
- Serial / Serial Old: 单线程收集器,进行垃圾收集时,必须暂停所有其他工作线程 (Stop-The-World)。简单高效,适用于客户端模式。
- ParNew: Serial收集器的多线程版本。
- Parallel Scavenge / Parallel Old: 关注吞吐量的收集器 (吞吐量 = 用户代码运行时间 / (用户代码运行时间 + 垃圾收集时间))。
- CMS (Concurrent Mark Sweep): 以获取最短回收停顿时间为目标的收集器。基于“标记-清除”算法实现,并发收集、低停顿。缺点是对CPU资源敏感,无法处理浮动垃圾,产生内存碎片。
- G1 (Garbage-First): 面向服务端应用的垃圾收集器,可预测的停顿时间模型。将堆划分为多个大小相等的独立区域 (Region),跟踪各个Region里面的垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。
- ZGC / Shenandoah: 低停顿时间的垃圾收集器 (JDK 11+)。
5. JVM调优简介
- 目标: 减少GC的频率和停顿时间,提高系统吞吐量。
- 常用参数: 5
-Xms
: 初始堆大小。-Xmx
: 最大堆大小。-Xmn
: 新生代大小 (或者-XX:NewRatio
设置新生代与老年代的比例)。-XX:PermSize
/-XX:MetaspaceSize
: 永久代/元空间初始大小。-XX:MaxPermSize
/-XX:MaxMetaspaceSize
: 永久代/元空间最大大小。-Xss
: 每个线程的栈大小。-XX:+PrintGCDetails
: 打印GC详细日志。-XX:+HeapDumpOnOutOfMemoryError
: OOM时生成堆转储文件。- 选择合适的垃圾收集器组合。
- 调优思路: 分析GC日志,根据应用特点调整堆大小、新生代与老年代比例、选择合适的GC策略等。
常用框架 (Spring, MyBatis等)
1. Spring Framework
- 概述: 一个轻量级的、非侵入式的Java EE应用框架,旨在简化企业级应用的开发。
- 核心特性:
- 控制反转 (IoC - Inversion of Control) / 依赖注入 (DI - Dependency Injection):
- IoC容器负责创建和管理对象(Bean)及其依赖关系,而不是由代码直接创建和控制。
- DI是实现IoC的一种方式,通过容器将依赖关系注入到Bean中 (构造器注入, Setter注入, 字段注入)。
- 面向切面编程 (AOP - Aspect-Oriented Programming):
- 将横切关注点(如日志、事务、安全)从业务逻辑中分离出来,模块化处理。
- 主要概念: 切面 (Aspect), 连接点 (Join Point), 通知 (Advice), 切点 (Pointcut)。
- 控制反转 (IoC - Inversion of Control) / 依赖注入 (DI - Dependency Injection):
- 主要模块:
- Spring Core: 提供IoC和DI的基础功能。
- Spring Beans: Bean工厂和Bean的定义。
- Spring Context: 应用上下文,提供企业级服务如JNDI, EJB, 国际化等。
- Spring AOP: AOP实现。
- Spring DAO: 对JDBC的抽象,简化数据库操作,提供事务管理。
- Spring ORM: 集成ORM框架如Hibernate, MyBatis。
- Spring Web: 提供Web应用开发的支持,如Spring MVC。
- Spring Test: 支持单元测试和集成测试。
- Spring MVC:
- 基于MVC设计模式的Web框架。
- 组件: DispatcherServlet (前端控制器), HandlerMapping (处理器映射), Controller (处理器), ModelAndView, ViewResolver (视图解析器)。
- Spring Boot:
- 简化Spring应用的初始搭建以及开发过程。
- 自动配置、起步依赖 (Starters)、嵌入式服务器、监控。
2. MyBatis
- 概述: 一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
- 核心组件:
- SqlSessionFactoryBuilder: 根据配置或代码构建SqlSessionFactory。
- SqlSessionFactory: 创建SqlSession的工厂,是MyBatis的核心,一旦创建,在应用运行期间一直存在。
- SqlSession: 执行SQL操作的接口,包含了所有执行SQL、获取Mapper、管理事务的方法。线程不安全,每个线程都应该有自己的SqlSession实例。
- Mapper接口: 定义SQL操作的方法,MyBatis会为其生成动态代理实现。
- 配置文件 (mybatis-config.xml):
environments
: 配置数据库环境 (数据源, 事务管理器)。mappers
: 指定Mapper XML文件的位置。typeAliases
: 类型别名。plugins
: 插件。settings
: 全局配置参数。
- Mapper XML文件:
namespace
: 绑定Mapper接口。- SQL映射语句:
<select>
,<insert>
,<update>
,<delete>
。 parameterType
: 输入参数类型。resultType
/resultMap
: 输出结果类型或结果映射。- 动态SQL:
<if>
,<choose>
,<when>
,<otherwise>
,<where>
,<set>
,<foreach>
等,允许根据条件动态生成SQL。
- 与Spring集成:
- 通常使用
mybatis-spring-boot-starter
。 - Spring管理SqlSessionFactory和SqlSession的生命周期,以及事务。
- 通常使用
3. SSM / SSH 框架组合
- SSM: Spring + Spring MVC + MyBatis。目前Java Web开发中非常流行的组合。
- SSH: Spring + Struts2 + Hibernate。曾经非常流行的组合,现在Struts2和Hibernate的使用有所减少。
数据库相关
1. JDBC (Java Database Connectivity)
- 概述: Java访问数据库的API,提供了一组用于执行SQL语句的Java类和接口,可以为多种关系数据库提供统一访问。 1
- 核心组件/步骤:
- 加载数据库驱动:
Class.forName("com.mysql.cj.jdbc.Driver");
(以MySQL为例)。 - 建立连接:
Connection conn = DriverManager.getConnection(url, user, password);
url
格式:jdbc:mysql://hostname:port/databaseName?characterEncoding=UTF-8&serverTimezone=UTC
(示例)
- 创建Statement/PreparedStatement:
Statement stmt = conn.createStatement();
(用于执行静态SQL语句)PreparedStatement pstmt = conn.prepareStatement(sql);
(用于执行预编译的SQL语句,防止SQL注入,性能更好)
- 执行SQL语句:
ResultSet rs = stmt.executeQuery(sql);
(执行查询语句)int rowsAffected = stmt.executeUpdate(sql);
(执行DML语句如INSERT, UPDATE, DELETE)boolean result = stmt.execute(sql);
(可执行任何SQL语句)
- 处理结果集 (ResultSet):
while(rs.next()) { ... rs.getXxx("columnName" or columnIndex); ... }
- 关闭资源:
rs.close(); stmt.close(); conn.close();
(顺序:ResultSet -> Statement -> Connection,确保在finally块中关闭)
- 加载数据库驱动:
DriverManager
: 管理JDBC驱动程序。Connection
: 代表与数据库的连接。Statement
: 用于执行静态SQL语句并返回其生成的结果的对象。PreparedStatement
: 继承自Statement
,表示预编译的SQL语句。可以包含输入参数(占位符?
)。ResultSet
: 表示数据库结果集的数据表,通常是通过执行查询数据库的语句生成的。- 事务管理:
conn.setAutoCommit(false);
(关闭自动提交)conn.commit();
(提交事务)conn.rollback();
(回滚事务)
2. SQL基础
- DDL (Data Definition Language): 数据定义语言,用于定义数据库对象 (库, 表, 索引等)。
CREATE DATABASE
,DROP DATABASE
CREATE TABLE
,ALTER TABLE
,DROP TABLE
CREATE INDEX
,DROP INDEX
- DML (Data Manipulation Language): 数据操作语言,用于操作数据库中的数据。
INSERT INTO ... VALUES ...
UPDATE ... SET ... WHERE ...
DELETE FROM ... WHERE ...
- DQL (Data Query Language): 数据查询语言,用于查询数据库中的数据。
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT ...
- DCL (Data Control Language): 数据控制语言,用于控制数据库的访问权限。
GRANT
,REVOKE
- TCL (Transaction Control Language): 事务控制语言。
COMMIT
,ROLLBACK
,SAVEPOINT
- 常用关键字和子句:
SELECT DISTINCT
: 返回唯一不同的值。WHERE
: 条件过滤。AND
,OR
,NOT
: 逻辑操作符。LIKE
: 模式匹配 (%, _)。IN
,BETWEEN
: 范围查询。ORDER BY
: 排序 (ASC, DESC)。GROUP BY
: 分组。HAVING
: 对分组结果进行过滤。JOIN
: 连接多个表 (INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN)。UNION
,UNION ALL
: 合并结果集。AS
: 别名。- 聚合函数:
COUNT()
,SUM()
,AVG()
,MAX()
,MIN()
.
3. 数据库范式
- 第一范式 (1NF): 属性不可再分。列具有原子性,所有字段值都是不可分解的原子值。
- 第二范式 (2NF): 在1NF的基础上,非主属性完全依赖于主键 (消除部分依赖)。
- 第三范式 (3NF): 在2NF的基础上,任何非主属性不依赖于其它非主属性 (消除传递依赖)。
- BCNF (Boyce-Codd Normal Form): 在3NF的基础上,任何非主属性不能对主键子集依赖。
4. 事务 (Transaction)
- 定义: 数据库操作的最小工作单元,它是一个序列操作,要么都执行,要么都不执行。
- ACID特性:
- 原子性 (Atomicity): 事务是不可分割的最小操作单元,要么全部成功,要么全部失败回滚。
- 一致性 (Consistency): 事务执行前后,数据库从一个一致性状态转变到另一个一致性状态。
- 隔离性 (Isolation): 一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的。
- 持久性 (Durability): 一个事务一旦提交,它对数据库中数据的改变就是永久性的。
- 事务隔离级别 (由低到高):
- 读未提交 (Read Uncommitted): 允许读取尚未提交的数据变更,可能导致脏读、不可重复读、幻读。
- 读已提交 (Read Committed): 只允许读取并发事务已经提交的数据,可以避免脏读,但可能导致不可重复读、幻读。 (大多数数据库默认级别,如Oracle, SQL Server)
- 可重复读 (Repeatable Read): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以避免脏读、不可重复读,但可能导致幻读。 (MySQL默认级别)
- 可串行化 (Serializable): 完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。可以避免脏读、不可重复读、幻读。
- 并发事务可能带来的问题:
- 脏读 (Dirty Read): 一个事务读取到另一个事务未提交的数据。
- 不可重复读 (Non-repeatable Read): 一个事务内多次读取同一数据集合,结果不一致 (因为其他事务修改并提交了数据)。
- 幻读 (Phantom Read): 一个事务内多次查询符合某个范围的数据,结果集数量不一致 (因为其他事务插入或删除了数据)。
5. 索引 (Index)
- 定义: 一种特殊的文件(数据结构),包含着对数据表里所有记录的引用指针,可以加快数据库的查询速度。
- 优点: 提高查询速度。
- 缺点: 创建和维护索引需要时间、空间成本;对数据进行增删改时,索引也需要动态维护,降低了数据的维护速度。
- 类型:
- 主键索引 (Primary Key Index): 数据列不允许重复,不允许为NULL,一个表只能有一个主键。
- 唯一索引 (Unique Index): 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。
- 普通索引 (Normal Index): 基本的索引类型,没有唯一性的限制,允许为NULL值。
- 组合索引 (Composite Index): 多列值组成一个索引,专门用于组合搜索,其效率大于索引合并。
- 全文索引 (Full-text Index): 用于搜索很长一篇文章的时候,效果最好。
- 索引的数据结构: B+树 (常见), 哈希索引等。
- 索引失效的场景: (需要根据具体数据库和版本确认)
WHERE
子句中使用!=
或<>
操作符。WHERE
子句中使用OR
连接条件 (可能导致索引失效,取决于优化器)。LIKE
查询以%
开头。- 索引列参与计算或函数操作。
- 数据类型不匹配 (隐式转换)。
- 索引列上使用
IS NULL
或IS NOT NULL
(部分情况)。 - 组合索引未使用第一列 (最左前缀原则)。
6. 视图 (View)
- 定义: 一张虚拟表,其内容由查询定义。本身不包含数据,数据来自定义视图的查询中涉及的基表,并在具体引用视图时动态生成。
- 优点:
- 简化复杂的SQL操作。
- 提高数据的安全性 (只暴露部分数据)。
- 数据独立性 (基表结构改变,若视图定义不变,则视图使用不受影响)。
- 缺点: 性能可能较差 (特别是复杂视图或嵌套视图)。
7. 存储过程 (Stored Procedure)
- 定义: 一组为了完成特定功能的SQL语句集,经编译后存储在数据库中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
- 优点:
- 模块化编程,提高代码复用性。
- 执行速度更快 (预编译)。
- 减少网络流量。
- 增强安全性。
设计模式
1. 什么是设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
2. 设计模式的原则
- 单一职责原则 (Single Responsibility Principle, SRP): 一个类只负责一项职责。
- 开闭原则 (Open/Closed Principle, OCP): 软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
- 里氏替换原则 (Liskov Substitution Principle, LSP): 所有引用基类的地方必须能透明地使用其子类的对象。
- 依赖倒置原则 (Dependence Inversion Principle, DIP): 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象(即面向接口编程)。
- 接口隔离原则 (Interface Segregation Principle, ISP):客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
- 迪米特法则 (Law of Demeter, LoD) / 最少知识原则: 一个对象应该对其他对象保持最少的了解。
- 组合/聚合复用原则 (Composition/Aggregation Reuse Principle, CARP): 尽量使用对象组合/聚合,而不是继承来达到复用的目的。
3. 设计模式的分类
通常分为三大类:创建型模式、结构型模式、行为型模式。
3.1. 创建型模式
这些模式与对象的创建有关,它们通过以一种受控的方式创建对象来增加系统的灵活性和可重用性。
- 单例模式 (Singleton): 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
- 工厂方法模式 (Factory Method): 定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
- 抽象工厂模式 (Abstract Factory): 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- 建造者模式 (Builder): 将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
- 原型模式 (Prototype): 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
- (简单工厂模式 (Simple Factory) - 非GoF): 一个工厂类根据传入的参数决定创建出哪一种产品类的实例。虽然常用,但非23种GoF设计模式之一。
3.2. 结构型模式
这些模式涉及如何组合类和对象以形成更大的结构。
- 适配器模式 (Adapter): 将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 桥接模式 (Bridge): 将抽象部分与它的实现部分分离,使它们都可以独立地变化。
- 组合模式 (Composite): 将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
- 装饰者模式 (Decorator): 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰者模式相比生成子类更为灵活。
- 外观模式 (Facade): 为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
- 享元模式 (Flyweight): 运用共享技术有效地支持大量细粒度的对象。
- 代理模式 (Proxy): 为其他对象提供一种代理以控制对这个对象的访问。
3.3. 行为型模式 (Behavioral Patterns)
这些模式关注对象之间的通信以及职责的划分。
- 责任链模式 (Chain of Responsibility): 为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
- 命令模式 (Command): 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
- 解释器模式 (Interpreter): 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
- 迭代器模式 (Iterator): 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
- 中介者模式 (Mediator): 用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
- 备忘录模式 (Memento): 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
- 观察者模式 (Observer): 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 状态模式 (State): 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
- 策略模式 (Strategy): 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
- 模板方法模式 (Template Method): 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
- 访问者模式 (Visitor): 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
面试常问问题
1. 什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?
- Java虚拟机 (JVM):是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。
- 平台无关性:Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。
2. JDK和JRE的区别是什么?
- JDK (Java Development Kit): Java开发工具包,包含了JRE、编译器(javac)和其他工具(如 javadoc, java调试器)。是开发Java程序所必需的。
- JRE (Java Runtime Environment): Java运行环境,包含JVM和Java程序所需的核心类库。如果只是运行Java程序,安装JRE即可。
- 关系: JDK包含JRE,JRE包含JVM。
3. == 和 equals 的区别是什么?
==
:- 基本类型: 比较的是值是否相同。
- 引用类型: 比较的是对象的内存地址是否相同,即是否指向同一个对象。
equals()
:- Object类中的默认实现: 与
==
行为一致,比较对象的内存地址。 - 重写后的实现: 许多类(如
String
,Integer
等)重写了equals()
方法,用于比较对象的内容是否相同。 - 示例:
String x = "string"; String y = "string"; String z = new String("string"); System.out.println(x == y); // true (x 和 y 指向常量池中的同一个对象) System.out.println(x == z); // false (z 是在堆上创建的新对象) System.out.println(x.equals(y)); // true (内容相同) System.out.println(x.equals(z)); // true (内容相同)
- Object类中的默认实现: 与
4. String, StringBuffer, StringBuilder 的区别是什么?
- String:
- 不可变性:
String
对象是不可变的。每次对String
对象进行修改(如拼接、替换等)都会创建一个新的String
对象。 - 线程安全: 由于其不可变性,
String
是线程安全的。 - 适用场景: 适用于字符串内容不经常改变的场景。
- 不可变性:
- StringBuffer:
- 可变性:
StringBuffer
对象是可变的,可以在原有对象的基础上进行修改,而不会创建新对象。 - 线程安全:
StringBuffer
的方法是同步的 (synchronized),因此是线程安全的。 - 性能: 由于同步开销,性能相较于
StringBuilder
较低。 - 适用场景: 适用于多线程环境下需要频繁修改字符串内容的场景。
- 可变性:
- StringBuilder:
- 可变性:
StringBuilder
对象也是可变的。 - 线程不安全:
StringBuilder
的方法不是同步的,因此是线程不安全的。 - 性能: 由于没有同步开销,性能相较于
StringBuffer
更高。 - 适用场景: 适用于单线程环境下需要频繁修改字符串内容的场景。
- 可变性:
- 总结:
- 操作少量数据: 适用
String
。 - 单线程操作字符串缓冲区下操作大量数据: 适用
StringBuilder
。 - 多线程操作字符串缓冲区下操作大量数据: 适用
StringBuffer
。
- 操作少量数据: 适用
5. 面向对象的特点有哪些?
- 封装 (Encapsulation): 将数据(属性)和操作数据的方法(行为)捆绑在一起,形成一个独立的实体(对象)。对象对外隐藏其内部实现细节,只提供公共的接口与外部交互。
- 继承 (Inheritance): 子类可以继承父类的属性和方法,并可以添加自己特有的属性和方法,或重写父类的方法。继承实现了代码的复用,并建立了类之间的层次关系。
- 多态 (Polymorphism): 同一个接口或方法可以有多种不同的实现方式。多态性允许使用父类类型的引用指向子类对象,并在运行时根据实际对象的类型来调用相应的方法。主要表现形式有方法的重载和重写。
- 抽象 (Abstraction): 将现实世界中的事物抽象成程序中的类。抽象关注的是对象的本质特征和行为,忽略非本质的细节。抽象类和接口是Java中实现抽象的主要方式。