大数据------javase基础------day18(完结)

类加载器

  • 作用

    负责将编译后的java文件(即.class文件)加载到内存中供虚拟机执行

  • 类加载的时机------总结一句话:用到类就加载,不用就不加载

    1. 创建类的实例
    2. 调用类的方法
    3. 访问类或者接口的类变量,或者为该类变量赋值
    4. 使用反射方法来强制创建某个类或接口对应的java.lang.Class对象
    5. 初始化某个类的子类
    6. 直接使用java.exe命令来运行某个主类
  • 类加载的过程

    1. 加载

      首先,通过一个类的全限定名来获取定义此类的二进制字节流(即通过包名+类名,来获取这个类,并准备用流进行传输)------通过包名+类名,获取这个类,准备用流进行传输

      然后,将这个字节流所代表的静态存储结构转化为运行时数据结构(即通过刚刚得到的流把字节码文件加载到内存中)------在这个类加载到内存中

      最后,在内存中生成一个代表这个类的java.lang.Class对象,任何类被使用时,系统都会为之建立一个java.lang.Class对象(即当一个类加载到内存后,虚拟机会创建一个.class对象来存储类中对应的java内容)------加载完毕创建一个.class对象

    2. 链接(链接中分为三步)

      1. 验证

        目的是为了确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的自身安全(即检查文件中的信息是否符合虚拟机规范,有没有安全隐患)

      2. 准备

        负责为类的类变量(即静态变量:被static修饰的变量)分配内存并设置默认初始化值

      3. 解析

        将类的二进制数据流中的符号引用替换为直接引用(即若本类中用到了其他的类就需要找到对应的类)

        解释:

        当一个字节码文件加载到内存中时,虚拟机会创建一个class对象,并为其分配内存,假设该class对象在内存中的地址是0x0011,该class对象中会记录类的成员信息,这些成员信息是有类型的,如下图所示,name属于String类型的,而String是特殊的数据类型,因为它是一个类,那么最初在加载Student类的时候,是否加载了String类以及String类在哪里都不知道, 则此时String则会用符号代替,这就是符号引用。

        当进行到解析这一步骤时,此时系统就会去找String这个类在哪里(此处假设String类在内存中的地址为0x0022),找到后就会把这个临时的符号变为实际的String引用,如图所示

在这里插入图片描述

  1. 初始化

    根据程序员通过程序制定的主观计划去初始化类变量和其他资源(即给静态变量赋值以及初始化其他资源)

  • 注意

    当一个类被使用的时候才会加载到内存

  • 类加载器分类

    1. 启动类加载器(Booststrap ClassLoader):虚拟机内置的加载器,底层用C++实现,当虚拟机启动时,它会随着自动启动
    2. 平台类加载器(Platform ClassLoader):负责加载JDK中一些特殊的模块
    3. 系统类加载器(System ClassLoader):负责加载用户类路径上所指定的类库
    4. 自定义类加载器(UserClassLoader):允许开发人员定义自己的类加载器来加载类文件
  • 类加载器的双亲委派模型------即类加载器之间的层次关系

    解释:除了启动类加载器之外,剩下的三种加载器都应该有自己的父类加载器。自定义类加载器的父类是系统类加载器,系统类加载器的父类是平台类加载器,平台类加载器的父类是启动类加载器

    注意:

    这里的父子关系并不是代码中的extends继承,而是逻辑上的继承。

    解释:

    假设我现在用自定义类加载器去加载一个字节码文件,它不会自己去尝试加载,它会把这个加载任务委派给系统类加载器完成,而系统类加载器又会将其委托给平台类加载器完成,而平台类加载器又会委托给启动类加载器去完成,而启动类加载器是最顶层的加载器,所以不会再继续往上委托,因此所有的加载请求最后都会传递到最顶层的启动类加载器中,将这种传递委托关系认为是逻辑上的继承

    当顶层的启动类加载器无法完成加载请求时,才会一层一层往下返回,每返回到一个子类加载器时,子类加载器才会去尝试着完成加载请求,若仍无法完成请求则会继续往下返回直至返回到自定义类加载器

ClassLoader类

  • ClassLoader类中的方法

    静态方法解释
    public static ClassLoader getSystemClassLoader()返回系统类加载器。
    普通方法解释
    public ClassLoader getParent()返回委托的父类加载器。 (即获取父类加载器,多次调用该方法可返回最顶层的加载器)
    public InputStream getResourceAsStream(String name)加载某一个资源文件(利用获取到的加载器去获取某一个资源文件)name:资源文件的路径;返回值为一个字节流InputStream,文件中的数据均在该字节流中
    public class Student {
        public static void main(String[] args) {
            //获得系统类加载器
            ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
            //获得系统类加载器的父加载器------平台类加载器
            ClassLoader platformClassLoader = systemClassLoader.getParent();
            //获得平台类加载器的父加载器------启动类加载器
            ClassLoader booststrapClassLoader = platformClassLoader.getParent();
            System.out.println("系统类加载器为:" + systemClassLoader);
            System.out.println("平台类加载器为:" + platformClassLoader);
            System.out.println("启动类加载器为:" + booststrapClassLoader);
        }
    }
    

在这里插入图片描述

public class TestOne {
    public static void main(String[] args) throws IOException {
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        //利用加载器加载一个指定的文件
        InputStream is = systemClassLoader.getResourceAsStream("prop.properties");
        Properties prop = new Properties();
        if (is != null) {
            prop.load(is);
        }
        System.out.println(prop);
        is.close();
    }
}

在这里插入图片描述

注意:

public InputStream getResourceAsStream(String name):该方法的参数表示的相对路径为当前类所在的src文件,而不是工程根目录Project01,如图所示,该Student类在Day18下,所以prop.properties文件的相对路径就是在Day18下的src文件夹下

在这里插入图片描述

