类加载器、反射、xml、枚举、注解、单元测试、日志

本文详细介绍了Java中的类加载器,包括加载时机、过程和分类,重点讲解了双亲委派模型。接着深入探讨了反射机制,从获取Class对象到使用构造方法、成员变量和成员方法。然后,详细阐述了XML的语法和解析,以及DTD和Schema约束。此外,文章还涵盖了枚举、注解和单元测试的基础知识,以及日志系统的概念,特别是Log4J的使用和配置。
摘要由CSDN通过智能技术生成

1. 类加载器

1.1 作用

  • 前期写的Java代码,属于源代码,后期要进行运行,首先需要将这个源代码进行编译,编译完成之后会形成一个.class文件,这个.class文件就是字节码文件,后期进行运行的时候,就需要将这个字节码文件加载到内存中,要把这个字节码文件加载到内存中就需要类加载器来完成
  • 也就是说,类加载器就是负责将.class文件加载到内存中

1.2 类的加载时机问题

  • 一个类什么时候被加载到内存?
    • 创建类的实例(即对象)
    • 调用类的类方法
    • 访问类或者接口的类变量,或者为该类变量赋值
    • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    • 初始化某个类的子类
    • 直接使用java.exe命令来运行某个主类

1.3 类的加载过程

  • 一个类通过类加载器,将其加载到内存是需要经过很多个阶段的。大致可以分为
    • 加载,链接,初始化
  • 加载
    • 通过包名+类名,获取这个类,准备用流进行传输
    • 在这个类加载到内存中
    • 加载完毕创建一个class对象,这个Class对象就是对这个类的字节码文件进行描述。Java语言是面向对象的语言,在Java看来万物皆对象,因此一个字节码文件Java也提供了对应的类对其进行描述,这个类就是Class
  • 链接
    • 验证
      • 确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全
    • 准备
      • 负责为类的类变量(被static修饰的变量)分配内存,并设置默认初始化值
    • 解析
      • 将类的二进制数据流中的符号引用替换为直接引用
  • 初始化
    • 根据程序员通过程序制定的主观计划去初始化类变量和其他资源
  • 总结
    • 当一个类被使用的时候,才会被加载到内存
    • 类加载的过程:加载、验证、准备、解析、初始化

1.4 分类

  • 分类
    • Bootstrap class loader:启动类加载器
      • 虚拟机的内置类加载器,通常表示为null,并且没有父null
    • Platfrom class loader:平台类加载器
      • 负责加载JDK中一些特殊的模块
    • System class loader:系统类加载器
      • 负责加载用户类路径上所指定的类库
  • 类加载器存在逻辑上的继承关系
    • System的父加载器为Platform
    • Platform的父加载器为Bootstrap
  • 逻辑上的继承关系
    • 没有通过extends关键字去指定某一个类加载器对应的父类加载器,但是他们的确是存在继承关系的,这是Java中的一种的机制,在类加载器这里可以看到
  • 代码演示
public class ClassLoaderDemo1 {
   
    public static void main(String[] args) {
   
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

        //获取系统类加载器的父加载器 --- 平台类加载器
        ClassLoader classLoader1 = systemClassLoader.getParent();

        //获取平台类加载器的父加载器 --- 启动类加载器
        ClassLoader classLoader2 = classLoader1.getParent();

        System.out.println("系统类加载器" + systemClassLoader);
        System.out.println("平台类加载器" + classLoader1);
        System.out.println("启动类加载器" + classLoader2);

    }
}

1.5 双亲委派模型

  • 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式

1.6 ClassLoader中的两个方法

  • Java语言的是面向对象的,万物皆对象。类加载器其实也是一个对象,在Java中就提供了一个类对其进行描述,这个类就是ClassLoader
  • 方法介绍
方法名 说明
public static ClassLoader getSystemClassLoader() 获取系统类加载器
public InputStream getResourceAsStream(String name) 加载某一个资源文件
  • 代码演示
public class ClassLoaderDemo2 {
   
    public static void main(String[] args) throws IOException {
   
        //static ClassLoader getSystemClassLoader() 获取系统类加载器
        //InputStream getResourceAsStream(String name)  加载某一个资源文件

        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

        //利用加载器去加载一个指定的文件
        //参数:文件的路径(放在src的根目录下,默认去那里加载)
        //返回值:字节流。
        InputStream is = systemClassLoader.getResourceAsStream("prop.properties");

        Properties prop = new Properties();
        prop.load(is);

        System.out.println(prop);

        is.close();
    }
}

2. 反射

2.1 概述

  • 本质上
    • 反射就是使用类的另外一种方式
  • 反射机制
    • 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
    • 对于任意一个对象,都能够调用它的任意属性和方法;
    • 这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制
  • 如何我们可以获取到一个类的字节码文件对象,那么我们就可以从这个字节码文件中解析出来这个类中的成员,然后在使用成员。Java是面向对象的,万物皆对象,因此在Java看来一个类的构造方法是一对象,成员变量也是对象,成员方法也是对象。那么Java就提供了对应的类对其进行描述:Constructor、Field、Method。当我们获取到的对应的成员对象以后,就可以调用成员对象中的方法使用对应的成员
  • 反射学习思路
    1. 首先获取一个类的字节码文件对象
    2. 调用字节码文件对象(Class)的方法获取类的指定的成员对象(构造方法,成员变量,成员方法)
    3. 调用对应的成员对象的方法使用类中的成员

