JavaSE(反射、注解、XML)

反射

类加载器

类的加载

  • 当我们的程序在运行后,第一次使用某个类的时候,会将此类的class文件读取到内存,并将此类的所有信息存储到一个Class对象中
    在这里插入图片描述

类的加载时机

  1. 创建类的实例。

  2. 类的静态变量,或者为静态变量赋值。

  3. 类的静态方法。

  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。

  5. 初始化某个类的子类。

  6. 直接使用java.exe命令来运行某个主类。

以上六种情况的任何一种,都可以导致JVM将一个类加载到方法区。

public class Test {
    public static void main(String[] args) throws Exception{
        // 类的加载时机
        //  1. 创建类的实例。
        //  Student stu = new Student();

        // 2. 类的静态变量,或者为静态变量赋值。
        // Person.country = "中国";

        // 3. 类的静态方法。
        // Person.method();

        // 4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
        // Class<?> c = Class.forName("com.xxx.demo_类的加载.Student");

        //  5. 初始化某个类的子类。
        // Zi zi = new Zi();

        // 6. 直接使用java.exe命令来运行某个主类。

        // 获取类加载器:
        // 获取Student类的类加载器
        System.out.println(Student.class.getClassLoader());// AppClassLoader
        // 获取String类的类加载器
        System.out.println(String.class.getClassLoader());// null
        // API中说明:一些实现可能使用null来表示引导类加载器。 如果此类由引导类加载器加载,则此方法将在此类实现中返回null

        System.out.println(Student.class.getClassLoader().getParent());// PlatformClassLoader
        System.out.println(Student.class.getClassLoader().getParent().getParent());// null 引导类加载器加载

    }
}

类加载器

类加载器:是负责将磁盘上的某个class文件读取到内存并生成Class的对象。

  • Java中有三种类加载器,它们分别用于加载不同种类的class:
    • 启动类加载器(Bootstrap ClassLoader):用于加载系统类库<JAVA_HOME>\bin目录下的class,例如:rt.jar。
    • 扩展类加载器(Extension ClassLoader):用于加载扩展类库<JAVA_HOME>\lib\ext目录下的class。
    • 应用程序类加载器(Application ClassLoader):用于加载我们自定义类的加载器。
public class Test{
    public static void main(String[] args){
       System.out.println(Test.class.getClassLoader());//sun.misc.Launcher$AppClassLoader
        System.out.println(String.class.getClassLoader());//null(API中说明:一些实现可能使用null来表示引导类加载器。 如果此类由引导类加载器加载,则此方法将在此类实现中返回null。 )
    }
}

反射的概述

反射的引入

  • 问题:IDEA中的对象是怎么知道类有哪些属性,哪些方法的呢?
 通过反射技术对象类进行了解剖得到了类的所有成员。

反射的概念

 反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造方法)

使用反射操作类成员的前提

要获得该类字节码文件对象,就是Class对象

反射在实际开发中的应用

* 开发IDE(集成开发环境),比如IDEA,Eclipse
* 各种框架的设计和学习 比如SpringHibernateStructMybaits....

Class对象的获取方式

* 方式1: 通过类名.class获得
* 方式2:通过对象名.getClass()方法获得
* 方式3:通过Class类的静态方法获得: static Class forName("类全名")
    * 每一个类的Class对象都只有一个。
  • 示例代码
package com.xxx.demo_Class对象的获取;

/**
 * @Author:xxx
 * @Date: 2020/5/14 9:35
 */
public class Student {
    private String name;

    public void method1(){

    }
}
public class Test {
    public static void main(String[] args) throws Exception{
          /*
            Class对象的获取:
                通过类名.class获得
                通过对象名.getClass()方法获得
                通过Class类的静态方法获得: static Class forName("类全名")
           */
        // 1.方式一:通过类名.class获得
        Class<Student> c1 = Student.class;
        System.out.println(c1);

        // 2.方式二:通过对象名.getClass()方法获得
        Student stu = new Student();
        Class<? extends Student> c2 = stu.getClass();
        System.out.println(c2);

        // 3.方式三:通过Class类的静态方法获得: static Class forName("类全名")
        Class<?> c3 = Class.forName("com.xxx.demo_Class对象的获取.Student");
        System.out.println(c3);

        // 问题:一个类只有一个字节码对象(Class对象)
        System.out.println(c1 == c2);// true
        System.out.println(c1 == c3);// true
    }
}

Class类常用方法

String getSimpleName(); 获得类名字符串:类名
String getName();  获得类全名:包名+类名
T newInstance() ;  创建Class对象关联类的对象
  • 示例代码
public class ReflectDemo02 {
    public static void main(String[] args) throws Exception {
        // 获得Class对象
        Class c = Student.class;
        // 获得类名字符串:类名
        System.out.println(c.getSimpleName());
        // 获得类全名:包名+类名
        System.out.println(c.getName());
        // 创建对象
        Student stu = (Student) c.newInstance();
        System.out.println(stu);
    }
}

