个人主页:Hello Code.
本文专栏:Java零基础指南
更多相关内容请点击前往 Java零基础指南 查看
如有问题,欢迎指正,一起学习~~
文章目录
类加载器&反射
类加载器
概述:负责将.class文件(存储的物理文件)加载到内存中
加载时机
- 创建类的实例(对象)
- 调用类的类方法(静态方法)
- 访问类或者接口的类变量,或者为该类变量赋值(静态变量)
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
用到就加载,不用不加载
加载过程:加载—验证—准备—解析—初始化
又将验证—准备—解析这三步称为链接
- 加载
通过一个类的全限定名来获取定义此类的二进制字节流(包名+类名;用流进行传输)
将这个字节流所代表的静态存储结构转化为运行时数据结构(将这个类加载到内存中)
在内存中生成一个代表这个类的java.lang.Class对象,任何类被使用时,系统都会为之建立一个java.lang.Class对象(加载完毕创建一个class对象)
- 链接
- 验证:确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全(文件中的信息是否符合虚拟机规范、有没有安全隐患)
- 准备:负责为类的类变量(静态变量)分配内存,并设置默认初始化值(初始化静态变量)
- 解析:将类的二进制数据流中的符号引用替换为直接引用(本类中用到了其他类,此时就要找到对应的类)
- 初始化:根据程序员通过程序制定的主观计划去初始化类变量和其他资源(静态变量赋值以及初始化其他资源)
分类
- 启动类加载器(Bootstrap ClassLoader):虚拟机内置的类加载器
- 平台类加载器(Platform Classloader):负责加载JDK中一些特殊的模块(继承启动类加载器)
- 系统类加载器(System Classloader):负责加载用户类路径上所指定的类库(继承平台类加载器)(用的最多)
- 自定义类加载器(UserClassloader):自定义的类加载器(继承系统类加载器)(用的不多)
双亲委派模型
- 当使用最下层的类加载器加载字节码文件时,首先会把加载任务逐层委派给父类加载器,直到最顶层的启动类加载器中
- 这些加载器都有各自的加载范围,当父类加载器无法完成加载请求时,就会一层层往下返回
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
:可以获得系统类加载器
systemClassLoader.getParent()
:得到父类加载器
常用方法
方法名 | 说明 |
---|---|
loadClass(String name, boolean resolve) | 加载指定名称(包括包名)的二进制类型 |
findClass(String name) | 当loadClass方法中父类加载失败时,调用自己的findClass方法来完成类加载 |
defineClass(byte[] b, int off, int len) | 将byte字节流解析成JVM能够识别的Class对象 |
resolveClass(Class<?> c) | 让使用类的Class对象创建完成也同时被解析 |
反射
Java反射机制
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法
对于任意一个对象,都能够调用它的任意属性和方法
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制
利用反射调用它类中的属性和方法时,无视修饰符。反射操作,程序比较灵活,动态的
先获取配置文件中的信息,动态获取信息并创建对象和调用方法
获取Class对象
以前调用一个类中的方法 | 反射调用一个类中的方法 |
---|---|
创建这个类的对象(new) | 反射方式:创建对象(利用Class对象) |
用对象调用方法 | 反射方式:调用方法 |
- 源代码阶段:
Class.forName(String className)
通过Class类静态方法传递全类名获取Class对象 - Class对象阶段:
类名.class
- Runtime运行时阶段:
对象.getClass()
获取Constructor对象
方法名 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 返回所有公共构造方法对象的数组 |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造方法对象的数组(包括私有) |
Constructor getConstructor(Class<?>… parameterTypes) | 返回单个公共构造方法对象 |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 返回单个构造方法对象(包括私有) |
利用Constructor创建对象
T newInstance(Object...initargs)
:根据指定的构造方法创建对象
package fanshe;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
// 获取字节码文件
Class clazz = Class.forName("fanshe.Student");
// 获取构造方法
Constructor constructor = clazz.getConstructor(String.class, int.class);
// 利用newInstance 创建对象
Student student = (Student) constructor.newInstance("张三", 11);
System.out.println(student);
}
}
在Class类中,有一个newInstance 方法,可以利用空参直接创建一个对象
Class clazz = Class.forName("fanshe.Student");
Student stu = (Student)clazz.newInstance();
不过这个方法现在已经过时了,了解即可
// 获取一个私有的构造方法并创建对象
package fanshe;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
// 获取字节码文件
Class clazz = Class.forName("fanshe.Student");
// 获取构造方法
Constructor constructor = clazz.getConstructor(String.class, int.class);
// 被private修饰的成员,不能直接使用
// 如果用反射强行获取并使用,需要临时取消访问检查
constructor.setAccessible(true);
// 利用newInstance 创建对象
Student student = (Student) constructor.newInstance("张三", 11);
System.out.println(student);
}
}
在获取到的构造方法创建对象时,如果是public,可以直接创建对象
如果是非public的,需要临时取消检查,然后再创建对象
setAccessible(boolean)
(暴力反射)
反射获取成员变量
- 步骤
- 获得class对象
- 获得Field对象
方法名 | 说明 |
---|---|
Field[] getFields() | 返回所有公共成员变量对象的数组 |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组(包括私有) |
Field[] getField(String name) | 返回单个公共成员变量对象 |
Field[] getDeclaredField(String name) | 返回单个公共成员变量对象(包括私有) |
赋值或者获取值
void set(Object obj, Object value)
:给指定对象的成员变量赋值
Object get(Object obj)
:返回指定对象的Field的值
代码实现
package fanshe;
import java.lang.reflect.Field;
public class Demo2 {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 获取class对象
Class clazz = Class.forName("fanshe.Student");
// 获取Field对象
Field field = clazz.getDeclaredField("name");
// 赋值
// 创建一个对象
Student stu = (Student) clazz.newInstance();
// 关闭访问检查(私有变量)
field.setAccessible(true);
// 赋值
field.set(stu, "张三");
System.out.println(stu);
}
}
获取Method对象
- 步骤
- 获取class对象
- 获取Method对象
方法名 | 说明 |
---|---|
Method[] getMethods() | 返回所有公共成员方法对象的数组,包括继承的 |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,不包括继承的,包括私有的 |
Method[] getMethod(String name, Class<?>…parameterTypes) | 返回单个公共成员方法对象 |
Method[] getDeclaredMethod(String name, Class<?>…parameterTypes) | 返回单个成员方法对象(包括私有) |
运行方法
Object invoke(Object obj, Object...args)
:运行方法
参数一:表示用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(没有就不写)
代码演示
package fanshe;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo3 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 获取class对象
Class clazz = Class.forName("fanshe.Student");
// 获取Method对象
Method method = clazz.getDeclaredMethod("function", String.class, int.class);
// 创建对象
Student stu = (Student) clazz.newInstance();
// 运行
method.setAccessible(true);
method.invoke(stu, "张三", 23);
}
}
xml
概述
- 由万维网联盟(W3C)推出
是Web领域最具权威和影响力的国际中立性技术标准机构
- 可扩展的标记语言XML(标准通用标记语言下的一个子集)
标记语言:通过标签来描述数据的一门语言(标签也称为元素)
可扩展:标签的名字是可以自己定义的
- 作用
- 用于进行存储数据和传输数据
- 作为软件的配置文件
规则
标签规则
- 标签由一对尖括号和合法的标识符组成
- 标签必须成对出现
- 特殊标签可以不成对,但是必须要有结束标记
- 标签中可以定义属性,属性和标签名空格隔开;属性值必须用引号引起来
- 标签需要正确的嵌套
语法规则
- 文件后缀名为:xml
- 文档声明必须是第一行第一列
<?xml version = "1.0" encoding = "UTF-8" standalone = "yes"?>
version:该属性是必须存在的
encoding:该属性不是必须的,声明打开当前xml文件的时候应该使用什么字符编码表
standalone:该属性不是必须的,描述XML文件是否依赖其他的xml文件,取值为yes/no
- 必须存在一个根标签,有且仅有一个
- XML文件中可以定义注释信息
- XML文件中可以存在特殊字符(< >…)
- XML文件中可以存在CDATA区
<![CDATA[...内容...]]>
演示
<?xml version="1.0"encoding="UTF-8"?>
<!--根标签-->
<students>
<!--第一个学生信息-->
<student id="1">
<name>张三</name>
<age>23</age>
<info>学生<>信息</info>
<!--CDATA区内的内容都会原样输出,大于号和小于号等不用使用特殊字符转义-->
<message><![CDATA[内<>容]]></message>
</student>
<!--第二个学生信息-->
<student id="2">
<name>李四</name>
<age>24</age>
<info></info>
</student>
</students>
解析
解析就是从xml中获取到数据
DOM(Document Object Model)文档对象模型:就是把文档的各个组成部分看做成对应的对象
会把xml文件全部加载到内存。在内存中形成一个树形结构,再获取到对应的值
常见的解析工具
- JAXP:SUN公司提供的一套XML的解析的API
- JDOM:开源组织提供了一套XML的解析的API-jdom
- DOM4J:开源组织提供了一套XML的解析的API-dom4j(全称:Dom For Java)
- pull:主要应用在Android手机端解析XML
DOM4J
下载地址:https://dom4j.github.io/
常用方法
方法名 | 说明 |
---|---|
SAXReader saxReader = new SAXReader(); | 获取一个解析器对象 |
Document document = saxReader.read(new File(“xml文件路径”)) | 利用解析器把xml文件加载到内存中,并返回一个文档对象 |
Element rootElement = document.getRootElement() | 获取到根标签 |
List list = rootElement.elements() | 获取调用者所有的子标签,并加入到集合,最终返回这个集合 |
List list = rootElement.elements(“标签名”) | 获取调用者所有的指定的子标签,会把这些子标签放到一个集合中并返回 |
Attribute attribute = element.attribute(“属性名”) | 获取标签对应的属性 |
String id = attribute.getValue() | 获取对应属性的值 |
Element nameElement = element.element(“标签名”); | 获取调用者指定的子标签 |
String name = nameElement.getText() | 获取这个标签的标签体内容 |
代码
package MyXml;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class Demo {
public static void main(String[] args) throws DocumentException {
// 获取一个解析器对象
SAXReader saxReader = new SAXReader();
// 利用解析器把xml文件加载到内存中
Document document = saxReader.read(new File("src\\MyXml\\Students.xml"));
// 获取到根标签
Element rootElement = document.getRootElement();
// 获取子标签
List<Element> list = rootElement.elements("student");
// 遍历获取每个student信息
ArrayList<Student> s = new ArrayList();
for (Element element : list) {
// 获取属性
Attribute attribute = element.attribute("id");
String id = attribute.getText();
// 获取name
Element nameElement = element.element("name");
String name = nameElement.getText();
// 获取age
Element ageElement = element.element("age");
int age = Integer.parseInt(ageElement.getText());
Student stu = new Student(id, name, age);
s.add(stu);
}
System.out.println(s);
}
}
DTD&schema
约束
用来限定xml文件中可使用的标签以及属性
分类
- DTD
- schema
DTD约束
步骤
- 创建一个文件,这个文件的后缀名为.dtd
- 看xml文件中使用了哪些元素 <!ELEMENT>可以定义元素:`<!ELEMENT persons>`
- 判断元素是简单元素还是复杂元素
简单元素:没有子元素
<!ELEMENT name (#PCDATA)>
复杂元素:有子元素的元素<!ELEMENT person (name,age)>
- 引入DTD
引入DTD约束的三种方法
- 引入本地dtd:
<!DOCTYPE 根标签名 SYSTEM 'DTD文件路径'>
- 在xml文件内部引入:
<!DOCTYPE 根标签名 [DTD文件内容]>
- 引入网络dtd:
<!DOCTYPE 根标签名 PUBLIC "dtd文件名称" "dtd文档的URL">
DTD语法规则
- 定义一个元素:
<!ELEMENT 元素名 元素类型>
- 元素类型
简单元素:
EMPTY:表示标签体为空ANY:表示标签体可以为空也可以不为空
PCDATA:表示该标签的内容部分为字符串
复杂元素:
直接写子元素名称
多个子元素可以用","或者’|'隔开;
","表示定义子元素的顺序
"|“表示子元素只能出现任意一个
(?表示零次或多次;”+“表示一次或多次;”*"表示零次或多次;如果不写默认表示出现一次)
- 定义一个属性:
<!ATTLIST 元素名称 属性名称 属性类型 属性约束>
属性类型:CDATA类型(普通的字符串)
属性约束
#REQUIRED:必需的
#IMPLIED:属性不是必需的
#FIXED value:属性值是固定的
schema约束
schema和DTD的区别
- schema约束文件也是一个xml文件,符合xml语法,后缀名为.xsd
- 一个xml中可以引用多个schema约束文件,多个schema使用名称空间区分(名称空间类似于java包名)
- dtd里面元素类型的取值比较单一常见的是PCDATA类型,但是在schema里面可以支持很多个数据类型
- schema语法更加复杂
schame文件用来约束一个xml文件,因为其自身也是一个xml文件,所以同时也被别的文件约束着
步骤
- 创建一个文件,这个文件后缀名为.xsd
- 定义文档声明
- schema文件的根标签为:
<schema>
- 在中定义属性:
xmlns=http://www.w3.org/2001/XMLSchema
(声明这个schema文件是一个约束文件,同时也被约束) - 在中定义属性:
targetNamespace=唯一的url地址
(指定当前这个schema文件的名称空间) - 在中定义属性:
elementFormDefault="qualified"
(表示当前schema文件是一个质量良好的文件) - 通过element定义元素
- 判断当前元素是简单元素还是复杂元素
代码实现
<? xml version="1.0"encoding="UTF-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.baidu.com/javase"
elementFormDefault="qualified"
>
<!--定义persons复杂元素-->
<element name="persons">
<complexType><!--复杂元素-->
<sequence><!--里面元素必须按顺序-->
<!--定义person复杂元素-->
<element name="person">
<complexType>
<sequence>
<!--定义name和age简单元素-->
<element name="name"type="string"></element>
<element name="age"type="int"></element>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
</element>
</schema>
引入schema文件约束
- 在根标签上定义属性
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
(表示当前文件被别人约束)
- 通过xmlns引入约束文件的名称空间
xmlns = "约束文件的名称空间"
- 给某一个xmlns属性添加一个标识,用于区分不同的名称空间
格式为
xmlns:标识 = "名称空间地址"
标识是可以任意的,但是一般取值都是xsi
- 通过
xsi:schemaLocation
指定名称空间所对应的约束文件路径
格式为:
xsi:schemaLocation = "名称空间url 文件路径"
Schema定义属性
<attribute name="id" type="string" use="required"></attribute>
use中两个选项:requried(必需的)、optional(可选的)
枚举&注解
- 用常量表示季节的弊端
代码不够简洁
不同类型的数据是通过名字区分的
不安全,由于是数字类型,就有可能使用这几个值做一些运算操作;
为了间接的表示一些固定的值,Java提供了枚举
枚举格式
枚举:是指将变量的值一一列出来,变量值只限于列举出来的值的范围内
格式
public enum s{
枚举项1,枚举项2,枚举项3;
}
// 定义一个枚举类,用来表示春、夏、秋、冬
public enum Season{
SPRING,SUMMER,AUTUMN,WINTER;
}
枚举特点
- 所有枚举类都是Enum的子类
- 可以通过"枚举类名.枚举项名称"去访问指定的枚举项
- 每一个枚举项其实就是该枚举的一个对象
- 枚举也是一个类,也可以去定义成员变量
- 枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略
- 枚举类可以有构造器,但必须是private的,它默认的也是private的。
- 枚举类也可以有抽象方法,但是枚举项必须重写该方法
public enum Season{
SPRING("春"){
// 枚举类中有抽象方法,必须重写
public void show(){
System.out.println(this.name);
}
},
SUMMER("夏"){
public void show(){
System.out.println(this.name);
}
},
AUTUMN("秋"){
public void show(){
System.out.println(this.name);
}
},
WINTER("冬"){
public void show(){
System.out.println(this.name);
}
};
// 成员变量
public String name;
// 有参构造
private Season(String name){
this.name = name;
}
// 抽象方法
public abstract void show();
}
枚举的方法
方法名 | 说明 |
---|---|
String name() | 获取枚举项的名称 |
int ordinal() | 返回枚举项在枚举类中的索引值 |
int compareTo(E o) | 比较两个枚举项,返回的是索引值的差值 |
String toString() | 返回枚举常量的名称 |
static T valueOf(Class type, String name) | 获取指定枚举类中的指定名称的枚举值 |
values() | 获得所有的枚举项 |
注解
主要作用:对程序进行标注和解释
注解名 | 说明 |
---|---|
@Override | 概述子类重写父类的方法 |
@Deprecated | 概述方法过时 |
@SuppressWarnings | 压制警告 |
@Retention(value = RetentionPolicy.RUNTIME) | 表示这个注解的存活时间 |
注释和注解
- 注释:给程序员看
- 注解:给编译器看(让虚拟机看到程序中的注解,注解代表程序的一些特殊功能)
自定义注解
public @interface 注解名称{
public 属性类型 属性名 () default 默认值; //这里只能是public,可省略
}
属性类型可以为基本数据类型、String、Class、注解、枚举以及以上类型的一维数组
在使用注解的时候如果注解里面的属性没有指定默认值,那么就需要手动给出注解属性的设置值
特殊属性value:在使用注解时如果只给value赋值,那么可以直接设置
isAnnotationPresent(Class<? extends Annotation> annotationClass)
判断当前方法上是否有指定的注解
参数:注解的字节码文件对象
返回值:布尔结果 true存在;false不存在
元注解
概述:就是描述注解的注解
元注解名 | 说明 |
---|---|
@Target | 指定了注解能在哪里使用 |
@Retention | 可以理解为保留时间(生命周期) |
@Inherited | 表示修饰的自定义注解可以被子类继承 |
@Documented | 表示该自定义注解,会出现在API文档里面 |
@Target
:成员变量(ElementType.FIELD),类(ElementType.TYPE),成员方法(ElementType.METHOD)
@Retention
:不写默认只能存活在java文件(不能存活在class文件中),括号写上RetentionPolicy.RUNTIME
就可以存活到字节码中
单元测试&日志技术
Junit概述:Junit是一个Java编程语言的单元测试工具。Junit是一个非常重要的测试工具
Junit特点
- 是一个开放源代码的测试工具
- 提供注解来识别测试方法
- 可以让编写代码更快,并提高质量
- 优雅简洁、简单
- 在一个条中显示进度,如果运行良好是绿色;运行失败为红色
基本使用
- 将junit的jar包导入到工程中
- 编写测试方法,该测试方法必须是公共的无参数无返回值的非静态方法
- 在测试方法上使用@Test注解标注该方法是一个测试方法
- 选中测试方法右键通过junit运行该方法
常用注解
注解 | 含义 |
---|---|
@Test | 表示测试该方法 |
@Before | 在测试的方法前运行 |
@After | 在测试的方法后运行 |
日志技术
日志:程序中的日志可以用来记录程序在运行时的点点滴滴,并可以进行永久存储
区别
输出语句 | 日志技术 | |
---|---|---|
取消日志 | 需要修改代码,灵活性比较差 | 不需要修改代码,灵活性比较好 |
输出位置 | 只能是控制台 | 可以将日志信息写入到文件或者数据库中 |
多线程 | 和业务代码处于一个线程中 | 多线程方式记录日志,不影响业务代码的性能 |
体系结构
Log4j
是Apache的一个开源项目,通过Log4j,可以控制日志信息输送的=目的地是控制台、文件等位置
也可以控制每一条日志输出格式
通过定义每一条日志信息的级别,能够更细致的控制日志的生成过程
这些可以通过一个配置文件来灵活的进行配置,不需要修改应用的代码
Log4j开发流程
- 导入log4j的相关jar包
- 编写log4j配置文件(log4j.properties/log4j.xml)
- 在代码中获取日志的对象
log4j自己的api(不推荐使用)
弊端:如果以后要更换日志的实现类,那么下面的代码就要跟着改
private static final Logger LOGGER = Logger.getLogger(.class字节码文件);
使用slf4j里面的api来获取日志的对象
好处:如果更换日志的实现类,下面的代码不需要更改
private static final Logger LOGGER = LoggerFactory.getLogger(clss字节码文件)
按照级别设置记录日志信息
Log4j组成
- Loggers(记录器):日志的级别
常用级别:
DEBUG:打印基本信息
INFO:打印重要信息
WARN:打印可能出现问题的信息
ERROR:出现错误的信息,不影响程序运行
FATAL:重大错误,程序可以停止
DEBUG<INFO<WARN<ERROE<FATAL
Log4j有一个规则:只输出级别不低于设定级别的日志文件
- Appenders(输出源):日志要输出的地方,如控制台(Console)、文件(Files)等。
org.apache.log4j.ConsoleAppender
(控制台)
org.apache.log4j.FileAppender
(文件)
log4j.appender.ca = org.apache.log4j.ConsoleAppender
log4j.appender.ca.设置1 = 值1
log4j.appender.ca.设置2 = 值2
...........
- Layouts(布局):日志输出的格式
常用布局管理器:
org.apache.log4j.PatternLayout
(可以灵活的指定布局模式)【最常用】
org.apache.log4j.SimpleLayout
(包含日志信息的级别和信息字符串)
org.apache.log4j.TTCCLayout
(包含日志产生的时间、线程、类别等信息)
log4j.appender.ca.layout = org.apache.log4j.PatternLayou
log4j.appender.ca.layout.设置1 = 值1
log4j.appender.ca.layout.设置2 = 值2
...........
配置文件详解
- 配置根Logger
格式:
log4j.rootLogger = 日志级别, appenderName1, appenderName2, ...
日志级别:OFF FATAL ERROR WARN INFO DEBUG ALL或者自定义的级别
appenderName1:就是指定日志信息要输出到哪里。可以同时指定多个输出目的地,用逗号隔开
例如:log4j.rootLogger = INFO, ca, fa
- 配置文件
ConsoleAppender常用的选项
ImmediateFlush = true 表示所有消息都会被立即输出,设置为false则不输出,默认为true
Target = System.err 默认值是System.out
FileAppender常用的选项
ImmediateFlush = true 表示所有消息都会立即被输出。设为false则不输出,默认值是true
Append = false true表示将消息添加到指定文件中,原来的消息不覆盖
false则将消息覆盖指定的文件内容,默认值为true
File = D:/logs/logging.log4j 指定消息输出到logging.log4j文件中
- 配置Layout
PatternLayout常用的选项
ConversionPattern=%m%n 设定以怎样的格式显示消息
【格式化符号说明可以查看网上的资料做一个了解】
Log4j应用
- 导入相关的依赖(jar)
- 将资料中的properties配置文件复制到src目录下
- 在代码中获取日志的对象
- 按照级别设置记录日志信息