反射

  • 定义

    在运行状态中,对于任意一个类,都能够知道该类的所有属性和方法

    对于任意一个对象都能够调用它的任意属性和方法

    这种动态获取信息以及动态调用对象方法的功能称为Java反射机制

  • 特点

    1. 利用反射可以无视修饰符获取类中所有的属性和方法(即此时可以调用任何属性和方法,不论修饰符是否是private)
    2. 先获取配置文件信息,动态获取信息并创建对象和调用方法(即在读取配置文件中的内容时读到什么就创建什么读到什么就运行什么)

反射获取Class类对象(即字节码文件对象)

  • 先导
    在这里插入图片描述

Class类对象解释:当一个Class文件加载到内存后创建的Class对象,该对象中包含了类中所有的信息(如:属性、构造方法、成员方法等等)

我们从Class类对象中所获取的所有成员变量就可认为是成员变量对象(即Field对象)

获取的构造方法就可认为是构造方法对象(即Constructor对象)

获取的成员方法就可认为是成员方法对象(即Method对象)

所以Field对象、Constructor对象、Method对象就是Class类对象的组成部分

在这里插入图片描述

Java文件运行的过程解析如上图所示,可知共有三个阶段:源代码阶段、Class对象阶段、Runtime运行时阶段,不同阶段获取Class类对象的方式不同

Student类代码

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 +
                '}';
    }
}
  • 源代码阶段

利用Class类中的静态方法public static Class <?> forName(String className)------返回与具有给定字符串名称的类或接口关联的 Class类对象。

className:一个字符串,表示要加载的类的完全限定名(即全类名:包名+类名)

获取Student类的Class类对象

获取Class类对象

public class TestTwo {
    public static void main(String[] args) throws Exception {
        //1.Class类中的静态方法forName("全类名")
        Class<?> clazz = Class.forName("at.guigu.leijiazaiqi.Student");
        /*
        等同于
        Class<at.guigu.leijiazaiqi.Student> clazz = (Class<Student>) Class.forName("at.guigu.leijiazaiqi.Student");
         */
        System.out.println(clazz);
    }
}

在这里插入图片描述

注意:

(1)Class类中的静态方法forName(String className)返回的是与具有给定字符串名称的类或接口关联的 Class类对象。 所以Class<at.guigu.leijiazaiqi.Student> clazz = (Class<Student>) Class.forName("at.guigu.leijiazaiqi.Student");为什么要进行强制转换?

Class.forName 方法的返回类型是 Class<?>,它表示任何类型的 Class 对象,而不是特定类型,即使在编译时你将Class<?>写为了Class<at.guigu.leijiazaiqi.Student>,但是Class.forName("at.guigu.leijiazaiqi.Student")对于编译器来说它默认返回的还是Class<?>即使你知道它返回的是StudentClass类对象,所以为了不报编译错误,你需要进行强转

(2)通常情况下,我们在使用 Class.forName 方法时并不知道要加载的类的具体类型,因此使用泛型通配符 <?> 是一种常见的做法,所以我们一般使用Class<?> clazz = Class.getforName(“全类名”)这种格式来获取Class类对象,而不使用Class<全类名> clazz = (Class<类名>)Class.forName("全类名")的格式获取Class类对象

  • Class对象阶段

利用类名.class方法来获取Class类对象

获取Student类的Class类对象

public class TestTwo {
    public static void main(String[] args) throws Exception {
        //通过class属性获取
        Class clazz = Student.class;
        System.out.println(clazz);
    }
}

在这里插入图片描述

  • Runtime运行时阶段

利用对象.getClass()方法来获取Class类对象

public class TestTwo {
    public static void main(String[] args) throws Exception {
        //利用对象的getClass()方法获取Class对象
        Student stu = new Student();
        Class clazz = stu.getClass();
        System.out.println(clazz);
    }
}

在这里插入图片描述

反射获取Constructor对象

Person类代码如下:

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

    //公共的无参构造方法
    public Person() {
        System.out.println("public...无参构造方法");
    }
    //公共的有参构造方法
    public Person(String name, int age) {
        System.out.println("name的值为:" + name + ",age的值为:" + age);
        System.out.println("public...有参构造方法");
    }
}
  • 用到的Class类中的方法
方法解释
public Constructor<?>[] getConstructors()返回所有公共构造方法对象的数组。(即返回一个数组,该数组包括所有公共的构造方法)
public Constructor<?>[] getDeclaredConstructors()返回所有构造方法对象的数组。(即返回一个数组,该数组包括所有公共的构造方法和私有的构造方法)
public Constructor<T> getConstructor(Class<?>...parameterTypes)返回单个公共构造方法对象。(只能返回公共的构造方法)
public Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes)返回单个构造方法对象。(不仅能返回公共的构造方法还可以返回私有的构造方法)

注意:

Class<?>... parameterTypes:一个可变数量的参数,表示要获取的构造方法的参数类型。可以提供一个或多个参数类型,用于精确匹配所需的构造方法。如果不提供任何参数,则表示获取无参数的构造方法。

public class TestConstructor {
    public static void main(String[] args) throws Exception{
        //第一步:获取Person的Class对象
        Class<?> clazz = Class.forName("at.guigu.leijiazaiqi.Person");

        //方式1:public Constructor<?>[] getConstructors()
        Constructor<?>[] constructors = clazz.getConstructors();
        /*
        等同于Constructor[] constructors = clazz.getConstructors();
         */
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("------------------");

        //方式2:`public Constructor<?>[] getDeclaredConstructors()
        Constructor<?>[] constructors1 = clazz.getDeclaredConstructors();//等同于Constructor[] constructors1 = clazz.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors1) {
            System.out.println(constructor);
        }
        System.out.println("------------------");

        //方式3:public Constructor<T> getConstructor(Class<?>...parameterTypes)
        //获取公共的有参构造方法
        Constructor<?> constructor = clazz.getConstructor(String.class,int.class);
        System.out.println(constructor);
        /*等同于
        Constructor constructor = clazz.getConstructor(String.class,int.class);或者
        Constructor<Person> constructor = (Constructor<Person>) clazz.getConstructor(String.class,int.class);
         */
        //获取公共的无参构造方法
        Constructor<?> constructor1 = clazz.getConstructor();
        System.out.println(constructor1);
        System.out.println("------------------");