反射之操作构造方法

Constructor类概述

反射之操作构造方法的目的
    * 获得Constructor对象来创建类的对象。

Constructor类概述
    * 类中的每一个构造方法都是一个Constructor类的对象

通过反射获取类的构造方法

Class类中与Constructor相关的方法 
1. Constructor getConstructor(Class... parameterTypes)
        * 根据参数类型获得对应的Constructor对象。
        * 只能获得public修饰的构造方法
 2. Constructor getDeclaredConstructor(Class... parameterTypes)
        * 根据参数类型获得对应的Constructor对象
    	* 可以是publicprotected(默认)private修饰符的构造方法。
 3. Constructor[] getConstructors()
        获得类中的所有构造方法对象,只能获得public4. Constructor[] getDeclaredConstructors()
        获得类中的所有构造方法对象
    	可以是publicprotected(默认)private修饰符的构造方法。

通过反射执行构造方法

Constructor对象常用方法
1. T newInstance(Object... initargs)
 	根据指定的参数创建对象
2. void setAccessible(true)
   设置"暴力反射"——是否取消权限检查,true取消权限检查,false表示不取消

示例代码

public class Student {
    // 属性
    public String name;// 姓名
    public String sex;// 性别
    public int age;// 年龄

    // 构造方法
    public Student() {
    }

