1. Junit单元测试
- 测试分类:
– 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
– 白盒测试:需要写代码的。关注程序具体的执行流程。
1.1 Junit介绍
Junit是一个Java语言的单元测试框架,属于白盒测试,简单理解为可以用于取代java的main方法。Junit属于第三方工具,需要导入jar包后使用。
1.2 Junit的使用
编写测试类,简单理解Junit可以用于取代java的main方法
在测试类方法上添加注解 @Test
@Test修饰的方法要求:public void 方法名() {…} ,方法名自定义建议test开头,没有参数。
添加Junit库到lib文件夹中,然后进行jar包关联
使用:点击方法左侧绿色箭头,执行当前方法(方法必须标记@Test)。执行结果红色:代表失败;执行结果绿色:代表成功
1.3 常用注解
- @Test,用于修饰需要执行的测试方法
- @Before,修饰的方法会在测试方法之前被自动执行
- @After,修饰的方法会在测试方法执行之后自动被执行
编写代码演示
2. 类加载器
2.1 类加载
- 类加载的描述
– 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
- 类的加载
– 就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象
– 任何类被使用时,系统都会为之建立一个 java.lang.Class 对象
- 类的连接
– 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
– 准备阶段:负责为类的类变量(静态成员变量)分配内存,并设置默认初始化值
– 解析阶段:将类的二进制数据中的符号引用替换为直接引用
- 类的初始化
– 在该阶段,主要就是对类变量进行初始化
- 类的初始化步骤
– 假如类还未被加载和连接,则程序先加载并连接该类
– 假如该类的直接父类还未被初始化,则先初始化其直接父类
– 假如类中有初始化语句,则系统依次执行这些初始化语句
– 注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
- 类的初始化时机
– 创建类的实例
– 调用类的类方法
– 访问类或者接口的类变量,或者为该类变量赋值
– 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
– 初始化某个类的子类
– 直接使用java.exe命令来运行某个主类
2.2 类加载器
2.2.1 类加载器的作用
- 负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象。虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行!
- 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
- Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null ,并且没有父null
- Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类
- System class loader:它也被称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
- 类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap
2.2.2 JVM的类加载机制
2.2.3 Java中的内置类加载器
2.2.4 ClassLoader 中的两个方法
- 方法分类
方法名 | 说明 |
static ClassLoader getSystemClassLoader() | 返回用于委派的系统类加载器 |
ClassLoader getParent() | 返回父类加载器进行委派 |
- 示例代码
public class ClassLoaderDemo { public static void main(String[] args) { //static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器 ClassLoader c = ClassLoader.getSystemClassLoader(); System.out.println(c); //AppClassLoader //ClassLoader getParent():返回父类加载器进行委派 ClassLoader c2 = c.getParent(); System.out.println(c2); //PlatformClassLoader ClassLoader c3 = c2.getParent(); System.out.println(c3); //null }}
3. 反射
3.1 反射的概述
- 是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
- 类名.class属性
- 对象名.getClass()方法
- Class.forName(全类名)方法
3.2 获取Class类对象的三种方式
3.2.1 三种方式分类
3.2.2 示例代码
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { //使用类的class属性来获取该类对应的Class对象 Class<Student> c1 = Student.class; System.out.println(c1); Class<Student> c2 = Student.class; System.out.println(c1 == c2); System.out.println("--------"); //调用对象的getClass()方法,返回该对象所属类对应的Class对象 Student s = new Student(); Class<? extends Student> c3 = s.getClass(); System.out.println(c1 == c3); System.out.println("--------"); //使用Class类中的静态方法forName(String className) Class<?> c4 = Class.forName("com.itheima_02.Student"); System.out.println(c1 == c4); }}
3.3 反射获取构造方法并使用
3.3.1 Class类获取构造方法对象的方法
- 方法分类
方法名 | 说明 |
Constructor<?>[] getConstructors() | 返回所有公共构造方法对象的数组 |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造方法对象的数组 |
Constructor getConstructor(Class<?>... parameterTypes) | 返回单个公共构造方法对象 |
Constructor getDeclaredConstructor(Class<?>... parameterTypes) | 返回单个构造方法对象 |
- 示例代码
public class ReflectDemo01 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //获取Class对象 Class<?> c = Class.forName("com.itheima_02.Student"); //Constructor<?>[] getConstructors() 返回一个包含 Constructor对象的数组, Constructor对象反映了由该 Class对象表示的类的所有公共构造函数// Constructor<?>[] cons = c.getConstructors(); //Constructor<?>[] getDeclaredConstructors() 返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组 Constructor<?>[] cons = c.getDeclaredConstructors(); for(Constructor con : cons) { System.out.println(con); } System.out.println("--------"); //Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数 //Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数 //参数:你要获取的构造方法的参数的个数和数据类型对应的字节码文件对象 Constructor<?> con = c.getConstructor(); //Constructor提供了一个类的单个构造函数的信息和访问权限 //T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例 Object obj = con.newInstance(); System.out.println(obj);// Student s = new Student();// System.out.println(s); }}
3.3.2 Constructor类用于创建对象的方法
方法名 | 说明 |
T newInstance(Object...initargs) | 根据指定的构造方法创建对象 |
3.4 反射获取构造方法并使用练习1
- 案例需求
– 通过反射获取公共的构造方法并创建对象
- 代码实现
– 学生类
public class Student { //成员变量:一个私有,一个默认,一个公共 private String name; int age; public String address; //构造方法:一个私有,一个默认,两个公共 public Student() { } private Student(String name) { this.name = name; } Student(String name, int age) { this.name = name; this.age = age; } public Student(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } //成员方法:一个私有,四个公共 private void function() { System.out.println("function"); } public void method1() { System.out.println("method"); } public void method2(String s) { System.out.println("method:" + s); } public String method3(String s, int i) { return s + "," + i; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", address='" + address + '\'' + '}'; }}
– 测试类
public class ReflectDemo02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //获取Class对象 Class<?> c = Class.forName("com.itheima_02.Student"); //public Student(String name, int age, String address) //Constructor<T> getConstructor(Class<?>... parameterTypes) Constructor<?> con = c.getConstructor(String.class, int.class, String.class); //基本数据类型也可以通过.class得到对应的Class类型 //T newInstance(Object... initargs) Object obj = con.newInstance("林青霞", 30, "西安"); System.out.println(obj); }}
3.5 反射获取构造方法并使用练习2
- 案例需求
– 通过反射获取私有构造方法并创建对象
- 代码实现
– 学生类:参见上方学生类
– 测试类
public class ReflectDemo03 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //获取Class对象 Class<?> c = Class.forName("com.itheima_02.Student"); //private Student(String name) //Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) Constructor<?> con = c.getDeclaredConstructor(String.class); //暴力反射 //public void setAccessible(boolean flag):值为true,取消访问检查 con.setAccessible(true); Object obj = con.newInstance("林青霞"); System.out.println(obj); }}
3.6 反射获取成员变量并使用
3.6.1 Class类获取成员变量对象的方法
- 方法分类
方法名 | 说明 |
Field[] getFields() | 返回所有公共成员变量对象的数组 |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组 |
Field getField(String name) | 返回单个公共成员变量对象 |
Field getDeclaredField(String name) | 返回单个成员变量对象 |
- 示例代码
public class ReflectDemo01 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //获取Class对象 Class<?> c = Class.forName("com.itheima_02.Student"); //Field[] getFields() 返回一个包含 Field对象的数组, Field对象反映由该 Class对象表示的类或接口的所有可访问的公共字段 //Field[] getDeclaredFields() 返回一个 Field对象的数组,反映了由该 Class对象表示的类或接口声明的所有字段// Field[] fields = c.getFields(); Field[] fields = c.getDeclaredFields(); for(Field field : fields) { System.out.println(field); } System.out.println("--------"); //Field getField(String name) 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定公共成员字段 //Field getDeclaredField(String name) 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定声明字段 Field addressField = c.getField("address"); //获取无参构造方法创建对象 Constructor<?> con = c.getConstructor(); Object obj = con.newInstance();// obj.addressField = "西安"; //Field提供有关类或接口的单个字段的信息和动态访问 //void set(Object obj, Object value) 将指定的对象参数中由此 Field对象表示的字段设置为指定的新值 addressField.set(obj,"西安"); //给obj的成员变量addressField赋值为西安 System.out.println(obj);// Student s = new Student();// s.address = "西安";// System.out.println(s); }}
3.6.2 Field类用于给成员变量赋值的方法
方法名 | 说明 |
voidset(Object obj,Object value) | 给obj对象的成员变量赋值为value |
3.7 反射获取成员变量并使用练习
- 案例需求
– 通过反射获取成员变量并赋值
- 代码实现
– 学生类:参见上方学生类
– 测试类
public class ReflectDemo02 { public static void main(String[] args) throws Exception { //获取Class对象 Class<?> c = Class.forName("com.itheima_02.Student"); //Student s = new Student(); Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); System.out.println(obj); //s.name = "林青霞";// Field nameField = c.getField("name"); //NoSuchFieldException: name Field nameField = c.getDeclaredField("name"); nameField.setAccessible(true); nameField.set(obj, "林青霞"); System.out.println(obj); //s.age = 30; Field ageField = c.getDeclaredField("age"); ageField.setAccessible(true); ageField.set(obj,30); System.out.println(obj); //s.address = "西安"; Field addressField = c.getDeclaredField("address"); addressField.setAccessible(true); addressField.set(obj,"西安"); System.out.println(obj); }}
3.8 反射获取成员方法并使用
3.8.1 Class类获取成员方法对象的方法
- 方法分类
方法名 | 说明 |
Method[] getMethods() | 返回所有公共成员方法对象的数组,包括继承的 |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,不包括继承的 |
Method getMethod(String name, Class<?>... parameterTypes) | 返回单个公共成员方法对象 |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象 |
- 示例代码
public class ReflectDemo01 { public static void main(String[] args) throws Exception { //获取Class对象 Class<?> c = Class.forName("com.itheima_02.Student"); //Method[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映由该 Class对象表示的类或接口的所有公共方法,包括由类或接口声明的对象以及从超类和超级接口继承的类 //Method[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映由 Class对象表示的类或接口的所有声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承方法// Method[] methods = c.getMethods(); Method[] methods = c.getDeclaredMethods(); for(Method method : methods) { System.out.println(method); } System.out.println("--------"); //Method getMethod(String name, Class<?>... parameterTypes) 返回一个 方法对象,该对象反映由该 Class对象表示的类或接口的指定公共成员方法 //Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 Class对象 //public void method1() Method m = c.getMethod("method1"); //获取无参构造方法创建对象 Constructor<?> con = c.getConstructor(); Object obj = con.newInstance();// obj.m(); //在类或接口上提供有关单一方法的信息和访问权限 //Object invoke(Object obj, Object... args) 在具有指定参数的指定对象上调用此 方法对象表示的基础方法 //Object:返回值类型 //obj:调用方法的对象 //args:方法需要的参数 m.invoke(obj);// Student s = new Student();// s.method1(); }}
3.8.2 Method类用于执行方法的方法
方法名 | 说明 |
Objectinvoke(Object obj,Object... args) | 调用obj对象的成员方法,参数是args,返回值是Object类型 |
3.9 反射获取成员方法并使用练习
- 案例需求
– 通过反射获取成员方法并调用
- 代码实现
– 学生类:参见上方学生类
– 测试类
public class ReflectDemo02 { public static void main(String[] args) throws Exception { //获取Class对象 Class<?> c = Class.forName("com.itheima_02.Student"); //Student s = new Student(); Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); //s.method1(); Method m1 = c.getMethod("method1"); m1.invoke(obj); //s.method2("林青霞"); Method m2 = c.getMethod("method2", String.class); m2.invoke(obj,"林青霞");// String ss = s.method3("林青霞",30);// System.out.println(ss); Method m3 = c.getMethod("method3", String.class, int.class); Object o = m3.invoke(obj, "林青霞", 30); System.out.println(o); //s.function();// Method m4 = c.getMethod("function"); //NoSuchMethodException: com.itheima_02.Student.function() Method m4 = c.getDeclaredMethod("function"); m4.setAccessible(true); m4.invoke(obj); }}
3.10 反射的案例
3.10.1反射练习之越过泛型检查
- 案例需求
– 通过反射技术,向一个泛型为Integer的集合中添加一些字符串数据
- 代码实现
public class ReflectTest01 { public static void main(String[] args) throws Exception { //创建集合 ArrayList<Integer> array = new ArrayList<Integer>();// array.add(10);// array.add(20);// array.add("hello"); Class<? extends ArrayList> c = array.getClass(); Method m = c.getMethod("add", Object.class); m.invoke(array,"hello"); m.invoke(array,"world"); m.invoke(array,"java"); System.out.println(array); }}
3.10.2 运行配置文件中指定类的指定方法
- 案例需求
– 通过反射运行配置文件中指定类的指定方法
- 代码实现
public class ReflectTest02 { public static void main(String[] args) throws Exception { //加载数据 Properties prop = new Properties(); FileReader fr = new FileReader("myReflect\\class.txt"); prop.load(fr); fr.close(); /* className=com.itheima_06.Student methodName=study */ String className = prop.getProperty("className"); String methodName = prop.getProperty("methodName"); //通过反射来使用 Class<?> c = Class.forName(className);//com.itheima_06.Student Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); Method m = c.getMethod(methodName);//study m.invoke(obj); }}
4. 注解
4.1 注解概述
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
作用分类:
- 编写文档:通过代码里标识的注解生成文档【例如,生成文档doc文档】
- 代码分析:通过代码里标识的注解对代码进行分析【例如,注解的反射】 (重要的,因为在框架中通常会采用注解的方式来代替配置文件,再通过反射获取注解中配置的信息)
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【例如,Override】
JDK中常用的3个注解
1.@Override
它是用来描述当前方法是一个重写的方法.
作用:在编译阶段对方法进行检查
注意事项:
jdk1.5:它只能描述继承中的重写
jdk1.6:它可以描述接口实现的重写,也能描述类的继承的重写
2.@Deprecated 过时的注解
它是用于描述当前方法是一个过时的方法.
问题:为什么描述这个方法过时了?
在旧的版本中的方法在新的版本中已经有了更好的实现,
旧的版本中的方法存在安全隐患,不建议使用时,这样的方法就可以标注成过时方法。
3.@SuppressWarnings
对程序中的警告去除。
unused变量未使用
deprecation 使用了不赞成使用的类或方法时的警告
unchecked 执行了未检查的转换时的警告,例如当使用集合时没有用泛型(Generics) 来指定集合保存的类型。
fallthrough 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告。
path 在类路径、源文件路径等中有不存在的路径时的警告。
serial 当在可序列化的类上缺少 serialVersionUID 定义时的警告。
finally 任何 finally 子句不能正常完成时的警告。
all 关于以上所有情况的警告
4.2 自定义注解
定义格式
元注解public @interface 注解名称{ 属性列表;}
- 注解本质上就是一个接口,该接口默认继承Annotation接口
public interface MyAnno extends java.lang.annotation.Annotation {}
注解的属性
属性的作用
– 可以让用户在使用注解时传递参数,让注解的功能更加强大。
属性的格式
– 格式1:数据类型 属性名();
– 格式2:数据类型 属性名() default 默认值;
属性定义示例
public @interface Student { String name(); // 姓名 int age() default 18; // 年龄 String gender() default "男"; // 性别} // 该注解就有了三个属性:name,age,gender
属性适用的数据类型
– 八种基本数据类型(int,float,boolean,byte,double,char,long,short)
– String类型,Class类型,枚举类型,注解类型
– 以上所有类型的一维数组
4.3 使用自定义注解
在程序中使用(解析)注解的步骤(获取注解中定义的属性值):
获取注解定义的位置的对象 (Class,Method,Field)
获取指定的注解 getAnnotation(Class)
调用注解中的抽象方法获取配置的属性值
需要代码演示
定义注解
定义一个注解:Book
– 包含属性:String value() 书名
– 包含属性:double price() 价格,默认值为 100
– 包含属性:String[] authors() 多位作者
代码实现
public @interface Book { // 书名 String value(); // 价格 double price() default 100; // 多位作者 String[] authors();}
4.4 使用注解
定义类在成员方法上使用Book注解
/** * @author itheima * @version 1.0 */public class BookShelf { @Book(value = "西游记",price = 998,authors = {"吴承恩","白求恩"}) public void showBook(){ }}
使用注意事项
– 如果属性有默认值,则使用注解的时候,这个属性可以不用赋值。
– 如果属性没有默认值,那么在使用注解时一定要给属性赋值。
4.5 特殊属性value
当注解中只有一个属性且名称是value,在使用注解时给value属性赋值可以直接给属性值,无论value是单值元素还是数组类型。
// 定义注解Bookpublic @interface Book { // 书名 String value();}// 使用注解Bookpublic class BookShelf { @Book("西游记") public void showBook(){ }}或public class BookShelf { @Book(value="西游记") public void showBook(){ }}
如果注解中除了value属性还有其他属性,且至少有一个属性没有默认值,则在使用注解给属性赋值时,value属性名不能省略。
// 定义注解Bookpublic @interface Book { // 书名 String value(); // 价格 double price() default 100; // 多位作者 String[] authors();}// 使用Book注解:正确方式@Book(value="红楼梦",authors = "曹雪芹")public class BookShelf { // 使用Book注解:正确方式 @Book(value="西游记",authors = {"吴承恩","白求恩"}) public void showBook(){ }}// 使用Book注解:错误方式public class BookShelf { @Book("西游记",authors = {"吴承恩","白求恩"}) public void showBook(){ }}// 此时value属性名不能省略了。
4.6 注解之元注解
默认情况下,注解可以用在任何地方,比如类,成员方法,构造方法,成员变量等地方。如果要限制注解的使用位置怎么办?那就要学习一个新的知识点:元注解。
- @Target
- @Retention
- 作用:指明此注解用在哪个位置,如果不写默认是任何地方都可以使用。
4.6.1 元注解之@Target
– 可选的参数值在枚举类ElemenetType中包括:
TYPE: 用在类,接口上 FIELD:用在成员变量上 METHOD: 用在方法上 PARAMETER:用在参数上 CONSTRUCTOR:用在构造方法上 LOCAL_VARIABLE:用在局部变量上
4.6.2 元注解之@Retention
- 作用:定义该注解的生命周期(有效范围)。
– 可选的参数值在枚举类型RetentionPolicy中包括
SOURCE:注解只存在于Java源代码中,编译生成的字节码文件中就不存在了。CLASS:注解存在于Java源代码、编译以后的字节码文件中,运行的时候内存中没有,默认值。RUNTIME:注解存在于Java源代码中、编译以后的字节码文件中、运行时内存中,程序可以通过反射获取该注解。
4.6.3 元注解使用示例
@Target({ElementType.METHOD,ElementType.TYPE})@interface Stu{ String name();}// 类@Stu(name="jack")public class AnnotationDemo02 { // 成员变量 @Stu(name = "lily") // 编译失败 private String gender; // 成员方法 @Stu(name="rose") public void test(){ } // 构造方法 @Stu(name="lucy") // 编译失败 public AnnotationDemo02(){}}
![](https://i-blog.csdnimg.cn/blog_migrate/eb3c0bac12b6088d148568ee4dc63ee9.png)
4.7 注解解析
4.7.1 什么是注解解析
通过Java技术获取注解数据的过程则称为注解解析。
4.7.2 与注解解析相关的接口
- Anontation:所有注解类型的公共接口,类似所有类的父类是Object。
- AnnotatedElement:定义了与注解解析相关的方法,常用方法以下四个:
boolean isAnnotationPresent(Class annotationClass); 判断当前对象是否有指定的注解,有则返回true,否则返回false。T getAnnotation(Class<T> annotationClass); 获得当前对象上指定的注解对象。Annotation[] getAnnotations(); 获得当前对象及其从父类上继承的所有的注解对象。Annotation[] getDeclaredAnnotations();获得当前对象上所有的注解对象,不包括父类的。
4.7.3 获取注解数据的原理
注解作用在那个成员上,就通过反射获得该成员的对象来得到它的注解。
- 如注解作用在方法上,就通过方法(Method)对象得到它的注解
// 得到方法对象 Method method = clazz.getDeclaredMethod("方法名"); // 根据注解名得到方法上的注解对象 Book book = method.getAnnotation(Book.class);
- 如注解作用在类上,就通过Class对象得到它的注解
// 获得Class对象Class c = 类名.class;// 根据注解的Class获得使用在类上的注解对象Book book = c.getAnnotation(Book.class);
4.8 使用反射获取注解的数据
4.8.1 需求说明
定义注解Book,要求如下:
– 包含属性:String value() 书名
– 包含属性:double price() 价格,默认值为 100
– 包含属性:String[] authors() 多位作者
– 限制注解使用的位置:类和成员方法上
– 指定注解的有效范围:RUNTIME
定义BookStore类,在类和成员方法上使用Book注解
定义TestAnnotation测试类获取Book注解上的数据
4.8.2 代码实现
注解Book
@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface Book { // 书名 String value(); // 价格 double price() default 100; // 作者 String[] authors();}
BookStore类
@Book(value = "红楼梦",authors = "曹雪芹",price = 998)public class BookStore { @Book(value = "西游记",authors = "吴承恩") public void buyBook(){ }}
TestAnnotation类
public class TestAnnotation { public static void main(String[] args) throws Exception{ System.out.println("---------获取类上注解的数据----------"); test01(); System.out.println("---------获取成员方法上注解的数据----------"); test02(); } /** * 获取BookStore类上使用的Book注解数据 */ public static void test01(){ // 获得BookStore类对应的Class对象 Class c = BookStore.class; // 判断BookStore类是否使用了Book注解 if(c.isAnnotationPresent(Book.class)) { // 根据注解Class对象获取注解对象 Book book = (Book) c.getAnnotation(Book.class); // 输出book注解属性值 System.out.println("书名:" + book.value()); System.out.println("价格:" + book.price()); System.out.println("作者:" + Arrays.toString(book.authors())); } } /** * 获取BookStore类成员方法buyBook使用的Book注解数据 */ public static void test02() throws Exception{ // 获得BookStore类对应的Class对象 Class c = BookStore.class; // 获得成员方法buyBook对应的Method对象 Method m = c.getMethod("buyBook"); // 判断成员方法buyBook上是否使用了Book注解 if(m.isAnnotationPresent(Book.class)) { // 根据注解Class对象获取注解对象 Book book = (Book) m.getAnnotation(Book.class); // 输出book注解属性值 System.out.println("书名:" + book.value()); System.out.println("价格:" + book.price()); System.out.println("作者:" + Arrays.toString(book.authors())); } }}
- 输出结果:
5. xml
5.1 xml概述
5.1.1 什么是XML
- XML :可扩展标记语言(EXtensible Markup Language)
XML 它是一种标记语言,很类似 HTML,HTML文件也是XML文档,标签都是自定义的。 如:<user></user> 或 <student></student>
- W3C在1988年2月发布1.0版本,2004年2月又发布1.1版本,单因为1.1版本不能向下兼容1.0版本,所以1.1没有人用。同时,在2004年2月W3C又发布了1.0版本的第三版。我们要学习的还是1.0版本。
- xml标签都是自定义的,html标签是预定义。
- xml的语法严格,html语法松散。
- xml是存储数据的,html是展示数据。
- 存放数据
5.1.2 XML 与 HTML 的主要差异
5.1.3 xml的作用
<?xml version="1.0" encoding="UTF-8"?><persons> <person id="p001"> <name>张三</name> </person> <person id="p002"> <name>李四</name> </person></persons>
类似于java代码
class Person{ String id; String name;}public void test(){ HashSet<Person> persons = new HashSet<Person>(); persons.add( new Person("p001","张三") ); persons.add( new Person("p002","李四") );}
- 配置文件
<?xml version="1.0" encoding="UTF-8"?><beans> <bean className="com.itheima_00_Bean.User"> <property name="username" value="jack"></property> </bean></beans>
类似于java代码
class Bean{ private String username; private String pws; //补全set\get方法}
import com.itheima_00_Bean.User;public static void main(){ Class clzzz = Class.forName("com.itheima_00_Bean.User"); Object obj = clazz.newInstance(); Method method = clazz.getMethod("setUsername",String.class); method.invoke(obj,"jack");}
5.2 xml的组成元素
5.2.1 文档声明
- XML文档声明格式:
<?xml version="1.0" encoding="UTF-8"?>
文档声明必须为结束;
文档声明必须从文档的0行0列位置开始;
文档声明只有2个属性:
1. versioin:指定XML文档版本。必须属性,因为我们不会选择1.1,只会选择1.0;2. encoding:指定当前文档的编码。可选属性,默认值是utf-8;
5.2.2 元素
- 元素 element
<bean></bean>
元素是XML文档中最重要的组成部分,
普通元素的结构开始标签、元素体、结束标签组成。例如:大家好
元素体:元素体可以是元素,也可以是文本,例如:你好
空元素:空元素只有开始标签,而没有结束标签,但元素必须自己闭合,例如:
元素命名:
1. 区分大小写2. 不能使用空格,不能使用冒号:3. 不建议以XML、xml、Xml开头
格式化良好的XML文档,必须只有一个根元素。
5.2.3 属性
- 属性 attribute
<beanid="" className="">
属性是元素的一部分,它必须出现在元素的开始标签中
属性的定义格式:属性名=属性值,其中属性值必须使用单引或双引
一个元素可以有0~N个属性,但一个元素中不能出现同名属性
属性名不能使用空格、冒号等特殊字符,且必须以字母开头
5.2.4 注释
XML的注释,以“”结束。注释内容会被XML解析器忽略!
5.2.5 转义字符
因为很多符号已经被XML文档结构所使用,所以在元素体或属性值中想使用这些符号就必须使用转义字符,例如:“<”、“>”、“’”、“””、“&”。
5.3 xml约束
在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束。 常见的xml约束:DTD、Schema
5.3.1 DTA约束
什么是DTD?
DTD(Document Type Definition),文档类型定义,用来约束XML文档。规定XML文档中元素的名称,子元素的名称及顺序,元素的属性等。
DTD重点要求
开发中,我们很少自己编写DTD约束文档,通常情况我们都是通过框架提供的DTD约束文档,编写对应的XML文档。常见框架使用DTD约束有:struts2、hibernate等。
通过提供的DTD“bean.dtd”编写XML
<?xml version="1.0" encoding="UTF-8"?><!--传智播客DTD教学实例文档。模拟spring规范,如果开发人员需要在xml使用当前DTD约束,必须包括DOCTYPE。格式如下:<!DOCTYPE beans SYSTEM "bean.dtd">--><!ELEMENT beans (bean*,import*) ><!ELEMENT bean (property*)><!ELEMENT property (#PCDATA)><!ELEMENT import (#PCDATA)><!ATTLIST bean id CDATA #REQUIRED className CDATA #REQUIRED><!ATTLIST property name CDATA #REQUIRED value CDATA #REQUIRED><!ATTLIST import resource CDATA #REQUIRED>
案例实现
完成xml内容编写
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans SYSTEM "bean.dtd"><beans> <bean id="" className=""></bean> <bean id="" className=""> <property name="" value=""></property> <property name="" value=""></property> </bean> <import resource=""></import> <import resource=""></import></beans>
5.3.2 Schema约束
什么是Schema?
Schema是新的XML文档约束; Schema要比DTD强大很多,是DTD 替代者; Schema本身也是XML文档,但Schema文档的扩展名为xsd,而不是xml。 Schema 功能更强大,数据类型更完善 Schema 支持名称空间
Schema重点要求
与DTD一样,要求可以通过schema约束文档编写xml文档。常见框架使用schema的有:Spring等
通过提供“bean-schema.xsd”编写xml文档
<?xml version="1.0" encoding="UTF-8"?><!-- 传智播客Schema教学实例文档。模拟spring规范,如果开发人员需要在xml使用当前Schema约束,必须包括指定命名空间。格式如下:<beans xmlns="http://www.itcast.cn/bean" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.itcast.cn/bean bean-schema.xsd">--><schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.itcast.cn/bean" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.itcast.cn/bean" elementFormDefault="qualified"> <!-- 声明根标签 --> <element name="beans"> <complexType> <choice minOccurs="0" maxOccurs="unbounded"> <element name="bean"> <complexType> <sequence minOccurs="0" maxOccurs="unbounded"> <element name="property"> <complexType> <attribute name="name" use="required"></attribute> <attribute name="value" use="required"></attribute> </complexType> </element> </sequence> <attribute name="id" use="required"></attribute> <attribute name="className" use="required"></attribute> </complexType> </element> <element name="import"> <complexType> <attribute name="resource" use="required"></attribute> </complexType> </element> </choice> </complexType> </element></schema>
- l 案例文档中同一个“命名空间”分别使用“默认命名空间”和“显示命名空间”进行引入,所以文档中和xsd:schema作用一样。
![](https://i-blog.csdnimg.cn/blog_migrate/7b690eeb53311e304d6ed9faafc19f91.png)
案例实现
完成xml内容编写
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.itcast.cn/bean" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.itcast.cn/bean bean-schema.xsd"> <bean id="" className=""></bean> <bean id="" className=""> <property name="" value=""/> <property name="" value=""/> </bean> <import resource=""/> <import resource=""/></beans>
5.4 命名空间(语法)
什么是命名空间
如果一个XML文档中使用多个Schema文件,而这些Schema文件中定义了相同名称的元素时就会出现名字冲突。这就像一个Java文件中使用了import java.util.和import java.sql.时,在使用Date类时,那么就不明确Date是哪个包下的Date了。
总之名称空间就是用来处理元素和属性的名称冲突问题,与Java中的包是同一用途。如果每个元素和属性都有自己的名称空间,那么就不会出现名字冲突问题,就像是每个类都有自己所在的包一样,那么类名就不会出现冲突。
约束文档和xml关系
当W3C提出Schema约束规范时,就提供“官方约束文档”。我们通过官方文档,必须“自定义schema 约束文档”,开发中“自定义文档”由框架编写者提供。我们提供“自定义文档”限定,编写出自己的xml文档。
命名空间
默认命名空间:<xxx xmlns=””>,使用<标签>显式命名空间:<xxx xmlns:别名=””>,使用<别名:标签>
![](https://i-blog.csdnimg.cn/blog_migrate/3e8b14d381af5fb2744609d42914d334.png)