Java-反射-程序员宅基地

技术标签: java  Java进阶  开发语言  

一、反射机制

1.反射介绍

(1)反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到

(2)在程序执行到new的时候,就会进行类的加载,加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射

2.反射机制原理示意图

3.Java 反射机制可以完成

(1)在运行时判断任意一个对象所属的类

(2)在运行时构造任意一个类的对象

(3)在运行时得到任意一个类所具有的成员变量和方法

(4)在运行时调用任意一个对象的成员变量和方法

(5)生成动态代理

4.反射相关的主要类

(1)java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象

(2)java.lang.reflect.Method:代表类的方法,一个Method对象就表示某个类的一个方法

(3)java.lang.reflect.Field:代表类的成员变量,一个Field对象就表示某个类的一个成员变量

(4)java.lang.reflect.Constructor:代表类的构造方法,一个Constructor对象就表示一个构造器

这些类在java.lang.reflection

5.反射优点和缺点

(1)优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。

(2)缺点:使用反射基本是解释执行,对执行速度有影响

6.反射调用优化-关闭访问检查

(1)Method和Field、Constructor对象都有setAccessible(boolean)方法

(2)setAccessible作用是启动和禁用访问安全检查的开关

(3)参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象在使用时执行访问检查

7、效率测试

代码:

public class Reflection02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        m1();
        m2();
        m3();
    }

    //传统方法来调用hi
    public static void m1() {

        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统方法调用 耗时=" + (end - start));
    }

    //反射机制调用方法hi
    public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        Class cls = Class.forName("com.hspedu.Cat");
        Object o = cls.newInstance();
        Method hi = cls.getMethod("hi");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            hi.invoke(o);//反射调用方法
        }
        long end = System.currentTimeMillis();
        System.out.println("反射机制调用 耗时=" + (end - start));
    }

    //反射调用优化 + 关闭访问检查

    public static void m3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        Class cls = Class.forName("com.hspedu.Cat");
        Object o = cls.newInstance();
        Method hi = cls.getMethod("hi");
        hi.setAccessible(true);//在反射调用方法时,取消访问检查
        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            hi.invoke(o);//反射调用方法
        }
        long end = System.currentTimeMillis();
        System.out.println("反射调用优化 + 关闭访问检查 耗时=" + (end - start));
    }
}

运行结果:

 

可以看到差距还是很明显的

二、Class类

1.介绍

(1)Class也是类,因此也继承Object类,类图如下

(2)Class类对象不是new出来的,而是java虚拟机在加载类时自动创建的

(3)对于某个类的Class类对象,在内存中只有一份,因为类只加载一次

(4)每个类的实例都会记得自己是由哪个Class实例所生成

(5)通过Class对象可以完整地得到一个类的完整结构,通过一系列API

(6)Class对象是存放在堆的

(7)类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码,变量名,方法名,访问权限等等)

2.Class 类的常用方法

方法名 功能说明
static Class forName(String name) 返回指定类名name的Class对象,参数name为全类名
Object newInstance() 调用缺省构造函数,返回该Class对象的一个实例
Package getPackage() 返回该Class对象对应的包对象
String getName() 返回此Class对象的全类名
Class[] getlnterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Class getSuperclass() 返回表示此Class所表示的实体的超类的Class
Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组
Field getField(String name); 获取到Class对象的一个非私有属性,参数name为属性名
Field[] getDeclaredFields() 返回Field对象的一个数组,获取到Class对象的所有属性
Method getMethod(String name,Class<?>...paramType)

返回一个Method对象,此对象的方法名为name,

形参类型为paramType

说明

(1)可使用cls.getPackage().getName()获取包名 

(2)在调用Field brand = cls.getField("brand"),获取到属性对象后,

可使用属性对象的get方法获得属性的值

brand.get(car);//参数car为该属性所在类的对象,即Car类

使用set方法给属性设置值

 brand.set(car, "奔驰");//第一个参数为对象实例,第二个参数为新的属性值

(3)直接输出Class对象,显示的是对应类的全类名

String classAllPath = "com.Car";
Class<?> cls = Class.forName(classAllPath);
//输出cls
System.out.println(cls); //这里输出的就是 com.Car

 3.获取 Class 类对象的六种方式

(1)若已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException

Class cls1 =Class.forName(“java.lang.Cat”);//应用场景:多用于配置文件,读取类全路径,加载类.

(2)若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高