    public Student(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    private Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 成员方法

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test {
    public static void main(String[] args) throws Exception{
        
        // 0.获取Student类的Class对象
        Class<?> c = Class.forName("com.xxx.demo_反射之构造方法.Student");

        // 1.获取单个public修饰的构造方法
        // Constructor getConstructor(Class... parameterTypes)  获取单个public修饰的构造方法
        //    参数:传入你要获取的构造方法的参数类型的Class对象

        // 获取Student类的空参构造方法
        Constructor<?> cons1 = c.getConstructor();
        System.out.println(cons1);

        // 获取Student类的带有三个参数的构造方法,并且参数的类型顺序为:String,String,int
        Constructor<?> cons2 = c.getConstructor(String.class,String.class, int.class);

        // public Student(String name,String sex,int.class);
        // public Student(String name,int.class,String sex);

        System.out.println(cons2);

        // 2.获取单个非public修饰的构造方法
        // Constructor getDeclaredConstructor(Class... parameterTypes) 获取单个构造方法
        //    参数:传入你要获取的构造方法的参数类型的Class对象

        // 获取Student类的带有2个参数的构造方法,并且参数的类型顺序为:String,int
        Constructor<?> cons3 = c.getDeclaredConstructor(String.class, int.class);
        System.out.println(cons3);

        System.out.println("============================================");
        // 3.获取多个public修饰的方法
        // Constructor[] getConstructors() 获取所有public修饰的构造方法
        Constructor<?>[] conArr1 = c.getConstructors();
        for (Constructor<?> con : conArr1) {
            System.out.println(con);
        }

        System.out.println("============================================");
        // 4.获取所有修饰的方法
        // Constructor[] getDeclaredConstructors() 获取所有构造方法
        Constructor<?>[] conArr2 = c.getDeclaredConstructors();
        for (Constructor<?> con : conArr2) {
            System.out.println(con);
        }

    }
}

public class Test2 {
    public static void main(String[] args) throws Exception{
        
        // 获取Student类的Class对象
        Class<?> c = Class.forName("com.xxx.demo_反射之构造方法.Student");

        // 1.获取public修饰的构造方法对象,并执行该对象表示的构造方法来创建对象
        // 1.1 获取public修饰的构造方法对象  public Student(String name, String sex, int age)
        Constructor<?> cons1 = c.getDeclaredConstructor(String.class, String.class, int.class);

        // 1.2 执行该对象表示的构造方法来创建对象
        Object obj = cons1.newInstance("小白", "女", 18);// 真正返回的是Student对象
        System.out.println(obj);// Student{name='小白', sex='女', age=18}

        System.out.println("==========================================");
        // 2.获取private修饰的构造方法对象,并执行该对象表示的构造方法来创建对象
        // 2.1 获取private修饰的构造方法对象
        Constructor<?> cons2 = c.getDeclaredConstructor(String.class, int.class);

        // 2.2 执行该对象表示的构造方法来创建对象
        // 设置"暴力反射"
        cons2.setAccessible(true);

        Object obj2 = cons2.newInstance("小明", 19);
        System.out.println(obj2);
    }
}

反射之操作成员方法

Method类概述

反射之操作成员方法的目的
    * 操作Method对象来调用成员方法
Method类概述
    * 每一个成员方法都是一个Method类的对象。

通过反射获取类的成员方法

Class类中与Method相关的方法
* Method getMethod(String name,Class...args);
    * 根据方法名和参数类型获得对应的构造方法对象,只能获得public* Method getDeclaredMethod(String name,Class...args);
    * 根据方法名和参数类型获得对应的构造方法对象,包括publicprotected(默认)private* Method[] getMethods();
    * 获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的

* Method[] getDeclaredMethods();
    * 获得类中的所有成员方法对象,返回数组,只获得本类的,包括publicprotected(默认)private

通过反射执行成员方法

Method对象常用方法
*  Object invoke(Object obj, Object... args)
    * 调用指定对象obj的该方法
    * args:调用方法时传递的参数
*  void setAccessible(true)
    设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消

示例代码

package com.xxx.demo_反射之操作成员方法;

public class Student {
    public void show1(){
        System.out.println("public 修饰的show1方法,无参数...");
    }

    public void show1(String str,int num){
        System.out.println("public 修饰的show1方法,2个参数...");
        System.out.println("str:"+str+",num:"+num);
    }

    public void show2(){
        System.out.println("public 修饰的show2方法...");
    }

    private void show3(){
        System.out.println("private 修饰的show3方法...");
    }
}
package com.xxx.demo_反射之操作成员方法;

import java.lang.reflect.Method;

public class Test1 {
    public static void main(String[] args) throws Exception {
        
        // 1.获取Student类的Class对象
        Class<?> c = Class.forName("com.xxx.demo_反射之操作成员方法.Student");

        // 2.获取单个成员方法
        // 2.1 获取单个public修饰的方法
        // Method getMethod(String name,Class...args);
        // 第一个参数: 方法名
        // 第二个参数: 该方法的参数类型的Class对象,如果没有参数就不传入
        Method show1M1 = c.getMethod("show1");// 第一个show1方法
        System.out.println(show1M1);

        Method show1M2 = c.getMethod("show1", String.class, int.class);
        System.out.println(show1M2);

        // 2.2 获取单个private修饰的成员方法
        // Method getDeclaredMethod(String name,Class...args);
        // 第一个参数: 方法名
        // 第二个参数: 该方法的参数类型的Class对象,如果没有参数就不传入
        Method show3M = c.getDeclaredMethod("show3");
        System.out.println(show3M);

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

        // 3.获取多个public修饰的成员方法
        // 3.1 获取所有public修饰的成员方法
        // Method[] getMethods();
        Method[] mArr1 = c.getMethods();
        for (Method method : mArr1) {
            System.out.println(method);
        }

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

        // 3.2 获取所有的成员方法
        // Method[] getDeclaredMethods();
        Method[] mArr2 = c.getDeclaredMethods();
        for (Method method : mArr2) {
            System.out.println(method);
        }

    }
}
package com.xxx.demo_反射之操作成员方法;

import java.lang.reflect.Method;

public class Test2 {
    public static void main(String[] args) throws Exception {
       
        // 0.获取Student类的Class对象
        Class<?> c = Class.forName("com.xxx.demo_反射之操作成员方法.Student");

        // 1.通过反射获取public修饰的成员方法对象,并通过反射执行该方法
        // 1.1 通过反射获取public修饰的成员方法对象
        Method show1M = c.getDeclaredMethod("show1", String.class, int.class);

        // 1.2 通过反射执行该方法
        Student stu = new Student();// 使用反射创建对象:通过反射获取构造方法对象,执行该构造方法
        show1M.invoke(stu,"xxx",14);

        System.out.println("=======================");
        // 2.通过反射获取private修饰的成员方法对象,并通过反射执行该方法
        // 2.1 通过反射获取private修饰的成员方法对象
        Method show3M = c.getDeclaredMethod("show3");

        // 2.2 通过反射执行该方法
        // 设置暴力反射
        show3M.setAccessible(true);
        show3M.invoke(stu);
    }
}

反射之操作成员变量

Field类概述

反射之操作成员变量的目的
    * 通过Field对象给对应的成员变量赋值和取值

Field类概述
    * 每一个成员变量都是一个Field类的对象。

通过反射获取类的成员变量

Class类中与Field相关的方法
* Field getField(String name);
    *  根据成员变量名获得对应Field对象,只能获得public修饰
* Field getDeclaredField(String name);
    *  根据成员变量名获得对应Field对象,包括publicprotected(默认)private* Field[] getFields();
    * 获得所有的成员变量对应的Field对象,只能获得public* Field[] getDeclaredFields();
    * 获得所有的成员变量对应的Field对象,包括publicprotected(默认)private

通过反射访问成员变量

Field对象常用方法
void  set(Object obj, Object value) 
void setInt(Object obj, int i) 	
void setLong(Object obj, long l)
void setBoolean(Object obj, boolean z) 
void setDouble(Object obj, double d) 

Object get(Object obj)  
int	getInt(Object obj) 
long getLong(Object obj) 
boolean getBoolean(Object ob)
double getDouble(Object obj) 

void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。
Class getType(); 获取属性的类型,返回Class对象。

setXxx方法都是给对象obj的属性设置使用,针对不同的类型选取不同的方法。

getXxx方法是获取对象obj对应的属性值的,针对不同的类型选取不同的方法。

示例代码

package com.xxx.demo_反射之操作成员变量;

public class Student {
    public String name;// 姓名
    public int age;// 年龄
    private String sex;// 性别

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        
        // 通过反射获取类的成员变量
        // 1.获取Student类的Class对象
        Class<?> c = Class.forName("com.xxx.demo_反射之操作成员变量.Student");

        // 2. 获取单个成员变量的Field对象
        // 2.1 获取单个public修饰的成员变量
        Field nameF = c.getDeclaredField("name");
        System.out.println(nameF);

        // 2.2 获取单个private修饰的成员变量
        Field sexF = c.getDeclaredField("sex");
        System.out.println(sexF);

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

        // 3.获取所有的成员变量的Field对象
        Field[] fieldArr = c.getDeclaredFields();
        for (Field field : fieldArr) {
            System.out.println(field);
        }
    }
}

public class Test2 {
    public static void main(String[] args) throws Exception {
        
        // 通过反射获取类的成员变量
        // 1.获取Student类的Class对象
        Class<?> c = Class.forName("com.xxx.demo_反射之操作成员变量.Student");

        // 2. 获取单个成员变量的Field对象
        // 2.1 获取单个public修饰的成员变量
        Field nameF = c.getDeclaredField("name");
        // 2.2 通过反射为name属性赋值
        Student stu = new Student();
        nameF.set(stu,"小明");

        Field ageF = c.getDeclaredField("age");
        // ageF.set(stu,18);
        ageF.setInt(stu,19);

        System.out.println(stu);// Student{name='小明', age=18, sex='null'}

        // 2.3 获取某个属性的值
        System.out.println(nameF.get(stu));// 小明
        System.out.println(ageF.get(stu));// 19

        // 2.2 获取单个private修饰的成员变量
        Field sexF = c.getDeclaredField("sex");
        // 暴力反射
        sexF.setAccessible(true);
        sexF.set(stu,"小红");

        // 获取
        System.out.println(sexF.get(stu));// 小红

        // 获取nameF属性的类型
        Class<?> c1 = nameF.getType();
        System.out.println(c1);// class java.lang.String
    }
}

注解

注解概述及作用

注解概述

