JAVA-15 (2025.08.20学习记录)
Junit(属于白盒测试)
Junit的使用
javadoc设置
导入Junit依赖的环境
Calculator类
package Calculator;public class Calculator {public int add(int a, int b) {return a + b;}public int sub(int a, int b) {return a - b;}
}
CalculatorTest类
package com.mm.test01;import Calculator.Calculator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;public class CalculatorTest {//加入@Before后,这个方法会在测试方法执行前先执行//一般在@Before修饰的方法中加入申请资源的代码:申请数据库资源、IO资源、网络资源@Beforepublic void init() {System.out.println("方法执行开始了");}//加入@After后,这个方法会在测试方法执行后执行//一般在@After修饰的方法中加入释放资源的代码:释放数据库资源、IO资源、网络资源@Afterpublic void close() {System.out.println("方法执行结束了");}@Testpublic void testadd() {Calculator cal = new Calculator();int result = cal.add(1, 3);System.out.println(result);//加入断言:判断预测结果与运行结果是否一致Assert.assertEquals(4, result);}@Testpublic void testadd2() {Calculator cal = new Calculator();int result = cal.add(1, 4);System.out.println(result);}
}
注解
自定义注解
Calculator类(使用注解)
package Calculator;import com.mm.anno.MyAnnotation;
import com.mm.anno.MyAnnotation2;//因为定义自定义注解时定义了配置参数,所以必须给配置参数赋值
//如果只有一个参数,且这个参数名字为value,那 value= 可以省略不写
@MyAnnotation({"abc", "def"})
//如果给配置参数设置默认值,那使用的时候无需传值
@MyAnnotation2
public class Calculator {public static void main(String[] args) {}
}
MyAnnotation1
package com.mm.anno;public @interface MyAnnotation {/*** value:成员变量的名字(注意:如果仅有一个成员变量,名字尽量叫value)*String[ ]:成员变量的类型(无参数方法的类型:基本数据类型(8种)、String、枚举、注解、以及以上类型的数组)*/String[] value();
}
MyAnnotation2
package com.mm.anno;public @interface MyAnnotation2 {//给配置参数设置默认值String value() default "abc";
}
MyAnnotation3
package com.mm.anno;public @interface MyAnnotation3
//注解内部是可以不定义配置参数的,内部没有定义配置参数的注解,叫标记
{
}
元注解
Retention
(用于声明被修饰的注解的生命周期)
package com.mm.anno;import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;//使注解只在源文件中有效(即源文件保留,在.class字节码文件中不会保留注解信息)
//@Retention(RetentionPolicy.SOURCE)//反编译查看字节码文件,字节码文件中带有MyAnnotation这个注解
//@Retention(RetentionPolicy.CLASS)//使注解在运行时有效。当运行java程序时,JVM会保留注解,加载在内存中,程序可以通过反射获取该注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {String[] value();
}
Target
package com.mm.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Target;//指定被修饰的注解能用于修饰哪些程序元素
//此处MyAnnotation注解可以用于修饰类、属性、构造器、方法
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface MyAnnotation {String[] value();
}
枚举
自定义枚举类
枚举类:属性限制不变,对象也限制不变
自定义枚举类的上层父类:
Object
注释掉toString方法后,打印对象输出的是地址
package com.mm.enum01;/*** 定义枚举类:季节*/
public class season {//属性使用final修饰不能改变private final String seasonName;//季节名字private final String seasonDesc;//季节描述//用构造器对属性进行赋值操作//构造器私有化,外界不能调用这个构造器,只能Season内部调用private season(String seasonName, String seasonDesc) {this.seasonName = seasonName;this.seasonDesc = seasonDesc;}//提供枚举类的有限的、确定的对象//对象使用final修饰,SPRING不能重新new对象public static final season SPRING = new season("春天", "春暖花开");//额外因素public String getSeasonDesc() {return seasonDesc;}public String getSeasonName() {return seasonName;}@Overridepublic String toString() {return "test{" +"seasonDesc='" + seasonDesc + '\'' +", seasonName='" + seasonName + '\'' +'}';}
}
自定义枚举类改成enum枚举类
用enum关键字定义枚举类
用enum关键字创建的枚举类的上层父类
java.lang.Enum
注释掉toString方法后,打印对象输出的不是地址,而是对象名
package com.mm.enum01;/*** 用enum关键字定义枚举类* enum枚举类要求对象(常量)必须放在最开始的位置*/
public enum season {//多个对象之间用逗号隔开,最后一个用;结束SPRING("春天", "春暖花开"),SUMMER("夏天", "烈日炎炎"),WINTER("冬天", "冰天雪地");//属性使用final修饰不能改变private final String seasonName;//季节名字private final String seasonDesc;//季节描述//用构造器对属性进行赋值操作
//构造器私有化,外界不能调用这个构造器,只能Season内部调用private season(String seasonName, String seasonDesc) {this.seasonName = seasonName;this.seasonDesc = seasonDesc;}//额外因素public String getSeasonDesc() {return seasonDesc;}public String getSeasonName() {return seasonName;}@Overridepublic String toString() {return "test{" +"seasonDesc='" + seasonDesc + '\'' +", seasonName='" + seasonName + '\'' +'}';}
}
枚举类底层没有属性
构造器、toString、get方法都删掉。只留下对象名/常量名 +,/;
package com.mm.enum01;/*** enum枚举类要求对象(常量)必须放在最开始的位置*/
public enum season {//多个对象之间用逗号隔开,最后一个用;结束//这个枚举类底层没有属性,构造器、toString、get方法都删掉SPRING, //原本该写为SPRING(),现在连()都省略了SUMMER,WINTER;
}
用enum关键字创建的枚举类
这个枚举类可以直接拿过来使用,3个常用方法
package com.mm.enum01;public class test {public static void main(String[] args) {//用enum关键字创建的枚举类的上层父类:java.lang.Enum//这个枚举类可以直接拿过来使用//toString:获取对象名season spring = season.SPRING;System.out.println(spring);//values:返回枚举类对象的数组season[] values = season.values();//遍历for (season s : values) {System.out.println(s);}//valueOf:通过对象名获取这个对象//注意:对象的名字必须传正确,否则报异常season spring1 = season.valueOf("SPRING");System.out.println(spring1);}
}
枚举类实现接口
所有枚举对象调用show方法走的都是同一个方法
TestInterface接口
package com.mm.enum01;public interface TestInterface {void show();
}
枚举类season
package com.mm.enum01;//枚举类实现接口
public enum season implements TestInterface {SPRING,SUMMER,WINTER;//需要重写接口内的抽象方法@Overridepublic void show() {System.out.println("重写接口内的抽象方法");}
}
test类
package com.mm.enum01;public class test {public static void main(String[] args) {season spring = season.SPRING;spring.show();season summer = season.SUMMER;summer.show();//所有枚举对象调用show方法走的都是同一个方法}
}
不同枚举对象调用show方法走的是不同方法
仅需重写枚举类
package com.mm.enum01;//枚举类实现接口
public enum season implements TestInterface {SPRING {@Overridepublic void show() {System.out.println("春天");}},SUMMER {@Overridepublic void show() {System.out.println("夏天");}},WINTER {@Overridepublic void show() {System.out.println("冬天");}};
}
枚举的应用
枚举Gender类
package com.mm.enum01;public enum Gender {男,女;
}
Person类
package com.mm.enum01;public class Person {private int age;private String name;private Gender sex;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Gender getSex() {return sex;}public void setSex(Gender sex) {this.sex = sex;}@Overridepublic String toString() {return "Person{" +"age=" + age +", name='" + name + '\'' +", sex='" + sex + '\'' +'}';}
}
test类
package com.mm.enum01;public class Test {public static void main(String[] args) {Person p = new Person();p.setAge(19);p.setName("lili");p.setSex(Gender.男);//传入枚举类对象,在入口处对参数进行了限制System.out.println(p);}
}
test02类
package com.mm.enum01;public class test02 {public static void main(String[] args) {//switch(表达式)表达式返回结果是个等值,等值的类型可以是int、byte、short、char、String、枚举中任意一种类型Gender sex = Gender.男;switch (sex) {case 女:System.out.println("女生");break;case 男:System.out.println("男生");break;}}
}
反射
美团实例
美团接口
package com.mm.Reflect;//接口的制定方:美团外卖
public interface Mtwm {//在线支付功能:void payonline();
}
微信支付类(实现美团接口)
package com.mm.Reflect;public class Wechat implements Mtwm {@Overridepublic void payonline() {//具体实现微信支付功能System.out.println("使用微信支付成功");}
}
支付宝支付类(实现美团接口)
package com.mm.Reflect;public class AliPay implements Mtwm {@Overridepublic void payonline() {//具体的支付宝支付System.out.println("支付宝付款成功");}
}
银行卡支付类
package com.mm.Reflect;public class BankCard implements Mtwm {@Overridepublic void payonline() {//银行卡具体功能System.out.println("银行卡支付成功");}
}
test类(普通写法)
package com.mm.Reflect;public class test {public static void main(String[] args) {//定义一个字符串,模拟前台的支付方式String str = "支付宝";if ("微信".equals(str)) {//为啥不用str.equals("微信")?避免空指针异常//微信支付
// new Wechat().payonline();//被下一句替代pay(new Wechat());}if ("支付宝".equals(str)) {//支付宝支付
// new AliPay().payonline();//被下一句替代pay(new AliPay());}if ("银行卡".equals(str)) {pay(new BankCard());}}//微信支付public static void pay(Wechat wc) {wc.payonline();}//支付宝支付public static void pay(AliPay al) {al.payonline();}//银行卡支付public static void pay(BankCard bk) {bk.payonline();}
}
假设后续还有多种支付方式,添加类和方法太麻烦了
为了提高代码的拓展性---->面向对象特性:多态
方法的形参是接口,具体传入的是接口的实现类的对象--->多态的一种形式
test类(多态)
package com.mm.Reflect;public class test {public static void main(String[] args) {//定义一个字符串,模拟前台的支付方式String str = "支付宝";if ("微信".equals(str)) {//为啥不用str.equals("微信")?避免空指针异常//微信支付
// new Wechat().payonline();//被下一句替代pay(new Wechat());}if ("支付宝".equals(str)) {//支付宝支付
// new AliPay().payonline();//被下一句替代pay(new AliPay());}if ("银行卡".equals(str)) {pay(new BankCard());}}//方法的形参是接口,具体传入的是接口的实现类的对象--->多态的一种形式public static void pay(Mtwm m) {m.payonline();}
}
多态确实可以提高代码的拓展性,但拓展性没有达到最好
上面的if分支,还是需要手动删除或者添加
解决方法:反射机制
test(反射)
package com.mm.Reflect;import java.lang.reflect.Method;public class test {public static void main(String[] args) throws Exception {//定义一个字符串,模拟前台的支付方式//字符串:是微信支付类的全限定路径String str = "com.mm.Reflect.Wechat";//下面的代码就是利用反射:Class cls = Class.forName(str);//cls-->Class类具体的对象,Alipay/微信pay字节码信息Object o = cls.newInstance();Method method = cls.getMethod("payonline");method.invoke(o);}
}
反射(面向对象思维)
获取字节码信息的四种方式
Person类(父类)
package com.mm.Reflect;//作为一个父类
public class Person {//属性private int age;public String name;//方法private void eat() {System.out.println("Person--eat");}public void sleep() {System.out.println("Person--sleep");}
}
test类(获取Person字节码信息)
package com.mm.Reflect;public class test {public static void main(String[] args) throws Exception {//案例:以Person的字节码信息为案例//方式1:通过getClass方式获取Person p = new Person();Class c1 = p.getClass();System.out.println(c1);//方式2:通过内置class属性Class c2 = Person.class;System.out.println(c2);//注意:方式1和方式2不常用//方式3:调用Class类提供的静态方法forName(用得最多)Class c3 = Class.forName("com.mm.Reflect.Person");//方式4:利用类的加载器ClassLoader loader = test.class.getClassLoader();Class c4 = loader.loadClass("com.mm.Reflect.Person");System.out.println(c1 == c2);//TrueSystem.out.println(c1 == c3);//TrueSystem.out.println(c1 == c4);//True}
}
可以作为Class类的实例的种类
Person类(父类)
package com.mm.Reflect;//作为一个父类
public class Person {//属性private int age;public String name;//方法private void eat() {System.out.println("Person--eat");}public void sleep() {System.out.println("Person--sleep");}
}
test类
package com.mm.Reflect;public class test {public static void main(String[] args) throws Exception {//可以作为Class类的实例的种类//1、类Class c1 = Person.class;//2、接口Class c2 = Comparable.class;//3、注解Class c3 = Override.class;//4、数组int[] arr1 = {1, 2, 3};Class c4 = arr1.getClass();int[] arr2 = {5, 6, 7};Class c5 = arr2.getClass();//同一个维度,同一个元素类型,得到的字节码就是同一个System.out.println(c4 == c5);//True//5、基本数据类型Class c6 = int.class;//6、void方法返回值Class c7 = void.class;}
}
获取运行时类的完整结构
Person类
package com.mm.Reflect;import java.io.Serializable;//作为一个父类,实现序列化Serializable的接口
public class Person implements Serializable {//属性private int age;public String name;//方法private void eat() {System.out.println("Person--eat");}public void sleep() {System.out.println("Person--sleep");}
}
Student类(子类)
package com.mm.Reflect;//作为子类,实现自定义接口MyInterface
@MyAnnotation(value = "hello")
public class Student extends Person implements MyInterface {//属性private int sno;//学号double height;//身高(default修饰)protected double weight;public double score;//成绩//方法@MyAnnotation(value = "hiMethod")public String showInfo() {return "我是三好学生";}public String showInfo(int a, int b) {return "重载方法,三好学生";}private void work() {System.out.println("我会工作");}void happy() {System.out.println("happy");}protected int getSno() {return sno;}//构造器public Student() {System.out.println("无参构造器");}public Student(double weight, double height) {this.weight = weight;this.height = height;}private Student(int sno) {this.sno = sno;}Student(int sno, double weight) {//default修饰的构造器this.sno = sno;this.weight = weight;}protected Student(int sno, double height, double weight) {this.sno = sno;}@Override@MyAnnotation(value = "hellomyMethod")public void myMethod() {System.out.println("重写的myMethod方法");}@Overridepublic String toString() {return "Student{" +"height=" + height +", sno=" + sno +", weight=" + weight +", score=" + score +'}';}
}
MyAnnotation接口
package com.mm.Reflect;import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.PARAMETER;@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {String value();//属性
}
获取构造器和创建对象
package com.mm.Reflect;import java.lang.reflect.Constructor;public class test {public static void main(String[] args) throws Exception {//获取字节码信息Class cls = Student.class;//通过字节码信息获取构造器//getConstructors只能获取当前运行时类的被public修饰的构造器,返回值是一个数组Constructor[] c1 = cls.getConstructors();for (Constructor c : c1) {System.out.println(c);}//getDeclaredConstructors能获取当前运行时类的全部修饰符的构造器,返回值是一个数组Constructor[] c2 = cls.getDeclaredConstructors();for (Constructor c : c2) {System.out.println(c);}//获取指定的构造器//不传参数,得到空构造器Constructor con1 = cls.getConstructor();System.out.println(con1);//获取两个参数的有参构造器Constructor con2 = cls.getConstructor(double.class, double.class);//getDeclaredConstructor:获取private修饰的有参构造器Constructor con3 = cls.getDeclaredConstructor(int.class);System.out.println(con3);//创建对象Object o1 = con1.newInstance();System.out.println(o1);Object o2 = con2.newInstance(180.5, 170.5);System.out.println(o2);}
}
获取属性和对属性进行赋值
package com.mm.Reflect;import java.lang.reflect.Field;
import java.lang.reflect.Modifier;public class test {public static void main(String[] args) throws Exception {//获取字节码信息Class cls = Student.class;//通过字节码信息获取属性//getFields:获取运行时类和其父类中被public修饰的属性,返回值是一个数组Field[] fields = cls.getFields();for (Field f : fields) {System.out.println(f);}//getDeclaredFields:获取当前运行时类的全部修饰符的属性Field[] declaredFields = cls.getDeclaredFields();for (Field f : declaredFields) {System.out.println(f);}//getField:获取指定的public修饰的属性Field score = cls.getField("score");System.out.println(score);//getDeclaredField:获取指定的private修饰的属性Field sno = cls.getDeclaredField("sno");System.out.println(sno);//获取属性的具体结构//获取属性的修饰符:返回结果是修饰符对应的数字int modifiers = sno.getModifiers();
// System.out.println(modifiers);//2//将对应数字转为修饰符System.out.println(Modifier.toString(modifiers));//获取属性的数据类型:Class type = sno.getType();System.out.println(type);System.out.println(type.getName());//获取属性的名字:String name = sno.getName();System.out.println(name);//给属性赋值Field sco = cls.getField("score");//创建一个cls类的obj对象(给属性赋值必须有对象)Object obj = cls.newInstance();//给o2对象设置sco属性,值为98sco.set(obj, 98);System.out.println(obj);}
}
Student类
package com.mm.Reflect;//作为子类,实现自定义接口MyInterface
@MyAnnotation(value = "hello")
public class Student extends Person implements MyInterface {//属性private int sno;//学号double height;//身高(default修饰)protected double weight;public double score;//成绩//方法@MyAnnotation(value = "hiMethod")public String showInfo() {return "我是三好学生";}public String showInfo(int a, int b) {return "有参数的方法,三好学生";}private void work() {System.out.println("我会工作");}void happy() {System.out.println("happy");}protected int getSno() {return sno;}//构造器public Student() {System.out.println("无参构造器");}public Student(double weight, double height) {this.weight = weight;this.height = height;}private Student(int sno) {this.sno = sno;}Student(int sno, double weight) {//default修饰的构造器this.sno = sno;this.weight = weight;}protected Student(int sno, double height, double weight) {this.sno = sno;}@Override@MyAnnotation(value = "hellomyMethod")public void myMethod() throws RuntimeException {System.out.println("重写的myMethod方法");}@Overridepublic String toString() {return "Student{" +"height=" + height +", sno=" + sno +", weight=" + weight +", score=" + score +'}';}
}
获取方法和调用方法
test类
package com.mm.Reflect;import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;public class test {public static void main(String[] args) throws Exception {//获取字节码信息Class cls = Student.class;//通过字节码信息获取方法//getMethods:获取运行时类和其所以父类中被public修饰的方法Method[] methods = cls.getMethods();for (Method m : methods) {System.out.println(m);}//getDeclaredMethods:获取当前运行时类的全部修饰符的方法Method[] declaredMethods = cls.getDeclaredMethods();for (Method m : declaredMethods) {System.out.println(m);}//获取指定方法//获取被public修饰的无参数指定方法Method method1 = cls.getMethod("showInfo");System.out.println(method1);//获取被public修饰的有参数指定方法Method method2 = cls.getMethod("showInfo", int.class, int.class);System.out.println(method2);//获取被private修饰的指定方法Method method3 = cls.getDeclaredMethod("work");System.out.println(method3);//获取方法的具体结构//名字System.out.println(method1.getName());//修饰符//返回结果是修饰符对应的数字int modifiers = method1.getModifiers();
// System.out.println(modifiers);//1System.out.println(Modifier.toString(modifiers));//返回值Class returnType = method3.getReturnType();System.out.println(returnType);//获取参数列表,返回值是一个列表Parameter[] parameters = method3.getParameters();for (Parameter p : parameters) {System.out.println(p);//此处无输出内容,因为无参数}//注解Method myMethod = cls.getMethod("myMethod");Annotation[] annotations = myMethod.getAnnotations();for (Annotation a : annotations) {System.out.println(a);//只能获取一个注解,因为@Override注解的生命周期只在编译期}//获取异常,返回值是一个列表Class[] exceptionTypes = myMethod.getExceptionTypes();for (Class c : exceptionTypes) {System.out.println(c);}//调用方法Object o2 = cls.newInstance();//调用o2对象的myMethod方法System.out.println(myMethod.invoke(o2));System.out.println(method2.invoke(o2, 12, 25));}
}
获取类的接口,所在包,注解
test类
package com.mm.Reflect;import java.lang.annotation.Annotation;public class test {public static void main(String[] args) throws Exception {//获取字节码信息Class cls = Student.class;//通过字节码信息获取接口Class[] interfaces = cls.getInterfaces();for (Class c : interfaces) {System.out.println(c);}//获取父类接口:// 先得到父类的字节码信息Class superclass = cls.getSuperclass();// 再得到父类接口Class[] interfaces2 = superclass.getInterfaces();for (Class c2 : interfaces2) {System.out.println(c2);}//获取类所在类的包Package aPackage = cls.getPackage();System.out.println(aPackage);//package com.mm.ReflectSystem.out.println(aPackage.getName());//com.mm.Reflect//获取类的注解Annotation[] annotations = cls.getAnnotations();for (Annotation a : annotations) {System.out.println(a);}}
}