1. 什么是泛型 ?与 T 的区别
- 原理
泛型是 Java 编程语言中的一个强大特性,它提供了编译时类型安全检查机制,允许在定义类、接口和方法时使用类型参数。这些类型参数在使用时会被具体的类型所替代,从而实现代码的复用和类型安全。泛型的实现基于类型擦除机制,在编译时,泛型类型信息会被擦除,替换为原始类型。
例如,定义一个泛型类 Box<T>
:
java
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
在使用时,可以指定具体的类型,如 Box<Integer>
或 Box<String>
。
T
是泛型类型参数的一个常用命名约定,它代表 “Type”,表示未知的类型。在 Java 中,你可以使用任意合法的标识符作为类型参数名,但通常遵循一些约定,如 T
用于一般的类型,E
用于集合元素类型,K
和 V
用于键值对。
- 要点
- 编译时类型检查:泛型在编译时进行类型检查,确保代码的类型安全性,避免了运行时的
ClassCastException
。 - 代码复用:通过泛型,可以编写通用的代码,减少重复代码的编写。
- 类型参数命名:
T
只是一个常用的类型参数名,你可以根据需要使用其他合法的标识符。
- 应用
- 泛型通配符:Java 提供了通配符
?
来表示未知类型,分为上界通配符? extends T
和下界通配符? super T
。上界通配符用于限制泛型类型的上限,下界通配符用于限制泛型类型的下限。 - 泛型方法:除了泛型类,还可以定义泛型方法。泛型方法可以在普通类中定义,也可以在泛型类中定义。
- 泛型接口:可以定义泛型接口,实现该接口的类需要指定具体的类型参数。
2. 什么是枚举类型,字节码层面理解 Enum
- 原理
在 Java 中,枚举类型(enum
)是一种特殊的类。从字节码层面看,枚举类型在编译后会被转换为一个继承自 java.lang.Enum
的类。每个枚举常量都是该类的一个静态最终实例,并且会在类加载时被初始化。
例如,以下枚举类型:
java
public enum Color {
RED, GREEN, BLUE;
}
编译后,Color
会变成一个类,其中 RED
、GREEN
和 BLUE
是该类的静态最终实例。编译器会为枚举类型生成一些额外的方法,如 values()
用于返回所有枚举常量的数组,valueOf()
用于根据名称获取枚举常量。
- 要点
- 继承自
java.lang.Enum
:枚举类型在字节码层面是一个普通的类,继承自java.lang.Enum
。 - 静态最终实例:枚举常量是该类的静态最终实例,在类加载时被初始化。
- 额外方法:编译器会为枚举类型生成一些额外的方法,方便使用。
- 应用
- 枚举属性和方法:枚举类型可以有自己的属性和方法,并且可以实现接口。通过为枚举常量添加属性和方法,可以实现更复杂的逻辑。
switch
语句中的枚举:在switch
语句中使用枚举类型可以提高代码的可读性和安全性。
3. 什么是值传递与引用传递,有什么区别
- 原理
- 值传递:在 Java 中,基本数据类型(如
int
、double
等)的参数传递是值传递。当调用方法时,会将实际参数的值复制一份传递给方法的形式参数,方法内部对形式参数的修改不会影响实际参数的值。 - 引用传递:对象类型的参数传递实际上也是值传递,但传递的是对象的引用(内存地址)。当调用方法时,会将对象的引用复制一份传递给方法的形式参数,方法内部可以通过这个引用修改对象的状态,但不能改变引用本身的值(即不能让引用指向另一个对象)。
- 要点
- 值传递:传递的是实际参数的值的副本,方法内部对形式参数的修改不会影响实际参数。
- 引用传递:传递的是对象引用的副本,方法内部可以通过引用修改对象的状态,但不能改变引用本身。
- Java 只有值传递:Java 中只有值传递,没有真正意义上的引用传递。
- 应用
- 理解值传递和引用传递的重要性:理解值传递和引用传递对于处理方法参数和对象状态的修改非常重要,特别是在处理复杂的对象和数据结构时。
- 避免意外的副作用:在编写方法时,要明确方法是否会修改传入的对象状态,以避免意外的副作用。
4. 什么是字节流 字符流,有什么区别
- 原理
- 字节流:以字节为单位进行数据的读写操作,主要用于处理二进制数据,如图片、音频、视频等。Java 中的字节流类都继承自
InputStream
和OutputStream
,如FileInputStream
和FileOutputStream
。 - 字符流:以字符为单位进行数据的读写操作,主要用于处理文本数据。Java 中的字符流类都继承自
Reader
和Writer
,如FileReader
和FileWriter
。
- 要点
- 数据处理单位:字节流处理的是字节数据,适用于任何类型的数据;字符流处理的是字符数据,适用于文本数据。
- 字符编码和解码:字符流在读写时会进行字符编码和解码操作,而字节流不会。
- 读写效率:字符流的读写效率通常比字节流高,因为它一次可以处理多个字节组成的字符。
- 应用
- 使用场景:在处理文本文件时,建议使用字符流,以避免字符编码问题;在处理二进制文件时,必须使用字节流。
- 字节流和字符流的转换:可以使用
InputStreamReader
和OutputStreamWriter
进行字节流和字符流的转换。
5. 什么是静态内部类 匿名类,有什么区别
- 原理
- 静态内部类:是定义在另一个类内部的类,使用
static
修饰。静态内部类不依赖于外部类的实例,可以直接创建对象。它只能访问外部类的静态成员。 - 匿名类:是一种没有显式名称的类,通常用于创建一个实现了某个接口或继承了某个类的对象。匿名类在创建时会立即实例化,并且只能使用一次。
- 要点
- 类名和使用次数:静态内部类有自己的类名,可以在多个地方使用;匿名类没有类名,只能使用一次。
- 成员变量和方法:静态内部类可以有自己的成员变量和方法;匿名类通常只重写接口或父类的方法。
- 外部类实例依赖:静态内部类不依赖于外部类的实例;匿名类可以访问外部类的成员变量和方法(如果是局部匿名类,访问外部局部变量时,该变量必须是
final
或effectively final
的)。
- 应用
- 静态内部类的使用场景:静态内部类可以用于封装一些与外部类相关的功能,提高代码的可读性和可维护性。
- 匿名类的使用场景:匿名类常用于事件处理、回调函数等场景,简化代码的编写。
6. 什么是 hashtable 和 hashmap,有什么区别
- 原理
- Hashtable:是 Java 早期的哈希表实现,它继承自
Dictionary
类,并且是线程安全的。Hashtable
使用哈希算法来存储和查找键值对,通过键的哈希码来确定存储位置。 - HashMap:是 Java 1.2 引入的哈希表实现,它继承自
AbstractMap
类,不是线程安全的。HashMap
同样使用哈希算法来存储和查找键值对,当发生哈希冲突时,HashMap
使用链表或红黑树(Java 8 及以后)来处理。
- 要点
- 线程安全性:
Hashtable
是线程安全的,而HashMap
不是。因此,在多线程环境下,如果需要保证线程安全,可以使用Hashtable
或ConcurrentHashMap
;在单线程环境下,建议使用HashMap
,因为它的性能更高。 null
值处理:Hashtable
的键和值都不允许为null
,而HashMap
的键和值都允许为null
。- 迭代器:
Hashtable
的迭代器是Enumeration
,而HashMap
的迭代器是Iterator
。
- 应用
HashMap
的优化:在 Java 8 中,HashMap
进行了优化,引入了红黑树来处理哈希冲突,当链表长度超过一定阈值时,会将链表转换为红黑树,提高了查找效率。- 使用建议:在单线程环境下,优先使用
HashMap
;在多线程环境下,如果对线程安全要求不高,可以使用Collections.synchronizedMap()
方法将HashMap
转换为线程安全的映射;如果对并发性能要求较高,建议使用ConcurrentHashMap
。
7. 什么是匿名类
- 原理
匿名类是一种没有显式名称的类,它是在创建对象的同时定义的。匿名类通常用于创建一个实现了某个接口或继承了某个类的对象,并且只能使用一次。
例如,以下是一个实现了 Runnable
接口的匿名类:
java
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Running in anonymous class");
}
};
- 要点
- 无类名和一次性使用:匿名类没有类名,只能使用一次。
- 实现接口或继承类:匿名类可以实现接口或继承类,并重写其中的方法。
- 访问外部类成员:匿名类可以访问外部类的成员变量和方法(如果是局部匿名类,访问外部局部变量时,该变量必须是
final
或effectively final
的)。
- 应用
- 使用场景:匿名类常用于事件处理、回调函数等场景,简化代码的编写。
- Lambda 表达式替代:在 Java 8 及以后的版本中,Lambda 表达式可以替代部分匿名类的使用,使代码更加简洁。
8. 什么是 Hashtable
- 原理
Hashtable
是 Java 早期的哈希表实现,它继承自 Dictionary
类,并且是线程安全的。Hashtable
使用哈希算法来存储和查找键值对,通过键的哈希码来确定存储位置。当发生哈希冲突时,Hashtable
使用链表来处理。
- 要点
- 线程安全性:
Hashtable
是线程安全的,它的所有方法都是同步的,因此在多线程环境下可以保证数据的一致性。 null
值处理:Hashtable
的键和值都不允许为null
,如果尝试插入null
键或null
值,会抛出NullPointerException
。- 迭代器:
Hashtable
的迭代器是Enumeration
,它不支持快速失败机制。
- 应用
- 性能开销:由于
Hashtable
的所有方法都是同步的,在单线程环境下会有较大的性能开销,因此在单线程环境下建议使用HashMap
。 - 替代方案:在多线程环境下,如果需要更高的并发性能,可以使用
ConcurrentHashMap
。
9. 什么是 ConcurrentHashMap
- 原理
ConcurrentHashMap
是 Java 1.5 引入的线程安全的哈希表实现,它继承自 AbstractMap
类。ConcurrentHashMap
在多线程环境下提供了高效的并发访问,它采用了分段锁(Java 7 及以前)或 CAS(Compare-And-Swap,Java 8 及以后)和 synchronized 相结合的机制来实现线程安全。
- 要点
- 线程安全性和并发性能:
ConcurrentHashMap
是线程安全的,并且在多线程环境下具有较高的并发性能。 null
值处理:ConcurrentHashMap
的键和值都不允许为null
。- 弱一致性迭代器:
ConcurrentHashMap
的迭代器是弱一致性的,即迭代器在遍历过程中可能会反映出其他线程对映射所做的修改。
- 应用
- Java 8 优化:在 Java 8 中,
ConcurrentHashMap
进行了重大优化,放弃了分段锁机制,采用了 CAS 和 synchronized 相结合的方式,进一步提高了并发性能。 - 使用场景:
ConcurrentHashMap
适用于多线程环境下的读写操作频繁的场景,如缓存、计数器等。
10. 什么是 HashMap
- 原理
HashMap
是 Java 1.2 引入的哈希表实现,它继承自 AbstractMap
类,不是线程安全的。HashMap
使用哈希算法来存储和查找键值对,通过键的哈希码来确定存储位置。当发生哈希冲突时,HashMap
使用链表或红黑树(Java 8 及以后)来处理。
- 要点
- 非线程安全和高性能:
HashMap
不是线程安全的,在单线程环境下性能较高。 null
值处理:HashMap
的键和值都允许为null
。- 快速失败迭代器:
HashMap
的迭代器是快速失败的,即当在迭代过程中发现映射被其他线程修改时,会抛出ConcurrentModificationException
。
- 应用
- Java 8 优化:在 Java 8 中,
HashMap
引入了红黑树来处理哈希冲突,当链表长度超过一定阈值(默认为 8)时,会将链表转换为红黑树,提高了查找效率。 - 使用建议:在单线程环境下,优先使用
HashMap
;在多线程环境下,如果需要线程安全,可以使用ConcurrentHashMap
或Collections.synchronizedMap()
方法将HashMap
转换为线程安全的映射。
友情提示:本文已经整理成文档,可以到如下链接免积分下载阅读
https://download.csdn.net/download/ylfhpy/90496302