  • 注解(annotation),是一种代码级别的说明,和类 接口平级关系.

    • 注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无标记,看你的程序有什么标记,就去干相应的事

    • 使用的注解:

      ​ 1).@Override:子类重写方法时——编译时起作用

      ​ 2).@FunctionalInterface:函数式接口——编译时起作用

      ​ 3).@Test:JUnit的测试注解——运行时起作用

注解的作用

  • 生成帮助文档**:**@author和@version

  • 执行编译期的检查 例如:@Override

  • 框架的配置(框架=代码+配置)

JDK提供的三个基本的注解

  • @Override:描述方法的重写.
  • @SuppressWarnings:压制\忽略警告.
  • @Deprecated:标记过时

自定义注解

自定义注解语法

public @interface 注解名{
     属性
}
  • 示例代码
/**
 * 定义了注解
 *
 */
public @interface Annotation01 {

}

注解属性

格式
  • 数据类型 属性名();
属性类型

​ 1.基本类型
​ 2.String
​ 3.Class类型
​ 4.注解类型
​ 5. 枚举类型
​ 6.以上类型的一维数组类型

  • 示例代码
public @interface Annotation01 {
    // 1.基本数据类型(4类8种)
    int a();
    double b();

    // 2.String类型
    String c();

    // 3.Class类型
    Class d();

    // 4.注解类型
    Annotation02 f();
    
    // 5.枚举类型
    Sex e();
    // 6.以上类型的一维数组类型
    int[] g();
    double[] h();
    String[] i();
    Sex[] j();
    Annotation02[] k();
}

使用注解并给注解属性赋值

使用注解:
        如果一个注解中有属性,那么使用注解的时候一定要给注解属性赋值
        如果一个注解没用属性,那么就不需要给注解属性赋值,直接使用即可
如何给注解属性赋值:
        @注解名(属性名=,属性名2=2) 

案例演示

public @interface MyAnnotation1 {
    // 不定义注解属性
}
public @interface MyAnnotation2 {
    int a();

    String[] b();
}

@MyAnnotation1()
public class Test1 {
    @MyAnnotation1
    public static void main(String[] args) {
        /*
            使用注解:
                不带属性的注解: 注解中没有定义属性,或者里面的属性已经给了默认值
                     在需要使用该注解的地方,写上 @注解名 即可 或者 @注解名()


         */
    }
}

@MyAnnotation2(a=18,b={"xxx","aaa"})
public class Test2 {

