15 Junit 、反射、注解

15 Junit 、反射、注解

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

1、Junit单元测试

junit概述

Junit是一个java语言的的一个单元测试框架,简单理解为用于java的main方法。Junit属于第三方工具,一般情况下需要导入jar包。不过,多数java开发环境已经集成了junit作为单元测试工具。

单元测试方法有两个要求:

(1)方法名以test开头(软性规定)
(2)Public修饰,没有参数没有返回值(硬性规定)

单元测试方法的运行方式:

(1)选中方法,右键run
(2)选中类,右键run,会运行该类中所有的单元测试方法

public class Demo01JUnit {
    @Test
    public void testFunction() {
        System.out.println("单元测试方法testFunction执行了");
    }
    @Test
    public void testMethod() {
        System.out.println("单元测试方法testMethod执行了");
    }
}

常用注解

@Before:在每个测试方法之前都会运行一次
@After:在每个测试方法运行以后运行的方法
@BeforeClass:在所有的运行方法运行之前,只运行一次,而且必须用在静态方法上面。
@After Class:所有的测试方法运行以后,只运行一次,必须用在静态方法上面。
BeforeClass和AfterClass这两个注解修饰的方法需要使用static修饰。

public class Demo02JUnit {
    @AfterClass
    public static void afterClassMethod() {
        System.out.println("afterClassMethod");
    }

    @BeforeClass
    public static void beforeClassMethod() {
        System.out.println("beforeClassMethod");
    }



    @After
    public void afterMethod() {
        System.out.println("afterMethod");
    }

    @Before
    public void beforeMethod() {
        System.out.println("beforeMethod");
    }

    @Test
    public void testMethod() {
        System.out.println("testMethod...");
    }

    @Test
    public void testFunction() {
        System.out.println("testFunction...");
    }
}运行结果:
beforeClassMethod
beforeMethod
testMethod
afterMethod
afterClassMethod

类加载

  • 类的初始化加载步骤
    (1)假如类还未被加载和连接,则程序先加载并且连接该类
    (2)假如类的直接父类还未被初始化,则先初始化其直接父类
    (3)假如类中有初始化语句,则系统依次执行这些初始化语句
    注意:在执行第二个步骤的时候,系统对其直接父类的初始化步骤也遵循初始化步骤1—3
  • 类加载器的作用:
    (1)负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
    (2)虽然我们不用过分关心了类加载机制,但是了解这个机制就能更好的理解程序的运行
  • 类加载器
    ClassLoader:是负责加载类的对象
    Java运行时具有以下内置类加载器
    Bootstrap class loader:
    Platform class loader:
    System class loader:
  • 类加载器的继承关系:System的父类加载器为Platform,而Platform的父类加载器为Bootstrap
    ClassLoader中的两个方法
    (1)static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
    (2)ClassLoader getParent():返回父类加载器进行委派

2、反射

反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的方法,属性,构造方法等成员。
Java的反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息;来创建对象,调用方法的一种机制。由于这种动态性,可以极大增强程序的灵活性,程序不用在编译器就可以完成确定,在运行期仍然可以扩展。

获取Class类的对象(三种方式)

(1)使用类的class属性来获取该类的Class对象。例如:Student.class将会返回Class对象。
(2)调用对象的getClass()方法,返回对象所属类对应的Class对象。
该方法是Object类中的方法,所有的java对象都可以调用该方法
(3)使用Class类中的静态方法forName(String name),该方法需要传入字符串参数,该字符窜的参数的值是某个类的全路径,也是该类的全类名(包含包名)。
注意:一个类的Class对象只有一个,不管获取多少次,获取的都是同一个Class对象。

Class中的常见方法:

String getName():获取类的全名(包含包的类名)
String getSimpleName():获取类的简单类名(不包含包的类名)
T newInstance():(记住)创建该类的对象(使用的空参构造的创建对象)

