web01 基础加强
1 junit
1.1 什么是junit
java语言的单元测试框架,目的是为了简化代码测试
单元:一个类就是一个单元
框架:代码的半成品,协助开发,提高效率
1.2 使用步骤
1 工程导入junit相关jar包(IDE eclipse 集成了)
2 编写一个普通的方法
要求:public修饰的 无返回值 没有参数列表
3 特殊的标记
@Test
4 运行
1.3 常用注解
注解 | 描述 |
---|---|
@Test | 执行测试方法 |
@Before | 每次测试方法执行之前,运行一次 |
@After | 每次测试方法执行之后,运行一次 |
@BeforeClass | 所有测试方法执行之前,只运行一次【静态方法】 |
@AfterClass | 所有测试方法执行之后,与运行一次【静态方法】 |
代码片段
public class Demo02 { @BeforeClass public static void beforeClass() { System.out.println("beforeClass..."); } @Before public void before() { System.out.println("before..."); } @Test // alt +enter public void sum1() { System.out.println("sun1..."); } @Test public void sum2() { System.out.println("sum2..."); } @After public void after() { System.out.println("after..."); } @AfterClass public static void afterClass() { System.out.println("afterClass..."); } }
注意:
@Test @Before @After 要求: public修饰 没有返回值 没有参数列表
@BeforeClass @AfterClass 要求: public修饰 没有返回值 没有参数列表 静态方法
2 反射
2.1 什么是反射
程序运行期间,使用另外一种技术来创建对象实例,调用方法,后期我们经常使用,但很少去写【框架】
2.2 字节码对象
获取字节码对象
// 对象实例.getClass() // 类.class // Class.forName("全限定类名")
代码片段
// Class.forName("全限定类名"); @Test public void test03() throws Exception { Class c1 = Class.forName("com.itheima.b_reflect.b1_class.Student"); System.out.println(c1); }
常用方法
public String getSimpleName(); 获取字节码简单名称 类名 public String getName(); 获取字节码全限定类名 包名+类名 public T newInstance(); 创建对象实例 要求:空参构造方法
代码片段
@Test public void test03() throws Exception { // 创建Student对象实例 Student o = (Student) clazz.newInstance(); System.out.println(o); }
2.3 构造器 【敲一遍即可】
通过字节码对象获取构造器
public Constructor[] getConstructors(); 获取所有public修饰的构造器 public Constructor [] getDeclaredConstructors(); 获取所有声明的构造器 public Constructor getConstructor(Class.. 类型列表); 获取指定public修饰的构造器 public Constructor getDeclaredConstructor(Class.. 类型列表); 获取指定声明的构造器
常用方法
public T newInstance(Object... 参数列表); 创建对象实例 public void setAccessible(boolean true); 暴力反射
代码片段
// 获取private修饰的二个参数构造器 @Test public void test05() throws Exception { // 字节码对象 Class clazz = Class.forName("com.itheima.b_reflect.b2_constructor.Student"); // 构造器 // 错误 java.lang.NoSuchMethodException // Constructor constructor = clazz.getConstructor(int.class, String.class); Constructor constructor = clazz.getDeclaredConstructor(int.class, String.class); // java.lang.IllegalAccessException constructor.setAccessible(true); // 取消全限检查 (暴力反射) // 创建对象实例 Object o = constructor.newInstance(3, "林允"); System.out.println(o); }
2.4 方法【重点】
通过字节码对象获取方法
public Method[] getMethods(); 获取所有public修饰的方法,包括父类的 public Method[] getDeclaredMethods();获取所有声明的方法,不包括父类 public Method getMethod(String 方法名,Class... 类型列表); 获取指定public修饰的方法 public Method getDeclaredMethod(String 方法名,Class... 类型列表); 获取指定声明的方法
常用方法
public T invoke(Object 对象实例,Object... 参数列表); 调用对象实例方法 public void setAccessible(boolean true); 取消全限检查(暴力反射)
代码片段
// private修饰的 sleep方法 有返回值 @Test public void test05() throws Exception { // 字节码对象 Class clazz = Class.forName("com.itheima.b_reflect.b3_method.Student"); // 方法 // 错误 java.lang.NoSuchMethodException // Method method = clazz.getMethod("sleep"); Method method = clazz.getDeclaredMethod("sleep"); // 创建对象实例 Object o = clazz.newInstance(); // java.lang.IllegalAccessException method.setAccessible(true);// 取消全限检查 (暴力反射) // 调用 Object result = method.invoke(o); System.out.println(result); } // public修饰的 静态 study 方法 @Test public void test06() throws Exception { // 字节码对象 Class clazz = Class.forName("com.itheima.b_reflect.b3_method.Student"); // 方法 Method method = clazz.getMethod("study"); // 调用方法 method.invoke(null); }
2.5 属性【字段】【了解内容】
因为我们定义的属性,都提供了 setXxx getXxx 方法
通过字节码对象获取字段
public Field[] getFields(); 获取所有public修饰的字段 public Field[] getDeclaredFields(); 获取所有声明的字段 public Field getField(String 字段名); 获取指定public修饰字段 public Field getDeclaredField(String 字段名); 获取指定声明的字段
常用方法
public void set(Object 对象实例,Object 实参内容); 对象实例赋值 setInt setDouble public T get(Object 对象实例); 对象实例取值 getInt getDouble public Class getType(); 当前字段的类型 public void setAccessible(boolean true); 暴力反射
代码片段
// 给 name字段 赋值 取值 并判类型 @Test public void test() throws Exception { // 字节码对象 Class clazz = Class.forName("com.itheima.b_reflect.b4_field.Student"); // 获取name字段 // Field field = clazz.getField("name"); Field field = clazz.getDeclaredField("name"); // 创建对象实例 Object o = clazz.newInstance(); // 暴力反射 field.setAccessible(true); // 赋值 field.set(o, "舒淇"); // 取值 field.get(o); System.out.println(o); // 字段类型 Class<?> type = field.getType(); System.out.println(type); }
2.6 反射案例
spring 容器 (创建对象实例、存储对象实例)
需求
编写一个工厂方法,根据配置文件的信息生产任意对象实例
需求分析
编写一个Student类(编号、姓名、性别) 编写一个配置文件(类的信息 全限定类名 属性和对应的值)编写一个工厂方法 加载配置文件 生产对象
步骤分析
1 编写一个Student类(编号、姓名、性别)
2 编写一个配置文件(类的信息 全限定类名 属性和对应的值)
3 编写一个工厂方法 加载配置文件 生产对象
// 读取配置文件信息
// 创建字节码对象
// 创建对象实例
// 根据其他的属性信息 给对象实例赋值
3 注解
3.1 什么是注解
相当于一种标记,是类的组成部分,我们可以给类携带一些额外的信息
给编译器看的(代码检查)
给JVM虚拟机看(代替配置文件)
3.2 jdk常用注解
@Override 声明方法是重写 父类或接口 @Deprecated 声明方法不赞成使用 有二种情况 1. 此方法过时了 2. 此方法可能bug
3.3 自定义注解
注解跟类和接口一样,同一级别
创建类 public class 类名{ } 创建注解 public @interface 注解名{ }
注解属性格式
第一种格式: 属性类型 属性名(); 第二种格式: 属性类型 属性名() default 默认值;
属性类型
基本数据类型 四类八种 String类型、Class类型、注解类型、枚举类型 以上类型一维数组
枚举:认为被阉割的类,存储常量
代码片段
public @interface MyAnnotation02 { int id(); String name(); // Date birthday(); Class clazz(); MyAnnotation01 m01(); Test01 t01(); String[] names(); }
3.4 使用注解
注解在默认情况下可以在类的任意位置使用
拿class举例
@注解名(属性名=属性值,属性名=属性值,属性名=属性值) public class 类名{}
注意:使用注解时
若注解有属性,那么注解属性必须有值
若注解属性为属性类型且仅为单值时,我们可以省略{}
若注解属性名为value且仅为value赋值时,我们可以省略value
推荐大家按照固定格式 :属性名=属性值
代码片段
@Book05(value = "三国演义",price = 99) public class TestBook05 { }
3.5 元注解
修饰注解的注解
@Target(value={ElementType.常量}) 作用:声明注解的使用位置,默认任意位置 ElementType 枚举类 TYPE 接口或类 CONSTRUCTOR 构造器 METHOD 方法 Field 字段 @Retention(value=RetentionPolicy.常量) 作用:声明注解的使用阶段,默认情况下 CLASS RetentionPolicy 枚举类 SOURCE 源码 CLASS 字节码 RUNTIME 运行
代码片段
@Target(value = {ElementType.METHOD,ElementType.TYPE,ElementType.FIELD}) //@Retention(value = RetentionPolicy.CLASS) //@Retention(value = RetentionPolicy.RUNTIME) public @interface Book01 { }
3.6 注解解析
在程序运行期间,获取注解属性的值
可以在类中,或方法或字段等等位置获取注解信息
public boolean isAnnotationPresent(Class 注解类型); 判断注解是否存在 public Annotation getAnnotation(Class 注解类型); 获取指定注解 Annotation 注解的公共接口 ,类比于 Object 所有类的父类
注意:
要求注解声明使用阶段 RUNTIME
代码片段
// 解析方法上的注解属性信息 @Test public void test02() throws Exception { // 获取Book03Demo字节码对象 Class clazz = Class.forName("com.itheima.c_annotation.c5_parse.Book03Demo"); // 获取bookStore的方法 Method method = clazz.getMethod("bookStore"); // 判断是否有Book03 注解 boolean flag = method.isAnnotationPresent(Book03.class); if (flag) { // 如果有获取注解属性的值 Book03 book03 = method.getAnnotation(Book03.class); System.out.println(book03.name()); System.out.println(book03.price()); System.out.println(Arrays.toString(book03.author())); } }
3.7 注解案例
需求
模拟junit单元测试
需求分析
编写一个普通的类,添加三个方法,其中二个方法增加一个标记(@MyTest),执行带有特殊标记的方法底层执行(main)
步骤分析
1 编写 @MyTest 自定义注解
声明使用位置 method
声明使用阶段 RUNTIME
2 编写一个普通的类,添加三个方法,其中二个方法增加一个标记(@MyTest)
3 编写一个程序入口类 main 获取 普通类信息,并执行带有 @MyTest方法
代码实现
public static void main(String[] args) throws Exception { // 获取普通类 字节码对象 Class clazz = Class.forName("com.itheima.c_annotation.c6_democase.MyTestDemo"); // 创建对象实例 Object o = clazz.newInstance(); // 获取所有public修饰的方法 Method[] methods = clazz.getMethods(); for (Method method : methods) { // 判断当前方法是否有@MyTest注解 if (method.isAnnotationPresent(MyTest.class)) { // 如果有执行当前方法 method.invoke(o ); } } }