MyException - 我的异常网
当前位置:我的异常网» XML/SOAP » Android三种模式解析Xml

Android三种模式解析Xml

www.MyException.Cn  网友分享于:2015-02-08  浏览:0次
Android三种方式解析Xml

在Android中,常见的XML解析器分别为SAX解析器、DOM解析器和PULL解析器

SAX解析器:

SAX(Simple API for XML)解析器是一种基于事件的解析器,它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。

SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。

DOM解析器:

DOM是基于树形结构的的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。

由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。

PULL解析器:

PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中,我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器。


下面我们做一个demo来具体实现下

首先在Androidmanifest.xml中添加权限,此权限用于输出xml文件

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

departments.xml被解析的xml文件

<?xml version="1.0" encoding="utf-8"?>
<department>
    <person>
        <id>1001</id>
        <name>张三</name>
        <salary>8000.0</salary>
        </person>
     <person>
        <id>1002</id>
        <name>李四</name>
        <salary>9000.0</salary>
        </person>
     <person>
        <id>1003</id>
        <name>王五</name>
        <salary>10000.0</salary>
        </person>
</department>


封装实体类Department

public class Department {

	private int id;
	
	private String name;
	
	private float salary;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public float getSalary() {
		return salary;
	}

	public void setSalary(float salary) {
		this.salary = salary;
	}
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		 return "id:" + id + ", name:" + name + ", salary:" + salary;  
	}
	
}


为解析器定义一个DepartParser接口,每种类型的解析器需要实现此接口

public interface DepartParser {

	/**
	 * 解析输入流 得到Department对象集合
	 * @param is
	 * @return
	 * @throws Exception
	 */
	public List<Department> parse(InputStream is) throws Exception;

	/**
	 * 序列化Department对象集合,得到XML形式的字符串
	 * @param departs
	 * @return
	 * @throws Exception
	 */
	public String serialize(List<Department> departs) throws Exception;
}


serialize(List<Department>)解析出来的数据样式如下:

 

<?xml version="1.0" encoding="utf-8"?>
<department>
    <person id="1001">
        <name>张三</name>
        <salary>8000.0</salary>
        </person>
     <person id="1002">
        <name>李四</name>
        <salary>9000.0</salary>
        </person>
     <person id="1003">
        <name>王五</name>
        <salary>10000.0</salary>
        </person>
</department>


使用SAX解析

import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Sax方式解析
 * 
 * @author 孟祥程
 * 
 */
public class SaxDepartmentParser implements DepartParser {

	@Override
	public List<Department> parse(InputStream is) throws Exception {
		// TODO Auto-generated method stub
		// 取得SAXParserFactory实例
		SAXParserFactory factory = SAXParserFactory.newInstance();
		// 从factory获取SAXParser实例
		SAXParser parser = factory.newSAXParser();
		// 实例化自定义Handler
		MyHandler handler = new MyHandler();
		// 根据自定义Handler规则解析输入流
		parser.parse(is, handler);
		return handler.getDeparts();
	}