    @MyAnnotation2(a=18,b={"xxx","aaa"})
    public static void main(String[] args) {
        /*
             使用注解:
                带有属性的注解: 注解中定义了属性,并这些属性没有给默认值
                    标准格式: @注解名(属性名=属性值,属性名=属性值,....)
         */

    }
}

给注解属性赋值的注意事项

  • 一旦注解有属性了,使用注解的时候,属性必须有值
  • 若属性类型是一维数组的时候,当数组的值只有一个的时候可以省略{}
  • 如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略
  • 注解属性可以有默认值 格式:属性类型 属性名() default 默认值;
public @interface MyAnnotation1 {
    int a();
}

public @interface MyAnnotation2 {
    int[] arr();
}

public @interface MyAnnotation3 {
    int value();
}

public @interface MyAnnotation33 {
    String[] value();
}

public @interface MyAnnotation4 {
    int a() default 10;
}

public class Test {
    public static void main(String[] args) {
       
    }

    //  注解属性可以有默认值  格式:属性类型 属性名() defaul t 默认值;
    //@MyAnnotation4
    //@MyAnnotation4()
    @MyAnnotation4(a = 100)
    public static void method4(){

    }

    // 若属性类型是一维数组的时候,当数组的值只有一个的时候可以省略{}
    //如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略
    //@MyAnnotation33(value={"xxx","aaa"})
    //@MyAnnotation33(value={"xxx"})
    //@MyAnnotation33(value="xxx")
    @MyAnnotation33("xxx")
    public static void method33(){

    }

    // 如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略
    //@MyAnnotation3(value=10)
    @MyAnnotation3(10)
    public static void method3(){

    }

    // 若属性类型是一维数组的时候,当数组的值只有一个的时候可以省略{}
    // @MyAnnotation2(arr={10,20,30})
    // @MyAnnotation2(arr={10})
    @MyAnnotation2(arr=10)
    public static void method2(){

    }

    // 一旦注解有属性了,使用注解的时候,属性必须有值
    @MyAnnotation1(a = 10)
    public static void method1(){

    }

}

元注解

什么是元注解

  • 定义在注解上的注解

常见的元注解

@Target:表示该注解作用在什么上面(位置),默认注解可以在任何位置. 值为:ElementType的枚举值

​ METHOD:方法

​ TYPE:类 接口

​ FIELD:字段

​ CONSTRUCTOR:构造方法声明

@Retention:定义该注解保留到那个代码阶段, 值为:RetentionPolicy类型,默认只在源码阶段保留

​ SOURCE:只在源码上保留(默认)

​ CLASS:在源码和字节码上保留

​ RUNTIME:在所有的阶段都保留

.java (源码阶段) ----编译—> .class(字节码阶段) ----加载内存–> 运行(RUNTIME)

案例:

// 标准格式:
//@Target(value={ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,ElementType.LOCAL_VARIABLE})
//@Retention(value= RetentionPolicy.RUNTIME)
    // 省略格式
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {// 默认该注解可以在任何位置使用

}

@MyAnnotation1
public class Test1 {

    @MyAnnotation1
    int age = 18;

    @MyAnnotation1
    public static void main(String[] args) {
       
        @MyAnnotation1
        int num1 = 10;// 局部变量

        // jdk中的@Override
        // @Override // 编译报错
        int num2 = 10;// 局部变量

    }

    @Override
    public String toString() {
        return super.toString();
    }
}

注解解析

java.lang.reflect.AnnotatedElement接口: Class、Method、Field、Constructor等实现了AnnotatedElement

  • T getAnnotation(ClassannotationType):得到指定类型的注解引用。没有返回null。

  • boolean isAnnotationPresent(Class<?extends Annotation> annotationType):判断指定的注解有没有。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
    String name();

    int age();

}


public class Test {

    @MyAnnotation1(name="小明",age=18)
    public void show1(){
        System.out.println("show1方法执行了....");
    }

    public void show2(){
        System.out.println("show2方法执行了....");
    }

    public static void main(String[] args) throws Exception{
        
        // 需求:1.获取show1方法上面的注解对象
        // 1.1 得到Test类的Class对象
        Class<?> c = Class.forName("com.xxx.demo_注解解析.Test");

        // 1.2 获得show1方法的Method对象
        Method show1M = c.getDeclaredMethod("show1");

        // 1.3 根据Method对象调用getAnnotation()方法得到注解对象
        MyAnnotation1 a1 = show1M.getAnnotation(MyAnnotation1.class);
        System.out.println(a1.name());
        System.out.println(a1.age());

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

        // 2.需求: 判断某个方法上是否有MyAnnotation1注解
        // 判断show1方法上是否有MyAnnotation1注解
        boolean res1 = show1M.isAnnotationPresent(MyAnnotation1.class);
        System.out.println(res1);// true

        // 判断show2方法上是否有MyAnnotation1注解
        Method show2M = c.getDeclaredMethod("show2");
        boolean res2 = show2M.isAnnotationPresent(MyAnnotation1.class);
        System.out.println(res2);// false
    }
}

