一、日志
1、简介
程序中的日志,用来记录应用程序的运行信息、状态信息、错误信息等
便于数据追踪、性能优化、问题排查、系统监控
2、日志框架
JUL:这是JavaSE平台提供的官方日志框架,也被称为JUL;配置相对简单,但不够灵活,性能较差
Log4j:一个流行的日志框架,提供了灵活的配置选项,支持多种输出目标
Logback(常用):基于Log4j升级而来,提供了更多的功能和配置选项,性能优于Log4j
Slf4j(Simple Logging Facade for Java):简单日志门面,提供了一套日志操作的标准接口及抽象类,允许应用程序使用不同的底层日志框架
3、配置文件
Logback 配置文件 logback.xml
该配置文件是对Logback日志框架输出的日志进行控制的,可以来配置输出的格式、位置及日志开关等。
常用的两种输出日志的位置:控制台、系统文件
XML
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--输出流对象 默认 System.out 改为 System.err-->
<target>System.out</target>
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度
%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%n</pattern>
</encoder>
</appender>
<!-- File是输出的方向通向文件的 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!--日志输出路径-->
<file>C:/code/itheima-data.log</file>
<!--指定日志文件拆分和压缩规则-->
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--通过指定压缩文件名称,来确定分割文件方式-->
<fileNamePattern>C:/code/itheima-data2-%d{yyyy-MMdd}.log%i.gz</fileNamePattern>
<!--文件拆分大小-->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
</appender>
<!--
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
,默认debug
<root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制
-->
<root level="ALL">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE" />
</root>
</configuration>
4、日志级别
日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级由低到高)
|-------|---------------------------------|------------------|
| trace | 追踪,记录程序运行轨迹(不常用) | log.trace("...") |
| debug | 调试,记录程序调试过程中的信息,实际应用中一般将其视为最低级别 | log.debug("...") |
| info | 记录程序运行的重要信息,如:数据库连接、网络连接、io操作 | log.info("...") |
| warn | 警告信息,可能会发生问题 | log.warn("...") |
| error | 错误信息 | log.error("...") |
可以在配置文件中,灵活控制输出哪些类型的日志,大于等于指定日志级别的才会输出
二、枚举
1、简介
枚举是 Java 中的一种特殊类型,一般用来做信息的标志和分类
相比于直接使用常量,枚举代码可读性更高,入参约束严谨,代码优雅
2、格式
修饰符 enum 枚举类名 {
枚举项1, 枚举项2, 枚举项3... ;
}
3、特点
每一个枚举项其实就是该枚举的一个对象
通过枚举类名去访问指定的枚举项
所有枚举类都是 Enum 的子类
枚举也是类, 可以定义成员变量(不常用)
枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的
但是如果枚举类有其他的东西,这个分号就不能省略(建议不要省略)
枚举类可以有构造器,但必须是 private 的,它默认也是 private(不常用)
枚举类也可以有抽象方法,但是枚举项必须重写该方法(不常用)
三、类加载器
1、简介
类加载器负责将类的字节码文件加载到内存的方法区
加载的时机:用到就加载
例如创建类的对象,调用类的静态成员,初始化继承体系等
2、加载过程
(1)加载
通过全类名(包名 + 类名)获取这个类,准备用流进行传输
将类加载到内存中
加载完毕创建一个 class 对象
(2)链接
验证:验证类是否符合 JVM 虚拟机规范,安全性检查
准备:为静态变量分配空间,设置默认值
解析:将常量池中的符号引用解析为直接引用
(3)初始化
根据程序员编码指定的主观计划去初始化类变量和其他资源
3、类加载器的分类
系统类加载器的上级是平台类加载器,平台类加载器的上级是启动类加载器
(1)Bootstrap classLLoade(启动类加载器)r:虚拟机的内置类加载器,通常表示为null
C++ 实现, 获取到的只能是 null
(2)Platform classLoader(平台类加载器):负责加载 JDK中一些特殊的模块
负责加载 lib\modules 内部的类
JDK9 之前是(Extension Class Loader) 扩展类加载器
负责加载 jre\lib\ext 目录下的类
(3)Application classLoader(系统类加载器):负责加载自己写的类
(4)自定义类加载器:上级为 Application,目前不做了解
4、常用方法
public ClassLoader getClassLoader() 获取该类的类加载器对象
5、双亲委派模式
如果一个类加载器收到了类加载请求,它并不会自己先去加载
先判断是否已经加载过,如果没有,就委托给父类
如果父类加载器还存在其父类加载器,则判断后进一步向上委托,依次递归
请求最终将到达顶层的启动类加载器
如果父类加载器可以完成类加载任务,就成功返回
倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
双亲委派模式可以避免类的重复加载
6、ClassLoader 的成员方法
|----------------------------------------------------------|-----------|
| public static ClassLoader getSystemClassLoader () | 获取系统类加载器 |
| public InputStream getResourceAsStream (String name) | 加载某一个资源文件 |
对于 src 下的文件,name 可以直接传入文件名而不用写路径
四、反射
1、简介
框架技术的灵魂
是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法
对于任意一个对象,都能够调用它的任意属性和方法
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制
2、获取类的字节码对象
java
// 法一:使用Class的静态方法forName
Class<?> class1 = Class.forName("com.example.Student");
// 法二:类名.class
Class<Student> class2 = Student.class;
// 法三:Object类中的getClass方法
Class<? extends Student> class3 = new Student().getClass();
注意:类的字节码对象只有一份
3、反射某个类的构造方法
Java 将构造方法封装成了一个类 Constructor<T>
|----------------------------------------------------------------------------|-----------------|
| Constructor<?>[] getConstructors () | 返回所有公共构造方法对象的数组 |
| Constructor<?>[] getDeclaredConstructors () | 返回所有构造方法对象的数组 |
| Constructor<T> getConstructor (Class<?>... parameterTypes) | 返回单个公共构造方法对象 |
| Constructor<T> getDeclaredConstructor (Class<?>... parameterTypes) | 返回单个构造方法对象 |
Constructor类用于创建对象的方法:
|---------------------------------------|------------------|
| T newInstance (Object...initargs) | 根据指定的构造方法创建对象 |
| setAccessible (boolean flag) | 设置为true,表示取消访问检查 |
java
Constructor<?> myConstructor = class1.getDeclaredConstructor(String.class, int.class);
System.out.println(myConstructor);
myConstructor.setAccessible(true);
Student stu1 = (Student) myConstructor.newInstance("aaa", 21);
System.out.println(stu1);
输出:
public com.example.Student(java.lang.String,int)
Student{name = aaa, age = 21}
4、反射某个类的成员变量
Java 将成员变量封装成了一个类 Field
|------------------------------------------|-----------------|
| Field[] getFields () | 返回所有公共成员变量对象的数组 |
| Field[] getDeclaredFields () | 返回所有成员变量对象的数组 |
| Field getField (String name) | 返回单个公共成员变量对象 |
| Field getDeclaredField (String name) | 返回单个成员变量对象 |
name 传入成员变量名
Tips:shift + F6 可以同时修改各处的同一变量的名称
Field类的设置和获取方法:
|-----------------------------------------|-----|
| void set (Object obj, Object value) | 赋值 |
| Object get (Object obj) | 获取值 |
java
Field nameField = class1.getDeclaredField("name");
nameField.setAccessible(true);
Field ageField = class1.getDeclaredField("age");
ageField.setAccessible(true);
nameField.set(stu1, "ccc");
ageField.set(stu1, 23);
System.out.println(nameField.get(stu1));
System.out.println(ageField.get(stu1));
输出:
ccc
23
5、反射某个类的成员方法
Java 将成员方法封装成了一个类 Method
|--------------------------------------------------------------------------|-----------------------|
| Method[] getMethods () | 返回所有公共成员方法对象的数组,包括继承的 |
| Method[] getDeclaredMethods () | 返回所有成员方法对象的数组,不包括继承的 |
| Method getMethod (String name, Class<?>... parameterTypes) | 返回单个公共成员方法对象 |
| Method getDeclaredMethod (String name, Class<?>... parameterTypes) | 返回单个成员方法对象 |
Method 类用于执行方法的方法
|------------------------------------------------|------|
| Object invoke (Object obj, Object... args) | 运行方法 |
Tips:Java 中的泛型是伪泛型,只在编译的时候有效,字节码文件中没有泛型