	@Override
	public String serialize(List<Department> departs) throws Exception {
		// TODO Auto-generated method stub
		SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory
				.newInstance();
		TransformerHandler handler = factory.newTransformerHandler(); // 从factory获取TransformerHandler实例
		Transformer transformer = handler.getTransformer(); // 从handler获取Transformer实例
		transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); // 设置输出采用的编码方式
		transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 是否自动添加额外的空白
		transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); // 是否忽略XML声明
		
		StringWriter writer = new StringWriter();
		//Result result = new StreamResult();这样的话,handler.startDocument()会报 No output specified异常
		Result result = new StreamResult(writer);
		handler.setResult(result);
		
		String uri = "";//代表命名空间的URI 当URI无值时 须置为空字符串  
		String localName = "";//命名空间的本地名称(不包含前缀) 当没有进行命名空间处理时 须置为空字符串 

	
		handler.startDocument();
		handler.startElement(uri, localName, "department", null);
		
		AttributesImpl attrs = new AttributesImpl();   //负责存放元素的属性信息  
		char[] ch = null;
		
		  for (Department depart : departs) {  
	            attrs.clear();  //清空属性列表  
	            attrs.addAttribute(uri, localName, "id", "string", String.valueOf(depart.getId()));//添加一个名为id的属性(type影响不大,这里设为string)  
	            handler.startElement(uri, localName, "person", attrs);    //开始一个元素 关联上面设定的id属性  
	              
	            handler.startElement(uri, localName, "name", null); //开始一个name元素 没有属性  
	            ch = String.valueOf(depart.getName()).toCharArray();  
	            handler.characters(ch, 0, ch.length);   //设置name元素的文本节点  
	            handler.endElement(uri, localName, "name");  
	              
	            handler.startElement(uri, localName, "salary", null);//开始一个price元素 没有属性  
	            ch = String.valueOf(depart.getSalary()).toCharArray();  
	            handler.characters(ch, 0, ch.length);   //设置price元素的文本节点  
	            handler.endElement(uri, localName, "salary");  
	              
	            handler.endElement(uri, localName, "person");  
	        }  
	        handler.endElement(uri, localName, "department");  
	        handler.endDocument();  
	          
	        return writer.toString();  
	        
	}

	/**
	 * DefaultHandler是一个事件处理器,可以接收解析器报告的所有事件,处理所发现的数据
	 * 
	 * @author 孟祥程
	 * 
	 */
	private class MyHandler extends DefaultHandler {
		private List<Department> departs;
		private Department depart;
		private StringBuilder builder;

		// 返回解析后得到的<span class="KSFIND_CLASS" id="2KSFindDIV">Department</span>对象集合
		public List<Department> getDeparts() {
			return departs;
		}

		@Override
		public void startDocument() throws SAXException {
			// TODO Auto-generated method stub
			super.startDocument();
			departs = new ArrayList<Department>();
			builder = new StringBuilder();
		}

		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			// TODO Auto-generated method stub
			super.startElement(uri, localName, qName, attributes);
			if (localName.equals("person")) {
				depart = new Department();
			}
			// 将字符长度设置为0 以便重新开始读取元素内的字符节点
			builder.setLength(0);
		}

		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			// TODO Auto-generated method stub
			super.characters(ch, start, length);
			// 将读取的字符数组追加到builder中
			builder.append(ch, start, length);//将读取的字符数组追加到builder中  
		}

		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			// TODO Auto-generated method stub
			super.endElement(uri, localName, qName);
			if (localName.equals("id")) {
				depart.setId(Integer.parseInt(builder.toString()));
			} else if (localName.equals("name")) {
				depart.setName(builder.toString());
			} else if (localName.equals("salary")) {
				depart.setSalary(Float.parseFloat(builder.toString()));
			} else if (localName.equals("person")) {
				departs.add(depart);
			}
		}

	}

}


使用DOM解析方式

import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class DomDepartmentParser implements DepartParser{

	@Override
	public List<Department> parse(InputStream is) throws Exception {
		// TODO Auto-generated method stub
		List<Department> <span class="KSFIND_CLASS" id="3KSFindDIV">depart</span>s  = new ArrayList<Department>();
		 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  //取得DocumentBuilderFactory实例  
	        DocumentBuilder builder = factory.newDocumentBuilder(); //从factory获取DocumentBuilder实例  
	        Document doc = builder.parse(is);   //解析输入流 得到Document实例  
	        Element rootElement = doc.getDocumentElement();  
	        NodeList items = rootElement.getElementsByTagName("person");  
	        for (int i = 0; i < items.getLength(); i++) {  
	            Department depart = new Department();  
	            Node item = items.item(i);  
	            NodeList properties = item.getChildNodes();  
	            for (int j = 0; j < properties.getLength(); j++) {  
	                Node property = properties.item(j);  
	                String nodeName = property.getNodeName();  
	                if (nodeName.equals("id")) {  
	                	depart.setId(Integer.parseInt(property.getFirstChild().getNodeValue()));  
	                } else if (nodeName.equals("name")) {  
	                	depart.setName(property.getFirstChild().getNodeValue());  
	                } else if (nodeName.equals("salary")) {  
	                	depart.setSalary(Float.parseFloat(property.getFirstChild().getNodeValue()));  
	                }  
	            }  
	            <span class="KSFIND_CLASS" id="4KSFindDIV">depart</span>s.add(depart);  
	        }  
	        return <span class="KSFIND_CLASS" id="5KSFindDIV">depart</span>s;  
	}

	@Override
	public String serialize(List<Department> departs) throws Exception {
		// TODO Auto-generated method stub
		 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
	        DocumentBuilder builder = factory.newDocumentBuilder();  
	        Document doc = builder.newDocument();   //由builder创建新文档  
	          
	        Element rootElement = doc.createElement("department");  
	  
	        for (Department depart : departs) {  
	            Element <span class="KSFIND_CLASS" id="6KSFindDIV">depart</span>Element = doc.createElement("person");  
	            <span class="KSFIND_CLASS" id="7KSFindDIV">depart</span>Element.setAttribute("id", depart.getId() + "");  
	              
	            Element nameElement = doc.createElement("name");  
	            nameElement.setTextContent(depart.getName());  
	            <span class="KSFIND_CLASS" id="8KSFindDIV">depart</span>Element.appendChild(nameElement);  
	              
	            Element priceElement = doc.createElement("salary");  
	            priceElement.setTextContent(depart.getSalary() + "");  
	            <span class="KSFIND_CLASS" id="9KSFindDIV">depart</span>Element.appendChild(priceElement);  
	              
	            rootElement.appendChild(<span class="KSFIND_CLASS" id="10KSFindDIV">depart</span>Element);  
	        }  
	          
	        doc.appendChild(rootElement);  
	          
	        TransformerFactory transFactory = TransformerFactory.newInstance();//取得TransformerFactory实例  
	        Transformer transformer = transFactory.newTransformer();    //从transFactory获取Transformer实例  
	        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");            // 设置输出采用的编码方式  
	        transformer.setOutputProperty(OutputKeys.INDENT, "yes");                // 是否自动添加额外的空白  
	        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");   // 是否忽略XML声明  
	          
	        StringWriter writer = new StringWriter();  
	          
	        Source source = new DOMSource(doc); //表明文档来源是doc  
	        Result result = new StreamResult(writer);//表明目标结果为writer  
	        transformer.transform(source, result);  //开始转换  
	          
	        return writer.toString();  
	}

}

