Java反射机制深度剖析

简介:

 Java反射机制是Java语言中一种很重要的机制,可能在工作中用到的机会不多,但是在很多框架中都有用到这种机制。我们知道Java是一门静态语言,在程序编译时变量的数据类型都已经确定,那么在Java运行时环境中,对于任意一个类,我们能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java的反射机制(Reflection)。

 

      1、Java反射机制提供的功能

      主要提供了以下几个功能:

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

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

      3)在运行时判断任意一个类所具有的成员变量和方法;

      4)在运行时调用任意一个对象的方法。

      反射让Java具有了动态的特性,这种机制允许程序在运行时透过Reflection API获取任意一个已知名称的类的内部信息,包括成员变量(fields)、方法(methods)、实现的接口(interfaces)、Java语言修饰符(modifiers)以及它的父类(superclass)等等,并可在运行时改变成员变量的内容或调用方法。

 

      2、Java Reflection API

      在JDK中,提供了以下类来实现Java反射机制,这些类都位于java.lang.reflect包下:

      Class类:代表一个类(注意:Class类位于java.lang包下);

      Field类:代表类的成员变量;

      Method类:代表类的方法;

      Constructor类:代表类的构造方法;

      Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

      通过API提供的这些类里的方法,我们可以动态获取想要的类的内部信息。

 

      3、获取类的Class对象

      Class类的实例表示正在运行的Java程序中的类和接口,每一个类都有对应的Class对象,不管一个类生成了多少个对象,这些对象都对应内存里的同一个Class对象。Class类没有public的构造方法,Class对象是在加载类时由Java虚拟机自动构建的。

      有以下几种方式来获取一个类的Class对象:

      1)Class类提供的静态方法:forName(String className),参数className表示所需类的完全限定名。     

 1 public class GetClassObject { 2      3     public static void main(String[] args) throws Exception { 4          5         Class<?> classType = Class.forName("java.lang.String"); 6          7         System.out.println(classType);//输出:class java.lang.String 8     } 9 10 }

     

      2)运用.class语法     

 1 public class GetClassObject { 2      3     public static void main(String[] args) throws Exception { 4          5         Class<?> classType = String.class; 6          7         System.out.println(classType);//输出:class java.lang.String 8     } 9 10 }

     

      3)Object类提供的方法:getClass()     

 1 public class GetClassObject { 2      3     public static void main(String[] args) throws Exception { 4          5         Map map = new HashMap(); 6         Class<?> classType = map.getClass(); 7          8         System.out.println(classType);//输出:class java.util.HashMap 9     }10 11 }

 

      4、获取类的Field(成员变量)对象

      类的每一个成员变量都对应一个Field对象,Class类提供了以下方法来获取类的成员变量对应的Field对象:

      1)Field getDeclaredField(String name):根据传入的变量名称返回此Class对象所表示的类或接口中声明的变量对应的Field对象。

      2)Field[] getDeclaredFields():返回一个Field类型的数组,包含此Class对象所表示的类或接口中声明的所有变量的Field对象。

      3)Field getField(String name):根据传入的变量名返回一个Field对象,注意与getDeclaredField(String name)不同的是,此方法返回的是public变量对应的Field对象。

      4)Field[] getFields():返回一个Field类型的数组,注意与Field[] getDeclaredFields()方法不同的是,此方法返回的是所有public变量对应的Field对象。

      代码示例:

 1 public class GetFieldObject { 2      3     public static void main(String[] args) throws Exception { 4          5         //首先,获得String类的Class对象 6         Class<?> classType = Class.forName("java.lang.String"); 7          8         //获得String类中声明的所有成员变量的Field对象的数组 9         Field[] fields = classType.getDeclaredFields();10         for(Field field : fields){11             System.out.println(field);12         }    
13         14         System.out.println("---------------------------------------------------------------------");15         16         //获得String类中声明的public成员变量的Field对象的数组17         Field[] publicFields = classType.getFields();18         for(Field field : publicFields){19             System.out.println(field);20         }21         22     }23 24 }

      输出结果:

      

      从结果输出可以看出getDeclaredFields()与getFields()的区别:getDeclaredFields()返回的是所有属性的Field对象;而getFields()返回的是声明为public的属性的Field对象。

 

      5、获取类的Method对象

      类中的每一个方法都对应一个Method对象,Class类提供了以下方法来获取类中的方法对应的Method对象:

      1)Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回一个Method对象,参数name表示方法名,可变参数parameterTypes是一个Class对象的数组,代表方法的参数的Class类型;

      2)Method[] getDeclaredMethods():返回Method对象的一个数组,这些对象反映此Class对象所表示的类或接口声明的所有方法,包括公共、保护、默认访问和私有方法,但不包括继承的方法;

      3)Method getMethod(String name, Class<?>... parameterTypes):返回一个Method对象,注意和此Method对象对应的方法是公共(public)方法;

      4)Method[] getMethods():返回一个Method数组,这些对象反映此Class对象所表示的类或接口中声明的公共(public)方法(也包括父类或父接口中声明的public方法)。
      代码示例:     

 1 public class GetMethodObject { 2      3     public static void main(String[] args) throws Exception { 4          5         //首先,获得类的Class对象 6         Class<?> classType = Class.forName("java.lang.reflect.Proxy"); 7          8         //获得类中声明的所有方法的Method对象的数组,不包括继承的父类的方法 9         Method[] methods = classType.getDeclaredMethods();10         for(Method method : methods){11             System.out.println(method);12         }    
13         14         System.out.println("----------------------------------------------------------------------");15         16         //获得类中的public方法的Method对象的数组,也包括继承的父类的public方法17         Method[] publicMethods = classType.getMethods();18         for(Method method : publicMethods){19             System.out.println(method);20         }21         22     }23 24 }

      输出结果:

      

 

      6、用反射机制调用对象的方法

      Java反射机制可以在运行时动态调用类中的方法,Java Reflection API提供了我们所需的方法来完成动态调用。要想调用类中的方法首先要创建一个对象,我们通过类的Class对象来创建它所代表的类的实例,通过Class对象我们还能获得类中声明的方法的Method对象,Method类提供了Invoke方法来调用此Method对象所表示的方法。反射机制调用方法代码示例如下:   

 1 public class InvokeTester { 2      3     public static int add(int a, int b){ 4         return a + b; 5     } 6      7     public static String echo(String str){ 8         return "hello "+str; 9     }10     11     12     public static void main(String[] args) throws Exception {13 //        InvokeTester invoke = new InvokeTester();14 //        System.out.println(invoke.add(1, 2));15 //        System.out.println(invoke.echo("tom"));16         17         18         //用反射机制调用,首先获得类的Class对象19         Class<?> classType = InvokeTester.class;20         21         //通过Class对象获得一个InvokeTester类的实例22         Object invoke = classType.newInstance();23         24         //获得add(int a, int b)方法的Method对象,getMethod方法的参数为方法名和方法参数类型的Class对象的数组25         Method addMethod = classType.getMethod("add", int.class, int.class);26         27         //通过Method类的invoke方法,调用invoke对象的add方法28         Object result = addMethod.invoke(invoke, 1, 2);29         30         System.out.println(result);31         32         Method echoMethod = classType.getMethod("echo", String.class);33         34         Object result2 = echoMethod.invoke(invoke, "Tom");35         36         System.out.println(result2);37         38     }39 }

 

      7、用反射机制调用类的私有方法

      我们知道正常情况下一个类的私有方法只允许这个类本身来调用,但使用反射机制能打破这种访问限制,让其他的类也能调用这个类的私有的方法。这种场景在实际开发中很少用到,Java也不提倡这种用法。代码示例如下:  

public class Private {    
    //定义一个私有方法
    private String sayHello(String name){        return "hello, "+name;
    }

}public class PrivateTest {    
    public static void main(String[] args) throws Exception {        //调用Private类的私有方法
        Private p = new Private();
        
        Class<?> classType = p.getClass();
        
        Method method = classType.getDeclaredMethod("sayHello", String.class);
        
        method.setAccessible(true);//取消Java访问检查,如果不设置此项则会报错        
        String str = (String)method.invoke(p, "Tracy");
        
        System.out.println(str);//输出:hello, Tracy    }
    
}

      Method、Field、Constructor类有一个共同的父类AccessibleObject类,它提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。在上面的代码中,我们在反射对象Method中设置accessible标志,它允许程序以某种通常禁止的方式来操作对象。

 

      8、用反射机制操作类的私有变量

      与前面调用类的私有方法类似,通过反射我们还能操作类的私有变量,代码示例如下:   

 1 public class Private2 { 2     //定义私有变量 3     private String name = "zhangsan"; 4      5     public String getName(){ 6         return name; 7     } 8 } 9 10 11 public class PrivateTest2 {12     13     public static void main(String[] args) throws Exception {14         //改变Private2类的私有变量的值15         Private2 p = new Private2();16         17         Class<?> classType = p.getClass();18         19         Field field = classType.getDeclaredField("name");20         21         field.setAccessible(true);//取消默认java访问控制检查,Field类的父类AccessibleObject类提供的方法22         23         field.set(p, "lisi");//Field类的set(Object obj, Object value)方法将指定对象上此Field对象表示的字段设置为指定的新值24         25         System.out.println(p.getName());//输出:lisi26         27     }28     29 }

      以上这些内容,我介绍了Java反射机制的中涉及的主要的几个类以及这些类的基本用法,这些类中还有很多的方法,大家可以通过查看API进行了解,用法都很简单。Java反射机制在很多框架的底层实现中有用到,还有一种很重要的设计模式也用到了反射,那就是代理模式中的动态代理,了解了动态代理模式的思想对我们研究框架有很大帮助,我会在后面的博客中介绍这些内容,欢迎大家共同探讨。


本文转自  zddnd   51CTO博客,原文链接:http://blog.51cto.com/13013666/1949212
相关文章
|
6月前
|
设计模式 人工智能 安全
AQS:Java 中悲观锁的底层实现机制
AQS(AbstractQueuedSynchronizer)是Java并发包中实现同步组件的基础工具,支持锁(如ReentrantLock、ReadWriteLock)和线程同步工具类(如CountDownLatch、Semaphore)等。Doug Lea设计AQS旨在抽象基础同步操作,简化同步组件构建。 使用AQS需实现`tryAcquire(int arg)`和`tryRelease(int arg)`方法以获取和释放资源,共享模式还需实现`tryAcquireShared(int arg)`和`tryReleaseShared(int arg)`。
383 32
AQS:Java 中悲观锁的底层实现机制
|
4月前
|
人工智能 缓存 安全
Java中的反射机制:深入探索与应用
Java反射机制是程序运行时动态获取类信息并操作类成员的特性,具备高度灵活性,但也伴随性能与安全风险。本文详解反射的基本用法、高级应用及最佳实践,助你掌握这一强大工具的正确使用方式。
157 0
|
6月前
|
人工智能 Java 关系型数据库
Java——SPI机制详解
SPI(Service Provider Interface)是JDK内置的服务提供发现机制,主要用于框架扩展和组件替换。通过在`META-INF/services/`目录下定义接口实现类文件,Java程序可利用`ServiceLoader`动态加载服务实现。SPI核心思想是解耦,允许不同厂商为同一接口提供多种实现,如`java.sql.Driver`的MySQL与PostgreSQL实现。然而,SPI存在缺陷:需遍历所有实现并实例化,可能造成资源浪费;获取实现类方式不够灵活;多线程使用时存在安全问题。尽管如此,SPI仍是Java生态系统中实现插件化和模块化设计的重要工具。
247 0
|
4月前
|
人工智能 前端开发 安全
Java开发不可不知的秘密:类加载器实现机制
类加载器是Java中负责动态加载类到JVM的组件,理解其工作原理对开发复杂应用至关重要。本文详解类加载过程、双亲委派模型及常见类加载器,并介绍自定义类加载器的实现与应用场景。
245 4
|
4月前
|
人工智能 安全 Java
掌握Java反射:在项目中高效应用反射机制
Java反射是一种强大功能,允许程序在运行时动态获取类信息、创建对象、调用方法和访问字段,提升程序灵活性。它在框架开发、动态代理、注解处理等场景中广泛应用,如Spring和Hibernate。但反射也存在性能开销、安全风险和代码复杂性,应谨慎使用。
126 0
|
5月前
|
人工智能 Java
Java中的反射机制:深入探索与应用
本文介绍了Java反射机制的基本概念、用途及其实现方式。反射机制允许程序在运行时动态获取类的属性和方法,并调用它们,适用于处理私有成员或权限受限的情况。文章详细讲解了`Class`类的功能,包括获取类的方法、属性、注解、构造器等信息,以及通过四种方式获取`Class`对象的示例代码。此外,还探讨了类加载器、继承关系判断、动态代理等高级内容,展示了如何在运行时创建接口实例并处理方法调用。文末提供了完整的代码示例以加深理解。
135 0
Java中的反射机制:深入探索与应用
|
6月前
|
Java 区块链 网络架构
酷阿鲸森林农场:Java 区块链系统中的 P2P 区块同步与节点自动加入机制
本文介绍了基于 Java 的去中心化区块链电商系统设计与实现,重点探讨了 P2P 网络在酷阿鲸森林农场项目中的应用。通过节点自动发现、区块广播同步及链校验功能,系统实现了无需中心服务器的点对点网络架构。文章详细解析了核心代码逻辑,包括 P2P 服务端监听、客户端广播新区块及节点列表自动获取等环节,并提出了消息签名验证、WebSocket 替代 Socket 等优化方向。该系统不仅适用于农业电商,还可扩展至教育、物流等领域,构建可信数据链条。
|
8月前
|
缓存 Dubbo Java
理解的Java中SPI机制
本文深入解析了JDK提供的Java SPI(Service Provider Interface)机制,这是一种基于接口编程、策略模式与配置文件组合实现的动态加载机制,核心在于解耦。文章通过具体示例介绍了SPI的使用方法,包括定义接口、创建配置文件及加载实现类的过程,并分析了其原理与优缺点。SPI适用于框架扩展或替换场景,如JDBC驱动加载、SLF4J日志实现等,但存在加载效率低和线程安全问题。
401 7
理解的Java中SPI机制
|
6月前
|
人工智能 JavaScript Java
Java反射机制及原理
本文介绍了Java反射机制的基本概念、使用方法及其原理。反射在实际项目中比代理更常用,掌握它可以提升编程能力并理解框架设计原理。文章详细讲解了获取Class对象的四种方式:对象.getClass()、类.class、Class.forName()和类加载器.loadClass(),并分析了Class.forName()与ClassLoader的区别。此外,还探讨了通过Class对象进行实例化、获取方法和字段等操作的具体实现。最后从JVM类加载机制角度解析了Class对象的本质及其与类和实例的关系,帮助读者深入理解Java反射的工作原理。
182 0
|
7月前
|
存储 Java 编译器
Java 中 .length 的使用方法:深入理解 Java 数据结构中的长度获取机制
本文深入解析了 Java 中 `.length` 的使用方法及其在不同数据结构中的应用。对于数组,通过 `.length` 属性获取元素数量;字符串则使用 `.length()` 方法计算字符数;集合类如 `ArrayList` 采用 `.size()` 方法统计元素个数。此外,基本数据类型和包装类不支持长度属性。掌握这些区别,有助于开发者避免常见错误,提升代码质量。
767 1