public class Demo02ClassMethod {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        //获取Class对象
        Class clazz = Class.forName("cn.itcast.demo02_reflect.Person");
        //String getName():获取类的全类名(包含包的类名)
        System.out.println(clazz.getName());//cn.itcast.demo02_reflect.Person
        //String getSimpleName(): 获取类的简单类名(不包含包的类名)
        System.out.println(clazz.getSimpleName());//Person
        //(记住)T newInstance():创建该类的对象(使用的空参构造创建对象)
        //因为Class对象是Person类的Class,所以调用newInstance,创建的是Person类的对象
        Object obj  = clazz.newInstance();
        System.out.println(obj);
    }
}

反射获取构造方法

  • 在Class中,有一些方法,可以获取到类中的构造方法:
    Construction[] getConstruction():获取到类中的所有构造方法:
    Construction getConstruction(Class… parameterTypes):获取到指定的构造方法。参数表示要获取的构造方法的参数列表。
    上面的方法只能获取到public权限的构造方法其他的权限一律获取不到。
  • Construction表示的构造方法里面有一些功能:
    T newInstance(Object… initargs):使用该构造方法对象,参数为执行构造方法时的实际参数。返回值为创建出来的对象。
public static void method2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Class对象。
        Class clazz = Class.forName("cn.itcast.demo02_reflect.Person");
        //Constructor getConstructor(Class... parameterTypes):获取到指定的构造方法。 参数表示要获取的构造方法的参数列表。
        //因为要获取的是空参数的构造方法,所以getConstructor小括号中不传递任何数据。
        Constructor c = clazz.getConstructor();
        // T newInstance(Object... initargs): 使用该构造方法对象
        //c表示的是空参构造,使用空参构造创建对象不需要传递任何实际参数。
        Object obj = c.newInstance();
        //输出
        System.out.println(obj);
    }
  • 反射获取有参构造方法并使用
    Constructor表示构造方法,里面有一些功能:
    T newInstance(Object… initargs): 使用该构造方法对象, 参数为执行构造方法时的实际参数。返回值为创建出来的对象。
    注意:所有的数据类型(包括基本类型)都有class属性,可以获取到对应的Class对象。
 public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    Class c = Class.forName("Class_Demo.Demo02.Person");
    Constructor constructor = c.getConstructor(String.class, int.class);
    Object obj = constructor.newInstance("狗蛋", 20);
    System.out.println(obj);
}

暴力反射

暴力反射(可以获取到类中的任何权限(包括私有)的成员并使用)
暴力反射不推荐,因为会破坏到类的封装性。
在Class中,有一些方法可以获取到类中的所有权限的构造方法:
Construction[] getDeclaredConstructors():获取类中的所有构造方法。
Construction getDeclaredConstructor(Class… parameterTypes):获取类中指定的构造方法。
虽然这个方法可以获取到私有的成员,但是在java中有一个权限安全机制,对私有的
成员,是不能在外边直接使用的。
解决方案:可以取消这个权限检查机制。
Constructor:获取构造方法
Method:成员方法
Field:成员变量
这些类都有一个共同的父类AccessibleObject,在这个父类中定义了一个方法,可以取消权限检查机制。void setAccessible(boolean flag):取消权限检查机制,参数如果为true表示取消。

public class Demo05ReflectConstructor {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        method2();
    }

    /*
        获取Person类中私有的构造方法,并使用该构造方法创建对象。
        private Person(String name)
     */
    public static void method2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Person类的Class
        Class clazz = Class.forName("cn.itcast.demo02_reflect.Person");
        //获取私有的构造方法。
        Constructor c = clazz.getDeclaredConstructor(String.class);
        //取消权限检查机制
        c.setAccessible(true);
        //使用这个私有的构造方法创建对象
        Object obj = c.newInstance("旺财");
        System.out.println(obj);
    }

    /*
        获取类中所有的构造方法(包括私有)
     */
    public static void method() throws ClassNotFoundException {
        //获取Person类的Class
        Class clazz = Class.forName("cn.itcast.demo02_reflect.Person");
        //获取所有的构造方法
        Constructor[] cs = clazz.getDeclaredConstructors();
        //遍历
        for (Constructor c : cs) {
            System.out.println(c);
        }
    }
}

反射获取成员方法并使用

