一、日志
1.程序日志:用来记录应用程序的运行信息、状态信息、错误信息等,类似生活中的日记。
2.日志作用:数据追踪、性能优化、问题排查、系统监控。
3.日志框架:
1)JUL:这是javaSE平台提供的官方日志框架,也被称为JUL,配置相对简单,但是不够灵活性能比较差
2)Log4j:一个流行的日志框架,提供灵活的配置选项,支持多重输出目标
3)Logback:基于Log4j升级而来,提供了更多的功能和配置选项,性能优于Log4j
4)Slf4j(Simple Logging Facade for Java):简单日志门面,提供了一套日志操作的标准接口及抽象类,允许应用程序使用不同的底层日志框架,Log4j和Logback是它的实现类
4.Logback的快速入门:
1)引入jar包
资源: 百度网盘 请输入提取码 提取码: mw5g
在项目中建立一个lib文件夹,放入,右键选择Add as Library…
2)导入配置文件
将网盘中的logback.xml文件放到项目的src文件夹(只能是这个文件夹下)下面
3)获取日志对象使用
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger("Main.class");
logger.info("打印一下");
}
二、枚举
1.介绍:枚举是java中的一种特殊类型,常用于信息的标志和分类。做标记和分类可以通过常量和枚举两个方法,但是对比下来还是枚举的入参更严谨,提示性更强,代码更优雅。
2.格式:enum 枚举类名{枚举1,枚举2,} enum Season { season1,season2}
3.枚举特点:
public class Main {
public static void main(String[] args) {
//特点2:通过枚举类名去访问指定的枚举项 Flag.FAIL
dome(Flag.FAIL);
//特点1:每一个枚举项其实就是该枚举的一个对象
boolean fail = Flag.FAIL.equals("FAIL");
//特点3:所有枚举类都是Enum的子类,可以通过API查询
int ordinal = Flag.FAIL.ordinal();
System.out.println(ordinal);
}
private static void dome(Flag flag){
switch (flag){
case FAIL:
System.out.println("失败");
break;
case WAIT:
System.out.println("等待");
break;
case SUCCESS:
System.out.println("成功");
break;
case PROGRESS:
System.out.println("进行中");
break;
}
}
}
enum Flag{
//特点5:枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的
WAIT("等待") ,PROGRESS("进行中"),SUCCESS("成功"),FAIL("失败");
//特点4:枚举也是类,可以定义成员变量
String name;
//特点6:枚举类可以有构造器,但必须是private 的,它默认也是private(了解,基本不用)
private Flag(String name){
this.name = name;
System.out.println(this.name);
}
//特点7:枚举类也可以有抽象方法,但是枚举项必须重写该方法(了解,基本不用)
// public abstract void show();
// 要写在前面的
// WAIT("等待") {
// @Override
// public void show() {
//
// }
// }
}
4.枚举使用场景: 用来做信息的标志和分类
三、类加载器
1.介绍:将类的字节码载入方法区中
2.加载时机:用到就会加载
3.加载过程:
1)加载:
通过包名+类名,获取这个类,准备用流进行传输
将类加载到内存中
加载完毕创建-一个class 对象,Class中包含成员变量、成员方法
2) 连接
①验证:验证类是否符合JVM规范,安全性检查
②准备:为static 变量分配空间,设置默认值(只是准备并没有赋值)
③解析:将常量池中的符号引用解析为直接引用
观察:看看C类有没有加载,如果没有加载,成员变量的C是啥…
public class Hsdb {
public static void main(String[] args) throws IOException {
//加载B类的加载器
ClassLoader bc = B.class.getClassLoader();
//观察创建B类和不创将B类时类C在常量池中的变化
//new B()
//阻止程序结束,保留进程
System.in.read();
}
}
class B{
C c = new C();
}
class C{
}
找到JDK8 进入文件夹,调出命令行,执行jps查看进程id,在运行java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB (JDK 8版本以后,HSDB被取消),点击左上角的file按钮然后点击第一个,在输入进程id进行查询,然后点击tools按钮后选择Class Browser浏览类的信息,找到对应的B类,点击进入找到Constant Pool常量池点击,观察C类的值。(会发现执行完new B()之后C在常量池中变成了一串地址,这就是地址引用)
3) 初始化
根据程序员程序编码制定的主观计划去初始化类变量和其他资源(就是将静态变量附上值)
4.分类: (getClassLoader()获取类加载器对象)
BootStrap class loader:虚拟机内置类加载器,通常表示null,是C++实现的,获取到的只能是null
Platform class loader:平台类加载器,负责加载JDK中一些特殊的模块,负责加载lib/modules内部类(这个类是从JDK9开始的,JDK9之前是: (Extension Class Loader) 扩展类加载器负责加载jre\lib\ext 目录下的类)
Application class loader:负责加载自己写的类
自定义类加载器:上级为 Application
public static void main(String[] args) {
//BootStrap class loader:加载JDK自带的类
ClassLoader classLoader = String.class.getClassLoader();
System.out.println(classLoader);
//Platform class loader:平台加载类 lib\modules
//Application class loader:系统类加载器 加载自己写的类
ClassLoader classLoader1 = loaderText.class.getClassLoader();
System.out.println(classLoader1);
//加载上下级关系
System.out.println(classLoader1.getParent());
System.out.println(classLoader1.getParent().getParent());
}
5.双亲委派模式(避免类的重复加载)
如果一个类加载器收到了类加载请求,它并不会自己先去加载
而是把这个请求委托给父类的加载器去执行
如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归
请求最终将到达顶层的启动类加载器
如果父类加载器可以完成类加载任务,就成功返回
倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
6.ClassLoader 的常用成员方法
方法 | 功能 |
public static ClassLoader getSystemClassLoader( ) | 获取系统类加载器 |
public InputStream getResourceAsStream(String name ) | 加载某一个资源文件 |
public static void main(String[] args) throws IOException {
//获取系统加载器 Application Class Loader
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//通过系统类加载器,加载配置文件
//系统类加载器加载我们自己编写的类,自己编写的类都在SRC下,可以通过文件名直接找到
InputStream resourceAsStream = systemClassLoader.getResourceAsStream("config.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
resourceAsStream.close();
final String name = properties.getProperty("name");
System.out.println(name);
}
四、反射
1.介绍
框架技术的灵魂
是在运行状态中,对于任意-个类, 都能够知道这个类的所有属性和方法
对于任意一个对象, 都能够调用它的任意属性和方法
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制
2.学习路径
注:所有带Declared的暴力返回方法都不建议使用
1)获取类的字节码对象
Class.forName("全类名"), 类名.class,对象.getClass();
public class domeOne {
public static void main(String[] args) throws ClassNotFoundException {
//Class.forName("全类名")
Class<?> aClass = Class.forName("domin.Student");
// 类名.class
Class<Student> studentClass = Student.class;
// 对象.getClass();
Student student = new Student();
Class<? extends Student> aClass1 = student.getClass();
System.out.println(aClass);
System.out.println(studentClass);
System.out.println(aClass1);
//类的自解码文件只有一份
System.out.println(aClass == aClass1);
System.out.println(aClass == studentClass);
System.out.println(aClass1 == studentClass);
}
}
2)反射类中的构造方法,随后创建对象
构造方法:
方法 | 功能 |
Constructor<?>[] getConstructors() | 返回所有公共构造方法对象的数组 |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造方法对象的数组 |
Constructor<T> getConstructor(Class<?>... parameterTypes) | 返回单个公共构造方法对象 |
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 返回单个构造方法对象 |
Constructor类用于创建对象的方法:
方法 | 功能 |
T newInstance(Object..initargs) | 根据指定的构造方法创建对象 |
setAccessible(boolean flag) | 设置为true,表示取消访问检查 |
public class domeTow {
public static void main(String[] args) throws Exception {
//1.获取自解码文件
Class<?> aClass = Class.forName("domin.Student");
//2.获取构造方法
//getConstructors只能返回带有public修饰的构造方法(基本不用,因为没有针对性)
Constructor<?>[] cs = aClass.getConstructors();
for (Constructor<?> constructor : cs) {
System.out.println(constructor);
}
System.out.println("===================================");
//getDeclaredConstructors返回所有方法,又称暴力返回(基本不用,因为没有针对性)
Constructor<?>[] ds = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : ds) {
System.out.println(declaredConstructor);
}
System.out.println("===================================");
//getConstructor只能返回带有public修饰的构造方法,返回不带public的会报错
Constructor<?> c = aClass.getConstructor();
System.out.println(c);
System.out.println("===================================");
//getDeclaredConstructor返回所有方法,又称暴力返回
Constructor<?> dC = aClass.getDeclaredConstructor(String.class, int.class);
System.out.println(dC);
//暴力返回虽然能返回方法,但是没发用,还是需要通过setAccessible设置权限才能进行使用
dC.setAccessible(true);
Object use = dC.newInstance("张三", 23);
System.out.println(use);
}
}
3)反射类中的成员变量,完成赋值和获取
方法 | 功能 |
Field[] getFields() | 返回所有公共成员变量对象的数组 |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组 |
Field getField(String name) | 返回单个公共成员变量对象 |
Field getDeclaredFields(String name) | 返回单个成员变量对象 |
Field类的设置和获取方法
方法 | 功能 |
void set(Object obj,Object value) | 赋值 |
Object get(Object obj) | 获取值 |
public class domeThree {
public static void main(String[] args) throws Exception {
//1.获取自解码文件
Class<?> aClass = Class.forName("domin.Student");
//2.获取类中的成员变量
//获取成员变量getFields只能获取public
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=======================");
//获取全部成员变量
Field[] decFields = aClass.getDeclaredFields();
for (Field decField : decFields) {
System.out.println(decField);
}
System.out.println("=======================");
//获取成员变量getField只能获取public,指定成员变量的名字,如果成员变量不是public会报错
// Field field = aClass.getField("name");
// System.out.println("field");
Field nf = aClass.getDeclaredField("name");
Field af = aClass.getDeclaredField("age");
System.out.println(nf+"____"+af);
//暴力返回需要设置权限
nf.setAccessible(true);
af.setAccessible(true);
Constructor<?> c = aClass.getConstructor();
Object stu1 = c.newInstance();
Object stu2 = c.newInstance();
nf.set(stu1,"lili");
nf.set(stu2,"mimi");
af.set(stu1,20);
af.set(stu2,18);
System.out.println(stu1);
System.out.println(stu2);
Object stu1n = nf.get(stu1);
Object stu2a = af.get(stu2);
System.out.println(stu1n);
System.out.println(stu2a);
}
}
4)反射类中的成员方法,调用成员方法
方法 | 说明 |
Method[] getMethods() | 返回所有公共成员方法对象的数据组,包括继承的 |
Method[] getDeclareMethods() | 返回所有成员方法对象的数组,不包括继承的 |
Method getMethod(String name,Class<?>... parameterTypes) | 返回单个公共成员方法对象 |
Method getDeclaredMethod(String name,Class<?>... parameterTypes) | 返回单个成员方法对象 |
Method类用于执行方法的方法
方法 | 说明 |
Object invoke(Object obj,Object... args) | 运行方法 |
需求:请向-一个泛型为Integer 的集合,添加一个String字符串
/*
* 需求:请向-一个泛型为Integer 的集合,添加一个String字符串
*思路: Java中的泛型是假的,只在编译的时候有效
* */
public class domeFour {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
final ArrayList<Integer> integers = new ArrayList<>();
Collections.addAll(integers,1,2,3,4);
//1.获取integers集合的字节码对象
Class<? extends ArrayList> aClass = integers.getClass();
//2.反射类中的add方法
Method aF = aClass.getMethod("add", Object.class);
//3.让add方法执行
aF.invoke(integers,"ABC");
System.out.println(integers);
}
}
练习:
编写三个任意的Javabean类,Student类, Teacher类, Worker类。
属性均为: name, age
方法要求:
Student里面写学习study和吃饭eat的方法(方法无参无返回)
Teacher里面写上课teach和吃饭eat的方法(方法无参无返回)
Worker里面写工作work和睡觉sleep的方法(方法无参无返回)
本地新建配置文件properties
属性: className=Student类的全类名 methodName= study
在测试类中读取properties文件中的两个属性值
利用反射创建对象学生类的对象,并调用方法
修改配置文件,不修改代码,运行老师类中的上课方法
修改配置文件,不修改代码,运行工人类中的工作方法
public class domeFive {
public static void main(String[] args) throws Exception {
//1.加载配置文件
InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("config.properties");
//2.创建一个Properties集合,加载数据
Properties prop = new Properties();
prop.load(resourceAsStream);
resourceAsStream.close();
//3.获取数据
String name = prop.getProperty("className");
String method = prop.getProperty("methodName");
//4.获取字节码对象
Class<?> aClass = Class.forName(name);
//5.反射内部的构造方法并实例化
final Object mF = aClass.getConstructor().newInstance();
//6.反射内部的成员方法对象
final Method method1 = aClass.getMethod(method);
method1.invoke(mF);
}
}
public class Student {
private String name;
private int age;
public void study(){
System.out.println("学生在学习");
}
public void eat(){
System.out.println("学生在吃饭");
}
public Student() {
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
//config.properties文件
className = domin.Student
methodName = study