1.junit&反射&注解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40406929/article/details/81984127

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 );
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40406929/article/details/81984127
今日推荐