目录
- 1、日志
- 1.1 日志概念
- 1.2 日志框架
- (1) Logback框架:
- (2)配置文件介绍:
- 2、枚举
- 3、类加载器
- 3.1 类加载器的介绍
- 3.2 类加载器的加载过程:加载、链接、初始化
- 3.3 类加载器的分类
- 3.4 双亲委派模式
- 3.4 ClassLoaser的常用成员方法
- 4、反射
- 4.1 反射概念
- 4.2 反射的使用
- 4.2.1 获取类的字节码对象:
- 4.2.2 反射类中的构造方法
- 4.2.3 反射类中的成员变量
- 4.2.4 反射类中的成员方法
- 4.2.5 案例:
- (1)
- (2)
1、日志
1.1 日志概念
前面我们其实已经在记录日志,如下所示,通过将信息打印到控制台来查看程序运行到哪里了。
接下来学习日志框架来记录日志。
1.2 日志框架
(1) Logback框架:
1、引入三个jar包:
这里有教程下载:https://www.cnblogs.com/yumengqifei/p/16407919.html
2、导入配置文件
3、获取日志对象使用:
package com.itheima.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogTest {
public static void main(String[] args) {
//获取日志对象
Logger logger = LoggerFactory.getLogger("LogTest.class");
logger.info("记录了一条日志");
}
}
运行结果:
(2)配置文件介绍:
下面是logback.xml的样例(可复制后自己改),以及说明:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!-- 控制台应用器:表示当前的日志信息是可以输出到控制台的 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 输出流对象,默认System.out,也可使用System.err -->
<!-- 输出流对象,默认System.out,也可使用System.err -->
<target>System.err</target>
<!-- 格式化输出:
%d:表示日期
%thread:表示线程名
%-5level:表示级别从左显示5个字符宽度
%c:表示获取日志时传入的字符串
%msg:表示日志消息
%n:是换行符
-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread]: %msg%n</pattern>
</encoder>
</appender>
<!-- 文件应用器:输出的方向通向文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志输出路径 -->
<file>logs/myapp.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!-- 指定日志文件拆分和压缩规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 通过指定压缩文件名称,来确定分割文件方式 -->
<fileNamePattern>logs/myapp.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 文件拆分大小 -->
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</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>
2、枚举
直接看一个例子:
package com.itheima.my_enum;
public class Enum_Test {
/**
* 枚举介绍:Java中一种特殊的类型,常用于信息的标记和分类,和常量相比,有如下特点:
* 1、常量
* 2、枚举:入参严谨、提示性更强、代码优雅
*/
public static void main(String[] args) {
method(Season.SUMMER);//注意:入参是枚举类Season的对象
System.out.println(Season.SPRING.getCode());//1
System.out.println(Season.SPRING.getName());//春天
System.out.println(Season.SPRING.getDesc());//我是春天
}
private static void method(Season season) {
switch (season){
case SPRING:
System.out.println("春天");
break;
case SUMMER:
System.out.println("夏天");
break;
case AUTUMN:
System.out.println("秋天");
break;
case WINTER:
System.out.println("冬天");
break;
}
}
}
enum Season{
//枚举项:枚举类的对象
SPRING("1", "春天", "我是春天"),
SUMMER("2", "夏天", "我是夏天"),
AUTUMN("3", "秋天", "我是秋天"),
WINTER("4", "冬天", "我是冬天");
private final String code;
private final String name;
private final String desc;
//枚举类的带参构造方法,则枚举类的对象入参必须要写
private Season(String code, String name, String desc){
this.code = code;
this.name = name;
this.desc = desc;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
}
3、类加载器
3.1 类加载器的介绍
3.2 类加载器的加载过程:加载、链接、初始化
上述的解析需要解释一下,如果一个类只是被加载到方法区,那么这个类里的其他类只是一个符号,并没有创建地址,只有当这个类被创建了之后,这个类里的其他类才会分配地址,也就是上述说的:将常量池中的符号引用解析为直接引用。
3.3 类加载器的分类
package com.itheima.classloader;
import sun.net.spi.nameservice.dns.DNSNameService;
public class ClassLoaderTest {
/**
* 各自类加载器的职责
*/
public static void main(String[] args) {
//1、加载JDK自带的类 BootStrap ClassLoader (启动类加载器)
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);
//2、Platform ClassLoader (平台类加载器) jdk8的话,获取的是jre\lib\ext\ jdk9及之后的话 lib\modules
ClassLoader classLoader2 = DNSNameService.class.getClassLoader();
System.out.println(classLoader2);
//3、Application ClassLoader (系统类加载器) 加载自己编写的类
ClassLoader classLoader3 = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader3);//sun.misc.Launcher$AppClassLoader@18b4aac2
//4、测试类加载器的上下级关系
System.out.println(classLoader3.getParent());//获取他的上级
System.out.println(classLoader3.getParent().getParent());//获取他上级的上级
}
}
3.4 双亲委派模式
3.4 ClassLoaser的常用成员方法
package com.itheima.classloader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class ClassLoaderMethod {
public static void main(String[] args) throws IOException {
//获取系统类加载器 Application Class Loader
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//通过系统类加载器,加载配置文件
//系统类加载器加载我们自己编写的类,因为自己编写的类在src文件夹下,因此下面的路径写相对路径即可
InputStream is = systemClassLoader.getResourceAsStream("config1.properties");
//FileReader fileReader = new FileReader("config1.properties");//之前我们通过字符流加载配置文件
Properties prop = new Properties();
prop.load(is);
//prop.load(fileReader);
is.close();
String username = prop.getProperty("username");
String password = prop.getProperty("password");
System.out.println(username);
System.out.println(password);
}
}
4、反射
4.1 反射概念
正常java程序运行时,先将
.java
文件编译成.class
的字节码文件后,将需要用到的字节码文件加载到方法区进行调用。而反射机制实际上就是直接拿到.class
的字节码文件后作解剖,即直接操作字节码文件对应的对象(即字节码对象,注:java中万物皆对象)。
4.2 反射的使用
4.2.1 获取类的字节码对象:
package com.itheima.reflect;
import com.itheima.domain.Student;
public class ReflectDemo1 {
/**
* 获取字节码对象的三种方式
*/
public static void main(String[] args) throws ClassNotFoundException {
//1、通过Class的静态方法forName
Class<?> class1 = Class.forName("com.itheima.domain.Student");
//2、类名.class
Class<Student> class2 = Student.class;
//3、Object类中的getClass()
Student stu = new Student();
Class<? extends Student> class3 = stu.getClass();
System.out.println(class1);
System.out.println(class2);
System.out.println(class3);
//字节码文件只有一份,那这三种方法获取到的字节码对象应该也是指向同一个地址,我们看下是否是这样:返回为true,表明正确
System.out.println(class1 == class2);//true
System.out.println(class2 == class3);//true
System.out.println(class3 == class1);//true
}
}
4.2.2 反射类中的构造方法
package com.itheima.reflect;
import java.lang.reflect.Constructor;
public class ReflectDemo2 {
/**
* 反射类中的构造方法
*
* 1. Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组
* 2. Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组
* 3. Constructor<?>[] getConstructor(Class<?>...parameterTypes) 返回单个公共构造方法对象,参数是字节码对象,用来识别返回哪个构造方法
* 4. Constructor<?>[] getDeclaredConstructor(Class<?>...parameterTypes) 返回单个构造方法对象,参数是字节码对象,识别返回哪个构造方法
*
* --------------------------------------------------------------------------------------------------------
* 创建对象的方法
* 1.T newInstance(Object...initargs) 根据指定的构造方法创建对象
* 2.setAccessible(boolean flag) 设置为true,表示取消访问检查
*/
public static void main(String[] args) throws Exception {
//1、获取类的字节码对象
Class<?> studentClass = Class.forName("com.itheima.domain.Student");
//2、反射构造方法对象
// Constructor<?>[] constructors = studentClass.getConstructors();//返回所有公共构造方法对象的数组
Constructor<?>[] constructors = studentClass.getDeclaredConstructors();//返回所有构造方法对象的数组
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
//3、反射单个构造方法
// Constructor<?> constructor = studentClass.getConstructor();//返回单个公共构造方法对象
Constructor<?> constructor = studentClass.getDeclaredConstructor(String.class, int.class);//返回带参构造方法对象
constructor.setAccessible(true);//设置为true,表示取消访问检查。即:私有构造方法也可以有权限使用
System.out.println(constructor);
//4、通过构造方法对象,完成实例化
Object o = constructor.newInstance("小明", 67);
System.out.println(o);
}
}
4.2.3 反射类中的成员变量
package com.itheima.reflect;
import com.itheima.domain.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectDemo3 {
/**
* 反射类中的成员变量
* 1.Field[] getFields() 返回所有公共成员变量对象的数组
* 2.Field[] getDeclaredFields() 返回所有成员变量对象的数组
* 3.Field getFields(String name) 返回单个公共成员变量对象
* 4.Field getDeclaredFields(String name) 返回单个成员变量对象
--------------------------------------------------------------------
* Field类的设置和获取方法
* 1.void set(Object obj, Object value) 赋值
* 2.Object get(Object obj) 获取值
*/
public static void main(String[] args) throws Exception {
//1、获取类的字节码对象
Class<Student> studentClass = Student.class;
//2、反射内部的成员变量对象
Field[] declaredFields = studentClass.getDeclaredFields();//暴力返回所有的成员变量(私有共有都返回)
Field[] fields = studentClass.getFields();//返回所有的公共成员变量
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
Field ageField = studentClass.getDeclaredField("age");
Field nameField = studentClass.getDeclaredField("name");
//3、设置访问权限
ageField.setAccessible(true);
nameField.setAccessible(true);
//4、使用成员变量,完成赋值和获取操作
Constructor<Student> constructor = studentClass.getConstructor();//获取反射类的构造方法
Student student = constructor.newInstance();//用反射类的构造方法new一个对象
ageField.set(student, 34);
nameField.set(student, "马斯");
System.out.println(ageField.get(student));
System.out.println(nameField.get(student));
}
}
4.2.4 反射类中的成员方法
package com.itheima.reflect;
import com.itheima.domain.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo4 {
/**
* 反射类中的成员方法
* 1.Method[] getMethods() 返回所有公共成员方法对象的数组,包括继承的
* 2.Method[] getDeclaredMethods() 返回所有公成员方法对象的数组,不包括继承的
* 3.Method getMethod(String name, Class<?>...parameterTypes) 返回单个
* 4.Method getDeclaredMethod(String name, Class<?>...parameterTypes) 返回单个
* ----------------------------------------------------------------------------
* Method类的执行方法
* Object invoke(Object obj, Object... args) 运行方法
*/
public static void main(String[] args) throws Exception{
//1、获取字节码对象
Class<Student> studentClass = Student.class;
//2、通过字节码对象,反射内部的成员方法对象(所有public修饰的)
Method[] methods = studentClass.getMethods();//返回所有公共成员方法对象的数组,包括继承的
Method[] declaredMethods = studentClass.getDeclaredMethods();//返回所有公成员方法对象的数组,不包括继承的
for (Method method : declaredMethods) {
System.out.println(method);
}
//3、通过字节码对象,反射指定的成员方法
Method method1 = studentClass.getMethod("eat");//eat为方法名,空参eat()方法
Method method2 = studentClass.getMethod("eat", int.class);//eat为方法名,带参数的eat(int num)方法
System.out.println(method1);
System.out.println(method2);
//4、让反射到的成员方法执行
Constructor<Student> constructor = studentClass.getConstructor();//用反射类的构造方法创建对象
Student student = constructor.newInstance();
method2.invoke(student, 2);//第一个参数为对象,第二个参数为方法的参数
}
}
4.2.5 案例:
(1)
package com.itheima.reflect;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
public class ReflectTest1 {
/**
* 需求:请向一个泛型为 Integer 的集合,添加一个 String 字符串
* 思路:Java 中的泛型是假的,只在编译的时候有效。也就是说,字节码文件里没有泛型
*/
public static void main(String[] args) throws Exception {
ArrayList<Integer> arrayList = new ArrayList<>();
Collections.addAll(arrayList, 1, 2, 3, 4);
//1、获取list集合对象的字节码对象
Class<? extends ArrayList> listClass = arrayList.getClass();
//2、反射类中的add成员方法
Method addMethod = listClass.getMethod("add", Object.class);
//3、调用add方法,添加字符串。注:因为字节码文件没有泛型,因此我们可以向之前定义泛型为int的集合添加字符串
addMethod.invoke(arrayList, "哈哈");
System.out.println(arrayList);//[1, 2, 3, 4, 哈哈]
}
}
(2)
public class Student {
private String name;
private int age;
public void eat(){
System.out.println("学生吃饭...");
}
public void study(){
System.out.println("学生学习...");
}
}
public class Teacher {
private String name;
private int age;
public void eat(){
System.out.println("老师吃饭...");
}
public void teach(){
System.out.println("老师教学...");
}
}
public class Worker {
private String name;
private int age;
public void sleep(){
System.out.println("工人睡觉...");
}
public void work(){
System.out.println("工人工作...");
}
}
config.properties文件:
className=com.itheima.reflect.Worker
methodName=sleep
package com.itheima.reflect;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;
public class Test {
/**
* 从配置文件读取类名和方法名,然后获取字节码对象后,反射其方法并调用
*/
public static void main(String[] args) throws Exception{
//1、加载配置文件
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("testConfig.properties");
//2、创建properties集合加载数据
Properties prop = new Properties();
prop.load(is);
is.close();
//3、取出数据
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
//4、获取字节码对象
Class<?> aClass = Class.forName(className);
//5、用字节码对象反射构造方法并创建对象
Object instance = aClass.getConstructor().newInstance();
//6、反射成员方法并调用
Method eat = aClass.getMethod(methodName);
eat.invoke(instance);
}
}