Junit,反射,枚举(小白)(供个人学习使用)

Junit单元测试

概述

Junit是一种Java写的单元测试框架,专门用来测试java程序,它可以取代main方法

Junit环境搭建

由于Junit使用的类不属于我们JDK的类库,需要引入第三方jar包,才能使用
junit需要的jar包,一个是核心jar包,一个是依赖jar包

链接:https://pan.baidu.com/s/15qs3IAKJzT7HHBXsW-icpg?pwd=0pry
提取码:0pry
拷贝jar包到工程下新建的lib文件夹里,lib专门用来存放工程中的各种jar包
在这里插入图片描述
拷贝过来之后,按ctrl+alt+shift+s,打开工程的结构,找到modules,点击对应的module,点击dependencies,再点击+号,选择library,再选择java
在这里插入图片描述
找到Lib,选择两个jar包
在这里插入图片描述
然后接着apply ok 就行了

Junit的使用

只需要在测试方法上添加一个注解@Test
原先只有在main方法才能运行,现在可以只用@Test注解就可以了
注意上面import的是org.junit.Test

public class JunitDemo01 {
	/*	public static void main(String[] args) {
			int[] arr = {3, 5, 7};
			for (int i : arr) {
				System.out.println(i);
			}
		}*/
	@Test
	public void method01() {
		int[] arr = {3, 5, 7};
		for (int i : arr) {
			System.out.println(i);
		}
	}
}	

此时运行发现控制台的视图不一样了
在这里插入图片描述
我们发现:

  • 我们在不同的方法加上@Test,每个方法都可以独立运行
  • 如果在类上运行,那么会测试该类的所有方法,如果有一个方法测试失败,那么该类就测试失败
  • 各个方法独立测试,相互之间不干扰

Junit注意事项

@Test 只能用在基于一下模板定义的方法

public void 方法名(){
}
  1. 权限修饰符必须是public
  2. 返回值类型必须是void
  3. 形参列表必须为空
  4. 该方法不能是静态方法
    在这里插入图片描述

解决方案

先定义一个可以使用@Test注解方法,然后去调用不能使用@Test注解方法来达到测试目的

public class JunitDemo03 {
	@Test
	public void testMethod01() {
		//System.out.println("abc");
		method01();
		System.out.println(method02());
	}

	@Test
	public void testMethod02() {
		method03(15);
	}

	@Test
	public void testMethod03() {
		JunitDemo03.method04();
		method04();
	}

	private void method01() {
		System.out.println("私有方法");
	}


	public int method02() {
		return 3;
	}


	public void method03(int i) {
		System.out.println(i);
	}


	public static void method04() {
		System.out.println("静态方法");
	}
}

反射

字节码对象

对于一个.java文件,首先,Javac.exe编译HelloWorld.java生成HelloWorld.class(字节码文件),然后java.exe执行HelloWorld.class,由于要使用HelloWorld这个类,所以JVM会将这个类加载到内存中。在内存的方法区中存储HelloWorld类中的定义信息,同时会在堆中生成字节码对象HelloWorld.class封装类中的信息
请添加图片描述所以当JVM用到一个类的时候,会把这个类加载到内存中,首先在方法区存放类的定义信息,还要在堆内存中创建一个字节码对象和类文件(字节码文件) 一一对应

Class类

请添加图片描述
由于所有的类都能定义构造方法,成员变量和成员方法,因此java专门定义一个Class类,这个类中提供了获取类信息的一套方法
为了更便于操作类中每种信息,java又分别定义了三个类,来更详细获取每种信息
构造方法信息–》Constructor
成员变量信息–》Field
成员方法信息–》Method

反射概述

反射其实就是解剖一个类,获取类中的各项信息(构造方法,成员变量,成员方法)
类比:

  • 法医解剖人的尸体,人体内又很多器官(心肝脾肺肾),法医获取这些器官
  • 程序员解剖一个类,类种有构造方法信息,成员变量信息,成员方法信息,程序员获取这些信息

获取字节码对象的三种方式

反射的第一步是获取字节码对象,因为只有获取到字节码对象,才能进一步获取类中的信息
1.类名.class代表一个字节码对象

