1、单元测试
单元测试就是针对最小的功能单元编写测试代码, Java 程序最小的功能单元是方法,因此,单元测试就是针对 Java 方法的测试,进而检查方法的正确性。
Junit单元测试框架
JUnit 是使用 Java 语言实现的单元测试框架,它是开源的, Java 开发者都应当学习并使用 JUnit 编写单元测试。此外,几乎所有的 IDE 工具都集成了 JUnit ,这样我们就可以直接在 IDE 中编写并运行 JUnit 测试, JUnit 目前最新版本是 5JUnit优点
JUnit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。
Junit可以生成全部方法的测试报告。
单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
单元测试快速入门
需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门
分析:
① 将 JUnit 的 jar 包导入到项目中IDEA 通常整合好了 Junit 框架,一般不需要导入。如果 IDEA 没有整合好,需要自己手工导入如下 2 个 JUnit 的 jar 包到模块
② 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。③ 在测试方法上使用 @Test 注解:标注该方法是一个测试方法④ 在测试方法中完成被测试方法的预期正确性测试。⑤ 选中测试方法,选择“ JUnit 运行” ,如果 测试良好 则是 绿色 ;如果 测试失败 ,则是 红色
Junit常用注解(Junit 4.xxxx版本)
注解
说明
@Test
测试方法
@Before
用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。
@After
用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。
@BeforeClass
用来静态修饰方法,该方法会在所有测试方法之前只执行一次。
@AfterClass
用来静态修饰方法,该方法会在所有测试方法之后只执行一次。
开始执行的方法:初始化资源。执行完之后的方法:释放资源。/** 业务方法 */ public class UserService { public String loginName(String loginName , String passWord){ if("admin".equals(loginName) && "123456".equals(passWord)){ return "登录成功"; }else { return "用户名或者密码有问题"; } } public void selectNames(){ System.out.println(10/2); System.out.println("查询全部用户名称成功~~"); } }
import org.junit.*; /** 测试类 */ public class TestUserService { // 修饰实例方法的 @Before public void before(){ System.out.println("===before方法执行一次==="); } @After public void after(){ System.out.println("===after方法执行一次==="); } // 修饰静态方法 @BeforeClass public static void beforeClass(){ System.out.println("===beforeClass方法执行一次==="); } @AfterClass public static void afterClass(){ System.out.println("===afterClass方法执行一次==="); } /** 测试方法 注意点: 1、必须是公开的,无参数 无返回值的方法 2、测试方法必须使用@Test注解标记。 */ @Test public void testLoginName(){ UserService userService = new UserService(); String rs = userService.loginName("admin","123456"); // 进行预期结果的正确性测试:断言。 Assert.assertEquals("您的登录业务可能出现问题", "登录成功", rs ); } @Test public void testSelectNames(){ UserService userService = new UserService(); userService.selectNames(); } }
Junit常用注解(Junit 5.xxxx版本)
注解
说明
@Test
测试方法
@BeforeEach
用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。
@AfterEach
用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。
@BeforeAll
用来静态修饰方法,该方法会在所有测试方法之前只执行一次。
@AfterAll
用来静态修饰方法,该方法会在所有测试方法之后只执行一次。
2、反射
反射是指对于任何一个 Class 类,在"运行的时候"都可以直接得到这个类全部成分。在运行时,可以直接得到这个类的构造器对象 : Constructor在运行时,可以直接得到这个类的成员变量对象 : Field在运行时,可以直接得到这个类的成员方法对象 : Method这种运行时动态获取类信息以及动态调用类中成分的能力称为 Java 语言的反射机制。、反射的关键:
反射的第一步都是先得到编译后的 Class 类对象, 然后 就 可以得到 Class 的全部成分。HelloWorld.java -> javac -> HelloWorld.class
Class c = HelloWorld.class;
反射的第一步:获取Class类的对象
public class Student { }
/** 目标:反射的第一步:获取Class对象 */ public class Test { public static void main(String[] args) throws Exception { // 1、Class类中的一个静态方法:forName(全限名:包名 + 类名) Class c = Class.forName("com.itheima.d2_reflect_class.Student"); System.out.println(c); // Student.class // 2、类名.class Class c1 = Student.class; System.out.println(c1); // 3、对象.getClass() 获取对象对应类的Class对象。 Student s = new Student(); Class c2 = s.getClass(); System.out.println(c2); } }
反射技术获取构造器对象并使用
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。Class 类中用于获取构造器的方法
方法
说明
Constructor<?>[] getConstructors()
返回所有构造器对象的数组(只能拿public的)
Constructor<?>[] getDeclaredConstructors()
返回所有构造器对象的数组,存在就能拿到
Constructor<T> getConstructor(Class<?>... parameterTypes)
返回单个构造器对象(只能拿public的)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
返回单个构造器对象,存在就能拿到
Constructor类中用于创建对象的方法
符号
说明
T newInstance(Object... initargs)
根据指定的构造器创建对象
public void setAccessible(boolean flag)
设置为true,表示取消访问检查,进行暴力反射
public class Student { private String name; private int age; public Student(){ System.out.println("无参数构造器执行!"); } public Student(String name, int age) { System.out.println("有参数构造器执行!"); this.name = name; this.age = age; } 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 "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
import org.junit.Test; import java.lang.reflect.Constructor; /** 目标:反射_获取Constructor构造器对象. 反射的第一步是先得到Class类对象。(Class文件) 反射中Class类型获取构造器提供了很多的API: 1. Constructor getConstructor(Class... parameterTypes) 根据参数匹配获取某个构造器,只能拿public修饰的构造器,几乎不用! 2. Constructor getDeclaredConstructor(Class... parameterTypes) 根据参数匹配获取某个构造器,只要申明就可以定位,不关心权限修饰符,建议使用! 3. Constructor[] getConstructors() 获取所有的构造器,只能拿public修饰的构造器。几乎不用!!太弱了! 4. Constructor[] getDeclaredConstructors() 获取所有申明的构造器,只要你写我就能拿到,无所谓权限。建议使用!! 小结: 获取类的全部构造器对象: Constructor[] getDeclaredConstructors() -- 获取所有申明的构造器,只要你写我就能拿到,无所谓权限。建议使用!! 获取类的某个构造器对象:Constructor getDeclaredConstructor(Class... parameterTypes) -- 根据参数匹配获取某个构造器,只要申明就可以定位,不关心权限修饰符,建议使用! */ public class TestStudent01 { // 1. getConstructors: // 获取全部的构造器:只能获取public修饰的构造器。 // Constructor[] getConstructors() @Test public void getConstructors(){ // a.第一步:获取类对象 Class c = Student.class; // b.提取类中的全部的构造器对象(这里只能拿public修饰) Constructor[] constructors = c.getConstructors(); // c.遍历构造器 for (Constructor constructor : constructors) { System.out.println(constructor.getName() + "===>" + constructor.getParameterCount()); } } // 2.getDeclaredConstructors(): // 获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。 @Test public void getDeclaredConstructors(){ // a.第一步:获取类对象 Class c = Student.class; // b.提取类中的全部的构造器对象 Constructor[] constructors = c.getDeclaredConstructors(); // c.遍历构造器 for (Constructor constructor : constructors) { System.out.println(constructor.getName() + "===>" + constructor.getParameterCount()); } } // 3.getConstructor(Class... parameterTypes) // 获取某个构造器:只能拿public修饰的某个构造器 @Test public void getConstructor() throws Exception { // a.第一步:获取类对象 Class c = Student.class; // b.定位单个构造器对象 (按照参数定位无参数构造器 只能拿public修饰的某个构造器) Constructor cons = c.getConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount()); } // 4.getConstructor(Class... parameterTypes) // 获取某个构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。 @Test public void getDeclaredConstructor() throws Exception { // a.第一步:获取类对象 Class c = Student.class; // b.定位单个构造器对象 (按照参数定位无参数构造器) Constructor cons = c.getDeclaredConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount()); // c.定位某个有参构造器 Constructor cons1 = c.getDeclaredConstructor(String.class, int.class); System.out.println(cons1.getName() + "===>" + cons1.getParameterCount()); } }
import org.junit.Test; import java.lang.reflect.Constructor; /** 目标: 反射_获取Constructor构造器然后通过这个构造器初始化对象。 反射获取Class中的构造器对象Constructor作用: 也是初始化并得到类的一个对象返回。 Constructor的API: 1. T newInstance(Object... initargs) 创建对象,注入构造器需要的数据。 2. void setAccessible(true) 修改访问权限,true代表暴力攻破权限,false表示保留不可访问权限(暴力反射) 小结: 可以通过定位类的构造器对象。 如果构造器对象没有访问权限可以通过:void setAccessible(true)打开权限 构造器可以通过T newInstance(Object... initargs)调用自己,传入参数! */ public class TestStudent02 { // 1.调用构造器得到一个类的对象返回。 @Test public void getDeclaredConstructor() throws Exception { // a.第一步:获取类对象 Class c = Student.class; // b.定位单个构造器对象 (按照参数定位无参数构造器) Constructor cons = c.getDeclaredConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount()); // 如果遇到了私有的构造器,可以暴力反射 cons.setAccessible(true); // 权限被打开 Student s = (Student) cons.newInstance(); System.out.println(s); System.out.println("-------------------"); // c.定位某个有参构造器 Constructor cons1 = c.getDeclaredConstructor(String.class, int.class); System.out.println(cons1.getName() + "===>" + cons1.getParameterCount()); Student s1 = (Student) cons1.newInstance("孙悟空", 1000); System.out.println(s1); } }
反射技术获取成员变量对象并使用
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。Class 类中用于获取成员变量的方法
方法
说明
Field[] getFields()
返回所有成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields()
返回所有成员变量对象的数组,存在就能拿到
Field getField(String name)
返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String name)
返回单个成员变量对象,存在就能拿到
Field类中用于取值、赋值的方法
符号
说明
void set(Object obj, Object value):
赋值
Object get(Object obj)
获取值。
public class Student { private String name; private int age; public static String schoolName; public static final String COUNTTRY = "中国"; public Student(){ System.out.println("无参数构造器执行!"); } public Student(String name, int age) { System.out.println("有参数构造器执行!"); this.name = name; this.age = age; } 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 "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
import org.junit.Test; import java.io.File; import java.lang.reflect.Field; /** 目标:反射_获取Field成员变量对象。 反射的第一步是先得到Class类对象。 1、Field getField(String name); 根据成员变量名获得对应Field对象,只能获得public修饰 2.Field getDeclaredField(String name); 根据成员变量名获得对应Field对象,只要申明了就可以得到 3.Field[] getFields(); 获得所有的成员变量对应的Field对象,只能获得public的 4.Field[] getDeclaredFields(); 获得所有的成员变量对应的Field对象,只要申明了就可以得到 小结: 获取全部成员变量:getDeclaredFields 获取某个成员变量:getDeclaredField */ public class FieldDemo01 { /** * 1.获取全部的成员变量。 * Field[] getDeclaredFields(); * 获得所有的成员变量对应的Field对象,只要申明了就可以得到 */ @Test public void getDeclaredFields(){ // a.定位Class对象 Class c = Student.class; // b.定位全部成员变量 Field[] fields = c.getDeclaredFields(); // c.遍历一下 for (Field field : fields) { System.out.println(field.getName() + "==>" + field.getType()); } } /** 2.获取某个成员变量对象 Field getDeclaredField(String name); */ @Test public void getDeclaredField() throws Exception { // a.定位Class对象 Class c = Student.class; // b.根据名称定位某个成员变量 Field f = c.getDeclaredField("age"); System.out.println(f.getName() +"===>" + f.getType()); } }
import org.junit.Test; import java.lang.reflect.Field; /** 目标:反射获取成员变量: 取值和赋值。 Field的方法:给成员变量赋值和取值 void set(Object obj, Object value):给对象注入某个成员变量数据 Object get(Object obj):获取对象的成员变量的值。 void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。 Class getType(); 获取属性的类型,返回Class对象。 String getName(); 获取属性的名称。 */ public class FieldDemo02 { @Test public void setField() throws Exception { // a.反射第一步,获取类对象 Class c = Student.class; // b.提取某个成员变量 Field ageF = c.getDeclaredField("age"); ageF.setAccessible(true); // 暴力打开权限 // c.赋值 Student s = new Student(); ageF.set(s , 18); // s.setAge(18); System.out.println(s); // d、取值 int age = (int) ageF.get(s); System.out.println(age); } }
反射技术获取方法对象并使用
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。Class 类中用于获取成员方法的方法
方法
说明
Method[] getMethods()
返回所有成员方法对象的数组(只能拿public的)
Method[] getDeclaredMethods()
返回所有成员方法对象的数组,存在就能拿到
Method getMethod(String name, Class<?>... parameterTypes)
返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回单个成员方法对象,存在就能拿到
Method类中用于触发执行的方法
符号
说明
Object invoke(Object obj, Object... args)
运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
public class Dog { private String name ; public Dog(){ } public Dog(String name) { this.name = name; } public void run(){ System.out.println("狗跑的贼快~~"); } private void eat(){ System.out.println("狗吃骨头"); } private String eat(String name){ System.out.println("狗吃" + name); return "吃的很开心!"; } public static void inAddr(){ System.out.println("在黑马学习Java!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
import org.junit.Test; import java.lang.reflect.Method; /** 目标:反射——获取Method方法对象 反射获取类的Method方法对象: 1、Method getMethod(String name,Class...args); 根据方法名和参数类型获得对应的方法对象,只能获得public的 2、Method getDeclaredMethod(String name,Class...args); 根据方法名和参数类型获得对应的方法对象,包括private的 3、Method[] getMethods(); 获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的 4、Method[] getDeclaredMethods(); 获得类中的所有成员方法对象,返回数组,只获得本类申明的方法。 Method的方法执行: Object invoke(Object obj, Object... args) 参数一:触发的是哪个对象的方法执行。 参数二: args:调用方法时传递的实际参数 */ public class MethodDemo01 { /** * 1.获得类中的所有成员方法对象 */ @Test public void getDeclaredMethods(){ // a.获取类对象 Class c = Dog.class; // b.提取全部方法;包括私有的 Method[] methods = c.getDeclaredMethods(); // c.遍历全部方法 for (Method method : methods) { System.out.println(method.getName() +" 返回值类型:" + method.getReturnType() + " 参数个数:" + method.getParameterCount()); } } /** * 2. 获取某个方法对象 */ @Test public void getDeclardMethod() throws Exception { // a.获取类对象 Class c = Dog.class; // b.提取单个方法对象 Method m = c.getDeclaredMethod("eat"); Method m2 = c.getDeclaredMethod("eat", String.class); // 暴力打开权限了 m.setAccessible(true); m2.setAccessible(true); // c.触发方法的执行 Dog d = new Dog(); // 注意:方法如果是没有结果回来的,那么返回的是null. Object result = m.invoke(d); System.out.println(result); Object result2 = m2.invoke(d, "骨头"); System.out.println(result2); } }
反射的作用-绕过编译阶段为集合添加数据
反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素
ArrayList<Integer> list = new ArrayList<>();
list.add(100);
// list.add(“雄安马"); // 报错
list.add(99);泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了。
import java.lang.reflect.Method; import java.util.ArrayList; public class ReflectDemo { public static void main(String[] args) throws Exception { // 需求:反射实现泛型擦除后,加入其他类型的元素 ArrayList<String> lists1 = new ArrayList<>(); ArrayList<Integer> lists2 = new ArrayList<>(); System.out.println(lists1.getClass()); System.out.println(lists2.getClass()); System.out.println(lists1.getClass() == lists2.getClass()); // ArrayList.class System.out.println("---------------------------"); ArrayList<Integer> lists3 = new ArrayList<>(); lists3.add(23); lists3.add(22); // lists3.add("黑马"); Class c = lists3.getClass(); // ArrayList.class ===> public boolean add(E e) // 定位c类中的add方法 Method add = c.getDeclaredMethod("add", Object.class); boolean rs = (boolean) add.invoke(lists3, "黑马"); System.out.println(rs); System.out.println(lists3); ArrayList list4 = lists3; list4.add("白马"); list4.add(false); System.out.println(lists3); } }
反射做通用框架
需求
给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去。分析
① 定义一个方法,可以接收任意类的对象。② 每次收到一个对象后,需要解析这个对象的全部成员变量名称。③ 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?④ 使用反射获取对象的 Class 类对象,然后获取全部成员变量信息。⑤ 遍历成员变量信息,然后提取本成员变量在对象中的具体值⑥ 存入成员变量名称和值到文件中去即可。public class Teacher { private String name; private char sex; private double salary; public Teacher(){ } public Teacher(String name, char sex, double salary) { this.name = name; this.sex = sex; this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
public class Student { private String name; private char sex; private int age; private String className; private String hobby; public Student(){ } public Student(String name, char sex, int age, String className, String hobby) { this.name = name; this.sex = sex; this.age = age; this.className = className; this.hobby = hobby; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } }
import java.io.FileOutputStream; import java.io.PrintStream; import java.lang.reflect.Field; public class MybatisUtil { /** 保存任意类型的对象 * @param obj */ public static void save(Object obj){ try ( PrintStream ps = new PrintStream(new FileOutputStream("day23/src/data.txt", true)); ){ // 1、提取这个对象的全部成员变量:只有反射可以解决 Class c = obj.getClass(); // c.getSimpleName()获取当前类名 c.getName获取全限名:包名+类名 ps.println("================" + c.getSimpleName() + "================"); // 2、提取它的全部成员变量 Field[] fields = c.getDeclaredFields(); // 3、获取成员变量的信息 for (Field field : fields) { String name = field.getName(); // 提取本成员变量在obj对象中的值(取值) field.setAccessible(true); String value = field.get(obj) + ""; ps.println(name + "=" + value); } } catch (Exception e) { e.printStackTrace(); } } }
import java.util.Date; import java.util.Properties; /** 目标:提供一个通用框架,支持保存所有对象的具体信息。 */ public class ReflectDemo { public static void main(String[] args) throws Exception { Student s = new Student(); s.setName("猪八戒"); s.setClassName("西天跑路1班"); s.setAge(1000); s.setHobby("吃,睡"); s.setSex('男'); MybatisUtil.save(s); Teacher t = new Teacher(); t.setName("波仔"); t.setSex('男'); t.setSalary(6000); MybatisUtil.save(t); } }
3、注解
Java 注解( Annotation )又称 Java 标注,是 JDK5.0 引入的一种注释机制。Java 语言中的类、 构造器、 方法、成员变量、参数等都可以被注解进行标注。
对 Java 中类、方法、成员变量做标记,然后进行特殊处理 ,至于到底做何种处理由业务需求来决定。例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行
自定义注解 --- 格式
自定义注解就是自己做一个注解来使用。public @interface 注解名称 {
public 属性类型 属性名() default 默认值 ;
}public @interface MyBook { String name(); String[] authors(); double price(); }
/** 目标:学会自定义注解。掌握其定义格式和语法。 */ @MyBook(name="《精通JavaSE》",authors = {"黑马", "dlei"} , price = 199.5) //@Book(value = "/delete") // @Book("/delete") @Book(value = "/delete", price = 23.5) //@Book("/delete") public class AnnotationDemo1 { @MyBook(name="《精通JavaSE2》",authors = {"黑马", "dlei"} , price = 199.5) private AnnotationDemo1(){ } @MyBook(name="《精通JavaSE1》",authors = {"黑马", "dlei"} , price = 199.5) public static void main(String[] args) { @MyBook(name="《精通JavaSE2》",authors = {"黑马", "dlei"} , price = 199.5) int age = 21; } }
特殊属性
value属性,如果只有一个value属性的情况下 , 使用value属性的时候可以省略value名称不写!!但是如果有多个属性, 且多个属性没有默认值,那么value 名称 是不能省略的。public @interface Book { String value(); // 特殊属性 double price() ; //double price() default 9.9; }
元注解
元注解有两个:
@Target : 约束自定义注解只能在哪些地方使用,@Retention:申明注解的生命周期
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.FIELD}) // 元注解 @Retention(RetentionPolicy.RUNTIME) // 一直活着,在运行阶段这个注解也不消失 public @interface MyTest { }
/** 目标:认识元注解 */ //@MyTest // 只能注解方法和成员变量 public class AnnotationDemo2 { @MyTest private String name; @MyTest public void test(){ } public static void main(String[] args) { } }
@Target中可使用的值定义在ElementType枚举类中,常用值如下
TYPE,类,接口FIELD, 成员变量METHOD, 成员方法PARAMETER, 方法参数CONSTRUCTOR, 构造器LOCAL_VARIABLE, 局部变量
@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下
SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
注解的解析
注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
与注解解析相关的接口
Annotation: 注解 的顶级接口, 注解都 是 Annotation 类型的 对象AnnotatedElement:该接口定义了与注解解析相关的解析方法
方法
说明
Annotation[] getDeclaredAnnotations()
获得当前对象上使用的所有注解,返回注解数组。
T getDeclaredAnnotation(Class<T> annotationClass)
根据注解类型获得对应注解对象
boolean isAnnotationPresent(Class<Annotation> annotationClass)
判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false
所有的类成分Class, Method , Field , Constructor , 都实现了AnnotatedElement接口他们都拥有解析注解的能力:
解析注解的技巧
注解在哪个成分上,我们就先拿哪个成分对象。比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解比如注解作用在类上,则要该类的Class对象,再来拿上面的注解比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解
注解解析的案例
需求:注解解析的案例
分析
① 定义注解Book,要求如下:
- 包含属性:String value() 书名
- 包含属性:double price() 价格,默认值为 100
- 包含属性:String[] authors() 多位作者
- 限制注解使用的位置:类和成员方法上
- 指定注解的有效范围:RUNTIME② 定义BookStore类,在类和成员方法上使用Book注解③ 定义AnnotationDemo01测试类获取Book注解上的数据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 Bookk { String value(); double price() default 100; String[] author(); }
import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; /** 目标:完成注解的解析 */ public class AnnotationDemo3 { @Test public void parseClass(){ // a.先得到类对象 Class c = BookStore.class; // b.判断这个类上面是否存在这个注解 if(c.isAnnotationPresent(Bookk.class)){ //c.直接获取该注解对象 Bookk book = (Bookk) c.getDeclaredAnnotation(Bookk.class); System.out.println(book.value()); System.out.println(book.price()); System.out.println(Arrays.toString(book.author())); } } @Test public void parseMethod() throws NoSuchMethodException { // a.先得到类对象 Class c = BookStore.class; Method m = c.getDeclaredMethod("test"); // b.判断这个类上面是否存在这个注解 if(m.isAnnotationPresent(Bookk.class)){ //c.直接获取该注解对象 Bookk book = (Bookk) m.getDeclaredAnnotation(Bookk.class); System.out.println(book.value()); System.out.println(book.price()); System.out.println(Arrays.toString(book.author())); } } } @Bookk(value = "《情深深雨濛濛》", price = 99.9, author = {"琼瑶", "dlei"}) class BookStore{ @Bookk(value = "《三少爷的剑》", price = 399.9, author = {"古龙", "熊耀华"}) public void test(){ } }
模拟Junit框架
需求
定义若干个方法,只要加了 MyTest 注解,就可以在启动时被触发执行分析
① 定义一个自定义注解 MyTest ,只能注解方法,存活范围是一直都在。② 定义若干个方法,只要有 @MyTest 注解的方法就能在启动时被触发执行,没有这个注解的方法不能执行。import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.FIELD}) // 元注解 @Retention(RetentionPolicy.RUNTIME) // 一直活着,在运行阶段这个注解也不消失 public @interface MyTest { }
import java.lang.reflect.Method; public class AnnotationDemo4 { public void test1(){ System.out.println("===test1==="); } @MyTest public void test2(){ System.out.println("===test2==="); } @MyTest public void test3(){ System.out.println("===test3==="); } /** 启动菜单:有注解的才被调用。 */ public static void main(String[] args) throws Exception { AnnotationDemo4 t = new AnnotationDemo4(); // a.获取类对象 Class c = AnnotationDemo4.class; // b.提取全部方法 Method[] methods = c.getDeclaredMethods(); // c.遍历方法,看是否有MyTest注解,有就跑它 for (Method method : methods) { if(method.isAnnotationPresent(MyTest.class)){ // 跑它 method.invoke(t); } } } }
4、动态代理
模拟企业业务功能开发,并完成每个功能的性能统计
需求
模拟某企业用户管理业务,需包含用户登录,用户删除,用户查询功能,并要统计每个功能的耗时。分析
① 定义一个 UserService 表示用户业务接口,规定必须完成用户登录,用户删除,用户查询功能。② 定义一个实现类 UserServiceImpl 实现 UserService ,并完成相关功能,且统计每个功能的耗时。③ 定义测试类,创建实现类对象,调用方法。/** 模拟用户业务功能 */ public interface UserService { String login(String loginName , String passWord) ; void selectUsers(); boolean deleteUsers(); void updateUsers(); }
public class UserServiceImpl implements UserService{ @Override public String login(String loginName, String passWord) { try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } if("admin".equals(loginName) && "1234".equals(passWord)) { return "success"; } return "登录名和密码可能有毛病"; } @Override public void selectUsers() { System.out.println("查询了100个用户数据!"); try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean deleteUsers() { try { System.out.println("删除100个用户数据!"); Thread.sleep(500); return true; } catch (Exception e) { e.printStackTrace(); return false; } } @Override public void updateUsers() { try { System.out.println("修改100个用户数据!"); Thread.sleep(2500); } catch (Exception e) { e.printStackTrace(); } } }
public class Test { public static void main(String[] args) { // 1、把业务对象,直接做成一个代理对象返回,代理对象的类型也是 UserService类型 UserService userService = ProxyUtil.getProxy(new UserServiceImpl()); System.out.println(userService.login("admin", "1234")); System.out.println(userService.deleteUsers()); userService.selectUsers(); userService.updateUsers(); // 走代理 } }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 参数一:类加载器,负责加载代理类到内存中使用。 参数二:获取被代理对象实现的全部接口。代理要为全部接口的全部方法进行代理 参数三:代理的核心处理逻辑 */ public class ProxyUtil { /** 生成业务对象的代理对象。 * @param obj * @return */ public static <T> T getProxy(T obj) { // 返回了一个代理对象了 return (T)Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 参数一:代理对象本身。一般不管 // 参数二:正在被代理的方法 // 参数三:被代理方法,应该传入的参数 long startTimer = System .currentTimeMillis(); // 马上触发方法的真正执行。(触发真正的业务功能) Object result = method.invoke(obj, args); long endTimer = System.currentTimeMillis(); System.out.println(method.getName() + "方法耗时:" + (endTimer - startTimer) / 1000.0 + "s"); // 把业务功能方法执行的结果返回给调用者 return result; } }); } }