//应用场景:多用于参数传递,比如通过反射得到对应构造器对象.
Class cls2 =Cat.class;

(3)若已知某个类的实例,调用该实例的getClass()方法可以获取Class对象

//运行类型 应用场景:通过创建好的对象,获取Class对象.
Class clazz =对象.getClass();

(4)通过类加载器得到Class对象

ClassLoader cl =对象.getClass().getClassLoader();
Class clazz4 =cl.loadClass(“类的全类名”);

(5)基本数据(int,char,boolean,float,double,byte,long,short)按如下方式得到Class类对象

Class cls =基本数据类型.class

(6)基本数据类型对应的包装类,可以通过.TYPE得到Class类对象(包装类和基本数据类型获取到的Class对象其实是同一个对象)

Class cls =包装类.TYPE

4.如下类型有 Class 对象

(1)外部类,成员内部类,静态内部类,局部内部类,匿名内部类
(2)interface:接口
(3)数组
(4)enum:枚举
(5)annotation:注解
(6)基本数据类型
(7)void

三、类加载

1.基本说明

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。

(1)静态加载:编译时加载相关的类,如果没有则报错,依赖性太强

(2)动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性

2.类加载的时机

(1)当创建对象时(new):静态加载

(2)当子类被加载时,父类也加载:静态加载

(3)调用类中的静态成员时:静态加载

(4)通过反射,只有执行到这段代码才会加载类:动态加载Class.forName("com.test.Cat");

3.类加载流程图

4.类加载各阶段完成任务 

(1)加载阶段

  • JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象

(2)连接阶段-验证

  • 目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证
  • 可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。

(3)连接阶段-准备

  • JVM会在该阶段对静态变量,分配内存井进行默认初始化(对应数据类型的默认初始值,如0、0L、null、false等)。这些变量所使用的内存都将在方法区中进行分配

(4)连接阶段-解析

  • 虚拟机将常量池内的符号引用替换为直接引用的过程。

(5)初始化

  • 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行<clinit>()方法的过程。
  • <clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
  • 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>0方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕

四、通过反射获取类的结构信息

1.第一组: java.lang.Class

//得到Class对象
Class<?> personCls = Class.forName("com.reflection.Person");

(1)String getName():获取全类名

String className = personCls.getName()

(2)String getSimpleName():获取简单类名

String className = personCls.getSimpleName()

(3)Field[] getFields():获取所有public修饰的属性,包含本类以及父类的

Field[] fields = personCls.getFields();

(4)Field[] getDeclaredFields():获取本类中所有属性

Field[] declaredFields = personCls.getDeclaredFields();

(5) Method[] getMethods():获取所有public修饰的方法,包含本类以及父类的

Method[] methods = personCls.getMethods();

(6) Method[] getDeclaredMethods():获取本类中所有方法

 Method[] declaredMethods = personCls.getDeclaredMethods();

(7)Constructor<?>[] getConstructors():获取本类所有public修饰的构造器

 Constructor<?>[] constructors = personCls.getConstructors();

(8)Constructor<?>[] getDeclaredConstructors():获取本类中所有构造器

 Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();

(9)Package getPackage():以Package形式返回包信息

Package p = personCls.getPackage()

(10)Class getSuperClass():以Class形式返回父类信息

 Class<?> superclass = personCls.getSuperclass();

(11)Class[] getInterfaces():以Class[]形式返回接口信息

Class<?>[] interfaces = personCls.getInterfaces();

(12)Annotation[] getAnnotations():以Annotation[]形式返回注解信息

 Annotation[] annotations = personCls.getAnnotations();

2.第二组: java.lang.reflect.Field

(1)int getModifiers():以int形式返回修饰符(说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16,public(1)+static(8)=9)

int m = field.getModifiers()

(2)Class getType():以Class形式返回该属性类型

Class type = field.getType()

(3)String getName():返回属性名

String methodName = field.getName()

3.第三组: java.lang.reflect.Method

(1)int getModifiers():以int形式返回修饰符(说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16)

int m = method.getModifiers()

(2)Class getReturnType():以Class形式获取返回类型

Class type = method.getReturnType()

(3)String getName():返回方法名

String name = method.getname()

(4)Class[] getParameterTypes():以Class[]返回参数类型数组

 Class<?>[] parameterTypes = declaredMethod.getParameterTypes();

4.第四组: java.lang.reflect.Constructor

(1)int getModifiers():以int形式返回修饰符

