1.反射 (反射方法 构造方法)
2.注解(使用注解)
3.动态代理(框架底层的,作用)
路径
类的加载
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a1m32rBm-1660142789503)(imgs/03_%E8%AF%BB%E5%8F%96class.png)]
说明:Class对象是指java.lang.Class类的对象,此类由Java类库提供,专门用于存储类型的信息
类的加载机制
在以下情况下会加载类:
创建一个类的对象(第1次)
调用类的静态方法,静态变量 (第1次)
使用一个类的子类时 (第1次)
通过反射进行加载类
java命令执行某一个类 (第1次)运行java程序
public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello World!");
}
}
//编译: javac HelloWorld.java //生成:HelloWorld.class 字节码文件
//运行: Java HelloWorld //使用java命令执行HelloWorld类(默认调用main方法)
//底层: HelloWorld.main(null);
小结
问题1:Student.class文件中都包含什么内容?
在jvm执行某个类时,如果该类是第一次被执行:
目标
路径
类加载器的作用
什么是类加载器?
类加载器的作用:
类加载器的分类:
Java中有三种类加载器,它们分别用于加载不同种类的class:
获取类加载器的方式
来自Class类型获取类加载器的方法:
public ClassLoader getClassLoader() //返回该类的类加载器
//有些实现可能使用null来表示引导类加载器(启动类加载器)
//如果该类由引导类加载器(启动类加载器)加载,则此方法在这类实现中将返回 null
代码实践:
//自定义类型
class Student{
private int age;
}
//测试类
public class Demo01 {
public static void main(String[] args){
//获取自定义类的类加载器
//1. 先获取Student对应的Class对象
Class<Student> cls = Student.class;
//2. 再通过该class对象获取类加载器
ClassLoader loader = cls.getClassLoader();
System.out.println("loader = "+loader);//ClassLoaders$AppClassLoader@2437c6dc
//获取String类的类加载器
ClassLoader loader2 = String.class.getClassLoader();
System.out.println("loader2 = " + loader2);
}
}
小结
类加载器的作用:
类加载器可以分为3种:
目标
路径
3种类加载器的关系
![ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7YiwtGsZ-1660142789505)(imgs/04_%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8.png)]](https://1000bd.com/contentImg/2022/08/14/165603798.png)
从上图可知,三种类加载器存在一定的关系:
结论:这种关系称为类加载器的"双亲委派模型"
双亲委派机制
"双亲委派模型"的工作机制:
“双亲委派模型"中,除了顶层的启动类加载器外,其余的类加载器都应当有自己的"父级类加载器”
小结
双亲委派机制:
目标
路径
反射
反射技术:
结论:反射技术就是把一个类进行了解剖,然后获取到 构造方法、成员变量、成员方法
反射技术的应用案例:
想要使用反射技术有一个必备条件:
反射技术的作用
使用反射技术,可以对类进行解剖,可以获取到类中的:构造方法、成员变量、成员方法
反射技术的作用:
示例:
"cn.itcast.pojo.Student" stu = new cn.itcast.pojo.Student();
给一个字符串:"cn.icast.pojo.Student"
创建一个对象:????? //使用new做不到
使用反射技术可以实现
示例:
给一个Student.class
程序在运行中,不能停止, 动态的获取一个Student.class
//使用反射技术,可以在JVM运行状态下,动态的获取并加载Student.class
小结
反射技术 :对类进行解剖的技术
反射技术的作用:可以不通过传统方式,来实现类的实例化、方法的调用
目标
路径
Class类
Class就是用来描述正在运行的java类型
Class类的实例表示Java中任何正在运行的类型,每一个类型都有与之对应的Class对象
比如:类,接口,枚举,注解,数组,基本数据类型,void 都有与之对应的Class对象
类名.class
接口名.class
int.class
boolean.class
array.class
获取Class对象
获取Class类对象的方式有3种:
方式一:类型名.class //Student.class
方式二:对象.getClass() //对象名.getClass()
方式三:Class.forName(String className) //className是全路径类名 = 包名+类型名
//方式1:类型名.class
//应用场景: 当类名明确时,可以直接使用"类名.class"
Class clz = String.class
Class clz = int.class
Class clz = double.class
//方式2:对象.getClass()
//应用场景:通常是应用方法中
public void method(Student stu){
Class clz = stu.getClass();
}
//方式3: Class.forName("类的全名称");//带有包名的类
//应用场景: 通常使用在读取配置文件中的类型
pro.properties文件
--------------文件内容------------------
className=cn.icast.pojo.Student
---------------------------------------
//代码实现
ResourceBundler r = ResourceBundler.getBundler("pro");
String className = r.getString("className");//"cn.icast.pojo.Student"
Class StudentClass = Class.forName(className);//className="cn.icast.pojo.Student"
//当获取到Class对象了,就可以对类进行解剖了
Class类中的常用方法
String getSimpleName() // 获得类名字符串:类名
String getName() // 获得类全名:包名+类名
T newInstance() // 创建Class对象关联类的对象 (前提:类中有一个无参构造方法)
//示例:
Studentod类 cn.itcast.pojo.Student //public Student(){}
Class stuClass = Student.class;
Object obj = stuClass.newInstance();//调用Student() 创建Student对象
Student stu = (Student) obj;
代码实现:
public class Test01 {
@Test
public void testMethod3() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class stuClass = Class.forName("com.itheima.cls.demo2.Student");
Student stu = (Student) stuClass.newInstance();
stu.study();
}
@Test
public void testMethod2() throws IllegalAccessException, InstantiationException {
Student stu =new Student();
//对象名.getClass()
Class studentClass = stu.getClass();
Student student = (Student) studentClass.newInstance();
student.study();
}
@Test
public void testMethod1() throws IllegalAccessException, InstantiationException {
// 类型名.class
Class studentClass = Student.class;
//System.out.println(studentClass);
System.out.println("带有包名的类:"+studentClass.getName());
System.out.println("类名:"+studentClass.getSimpleName());
//实例化Student对象
Object obj = studentClass.newInstance();
Student stu = (Student) obj;
stu.age=20;
stu.name="张三";
System.out.println(stu.name+"==="+stu.age);
stu.study();
}
}
小结
获取Class对象的方式:
目标
路径
Constructor类
Class中的类型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AzYmCHBD-1660142789506)(imgs/image-20201118105830818.png)]
构造方法对应的类型: Constructor类型
字段:Field
方法:Method
Constructor类
反射技术中构造器的目的:
获取Constructor对象的方式
Constructor对象的获取和Class类中方法有关:
Constructor[] getConstructors()
//获得类中的所有构造方法对象,只能获得public的
Constructor[] getDeclaredConstructors()
//获得类中的所有构造方法对象
//可以是public、protected、(默认)、private修饰符的构造方法
Constructor getConstructor( Class... parameterTypes)
//根据参数类型获得对应的Constructor对象 获取public修饰的
//只能获得public修饰的构造方法
/*示例: Student public Student(String name, int age) public Student(int age)
Class stuClass = Student.class;
//根据给定的参数类型,来获取匹配的构造器对象
Constructor c = stuClass.getConstructor( String.class , int.class );
*/
Constructor getDeclaredConstructor(Class... parameterTypes)
//根据参数类型获得对应的Constructor对象
//可以是public、protected、默认、private修饰符的构造方法
Constructor类常用方法
T newInstance(Object... initargs)
//根据指定的参数创建对象
/*
Class stuClass = Student.class;
//根据给定的参数类型,来获取匹配的构造器对象
Constructor c = stuClass.getConstructor( String.class , int.class );
//使用构造器对象,来实例化Student
c.newInstance( "张三", 22 );//Student(String name, int age)
//无参构造
Constructor c = stuClass.getConstructor();
Student stu = (Student) c.newInstance();
*/
void setAccessible(true)//应用场景:仅适用于访问有权限检查的成员
//设置"暴力反射" ——是否取消权限检查,true取消权限检查,false表示不取消
代码实现:
public class Test1 {
@Test
public void testMethod1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取构造器的步骤
/*1、先获取到Class对象
2、使用Class对象中的方法,获取Constructor对象
3、使用Constructor对象,实例化类
*/
//获取Class对象(Student.class)
Class stuClass = Student.class;
//获取Constructor对象: public Student()
Constructor con1 = stuClass.getConstructor();
System.out.println(con1);
Student stu = (Student)con1.newInstance();
stu.name="小崔";
System.out.println(stu.name);
}
}
小结
Constructor类:
获取Constructor类的方式:
//获取public修饰的构造方法
Constructor getConstructor(Class... parameterTypes)
//获取非public修饰的方法
Constructor getDeclaredConstructor(Class... parameterTypes)
常用方法:
Object newInstance(Object... param) //利用构造器对象,实例化自定义对象
void setAccessible(true) //消除JVM对权限的检查操作 (一次性的。只是对当前操作去除)
目标
路径
案例:使用无参构造器创建对象
@Test
public void testMethod1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取Class对象(Student.class)
Class stuClass = Student.class;
//利用Class对象,来获取构造器对象
Constructor con = stuClass.getConstructor();//方法中的参数为可变参数,可以不传递值
//使用Constructor对象中的方法,来实例化Student类对象
Student stu = (Student) con.newInstance();//方法中的参数是可变参数
stu.study();
}
案例:使用有参构造器创建对象
//有参构造方法
@Test
public void testMethod2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取Class对象(Student.class)
Class stuClass = Student.class;
//public Student(String name, int age, String gender)
//获取带有参数的构造器对象
//参数:是用来设置构造方法中参数的类型是什么
Constructor con = stuClass.getConstructor(String.class, int.class, String.class);
//实例化有参构造方法
//参数:要传递给Student(String name, int age, String gender)的数据
Student stu = (Student) con.newInstance("熊大", 22, "男");
//调用对象中的方法
stu.study();
}
案例:使用私有构造器创建对象
@Test
public void testMethod3() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取Class对象(Student.class)
Class stuClass = Student.class;
// private Student(String name)
//获取私有构造器对象
Constructor con = stuClass.getDeclaredConstructor(String.class);
//当需要对私有成员操作时,需要先取消JVM对访问权限的检查操作
con.setAccessible(true);//暴力破解(取消权限检查) //仅在当前次取消
//使用私有构造器,实例化Student对象
Student stu = (Student) con.newInstance("文平");
System.out.println(stu.name);
stu.study();
}
小结
当获取到私有构造器后,要使用该构造器创建对象,需要:
目标
路径
Method
Method类
代表一个成员方法
反射技术中使用Method的目的:
获取Method对象的方式
Method对象的获取和Class类中方法有关:
Method[] getMethods();
//获得当前类和其父类中的所有public成员方法对象,返回数组
Method[] getDeclaredMethods();
//获得当前类中的所有成员方法对象,返回数组
//只获得本类的,包括public、protected、默认、private的
Method getMethod(String name,Class...args);
//根据方法名和参数类型获得对应的成员方法对象,只能获得public的
//参数说明:
name : 类中方法的名字
args : 方法中参数类型的Class 例:int.class
Method getDeclaredMethod(String name,Class...args);
//根据方法名和参数类型获得对应的成员方法对象,包括public、protected、(默认)、private的
Method类常用方法
//使用方法对象,调用对象中的方法执行(入栈执行)
Object invoke(Object obj, Object... args)
// obj: 对象 //"对象名.方法"
// args:调用方法时传递的实参
//返回值: Object类型
void setAccessible(true)
// 设置"暴力访问" ——是否取消权限检查,true取消权限检查,false表示不取消
代码实现:
//获取Method对象的步骤:
1、先获取Class对象
2、使用Class对象,获取Method对象
3、使用Method对象,执行方法
public class Test01 {
@Test
public void testMethod1() throws ClassNotFoundException {
//获取Class对象
Class stuClass = Class.forName("com.itheima.method.demo1.Student");
//使用Class对象,获取Method对象
Method[] methods = stuClass.getMethods();//获取本类及父类中所有的public方法
for (Method m : methods){
System.out.println(m);
}
}
@Test
public void testMethod2() throws ClassNotFoundException {
//获取Class对象
Class stuClass = Class.forName("com.itheima.method.demo1.Student");
//使用Class对象,获取Method对象
Method[] methods = stuClass.getDeclaredMethods();//获取本类中所有方法(包含私有)
for (Method m : methods){
System.out.println(m);
}
}
}
小结
在反射中获取Method对象的步骤:
1、 先获取Class对象
2、基于Class对象,获取Method对象
3、使用Method对象,执行该方法
获取Method对象的方式:
//可以获取到本类及父类中public修饰的方法(指定方法名及方法中的参数类型)
Method getMethod(String name,Class...args);
//获取本类中任意的方法(指定方法名及方法中的参数类型)
Method getDeclaredMethod(String name,Class...args);
Method类中常用方法:
//执行Method对象所代表的方法
Object invoke(Object 对象 , Object... 方法中需要的实参);
void setAccessible(true)
回顾上午内容:
类加载过程
类加载器
java程序要运行,需要依赖于JRE(java运行环境),而jre中有类加载器,利用类加载器来加载.class文件并创建Class对象
获取类加载器的方式:
Class类中有一个方法:
ClassLoader cl = Class对象.getClassLoader();
反射:
什么是反射技术:
反射技术的应用:
反射技术的使用:
核心:需要利用Class对象
Class类
//获取Class对象
//方式1: Class clazz = 类名.class
//方式2: Class clazz = 对象名.getClass()
//方式3: Class clazz = Class.forName("类的全名称")
常用方法:
String getName() //获取带有包名的类
String getSimpleName() //获取类名(不包含包名称)
T newInstance() //实例化对象(注意:需要有无参构造方法)
构造器:Constructor类
//获取Constructor对象
//方式1: 获取public修饰的构造方法
Constructor cons = Class对象.getConstructor(Class... 参数类型)
//方式2: 获取private修饰的构造方法
Constructor cons = Class对象.getDeclaredConstructor(Class... 参数类型)
//获取所有的构造器对象:
Constructor[] cons = Class对象.getConstructors();
Constructor[] cons = Class对象.getDeclaredConstructors();
常用方法:
//实例化对象
T newInstance(Object... 实参)
//去除权限检查
void setAccessible(true) //针对非public修饰的
成员方法:Method类
//获取Method对象
//方式1: 获取本类或父类中的public修饰的成员方法
Method m = Class对象.getMethod("方法名", Class... 方法中参数类型)
//方式2: 获取本类中private修饰的成员方法
Method m = Class对象.getDeclaredMethod("方法名", Class... 方法中参数类型)
//获取所有的成员方法:
Method[] m = Class对象.getMethods()
Method[] m = Class对象.getDeclaredMethods()
常用方法:
//调用方法
Object invoke(Object 实例对象 , Object... 实参)
//去除权限检查
void setAccessible(true)
目标
路径
案例:调用无参无返回值的方法
@Test
public void testMethod1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取Class对象
Class stuClass = Student.class;
//因为在调用Method时,需要传递Student对象
Constructor con = stuClass.getConstructor();
Student stu = (Student)con.newInstance();
//获取public void study()方法的对象
Method method = stuClass.getMethod("study");
//使用Method对象 执行study()方法
method.invoke( stu );
}
案例:调用有参有返回值的方法
//有参有返回值
@Test
public void testMethod2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取Class对象
Class stuClass = Student.class;
//因为在调用Method时,需要传递Student对象
Constructor con = stuClass.getConstructor();
Student stu = (Student)con.newInstance();
//获取public String sayHello(String name)方法的Method对象
Method method = stuClass.getMethod("sayHello", String.class);
//调用method方法
Object result = method.invoke(stu,"波波");
System.out.println(result);
}
案例:调用私有方法
//私有方法
@Test
public void testMethod3() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
//获取Class对象
Class stuClass = Student.class;
//因为在调用Method时,需要传递Student对象
Constructor con = stuClass.getConstructor();
Student stu = (Student)con.newInstance();
//获取 private void eat(String name)方法的Method对象
Method method = stuClass.getDeclaredMethod("eat", String.class);
//去除JVM对当前次权限的检查
method.setAccessible(true);
//执行method方法
method.invoke(stu,"红烧肉");
}
案例:调用静态方法
//静态方法
@Test
public void testMethod4() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//获取Class对象
Class stuClass = Student.class;
//静态方法的调用不需要对象。"类名.静态方法()"
//获取public static void sleep()方法的Method对象
Method method = stuClass.getMethod("sleep");
//执行静态方法
method.invoke(null);//不需要传递对象(null就表示执行静态方法)
}
小结
Method对象的使用步骤:
1、获取Class对象
2、基于Class对象,获取Method对象
//有参方法
Method method = Class对象.getMethod("方法名",参数1类型.class,参数2类型.class ...);
//无参方法
Method method = class对象.getMethod("方法名");
3、使用Method对象,执行方法
//调用非静态方法
method对象.invoke(实例对象,方法中需要的实参)
//调用静态方法
method对象.invoke(null,方法中需要的实参)
作用
反射是框架的灵魂!框架的底层一定会用到反射技术。
需求:要把猫的睡觉方法 变成 狗的吃饭方法
效果:使用反射+Properties完成配置文件。把需要修改的灵活的内容写在配置文件中,代码不需要做任何的改动。
public class Dog {
public void eat(){
System.out.println("狗爱吃肉");
}
public void sleep(){
System.out.println("狗睡觉流口水");
}
}
public class Cat {
public void eat(){
System.out.println("猫爱吃鱼");
}
public void sleep(){
System.out.println("猫睡觉打呼噜");
}
}
public class Demo {
public static void main(String[] args) throws Exception{
//不使用反射
//需求: 要把猫的睡觉方法 变成 狗的吃饭方法
//Dog d = new Dog();
//d.eat();
//使用反射
//properties
Properties pro = new Properties();
//load():可以把文件中的键值对读取到集合中
FileReader fr = new FileReader("day21\\aaa.txt");
pro.load(fr);
//通过键获取值
String cn = pro.getProperty("className");
String mn = pro.getProperty("methodName");
//获取字节码对象
Class c = Class.forName(cn);
//获取空参构造
Constructor con = c.getConstructor();
//执行构造方法
Object o = con.newInstance();
//获取方法
Method m = c.getMethod(mn);
//执行方法
m.invoke(o);
}
}
配置文件:
className=com.itheima_05.Cat
methodName=sleep
目标
路径
注解
什么是注解?
注解:就是具有特殊含义的标记(注解是给机器阅读的)
注解的作用
注解就是在代码里添加一些特殊标志,这些标志可以在编译,类加载,运行时被读取,并执行相应的处理,以便于其他工具补充信息或者进行操作
注解的作用:
编译检查:
@Override:用来修饰方法声明。
用来告诉编译器该方法是重写父类中的方法,如果父类不存在该方法,则编译失败
代码分析:
通过代码里标识的注解对代码进行分析
框架的配置( 框架 = 代码 + 配置 )
生成帮助文档:
@author:用来标识作者姓名
@version:用于标识对象的版本号,适用范围:文件、类、方法
使用过的注解:
@Override
@Test
@FunctionalInterface //函数式接口
小结
注解:在代码中添加的标记,JVM看到注解标记后会执行一些操作
注解的作用:
目标
路径
注解的定义格式
定义注解使用关键字:@interface
public @interface 注解名{
//内容
}
注解本质上就是一个接口。所有注解都会继承一个接口:Annotation
public interface 自定义注解名 extends java.lang.annotation.Annotation {}
带有属性的注解
属性的格式
属性定义示例
public @interface Student {
String name(); // 姓名
int age() default 18; // 年龄
String gender() default "男"; // 性别
}
// 该注解就有了三个属性:name,age,gender
属性适用的数据类型【记住】
1. 八种数据数据类型(int,short,long,double,byte,char,boolean,float)
2. String,Class,注解类型,枚举类
3. 以上类型的一维数组形式
小结
注解的定义格式:
public @interface 注解名
{
//属性格式 : 数据类型 属性名()
String 属性名();
int 属性名() default 默认值;
}
//注意:属性的类型只能是8种基本数据类型、String、Class、枚举、注解、(以及一维数组形式)
目标
路径
注解的使用格式
格式:
//无属性注解
@注解名 例:@Test
//有属性注解
@注解名(属性=值,属性=值)
注解可以在类上,成员变量上,构造方法上,方法上,参数上…
有默认值的属性,可以不用进行赋值。
案例代码
将Book注解使用起来
public @interface Book {
String name();
double price() default 100.0;
String[] author();
}
建立一个BookStore的类,在类中定义成员变量,构造方法,成员方法
@Book(name = "西游记", author = {"吴承恩", "张三"})
public class BookStore {
@Book(name = "三国", price = 10, author = {"罗贯中"})
private String book;
@Book(name = "三国", price = 10, author = {"罗贯中"})
public BookStore() {
}
@Book(name = "三国", price = 10, author = {"罗贯中"})
public void test() {
}
}
特殊属性value
如果注解中只有一个属性要赋值,而且名字是value,可以将value给省略,可以直接给值
@interface A{
String value();
}
@interface B{
String value();
String name();
}
@interface C{
String value();
int price() default 100; //有默认值
}
//测试类
public class Demo {
@A("值") //当自定义注解中仅有一个value属性时,可以省略value属性名
public void test1() {
}
@B(value = "值",name="aa") //当自定义注解中除了value属性外,还有其它属性,value不能省略
public void test2() {
}
@C("值")//自定义注解中除了value属性外,还有其它带有默认值的属性,value可以省略
public void test3() {
}
}
小结
自定义注解:
public @interface Book{
//属性
String name();
double price() default 100;
}
在程序代码中使用自定义注解:
//注解可以应用在:类、方法、变量、构造方法
@Book(name="属性值") //price属性使用默认值
public class BookStore{
@Book(name="属性值",price="新的值") //新的price值,会覆盖默认值
public void method(){
}
}
1.如果自定义注解中的属性不给默认值,那么使用的时候必须赋值,如果有默认值,使用的时候可以不给值
2.使用注解:@注解名(属性名=属性值,属性名=属性值,....)
3.如果注解中没有属性,使用的时候@注解名
4.如果属性是数组,那么赋值方式:
@注解名(属性名=属性值,属性名=属性值,属性名={属性值,属性值,..}....)
5.如果属性是数组类型,并且使用的时候只给一个值,那么可以省略大括号
6.如果属性只有一个并且属性名是value,那么给value赋值的时候可以不写value
7.如果含有多个属性,并且没有默认值,那么给value赋值的时候必须书写value
8.如果含有多个属性,并且具有默认值,那么给value赋值的时候可以不写value********************
9.同一个注解不能同时修饰同一个方法或者同一个类
10.不同注解可以同时修饰同一个方法或者同一个类
目标
路径
元注解的作用
默认情况下,注解可以用在任何地方,比如类,成员方法,构造方法,成员变量等地方
如果要限制自定义注解的使用位置怎么办?那就要学习一个新的知识点:元注解
结论:元注解是用来约束自定义注解的使用范围、生命周期
常用的元注解
常用元注解:@Target、@Retention
@Target
作用:指明此注解用在哪个位置,如果不写默认是任何地方都可以使用
@Target可选的参数值在枚举类ElemenetType中包括:
@Retention
作用:定义该注解的生命周期(有效范围)。
@Retention可选的参数值在枚举类型RetentionPolicy中包括:
SOURCE:注解只存在于Java源代码中,编译生成的字节码文件中就不存在了
CLASS:注解存在于Java源代码、编译以后的字节码文件中,运行的时候内存中没有,默认值
RUNTIME:注解存在于Java源代码中、编译以后的字节码文件中、运行时内存中,程序可以通过反射获取该注解
元注解的使用
@Target({ElementType.METHOD,ElementType.TYPE}) //元注解
@interface Stu{
String name();
}
//类
@Stu(name="jack") //成功
public class AnnotationDemo02 {
// 成员变量
@Stu(name = "lily") // 编译失败
private String gender;
// 成员方法
@Stu(name="rose") //成功
public void test(){
}
// 构造方法
@Stu(name="lucy") // 编译失败
public AnnotationDemo02(){}
}
小结
元注解的作用:
目标
路径
获取注解数据的原理
想要对注解中的数据进行解析,需要借助:AnnotatedElement接口
AnnotatedElement 是一个接口,定义了解析注解的方法:
1. boolean isAnnotationPresent(Class<Annotation> annotationClass)
判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false
public class BookStore{
@Book(name="书名")
public void buy() // Method对象 判断该对象上是否使用了@Book注解
{ // boolean flag = Method对象.isAnnotationPresent(Book.class)
}
}
2. T getAnnotation(Class<T> annotationClass)
根据注解类型获得对应注解对象
// 获取对象上的自定义注解
// Book bookAnno = Method对象.getAnnotation(Book.class);
// bookAnno.属性 //获取注解中属性的值
Class,Constructor,Method,Field都会实现AnnotatedElement 接口
注解解析的步骤:(注解是书写在:类、方法、变量上)
1、利用反射技术获取注解作用的对象:类、方法、变量、构造方法
2、判断对象上是否有自定义注解存在
3、有:获取对象上的自定义注解
4、使用获取到的自定义注解对象,拿到注解中的属性值
注意:注解解析必须保证自定义注解生命周期在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.TYPE}) //使用范围:方法、类
@Retention(RetentionPolicy.RUNTIME) //保证注解在程序执行时有效(适用于注解解析)
public @interface Book {
String value();
double price() default 100;
String[] authors();
}
//类
public class BookStore {
@Book(value = "Java入门", authors = {"张老师", "毕老师"})
public void buy() {
System.out.println("购书.....");
}
}
//对注解中的数据进行解析
public class TestBookStore {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//反射套路:1、Class 2、构造器 3、Method
Class<BookStore> bookStoreClass = BookStore.class;
//获取构造器
Constructor<BookStore> con = bookStoreClass.getConstructor();
BookStore bookStore = con.newInstance();//实例化对象
//获取Method
Method method = bookStoreClass.getMethod("buy");
//解析注解的步骤
if(method.isAnnotationPresent(Book.class)){
//获取Method对象上的Book注解
Book bookAnno = method.getAnnotation(Book.class);
//获取注解上的数据
String bookName = bookAnno.value();
double bookPrice = bookAnno.price();
String[] bookAuthors = bookAnno.authors();
System.out.println("书名:"+bookName);
System.out.println("价格:"+bookPrice);
System.out.println("作者:"+ Arrays.toString(bookAuthors));
}
}
}
小结
注解解析的步骤:
注解扩展小示例:
public class Student{
private String name;//姓名
private int age;//年龄
private String gender;//性别
private double score;//成绩
private String id;//学号
}
数据:
张三,20,男,90,it1001
利用注解+反射,给Student类中的成员变量赋值
//自定义注解
public @interface Entity{
String value;
}
//Student类
public class Student{
@Entity(value="张三")
private String name;//姓名
@Entiry(value="20")
private int age;//年龄
private String gender;//性别
private double score;//成绩
private String id;//学号
}
目标
路径
需求
需求:模拟Junit测试的@Test
案例分析
案例代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定义注解
@Target(ElementType.METHOD)//仅能应用在方法上
@Retention(RetentionPolicy.RUNTIME)//生命周期 :运行时
public @interface MyTest {
//无属性注解
}
public class TestAnnotationParse {
//方法1
@MyTest
public void method1(){
System.out.println("我是方法1");
}
@MyTest
public void method3(){
System.out.println("我是方法3");
}
public void method2(){
System.out.println("我是方法2");
}
}
public class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException {
//获取Class对象
Class<TestAnnotationParse> testAnnotationParseClass = TestAnnotationParse.class;
//获取Class对象中,所有的Method方法
Method[] methods = testAnnotationParseClass.getMethods();
//遍历数组
for (int i = 0; i < methods.length; i++) {
//获取每一个Method对象
Method method = methods[i];
//判断Method对象上,是否存在@MyTest
if(method.isAnnotationPresent(MyTest.class)){
method.invoke(testAnnotationParseClass.newInstance());
}
}
}
}
小结
注解解析的步骤:
package com.itheima.sh.web;
public interface UserService {
void login();
void delete();
void query();
}
package com.itheima.sh.web;
public class UserServiceImpl implements UserService{
@Override
public void login() {
try {
long start = System.currentTimeMillis();
System.out.println("登录");
Thread.sleep(3000);
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void delete() {
try {
long start = System.currentTimeMillis();
System.out.println("删除");
Thread.sleep(4000);
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void query() {
try {
long start = System.currentTimeMillis();
System.out.println("查询");
Thread.sleep(5000);
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.itheima.sh.web;
public class Test01 {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.login();
userService.delete();
userService.query();
}
}
业务对象的的每个方法都要进行性能统计,存在大量重复的代码。
代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理的。
关键步骤
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V7kDOnz4-1660142789509)(imgs\image-20210913110300563.png)]
相关API介绍
动态代理API
在java中实现动态代理,关键要使用到一个Proxy类和一个InvocationHandler接口
Proxy类
java.lang.reflect.Proxy:是 Java 动态代理机制的主类(父类),它提供了用于创建动态代理类和实例的静态方法
public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler handle
)
**解释说明:
- 返回值: 该方法返回就是动态生成的代理对象
- 参数列表说明:
ClassLoader loader :定义代理类的类加载器
Class<?>[] interfaces :代理类要实现的接口列表,要求与被代理类的接口一样
InvocationHandler handle :就是具体实现代理逻辑的接口
//参数的应用:
ClassLoader loader //对象.getClass().getClassLoader()
//目标对象通过getClass方法获取类的所有信息后,调用getClassLoader()方法来获取类加载器
/*获取类加载器后,可以通过这个类型的加载器,在程序运行时,将生成的代理类加载到JVM即Java虚拟机中,以便运行时需要*/
Class<?>[] interfaces //对象.getClass().getInterfaces()
//获取被代理类的所有接口信息,以便于生成的代理类可以具有代理类接口中的所有方法
InvocationHandler
//用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类方法的处理以及访问
InvocationHandler接口
java.lang.reflect.InvocationHandler是代理对象的实际处理代理逻辑的接口,具体代理逻辑在其 invoke 方法中实现
public Object invoke(Object proxy, Method method, Object[] args)
**解释说明:
- 返回值:方法被代理后执行的结果
- 参数列表说明:
Object proxy : 就是代理对象(通常不使用)
Method method : 代理对象调用的方法
Object[] args : 被代理方法中的参数 (因为参数个数不定,所以用一个对象数组来表示)
如果方法不使用参数,则为 null
//所有代理对象调用的方法,执行是都会经过invoke
//因此如果要对某个方法进行代理增强,就可以在这个invoke方法中进行定义
package com.itheima.sh.web;
public interface UserService {
void login();
void delete();
void query();
}
package com.itheima.sh.web;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test02 {
public static void main(String[] args) {
ClassLoader loader = UserService.class.getClassLoader();
Class[] interfaces = {UserService.class};
InvocationHandler h = new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Exception{
String name = method.getName();
long start = System.currentTimeMillis();
if("login".equals(name)){
//登录
System.out.println("登录");
Thread.sleep(3000);
}else if("delete".equals(name)){
//删除
System.out.println("删除");
Thread.sleep(4000);
}else if("query".equals(name)){
//查询
System.out.println("查询");
Thread.sleep(5000);
}
long end = System.currentTimeMillis();
System.out.println(name+"耗时:"+(end-start));
return null;
}
};
UserService proxyUserServiceImpl = (UserService) Proxy.newProxyInstance(loader, interfaces, h);
proxyUserServiceImpl.query();
proxyUserServiceImpl.login();
proxyUserServiceImpl.delete();
}
}
非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接口本身做代理。
可以为被代理对象的所有方法做代理。
可以在不改变方法源码的情况下,实现对方法功能的增强。
不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率。