JAVA中的糕富帅技术——反射(一)

简介:   前言   突然发现好久没写博客了,前面写的都是关于Android的东西,今天心血来潮突然有一种冲动想写一篇基于JAVA技术的博客,别问我为什么?有钱、任性!   今天就来谈谈反射机制;学过JAVA的人不一定懂得反射,但是一定听说过反射,不过也仅仅是听说过而已;因为反射用的地方也不会那么多,但是反射用的妙经常会解决我们挠破头皮的大问题。

 

前言

  突然发现好久没写博客了,前面写的都是关于Android的东西,今天心血来潮突然有一种冲动想写一篇基于JAVA技术的博客,别问我为什么?有钱、任性!

  今天就来谈谈反射机制;学过JAVA的人不一定懂得反射,但是一定听说过反射,不过也仅仅是听说过而已;因为反射用的地方也不会那么多,但是反射用的妙经常会解决我们挠破头皮的大问题。至于诸如为什么叫做反射、而不叫做正射倒射此类的历史问题,还是交给历史学家去研究吧。。。

反射的基石

  在谈反射之前,我们应该先了解下类的概念来引入。类是一种抽象的概念,举个例子“我爸是李刚我爸李双江”,从这句话中我们发现有李刚、李双江这两个人,我们来抽象它们的特点,我们发现它们都像人。没错,那么我们就可以将人作为它们的一个抽象,反过来说李刚和李双江就是人的一个具体实例;所以我们可以用一个Person类代表人来表示这种抽象。既然理解了类的概念,那些年那些陪我们度过日日夜夜的java类们,我们是不是也应该抽象出一个类来证明一下他们,没错,那就是Class了!

  Class就是java类的抽象,它抽象出了java的共性,如类的名字、类的构造方法、类的成员变量、类的老爸、类的方法等等等。既然这么说,那么我们通过这个Class,我们就可以得到这个类的方方面面的信息、兴许还能比查户口还详细呢。我们创建出的每一个类,例如person类,说到底也就是我们实例化了一个Class的实例,来保存person类的名字、变量、方法这些信息,在内存中表示就是保存了person类的字节码,如果你理解了这些并且接受了我的看法,那么咱们有共同语言,可以继续往下说。

  既然person类有拥有自己的字节码,那么我们可以获取到这个字节码吗?答案是肯定的,而且还不止一种方法。参见代码:

复制代码
public static void main(String[] args) throws Exception {
  //第一种方法,直接通过Person类来获取字节码
  Class cls1 = Person.class;
  //第二种方法,通过类的实例来获取Person类的字节码
  Person person = new Person();
  Class cls2 = person.getClass();
  //第三种方法,调用Class类的静态方法来获取对应类的字节码,该方法会抛出异常
  Class cls3 = Class.forName("Person");
}
复制代码

  从代码中看,我们可以断定:Person类的字节码就是Class的具体实例;我们也可以猜到,至于类的字节码有包含什么东东,大家尽管猜吧,后面我会慢慢讲解。我们再来看看下面的代码:

  System.out.println(cls1 == cls2);
  System.out.println(cls2 == cls3);

我们运行程序,会发现输出了:

true
true

这三个玩意竟然是同一个东西,那么就很好解释了:在java的虚拟机中,每一个类都会被保存成为一个字节码,用来保存该类的信息如名字、父类、变量、方法等。一个类的字节码在虚拟机中有且只有一个,也就是在第一次加载该类的时候会将类的字节码加载到java虚拟机中,而上面有三种方法可以从虚拟机中获取类的字节码(PS:第三种方法最为常用),但是你别疑惑获取这个字节码干嘛嘛用,我们要反射嘛,说白了我们就是要来强暴这字节码(Class)。。。。。(~ o ~)~zZ

 理解反射

  既然前面讲解了Class类,现在我们可以开始讲反射了。反射是什么呢?反射就是将类的各种成分映射成各种类,我们知道一个java类可以用一个class的对象来表示,这个类的组成成分有名字、变量、构造方法等信息,我们当然可以用一个个java类的表示。换句话说,表示java类的Class类提供了一系列的方法给我们用来获取其中的变量、方法、构造方法等信息,这些信息也有相应的类的实例来表示,也就是Field、Method、Constructor等等。或者更通俗的说,Field就是java类中的所有变量的抽象、同理Method就是java类中所有方法的抽象,如果还是看不懂,很正常,往下看代码估计更好理解。

构造方法的反射

  从前面我们知道,Constructor就是java类所有构造方法的抽象。那么我们怎么通过反射来获取类的构造方法呢,参见代码:

复制代码
public class Test{
    
