目录
可变参数
- 可变参数用在形参中可以接收多个数据
- 可变参数的格式: 数据类型…参数名称
public static void sum(int...a){}
可变参数的作用
- 传输参数非常灵活,方便。可以不传输参数,可以传输1个或者多个,也可以传输一个数组
- 可变参数在方法内部本质上就是一个数组
可变参数的注意事项:
- 一个形参列表中可变参数只能有一个
- 可变参数必须放在形参列表的最后面
Collections集合工具类
- java.utils.Collections:是集合工具类
- 作用: Collections并不属于集合,是用来操作集合的工具类
Collections常用的API
方法名称 | 说明 |
---|---|
public static< T > boolean addAll(Collection<? super T>c,T…elements) | 给集合对象批量添加元素 |
public static void shuffle(List<?> list) | 打乱List集合元素的顺序 |
Collections排序相关API
- 使用范围:只能对于List集合的排序
排序方式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集合是一种双列集合,每个元素包含两个数据。
- Map集合的每个元素的格式: key=value(键值对元素)
- Map集合也被称为“键值对集合”
Map集合整体格式:
- Collection集合的格式:[元素1,元素2,元素3…]
- Map集合的完整格式:{key1=value1,key2=value2,key3=value3,…]
代码样式展示
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集合后面重复的键对应的值会覆盖前面重复键的值
- Map集合的键值对都可以为null。
Map集合实现类特点
- HashMap:元素按照键是无序,不重复,无索引,值不做要求。 (与Map体系一致)
- LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求
- TreeMap:元素按照键是排序,不重复,无索引的,值不做要求。
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集合的遍历方式
Map集合的遍历方式有3种
方式一: 键找值
- 先获取Map集合的全部键的Set集合。
- 遍历键的Set集合,然后通过键提取对应值。
键找值涉及到的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));
}
方式二:键值对的方式遍历,把“键值对“看成一个整体,难度较大
- 先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了。
- 遍历Set集合,然后提取键以及提取值。
键值对涉及到的API:
方法名称
方法名称 | 说明 |
---|---|
Set<Map.Entry> entrySet() | 获取所有键的集合 |
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
- 得益于JDK8开始的新技术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集合
HashMap的特点
- HashMap是Map里面的一个实现类。特点都是由键决定的:无序、不重复、无索引
- 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
- HashMap跟HashSet底层原理是一模一样的,都是哈希表结构,只是HashMap的每个元素包含两个值而已。
实际上: Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已
例如:Set< T > s=new HashSet<>(); 该构造方法的源码如下:
public HashSet() {
map = new HashMap<>();
}
底层原理
- 基于哈希表。
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键要存储的是自定义对象,需要重写hashCode和equals方法。
特点:增删改查的性能都较好。
LinkedHashMap集合
特点
- 由键决定: 有序、不重复、无索引。
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构依然是哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序
模型图
TreeMap集合
TreeMap集合概述和特点
- 由键决定特性:不重复、无索引、可排序
- 可排序: 按照键数据的大小默认升序(有小到大)排序。只能对键排序
- TreeMap跟TreeSet一样底层原理是一样的.
注意: TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
TreeMap集合自定义排序规则有2种
- 类实现Comparable接口,重写比较规则
- 集合自定义Comparator比较器对象,重写比较规则
如何使用可以看一看我前面写的Set集合中的TreeSet集合中的自定义排序方式,原理是一样的
链接: List系列集合、泛型、Set系列集合、Collection系列集合使用场景总结
Map集合实现类特点小结
- HashMap:元素按照键是无序,不重复,无索引,值不做要求,基于哈希表(与Map体系一致)
- LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求,基于哈希表
- TreeMap: 元素只能按照键排序,不重复,无索引的,值不做要求,可以做排序
不可变集合
什么是不可变集合?
- 不可变集合,就是不可被修改的集合
- 集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。否则报错
为什么要创建不可变集合?
- 如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践
- 或者当集合对象被不可信的库调用时,不可变形式是安全的。
如何创建不可变集合?
- 在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合
- List.of() 方法是jdk9以上的才行
方法名称 | 说明 |
---|---|
static < E > List< E > of(E…elements) | 创建一个具有指定元素的List集合对象 |
static < E > Set< E > of(E…elements) | 创建一个具有指定元素的Set集合对象 |
static <K,V> Map<K,V> of(E…elements) | 创建一个具有指定元素的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流?
- 在JDK8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream流概念
- 目的: 用于简化集合和数组操作的API
Stream流式思想的核心:
- ①先得到集合或者数组的Stream流(就是一根传送带)
- ②把元素放上去
- ③然后就用这个Stream流简化的API来方便的操作元素
Stream流的三类方法
(1)获取Stream流
- 创建一条流水线,并把数据放到流水线上准备进行操作
(2)中间方法
- 流水线上的操作。一次操作完毕之后,还可以继续进行其他操作
(3)终结方法
- 一个Stream流只能有一个终结方法,是流水线上的最后一个操作
Collection集合和Map集合获取流的方法
(1)Stream操作集合或者数组的第一步是先得到Stream流,然后才能使用流的功能。
集合获取Stream流的方式
- 可以使用Collection接口中的默认方法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");//加入多个元素
Stream流的常用API
(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两个流为一个流 |
注意:
- 中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程
- 在Stream流中无法直接修改集合、数组中的数据
接下来,我给大家讲讲这些方法的使用
先提前定义一个集合并获取它的流,代码如下
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流的收集操作
- 收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去
- 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类型数组中 |
异常
什么是异常?
- 异常是程序在“编译”或者“执行”的过程中可能出现的问题
- 注意:语法错误不算
- 在异常体系中比如:数组索引越界、空指针异常、日期格式化异常,等…
为什么要学习异常?
- 异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止
- 研究异常并且避免异常,然后提前处理异常,体现的是程序的安全,健壮性
目的: 避免异常的出现,同时处理可能出现的异常,让代码更稳健
异常体系
Error:
- 系统级别问题、JVM退出等,代码无法控制
Exception:java.lang包下,称为异常类,它表示程序本身可以处理的问题
Exception:
-
RuntimeException及其子类:运行时异常,编译阶段不会报错。(空指针异常,数组索引越果异常)
-
除RuntimeException之外所有的异常:编译时异常,编译期必须处理的,否则程序不能通过编译。(日期格式化异常)
异常分为两类:编译时异常和运行时异常
-
编译时异常,是在编译成class文件时必须要处理的异常,也称之为受检异常
-
运行时异常,在编译成class文件不需要处理,在运行字节码文件时可能出现的异常
如下图
简单来说:
- 编译时异常就是在编译的时候出现的异常
- 运行时异常就是在运行时出现的异常
运行时异常
- 直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。
运行时异常示例
- 数组索引越界异常:ArraylndexOutOfBoundsException
代码案例: 数组查询索引超过数组的长度-1
int[] arr={10,20,30}; //最大索引为2
System.out.println(arr[3]); //超过了最大索引
- 空指针异常:NullPointerException,直接输出没有问题,但是调用空指针的变量的功能就会报错
代码案例: 查询为开辟空间(null)的数组的长度
String[] name=null;
System.out.println(name.length);
- 数学操作异常: ArithmeticException
代码案例: 数学上分母不能为0
int a=10/0;
System.out.println(a);
- 类型转换异常: ClassCastException
代码案例: 多态中强装时与被转真实数据不一致(以前遇到过)
Object a=10;
String b=(String) a;
- 数字转换异常: NumberFormatException
代码案例: 含有字母的字符串转为数字时(以前遇到过)
String s="152aaa";
Integer i=Integer.valueOf(s);
运行时异常: 一般是程序员业务没有考虑好或者是编程逻辑不严谨引起的程序错误
编译时异常
编译时异常
- 不是RuntimeException或者其子类的异常,编译阶就报错,必须处理,否则代码不通过。
编译时异常示例
SimpleDateFormat解析字符串时间成为日期对象
String date="2022月12月18天 21时35分17秒";
SimpleDateFormat sdf=new SimpleDateFormat("yyyy月MM月dd天 HH时mm分ss秒");
Date d=sdf.parse(date);
编译时异常的作用是:
- 担心程序员的技术不行,在编译阶段就爆出一个错误, 目的在于提醒!提醒程序员这里很可能出错,请检查并注意不要出bug
- 编译时异常是可遇不可求
编译时异常的特点
- 编译时异常:继承自Exception的异常或者其子类
- 编译阶段报错,必须处理,否则代码不通过。
默认异常处理机制
- ①默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException
- ②异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机。
- ③虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据
- ④直接从当前执行的异常点干当前程序
- ⑥后续代码没有机会执行了,因为程序已经死亡
默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡!
编译时异常的处理形式
编译时异常是编译阶段就出错的,所以必须处理,否则代码根本无法通过
编译时异常的处理形式有三种:
- ①出现异常直接抛出去给调用者,调用者也继续抛出去
- ②出现异常自己捕获处理,不麻烦别人
- ③前两者结合,出现异常直接抛出去给调用者,调用者捕获处理
异常处理方式1–throws
- throws: 用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理
- 这种方式并不好,发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起程序死亡
抛出异常格式:
方法 throws 异常1 ,异常2 ,异常3 … { }
规范做法:
方法 throws Exception{ }
异常处理方式2-- try…catch…
- 监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理
- 这种方式还可以,发生异常的方法自己独立完成异常的处理,程序可以继续往下执行
格式:
try{
// 监视可能出现异的代码!
}catch(异常类型1 变量)
// 外理异常
}catch(异常类型2 变量)
//处理异常…
}
建议格式:
try{
// 可能出现异常的代码!
}catch (Exception e)
e.printSstackTrace();//直接打印异常栈信息
Exception可以捕获处理一切异常类型!
异常处理方式3一前两者结合
- 方法直接将异常通过throws抛出去给调用者
- 调用者收到异常后直接捕获处理
异常处理的总结
- 在开发中按照规范来说第三种方式是最好的:底层的异常抛出去给最外层,最外层集中捕获处理
- 实际应用中,只要代码能够编译通过,并且功能能完成,那么每一种异常处理方式似乎也都是可以的
运行时异常的处理形式
运行时异常的处理形式
- 运行时异常编译阶段不会出错,是运行时才可能出错的,所以编译阶段不处理也可以
- 按照规范建议还是处理:建议在最外层调用处集中捕获处理即可
自定义异常
自定义异常的必要?
- Java无法为这个世界上全部的问题提供异常类
- 如果企业想通过异常的方式来管理自己的某个业务问题,就需要自定义异常类了
自定义异常的好处:
- 可以使用异常的机制管理业务问题,如提醒程序员注意
- 同时一旦出现bug,可以用异常的形式清晰的指出出错的地方
自定义异常的分类
(1)自定义编译时异常
- 定义一个异常类继承Exception
- 重写构造器
- 在出现异常的地方用throw new 自定义对象抛出作用:编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理! !
(2)自定义运行时异常
- 定义一个异常类继承RuntimeException
- 重写构造器
- 在出现异常的地方用throw new 自定义对象抛出!
作用:提醒不强烈编译阶段不报错!!运行时才可能出现!!
具体代码请看博主chris-gardner的这篇博客
链接: Java学习之自定义异常和抛出异常
日志
日志可以用来记录程序运行过程中的信息,并可以进行永久存储
学习日志之前记录日志的方式是由输出语句记录的,它有许多弊端
- 信息只能展示在控制台
- 不能将其记录到其他的位置(文件,数据库)
- 想取消记录的信息需要修改代码才可以完成
- …
而学习了日志技术后,就可以解决以上问题
好处
- 可以将系统执行的信息选择性的记录到指定的位置(控制台、文件中、数据库中)
- 可以随时以开关的形式控制是否记录日志,无需修改源代码
输出语句和日志技术相比
输出语句 | 日志技术 | |
---|---|---|
输出位置 | 只能是控制台 | 可以将日志信息写入到文件或者数据库中 |
取消日志 | 需要修改代码,灵活性比较差 | 不需要修改代码,灵活性比较好 |
多线程 | 性能较差 | 性能较好 |
日志的体系结构
- 日志规范:一些接口,提供给日志的实现框架设计的标准
- 日志框架:牛人或者第三方公司已经做好的日志记录实现代码,后来者直接可以拿去使用。
- 因为对CommonsLogging的接口不满意,有人就搞了SLF4J。因为对Log4j的性能不满意,有人就搞了Logback
日志的规范
- 日志规范大多是一些接口,提供给实现框架去设计的
常见的规范是:
- Commons Logging
- Simple Logging Facade for java
Logback日志框架
Logback文件获取
链接: Logback
- Logback是由log4j创始人设计的另一个开源日志组件,性能比log4j要好
- 官方网站: https://logback.qos.ch/index.html
- Logback是基于slf4j的日志规范实现的框架。
Logback主要分为三个技术模块:
- logback-core: logback-core 模块为其他两个模块奠定了基础,必须有
- logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j APl。
- logback-access 模块与Tomcat和Jetty等 Servlet 容器集成,以提供 HTTP 访问日志功能
使用Logback日志技术
- 导入Logback日志技术到项目中,用于纪录系统的日志信息
- ①在项目下新建文件夹lib,导入Logback的相关jar包到该文件夹下,并添加到项目依赖库中去
- ②将Logback的核心配置文件logback.xml直接拷贝到src目录下(必须是src下)
- ③在代码中获取日志的对象
public static final Logger LOGGER= LoggerFactory.getLoggen("类对象");
④ 使用日志对象LOGGER调用其方法输出不能的日志信息
Logback日志系统的特性都是通过核心配置文件logback.xml控制的
Logback日志输出位置、格式设置:
- 通过logback.xml中的标签可以设置输出位置和日志信息的详细格式
- 通常可以设置2个日志输出位置: 一个是控制台、一个是系统文件中
输出到控制台的配置标志
<appender name="CONSOLE” class="ch.gos.logback.core.ConsoleAppender">
输出到系统文件的配置标志
<appender name="FILE”class="ch.qos.logback.core.rolling.RollingFileAppender">
日志级别
- 级别程度依次是:TRACE< DEBUG<INFO<WARN<ERROR;默认级别是debug (忽略大小写),对应其方法
- 作用: 用于控制系统中哪些日志级别是可以输出的,只输出级别不低于设定级别的日志信息
- ALL和 OFF分别是打开全部日志信息,及关闭全部日志信息
具体在<rootlevel=“INFO”>标签的level属性中设置日志级别