封装(Encapsulation)
将数据(属性)和操作数据的方法(行为)绑定在一起,隐藏内部实现细节,仅暴露必要的接口。
目的:提高安全性(如私有属性)、降低复杂度(使用者无需关心内部逻辑)。
继承(Inheritance)
子类可以继承父类的属性和方法,并扩展或重写功能。
目的:实现代码复用,建立类之间的层次关系(如Dog extends Animal
)。
多态(Polymorphism)
同一操作作用于不同对象时,可能产生不同的行为。通常通过方法重写(Override)或接口实现来实现。
例子:Animal
类的speak()
方法,Dog
返回“Woof!”,Cat
返回“Meow!”。
抽象(Abstraction)
提取共性特征形成抽象类或接口,忽略非关键细节。
例子:Shape
抽象类定义calculateArea()
方法,由子类Circle
、Square
具体实现。
想象你造一辆车:
- 类是设计图纸(定义车的属性和功能);
- 对象是按图纸造出的具体车辆;
- 继承是基于基础车型设计豪华版;
- 多态是踩油门时,电动车和燃油车响应方式不同;
- 封装是隐藏发动机细节,只给你方向盘和油门接口。
面向对象的本质是**用代码模拟现实世界的协作关系**,让复杂系统更易于理解和构建。
重载(Overload)和重写(Override)是Java中两个重要的概念,主要区别如下:
重载(Overload)
- 发生范围:同一个类中
- 方法签名:方法名相同,但参数列表不同(参数类型、个数或顺序)
- 返回值:无关,可以不同
- 访问修饰符:无关,可以不同
- 用途:提供功能相似但参数不同的方法变体
重写(Override)
- 发生范围:子类与父类之间
- 方法签名:方法名、参数列表、返回值类型必须与父类完全相同(Java 5+支持协变返回类型)
- 访问修饰符:子类方法的访问权限不能小于父类(如父类是protected
,子类只能是protected
或public
)
- 异常:子类方法抛出的异常范围不能大于父类
- 用途:子类修改或扩展父类的方法实现
示例
// 重载示例(同一个类中)
class Calculator {
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; } // 参数类型不同
}
// 重写示例(子类与父类之间)
class Animal {
void makeSound() { System.out.println("Animal sound"); }
}
class Dog extends Animal {
@Override
void makeSound() { System.out.println("Bark"); } // 方法实现不同
}
浅拷贝(Shallow Copy)和深拷贝(Deep Copy)是对象复制的两种方式,区别在于是否递归复制对象的所有层级:
(1)浅拷贝:
- 复制对象本身,但仅复制引用类型的内存地址(不复制对象内容)
- 原始对象和副本共享引用类型的内部对象
- 修改引用类型成员会影响所有关联对象
- 常见实现方式:Object.clone()(默认是浅拷贝)
(2)深拷贝:
- 递归复制对象及其所有嵌套的引用类型对象
- 原始对象和副本完全独立,无共享内存
- 修改任何一方不会影响另一方
- 常见实现方式:序列化 / 反序列化、手动递归复制
简单记忆: /浅拷贝是 “表面复制”,深拷贝是 “彻底复制”。/
Java 8 核心新特性:
1. Lambda表达式:(a, b) -> a + b
2. Stream API:list.stream().filter(x -> x>0).map(...)
3. 默认方法:interface
中 default void foo(){}
4. 方法引用:System.out::println
5. 新日期API:LocalDate
、LocalDateTime
6. Optional:Optional.ofNullable(x).orElse(...)
7. 函数式接口:@FunctionalInterface
(如 Predicate
、Function
)
==
- 基本类型:比较值是否相等(如int a=1; b=1; a==b
为true
)。
- 引用类型:比较内存地址是否相同(是否指向同一对象)。
equals()
- 默认行为:同==
(比较地址,如Object
类)。
- 重写后:通常比较对象内容(如String
、Integer
等类)。
示例
String s1 = new String("abc");
String s2 = new String("abc");
s1 == s2; // false(地址不同)
s1.equals(s2); // true(内容相同)
final 类:禁止被继承(如String
)。
final 方法:禁止被子类重写。
final 属性:赋值后不可变(基本类型值不变,引用类型地址不变)。
内存泄漏(Memory Leak):
程序中已不再使用的对象无法被垃圾回收(GC),导致持续占用内存,最终可能引发OutOfMemoryError
。
常见场景:
1. 静态集合持有对象:如static List
添加元素后未清理。
2. 未关闭资源:如InputStream
、数据库连接等。
3. 内部类持有外部类引用:匿名内部类隐式引用外部对象。
4. 缓存未失效:缓存条目不再使用但未被移除。
排查工具:
- 堆转储(Heap Dump)分析(如Eclipse MAT)。
- 内存分析器(如JProfiler、VisualVM)。
示例:
static List<Object> cache = new ArrayList<>();
public void addToCache(Object obj) {
cache.add(obj); // 对象无法被GC回收,即使不再使用
}
Java集合框架核心接口:
1. Collection
- List(有序可重复):ArrayList
、LinkedList
、Vector
。
- Set(无序唯一):HashSet
、TreeSet
、LinkedHashSet
。
- Queue(队列):LinkedList
、PriorityQueue
。
2. Map(键值对)
- HashMap
、TreeMap
、LinkedHashMap
、ConcurrentHashMap
。
关键抽象类:
- AbstractList
、AbstractSet
、AbstractMap
等。
工具类:
- Collections
(静态方法)、Arrays
(数组转集合)。
并发集合:
- ConcurrentHashMap
、CopyOnWriteArrayList
。
注意:
- fail-fast:ArrayList
、HashMap
(迭代时修改抛ConcurrentModificationException
)。
- fail-safe:ConcurrentHashMap
、CopyOnWriteArrayList
(迭代时允许修改)。
ArrayList 和 LinkedList 是 List 接口的两种主要实现,区别如下:
(1)数据结构:
- ArrayList:动态数组
- LinkedList:双向链表
(2)适用场景:
- ArrayList:频繁随机访问,较少插入删除
- LinkedList:频繁插入删除,较少随机访问
(3)注意:
简单记忆:随机访问选 ArrayList,频繁插入删除选 LinkedList。
List 和 Set 的区别(记忆口诀)
👉 “List 有序可重复,Set 无序独一份”
List(如 ArrayList、LinkedList):
- 有序:存进去的顺序就是取出来的顺序(像排队)
- 可重复:能存多个相同的元素(像购物车里的商品)
- 按索引访问:直接用 get(0)
取第一个
Set(如 HashSet、TreeSet):
- 无序(*HashSet*)或 按规则排序(*TreeSet*)
- 唯一性:自动去重(像数学集合,相同元素存不进)
- 无索引:只能遍历或转成数组访问
场景选择:
- 需要重复元素?用 List ✅
- 需要自动去重?用 Set ✅
HashMap
适用于以下核心场景:
LinkedHashMap
),例如临时存储中间结果、统计频次(键为元素,值为计数)。核心优势:以空间换时间,提供高效的键值操作,是 Java 中最常用的映射容器。
Java集合分类与接口关系(记忆口诀)
👉 “单列双列分清楚,有序无序再区分”
Collection
):ArrayList
(数组)、LinkedList
(链表)、Vector
(线程安全)HashSet
(哈希表)、TreeSet
(红黑树排序)Map
):键值对存储 → 像字典HashMap
、TreeMap
、Hashtable
(线程安全)Collection Map
├── List ├── HashMap
│ ├── ArrayList ├── TreeMap
│ └── LinkedList └── Hashtable
└── Set
├── HashSet
└── TreeSet
LinkedList
也实现了它TreeSet/TreeMap
)一句话总结:
“先分单双列,再看序与序,最后挑实现类!”
Cookie 和 Session 的区别(记忆口诀)
👉 “Cookie 客户存,Session 服务端,一个明晃晃,一个暗藏藏”
HttpOnly
防XSS)setMaxAge(3600)
)Cookie | Session |
---|---|
记住登录状态(7天免密) | 保存购物车商品详情 |
保存用户偏好(如语言主题) | 存敏感信息(如权限角色) |
一句话总结:
“Cookie 是用户口袋里的便利贴,Session 是服务器的秘密备忘录!”
String、StringBuffer、StringBuilder 的区别(记忆口诀)
👉 “String 不可变,Buffer 线程安,Builder 单线程快如箭!”
synchronized
锁)操作 | String | StringBuffer | StringBuilder |
---|---|---|---|
频繁拼接字符串 | 最慢 | 较快 | 最快 |
示例代码:
// String:产生大量临时对象(不推荐循环拼接)
String s = "a";
for (int i = 0; i < 100; i++) { s += i; } // 慢!
// StringBuilder:单线程首选
StringBuilder sb = new StringBuilder("a");
for (int i = 0; i < 100; i++) { sb.append(i); } // 快!
// StringBuffer:多线程兼容
StringBuffer sbf = new StringBuffer("a");
for (int i = 0; i < 100; i++) { sbf.append(i); } // 安全但稍慢
一句话总结:
“String 只读不写,Builder 单线程飞起,Buffer 为多线程保驾护航!”
HashMap 是 Java 中最常用的哈希表实现,其底层原理基于 数组 + 链表 + 红黑树 结构,核心机制包括哈希函数、碰撞处理和扩容策略。以下是详细解析:
// Node 节点结构(简化版)
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // 哈希值(经扰动函数处理)
final K key;
V value;
Node<K,V> next; // 链表下一个节点
}
key.hashCode()
计算原始哈希值,再经 扰动函数(hash = key.hashCode() ^ (key.hashCode() >>> 16)
)降低哈希冲突概率。index = (table.length - 1) & hash
,等价于 hash % table.length
(数组长度为 2 的幂时位运算更高效)。e.hash & oldCap
判断元素在新数组中的位置)。ConcurrentHashMap
(线程安全,分段锁机制,JDK 8 改用 CAS + synchronized)。Collections.synchronizedMap()
(全局锁,性能较差)。equals()
方法匹配 key。(table.length - 1) & hash
等价于取模运算,且扩容时无需重新计算哈希值。HashMap 通过哈希函数快速定位元素,用链表/红黑树处理冲突,动态扩容保证性能。在单线程场景中高效易用,但多线程环境需选择线程安全的替代方案。理解其底层原理有助于合理设置参数(如初始容量)和避免常见陷阱。