09-反射,注解,XML

反射_注解

Junit

简介

  • 概述:

    • Junit 它属于白盒测试的内容, 简单理解就是用来替代main方法的, 即: 代码不用往main()方法中放, 也能实现直接运。
      它使用Java语言编写的第三方的框架, 用之前需要先导包
  • 具体步骤:

    1. 项目(模块)新建一个文件夹, 名字叫: lib
    2. 把要使用的jar包拷贝进去.
    3. 配置运行环境.
      选中jar包, 右键 -> Add as Library
  • Junit单元测试涉及到的注解:

    • @Before 被它标注的方法, 会在**@Test标注的方法执行之前**, 自动调用.
    • @Test 被它标注的方法, 可以进行Junit单元测试.
    • @After 被它标注的方法, 会在**@Test标注的方法执行之后**, 自动调用.
  • 细节:
    Junit单元测试只针对于 非静态无参无返回值的方法有效.

package com.ithiema.junit;

import org.junit.Test;

public class Demo01 {

    @Test
    public void show(){
        System.out.println("我是show()方法");
    }
}
演示Junit相关的注解
package com.ithiema.junit;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class Demo02 {

    @Before
    public void init() {
        System.out.println("我是初始化数据的,我会在@Test之前,会被自动调用");
    }

    @Test
    public void show1() {
        System.out.println("我是show1()方法");
    }

    @Test
    public void show2() {
        System.out.println("我是show2()方法");
    }

    @After
    public void destroy() {
        System.out.println("我是释放资源的,我会在@Test之后,会被自动调用");
    }
}

有几个@Test@Before@After就打印几次

一个完整的JavaWeb项目的开发流程:

  1. 需求调研.
  2. 编写需求(调研)文档. //产品经理.
  3. 编写静态页面. //UI工程师.
  4. 技术选型, 框架选定, 统一工具.
  5. 数据库建模, 数据表设计. //架构师, 项目经理, 你
  6. 软件开发. //程序员.
  7. 测试, 部署, 运维. //测试人员, 运维人员.

类加载器

简介

  • 概述:它指的是ClassLoader, 主要是用于加载类的字节码文件(.class文件)进内存, 并为之创建与其对应的 Class对象.
什么时候才会加载类的字节码文件进内存?
  • 第一次使用类的成员, 但是该类的字节码文件内存中不存在时, 就会由 类加载器加载它的字节码文件进内存.
类加载器的机制是什么?
  • 全盘加载
    • 加载子类的时候, 连带它的父级别一起加载. Animal, Cat, Dog, JumpCat
  • 父类委托
    • 子类加载器加载某个字节码文件时, 会先询问它的父类加载器, 加不加载整个字节码, 父类做, 子类不做, 父类不做, 子类做.
      关系: BootStrapClassLoader(爷爷), ExtClassLoader(父级), APPClassLoader(孙子)
  • 缓存机制
    • 当第一次加载某个字节码文件进内存后, 就会存储到内存中, 之后在使用就不加载了, 而是从缓存直接读取.
三种类加载器的作用分别是什么?
  • BootStrapClassLoader
    • 也叫根类加载器, 负责加载 jre/lib/rt.jar 相关的 jar, 它的底层是通过 C语言实现的.
  • ExtClassLoader
    • 也叫扩展(Extension)类加载器, 负责加载 jre/lib/ext/*.jar 相关的jar包. JDK1.9更名为: PlatformClassLoader
  • AppClassLoader
    • 也叫应用程序(Application)类加载器, 负责加载用户自定义的类以及配置的classpath中的内容. JDK1.9更名为: SystemClassLoader

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nrmFQdO8-1669233094352)(F:\BigData\java\图片\day15图片\JVM是什么.png)]

ClassLoader类中的成员方法:
  • static ClassLoader getSystemClassLoader(); 返回用于委派的系统类加载器
  • ClassLoader getParent(); 返回父类加载器进行委派
案例: 演示类加载器
package com.ithiema.junit;

public class Demo03 {
    public static void main(String[] args) {
        //1. 获取当前类的 类加载器.
        ClassLoader classLoader1 = ClassLoader.getSystemClassLoader();

        //2. 打印当前类的 类加载器.
        System.out.println(classLoader1);               //AppClassLoader

        //3. 打印当前类的 类加载器的父类.
        System.out.println(classLoader1.getParent());   //ExtClassLoader

        //4. 当前当前类的 类加载器的父类的父类.
        /*
            这里理论上来讲要打印BootStrapClassLoader, 但是它的底层是通过C语言编写的,
            在java中没有与其对应的对象形式, 所以是一个 null
         */
        System.out.println(classLoader1.getParent().getParent());   //null
    }
}

反射

简介

  • 概述:
    • 反射指的就是在程序的运行期间, 通过 类的字节码文件对象操作 类中的成员(成员变量, 构造方法, 成员方法)等.
反射机制 图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CeXeblRy-1669233094354)(F:\BigData\java\图片\day15图片\03.反射机制.png)]

  • 步骤:
    1. 获取指定类字节码文件对象.
    2. 根据字节码文件对象, 获取指定的要操作的 成员对象.
    3. 操作指定的成员即可.

