XML解析和Java的反射

tech2026-09-03  3

目录

XML解析XML概述XML的作用XML语法文档声明元素属性注释转义字符CDATA XML约束DTD约束Schema约束 dmo4j解析DOM解析原理API使用 反射类的初始化反射的概述Class获得方式无参构造有参构造无参构造,简化版获得实例私有构造 方法与执行添加set、get方法public 方法私有方法 字段(成员变量)与数据操作添加public字段public 字段的操作 BeanUtils工具类

XML解析

XML概述

XML全称为Extensible Markup Language,意思是可扩展的标记语言。

W3C在1998年2月发布1.0版本,2004年2月又发布1.1版本,但因为1.1版本不能向下兼容1.0版本,所以1.1很少有人使用。同时,在2004年2月W3C又发布了1.0版本的第三版。所以我们通常使用1.0版本。

XML的作用

存放数据 <?xml version="1.0" encoding="UTF_8"?> <persons> <person id="p001"> <name>张三</name> </person> <person id="p002"> <name>李四</name> </person> </persons>

类似于Java代码:

class Person{ String id; String name; } public void test(){ HashSet<Person> persons=new HashSet<Person>(); persons.add(new Person("p001","张三")); persons.add(new Person("p002","李四")); } 配置文件(properties) <?xml version="1.0" encoding="UTF-8"?> <beans> <bean className="com.baidu.User"> <property name="username" value="jack"></property> </bean> </beans>

类似于Java代码:

class Bean{ private String username; private String pws; //补全set\get方法 } import com.baidu.User; public static void main(){ Class clazz = Class.forName("com.baidu.User"); Object obj = clazz.newInstance(); Method method = clazz.getMethod("setUsername",String.class); method.invoke(obj,"jack"); }

XML语法

文档声明

XML文档声明格式: <?xml version="1.0" encoding="UTF-8"?> 1. 文档声明必须为<?xml 开头,以?>结束; 2. 文档说明必须从文档的0行0列位置开始; 3. 文档声明只有两个属性 version:指定XML文档版本。必须属性。 encoding:指定当前文档的编码,可选属性,默认值是utf-8

元素

元素 element 元素是XML文档中最重要的组成部分,普通元素的结构 开始标签、元素体、结束标签组成。元素体:元素体可以是元素,也可以是文本。空元素:空元素只有开始标签,而没有结束标签,但元素必须自己闭合。元素命名: 区分大小写, 不能使用空格,不能使用冒号 不建议以XML、xml、Xml开头格式化良好的XML文档,必须只有一个根元素

属性

属性 attribute <bean id="" className=""> 属性是元素的一部分,它必须出现在元素的开始标签中,不能出现在结束标签中属性的定义格式:属性名=属性值,其中属性值必须使用单引号或者双引号(最好使用双引号)一个元素可以有0-N个属性,但是一个元素不能出现同名属性,没有顺序之分属性名不能使用空格,冒号等特殊符号,且必须以字母开头

注释

XML得注释,以<!- 开始,以 -->结束。注释内容会被XML解析器忽略!

转义字符

转义字符 因为很多符号已经被XML文档结构锁使用,所以在元素体或属性值中想使用这些符号就必须使用转义字符,列如:"<"、">"、"&"。

CDATA

CDATA区 <![CDATA[ 任意内容 < > ]]>

当大量的转义字符出现在XML文档中时,会使xml文档的可读性大幅度降低,这是如果使用CDATA段就会好一些。

在CDATA段中出现的“<”、“>”、“””、“’”、“&”,都无需使用转义字符。这可以提高xml文档的可读性。

在CDATA段中不能包含“]]>”,即CDATA段结束定界符。

XML约束

在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束

常见的xml约束:DTD、Schema

DTD约束

DTD(Document Type Definition),文档类型定义,用来约束XML文档。规定XML文档中元素的名称,子元素的名称及顺序,元素的属性等。

开发中,我们很少自己编写DTD约束文档,通常情况我们都是通过框架提供的DTD约束文档,编写对应的XML文档,常见的框架使用DTD约束有:struts2、hibernte等。 通过提供的DTD“bean.dtd”编写xml

