Java入门系列-27-反射

简介:

咱们可能都用过 Spring AOP ,底层的实现原理是怎样的呢?

反射常用于编写工具,企业级开发要用到的 Mybatis、Spring 等框架,底层的实现都用到了反射。能用好反射,就能提高我们编码的核心能力。

反射机制

JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。

作用:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理

常用的类:

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造方法

Class 类

Class 类的实例表示正在运行的 Java 应用程序中的类和接口,Class 没有公共构造方法,Class 对象是在加载类时由 Java 虚拟机及通过调用类加载器中的 defineClass 方法自动构造的。

  • 一个类在 JVM 中只会有一个 Class 实例
  • 一个 Class 对象对应的是一个加载到 JVM 中的一个 .class 文件
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成
  • 通过 Class 可以完整地得到一个类中的完整结构

获取 Class 对象

获取 Class 对象有4种方式,前三种比较常用。

首先创建一个类用于测试

package com.jikedaquan.reflection;

public class User {
    
    private int id;
    private String username;
    private String password;

    public User() {
    }
    
    public User(int id, String username, String password) {
        super();
        this.id = id;
        this.username = username;
        this.password = password;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    
    public void show() {
        System.out.println("Hello");
    }
    
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
    }
}

编写测试

package com.jikedaquan.reflection;

public class GetClass {

    public static void main(String[] args) {
        //方法1
        try {
            Class clz1=Class.forName("com.jikedaquan.reflection.User");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("找不到指定类");
        }
        //方法2
        Class clz2=User.class;
        //方法3
        User user=new User();
        Class clz3=user.getClass();
        
        //方法4 类的加载器
        try {
            Class clz4=GetClass.class.getClassLoader().loadClass("com.jikedaquan.reflection.User");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("找不到指定类");
        }
    }
}

方法1语法:Class Class对象 = Class.forName(包名+类名);

方法2语法:Class Class对象 = 类名.class;

方法3语法:Class Class对象 = 对象.getClass();

getClass() 方法是从 Object 类中继承过来的

获取类的结构

Class 类常用方法

方法名称 说明
Annotation[] getAnnotations() 返回此元素上存在的所有注解
Constructor getConstructor(Class<?>... parameterTypes) 获取指定参数的构造函数
Constructor<?>[] getConstructors() 返回包含的公有构造方法
Constructor<?>[] getDeclaredConstructors() 返回所有构造方法
Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 根据方法名和参数获取方法对象

API 中可以看到有两种获取结构的方式:getDeclaredXxx()和getXxx();getDeclaredXxx()可以获取所有包括私有的

获取类的结构