2.2 获取Class对象

  • 获取Class对象的三种方式
    1. 通过Class类中的静态方法forName来进行获取
    2. 通过类的class属性来获取,即类名.class
    3. 通过对象名.getClass()方法
  • 三种方式获取的是同一个Class对象,因为一个类的字节码文件只有一个,所以一个类的字节码文件对象只有一个
  • 代码演示
public class Student {
   
    private String name;
    private int age;

    public Student() {
   
    }

    public Student(String name, int age) {
   
        this.name = name;
        this.age = age;
    }

    public String getName() {
   
        return name;
    }

    public void setName(String name) {
   
        this.name = name;
    }

    public int getAge() {
   
        return age;
    }

    public void setAge(int age) {
   
        this.age = age;
    }

    public void study(){
   
        System.out.println("学生在学习");
    }

    @Override
    public String toString() {
   
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class ReflectDemo1 {
   
    public static void main(String[] args) throws ClassNotFoundException {
   
        //1.Class类中的静态方法forName("全类名")
            //全类名:包名 + 类名
        Class clazz = Class.forName("com.itheima.myreflect2.Student");
        System.out.println(clazz);

        //2.通过class属性来获取
        Class clazz2 = Student.class;
        System.out.println(clazz2);

        //3.利用对象的getClass方法来获取class对象
        //getClass方法是定义在Object类中.
        Student s = new Student();
        Class clazz3 = s.getClass();
        System.out.println(clazz3);

        System.out.println(clazz == clazz2);
        System.out.println(clazz2 == clazz3);
    }
}

2.3 获取构造方法对象

  • 方法介绍
方法名 说明
Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组
Constructor getConstructor(Class<?>… parameterTypes) 返回单个公共构造方法对象
Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回单个构造方法对象
  • 常用的是第四种
    • parameterTypes:表示的是构造方法参数对应的Class类型
  • 代码演示
public class Student {
   
    private String name;
    private int age;

    //私有的有参构造方法
    private Student(String name) {
   
        System.out.println("name的值为:" + name);
        System.out.println("private...Student...有参构造方法");
    }

    //公共的无参构造方法
    public Student() {
   
        System.out.println("public...Student...无参构造方法");
    }

    //公共的有参构造方法
    public Student(String name, int age) {
   
        System.out.println("name的值为:" + name + "age的值为:" + age);
        System.out.println("public...Student...有参构造方法");
    }
}
public class ReflectDemo1 {
   
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
   
        //method1();
        //method2();
        //method3();
        //method4();
    }

    private static void method4() throws ClassNotFoundException, NoSuchMethodException {
   
        //        Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):
//                                      返回单个构造方法对象
        //1.获取Class对象
        Class clazz = Class.forName("com.itheima.myreflect3.Student");
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        System.out.println(constructor);
    }

    private static void method3() throws ClassNotFoundException, NoSuchMethodException {
   
        //        Constructor<T> getConstructor(Class<?>... parameterTypes):
//                                      返回单个公共构造方法对象
        //1.获取Class对象
        Class clazz = Class.forName("com.itheima.myreflect3.Student");
        //小括号中,一定要跟构造方法的形参保持一致.
        Constructor constructor1 = clazz.getConstructor();
        System.out.println(constructor1);

        Constructor constructor2 = clazz.getConstructor(String.class, int.class);
        System.out.println(constructor2);

        //因为Student类中,没有只有一个int的构造,所以这里会报错.
        Constructor constructor3 = clazz.getConstructor(int.class);
        System.out.println(constructor3);
    }

    private static void method2() throws ClassNotFoundException {
   
        //        Constructor<?>[] getDeclaredConstructors():
//                                      返回所有构造方法对象的数组
        //1.获取Class对象
        Class clazz = Class.forName("com.itheima.myreflect3.Student");

        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
   
            System.out.println(constructor);
        }
    }

    private static void method1() throws ClassNotFoundException {
   
        //        Constructor<?>[] getConstructors():
//                                      返回所有公共构造方法对象的数组
        //1.获取Class对象
        Class clazz = Class.forName("com.itheima.myreflect3.Student");
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
   
            System.out.println(constructor);
        }
    }
}

2.4 使用构造方法对象

  • 作用
    • 用来创建对象
  • 方法介绍
方法名 说明
T newInstance(Object…initargs) 根据指定的构造方法创建对象
setAccessible(boolean flag) 设置为true,表示取消访问检查
  • initargs: 表示的就是给构造方法所传递的具体的数据(实际参数)
  • 代码演示
// Student类同上一个示例,这里就不在重复提供了
public class ReflectDemo2 {
   
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
   
        //T newInstance(Object... initargs):根据指定的构造方法创建对象
        //method1();
        //method2();
        //method3();
        //method4();

    }

    private static void method4() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
   
        //获取一个私有的构造方法并创建对象
        //1.获取class对象
        Class clazz = Class.forName("com.itheima.myreflect3.Student");

        //2.获取一个私有化的构造方法.
        Constructor constructor = clazz.getDeclaredConstructor(String.class);

        //被private修饰的成员,不能直接使用的
        //如果用反射强行获取并使用,需要临时取消访问检查
        constructor.setAccessible(true);

        //3.直接创建对象
        Student student = (Student) constructor.newInstance("zhangsan");

        System.out.println(student);
    }

    private static void method3() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
   
        //简写格式
        //1.获取class对象
        Class clazz = Class.forName("com.itheima.myreflect3.Student");

        //2.在Class类中,有一个newInstance方法,可以利用空参直接创建一个对象
        Student student = (Student) clazz.newInstance();//这个方法现在已经过时了,了解一下

        System.out.println(student);
    }

    private static void method2() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
   
       
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值