Class中有一些方法,可以获取到这个类中的成员方法:
Method[] getMethods():获取类中所有的成员方法。
Method getMethod(String name, Class… parameterTypes):获取类中指定的成员方法。参数名name表示方法名。参数parameterTypes表示获取的方法的参数列表。
注意:上面两个方法只能获取类中的public权限的成员方法,其他权限的一律获取不到。
Method表示成员方法,里面有一些功能:
Object invoke(Object obj, Object… args):让该方法执行。
参数obj:表示通过哪个对象调用了该方法。如果调用静态方法,该方法可以传递null
参数args:是可变参数,表示执行成员方法时传递的实际参数。
返回值Object:表示方法调用后的结果(方法调用的返回值)。

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        //获取Person类的Class
        Class clazz = Class.forName("cn.itcast.demo02_reflect.Person");
        //调用方法,获取Person类中的eat方法
        //Method getMethod(String name, Class... parameterTypes)
        //因为第一个参数传递的是eat,表示获取eat方法
        //因为第二个可变参数传递的是String.class,int.class表示要获取的eat方法有String和int两个参数
        Method m = clazz.getMethod("eat", String.class, int.class);
        //创建Person对象
        Object obj = clazz.newInstance();
        //让eat方法执行
        //表示通过obj调用了eat方法,在调用eat方法的时候传递了"面包"和10这两个参数。返回值result是方法调用后的结果
        Object result = m.invoke(obj, "面包", 10);//相当于 obj.eat("面包", 10);
        System.out.println(result);
}

反射获取成员变量

Class中获取成员变量的两种方法:
Field[] getFields():获取类中的所有的成员变量
Field getFile(String name):获取类中指定的成员变量,参数为成员变量的名字。
上面两个方法只能获取到public权限的成员变量,其他的权限无法获取

Field表示成员能量,里面有一些方法:
Void set(Object obj, Object value):给成员变量赋值。
参数obj:表示给哪个对象的成员变量赋值。
参数value:表示赋值成什么。
Object get(Object obj):获取成员变量的值。
参数Object:表示获取哪个对象的成员变量。
返回值Object:表示获取到的结果。

/*
        获取指定的成员变量,然后使用
        public String hobby;
     */
    public static void method2() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
        //获取Person类的Class
        Class clazz = Class.forName("cn.itcast.demo02_reflect.Person");
        //Field getField(String name):获取类中指定的成员变量,参数为成员变量的名字。
        Field field = clazz.getField("hobby");
        //创建Person对象
        Object obj = clazz.newInstance();
        //给对象的hobby属性赋值
        //void set(Object obj, Object value): 给成员变量赋值。
        //把obj的hobby属性赋值为学习
        field.set(obj, "学习");
        //单独获取hobby属性的值
        //Object get(Object obj): 获取成员变量的值。
        Object value = field.get(obj);//表示获取obj对象的hobby属性
        System.out.println(value);
    }

    /*
        获取所有的成员变量
     */
    public static void method() throws ClassNotFoundException {
        //获取Person类的Class
        Class clazz = Class.forName("cn.itcast.demo02_reflect.Person");
        //通过Class对象获取所有的成员变量
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }

3、注解(@xxx)

注解简述

(注解是jdk5的新特性)
我们可以在方法,类上,成员变量,参数等等位置加上注解。
注解可以完成一些功能。

  • 注解和注释的区别:
    1、注释:是给程序员看的,不会影响到程序的编译和执行。
    2、注解:是给程序看的,程序可以根据注解完成一些功能
  • 常见的注解:
    @Override:可以验证一个方法是否是重写父类的方法。
    @FunctionalInterface:可以验证一个接口是否是函数式接口。
    @Deprecated:标记一个方法已经过时了,不推荐给使用了。

自定义注解:

自定义注解的格式:
public @interface 注解名() {
}
注解中可以写一些属性:
属性定义格式:
数据类型 属性名();
数据类型 属性名() default 默认值;

注解中的属性的数据类型只能是以下几种:
1、八种基本数据类型(byte short int long float double boolean char)
2、String , Class, 枚举, 注解。
3、以上所有类型的一维数组

注解的使用:

我们可以在类上,方法上,变量上加注解。
使用格式:
@注解名
@注解名(属性名 = 属性值, 属性名 = 属性值)
注意事项:
1、在使用注解时,如果注解中存在没有默认值的属性,那么必须要给属性赋值。
2、如果注解中的某个属性是有默认值的,那么在使用注解是可以给属性赋值,也可以不赋值。
3、如果使用注解时,给数组类型的属性赋值,那么可以是用大括号包裹,大括号里面的元素用逗号隔开。
4、如果再给数组类型的属性赋值是只需要赋值一个元素,那么可以省略大括号。

public @interface Student {
    //定义属性,表示姓名
    String name();
    //定义属性,表示年龄,给年龄初始值0
    int age() default 0;
    //定义属性,表示爱好
    String[] hobbies();
}


注解中的特殊属性value
如果注解中只有一个没有默认值的属性,并且该属性叫做value,那么在给该属性赋值的时候可以省略属性名,反之必须全写。
public @interface Book {
    //定义属性表示书名
    String value();
    int price() default 100;
}

元注解

元注解是用来修饰注解的
元注解也是注解,用来对注解完成一些功能。
在JDk中提供了常见的两个元注解
@Target
@Retention

  • @Target元注解可以限制注解的是用位置,如果不使用@Target元注解去修饰注解,那么该注解可以在任何位置使用。
    @Target中只有一个属性叫做你value,可以在该属性赋值的时候使用,可以上略属性名,该属性表示被修饰的注解的使用位置。
    Value是ElementType数组类型的,ElemetType中的属性,每一个属性都有特殊作用。
    我们要向@Target元注解的value属性位置传递ElemetType中的属性,每一个属性都有特殊作用。
    ElementType是枚举类型,里面的每一个属性都默认使用static修饰了,要使用枚举名字去访问
    ElementType.TYPE: 用在类,接口上
    ElementType.FIELD:用在成员变量上
    ElementType.METHOD: 用在方法上
    ElementType.PARAMETER:用在参数上
    ElementType.CONSTRUCTOR:用在构造方法上
    ElementType.LOCAL_VARIABLE:用在局部变量上

  • @Retention
    元注解@Retention用来限制注解的生命周期,如果不使用@Retention修饰,那么注解只能在源代码阶段和编译后的.calss中有效,在运行时期内存是没有的。
    元注解只有一个属性叫做value,所以在给该属性赋值的时候可以省略属性名。
    这个value属性是RetentionPolicy类型,RetentionPolicy是枚举类型,在枚举中,每一个属性都是本身类型
    在使用Retention元注解的时候,可以向value属性位置传递RetentionPolicy中的属性,每一个属性都有特殊的作用
    RetentionPolicy.SOURCE:表示被修饰的注解只在源代码阶段有效,编译后的class以及运行时内存中是没有的。
    RetentionPolicy.CLASS:表示被修饰的注解在源代码阶段和编译后的.class中有效,运行时期内存中是没有的(默认值)
    RetentionPolicy.RUNTIME:表示被修饰的注解在源代码阶段,编译后的.class,运行时内存中都有效。

  • 注解的解析
    注解解析指的是获取注解中的内容并进行使用。
    相关API:
    Annotation接口: 是所有注解的根接口,所有的注解都会默认实现该接口。
    AnnotatedElement接口: 这个接口中定义了操作注解的方法。
    T getAnnotation(Class annotationClass):获取指定注解,参数为注解的Class对象。
    Annotation[] getAnnotations():获取所有注解。
    boolean isAnnotationPresent(Class annotationClass):判断指定注解是否存在,参数为注解的Class
    注解解析要通过反射技术去实现,反射有关的类都实现了AnnotatedElement接口,都是AnnotationElement的实现类。所以可以通过反射
    有关的类去操作注解。
    如果要操作类上的注解,可以使用Class对象调用AnnotatedElement中的方法操作注解。
    如果要操作方法上的注解,可以使用Method对象调用AnnotatedElement中的方法操作注解
    如果要操作构造方法上的注解,可以使用Constructor对象调用AnnotatedElement中的方法操作注解。

猜你喜欢

转载自blog.csdn.net/weixin_45507013/article/details/99749866
今日推荐