如何获取类的字节码对象:

  • 方式1:通过 类的class属性获取.
    格式: Student.class
    应用场景: 一般用作 锁对象 使用.

  • 方式2:通过 **对象名.getClass()**方法获取.
    格式:new Student().getClass();
    应用场景:一般用来 判断两个对象是否是同一个类型的对象.

  • 方式3:通过 反射方式, 强制加载某个类的字节码文件进内存, 并获取它对应的Class对象.
    格式:Class.forName(“com.itheima.pojo.Student”),

    ​ Class.forName(“全类名”),全类名 就是 包名+类名
    ​ 应用场景:强制加载某个类的字节码文件进内存, 一般结合反射使用.

案例:反射入门
public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //演示:  获取类的字节码文件对象的3种方式:
        //方式1: 类名.class属性
        Class<Student> clazz1 = Student.class;

        //方式2: 对象名.getClass()方法
        //?表示泛型的通配符, 即: 可以用来表示任意的数据类型
        //? extends Student:  向下限定, 这里的?必须是 Student类或者其子类对象
        //? super Student:    向上限定, 这里的?必须是 Student类或者其父类
        Class<? extends Student> clazz2 = new Student().getClass();

        //方式3: Class.forName("全类名"),全类名 就是 包名+类名
        Class<?> clazz3 = Class.forName("com.itheima.pojo.Student");


        //验证结论: 一个.java文件, 对应一个.class文件, 即: 一个java文件只有一个 字节码文件对象.
        System.out.println(clazz1 == clazz2);		//true
        System.out.println(clazz1 == clazz3);		//true
        System.out.println(clazz2 == clazz3);		//true
    }
}

Class类中相关的成员方法:

  • public static Class forName(String classname); 根据全类名, 获取该类对应的 字节码文件对象

  • public Object newInstance(); 根据类中的公共的空参构造方法, 获取该类的实例(对象)

  • public Constructor getConstructor(Class… clazz);
    根据字节码文件类型, 获取指定构造方法(不包含私有)

  • public Constructor getDeclaredConstructor(Class… clazz);
    根据字节码文件类型, 获取指定构造方法(包含私有)

  • public Constructor[] getConstructors(); 获取类中, 所有构造器对象(不包含私有)

  • public Constructor[] getDeclaredConstructors(); 获取类中, 所有构造器对象(包含私有)

  • public Field getField(String name); 根据字段名, 获取指定字段对象(不包含私有)

  • public Field getDeclaredField(String name); 根据字段名, 获取指定字段对象(包含私有)

  • public Field[] getFields(); 获取类中, 所有字段对象(不包含私有)

  • public Field[] getDeclaredFields(); 获取类中, 所有字段对象(包含私有)

  • public Method getMethod(String name, Class… params); 根据字段名, 获取该类中指定成员方法对象, 包括继承过来的方法

  • public Method getDeclaredMethod(String name, Class… params); 根据字段名, 获取该类中指定成员方法对象, 不包括继承过来的方法

  • public Method[] getMethods(); 根据字节码文件对象, 获取该类中所有成员方法对象, 包括继承过来的方法

  • public Method[] getDeclaredMethods(); 根据字节码文件对象, 获取该类中所有成员方法对象, 不包括继承过来的方法

Constructor类中的相关方法:

  • public T newInstance(Object… values); 根据指定的参数值, 获取该类的对象.

  • public void setAccessible(boolean flag); 设置暴力反射, 如果为true, 说明设置暴力反射.

Field类中的相关方法:

  • public void set(Object obj, Object value); 设置obj对象的指定属性指定的值(value)

  • public void setAccessible(boolean flag); 设置暴力反射, 如果为true, 说明设置暴力反射

Method类中的相关方法:

  • public void setAccessible(boolean flag); 设置暴力反射, 如果为true, 说明设置暴力反射
  • public Object invoke(Object obj, Object… value); 执行方法

反射小技巧, 掌握如下单词:

get(获取), declared(包含私有), Constructor(构造器, 构造方法), Field(字段, 成员变量), method(方法), newInstance(创建实例), set(设置), invoke(表示调用方法), accessible(权限)

案例:反射_操作类中的 构造方法

需求1:获取类中 所有的 构造方法,不包含私有 理解
需求2:获取类中 所有的 构造方法,包含私有 理解
需求3:通过类中 公共空参构造方法 创建对象
需求4:通过类中的 公共带参构造 创建该类的对象
需求5:通过类中的 私有带参构造 创建该类的对象

涉及到的成员方法
  • Class类中相关的成员方法:
public static Class forName(String classname);  据全类名, 获取该类对应的 字节码文件对象.
public Object newInstance();   根据类中的公共的空参构造方法, 获取该类的实例(对象)

public Constructor getConstructor(Class... clazz);  根据字节码文件类型, 获取指定的构造方法(不包含私有)
public Constructor getDeclaredConstructor(Class... clazz);  根据字节码文件类型, 获取指定的构造方法(包含私有)

