Reflection反射

一、反射机制

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完成的。
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sorenw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值