小白这周把网络编程最后的一部分给结束了,然后接触了注解的内容。开始下一阶段的内容学习。fighting!!!
1、XML 不是 HTML 的替代。XML 和 HTML 为不同的目的而设计:
(1)XML 被设计用来传输和存储数据,其焦点是数据的内容。
(2)HTML 被设计用来显示数据,其焦点是数据的外观。
2、HTML 旨在显示信息,而 XML 旨在传输信息。
3、XML语言的解析:基本的解析方式有两种
-SAX:SAX是基于事件流的解析,逐句进行解析每一行代码,一旦解析之后,就不会再重复解析了,可以适用于大数据代码。
-DOM:DOM是基于XML文档树结构的解析。将整个代码全部都加载进入计算机中,然后需要哪一行就会去解析哪一行。在使用的时候需要使用大量的内存,所以适用情况仅限于小数据量的代码。
下面用实例解析XML语言:
1、首先我们先创建一个xml文件
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person>
<name>至尊宝</name>
<age>9000</age>
</person>
<person>
<name>紫霞</name>
<age>8000</age>
</person>
</persons>
解析:上述代码就是利用XML语言写的一个小实例,可以看出,XML语言的风格和HTML风格相似,内容属性都是成对出现的。所有的XML代码第一行都是固定的“<?xml version="1.0" encoding="UTF-8"?>”,以“?xml”开头,然后是对应的版本号,然后是编码解码集。
2、然后创建一个Person类
package com.peng.xml;
public class Person {
private String name;
private int age;
public Person() {
// TODO Auto-generated constructor stub
}
public Person(String name, int age) {
super();
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;
}
}
解析:此处创建一个Person类主要就是便于对xml代码块中person的处理,在xml代码中,我们已经自定义了一个person类,并且使用到了类的属性,name和age。
3、编写处理器
package com.peng.xml;
public class PersonHandler extends DefaultHandler{
private List<Person> persons;
private Person person;
private String tag;//记录标签名
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super.startDocument();
System.out.println("处理文档开始");
persons = new ArrayList<Person>();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.println("开始一个元素"+qName);
if(null != qName) {
tag = qName;
}
if(null != qName && qName.equals("person")) {
person = new Person();
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
String str = new String(ch,start,length);
if(null != tag && tag.equals("name")) {
person.setName(str);
}else if(null != tag && tag.equals("age")) {
Integer age = Integer.valueOf(str);
person.setAge(age);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equals("person")) {
persons.add(person);
}
tag = null;
}
@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
super.endDocument();
System.out.println("文档处理结束");
}
public List<Person> getPersons() {
return persons;
}
}
解析:在编写自己的处理器的时候,首先需要继承DefaultHandler类别,然后我们主要重写了5个方法,分别是代表着开始和结束文件及元素的处理,还有一个对字符的处理。
4、此处我们使用SAX解析方式进行解析XML语言,主要是有4步
package com.peng.xml;
public class ParseDemo01 {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
//1、获取解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
//2、从解析工厂中获取解析器
SAXParser parse = factory.newSAXParser();
//3、加载文档Document注册处理器
//4、编写处理器
PersonHandler handler = new PersonHandler();
parse.parse(Thread.currentThread().getContextClassLoader().
getResourceAsStream("com/peng/xml/person.xml"),handler);
List<Person> persons = handler.getPersons();
for(Person p:persons) {
System.out.println(p.getName()+"----->"+p.getAge());
}
}
}
5、查看结果
通过结果,我们可以明显的看出SAX解析的特点,按照事件流的结构进行处理,一行一行的逐条执行。
二、Annotation注解
2.1 Annotation的作用
1、不是程序本身,可以对程序做出解释。(这一点和注释没有什么区别)
2、可以被其他程序读取(比如编译器等)。注解信息处理流程,是注解和注释的重大区别,如果没有注解信息处理流程,则注解毫无意义。
2.2 Annotation的格式
注解是以"@注释名"在代码中存在的,还可以添加一些参数值,例如:@SuppressWarnings(value="unchecked")。
2.3 Annotaion在哪里使用
可以附加在package,class,method,field等上面,相当于给它们添加额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。
下面我们结合几个常用的注解实例进行解析:
package com.peng.test.annotation;
public class Demo01 {
@Override //表示重写父类方法
public String toString() {
return "";
}
@Deprecated //表示此方法不建议使用,而不是不能使用
public static void test001() {
System.out.println("test001");
}
@SuppressWarnings("all") //抑制警告
public static void test002() {
List list1 = new ArrayList();
List list2 = new ArrayList<>();
}
public static void main(String[] args) {
Date d = new Date();
test001();
}
}
解析:所有的注解都需要通过“@”符号作为开始,上面我们使用到了三个内置注解,分别是@Override,@Deprecated,@SuppressWarnings("all")。
@Override:表示重写父类方法,当我们在自己的前面加上“@Override”符号的时候,编译器会自动检测,在父类中是否存在这样一个方法名,如果我们的方法名在父类中没有检测到,那么编译器会自动报出错误,提示我们此处有语法错误。
@Deprecated:表示此方法不建议使用,而不是不能使用。在随着jdk不断的更新,之前的版本有许多不安全或者容易产生异常的方法会慢慢被解决,但是为了保证jdk向下的兼容性,jdk开发人员会对其进行相应的标注,使用@Deprecated注解,可以提示用户进行相应的舍取。
@SuppressWarnings:表示抑制警告,在其中可以添加一系列的参数,主要的参数如下:
三、自定义注解
1、我们定义一个简单的注解:
//target主要用于标记此注解的作用范围 (属于元注解)
@Target(value= {ElementType.METHOD,ElementType.PACKAGE})
//表示保留策略 runtime 表示在运行的时候被读取
@Retention(RetentionPolicy.RUNTIME)
public @interface PengAnnotation {
//注意:此处的studentName() 并不是方法,仅仅是一个参数名
String studentName() default "";
int age() default 0;
int id() default -1;
String[] schoolName() default {"北京大学","清华大学"};
}
解析:自定义注解的语法格式如上所示,基本上也就是一个类,但是开头是以“@interface”作为开头,在注解中定义属性的时候,需要在属性名后面加一个小括号,这里和我们之前在类中定义属性值有所不同。
在每个注解中,需要在注解头上添加两个元注解,“@Target”和“@Retention”。“@Target”:表示自定义注解适用的范围,可以用在哪里,比如用于修饰方法、类、属性等等。“@Retention”:表示保留策略,此处使用的是runtime,表示在运行的时候被读取。同时,我们在设定属性值的时候,也可以对其给一个默认值,这样当我们使用此注解的时候可以根据用户的需求再进行属性值的填写,而不必强行添加。
2、我们使用此注解放在实例中:
package com.peng.test.annotation;
public class Demo02 {
@PengAnnotation(studentName="peng",age=2,id=3,schoolName= {"UESTC"})
public void test() {
;
}
}
解析:在此处,我们已经指定了该注解可以用来修饰方法,所以可以将其添加在方法前面。同时,我们在定义注解“@PengAnnotation”的时候,对属性值添加了默认值,这样的话,我们在使用的时候,可以对每一个属性值进行重新定义,也可以使用默认值,否则的话,编译器将会报错。
四、利用反射机制读取注解
在我们未来的工作中,一般是将数据存储在数据库中。然而在java程序中,对数据的描述和数据库中是不同的,此时我们需要将程序中的数据与数据库中的数据进行对应,一般的规则是:类和表结构进行对应,属性和字段进行对应,对象和记录进行对应。我们使用注解完成类和表结构的映射关系。
例如我们需要将下面的代码和表格进行映射对应:
public class PengStudent{
private String studentName;
private int age;
private int id;
}
在存储的时候,我们需要使用注解来获取对应在表格中的每一条信息。
1、建立一个数据库和java程序的映射的注解
@Target(value= {ElementType.TYPE}) //仅在类上起作用
@Retention(RetentionPolicy.RUNTIME)
public @interface PengTable {
String value();
}
2、建立另一个注解,用于说明属性的一些特点
@Target(value= {ElementType.FIELD}) //仅修饰属性
@Retention(RetentionPolicy.RUNTIME)
public @interface PengField {
String columnName();//列名
String type();//数据的类型
int length();//数据的长度
}
3、在我们需要存储的类中,对类以及属性值都使用注解进行解释相关的意义。
@PengTable("tb_student") //将“PengStudent”类型与数据库“tb_student”做映射
public class PengStudent {
@PengField(columnName="age",length=3,type="int")
private int age;
@PengField(columnName="studentName",length=10,type="varvhar")
private String studentName;
@PengField(columnName="age",length=10,type="int")
private int id;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
4、编写一个第三方代码来获取注解中的信息
/**
* 使用反射读取注解的信息,模拟处理注解信息的流程
*/
public class Demo03 {
public static void main(String[] args) {
try {
Class clazz = Class.forName("com.peng.test.annotation.PengStudent");
//获取此类中的所有注解
Annotation[] annotation = clazz.getAnnotations();
for(Annotation a:annotation) {
System.out.println(a);
}
//在所有的注解中,单独获取某一个注解
PengTable p = (PengTable) clazz.getAnnotation(PengTable.class);
System.out.println(p.value());
//获得类的属性的注解
Field f = clazz.getDeclaredField("studentName");//获取属性studengName的注解
PengField pengField = f.getAnnotation(PengField.class);
System.out.println(pengField.columnName()+"---"+pengField.type()+"---"+pengField.length());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
解析:上述代码Demo03使用反射读取注解的信息,模拟处理注解信息的流程,当我们模拟完上述流程之后,就可以获取得到每个注解中的信息。然后根据获得的表名、字段的信息,拼出DDL语句,然后,使用JDBC执行这个SQL,在数据库中生成相关的表。
五、对注解的总结
经过上面的讲述,大家应该可以对注解有一个初步的映像了,注解与注释最大区别并不在于解释说明部分。注释只能提供给开发者自己进行阅读,提高代码的可读性,而注解除了提供给开发人员阅读之外,最大的优势就在于可以提供给第三方软件进行阅读,使得计算机可以提取出注解中的信息,便于信息在不同程序之间的传递。所以说,在使用注解的时候,如果没有注解的信息处理流程,那么注解将会与注释毫无区别,同时,注解也将会失去其存在的意义。