public Constructor[] getConstructors();   获取类中, 所有的构造器对象(不包含私有)
public Constructor[] getDeclaredConstructors();  获取类中, 所有的构造器对象(包含私有)
  • Constructor类中的相关方法:
public T newInstance(Object... values);   根据指定的参数值, 获取该类的对象.
public void setAccessible(boolean flag);  设置暴力反射, 如果为true, 说明设置暴力反射.

JavaBean类

package com.itheima.junit;

public class Student {
    private String name;
    public int age;

    //公共的,空参构造
    public Student() {
    }

    //私有的带参构造
    private Student(String name) {
        this.name = name;
    }

    //公共的,全参构造
    public Student(String name, int age) {
        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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类

package com.itheima.junit;

import org.junit.Test;

import java.lang.reflect.Constructor;


public class Demo05 {
    //需求一:获取类中 所有的 构造方法,不包含私有     理解
    @Test
    public void show1() throws Exception {
        //1.获取Student类的字节码文件,不包含私有
        Class<?> clazz = Class.forName("com.itheima.junit.Student");
        //2.获取该类的 所有的 构造方法对象,不包含私有
        Constructor<?>[] cons = clazz.getConstructors();
        //3.遍历,打印每一个构造器对象
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }
    }

    //需求2:获取类中 所有的 构造方法,包含私有     理解
    @Test
    public void show2() throws Exception {
        //1.获取Student类的字节码文件,不包含私有
        Class<?> clazz = Class.forName("com.itheima.junit.Student");
        //2.获取该类的 所有的 构造方法对象,不包含私有
        Constructor<?>[] cons = clazz.getDeclaredConstructors();
        //3.遍历,打印每一个构造器对象
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }
    }

    //需求3:通过类中 公共的 空参构造方法 创建对象
    @Test
    public void show3() throws Exception {

        //标准版

        //1.获取该类的字节码文件对象
        Class<?> clazz = Class.forName("com.itheima.junit.Student");
        //2.获取指定的构造器对象
        Constructor<?> con = clazz.getConstructor();
        //3.通过Constructor#newInstance()方法,创建对象
        Student s = (Student) con.newInstance();
        //4.打印对象
        System.out.println(s);

        //优化版,注意,该方法之针对于 类中的 公共的,空参构造有效
        //即:通过Class#newInstance()方法实现

        //1.获取该类的字节码文件对象
        Class<?> clazz2 = Class.forName("com.itheima.junit.Student");
        //2.通过Class#newInstance()方法实现
        Student s2 = (Student) clazz2.newInstance();
        //3.打印对象
        System.out.println(s2);
    }

    //需求4:通过类中的公共的带参构造 创建该类的对象
    @Test
    public void show4() throws Exception {
        //1.获取该类的字节码文件对象
        Class<?> clazz = Class.forName("com.itheima.junit.Student");
        //2.获取指定的构造器对象
        Constructor<?> con = clazz.getConstructor(String.class, int.class);
        //3.通过Class#newInstance()方法实现
        Student s = (Student) con.newInstance("李四", 23);
        //4.打印对象
        System.out.println(s);
    }

    //需求5:通过类中的私有的带参构造 创建该类的对象
    @Test
    public void show5() throws Exception {
        //1.获取该类的字节码文件对象
        Class<?> clazz = Class.forName("com.itheima.junit.Student");
        //2.获取指定的构造器对象
        Constructor<?> con = clazz.getDeclaredConstructor(String.class);

        //细节:反射但凡操作 私有成员,用它之前一定要先进行:暴力反射
        con.setAccessible(true);

        //3.通过Class#newInstance()方法实现
        Student s = (Student) con.newInstance("李四");
        //4.打印对象
        System.out.println(s);
    }
}
案例:反射_操作类中的 成员变量

需求1. 如何获取类中 所有的 字段对象(包括私有)
需求2. 如何操作类中 私有的 成员变量

涉及到的成员方法

Class类中相关的成员方法:

public static Class forName(String classname);  据全类名, 获取该类对应的 字节码文件对象.
public Object newInstance();   根据类中的公共的空参构造方法, 获取该类的实例(对象)
            
public Field getField(String name);    根据字段名, 获取指定的字段对象(不包含私有)
public Field getDeclaredField(String name);   根据字段名, 获取指定的字段对象(包含私有)
public Field[] getFields();    获取类中, 所有的字段对象(不包含私有)
public Field[] getDeclaredFields();    获取类中, 所有的字段对象(包含私有)

Field类中的相关方法:

public void set(Object obj, Object value);  设置obj对象的指定属性为指定的值(value)
public void setAccessible(boolean flag);  设置暴力反射, 如果为true, 说明设置暴力反射.

JavaBean类

package com.itheima.junit;

public class Student {
    private String name;
    public int age;

    //公共的,空参构造
    public Student() {
    }

    //私有的带参构造
    private Student(String name) {
        this.name = name;
    }

