反射、注解、xml、DTD、Schema技术

一、反射

1.反射的介绍

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

java反射机制,可以实现以下功能:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法
  • 生成动态代理

2.获取源头Class(重点)

每一个类的.class加载到内存中的对象一个类。

1)获取类的Class对象的三种方式

获取Class对象的方式:

  • 1.Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象

多用于配置文件,将类名定义在配置文件中。读取文件,加载类

  • 2.类名.class:通过类名的属性class获取

多用于参数的传递

  • 3.对象.getClass():getClass()方法在Object类中定义着。

多用于对象的获取字节码的方式

结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

/**
 * 反射:
 *      获取Class 对象
 *      String 为例子
 *      通过对象获取
 *      通过类获取
 *      通过字符串获取
 */
public class Reflect001 {
    public static void main(String[] args) throws Exception{
        //第一种方式:对象.getClass
        String str = new String("abc");
        Class<?> clz = str.getClass();
        System.out.println(clz.getName());
        System.out.println(clz);

        System.out.println("===============");

        //第二种方式:类.class
        clz = Person.class;//Person是自定义类,返回的是包名+类名
        System.out.println(clz);//class com.shsxt0622.myreflect.Person

        System.out.println("==============");

        //第三种方式(推荐方式)通过字符串来获取:Class.forName()
        String clzName = "java.lang.String";
        clz = Class.forName(clzName);
        System.out.println(clz);//class java.lang.String  
    }
}

2)获取类的权限修饰符

获取修饰符,使用getModifiers()即可。

/**
 * 获取类修饰符
 *      先获取类
 *      getModifiers()方法
 */
public class Reflect002 {
    public static void main(String[] args) {
        String str1 = new String("hello");
        String str2 = new String("world");

        Class<?> clz1 = str1.getClass();//第一种方式:对象.getClass
        Class<?> clz2 = str2.getClass();
        System.out.println(clz1==clz2);//true

        int i = clz1.getModifiers();//获取修饰符所对应的int类型的数字
        System.out.println(i);//17
        System.out.println(Modifier.toString(i));//把i转化为权限修饰符 public final
        System.out.println("==============");
    }
}

3.创建对象(获取构造器)

1)获取构造器的几种方法

  • 1.getConstructor(...)获取指定的公开权限的构造器
  • 2.getDeclaredConstructor(...)获取指定的本类的构造器
  • 3.getConstructors()获取所有公开的构造器
  • 4.getDeclaredConstructors()获取本类所有的构造器

2)实例化对象(重点)

创建对象的方式,有new 、克隆、反序列化,再加一种,根据Class对象,使用newInstance() 或者构造器实例化对象。

//无参
 Person p = c.newInstance();
 //一个参数
  p = c.newInstance("张三");
  //两个参数
  p = c.newInstance("张三",18);

注意:newInstance()是调用空构造,如果空构造不存在,会出现异常。由此可知,使用其他构造器创建对象比较麻烦,使用空构造非常简单。确保空构造存在

3)打开私有权限

//c是构造器,打开构造器的私有权限
 c.setAccessible(true);//打开权限
 //f是属性,打开属性的私有权限
 f.setAccessible(true);
 //m是方法打开属性的私有权限
 m.setAccessible(true);

综合案例:

public class Reflect003 {
    public static void main(String[] args) throws Exception{
        //1.获取Class
        Class<Person> clz = Person.class;
        //2.获取构造器
        Constructor<Person> c = clz.getConstructor(null);
        System.out.println(c.getName());//com.shsxt0622.myreflect.Person

        //3.执行构造器  无参
        Person p = c.newInstance();
        System.out.println(p);//Person{name='null', age=0}
        System.out.println("============");

        //一个参数
        c = clz.getConstructor(String.class);
        System.out.println(c.getName());//com.shsxt0622.myreflect.Person

        p = c.newInstance("张三");
        System.out.println(p);//Person{name='张三', age=0}
        System.out.println("==============");

        //两个参数的构造器被private修饰了
        c = clz.getDeclaredConstructor(String.class, int.class);//私有的构造器
        c.setAccessible(true);//打开私有权限
        System.out.println(c.getName());//com.shsxt0622.myreflect.Person

        p = c.newInstance("张三",18);
        System.out.println(p);//Person{name='张三', age=18}

        //1、获取所有的public 权限的构造器
        Constructor<?>[] con = clz.getConstructors();
        for(Constructor<?> p1 : con){
            System.out.println(p1);//public com.shsxt0622.myreflect.Person(java.lang.String)
                                //public com.shsxt0622.myreflect.Person()
        }
    }
}