        //方式4:public Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes)
        //获取私有的有参构造方法
        Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);
        System.out.println(constructor2);
        /*等同于
        Constructor constructor2 = clazz.getDeclaredConstructor(String.class);
        Constructor<Person> constructor2 = (Constructor<Person>)clazz.getDeclaredConstructor(String.class);
         */
    }
}

在这里插入图片描述

利用Constructor创建对象

  • Constructor类中创建对象用到的方法
方法解释
public T newInstance(Object... initargs)根据指定的构造方法创建对象。Object... initargs:一个可变数量的参数,表示要传递给构造函数的初始化参数。这些参数的类型必须与构造函数的参数类型相匹配。
public void setAccessible(boolean flag)是否允许访问此对象的反射对象在其私有成员上执行操作。如果为 true,则允许访问,否则不允许。
  • 创建公共的有参构造器对象
public class TestConstructor1 {
    public static void main(String[] args) throws Exception{
        //第一步:获取Person的Class对象
        Class<?> clazz = Class.forName("at.guigu.leijiazaiqi.Person");
        //第二步:获取Constructor对象
        Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
        //第三步:利用newInstance(Object... initargs)方法创建对象
        Person person = (Person) constructor.newInstance("张三", 15);
        /*等同于
        Object object = constructor.newInstance("张三", 15);
         */
        System.out.println(person);
    }
}

在这里插入图片描述

  • 创建公共的空参构造器对象
public class TestConstructor1 {
    public static void main(String[] args) throws Exception{
        //第一步:获取Person的Class对象
        Class<?> clazz = Class.forName("at.guigu.leijiazaiqi.Person");
        //第二步:获取Constructor对象
        Constructor<?> constructor = clazz.getConstructor();
        //第三步:利用newInstance(Object... initargs)方法创建对象
        Person person = (Person) constructor.newInstance();
        /*等同于
        Object objct = constructor.newInstance();
        */
        System.out.println(person);
    }
}

创建空参构造器对象的简写格式代码如下:

public class TestConstructor1 {
    public static void main(String[] args) throws Exception{
        //第一步:获取Person的Class对象
        Class<?> clazz = Class.forName("at.guigu.leijiazaiqi.Person");
        //第二步:调用Class类中的ewInstance(Object... initargs)方法创建对象------已过时
        Person person = (Person) clazz.newInstance();
        System.out.println(person);
    }
}

注意:

​ Class类中的 public T newInstance(Object... initargs)方法已过时,此处了解即可

  • 创建私有的有参构造器对象

在创建私有的有参构造器对象时,与创建共有的构造器对象不同,其不能直接使用Constructor类的newInstance(Object... initargs)方法来获取对象,因为其是被private修饰的成员。 若用反射强行获取并使用,则需要临时取消访问检查,即将setAccessible(boolean flag)参数设为true

public class TestConstructor1 {
    public static void main(String[] args) throws Exception{
        //第一步:获取Person的Class对象
        Class<?> clazz = Class.forName("at.guigu.leijiazaiqi.Person");
        //第二步:获取私有的有参构造方法
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
        //第三步:允许访问此对象的反射对象在其私有成员上执行操作
        constructor.setAccessible(true);
        //第三步:获取私有的有参构造器对象
        Person person = (Person) constructor.newInstance("张三");
        System.out.println(person);
    }
}

在这里插入图片描述

反射获取Field对象

Teacher类代码如下:

public class Teacher {
    public String name;
    public int age;
    public String gender;
    private int money = 300;
}

在这里插入图片描述

  • 用到的Field类中的方法
方法解释
public Field[] getFields()返回所有公共成员变量对象的数组
public Field[] getDeclaredFields()返回所有成员变量对象的数组**(即公有和私有的都可返回)**
public Field getField(String name)返回单个公共成员变量对象。String name:表示要获取的字段的名称
public Field getDeclaredField(String name)返回单个成员变量对象**(即公有和私有的都可返回)**。String name:表示要获取的字段的名称
public class TestField {
    public static void main(String[] args) throws Exception{
        //第一步:获取Person的Class对象
        Class<?> clazz = Class.forName("at.guigu.leijiazaiqi.Teacher");
        //第二步:获取Field对象
        //方式1:public Field[] getFields()
        Field[] fields1 = clazz.getFields();
        for (Field field : fields1) {
            System.out.println(field);
        }
        System.out.println("------------------------");
        //方式2:public Field[] getDeclaredFields()
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }
        System.out.println("------------------------");
        //方式3:public Field getField(String name)
        Field field1 = clazz.getField("name");
        System.out.println(field1);
        System.out.println("------------------------");
        //方式4:public Field getDeclaredField(String name)
        Field field2 = clazz.getDeclaredField("money");
        System.out.println(field2);
    }
}

在这里插入图片描述

利用Field对象赋值、获取值

  • Field类中用到的方法
方法解释
public void set(Object obj, Object value)将指定对象参数上此 字段对象表示的字段设置为指定的新值。(即给指定对象的成员变量赋值)
public Object get(Object obj)返回指定对象上此 字段表示的字段的值。(即返回指定对象的Field的值)
public void setAccessible(boolean flag)是否允许访问此字段的反射对象在其私有成员上执行操作

注意:

(1)

Object obj:表示要设置或返回字段值的对象实例

Object value:表示要设置的新值。该值的类型必须与字段的类型相匹配,否则将抛出 IllegalArgumentException 异常。

(2)

返回私有成员变量的值时要先取消一下访问检查------利用Field类中的setAccessible(boolean flag)方法

(3)

在赋值和返回值之前都需要先获取Constructor对象并利用Constructor创建对象,然后才能使用Field类中的setget方法来赋值和返回值