public class ReflectDemo1 {
    @Test
    public void testMethod01(){
        System.out.println(Person.class);
    }
}

2.通过Class类的方法,所有的字节码对象都是Class类的实例
static Class<?> forName(String className),返回与带有给定字符串名的类或接口相关联的 Class 对象。

@Test
    public void testMethod02() throws ClassNotFoundException {
        Class p = Class.forName("reflect01.Person");
        System.out.println(p);
    }

3.通过Objec类中的getClass()方法获取该类的字节码对象
Class<?> getClass()返回此Object的运行时类

@Test
    public void testMethod03(){
        Person p =new Person();
        System.out.println(p.getClass());
    }

注意:无论采用哪种方式获取该类的字节码对象,都是获取的同一个字节码对象

@Test
    public void testMethod04() throws ClassNotFoundException {
        Class p1 = Person.class;
        Class p2 = Class.forName("reflect01.Person");
        Class p3 = new Person().getClass();
        System.out.println(p1 == p2);//true
        System.out.println(p1 == p3);//true
        System.out.println(p2 == p3);//true
    }

反射构造方法

反射空参构造方法

Class类中成员方法:
Constructor getConstructor(Class<?>… parameterTypes),此处的可以用任意参数
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定==公有(public)==构造方法。
<T>表示当前字节码对象代表的类

public class ReflectDemo01 {
    @Test
    public void testMethod01() throws Exception{
        //1.获取字节码对象
        Class<Person> pCLs = Person.class;
        //2.获取构造方法
        Constructor<Person> cons = pCLs.getConstructor();
    }
}

由于获取的时候空参构造,所以不需要传递任何参数,因此使用getConstructor()方法也不需要传递参数
Constructor类中的方法
String getName(),以字符串形式返回此构造方法的名称。
T newInstance(Object… initargs),使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例

//3.获取构造方法的信息
System.out.println(cons.getName());//reflect01.Person
//4.利用构造方法创建对象
Person p = cons.newInstance();//利用空参构造创建对象
                              //类比:Person p=new Person();
System.out.println(p);//reflect01.Person@32a1bec0
System.out.println(new Person());//reflect01.Person@22927a81

同时,对于用字节码对象获取空参构造方法,还可以用Class中的方法
T newInstance(),只能利用该类的空参构造创建对象,不能利用有参构造创建对象

    @Test
    public void testMethod02() throws InstantiationException, IllegalAccessException {
        //1.获取字节码对象
        Class<Person> pCLs = Person.class;
        //2.直接利用Class类的newInstance()方法创建该类实例
        Person p = pCLs.newInstance();//底层相当于:Person p=new Person();
        System.out.println(p);
    }

反射有参构造方法

  • Class<T>:T代表字节码对象所表示的类
  • 基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
  • 只需要将基本类型后面带上.class就表示Class的实例
    @Test
    public void testMethod03() throws Exception{
        Constructor<Person> cons = Person.class.getConstructor(String.class, int.class);
    //  Person p = cons.newInstance();//运行失败
    //  System.out.println(p);
        Person p2 = cons.newInstance("老王", 30);
        System.out.println(p2);
    }

当获取私有的构造方法时

    @Test
    public void testMethod04() throws Exception{
        //1.获取字节码对象
        Class<Person> pCls = Person.class;
        //2.获取构造方法
        Constructor<Person> cons = pCls.getConstructor(String.class);
        Person p = cons.newInstance("老王");
        System.out.println(p);
    }

再用getConstructor会报错
此时我们可以使用暴力反射来获取类中的私有属性

  • Constructor getDeclaredConstructor(Class<?>… parameterTypes)
  • 获取类中声明的任意一个构造方法
    注意使用时需要先取消权限检查
    @Test
    public void testMethod04() throws Exception{
        //1.获取字节码对象
        Class<Person> pCls = Person.class;
        //2.获取构造方法
        //Constructor<Person> cons = pCls.getConstructor(String.class);
        Constructor<Person> cons = pCls.getDeclaredConstructor(String.class);
        //3.由于是将要利用私有构造方法创建对象,在类外无法调用私有构造方法,但是我们可以让java取消权限检查
        cons.setAccessible(true);
        Person p = cons.newInstance("老王");
        System.out.println(p);
    }