int m = constructor.getModifiers();

(2)String getName();返回构造器名(全类名)

String name = constructor.getName();

(3)Class[] getParameterTypes():以Class[]返回参数类型数组

Class<?>[] parameters = constructor.getParameterTypes();

五、通过反射创建对象

1.方式一:调用类中的public修饰的无参构造器
2.方式二:调用类中的指定构造器

Class类相关方法

  • newlnstance:调用类中的无参构造器,获取对应类的对象
  • getConstructor(Class... class):根据参数列表,获取对应的public构造器对象
  • getDecalaredConstructor(Class… class):根据参数列表,获取对应的所有构造器对象

Constructor类相关方法

  • ·setAccessible;暴破
  • ·newinstance(Object.…obj):调用构造器

示例:

package com.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 演示通过反射机制创建实例
 */
public class ReflecCreateInstance {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        //1. 先获取到User类的Class对象
        Class<?> userClass = Class.forName("com.reflection.User");
        //2. 通过public的无参构造器创建实例
        Object o = userClass.newInstance();
        System.out.println(o);
        //3. 通过public的有参构造器创建实例
        /*
            constructor 对象就是
            public User(String name) {//public的有参构造器
                this.name = name;
            }
         */
        //3.1 先得到对应构造器
        Constructor<?> constructor = userClass.getConstructor(String.class);
        //3.2 创建实例,并传入实参
        Object wwj = constructor.newInstance("wwj");
        System.out.println("wwj=" + wwj);
        //4. 通过非public的有参构造器创建实例
        //4.1 得到private的构造器对象
        Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
        //4.2 创建实例
        //暴破(暴力破解), 使用反射可以访问private构造器/方法/属性, 反射面前,都是纸老虎
        constructor1.setAccessible(true);
        Object user2 = constructor1.newInstance(100, "张三丰");
        System.out.println("user2=" + user2);
    }
}

class User { //User类
    private int age = 10;
    private String name = "教育";

    public User() {//无参 public
    }

    public User(String name) {//public的有参构造器
        this.name = name;
    }

    private User(int age, String name) {//private 有参构造器
        this.age = age;
        this.name = name;
    }

    public String toString() {
        return "User [age=" + age + ", name=" + name + "]";
    }
}

六、通过反射访问类中的成员

1.访问属性

(1)根据属性名获取Field对象:Field f =class对象.getDeclaredField(属性名);
(2)暴破:f.setAccessible(true);//f是Field
(3)访问:

Object o=class.newlnstance();//获取对象

f.set(o,值);//o表示实例对象

f.get(o);//o表示实例对象

注意:如果是静态属性,则set和get中的参数o,可以写成null

2.访问方法

(1)根据方法名和参数列表获取Method方法对象:Method m =class.getDeclaredMethod(方法名,XX.class);//得到本类的所有方法
(2)获取对象:Object o=class.newlnstance();
(3)暴破:m.setAccessible(true);
(4)访问:Object returnValue =m.invoke(o,实参列表);//o就是实例对象
注意:如果是静态方法,则invoke的参数o,可以写成null!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/2301_76144723/article/details/137883077

智能推荐

Ajax跨域问题_ajax请求跨域-程序员宅基地

文章浏览阅读3.2k次,点赞3次,收藏13次。ajax 是不能跨域。那么怎么解决前端发送请求的跨域问题呢。超详细,1、设置响应头、2、通过jsonp 3、通过调用jQuery封装的jsonp 4、httpclient 5、nginx_ajax请求跨域

HTML5+CSS期末大作业:个人网站设计——响应式个人简历介绍网页(5页) HTML+CSS+JavaScript_响应 期末 作业-程序员宅基地

文章浏览阅读2.9w次,点赞68次,收藏453次。HTML5+CSS期末大作业:个人网站设计——响应式个人简历介绍网页(5页) HTML+CSS+JavaScript 期末作业HTML代码 学生网页课程设计期末作业下载 web网页设计制作成品常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 明星、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 军事、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他 等网页设计题目, A+水_响应 期末 作业

python matplotlib显示图片_python 用PIL Matplotlib处理图像的基本操作-程序员宅基地

文章浏览阅读1.4k次。python 用PIL Matplotlib处理图像的基本操作_jupyter 显示matplotlib图片完全

Pandas实现两个表格内容模糊匹配_pandas 模糊匹配-程序员宅基地