    public static void main(String[] args) throws Exception {
        
        Class cls = Person.class;//获取Person类的字节码
        
        Constructor constructor1 = cls.getConstructor();//调用getConstructor()获取Person无参构造方法
        Person p1 = (Person) constructor1.newInstance();//通过调用newInstance()来执行无参构造方法
        
        Constructor constructor2 = cls.getConstructor(int.class);//调用getConstructor(*.class)获取Person带参构造方法
        Person p2 = (Person) constructor2.newInstance(1);//通过调用newInstance(int)来执行带参构造方法
    }

}

class Person{    
    public Person(){System.out.println("无参构造方法");}
    public Person(int i){System.out.println("带参构造方法");}
}
复制代码

控制台输出:

无参构造方法
带参构造方法

  这里我们开始讲解一下,代码通过Person.class来获取Person类的字节码并将其保存在一个Class类的实例cls中,然后再通过cls.getConstructor()来获取字节码中的构造方法并将其放入Constructor的实例constructor之中,很明显,这个constructor并不是Person的构造方法,而是保存Person构造方法的一个实例,所以我们可以通过调用newInstance()来获取保存在constructor中的person类的构造方法并执行,构造方法执行并返回一个Object的实例,并将其强转为Person并保存在person的变量中,这就是调用反射来获取构造方法生成实例的全过程。

  在代码中,我们也可以知道怎么获取带参的构造方法,这是我们需要在getConstructor()是传入构造方法对应参数的字节码,例如代码中Person(int i)我们需要传入一个int.class(或者是Integet.TYPE)的字节码提供给Class定位需要获取的构造方法。但是如果你比较贪心想获取全部的构造方法,没问题,通过getConstructors():

Class cls = Person.class;//获取Person类的字节码
Constructor[] constructors = cls.getConstructors();//调用getConstructor()获取Person无参构造方法
for(Constructor c : constructors){
  //Person p = c.newInstance(****);遍历执行构造方法
}

然后通过for循环,就可以处理你所需要的构造方法了。

成员变量的反射

  我们说完了构造方法的反射,我们就接下来谈谈成员变量的反射的用法。惯例还是先看代码:

复制代码
public class Test{
    
    public static void main(String[] args) throws Exception {
        
        Person p = new Person("小红", 20);
        
        Class cls = Class.forName("com.net168.test.Person");
        Field fieldName = cls.getField("name");
        //fieldNmae的值是小红吗?错!它只是代表Person类身上name的这个变量,并没有对应到对象身上
//        System.out.println(fieldNmae); 
        //fieldNmae不代表具体的值,只代表一个变量,所以我们需要传入一个person实例才能获取到其对应的值
        System.out.println(fieldName.get(p));
    }

}

class Person{    
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    public String name;
    private int age;//对于某些人来说,年龄是秘密!
}
复制代码

  跟构造方法的反射的实现差不多,我们也是先通过获取Person的字节码cls,然后从其中将Person的成员变量映射成一个Field类,在这里我们将Person.name这个变量映射成fieldName这个对象,当然我们不可能单纯的从fieldName这个对象中获取咱们的“小红”,因为fieldName是从cls中获取的而并不是从person的实例中获取的,所以它值并不是小红;而是我们可以通过小红这个person的实例p与fieldName联系起来,也就是调用fieldName.get(p)才能获取小红这个字符串。

  但是我们如果想获取小红年龄呢,女人的年龄大多是秘密,私有变量我们也可以这样获取吗?修改下代码:

Person p = new Person("小红", 20);
Class cls = Class.forName("com.net168.test.Person");
Field fieldAge = cls.getField("age");
System.out.println(fieldAge.get(p));

执行结果是:

Exception in thread "main" java.lang.NoSuchFieldException: age
at java.lang.Class.getField(Unknown Source)
at com.net168.test.Test.main(Test.java:11)

没有这个字段,明明是有这个age的字段呀!但是我们发现,原来这个女生的年龄是私有的,她就是不肯告诉咱们啊,那怎么办?她不想告诉我们,我们就没法知道了吗?屌丝是不会那么容易屈服的!所以我们可以稍作一点处理,如下:

Person p = new Person("小红", 20);
Class cls = Class.forName("com.net168.test.Person");
Field fieldAge = cls.getDeclaredField("age");//获取类的私有变量
fieldAge.setAccessible(true);//设置该私有变量可被外面访问
System.out.println(fieldAge.get(p));

可以通过getDeclaredField()来获取Person类的私有变量,而且我们还可以在获取到外界看不到的私有变量后,再通过setAccessible(true)设置该私有变量可以被强制访问。暴力吧,JAVA的反射也被有些人叫做暴力反射。。。运行代码,我们就知道了小红的芳龄 了:20