MyTest注解案例

需求

在一个类(测试类,TestDemo)中有三个方法,其中两个方法上有@MyTest,另一个没有.还有一个主测试类(MainTest)中有一个main方法. 在main方法中,让TestDemo类中含有@MyTest方法执行. 自定义@MyTest, 模拟单元测试.

思路分析

  1. 定义两个类和一个注解
  2. 在MainTest的main()方法里面:
    ​ 1.获得TestDemo字节码对象
    ​ 2.反射获得TestDemo里面的所有的方法
    ​ 3.遍历方法对象的数组. 判断是否有@MyTest(isAnnotationPresent)
    ​ 4.有就执行(method.invoke())

代码实现

  • MyTest.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {

}
  • TestDemo.java
public class TestDemo {
    @MyTest
    public void show1(){
        System.out.println("show1方法执行了...");
    }

    @MyTest
    public void show2(){
        System.out.println("show2方法执行了...");
    }

    public void show3(){
        System.out.println("show3方法执行了...");
    }
}
  • MainTest.java
public class MainTest {

    public static void main(String[] args) throws Exception {
        // 让第一个类中含有@MyTest注解的方法执行
        // 1.获取TestDemo类的字节码对象
        Class<TestDemo> clazz = TestDemo.class;

        // 2.使用字节码对象获取该类中所有方法对象
        Method[] methods = clazz.getDeclaredMethods();

        // 3.循环遍历所有方法对象
        for (Method method : methods) {
            // 4.在循环中,判断遍历出来的方法对象是否含有@MyTest注解
            boolean res = method.isAnnotationPresent(MyTest.class);
            if (res) {
                // 5.如果有,就调用该方法执行
                method.invoke(clazz.newInstance());
            }
        }
    }
}

XML

XML介绍

