面向对象编程(OOP)
-
类与对象
- 类与对象是面向对象编程的核心思想
- 类是对象的抽象,对象是类的实例
- 例如人类有姓名、身高、体重等特征,而张三是人类的一个实例
-
封装
- 封装是指隐藏类的属性和方法,只通过必要的方法暴露接口
- 说白了就是将方法的属性设置为private,以防止属性被非法访问和修改
-
继承
- 继承是指子类继承现有的父类
- 继承后的子类可以共享父类的属性和方法
- 在Java中一个子类只能继承一个父类,而一个类可以由多个子类继承(一个儿子只能有一个爸爸,而一个爸爸可以有多个儿子)
-
多态
- 多态是指实体类具有多种形态,多态主要体现形式有重载和重写
- 重载是指方法名相同,而参数的类型或者参数的个数不同
- 重写是指子类继承父类后重新改写了父类方法的方法体,虽然方法名和父类相同,但是内部实现操作不同
集合框架
-
有序集合List,如ArrayList和LinkedList
- list集合是有序的,允许元素重复,每个元素都有对应的下标索引,
ArrayList
:基于动态数组实现的,支持快速随机访问。当你需要频繁访问列表中的元素时,ArrayList
是一个很好的选择。但不适合频繁的插入和删除。LinkedList
:基于双向链表实现的,适合于插入和删除操作,特别是当你需要在列表的开头或中间频繁进行这些操作时。但是,由于链表的随机访问速度较慢,如果你需要频繁访问元素,LinkedList
可能不是最佳选择。- list操作方法:
add(E e):在列表末尾添加一个元素,返回true。 add(int index, E element):在指定位置插入一个元素。 get(int index):返回指定位置的元素。 set(int index, E element):替换指定位置的元素,并返回被替换的元素。 remove(Object o):从列表中删除指定元素,删除成功返回true。 remove(int index):删除指定位置的元素,并返回被删除的元素。 size():返回列表中的元素数量。 isEmpty():检查列表是否为空,为空则返回true。 clear():清空列表中的所有元素。 indexOf(Object o):返回对象在列表中第一次出现的索引。 lastIndexOf(Object o):返回对象在列表中最后一次出现的索引。
-
无序集合Set,如HashSet和TreeSet
- set集合不包含重复元素,没有顺序,主要用于保证元素唯一性,
HashSet
:基于哈希表实现的,它不保证元素的顺序,但提供快速的插入、删除和查找操作。HashSet
在内部使用哈希函数将元素分布到不同的桶中,这使得大多数操作的时间复杂度为O(1)。TreeSet
:基于红黑树实现的,它不仅确保元素的唯一性,还维护元素的排序(自然排序或根据提供的比较器定制排序)。TreeSet
适合于需要有序元素的场景,但与HashSet
相比,它的操作速度稍慢,因为需要维护树的平衡。- list操作方法:
add(E e):向集合中添加元素,若添加成功返回true。 remove(Object o):从集合中删除元素,若删除成功返回true。 contains(Object o):检查集合是否包含指定元素,包含则返回true。 size():返回集合中的元素数量。 isEmpty():检查集合是否为空,为空则返回true。 clear():清空集合中的所有元素。 iterator():返回集合的迭代器,用于遍历集合。
-
键值对集合Map,如HashMap和TreeMap。
- Map集合是键值对集合,其中键是唯一的不可重复的,提供键到值的映射,
HashMap
:基于哈希表实现的,它允许空键和空值。HashMap
不保证元素的顺序,提供快速的插入、删除和查找操作。与HashSet
类似,HashMap
在内部使用哈希函数将键分布到不同的桶中,大多数操作的时间复杂度为O(1)。TreeMap
:基于红黑树实现的,它不仅确保键的唯一性,还维护键的排序(自然排序或根据提供的比较器定制排序)。TreeMap
适合于需要有序键的场景,但与HashMap
相比,它的操作速度稍慢,因为需要维护树的平衡。- Map操作方法:
put(K key, V value):将指定的值与此映射中的指定键关联,返回先前的值。 get(Object key):返回指定键所映射的值。 remove(Object key):如果存在一个键的映射关系,则将其从映射中移除。 keySet():返回映射中包含的键的Set集合。 values():返回映射中包含的值的Collection视图。 entrySet():返回映射中包含的键值映射关系的Set集合。 size():返回映射中的键-值映射关系的数量。 isEmpty():检查映射是否为空,为空则返回true。 clear():清空映射中的所有键值映射关系。
多线程和并发
多线程
- 线程(Thread):线程是程序中最小的执行单元,可以同时运行多个线程以提高效率
- 创建线程:
- 通过继承Thread类创建线程,
// 继承Thread类 class MyThread extends Thread { @Override public void run() { System.out.println("线程运行中:" + Thread.currentThread().getName()); } } // 在主类中创建并启动线程 public class ThreadExample { public static void main(String[] args) { MyThread myThread = new MyThread(); // 创建线程 myThread.start(); // 启动线程 } }
- 或者实现Runnable接口来创建线程
//通过实现Runnable接口创建线程 // 实现Runnable接口 class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程运行中:" + Thread.currentThread().getName()); } } // 在主类中创建并启动线程 public class RunnableExample { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); // 创建Runnable实例 Thread thread = new Thread(myRunnable); // 将Runnable实例传给Thread thread.start(); // 启动线程 } }
- 同步(Synchronized):确保多个线程访问共享资源时,一次只有一个线程进行操作,避免数据的不一致
- 应用:用
synchronized
关键字修饰方法或代码块,确保线程安全。class Account { private int balance; public Account(int balance) { this.balance = balance; } // 使用synchronized修饰方法 public synchronized void deposit(int amount) { balance += amount; System.out.println("存款后余额:" + balance); } } class AccountThread extends Thread { private Account account; public AccountThread(Account account) { this.account = account; } @Override public void run() { account.deposit(100); } } public class SynchronizedExample { public static void main(String[] args) { Account account = new Account(0); // 创建并启动两个线程 Thread thread1 = new AccountThread(account); Thread thread2 = new AccountThread(account); thread1.start(); thread2.start(); } }
- 锁(lock):提供了比synchronized更灵活的线程同步控制。可以通过Lock接口实现更复杂的同步策略,如尝试非阻塞获取锁、可中断的锁获取等。
使用
ReentrantLock
实现锁:import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Account { private int balance; private final Lock lock = new ReentrantLock(); // 创建一个ReentrantLock对象作为锁 public Account(int balance) { this.balance = balance; } // 使用Lock实现存款方法 public void deposit(int amount) { lock.lock(); // 获取锁 try { balance += amount; System.out.println("存款后余额:" + balance); } finally { lock.unlock(); // 释放锁 } } } class AccountThread extends Thread { private Account account; public AccountThread(Account account) { this.account = account; } @Override public void run() { account.deposit(100); } } public class LockExample { public static void main(String[] args) { Account account = new Account(0); // 创建并启动两个线程 Thread thread1 = new AccountThread(account); Thread thread2 = new AccountThread(account); thread1.start(); thread2.start(); } }
上述案例使用了
ReentrantLock
来替代synchronized
关键字。ReentrantLock
提供了比synchronized
更灵活的锁定机制,例如:
尝试获取锁(tryLock):可以非阻塞地尝试获取锁,如果锁不可用,线程可以不做等待继续执行其他任务。
可中断的锁获取(lockInterruptibly):在等待锁的过程中,如果线程被中断,可以响应中断,而不是无限期地等待锁。
公平锁(Fair Lock):可以选择创建公平锁,使得等待时间最长的线程优先获取锁。
并发集合
-
并发集合:并发集合是指Java集合框架中专门为多线程环境设计的集合,它们内部已经处理好了线程安全问题,使得在多线程访问时不需要额外的同步措施,如ConcurrentHashMap。这些集合类位于
java.util.concurrent
包中。
ConcurrentHashMap
是并发集合中的一个,它是一个线程安全的哈希表。与Hashtable
不同,Hashtable
在每次访问时都需要对整个结构加锁,而ConcurrentHashMap
采用了一种细粒度的锁定机制,这意味着在进行读操作时通常不需要加锁,而在写操作时只锁定相关的一部分,从而提供了更好的并发性能。特点:
线程安全:
ConcurrentHashMap
内部通过分段锁(segmentation)或CAS(Compare-And-Swap)操作来保证线程安全,无需外部同步。高性能:由于锁的粒度更细,
ConcurrentHashMap
在读操作密集或写操作较少的场景下,性能通常优于Hashtable
。动态扩容:
ConcurrentHashMap
在需要时可以动态地进行扩容,而不需要重新构建整个哈希表。支持并发操作:
ConcurrentHashMap
允许多个线程同时进行操作,而不会产生冲突。使用场景:
当你需要一个线程安全的哈希表,并且希望在高并发环境下保持良好的性能时,
ConcurrentHashMap
是一个理想的选择。import java.util.concurrent.ConcurrentHashMap; public class ConcurrentHashMapExample { public static void main(String[] args) { ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); // 并发地对map进行操作 map.put("one", 1); map.put("two", 2); map.put("three", 3); // 并发读取 System.out.println(map.get("two")); // 输出 2 } }
JVM(Java虚拟机)
类加载机制
加载(Loading):JVM通过全限定名查找.class文件,加载类的数据到方法区。
链接(Linking):将类的二进制数据合并到JVM中,包括验证、准备(为静态变量分配内存)和解析(将符号引用替换为直接引用)。
初始化(Initialization):执行类构造器
<clinit>()
方法,初始化静态变量。加载、链接、初始化,是类生命周期的三个阶段
内存模型
堆(Heap):存放对象实例和数组,所有线程共享,垃圾回收主要在此区域进行。
栈(Stack):每个线程私有,存放局部变量和方法调用信息,方法执行完毕后自动释放。
方法区(Method Area):存放类信息、常量和静态变量,所有线程共享。
堆、栈、方法区,是JVM内存的三大区域。
垃圾回收
标记-清除(Mark-Sweep):标记可回收对象,然后清除,但可能产生内存碎片。
复制(Copying):将内存分为两半,每次只使用一半,用完复制存活对象到另一半,然后清理。
标记-整理(Mark-Compact):先标记可回收对象,然后将存活对象向一端移动,紧凑排列。
分代收集(Generational Collection):根据对象生命周期分为新生代和老年代,新生代使用复制算法,老年代使用标记-清除或标记-整理。
标记-清除、复制、标记-整理,是垃圾回收的三种基本算法。
性能调优
参数调整:通过调整JVM启动参数(如堆大小、垃圾回收器选择)来优化性能。
监控工具:使用JVM监控工具(如JConsole、VisualVM)来分析应用性能,找出瓶颈。
参数调整和监控工具,是优化JVM性能的两大手段。