使用PULL解析

import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;

import android.util.Xml;

public class PullDepartmentParser implements DepartParser {  
    
  @Override  
  public List<Department> parse(InputStream is) throws Exception {  
      List<Department> departs = null;  
      Department depart = null;  
        
        
      XmlPullParser parser = Xml.newPullParser(); //由android.util.Xml创建一个XmlPullParser实例  
      parser.setInput(is, "UTF-8");               //设置输入流 并指明编码方式  

      int eventType = parser.getEventType();  
      while (eventType != XmlPullParser.END_DOCUMENT) {  
          switch (eventType) {  
          case XmlPullParser.START_DOCUMENT:  
        	  departs = new ArrayList<Department>();  
              break;  
          case XmlPullParser.START_TAG:  
              if (parser.getName().equals("person")) {  
            	  depart = new Department();  
              } else if (parser.getName().equals("id")) {  
                  eventType = parser.next();  
                  depart.setId(Integer.parseInt(parser.getText()));  
              } else if (parser.getName().equals("name")) {  
                  eventType = parser.next();  
                  depart.setName(parser.getText());  
              } else if (parser.getName().equals("salary")) {  
                  eventType = parser.next();  
                  depart.setSalary(Float.parseFloat(parser.getText()));  
              }  
              break;  
          case XmlPullParser.END_TAG:  
              if (parser.getName().equals("person")) {  
            	  departs.add(depart);  
            	  depart = null;      
              }  
              break;  
          }  
          eventType = parser.next();  
      }  
      return departs;  
  }  
    
  @Override  
  public String serialize(List<Department> departs) throws Exception {  
        
      XmlSerializer serializer = Xml.newSerializer(); //由android.util.Xml创建一个XmlSerializer实例  
      StringWriter writer = new StringWriter();  
      serializer.setOutput(writer);   //设置输出方向为writer  
      serializer.startDocument("UTF-8", true);  
      serializer.startTag("", "department");  
      for (Department depart : departs) {  
          serializer.startTag("", "person");  
          serializer.attribute("", "id", depart.getId() + "");  
            
          serializer.startTag("", "name");  
          serializer.text(depart.getName());  
          serializer.endTag("", "name");  
            
          serializer.startTag("", "salary");  
          serializer.text(depart.getSalary() + "");  
          serializer.endTag("", "salary");  
            
          serializer.endTag("", "person");  
      }  
      serializer.endTag("", "department");  
      serializer.endDocument();  
        
      return writer.toString();  
  }  
}  

布局文件activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="${packageName}.${activityClass}" >

   <Button
       android:id="@+id/btn_sax"
       android:layout_width="300dp"
       android:layout_height="150dp"
       android:text="sax解析xml"
       />
   <Button 
       android:id="@+id/btn_dom"
        android:layout_width="300dp"
       android:layout_height="150dp"
        android:text="dom解析xml"
       />
   <Button 
       android:id="@+id/btn_pull"
        android:layout_width="300dp"
       android:layout_height="150dp"
         android:text="pull解析xml"
       />
    <Button 
       android:id="@+id/btn_outxml"
        android:layout_width="300dp"
       android:layout_height="150dp"
       android:text="输出xml"
       />


</LinearLayout>