<?xml version="1.0" encoding="UTF-8"?> <!-- DTD实例文档。 模拟spring规范,如果开发人员需要在xml使用当前DTD约束,必须包括DOCTYPE。 格式如下: <!DOCTYPE beans SYSTEM "bean.dtd"> --> <!ELEMENT beans (bean*,import*) > <!ELEMENT bean (property*)> <!ELEMENT property (#PCDATA)> <!ELEMENT import (#PCDATA)> <!ATTLIST bean id CDATA #REQUIRED className CDATA #REQUIRED > <!ATTLIST property name CDATA #REQUIRED value CDATA #REQUIRED > <!ATTLIST import resource CDATA #REQUIRED>

大概了解一下

Schema约束

什么是Schema Schema是新的XML文档约束; Schema要比DTD强大很多,是DTD替代者 Schema本身也是XML文档,但Schema文档的扩展名为xsd,而不是xml Schema功能更加强大,数据类型更完善 Schema支持名称空间

Schema重点要求 与DTD一样,要求可以通过Schema约束文档编写xml文档,常见的框架使用Schema的有:spring等。

通过提供“bean-schema.xsd”编写文档

<?xml version="1.0" encoding="UTF-8"?> <!-- Schema实例文档。 模拟spring规范,如果开发人员需要在xml使用当前Schema约束,必须包括指定命名空间。 格式如下: <beans xmlns="http://www.spring.cn/bean" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.spring.cn/bean bean-schema.xsd" > --> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.spring.cn/bean" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.spring.cn/bean" elementFormDefault="qualified"> <!-- 声明根标签 --> <element name="beans"> <complexType> <choice minOccurs="0" maxOccurs="unbounded"> <element name="bean"> <complexType> <sequence minOccurs="0" maxOccurs="unbounded"> <element name="property"> <complexType> <attribute name="name" use="required"></attribute> <attribute name="value" use="required"></attribute> </complexType> </element> </sequence> <attribute name="id" use="required"></attribute> <attribute name="className" use="required"></attribute> </complexType> </element> <element name="import"> <complexType> <attribute name="resource" use="required"></attribute> </complexType> </element> </choice> </complexType> </element> </schema> 命名空间(语法) 什么是命名空间 如果一个XML文档中使用多个Schema文件,而这些Schema文件中定义了相同名称的元素时就会出现名字冲突。这就像一个Java文件中使用了import java.util.*和import java.sql.*时,在使用Date类时,那么就不明确Date是哪个包下的Date了。 总之名称空间就是用来处理元素和属性的名称冲突问题,与Java中的包是同一用途。如果每个元素和属性都有自己的名称空间,那么就不会出现名字冲突问题,就像是每个类都有自己所在的包一样,那么类名就不会出现冲突。 约束文档和XML关系 当W3C提出Schema约束规范时,就提供“官方约束文档”。我们通过官方文档,必须“自定义schema 约束文档”,开发中“自定义文档”由框架编写者提供。我们提供“自定义文档”限定,编写出自己的xml文档。

dmo4j解析

当将数据存储在xml后,我们就希望通过程序获得xml的内容,如果我们使用Java基础所学习的IO知识是可以完成的,不过你需要非常繁琐的操作才可以完成,且开发中会遇到不同的问题(只读、读写)。人们为不同问题提供不同的解析方式,并提交对应的解析器,方便开发人员操作xml。

开发中比较常见的解析方式有三种,如下: 1. DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象。 优点:元素与元素之间保存结构关系,故可以进行增删改查操作。 缺点:xml文档过大,可以出现内存溢出显现。 2. SAX:是一种速度更快,更有效的方法。它逐行扫描,一遍扫描一遍解析。 并以事件驱动的方式进行具体解析,每执行一行,都将触发对应的事件。 优点:处理速度快,可以处理大文件 缺点:只能读,逐行后将释放资源。 3. PULL:Android内置的xml解析方式,类似SAX。 解析器:就是根据不同的解析方式提供的具体实现,有的解析器操作过于繁琐,为了方便开发人员,有提供易于操作的解析开发包。常见的解析开发包

JAXP:sun公司提供支持DOM和SAX开发包

JDom:dom4j兄弟

jsoup:一种处理HTML特定解析开发包

dom4j:比较常用的解析开发包,hibernate底层采用。

DOM解析原理

XML DOM将整个XML文档加载到内存,生成一个DOM树,并获得一个Document对象,通过Document对象就可以对DOM进行操作

DOM中的核心概念就是节点,在XML文档中的元素、属性、文本等,在DOM中都是节点。

API使用

dom4j 必须使用核心类SacReader加载xml文档获得Document,通过Document对象获得文档的根元素,然后就可以操作了。

常用API如下:

SaxReader对象 read(…)加载执行xml文档Document对象 getRootElement()获得根元素Element对象 elements(…)获得指定形成的所有子元素,可以不指定名称 element(…)获得指定名称的第一个子元素, 指定名称 getName() 获得当前元素的元素名 attributeValue(…) 获得指定属性名的属性值 elementText(…) 获得指定名称子元素的文本值 getText()获得当前元素的文本内容 public static void main(String[] args) throws Exception { SAXReader sax = new SAXReader(); // 加载执行XMl文档 Document document = sax.read( "D:\\Java\\Java8\\day0903\\beans.xml"); // 获得根元素 Element rootElement = document.getRootElement(); // 获得指定名称对的所有字元素。可以不指定名称 List<Element> list = rootElement.elements(); for(Element element :list){ List<Element> list1 = element.elements(); System.out.println(element.attributeValue("id")); System.out.println(element.attributeValue("className")); for (Element e : list1) { System.out.println(e.attributeValue("user")); System.out.println(e.attributeValue("value")); } } }

benans.xml

<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="001" className="com.baidu.demo.User"> <property name="user" value="jacl"></property> <property name="user" value="rose"></property> </bean> <bean id="002" className="com.baidu.demo.Admin"> <property name="user" value="admin"></property> <property name="user" value="write"></property> </bean> </beans>

反射

类的初始化

创建类的实例类的静态变量,或者为静态变量赋值类的静态方法使用反射方式来强制创建某个类或者接口对应的java.lang.Class对象初始化某个类的子类直接使用java.exe命令来运行某个主类

目前为止我们已经知道把class文件加载到内存做了哪些事情,那么,如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件内容呢?

反射的概述

Java反射机制是在运行状态中(和编译无关),对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。(框架的灵魂就是反射)

要想解刨一个类,必须先要获取到该类的字节码文件对象,而解刨这个类,使用的就是Class类中的方法,所以先要获取到每一个字节码文件所对应的Class类型对象。

类的加载器把要运行的类装进内存 类的加载器有三个: (1)引导类加载器:由C语言编写(加载使用的核心类库) (2)扩展类加载器:Java语言编写(加载JDK的扩展类库) (3) 应用类加载器:Java语言编写(加载我们写的类) Java.lang.ClassLoader表示类的加载器的对象 一个类智能被加载一次,不能被加载两次,(java类加载的双亲委派模型) 对象都是由类产生的

类的加载器,负责运行的类加载到内存 一件事情:创建一个对象,创建A.Class文件的对象

Class获得方式

//1.通过类型获得 // 语法:类名.class // 应用场景:确定类型等 Class classz=Bean.class //2 通过实例对象获得 // 语法:变量.getClass() // 应用场景:在方法内部通过参数获得类型等 Bean bean = newBean(); Class clazz2 = bean.getClass(); //3 通过字符串获得 // 语法:Class.forName("全限定类名") // 应用场景:通过配置获得字符串等 Class clazz3 = Class.forName("com.baidu.Bean");

无参构造

无参构造,并获得实例

public void test DefaultCons() throws Exception{ //无参构造 , 并实例化 //1获得Class Class beanClass = Bean.class; //2获得构造 -- 没有形参 Constructor constructor = beanClass.getConstructor(); //3 实例对象,没有实参 Object bean = constructor.newInstance(); System.out.println(bean); /* 结果: * 无参构造 * com.baidu.Bean@8c5488 */ }

有参构造

有参构造,并获得实例

publicvoidtestParamCons() throws Exception{ //有参构造 , 并实例化 //1获得Class ClassbeanClass = Bean.class; //2获得构造 -- 两个字符串形参 -- Bean(String id, String className) Constructorconstructor = beanClass.getConstructor(String.class,String.class); //3 实例对象,两个字符串实参 Object bean = constructor.newInstance("ArrayListId","java.util.ArrayList"); System.out.println(bean); /* 结果: * 有参构造:ArrayListId,java.util.ArrayList * com.baidu.Bean@101acff */ }

无参构造,简化版获得实例

无参构造,简化版获得实例

publicvoidtestDefaultSimple() throws Exception{ //无参构造 , 简化版 //1获得Class Class beanClass = Bean.class; //2 直接获得实例对象,两个字符串实参 Object bean = beanClass.newInstance(); System.out.println(bean); /* 结果: * 无参构造 * com.baidu.Bean@101acff */ }

私有构造

修改Bean添加私有构造

getConstructor()使用该方法将无法获得私有方法,程序运行抛异常

没有使用setAccessible(true) ,将抛异常

publicvoidtestPrivateCons() throws Exception{ //私有构造 //1获得Class Class beanClass = Bean.class; //2获得构造 -- 两个字符串形参 -- Bean(String id, String className) // * getConstructor() 将抛异常java.lang.NoSuchMethodException // * getDeclaredConstructor可以获得私有构造 Constructor constructor = beanClass.getDeclaredConstructor(String.class); //暴力访问 constructor.setAccessible(true); //3 实例对象,两个字符串实参 Object bean = constructor.newInstance("userId"); System.out.println(bean); /* 结果: * 有参构造:userId * com.baidu.Bean@8c5488 */ }

方法与执行

添加set、get方法

//Bean类提供setter和setter方法 public String getId(){ Ststem.out.println("getId方法执行"); return id; } public void setId(String id) { System.out.println("setId方法执行:" + id); this.id = id; }

public 方法

public void testMethod() throws Exception{ //1 获得Class Class clazz = Class.forName("com.baidu.Bean"); //2 获得实例 ,相当于 Object obj = new Bean(); Object obj = clazz.newInstance(); //3 操作setId方法 //3.1 获得的方法,一个形参 // * 格式:getMethod(方法名,参数列表) Method setMethod = clazz.getMethod("setId", String.class); //3.2 执行方法,一个实参 Object setReturnObj = setMethod.invoke(obj, "我是参数"); System.out.println("set方法返回值:" + setReturnObj); /* 运行结果: * 无参构造 * setId方法执行:我是参数 * set方法返回值:null */ System.out.println("---------------"); //4 操作getId方法 (巩固) // 3.1 获得方法,没有形参 Method getMethod = clazz.getMethod("getId"); // 3.2 执行方法,没有实参 Object getReturnObj = getMethod.invoke(obj); System.out.println("get方法返回值:" + getReturnObj); /* 运行结果: * getId方法执行 * get方法返回值:我是参数 */ }

私有方法

private String show(){ System.out.println("私有方法执行"); return "Bean["+id+","+className+"]"; }

getMethod() 使用该方法将无法获得私有方法,程序运行抛异常。

没有使用setAccessible(true) ,将抛异常

publicvoidtestPrivateMethod() throws Exception{ //1 获得Class Class clazz = Class.forName("com.baidu.Bean"); //2获得构造 -- 两个字符串形参 -- Bean(String id, String className) Constructor constructor = clazz.getConstructor(String.class,String.class); //3 实例对象,两个字符串实参 Object bean = constructor.newInstance("ArrayListId","java.util.ArrayList"); //3获得方法 -- 私有方法 -- private String show() // * getMethod(方法名,形成列表) 将抛异常java.lang.NoSuchMethodException // * getDeclaredMethod(方法名,形成列表) 可以获得私有构造 Method showMethod = clazz.getDeclaredMethod("show"); //暴力访问 showMethod.setAccessible(true); // 4 执行方法,没有实参 Object getReturnObj = showMethod.invoke(bean); System.out.println("show方法返回值:" + getReturnObj); /* 运行结果: * 有参构造:ArrayListId,java.util.ArrayList * 私有方法执行 * show方法返回值:Bean[ArrayListId, java.util.ArrayList] */ }

字段(成员变量)与数据操作

添加public字段

public class Bean{ private String id; private String className; public String description; }

public 字段的操作

publicvoidtestField() throws Exception{ /* 获得实例,为public字段赋值、获取值 * public String description; */ //1 获得Class Class clazz = Class.forName("com.baidu.Bean"); //2 获得实例 ,相当于 Object obj = new Bean(); 但是必须是无参构造 Object bean= clazz.newInstance(); //3 操作字段,进行赋值,public String description; //3.1 获得的字段,一个形参 // * 格式:getField(字段名) Field descriptionField = clazz.getField("description"); //3.2 为对象的字段赋值 descriptionField.set(bean, "Bean的描述"); //3.3 获取对象的字段值 Object fieldReturnObj = descriptionField.get(bean); System.out.println("description字段返回值:"+fieldReturnObj); // 操作私有属性 // 格式 getDeclaredField(属性名) Field age = aClass.getDeclaredField("age"); age.setAccessible(true); age.set(o,"18"); System.out.println("私有属性age的值:"+age.get(o)); }

BeanUtils工具类

BeanUtils是Apache commons组件的成员之一,主要用于简化JavaBean封装数据的操作,它可以给JavaBean封装一个字符串数据,也可以将一个表单提交的所有数据封装到JavaBean中。

使用需要导入包:

commons-beanutils-1.8.3.jar commons-logging-1.1.1.jar

工具类常用类有两个:BeanUtils、ConvertUtils。BeanUtils用于封装数据,ConvertUtils用于处理类型转换。

提供JavaBean User,并提供对应的构造方法 public class User { private String id; private String username; private String pwd; @Override public String toString() { return "User{" + "id='" + id + '\'' + ", username='" + username + '\'' + ", pwd='" + pwd + '\'' + '}'; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } } 设置属性 User user=new User(); /** * 设置属性 */ BeanUtils.setProperty(user,"id","001"); BeanUtils.setProperty(user,"username","zhangsan"); BeanUtils.setProperty(user,"pwd","1234"); System.out.println(user); 获得属性 /** * 获得属性 */ String id = BeanUtils.getProperty(user, "id"); String username = BeanUtils.getProperty(user, "username"); System.out.println(id); System.out.println(username); 封装表单数据,populate 方法 /** * 封装表单数据 */ Map<String,Object> properties=new HashMap<>(); properties.put("id","1001"); properties.put("username","lisi"); properties.put("pwd","111"); //批量设置属性 BeanUtils.populate(user,properties); System.out.println(user);
最新回复(0)