单元测试:
![](https://i-blog.csdnimg.cn/blog_migrate/38b7363a180b52f0050c586fd7da134e.png)
没有单元测试时是怎么测试代码的?
Junit单元测试框架
优点:
单元测试的具体步骤:
常用注解:
这里我们采用Junit4的版本的注解,这个版本比较经典。
示例代码:
比如我们有一个StringUtil类,是我们写好的一个类:------也就是等待测试的类
package com.itheima.d1_junit;
public class StringUtil {
public static void printNumber(String name){
if(name == null){
System.out.println(0);
return; // 停掉方法
}
System.out.println("名字长度是:" + name.length());
}
/**
* 求字符串的最大索引
*/
public static int getMaxIndex(String data){
if(data == null) {
return -1;
}
return data.length() - 1;
}
}
我们要测试其中的方法,看看有没有问题
创建一个StringUtilTest类来开始进行单元测试:---步骤上面说过了,一共有四步
package com.itheima.d1_junit;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* 测试类
*/
public class StringUtilTest {
@Before
public void test1(){
System.out.println("---> test1 Before 执行了---------");
}
@Test
public void testPrintNumber(){
StringUtil.printNumber("admin");
StringUtil.printNumber(null);//测试的时候要全面一点
}
@Test
public void testGetMaxIndex(){
int index1 = StringUtil.getMaxIndex(null);
System.out.println(index1);
int index2 = StringUtil.getMaxIndex("admin");
System.out.println(index2);
//断言机制:程序员可以通过预测业务方法的结果。
//4!=index2 --输出bug
Assert.assertEquals("方法内部有bug!", 4, index2);
}
}
测试的时候,把鼠标光标点到某测试方法中,想要一键测试某个类中的所有方法,那就把光标点到StringUtilTest上,如何想把整个模块的测试类都一键测试,怎么实现?
点击Run All Tests即可,非常方便。
反射:
反射学什么?
学习获取类的信心,操作它们:
反射第一步:
获取class对象的三种方法:
代码演示:
准备一个学生类:
package com.itheima.d2_reflect;
public class Student {
private String name;
private int age;
private char sex;
private double height;
private String hobby;
public Student() {
}
public Student(String name, int age, char sex, double height, String hobby) {
this.name = name;
this.age = age;
this.sex = sex;
this.height = height;
this.hobby = hobby;
}
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 char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
获取该学生类的class对象:
package com.itheima.d2_reflect;
public class Test1Class {
public static void main(String[] args) throws Exception {
Class c1 = Student.class;
System.out.println(c1.getName()); // 全类名
System.out.println(c1.getSimpleName()); // 简名:Student
Class c2 = Class.forName("com.itheima.d2_reflect.Student");
System.out.println(c1 == c2);
Student s = new Student();
Class c3 = s.getClass();
System.out.println(c3 == c2);
//com.itheima.d2_reflect.Student
//Student
//true
//true
}
}
获取类的构造器,并对其进行操作:
![](https://i-blog.csdnimg.cn/blog_migrate/8dca7029588f3874841355f691c8610c.png)
获取类的构造器作用:
初始化对象返回。
代码演示:
这里准备一个Cat类:
package com.itheima.d2_reflect;
public class Cat {
public static int a;
public static final String COUNTRY = "中国";
private String name;
private int age;
private Cat(){
System.out.println("无参数构造器执行了~~");
}
private Cat(String name, int age) {
System.out.println("有参数构造器执行了~~");
this.name = name;
this.age = age;
}
private void run(){
System.out.println("🐱跑的贼快~~");
}
public void eat(){
System.out.println("🐱爱吃猫粮~");
}
private String eat(String name){
return "🐱最爱吃:" + name;
}
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 "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
写测试代码:----讲解在注释中
package com.itheima.d2_reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.util.concurrent.atomic.AtomicInteger;
public class Test2Constructor {
@Test
public void testGetConstructors(){
// 1、反射第一步:必须先得到这个类的Class对象
Class c = Cat.class;
// 2、获取类的全部构造器
// Constructor[] constructors = c.getConstructors();
Constructor[] constructors = c.getDeclaredConstructors();
// 3、遍历数组中的每个构造器对象
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "--->"
+ constructor.getParameterCount());//名和参数
}
}
@Test
public void testGetConstructor() throws Exception {
// 1、反射第一步:必须先得到这个类的Class对象
Class c = Cat.class;
// 2、获取类的某个构造器:无参数构造器
Constructor constructor1 = c.getDeclaredConstructor();
System.out.println(constructor1.getName() + "--->"
+ constructor1.getParameterCount());
constructor1.setAccessible(true); // 禁止检查访问权限 因为是private权限,不加这一行下面2行会出现异常
Cat cat = (Cat) constructor1.newInstance();//返回Object类型,强转
System.out.println(cat);
// 3、获取有参数构造器
Constructor constructor2 =
c.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor2.getName() + "--->"
+ constructor2.getParameterCount());
constructor2.setAccessible(true); // 禁止检查访问权限
Cat cat2 = (Cat) constructor2.newInstance("叮当猫", 3);
System.out.println(cat2);
}
}
获取类的成员变量:
获取到成员变量的作用:
赋值,取值。
代码演示:
这里准备一个Cat类:
package com.itheima.d2_reflect;
public class Cat {
public static int a;
public static final String COUNTRY = "中国";
private String name;
private int age;
public Cat(){
System.out.println("无参数构造器执行了~~");
}
private Cat(String name, int age) {
System.out.println("有参数构造器执行了~~");
this.name = name;
this.age = age;
}
private void run(){
System.out.println("🐱跑的贼快~~");
}
public void eat(){
System.out.println("🐱爱吃猫粮~");
}
private String eat(String name){
return "🐱最爱吃:" + name;
}
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 "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试代码:
package com.itheima.d2_reflect;
import org.junit.Test;
import java.lang.reflect.Field;
public class Test3Field {
@Test
public void testGetFields() throws Exception {
// 1、反射第一步:必须是先得到类的Class对象
Class c = Cat.class;
// 2、获取类的全部成员变量。
Field[] fields = c.getDeclaredFields();
// 3、遍历这个成员变量数组
for (Field field : fields) {
System.out.println(field.getName() + "---> "+ field.getType());
}
// 4、定位某个成员变量
Field fName = c.getDeclaredField("name");
System.out.println(fName.getName() + "--->" + fName.getType());
Field fAge = c.getDeclaredField("age");
System.out.println(fAge.getName() + "--->" + fAge.getType());
// 赋值
Cat cat = new Cat();
fName.setAccessible(true); // 禁止访问控制权限 因为:private String name;
fName.set(cat, "卡菲猫");
System.out.println(cat);
// 取值
String name = (String) fName.get(cat);
System.out.println(name);
}
}
获取类的成员方法:
获取类的成员方法作用:
执行。
代码演示:
借用------获取成员变量里面的Cat类
开始测试:
package com.itheima.d2_reflect;
import org.junit.Test;
import java.lang.reflect.Method;
public class Test4Method {
@Test
public void testGetMethods() throws Exception {
// 1、反射第一步:先得到Class对象。
Class c = Cat.class;
// 2、获取类的全部成员方法。
Method[] methods = c.getDeclaredMethods();
// 3、遍历这个数组中的每个方法对象
for (Method method : methods) {
System.out.println(method.getName() + "--->"
+ method.getParameterCount() + "---->"
+ method.getReturnType());
}
// 4、获取某个方法对象
Method run = c.getDeclaredMethod("run"); // 拿run方法,无参数的
System.out.println(run.getName() + "--->"
+ run.getParameterCount() + "---->"
+ run.getReturnType());
Method eat = c.getDeclaredMethod("eat", String.class);
System.out.println(eat.getName() + "--->"
+ eat.getParameterCount() + "---->"
+ eat.getReturnType());
Cat cat = new Cat();
run.setAccessible(true); // 禁止检查访问权限
Object rs = run.invoke(cat); // 调用无参数的run方法,用cat对象触发调用的。
System.out.println(rs);//null
eat.setAccessible(true); // 禁止检查访问权限
String rs2 = (String) eat.invoke(cat, "鱼儿");
System.out.println(rs2);//🐱最爱吃:鱼儿
}
}
反射的作用:
反射的重要用途:
![](https://i-blog.csdnimg.cn/blog_migrate/d657f6864e4800981943de25d5d54213.png)
使用反射做一个简易版的框架
需求:
实现步骤:
准备一个Student类:
package com.itheima.d2_reflect;
public class Student {
private String name;
private int age;
private char sex;
private double height;
private String hobby;
public Student() {
}
public Student(String name, int age, char sex, double height, String hobby) {
this.name = name;
this.age = age;
this.sex = sex;
this.height = height;
this.hobby = hobby;
}
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 char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
准备一个Teacher类:
package com.itheima.d2_reflect;
public class Teacher {
private String name;
private double salary;
public Teacher() {
}
public Teacher(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
测试类如下:
package com.itheima.d2_reflect;
import org.junit.Test;
public class Test5Frame {
@Test
public void save() throws Exception {
Student s1 = new Student("黑马吴彦祖", 45, '男', 185.3, "蓝球,冰球,阅读");
Teacher t1 = new Teacher("播妞", 999.9);
// 需求:把任意对象的字段名和其对应的值等信息,保存到文件中去。
ObjectFrame.saveObject(s1);
ObjectFrame.saveObject(t1);
}
}
还剩一个框架没写,如下是框架类:
package com.itheima.d2_reflect;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
public class ObjectFrame {
// 目标:保存任意对象的字段和其数据到文件中去
public static void saveObject(Object obj) throws Exception {
PrintStream ps = new PrintStream(new FileOutputStream("D:\\code\\javasepro\\helloworld-app\\src\\data.txt", true));
// obj是任意对象,到底有多少个字段要保存。
Class c = obj.getClass();
String cName = c.getSimpleName();
ps.println("---------------" + cName + "------------------------");
// 2、从这个类中提取它的全部成员变量
Field[] fields = c.getDeclaredFields();
// 3、遍历每个成员变量。
for (Field field : fields) {
// 4、拿到成员变量的名字
String name = field.getName();
// 5、拿到这个成员变量在对象中的数据。
field.setAccessible(true); // 禁止检查访问控制
String value = field.get(obj)+""; //(String)强转不可用
ps.println(name + "=" + value);
}
ps.close();
}
}
注意注释中的细节。
测试结果图:-----正常运行
注解
自定义注解:
![](https://i-blog.csdnimg.cn/blog_migrate/542008a273c0ade8ac1cfbbb64bcebce.png)
在IDEA中创建注解:----选择Annotation即可。
这里我们准备一个MyTest注解:
package com.itheima.d3_annotation;
public @interface MyTest1 {
//public可以不写
String aaa();
boolean bbb() default true;
String[] ccc();
}
我们准备一个MyTest2注解:
package com.itheima.d3_annotation;
public @interface MyTest2 {
String value(); // 特殊属性
int age() default 23;
}
创建测试类:---注意注释
package com.itheima.d3_annotation;
@MyTest1(aaa="牛魔王",ccc={"HTML", "Java"})
// @MyTest2(value = "孙悟空")
//@MyTest2("孙悟空") 只有一个value可以这样写
//@MyTest2(value = "孙悟空", age = 1000) 加个age就不行,age没有默认值
@MyTest2("孙悟空") //如果age有 默认值,又可以这样写
public class AnnotationTest1 {
@MyTest1(aaa="铁扇公主", bbb=false, ccc={"Python", "前端", "Java"})
public void test1(){
}
public static void main(String[] args) {
//执行main方法
}
}
我们执行main方法后,每个注解会生成一个.class文件,这个文件在哪?
右击我们的模块,找到Open ln ->Explorer,点击它,出现下方图:
点out文件夹,一直找到我们的包,就能看到.class文件:---只截取一部分
我们要用反编译软件编译它:
下面链接是反编译软件。
链接:https://pan.baidu.com/s/1MhyR5wqQHCwHVU84fHvGsw?pwd=8888
提取码:8888
下载完反编译软件后,点击下图的exe文件:
把MyTest1.class文件拖进去就能看到真正的源码了:
总结:
元注解
定义:
代码示例:
元注解写法: ---元注解是修饰注解的注解。
package com.itheima.d3_annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD}) // 当前被修饰的注解只能用在类上,方法上。
@Retention(RetentionPolicy.RUNTIME) // 控制下面的注解一直保留到运行时
public @interface MyTest3 {
}
测试代码:
package com.itheima.d3_annotation;
@MyTest3
public class AnnotationTest2 {
// @MyTest3 不能用在变量上
private String name;
@MyTest3
public void test(){
}
}
注解的解析
如何解析注解:
解析注解案例
需求:
代码示例:
MyTest4类:
package com.itheima.d3_annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4 {
String value();
double aaa() default 100;
String[] bbb();
}
Demo类:
package com.itheima.d3_annotation;
@MyTest4(value = "蜘蛛精", aaa=99.5, bbb = {"至尊宝", "黑马"})
@MyTest3
public class Demo {
@MyTest4(value = "孙悟空", aaa=199.9, bbb = {"紫霞", "牛夫人"})
public void test1(){
}
}
测试类:
package com.itheima.d3_annotation;
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.Arrays;
public class AnnotationTest3 {
@Test
public void parseClass(){
// 1、先得到Class对象
Class c = Demo.class;
// 2、解析类上的注解
// 判断类上是否包含了某个注解
if(c.isAnnotationPresent(MyTest4.class)){
MyTest4 myTest4 =
(MyTest4) c.getDeclaredAnnotation(MyTest4.class);
System.out.println(myTest4.value());
System.out.println(myTest4.aaa());
System.out.println(Arrays.toString(myTest4.bbb()));
}
}
@Test
public void parseMethod() throws Exception {
// 1、先得到Class对象
Class c = Demo.class;
Method m = c.getDeclaredMethod("test1");
// 2、解析方法上的注解
// 判断方法对象上是否包含了某个注解
if(m.isAnnotationPresent(MyTest4.class)){
MyTest4 myTest4 =
(MyTest4) m.getDeclaredAnnotation(MyTest4.class);
System.out.println(myTest4.value());
System.out.println(myTest4.aaa());
System.out.println(Arrays.toString(myTest4.bbb()));
}
}
}
模拟Junit框架
需求:
代码示例:
MyTest类:
package com.itheima.d3_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 MyTest {
}
开始模拟:
package com.itheima.d3_annotation;
import java.lang.reflect.Method;
public class AnnotationTest4 {
// @MyTest
public void test1(){
System.out.println("===test1====");
}
@MyTest
public void test2(){
System.out.println("===test2====");
}
//@MyTest
public void test3(){
System.out.println("===test3====");
}
@MyTest
public void test4(){
System.out.println("===test4====");
}
public static void main(String[] args) throws Exception {
AnnotationTest4 a = new AnnotationTest4();
// 启动程序!
// 1、得到Class对象
Class c = AnnotationTest4.class;
// 2、提取这个类中的全部成员方法
Method[] methods = c.getDeclaredMethods();
// 3、遍历这个数组中的每个方法,看方法上是否存在@MyTest注解,存在
// 触发该方法执行。
for (Method method : methods) {
if(method.isAnnotationPresent(MyTest.class)){
// 说明当前方法上是存在@MyTest,触发当前方法执行。
method.invoke(a);
}
}
}
}
模拟结果: ---完美运行
动态代理
程序为什么需要代理?代理长什么样?
代理的案例图:
如何为Java对象创建一个代理对象?
代码示例:
根据案例图
先创建Star接口---当作中介公司:
package com.itheima.d4_proxy;
public interface Star {
String sing(String name);
void dance();
}
创建BigStar类:-----明星类
package com.itheima.d4_proxy;
public class BigStar implements Star {
private String name;
public BigStar(String name) {
this.name = name;
}
public String sing(String name){
System.out.println(this.name + "正在唱:" + name);
return "谢谢!谢谢!";
}
public void dance(){
System.out.println(this.name + "正在优美的跳舞~~");
}
}
创建代理类:
package com.itheima.d4_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
public static Star createProxy(BigStar bigStar){
/* newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数1:用于指定一个类加载器
参数2:指定生成的代理长什么样子,也就是有哪些方法
参数3:用来指定生成的代理对象要干什么事情
*/
// 主程序:Star starProxy = ProxyUtil.createProxy(s);
// starProxy.sing("好日子") starProxy.dance() 去调用 invoke
Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
new Class[]{Star.class}, new InvocationHandler() {
@Override // 回调方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理对象要做的事情,会在这里写代码
if(method.getName().equals("sing")){
System.out.println("准备话筒,收钱20万");
}else if(method.getName().equals("dance")){
System.out.println("准备场地,收钱1000万");
}
return method.invoke(bigStar, args);
}
});
return starProxy;
}
}
创建测试类:
package com.itheima.d4_proxy;
public class Test {
public static void main(String[] args) {
BigStar s = new BigStar("杨超越");
Star starProxy = ProxyUtil.createProxy(s);
String rs = starProxy.sing("好日子");
System.out.println(rs);
starProxy.dance();
}
}
运行结果图: