public static void sum(int...a){}
可变参数的作用
可变参数的注意事项:
Collections常用的API
方法名称 | 说明 |
---|---|
public static< T > boolean addAll(Collection super T>c,T…elements) | 给集合对象批量添加元素 |
public static void shuffle(List> list) | 打乱List集合元素的顺序 |
Collections排序相关API
排序方式1:
方法名称 | 说明 |
---|---|
public static void sort(List list) | 将集合中元素按照默认规则排序 |
注意:本方式不可以直接对自定义类型的List集合排序,除非自定义类型实现了比较规则Comparable接口。
排序方式2:
方法名称 | 说明 |
---|---|
public static < T > void sort(List< T > list, Comparator super T> c) | 将集合中元素按照指定规则排序 |
Map集合概述和使用
Map集合整体格式:
代码样式展示
Map<String,Integer> m=new HashMap<>();
m.put("玩具",10);
m.put("游戏机",5);
System.out.println(m); //输出格式:{玩具=10, 游戏机=5}
Map集合非常适合做类购物车这样的业务场景
Map集合体系
使用最多的Map集合是HashMap(主要掌握HashMap,LinkedHashMap,TreeMap)
Map集合体系特点
Map集合实现类特点
Map集合
Map API如下:
方法名称 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
public V get(Object key) | 根据键获取对应值 |
Set< K > keySet() | 获取全部键的集合 |
Collection< V > values() | 获取全部值的集合 |
void putAll(Map extends K, ? extends V> m) | 合并其他Map集合 |
Map集合的遍历方式有3种
方式一: 键找值
键找值涉及到的API:
方法名称 | 说明 |
---|---|
Set keySet() | 获取所有键的集合 |
V get(Object key) | 根据键获取值 |
代码展示
Map<String,Integer> m=new HashMap<>();
m.put("玩具",10);
m.put("游戏机",5);
Set<String> s= m.keySet();
for (String s1 : s) {
System.out.println(m.get(s1));
}
方式二:键值对的方式遍历,把“键值对“看成一个整体,难度较大
键值对涉及到的API:
方法名称
方法名称 | 说明 |
---|---|
Set |
获取所有键的集合 |
K getKey() | 获得键 |
V getValue() | 获取值 |
代码展示
Map<String,Integer> m=new HashMap<>();
m.put("玩具",10);
m.put("游戏机",5);
Set<Map.Entry<String,Integer>> s=m.entrySet();
for (Map.Entry<String, Integer> stringIntegerEntry : s) {
System.out.println(stringIntegerEntry.getKey()+"="+stringIntegerEntry.getValue());
}
方式三:Lambda
Map结合Lambda遍历的API
方法名称 | 说明 |
---|---|
default void forEach(BiConsumer super K,? super V> action) | 结合lambda遍历Map集合 |
代码展示
Map<String,Integer> m=new HashMap<>();
m.put("玩具",10);
m.put("游戏机",5);
m.forEach(new BiConsumer<String, Integer>() {
@Override
public void accept(String s, Integer integer) {
System.out.println(s+"="+integer);
}
});
简化
Map<String,Integer> m=new HashMap<>();
m.put("玩具",10);
m.put("游戏机",5);
m.forEach((s,integer) ->System.out.println(s+"="+integer));
HashMap的特点
实际上: Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已
例如:Set< T > s=new HashSet<>(); 该构造方法的源码如下:
public HashSet() {
map = new HashMap<>();
}
底层原理
特点:增删改查的性能都较好。
特点
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构依然是哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序
TreeMap集合概述和特点
注意: TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
TreeMap集合自定义排序规则有2种
如何使用可以看一看我前面写的Set集合中的TreeSet集合中的自定义排序方式,原理是一样的
链接: List系列集合、泛型、Set系列集合、Collection系列集合使用场景总结
什么是不可变集合?
为什么要创建不可变集合?
如何创建不可变集合?
方法名称 | 说明 |
---|---|
static < E > List< E > of(E…elements) | 创建一个具有指定元素的List集合对象 |
static < E > Set< E > of(E…elements) | 创建一个具有指定元素的Set集合对象 |
static |
创建一个具有指定元素的Map集合对象 |
注意:这个集合不能添加,不能删除,不能修改
List和Set集合使用代码如下(可以直接加入集合,或直接在里面加数据)
List<String> list=new ArrayList<>();
List lists=List.of(list);
List lists1=List.of("玩具","游戏机");
Set<String> set=new HashSet<>();
Set sets= Set.of(set);
Set sets1= Set.of("玩具","游戏机");
Map集合使用代码如下(不能直接把Map类集合加入其中,只能一个一个加数据)
Map<String,Integer> map=new HashMap<>();
Map<String,Integer> maps=Map.of("玩具",10,"游戏机",5);
什么是Stream流?
Stream流式思想的核心:
Stream流的三类方法
(1)获取Stream流
(2)中间方法
(3)终结方法
(1)Stream操作集合或者数组的第一步是先得到Stream流,然后才能使用流的功能。
集合获取Stream流的方式
方法名称 | 说明 |
---|---|
default Stream stream() | 获取当前集合对象的Stream流 |
Collection集合获取流代码如下
Collection<String> collection=new ArrayList<>();
Stream<String> s= collection.stream();
Map集合获取流代码如下
Map<String,Integer> maps=new HashMap();
//获取键流
Set<String> sets= maps.keySet();
Stream<String> s= sets.stream();
//获取值流
Collection<Integer> lists= maps.values();
Stream<Integer> s1=lists.stream();
数组获取Stream流的方式
方法名称 | 说明 |
---|---|
public static < T > Stream< T> stream(T[]array)) | 获取当数组的Stream流 |
public static< T > Stream< T > of(T…values) | 获取当前数组/可变数据的Stream流 |
代码如下
String[] arr={"玩具","游戏机","布偶"};
String[] arr1={"10","20","30"};
Stream<String> s=Arrays.stream(arr); //Arrays类中的静态方法
Stream<String> s1=Stream.of(arr1); //直接加入数组
Stream<String> s2=Stream.of("hello","ok","fuck");//加入多个元素
(2)Stream流的常用API(中间操作方法)
方法名称 | 说明 |
---|---|
①Stream< T >filter(Predicate super T> predicate) | 用于对流中的数据进行过滤 |
②Stream< T > limit(long maxSize) | 获取前几个元素 |
③Stream< T > skip(long n) | 跳过前几个元素 |
④Stream< T > distinct() | 去除流中重复的元素 |
⑤ < R > Stream< R > map(Function super T, ? extends R> mapper); | 加工方法 |
⑥static < T > Stream< T > concat(Stream a,Stream b) | 依赖(hashCode和equals方法)合并a和b两个流为一个流 |
注意:
接下来,我给大家讲讲这些方法的使用
先提前定义一个集合并获取它的流,代码如下
Collection<String> list=new ArrayList<>();
Collections.addAll(list,"张三","李四","李四","张三四","张三五");
Stream<String> s=list.stream();
①Stream< T >filter(Predicate super T> predicate) 用于对流中的数据进行过滤
过滤就是按照我们的想法挑出集合中的一些数据,可以按体重,可以按年龄,按上面的集合,我们用姓氏做例子
比如,我们想把张姓的名字挑出来
s.filter(new Predicate<String>() {
@Override
public boolean test(String s1) {
return s1.startsWith("张");
}
});
简化
s.filter( s1 -> s1.startsWith("张")); //s1中为"张三","张三四","张三五"的地址
返回值类型是 Stream ,需要的话定义一个 Stream 类型的变量去接就行了
②Stream< T > limit(long maxSize) 获取前几个元素
比如我们想得到 “张三”,“李四”,“李四” 这前三个数据
Stream<String> s1=s.limit(3); //s1中为"张三","李四","李四"的地址
③Stream< T > skip(long n) 跳过前几个元素
比如我们想跳过 “张三”,“李四”,“李四” 这前三个数据,得到 “张三四”,“张三五” 这后两个数据
Stream<String> s1=s.skip(3); //s1中为"张三四","张三五"的地址
④Stream< T > distinct() 去除流中重复的元素
就是去重复
Stream<String> s1=s.distinct(); //s1中为"张三","李四","张三四","张三五"的地址
⑤ < R > Stream< R > map(Function super T, ? extends R> mapper); 加工方法
比如说,我们想在每个元素前加一个“你好:”。代码如下
Stream<String> s=list.stream();
s.map(new Function<String, Object>() {
@Override
public Object apply(String s1) {
return "你好:"+s1;
}
});
简化
s.map(s1 -> "你好:"+ s1);
⑥static < T > Stream< T > concat(Stream a,Stream b) 依赖(hashCode和equals方法)合并a和b两个流为一个流
我们需要再创一个集合并把它变成流,代码如下
Collection<String> list=new ArrayList<>();
Collections.addAll(list,"张三","李四","李四","张三四","张三五");
Collection<Integer> list1=new ArrayList<>();
Collections.addAll(list1,10,20,30,30);
Stream<String> s=list.stream();
Stream<Integer> s1=list1.stream();
Stream<Object> s2= Stream.concat(s,s1);
//s2中为"张三","李四","张三四","张三五",10,20,30,30的地址
我们需要看一下它的源码
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
如果我们要去接这两个流的合并流,这两个要合并的流的数据类型必须是我们用于接收流的本身的数据类型或用于接收流的子类数据类型
方法名称 | 说明 |
---|---|
long count() | 统计个数 |
(3)Stream流的常见终结操作方法
方法名称 | 说明 |
---|---|
long count() | 返回此流中的元素数 |
void forEach(Consumer action) | 对此流的每个元素执行遍历操作 |
注意: 终结操作方法,调用完成后流就无法继续使用了,原因是不会返回stream了
终结和非终结方法的含义是什么?
Stream流的收集方法
方法名称 | 说明 |
---|---|
R collect(Collector collector) | 开始收集Stream流,指定收集器 |
Collectors工具类提供了具体的收集方式
方法名称 | 说明 |
---|---|
public static < T > Collector toList() | 把元素收集到List集合中 |
public static < T > Collector toSet() | 把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper , Function valueMapper) | 把元素收集到Map集合中 |
上面两种方法要结合使用
public static < T > Collector toList() 以该方法为例,使用代码如下
Collection<String> list=new ArrayList<>();
Collections.addAll(list,"张三","李四","李四","张三四","张三五");
Stream<String> s=list.stream();
List<String> lists=s.collect(Collectors.toList());
public static Collector toMap(Function keyMapper , Function valueMapper),我再说一下这个方法
代码如下
List<String> list=new ArrayList<>();
Collections.addAll(list,"玩具,5","手机,2","蛋糕,5");
Stream<String> s=list.stream();
Map map=s.collect(Collectors.toMap(new Function<String, String>() {
@Override
public String apply(String s) {
//以","分开得到数组,并返回数组索引为0位置的数
return s.split(",")[0];
}
}, new Function<String, Integer>() {
@Override
public Integer apply(String s) {
以","分开得到数组,并返回数组索引为1位置的数
return Integer.valueOf(s.split(",")[1]);
}
}));
System.out.println(map.toString());//打印map重的内容
代码简化
List<String> list=new ArrayList<>();
Collections.addAll(list,"玩具,5","手机,2","蛋糕,5");
Stream<String> s=list.stream();
Map map=s.collect(Collectors.toMap(s1->s1.split(",")[0]
,s2->Integer.valueOf(s2.split(",")[1])));
System.out.println(map.toString());
补充:
JDK16之后,把元素收集到List集合中可以直接用 default List toList() 这个方法收集,但得到的是不可变集合
代码如下:
Collection<String> list=new ArrayList<>();
Collections.addAll(list,"张三","李四","李四","张三四","张三五");
Stream<String> s=list.stream();
List<String> lists=s.toList();
Stream流操作后的结果数据转回到数组中的方法
方法名称 | 说明 |
---|---|
Object[] toArray() | Stream流操作后的结果数据转回到Object类型数组中 |
什么是异常?
为什么要学习异常?
目的: 避免异常的出现,同时处理可能出现的异常,让代码更稳健
Exception:
RuntimeException及其子类:运行时异常,编译阶段不会报错。(空指针异常,数组索引越果异常)
除RuntimeException之外所有的异常:编译时异常,编译期必须处理的,否则程序不能通过编译。(日期格式化异常)
异常分为两类:编译时异常和运行时异常
编译时异常,是在编译成class文件时必须要处理的异常,也称之为受检异常
运行时异常,在编译成class文件不需要处理,在运行字节码文件时可能出现的异常
简单来说:
运行时异常示例
int[] arr={10,20,30}; //最大索引为2
System.out.println(arr[3]); //超过了最大索引
String[] name=null;
System.out.println(name.length);
int a=10/0;
System.out.println(a);
Object a=10;
String b=(String) a;
String s="152aaa";
Integer i=Integer.valueOf(s);
运行时异常: 一般是程序员业务没有考虑好或者是编程逻辑不严谨引起的程序错误
编译时异常
编译时异常示例
SimpleDateFormat解析字符串时间成为日期对象
String date="2022月12月18天 21时35分17秒";
SimpleDateFormat sdf=new SimpleDateFormat("yyyy月MM月dd天 HH时mm分ss秒");
Date d=sdf.parse(date);
编译时异常的作用是:
编译时异常的特点
默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡!
编译时异常是编译阶段就出错的,所以必须处理,否则代码根本无法通过
编译时异常的处理形式有三种:
异常处理方式1–throws
抛出异常格式:
方法 throws 异常1 ,异常2 ,异常3 … { }
规范做法:
方法 throws Exception{ }
异常处理方式2-- try…catch…
格式:
try{
// 监视可能出现异的代码!
}catch(异常类型1 变量)
// 外理异常
}catch(异常类型2 变量)
//处理异常…
}
建议格式:
try{
// 可能出现异常的代码!
}catch (Exception e)
e.printSstackTrace();//直接打印异常栈信息
Exception可以捕获处理一切异常类型!
异常处理方式3一前两者结合
异常处理的总结
运行时异常的处理形式
自定义异常的必要?
自定义异常的好处:
自定义异常的分类
(1)自定义编译时异常
(2)自定义运行时异常
作用:提醒不强烈编译阶段不报错!!运行时才可能出现!!
具体代码请看博主chris-gardner的这篇博客
链接: Java学习之自定义异常和抛出异常
日志可以用来记录程序运行过程中的信息,并可以进行永久存储
学习日志之前记录日志的方式是由输出语句记录的,它有许多弊端
而学习了日志技术后,就可以解决以上问题
好处
输出语句和日志技术相比
输出语句 | 日志技术 | |
---|---|---|
输出位置 | 只能是控制台 | 可以将日志信息写入到文件或者数据库中 |
取消日志 | 需要修改代码,灵活性比较差 | 不需要修改代码,灵活性比较好 |
多线程 | 性能较差 | 性能较好 |
日志的规范
常见的规范是:
Logback文件获取
链接: Logback
Logback主要分为三个技术模块:
public static final Logger LOGGER= LoggerFactory.getLoggen("类对象");
④ 使用日志对象LOGGER调用其方法输出不能的日志信息
Logback日志系统的特性都是通过核心配置文件logback.xml控制的
Logback日志输出位置、格式设置:
输出到控制台的配置标志
<appender name="CONSOLE” class="ch.gos.logback.core.ConsoleAppender">
输出到系统文件的配置标志
<appender name="FILE”class="ch.qos.logback.core.rolling.RollingFileAppender">
具体在