4.获取父类与接口

  • 1.getSuperclass();获取父类
  • 2.getInterfaces();获取接口
public class Reflect005 {
    public static void main(String[] args) throws Exception{
        Class<?> clz = Class.forName("com.shsxt0622.myreflect.Son");

        //获取父类
        Class superClz = clz.getSuperclass();
        System.out.println(superClz);//class com.shsxt0622.myreflect.Father

        //获取接口
        Class<?>[] interfaces = clz.getInterfaces();
        System.out.println(interfaces.length);//2
    }
}

5.获取属性和方法

1)获取属性

  • 1.getField(属性名);获取类指定的 public属性成员
  • 2. getDeclaredFiled(属性名);获取类指定的属性成员(包括私有、共有、保护)
  • 3.getFields();获取所有公开的包括本类的父类的属性
  • 4.getDeclaredFields();取得本类的全部属性
  • 5.getName();获取属性对应的名字
  • 6.getModifiers();获取属性对应的权限修饰符
public class Reflect006 {
    public static void main(String[] args) throws Exception{
        //获取类
        Class clz = Class.forName("com.shsxt0622.myreflect.Person");

        // 获取属性, 属性对象  私有的 和公开的, 只能是本类的
        Field[] fs = clz.getDeclaredFields();
        for(Field f : fs){
            System.out.println(f);
        }
        /**
         * private java.lang.String com.shsxt0622.myreflect.Person.name
         * private int com.shsxt0622.myreflect.Person.age
         */

        System.out.println("==================");

        // 获取属性    属性对象   所有公开的  包括本类的 父类的
        fs = clz.getFields();
        for(Field f : fs){
            System.out.println(f);
        }
        System.out.println("==================");

        Field f = clz.getField("age");
        System.out.println(f);//public int com.shsxt0622.myreflect.Person.age
        System.out.println("==================");

        Field f1 = clz.getDeclaredField("name");
        System.out.println(f1);//private java.lang.String com.shsxt0622.myreflect.Person.name
        System.out.println("=====================");

        System.out.println(f1.getName());//name
        System.out.println(f1.getModifiers());//2 private
        System.out.println(f1.getType());//class java.lang.String
    }
}

2)获取方法

  • 1.getMethod(方法名, 参数...);获取指定的公开的方法
  • 2. getDeclaredMethod(方法名, 参数...);获取指定的私有的、公开的方法
  • 3.getMethods();获取所有公开的包括本类的父类的方法
  • 4.getDeclaredMethods();取得本类的全部方法
  • 5.getName();获取方法对应的名字
  • 6.getModifiers();获取方法对应的权限修饰符
  • 7.getParameterCount();获取方法参数的个数
  • 8.getReturnType();获取方法的返回值

关于反射的综合案例:

public class Demo001Reflect {
    public static void main(String[] args) throws Exception{
        // 创建对象, 不想自己new
        Class clz = Class.forName("com.shsxt0623.reflect001.Person");
        // 获取构造器
        Constructor c = clz.getConstructor();
        // 执行构造器
        Person p = (Person) c.newInstance();
        System.out.println(p);

        // 获取私有的构造器
        c = clz.getDeclaredConstructor(String.class,int.class);
        // 打开权限
        c.setAccessible(true);
        // 执行构造器
        p = (Person) c.newInstance("黄磊",40);
        System.out.println(p);

        System.out.println("=======================");
        // 获取属性
        Field field = clz.getField("age");
        System.out.println(field.getModifiers());
        System.out.println(field.getType());
        System.out.println(field.getName());
        // 设置指定对象的这个属性的值为 指定的值
        field.set(p,35);
        System.out.println(p);
        System.out.println("================");
        // 获取私有属性
        field = clz.getDeclaredField("name");
        field.setAccessible(true);
        field.set(p,"何炅");
        System.out.println(p);

        //获取指定的公开的方法
        Method m = clz.getMethod("testPublic",String.class,String.class);
        System.out.println(m);//public void com.shsxt0623.reflect001.Person.testPublic(java.lang.String,java.lang.String)
        //非静态方法要传一个对象,后面加参数
        m.invoke(p,"张三","李四");//张三----李四
        System.out.println("================");

        //获取指定的私有的方法
        m = clz.getDeclaredMethod("testPrivate", String.class, int.class);
        m.setAccessible(true);
        System.out.println(m);//private int com.shsxt0623.reflect001.Person.testPrivate(java.lang.String,int)
        m.invoke(p,"张三",18);//张三---18

        //获取名字
        System.out.println(m.getName());//testPrivate

        //获取权限修饰符
        int i = m.getModifiers();
        System.out.println(i);//2
        System.out.println(Modifier.toString(i));//private

        //获取返回值类型
        System.out.println(m.getReturnType());//int

        //获取参数的个数
        System.out.println(m.getParameterCount());//2
    }
}

