Junit单元测试
没有Junit情况下如何测试
Junit的使用
public class CalculatorTest {
//测试add方法
@Test//这是注解
public void testAdd(){
System.out.println("测试add方法:");
Calculator cal=new Calculator();
int add = cal.add(10, 30);
System.out.println(add);
}
//测试minus方法
@Test
public void testMinus(){
System.out.println("测试minus方法:");
Calculator cal=new Calculator();
int result = cal.minus(10, 30);
System.out.println(result);
}
}
//测试add方法
@Test//这是注解
public void testAdd(){
System.out.println("测试add方法:");
Calculator cal=new Calculator();
int result = cal.add(10, 30);
//System.out.println(result);------>程序的运行结果可以不关注
//加入断言:预测一些结果,判断一下我预测的结果与实际结果是否一致
Assert.assertEquals(40,result);//第一个参数是:预测结果,第二个参数是:实际结果
}
long类型可以接int类型的数
@Before;@After
package com.msb.test;
import nojunit.Calculator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* @Auther:sky
* @Date:2022/3/16-03-16-16:06
* @Description:test
* @Version:1.0
*/
public class CalculatorTest {
@Before
public void init(){
System.out.println("方法测试开始了");
}
@After
public void close(){
System.out.println("方法测试结束了");
}
//测试add方法
@Test
public void testAdd(){
System.out.println("测试add方法:");
Calculator cal=new Calculator();
int result = cal.add(10, 30);
//System.out.println(result);------>程序的运行结果可以不关注
//加入断言:预测一些结果,判断一下我预测的结果与实际结果是否一致
Assert.assertEquals(40,result);
}
//测试minus方法
@Test
public void testMinus(){
System.out.println("测试minus方法:");
Calculator cal=new Calculator();
int result = cal.minus(10, 30);
System.out.println(result);
}
}
注解 Annotation
注解的实例:文档注解
/**
文档注释
@version 1.0//这个就叫文档注解
@author sky
*/
package anno;
/**
* @author : sky
* @version : 1.0
*/
public class Person {
/**
* 下面是eat方法,实现了XXX功能
* @param num1 int 就餐人数
* @param num2 int 点了几个菜
*/
public void eat(int num1,int num2){
}
/**
*
* @param age int 年龄
* @return int
* @exception RuntimeException 当年龄过大时
* @exception IndexOutOfBoundsException 当年龄过小时
* @see Student
*/
public int sleep(int age){
new Student();
if(age>100){
throw new RuntimeException();
}
if(age<0){
throw new IndexOutOfBoundsException();
}
return age;
}
}
JDK内置的3个注解
public class Student extends Person{
/*@Override的作用:限定重写的方法,只要重写的方法有问题,就有错误提示*/
@Override
public void eat(int num1, int num2) {
super.eat(num1, num2);
}
/*在方法前加入@Deprecated,这个方法就会变成一个废弃方法/过期方法/过时方法*/
@Deprecated
public void study(){
System.out.println("study");
}
@SuppressWarnings("unused")//压制未使用警告
int age=10;
int num=10;
@SuppressWarnings({"unused","rwatypes"})//压制警告泛型
ArrayList al=new ArrayList();
}
实现替代配置文件功能的注解
学高级课程再看
自定义注解
元注解
Retention:声明注解生命周期
RetentionPolicy.SOURCE
RetentionPolicy.CLASS:如果没有加Retention注解,那么默认为.CLASS状态
RetentionPolicy.RUNTIME
反射再学
Target:声明可以修饰什么
Documented(了解即可):提取到API
Inherited(极少):继承
枚举
自定义枚举类:JDK1.5之前
public class Season {
//属性:
private final String seasonName;//季节名字
private final String seasonDesc;//季节描述
//利用构造器对属性进行赋值
//构造器私有化,外界不能调用,只能在season内部调用
private Season(String seasonName,String seasonDesc) {
this.seasonName=seasonName;
this.seasonDesc=seasonDesc;
}
//提供枚举类的有限的,确定的对象
public static final Season SPRING =new Season("春天","春暖花开");
public static final Season SUMMER =new Season("夏天","烈日炎炎");
public static final Season AUTUMN =new Season("秋天","硕果累累");
public static final Season WINTER =new Season("冬天","冰天雪地");
//额外因素:
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
public class TestSeason {
public static void main(String[] args) {
Season autumn = Season.AUTUMN;
System.out.println(autumn);
System.out.println(autumn.getSeasonDesc());
System.out.println(autumn.getSeasonName());
}
}
JDK1.5之后使用enum关键字来创建枚举类
变为下面的枚举类
public enum Season {
//enum枚举类要求对象(常量)必须放在最开始的位置
//提供枚举类的有限的,确定的对象
//多个对象之间用逗号进行链接,最后一个用分号完结
SPRING ("春天","春暖花开"),
SUMMER ("夏天","烈日炎炎"),
AUTUMN ("秋天","硕果累累"),
WINTER("冬天","冰天雪地");
//属性:
private final String seasonName;//季节名字
private final String seasonDesc;//季节描述
//利用构造器对属性进行赋值
//构造器私有化,外界不能调用,只能在season内部调用
private Season(String seasonName, String seasonDesc) {
this.seasonName=seasonName;
this.seasonDesc=seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
public class TestSeason {
public static void main(String[] args) {
Season winter = Season.WINTER;
System.out.println(winter);//WINTER
//enum关键字对应的枚举类的上层父类是:java.lang.Enum
//我们自定义的枚举类的父类是Object
System.out.println(Season.class.getSuperclass().getName());//java.lang.Enum
}
}
在源码中经常看到别人定义的枚举类的形态:
public enum Season {
SPRING,
SUMMER,
AUTUMN,
WINTER;
}
Enum类的常用方法
public class TestSeason {
public static void main(String[] args) {
//用enum关键字创建的seaseon枚举类的父类是:java.lang.Enum,
//常用方法子类season可以直接拿来使用:
//1.toSting:----->获取对象名字
Season autumn = Season.AUTUMN;
System.out.println(autumn);
//2.values:----》返回枚举类对象的数组
Season[] values = Season.values();
for(Season s:values){
System.out.println(s);
}
//3.valueOf---->通过对象名字获取枚举对象
//注意:对象的名字必须传正确,否则抛出异常
Season autumn1 = Season.valueOf("AUTUMN");
System.out.println(autumn1);
}
}
枚举类实现接口
接口:
public interface MeiJu {
void show();
}
枚举类:
public enum Season implements MeiJu {
SPRING,
SUMMER,
AUTUMN,
WINTER;
@Override
public void show() {
System.out.println("season");
}
}
测试:
public class Deno {
public static void main(String[] args) {
Season autumn = Season.AUTUMN;
autumn.show();
Season summer= Season.SUMMER;
summer.show();
}
}
修改枚举类:
public enum Season implements MeiJu {
SPRING{
@Override
public void show() {
System.out.println("spring");
}
},
SUMMER{
@Override
public void show() {
System.out.println("summer");
}
},
AUTUMN{
@Override
public void show() {
System.out.println("autumn");
}
},
WINTER{
@Override
public void show() {
System.out.println("winter");
}
}
}
运行结果:
实际应用
public enum Gender {
男,
女;
}
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;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", sex=" + sex +
'}';
}
}
public class Test {
public static void main(String[] args) {
Person p=new Person();
p.setAge(19);
p.setName("sky");
p.setSex(Gender.女);//传入枚举类gender的对象;----》在入口处对参数进行了限制
System.out.println(p);
}
}
枚举还可以通过结合switch来处理
public class Test01 {
public static void main(String[] args) {
Gender sex=Gender.男;
//switch后面的()可以传入枚举类型
//switch后面的()可以传入int,short,byte,char,string,枚举;
// float double,long,boolean都不可以
switch(sex){
case 女:
System.out.println("girl");
break;
case 男:
System.out.println("boy");
break;
}
}
}
反射
案例
美团外卖—》付款—》要么使用微信要么使用支付宝支付
//接口的制定方:美团外卖
public interface MeiTuan {
//在线支付功能:
public abstract void payOnline();
}
微信
public class WeChat implements MeiTuan{
@Override
public void payOnline() {
//具体实现微信支付的功能
System.out.println("我已经点了外卖,正在使用微信支付");
}
}
支付宝
public class AliPay implements MeiTuan{
@Override
public void payOnline() {
//具体的支付宝的支付
System.out.println("我已经点了外卖,正在使用支付宝支付");
}
}
银行卡
public class BankCard implements MeiTuan{
@Override
public void payOnline() {
System.out.println("点外卖,银行卡支付");
}
}
实现
public class Test {
public static void main(String[] args) {
//定义一个字符串,用来模拟前台的支付方式:
String str="银行卡";
if("微信".equals(str)){//不用str.equals("微信")这个,是为了避免空指针异常
//微信支付:
//WeChat weChat = new WeChat();
// weChat.payOnline();
pay(new WeChat());
}
if("支付宝".equals(str)){
//支付宝支付
//AliPay aliPay = new AliPay();
//aliPay.payOnline();
pay(new AliPay());
}
if("银行卡".equals(str)){
//new BankCard().payOnline();
pay(new BankCard());
}
}
public static void pay(WeChat wc){
wc.payOnline();
}
//重载
public static void pay(AliPay ap){
ap.payOnline();
}
public static void pay(BankCard bc){
bc.payOnline();
}
}
为了提高代码的扩展性------》面向对象的特性–》多态
public class Test {
public static void main(String[] args) {
//定义一个字符串,用来模拟前台的支付方式:
String str="微信";
if("微信".equals(str)){//不用str.equals("微信")这个,是为了避免空指针异常
//微信支付:
pay(new WeChat());
}
if("支付宝".equals(str)){
//支付宝支付
pay(new AliPay());
}
if("银行卡".equals(str)){
pay(new BankCard());
}
}
//方法形参是接口,具体传入的是接口的实现类的对象-----》多态的一种形式
public static void pay(MeiTuan mt){
mt.payOnline();
}
}
多态确实可以提高代码的扩展性,但是,扩展性并没有达到最好。
怎么没有达到最好?上面的分支还是需要手动的删除和添加
解决办法:反射机制
利用反射实现上述功能:
public class Demo {
public static void main(String[] args) throws Exception {
//利用反射实现上述功能
//定义一个字符串,用来模拟前台的支付方式:
String str="fanshe.BankCard";//字符串:实际上就是微信类的全限定路径
//下面的代码就是利用反射
Class cls = Class.forName(str);//cls---》Class类的具体的对象---》Alipay字节码信息
Object o = cls.newInstance();
Method method = cls.getMethod("payOnline");
method.invoke(o);
}
}
其他代码和没用反射前一样
Class类
具体实例向上抽取形成对象--------》类本身就是对象
获取字节码信息的四种方式
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<Person> c2 = Person.class;
System.out.println(c2);
System.out.println(c1==c2);//true;获取的是一个字节码信息
//注意:方式1与方式2不常用
//方式3.最常用;通过调用Class类提供的静态方法forName()获取
Class c3=Class.forName("fanbao.Person");//里面传一个person类的全限定路径
//方式4.了解即可;利用类的加载器
ClassLoader loader = Test.class.getClassLoader();//系统类加载器
Class<?> c4 = loader.loadClass("fanbao.Person");
}
}
Class类的具体实例
public class Demo {
public static void main(String[] args) {
//1.类
Class personClass = Person.class;
//2.接口
Class comparableClass = Comparable.class;
//3.注解
Class overrideClass = Override.class;
//4.数组
int[] arry1={1,2,3};
Class aClass = arry1.getClass();
int[] arry2={4,5,6};
Class aClass1 = arry2.getClass();
System.out.println(aClass==aClass1);//true;同一个维度,同一个元素类型,得到的字节码就是同一个
//5.基本数据类型
Class integerClass = int.class;
//6.void
Class voidClass = void.class;
}
}
获取运行时类的完整结构
//作为一个父类
public class Person implements Serializable {
//属性
private int age;
public String name;
//方法
private void eat(){
System.out.println("eat---------");
}
public void sleep(){
System.out.println("sleep------------");
}
}
//作为一个子类
@MyAnno("hello")
public class Student extends Person implements MyInterface{
//属性
private int sno;
double hight;
protected double weight;
public double score;
//方法
@MyAnno("method")
public String showInfo(){
return "我是好学生";
}
public String showInfo(int a,int b){
return "重载方法======我是好学生";
}
private void work(){
System.out.println("我是程序媛");
}
void happy(){
System.out.println("做人最重要的就是开心啦!");
}
protected int getSno(){
return sno;
}
//构造器
public Student() {
System.out.println("空参构造器");
}
private Student(int sno){
this.sno=sno;
}
Student(int sno,double weight){
this.sno=sno;
this.weight=weight;
}
protected Student(int sno,double hight,double weight){
this.sno=sno;
}
@Override
@MyAnno("hello my")
public void myMethod() {
}
@Override
public String toString() {
return "Student{" +
"sno=" + sno +
", hight=" + hight +
", weight=" + weight +
", score=" + score +
'}';
}
}
public interface MyInterface {
public abstract void myMethod();
}
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
String value();//属性
}
获取构造器和创建对象
public class Test01 {
public static void main(String[] args) throws Exception {
//获取字节码信息
Class cls= Class.forName("fanbao.Student");
//通过字节码信息获取构造器
//getConstructors()方法只能获取被public修饰的构造器
Constructor[] con1 = cls.getConstructors();
for(Constructor c:con1){
System.out.println(c);//只能获取被public修饰的构造器
}
System.out.println("-----------------------");
//getDeclaredConstructors()获取运行时类的全部修饰符的构造器
Constructor[] con2 = cls.getDeclaredConstructors();
for(Constructor c:con2){
System.out.println(c);//所有构造器都可以获取
}
System.out.println("-----------------------");
//获取指定构造器
//getConstructor()只能得到public修饰的
//getConstructor()什么都不传得到空构造器
Constructor con3 = cls.getConstructor();
System.out.println(con3);
System.out.println("-----------------------");
//传可变参数可以得到指定的构造器
Constructor con4= cls.getConstructor(double.class,double.class);
System.out.println(con4);
System.out.println("-----------------------");
//getDeclaredConstructor可以得到任意修饰的构造器,传参就行
Constructor con5 = cls.getDeclaredConstructor(int.class);
System.out.println(con5);
System.out.println("-----------------------");
//有了构造器之后就可以创建对象了
Object o1 = con3.newInstance();
System.out.println(o1);
Object o2 = con4.newInstance(170.2, 135.6);
System.out.println(o2);
}
}
获取属性和对属性进行赋值
public class Test02 {
public static void main(String[] args) throws Exception {
//获取运行时类的字节码信息
Class cls=Student.class;
//获取属性
//getFields()获取当前运行时类student和父类person中被public修饰的属性
Field[] fields = cls.getFields();
for(Field f:fields){
System.out.println(f);
}
System.out.println("------------------------------");
//getDeclaredFields()获取当前运行时类student的所有属性
Field[] declaredFields = cls.getDeclaredFields();
for(Field f:declaredFields){
System.out.println(f);
}
System.out.println("------------------------------");
//获取指定的属性
Field score = cls.getField("score");
System.out.println(score);
System.out.println("------------------------------");
Field sno = cls.getDeclaredField("sno");
System.out.println(sno);
System.out.println("------------------------------");
//属性的具体结构
//获取属性名字
String name = sno.getName();
System.out.println(name);
System.out.println("------------------------------");
//获取属性数据类型
Class type = sno.getType();
System.out.println(type.getName());
System.out.println("------------------------------");
//获取属性修饰符
int modifiers = sno.getModifiers();//返回的是修饰符对应的数
System.out.println(modifiers);
System.out.println(Modifier.toString(modifiers));//private
System.out.println("------------------------------");
//给属性赋值:给属性赋值必须要有对象
Field sco = cls.getField("score");
Object obj = cls.newInstance();
sco.set(obj,98);//给obj对象的score属性设置具体的值
System.out.println(obj);
}
}
获取方法和调用方法
public class Test03 {
public static void main(String[] args) throws Exception {
//获取字节码信息
Class<Student> cls= Student.class;
//获取方法
//获取被public修饰的运行时类的方法和所有父类的方法
Method[] methods = cls.getMethods();
for(Method m:methods){
System.out.println(m);
}
System.out.println("--------------------------");
//获取运行时类中所有方法,父类不可以
Method[] methods1 = cls.getDeclaredMethods();
for(Method m:methods1){
System.out.println(m);
}
System.out.println("--------------------------");
//获取指定方法
Method showInfo = cls.getMethod("showInfo");
System.out.println(showInfo);
Method showInfo1 = cls.getMethod("showInfo", int.class, int.class);
System.out.println(showInfo1);
//private修饰
Method work = cls.getDeclaredMethod("work",int.class);//注意可能会错
System.out.println(work);
//获取方法的具体结构
//@注解
//修饰符 返回值类型 方法名(参数列表) throws xxxxx{}
//名字
System.out.println(work.getName());
//修饰符
System.out.println(Modifier.toString(work.getModifiers()));
//返回值
System.out.println(work.getReturnType());
//参数列表
Class[] parameterTypes = work.getParameterTypes();
for(Class c:parameterTypes){
System.out.println(c);
}
//注解
Method myMethod = cls.getMethod("myMethod");
Annotation[] annotations = myMethod.getAnnotations();
for(Annotation a:annotations){
System.out.println(a);//只能获取到生命周期是runtime的注解
}
//异常
Class[] exceptionTypes = myMethod.getExceptionTypes();
for(Class c1:exceptionTypes){
System.out.println(c1);
}
//调用方法
Student s = cls.newInstance();
myMethod.invoke(s);//调用s对象的myMethod
System.out.println(showInfo1.invoke(s, 12, 45));
}
}
###获取类的 接口,所在包,注解
public class Test04 {
public static void main(String[] args) {
//获取字节码信息
Class<Student> cls= Student.class;
//获取运行时类的接口
Class<?>[] interfaces = cls.getInterfaces();
for(Class c:interfaces){
System.out.println(c);
}
System.out.println("-----------------------");
//获取父类接口
//先得到父类的字节码信息在得到接口
Class superclass = cls.getSuperclass();
Class[] interfaces1 = superclass.getInterfaces();
for(Class c:interfaces1){
System.out.println(c);
}
System.out.println("-----------------------");
//获取运行时类所在的包
Package aPackage = cls.getPackage();
System.out.println(aPackage);
System.out.println(aPackage.getName());
//获取运行类的注解
Annotation[] annotations = cls.getAnnotations();
for(Annotation a:annotations){
System.out.println(a);
}
}
}
关于反射的面试题
1=什么时候用反射?
需要体现程序的扩展性和动态性时;
2、没有;(操作private时,确实破坏了)
封装是为了提高代码的安全性
反射提出最重要的目的是为了动态性,封装只是不建议使用private修饰的东西