反射_注解
Junit
简介
-
概述:
- Junit 它属于白盒测试的内容, 简单理解就是用来替代main方法的, 即: 代码不用往main()方法中放, 也能实现直接运。
它使用Java语言编写的第三方的框架, 用之前需要先导包。
- Junit 它属于白盒测试的内容, 简单理解就是用来替代main方法的, 即: 代码不用往main()方法中放, 也能实现直接运。
-
具体步骤:
- 在项目(模块)下新建一个文件夹, 名字叫: lib
- 把要使用的jar包拷贝进去.
- 配置运行环境.
选中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项目的开发流程:
- 需求调研.
- 编写需求(调研)文档. //产品经理.
- 编写静态页面. //UI工程师.
- 技术选型, 框架选定, 统一工具.
- 数据库建模, 数据表设计. //架构师, 项目经理, 你
- 软件开发. //程序员.
- 测试, 部署, 运维. //测试人员, 运维人员.
类加载器
简介
- 概述:它指的是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:通过 类的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);
}
}
- 需求:
- 已知在项目(模块)下有一个config.properties,文件中记录的有,类名 和 方法名 两组属性
- 请用所学,根据配置文件中的类名和方法名,调用指定类的指定方法
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 默认值;
......
}
-
细节:
-
注解的成员变量的数据类型必须是: 所有的基本类型, String类型, Class类型, 枚举类型, 注解类型
-
以上所有类型的数组形式.
-
上述部分的格式中, public 和 default 默认值这部分是可以省略的.
-
使用注解的时候, 如果注解中的属性有默认值, 则在使用注解时, 可以不同给该属性赋值.
-
使用注解的时候, 如果注解只有一个属性, 且属性名叫value, 不管该属性是单值类型还是数组类型,
在使用注解时, 都可以省略 属性名. -
如果某一个注解 想被注解解析, 则在该注解上必须定义: 元注解.
-
案例:注解入门
//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,可扩展的标记语言.
- 它属于W3C(万维网联盟)公司的产品,主要是用来当做配置文件用的,即:存储文件.
-
名词解释:
- 可扩展: 它的标签可以自定义.
- 标记语言: 即:它是由标签组成的.
-
面试题:XML和HTML的区别是什么?
- 关于标签.
HTMl的标签是预定义的, 即: 已经定义好了, 各个标签的作用.
XML的标签是自定义的, 你可以自己写. - 关于语法.
HTML语法松散, XML语法严谨. - 关于作用.
HTML主要用于展示数据, XML主要用于存储数据.
XML文件的组成:
-
0行0列的位置是文档声明,指定:版本和编码表.
-
有且只能有一组根标签.
-
根标签可以有多个子标签.
-
标签可以有属性,格式为:属性名=“属性值”,单双引号均可.
-
注释写法
-
常用的转义符:
-
< < //小于号 > > //大于号
-
<?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文件进行精细化限定.
- 例如: 可以限定某一个标签的值必须为谁, 支持自定义类型, 我们也是通过自定义类型来限定标签的.
- DTD:
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中的导包, 用来区分 不同约束文件中, 相同名字的约束信息的
#