【JUnit单元测试、反射、注解、动态代理】
第一章 Junit单元测试
知识点–Junit单元测试
目标
- 掌握Junit的使用
讲解
Junit的概念
- 概述 : Junit是Java语言编写的第三方单元测试框架(工具类)
- 作用 : 用来做“单元测试”——针对某个普通方法,可以像main()方法一样独立运行,它专门用于测试某个方法。
Junit的使用步骤
-
1.在模块下创建lib文件夹,把Junit的jar包复制到lib文件夹中
-
2.选中Junit的jar包,右键选中 add as Library,把JUnit4的jar包添加到classPath中
-
3.在测试方法上面写上@Test注解
-
4.执行测试方法
public class Person { @Test public void test1(){ System.out.println("Person test1 方法执行了...."); } @Test public void test2(){ System.out.println("Person test2 方法执行了...."); } }
执行测试方法
-
1.选中方法名—>右键—>选中执行 只执行选中的测试方法
-
2.选中类名----->右键—>选中执行 执行该类中所有的测试方法
-
3.选中模块---- ->右键—>选中all tests 执行 执行该模块中所有的测试方法
-
如何查看测试结果
- 绿色:表示测试通过
- 红色:表示测试失败,有问题
小结
略
知识点–Junit单元测试的注意事项
- 1.测试方法的权限修饰符一定是public
- 2.测试方法的返回值类型一定是void
- 3.测试方法一定没有参数
- 4.测试方法 的声明之上一定要使用@Test注解
小结
略
知识点–Junit其他注解
目标
- Junit其他注解的使用
讲解
- @Before:用来修饰方法,该方法会在每一个测试方法执行之前执行一次。
- @After:用来修饰方法,该方法会在每一个测试方法执行之后执行一次。
- @BeforeClass:用来静态修饰方法,该方法会在所有测试方法之前执行一次,而且只执行一次。
- @AfterClass:用来静态修饰方法,该方法会在所有测试方法之后执行一次,而且只执行一次。
public class Demo {
/*
- @Before:用来修饰方法,该方法会在每一个测试方法执行之前执行一次。
- @After:用来修饰方法,该方法会在每一个测试方法执行之后执行一次。
- @BeforeClass:用来静态修饰方法,该方法会在所有测试方法之前执行一次,而且只执行一次。
- @AfterClass:用来静态修饰方法,该方法会在所有测试方法之后执行一次,而且只执行一次。
*/
@BeforeClass
public static void bc1(){
System.out.println("bc1 方法");
}
@Before
public void b1(){
System.out.println("b1 方法");
}
@Test
public void show1(){
System.out.println("show1 方法");
}
@Test
public void show2(){
System.out.println("show2 方法");
}
@After
public void a1(){
System.out.println("a1 方法");
}
@AfterClass
public static void ac1(){
System.out.println("ac1 方法");
}
}
小结
略
知识点–Junit断言
目标
- Junit断言
路径
- Junit断言
讲解
- 断言:预先判断某个条件一定成立,如果条件不成立,则直接报错。 使用Assert类中的assertEquals()方法
- 案例:
public class Demo02 {
@Test
public void addTest(){
//测试
int add = add(3, 6);
//断言判断结果
//第一个参数表示期望值
//第二个参数表示实际值
//如果结果正确的就测试通过,如果结果错误的,就会报错
Assert.assertEquals(9,add);
}
//加法
//这个代码的语法没问题,也没有异常。他是逻辑错误,系统不知道你要算的是加法
public int add(int a, int b){
int sum = a * b;
return sum;
}
}
小结
略
第二章 反射
知识点–类加载器
目标
- 理解类在什么时候加载,以及如何获取类加载器
路径
- 类的加载
- 类的加载时机
- 类加载器
讲解
类的加载
- 当我们的程序在运行时,第一次使用某个类的时候,会将此类的class文件读取到内存,并将此类的所有信息存储到一个Class对象中
类的加载时机
-
创建类的实例。
-
类的静态变量,或者为静态变量赋值。
-
类的静态方法。
-
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
-
初始化某个类的子类。
-
直接使用java.exe命令来运行某个主类。
以上六种情况的任何一种,都可以导致JVM将一个类加载到方法区。
public class Person {
static {
System.out.println("Person 静态代码块");
}
static int num = 10;
public static void method(){
System.out.println("Person 静态方法");
}
}
public class Student extends Person {
}
public class Test {
public static void main(String[] args) throws Exception{
// 1. 创建类的实例。
//Person p = new Person();
// 2. 类的静态变量,或者为静态变量赋值。
//System.out.println(Person.num);// 10
// 3. 类的静态方法。
//Person.method();
// 4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
//Class<?> c = Class.forName("com.itheima.demo1_类加载的时机.Person");
// 5. 初始化某个类的子类。
//Student stu = new Student();
// 6. 直接使用java.exe命令来运行某个主类。
}
}
类加载器
类加载器:是负责将磁盘上的某个class文件读取到内存并生成Class的对象。
- Java中有三种类加载器,它们分别用于加载不同种类的class:
- 启动类加载器(Bootstrap ClassLoader):用于加载系统类库<JAVA_HOME>\bin目录下的class,例如:rt.jar。
- 扩展类加载器(Extension ClassLoader):用于加载扩展类库<JAVA_HOME>\lib\ext目录下的class。
- 应用程序类加载器(Application ClassLoader):用于加载我们自定义类的加载器。
package com.itheima.demo6_类加载器;
/**
* @Author:pengzhilin
* @Date: 2020/9/24 9:31
*/
public class Test {
public static void main(String[] args) {
// 如何获取类加载器: 使用类的class对象调用getClassLoader()方法
// 获取Person类的类加载器
ClassLoader c1 = Person.class.getClassLoader();
System.out.println(c1);// ClassLoaders$AppClassLoader
// 获取String类的类加载器
ClassLoader c2 = String.class.getClassLoader();
System.out.println(c2);// null
//API中说明:一些实现可能使用null来表示引导类加载器。 如果此类由引导类加载器加载,则此方法将在此类实现中返回null
}
}
小结
- 类加载的时机
- 如何获取类加载器: 使用类的class对象调用getClassLoader()方法
知识点–反射的概述
目标
- 理解反射的概念
路径
- 反射的引入
- 反射的概念
- 使用反射操作类成员的前提
- 反射在实际开发中的应用
讲解
反射的引入
- 问题:IDEA中的对象是怎么知道类有哪些属性,哪些方法的呢?
通过反射技术对类进行了解剖得到了类的所有成员。
反射的概念
反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造方法)
使用反射操作类成员的前提
要获得该类字节码文件对象,就是Class对象
对于各个类在内存中的字节码,例如person类的字节码,Arraylist类的字节码等等,一个类被类加载器加载到内存中时,这个空间里面的内容就是类的字节码,不同类的字节码文件是不同的,这一个个空间分别用一个对象来表示,这些对象具备相同的类型;
变量,方法,构造方法,修饰符,包等;;这些信息就是用对应的实例对象来表示,他们是Field,Method,Contructor,Package等等
①反射获取对象实例的一般方式是class对象->(declared)Constructor(class...parameterTypes)对象->newInstance(Object... initargs)对象实例
②反射获取对象成员方法的一般方式是class->(declared)Method(String name(方法名),class...args)->invoke(Object obj(方法对象), Object... args(方法参数))
③反射获得类的成员变量一般方式为:Class->(declared)Field(String name(成员变量名))->(赋值)set(对象实例,要赋的值) 或者 (取值) get((对象实例))
反射在实际开发中的应用
* 开发IDE(集成开发环境),比如IDEA,Eclipse
* 各种框架的设计和学习 比如Spring,Hibernate,Struct,Mybaits....
小结
- 通过反射技术去获取一个类的成员变量,成员方法,构造方法…,并可以访问
知识点-- 获取Class对象
目标
- 能够获取一个类的Class对象
路径
- 通过类名.class获得
- 通过对象名.getClass()方法获得
- 通过Class类的静态方法获得: static Class forName(“类全名”)
讲解
* 方式1: 通过类名.class获得
* 方式2:通过对象名.getClass()方法获得
* 方式3:通过Class类的静态方法获得: static Class forName("类全名")
* 每一个类的Class对象都只有一个。
- 示例代码
public class Test {
public static void main(String[] args) throws Exception{
// 方式1: 通过类名.class获得
Class<Person> c1 = Person.class;
// 方式2:通过对象名.getClass()方法获得
Person p = new Person();
Class<? extends Person> c2 = p.getClass();
// 方式3:通过Class类的静态方法获得: static Class forName("类全名")
Class<?> c3 = Class.forName("com.itheima.demo3_获取类的Class对象.Person");
// 每一个类的Class对象都只有一个。
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c1 == c2);// true
System.out.println(c2 == c3);// true
}
}
小结
略
知识点–Class类常用方法
目标
- Class类的常用方法
路径
- Class类的常用方法
讲解
String getSimpleName(); 获得类名字符串:类名
String getName(); 获得类全名:包名+类名
T newInstance() ; 创建Class对象关联类的对象,已过时
- 示例代码
public class Test {
public static void main(String[] args) throws Exception{
// 获得Person类的Class对象
Class<Person> c = Person.class;
// String getSimpleName(); 获得类名字符串:类名
String simpleName = c.getSimpleName();
System.out.println("类名:"+simpleName);// 类名:Person
// String getName(); 获得类全名:包名+类名
String name = c.getName();
System.out.println("类的全名:"+name);// 类的全名:com.itheima.demo4_Class类常用方法.Person
// T newInstance() ; 创建Class对象关联类的对象
Person p = c.newInstance();
System.out.println("p:"+p);
}
}
小结
略
知识点–反射之操作构造方法
目标
- 通过反射获取类的构造方法,并执行构造方法
路径
- Constructor类概述
- 通过反射获取类的构造方法
- 通过反射执行构造方法
讲解
类概述
Constructor类概述
* 类中的每一个构造方法都是一个Constructor类的对象
通过反射获取类的构造方法
Class类中与Constructor相关的方法
1. Constructor getConstructor(Class... parameterTypes)
* 根据参数类型获得对应的Constructor对象。
* 只能获得public修饰的构造方法
2. Constructor getDeclaredConstructor(Class... parameterTypes)-----推荐
* 根据参数类型获得对应的Constructor对象
* 可以是public、protected、(默认)、private修饰符的构造方法。
3. Constructor[] getConstructors()
获得类中的所有构造方法对象,只能获得public的
4. Constructor[] getDeclaredConstructors()-----推荐
获得类中的所有构造方法对象
可以是public、protected、(默认)、private修饰符的构造方法。
通过反射执行构造方法(创建对象)
Constructor对象常用方法
1. T newInstance(Object... initargs)
根据指定的参数创建对象
2. void setAccessible(true)
设置"暴力反射"——是否取消权限检查,true取消权限检查,false表示不取消
示例代码
public class Person {
public String name;
public int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args)throws Exception {
/*
Class类中与Constructor相关的方法
1. Constructor getDeclaredConstructor(Class... parameterTypes)
* 根据参数类型获得对应的Constructor对象
* 可以是public、protected、(默认)、private修饰符的构造方法。
2. Constructor[] getDeclaredConstructors()
获得类中的所有构造方法对象
可以是public、protected、(默认)、private修饰符的构造方法。
Constructor对象常用方法
1. T newInstance(Object... initargs)
根据指定的参数创建对象
2. void setAccessible(true)
设置"暴力反射"——是否取消权限检查,true取消权限检查,false表示不取消
*/
// 获取Person类的Class对象
Class<Person> c = Person.class;
// 获取指定的构造方法对象:
Constructor<Person> con1 = c.getDeclaredConstructor();
System.out.println(con1);
Constructor<Person> con2 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(con2);
Constructor<Person> con3 = c.getDeclaredConstructor(int.class);
System.out.println(con3);
System.out.println("=============================");
// 获取所有的构造方法对象:
Constructor<?>[] arr = c.getDeclaredConstructors();
for (Constructor<?> con : arr) {
System.out.println(con);
}
System.out.println("=============================");
// 反射执行获取的构造方法:
// 执行con1表示的构造方法创建Person对象
Person p1 = con1.newInstance();
System.out.println("p1:"+p1);
// 执行con2表示的构造方法创建Person对象
Person p2 = con2.newInstance("itheima", 14);
System.out.println("p2:"+p2);
// 执行con3表示的构造方法创建Person对象
// 取消con3表示的构造方法的权限检查
con3.setAccessible(true);
Person p3 = con3.newInstance(18);
System.out.println("p3:"+p3);
}
}
小结
获取类的构造方法对象: Class类
Constructor getDeclaredConstructor(Class... parameterTypes)
* 根据参数类型获得对应的Constructor对象
* 可以是public、protected、(默认)、private修饰符的构造方法。
Constructor[] getDeclaredConstructors()
获得类中的所有构造方法对象
可以是public、protected、(默认)、private修饰符的构造方法。
Constructor对象常用方法
1. T newInstance(Object... initargs)
根据指定的参数创建对象
2. void setAccessible(true)
设置"暴力反射"——是否取消权限检查,true取消权限检查,false表示不取消
知识点–反射之操作成员方法
目标
- 通过反射获取类的成员方法,并执行成员方法
路径
- Method类概述
- 通过反射获取类的成员方法
- 通过反射执行成员方法
讲解
Method类概述
Method类概述
* 每一个成员方法都是一个Method类的对象。
通过反射获取类的成员方法
Class类中与Method相关的方法
* Method getMethod(String name,Class... args);
* 根据方法名和参数类型获得对应的构造方法对象,只能获得public的
* Method getDeclaredMethod(String name,Class... args);----->推荐
* 根据方法名和参数类型获得对应的构造方法对象,包括public、protected、(默认)、private的
* Method[] getMethods();
* 获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的
* Method[] getDeclaredMethods();----->推荐
* 获得类中的所有成员方法对象,返回数组,只获得本类的,包括public、protected、(默认)、private的
通过反射执行成员方法
Method对象常用方法
* Object invoke(Object obj, Object... args)
* 调用指定对象obj的该方法
* args:调用方法时传递的参数
* void setAccessible(true)
设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消
示例代码
public class Person {
public void show1() {
System.out.println("show1 方法...");
}
public void show2(int num) {
System.out.println("show2 方法...num: " + num);
}
public int show3(int num) {
System.out.println("show3 方法...num: " + num);
return 1000;
}
private void show4(String str) {
System.out.println("show4 方法...str: " + str);
}
}
public class Test {
public static void main(String[] args) throws Exception{
/*
Class类中与Method相关的方法:
Method getDeclaredMethod(String name,Class... args);----->推荐
根据方法名和参数类型获得对应的构造方法对象,包括public、protected、(默认)、private的
Method[] getDeclaredMethods();----->推荐
获得类中的所有成员方法对象,返回数组,只获得本类的,包括public、protected、(默认)、private的
Method对象常用方法
Object invoke(Object obj, Object... args)
调用指定对象obj的该方法
args:调用方法时传递的参数
void setAccessible(true)
设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消
*/
// 获取Person类的Class对象
Class<Person> c = Person.class;
// 获取指定的成员方法:
// 获取show1方法的Method对象
Method m1 = c.getDeclaredMethod("show1");
System.out.println("m1:"+m1);
// 获取show2方法的Method对象
Method m2 = c.getDeclaredMethod("show2",int.class);
System.out.println("m2:"+m2);
// 获取show3方法的Method对象
Method m3 = c.getDeclaredMethod("show3",int.class);
System.out.println("m3:"+m3);
// 获取show4方法的Method对象
Method m4 = c.getDeclaredMethod("show4",String.class);
System.out.println("m4:"+m4);
System.out.println("==============================");
// 获取所有的成员方法:
Method[] arr = c.getDeclaredMethods();
for (Method m : arr) {
System.out.println(m);
}
System.out.println("==============================");
// 通过反射执行成员方法:
Person p = c.newInstance();
// 执行m1表示的show1方法
m1.invoke(p);
// 执行m2表示的show2方法
m2.invoke(p,100);
// 执行m3表示的show3方法
Object res = m3.invoke(p, 200);
System.out.println("res:"+res);// 1000
// 执行m4表示的show4方法
// 取消m4表示的方法的权限检查
m4.setAccessible(true);
m4.invoke(p,"Java");
}
}
小结
Class类中与Method相关的方法
获取单个成员方法:
Method getDeclaredMethod(String name,Class...args);
根据方法名和参数类型获得对应的构造方法对象,包括public、protected、(默认)、private的
获取所有成员方法:
Method[] getDeclaredMethods();
获得类中的所有成员方法对象,返回数组,只获得本类的,包括public、protected、(默认)、private的
Method对象常用方法:
Object invoke(Object obj, Object... args)
* 调用指定对象obj的该方法
* args:调用方法时传递的参数
void setAccessible(true)
设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消
知识点–反射之操作成员变量
目标
- 通过反射获取类的成员变量,并访问成员变量
路径
- Field类概述
- 通过反射获取类的成员变量
- 通过反射访问成员变量
讲解
Field类概述
Field类概述
* 每一个成员变量都是一个Field类的对象。
通过反射获取类的成员变量
Class类中与Field相关的方法
* Field getField(String name);
* 根据成员变量名获得对应Field对象,只能获得public修饰
* Field getDeclaredField(String name);----->推荐
* 根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的
* Field[] getFields();
* 获得所有的成员变量对应的Field对象,只能获得public的
* Field[] getDeclaredFields();---->推荐
* 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的
通过反射访问成员变量
Field对象常用方法
给对象的属性赋值的方法
void set(Object obj, Object value) ----->推荐
void setInt(Object obj, int i)
void setLong(Object obj, long l)
void setBoolean(Object obj, boolean z)
void setDouble(Object obj, double d)
...
获取对象属性的值的方法
Object get(Object obj) ----->推荐
int getInt(Object obj)
long getLong(Object obj)
boolean getBoolean(Object ob)
double getDouble(Object obj)
void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。 ----->推荐
Class getType(); 获取属性的类型,返回Class对象。
set()方法都是给对象obj的属性设置使用,针对不同的类型选取不同的方法。
get()方法是获取对象obj对应的属性值的,针对不同的类型选取不同的方法。
示例代码
public class Student {
public String name;
private int age;
}
public class Test {
public static void main(String[] args) throws Exception{
/*
Class类中与Field相关的方法
Field getDeclaredField(String name);----->推荐
* 根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的
Field[] getDeclaredFields();---->推荐
* 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的
Field对象常用方法
给对象的属性赋值的方法
void set(Object obj, Object value) ----->推荐
获取对象属性的值的方法
Object get(Object obj) ----->推荐
void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。 ----->推荐
*/
// 获取Student类的Class对象
Class<Student> c = Student.class;
// 获取指定的成员变量:
// 获取name成员变量对象
Field f1 = c.getDeclaredField("name");
System.out.println("f1:"+f1);
// 获取age成员变量对象
Field f2 = c.getDeclaredField("age");
System.out.println("f2:"+f2);
System.out.println("=====================");
// 获取所有的成员变量对象:
Field[] arr = c.getDeclaredFields();
for (Field field : arr) {
System.out.println(field);
}
System.out.println("=====================");
// 得到Student对象
Student stu = c.newInstance();
// 给成员变量赋值:
// 给f1表示的name成员变量赋值
f1.set(stu,"itheima");
// 给f2表示的age成员变量赋值
// 取消f2表示的age成员变量的权限检查
f2.setAccessible(true);
f2.set(stu,14);
// 获取成员变量的值:
// 获取f1表示的name成员变量的值
Object res1 = f1.get(stu);
System.out.println("res1:"+res1);// itheima
// 获取f2表示的age成员变量的值
Object res2 = f2.get(stu);
System.out.println("res2:"+res2);// 14
}
}
小结
Class类中与Field相关的方法:
* Field getDeclaredField(String name);
* 根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的
* Field[] getDeclaredFields();
* 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的
Field对象常用方法:
void set(Object obj, Object value) 给对象的属性设置值
Object get(Object obj) 获取对象的属性值
void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。
第三章 注解
知识点-注解概述
目标
- 掌握什么是注解, 注解的作用
路径
- 注解概述
- 注解的作用
讲解
注解概述
-
注解(annotation),是一种代码级别的说明,和类 接口平级关系.
-
注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无标记,看你的程序有什么标记,就去干相应的事
-
我们之前使用过的注解:
1).@Override:子类重写方法时——编译时起作用
2).@FunctionalInterface:函数式接口——编译时起作用
3).@Test:JUnit的测试注解——运行时起作用
-
注解的作用
-
生成帮助文档**:**@author和@version
-
执行编译期的检查 例如:@Override
-
框架的配置(框架=代码+配置)
- 具体使用请关注框架课程的内容的学习。
小结
- 注解用在“源码中”,作为一个“标记”。给“注解解析器”看的,告诉“注解解析器”怎样编译、运行下面的代码。
- 开发中,我们一般都是使用注解
知识点-JDK提供的三个基本的注解
目标
- 掌握JDK中提供的三个基本的注解
路径
- 三个基本的注解
讲解
@Override:描述方法的重写.
@SuppressWarnings:压制\忽略警告.
@Deprecated:标记过时
public class Test {
public static void main(String[] args) throws Exception{
/*
@Override:描述方法的重写.
@SuppressWarnings:压制\忽略警告.
@Deprecated:标记过时
*/
@SuppressWarnings("all")
int num;
Test t = Test.class.newInstance();
show();//删除线,标记该方法以及过时,慎用
}
@Override
public String toString() {
int num;
return "toString";
}
@Deprecated9(标记过时)
public static void show(){
}
}
小结
- @Override: 重写父类的方法
- @SuppressWarnings: 压制警告
- @Deprecated: 标记方法的过时
知识点-自定义注解
目标
- 掌握自定义注解和定义注解属性
路径
- 自定义注解格式
- 定义注解属性
讲解
自定义注解语法
public @interface 注解名{
属性
}
- 示例代码
/**
* 定义了注解
*
*/
public @interface Annotation01 {
}
注解属性
格式
数据类型 属性名();
属性类型
1.基本类型
2.String
3.Class类型
4.注解类型
5. 枚举类型
6.以上类型的一维数组类型
- 示例代码
public @interface Annotation01 {
// 1.基本数据类型(4类8种)
int a();
double b();
// 2.String类型
String c();
// 3.Class类型
Class d();
// 4.注解类型
Annotation02 f();
// 5.枚举类型
Sex e();
// 6.以上类型的一维数组类型
int[] g();
double[] h();
String[] i();
Sex[] j();
Annotation02[] k();
}
小结
略
知识点-- 使用注解----重点
目标
- 能够使用注解并给注解属性赋值
路径
- 使用注解并给注解属性赋值
讲解
使用注解:
如果一个注解中有属性,那么使用注解的时候一定要给注解属性赋值
如果一个注解没用属性,那么就不需要给注解属性赋值,直接使用即可
如何给注解属性赋值:
@注解名(属性名=值,属性名2=值2)
案例演示
// 不带属性的注解
public @interface MyAnnotation1 {
}
// 有属性的注解
public @interface MyAnnotation2 {
// 属性
int num();
String[] arr();
}
@MyAnnotation1
@MyAnnotation2(num=10,arr={"itheima","itcast"})
public class Test {
@MyAnnotation1
@MyAnnotation2(num=10,arr={"itheima","itcast"})
String str;
@MyAnnotation1
@MyAnnotation2(num=10,arr={"itheima","itcast"})
public static void main(String[] args) {
/*
使用注解:
无属性的注解: 如果一个注解没用属性,那么就不需要给注解属性赋值,直接使用即可
有属性的注解: 如果一个注解中有属性,那么使用注解的时候一定要给注解属性赋值
格式: @注解名(属性名=属性值,属性名=属性值,...)
*/
@MyAnnotation1
@MyAnnotation2(num=10,arr={"itheima","itcast"})
int num = 10;
}
}
小结
略
知识点–给注解属性赋值的注意事项-----重点
目标
- 理解给注解属性赋值的注意事项
路径
讲解
- 一旦注解有属性了,使用注解的时候,属性必须有值
- 若属性类型是一维数组的时候,当数组的值只有一个的时候可以省略{}
- 如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略
- 注解属性可以有默认值 格式:属性类型 属性名() defaul t 默认值;
public @interface MyAnnotation1 {
int num();
String[] arr();
}
public @interface MyAnnotation2 {
int value();
}
public @interface MyAnnotation3 {
String[] value();// 只有一个属性,并且属性名为value
}
public @interface MyAnnotation4 {
int num() default 10;
}
public class Test {
public static void main(String[] args) {
/*
给注解属性赋值的注意事项:
- 一旦注解有属性了,使用注解的时候,属性必须有值
- 若属性类型是一维数组的时候,当数组的值只有一个值的时候可以省略{}
- 如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略
- 注解属性可以有默认值 格式:属性类型 属性名() default 默认值;
*/
}
// 若属性类型是一维数组的时候,当数组的值只有一个值的时候可以省略{}
//@MyAnnotation1(num=10,arr={"itheima"})
@MyAnnotation1(num=10,arr="itheima")
public void show1(){
}
// 如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略
//@MyAnnotation2(value=10)
@MyAnnotation2(10)
public void show2(){
}
// 若属性类型是一维数组的时候,当数组的值只有一个值的时候可以省略{}
// 如果注解中只有一个属性,并且属性名为value,那么使用注解给注解属性赋值的时候,注解属性名value可以省略
//@MyAnnotation3(value={"itheima"})
//@MyAnnotation3(value="itheima")
@MyAnnotation3("itheima")
public void show3(){
}
// 注解属性可以有默认值 格式:属性类型 属性名() default 默认值;
//@MyAnnotation4
@MyAnnotation4(num=20)
public void show4(){
}
}
小结
略
知识点-元注解
目标
- 能够说出常用的元注解及其作用
路径
- 什么是元注解
- 常见的元注解
讲解
什么是元注解
定义在注解上的注解
常见的元注解
@Target:表示该注解作用在什么上面(位置),默认注解可以在任何位置. 值为:ElementType的枚举值
METHOD:方法
TYPE:类 接口
FIELD:字段
CONSTRUCTOR:构造方法声明
@Retention:定义该注解保留到那个代码阶段, 值为:RetentionPolicy类型,默认只在源码阶段保留
SOURCE:只在源码上保留(默认)
CLASS:在源码和字节码上保留
RUNTIME:在所有的阶段都保留
.java (源码阶段) ----编译—> .class(字节码阶段) ----加载内存–> 运行(RUNTIME)
案例:
package com.itheima.demo17_元注解;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author:pengzhilin
* @Date: 2020/9/24 15:14
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
// 限制该注解只能在方法上和类上使用
// 设置注解保留到运行阶段
}
package com.itheima.demo17_元注解;
/**
* @Author:pengzhilin
* @Date: 2020/9/24 15:12
*/
@MyAnnotation1
public class Test {
//@MyAnnotation1 // 编译报错
int num;
@MyAnnotation1
public static void main(String[] args) {
//@MyAnnotation1 // 编译报错
String str;
}
}
小结
略
知识点-注解解析
目标
- 理解常见注解解析方法
路径
- 使用注解解析
讲解
java.lang.reflect.AnnotatedElement接口: Class、Method、Field、Constructor等实现了AnnotatedElement
-
T getAnnotation(ClassannotationType):得到指定类型的注解引用。没有返回null。
-
boolean isAnnotationPresent(Class<?extends Annotation> annotationType):判断指定的注解有没有。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
String name();
int age();
}
public class Test {
@MyAnnotation1(name="张三",age=18)
public void show1(){
System.out.println("show1方法执行了....");
}
public void show2(){
System.out.println("show2方法执行了....");
}
public static void main(String[] args) throws Exception{
/*
java.lang.reflect.AnnotatedElement接口: Class、Method、Field、Constructor等实现了AnnotatedElement
- T getAnnotation(Class<T> annotationType):得到指定类型的注解引用。没有返回null。
- boolean isAnnotationPresent(Class<?extends Annotation> annotationType):判断指定的注解有没有。
*/
// 需求:1.获取show1方法上面的注解对象
// 1.1 得到Test类的Class对象
Class<?> c = Class.forName("com.itheima.demo12_注解解析.Test");
// 1.2 获得show1方法的Method对象
Method show1M = c.getDeclaredMethod("show1");
// 1.3 根据Method对象调用getAnnotation()方法得到注解对象
MyAnnotation1 a1 = show1M.getAnnotation(MyAnnotation1.class);
System.out.println(a1.name());
System.out.println(a1.age());
System.out.println("======================");
// 2.需求: 判断某个方法上是否有MyAnnotation1注解
// 判断show1方法上是否有MyAnnotation1注解
boolean res1 = show1M.isAnnotationPresent(MyAnnotation1.class);
System.out.println(res1);// true
// 判断show2方法上是否有MyAnnotation1注解
Method show2M = c.getDeclaredMethod("show2");
boolean res2 = show2M.isAnnotationPresent(MyAnnotation1.class);
System.out.println(res2);// false
}
}
小结
略
实操–完成注解的MyTest案例
需求
在一个类(测试类,TestDemo)中有三个方法,其中两个方法上有@MyTest,另一个没有.还有一个主测试类(MainTest)中有一个main方法. 在main方法中,让TestDemo类中含有@MyTest方法执行. 自定义@MyTest, 模拟单元测试.
思路分析
-
定义两个类和一个注解
-
在MainTest的main()方法里面:
//1.获得TestDemo字节码对象
//2.反射获得TestDemo里面的所有的方法
//3.遍历方法对象的数组. 判断是否有@MyTest(isAnnotationPresent)
//4.有就执行(method.invoke())
代码实现
- MyTest.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
- TestDemo.java
public class TestDemo {
@MyTest
public void show1(){
System.out.println("show1方法执行了...");
}
@MyTest
public void show2(){
System.out.println("show2方法执行了...");
}
public void show3(){
System.out.println("show3方法执行了...");
}
}
- MainTest.java
public class MainTest {
public static void main(String[] args) throws Exception {
// 让第一个类中含有@MyTest注解的方法执行
// 1.获取TestDemo类的字节码对象
Class<TestDemo> clazz = TestDemo.class;
// 2.使用字节码对象获取该类中所有方法对象
Method[] methods = clazz.getDeclaredMethods();
// 3.循环遍历所有方法对象
for (Method method : methods) {
// 4.在循环中,判断遍历出来的方法对象是否含有@MyTest注解
boolean res = method.isAnnotationPresent(MyTest.class);
if (res) {
// 5.如果有,就调用该方法执行
method.invoke(clazz.newInstance());
}
}
}
}
小结
略
第四章 动态代理
目标
- 能够使用动态代理生成一个代理对象
路径
- 代理模式概念
- 动态代理
- 动态代理相关api介绍
- 案例演示
讲解
代理模式概述
为什么要有“代理”?生活中就有很多代理的例子,例如,我现在需要出国,但是我不愿意自己去办签证、预定机票和酒店(觉得麻烦 ,那么就可以找旅行社去帮我办,这时候旅行社就是代理,而我自己就是被代理了。
代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者, 所以代理模式包含了3个角色: 被代理角色 代理角色 抽象角色(协议)
静态代理:(装饰者模式)
public interface FindHappy {
void happy();
}
public class JinLian implements Happy {
public void happy(){
System.out.println("金莲在happy...");
}
}
public class WangPo implements Happy{
// 成员变量
JinLian jl;
// 构造方法
public WangPo(JinLian jl) {
this.jl = jl;
}
// 成员方法
@Override
public void happy() {
System.out.println("王婆以做衣服的名义开好房间,并把2人约到房间里...");
// 金莲happy
jl.happy();
System.out.println("王婆打扫战场...");
}
}
public class XiMen {
public static void main(String[] args) {
/*
案例: 金莲要找西门happy
代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者,
所以代理模式包含了3个角色: 被代理角色 代理角色 抽象角色(协议)
*/
// 不请代理: 金莲直接找西门happy
// 创建金莲对象
JinLian jl = new JinLian();
// happy
// jl.happy();
// 请代理: 静态代理,代理类真实存在
Happy wp = new WangPo(jl);// wp:代理对象 WangPo类: 代理类 Happy接口: 协议 JinLian: 被代理类
wp.happy();
}
}
动态代理介绍
-
概述 : 动态代理就是直接通过反射生成一个代理对象,代理对象所属的类是不需要存在的
-
动态代理的获取:
jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象
动态代理相关api介绍
Java.lang.reflect.Proxy类可以直接生成一个代理对象
- static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
- 参数1:ClassLoader loader 被代理对象的类加载器
- 参数2:Class<?>[] interfaces 被代理对象的要实现的接口
- 参数3:InvocationHandler h (接口)执行处理类
- 返回值: 代理对象
- 前2个参数是为了帮助在jvm内部生成被代理对象的代理对象,第3个参数,用来监听代理对象调用方法,帮助我们调用方法
- InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行
- 参数1:代理对象(慎用)
- 参数2:当前执行的方法
- 参数3:当前执行的方法运行时传递过来的参数
返回值:当前方法执行的返回值
案例演示
案例1: 代理方法无参数
public interface Happy {// 协议,被代理者需要代理的方法,就定义在这里,然后让代理者和被代理者去实现
// 被代理者实现: 为了确保和代理者实现的方法一致
// 代理者实现: 为了增强被代理者的这些方法
public abstract void happy();
}
public class JinLian implements Happy {
public void happy(){
System.out.println("金莲在happy...");
}
}
public class Test {
public static void main(String[] args) {
/*
Proxy类可以直接生成一个代理对象
- static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
- 参数1:ClassLoader loader 被代理对象的类加载器
- 参数2:Class<?>[] interfaces 被代理对象的要实现的接口
- 参数3:InvocationHandler h (接口)执行处理类
- 返回值: 代理对象
- 前2个参数是为了帮助在jvm内部生成被代理对象的代理对象,第3个参数,用来监听代理对象调用方法,帮助我们调用方法
- InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行
- 参数1:代理对象(慎用)
- 参数2:当前执行的方法
- 参数3:当前执行的方法运行时传递过来的参数
- 返回值:当前方法执行的返回值
*/
// 创建金莲对象
JinLian jl = new JinLian();
// 看不到代理类,直接就动态生成一个代理对象
// 得到被代理对象的类加载器
ClassLoader cLoader = JinLian.class.getClassLoader();
// 被代理对象的要实现的接口
Class<?>[] arr = JinLian.class.getInterfaces();
// 确定:返回的代理对象一定实现了接口
FindHappy proxy = (FindHappy)Proxy.newProxyInstance(cLoader, arr, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 当代理对象调用方法,就会来到这里执行代码
// 参数1:返回的代理对象,一般不用
// 参数2:代理对象调用的方法
// 参数3:代理对象调用的方法传入的实际参数
// 返回值:代理对象调用的方法需要的返回值
System.out.println("王婆以做衣服的名义把房间开好...");//增强的方法
// 金莲调用happy方法
method.invoke(jl);
System.out.println("王婆打扫战场...");//增强的方法
return null;
}
});
System.out.println(1);
// 代理对象
proxy.happy();
System.out.println(2);
}
}
小结
略