反射字段

Class类中的成员方法

  • Field getField(String name)
  • 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定public修饰的成员字段。
    因为Class.forName返回的时通配符而不是Person,所以需要强转
        //1.获取Person类的字节码对象
        //因为Class.forName返回的时通配符而不是Person,所以需要强转
        Class<Person> pCls = (Class<Person>) Class.forName("reflect01.Person");
        //2.获取字段信息
        Field f = pCls.getField("name");
        System.out.println(f);

Field类中的成员方法:

  • Object get(Object obj)
  • 返回指定对象上此 Field 表示的字段的值。
    由于成员变量随着对象的存在而存在,所以必须指定获取哪个对象
        //3.获取该字段的值
        Person p = pCls.newInstance();
        //由于成员变量随着对象的存在而存在,所以必须指定获取哪个对象
        System.out.println(f.get(p));

Field getDeclaredField(String name)

  • 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段
@Test
    public void testMethod02() throws Exception{
        //1.获取Person类的字节码对象
        Class<Person> pCls = (Class<Person>) Class.forName("reflect01.Person");
        //2.利用getDeclaredField方法获取非public修饰的字段
        Field f = pCls.getDeclaredField("age");
        //3.通过setAccessiable设置java的访问权限检查
        f.setAccessible(true);
        //4.获取当前field字段的值
        Person p = pCls.newInstance();
        System.out.println(f.get(p));
    }

反射方法

Class类中的成员方法:

  • Method getMethod(String name, Class<?>… parameterTypes)
    • 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
      Method类的方法:
  • Object invoke(Object obj, Object… args)
    • 形参:
    • Object obj:代表该类的一个对象
    • args:代表传递给方法的实参
    • 返回值:
    • 代表调用的方法的返回值
    • 如果该方法没有返回值,那么invoke返回null
    @Test
    public void testMethod01()throws Exception{
        Class<Person> pCls =(Class<Person>) Class.forName("reflect01.Person");
        Method sleepMethod = pCls.getMethod("sleep");
        //通过invoke调用该方法
        Person p = pCls.newInstance();
        System.out.println(sleepMethod.invoke(p));
    }
    @Test
    public void testMethod02()throws Exception{
        Class<Person> pCls =(Class<Person>) Class.forName("reflect01.Person");
        Method sleepMethod = pCls.getMethod("eat", String.class);
        //通过invoke调用该方法
        Person p = pCls.newInstance();
        Object food =  sleepMethod.invoke(p,"面条");
        System.out.println(food);
    }

枚举

枚举概述

其实就是一一列举,如果某些变量的取值是固定几个值中其中一个,我们就考虑顶定义枚举
例如:星期(一-日)月份(1-12月)性别(男,女)

模拟java中的枚举(枚举的内部原理)

首先,我们定义一个类用作枚举

public class Week {
    Week monday=new Week();
    Week tuesday=new Week();
    Week wednesday=new Week();
    Week thursday=new Week();
    Week friday=new Week();
    Week saturday=new Week();
    Week sunday=new Week();
}

我们发现这样定义,我们依然可以在其他类中去Week week=new Week()不符合我们固定几个值的含义了,因此我们需要把Week类的构造器私有化
但是这样我们在其他类中调用还是需要构造新对象,为了能够不构造对象就能使用,我们需要在Week类中的变量加static变为常量,同时我们需要加上final来确保不会被改变值

public class Week {
    private Week(){}
    public static final Week MONDAY=new Week();
    public static final Week TUESDAY=new Week();
    public static final Week WEDNESDAY=new Week();
    public static final Week THURSDAY=new Week();
    public static final Week FRIDAY=new Week();
    public static final Week SATURDAY=new Week();
    public static final Week SUNDAY=new Week();
}

此时即可调用

public class WeekDemo {
    @Test
    public void testMethod01(){
        System.out.println(Week.FRIDAY);
    }
}