public class TestField1 {
    public static void main(String[] args) throws Exception{
        //第一步:获取Person的Class对象
        Class<?> clazz = Class.forName("at.guigu.leijiazaiqi.Teacher");
        //第二步:获取Field对象
        Field fieldName = clazz.getField("name");
        Field fieldAge = clazz.getField("age");
        Field fieldGender = clazz.getField("gender");
        Field fieldMoney = clazz.getDeclaredField("money");
        //第三步:利用Set(Object obj, Object value)方法进行赋值
        //3.1先创建一个Teacher对象,有了Teacher对象才能给指定对象赋值
        Constructor constructor = clazz.getConstructor();
        Teacher teacher = (Teacher) constructor.newInstance();
        fieldName.set(teacher, "张三");
        fieldAge.set(teacher, 15);
        fieldGender.set(teacher, "男");
        System.out.println(teacher);
        //第四步:获取值
        //获取普通成员变量的值
        String name = (String) fieldName.get(teacher);
        System.out.println(name);
        //获取私有成员变量的值
        fieldMoney.setAccessible(true);
        int money = (int) fieldMoney.get(teacher);
        System.out.println(money);
    }
}

在这里插入图片描述

反射获取Method对象

Worker类如下:

public class Worker {
    private void show() {
        System.out.println("私有的show方法,无参无返回值");
    }
    public void function1() {
        System.out.println("公共的function1方法,无参无返回值");
    }
    public void function2(String name) {
        System.out.println("公共的function2方法,有参无返回值,参数为:" + name);
    }
    public String function3() {
        System.out.println("公共的function3方法,无参有返回值");
        return "aaa";
    }
    public String function4(String name) {
        System.out.println("公共的function4方法,有参有返回值,参数为:" + name);
        return "aaa";
    }
}

在这里插入图片描述

  • 用到的Class类中的方法如下:
方法解释
public Method[] getMethods()返回所有公共成员方法对象的数组,包括继承的
public Method[] getDeclaredMethods()返回所有成员方法对象的数组,不包括继承的。(包括私有方法和公共方法)
public Method getMethod(String name, Class<?>... parameterTypes)返回单个公共成员方法对象
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)返回单个成员方法对象(包括私有方法和公共方法)

解释:

String name: 这是要获取的方法的名称,需要提供方法的准确名称作为参数。

Class<?>... parameterTypes: 这是一个变长参数,用于指定方法的参数类型。这表示你可以传递零个或多个参数类型作为参数。参数类型是 Class<?> 对象的可变数量。Class<?> 表示一个未知的类型,这允许方法接受任何类型的参数。通常,你会传递实际的类对象来指定方法的参数类型。

后两个方法不会返回继承的方法

以上四种方法若找不到指定的方法将返回null

public class TestMethod {
    public static void main(String[] args) throws Exception{
        //第一步:获取Class对象
        Class<?> clazz = Class.forName("at.guigu.leijiazaiqi.Worker");
        //第二步:获取Method对象
        //方式1:public Method[] getMethods()
        Method[] methods1 = clazz.getMethods();
        for (Method method : methods1) {
            System.out.println(method);
        }
        System.out.println("--------------------------------------");
        //方式2:public Method[] getDeclaredMethods()
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.println(method);
        }
        System.out.println("--------------------------------------");
        //方式3:public Method getMethod(String name, Class<?>... parameterTypes)
        Method method1 = clazz.getMethod("function1");
        System.out.println(method1);
        Method method2 = clazz.getMethod("function2", String.class);
        System.out.println(method2);
        Method method3 = clazz.getMethod("function3");
        System.out.println(method3);
        Method method4 = clazz.getMethod("function4", String.class);
        System.out.println(method4);
        System.out.println("--------------------------------------");
        //方式4:public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        Method methodPrivate = clazz.getDeclaredMethod("show");
        System.out.println(methodPrivate);
    }
}

在这里插入图片描述

利用Method对象运行方法

  • Method类中用到的方法
方法解释
public Object invoke(Object obj, Object... args)在具有指定参数的指定对象上调用此 方法对象表示的基础方法。(即在指定的对象上调用方法)

注意:

​ (1)参数解释

Object obj: 这是要调用方法的对象。如果方法是静态的,则可以将 null 传递给此参数

Object... args: 这是一个可变数量的参数,表示方法调用时传递的参数。你可以传递零个或多个参数给方法。这些参数的类型应该与要调用的方法的参数类型匹配。

Object返回值:invoke 方法的返回值是被调用方法的返回值。因为被调用方法的返回类型可能是任何类型,所以这里返回的是 Object。若没有返回值就不写

public class TestMethod1 {
    public static void main(String[] args) throws Exception {
        //第一步:获取Class对象
        Class<?> clazz = Class.forName("at.guigu.leijiazaiqi.Worker");
        //第二步:创建Worker对象
        Constructor<?> constructor = clazz.getConstructor();
        Worker worker = (Worker) constructor.newInstance();
        //第三步:获取Method对象
        Method method4 = clazz.getMethod("function4", String.class);
        //第四步:利用Method对象运行方法
        String a = (String) method4.invoke(worker, "张三");
        System.out.println(a);
    }
}

在这里插入图片描述

XML

  • 前导

properties作为配置文件的缺点:

若配置文件中运行的方法比较多,则只能在“=”的后面写多个值,并且用逗号或者一个指定的符号隔开,在利用代码解析properties文件时还需要用逗号进行切割,这样就比较麻烦。如图所示
在这里插入图片描述