Activity代码

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener {
	private static final String TAG = "XML";

	private DepartParser parser;
	private List<Department> departs;

	private Button saxIn, domIn, pullIn, outXml;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
	}

	private void initView() {
		// TODO Auto-generated method stub
		saxIn = (Button) findViewById(R.id.btn_sax);
		saxIn.setOnClickListener(this);
		domIn = (Button) findViewById(R.id.btn_dom);
		domIn.setOnClickListener(this);
		pullIn = (Button) findViewById(R.id.btn_pull);
		pullIn.setOnClickListener(this);
		outXml = (Button) findViewById(R.id.btn_outxml);
		outXml.setOnClickListener(this);
	}

	@Override
	public void onClick(View arg0) {
		// TODO Auto-generated method stub
		int id = arg0.getId();
		switch (id) {
		case R.id.btn_sax:
			saxReadXml();
			break;
		case R.id.btn_dom:
			domReadXml();
			break;
		case R.id.btn_pull:
			pullReadXml();
			break;
		case R.id.btn_outxml:
			writeXml();
			break;
	
		}
	}
	
	private void saxReadXml(){
		try {
			InputStream is = getAssets().open("<span class="KSFIND_CLASS" id="11KSFindDIV">depart</span>s.xml");
			parser = new SaxDepartmentParser(); // 创建SaxD<span class="KSFIND_CLASS" id="12KSFindDIV">epart</span>Parser实例
			departs = parser.parse(is); // 解析输入流
			for (Department depart : departs) {
				Log.i(TAG, "sax read xml:"+depart.toString());
			}
		} catch (Exception e) {
			Log.e(TAG, "sax read xml:"+e.getMessage());
		}
	}
	
	private void domReadXml(){
		try {  
	        InputStream is = getAssets().open("<span class="KSFIND_CLASS" id="13KSFindDIV">depart</span>s.xml");  
	        parser = new DomDepartmentParser();  
	        departs = parser.parse(is);  
	        for (Department depart : departs) {  
	            Log.i(TAG, "dom read xml:"+depart.toString());  
	        }  
	    } catch (Exception e) {  
	        Log.e(TAG, "dom read xml:"+e.getMessage());  
	    }  
	}
	
	private void pullReadXml(){
		 try {  
             InputStream is = getAssets().open("<span class="KSFIND_CLASS" id="14KSFindDIV">depart</span>s.xml");  
             parser = new PullDepartmentParser();  
             departs = parser.parse(is);  
             for (Department depart : departs) {  
                 Log.i(TAG, "pull read xml:"+depart.toString());  
             }  
         } catch (Exception e) {  
             Log.e(TAG, "pull read xml:"+e.getMessage());  
         }  
	}
	
	private void writeXml(){
		 try {  
             String xml = parser.serialize(departs);  //序列化  
             File dir = null;
             if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
            	  dir = new File(Environment.getExternalStorageDirectory()+"/xml");
            	  if(!dir.exists()){
            		  System.out.println("mkdirs:"+dir.mkdirs());
            	  }
             }
             File file = new File(dir.getPath(), "departs.xml"); 
 			if (file.exists()) {
 				file.delete();
 			}
 			file.createNewFile();
 			FileOutputStream out = new FileOutputStream(file);
 			out.write(xml.getBytes("UTF-8"));
 			out.flush();
 			out.close();
         } catch (Exception e) {  
             Log.e(TAG, e.getMessage());  
         }  
	}
	
	
}

输出文件在sd卡xml文件夹下,每获取一遍xml就可以输出一遍

文章评论

Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
那些争议最大的编程观点
那些争议最大的编程观点
如何成为一名黑客
如何成为一名黑客
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
每天工作4小时的程序员
每天工作4小时的程序员
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序员的鄙视链
程序员的鄙视链
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
10个调试和排错的小建议
10个调试和排错的小建议
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
中美印日四国程序员比较
中美印日四国程序员比较
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
程序员必看的十大电影
程序员必看的十大电影
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
编程语言是女人
编程语言是女人
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
程序员和编码员之间的区别
程序员和编码员之间的区别
漫画:程序员的工作
漫画:程序员的工作
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
Java程序员必看电影
Java程序员必看电影
鲜为人知的编程真相
鲜为人知的编程真相
程序员应该关注的一些事儿
程序员应该关注的一些事儿
 程序员的样子
程序员的样子
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
我是如何打败拖延症的
我是如何打败拖延症的
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
我的丈夫是个程序员
我的丈夫是个程序员
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
代码女神横空出世
代码女神横空出世
老程序员的下场
老程序员的下场
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
总结2014中国互联网十大段子
总结2014中国互联网十大段子
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
一个程序员的时间管理
一个程序员的时间管理
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有