反射
1. Junit单元测试
测试分类
- 黑盒测试:不需要写代码,输入值看结果
- 白盒测试:需要写代码,关注程序的执行流程(如:Junit测试)
Junit测试
实现步骤:
- 定义一个测试类(测试用例)
- 测试类名:被测试的类名Test
- 包名:xxx.xxx.xx.test
- 定义测试方法:可以独立运行
- 方法名:test即将测试的方法名
- 返回值:void
- 参数列表:空参
- 给方法加注解:@Test
- 导入Junit的依赖环境
判定结果:
-
红色:失败
-
绿色:失败
-
一般情况下,使用断言操作来操作结果
Assert.assertEquals(期望值,result);
@Before&@After
- @Before:修饰的方法会在测试方法之前被自动执行
- @After:修饰的方法会在测试方法之后自动被执行
实例代码
public class Calcutor {
public int add(int a,int b){
return a+b;
}
public int del(int a,int b){
return a-b;
}
}
Junit测试类实例代码
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class CalcutorTest {
@Before
public void init(){
System.out.println("执行初始化方法");
}
@Test
public void testadd(){
Calcutor calcutor = new Calcutor();
int result = calcutor.add(3,4);
Assert.assertEquals(7,result);
}
@After
public void after(){
System.out.println("执行的最后方法");
}
}
2.反射
反射概述
反射是框架设计的灵魂
框架:半成品软件。可以在框架的基础上进行软件开发,简化编码
反射机制:将类的各个组成部分(成员变量、成员方法等)封装为其他对象。
反射优点:
- 可以在程序运行过程中,操作这些对象
- 可以解耦,提高程序的扩展性
反射详解
获取class对象的3种方式
Class.forName("全类名")
应用:多用于配置文件,将类名定义在配置文件中。读取文件,加载类
类名.class
应用:多用于参数的传递
对象.getClass()
(常用)
应用:多用于对象获取字节码
结论:同一字节码文件.class文件
在程序运行过程中,只会加载一次,三种方式获取的class对象都是同一个。
实例代码
public class Example {
public static void main(String[] args) throws Exception {
//1.Class.forName("全类名");
Class cls1 = Class.forName("AFan.Test.Calcutor");
System.out.println(cls1); // class AFan.Test.Calcutor
//2.类名.class
Class cls2 = Calcutor.class;
System.out.println(cls2); // class AFan.Test.Calcutor
//3.对象.getClass()
Calcutor calcutor = new Calcutor();
Class cls3 = calcutor.getClass();
System.out.println(cls3); // class AFan.Test.Calcutor
System.out.println(cls1 == cls2); //true
System.out.println(cls1 == cls3); //true
/*结论:三种方式指向的是同一对象*/
}
}
Class对象的常用方法
将类的各个部分转化为对象
- 获取成员变量们
方法声明 | 功能描述 |
---|---|
Field[] getFields() | 获取所有public修饰的成员变量 |
Field getFields(String name) | 获取所有public修饰的name变量名的成员变量 |
Filed[] getDeclaredFields() | 获取所有成员变量,不考虑修饰符 |
Field getDeclaredField(String name) | 获取name变量名的成员变量 |
- 获取构造方法们
方法声明 | 功能描述 |
---|---|
Constructor<?>[] getConstructors() | 获取所有public修饰的构造器 |
Constructor getConstructor(类<?>… parameterTypes) | 获取public修饰的指定的构造器 |
Constructor<?>[] getDeclaredConstructors() | 获取所有构造器 |
Constructor getDeclaredConstructor(类<?>… parameterTypes) | 获取指定的构造器 |
- 获取成员方法们
方法声明 | 功能描述 |
---|---|
Method[] getMethods() | 获取所有public修饰的成员方法 |
Method getMethod(String name,类<?>… parameterTypes) | 获取public修饰的指定的成员方法 |
Method[] getDeclaredMethods() | 获取所有成员方法 |
Method getDeclaredMethod(String name,类<?>… parameterTypes) | 获取指定的常用方法 |
- 获取类名
方法声明 | 功能描述 |
---|---|
String getName() | 获取类名 |
操作class对象的方法
- Field:操作成员变量们
方法声明 | 功能描述 |
---|---|
void set(Object obj,Object value) | 设置值 |
Object get(Object obj) | 获取值 |
void setAccessible(true) | 忽略访问权限修饰符的安全检查 |
- Constructor:创建对象
方法声明 | 功能描述 |
---|---|
T newInstance(Object obj) | 创建对象 |
- Method:方法对象
方法声明 | 功能描述 |
---|---|
Object invoke(Object obj,Object …args) | 创建方法对象 |
String getName() | 获取方法名 |
反射实例代码
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Example {
public static void main(String[] args) throws Exception {
//获取class对象
Class personClass = Person.class;
//获取构造方法的对象
Constructor personClassConstructor = personClass.getConstructor(String.class, int.class);
Object person = personClassConstructor.newInstance("afan", 20);
System.out.println(person);
//获取成员变量
Field a = personClass.getField("a");
a.set(person,"我是a");
System.out.println(a.get(person));
//获取成员方法的对象
Method method = personClass.getMethod("eat", String.class);
method.invoke(person,"大米饭");
}
}
反射案例
需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
实现步骤:
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
- 在程序中加载读取配置文件
- 使用反射技术来加载类文件进内存
- 创建对象,执行方法
实例代码
className = AFan.Person
methodName = eat
public class Person {
private String name;
private int age;
public Person(){
}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("eat···");
}
}
import java.io.InputStream;
import java.util.Properties;
public class Example {
public static void main(String[] args) throws Exception {
//创建Properties对象
Properties pro = new Properties();
//加载配置文件,转换为一个集合
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("pro.properties");
pro.load(in);
//获取全类名和方法
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//创建对象,执行方法
Class proClass = Class.forName(className);
Object obj = proClass.getConstructor().newInstance();
proClass.getMethod(methodName).invoke(obj);
}
}
3.注解
注解介绍
作用:用来对包、类、方法、局部变量、方法参数等元素进行说明,注释。
使用注解:@注解名称
注解与注释的区别:
- 注解:说明程序,给计算机看的
- 注释:用文字描述程序的,给程序员看的
作用分类:
- 编写文档:生成javadoc (API)文档
- 代码分析:使用反射
- 编译检查:Override
JDK预定义的一些注解
注解 | 功能描述 |
---|---|
@Override | 检测被该注解标注的方法是否继承自父类(接口) |
@Deprecated | 表示该方法已过时 |
@SuppressWarnings | 压制警告 |
@SuppressWarnings(“all”) | 压制所有警告 |
自定义注解
- 格式:
public @interface 类名{
//属性列表(返回值类型 方法名() )
}
- 本质:注解本质还是一个接口,该接口默认继承Annotation接口
public interface MyAnno extends java.lang.annotation.Annotation {}
- 属性:接口中可以定义的抽象方法
- 属性的返回值类型:基本数据类型、String、枚举、注解、数组
- 定义了属性,在使用时需要给属性赋值
- 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值
- 如果只有一个属性需要赋值,并且属性的名称是value。则value可以省略,直接定义值即可。
- 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}省略
元注解
定义:描述注解的注解
元注解 | 功能描述 |
---|---|
@Target(ElementType e) | 描述注解能够作用的位置 |
@Retention | 描述注解被保留的阶段 |
@Documented | 描述注解是否被抽取到API文档中 |
@Inherited | 描述注解是否被子类继承 |
注意:
@Target中ElementType的取值:
- TYPE:作用在类上
- METHOD:作用在方法上
- FIELD:作用在成员变量上
@Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
解析注解
作用:获取注解中定义的属性值
实例代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Anna {
String className();
String methodName();
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@Anna(className = "AFan.Person",methodName = "eat")
public class Example {
public static void main(String[] args) throws Exception {
//获取注解定义的位置的对象
Class<Example> exampleClass = Example.class;
//获得指定的注解
Anna an = exampleClass.getAnnotation(Anna.class);
String className = an.className();
String methodName = an.methodName();
Class newclass = Class.forName(className);
Constructor constructor = newclass.getConstructor();
Object p = constructor.newInstance();
Method method = newclass.getMethod(methodName);
method.invoke(p);
}
}