但最后发现运行结果为enum01.test.Week@573fd745。这是一个地址,原因在于print的Week.FRIDAY是一个对象,直接打印对象会打印它的toString方法,若toString方法没有重写,就是打印地址值
为此,我们需要重写Week中的toString方法,直接打印return ”星期()"不可取,这样就固定住了。
**我们可以借助每个对象名不一样,来在Week中创建一个变量来对应对象名,那么就可以toString打印这个和对象名一样的变量了,**我们发现可以借助构造器来将改变变量名

private String weekName;
private Week(String name){
  weekName=name;
}

此时再去调用

public class Week {
    private Week(){}
    private String weekName;
    private Week(String name){
        weekName=name;
    }
    public static final Week MONDAY = new Week("星期一");
    public static final Week TUESDAY = new Week("星期二");
    public static final Week WEDNESDAY = new Week("星期三");
    public static final Week THURSDAY = new Week("星期四");
    public static final Week FRIDAY = new Week("星期五");
    public static final Week SATURDAY = new Week("星期六");
    public static final Week SUNDAY = new Week("星期日");

    @Override
    public String toString() {
        return weekName;
    }
}
public class WeekDemo {
    @Test
    public void testMethod(){
        System.out.println(Week.SATURDAY);
    }
}

打印“星期六”

利用enum创建枚举

java中利用enum关键字定义枚举
格式:
权限修饰符 enum 枚举名称{
枚举常量1,枚举常量2,枚举常量3,…;
}
Java中利用enum定义的枚举的原理和我们自己模拟的枚举原理相同

public enum Week {
    MONDAY,TUESDAY,WEDNESDAY,THURSDAY,
    FRIDAY,SATURDAY,SUNDAY;
}

此时

public class WeekDemo {
    @Test
    public void testMethod(){
        System.out.println(Week.TUESDAY);
    }
}

会直接打印TUESDAY
若我们想改为汉字,可根据枚举的原理,自己定义一个变量和构造器,再改写一下toString方法
MONDAY(“星期一”)相当于:public static final Week MONDAY = new Week(“星期一”);

public enum Week {
    MONDAY("星期一"),TUESDAY("星期二"),
    WEDNESDAY("星期三"),THURSDAY("星期四"),
    FRIDAY("星期五"),SATURDAY("星期六"),SUNDAY("星期日");
    private String weekName;
    private Week(){
    }
    private Week(String name){
        weekName=name;
    }

    @Override
    public String toString() {
        return weekName;
    }
}

此时再次调用,就会打印汉字

枚举中的常用方法

Java中所有用enum定义的枚举默认会继承一个类:java.lang.Enum
Class类中的方法:
Class<? super T> getSuperclass()
获取该字节对象的父字节码对象
Method[] getMethods()
获取该字节码对象中所有public修饰的成员方法

枚举中的常用方法:
public static Week[] values()
获取所有的枚举常量,并把所有的枚举常量封装到一个数组返回
public static Week valueOf(String str)
public final String name()返回此枚举常量的名称,在其枚举声明中对其进行声明

    @org.junit.Test
    public void testMethod01(){
        System.out.println(Week.class.getSuperclass());
    }

打印class java.lang.Enum

    @org.junit.Test
    public void testMethod02(){
        Method[] methods = Week.class.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }

打印
在这里插入图片描述

    @org.junit.Test
    public void testMethod03(){
        Week[] values = Week.values();
        for (Week week : values) {
            System.out.println(week);
        }
    }

打印
MONDAY
TUESDAY
WEDNESDAY
THURSDAY
FRIDAY
SATURDAY
SUNDAY

    @org.junit.Test
    public void testMethod04(){
        System.out.println(Week.valueOf("THURSDAY"));
        System.out.println(Week.valueOf("THURSDAY")==Week.THURSDAY);
    }

我们发现Week.valueOf(“THURSDAY”)和Week,THURSDAY是等价的
另外

    @org.junit.Test
    public void testMethod05(){
        System.out.println(Week.SUNDAY.name());
        System.out.println(Week.SUNDAY);
        System.out.println(Week.SUNDAY.toString());
    }

这三者也是一样的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值