第十一章:集合框架和泛型
1.集合框架
1.1认识集合
由于数组的使用过程中存在:
- 数组长度固定不变,不能很好的适应,元素数量动态变化的情况
- 可以通过
length
获取数组的长度,但却无法直接获取数组中实际存储的元素个数。 - 数组采用在内存中连续分配空间的存储方式,根据元素信息查找时效率低
这些问题,所以针对这些问题Java提出了比数组更灵活,更实用的集合框架,提高软甲开发的效率。不同集合可以适用于不同的应用场合。
1.2Collection接口
collection
接口是Java中标准库中的一个接口,用于表示一组对象的集合。它是Java集合框架中的一个根接口,继承 Iterable
接口,因此它可以用于遍历集合中的元素。
这是 Collection
中可以使用的方法
1.3 Iterable
接口
java.util.Iterable
接口是Java标准库中的一个接口,用于表示可迭代的集合类。实现了 Iterable
接口的类可以使用 for-each
循环语法来遍历其中元素,使其具有可迭代的特性。
1.4Queue接口
1.4.1Queue接口概述
Java中的 Queue
接口是一个继承自 Collection
接口的子接口,用于表示一种队列,的数据结构。
这是 Queue
接口中的可应用的方法
1.4.2 Deque
接口
java.util.Deque
接口(双端队列)是Java标准库中的一种结合接口,代表了一种具有队列和栈特性的数据结构,支持在队列的两端进行插入和删除的操作
其特性包括:
- 双向操作:可以从两端进行操作
- 队列特性:支持先进先出的队列行为
- 栈特性:支持后进先出的栈行为
- 实现类:提供实现了多个
Deque
接口的类,包括ArrayDeque
和LinkedList
1.5List接口
Collection
接口是最基本的集合接口,可以存储一组不唯一,无序的对象。 List
接口继承自 Collection
接口,是有序集合。(List
接口有点类似于数组,我们可以通过下标对集合中的元素进行访问)
1.5.1使用 ArrayList
类动态存储数据
ArrayList
集合类,对数组进行了封装,实现了长度可变的数组,而且和数组采取相同的存储方式,在内存中分配连续的空间(ArrayList
常被称为动态数组)
-
ArrayList
定义类有两个私有属性private transient Object[] elementData;
private int size;
其中:
transient
关键字是用于修饰类的成员变量。当成员变量被声明为transient时,它将不会参与对象的序列化过程。(需要注意的是,transient关键字仅适用于对象的序列化处理,而不影响对象在内存中的正常使用。) -
构造方法
ArrayList
提供了三种方式的构造器,可以构造一个默认初始容量为10的空列表,构造一个指定容量的空列表以及构造一个包含指定Collection
的元素的列表,这些元素按照该Collection
的迭代器返回它们的顺序排列的。 -
操作数据
1.5.2使用LinkedList
类动态存储数据
LinkedList
类是 List
接口的实现类,它支持实习所有 List
接口可选的列表的操作,并且允许元素值是任何类型,包括 Null
-
LinkedList
中定义的属性
transient int size = 0; transient LinkedList.Node<E> first; transient LinkedList.NOde<E> last;
-
Node
类定义
LinkedList
是通过双向链表实现的,而双向链表就是通过Node
类来实现的,Node
类中通过item
变量存储当前元素,通过next
变量指向当前节点的下一个节点,通过prev
变量指向当前节点的上一个节点。 -
构造方法
LinkedList
的无参构造是构造一个空的List
结合,含有参数的构造函数是一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序 -
操作数据
1.5.3使用 Vector
类动态存储数据
Java中Vector类是一种动态数组,它可以自动扩展和缩小数组的大小,实现了List接口,且所有的方法都是同步的(线程安全的),适用于多线程环境中操作。
Vector类的特点包括
- 动态数组:可以动态的调整数组的大小,根据需要自动扩展或者缩小数组的容量
- 线程安全:Vector类中的方法都是同步的,可以在多线程环境中使用,保证了线程安全
- 支持随机访问:Vector类支持通过索引值来访问集合中的元素,具有常数时间复杂度的随机访问特性
- 可以包含重复元素:与Set不同,Vector类可以包含重复的元素
- 可以插入null元素:与Set不同,Vector类可以插入null元素。
Vector类中常用的方法
Vector类是Java集合框架的一员,但由于其线程安全性和性能相对较低的特点,一般在在现在的Java中应用很少了。
1.5.4使用 Stack
类动态存储数据
Java中Stack类是一种基于后进先出的数据结构(LIFO),实现了栈的特点,可以用于存储和管理元素。
Stack继承于Vector类,所以这里就不在赘述
Stack的特点
- 后进先出、继承自Vector类
Stack类继承于Vector类,并且所有的方法都是同步的(线程安全的)
1.6 Set
接口
1.6.1 Set
接口的概述
Set接口是Collection接口的另外一个常用的子接口,Set接口描述的是一种比较简单的集合。集合中的对象并不按照特定顺序进行排序,并且不能保存重复对象(总结来说Set接口可以存储一组唯一的,无序的对象)
Set接口的特点:
- 元素唯一性、无序性、不允许重复元素、不允许使用索引、非同步的(非线程安全的)
Set接口常用的实现类:HashSet,TreeSet,LinkedHashSet
HashSet
:基于哈希表实现,具有O(1)的平均时间复杂度的插入,查找,删除操作TreeSet
:基于红黑树实现的,具有排序的功能,元素会按照自然顺序或者指定的比较器顺序进行排列LinkedHashSet
:基于哈希表和链表实现,保留元素的插入顺序,具有O(1)的平均时间复杂度的查找,插入,查询操作。
1.6.2使用HashSet
类动态存储数据
Java提供了一个查找效率高的集合类 HashSet
HashSet
集合的特点
- 元素唯一性、无序性、基于哈希表、非同步(非线程安全的)、允许Null元素
HashSet
为什么查找效率高
HashSet
的查找效率高主要是因为它基于哈希表(Hash Table)实现。在 HashSet
中,元素被存储在一个哈希表中。当插入元素时,HashSet
会根据元素的哈希值计算出对应的索引位置,并将元素存储在该位置上。当需要查找元素时,HashSet
会再次计算元素的哈希值,并根据哈希值找到对应的索引位置,从而直接定位到元素。这个过程的时间复杂度通常是 O(1),即常数时间复杂度。
1.6.3使用TreeSet
类动态存储数据
TreeSet
是Java集合框架中的一个实现了Set接口的有序集合,它使用红黑树数据结构来实现元素的排序和存储。TreeSet
中的元素按照自然排序或者指定的比较器进行排序,并且不允许有重复元素
使用 TreeSet
时,需要注意一下几点
-
自然排序与自定义比较器
TreeSet
可以按照元素的自然顺序进行排序,也可以通过提供的自定义比较器来制定排序规则。(如果不提供比较器,那么元素必须实现Comparable接口(比较器的实现必须与集合中的元素类型保持一致,否则可能出现Class Cast Exception的异常)) -
元素唯一性
-
Null的处理
TreeSet
不允许出现Null元素,如果尝试插入Null元素,会抛出空指针异常。
- 插入,删除,查找的性能
由于 TreeSet
是由红黑树实现的,所以在进行插入,删除,查找的操作时间复杂度都是O(log n)的
-
迭代器顺序
其迭代器将按照集合的排序顺序进行遍历,从最小元素到最大元素。
以下是 TreeSet
中常用的方法
1.6.4使用LinkedHashSet
动态存储数据
LinkedHashSet
是Java集合框架中的一种实现,它继承自 HashSet
类,同事也实现了 Set
接口。其也是基于哈希表实现的,但是其迭代顺序是按照插入的顺序进行的。
LinkedHashSet
的一些特点
- 元素唯一性、元素保留其插入顺序,允许插入Null元素,其插入,删除,查找的性能时间复杂度为O(1)、其迭代器顺序为将按照元素的插入顺序
1.7 Interator
接口
1.7.1 Interator
接口概述
Interator
接口表示对集合进行迭代的迭代器,其为集合而生,专门实现集合的遍历。其中主要实现了两个方法:一个是 hashNext()
判断是否存在下一个可以访问的元素,如果仍然有元素可以迭代,则返回True;另一个方法 next()
返回要访问的下一个元素
注意:凡是由Collection接口派生的类或者接口,都实现了 iterator()
方法,该方法返回了一个 Interator
对象
1.7.2使用 Iterator
遍历集合
ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(1);
arrayList.add(1);
arrayList.add(1);
arrayList.add(5);
Iterator<Object> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Object num = iterator.next();
System.out.println(num);
}
1.7.3在循环中删除集合元素
在Java中,使用 for-each
循环或 Iterator
迭代器遍历集合时,不能直接在循环中删除集合中的元素,因为会导致 ConcurrentModificationException
的异常或者遍历的结果不能符合预期
这是因为在使用for-each循环或者迭代器遍历的集合时,集合的结构被修改了会导致遍历过程中的迭代器失效。
所以想要修改需要使用 Iterator
的 remove()
方法。
1.8 Map
接口
1.8.1 Map接口的概述
Map接口是Java集合框架的一种用于存储键值对映射关系的接口。Map接口提供了一种通过键来访问值的方法,其中每个键都是唯一的,值可以重复。Map接口的常用实现类包括 HashMap、TreeMap、LinkedHashMap
等
Map接口的主要特点:
- 键值对的映射:Map中的元素有键和值组成,每个键都唯一,值可以重复
- 无序性:除了
HashMap,LinkedHashMap
以外,Map中的键值对没有固定的顺序,不会按照插入顺序或者其他顺序进行排列。但是,TreeMap
会按照键的自然顺序或者指定的比较器顺序进行排序。 - 允许插入Null值:Map中允许使用Null作为键和值
- 非同步:Map的实现并不是线程安全的
以下是常用的Map中的方法
1.8.2使用HashMap
动态存储数据
HashMap
是Java集合框架中的一种实现了Map接口的集合类,它用于存储键值对映射关系。
1.HashMap中声明字段
int threshold; // 阈值,当HashMap的大小超过一定阈值时,出发扩容操作
final float loadFactor; // 负载因子,判断HashMap是否需要扩容,默认值为0.75
对于为什么是 0.75 这是一个经验上的权衡值,被认为是在时间和空间上开销之间取得了较好的平衡
(1)减少了冲突
(2)减少了扩容次数
(3)空间利用率的平衡
2.构造方法
(1)无参构造:默认容量初始化为16,默认的加载因子是0.75
(2)HashMap(int initiaCapacity)
,可以对容量进行设置,加载因子也是默认值0.75
(3)HashMap(int initialCapacity,float loadFactor)
,可以指定容量和加载因子。容量不可用小于0,也不可以无限大;加载因子不能为0,也不能小于0
(4)HashMap(Map map)
创建一个新的 HashMap
,加载因子也是0.75。
3.HashMap
存储数据的原理(put的流程)
其底层是数组,称为哈希桶,JDK7中其实现由数组和链表构成,JDK8中其实现是由数组,链表,红黑树组成。
(1)根据key计算hash的值,在put的时候判断数组是否存在,如果不存在则用ressize
方法创建一个默认的长度为16的数组
(2)确定要存入的Node在数组中的位置,根据hash值与数组最大索引进行按位与运算得到索引的位置
(3)判断改为该位置是否有元素,如果没有直接创建一个Node存入。如果有元素,判断key是否相同,如果相同则进行覆盖,并且将原来的值直接返回。如果不同,在原来的Node基础上创建一个新的Node,判断该位置是链表还是红黑树。
(4)如果是链表,遍历链表,找到最后一位,将Node存入
(5)如果是红黑树,存入红黑树中。
(6)将Node存入链表后,判断链表的结构是否要调整,判断链表的长度是否超过8,如果超过要将链表转为红黑树,另外如果数组的容量小于64,不转换红黑树,而是进行数组的扩容,当数组的容量达到64的时候,再将链表转化为红黑树
(7)存储完后,再次判断数组是否需要扩容,根据负载因子来判断
1.8.3使用 TreeMap
动态存储数据
TreeMap
是java中实现了Map接口的有序键值对集合。它基于红黑树的数据结构实现,用于存储键值对,并且键按照自然顺序或者指定的比较器排序。
其特点包括:
- 有序性、基于红黑树实现、允许Null值、支持键值对的检索,插入,删除等操作、
NavigableMap
接口的实现(其中提供了一些导航方法,例如firstKey(),lastKey(),lowerKey(),higherKey()
)
注意:TreeMap
是线程不安全的,不适合在多线程环境下使用
1.8.4 使用 Hashtable
动态存储数据
Hashtable
是java中的一种哈希表数据结构,它实现了Map接口,提供了键值对的存储和检索功能
其特点包括:
- 线程安全、键值不允许为Null、解决哈希冲突、动态扩容
其中解决哈希冲突为:由于其使用哈希算法来计算键的哈希值,根据哈希值将键值对存储在数组的对应位置。当不同的键计算得到相同的哈希值时,就会产生哈希冲突。而 Hashtable
使用链地址法来解决哈希冲突问题。
1.8.5使用LinkedHashMap
动态存储数组
LinkedHashMap
是Java中的一种特殊的哈希表实现,它继承自 HashMap
,并且保持了元素的插入顺序,,即按照元素的插入顺序来维护键值对的顺序。其使用的是双向链表,以维护元素的插入顺序。
其特点包括:
- 保持插入顺序:其会根据元素的插入顺序来维护键值对的顺序,元素顺序会根据插入的顺序来输出
- 初始容量和负载因子:其可以指定初始的容量和负载因子来控制其内部数组的大小和扩容行为,与
HashMap
类似。 - 解决哈希冲突:使用的是拉链法解决哈希冲突,即将具有相同哈希值的键值对存储在同一个桶中的链表中。
其对于 HashMap
在性能上略低,因为需要额外的链表来维护插入顺序或访问顺序。但是在需要保持元素插入顺序或者访问顺序的场景下,其是一个很好的选择
1.8.6 使用Properties
动态存储数据
其是java中标准库中提供的一个用于处理属性文件的类,属性文件通常包括了一些键值对的配置信息。它继承自 Hashtable
类,并添加了一些特定属于属性文件的方法。
Properties
类的底层数据结构是基于哈希表的,其中键和值都是字符串类型。每个键值对都是唯一的,可以通过键来访问对应的值。 Properties
类还保持了键值对的插入顺序,因此可以通过插入的顺序来遍历属性文件中的配置项。
1.8.7 使用 ConcurrentHashMap
动态存储数据
ConcurrentHashMap
是Java标准库提供的一种线程安全的哈希表实现,用于在多线程环境下并发操作。它继承自 AbstractMap
类,实现了 ConcurrentMap
接口,并且在Java8之后,还实现了Map接口。
其实现了一些特定的技术来保证线程的安全性,例如:分段锁,无锁算法等。
-
分段锁:分段锁就是将数组或者集合中的元素进行分段,然后将每一段数据添加一个锁,以达到线程安全的目的
-
无锁算法:无锁算法(Lock-Free Algorithm)是一种并发编程的技术,用于实现无竞争的并发操作,而不使用传统的锁机制。它的设计目标是避免低效的锁竞争以提高并发性能。无锁算法通常使用原子操作和并发数据结构来实现。它们允许多个线程同时访问共享资源,而不需要像锁一样互斥地获取和释放锁。相反,无锁算法设计要求所有线程都能够推进并最终完成操作,即使有其他线程在同时进行。
这里就不在进行赘述。
1.9 Record
JDK16引入了Java语言的新特性之一 ——Record(记录类型)。其是一种简洁的类声明方法,用于定义数据传输对象(Data Transfer Objects 简称DTOs)或者用于表示不可变的数据对象。
public record Person(String name , int age , String address){
// 这就是一个简单的Record实现
}
注意:Record可以与其他Java特性(如接口,继承,泛型)一起使用,从而提高了灵活性,与提供了强大的功能,Records 类型是不可变动的,一旦创建后,其属性就不能被修改,从而保证了数据的不可变性
1.10 使用Stream操作集合
Stream是Java8引入的一种处理集合于数组的函数式编程工具,用于在集合上进行高效,并行的数据处理操作。
1.10.1 创建Stream流对象
一下是一个对于Stream的实现
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamExample{
public static void main (String[] args)
{
List numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Stream stream = numbers.stream;
}
}
1.10.2 Stream操作流
Stream流提供了丰富的方法链式调用,因此可以使用一种更加简洁,更加函数式的方式进行对于数据的筛选,转换,聚合
1.11 对集合元素进行排序比较
- 使用Comparable接口实现默认排序
排序是针对集合的一个常见的要求。要排序就要知道两个元素中谁大谁小。Comparable接口可以实现这个需求。
实现了该接口的类可以比较其对象的大小(包装类都实现了该接口),从而可以在集合类中进行排序和查找操作。
注意:compare To() 方法的返回值有三种情况
(1)返回负整数,表示当前对象小于传入对象(2)返回零,表示当前对象等于传入对象(3)返回正整数,表示当前对象大于传入对象
- 使用Comparator接口实现比机器的排序
Comparator接口允许在排序时间使用独立于对象类的比较逻辑,因此可以在不修改对象类的情况下实现多种不同的排序方式
comparator比较器与comparator接口的区别
- Comparator是一个接口:Comparator是Java提供的一个接口,它定义了用于比较对象的方法。你需要实现Comparator接口,并实现其中的compare()方法来定义对象之间的比较规则。
- Comparator接口定义了比较规则:Comparator接口定义了compare()方法,该方法接受两个参数并返回一个整数值。根据这个返回值,可以确定两个对象的相对顺序。如果返回值为负数,则第一个对象小于第二个对象;如果返回值为正数,则第一个对象大于第二个对象;如果返回值为零,则两个对象相等。
- Comparator提供了自定义排序的能力:通过实现Comparator接口,你可以根据自己的需求来定义对象的排序规则。这使得你可以对任何类型的对象进行排序,而无需修改对象本身的代码。你可以根据对象的任何属性进行比较,并定义升序或降序的排序规则。
1.12 Collections类
`Collections` 是 Java 中提供的一个实用工具类,它包含了一系列静态方法,用于操作和处理集合(Collection)对象。它提供了一些常见的集合操作,如排序、搜索、填充等,以及对集合的不可变或线程安全的封装。
下面列举一些 Collections
类中常用的方法:
sort(List<T> list)
:对列表进行升序排序。reverse(List<T> list)
:反转列表中的元素顺序。shuffle(List<T> list)
:随机打乱列表中的元素顺序。binarySearch(List<T> list, T key)
:在已排序的列表中使用二分查找算法查找指定元素的索引。max(Collection<? extends T> coll)
:找到集合中的最大元素。min(Collection<? extends T> coll)
:找到集合中的最小元素。unmodifiableCollection(Collection<? extends T> coll)
:返回一个不可修改的集合视图。synchronizedCollection(Collection<T> coll)
:返回一个线程安全的集合。
除了上述示例之外,Collections
类还提供了许多其他实用的方法,用于处理集合和数组,以及实现线程安全和不可变性等。
使用 Collections
类的方法可以有效地简化集合操作的代码,提供更方便和高效的集合处理功能。它为我们提供了一些常见的操作,避免了手动实现这些功能的繁琐和容易出错的过程。
值得注意的是,Collections
类是针对集合框架中的集合类型(如 List
、Set
、Map
等)提供的便利方法,而不是针对数组。如果需要对数组进行排序、搜索等操作,可以使用 Arrays
类提供的相应静态方法。
2泛型
2.1认识泛型
泛型是一种在编译时期进行类型检查和类型判断的特性,它允许在代码中使用参数化类型,从而使代码更加灵活和类型安全,泛型的目的在于保证类型转换的安全性和稳定性
2.1.1泛型的定义
类型<E> 对象 = new 类型<E>();
// 其中E表示某种类型,也可以用其他字母代替,在实际的使用过程中,需要使用明确的类型代替
// <> 是泛型的特性
2.1.2泛型的应用
泛型的优点:类型安全(泛型可以在编译时期进行类型检查,避免了在运行时期发生类型转换的问题),代码复用(泛型可以使西安通的代码处理不同类型的数据,提高了代码的复用性和灵活性),简化代码(泛型可以简化代码,避免了使用Object类型进行强制类型转换的繁琐和不安全操作)
public class GenericTest {
public static void main(String[] args) {
List<Integer> number = new ArrayList<>();
number.add(1564);
number.add(135);
number.add(1);
number.add(564);
number.add(45);
number.add(56);
System.out.println(number.size()+"++++++++++++");
for (Integer i : number) {
System.out.println(i);
}
}
}
//输出结果为
/**
6++++++++++++
1564
135
1
564
45
56 /
2.2深入泛型
泛型的应用不单单在于集合中,其在接口、类、方法等方面也可以使用。
泛型的本质就是参数化类型,参数化类型的重要性在于允许创建一些类、接口和方法,其操作的数据类型被定义为参数,可以在真正使用时指定具体的类型。
- 参数化类型:参数化类型包含一个类或者接口,以及实际的类型参数列表。
- 类型变量:是一种非限定性标识符,用来指定类、接口或者方法的类型。
2.2.1泛型接口、泛型类、泛型方法
(1)泛型类:简单的说就是具有一个或者多个类型参数的接口
(2)泛型接口:泛型接口就是拥有一个或者多个类型参数的接口。泛型接口的定义方式与定义泛型类型类似
(3)泛型方法:实际上就是带有类型参数的方法。需要注意的是,定义泛型方法与方法所在的类,或者接口是否是泛型类或者泛型接口没有直接的联系,依旧是泛型类型还是非泛型类,如果需要就可以定义泛型方法
(4)通配符:使用?通配符表示未知类型,可以在泛型类,泛型方法,泛型接口等中使用,用于灵活处理不同类型的数据。
总结:
泛型:参数化类型
泛型的类型不能是基本数据类型
泛型传递给集合后,约束了集合元素的类型
java中的泛型是编译时做类型检查,运行时强制类型转换,最后做泛型擦除
3.枚举
在Java中,枚举(Enum
)是一种特殊的数据类型,用于定义一组命名的常量。枚举常用来表示一组相关的常量,并且可以增加额外的属性和方法。在java中,枚举是一种类,可以像类一样定义成员变量和方法。
当在Java中定义枚举时,它们会以类的形式存在,并且可以具有成员变量、方法和构造函数。枚举常用于表示一组相关的常量,例如颜色、星期几等。
下面是一个表示星期几的枚举的例子:
enum DayOfWeek {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
}
在上面的例子中,DayOfWeek
枚举定义了一个表示星期几的枚举类型,它包含了七个常量。每个常量都是枚举类型的一个实例,并且以大写字母命名,多个常量之间使用逗号分隔。
枚举常量是静态常量,可以使用枚举类型的名称和常量名称进行访问,例如:
DayOfWeek monday = DayOfWeek.MONDAY;
枚举类型还可以具有成员变量和方法。例如,我们可以给 DayOfWeek
枚举添加一个 isWeekend()
方法来判断是否是周末:
enum DayOfWeek {
// 枚举常量...
public boolean isWeekend() {
return this == SATURDAY || this == SUNDAY;
}
}
在上面的例子中,isWeekend()
方法是一个实例方法,可以通过枚举类型的实例进行调用:
DayOfWeek saturday = DayOfWeek.SATURDAY;
boolean isWeekend = saturday.isWeekend(); // 返回 true
需要注意的是,枚举不能被继承,因为它们已经被隐式地继承了 java.lang.Enum
类。此外,枚举的构造函数默认是私有的,不允许在外部进行实例化。
枚举在Java中是一个非常有用的特性,它可以帮助我们以更加清晰和类型安全的方式表示一组常量,并且可以添加自定义的属性和方法来扩展其功能。
(1)Values()方法
values()
方法是 Java 枚举类型中的一个静态方法,用于获取该枚举类型中所有定义的枚举常量的数组。它返回一个包含枚举常量的数组,按照声明顺序排列。使用 values()
方法可以方便地遍历枚举常量,并对它们进行操作。
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
}
public class Main {
public static void main(String[] args) {
Day[] days = Day.values();
for (Day day : days) {
System.out.println(day);
}
}
}
(2)ordinal()方法
ordinal()
方法是 Java 枚举类型中的一个实例方法,用于获取枚举常量在枚举类型中的序号(从0开始计数)。每个枚举常量都有一个与之关联的序号,该序号表示了它在枚举类型中的声明顺序。
enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
public class Main {
public static void main(String[] args) {
Day day = Day.TUESDAY;
System.out.println(day.ordinal()); // 输出 2
}
}
(3)valueOf()
方法
valueOf()
方法是 Java 枚举类型中的一个静态方法,用于根据枚举常量的名称获取对应的枚举实例。它接受一个字符串参数,返回与指定名称相对应的枚举常量。如果指定名称不存在,则会抛出 IllegalArgumentException
异常。
enum Color {
RED, GREEN, BLUE
}
public class Main {
public static void main(String[] args) {
Color color = Color.valueOf("RED");
System.out.println(color); // 输出 RED
}
}
单例模式:
单个实例的模式
(1)构造方法需要私有化,(2)提供获取唯一实例的方法
懒汉单例:懒汉单例是指在首次使用时才创建实例。其特点是延迟加载,只有在需要使用实例时才会进行初始化。典型的懒汉单例实现方式是使用双重检查锁定(DCL)或者静态内部类。
饿汉单例:饿汉单例是指在类加载时就创建实例。其特点是立即加载,不管是否使用实例都会进行初始化。在多线程环境下,饿汉单例不需要考虑线程安全问题。
懒汉单例需要考虑线程安全问题,而饿汉单例不需要。另外,使用静态内部类实现的懒汉单例也能够保证线程安全。
枚举:实现单例模式