web
8.18
Junit
属于白盒测试
- 使用步骤:
- 定义测试类:类名 ClassTest、包名 package.test
- 定义测试方法:void testMethond()
- @Test并导包
注:junit测试建议参考如上定义方式
- 判断结果:断言 Assert
- @Before、@After
反射
反射机制:将类的各个组成部分封装成对象
-
获取一个Class对象的方式:
. 类名.class,通过类名的属性class获取,多用传参,即调用其他方法引出需要的对象!!比如非静态方法使用this关键字去获取流对象,类加载器对象,注解(实现类)对象。
3个方法:加载类进内存1. Class.forName(" 类全名 "), 将字节码文件加载入内存,返回Class对象,多用于配置文件 2. 类名.class,通过类名的属性class获取,多用传参,即调用其他方法引出需要的对象!!比如流对象,类加载器对象,注解(实现类)对象。 3. 对象.getClass(),此方法在Object类中定义,针对对象的获取方式;可以new对象时直接使用此方法获取
-
Class类的(功能)方法
获取:
获取成员变量们 Field[] getFields() :获取所有`public`修饰的成员变量 Field getField(String name) 获取`指定名称的public`修饰的成员变量 Field[] getDeclaredFields() 获取`所有`的成员变量,不考虑修饰符 Field getDeclaredField(String name) 获取构造方法们 Constructor<?>[] getConstructors() Constructor<T> getConstructor(类<?>... parameterTypes) Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) Constructor<?>[] getDeclaredConstructors() 获取成员方法们: Method[] getMethods() :包含Object继承的方法 Method getMethod(String name, 类<?>... parameterTypes) Method[] getDeclaredMethods() Method getDeclaredMethod(String name, 类<?>... parameterTypes) 获取全类名 String getName()
//指定构造方法获取格式:
Constructor constructor = personcls.getConstructor(String.class, int.class);
Field:成员变量
操作:
1. 设置值 void set(Object obj, Object value)
2. 获取值 get(Object obj) 私有的无法访问,需要使用 3
3. 忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射,在方法也是可以使用
//获取成员变量并操作
Class personcls = Person.class;
Field[] fields = personcls.getDeclaredFields();
Person p = new Person();
fields[0].setAccessible(true);
fields[0].set(p,"lee");
Object o = fields[0].get(p);
System.out.println(o);
Constructor:构造方法
创建对象: T newInstance(Object... initargs)
如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
//获取构造器并new
Class personcls = Person.class;
Constructor constructor = personcls.getConstructor(String.class, int.class);
Object person = constructor.newInstance("lee", 13);
System.out.println(person);
//空参构造
personcls.newInstance();
Method:方法对象
执行方法: Object invoke(Object obj, Object... args)
获取方法名称: String getName:获取方法名
//获取方法并run
Class personcls = Person.class;
Method toString = personcls.getMethod("toString");
String name = toString.getName();
System.out.println(name);
toString.invoke(new Person());
个人理解:反射也是基于一个类,万物皆对象。使用Class类来获取其他类的各个组成部分,构造器Constructor用来new,方法Method用来run,成员Field用来操作
(Object obj, Object… args) :(对象,参数)
案例
ReflectDemo.java
package day01;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectDemo {
/*
* 利用反射实现: 1.前提不改变代码 2.加载配置文件 3.利用反射技术将类文件加载入内存 4.实现创建对象,并调用方法
*/
public static void main(String[] args) {
Properties prop = new Properties();
String file = "otherclasses.properties";
String className = null;
String methodName = null;
InputStream is = null;
try {
// 使用反射机制获取流
is = ReflectDemo.class.getResourceAsStream(file);
prop.load(is);
className = prop.getProperty("className");
methodName = prop.getProperty("methodName");
// 加载类进入内存,只要获取也可以间接加载
Class cls = Class.forName(className);
// 解决创建对象类型不一致问题
Object obj = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
.properties
className=Person
methodName=eat
Person.java
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/* eat方法用于测试反射 */
public void eat(){
System.out.println("我吃过了!");
}
}
保险起见使用自己写的类获取ClassLoader: this.getClass().getClassLoader(),解决返回null问题
对于获取输入流对象空指针异常
:获取资源文件路径的一些方法
//表示在src下找配置文件
fis = this.getClass().getClassLoader().getResourceAsStream("config.properties");
//表示在类包下找配置文件
fis = this.getClass().getResourceAsStream("config.properties");
//静态无法使用this关键字,解决
Reflect.class.getResourceAsStream(file)
/*两者也是便于理解的,对于报错出现空指针异常`java.lang.NullPointerException`应该关注一下这里的区别*/
//对于URI资源转换的文件资源路径:因为使用了URI编码,导致特殊符号和中文的异常,比如空格。如果文件路径有空格等特殊字符,应该使用toURI()方法解决
String path = JsoupDemo1.class.getClassLoader().getResource("student.xml").toURI().getPath();
使用注解简化的反射案例:
AnnoAboutReflect.java
package day01;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/* 这是用于简化反射demo:ReflectDemo 的注解 */
//使用元注解描述注解
@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnoAboutReflect {
String className();
String methodName();
}
SimplifyReflectDemo.java
package day01;
import java.lang.reflect.Method;
@AnnoAboutReflect(className = "Person", methodName = "eat")
public class SimplifyReflectDemo {
/* 使用注解简化的反射demo */
/*
* 利用反射实现: 1.前提不改变代码 2.加载配置文件 3.利用反射技术将类文件加载入内存 4.实现创建对象,并调用方法
*/
public static void main(String[] args) {
String className = null;
String methodName = null;
// 解析注解
// 1.获取该类字节码对象
Class<SimplifyReflectDemo> reflectcls = SimplifyReflectDemo.class;
// 2.获取该类的注解对象,此getAnnotation方法是在内存中生成了一个注解*实现类*对象并返回,如下:
/*
* public class AarImpl implements AnnoAboutReflect {
* //事实上不是这个样子的过程,这只是一个逻辑,里面有些东也不理解,就这样理解把。
*
* @Override public String className() { return "Person"; }
*
* @Override public String methodName() { return "eat"; } }
*/
AnnoAboutReflect anno = reflectcls.getAnnotation(AnnoAboutReflect.class);
// 3.调用注解对象(此对象是实现类)的(重写)方法
className = anno.className();
methodName = anno.methodName();
System.out.println(className + "---" + methodName);// Person---eat
try { // 使用反射创建对象并操作方法
// 加载类进入内存,只要获取也可以间接加载
Class<?> cls = Class.forName(className);
// 解决创建对象类型不一致问题
Object obj = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注解
注解概念:说明程序给计算机看
注释:说明程序给程序员看
Java注解:元数据
作用分类:
①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
②代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
JDK预定义注解
@Override @Deprecated @SuppressWarnings
自定义注解
1.格式:
元注解
public @interface MyAnno {
属性列表;
}
2.本质:反编译后可以看出注解本质是一个接口默认继承Annotation
javap MyAnno.class
Compiled from "MyAnno.java"
public interface MyAnno extends java.lang.annotation.Annotation {
}
3.属性:接口(注解)中的抽象方法
要求
属性返回值类型
基本数据类型
String
枚举
注解
以上返回类型的数组
使用
package day01;
public enum User {
u1,u2;
}
package day01;
public @interface MyAnno2 {
}
package day01;
public @interface MyAnno {
int showint();
String showString() default "hahaha";
User showenum();// MyAnno的属性(方法)返回User枚举类型
MyAnno2 showAnno();//传入注解
String[] showStringArr();//传入数组,只有一个元素可以省略{}
}
package day01;
/* test the MyAnno */
/* showString()是默认传入字符串,不需要初始化 */
@MyAnno(showint = 1,showenum = User.u1,showAnno = @MyAnno2,showStringArr = {"ab","c"})
public class MyAnnoDemo{
}
*4.元注解:用于描述注解的注解
package day01;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
@Target:描述注解作用的位置
使用ElementType(是一个数组)取值决定作用域:
TYPE:作用于类
METHOD:作用于方法
FIELD:作用于字段
@Retention:描述注解保留的阶段
@Documented:描述注解是否被抽取到API文档中
@Inherited:描述注解是否被子类继承
*/
//关于MyAnno3注解
//作用域类、方法
@Target(value = { ElementType.TYPE, ElementType.METHOD })
// 保留时段一般取值RUNTIME
@Retention(RetentionPolicy.RUNTIME)
// 添加此注解可以使用javadoc抽取注解
@Documented
// 添加此注解可以被子类继承
@Inherited
public @interface MyAnno3 {
}
在程序中使用(解析)注解
- 获取注解定义位置的对象
- 获取实现注解的对象
- 调用方法获取配置的属性值
示例在反射案例的简化部分
案例
//关于Check注解检查程序异常,输出日志