而对于XML来说,里面的每个值都是独立存在的,不需要进行切割,操作方便如图所示
在这里插入图片描述

  • XML学习网址:https://www.w3school.com.cn/

  • XML文件都会存在XML文件夹下

  • 概述

    全称为Extensible Markup Language,是一种可扩展标记语言 (可理解为XML文件是由很多标签组成的,而标签名是可以自定义的)

    可扩展:标签的名字是可以自定义的

    1. 标记语言:通过标签(也称为元素)来描述数据的一门语言
  • 作用

    1. 用于进行存储数据和传输数据
    2. 作为软件的配置文件
  • XML标签规则

    1. 标签由一对尖括号和合法标识符组成,如:<student>
    2. 标签必须成对出现(即需要有开始和结束两个标签),如:<student> </student>
    3. 特殊标签可以不成对出现,但必须有结束标记/,如:<adress/>
    4. 标签中可以定义属性,属性和标签名空格隔开,属性值必须用引号引起来,如:<student id="1"> </student>
    5. 标签需要正确的嵌套,如下所示
    <student id="1">
        <name>张三</name>
    </student>
    <!--下列为错误格式
    <student id="1">
        <name>张三
    </student>
    	</name>
    -->
    
  • XML语法规则

    1. XML文件后缀名为:xml

    2. 文档声明必须是第一行第一列

      <?xml version="1.0" encoding="UTF-8" standalone="yes" >
      

      <?xml ... ?> 是声明的开始和结束标记,在这个标记中,我们可以指定以下信息:

      version:该属性必须存在,这是XML版本号。它指定了XML规范的版本。通常情况下,它的值是 “1.0” 或 “1.1”

      encoding :该属性非必须存在。这是XML文档所使用的字符编码(一般取值都为UTF-8)。字符编码指定了文档中的字符如何转换为字节序列。

      standalone :该属性非必须存在。指定了文档是否是独立的,即是否依赖外部资源。如果文档声明中没有 standalone 属性,那么文档的独立性会根据具体的XML解析器的默认行为来决定。

    3. XML文件中必须存在一个根标签,但是有且只能有一个

    4. XML文件可以定义注释信息

    5. XML文件中的特殊字符

      1. &lt;------<------小于
      2. &gt;------>------大于
      3. &amp;------&------和号
      4. &apos;------'------单引号
      5. &quot;------"------引号
    6. XML文件中可以存在CDATA区------可以包含任意的文本数据,而且不需要对其中的特殊字符进行转义。

      <![CDATA[...内容...]]>
      
    <?xml version="1.0" encoding="UTF-8" ?>
    <students><!--根标签-->
    <!--    第一个学生信息-->
        <student id="1">
            <name>张三</name>
            <age>23</age>
            <info>学生&lt;&gt;信息</info>
            <message><![CDATA[...<内容>...]]></message>
        </student>
    
    <!--    第二个学生信息-->
        <student id="2">
            <name>李四</name>
            <age>24</age>
        </student>
    </students>
    
  • 判断XML文件中代码的正确性方法

    1. idea中会自动进行错误标红提示
    2. 用浏览器打开xml文件,若有误则会有错误提示,如图所示在这里插入图片描述

XML解析

  • 常见的解析思想

    DOM(Document Object Model,文档对象模型) ,是一种表示XML或HTML文档结构的标准接口。它将文档中的每个组成部分(如元素、属性、文本)都表示为一个对象,这些对象可以被程序(通常是JavaScript、Java等)访问和操作

    它会把xml文件全部加载到内存,在内存中形成一个树形结构,再获取对应的值

  • DOM解析思想

    是一种解析 XMLHTML 文档的方法,它将文档解析为一个树状的对象模型,xml文档中的所有信息均以树状形式显示

在这里插入图片描述

注意:

  1. Document:整个xml文档对象
  2. 图中蓝色的标签为Element对象,标签之间的内容叫做标签体
  3. id是一个属性,属性属于Attribute对象
  4. 图中紫色为标签中的内容,属于标签体,叫做 Text(文本)对象
  5. id后面的值不是一个对象,可以通过id直接获取到,只是一个普通的属性值
  6. Element对象、Attribute对象、Text对象有一个共同的父类Node对象
  • 常见的解析工具

    1. JAXP
    2. JDOM------开源组织提供
    3. DOM4J(全称Dom For Java)------开源组织提供------以后用dom4j解析
    4. pull:主要应用在Android手机端解析XML
  • 解析工作准备

    1. 在项目根目录下创建一个名为libs的文件夹(以后所有的jar包都是放在其中)
    2. dom4j jar包放入,放入后右键jar包→Add as LibraryOK(查看jar包是否成功导入方法在day17日志技术中已提到)
  • 解析用到dom4j架包中的的类及方法

