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.测试:创建代理对象生产器,生产代理对象
Java入门系列-27-反射的更多相关文章
- Java入门系列-26-JDBC
认识 JDBC JDBC (Java DataBase Connectivity) 是 Java 数据库连接技术的简称,用于连接常用数据库. Sun 公司提供了 JDBC API ,供程序员调用接口和 ...
- Java入门系列-19-泛型集合
集合 如何存储每天的新闻信息?每天的新闻总数是不固定的,太少浪费空间,太多空间不足. 如果并不知道程序运行时会需要多少对象,或者需要更复杂方式存储对象,可以使用Java集合框架. Java 集合框架提 ...
- Java入门系列(十二)Java反射
Why--指的是为什么做这件事,也既事物的本质. 反射之中包含了一个“反”的概念,所以要想解释反射就必须先从“正”开始解释,一般而言,当用户使用一个类的时候,应该先知道这个类,而后通过这个类产生实例化 ...
- Java入门系列:实例讲解ArrayList用法
本文通过实例讲解Java中如何使用ArrayList类. Java.util.ArrayList类是一个动态数组类型,也就是说,ArrayList对象既有数组的特征,也有链表的特征.可以随时从链表中添 ...
- Java入门系列之hashCode和equals(十二)
前言 前面两节内容我们详细讲解了Hashtable算法和源码分析,针对散列函数始终逃脱不掉hashCode的计算,本节我们将详细分析hashCode和equals,同时您将会看到本节内容是从<E ...
- Java入门系列之字符串创建方式、判断相等(一)
前言 陆续从0开始学习Java出于多掌握一门语言以后的路也会更宽,.NET和Java兼顾,虽然路还很艰难,但事在人为.由于Java和C#语法相似,所以关于一些很基础的内容不会再重头讲,Java系列中所 ...
- JAVA基础系列:反射
1. 定义 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这 种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. ...
- Java入门系列之重写
前言 关于所有Java系列文章面向有一定基础的童鞋,所写每一篇希望有一定含金量,有些内容可能会从Java整个语法全局考虑穿插后续要讲解的内容以成系统,若不理解,请看完后再学习.上一节我们讲解完了fin ...
- Java入门系列 泛型
前言 <Java编程思想>第四版足足用了75页来讲泛型——厚厚的一沓内容,很容易让人头大——但其实根本不用这么多,只需要一句话:我是一个泛型队列,狗可以站进来,猫也可以站进来,但最好不要既 ...
随机推荐
- SVN检出忽略文件夹文件
具体实现:1.在解决方案目录上点右键2.在乌龟SVN菜单中找到"属性"点开 3.在弹出窗中点 新建--其他 4.在弹出窗中的"属性"中选择"svn: ...
- JDK安装目录分析-两个jre和三个lib
安装JDK后,Java目录下有jdk和jre两个目录,但jdk下还有一个jre目录,而且这个jre比前面那个jre在bin目录下少了个server文件夹(Server端的Java虚拟机)!前一个jre ...
- 贝塞尔曲线 WPF MVVM N阶实现 公式详解+源代码下载
源代码下载 效果图: 本程序主要实现: N阶贝塞尔曲线(通用公式) 本程序主要使用技术 MVVM InterAction 事件绑定 动态添加Canvas的Item 第一部分公式: n=有效坐标点数量 ...
- Python一篇学会多进程
阅读目录 1. Process 2. Lock 3. Semaphore 4. Event 5. Queue 6. Pipe 7. Pool 序. multiprocessing python 中的多 ...
- [Objective-C语言教程]变量(6)
变量是程序可以操作的存储区域的名称. Objective-C中的每个变量都有一个特定的类型,它决定了变量内存的大小和布局; 可存储在内存中的值的范围; 以及可以应用于变量的操作集. 变量的名称可以由字 ...
- Oracle中对多行查询结果进行拼接
to_char(wmsys.wm_concat(to_char( st.col_name))) as new_name to_char: 将当前值转换成字符串类型; wmsys.wm_concat:拼 ...
- POJ-2387-Til the Cows Come Home(最短路)
Til the Cows Come Home Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 72844 Accepted ...
- leetcode-118-Pascal's Triangle(生成具有n行的帕斯卡三角形)
题目描述: Given a non-negative integer numRows, generate the first numRows of Pascal's triangle. Example ...
- 2018年10月19 手记 - 身为开发者的我de窘境
从10月1国庆过完节回来,那已经是7号了,之后便开始紧锣密鼓的筹划着接下来11月份的公司组织的对外活动,这边新来的产品对产品或者说对任务很是负责,并且策划了很多的方案,并且乐意站在我们开发的角度上去考 ...
- 为什么我会选择走 Java 这条路?
阅读本文大概需要 2.8 分钟. 作者:黄小斜 文章来源:微信公众号[程序员江湖] 最近有一些小伙伴问我,为什么当初选择走Java这条路,为什么不做C++.前端之类的方向呢,另外还有一些声音:研究 ...