基础加强
一、Junit单元测试
1. 测试分类
- 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
- 白盒测试:需要写代码,关注程序的具体执行流程。
2. Junit使用
Junit
是一种白盒测试。- 步骤:
- 定义一个测试类(测试用例)
- 建议:
- 测试类名:被测试的类名Test。例如,
CalculatorTest
- 包名:xxx.xxx.xxx.test。例如,
guli.zhu.test
- 测试类名:被测试的类名Test。例如,
- 建议:
- 定义测试方法:可以独立运行
- 建议:
- 方法名:test测试的方法名。例如,
testAdd()
- 返回值:
void
- 参数列表:空参数
- 方法名:test测试的方法名。例如,
- 建议:
- 给方法前加注解
@Test
- 导入
junit
依赖环境
- 定义一个测试类(测试用例)
- 判定结果:
- 红色:失败
- 绿色:成功
- 一般使用断言操作来处理结果:
Assert.assertEquals(期望的结果, 运算的结果);
package guli.zhu.junit;
public class Calculator {
//定义加法方法
public int add(int a, int b) {
return a + b;
}
//定义减法方法
public int sub(int a, int b) {
return a - b;
}
}
//测试类
package guli.zhu.test;
import guli.zhu.junit.Calculator;
import org.junit.Assert;
import org.junit.Test;
public class TestCalculator {
//测试加法方法
@Test
public void testAdd() {
Calculator c = new Calculator();
int rs = c.add(1, 2);
//使用断言
Assert.assertEquals(3,rs);
}
//测试减法方法
@Test
public void testSub() {
Calculator c = new Calculator();
int rs = c.sub(1, 2);
//使用断言
Assert.assertEquals(-1,rs);
}
}
3. 两个注解
- **@Before:**该注解修饰的方法会在测试方法之前被自动执行。
- **@After:**该注解修饰的方法会在测试方法之后被自动执行。
二、反射
1. 反射概述
-
框架:半成品软件。可以在框架的基础上进行软件开发,简化编码。
-
**反射:**将类的各个组成部分封装为其他对象,这就是反射机制。
- 好处:
- 可以在程序运行的过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
- 好处:
-
Java代码在计算机中经历的阶段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyT825oH-1589508515123)(https://raw.githubusercontent.com/zhugulii/picBed/master/Java%E4%BB%A3%E7%A0%81%E7%9A%84%E4%B8%89%E4%B8%AA%E9%98%B6%E6%AE%B5.bmp)]
2. 获取字节码Class对象的三种方式
- **Class.forName(“全类名”):**将字节码文件加载进内存,返回
Class
对象。- 多用于配置文件,将类名定义在配置文件中。读取文件,加载类。
- **类名.class:**通过类名的属性
class
获取。- 多用于参数的传递。
- 对象.getClass():
getClass()
方法在Object
类中定义着。- 多用于对象的获取字节码的方式。
package guli.zhu.domain;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package guli.zhu.reflect;
import guli.zhu.domain.Person;
public class Demo01Reflect {
public static void main(String[] args) throws Exception {
//1.Class.forName("全类名")
Class cls1 = Class.forName("guli.zhu.domain.Person");
System.out.println(cls1); // class guli.zhu.domain.Person
//2.类名.class
Class cls2 = Person.class;
System.out.println(cls2); // class guli.zhu.domain.Person
//3.对象.getClass()
Class cls3 = new Person().getClass();
System.out.println(cls3); // class guli.zhu.domain.Person
System.out.println(cls1 == cls2); // true
System.out.println(cls1 == cls3); // true
}
}
- 结论:同一个字节码文件(
*.class
)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class
文件都是同一个。
3. Class对象功能概述
-
获取成员变量们
Field getField(String name) // 获取指定名称的public修饰的成员变量 Field[] getFields() // 获取所有public修饰的成员变量 Field getDeclaredField(String name) // 获取指定的的成员变量,不考虑修饰符 Field[] getDeclaredFields() // 获取所有的成员变量,不考虑修饰符
-
获取构造方法们
Constructor<T> getConstructor(Class<?>... parameterTypes) Constructor<?>[] getConstructors() Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) Constructor<?>[] getDeclaredConstructors()
-
获取成员方法们
Method getMethod(String name, Class<?>... parameterTypes) Method[] getMethods() Method getDeclaredMethod(String name, Class<?>... parameterTypes) Method[] getDeclaredMethods()
-
获取类名
String getName()
4. Class对象功能:获取Field
- Field:成员变量
- 操作:
- 设置值:
void set(Object obj, Object value)
- 获取值:
get(Object obj)
- 忽略访问权限修饰符的安全检查:
setAccessible(true)
暴力反射
- 设置值:
- 操作:
package guli.zhu.domain;
public class Person {
public String name;
protected int age;
String sex;
private String id;
public Person() {
}
public Person(String name, int age, String sex, String id) {
this.name = name;
this.age = age;
this.sex = sex;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", id='" + id + '\'' +
'}';
}
}
package guli.zhu.reflect;
import guli.zhu.domain.Person;
import java.lang.reflect.Field;
public class Demo02Reflect {
public static void main(String[] args) throws Exception {
//获取Class对象
Class personClass = Person.class;
//Field getField(String name) // 获取指定名称的public修饰的成员变量
Field name = personClass.getField("name");
//获取值:get(Object obj)
Person p = new Person();
Object o = name.get(p);
System.out.println(o); // null
//设置值:set(Object obj, Object value)
name.set(p,"zhuguli");
System.out.println(p); // Person{name='zhuguli', age=0, sex='null', id='null'}
//Field[] getFields() // 获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.println(field); // public java.lang.String guli.zhu.domain.Person.name
}
//Field getDeclaredField(String name) // 获取指定的的成员变量,不考虑修饰符
Field id = personClass.getDeclaredField("id");
//暴力反射
id.setAccessible(true);
//获取值:get(Object obj)
Object o1 = id.get(p);
System.out.println(o1); // null
//设置值:set(Object obj, Object value)
id.set(p,"1234");
System.out.println(p); // Person{name='zhuguli', age=0, sex='null', id='1234'}
//Field[] getDeclaredFields() // 获取所有的成员变量,不考虑修饰符
Field[] declaredField = personClass.getDeclaredFields();
for (Field field : declaredField) {
System.out.println(field); // public java.lang.String guli.zhu.domain.Person.name
// protected int guli.zhu.domain.Person.age
// java.lang.String guli.zhu.domain.Person.sex
// private java.lang.String guli.zhu.domain.Person.id
}
}
}
5. Class对象功能:获取Constructor
- Constructor:构造方法
- 创建对象
T newInstance(Object...initargs)
- 如果使用空参数构造方法创建对象,操作可以简化:
Class
对象的newInstance()
- 创建对象
package guli.zhu.reflect;
import guli.zhu.domain.Person;
import java.lang.reflect.Constructor;
public class Demo03Reflection {
public static void main(String[] args) throws Exception {
Class<Person> personClass = Person.class;
//Constructor<T> getConstructor(Class<?>... parameterTypes)
//Constructor<?>[] getConstructors()
//
//Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//Constructor<?>[] getDeclaredConstructors()
//Constructor<T> getConstructor(Class<?>... parameterTypes)
//获取空参数的构造方法
Constructor<Person> constructor = personClass.getConstructor();
//创建对象
Person person = constructor.newInstance();
System.out.println(person); // Person{name='null', age=0, sex='null', id='null'}
//简化创建对象的操作
Person person1 = personClass.newInstance();
System.out.println(person1); // Person{name='null', age=0, sex='null', id='null'}
//获取带参数的构造方法
Constructor<Person> constructor1 =
personClass.getConstructor(String.class, int.class,
String.class,String.class);
Person person2 = constructor1.newInstance("zhuguli", 22, "male", "1234");
System.out.println(person2); // Person{name='zhuguli', age=22, sex='male', id='1234'}
}
}
6. Class对象的功能:获取Method
- Method:方法对象
- 执行方法:
Object invoke(Object obj, Object... args)
- 获取方法名称:
String getName
- 执行方法:
package guli.zhu.domain;
public class Person {
public String name;
protected int age;
String sex;
private String id;
public Person() {
}
public Person(String name, int age, String sex, String id) {
this.name = name;
this.age = age;
this.sex = sex;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", id='" + id + '\'' +
'}';
}
public void eat(){
System.out.println("吃");
}
public void eat(String food) {
System.out.println("吃" + food);
}
}
package guli.zhu.reflect;
import guli.zhu.domain.Person;
import java.lang.reflect.Method;
public class Demo04Reflect {
public static void main(String[] args) throws Exception {
//Method getMethod(String name, Class<?>... parameterTypes)
//Method[] getMethods()
//
//Method getDeclaredMethod(String name, Class<?>... parameterTypes)
//Method[] getDeclaredMethods()
Class<Person> personClass = Person.class;
//Method getMethod(String name, Class<?>... parameterTypes)
Method method = personClass.getMethod("eat");
Person p = new Person();
method.invoke(p); // 吃
Method method1 = personClass.getMethod("eat", String.class);
method1.invoke(p,"水果"); // 吃水果
//获取所有public方法
Method[] methods = personClass.getMethods();
for (Method method2 : methods) {
System.out.println(method2);
//获取方法名
String name = method2.getName();
System.out.println(name);
}
}
}
7. Class对象的功能:获取类名
package guli.zhu.reflect;
import guli.zhu.domain.Person;
public class Demo05Reflect {
public static void main(String[] args) {
Class<Person> personClass = Person.class;
//获取类名
String name = personClass.getName();
System.out.println(name); // guli.zhu.domain.Person
}
}
8. 反射案例
/*
需求:写一个“框架”,在不改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意的方法。
实现:
1.配置文件
2.反射
步骤:
1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中。
2.在程序中加载读取配置文件
3.使用反射技术来加载类文件进内存
4.创建对象
5.执行方法
*/
//配置文件
className=guli.zhu.domain.Student
methodName=sleep
//Person类
package guli.zhu.domain;
public class Person {
public String name;
protected int age;
String sex;
private String id;
public Person() {
}
public Person(String name, int age, String sex, String id) {
this.name = name;
this.age = age;
this.sex = sex;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", id='" + id + '\'' +
'}';
}
public void eat(){
System.out.println("吃");
}
public void eat(String food) {
System.out.println("吃" + food);
}
}
//Student类
package guli.zhu.domain;
public class Student {
public void sleep(){
System.out.println("sleep");
}
}
//测试类
package guli.zhu.reflect;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectTest {
public static void main(String[] args) throws Exception {
//1.在程序中加载读取配置文件
//1.1创建Properties对象
Properties pro = new Properties();
//1.2加载配置文件,转换为一个集合
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载类进内存
Class cls = Class.forName(className);
//4.创建对象
Object o = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(o);
}
}
三、注解
1. 注解的概念
- **概念:**说明程序的。给计算机看的。
- **注释:**用文字描述程序的。给程序员看的。
- **定义:**注解(
Annotation
),也叫元数据。一种代码级别的说明。它是JDK1.5
及以后版本引入的一个特性,与类、接口、枚举是同一个层次。它可以声明在包、类、字段、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。 - 概念描述:
JDK1.5
之后的新特性- 说明程序的
- 使用注解:@注解名称
- 作用分类:
- 编写文档:通过代码里标识的注解生成文档(生成文档
doc
文档) - 代码分析:通过代码里标识的注解对代码进行分析(使用反射)
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查(
Override
)
- 编写文档:通过代码里标识的注解生成文档(生成文档
2. JDK内置注解
@Override
:检测被该注解标注的方法是否是继承自父类(接口)的。@Deprecated
:表示该注解标注的内容已过时。@SuppressWarnings
:压制警告。- 一般传递参数
all
:@SuppressWarnings("all")
- 一般传递参数
package guli.zhu.annotation;
@SuppressWarnings("all")
public class Demo01Annotation {
@Override
public String toString() {
return super.toString();
}
@Deprecated
public void show1() {
//已过时,被show2代替
}
public void show2() {
//代替show1
}
}
3. 自定义注解:格式与本质
-
格式:
元注解 public @interface 注解名称{ 属性列表; }
-
本质:注解本质上就是一个接口,该接口默认继承
Annotation
接口public interface MyAnno extends java.lang.annotation.Annotation {}
4. 自定义注解:属性定义
- 属性:接口中的抽象方法
- 属性的要求;
- 属性的返回值类型只能是以下几种:
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
- 定义了属性,在使用时需要给属性赋值
- 如果定义属性时,使用了
default
关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。 - 如果只有一个属性需要赋值,并且属性的名称是
value
,则value
可以省略,直接定义值即可。 - 数组赋值时,值使用
{}
包裹,如果数组中只有一个值,则{}
省略。
- 如果定义属性时,使用了
- 属性的返回值类型只能是以下几种:
5. 自定义注解:元注解
-
元注解:用于描述注解的注解
-
@Target
:描述注解能够作用的位置ElementType
取值:TYPE
:可以作用于类上,@Target({ElementType.TYPE})
METHOD
:可以作用于方法上FIELD
:可以作用于成员变量上
-
@Retention
:描述注解被保留的阶段@Retention(RetentionPolicy.RUNTIME)
:当前被描述的注解,会保留到class
字节码文件中,并被JVM
读取到
-
@Documented
:描述注解是否被抽取到api
文档中 -
@Inherited
:描述注解是否被子类继承
-
6. 解析注解
- 解析注解:获取注解中定义的属性值
- 获取注解定义的位置的对象(
Class、Method、Field
); - 获取指定注解
getAnnotation(Class)
,其实是在内存中生成了一个该注解接口的子类实现对象; - 调用注解中的抽象方法获取配置的属性值。
- 获取注解定义的位置的对象(
7. 案例:简单的测试案例
//注解
package guli.zhu.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
//被检测代码
package guli.zhu.annotation;
public class Calculator {
@Check
public void add() {
System.out.println("1 + 0 = " + (1 + 0));
}
@Check
public void sub() {
System.out.println("1 - 0 = " + (1 - 0));
}
@Check
public void mul() {
System.out.println("1 * 0 = " + (1 * 0));
}
@Check
public void div() {
System.out.println("1 / 0 = " + (1 / 0));
}
}
//测试Calculator
package guli.zhu.annotation;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
public class TestCalculator {
public static void main(String[] args) throws IOException {
//1.创建计算器对象
Calculator calculator = new Calculator();
int count = 0; //记录异常次数
BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt")); // 记录异常
//2.获取字节码文件对象
Class calculatorClass = calculator.getClass();
//3.获取所有方法
Method[] methods = calculatorClass.getMethods();
//4.判断方法上是否有Check注解
for (Method method : methods) {
if (method.isAnnotationPresent(Check.class)) {
//5.有,执行
try {
method.invoke(calculator);
} catch (Exception e) {
//6.捕获异常
count++;
bw.write(method.getName() + "方法出异常了");
bw.newLine();
bw.write("异常的名称:" + e.getCause().getClass().getSimpleName());
bw.newLine();
bw.write("异常的原因:" + e.getCause().getMessage());
bw.newLine();
bw.write("----------------------");
}
}
}
bw.newLine();
bw.write("此次测试共出现了" + count + "个异常!");
bw.flush();
bw.close();
}
}
//结果bug.txt
div方法出异常了
异常的名称:ArithmeticException
异常的原因:/ by zero
----------------------
此次测试共出现了1个异常!
转载:https://blog.csdn.net/weixin_44781238/article/details/106135746
查看评论