1. Java包装类与类型转换解析
Java作为一门面向对象的编程语言,其基本数据类型(如int、char等)并不具备对象的特性。为了在需要对象的场景中使用这些基本类型,Java提供了对应的包装类(Wrapper Classes)。包装类的主要作用包括:
- 将基本数据类型封装成对象
- 提供类型转换的方法
- 实现基本数据类型与字符串之间的转换
1.1 基本类型与包装类对应关系
Java中的8种基本数据类型都有对应的包装类:
| 基本类型 | 包装类 | 位数 | 默认值 |
|---|---|---|---|
| byte | Byte | 8 | 0 |
| short | Short | 16 | 0 |
| int | Integer | 32 | 0 |
| long | Long | 64 | 0L |
| float | Float | 32 | 0.0f |
| double | Double | 64 | 0.0d |
| char | Character | 16 | '\u0000' |
| boolean | Boolean | 1 | false |
1.2 自动装箱与拆箱
Java 5引入了自动装箱(Autoboxing)和拆箱(Unboxing)机制,简化了基本类型与包装类之间的转换:
java复制// 自动装箱
Integer num = 10; // 编译器自动转换为 Integer.valueOf(10)
// 自动拆箱
int n = num; // 编译器自动转换为 num.intValue()
注意:自动装箱机制虽然方便,但在循环或大量数据操作时可能会影响性能,因为会频繁创建对象。
1.3 类型转换实践
基本类型之间的转换
java复制// 小范围转大范围(自动类型转换)
int i = 100;
long l = i; // 自动转换为long
// 大范围转小范围(强制类型转换)
double d = 100.04;
long ll = (long)d; // 结果为100,小数部分被截断
包装类与字符串转换
java复制// 字符串转包装类
Integer num1 = Integer.valueOf("123");
Double num2 = Double.valueOf("3.14");
// 包装类转字符串
String s1 = Integer.toString(123);
String s2 = Double.toString(3.14);
常见问题解决
问题1:C语言错误"loadimage"重载解决方案
虽然这不是Java问题,但在Java中类似的重载问题可以这样处理:
java复制// 明确指定参数类型
Image img = loadImage((String)path); // 调用String参数的版本
Image img2 = loadImage((InputStream)input); // 调用InputStream参数的版本
问题2:int强制转换为char
java复制int i = 65;
char c = (char)i; // c的值为'A'
System.out.println(c); // 输出: A
问题3:Timestamp类型转换报错
处理数据库时间类型转换时:
java复制// 使用SimpleDateFormat进行格式化
Timestamp timestamp = resultSet.getTimestamp("create_time");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formatted = sdf.format(timestamp);
2. Set接口及其实现类详解
Set是Java集合框架中的重要接口,它继承自Collection接口,具有以下特点:
- 不允许重复元素
- 最多包含一个null元素
- 不保证元素的顺序(除非使用特定的实现类)
2.1 Set主要实现类对比
| 实现类 | 底层实现 | 是否有序 | 线程安全 | 允许null | 时间复杂度 |
|---|---|---|---|---|---|
| HashSet | 哈希表 | 无序 | 不安全 | 允许 | O(1) |
| LinkedHashSet | 哈希表+链表 | 插入顺序 | 不安全 | 允许 | O(1) |
| TreeSet | 红黑树 | 自然排序 | 不安全 | 不允许 | O(log n) |
2.2 HashSet深度解析
HashSet是最常用的Set实现,基于HashMap实现:
java复制// HashSet构造方法
public HashSet() {
map = new HashMap<>();
}
// 添加元素实际调用HashMap的put方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
使用示例:
java复制Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Apple"); // 重复元素不会被添加
System.out.println(set.contains("Banana")); // true
System.out.println(set.size()); // 2
2.3 TreeSet排序特性
TreeSet基于TreeMap实现,可以保持元素有序:
java复制Set<Integer> numbers = new TreeSet<>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);
// 输出会自动排序
System.out.println(numbers); // [1, 2, 5, 8]
自定义排序:
java复制// 使用Comparator自定义排序
Set<String> customOrder = new TreeSet<>((a, b) -> b.compareTo(a));
customOrder.add("Apple");
customOrder.add("Banana");
customOrder.add("Orange");
System.out.println(customOrder); // [Orange, Banana, Apple]
2.4 Set使用注意事项
-
元素唯一性判断:Set使用equals()和hashCode()方法判断元素是否相同,放入Set的对象应该正确重写这两个方法。
-
性能考虑:
- HashSet对于插入、删除和查找操作都是O(1)时间复杂度
- TreeSet这些操作是O(log n)时间复杂度,但可以保持有序
-
内存占用:HashSet比TreeSet占用更多内存,因为需要维护哈希表
-
线程安全:标准Set实现都不是线程安全的,多线程环境下可以使用:
java复制Set<String> safeSet = Collections.synchronizedSet(new HashSet<>());
3. Map接口及其实现类剖析
Map接口表示键值对映射,是Java集合框架中最重要的接口之一。主要特点包括:
- 每个键对应一个值
- 键不能重复
- 一个映射可以包含多个值
3.1 Map核心实现类对比
| 实现类 | 底层结构 | 是否有序 | 允许null键/值 | 线程安全 | 时间复杂度 |
|---|---|---|---|---|---|
| HashMap | 数组+链表/红黑树 | 无序 | 允许/允许 | 不安全 | O(1) |
| LinkedHashMap | 哈希表+双向链表 | 插入/访问顺序 | 允许/允许 | 不安全 | O(1) |
| TreeMap | 红黑树 | 键的自然顺序 | 不允许/允许 | 不安全 | O(log n) |
| Hashtable | 哈希表 | 无序 | 不允许/不允许 | 安全 | O(1) |
| ConcurrentHashMap | 分段数组+链表 | 无序 | 不允许/允许 | 安全 | O(1) |
3.2 HashMap深度解析
HashMap是最常用的Map实现,JDK 8后其结构为数组+链表/红黑树:
java复制// HashMap关键参数
static final int DEFAULT_INITIAL_CAPACITY = 16; // 默认初始容量
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认负载因子
static final int TREEIFY_THRESHOLD = 8; // 链表转红黑树阈值
使用示例:
java复制Map<String, Integer> map = new HashMap<>();
map.put("Apple", 10);
map.put("Banana", 5);
map.put("Orange", 8);
// 获取值
int appleCount = map.get("Apple"); // 10
// 遍历
map.forEach((k, v) -> System.out.println(k + ": " + v));
3.3 TreeMap排序特性
TreeMap基于红黑树实现,可以保持键的有序:
java复制Map<String, Integer> sortedMap = new TreeMap<>();
sortedMap.put("Orange", 8);
sortedMap.put("Apple", 10);
sortedMap.put("Banana", 5);
// 按键的自然顺序输出
System.out.println(sortedMap); // {Apple=10, Banana=5, Orange=8}
自定义排序:
java复制// 按值排序的TreeMap
Map<String, Integer> valueSorted = new TreeMap<>(
Comparator.comparing(map::get).thenComparing(Map.Entry::getKey)
);
valueSorted.putAll(map);
3.4 ConcurrentHashMap并发控制
ConcurrentHashMap是线程安全的HashMap实现,采用分段锁设计:
java复制ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("A", 1);
concurrentMap.putIfAbsent("B", 2);
// 原子操作
concurrentMap.compute("A", (k, v) -> v + 1); // A的值变为2
3.5 Map使用技巧与陷阱
-
初始容量设置:预估元素数量设置初始容量,避免频繁扩容
java复制// 预计存放1000个元素,负载因子0.75 Map<String, Integer> map = new HashMap<>(1333); // 1000/0.75 -
键对象要求:作为键的对象必须正确实现hashCode()和equals()方法
-
遍历方式选择:
java复制// 只遍历键 for (String key : map.keySet()) {} // 只遍历值 for (Integer value : map.values()) {} // 遍历键值对(推荐) for (Map.Entry<String, Integer> entry : map.entrySet()) {} -
Java 8新增方法:
java复制// 键不存在时计算 map.computeIfAbsent("NewKey", k -> calculateValue(k)); // 合并值 map.merge("Apple", 1, Integer::sum); -
内存泄漏风险:使用可变对象作为键可能导致内存泄漏
java复制Map<List<String>, Integer> dangerousMap = new HashMap<>(); List<String> key = new ArrayList<>(); dangerousMap.put(key, 1); key.add("modified"); // 修改了键对象,可能导致无法访问该条目
4. 集合框架实战问题与解决方案
4.1 类型转换常见问题
问题1:包装类与基本类型转换NPE
java复制Integer num = null;
int n = num; // 抛出NullPointerException
// 解决方案:先进行null检查
int safeN = (num != null) ? num : 0;
问题2:字符串与数字转换格式错误
java复制try {
int value = Integer.parseInt("123a"); // 抛出NumberFormatException
} catch (NumberFormatException e) {
System.out.println("无效的数字格式");
}
4.2 Set去重高级用法
对象去重示例:
java复制class Product {
String id;
String name;
// 必须重写equals和hashCode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Product)) return false;
Product product = (Product) o;
return id.equals(product.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Set<Product> products = new HashSet<>();
products.add(new Product("1", "Apple"));
products.add(new Product("1", "Apple")); // 不会重复添加
4.3 Map高级应用
多级Map示例:
java复制Map<String, Map<String, Integer>> multiLevelMap = new HashMap<>();
// 初始化内层Map
multiLevelMap.computeIfAbsent("Fruits", k -> new HashMap<>())
.put("Apple", 10);
// Java 8合并操作
multiLevelMap.merge("Fruits",
new HashMap<>(Map.of("Banana", 5)),
(oldMap, newMap) -> { oldMap.putAll(newMap); return oldMap; });
统计词频示例:
java复制String text = "apple banana apple orange banana apple";
Map<String, Integer> frequency = new HashMap<>();
Arrays.stream(text.split(" "))
.forEach(word -> frequency.merge(word, 1, Integer::sum));
System.out.println(frequency); // {orange=1, banana=2, apple=3}
4.4 性能优化建议
-
集合初始化:预估大小设置初始容量,避免扩容开销
java复制// 不好的做法:默认初始容量16,添加1000个元素需要多次扩容 Set<String> set1 = new HashSet<>(); // 好的做法:指定初始容量 Set<String> set2 = new HashSet<>(1000); -
遍历优化:使用entrySet遍历Map比先获取keySet再get更高效
java复制// 低效方式 for (String key : map.keySet()) { Integer value = map.get(key); } // 高效方式 for (Map.Entry<String, Integer> entry : map.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); } -
并发选择:
- 读多写少:使用ConcurrentHashMap
- 写多读少:考虑使用Collections.synchronizedMap()
- 需要排序:使用ConcurrentSkipListMap
-
内存优化:对于基本数据类型集合,考虑使用第三方库如Eclipse Collections的原始类型集合
4.5 Java新版本特性
Java 9+ 新增工厂方法:
java复制// 创建不可变集合
Set<String> immutableSet = Set.of("A", "B", "C");
Map<String, Integer> immutableMap = Map.of("A", 1, "B", 2);
// 创建可变集合
List<String> list = new ArrayList<>(List.of("A", "B", "C"));
Java 10+ 局部变量类型推断:
java复制var set = new HashSet<String>(); // 推断为HashSet<String>
var map = new HashMap<Integer, String>(); // 推断为HashMap<Integer, String>
在实际项目中,合理选择集合类型和实现类可以显著提高代码的性能和可维护性。对于高频访问的数据,考虑使用专门优化的集合实现;对于并发场景,优先选择线程安全的集合实现;对于需要排序的场景,TreeSet和TreeMap是不错的选择。