二、注解

1.注解的介绍

注解相当于是一种嵌入在程序中的元数据,可以使用注解解析工具或编译器对其进行解析,也可以指定注解在编译期或运行期有效。

元数据:描述数据的数据

2.注解的分类

根据注解参数的个数:

1)、标记注解:一个没有成员定义的Annotation类型被称为标记注解。
2)、单值注解:只有一个值
3)、完整注解:拥有多个值

根据注解使用方法和用途:

1)、JDK内置系统注解
2)、元注解
3)、自定义注解

3.内置注解

JavaSE中内置三个标准注解,定义在java.lang中:

1)@Override

限定重写父类方法,若想要重写父类的一个方法时,需要使用该注解告知编译器我们正在重写一个方法。如此一来,当父类的方法被删除或修改了,编译器会提示错误信息;或者该方法不是重写也会提示错误。

Override的源码:

@Target(ElementType.METHOD)表示只能在方法上使用

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

2)@Deprecated

标记已过时,当我们想要让编译器知道一个方法已经被弃用(deprecate)时,应该使用这个注解。Java推荐在javadoc中提供信息,告知用户为什么这个方法被弃用了,以及替代方法是什么

Deprecated的源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

3)@SuppressWarnings

抑制编译器警告,该注解仅仅告知编译器,忽略它们产生了特殊警告。如:在java泛型中使用原始类型。其保持性策略(retention policy)是SOURCE,在编译器中将被丢弃。

SuppressWarnings的源码:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {

    String[] value();//value值可以省略
}

4.自定义注解

1)简单入门

@interface:用来声明一个注解。注解类里的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型。可以通过default来声明参数的默认值。

@interface Simple{
	//这里定义了一个空的注解,它能干什么呢?我也不知道,但他能用。后面有补充
}

2)元注解

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它annotation类型作说明。Java5.0定义的元注解有四个,这些类型和它们所支持的类在ava.lang.annotation包中可以找到。

1)@Target

用于描述注解的使用范围(即:被描述的注解可以用在什么地方)。表示支持注解的程序元素的种类,一些可能的值有TYPE, METHOD, CONSTRUCTOR, FIELD等等。如果Target元注解不存在,那么该注解就可以使用在任何程序元素之上。取值(ElementType)有:

1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

此时在空注解中加入@Target元注解如:

//此注解只能用在方法上
@Target(ElementType.METHOD)
@interface TestMethod {
}

2)@Retention(了解)

表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)表示注解类型保留时间的长短。取值(RetentionPoicy)有:

1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)

此时在上述注解中加入@Retention元注解如:

// 此注解可以用于注解类、接口(包括注解类型) 或enum声明
@Target(ElementType.TYPE)
//该注解运行时有效。注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理
@Retention(RetentionPolicy.RUNTIME)
@interface TestRn{
}

3.@Documented(了解)

表示使用该注解的元素应被javadoc或类似工具文档化,它应用于类型声明,类型声明的注解会影响客户端对注解元素的使用。如果一个类型声明添加了Documented注解,那么它的注解会成为被注解元素的公共API的一部分,@Documented是一个标记注解。

//可以被例如javadoc此类的工具文档化
@Documented
@interface TestDoc{
}

4.@Inherited

表示一个注解类型会被自动继承,如果用户在类声明的时候查询注解类型,同时类声明中也没有这个类型的注解,那么注解类型会自动查询该类的父类,这个过程将会不停地重复,直到该类型的注解被找到为止,或是到达类结构的顶层(Object)。

//被子类继承的注解
@Inherited
@interface TestInheri{}

5.自定义注解

