JAVA EE(六)—— XML(XML文件概述、XML文件的約束、XML文件的解析、XML的反射)

一、XML文件概述

1、XML文件

(1)介紹

  • 可擴展標記語言,標準通用標記語言的子集,是一種用於標記電子文件使其具有結構性的標記語言。XML 的設計宗旨是傳輸數據,而不是顯示數據。
  • XML文件可以直接使用解析器解析(IE瀏覽器)。

(2)作用

  • 用於存放數據
  • 用於配置文件

(3)基本語法
① 文檔聲明

<?xml version="1.0" encoding="utf-8" standalone="no" ?>
  • 文檔聲明必須以<?xml開頭,以?>結束,中間沒有空格,? 表示開始或者結束;
  • 文檔聲明必須從文檔的0行0列位置開始;
  • xml 表示文件是xml格式,文件後綴是.xml;
  • 文檔聲明只有兩個屬性,格式:屬性名=“屬性值”,屬性值必須使用"";
  • version 指定XML文檔版本。必須屬性,目前只有1.0版本;
  • encoding 表示指定當前文檔的編碼。可選屬性,默認值爲UTF-8;
  • standlone 表示文件是否獨立,yes表示是,no表示不是。

2、XML 文件示例

<?xml version="1.0" encoding="utf-8" ?>
<中國>
	<重慶>
		<江北>江北</江北>
		<渝北>渝北</渝北>
	</重慶>
	<四川>
		<成都>成都</成都>
		<南充>南充</南充>
	</四川>
</中國>

② 元素(Element)和標籤(Tag)

  • 以尖括號括起來的就是元素(和html一致,但所有的標籤名可以自定義)
  • 元素體可以寫也可以不寫
  • 元素體裏面可以是文本也可以寫其他標籤
  • 元素命名:
    區分大小寫
    不能使用空格,冒號第特殊字符
    不建議以XML、xml、Xml開頭
  • 格式化良好的xml文檔,必須只有一個根元素

③ 屬性(attribute):

  • 屬性是元素的一部分,寫在開始標籤的尖括號裏面,並且多個屬性用空格隔開
  • 屬性的定義格式:屬性名=“屬性值”,其中屬性值必須使用單引號或雙引號
  • 一個元素可以有0-N個屬性,但一個元素中不能出現同名屬性
  • 屬性名不能使用空格、冒號等特殊字符,且必須以字母開頭

④ 註釋

<!-- 註釋內容 -- >

⑤ CDATA區

  • 原始數據,將所有的內容都作爲原始數據。
  • 語法:<![CDATA][需要轉譯的字符]>

⑥ 轉義字符:

&lt; < 小於
&gt; > 大於
&amp; &
&apos; 單引號
&quot; " 雙引號

⑦ 指令:引入標籤 xml-stylesheet
如:在一個xml中引入css文件:

<?xml-stylesheet type="text/css" href="css文件名.css" ?>

二、XML文件的約束

1、約束概述

(1)介紹

  • 按照一定的規則編寫xml文件,指定位置只能存放固定的標籤。

(2)DTD 約束

