一、Junit单元测试
1、单元测试:可以代替掉程序中的main方法。程序可以从单元测试方法开始执行。
使用:
1. 要在单元测试方法上面加上注解 @Test
2. 以单元测试的方式去运行即可。
要求:
1. 单元测试方法的方法名一般以test开头 (软性规定)
2. 单元测试方法要以public 修饰,没有返回值,没有参数 (硬性规定)
单元测试方法的运行:
1. 如果选中某个单元测试方法,然后右键运行,那么运行的就是这个方法。
2. 如果选中的是某个人,然后右键运行,那么运行的就是这个类中所有的单元测试方法。
public class Demo01Test {
@Test
public void testMethod() {
System.out.println("testMethod...");
}
@Test
public void testFunction() {
System.out.println("testFunction");
}
}
2、单元中其他注解
在单元测试中,还有一些其他注解,可以完成一些功能
@Before:加上此注解的方法,会在每次单元测试方法执行之前执行。
@After: 加上此注解的方法,会在每次单元测试方法执行之后执行。
@BeforeClass: 加上此注解的方法,会在所有单元测试方法执行前执行,终身只执行一次。
@AfterClass: 加上此注解的方法,会在所有单元测试方法执行后执行,终身只执行一次。
注意:
BeforeClass和AfterClass 这两个注解修饰的方法必须是静态方法
public class Demo02Test {
@AfterClass
public static void afterClassMethod() {
System.out.println("afterClassMethod....");
}
@BeforeClass
public static void beforeClassMethod() {
System.out.println("beforeClassMethod...");
}
@Before
public void beforeMethod() {
System.out.println("beforeMethod...");
}
@After
public void afterMethod() {
System.out.println("afterMethod...");
}
@Test
public void testMethod() {
System.out.println("testMethod...");
}
@Test
public void testFunction() {
System.out.println("testFunction...");
}
}
二、反射
1、类的Class对象的三种获取方式
1. 通过对象名.getClass方法获取
Class getClass(): 获取此对象所在类的Class类型的对象.
2. 通过类名.class属性获取
每一个数据类型,都有一个属性,叫做class,这个使用这个属性就可以获取对应的class
3. 通过Class类的静态方法forName进行获取
static Class forName(String className): 参数传递字符串类型的全类名(包含包的类名, 包名.类名)
推荐第三种: 因为第三种更加灵活.
注意:
每一个类的Class类型的对象只有一个,所以不管怎么获取,获取到的都是同一个Class对象。
public class Demo01Reflect {
public static void main(String[] args) throws ClassNotFoundException {
//1. 通过对象名.getClass方法获取
Person p = new Person();
Class clazz1 = p.getClass();
System.out.println(clazz1);
//2. 通过类名.class属性获取
Class clazz2 = Person.class;
System.out.println(clazz2);
//3. 通过Class类的静态方法forName进行获取
Class clazz3 = Class.forName("cn.itcast.demo02.Person");
System.out.println(clazz3);
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
}
}
2、Class对象中的常用方法:
String getSimpleName(): 获取简单类名. (不包含包的类名)
String getName():获取全类名 (包含包的类名)
T newInstance():直接创建该类的对象。
public class Demo02Reflect {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//获取Person类的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//调用getSimpleName,获取类名
System.out.println(clazz.getSimpleName()); //Person
//调用getName, 获取全类名
System.out.println(clazz.getName()); //cn.itcast.demo02.Person
//T newInstance():直接创建该类的对象。
//clazz是Person类的Class对象,所以调用newInstance创建的是Person对象.
Object obj = clazz.newInstance();
Person p = (Person)obj;
p.setName("大幂幂");
p.setAge(12);
System.out.println(p.getName() + "-" + p.getAge());
}
}
3、Constructor: 构造方法
Method: 成员方法
Filed: 成员变量
反射获取类的构造方法。
Class类中,有一些方法,可以获取对应的构造方法:
Constructor[] getConstructors(): 获取该类中的所有的公共(public)的构造方法。
Constructor getConstructor(Class... parameterTypes): 获取该类中指定的公共的构造方法。 参数表示要获取的构造方法的参数列表。
上面两个方法只能获取public修饰的构造方法。
Constructor表示构造方法, 里面有一些方法可以完成功能。
T newInstance(Object... initargs): 这个方法可以创建一个对象。
参数initargs: 可变参,表示调用构造方法的时候传递的实际参数
返回值: 使用该构造方法创建的对象.
public class Demo03Reflect {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
method2();
}
//获取Person类中的空参数构造方法,然后使用这个空参数构造方法创建一个Person对象
public static void method2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取Person类的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//调用getConstructor获取指定的构造方法
//在getConsturctor中,参数表示的含义是要获取的构造方法的参数列表
//因为要获取的是空参数构造方法,所以在getConstructor方法中不需要传递参数。
Constructor c = clazz.getConstructor();
//通过这个构造方法去创建一个对象
//直接调用构造方法的newInstance方法就可以根据这个构造方法创建一个对象
//因为是通过空参数构造方法创建度夏宁,所以newInstance方法中不需要写任何的参数。
Object obj = c.newInstance();
//打印这个对象
System.out.println(obj);
}
//获取Person类中所有的构造方法并打印
public static void method1() throws ClassNotFoundException {
//获取Person类的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//调用getConstructors,获取该类的所有的公共的构造方法。
Constructor[] cs = clazz.getConstructors();
//遍历这个数组,打印每一个元素
for (Constructor c : cs) {
System.out.println(c);
}
}
}
4、反射获取有参构造并且使用。
Class类中,有一些方法,可以获取对应的构造方法:
Constructor[] getConstructors(): 获取该类中的所有的公共(public)的构造方法。
Constructor getConstructor(Class... parameterTypes): 获取该类中指定的公共的构造方法。 参数表示要获取的构造方法的参数列表。
上面两个方法只能获取public修饰的构造方法。
Constructor表示构造方法, 里面有一些方法可以完成功能。
T newInstance(Object... initargs): 这个方法可以创建一个对象。
参数initargs: 可变参,表示调用构造方法的时候传递的实际参数
返回值: 使用该构造方法创建的对象.
注意:
任何类型都有.class。 包括基本类型。
public class Demo04Reflect {
//反射获取到Person类中的有参构造 Person(String name, int age),然后通过这个构造方法创建Person对象
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取这个类的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//可以通过Class对象获取Person类中的有参构造方法
//因为要获取的构造方法的第一个参数是String类型,第二个参数是int类型。
//所以要在这个getConstructor方法的第一个参数位置传递String类型的Class对象。 第二个参数传递int类型的class对象
Constructor c = clazz.getConstructor(String.class, int.class);
//根据这个构造方法创建对象
//因为获取到的构造方法是有参数的构造方法,所以使用这个构造方法创建对象时必须要传递实际参数。
//构造方法第一个参数是字符串,第二个参数是int类型,所以在newInstance参数位置给出对应的数据。
Object obj = c.newInstance("大幂幂", 30); //
//打印
System.out.println(obj);
}
}
5、暴力反射(不推荐, 因为会破坏封装性)
Constructor[] getDeclaredConstructors():获取类中的所有的构造方法,包括私有
Constructor getDeclaredConstructor(Class... parameterTypes): 获取指定的构造方法,包括私有
在Java中,有一个权限安全检查机制,私有的内容虽然可以获取到,但是并不能直接去使用。
解决方式: 取消这个权限安全检查机制。
Constructor: 构造方法
Method: 成员方法
Field: 字段(成员变量)
这是三个类有一个共同的父类,叫做AccessibleObject, 里面有一个方法,可以取消权限检查
void setAccessible(boolean flag): 如果参数是true,表示取消安全检查
public class Demo05Reflect {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
method2();
}
//使用反射获取私有构造方法,并且进行使用.
//private Person(String name)
public static void method2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取Person类的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//获取私有的构造方法
Constructor c = clazz.getDeclaredConstructor(String.class);
//在使用之前取消权限安全检查
c.setAccessible(true);
//使用这个构造方法创建对象。传递的实际参数为大幂幂
Object obj = c.newInstance("大幂幂");
System.out.println(obj);
}
//获取所有的构造方法
public static void method1() throws ClassNotFoundException {
//获取Person类的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//获取所有的构造方法
Constructor[] cs = clazz.getDeclaredConstructors();
//遍历
for (Constructor c : cs) {
System.out.println(c);
}
}
}
6、反射获取成员方法并使用。
在Class类中,有一些方法,可以获取到类的成员方法
Method getMethod(String name, Class... parameterTypes): 获取指定的成员方法,第一个参数为成员方法的名字。 第二个参数是该成员方法的参数列表。
Method[] getMethods(): 获取所有的成员方法。
注意: 上面两个方法,只能获取到public修饰的方法,其他权限获取不到.
Method这个类表示的是方法。 这个类中有一些方法可以完成一些功能。
Object invoke(Object obj, Object... args): 让该方法被调用执行。
参数obj:表示通过哪个对象去调用
参数args:表示调用该方法时传递的实际参数。
返回值: 这个方法调用的返回值。
public class Demo06Reflect {
public static void main(String[] args) throws Exception {
method2();
}
//获取指定的空参数成员方法,然后并调用执行。
//eat()
public static void method2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//获取Person类的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//获取指定的成员方法
//这个getMethod方法第一个参数为要获取的方法名,第二个参数为获取的方法的参数列表
//因为eat方法没有参数,所以只需要传递一个方法名即可
Method method = clazz.getMethod("eat");
Object obj = clazz.newInstance(); //通过反射创建一个Person对象
//让这个eat()方法被调用执行
//invoke方法要两个参数,第一个参数表示通过哪个对象去调用这个方法,第二个对象是一个可变参数,表示调用这个方法时传递的实际参数
//此方法执行(eat方法), 这个方法是通过obj对象调用的,没有参数
method.invoke(obj); //相当于 obj.eat();
}
//获取所有的公共的成员方法并使用
public static void method1() throws ClassNotFoundException {
//获取Person类的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//调用getMethods,获取到所有的成员方法
Method[] ms = clazz.getMethods(); //获取到的成员方法包括继承下来的。
//遍历
for (Method m : ms) {
System.out.println(m);
}
}
}
7、反射获取有参成员方法并调用
在Class类中,有一些方法,可以获取到类的成员方法
Method getMethod(String name, Class... parameterTypes): 获取指定的成员方法,第一个参数为成员方法的名字。 第二个参数是该成员方法的参数列表。
Method[] getMethods(): 获取所有的成员方法。
注意: 上面两个方法,只能获取到public修饰的方法,其他权限获取不到.
Method这个类表示的是方法。 这个类中有一些方法可以完成一些功能。
Object invoke(Object obj, Object... args): 让该方法被调用执行。
参数obj:表示通过哪个对象去调用
参数args:表示调用该方法时传递的实际参数。
返回值: 这个方法调用的返回值。
public class Demo07Reflect {
//使用反射获取有参成员方法并且使用
//public void sleep(String address, int time)
public static void main(String[] args) throws Exception {
//获取这个类的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//获取要调用的成员方法
Method m = clazz.getMethod("sleep", String.class, int.class);
//因为调用方法需要Person对象,所以获取一个Person对象
Object obj = clazz.newInstance();
//调用invoke方法,让方法执行。
//m指的是sleep方法,所以说当通过m调用invoke的时候。sleep方法会执行。
//因为第一个参数传递的是obj,所以是通过这个obj调用的sleep方法
//因为后面可变参数传递的是"天上人间", 5, 所以表示这个sleep方法被调用的时候传递的实际参数是这两个
m.invoke(obj, "天上人间", 5); //obj.sleep("天上人间", 5);
}
}
8、 反射获取Person中的get/set方法,进行赋值和取值的操作
public class Demo08Reflect {
public static void main(String[] args) throws Exception {
//获取Perosn的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//获取这个Person类中的setName方法
Method setMethod = clazz.getMethod("setName", String.class);
//创建一个对象
Object obj = clazz.newInstance();
//通过这个obj对象执行setName方法,给姓名赋值
setMethod.invoke(obj, "大幂幂");
//获取这个Person类的getName方法
Method getMethod = clazz.getMethod("getName");
//通过反射的方式调用getName方法,获取obj对象的姓名属性
//invoke方法的返回值,就是getName方法调用之后的结果
Object result = getMethod.invoke(obj); //相当于obj.getName();
System.out.println(result);
}
}
9、反射获取成员变量
在Class中有些方法,可以获取成员变量
Field getField(String name): 获取指定的成员变量,参数为成员变量名字
Field[] getFields():获取所有的成员变量。
注意: 上面两个方法,只能获取public修饰的成员变量。
Field表示的就是成员变量。里面有一些方法,可以去使用
void set(Object obj, Object value):给这个成员变量赋值
参数obj:表示给哪个对象的该成员变量赋值
参数value:表示赋值成什么
Object get(Object obj):获取成员变量的值
参数obj:表示获取哪个对象的该成员变量的值。
public class Demo09Reflect {
public static void main(String[] args) throws Exception {
method2();
}
//获取指定的成员变量并使用
public static void method2() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
//获取Perosn的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//通过这个Class对象获取到Person中的hobby成员变量
Field field = clazz.getField("hobby");
//通过反射创建对象
Object obj = clazz.newInstance();
//调用Field的set方法,给obj的该属性赋值
field.set(obj, "抽烟"); //给obj的hobby属性赋值成了抽烟
//调用get方法获取该对象的hobby属性值
Object result = field.get(obj); //获取obj对象的hobby,并返回
System.out.println(result);
}
//获取所有的成员变量
public static void method1() throws ClassNotFoundException {
//获取Perosn的Class对象
Class clazz = Class.forName("cn.itcast.demo02.Person");
//获取所有的成员变量
Field[] fields = clazz.getFields();
//遍历
for (Field field : fields) {
System.out.println(field);
}
}
}
三、注解
1、注解: @XXX
1. 注解是JDK1.5之后才有
2. 注解可以加在类上面,方法上面,变量上面....
3. 程序可以根据注解完成一些功能
注解和注释:
注释: 是给程序员看的, 注释被程序编译和执行。
注解: 是给程序看的, 程序可以根据注解完成一些功能。
常见的注解:
@Override: 验证是否是重写父类的方法。
@FunctionalInterface: 验证是否是函数式接口。
@Deprecated: 方法已过时
public class Demo01Anno {
public static void main(String[] args) {
method();
}
@Deprecated
public static void method() {
}
}
2、 注解的定义格式:
public @interface 注解名 {
}
public class Demo02Anno {
// @Student(name = "大幂幂", age = 20, hobbies = {"抽烟", "喝酒", "洗脚"})
@Student(name = "大幂幂", age = 20, hobbies = "抽烟")
public void method() {
}
}
@Book("红楼梦")
public class Demo03Anno {
@Book("三国演义")
int num;
//表示把"西游记" 赋值给了Book注解value属性
@Book("西游记")
public void method() {
}
}
3、 一个注解中可以有一些属性。
属性的定义格式:
1. 数据类型 属性名();
2. 数据类型 属性名() default 默认值;
注意,注解中属性的数据类型必须是以下内容
1. 可以是所有的基本类型。 byte short int long float double char boolean
2. 字符串, Class, 枚举, 注解类型。
3. 以上所有类型的一维数组。
使用注解的注意事项:
1. 如果注解中的某个属性没有默认值,那么在使用注解的时候,必须要给这个属性赋值。
2. 如果注解中某个属性有默认值, 在使用注解的时候,可以赋值,也可以不赋值,如果不赋值,那么该属性的值为默认值
3. 如果注解某个属性是一个数组,那么给该属性赋值的时候,可以使用大括号包裹起来 比如 {"aa", "bb", "cc"}
4. 如果给数组类型的属性赋值的时候,该数组中只有一个值,那么可以省略大括号。
如果要使用注解,直接在类或者方法或者变量上面加上@注解,如果这个注解有需要被赋值的属性,后面
跟上小括号,在小括号中给属性赋值即可。
public @interface Student {
//姓名属性
String name();
//年龄属性
int age() default 10;
//爱好
String[] hobbies();
}
4、如果一个注解中只有一个没有默认值的属性,并且这个属性叫做value, 那么再给这个属性赋值的时候,可以省略属性名。
反之必须写全
public @interface Book {
//书名
String value();
//价格
int price() default 100;
}
四、元注解
1、元注解。
元注解是jdk提供的,用来修饰注解的注解(加在注解上面的注解)
常用的元注解:
1. Target: 可以限制注解的使用位置
2. Retention: 可以限制注解的生命周期
@MyAnno
public class Demo01Anno {
@MyAnno
public void method() {
}
}
在java中还有一个元注解,叫做@Retention,可以限制注解的生命周期
@MyAnno1
@MyAnno2
@MyAnno3
public class Demo02Anno {
}
2、模拟单元测试。
自定义一个注解@MyTest。
然后再一个类中定义一些方法,在其中的方法上加上@MyTest注解,然后获取这个类中的所有方法,并且运行
带有@MyTest的方法
3、Target是jdk提供的一个元注解,用来修饰注解, 可以限制注解的使用范围。
Target这个元注解中有一个属性叫做value,用来表示被修饰的注解可以使用在哪个位置。这个
参数是一个ElementType[]。
ElementType是一个枚举,枚举其实就是对象,枚举中的每一个成员变量都是自己的对象。
Target注解的参数要传递ElementType的属性值
枚举中有下面属性,每一个属性都有特别的含义
ElementType.TYPE: 用在类,接口上
ElementType.FIELD:用在成员变量上
ElementType.METHOD: 用在方法上
ElementType.PARAMETER:用在参数上
ElementType.CONSTRUCTOR:用在构造方法上
ElementType.LOCAL_VARIABLE:用在局部变量上
//表示被修饰的注解,既可以使用在方法上,又可以使用在类或者接口的上面
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnno {
}
4、 @Retention 是一个元注解,可以限制注解的声明 周期, 如果不使用这个元注解,那么注解默认在
源代码阶段和编译后的.class阶段有效,一旦程序运行,那么注解就会消失。
Retention这个元注解中有一个属性叫做value, 所以我们给这个属性赋值的时候可以省略属性名。
并且这个value表示的含义为被修饰注解的声明周期。 这个value属性是 RetentionPolicy类型
我们可以给value属性传递三个东西:
RetentionPolicy.SOURCE: 表示被修饰的注解只在源代码阶段有效。
RetentionPolicy.CLASS: 表示被修饰的注解在源代码阶段和编译后的.class文件中有效,一旦运行之后就会消失
RetentionPolicy.RUNTIME:表示被修饰的注解在源代码阶段,编译后的.class并且运行时有效。
//这个注解只在源代码中有效。编译后就会消失
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnno1 {
}
//在源代码阶段和编译后的class中有效,运行时期内存中是没有的
@Retention(RetentionPolicy.CLASS)
public @interface MyAnno2 {
}
//运行时期也有效
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno3 {
}
5、注解的解析,获取注解中的内容并使用,比如获取注解中的属性。
相关API
Annotation接口: 是所有注解的根接口。 所有的注解都默认实现了这个接口。
AnnotatedElement接口: 里面的方法可以对注解进行操作。
boolean isAnnotationPresent(Class annotationClass):判断是否有指定的注解。 参数Class对象为注解的Class
T getAnnotation(Class annotationClass): 获取指定的注解。 参数是注解的.class对象。 要获取哪个注解,就传递哪个注解的.class
Annotation[] getAnnotations():获取所有的注解
解析注解,必须要结合反射技术。
AnnotatedElement这个接口下面有一些实现类,分别是 Class, Method, Filed。 所以我们可以通过这些实现类对象化
去调用上面操作注解的方法。
如果通过Class对象调用上面的方法,比如getAnnotations,那么就是获取类上面的注解。
如果通过Method对象调用上面的方法,获取的就是方法上面的注解
如果通过Field对象调用上面的注解,获取的就是成员变量上的注解....
想要获取什么上的注解,那么就通过什么对象去调用操作注解的方法就可以了。
public class Demo01AnnoParse {
//获取到BookStore这个类上面的注解属性并且打印
//解析注解要通过反射技术,如果要获取类上面的注解通过Class对象调用getAnnotation获取注解即可
public static void main(String[] args) throws ClassNotFoundException {
//获取BookStore这个类的Class对象
Class clazz = Class.forName("cn.itcast.demo05.BookStore");
//判断这个类上面有没有Book注解。
boolean flag = clazz.isAnnotationPresent(Book.class);
//如果这个类上面有Book注解,那么就获取
if (flag) {
//调用getnnotation获取指定的注解
Annotation annotation = clazz.getAnnotation(Book.class);
//获取到的注解真正是Book,可以向下转型成Book,然后获取里面的属性
Book book = (Book)annotation;
//打印注解的属性
System.out.println(book.value());
System.out.println(book.price());
System.out.println(book.author());
}
}
}
6、模拟单元测试。
自定义一个注解@MyTest。
然后再一个类中定义一些方法,在其中的方法上加上@MyTest注解,然后获取这个类中的所有方法,并且运行
带有@MyTest的方法
public class Demo02MyTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
//获取MyTestClass的Class对象
Class clazz = Class.forName("cn.itcast.demo05.MyTestClass");
//创建对象,留作调用方法使用
Object obj = clazz.newInstance();
//获取这个类中所有的成员方法
Method[] methods = clazz.getMethods();
//遍历这个数组,获取到每一个方法
for (Method method : methods) {
//拿到这个方法,判断这个方法上有没有@MyTest注解,如果有,就执行这个方法
if(method.isAnnotationPresent(MyTest.class)) {
method.invoke(obj); //通过obj对象调用了这个方法
}
}
}
}
@Book(value = "西游记", price = 100, author = "吴承恩")
public class BookStore {
public void method() {
}
}
//使用元注解,让Book在运行时期也有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
//书名
String value();
//价格
int price();
//作者
String author();
}
public class MyTestClass {
@MyTest
public void method1() {
System.out.println("method1");
}
public void method2() {
System.out.println("method2");
}
public void method13() {
System.out.println("method3");
}
@MyTest
public void method4() {
System.out.println("method4");
}
}
@Retention(RetentionPolicy.RUNTIME) //注解在运行时期有效
@Target(ElementType.METHOD) //只能加在方法上
public @interface MyTest {
}