    //公共的,全参构造
    public Student(String name, int age) {
        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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类

package com.itheima.junit;

import org.junit.Test;

import java.lang.reflect.Field;

public class Demo06 {


    //需求1. 如何获取类中 所有的 字段对象(包括私有)
    @Test
    public void show1() throws Exception {
        //1.获取Student类的字节码文件,包含私有
        Class<?> clazz = Class.forName("com.itheima.junit.Student");

        //2.获取该类的 所有的 字段对象,包含私有
        Field[] fields = clazz.getDeclaredFields();

        //3.遍历,打印每一个字段对象
        for (Field field : fields) {
            System.out.println(field);
        }
    }

    @Test
    //需求2. 如何操作类中 私有的 成员变量
    public void show2() throws Exception {
        //1.获取该类的字节码文件对象
        Class<?> clazz = Class.forName("com.itheima.junit.Student");

        //2.获取该类的实例对象,通过 公共的空参实现
        Student s = (Student) clazz.newInstance();

        //3.根据字节码文件,获取该类中指定的 字段对象
        Field name = clazz.getDeclaredField("name");

        //细节:因为是私有,记得:暴力反射
        name.setAccessible(true);

        //4.设置指定对象s 的指定属性name 为指定的值 李四
        name.set(s, "李四");

        //5.设置年龄
        Field age = clazz.getField("age");
        //age.setAccessible(true);      如果是私有,记得暴力反射
        age.set(s,35);

        //5.打印结果
        System.out.println(s);
    }
}
案例:反射_操作类中的 成员方法

需求1:反射操作类中的 公共的 无参无返回值
需求2:反射操作类中的 公共的 有参有返回值

涉及到的成员方法

Class类中相关的成员方法:

public static Class forName(String classname);  据全类名, 获取该类对应的 字节码文件对象.
public Object newInstance();   根据类中的公共的空参构造方法, 获取该类的实例(对象)
            
public Method[] getMethods();
	    根据字节码文件对象, 获取该类中所有的成员方法对象, 包括继承过来的方法.
public Method[] getDeclaredMethods();
        根据字节码文件对象, 获取该类中所有的成员方法对象, 不包括继承过来的方法..
public Method getMethod(String name, Class... params);
		根据字段名, 获取该类中指定的成员方法对象, 包括继承过来的方法.
public Method getDeclaredMethod(String name, Class... params);
        根据字段名, 获取该类中指定的成员方法对象, 不包括继承过来的方法.

Method类中的相关方法:

public void setAccessible(boolean flag);        	如果为true, 表示: 暴力反射.
public Object invoke(Object obj, Object... value);	执行方法.

JavaBean类

package com.itheima.junit;

public class Student {
    private String name;
    public int age;

    //公共的,空参构造
    public Student() {
    }

    //私有的带参构造
    private Student(String name) {
        this.name = name;
    }

    //公共的,全参构造
    public Student(String name, int age) {
        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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void eat() {
        System.out.println("吃饭");
    }

    public int getSum(int a, int b) {
        return a + b;
    }
}

测试类

package com.itheima.junit;

import org.junit.Test;

import java.lang.reflect.Method;


public class Demo07 {

    //需求1:反射操作类中的公共的 无参无返回值
    @Test
    public void show1() throws Exception {
        //1.获取该类的字节码文件对象
        Class<?> clazz = Class.forName("com.itheima.junit.Student");

        //2.获取该类的实例对象,通过 公共的空参实现
        Student s = (Student) clazz.newInstance();

        //3.根据字节码文件,获取该类中指定的 方法对象
        Method eat = clazz.getMethod("eat");

        //4.调用方法
        eat.invoke(s);
        //5.打印方法的返回值,如果有返回值
    }

    //需求2:反射操作类中公共的 有参有返回值
    @Test
    public void show2() throws Exception {
        //1.获取该类的字节码文件对象
        Class<?> clazz = Class.forName("com.itheima.junit.Student");

        //2.获取该类的实例对象,通过 公共的空参实现
        Student s = (Student) clazz.newInstance();

        //3.根据字节码文件,获取该类中指定的 方法对象
        //Method getSum = clazz.getDeclaredMethod("getSum", int.class, int.class);
        Method getSum = clazz.getMethod("getSum", int.class, int.class);

        //如果是私有成员,记得暴力反射
        //getSum.setAccessible(true);

        //4.调用方法
        int sum = (int) getSum.invoke(s, 10, 20);

        //5.打印方法的返回值,如果有返回值
        System.out.println("求和结果: " + sum);
    }
}
反射案例:反射越过泛型检查

需求:给ArrayList集合中添加 字符串

  • 细节:泛型只在程序编写期间有效,在编译,运行期间无效
  • 解题思路:因为泛型在程序运行期间无效,所以我们要在程序的运行期间 往集合中添加元素,这就要用:反射技术
package com.itheima.junit;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Demo08 {
    public static void main(String[] args) throws Exception{
        //1.创建集合对象
        ArrayList<Integer> list = new ArrayList<>();

        //2.尝试往集合中添加数据
        list.add(10);
        list.add(20);
        list.add(30);
        //list.add("abc");

        //3.获取该类的字节码文件对象
        Class<?> clazz = Class.forName("java.util.ArrayList");

        //4.获取add方法对象,指定方法的形参类型为:Object类型
        Method add = clazz.getMethod("add", Object.class);

        //5.通过反射的方式调用方法,往集合中添加元素
        add.invoke(list,"abc");
        add.invoke(list,false);
        add.invoke(list,10.20);
        add.invoke(list,new Student("李四",23));

        //6.打印集合
        System.out.println(list);
    }
}

Properties

简介
  • 概述:
    • 它是一个集合类, 是Hashtable的子类, 一般用来操作属性集的, 即: 结合配置文件使用, 且它的键值都是String类型
    • 它是唯一一个可以和IO流直接相结合使用的集合类, 它可以直接从流中读取数据, 或者直接写入集合数据到文件中
涉及到的成员方法:
  • setProperty(String key, String value); 添加元素到Properties集合中, 类似于: Map#put()

  • getProperty(String key); 根据 获取, 类似于: Map#get()

  • load(InputStream is); 从流中(文件中)直接加载数据到 Properties 集合中.

  • load(Reader r); 从流中(文件中)直接加载数据到 Properties集合中.

  • store(OutputStream os, String comments); 把集合中的数据直接写入到文件中, 第二个参数表示: 为什么修改配置文件, 即: 原因, 一般写 null

  • store(Writer w, String comments); 把集合中的数据直接写入到文件中, 第二个参数表示: 为什么修改配置文件, 即: 原因, 一般写 null

  • 细节:和Properties集合类相结合使用的文件, 一般都是配置文件(即: 后缀名为.properties的文件), 即: 键值对形式的数据

  • 特点:

    • 因为是键值对,所以是双列集合,特点:无序键具有唯一性
案例:反射加载配置文件,并执行指定的方法

需求1:演示Properties入门

需求2:演示集合从(配置)文件中直接读取数据

需求3:演示集合数据写入到文件中

package com.itheima.junit;

import java.io.*;
import java.util.Properties;

public class Demo09 {
    public static void main(String[] args) throws Exception {
        method01();
        method02();
        method03();
    }

    private static void method03() throws IOException {
        //需求3:演示集合数据写入到文件中
        //1.创建集合对象
        Properties pp = new Properties();
        //2.加载配置文件中的数据到集合中
        pp.load(new FileInputStream("javaSE/src/com/itheima/junit/my.properties"));
        //3.打印集合对象
        System.out.println("修改之前:" + pp);
        //4.修改集合数据
        pp.setProperty("age","39");
        pp.setProperty("phone","123456");
        System.out.println("修改之后:" + pp);
        //5.把修改后的数据,重新写回到配置文件中
        pp.store(new FileWriter("javaSE/src/com/itheima/junit/my.properties"),null);
    }

    private static void method02() throws IOException {
        //需求2:演示集合从(配置)文件中直接读取数据
        //1.创建集合
        Properties pp = new Properties();
        //2.加载配置文件中的数据到集合
        pp.load(new FileReader("javaSE/src/com/itheima/junit/my.properties"));
        //3.打印集合对象
        System.out.println(pp);
        System.out.println(pp.getProperty("age"));
    }

    private static void method01() {
        //需求1:演示Properties入门
        //1.创建集合
        Properties pp = new Properties();
        //2.添加元素
        pp.setProperty("username", "admin01");
        pp.setProperty("password", "pw111");
        pp.setProperty("password", "pw222");
        pp.setProperty("password2", "pw222");
        //3.根据键获取值
        pp.getProperty("password2");
        //4.打印集合元素
        System.out.println("pp: " + pp);
    }
}
  • 需求:
    1. 已知在项目(模块)下有一个config.properties,文件中记录的有,类名 和 方法名 两组属性
    2. 请用所学,根据配置文件中的类名和方法名,调用指定类的指定方法

my.properties

className=com.itheima.pojo.Student
methodName=eat

JavaBean类

package com.itheima.pojo;

public class Student {
    public void eat(){
        System.out.println("学生吃牛肉");
    }

    public void sleep(){
        System.out.println("学生睡觉");
    }

    public void study(){
        System.out.println("学生好好学习");
    }
}

测试类

package com.itheima.junit;

import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;

public class Demo10 {
    public static void main(String[] args) throws Exception{
        //1.创建Properties集合类
        Properties pp = new Properties();
        //2.读取配置文件中的信息到集合中
        pp.load(new FileInputStream("javaSE/src/com/itheima/junit/my.properties"));
        //3.获取具体的类名 和 方法名
        String className = pp.getProperty("className");
        String methodName = pp.getProperty("methodName");

        //4.根据类名,获取类的字节码文件对象
        Class<?> clazz = Class.forName(className);
        //5.创建该来的实例对象
        Object obj = clazz.newInstance();
        //6.根据方法名,获取具体的方法对象
        Method eat = clazz.getMethod(methodName);
        //7.调用方法
        eat.invoke(obj);
    }
}

注解

学习注解,就是学习注解解析,只要能做到:

1.自定义注解

2.使用注解,即:把注解加到类上,方法上 等…

3.注解解析:通过反射技术,获取类上,方法上的注解信息

简介

  • 概述:

    • 它是JDK1.5出来的,是一种代码级别的说明,和类,接口,枚举等是一个级别的.
    • 主要是用来对他们**(类,接口,枚举,方法,变量等)的功能做扩展,延伸**
  • 作用:

    • 编写文档:通过代码里标识的注解生成文档【例如:生成文档doc文档】

    • 代码分析:通过代码里标识的注解对代码进行分析【例如,注解的反射】

      重要的: 因为在框架中通常会采用注解的方式来代替配置文件再通过反射获取注解中配置的信息, 整个动作叫: 注解解析.

    • 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【例如,Override】

  • 常用的注解演示:

    ​ @Override 用来标记方法重写的,做基本的编译检查.
    ​ @Deprecated 用来标记你方法已过时,即:中划线.
    ​ @SupperssWarning 压制警告,传all表示压制所有.

自定义注解:

  • 概述:

    • 所谓的自定义注解,就是我们自己定义的一个注解,并使用它.
    • 注解的本质就是一个继承了Annotation接口接口.
  • 格式

public @interface MyAnnotation{
    public 数据类型 变量名() default 默认值;
    public 数据类型 变量名() default 默认值;
    public 数据类型 变量名() default 默认值;
    ......
}
  • 细节:

    1. 注解的成员变量的数据类型必须是: 所有的基本类型, String类型, Class类型, 枚举类型, 注解类型

    2. 以上所有类型的数组形式.

    3. 上述部分的格式中, public 和 default 默认值这部分是可以省略的.

    4. 使用注解的时候, 如果注解中的属性默认值, 则在使用注解时, 可以不同给该属性赋值.

    5. 使用注解的时候, 如果注解只有一个属性, 且属性名叫value, 不管该属性是单值类型还是数组类型,
      在使用注解时, 都可以省略 属性名.

    6. 如果某一个注解 想被注解解析, 则在该注解上必须定义: 元注解.

案例:注解入门
//MyAnnotation2		注解
package com.itheima.junit;

public @interface MyAnnotation2 {
}

//Gender 	枚举
package com.itheima.junit;

public enum Gender {
    male,female
}


package com.itheima.junit;

import java.lang.annotation.Annotation;

//自定义注解,用来演示注解中的成员的,即:能写啥,不能些啥,以及怎么写
//public interface MyAnnotation extends Annotation {   //早期写法,注解的本质就是一个,继承了Annotation接口的子接口
public @interface MyAnnotation {
    //成员变量,字符串类型
    public String name() default "李四";

    //基本类型
    int age();

    //Class类型
    Class clazz();

    //枚举类型,枚举解释:可以把它简单理解为就是一种统一规范,例如:male,female
    Gender gender() default Gender.male;

    //注解类型
    MyAnnotation2 my();

    //以上所有类型的 数组形式
    String[] authors();
}

元注解解释:
  • 概述:就是用来限定注解可以定义到哪里, 以及注解的生命周期的.

  • 分类:

    • @Target: 限定注解可以定义到哪里, 默认情况下: 注解可以定义到任意位置.
    • @Retention:限定注解的生命周期
注解解析:
  • 概述:所谓的注解解析指的就是通过反射的方式, 获取注解中内容的操作.
涉及到的Class类中的成员方法:
  • boolean isAnnotationPresent(Class annotationClass); 判断当前对象是否有指定的注解则返回true则返回false

  • T getAnnotation(Class annotationClass); 获得当前对象上指定的注解对象

  • Annotation[] getAnnotations(); 获得当前对象及其从父类上继承所有的注解对象

  • Annotation[] getDeclaredAnnotations(); 获得当前对象上所有的注解对象不包括父类的。

注解解析案例

步骤:

1.自定义Book注解,该注解有:name,price,authors三个属性,分别表示,书名,价格,作者
2.自定义类BookShelf(表示书架),该类中有一个 showBook()方法。
3.在BookShelf类以及showBook()方法上定义Book注解,并给该注解的属性赋值。
4.在测试类中,通过反射的方式,获取BookSelf类上以及showBook()方法上的注解

@Book

package com.itheima.junit;
//1.自定义Book注解,该注解有:name,price,authors三个属性,分别表示,书名,价格,作者

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//5.定义元注解
@Target({ElementType.TYPE,ElementType.METHOD})  //限定注解可以定义到类上方法上
@Retention(RetentionPolicy.RUNTIME) //限定注解在源码,字节码,运行期间 都有效
public @interface Book {

    public String name();

    double price() default 33.3;

    String[] authors();
}

@Book2

package com.itheima.junit;

//自定义的注解,演示只有一个属性且属性名叫value的特殊情况的
public @interface Book2 {
    //单值类型
    //String value();

    //数组类型
    String[] value();
}

自定义类BookShelf

package com.itheima.junit;

//2.自定义类BookShelf(表示书架),该类中有一个 showBook()方法。
//3.在BookShelf类以及showBook()方法上定义Book注解,并给该注解的属性赋值。
    //这个动作的目的是为了演示:注解可以用来替代配置文件

//@Book2(value = "李四")
//@Book2("李四")    //特殊写法,注解只有一个属性,且属性名叫value,可以省略属性名
//@Book2(value = {"张三","李四"})
@Book2({"张三","李四"})

@Book(name = "西游记", authors = {"唐僧", "孙悟空"})
public class BookShelf {

    @Book(name = "水浒传", price = 50.2, authors = {"施耐庵", "佚名"})
    public void showBook() {

    }
}

测试类

package com.itheima.junit;

import java.lang.reflect.Method;
import java.util.Arrays;

public class Demo11 {
    public static void main(String[] args) throws Exception{
        //4.在测试类中,通过反射的方式,获取BookSelf类上以及showBook()方法上的注解
        //4.1 解析BookShelf类上的@Book注解信息
        //1.获取该类的字节码文件对象
        Class<BookShelf> clazz = BookShelf.class;

        //2.判断其上是否有@Book注解
        if(clazz.isAnnotationPresent(Book.class)){

            //3.如果有,就获取@Book注解对象
            Book book = clazz.getAnnotation(Book.class);

            //4.从注解对象中个,获取该注解的各项信息,并打印
            System.out.println(book.name());
            System.out.println(book.price());
            System.out.println(Arrays.toString(book.authors()));
        }

        //4.2 解析BookShelf#showBook()方法上的@Book注解信息
        //1.获取该类的字节码文件对象
        Class<BookShelf> clazz2 = BookShelf.class;

        //2.获取指定的成员方法对象
        Method showBook = clazz2.getMethod("showBook");

        //3.判断该方法上是否有@Book注解
        if(clazz.isAnnotationPresent(Book.class)){
            //4.如果有,就获取该注解对象
            Book book = showBook.getAnnotation(Book.class);

            //5.通过注解对象,获取各个属性信息,并打印
            System.out.println(book.name());
            System.out.println(book.price());
            System.out.println(Arrays.toString(book.authors()));
        }
    }
}

XML

简介

  • 概述:

    • 它属于W3C(万维网联盟)公司的产品,主要是用来当做配置文件用的,即:存储文件.
      目前最新的版本到1.1,但是因为不向下兼容,所以我们还是用1.0
      全称:Extensible Markup Language,可扩展的标记语言.
  • 名词解释:

    • 可扩展: 它的标签可以自定义.
    • 标记语言: 即:它是由标签组成的.
  • 面试题:XML和HTML的区别是什么?

  1. 关于标签.
    HTMl的标签是预定义的, 即: 已经定义好了, 各个标签的作用.
    XML的标签是自定义的, 你可以自己写.
  2. 关于语法.
    HTML语法松散, XML语法严谨.
  3. 关于作用.
    HTML主要用于展示数据, XML主要用于存储数据.
XML文件的组成:
  1. 0行0列的位置是文档声明,指定:版本和编码表.

  2. 有且只能有一组根标签.

  3. 根标签可以有多个子标签.

  4. 标签可以有属性,格式为:属性名=“属性值”,单双引号均可.

  5. 注释写法

  6. 常用的转义符:

    • &lt;    <			//小于号
      &gt;    >			//大于号
      
<?xml version="1.0" encoding="utf-8" ?>
<!--根标签,有且只能有一个-->
<students>
    <!--第1个学生的信息-->
    <student id="heima_001">
        <name>李四</name>
        <age>23</age>
    </student>

    <!--第2个学生的信息-->
    <student id="heima_002">
        <name>王五</name>
        <age>33</age>
    </student>

    <cat id="003">
        <name>abc</name>
        <age>bcd</age>
    </cat>
</students>
XML的约束:
  • 概述:所谓的约束, 就使用来限定XML文件中, 能写啥, 不能写啥, 以及标签的顺序, 类型等信息的.

  • 分类:

    • DTD
      • 旧的约束规范, 只能大概限定XML文件,
      • 例如: 限定根标签名字, 子标签名字, 数量, 顺序, 属性等。并不能精确限定, 例如: 限定属性值必须为谁.
    • Schema
      • 新的约束规范, 本质也是一个XML文件, 可以对XML文件进行精细化限定.
      • 例如: 可以限定某一个标签的值必须为谁, 支持自定义类型, 我们也是通过自定义类型来限定标签的.

student.dtd

<!--这个是DYD约束文件,内容分析如下:-->

<!--限定根标签的名字必须是:students, 它的子元素名叫:student, *的意思是: 至少0次, 至多无所谓-->
<!ELEMENT students (student*) >

<!--student标签的子标签为:name, age, sex,且顺序也必须如此-->
<!ELEMENT student (name,age,sex)>

<!--用来限定这三个标签的属性值为: PCDATA类型,文本类型, 类似于Java中的String字符串形式-->
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT sex (#PCDATA)>

<!--限定student标签有一个number属性,表示id, REQUIRED表示具有唯一性-->
<!ATTLIST student number ID #REQUIRED>

student.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!--引入名字叫 student.dtd的约束文件,system表示约束本地文件的意思-->
<!DOCTYPE students SYSTEM "student.dtd">

<!--根标签-->
<students>
    <!--子标签,即:第1个学生的信息-->
	<student number="itcast_0001">
		<name>tom</name>
		<age>18</age>
		<sex>male</sex>
	</student>

    <student number="itcast_0002">
        <name>tom</name>
        <age>18</age>
        <sex>male</sex>
    </student>
</students>

student.xsd

<?xml version="1.0"?>
<!---因为Schema约束文件,本质也是一个XML文件,所以它也要遵循某些规范-->
<xsd:schema xmlns="http://www.itcast.cn/xml"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.itcast.cn/xml" elementFormDefault="qualified">

    <!--限定student标签,必须满足自定义类型 studentsType的规范-->
    <xsd:element name="students" type="studentsType"/>

    <!--这里定义的是 制定会议类型 studentType的具体规则-->
    <xsd:complexType name="studentsType">
        <xsd:sequence>
            <!--限定students的子标签为:student, 类型为studentType, student标签至少出现0次, 至多无所谓-->
            <xsd:element name="student" type="studentType" minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>

    <!--这里定义的是 自定义类型 studentType 具体规则-->
    <xsd:complexType name="studentType">
        <xsd:sequence>
            <!--限定子元素名叫:name, age, sex,且顺序也必须如此-->
            <xsd:element name="name" type="xsd:string"/>

            <!--限定age标签的内容,必须满足 自定义类型 ageType的规则-->
            <xsd:element name="age" type="ageType" />
            <!--限定sex标签的内容,必须满足 自定义类型 sexType的规则-->
            <xsd:element name="sex" type="sexType" />
        </xsd:sequence>

        <!--限定该标签(student),有一个number属性表示id,该属性必须满足:numberType类型的规则-->
        <xsd:attribute name="number" type="numberType" use="required"/>
    </xsd:complexType>

    <!--这里定义的是 自定义类型 sexType 具体规则-->
    <xsd:simpleType name="sexType">
        <!--限定内容是字符串,且必须是male或者female-->
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="male"/>
            <xsd:enumeration value="female"/>
        </xsd:restriction>
    </xsd:simpleType>

    <!--age标签的类型限定-->
    <xsd:simpleType name="ageType">
        <!--限定内容是整数,且必须是0~256-->
        <xsd:restriction base="xsd:integer">
            <xsd:minInclusive value="0"/>
            <xsd:maxInclusive value="256"/>
        </xsd:restriction>
    </xsd:simpleType>

    <!--限定student标签的number属性的 值的格式-->
    <xsd:simpleType name="numberType">
        <!--限定内容是字符串,且必须满足heima_四个数字 这种格式-->
        <xsd:restriction base="xsd:string">
            <xsd:pattern value="heima_\d{4}"/>
        </xsd:restriction>
    </xsd:simpleType>
</xsd:schema> 

  • 名称空间:
    • 概述:就类似于Java中的导包, 用来区分 不同约束文件中, 相同名字的约束信息的

:string"/>

        <!--限定age标签的内容,必须满足 自定义类型 ageType的规则-->
        <xsd:element name="age" type="ageType" />
        <!--限定sex标签的内容,必须满足 自定义类型 sexType的规则-->
        <xsd:element name="sex" type="sexType" />
    </xsd:sequence>

    <!--限定该标签(student),有一个number属性表示id,该属性必须满足:numberType类型的规则-->
    <xsd:attribute name="number" type="numberType" use="required"/>
</xsd:complexType>

<!--这里定义的是 自定义类型 sexType 具体规则-->
<xsd:simpleType name="sexType">
    <!--限定内容是字符串,且必须是male或者female-->
    <xsd:restriction base="xsd:string">
        <xsd:enumeration value="male"/>
        <xsd:enumeration value="female"/>
    </xsd:restriction>
</xsd:simpleType>

<!--age标签的类型限定-->
<xsd:simpleType name="ageType">
    <!--限定内容是整数,且必须是0~256-->
    <xsd:restriction base="xsd:integer">
        <xsd:minInclusive value="0"/>
        <xsd:maxInclusive value="256"/>
    </xsd:restriction>
</xsd:simpleType>

<!--限定student标签的number属性的 值的格式-->
<xsd:simpleType name="numberType">
    <!--限定内容是字符串,且必须满足heima_四个数字 这种格式-->
    <xsd:restriction base="xsd:string">
        <xsd:pattern value="heima_\d{4}"/>
    </xsd:restriction>
</xsd:simpleType>

</xsd:schema>


* 名称空间:
  * 概述:就类似于Java中的导包, 用来区分 不同约束文件中, 相同名字的约束信息的

# 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值