反射
- 认知:反射技术就是对类进行解剖,解剖出"构造器"、"成员变量"、"成员方法"
- 构造器: 可以实例化对象
- 成员变量:可以赋值、取值
- 成员方法:调用方法
- 大白话:不使用new关键字,可以实例化对象,可以访问对象中的成员
- 反射技术,通常应用于:框架
- 反射技术的程序书写步骤:
-
- 获取Class对象
- 获取构造器
- 获取成员(成员方法、成员变量)
- 实例化对象 | 调用方法 | 给成员变量赋值取值
- 使用反射技术:
- 核心点:Class类
- Class类是什么呢?
- JVM中只能执行.class字节码文件(Java程序中会存在大量的.class文件)
- .class文件是通过
类加载器
读取到内存中,并基于这个.class文件创建出:Class对象
- .class文件是通过
- Class对象,就是指一个.class文件
- Class类的作用
- 通过Class对象,获取"构造器"、"成员方法"、"成员变量"
- 步骤:
- 1、获取Class对象
Class cls = 类名.class;
Class cls = 对象名.getClass();
Class cls = Class.forName("类的全限定名");
//回顾:同步方法(线程), 非静态方法默认有一个同步锁:this
// 静态方法的同步锁:类名.class
-
-
- 2、基于Class对象,可以获取:构造器、成员方法、成员变量
-
-
- 构造器:Constructor类
Constructor c = Class对象.getConstructor();//无参构造器
Constructor c = Class对象.getConstructor(String.class);//有参构造器
-
-
-
- 成员方法:Method类
-
-
Method m = Class对象.getMethod("方法名");//获取指定方法名的无参方法
Method m = Class对象.getMethod("方法名",int.class);
-
-
-
- 成员变量:Field类
-
-
Field f = Class对象.getDeclaredField("属性名");
//针对私有成员:成员变量、成员方法、构造器,需要取消权限检查
f.setAccessible(true);//true就表示关闭本次权限检查
-
-
- 3、使用构造器,调用
newInstance(...)
方法,来实例化对象 - 4、使用成员方法,调用
invoke(...)
方法,来调用方法入栈执行 - 5、使用成员变量,调用
set(...)
方法、get(...)
方法,对变量进行赋值、取值
- 3、使用构造器,调用
-
注解
- 认知:
-
- 注解单独使用,没有任何意义。
- 通常注解会配合反射技术一起使用,常用于框架技术
- 注解的定义:
public @interface 注解名{
数据类型 属性名();
数据类型 属性名() default 默认值;
数据类型[] 属性名();
}
注解中的数据类型可以有哪些:
1. 8种基本数据类型
2. 枚举
3. String
4. Class
5. 注解
6. 以上所有类型的一维数组形式
- 注解的使用:
@注解名
public class 类{
@注解名(属性名=值)
private String 成员变量;
@注解名 //@Test
public void 成员方法(int 参数){
}
}
- 元注解:
- 作用:限定注解的使用位置、注解的生命周期
- @Target //指定注解的使用位置
- @Retention //设定注解的生命周期
-
- SOURCE : 只能在源码中使用 。 例:@Override
- CLASS :可以用于源码中、字节码文件中 【默认】
- RUNTIME : 可以用于源码、字节码文件、程序运行 例:@Test
- 注解解析(注解存在的意义)
注解解析的步骤:
1、获取一个对象(构造器Constructor、成员变量Field、成员方法Method)
2、判断对象上是否有指定的注解
有: 获取对象上的注解对象
对象上有注解后,获取注解对象中的属性值
//API:
//判断某个对象(类、接口、成员变量、构造器、成员方法)上是否有使用注解
boolean flag = isAnnotationPresent(注解.class)//获取某个对象上的注解对象
注解名 对象 = getAnnotation(注解.class)
数据类型 变量 = 注解对象.属性;
- 示例
//前置铺垫:
一张数据表 对应 类
一行记录 对应 对象
一个字段 对应 成员变量
create table t_student
(
sname varchar(20),
sage int
);
//类
class Student{
private String name;
private int age;
}
//JDBC程序: (程序员自己编写)
连接数据库
创建数据库操作对象
发送sql语句: select ...
处理结果集
while(rs.next()){
age = rs.getInt("sage");
name =rs.getString("sname");
Student stu = new Student();
stu.setName( name );
stu.setAge( age );
}
//框架技术: 反射+注解 (别人已经完成的)
//1、自定义一些注解
public @interface Entity{ //实体
String table(); //表名
}
public @interface property{ //属性
String name();
}
//2、解析@Entity、@Property注解
扫描某些包下的的类,发现这些类上有@Entity、@Property注解,就会利用反射
利用反射技术: 获取 Student.class 对象 (Class对象)
解析 类上的@Entity注解
利用反射技术: 获取所有的成员变量 :
解析所有成员变量上的@Property注解
//JDBC程序中使用框架技术中注解 ( 程序员自己编写 )@Entiry(table="t_student") //表示当前的Student类和数据表t_student关联
class Student{
@Property(name="sname") //表示当前的成员变量name和字段sname关联
private String name;
@Property(name="sage") //表示当前的成员变量age和字段sage关联
private int age;
}
动态代理
- 动态代理,提供了一个代理的对象,有了代理对象后,当访问某个方法时,会被代理对象拦截(拦截后可以对方法进行前增强、后增强【代理对象不会破坏原有方法的代码】)
- 动态代理的特点:
-
- 动态的创建.class文件(创建一个和被代理类实现相同父接口的子类[代理类])
- 动态的加载.class文件到内存中(创建Class对象)
-
-
- Proxy类需要一个"类加载器"
-
-
- 对程序员来讲,不需要书写代理类
- 动态代理的代码实现:
代理对象 = Proxy.newProxyInstance(类加载器 , 父接口 , 处理器)
类加载器: 动态的加载.class文件
父接口 : 代理类和被代理类需要拥有共同的父接口
处理器: 代理对象拦截了方法后,对方法进行前增强、后增强,是由处理器来书写逻辑
代理对象 = Proxy.newProxyInstance(
类.class.getClassLoader(), //类加载器
被代理类.class.getInterfaces(), //父接口
new InvocationHandler(){
public Object invoke(
Object 代理对象,
Method 被拦截的方法对象 ,
Object[] 方法中的实参
){
//业务逻辑
}
}
)
动态代理:
- 代理程序中的某个类中的功能,为该功能进行增强
动态代理实现的关键步骤:
- 被代理类(例:UserServiceImpl),必须有实现接口
- 创建被代理类对象(例: new UserServiceImpl() ),交给代理对象使用
动态代理的实现:
- JDK已经提供了现成的代理对象的生成
- Proxy类
- 静态方法:newProxyInstance(类加载器 , 接口数组 , 处理器)
- Proxy创建一个子类
- 子类: 必须和被代理类实现相同的父接口
- 子类编译后是一个.class文件, 需要使用类加载器,加载.class文件到内存中