定义:
@Target()  --> 标识这个注解能用在哪里
@interface 注解名{
返回值类型1  方法名1();
返回值类型2  方法名2() default 对应默认值;
// 返回值类型:基本,String,Class, 枚举, Annotation,  对应数组
}
使用:
关注:Target,注解名,方法(方法名,返回值)
@注解名(方法名1=对应的值,方法名2={1, 值2})

三、xml

1.概念

可扩展标记语言,Xml用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。

2.特点与作用

1)特点

  • xml与操作系统、编程语言的开发平台都无关
  • 现不同系统之间的数据交互

2)作用

  • 配置应用程序(servlet 配置,框架环境配置等)和网站
  • 数据交互
  • Ajax基石

3.基础语法

格式良好的 XML 文档需满足以下条件:

  • 必须有XML声明语句
  • 必须有且仅有一个根元素
  • 标签大小写敏感
  • 属性值用双引号
  • 标签成对
  • 元素正确嵌套

注:

  1. xml声明一般是xml文档的第一行
  2. xml中,一些字符拥有特殊的意义。如果把字符“<”放在xml元素中,会发生错误,这是因为解析器会把它当作新元素的开始,这样会产生xml错误

xml中5个预定义实体:
在这里插入图片描述

1)定义一个合法的xml

<?xml version="1.0" encoding="UTF-8" ?>
<students>
    <student>
        <id>1</id>
        <name>王同</name>
        <curriculum>java</curriculum>
        <score>89</score>
    </student>
    <student>
        <id>2</id>
        <name>李佳</name>
        <curriculum>sql</curriculum>
        <score>58</score>
    </student>
</students>

四、DTD技术

DTD即文档类型定义–Document Type Definition

约束xml中有哪些元素,使每个XML文件可以携带一个自身格式的描述。

一个DTD文档可能包含如下内容:

  • 元素的定义规则
  • 元素之间的关系规则
  • 属性的定义规则

1.使用DTD验证描述XML文档

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE students[
        <!ELEMENT students (student*)>
        <!ELEMENT student (id,name,curriculum,score)>
        <!ELEMENT id (#PCDATA)>
        <!ELEMENT name (#PCDATA)>
        <!ELEMENT curriculum (#PCDATA)>
        <!ELEMENT score (#PCDATA)>
        ]>
<students>
    <student>
        <id>1</id>
        <name>王同</name>
        <curriculum>java</curriculum>
        <score>89</score>
    </student>
    <student>
        <id>2</id>
        <name>李佳</name>
        <curriculum>sql</curriculum>
        <score>58</score>
    </student>
</students>

1)PCDATA

PCDATA 的意思是被解析的字符数据(parsed character data)。

可把字符数据想象为 XML 元素的开始标签与结束标签之间的文本。

PCDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。

文本中的标签会被当作标记来处理,而实体会被展开。

不过,被解析的字符数据不应当包含任何 &、< 或者 > 字符;需要使用 &、< 以及 > 实体来分别替换它们。

2)CDATA

CDATA 的意思是字符数据(character data)。

CDATA 是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。

<![CDATA[
	A>B && A>C
	]]>

2.引入DTD的方式

当验证的XML文件较多时,使用内部DTD可能出现冗余,此时可以通过引入dtd 文件进行xml 约束。

1.把DTD约束单独拿出来,新建一个DTD文件

2.在xml中引入

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!--引入外部的dtd文件来对内容进行约束-->
<!DOCTYPE students SYSTEM  "http;//score//scores.dtd"><!--加地址是要双击-->

3.怎么加"http;//score//scores.dtd"这个地址(idea中)

  • 点击idea左上角File–>settings…–>Languages & -->Schemas and DTDs–>点击右上角Project旁边的加号–>在URL里随便写一个地址–>双击dtd文件。

3.Schema技术

Schema是用一套预先规定的xml元素和属性创建的,这些元素和属性定义了xml文档的结构和内容模式。XmlShema规定xml文档实例的结构和每个元素/属性的数据类型。

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.myschema.com.cn"
           xmlns="http://www.myschema.com.cn"
           elementFormDefault="qualified">
    <xs:element name="person">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="id" type="xs:integer"/>
                <xs:element name="name" type="xs:string"/>
                <xs:element name="sex" type="xs:string"/>
                <xs:element name="address" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
<?xml version="1.0" encoding="utf-8" ?>
<person xmlns="http://www.myschema.com.cn">
    <id>1</id>
    <name>张三</name>
    <sex></sex>
    <address>上海浦东</address>
</person>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值