SAXReader构造器解释
public SAXReader()创建一个 SAXReader 解析器对象,该对象将使用默认的配置和设置来解析 XML 文档。
SAXReader类方法解释
public Document read(File file)从指定的文件中读取 XML 文档并将其解析为一个 DOM4J 文档对象(即利用解析器把xml文件加载到内存中并返回一个Document对象)
Document方法解释
public Element getRootElement()获取 XML 文档的根元素Element对象(即获取根标签
Element接口方法解释
public List elements()获取该元素对象的所有一级子元素每个子元素都表示为一个 Element 对象。如果该元素没有子元素,则返回一个空的 List。(即获取调用者的所有一级子标签并把这些子标签放到一个集合中返回
public List elements(QName, qName)获取该元素中指定 QName 的所有一级子元素每个子元素都表示为一个 Element 对象。如果该元素没有子元素,则返回一个空的 ListQName qName:这是一个 QName 对象,用于指定要获取的子元素的名称。QNameDOM4J 中的一个类,用于表示 XML 元素的限定名称。QName 对象包含了元素的命名空间 URI、本地名称和前缀。(即获取调用者指定的所有一级子标签并把这些子标签放到一个集合中返回
public Element element(String s)获取调用者指定的子标签。String s:指定的子标签字符串名
public Attribute attribute(String name)获取Element对象指定名称的属性,如果指定名称的属性不存在,则返回 null。(即获取标签中的属性)
public String getText()获取标签的标签体内容
Attribute方法解释
public String getValue()获取指定属性的值
  • 解析代码实现

在这里插入图片描述

public class XmlParse {
    public static void main(String[] args) throws DocumentException {
        //创建一个ArrayList集合用来装学生对象
        ArrayList<Student> stuList = new ArrayList<>();
        //第一步:获取解析器对象
        SAXReader saxReader = new SAXReader();
        //第二步:利用解析器把xml文件加载到内存中并返回一个对象
        Document document = saxReader.read(new File("xml\\student.xml"));
        //第三步:获取根标签
        Element rootElement = document.getRootElement();
        //第四步:通过根标签来获取Student标签
        //elements():获取调用者的所有一级子标签并把这些子标签放到一个集合中返回
        List<Element> list = rootElement.elements();
        System.out.println(list.size());//查看根目录下有几个一级子标签
        System.out.println("----------------------------");
        //List elements(QName, qName):可以获取调用者指定的所有一级子标签,并把这些子标签放在List集合中
        List<Element> stuElements = rootElement.elements("student");
        for (Element stuElement : stuElements) {
            //获取标签中的属性
            //获取id这个属性
            Attribute idAttribute = stuElement.attribute("id");
            System.out.println(idAttribute);
            //获取id的属性值
            String idValue = idAttribute.getValue();
            System.out.println(idValue);

            //获取name标签
            Element nameElement = stuElement.element("name");
            System.out.println(nameElement);
            //获取name标签的标签体
            String nameText = nameElement.getText();
            System.out.println(nameText);
            //获取age标签
            Element ageElement = stuElement.element("age");
            String ageText = ageElement.getText();
            System.out.println(ageText);
            Student s = new Student(idValue, nameText, Integer.parseInt(ageText));
            stuList.add(s);
        }
        System.out.println("---------------------------");
        for (Student student : stuList) {
            System.out.println(student);
        }
    }
}

在这里插入图片描述

注意:

​ String类型的数据不能直接强制转换为int类型。这是因为String类型和int类型是不兼容的数据类型,它们之间没有继承或兼容关系。可以使用Integer类提供的parseInt()方法或valueOf()方法将String类型的数据转换为int类型,或者在需要时使用自动装箱和拆箱来进行转换

XML文件(文档)约束

  • 什么是约束

    用来限定xml文件中可使用的标签和属性(说白了就是告诉程序员xml该怎么写,自己不要随便写标签名和属性名等内容,按照要求来)

  • 约束分类

    1. DTD约束------需掌握内容如下:
      1. 能在xml中引入约束文档
      2. 能够简单阅读约束文档
      3. 根据约束编写xml文件
    2. schema约束
  • schema约束文件和dtd约束文件区别

    • schama语法更加复杂

    • dtd文件中元素类型取值比较单一,常见的是PCDATA类型;在schema文件中可支持多个数据类型

    • dtd约束文件不是xml文件;而schema约束文件是XML文件,符合XML语法,该文件后缀名为.xsd

    • 一个xml文件中可以引用多个schema约束文件,多个schema使用名称空间区分(名称空间类似于java包名)

    • schema文件用来约束xml文件但由于它是一个xml文件,所以它同时也被其他文件约束着
      在这里插入图片描述

DTD约束

person.xml文件如下:

在这里插入图片描述

  • 编写dtd约束文件的步骤

    1. 创建一个后缀名为.dtd的文件

    2. xml文件中使用了哪些元素(即在此☞标签),并用<!ELEMENT>去定义这些元素,写法为:<!ELEMENT 标签名>,如:在上图中有四个标签,所以要定义这四个标签,代码如下:

      <!ELEMENT persons><!--复杂元素-->
      <!ELEMENT person><!--复杂元素-->
      <!ELEMENT name><!--简单元素-->
      <!ELEMENT age><!--简单元素-->
      
    3. 判断元素是简单元素(没有子元素)还是复杂元素(有子元素),如上所示,但是我们一般用代码来显示是简单元素还是复杂元素,格式为:

      1. 简单元素:<!ELEMENT 标签名 (#PCDATA)>
      2. 复杂元素:<!ELEMENT 标签名 (该标签的一级子标签名,多个用逗号隔开)>

person.xml文件对应的完整persondtd.dtd约束文件代码如下所示:

<!ELEMENT persons (person)>
<!ELEMENT person (name,age)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>

XML文件引入dtd约束文件

  • 第一种方法:引入本地dtd文件

​ 文档声明下面第一行写入代码,格式为:<!DOCTYPE 根标签名 SYSTEM 'dtd文件路径及后缀名'>,如下代码所示

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE persons SYSTEM 'persondtd.dtd'>

<persons>
    <person>
        <name>张三</name>
        <age>23</age>
    </person>
</persons>

​ 其中SYSTEM代表从本地引入dtd文件,成功引入之后按住Ctrl + 鼠标左键单击dtd文件路径,即可跳转到dtd文件

引入dtd文件后,若定义dtd文件中没有的标签名,则系统会自动报错,如下图所示

在这里插入图片描述

  • 第二种方法:在xml文件内部引入—即dtd文件的内容跟xml文件的内容在同一个文件中

文档声明下面第一行写入dtd文件内容的代码格式为:<!DOTYPE persons [方括号中写dtd文件内容]>,详细代码如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE persons [
        <!ELEMENT persons (person)>
        <!ELEMENT person (name,age)>
        <!ELEMENT name (#PCDATA)>
        <!ELEMENT age (#PCDATA)>
        ]>

<persons>
    <person>
        <name>张三</name>
        <age>23</age>
    </person>
</persons>
  • 第三种方法:引入网络中的dtd文件

文档声明下面第一行写入dtd文件内容的代码格式为:<!DOCTYPE 根标签名 PUBLIC "dtd文件名称" "dtd文档的URL">

注意:

​ 第一种方法中用的单引号,第三种方法中用的是双引号

DTD语法规则

  • 定义一个元素的格式为:<!ELEMENT 元素名 元素类型>

    • 简单元素

      • EMPTY:表示标签体为空
      • ANY:表示标签体可以为空也可以不为空
      • #PCDATA:表示该元素的内容为部分字符串
    • 复杂元素------直接写子元素名称

      • 多个子元素可以用,或者|隔开。其中,表示定义子元素的顺序|表示子元素只能出现任意一个,还有一些特殊的符号意义如下:

      :零次或一次

      +:一次或多次

      *:零次或多次

      若不写则表示出现一次

    • 举例说明

      在这里插入图片描述

  • 定义一个属性的格式为:<!ATTLIST 元素名称 属性名称 属性的类型 属性的约束>

    • 属性的类型

      • CDATA类型:普通的字符串
    • 属性的约束

      • #REQUIRED:属性是必须的
      • #IMPLIED:属性不是必须的
      • #FIXED:属性值是固定的
    • 举例说明

      • 属性值固定,此时格式为:<!ATTLIST 元素名称 属性名称 属性的类型 #FIXED "属性值">

    在这里插入图片描述

    • 属性值是必须,此时必须有属性值,但是属性值可以相同也可不同,格式为:<!ATTLIST 元素名称 属性名称 属性的类型 #REQUIRED>在这里插入图片描述

    • 属性值非必须,此时属性值可有可无,且属性值可相同也可不相同,格式为:<!ATTLIST 元素名称 属性名称 属性的类型 #IMPLIED>
      在这里插入图片描述

schema约束

person.xml文件如下:
在这里插入图片描述

  • 编写schema约束文件的步骤

    1. 创建一个名为.xsd的文件
    2. 定义文档声明<?xml version="1.0" encoding="UTF-8" ?>
    3. schema文件的根标签为:<schema>
    4. <schema>中定义属性:
      1. xmlns=http://www.w3.org/2001/XMLSchema,表示当前的文件是一个约束文件(对xml文件进行约束)
      2. targetNamespace=唯一的url地址,作用是指定当前schema文件的名称空间
      3. elementFormDefault="qualified",表示当前schema文件是一个质量良好的文件
    5. 通过element来定义元素,并判断当前元素是简单元素还是复杂元素

    代码结构如下:

    <?xml version="1.0" encoding="UTF-8" ?>
    <schema 
            xmlns="http://www.w3.org/2001/XMLSchema" 
            targetNamespace="自己的命名空间"
            elementFormDefault="本文件质量良好">
        
        <element name="根标签名">
            <complexType><!--复杂元素-->
                <sequence><!--里面的子元素必须要按照顺序定义-->
                    
                </sequence>
            </complexType>
            
        </element>
    
    </schema> 
    

person.xml文件对应的完整persondtd.xsd约束文件代码如下所示:

<?xml version="1.0" encoding="UTF-8" ?>
<schema
        xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://blog.csdn.net/cgrs5572/javase"
        elementFormDefault="qualified">

    <!--定义persons复杂元素-->
    <element name="persons">
        <complexType>
            <sequence>
                <!--定义person复杂元素-->
                <element name="person">
                    <complexType>
                        <sequence>
                            <!--定义name和age简单元素-->
                            <element name="name" type="string"></element>
                            <element name="age" type="string"></element>
                        </sequence>
                    </complexType>
                </element>
            </sequence>
        </complexType>
    </element>
    
</schema>

XML文件引入schema约束

  • 引入步骤

    • 在根标签定义属性:xmlns="http://www.w3.org/2001/XMLSchema-instance",代表该xml文件是被别人约束的
    • 通过xmlns引入约束文件的targetNamespace命名空间(即该xml文件是被哪个约束的,要用xmlns属性写出约束文件的命名空间来告诉系统)
    • 给某一个xmlns属性添加一个标识,用于区分不同的名称空间,格式为:xmlns:标识="名称空间地址"(注意:标识可以是任意的,但是一般取值都是xsi
    • 通过xsi:schemaLocation指定名称空间所对应的约束文件路径格式为:xsi:schemaLocation="名称空间url 文件路径"(注意:双引号中由两部分即名称空间url和文件路径)

    代码如图所示

在这里插入图片描述

Schema定义属性

  • 代码格式:

<attribute name="属性名" type="属性值的数据类型" use="属性是否必须"></attribute>

use的参数如下:

​ required:该属性必须有

​ optional:该属性可有可无

注意:给复杂元素添加属性的代码要写在该标签最外层的</sequence></complexType>之间

代码示例如图所示
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/482707.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

阿里云幻兽帕鲁4核16G和8核32G服务器优惠价格

2024阿里云幻兽帕鲁专用服务器价格表&#xff1a;4核16G幻兽帕鲁专用服务器26元一个月、149元半年&#xff0c;默认10M公网带宽&#xff0c;8核32G幻兽帕鲁服务器10M带宽价格90元1个月、271元3个月。阿里云提供的Palworld服务器是ECS经济型e实例&#xff0c;CPU采用Intel Xeon …

Linux:详解https协议

文章目录 什么是https协议信息窃取常见的加密数据摘要和数据指纹https的工作过程只使用对称加密只使用非对称加密都使用非对称加密非对称加密对称加密 证书数据签名https方案 本篇要总结的内容是关于https协议的相关内容 什么是https协议 在讲述https协议之前&#xff0c;首先…

差分约束系统

差分约束系统 差分约束系统&#xff08;spfa&#xff09;1、概述2、过程模拟3、推理 差分约束系统&#xff08;spfa&#xff09; 1、概述 x j − x i ≤ w k x_j-x_i\le w_k xj​−xi​≤wk​转换为&#xff1a; x j ≤ w k x i x_j\le w_kx_i xj​≤wk​xi​ 在松弛操作中&…

dubbo 源码系列之-集群三板斧---负载均衡(-)

dubbo 源码系列之-负载均衡 概述核心接口 LoadBalanceDubbo 提供了 5 种负载均衡实现&#xff0c;分别是&#xff1a;LoadBalance 接口AbstractLoadBalance ConsistentHashLoadBalance 一致性hash1. 一致性 Hash 简析1.0 hash 算法2.0 一致性Hash算法3.0 一致性hash算法 引入槽…

K8S--SpringCloud应用整合Nacos实战

原文网址&#xff1a;K8S--SpringCloud应用整合Nacos实战-CSDN博客 简介 本文介绍K8S部署SpringCloud应用整合Nacos实战。 本文是将原来的SpringCloud项目&#xff08;闪速优选&#xff09;迁移到K8S上&#xff0c;一行代码都不需要改动。用K8S运行Nacos、Gateway、SpringCl…

PHP 读取嵌入式数据 SQLite3

SQLite3 属于轻量级开源的嵌入式关系型数据库&#xff0c;但它支持 ACID(Atomicity,Consistency,Isolation,Durability) 事务。 SQLite Download Page: https://www.sqlite.org/download.html 第一步&#xff1a;在 php.ini 中开启 extensionsqlite3 第二步&#xff1a;连接数…

Redis的String类型为什么重新设计使用了SDS数据结构呢

Redis 选择重新设计其 String 类型的底层数据结构&#xff0c;采用 SDS&#xff08;Simple Dynamic String&#xff09;而不是直接使用 C 语言标准库提供的原生字符串&#xff08;char*&#xff09;的原因主要包括以下几点&#xff1a; O(1) 时间复杂度获取长度&#xff1a; 在…

机器学习金融应用技术指南

1 范围 本文件提供了金融业开展机器学习应用涉及的体系框架、计算资源、数据资源、机器学习引擎、机 器学习服务、安全管理、内控管理等方面的建议。 本文件适用于开展机器学习金融应用的金融机构、技术服务商、第三方安全评估机构等。 2 规范性引用文件 下列文件中的内容通过…

C#,图论与图算法,用于检查给定图是否为欧拉图(Eulerian Graph)的算法与源程序

1 欧拉图 欧拉图是指通过图(无向图或有向图)中所有边且每边仅通过一次通路, 相应的回路称为欧拉回路。具有欧拉回路的图称为欧拉图(Euler Graph), 具有欧拉通路而无欧拉回路的图称为半欧拉图。 对欧拉图的一个现代扩展是蜘蛛图,它向欧拉图增加了可以连接的存在点。 这给…

目标检测预测框可视化python代码实现--OpenCV

import numpy as np import cv2 import colorsys from PIL import Image, ImageDraw, ImageFontdef puttext_cn(img, text, pt, color(255,0,0), size16):if (isinstance(img, np.ndarray)): # 判断是否OpenCV图片类型img Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2…

【HarmonyOS】ArkUI - 状态管理

在声明式 UI 中&#xff0c;是以状态驱动视图更新&#xff0c;如图1所示&#xff1a; 图1 其中核心的概念就是状态&#xff08;State&#xff09;和视图&#xff08;View&#xff09;&#xff1a; 状态&#xff08;State&#xff09;&#xff1a;指驱动视图更新的数据&#xf…

BI技巧丨个性化视觉对象

BOSS&#xff1a;那个&#xff0c;那个谁&#xff0c;最近用户反映了&#xff0c;说是你们做的报表不太行啊&#xff1f;&#xff01; 白茶&#xff1a;&#xff08;&#xff1f;&#xff1f;&#xff1f;&#xff09;老板&#xff0c;怎么说&#xff1f; BOSS&#xff1a;就是…

pytest之统一接口请求封装

pytest之统一接口请求封装 pytest的requests_util.pyrequests_util.py 接口自动化测试框架的封装yaml文件如何实现接口关联封装yaml文件如何实现动态参数的处理yaml文件如何实现文件上传有参数化时候&#xff0c;怎么实现断言yaml的数据量大怎么处理接口自动化框架的扩展&#…

CSK6 接入聆思平台(LSPlatform)

一、开发环境 硬件&#xff1a;视觉语音大模型AI开发套件 二、使用大语言模型 官方指导文档&#xff1a; 开始使用 | 聆思文档中心 获取API密钥 | 聆思文档中心 1、注册 提交申请之后需要将注册电话号码通过微信发送给聆思科技工作人员&#xff0c;工作人员授权后&#xff…

阿里云4核16G服务器价格26.52元1个月、149.00元半年,ECS经济型e实例

阿里云4核16G服务器优惠价格26.52元1个月、79.56元3个月、149.00元半年&#xff0c;配置为阿里云服务器ECS经济型e实例ecs.e-c1m4.xlarge&#xff0c;4核16G、按固定带宽 10Mbs、100GB ESSD Entry系统盘&#xff0c;活动链接 aliyunfuwuqi.com/go/aliyun 活动链接打开如下图&a…

Docker搭建LNMP环境实战(02):Win10下安装VMware

实战开始&#xff0c;先安装 VMware 虚拟机。话不多说&#xff0c;上手就干&#xff01; 1、基本环境检查 1.1、本机Bios是否支持虚拟化 进入&#xff1a;任务管理器- 性能&#xff0c;查看“虚拟化”是否启用&#xff0c;如果已启用&#xff0c;则满足要求&#xff0c;如果未…

Linux 中的vim和gdb

目录 vim命令模式(常用)nyy-----复制n次np------黏贴n次u------撤销dd-----剪切/删除$-----将光标定位到当前行结尾^-----将光标定位到最开始。gg------将光标定位文本开始shiftg-----将光标定位文件尾。nshiftg----将光标定位到第n行上下左右键&#xff1a;h j k l (左下上右)…

故障诊断 | 一文解决,CNN-BiLSTM卷积神经网络-双向长短期记忆神经网络组合模型的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | 一文解决,CNN-BiLSTM卷积神经网络-双向长短期记忆神经网络组合模型的故障诊断(Matlab) 模型描述 CNN-BiLSTM卷积神经网络-双向长短期记忆神经网络组合模型是一种深度学习模型,结合了卷积神经网络(CNN)和双向长短期记忆网络(BiLSTM)的优点…

【Linux】详谈命令行参数环境变量

目录 一、浅谈命令行参数 二、环境变量 2.1环境变量的内涵以及理解 2.2PATH环境变量&#xff1a; 2.3输入程序名就能运行我们的程序 2.4系统中的环境变量 2.5导出环境变量 三、main函数的第三个参数 3.1获得环境变量的三种方法 四、本地变量 一、浅谈命令行参数 我们的m…

ubuntu arm qt 读取execl xls表格数据

一&#xff0c;ubuntu linux pc编译读取xls的库 1&#xff0c;安装libxls(读取xls文件 电脑版) 确保你已经安装了基本的编译工具&#xff0c;如gcc和make。如果没有安装&#xff0c;可以使用以下命令安装&#xff1a; sudo apt-update sudo apt-get install build-essentia…
最新文章