什么是XML

  • XML 指可扩展标记语言(EXtensible Markup Language
  • XML是用来传输数据的,不是用来显示数据的。另外一个HTML是用来显示数据的。
  • XML 标签没有被预定义。您需要自行定义标签。
  • XML 是 W3C 的推荐标准
    W3C在1988年2月发布1.0版本,2004年2月又发布1.1版本,但因为1.1版本不能向下兼容1.0版本,所以1.1没有人用。同时,在2004年2月W3C又发布了1.0版本的第三版。
    在这里插入图片描述

XML 与 HTML 的主要差异

  • html语法松散,xml语法严格,区分大小写
  • html做页面展示,xml传输数据
  • html所有标签都是预定义的,xml所有标签都是自定义的

xml的作用

  • 作为配置文件。 javaee框架 ssm大部分都会使用xml作为配置文件
  • XML可以存储数据 , 作为数据交换的载体(使用XML格式进行数据的传输)。

XML组成元素

一个标准XML文件一般由以下几部分组成:文档声明、元素、属性、注释、转义字符、字符区。

文档声明

<?xml version="1.0" encoding="utf-8" ?>
  1. 文档声明可以不写
  2. 文档声明必须为<?xml开头,以?>结束
  3. 文档声明必须从文档的0行0列位置开始
  4. 文档声明中常见的两个属性:
  • version:指定XML文档版本。必须属性,这里一般选择1.0;
  • enconding:指定当前文档的编码,可选属性,默认值是utf-8;

元素\标签

  1. 元素是XML中最重要的组成部分,元素也叫标签
  2. 标签分为开始标签和结束标签,开始标签<名字> 结束标签</名字>
  3. 开始标签和结束标签中间写的是标签内容,标签的内容可以是文本,也可以是其他标签
  4. 如果标签没有任何内容,那么可以定义空标签(比如:<名字/>)
  5. 标签可以嵌套,但是不能乱嵌套
  6. 一个XML文件只有一个根标签
  7. 命名规则:
    不要使用XML xML xml 写样的单词
    ​不能使用空格,冒号
    ​命名区分大小写
    数字不能开头
    <?xml version="1.0" encoding="UTF-8" ?>
    <person>
        <name>小明</name>
        <age>年龄</age>
        <aaa/>
    </person>
    

属性

  1. 位置: 属性是元素的一部分,它必须出现在元素的开始标签中,不能写在结束标签中

  2. 格式: 属性的定义格式:属性名=“属性值”,其中属性值必须使用单引或双引号括起来

  3. 一个元素可以有0~N个属性,但一个元素中不能出现同名属性

  4. 属性名不能使用空格,不要使用冒号等特殊字符,且必须以字母开头

    <?xml version="1.0" encoding="UTF-8" ?>
    <person>
        <name id = "001" level = '98'>小明</name>
        <age>10</age>
    
        <aaa type = 'xxx' />
    </person>
    

注释

<!--注释内容-->
  • XML的注释,既以<!--开始,-->结束。
  • 注释不能嵌套
  • idea上快捷键:ctrl + /

转义字符

因为有些特殊的字符在XML中是不会被识别的,所以在元素体或属性值中想使用这些符号就必须使用转义字符(也叫实体字符),例如:">"、"<"、"’"、"""、"&"。
在这里插入图片描述
注意:严格地讲,在 XML 中仅有字符 “<“和”&” 是非法的。省略号、引号和大于号是合法的,但是把它们替换为实体引用是个好的习惯。
转义字符应用示例:

<price> 苹果的价格: price > 5 &amp;&amp;  price &lt; 10</price>

字符区

  • CDATA 内部的所有东西都会被解析器忽略,当做文本
<![CDATA[
	文本数据
]]>
<!--写步骤 -->
<>
<!>
<![]>
<![CDATA]>
<![CDATA[ 文本 ]]>

<!-- 案例 -->
 <price>
        <![CDATA[
            苹果的价格: price > 5 &&  price < 10
        ]]>
 </price>

XML文件的约束-DTD约束

xml约束概述

  • 在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束。
  • 约束文档定义了在XML中允许出现的元素(标签)名称、属性及元素(标签)出现的顺序等等。
  • 两种约束:DTD约束(文件后缀为dtd),Schema约束(文件后缀为xsd)
  • 注意: 约束不是我们要写的东西,我们的工作是根据约束去写XML

根据DTD约束写XML

  • DTD约束文档
<?xml version="1.0" encoding="UTF-8" ?>
<!--
    复制内容如下到XML文件中:
        <!DOCTYPE 书架 SYSTEM "bookdtd.dtd">
-->
<!--
对元素的约束:
ELEMENT表示这是一个元素 书架表示根标签  书是书架的子标签  +数量词,表示出现的次数,至少出现一次(大于等于1次)
-->
<!ELEMENT 书架 (书+)>
<!--书 这是一个标签  书标签中包含书名,作者,售价这些子标签 ,表示子标签出现的顺序关系-->
<!ELEMENT  (书名,作者,售价)>
<!--书名 这是一个标签  #PCDATA标签类型 书名标签中是文本-->
<!ELEMENT 书名 (#PCDATA)>
<!ELEMENT 作者 (#PCDATA)>
<!ELEMENT 售价 (#PCDATA)>

<!--ATTLIST表示对属性的约束 对书标签的 id,编号,出版社,type属性进行约束 -->
<!--属性名为id 属性的类型为ID(ID类型表示唯一,并且不能以数字开头) #REQUIRED表示id属性必须要有 -->
<!--属性名为编号  属性的类型为CDATA(文本) #IMPLIED表示编号属性可有可无-->
<!--属性名为出版社 属性的类型为枚举类型(任选其一)  "xxxx" 默认值为xxxx-->
<!-- 属性名为type 属性的类型为CDATA文本 #FIXED表示固定值为 "IT" -->
<!ATTLIST 
        id ID #REQUIRED
        编号 CDATA #IMPLIED
        出版社 (xx|xx|xxx) "xxx"
        type CDATA #FIXED "xx"
        >       
  • XML

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE 书架 SYSTEM "bookdtd.dtd">
    <书架>
        < id="a1" 编号="001" 出版社="xx" type="xx">
            <书名>xxxx</书名>
            <作者>xxxx</作者>
            <售价>99.8</售价>
        </>
        < id="a2">
            <书名>xxxxx</书名>
            <作者>xx</作者>
            <售价>9.8</售价>
        </>
    </书架>
    

语法(了解)

文档声明(了解)
  1. 内部DTD,在XML文档内部嵌入DTD,只对当前XML有效。
   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE 根元素 [元素声明]>><!--内部DTD-->
  1. 外部DTD—本地DTD,DTD文档在本地系统上,企业内部自己项目使用。
   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE 根元素 SYSTEM "文件名"><!--外部本地DTD-->
  1. 外部DTD—公共DTD,DTD文档在网络上,一般都有框架提供 , 也是我们使用最多的.
   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL">
   
   例如: <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
元素声明(了解)
  1. 约束元素的嵌套层级
    语法:
   <!ELEMENT 父标签 (子标签1,子标签2,…)>
   例如:
   <!ELEMENT books (book+)> <!--约束根元素是"books","books"子元素为"book",“+”为数量词-->
   <!ELEMENT book (name,author,price)><!--约束"book"子元素依次为“name”、“author”、“price”,-->
  1. 约束元素体里面的数据
    语法:
   <!ELEMENT 标签名字 标签类型>
   例如 <!ELEMENT name (#PCDATA)>

标签类型: EMPTY(即空元素,例如<hr/>) ANY(任意类型) (#PCDATA) 字符串数据
代码:

   <!ELEMENT name (#PCDATA)>
   <!ELEMENT author (#PCDATA)>
   <!ELEMENT price (#PCDATA)>
  1. 数量词(掌握)

    数量词符号含义
    *表示元素可以出现0到多个
    +表示元素可以出现至少1个
    ?表示元素可以是0或1个
    ,表示元素需要按照顺序显示
    |表示元素需要选择其中的某一个
属性声明(了解)

语法:

<!ATTLIST 标签名称 
		属性名称1 属性类型1 属性说明1
		属性名称2 属性类型2 属性说明2
		…
>
例如
<!ATTLIST book bid ID #REQUIRED>

属性类型

  • CDATA :表示文本字符串
  • ID:表示属性值唯一,不能以数字开头
  • ENUMERATED (DTD没有此关键字):表示枚举,只能从枚举列表中任选其一,如(鸡肉|牛肉|猪肉|鱼肉)

属性说明:

  • REQUIRED:表示该属性必须出现
  • IMPLIED:表示该属性可有可无
  • FIXED:表示属性的取值为一个固定值。语法:#FIXED “固定值”

代码:

<!ATTLIST 书									<!--设置"书"元素的的属性列表-->
		id ID #REQUIRED						 <!--"id"属性值为必须有-->
		编号 CDATA #IMPLIED				    <!--"编号"属性可有可无-->
		出版社 (xx|xx|xxxx) "xxxx"   <!--"出版社"属性值是枚举值,默认为“xxxx”-->
		type CDATA #FIXED "IT"                <!--"type"属性为文本字符串并且固定值为"IT"-->
>
案例
<?xml version = "1.0" encoding="GB2312" standalone="yes"?>
<!DOCTYPE 购物篮 [
   <!ELEMENT 购物篮 (肉+)>
	<!ELEMENT  EMPTY>
	<!ATTLIST  品种 ( 鸡肉 | 牛肉 | 猪肉 | 鱼肉 ) "鸡肉">
]>
<购物篮>
	< 品种="牛肉"></>
    < 品种="牛肉"></>
    < 品种="鱼肉"></>
    </>
</购物篮>

schema约束(了解)

概念

schema和DTD一样,也是一种XML文件的约束。

Schema 语言也可作为 XSD(XML Schema Definition)。

Schema约束的文件的后缀名.xsd

Schema 功能更强大,数据类型约束更完善。

根据schema约束写出xml文档

  • Schema约束文档:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!--    xxxxxxx.将注释中的以下内容复制到要编写的xml的声明下面
    复制内容如下到XML文件中:
    <书架 xmlns="http://www.xxxx.cn"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.xxxx.cn bookSchema.xsd" >
    -->
    <xs:schema
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.xxxx.cn"
            elementFormDefault="qualified">
        <!--element代表元素   元素名 叫 书架-->
        <xs:element name='书架'>
            <!--书架是一个复杂元素-->
            <xs:complexType>
                <!--sequence代表子元素要顺序出现 unbounded代表子元素可以出现无数次-->
                <xs:sequence maxOccurs='unbounded'>
                    <!--书架中的子元素叫 书-->
                    <xs:element name=''>
                        <!--书也是一个复杂元素-->
                        <xs:complexType>
                            <!--书中的子元素是顺序出现的-->
                            <xs:sequence>
                                <!--书名是书的子元素 书名是字符串类型-->
                                <xs:element name='书名' type='xs:string'/>
                                <!--作者是书的子元素 作者是字符串类型-->
                                <xs:element name='作者' type='xs:string'/>
                                <!--售价是书的子元素 售价是小数类型-->
                                <xs:element name='售价' type='xs:double'/>
                            </xs:sequence>
                        </xs:complexType>
                    </xs:element>
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:schema>
    
  • 根据上面的Schema约束编写XML

    • 声明方式
    <书架 xmlns="http://www.xxxx.cn"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.xxxx.cn bookSchema.xsd" >
            
  <?xml version="1.0" encoding="UTF-8" ?>
    <b:书架 xmlns:a="http://www.xxxx.cn"
      xmlns:b="http://www.xxxx.cn"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.xxxx.cn bookSchema.xsd
                            http://www.xxxx.cn bookSchema2.xsd
        " >
        <b:书 bid="10">
            <b:书名>xxxx</b:书名>
            <b:作者>xxxx</b:作者>
            <b:售价>99.8</b:售价>
        </b:>
        <b:>
            <b:书名>xxxx</b:书名>
            <b:作者>xxxx</b:作者>
            <b:售价>99.8</b:售价>
        </b:>
    </b:书架>

  • XML编写示例
    <?xml version="1.0" encoding="UTF-8" ?>
    <书架 xmlns="http://www.xxxx.cn"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.xxxx.cn bookSchema.xsd" >
        <>
            <书名>xxxx</书名>
            <作者>xxxx</作者>
            <售价>99.8</售价>
        </>
    
    </书架>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值