Java基础 Day27
一、日志
1、简介
程序中的日志,用来记录应用程序的运行信息、状态信息、错误信息等
便于数据追踪、性能优化、问题排查、系统监控
2、日志框架
JUL:这是JavaSE平台提供的官方日志框架,也被称为JUL;配置相对简单,但不够灵活,性能较差
Log4j:一个流行的日志框架,提供了灵活的配置选项,支持多种输出目标
Logback(常用):基于Log4j升级而来,提供了更多的功能和配置选项,性能优于Log4j
Slf4j(Simple Logging Facade for Java):简单日志门面,提供了一套日志操作的标准接口及抽象类,允许应用程序使用不同的底层日志框架
3、配置文件
Logback 配置文件 logback.xml
该配置文件是对Logback日志框架输出的日志进行控制的,可以来配置输出的格式、位置及日志开关等。
常用的两种输出日志的位置:控制台、系统文件
<?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><!--指定日志文件拆分和压缩规则--><rollingPolicyclass="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("...") | |
调试,记录程序调试过程中的信息,实际应用中一般将其视为最低级别 | ||
info | log.info("...") | |
警告信息,可能会发生问题 | ||
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、获取类的字节码对象
// 法一:使用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) |
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) |
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 |
Method 类用于执行方法的方法
Object invoke(Object obj, Object... args) |
Tips:Java 中的泛型是伪泛型,只在编译的时候有效,字节码文件中没有泛型