大数据------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("学习");}@Overridepublic 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" ?>
<schemaxmlns="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.hqwc.cn/news/562665.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

列车票务信息管理系统设计与实现|jsp+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW调试部署环境&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java…

多线程和线程同步

文章目录 进程和线程线程的操作线程创建线程退出线程回收线程分离线程取消和ID比较 线程同步互斥锁死锁读写锁条件变量信号量 进程和线程 线程是轻量级的进程&#xff0c;在Linux环境下线程的本质还是进程。 在计算机上运行的程序是一组指令及指令参数的组合&#xff0c;指令按…

混合云构建-使用 Azure ExpressRoute 建立从本地到 Azure 虚拟网络的专用连接

如果有大量业务数据需要在本地数据中心和azure私有网络进行传输&#xff0c;同时保证带宽和时延的情况需要使用 ExpressRoute 设置从本地网络到 Azure 中的虚拟网络的专用连接。以下是实操步骤供参考&#xff1a; 一、创建和预配 ExpressRoute 线路 登录 Azure 门户。 在页面…

Vue3 + Django 前后端分离项目实现密码认证登录

1、功能需求 通常中小型前后端项目&#xff0c;对安全要求不高&#xff0c;也可以采用密码认证方案。如果只用django来实现非常简单。采用 Vue3 前后端分离架构&#xff0c;实现起来稍繁琐一点&#xff0c;好处是可以利用各种前端技术栈&#xff0c;如element-plus UI库来渲染…

ETH Gas 之 Base Fee Priority Fee

前情回顾 ETH网络 之 Gas EIP-1559 EIP-1559 EIP-1559是以太坊改进提案&#xff08;Ethereum Improvement Proposal&#xff09;&#xff0c;旨在改进以太坊的交易费用机制。该提案引入了一种新的交易费用模型&#xff0c;以提高交易费用的可预测性和网络的效率。我们本文各…

使用 PyOpenGL 进行 2D 图形渲染总结

一、说明 OpenGL是一个广泛使用的开放式跨平台实时 3D 图形库&#xff0c;开发于二十多年前。它提供了一个低级API&#xff0c;允许开发人员以统一的方式访问图形硬件。在开发需要硬件加速且需要在不同平台上运行的复杂 2D 或 3D 应用程序时&#xff0c;它是首选平台。它可以在…

文件上传二—WEB攻防-PHP应用文件上传中间件CVE解析第三方编辑器已知CMS漏洞

演示案例&#xff1a; PHP-中间件-上传相关-Apache&NginxPHP-编辑器-上传相关-第三方处理引用PHP-CMS源码-上传相关-已知识别到利用 #PHP-中间件-上传相关-Apache&Nginx 复现漏洞环境&#xff1a;vulhub &#xff08;部署搭建看打包视频&#xff09; 由于PHP搭建常用中…

CentOS系统部署YesPlayMusic播放器并实现公网访问本地音乐资源

文章目录 1. 安装Docker2. 本地安装部署YesPlayMusic3. 安装cpolar内网穿透4. 固定YesPlayMusic公网地址 本篇文章讲解如何使用Docker搭建YesPlayMusic网易云音乐播放器&#xff0c;并且结合cpolar内网穿透实现公网访问音乐播放器。 YesPlayMusic是一款优秀的个人音乐播放器&am…

【ORB-SLAM3】在 Ubuntu20.04 上编译 ORM-SLAM3 并使用 D435i 运行测试

【ORB-SLAM3】在 Ubuntu20.04 上编译 ORM-SLAM3 并使用 D435i 运行测试 1 Prerequisites1.1 C11 or C0x Compiler1.2 Pangolin1.3 OpenCV1.4 Eigen3 2 安装 Intel RealSense™ SDK 2.02.1 测试设备2.2 编译源码安装 (Recommend)2.3 预编译包安装 3 编译 ORB-SLAM34 使用 D435i …

深入理解Redis的Sentinel机制

Sentinel简述 Sentinel为了解决什么问题&#xff1f; Sentinel&#xff08;哨岗、哨兵&#xff09;是Redis的高可用性&#xff08;high availability&#xff09;解决方案。 我们知道Redis 的主从复制模式可以将主节点的数据改变同步给从节点&#xff0c;这样从节点就可以起…

K8S之DaemonSet控制器

DaemonSet控制器 概念、原理解读、应用场景概述工作原理典型的应用场景介绍DaemonSet 与 Deployment 的区别 解读资源清单文件实践案例 概念、原理解读、应用场景 概述 DaemonSet控制器能够确保K8S集群所有的节点都分别运行一个相同的pod副本&#xff1b; 当集群中增加node节…

SpringSecurity6.x

文章目录 一.什么是SpringSecurity二.SpringSecurity的特征三.SpringSecurity的第一个例子3.1 创建SpringBoot项目3.2 创建IndexController3.3 创建index.html3.4 启动项目3.5 Spring Security默认做了什么 四.SpringSecurity的整体架构4.1 Filter4.2 DelegatingFilterProxy4.3…