经过不断的学习
博主更新了Java进阶高级第二天的知识
欢迎大家与我一起交流学习哦
目录
3.2 Comparable和Comparator两个接口的区别
1. List集合
java.util.List
接口继承自 Collection
接口,是单列集合的一个重要分支,习惯性地会将实现了 List
接口的对象称为 List
集合。在 List
集合中允许出现重复的元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。另外, List
集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。
List
接口特点:
-
它是一个元素存取有序的集合。
-
它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素。
-
集合中可以有重复的元素,通过元素的
equals
方法,来比较是否为重复的元素。
List
常用方法:
-
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。 -
public E get(int index)
:返回集合中指定位置的元素。 -
public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。 -
public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
1.1 ArrayList集合
java.util.ArrayList
集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以 ArrayList
是最常用的集合。
1.2 LinkedList集合
java.util.LinkedList
集合数据存储的结构是链表结构。方便元素添加、删除的集合。
LinkedList
常用方法:
-
public void addFirst(E e)
:将指定元素插入此列表的开头。 -
public void addLast(E e)
:将指定元素添加到此列表的结尾。 -
public E getFirst()
:返回此列表的第一个元素。 -
public E getLast()
:返回此列表的最后一个元素。 -
public E removeFirst()
:移除并返回此列表的第一个元素。 -
public E removeLast()
:移除并返回此列表的最后一个元素。 -
public E pop()
:从此列表所表示的堆栈处弹出一个元素。 -
public void push(E e)
:将元素推入此列表所表示的堆栈。 -
public boolean isEmpty()
:如果列表不包含元素,则返回true。
1.3 List集合元素替换
代码实现: List
集合元素替换
public class ListTest {
public static void main(String[] args) {
// 创建List集合对象
List<String> list = new ArrayList<>();
// 存入数据
list.add("张三");
list.add("李四");
list.add("王五");
list.add("二麻子");
list.add("老王");
// 遍历集合,找到"老王",将其替换为"隔壁老王"
// 利用普通for循环遍历List集合
for(int i = 0;i<list.size();i++) {
// 获取当前元素
String thisName = list.get(i);
// 如果当前元素是"老王"
if("老王".equals(thisName)) {
// 将其改为"隔壁老王"
list.set(i, "隔壁老王");
}
}
System.out.println(list);
}
}
2. Set接口
java.util.Set
接口和 java.util.List
接口一样,同样继承自 Collection
接口,它与 Collection
接口中的方法基本一致,并没有对 Collection
接口进行功能上的扩充,只是比 Collection
接口更加严格了。与 List
接口不同的是, Set
接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
Set
集合有多个子类,这里我们介绍其中的 java.util.HashSet
、 java.util.LinkedHashSet
这两个集合。
2.1 HashSet集合
java.util.HashSet
是 Set
接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。 java.util.HashSet
底层的实现其实是一个 java.util.HashMap
支持。
HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于: hashCode
与 equals
方法。
2.2 HashSet集合存储结构(哈希表)
在JDK1.8之前,哈希表底层采用【数组+链表】实现,即使用链表处理冲突,同一 hash
值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即 hash
值相等的元素较多时,通过 key
值依次查找的效率较低。而JDK1.8中,哈希表存储采用【数组+链表+红黑树】实现,当链表长度超过阈值8时,将链表转换为红黑树,这样大大减少了查找时间。 如下图:
2.3 HashSet存储自定义类型元素
给 HashSet
中存放自定义类型元素时,需要重写对象中的 hashCode
和 equals
方法,建立自己的比较方式,才能保证 HashSet
集合中的对象唯一。
代码实现:重写 equals
方法和 hashCode
方法
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// 重写equals()方法
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
// 重写hashCode()方法
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
2.4 LinkedHashSet类
在 HashSet
下面有一个子类 java.util.LinkedHashSet
,它是链表和哈希表组合的一个数据存储结构。能够保证存放进去的元素有序且唯一。
代码实现:使用 LinkedHashSet
子类实例
public class LinkedHashSetTest {
public static void main(String[] args) {
// 创建LinkedHashSet
LinkedHashSet<String> Set = new LinkedHashSet<String>();
// 使用add方法添加元素到LinkedHashSet
Set.add("玩家1");
Set.add("玩家1");
Set.add("玩家2");
Set.add("玩家1");
Set.add("玩家3");
Set.add("玩家4");
// 使用迭代器获取LinkedHashSet中的元素
Iterator<String> iterator = Set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 使用增强for获取LinkedHashSet中的元素
for (String string : Set) {
System.out.println(string);
}
}
}
2.5 可变参数
在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下两种格式:
修饰符 返回值类型 方法名(参数类型... 形参名){ }
修饰符 返回值类型 方法名(参数类型[] 形参名){ }
3. Collections
java.utils.Collections
是集合工具类,用来对集合进行操作。部分方法如下:
-
public static <T> boolean addAll(Collection<T> c, T... elements)
:往集合中添加一些元素。 -
public static void shuffle(List<?> list)
:打乱集合顺序。 -
public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。 -
public static <T> void sort(List<T> list,Comparator<? super T> )
:将集合中元素按照指定规则排序。
3.1 Comparator比较器
public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。
说到排序了,简单的说就是两个对象之间比较大小,那么在JAVA中提供了两种比较实现的方式,一种是比较死板的采用 java.lang.Comparable
接口去实现,一种是灵活的当我需要做排序的时候在去选择的 java.util.Comparator
接口完成。
代码实现:使用 Comparator
接口排序字符串
public class CollectionsDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("cba");
list.add("aba");
list.add("sba");
list.add("nba");
//排序方法
Collections.sort(list);
System.out.println(list);
}
}
public int compare(String o1, String o2)
:比较其两个参数的顺序。
两个对象比较的结果有三种:大于,等于,小于。
如果要按照升序排序,则o1 小于o2,返回(负数),相等返回0,o1大于o2返回(正数)
如果要按照降序排序,则o1 小于o2,返回(正数),相等返回0,o1大于o2返回(负数)
代码实现:使用 compare
方法排序字符串
public class CollectionsDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("cba");
list.add("aba");
list.add("sba");
list.add("nba");
// 排序方法:按照单词的第一个字母降序排列
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.charAt(0) - o1.charAt(0);
}
});
System.out.println(list);
}
}
3.2 Comparable和Comparator两个接口的区别
Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo
方法被称为它的自然比较方法。只能在类中实现 compareTo()
一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过 Collections.sort
或 Arrays.sort
进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
Comparator:强行对某个对象进行整体排序。可以将 Comparator
传递给 sort
方法(如 Collections.sort
或 Arrays.sort
),从而允许在排序顺序上实现精确控制。还可以使用 Comparator
来控制某些数据结构(如有序 set
或有序映射)的顺序,或者为那些没有自然顺序的对象 collection
提供排序。
4. Map集合
现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即 java.util.Map
接口。
通过查看 Map
接口描述,发现 Map
接口下的集合与 Collection
接口下的集合,它们存在的不同如下:
-
Collection
中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。 -
Map
中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。 -
Collection
中的集合称为单列集合,Map
中的集合称为双列集合。 -
Map
中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
4.1 Map常用子类
-
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的
hashCode()
方法与equals()
方法。 -
LinkedHashMap<K,V>:
HashMap
下有个子类LinkedHashMap
,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()
方法、equals()
方法。
4.2 Map接口中的常用方法
Map
接口中定义了很多方法,常用的如下:
-
public V put(K key, V value)
: 把指定的键与指定的值添加到Map
集合中。 -
public V remove(Object key)
: 把指定的键所对应的键值对元素 在Map
集合中删除,返回被删除元素的值。 -
public V get(Object key)
:根据指定的键,在Map
集合中获取对应的值。 -
boolean containsKey(Object key)
:判断集合中是否包含指定的键。 -
public Set<K> keySet()
: 获取Map
集合中所有的键,存储到Set
集合中。 -
public Set<Map.Entry<K,V>> entrySet()
: 获取到Map
集合中所有的键值对对象的集合(Set
集合)。
4.3 Map集合遍历键找值方式
键找值方式:即通过元素中的键,获取键所对应的值。步骤如下:
-
获取
Map
中所有的键,由于键是唯一的,所以返回一个Set
集合存储所有的键。方法提示:keyset()
-
遍历键的
Set
集合,得到每一个键。 -
根据键,获取键所对应的值。方法提示:
get(K key)
代码实现: Map
集合遍历键找值方式
public class MapDemo {
public static void main(String[] args) {
// 创建Map集合对象
HashMap<String, String> map = new HashMap<String,String>();
// 添加元素到集合
map.put("1529***5693", "玩家1");
map.put("1624***9642", "玩家2");
map.put("1486***5682", "玩家3");
// 获取所有的键、键集
Set<String> keys = map.keySet();
// 遍历键集,得到每一个键
for (String key : keys) {
String value = map.get(key);
System.out.println(key+"的对应值是:"+value);
}
}
}
4.4 Entry键值对对象
Map
中存放的是两种对象,一种称为key(键),一种称为value(值),它们在 Map
中是一一对应关系,这一对对象又称做 Map
中的一个 Entry(项)
。 Entry
将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历 Map
集合时,就可以从每一个键值对 (Entry)
对象中获取对应的键与对应的值。获取对应键和对应值得方法:
-
public K getKey()
:获取Entry
对象中的键。 -
public V getValue()
:获取Entry
对象中的值。 -
public Set<Map.Entry<K,V>> entrySet()
: 获取到Map
集合中所有的键值对对象的集合(Set
集合)。
4.5 Map集合遍历键值对方式
键值对方式:即通过集合中每个键值对 (Entry)
对象,获取键值对 (Entry)
对象中的键与值。操作步骤如下:
-
获取
Map
集合中,所有的键值对(Entry)
对象,以Set
集合形式返回。方法提示:entrySet()
-
遍历包含键值对
(Entry)
对象的Set
集合,得到每一个键值对(Entry)
对象。 -
通过键值对
(Entry)
对象,获取(Entry)
对象中的键与值。 方法提示:getkey() getValue()
代码实现:获取键值对 (Entry)
对象中的键与值
public class MapDemo {
public static void main(String[] args) {
// 创建Map集合对象
HashMap<String, String> map = new HashMap<String,String>();
// 添加元素到集合
map.put("1685***5203", "玩家1");
map.put("1695***7512", "玩家2");
map.put("1575***5623", "玩家3");
// 获取所有的entry对象
Set<Entry<String,String>> entrySet = map.entrySet();
// 遍历得到每一个entry对象
for (Entry<String, String> entry : entrySet) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"的对应值是:"+value);
}
}
}
4.6 LinkedHashMap
-
当给
HashMap
中存放自定义对象时,如果自定义对象作为key
存在,这时要保证对象唯一,必须复写对象的hashCode
和equals
方法。 -
如果要保证
map
中存放的key
和取出的顺序一致,可以使用java.util.LinkedHashMap
集合来存放。
5. 异常
-
异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
5.1 异常体系
异常机制其实是帮助我们找到程序中的问题,异常的根类是 java.lang.Throwable
,其下有两个子类: java.lang.Error
与 java.lang.Exception
,平常所说的异常指 java.lang.Exception
。
Throwable体系:
-
Error:严重错误
Error
,无法通过处理的错误,只能事先避免。 -
Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。
Throwable中的常用方法:
-
public void printStackTrace()
:打印异常的详细信息。包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用
printStackTrace()
。 -
public String getMessage()
:获取发生异常的原因。提示给用户的时候,就提示错误原因。
-
public String toString()
:获取异常的类型和异常描述信息。
5.2 异常分类
-
编译时期异常:
checked
异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常) -
运行时期异常:
runtime
异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)。(如数学异常)
5.3 抛出异常throw
在java中,提供了一个 throw
关键字,它用来抛出一个指定的异常对象。那么,抛出一个异常具体如何操作呢?
-
创建一个异常对象。封装一些提示信息(信息可以自己编写)。
-
需要将这个异常对象告知给调用者。
throw
异常对象。并且throw
用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
格式如下:
throw new 异常类名(参数);
代码实现:方法体中 throw
的使用
public static int getElement(int[] arr,int index){
// 判断:索引是否越界
if(index<0 || index>arr.length-1){
// 如果条件满足,执行完throw抛出异常后,方法无法继续运算
throw new ArrayIndexOutOfBoundsException("角标越界!");
}
int element = arr[index];
return element;
}
5.4 Objects非空判断
public static <T> T requireNonNull(T obj)
:查看指定引用对象不是null。
代码实现:对为null的进行了抛出异常操作
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
5.5 声明异常throws
声明异常:将问题标识出来,报告给调用者。如果方法内通过 throw
抛出了编译时异常,而没有捕获处理,那么必须通过 throws
进行声明,让调用者去处理。关键字 throws
运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常)。
声明异常格式:
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }
5.6 throw与throws的区别
throw
关键字通常用在方法体中,并且抛出一个异常对象。程序在执行到 throw
语句时立即停止,它后面的语句都不执行。
throws
关键字通常被应用在声明方法时,用来指定可能抛出的异常。多个异常可以使用逗号隔开。当在主函数中调用该方法时,如果发生异常,就会将异常对象抛给方法调用处。
5.7 捕获异常try…catch
如果异常出现的话,会立刻终止程序,所以我们得处理异常:
-
该方法不处理,而是声明抛出,由该方法的调用者来处理
(throws)
。 -
在方法中使用
try-catch
的语句块来处理异常。
try-catch
的方式就是捕获异常。捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。
捕获异常语法如下:
try{
编写可能会出现异常的代码
}catch(异常类型 e){
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
try:该代码块中编写可能产生异常的代码。
catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。
5.8 finally 代码块
finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而 finally
就是解决这个问题的,在 finally
代码块中存放的代码都是一定会被执行的。
代码实现:实现 try-catch-finally
操作参考
public class TryCatchDem {
public static void main(String[] args) {
try {
read("a.txt");
} catch (FileNotFoundException e) {
// 抓取到的是编译期异常,抛出去的是运行期异常
throw new RuntimeException(e);
} finally {
System.out.println("不管程序怎样,finally中程序都会被执行。");
}
System.out.println("结束!");
}
public static void read(String path) throws FileNotFoundException {
if (!path.equals("a.txt")) {
// 如果不是a.txt这个文件,抛出异常
throw new FileNotFoundException("文件不存在!");
}
}
}
5.9 自定义异常
异常类如何定义:
-
自定义一个编译期异常: 自定义类 并继承于
java.lang.Exception
。 -
自定义一个运行时期的异常类:自定义类 并继承于
java.lang.RuntimeException
。
多个异常使用捕获又该如何处理呢?
-
多个异常分别处理。
-
多个异常一次捕获,多次处理。
-
多个异常一次捕获一次处理。
一般处理方式的格式如下:
try{
编写可能会出现异常的代码
}catch(异常类型A e){ // 当try中出现A类型异常,就用该catch来捕获
处理异常的代码
// 记录日志/打印异常信息/继续抛出异常
}catch(异常类型B e){ // 当try中出现B类型异常,就用该catch来捕获
处理异常的代码
// 记录日志/打印异常信息/继续抛出异常
}
-
运行时异常被抛出可以不处理。即不捕获也不声明抛出。
-
如果
finally
有return
语句,永远返回finally
中的结果,避免该情况。 -
如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
-
父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出。
欢迎关注博主,欢迎互粉,一起学习!
感谢您的阅读,不足之处欢迎指正!