Java高级技术:单元测试、反射、注解
目录
单元测试
单元测试概述
单元测试快速入门
单元测试常用注解
反射
反射概述
反射获取类对象
反射获取构造器对象
反射获取成员变量对象
反射获取方法对象
反射的作用-绕过编译阶段为集合添加数据
反射的作用-通用框架的底层原理
注解
注解概述
自定义注解
元注解
注解解析
注解的应用场景一:junit框架
动态代理
准备案例、提出问题
使用动态代理解决问题
单元测试
单元测试概述
单元测试单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。
目前测试方法是怎么进行的,存在什么问题只有一个main方法,如果一个方法的测试失败了,其他方法测试会受到影响。无法得到测试的结果报告,需要程序员自己去观察测试是否成功。无法实现自动化测试。
Junit单元测试框架
JUnit是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习并使用JUnit编写单元测试。
此外,几乎所有的IDE工具都集成了JUnit,这样我们就可以直接在IDE中编写并运行JUnit测试,JUnit目前最新版本是5。
JUnit优点JUnit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。Junit可以生成全部方法的测试报告。单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
总结
Junit单元测试是做什么的?测试类中方法的正确性的。
Junit单元测试的优点是什么?JUnit可以选择执行哪些测试方法,可以一键执行全部测试方法的测试。JUnit可以生测试报告,如果测试良好则是绿色;如果测试失败,则是红色。单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
单元测试快速入门
单元测试快速入门
需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门
分析:将JUnit的jar包导入到项目中IDEA通常整合好了Junit框架,一般不需要导入。如果IDEA没有整合好,需要自己手工导入如下2个JUnit的jar包到模块编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。在测试方法上使用@Test注解:标注该方法是一个测试方法在测试方法中完成被测试方法的预期正确性测试。选中测试方法,选择“JUnit运行” ,如果测试良好则是绿色;如果测试失败,则是红色
/**业务方法*/
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 {// 修饰实例方法的@Beforepublic void before(){System.out.println("===before方法执行一次===");}@Afterpublic void after(){System.out.println("===after方法执行一次===");}// 修饰静态方法@BeforeClasspublic static void beforeClass(){System.out.println("===beforeClass方法执行一次===");}@AfterClasspublic static void afterClass(){System.out.println("===afterClass方法执行一次===");}/**测试方法注意点:1、必须是公开的,无参数 无返回值的方法2、测试方法必须使用@Test注解标记。*/@Testpublic void testLoginName(){UserService userService = new UserService();String rs = userService.loginName("admin","123456");// 进行预期结果的正确性测试:断言。Assert.assertEquals("您的登录业务可能出现问题", "登录成功", rs );}@Testpublic void testSelectNames(){UserService userService = new UserService();userService.selectNames();}
}
单元测试常用注解
Junit常用注解(Junit 4.xxxx版本)
注解 | 说明 |
@Test | 测试方法 |
@Before | 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 |
@After | 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 |
@BeforeClass | 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 |
@AfterClass | 用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 |
开始执行的方法:初始化资源。
执行完之后的方法:释放资源。
反射
反射概述
反射概述反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。在运行时,可以直接得到这个类的构造器对象:Constructor在运行时,可以直接得到这个类的成员变量对象:Field在运行时,可以直接得到这个类的成员方法对象:Method这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。
反射的关键:反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。
反射获取类对象
反射的第一步:获取Class类的对象
package com.deng.hello;public class Student {
}
package com.deng.hello;/**目标:反射的第一步:获取Class对象*/
public class Test {public static void main(String[] args) throws Exception {// 1、Class类中的一个静态方法:forName(全限名:包名 + 类名)Class c = Class.forName("package com.deng.hello.Student");System.out.println(c); // Student.class// 2、类名.classClass c1 = Student.class;System.out.println(c1);// 3、对象.getClass() 获取对象对应类的Class对象。Student s = new Student();Class c2 = s.getClass();System.out.println(c2);}
}
总结
反射的第一步是什么?获取Class类对象,如此才可以解析类的全部成分
获取Class类的对象的三种方式方式一:Class c1 = Class.forName(“全类名”);方式二:Class c2 = 类名.class方式三:Class c3 = 对象.getClass();
反射获取构造器对象
使用反射技术获取构造器对象并使用
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
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;private 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;}@Overridepublic 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()@Testpublic 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():// 获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。@Testpublic 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修饰的某个构造器@Testpublic 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)// 获取某个构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。@Testpublic 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.调用构造器得到一个类的对象返回。@Testpublic 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);}
}
总结
利用反射技术获取构造器对象的方式getDeclaredConstructors()getDeclaredConstructor (Class<?>... parameterTypes)
反射得到的构造器可以做什么?依然是创建对象的public newInstance(Object... initargs)
如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象setAccessible(boolean)反射可以破坏封装性,私有的也可以执行了。
反射获取成员变量对象
使用反射技术获取成员变量对象并使用
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
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;}@Overridepublic 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对象,只要申明了就可以得到*/@Testpublic 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);*/@Testpublic 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 {@Testpublic 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);}
}
总结
利用反射技术获取成员变量的方式
获取类中成员变量对象的方法
getDeclaredFields()
getDeclaredField (String name)
反射得到成员变量可以做什么?
依然是在某个对象中取值和赋值。
void set(Object obj, Object value):
Object get(Object obj)
如果某成员变量是非public的,需要打开权限(暴力反射),然后再取值、赋值
setAccessible(boolean)
反射获取方法对象
使用反射技术获取方法对象并使用
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
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.获得类中的所有成员方法对象*/@Testpublic 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. 获取某个方法对象*/@Testpublic 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);}
}
总结
1、利用反射技术获取成员方法对象的方式获取类中成员方法对象getDeclaredMethods()getDeclaredMethod (String name, Class<?>... parameterTypes)
2、反射得到成员方法可以做什么?依然是在某个对象中触发该方法执行。Object invoke(Object obj, Object... args)如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行setAccessible(boolean)
反射的作用-绕过编译阶段为集合添加数据
反射的作用-绕过编译阶段为集合添加数据反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的。
泛型只是在编译阶段可以约束集合只能操作某种数据类型,
在编译成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.classSystem.out.println("---------------------------");ArrayList<Integer> lists3 = new ArrayList<>();lists3.add(23);lists3.add(22);// lists3.add("Java");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, "Java");System.out.println(rs);System.out.println(lists3);ArrayList list4 = lists3;list4.add("MySQL");list4.add(false);System.out.println(lists3);}
}
反射的作用-通用框架的底层原理
案例
反射做通用框架
需求
给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去。
分析
定义一个方法,可以接收任意类的对象。
每次收到一个对象后,需要解析这个对象的全部成员变量名称。
这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
使用反射获取对象的Class类对象,然后获取全部成员变量信息。
遍历成员变量信息,然后提取本成员变量在对象中的具体值
存入成员变量名称和值到文件中去即可。
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;}
}
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;}
}
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("junit-reflect-annotation-proxy-app/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);}
}
总结
反射的作用?
可以在运行时得到一个类的全部成分然后操作。
可以破坏封装性。(很突出)
也可以破坏泛型的约束性。(很突出)
更重要的用途是适合:做Java高级框架
基本上主流框架都会基于反射设计一些通用技术功能。
注解
注解概述
注解的概述、作用
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
作用对Java中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理由业务需求来决定。例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。
自定义注解
自定义注解 --- 格式
自定义注解就是自己做一个注解来使用。
public @interface MyBook {String name();String[] authors();double price();
}
/**目标:学会自定义注解。掌握其定义格式和语法。*/
@MyBook(name="《精通JavaSE》",authors = {"Java", "dlei"} , price = 199.5)
//@Book(value = "/delete")
// @Book("/delete")
@Book(value = "/delete", price = 23.5)
//@Book("/delete")
public class AnnotationDemo1 {@MyBook(name="《精通JavaSE2》",authors = {"Java", "dlei"} , price = 199.5)private AnnotationDemo1(){}@MyBook(name="《精通JavaSE1》",authors = {"Java", "dlei"} , price = 199.5)public static void main(String[] args) {@MyBook(name="《精通JavaSE2》",authors = {"Java", "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:申明注解的生命周期
@Target中可使用的值定义在ElementType枚举类中,常用值如下TYPE,类,接口FIELD, 成员变量METHOD, 成员方法PARAMETER, 方法参数CONSTRUCTOR, 构造器LOCAL_VARIABLE, 局部变量
@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
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 {@MyTestprivate String name;@MyTestpublic void test(){}public static void main(String[] args) {}
}
注解解析
注解的解析注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
与注解解析相关的接口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 {@Testpublic 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()));}}@Testpublic 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框架
模拟Junit框架
需求
定义若干个方法,只要加了MyTest注解,就可以在启动时被触发执行
分析
定义一个自定义注解MyTest,只能注解方法,存活范围是一直都在。
定义若干个方法,只要有@MyTest注解的方法就能在启动时被触发执行,没有这个注解的方法不能执行。
动态代理
准备案例、提出问题
模拟企业业务功能开发,并完成每个功能的性能统计
需求
模拟某企业用户管理业务,需包含用户登录,用户删除,用户查询功能,并要统计每个功能的耗时。
分析
定义一个UserService表示用户业务接口,规定必须完成用户登录,用户删除,用户查询功能。
定义一个实现类UserServiceImpl实现UserService,并完成相关功能,且统计每个功能的耗时。
定义测试类,创建实现类对象,调用方法。
/**模拟用户业务功能*/
public interface UserService {String login(String loginName , String passWord) ;void selectUsers();boolean deleteUsers();void updateUsers();
}
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(); // 走代理}
}
public class UserServiceImpl implements UserService{@Overridepublic 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 "登录名和密码可能有毛病";}@Overridepublic void selectUsers() {System.out.println("查询了100个用户数据!");try {Thread.sleep(2000);} catch (Exception e) {e.printStackTrace();}}@Overridepublic boolean deleteUsers() {try {System.out.println("删除100个用户数据!");Thread.sleep(500);return true;} catch (Exception e) {e.printStackTrace();return false;}}@Overridepublic void updateUsers() {try {System.out.println("修改100个用户数据!");Thread.sleep(2500);} catch (Exception e) {e.printStackTrace();}}
}
本案例存在什么问题?
答:业务对象的的每个方法都要进行性能统计,存在大量重复的代码。
使用动态代理解决问题
动态代理代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理的。
关键步骤1.必须有接口,实现类要实现接口(代理通常是基于接口实现的)。2.创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象。
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() {@Overridepublic 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;}});}
}
动态代理的优点非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理。可以为被代理对象的所有方法做代理。可以在不改变方法源码的情况下,实现对方法功能的增强。不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。
相关文章:
Java高级技术:单元测试、反射、注解
目录 单元测试 单元测试概述 单元测试快速入门 单元测试常用注解 反射 反射概述 反射获取类对象 反射获取构造器对象 反射获取成员变量对象 反射获取方法对象 反射的作用-绕过编译阶段为集合添加数据 反射的作用-通用框架的底层原理 注解 注解概述 自定义注解 …...
C语言初识
#include <stdio.h>//这种写法是过时的写法 void main() {}//int是整型的意思 //main前面的int表示main函数调用后返回一个整型值 int main() {return 0; }int main() { //主函数--程序的入口--main函数有且仅有一个//在这里完成任务//在屏幕伤输出hello world//函数-pri…...
Cadence Allegro 导出Etch Length by Layer Report报告详解
⏪《上一篇》 🏡《上级目录》 ⏩《下一篇》 目录 1,概述2,Etch Length by Layer Report作用3,Etch Length by Layer Report示例4,Etch Length by Layer Report导出方法4.2,方法14.2,方法2B站关注“硬小二”浏览更多演示视频...
无监督对比学习(CL)最新必读经典论文整理分享
对比自监督学习技术是一种很有前途的方法,它通过学习对使两种事物相似或不同的东西进行编码来构建表示。Contrastive learning有很多文章介绍,区别于生成式的自监督方法,如AutoEncoder通过重建输入信号获取中间表示,Contrastive M…...
最长回文子串【Java实现】
题目描述 现有一个字符串s,求s的最长回文子串的长度 输入描述 一个字符串s,仅由小写字母组成,长度不超过100 输出描述 输出一个整数,表示最长回文子串的长度 样例 输入 lozjujzve输出 // 最长公共子串为zjujz,长度为…...
LeetCode 438. Find All Anagrams in a String
LeetCode 438. Find All Anagrams in a String 题目描述 Given two strings s and p, return an array of all the start indices of p’s anagrams in s. You may return the answer in any order. An Anagram is a word or phrase formed by rearranging the letters of a…...
MyBatis-1:基础概念+环境配置
什么是MyBatis?MyBatis是一款优秀的持久层框架,支持自定义sql,存储过程以及高级映射。MyBatis就是可以让我们更加简单的实现程序和数据库之间进行交互的一个工具。可以让我们更加简单的操作和读取数据库的内容。MyBatis的官网:htt…...
R语言基础(五):流程控制语句
R语言基础(一):注释、变量 R语言基础(二):常用函数 R语言基础(三):运算 R语言基础(四):数据类型 6.流程控制语句 和大多数编程语言一样,R语言支持选择结构和循环结构。 6.1 选择语句 选择语句是当条件满足的时候才执行…...
【Java开发】设计模式 02:工厂模式
1 工厂模式介绍工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使…...
合并两个链表(自定义位置合并与有序合并)LeetCode--OJ题详解
图片: csdn 自定义位置合并 问题: 给两个链表 list1 和 list2 ,它们包含的元素分别为 n 个和 m 个。 请你将 list1 中 下标从 a 到 b 的全部节点都删除,并将list2 接在被删除节点 的位置。 比如: 输入:list1 [1…...
Java编程问题总结
Java编程问题总结 整理自 https://github.com/giantray/stackoverflow-java-top-qa 基础语法 将InputStream转换为String apache commons-io String content IOUtils.toString(new FileInputStream(file), StandardCharsets.UTF_8); //String value FileUtils.readFileT…...
binutils工具集——objcopy的用法
以下内容源于网络资源的学习与整理,如有侵权请告知删除。 一、工具简介 objcopy主要用来转换目标文件的格式。 在实际开发中,我们会用该工具进行格式转换与内容删除。 (1)在链接完成后,将elf格式的.out文件转化为bi…...
Windows使用Stable Diffusion时遇到的各种问题和知识点整理(更新中...)
Stable Diffusion安装完成后,在使用过程中会出现卡死、文件不存在等问题,在本文中将把遇到的问题陆续记录下来,有兴趣的朋友可以参考。 如果要了解如何安装sd,则参考本文《Windows安装Stable Diffusion WebUI及问题解决记录》。如…...
MySQL workbench基本查询语句
1.查询所有字段所有记录 SELECT * FROM world.city; select 表示查询;“*” 称为通配符,也称为“标配符”。表示将表中所有的字段都查询出来;from 表示从哪里查询;world.city 表示名为world的数据库中的city表; 上面…...
软件测试详解
文章目录一、软件危机(一)概念(二)产生软件危机的原因(三)消除软件危机的途径二、软件过程模型(一)软件生命周期概念(二)软件开发模型1. 瀑布模型2. 螺旋模型…...
YOLOS学习记录
在前面,博主已经完成了YOLOS项目的部署与调试任务,并在博主自己构造的数据集上进行了实验,实验结果表明效果并不显著,其实这一点并不意外,反而是在情理之中。众所周知,Transformer一直以来作为NLP领域的带头…...
数组边遍历(for循环)边删除为什么删不干净 及三种实现删除的方法
文章目录1、为什么删不干净倒序删迭代器lambda表达式删除为什么说数组边for循环遍历边删除会出现删不干净的情况1、为什么删不干净 先写一个例子:可以先猜一下控制台会打印出什么内容? public class removeIterator {public static void main(String[]…...
环境配置之Keepass
前言很久以前,就有了想要一个自己密码管理器的念头。毕竟,即使浏览器能记住各个网站的账号密码,但是在登录单独客户端的时候,仍然要翻找密码。为了省事,也曾经是一个密码走天下。然后被劫持了QQ给同学发黄色小网站&…...
Java 电话号码的组合
电话号码的字母组合中等给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。示例 1:输入:digits "23…...
MATLAB——将直接型转化为并联型和级联型
题目1(IIR): 已知一个系统的传递函数为: H(z)8−4z−111z−2−2z−31−1.25z−10.75z−2−0.125z−3H(z)\frac{8-4z^{-1}11z^{-2}-2z^{-3}}{1-1.25z^{-1}0.75z^{-2}-0.125z^{-3}}H(z)…...
.NET Framework .NET Core与 .NET 的区别
我们在创建C#程序时,经常会看到目标框架以下的选项,那么究竟有什么区别? 首先 .NET是一种用于构建多种应用的免费开源开发平台,可以使用多种语言,编辑器和库开发Web应用、Web API和微服务、云中的无服务器函数、云原生应用、移动应用、桌面应用、Windows WPF、Windows窗体…...
carla与ros2的自动驾驶算法-planning与control算法开发与仿真
欢迎仪式 carla与ros2的自动驾驶算法-planning与control算法开发与仿真欢迎大家来到自动驾驶Player(L5Player)的自动驾驶算法与仿真空间,在这个空间我们将一起完成这些事情: 控制算法构建基础模块并仿真调试:PID、LQR、Stanley 、MPC、滑膜控…...
corn表达式
简单理解corn表达式:在使用定时调度任务的时候,我们最常用的,就是cron表达式了。通过cron表达式来指定任务在某个时间点或者周期性的执行。cron表达式配置起来简洁方便,无论是Spring的Scheduled还是用Quartz框架,都支持…...
推荐系统中对抗性机器学习-文献综述与未来发展整理分享
对抗学习是一种机器学习技术,旨在通过提供欺骗性输入来欺骗模型。最常见的原因是导致机器学习模型出现故障。大多数机器学习技术旨在处理特定的问题集,其中从相同的统计分布(IID)生成训练和测试数据。当这些模型应用于现实世界时&…...
Proteus8.15安装教程
1、解压Proteus8.15 安装包,然后双击进去,找到setup文件,右键,以管理员身份运行。 2、需要安装一些插件,点击“next”。把插件安装完成。 点击“finfish” 点击“install” 点击“Cancel” 3、如果没有上面步骤&…...
Shell 基本运算符
Shell 和其他编程语言一样,支持多种运算符,包括: 算数运算符关系运算符布尔运算符字符串运算符文件测试运算符 原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。…...
Linux基础命令-sed流编辑器
Linux三剑客-grep命令 Sed 一. 命令介绍 先到帮助文档中查看命令的描述信息 NAME sed - stream editor for filtering and transforming text sed命令是操作、过滤和转换文本内容的强大工具,常用功能包括结合正则表达式对文件实现快速增删改查,其中查询…...
C语言笔试题(1)
#include <stdio.h> #include <stdlib.h> #include <string.h> void getmemory(char *p) { p(char *) malloc(100); strcpy(p,“hello world”); } int main(void) { char *strNULL; getmemory(str); printf(“%s/n”,str); free(str); return 0; } 上述程序…...
网络连接的三种模式
文章目录前言一、三种连接模式介绍二、三种网络连接模式的区别前言 在进行虚拟机配置时,网络连接分为三种模式:桥接模式,NAT模式,主机模式 一、三种连接模式介绍 张三、李四、王五在同一个网段,所以他们之间可以相互…...
大学模拟电路期末考试模拟题详解
(一)选择题 3.4.5.6.7.8.9.10. (二)填空题 1.漂流电流是温度电流,它由少数、载流子形成、其大小与温度有关,而与外加电压无关。 反向电流是由少数载流子形成、其大小与温度有关,而与外加电压无…...
盐城专业做网站的公司/优化游戏卡顿的软件
pt-query-digest_详细使用方法1. pt介绍1.1. 说明1.2. 安装2 语法选项2.1 所有参数2.2 常见参数2.3 事件和属性2.4 分组2.5 过滤2.6 排序2.7 输出选项2.8 DSN(数据源)选项3. 慢日志3.1 事件属性3.2 分析报告3.2.1 第一部分:总体概况说明3.2.2 第二部分:查…...
济南外贸网站建设公司排名/seo关键词如何布局
安全清除你C盘垃圾转载于:https://blog.51cto.com/241998/43808...
wordpress 一栏/系列推广软文范例
题 目 非线性规划的MATLAB解法及其应用(一) 问题描述非线性规划是具有非线性约束条件或目标函数的数学规划,是运筹学的一个重要分支。非线性规划是20世纪50年代才开始形成的一门新兴学科。70年代又得到进一步的发展。非线性规划在工程、管理、经济、科研、军事等方面…...
wordpress cdn 阿里/深圳企业网站制作
LaTeX 中插入图片使其不跑到每页的开头而紧跟插入的文字之后。 此次建模过程中,遇到的一个比较棘手的问题是: 当插入图片时,图片的位置总是会自动跑到当页(或下一页)的最上方,而不是紧跟在其对应的说明文字…...
在线定制/白帽seo
#!/bin/bash #################################################### # version:1.01 # # link:http://www.cnblogs.com/netsa # # qq:402151718 # # author:bekey …...
手机网站做分享到微信/网站服务器搭建与管理
MongoDB安装流程:1.在该网站上下载MongoDB软件包:-url:http://dl.mongodb.org/dl/win32/x86_64-版本(3.4/3.6安装有问题):win32/mongodb-win32-x86_64-v3.4-latest-signed.msi2.安装时自定义安装路径(路径不要有中文字符)3.在安装路径下有一个bin文件,将此路径写入环…...