<!ELEMENT 根標籤名 (數據類型或者子標籤,如果標籤名是相同的(標籤+))>
<!ELEMENT 父標籤名 (子標籤名不同的情況下,直接列舉,中間用逗號隔開)>
<!ELEMENT 子標籤名 (#PCDATA)>
引入DTD約束:
<!DOCTYPE 根標籤名 SYSYTEM "引入的文件">

(3)三種DTD約束

  • 內部DTD,在XML文檔內部嵌入DTD,只對當前XML有效
  • 外部DTD-本地DTD,DTD文檔在本地系統上,公司內部自己項目使用
  • 外部DTD-公共DTD,DTD文檔在網絡上,一般由框架提供

2、DTD 約束示例

xml文件導入約束
① xml文件

<?xml version="1.0" encoding="utf-8" standalone="no" ?>
<!DOCTYPE 信息 SYSTEM "users.dtd">
<信息>
	<用戶>
		<姓名>張三</姓名>
		<性別></性別>
		<年齡>12</年齡>
	</用戶>
	<用戶1>
		<姓名>李四</姓名>
		<性別></性別>
		<年齡>23</年齡>
	</用戶1>
</信息>

② dtd 約束文件

<!ELEMENT 信息 (用戶+)>
<!ELEMENT 用戶 (姓名,性別,年齡)>
<!ELEMENT 姓名 (#PCDATA)>
<!ELEMENT 性別 (#PCDATA)>
<!ELEMENT 年齡 (#PCDATA)>

IE瀏覽器默認關閉了校驗文件,需要使用腳本文件將其打開:

  • 創建IE瀏覽器中的ActiveXObject對象,使用其對象中的控件(Microsoft.XMLDOM);
  • 設置validateOnParse變量的值爲false;
  • 讀取要校驗的xml文件:load(“xml文件”)。
<html>
	<head>
		<script>
			var xmldom = new ActiveXObject("Microsoft.XMLDOM");
			xmldom.validateOnParse = true;
			xmldom.load("users.xml");
			//輸出錯誤原因和行數
			document.write(xmldom.parseError.reason+"<br />");
			document.write(xmldom.parseError.line);
		</script>
	</head>
	<body>
	</body>
</html>

三、XML文件的解析

1、XML文件解析概述

(1)介紹
在java中提供了三種解析xml文件的開發包: JAXP Jdom dom4j,後面兩個其實可以合成一個。

2、DOM 解析

(1)介紹

  • DOM解析方式屬於JAXP開發包:DocumentBuilderFactory抽象類
  • DOM解析首先將一個xml文件中的內容全部解析到內存中,然後在內存中構建Document樹。
  • 標籤解析爲Element,屬性解析爲 attr,內容解析爲 text,但不管是屬性或者是標籤或者是文本內容都會解析爲Node節點。

(2)優缺點
優點:元素與元素之間保留結構關係,可以進行增刪改查操作,其查詢比較快;
缺點:佔用內存,XML文檔過大時,可能出現內存溢出現象;

(3)DOM 解析過程:
① 創建解析xml文件的工廠:DocumentBuilderFactory 對象;
② 根據獲取的工廠得到解析xml文檔的解析器:DocumentBuilder 對象;
③ 獲取解析器,真正開始解析xml文件:使用對象調用parse方法解析xml文檔。

DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File("文件路徑")); 

④ 解析節點屬性名、節點屬性值
對於節點個數未知

獲取所有節點的集合
Nodelist klist = document.getElementByTagName("節點");
使用for循環通過item()方法遍歷每個節點
Node node= list.item(i);
並獲取節點的所有屬性集合
NameNodeMap attrs = node.getAttribute();
使用for循環通過item()方法遍歷節點的屬性
Node attr = attrs.item(j);
獲取屬性名
attr.getNodeName();
獲取屬性值
attr.getNodeValue();

對於已經找到book節點只有一個ID屬性可用以下方法,使用強制類型轉換

Element node= (Element) list.item(i);
String attrValue = node.getAttribute("屬性名");
獲取id屬性的屬性值:attrValue;

⑤ 解析子節點名、子節點值

Nodelist childNodes = node.getChildNodes();
遍歷childNodes獲取每個節點的節點名和節點值,其中節點中包括空格和換行符
使用for循環遍歷
區分text類型和element類型的node,通過getNodeType();
childNodes.item(k).getNodeType() == Node.ELEMENT_NODE
childNodes.item(k).getNodeName();
獲取子節點的節點值
需要先獲取book子節點,然後獲得子節點的子節點的節點值
chileNodes.item(k).getFirstChild().getNodeValue(); 

getNodeValue()getTextContent() 的區別

ChildNodes.item(i).getFirstChild().getNodeValue()與ChildNodes.item(i).getTextContent()的
區別:子節點中還包含其他子節點時,後者可以把子節點的值都顯示出來。
getTextContent() 獲取節點中的text內容(即節點值).
getNodeType()有text、element、attr三個
而Element如果要獲取值,必須讀取它的子節點,<name>content</name>認爲content是name的子節點;
兩種方法:
getFirstChild().getNodeName();(獲取子節點再獲取值)
getTextContent();(獲取content方法)

⑥ 將內存中的xml修改的數據更新到文件中,使用Transformer()。

//將修改後的數據寫到文件中
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer former = transFactory.newTransformer();
former.transform(new DOMSource(document), new StreamResult(new File(file)));

(4)DOM解析增刪改查示例

public class XMLDemo1 {
	public static void main(String[] args) throws Exception {
		XMLDemo1 xml1 = new XMLDemo1();
		xml1.read1();
		//xml1.read2();
		//update()
		//delete()
		//add()
	}
	//定義一個方法,獲取標籤中的文本內容(查詢)
	public void read() throws Exception {
		//獲取工廠
		DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance();
		//根據工廠,獲取解析器
		DocumentBuilder builder = factory.newDocumentBuilder();
		//調用parse開始解析xml文件
		Document document = builder.parse(new File("D:\\JAVA.Document\\xml文件\\users.xml")); 
		
		//獲取節點
		NodeList list = document.getElementsByTagName("姓名");
		//獲取標籤節點
		Node node = list.item(0);
		//獲取標籤中的文本內容
		String userName = node.getTextContent();
		System.out.println(userName);
	}
	//定義一個方法,獲取標籤中的id屬性的值(查詢)
	public void read2() throws Exception {
		//獲取工廠
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		//獲取解析器
		DocumentBuilder builder = factory.newDocumentBuilder();
		//解析xml文件
		Document document = builder.parse(new File("D:\\JAVA.Document\\xml文件\\users.xml"));
		//獲取節點
		NodeList list = document.getElementsByTagName("姓名");
		//獲取標籤節點
		Element node = (Element)list.item(0);
		String text = node.getAttribute("id");
		System.out.println(text);
	}
	//更改
	public void update() throws Exception{
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document document = builder.parse(new File("src/users.xml"));
		//現獲取內容的父節點,再修改值
		NodeList list = document.getElementsByTagName("姓名");
		Node node = list.item(0);
		//這種修改只是修改了內存中的對象,沒有將修改後的數據寫入到文件中
		node.setTextContent("張三");
		//將修改後的數據寫到文件中
		TransformerFactory transFactory = TransformerFactory.newInstance();
		Transformer former = transFactory.newTransformer();
		former.transform(new DOMSource(document), new StreamResult(new File("src/users.xml")));
	}
	//刪除
	public void delete() throws Exception {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document document = builder.parse(new File("src/users.xml"));
		
		//獲取節點
		Node node = document.getElementsByTagName("身高").item(0);
		//刪除節點:只能先獲取父節點,再刪除自己
		node.getParentNode().removeChild(node);
		
		//將修改後的數據寫到文件中
		TransformerFactory transFactory = TransformerFactory.newInstance();
		Transformer former = transFactory.newTransformer();
		former.transform(new DOMSource(document), new StreamResult(new File("src/users.xml")));
	}
	//增加
	public void add()  throws Exception {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document document = builder.parse(new File("src/users.xml"));
		
		Element node = document.createElement("用戶");
		//得到父節點
		Node parentNode = document.getElementsByTagName("信息").item(0);
		//用戶掛載到信息
		parentNode.appendChild(node);
		Element nameNode = document.createElement("姓名");
		nameNode.setTextContent("王五");
		node.appendChild(nameNode);
	}
}

3、SAX 解析

(1)SAX解析與DOM解析的差異

  • 在使用 DOM 解析 XML 文檔時,需要讀取整個 XML 文檔,在內存中構架代表整個 DOM 樹的Doucment對象,從而再對XML文檔進行操作。此種情況下,如果 XML 文檔特別大,就會消耗計算機的大量內存,並且容易導致內存溢出。
  • SAX解析允許在讀取文檔的時候,即對文檔進行處理,而不必等到整個文檔裝載完纔會文檔進行操作。從xml文件的最上面開始逐一解析每一個節點,可以對於每一個節點做出其處理方式,逐一解析到內存中,尋找到符合要求的節點則結束解析。

(2)SAX解析優缺點
優點:處理速度快,可以處理大文件,節省內存。
缺點:只能讀,逐行解析後將釋放資源,其查詢比較慢。

(3)SAX解析步驟:
① 創建SAX解析工廠、解析器對象、XML的讀取器

SAXParserFactory spf = SAXParserFactory.newInstance();       
SAXParser sp = spf.newSAXParser();
XMLReader xmlReader = sp.getXMLReader();

② 設置讀取器的事件處理器、解析xml文件

xmlReader.setContentHandler(new BookParserHandler());  
xmlReader.parse("xml文件路徑");

③ 創建 ParserHandler 的類

並且重寫startDocument、endDocument、startElement、endElement的方法
startDocument:解析xml文件的標籤開始
endDocument:解析xml文件的標籤開始
startElement:解析xml文件開始
endElement:解析xml文件開始
characters:解析xml節點屬性
可以定義全局變量int index = 0;爲在main中設置解析每本書

④ 解析xml文件的節點屬性

調用startElement(String url,String localName,String qName,Attributes attributes)方法;
開始解析book元素的屬性
if(qName.equals("book")){
	//已知book元素下屬性名稱,根據屬性名稱獲取屬性值
	String value = attributes.getValue("id");
	//不知道book元素下屬性名和個數,可以使用for循環遍歷book下屬性
	屬性名:attributes.getQName(i);
	屬性值:attributes.getValue(i);
}
判斷解析book是否結束
在endElement方法中使用
if(qName.equals("book")){
	結束該本書的解析 
}

⑤ 解析xml文件的節點名和節點間文本

解析文件的節點名,可以判斷
if(!qName.equals("book")&&!qName.equals("bookstore")){
	打印出解析節點值
}
	
新建Characters方法
public void characters(char[] ch, int start, int length){
	String value = new String(ch, start, length);
	if(!value.trim().equals("")){//去掉文本後的空格
		value;//可以得到節點值
	}
}

⑥ 使用xml解析將xml的內容和結構存入Java對象

新建book類,創建所有屬性,並創建get和set方法
並在解析book值時,將得到的屬性值set進book的方法中。

4、DOM4J解析

(1)DOM4J解析示例
① DOM4J 解析節點

解析books.xml文件
(1)創建SAXReader的對象reader   
SAXReader reader = new SAXReader();
(2)通過reader對象的read方法加載book.xml文件,獲取document對象
Document document = reader.reader(new File("src/res/book.xml"));
(3)通過document對象獲取根節點bookstores
Element bookStore = document.getRootElement();
(4)通過element獨享的elementIterator方法獲取迭代器
Iterator it = bookStore.elementIterator();
(5)遍歷迭代器,獲取根節點中的信息(書籍)
while(it.hasNext()){
	Element book = (Element)it.next();
	獲取book的屬性名和屬性值
	List<Attribute>bookAttrs = book.attribute();
	遍歷每本書中的所有節點
	for (Attribute attr : bookAttrs){
		獲取屬性名
		attr.getName(); 
		獲取屬性值
		attr.getValue();
	}
}

② DOM4J 解析子節點信息

遍歷book子節點依然可以使用elementIterator方法獲取迭代器
Iterator itt = book.elementIterator(); 
while(itt.hasNext()){
	Element bookChild = (Element)itt.next();
	獲取節點名
	bookChild.getName(); 
	獲取節點值
	bookChild.getStringValue();      
}

5、JDOM 解析

(1)JDOM 解析步驟
① 導入jar包

② 準備工作

(1)創建一個SAXBuilder對象
SAXBuilder saxBuilder = new SAXBuilder;
(2)創建一個輸入流,將xml文件加載到輸入流
InputStream in = new FileInputStream("src/res/books.xml");
(3)通過saxBuilder的build方法,將輸入流加載到saxBuilder
Document document = saxBuilder.build(in);
(4)通過document對象獲取xml的根節點
Element rootElement = document.getRootElement();
(5)獲取根節點下的子節點的List集合
List<Element> bookList = rootElement.getChildren();

③ 解析節點屬性

(1)採用for循環遍歷bookList中每個元素
for(Element book : bookList){
	bookList.indexof(book);可以獲取當前該本書處於bookList中的位置
}  
(2)解析book的屬性
List<Attribute> attrList = book.getAttributes();
遍歷attrList(針對不清楚book節點下屬性的名字及數量)

   book.getAttributeName("");
   book.getAttributeValue("屬性名");
   用以上兩種方法可以獲取已知的節點屬性名和屬性值
        
   for(Attribute attr : attrList){
	獲取屬性名
	String attrName = attr.getName();
	獲取屬性值
	String attrValue = attr.getValue();
   }
(3)解析子節點名和子節點值
List<Element> bookChilds = book.getChildren();
遍歷bookChilds 
for (Element child : bookChilds ){
	child.getName();
	child.getValue();
}

④ 解析時亂碼的處理

(1)encoding編解碼方式更改
(2)(1)無法解決時
在創建輸入流時可以改爲使用InputStreamReader( ,"UTF-8");
通過使用字符流的方式

⑤ JDOM中存儲Book對象

(1)創建book對象
Book bookEntity = new Book();

(2)判斷id屬性
if(attrName.equals("id")){
	bookEntity.setId(attrValue)
}

(3)判斷屬性名
if(child.getName().equals("name")){
	bookEntity.setName(child.getValue());
}
通過以上方法對每一個屬性都進行判斷

(4)創建一個ArrayList來存儲對象
private static ArrayList<book> booksList = new ArrayList<book>();
booksList.add(bookEntity);
bookEntity = null;

⑥ JDOM中jar包的引用
若按之前的操作,有可能會發生由於工程的導入導出操作導致jar丟失,此時可以按一下操作解決。
在package下new一個folder,然後將放在桌面的jar包複製進剛剛new的folder中去,然後在package中繼續build path,然後選中剛剛複製的jar 包。

6、幾種解析方法比較

(1)解析方法分類

  • 基礎解析方式:DOM、SAX
  • 擴展解析方式:JDOM、DOM4J

(2)DOM解析過程:

  • 一次性將整個xml文件加載到內存中,形成DOM樹
  • 優點:形成了樹結構,直觀好理解,代碼更容易編寫解析過程中樹結構保留在內存中,方便修改
  • 缺點:當xml文件較大時,對內存消耗比較大,容易影響解析性能並造成內存溢出

(3)SAX解析過程:

  • 逐條語句判斷解析(基於時間的解析方式)
  • 優點:採用事件驅動模式,對內存消耗比較小適用於只需要處理xml中數據時
  • 缺點:不易編碼,很難同時訪問同一個xml中的多處不同數據

(4)JDOM解析過程:

  • 僅使用具體類而不使用接口
  • API大量使用了Collections類

(5)DOM4J解析過程:

  • JDOM的一種智能分支,它合併了許多超出基本XML文檔表示的功能
  • DOM4J使用接口和抽象基本類方法,一個優秀的Java XML API具有性能優異、靈活性好、功能強大和極端易使用的特點,
  • 是一個開放源代碼的軟件

四、XML的反射

1、反射介紹

  • 獲取對象類,並解析出其組成部分(方法,屬性,構造方法)。
  • Java反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性。
  • 使用反射,可以在運行時對類Class、構造方法Constructor、普通方法Method、字段Field進行操作。

2、Class 對象
(1)getConstructor():獲取一個類的公共的構造方法

  • 獲取無參構造:getConstructor(null);
  • 獲取有參構造:getConstructor(數據類型.class);

調用到構造方法之後,想要使用反射創建對象,那麼就必須使用newInstance()方法,有參就需要傳遞參數,無參則不需要。

(2)getMethod():獲取一個類的公共的成員方法

  • 無參的方法:getMethod(null); 想要調用方法,需要使用invoke(對象, null); 方法
  • 有參的方法:getMethod(方法名, 數據類型.class);想要調用方法,需要使用invoke(對象, 參數列表); 方法

(3)getField():獲取一個公共的成員屬性

Field field = clazz.getField("成員屬性名"); 

想要使用到該屬性,使用field.get(對象)方法獲取其數據,field.set()能夠設置數據,如果是final成員屬性,需要使用setAccessible(true); 暴力破解

(4)getDeclaredConstructor():獲取一個類的私有的構造方法
(5)getDeclaredMethod():獲取一個類的私有的成員方法
(6)getDeclaredField():獲取一個私有的成員屬性
(4)、(5)、(6)都需要使用setAccessible(true);暴力破解

(7)使用反射創建字節碼
方式1:Class cla = Class.forName(“完整類名”);完整類名是指:包名.類名

Class clazz = Class.forName("com.study.Person");//必須類全名

方式2:Class cla = 類名.class;

Class clazz = Person.class;

方式3:Class cla = new 類名().getClass();

	//創建Peron對象
	Person p = new Person();
	//通過object繼承來的方法(getClass)獲取Person對應的字節碼文件對象
	Class clazz = p.getClass();

(8)反射示例
示例1:反射操作構造方法
① Person類

	public class Person {
		public String name;
		private int age;
		public Person() {
			System.out.println("這是無參構造");
		}
		public Person(String name) {
			System.out.println(name + "這是有參構造");
		}
		public Person(String name, int age) {
			System.out.println(name + "說這是有參構造,今年" + age + "歲");
		}
		private Person(int age) {
			System.out.println("私有構造方法" + age);
		}
		public void eat() {
			System.out.println("吃飯");
		}
		public void eat(String name) {
			System.out.println(name + "吃飯");
		}
		private void sleep() {
			System.out.println("睡覺");
		}
	}

② main函數類

public class Demo {
	@Test
	public void refect1() throws Exception{	//調用無參構造
		//1、使用反射,獲取字節碼對象
		Class cla = Class.forName("javaeestudy.num1.Person");
		//2、使用Class類中的getConstructor()方法,調用其類的公共無參構造方法
		Constructor constructor = cla.getConstructor(null);
		constructor.newInstance();
	}
	
	@Test
	public void refect2() throws Exception{	//調用有參構造
		//1、使用反射,獲取字節碼對象
		Class cla = Class.forName("javaeestudy.num1.Person");
		//2、使用Class類中的getConstructor()方法,調用其類的公共有參構造方法
		Constructor constructor = cla.getConstructor(String.class);
		constructor.newInstance("張三");
	}
	
	@Test
	public void refect3() throws Exception{	//調用有參構造
		//1、使用反射,獲取字節碼對象
		Class cla = Class.forName("javaeestudy.num1.Person");
		//2、使用Class類中的getConstructor()方法,調用其類的公共有參構造方法
		Constructor constructor = cla.getConstructor(String.class, int.class);
		constructor.newInstance("張三", 18);
	}
	
	@Test
	public void refect4() throws Exception{	//調用私有構造方法
		//1、使用反射,獲取字節碼對象
		Class cla = Class.forName("javaeestudy.num1.Person");
		//2、使用Class類中的getConstructor()方法,調用其類的私有構造方法
		Constructor constructor = cla.getDeclaredConstructor(int.class);
		//3、開啓暴力破解
		constructor.setAccessible(true);
		constructor.newInstance(18);
	}
}

示例2:反射對方法和屬性的操作

public class Demo2 {
	@Test
	public void method1() throws Exception {	//使用反射操作其類中的方法
		Class cla = Class.forName("javaeestudy.num1.Person");
		//參數(name, parameter),name表示方法名,parameter表示參數的數據類型
		Method method = cla.getMethod("eat", null);
		//invoke(obj, args)方法,使用對象調用方法,obj表示對象,args表示參數值
		method.invoke(cla.newInstance(), null);
	}
	@Test
	public void method2() throws Exception {	//操作有參方法
		Class cla = Class.forName("javaeestudy.num1.Person");
		//參數(name, parameter),name表示方法名,parameter表示參數的數據類型
		Method method = cla.getMethod("eat", String.class);
		//invoke(obj, args)方法,使用對象調用方法,obj表示對象,args表示參數值
		method.invoke(cla.newInstance(), "張三");
	}
	@Test
	public void method3() throws Exception {	//操作私有方法
		Class cla = Class.forName("javaeestudy.num1.Person");
		//參數(name, parameter),name表示方法名,parameter表示參數的數據類型
		Method method = cla.getDeclaredMethod("sleep", String.class);
		//開啓暴力拆解
		method.setAccessible(true);
		//invoke(obj, args)方法,使用對象調用方法,obj表示對象,args表示參數值
		method.invoke(cla.newInstance(), "張三");
	}
	@Test
	public void method4() throws Exception {	//操作公共屬性
		Class cla = Class.forName("javaeestudy.num1.Person");
		Field field = cla.getField("name");
		//獲取字段的值
		//obj參數,代表的是那個對象的字段
		String obj = (String) field.get(cla.newInstance());
		System.out.println(obj);
	}
	@Test
	public void method5() throws Exception {	//操作公共屬性
		Class cla = Class.forName("javaeestudy.num1.Person");
		Field field = cla.getDeclaredField("age");
		//開啓暴力拆解
		field.setAccessible(true);
		//獲取字段的值
		//obj參數,代表的是那個對象的字段
		int age = field.getInt(cla.newInstance());
		System.out.println(age);
	}
}

示例3:通過操作xml文件和反操作方法
① xml文件

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<package pname="javaeestudy.study">
	<clazz cname="Person">
		<method type="public">eat</method>
		<method type="private">sleep</method>
	</clazz>
</package>

② Person類

public class Person {
	//公共方法
	public void eat() {
		System.out.println("吃飯");
	}
	//私有方法
	private void sleep() {
		System.out.println("睡覺");
	}
}

③ main函數:操作類

public class Demo {
	public static void main(String[] args) throws Exception {
		new Demo2().method1();
	}
	//操作方法
	public void method1() throws Exception {
		Document document = getDocument();
		//獲取包名
		NodeList packageList = document.getElementsByTagName("package");
		Element packageNode = (Element)packageList.item(0);
		String text = packageNode.getAttribute("pname");
		
		//獲取類名
		NodeList clazzList = document.getElementsByTagName("clazz");
		Element clazzNode = (Element)clazzList.item(0);
		text = text + "." + clazzNode.getAttribute("cname");

		//使用反射,獲取字節碼對象
		Class cla = Class.forName(text);
		
		//獲取方法節點
		NodeList methodList = document.getElementsByTagName("method");
		
		for(int i = 0; i < methodList.getLength(); i++) {
			//獲取方法類型type
			Element typeNode = (Element)methodList.item(i);
			String typeText = typeNode.getAttribute("pname");
			//獲取方法名
			Node methodNode = methodList.item(i);
			String methodName = methodNode.getTextContent();
			
			Method method = null;
			if(typeText.equals("public")) {
				//使用Class類中的getMethod方法,調用其類的公共無參構造方法
				method = cla.getMethod(methodName, null);
			}else {
				//使用Class類中的getDeclaredMethod方法,調用其類的私有無參構造方法
				method = cla.getDeclaredMethod(methodName, null);
				//開啓暴力拆解
				method.setAccessible(true);
			}
			//invoke(obj, args)方法,使用對象調用方法,obj表示對象,args表示參數值
			method.invoke(cla.newInstance(), null);
		}
	}
	//獲取document方法
	public Document getDocument() throws Exception {
		//獲取工廠
		DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance();
		//根據工廠,獲取解析器
		DocumentBuilder builder = factory.newDocumentBuilder();
		//調用parse開始解析xml文件
		return builder.parse(new File("src\\javaeestudy\\study\\person.xml"));
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章