成员方法的反射

  如果大家看懂了前面成员变量和构造方法的反射,基本上再了解成员方法的反射就没有什么困难了,不卖关子,还是先上下代码:

复制代码
public class Test{
    
    public static void main(String[] args) throws Exception {
        Person p = new Person();
        Class cls = p.getClass();//获取Person的字节码
        //获取setName()方法,需要传入参数为String
        Method method1 = cls.getMethod("setName", String.class);
        method1.invoke(p, "小明");//关联p,输入“小明”并执行该方法
        //获取getName()方法,无参则设为null
        Method method2 = cls.getMethod("getName", null);
        String name = (String) method2.invoke(p, null);//invoke返回的类型为Object
        System.out.println(name);
        //获取静态方法,由于静态方法只依赖与类,所以不需要提供具体的实例
        Method method3 = cls.getMethod("show", int.class);
//        method3.invoke(p, 1);提供具体实例p也可通过编译
        method3.invoke(null, 1);
        
    }
}

class Person{    
    public String name;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
        System.out.println("设置name值为:" + name);
    }
    public static void show(int i){
        System.out.println("这是一个静态方法:" + i);
    }
}
复制代码

程序运行结果:

设置name值为:小明
小明
这是一个静态方法:1

  在方法的反射中,我们是利用了Method这个类,由于跟构造方法类似,所以我不就再就获取有参无参的方法的不同之处进行讲解。总体来说,就是通过Person的字节码获取到Person类中对应的方法并将其保存到Method的一个对象中,然后通过这个对象跟Person的具体实例进行搭配,通过invoke()就可以调用到具体实例的对应方法。在这里我们需要注意的时静态方法的反射,由于静态方法属于一个类并不是属于特定的一个对象,所以我们在调用静态方法的invoke()时,并不需要传入一个对象,当然你非要传入一个具体的实例也是没有关系的,答案依然正确。

 

对于反射就先讲这么多吧,后面有时间再来讲讲反射的深入应用以及反射在框架搭建的用处。又浪费我一晚上游戏时间了。。。。任性呀。。。

 

 

作者:enjoy风铃
出处:http://www.cnblogs.com/net168/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则下次不给你转载了

目录
相关文章
|
6天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
32 7
|
6天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
17天前
|
监控 Java
Java基础——反射
本文介绍了Java反射机制的基本概念和使用方法,包括`Class`类的使用、动态加载类、获取方法和成员变量信息、方法反射操作、以及通过反射了解集合泛型的本质。同时,文章还探讨了动态代理的概念及其应用,通过实例展示了如何利用动态代理实现面向切面编程(AOP),例如为方法执行添加性能监控。
|
23天前
|
监控 前端开发 Java
【技术开发】接口管理平台要用什么技术栈?推荐:Java+Vue3+Docker+MySQL
该文档介绍了基于Java后端和Vue3前端构建的管理系统的技术栈及功能模块,涵盖管理后台的访问、登录、首页概览、API接口管理、接口权限设置、接口监控、计费管理、账号管理、应用管理、数据库配置、站点配置及管理员个人设置等内容,并提供了访问地址及操作指南。
|
1月前
|
JSON 前端开发 JavaScript
java-ajax技术详解!!!
本文介绍了Ajax技术及其工作原理,包括其核心XMLHttpRequest对象的属性和方法。Ajax通过异步通信技术,实现在不重新加载整个页面的情况下更新部分网页内容。文章还详细描述了使用原生JavaScript实现Ajax的基本步骤,以及利用jQuery简化Ajax操作的方法。最后,介绍了JSON作为轻量级数据交换格式在Ajax应用中的使用,包括Java中JSON与对象的相互转换。
43 1
|
1月前
|
SQL 监控 Java
技术前沿:Java连接池技术的最新发展与应用
本文探讨了Java连接池技术的最新发展与应用,包括高性能与低延迟、智能化管理和监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,为开发者提供了一份详尽的技术指南。
33 7
|
1月前
|
Java
Java的反射
Java的反射。
26 2
|
1月前
|
移动开发 前端开发 Java
过时的Java技术盘点:避免在这些领域浪费时间
【10月更文挑战第14天】 在快速发展的Java生态系统中,新技术层出不穷,而一些旧技术则逐渐被淘汰。对于Java开发者来说,了解哪些技术已经过时是至关重要的,这可以帮助他们避免在这些领域浪费时间,并将精力集中在更有前景的技术上。本文将盘点一些已经或即将被淘汰的Java技术,为开发者提供指导。
85 7
|
1月前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
48 3
|
1月前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
16 2