文章浏览阅读8.6k次,点赞11次,收藏59次。目录一、方法21. 导入库2. 构建关键词3. 构建句子4. 建立统一索引5. 表连接6. 关键词匹配二、方法21. 构建字典2. 关键词匹配3. 结果展示4. 匹配结果展开一、方法2此方法是两个表构建某一相同字段,然后全连接,在做匹配结果筛选,此方法针对数据量不大的时候,逻辑比较简单,但是内存消耗较大1. 导入库import pandas as pdimport numpy as npimport re2. 构建关键词#关键词_pandas 模糊匹配

采集动态页面的内容(采集JS加载的网页信息)_js动态加载怎么爬取-程序员宅基地

文章浏览阅读393次。一键快速批量采集JavaScript加载的动态页面数据内容的方法_js动态加载怎么爬取

windows10环境下docker安装elasticsearch+kibana+KI分词器+ElasticHD_windows10 docker安装kibana use --allow-root to conti-程序员宅基地

文章浏览阅读1.1k次。其实docker安装的话,windows和centos没什么区别#拉去es镜像文件docker pull docker.elastic.co/elasticsearch/elasticsearch:7.6.1#启动es单机版docker run-p 0.0.0.0:9200:9200 -p 0.0.0.0:9300:9300--env discovery.type=single-nod..._windows10 docker安装kibana use --allow-root to continue.

随便推点

广度优先搜索算法及其MATLAB实现_广度优先算法可行路径matlab-程序员宅基地

文章浏览阅读6.2k次,点赞6次,收藏39次。摘要广度优先搜索算法(又称宽度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。(来自百度百科)算法思想1.对图中的任..._广度优先算法可行路径matlab

微信和支付宝相关支付业务场景介绍_支付宝的应用场景-程序员宅基地

文章浏览阅读1.1w次,点赞5次,收藏38次。支付宝 当面付 条码支付 应用场景:商家使用扫码设备,扫描用户支付宝钱包上的条码/二维码,完成收款。支付流程:API列表: 接口名称 描述 API地址 alipay.trade.pay 统一收单交易支付接口 https://docs.op..._支付宝的应用场景

iphone隐藏底条_iPhone12隐藏底部横条方法 iPhone12怎么隐藏底部小白条-程序员宅基地

文章浏览阅读7.7k次。iPhone12怎么隐藏底部小白条?很多iPhone 12用户反馈在看手机或者玩游戏的时候,屏幕底部的小白横条非常碍眼,但是又不知道怎么隐藏掉,所以小编今天整理了下iPhone12隐藏底部横条方法,帮大家一键隐藏底部横条,一起来看看吧!iPhone12隐藏底部横条方法:利用“引导式访问“功能。打开 iPhone “设置”-“辅助功能”,下拉找到“引导式访问”并开启: 在使用该功能之前,建议仔细阅..._iphone玩王者荣耀怎么把下面那个横条去掉

深度Linux 安装英伟达闭源驱动,deepin20 安装英伟达闭源驱动-程序员宅基地

文章浏览阅读550次。第一步、安装深度的“显卡驱动器”在deepin v20 中默认没有显卡驱动管理器,需要命令行安装,命令如下(刚开始一直出错,当我第一次打开应用商店,就可以安装了,好神奇):sudo apt install deepin-graphics-driver-manager安装深度的“显卡驱动器”,切换到因特尔默认驱动,然后重启两次,确认切换成功后,进行下一步。第二步、卸载英伟达开源驱动如果刚刚安装好系统..._linux终端命令安装显卡驱动是闭源的吗

C++编程常见错误及处理_c++常见错误及解决方法-程序员宅基地

文章浏览阅读1.3w次,点赞5次,收藏36次。C++编程常见错误及处理。在 C++ 程序错误一般分类:语法错误;运行错误;语义错误(也称逻辑错误)。本文介绍相关错误产生的原因及处理_c++常见错误及解决方法

安装GRID时跑root.sh脚本报错(ORA-27091: unable to queue I/O)-程序员宅基地

文章浏览阅读137次。在安装GRID过程中,运行root.sh脚本时报如下信息:Adding Clusterware entries to upstartCRS-2672: Attempting to start 'ora.mdnsd' on 'rac11g1'CRS-2676: Start of 'ora.mdnsd' on 'rac11g1' succeededCRS-2672: Attempt..._ora-27091 ora-15081

推荐文章

热门文章

相关标签