1.Junit单元测试
-
测试分类
- 测试就是在我们编写完代码之后对代码的功能进行测试,测试一般分为黑盒测试和白盒测试.
- 黑盒测试:黑盒测试也称为功能测试,用于检测每个功能是否可以正常使用,在测试中把程序看为一个不能打开的黑盒子,在完全不考虑程序的代码内部结构和内部特性的情况下,传入数据查看结果.
- 白盒测试:白盒测试也称为结构测试,主要用于检测软件编码过程中的错误.
- 黑盒测试人员可以不用编写代码,只需要测试代码是否可以运行,检测不同情况下代码的运行结果即可.(市面上大多数的测试人员)
- 白盒测试人员需要知道如何编写代码,检测代码的健壮性,拓展性,耦合程度等.
- 测试就是在我们编写完代码之后对代码的功能进行测试,测试一般分为黑盒测试和白盒测试.
-
Junit的使用步骤
- 1.定义测试类(测试用例)
- 建议:
- 测试类的类名应该为:被测试的类名+Test
- 例:测试Calc类,测试类名应该叫做CalcTest
- 测试类所在的包应该为:xxx.xxx.test
- 例:测试类CalcTest应该在com.itheima.test包下.
- 测试类的类名应该为:被测试的类名+Test
- 建议:
- 2.定义测试方法
- 建议:
- 测试方法的方法名称应该为:test+方法名
- 例:测试add方法,测试方法名应该叫做testAdd(小驼峰命名法)
- 测试方法的返回值类型应该为:void
- 运行测试方法查看结果即可,因为测试方法是单独运行的,返回结果无意义,因为没有调用者进行接收。
- 测试方法的参数列表应该为:空
- 运行测试方法为单独运行,没必要传入参数.
- 测试方法的方法名称应该为:test+方法名
- 建议:
- 3.在测试方法上添加注解@Test
- 4.导入Junit依赖,在@Test注解处使用万能键Alt+Enter选择Add’JUnit4’ to classpath,等待下载之后会自动导入到项目中.
- 1.定义测试类(测试用例)
-
测试方法的结果分类
-
package com.itheima.junit.use; import com.itheima.junit.function.Calc; import org.junit.Assert; import org.junit.Test; public class CalcTest { /** * 测试Calc类的divisionFunction方法. */ @Test public void testDivision1(){ //创建Calc类对象. Calc c = new Calc(); //调用Calc类对象的divisionFunction方法进行测试(代码运行成功为绿色) int result = c.divisionFunction(9, 3); System.out.println(result); } /** * 测试Calc类的divisionFunction方法. */ @Test public void testDivision2(){ //创建Calc类对象. Calc c = new Calc(); //调用Calc类对象的divisionFunction方法进行测试(代码运行出错为为红色) int result = c.divisionFunction(9, 0); System.out.println(result); } /** * 测试Calc类的divisionFunction方法. */ @Test public void testDivision3(){ //创建Calc类对象. Calc c = new Calc(); //调用Calc类对象的divisionFunction方法进行测试(代码运行结果与预期不符为黄色) int result = c.divisionFunction(9, 3); //使用Assert类的静态方法对结果与预期结果进行判断. Assert.assertEquals(4,result); } }
代码运行成功没有异常控制台显示绿色
代码运行失败出现错误控制台显示红色:
代码运行没问题但是与预期结果不同控制台显示黄色:
-
-
@Before注解
- @Before用于修饰测试类中的初始化方法,一般用于资源的申请.
- 初始化方法:所有的测试方法执行之前都会先执行该方法.
-
@After注解
- @After用于修饰测试类中的结束方法,一般用于资源的释放.
- 结束方法:所有的测试方法执行完之后,都会自动执行该方法.
2.反射Reflex
-
反射是框架设计的灵魂
- 框架:半成品软件,我们在框架的基础上进行软件开发,可以简化编码.
- 反射:将类的各个组成部分封装为其他对象.
-
Java代码在计算机中的三个阶段
- Source源代码阶段
- Class类对象阶段
- Runtime运行时阶段
-
反射的好处
- 可以在程序的运行过程中,操作这些对象.
- 可以降低程序的耦合度,提高程序的可拓展性.
-
获取Class对象的三种方式
-
Class.forName(“全类名”) 源代码阶段
- 将字节码文件加载进内存,返回Class对象.
- 多用于配置文件,将类名可以定义在配置文件中,读取文件加载类.
- 将字节码文件加载进内存,返回Class对象.
-
类名.class Class类对象阶段
- 通过类名的属性class来获取.
- 多用于参数的传递.
- 通过类名的属性class来获取.
-
对象.getClass() Runtime运行时阶段
- getClass()方法在Object类中.
- 多用于对象获取字节码的方式.
- getClass()方法在Object类中.
-
package com.itheima.reflect; import com.itheima.domain.Person; public class ReflectDemo01 { public static void main(String[] args) throws Exception { //1.通过Class.forName("全类名")源代码阶段获取Person的Class Class cls1 = Class.forName("com.itheima.domain.Person"); System.out.println(cls1); //2.通过类名.class获取Person的Class(类加载阶段) Class cls2 = Person.class; System.out.println(cls2); //3.对象.getClass()Runtime运行时阶段获取Person的class. Class cls3 = new Person().getClass(); System.out.println(cls3); //使用==比较三个class对象. System.out.println(cls1 == cls2); //true System.out.println(cls2 == cls3); //true System.out.println(cls1 == cls3); //true } }
-
同一个字节码文件(*.class)在一次程序的运行过程中只会被加载一次,无论通过哪一种方式获取的class文件都是同一个.Class对象的获取功能
-
Class对象获取成员变量
-
//获取单个成员变量[public] Field getField(String name) //获取单个成员变量[任意修饰符] Field getDeclaredField(String name) //获取所有成员变量[public] Field[] getFields() //获取所有成员变量[任意修饰符] Field[] getDeclaredFields()
-
Field类
-
Field提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
-
常用方法
-
//指定一个对象,获取该对象的Field字段值. Object get(Object obj); //指定一个对象,设置该对象的Field字段的值. set(Object obj, Object value); //是否忽略访问修饰符的安全检查. setAccessible(boolean b);
-
-
-
代码演示
-
package com.itheima.reflect; import com.itheima.domain.Student; import java.lang.reflect.Field; /** * 获取Student的成员变量. */ public class Reflect_GetField { public static void main(String[] args) throws Exception { //获取Student的Class字节码对象. Class cls = Student.class; /* 获取所有的成员变量[public] Field[] getFields() */ Field[] clsFields = cls.getFields(); for (Field field : clsFields) { System.out.println(field); //public java.lang.String com.itheima.domain.Student.address 只获取到public修饰的String类型的成员变量. } System.out.println("----------------------------"); /* 获取指定名称的成员变量[public] */ //Field field = cls.getField("name"); 如果获取的不是public修饰的或者成员变量不存在,则会抛出异常. Field field = cls.getField("address"); /* Field是Java封装成员变量的对象,通过Field对象可以实现设置成员变量的值和获取某个对象的成员变量的值. Object get(Object obj):指定一个对象,获取该对象的Field字段值. set(Object obj, Object value):指定一个对象,设置该对象的Field字段的值. */ Student student = new Student("001", "路飞", 19, "风车村"); Object result = field.get(student); //获取s对象的address成员变量的值. System.out.println(result); //设置s对象的address成员变量的值. field.set(student, "东海"); System.out.println(student); System.out.println("----------------------------"); /* 获取所有成员变量[任意修饰符] */ Field[] declaredFields = cls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); /* private java.lang.String com.itheima.domain.Student.sid protected java.lang.String com.itheima.domain.Student.name java.lang.Integer com.itheima.domain.Student.age public java.lang.String com.itheima.domain.Student.address 获取到的所有的成员变量,所有修饰符. */ } System.out.println("----------------------------"); /* 获取单个成员变量[任意修饰符] */ Field f = cls.getDeclaredField("name"); System.out.println(f); //这次可以获取到name的成员变量字段,因为getDeclaredField方法可以获取到任意修饰符修饰的成员变量. //Object value = f.get(student);//student成员变量是受保护的,所以获取的时候会报异常,失败. //如果想要获取到任意修饰符修饰的成员变量的具体值,需要忽略访问修饰符的安全检查,也就是进行暴力反射. f.setAccessible(true); Object value = f.get(student); System.out.println(value); } }
-
-
-
Class对象获取构造方法
-
//获取单个构造方法[public] Constructor<T> getConstructor(类<?>... parameterTypes) //获取单个构造方法[任意修饰符] Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) //获取所有构造方法[public] Constructor<?>[] getConstructors() //获取所有构造方法[任意修饰符] Constructor<?>[] getDeclaredConstructors()
-
Constructor类
-
提供了一个类的单个构造函数.
-
常用方法
-
//使用由此Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例. T newInstance(Object... initargs);
-
-
-
-
如果想通过Class对象创建一个空参对象可以直接通过newInstance()方法获得一个对象,而不需要去通过获取无参构造器对象来实现.
-
代码演示
-
package com.itheima.reflect; import com.itheima.domain.Student; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * 获取Student类的构造方法 */ public class Reflect_GetConstructor { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //获取Student类的字节码对象. Class cls = Class.forName("com.itheima.domain.Student"); /* 获取所有构造方法[public] Constructor<?>[] getConstructors() */ Constructor[] constructors = cls.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); /* public com.itheima.domain.Student(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String) public com.itheima.domain.Student(java.lang.String,java.lang.Integer) public com.itheima.domain.Student() 获取到的所有构造方法都是public修饰的. */ } System.out.println("----------------------------"); /* 获取所有构造方法[任意修饰符] Constructor<?>[] getDeclaredConstructors() */ Constructor[] declaredConstructors = cls.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); /* protected com.itheima.domain.Student(java.lang.String,java.lang.String,java.lang.Integer) public com.itheima.domain.Student(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String) public com.itheima.domain.Student(java.lang.String,java.lang.Integer) public com.itheima.domain.Student() 获取到任意修饰符修饰的构造方法. */ } System.out.println("----------------------------"); /* 获取单个构造方法[public] Constructor<T> getConstructor(类<?>... parameterTypes) */ Constructor constructor = cls.getConstructor(String.class, Integer.class); Object o = constructor.newInstance("路飞", 23); System.out.println(o); System.out.println("----------------------------"); //如果想要通过Class对象创建一个对象,而无需指定任何参数,可以通过newInstance方法获取一个对象. Object o1 = cls.newInstance(); System.out.println(o1); System.out.println("----------------------------"); /* 获取单个构造方法[任意修饰符] Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) */ Constructor declaredConstructor = cls.getDeclaredConstructor(String.class, String.class, Integer.class); //忽略访问修饰符的安全检查,进行暴力反射. declaredConstructor.setAccessible(true); Object o2 = declaredConstructor.newInstance("001", "索隆", 29); System.out.println(o2); } }
-
-
-
Class对象获取成员方法
-
//获取单个成员方法[public] Method getMethod(String name, 类<?>... parameterTypes) //获取单个成员方法[任意修饰符] Method getDeclaredMethod(String name, 类<?>... parameterTypes) //获取所有成员方法[public] Method[] getMethods() //获取所有成员方法[任意修饰符] Method[] getDeclaredMethods()
-
Method类
-
方法在类或接口上提供有关单一方法的信息.
-
常用方法
-
//在具有指定参数的指定对象上调用此 方法对象表示的基础方法. Object invoke(Object obj, Object... args); //返回由该 方法对象表示的方法的名称,作为String. String getName();
-
-
-
代码演示
-
package com.itheima.reflect; import com.itheima.domain.Student; import java.lang.reflect.Method; /** * 获取Student的成员方法 */ public class Reflect_GetMethod { public static void main(String[] args) throws Exception { //获取Student的Class字节码对象. Class cls = Student.class; /* 获取所有成员方法[public] Method[] getMethods() */ Method[] methods = cls.getMethods(); for (Method method : methods) { System.out.println(method); /* Get/Set方法/继承Object类的方法此处忽略. public void com.itheima.domain.Student.show() 获取到的所有方法都是public修饰的. */ } System.out.println("----------------------------"); /* 获取所有成员方法[任意修饰符] Method[] getDeclaredMethods() */ Method[] declaredMethods = cls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); /* Get/Set方法/继承Object类的方法此处忽略. void com.itheima.domain.Student.eat() protected void com.itheima.domain.Student.play() private void com.itheima.domain.Student.study() public void com.itheima.domain.Student.show() 获取到任意修饰符修饰的所有方法. */ } System.out.println("----------------------------"); /* 获取指定的成员方法[public] Method getMethod(String name, 类<?>... parameterTypes) */ Method method = cls.getMethod("show"); //show方法无参. //想要执行method方法,需要一个对象进行执行,那么在这里我们使用反射获取一个对象. method.invoke(cls.newInstance()); Method method1 = cls.getMethod("goHome", String.class); //goHome方法需要一个String类型的参数. //想要执行method方法,需要一个对象进行执行,那么在这里我们使用反射获取一个对象. method1.invoke(cls.newInstance(), "西安"); System.out.println("----------------------------"); /* //获取单个成员方法[任意修饰符] Method getDeclaredMethod(String name, 类<?>... parameterTypes) */ Method method2 = cls.getDeclaredMethod("study"); //忽略访问修饰符的安全检查,也就是进行暴力反射 method2.setAccessible(true); method2.invoke(cls.newInstance()); } }
-
-
-
Class对象获取类名
-
获取类名 String getName();
-
-
反射案例(编写"框架",不改变任何代码的前提下,可以执行任意类的任意方法)
-
package com.itheima.reflect; import java.io.*; import java.lang.reflect.Method; import java.util.Properties; public class Reflect_Framework { /** * 编写"框架",不改变任何代码的前提下,可以执行任意类的任意方法. * @param args */ public static void main(String[] args) throws Exception { //创建Properties对象,用于加载配置文件. Properties prop = new Properties(); //获取class目录下的配置文件信息 -> 获取本类的ClassLoader. ClassLoader classLoader = Reflect_Framework.class.getClassLoader(); InputStream stream = classLoader.getResourceAsStream("info.properties"); //使用prop对象读取stream流中的信息. prop.load(stream); //获取配置文件的信息. String className = prop.getProperty("className"); String methodName = prop.getProperty("methodName"); //加载该类进入到内存中,获取到对应的字节码对象. Class cls = Class.forName(className); //通过字节码对象获取到className的实例. Object o = cls.newInstance(); //通过字节码对象获取指定名称的method方法对象(methodName). Method method = cls.getDeclaredMethod(methodName); //忽略访问修饰符检查,进行暴力反射. method.setAccessible(true); method.invoke(o); } }
-
-
3.注解Annoation
-
注解的概念
- 注解(Annotation)也称为元数据,一种代码级别的说明,它是Jdk1.5之后进入的一个新特性,与类/接口/枚举在同一个层次,它可以声明在包/类/字段/方法/局部变量/方法参数等的前面,用来对这些元素进行说明,注释.
-
注解的作用
- 编写文档:通过代码里面标识的注解生成文档(javadoc).
- 代码分析:通过代码里面表示的注解对代码进行分析.
- 编译检查:通过代码里标识的注解让编译期能够实现基本的编译检查.
-
Jdk中预定义的一些注解
-
@Override
-
//@Override注解可以帮助我们检测被该注解标注的方法是否是继承父类(接口)的. @Override public String toString() { return super.toString(); } //如果@Override注解标注的方法不是继承自父类(接口)的,那么@Override注解将会报错. //@Override public int hhashCode() { return super.hashCode(); }
-
-
自定义注解
-
格式:
-
package com.itheima.annotation; //自定义的注解MyAnno public @interface MyAnno { }
public @interface 注解名称
-
-
注解的本质:
-
自定义注解的Java文件内容
-
使用javac命令进行编译之后对生成的class文件进行反编译.
注解的本质就是一个接口.
- 一个继承了java.lang.annotation.Annotation接口的接口.
-
-
注解的属性
- 注解的属性其实就是接口中可以定义的内容
- 常量
- 抽象方法
- 抽象方法的要求
- 方法的返回值类型只能是如下几种,其他的都不可以.
- (1)基本数据类型
(2)String
(3)枚举
(4)注解
(5)以上几种类型的数组
- (1)基本数据类型
- 定义属性(注解中的方法)之后,在使用注解的时候需要赋值,如果不赋值,要在注解的定义中使用default进行赋值.
- 如果注解中只有一个属性,并且属性的名称是value,则value可以省略,直接定义值可以.
- 数组赋值时,如果需要使用大括号进行包裹,如果只有一个值,则大括号可以省略不写.
- 方法的返回值类型只能是如下几种,其他的都不可以.
- 抽象方法的要求
- 注解的属性其实就是接口中可以定义的内容
-
元注解
- 概述:用于描述注解的注解.
- 常用元注解
-
@Target 描述注解可以作用的位置.
- @Target注解的属性是一个ElementType类型的数组,ElementType是一个枚举类.
- ElementType.TYPE 可以作用在类上.
- ElementType.METHOD 可以作用在方法上.
- ElementType.FIELD 可以作用于成员变量上.
- 注意此处的构造方法为单独的ElementType.CONSTRUCTOR,表示可以作用于构造方法上.
- @Target注解的属性是一个ElementType类型的数组,ElementType是一个枚举类.
-
@Retention 描述注解被保留的阶段.
- RetentionPolicy.SOURCE
- 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃.
- RetentionPolicy.CLASS
- 注解被保留到class文件,但JVM加载class文件时候被遗弃.
- RetentionPolicy.RUNTIME
- 注解不仅被保存到class文件中,JVM加载class文件之后,仍然存在.
- RetentionPolicy.SOURCE
-
@Documented 描述注解能否被抽取到API文档中.
-
@Inherited 描述注解能否被子类继承.
-
-
注解解析的方式
-
如果我们想要获取一个类上的注解中的信息的话,可以通过以下方式进行获取
-
注解
-
package com.itheima.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 此注解用于描述需要执行的类名,方法名. */ @Target(value = {ElementType.TYPE}) //表示此注解可以标注在类上. @Retention(RetentionPolicy.RUNTIME) //表示此注解可以保留到运行期. public @interface Pro { //描述要获取的类的信息. String className(); //描述要获取的方法的信息. String methodName(); }
-
-
获取方式
-
package com.itheima.annotation; import java.lang.reflect.Method; @Pro(className = "com.itheima.domain.Student", methodName = "study") public class Reflect_InvokeByAnno{ public static void main(String[] args) throws Exception { //获取本类的字节码文件对象. Class<Reflect_InvokeByAnno> cls = Reflect_InvokeByAnno.class; //通过本类的字节码文件对象获取本类的指定注解. Pro pro = cls.getAnnotation(Pro.class); //根据Pro注解的方法获取对应的信息. String methodName = pro.methodName(); //获取到的值:study String className = pro.className(); //获取到的值:com.itheima.domain.Student } }
-
-
-
-
以后大多数情况,我们会使用注解,但是我们会使用Java或者第三方类库给我们提供好的注解帮助我们提高开发效率.
-
注解一般用于用于编译期,或者用于解析程序.
-
注解不是程序的一部分.
获取本类的字节码文件对象.
Class<Reflect_InvokeByAnno> cls = Reflect_InvokeByAnno.class;
//通过本类的字节码文件对象获取本类的指定注解.
Pro pro = cls.getAnnotation(Pro.class);
//根据Pro注解的方法获取对应的信息.
String methodName = pro.methodName(); //获取到的值:study
String className = pro.className(); //获取到的值:com.itheima.domain.Student
}
}
```
-
以后大多数情况,我们会使用注解,但是我们会使用Java或者第三方类库给我们提供好的注解帮助我们提高开发效率.
-
注解一般用于用于编译期,或者用于解析程序.
-
注解不是程序的一部分.