package com.jikedaquan.reflection;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class GetClassStruct {

    public static void main(String[] args) {
        try {
            Class clz=Class.forName("com.jikedaquan.reflection.User");
            System.out.println("===========构造===========");
            //获取构造方法
            Constructor[] cons=clz.getDeclaredConstructors();
            for (Constructor constructor : cons) {
                System.out.println(constructor);
            }
            //获取字段
            System.out.println("===========字段===========");
            Field[] fields=clz.getDeclaredFields();
            for (Field field : fields) {
                System.out.println(field);
            }
            //获取方法
            System.out.println("===========方法===========");
            Method[] methods=clz.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println(method);
            }
            //获取父类
            System.out.println("===========父类===========");
            Class supperClass=clz.getSuperclass();
            System.out.println(supperClass.getName());
            //获取实现的接口
            System.out.println("===========接口===========");
            Class[] interfaces=clz.getInterfaces();
            for (Class interf : interfaces) {
                System.out.println(interf);
            }
            //获取注解
            System.out.println("===========注解===========");
            Annotation[] annotations=clz.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

调用类的指定方法、属性

获取构造方法并实例化对象

注意:jdk1.9弃用此方式实例化对象
Object obj=clz.newInstance();

通过反射获取有参或无参构造后方可实例化化对象

package com.jikedaquan.reflection;

import java.lang.reflect.Constructor;

public class CallConstructor {

    public static void main(String[] args) {
        //获取User 的 Class
        Class<User> clz=User.class;
        
        //获取无参构造方法并实例化
        try {
            //getConstructor()方法不传参即无参
            Constructor<User> constructor=clz.getConstructor();
            User user=constructor.newInstance();
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //获取有参构造方法并实例化
        try {
            Constructor<User> constructor=clz.getConstructor(int.class,String.class,String.class);
            User user=constructor.newInstance(18,"张三","abc123");
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

获取指定构造方法时,第二个参数为动态参数,不填写即获取无参构造方法,填写指定个数和指定类型.class可获取对应方式的构造方法。

调用类中的方法

package com.jikedaquan.reflection;

import java.lang.reflect.Method;

public class CallMethod {

    public static void main(String[] args) {
        //获取User 的 Class
        Class<User> clz=User.class;
        //获取无参方法  show
        try {
            Method method=clz.getMethod("show");
            //执行clz中的方法
            method.invoke(clz.getConstructor().newInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
        //获取一个参数为String的方法
        try {
            Method method=clz.getMethod("setUsername", String.class);
            //反射实例化对象
            User user=clz.getConstructor().newInstance();
            //执行这个对象的方法
            method.invoke(user, "反射");
            //测试结果
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

如果有多个参数,获取方法:getMethod("方法名称",参数1.class,参数2.class,参数3.class)

多个参数执行时:method.invoke(对象,参数1,参数2,参数3);

动态代理

动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要创建目标类的代理对象。

原理:

使用一个代理将对象包装起来,然后用该代理对象取代原对象,任何对原始对象的调用都要通过dialing,代理对象决定是否以及何时将方法调用转到原始对象上。

生活中海外代购其实就用到了代理,你可能不方便出国,但是代购可以,最终帮你完成购买行为。

以代购为例子完成静态代理

package com.jikedaquan.reflection;

//购买接口(约定)
interface Buy{
    void buyProduct();
}
//被代理的
class Customer implements Buy{

    @Override
    public void buyProduct() {
        System.out.println("购买商品");
    }
}
//代理
class ProxyBuy implements Buy{
    private Customer customer;
    
    public ProxyBuy(Customer customer) {
        this.customer=customer;
    }
    
    @Override
    public void buyProduct() {
        System.out.println("代理:出国");
        //被代理的对象的行为
        customer.buyProduct();
        System.out.println("代理:回国");
    }
}

public class TestStaticProxy {

    public static void main(String[] args) {
        Customer customer=new Customer();
        ProxyBuy proxyBuy=new ProxyBuy(customer);
        proxyBuy.buyProduct();
    }
}

那么动态代理意味着不能只代理 Customer 类的行为,还可以代理其他类的行为

package com.jikedaquan.reflection;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//工厂接口
interface Factory{
    void product();
}
//电脑工厂
class ComputerFactory implements Factory{

    @Override
    public void product() {
        System.out.println("生产电脑");
    }
}
//动态代理处理器
class MyInvocationHandler implements InvocationHandler{
    //要被代理的对象
    private Object proxyObj;
    //产生代理对象
    public Object bind(Object proxyObj) {
        this.proxyObj=proxyObj;
        return Proxy.newProxyInstance(
                proxyObj.getClass().getClassLoader(),
                proxyObj.getClass().getInterfaces(), 
                this
                );
    }
    //代理对象实际执行的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理:收收费");
        Object result=method.invoke(proxyObj, args);
        System.out.println("代理:代理完成");
        return result;
    }

}

public class TestDynamicProxy {

    public static void main(String[] args) {
        //创建代理对象生产器
        MyInvocationHandler invocationHandler=new MyInvocationHandler();

        //创建要被代理的对象
        ComputerFactory computerFactory=new ComputerFactory();
        //生产代理对象
        Object factoryProxy=invocationHandler.bind(computerFactory);
        Factory factory=(Factory) factoryProxy;
        factory.product();

        //创建另一个要被代理的对象(上个示例静态代理的对象和接口)
        Customer customer=new Customer();
        //生产代理对象
        Object buyProxy=invocationHandler.bind(customer);
        Buy buy=(Buy) buyProxy;
        buy.buyProduct();
    }
}

在 main 方法中,创建了一个 MyInvocationHandler 对象,通过 bind 方法可以传入任意要被代理的对象,实现了动态。

重点来了,拿好小本子笔记!!!!!

实现动态代理的步骤

1.创建要被代理的类的接口

2.创建要被代理的类实现类

3.创建代理对象处理器(MyInvocationHandler),实现 InvocationHandler 接口

4.编写生产代理对象的方法,方法内调用 Proxy.newInstance() 方法,返回代理对象

5.重写 InvocationHandler 的 invoke 方法

6.测试:创建代理对象生产器,生产代理对象

相关文章
|
26天前
|
存储 安全 Java
从入门到精通:Java Map全攻略,一篇文章就够了!
【10月更文挑战第17天】本文详细介绍了Java编程中Map的使用,涵盖Map的基本概念、创建、访问与修改、遍历方法、常用实现类(如HashMap、TreeMap、LinkedHashMap)及其特点,以及Map在多线程环境下的并发处理和性能优化技巧,适合初学者和进阶者学习。
39 3
|
5天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
11天前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
17天前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
55 5
|
14天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
29 1
|
20天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
43 3
|
22天前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
24天前
|
存储 安全 Java
从入门到精通:Java Map全攻略,一篇文章就够了!
【10月更文挑战第19天】本文介绍了Java编程中重要的数据结构——Map,通过问答形式讲解了Map的基本概念、创建、访问与修改、遍历方法、常用实现类(如HashMap、TreeMap、LinkedHashMap)及其特点,以及Map在多线程环境下的使用和性能优化技巧,适合初学者和进阶者学习。
41 4
|
1月前
|
开发框架 IDE Java
java制作游戏,如何使用libgdx,入门级别教学
本文是一篇入门级教程,介绍了如何使用libgdx游戏开发框架创建一个简单的游戏项目,包括访问libgdx官网、设置项目、下载项目生成工具,并在IDE中运行生成的项目。
48 1
java制作游戏,如何使用libgdx,入门级别教学
|
23天前
|
Java
[Java]Socket套接字(网络编程入门)
本文介绍了基于Java Socket实现的一对一和多对多聊天模式。一对一模式通过Server和Client类实现简单的消息收发;多对多模式则通过Server类维护客户端集合,并使用多线程实现实时消息广播。文章旨在帮助读者理解Socket的基本原理和应用。
19 1