一、反射机制
1.概述
Java 反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,应该先了解两个概念:编译期和运行期
编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作。比如:检查语法错误。
//编译器完成对象类型和成员变量的访问
Book book = new Book();
book.bookName = "葫芦娃大战变形金刚";
运行期:是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
//运行期
//使用反射的方式,完成成员变量保存值
Class cls = Book.class; //获取Class类型对象
Object obj = cls.newInstance(); //通过反射创建Book类的对象
Java反射机制,简单来说指的是程序在运行时能够动态的获取自身的信息。
Java反射有以下3个动态特征:
1.运行时创建实例
2.运行期间调用方法
3.运行时更改属性
Java中编译类型有两种:
-
静态编译:在编译时确定类型,绑定对象即通过。
-
动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以降低类之间的耦合性。
2.功能
通过Java反射可以实现以下功能:
1.在运行时探知任意一个实例所属的类。
2.在运行时构造任意一个类的实例。
3.在运行时探知任意一个类所具有的方法和属性。
4.在运行时调用任意一个实例的方法。
二、实现
1.Class类对象获取
常用获得类相关的方法:
方法 | 用途 |
getClassLoader() | 获得类的加载器 |
getDeclaredClass() | 返回一个数组,数组中包含该类中所有类和接口类的对象(包含私有的) |
forName(String className) | 根据类名返回类的对象 |
newInstance() | 创建类的实例 |
getName() | 获得类的完整路径名字 |
获得Class对象的三种方式:
第一种,使用 Class.forName(“类的全路径名”); 静态方法。
前提:已明确类的全路径名。
第二种,使用 .class 方法。
说明:仅适合在编译前就已经明确要操作的 Class
第三种,使用类对象的 getClass() 方法
示例:
//Class对象的三种创建方式
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException {
//方式1:通过类名访问class
Class stringCls1 = String.class;
//方式2:通过实例访问getClass()
String s = "";
Class stringCls2 = s.getClass();
//方式3:通过Class类的静态方法forName(类名)
Class stringCls3 = Class.forName("java.lang.String"); //类名可变
//相同的Class对象
System.out.println(stringCls1.hashCode());
System.out.println(stringCls2.hashCode());
System.out.println(stringCls3.hashCode());
}
}
2.反射Field获取字段
常用获得类中属性相关的方法:
方法 | 用途 |
getField(String name) | 获得某个公有的属性对象(包含父类) |
getFields() | 获得所有共有的属性对象(包含父类) |
getDeclaredField(String name) | 获得某个定义的属性对象(子类某个属性) |
getDeclaredFields() | 获得所有属性对象(子类所有属性) |
getFields()和getDeclaredFields():
//反射操作:访问字段(成员变量)
//每个字段都会被封装成一个Field对象
public class Demo3 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
Class cls = Book.class;
//所有public访问修饰符定义的字段(子类和父类)
// Field[] fields = cls.getFields();
//所有定义的字段(仅子类)
Field[] fields = cls.getDeclaredFields();
for (Field field :
fields) {
System.out.println("成员变量访问修饰符(int):" + field.getModifiers());
System.out.println("成员变量访问修饰符:" + Modifier.toString(field.getModifiers()));
System.out.println("成员变量类型:" + field.getType());
System.out.println("成员变量名称:" + field.getName());
System.out.println();
}
}
}
class Book {
public String bookName;
public double price;
private int page;
private String author;
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book[bookName = " + bookName + "price = " + price + "author = " + author + "page = " + page;
}
}
getField(String name)和getDeclaredField(String name):
public class Demo4 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
//编译器完成对象类型和成员变量的访问
Book book = new Book();
book.bookName = "葫芦娃大战变形金刚";
//运行期
//使用反射的方式,完成成员变量保存值
Class cls = com.apesource.demo2.Book.class; //获取Class类型对象
Object obj = cls.newInstance(); //通过反射创建Book类的对象
//按照字段名称,获取指定字段(public)
Field field = cls.getField("bookName");
//参数1:目标Book对象
field.set(obj,"晚熟的人");
System.out.println(obj);
//author字段(private)
//按照字段名称,获取指定字段
Field field2 = cls.getDeclaredField("author");
field2.setAccessible(true); //设置为允许访问
field2.set(obj,"莫言");
System.out.println(obj);
}
}
3.反射构造方法
常用的类中构造方法相关的方法:
方法 | 用途 |
getConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstruceor(Class...<?>parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
getConstructors()和getConstructor(int.class)
//反射的方式调用构造方法
public class Demo5 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class cls = Example.class;
//调用无参构造方法创建Example类型的对象
Example example1 = (Example) cls.newInstance();
System.out.println(example1);
//获取所有Public公有的构造方法
Constructor[] constructorArray = cls.getConstructors();
for (Constructor constructor:constructorArray
) {
System.out.println(constructor);
}
System.out.println("-------------------------------------");
//调用有参构造方法创建Example类型的对象
//第1步:获取指定参数类型的构造方法
//有1个参数
Constructor constructor1 = cls.getConstructor(int.class);
System.out.println(constructor1);
//有2个参数
// Constructor constructor2 = cls.getConstructor(int.class,double.class);
// System.out.println(constructor2);
//第2步:执行构造方法,创建Example类型的对象
Example example2 = (Example)constructor1.newInstance(1024);
System.out.println(example2);
}
}
class Example{
public Example(){
System.out.println("Example的无参构造方法");
}
public Example(int a){
System.out.printf("Example的有参构造方法a = %d",a);
}
public Example(int a,double b){
System.out.printf("Example的有参构造方法a = %d,b = %f",a,b);
}
}
getDeclaredConstruceor(String.class)和getDeclaredConstruceors():
public class Demo6 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class cls = Example1.class;
System.out.println("所有构造方法:");
//获取所有定义的构造方法
Constructor[] constructorArray = cls.getDeclaredConstructors();
for (Constructor constructor
:constructorArray) {
System.out.println(constructor);
}
//获取一个私有的构造方法
Constructor privateConstruct = cls.getDeclaredConstructor(String.class); //Declared定义的
//调用私有的构造方法
privateConstruct.setAccessible(true); //设置该私有方法可以使用
Example1 example1 = (Example1) privateConstruct.newInstance("final");
System.out.println(example1);
}
}
class Example1{
private Example1(String s){
System.out.printf("Example1的有参数构造方法 s = %s",s);
}
protected Example1(float f){
System.out.printf("Example1的有参数构造方法 s = %s",f);
}
private Example1(){
System.out.println("Example1的无参数构造方法 ");
}
private Example1(int a){
System.out.printf("Example1的有参数构造方法 a = %d",a);
}private Example1(int a,double b){
System.out.printf("Example1的有参数构造方法 a = %d,b = %f",a,b);
}
}
4.反射私有的和公开的方法
常用的类中方法相关的方法:
方法 | 用途 |
getMethod(String name,Class...<?>paramterTypes) | 获得该类某个公有的方法(子类和父类) |
getMethods() | 获得该类所有公有的方法(子类和父类) |
getDeclaredMethod(String name,Class...<?>paramterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有的方法 |
getMethods()和getDeclaredMethods():
public class Demo6 {
public static void main(String[] args) {
Class cls = Book.class;
//获取所有的public方法(包含父类)
// Method[] methods = cls.getMethods();
//获取定义的方法(仅包含当前类)
Method[] methods = cls.getDeclaredMethods();
for (Method method :
methods) {
System.out.println("方法的访问修饰符:" + Modifier.toString(method.getModifiers()));
System.out.println("方法的名称:" + method.getName());
System.out.println("方法的返回类型:" + method.getReturnType());
System.out.println("------------------");
//获取所有的参数类型
//System.out.println("方法的参数类型");
//Class[] parameters = method.getParameterTypes();
//获取参数对象
Parameter[] parameter = method.getParameters();
for (Parameter p : parameter
) {
System.out.println(p.getName());
System.out.println(p.getType());
}
System.out.println();
}
}
getDeclaredMethod(String name,Class...<?>paramterTypes):
//反射操作:调用方法
public class Demo7 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//获取Class对象
Class cls = Base.class;
Object obj = cls.newInstance();
//按照方法名称和”参数类型“获取Method方法对象
Method method = cls.getDeclaredMethod("create",int.class);
//Method对象的invoke()作用:
//以反射的方式执行create()方法
int r = (int) method.invoke(obj,1000);
System.out.println(r);
}
}
class Base{
public int create(){
return create(100);
}
public int create(int a){
return (int) (Math.random()*a);
}
}
getMethod(String name,Class...<?>paramterTypes):
//以反射的方式调用静态方法
public class Demo8 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//计算以10为底的对数
//获取某个指定数字的对数
System.out.println((int)Math.log10(1234567) + 1);
//反射调用
Class cls = Math.class;
Method methodlog10 = cls.getMethod("log10", double.class);
int n = Double.valueOf((double)methodlog10.invoke(null,1234567)).intValue() +1;
System.out.println(n);
}
}
5.反射代理
代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
代理模式角色分为 3 种:
● Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,通常被设计成接口;
● RealSubject(真实主题角色):真正实现业务逻辑的类;
● Proxy(代理主题角色):用来代理和封装真实主题;
1.静态代理:
假设有UserService接口及其实现类UserServiceImpl,我们需要在不改变实现类代码的基础上,增加日志记录的功能。
UserService:
public interface UserService {
void select();
void update();
}
UserServiceImpl:(真正的实现类)
// 真正的实现类
public class UserServiceImpl implements UserService {
@Override
public void select() {
System.out.println("select * ..................");
System.out.println("数据库中完成用户信息的查询执行!");
}
@Override
public void update() {
System.out.println("update ...................");
System.out.println("数据库中用户状态的更新执行!");
}
}
通过静态代理对 UserServiceImpl 进行功能增强,在调用select和update之前记录一些日志。静态代理通过 UserServiceProxy实现,代理类同时也需要实现 UserService接口。
UserServiceProxy:(代理类)
public class UserServiceProxy implements UserService{
private UserService target;
public UserServiceProxy(){
target = new UserServiceImpl();
}
@Override
public void select() {
before();
target.select();
after();
}
@Override
public void update() {
before();
target.update();
after();
}
public void before(){
System.out.println("方法开始执行");
}
public void after(){
System.out.println("方法结束执行");
}
}
client:
public class client {
public static void main(String[] args) {
UserService us = new UserServiceProxy();
us.select();
us.update();
}
}
静态代理的缺点:
1、 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
● 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
● 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
2、 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护
2.动态代理:
Java中两种常见的动态代理方式:JDK原生动态代理和CGLIB动态代理(第三方开源类库)。
JDK动态代理主要涉及两个类:java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler。通过编写一个调用逻辑处理器 LogHandler 类案例来提供日志增强功能,并实现 InvocationHandler 接口;在 LogHandler 中维护一个目标对象,这个对象是被代理的对象(真实主题角色);在 invoke()方法中编写方法调用的逻辑处理。
client:
public class client {
public static void main(String[] args) {
//案例1
//创建目标对象
//创建InvocationHandler
LogInvocationHandlerImpl handler1 = new LogInvocationHandlerImpl(new UserServiceImpl());
//创建UserService接口的动态代理对象
UserService proxy1 = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
handler1);
//通过代理对象调用方法 => LogInvocationHandlerImpl实现类的invoke
proxy1.select();
proxy1.update();
System.out.println("Proxy1的类型:" + proxy1.getClass());
//案例2
//创建目标对象
OrderService osTarget = new OrderServiceImpl();
//创建InvocationHandler
LogInvocationHandlerImpl handler2 = new LogInvocationHandlerImpl(new OrderServiceImpl());
//创建OrderService的“动态”代理对象
OrderService proxy2 = (OrderService) Proxy.newProxyInstance(
osTarget.getClass().getClassLoader(),
osTarget.getClass().getInterfaces(),
handler2);
proxy2.createOrder();
System.out.println("Proxy2的类型:" + proxy2.getClass());
}
}
LogInvocationHandlerImpl:(InvocationHandler接口实现类)
/InvocationHandler接口实现类:代理类中扩展逻辑抽取封装
public class LogInvocationHandlerImpl implements InvocationHandler {
private Object target;
public LogInvocationHandlerImpl(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.printf("方法%s开始执行\n",method.getName());
//执行目标对象的目标方法(反射)
Object returnValue = method.invoke(target,args);
System.out.printf("方法%s结束执行\n",method.getName());
return returnValue;
}
}
Order:(订单服务接口)
// 订单服务
public interface OrderService {
void createOrder();
void closeOrder();
}
OrderServiceImpl:(真正的实现类)
public class OrderServiceImpl implements OrderService{
@Override
public void createOrder() {
System.out.println("生成新的订单");
}
@Override
public void closeOrder() {
System.out.println("关闭当前订单");
}
}
总结:
● Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例;
● 动态代理是通过Proxy创建代理对象,然后将接口方法“代理”给InvocationHandler完成的。