JAVA编程不可不知的反射用法总结

简介: Java反射是一种非常强大的机制,它可以在同一个系统中去检测内部的类的字段、方法和构造函数。它非常多的Java框架中,都大量应用了反射技术,如Hibernate和Spring。可以说,反射机制的特征让Java可以构建异常强大,具备柔性的系统。

       Java反射是一种非常强大的机制,它可以在同一个系统中去检测内部的类的字段、方法和构造函数。它非常多的Java框架中,都大量应用了反射技术,如Hibernate和Spring。可以说,反射机制的特征让Java可以构建异常强大,具备柔性的系统。

        虽然Java反射机制存在效率低、速度慢和安全性不高等弊端,但在很多场景下,这些特征并不是主要的因素,或者可以通过缓存或者JVM优化等来逐步提升执行效率。

      根据网站https://docs.oracle.com/javase/tutorial/reflect/index.html的说法,反射技术能够检查或修改在JVM中应用程序在运行时的行为,这是一个比较高级的语言特性和一种强大的技术,反射可以使应用程序实现本来不可能的操作。

     下面对Java反射的基础知识进行说明和总结:

首先定义一个MyBase类,其中有私有字段,也有公有字段。同时也有公有方法和私有方法。MyBase类示例如下:

packagecom.hwdev.demo;
/*** 基类示例* @author wangming*/publicclassMyBase {
//公有字段  publicintversion=1;
//私有字段privateStringdate="2021-05-18" ;
//公有方法   publicvoidsay2(Stringmsg){
System.out.println("Hello "+msg);
    }
//私有方法privateStringgetData(){
returnthis.date;
    }
}

这里再定义一个Hello类,它继承自MyBase类,通过继承主要用于验证一下反射对于父类、子类的反射用法。

packagecom.hwdev.demo;
/**** @author wangming*/publicclassHelloextendsMyBase {
publicStringauthor="JackWang" ;
publicintversion=1;
privateStringcompany="kzcloud" ;
publicvoidsay(Stringmsg){
System.out.println("Hello "+msg);
    }
publicvoidsetAuthor(Stringauthor){
this.author=author;
    }
publicStringgetAuthor(){
returnthis.author;
    }
privateintgetVersion(){
returnthis.version;
    }
}

关于Java反射,功能强大的就是可以通过字符串配置来动态从系统中调用方法或者修改其中某个对象的字段值,而Class.forName方法即可以通过传入类全路径字符串名称来获取对应的Class对象,非常的方便。另外通过getField方法和GetMethod方法可以获取指定字段和方法,并动态调用。

packagecom.hwdev.demo;
importjava.lang.reflect.*;
importjava.util.Arrays;
/*** 反射第一种用法 Class.forName* @author wangming*/publicclassReflectDemo01 {
publicstaticvoidTest() {
try        {
//通过字符串全路径类名查找ClassClasshelloC=Class.forName("com.hwdev.demo.Hello"); 
//获取所有公有的字段数组,私有的无法获取   Field [] fields=helloC.getFields();
//打印字段数组内容System.out.println(Arrays.toString(fields));
//[public java.lang.String com.hwdev.demo.Hello.author, public int com.hwdev.demo.Hello.version]//实例化Objectobj=helloC.newInstance();
//获取特定字段,比遍历Field[]效率更高Fieldf=helloC.getField("author");
if (f!=null){
//关闭安全检查,提高效率f.setAccessible(true);
//获取字段author内容Stringauthor= (String)f.get(obj);
System.out.println("author="+author);
//author=JackWang            }
//获取所有公有的方法数组,私有的无法获取 Method [] methods=helloC.getMethods();
//打印方法数组内容,子类等方法也可以获取到System.out.println(Arrays.toString(methods));
//本类所有方法Method [] methods2=helloC.getDeclaredMethods();
//打印方法数组内容System.out.println(Arrays.toString(methods2));
//获取特定方法,第二个参数String.class为say方法的参数类型//say(java.lang.String)Methodm=helloC.getDeclaredMethod("say",String.class); 
if (m!=null){
//关闭安全检查,提高效率m.setAccessible(true);
//获取字段author内容ObjectreturnValue=m.invoke(obj, newObject[]{"Java"});
//Hello Javaif (returnValue!=null){
System.out.println("returnValue ="+returnValue);    
                }
            }
        }catch(ClassNotFoundException|SecurityExceptionex){
ex.printStackTrace();
        }
catch(Exceptionex){
ex.printStackTrace();
        }
    }    
}

JAVA编程不可不知的反射用法总结丨【奔跑吧!JAVA】

jackwangcumt发表于 2021/05/18 13:36:39

 

3.7k+00

编辑删除

【摘要】 Java反射是一种非常强大的机制,它可以在同一个系统中去检测内部的类的字段、方法和构造函数。它非常多的Java框架中,都大量应用了反射技术,如Hibernate和Spring。可以说,反射机制的特征让Java可以构建异常强大,具备柔性的系统。

       Java反射是一种非常强大的机制,它可以在同一个系统中去检测内部的类的字段、方法和构造函数。它非常多的Java框架中,都大量应用了反射技术,如Hibernate和Spring。可以说,反射机制的特征让Java可以构建异常强大,具备柔性的系统。

        虽然Java反射机制存在效率低、速度慢和安全性不高等弊端,但在很多场景下,这些特征并不是主要的因素,或者可以通过缓存或者JVM优化等来逐步提升执行效率。

      根据网站https://docs.oracle.com/javase/tutorial/reflect/index.html的说法,反射技术能够检查或修改在JVM中应用程序在运行时的行为,这是一个比较高级的语言特性和一种强大的技术,反射可以使应用程序实现本来不可能的操作。

     下面对Java反射的基础知识进行说明和总结:

首先定义一个MyBase类,其中有私有字段,也有公有字段。同时也有公有方法和私有方法。MyBase类示例如下:

package com.hwdev.demo;
/**
 * 基类示例
 * @author wangming
 */
public class MyBase {
    //公有字段  
    public int version  = 1;
    //私有字段
    private String date = "2021-05-18" ;
    //公有方法   
    public void say2(String msg){
  System.out.println("Hello " + msg);
    }
    //私有方法
    private String getData(){
        return this.date;
    }
}

这里再定义一个Hello类,它继承自MyBase类,通过继承主要用于验证一下反射对于父类、子类的反射用法。

package com.hwdev.demo;
/**
 *
 * @author wangming
 */
public class Hello extends MyBase {
    public String author = "JackWang" ;
    public int version  = 1;
    private String company = "kzcloud" ;
    public void say(String msg){
       System.out.println("Hello " + msg);
    }
    public void setAuthor(String author){
      this.author = author;
    }
    public String getAuthor(){
        return this.author;
    }
    private int getVersion(){
        return this.version;
    }
}

关于Java反射,功能强大的就是可以通过字符串配置来动态从系统中调用方法或者修改其中某个对象的字段值,而Class.forName方法即可以通过传入类全路径字符串名称来获取对应的Class对象,非常的方便。另外通过getField方法和GetMethod方法可以获取指定字段和方法,并动态调用。

package com.hwdev.demo;
import java.lang.reflect.*;
import java.util.Arrays;
/**
 * 反射第一种用法 Class.forName
 * @author wangming
 */
public class ReflectDemo01 {
     public static void Test() {
        try
        {
            //通过字符串全路径类名查找Class
            Class helloC = Class.forName("com.hwdev.demo.Hello"); 
            //获取所有公有的字段数组,私有的无法获取   
            Field [] fields = helloC.getFields();
            //打印字段数组内容
            System.out.println(Arrays.toString(fields));
            //[public java.lang.String com.hwdev.demo.Hello.author, public int com.hwdev.demo.Hello.version]
            //实例化
            Object obj = helloC.newInstance();
            //获取特定字段,比遍历Field[]效率更高
            Field f = helloC.getField("author");
            if (f != null){
                //关闭安全检查,提高效率
                f.setAccessible(true);
                //获取字段author内容
                String author = (String)f.get(obj);
                System.out.println("author=" + author);
                //author=JackWang
            }
            //获取所有公有的方法数组,私有的无法获取 
            Method [] methods = helloC.getMethods();
            //打印方法数组内容,子类等方法也可以获取到
            System.out.println(Arrays.toString(methods));
            //本类所有方法
            Method [] methods2 = helloC.getDeclaredMethods();
            //打印方法数组内容
            System.out.println(Arrays.toString(methods2));
            //获取特定方法,第二个参数String.class为say方法的参数类型
            //say(java.lang.String)
            Method m = helloC.getDeclaredMethod("say",String.class); 
            if (m != null){
                //关闭安全检查,提高效率
                m.setAccessible(true);
                //获取字段author内容
                Object returnValue = m.invoke(obj, new Object[]{"Java"});
                //Hello Java
                if (returnValue!=null){
                    System.out.println("returnValue =" + returnValue);    
                }
            }
        }catch(ClassNotFoundException | SecurityException ex){
            ex.printStackTrace();
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
    }    
}

     这里需要注意:xxx.getMethods()方法默认情况下,会返回本类、父类、父接口的公有方法,而xxx.getDeclaredMethods()返回本类的 所有方法,包括私有的方法。同理,反射API中其他getXXX和getDeclaredXXX的用法类似。

packagecom.hwdev;
importcom.hwdev.demo.ReflectDemo01;
/*** * @author wangming*/publicclassMain {
/*** @param args the command line arguments*/publicstaticvoidmain(String[] args) {
//反射第一种用法 Class.forNameReflectDemo01.Test();
    }    
}

执行程序,输出的结果如下:

[publicjava.lang.Stringcom.hwdev.demo.Hello.author, publicintcom.hwdev.demo.Hello.version, publicintcom.hwdev.demo.MyBase.version]
author=JackWang[publicvoidcom.hwdev.demo.Hello.say(java.lang.String), publicvoidcom.hwdev.demo.Hello.setAuthor(java.lang.String), publicjava.lang.Stringcom.hwdev.demo.Hello.getAuthor(), publicvoidcom.hwdev.demo.MyBase.say2(java.lang.String), publicfinalvoidjava.lang.Object.wait() throwsjava.lang.InterruptedException, publicfinalvoidjava.lang.Object.wait(long,int) throwsjava.lang.InterruptedException, publicfinalnativevoidjava.lang.Object.wait(long) throwsjava.lang.InterruptedException, publicbooleanjava.lang.Object.equals(java.lang.Object), publicjava.lang.Stringjava.lang.Object.toString(), publicnativeintjava.lang.Object.hashCode(), publicfinalnativejava.lang.Classjava.lang.Object.getClass(), publicfinalnativevoidjava.lang.Object.notify(), publicfinalnativevoidjava.lang.Object.notifyAll()]
[publicvoidcom.hwdev.demo.Hello.say(java.lang.String), publicvoidcom.hwdev.demo.Hello.setAuthor(java.lang.String), publicjava.lang.Stringcom.hwdev.demo.Hello.getAuthor(), privateintcom.hwdev.demo.Hello.getVersion()]
HelloJava

从输出结果上来看,Field [] fields = helloC.getFields();不但可以获取Hello类的公有字段,还可以获取到父类MyBase的公有字段:com.hwdev.demo.MyBase.version

而Method [] methods2 = helloC.getDeclaredMethods();则可以获取本类,即Hello类所有的方法,包括公有的方法和私有的方法。因此,Java反射可以访问类的私有字段和方法,从而暴露内部的信息,这也是Java反射有安全问题的原因。

       由于Java方法支持重载,因此同名的方法可以存在多个,即参数不同,因此在用反射调用方法时,需要指定方法的参数类型,这样就可以明确调到的具体是哪个方法签名,如Method m = helloC.getDeclaredMethod("say",String.class); 调用的是public void com.hwdev.demo.Hello.say(java.lang.String) 。

      除了可以用Class.forName来进行反射外,还可以通过如下方式来获取反射对象:

Hellohello=newHello();
ClasshelloC=hello.getClass();
Field [] fields=helloC.getFields();
//////////////////////////////////////////ClasshelloC=Hello.class; 
Field [] fields=helloC.getFields();

    下面介绍一下如何用Java反射修改私有字段和调用私有方法的示例:

packagecom.hwdev.demo;
importjava.lang.reflect.*;
/*** 反射访问私有字段和方法* @author wangming*/publicclassReflectDemo02 {
publicstaticvoidTest() {
try        {
//通过已有类查找Class         ClasshelloC=Hello.class; 
//实例化Objectobj=helloC.newInstance();
//获取特定私有字段Fieldf=helloC.getDeclaredField("company");
if (f!=null){
//私有必须开启f.setAccessible(true);
//设置私有字段值f.set(obj, "newKZ");
//获取字段author内容Stringfv= (String)f.get(obj);
System.out.println("company="+fv);
//company=newKZ            } 
//获取私有方法Methodm=helloC.getDeclaredMethod("getVersion", null); 
if (m!=null){
//私有必须开启m.setAccessible(true);
ObjectreturnValue=m.invoke(obj, null);
if (returnValue!=null){
//returnValue =1System.out.println("returnValue ="+returnValue);    
                }
            }
        }catch(SecurityExceptionex){
ex.printStackTrace();
        }
catch(Exceptionex){
ex.printStackTrace();
        }
    }    
}

     另外,Java反射可以获取注解信息,这个对于ORM框架来讲,用的非常多。

packagecom.hwdev.demo;
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
/*** 注解示例* @author wangming*/@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public@interfaceORMAnnotation {
publicStringFieldName();
publicStringFieldType();
}

其中,@Retention(RetentionPolicy.RUNTIME)表示注解可以在运行时通过反射访问。@Target(ElementType.FIELD) 表示这个注解只能用在字段上面。同理,可以把FIELD改为Type或者Method等。

packagecom.hwdev.demo;
importjava.lang.annotation.Annotation;
importjava.lang.reflect.*;
/*** 反射或者字段注解* @author wangming*/publicclassReflectDemo03 {
publicstaticvoidTest() {
try        {
ClasshelloC=Class.forName("com.hwdev.demo.HelloAnnotation");      
Field[] fields=helloC.getDeclaredFields();
for(Fieldf : fields){
//关闭安全检查,提高效率f.setAccessible(true);
Annotationann=f.getAnnotation(ORMAnnotation.class);
if(anninstanceofORMAnnotation){
ORMAnnotationormAnn= (ORMAnnotation) ann;
System.out.println("FieldName="+ormAnn.FieldName());
System.out.println("FieldType="+ormAnn.FieldType());
                }
            }          
        }catch(ClassNotFoundException|SecurityExceptionex){
ex.printStackTrace();
        }
catch(Exceptionex){
ex.printStackTrace();
        }
    }    
}

      执行此示例,则输出如下:

FieldName=f_authorFieldType=varchar(50)
FieldName=f_verFieldType=int

     再次,介绍一下如何用反射获取方法参数个数和类型,其中包含泛型的信息获取:

packagecom.hwdev.demo;
importjava.util.ArrayList;
importjava.util.List;
/*** 泛型示例* @author wangming*/publicclassGenericCls {
protectedList<String>myList=newArrayList(); 
publicGenericCls(intsize){      
for(inti=0;i<size;i++){
myList.add("item"+i);
       }
   }
publicList<String>getList(){ 
returnthis.myList;
   }
publicStringgetList(intidx){ 
returnthis.myList.get(idx);
   }
}
packagecom.hwdev.demo;
importjava.lang.reflect.*;
/*** 反射获取方法参数* @author wangming*/publicclassReflectDemo05 {
publicstaticvoidTest() {
try        {
ClasshelloC=Class.forName("com.hwdev.demo.GenericCls"); 
//构造函数调用Objectobj=helloC.getConstructor(int.class).newInstance(3);
Methodmethod=helloC.getMethod("getList", int.class);
Class<?>returnType=method.getReturnType();
System.out.println("ReturnType = "+returnType.getName());
Parameter[] params=method.getParameters();
for(Parameterp : params){    
System.out.println("ParameterizedType = "+p.getParameterizedType());
System.out.println("getModifiers = "+p.getModifiers());
System.out.println("getName = "+p.getName());
System.out.println("getType = "+p.getType());
            }
//调用方法Objectret=method.invoke(obj, newObject[]{2});
System.out.println("ret = "+ret.toString());
Methodmethod2=helloC.getMethod("getList", null);
TypegreturnType=method2.getGenericReturnType();
System.out.println("getGenericReturnType = "+returnType.getName());
if(greturnTypeinstanceofParameterizedType){
ParameterizedTypetype= (ParameterizedType) greturnType;
System.out.println("type = "+type.getTypeName());
Type[] typeArguments=type.getActualTypeArguments();
for(TypetypeArgument : typeArguments){
ClasstypeArgClass= (Class) typeArgument;
System.out.println("typeArgClass = "+typeArgClass);
                }
            }
        }catch(ClassNotFoundException|SecurityExceptionex){
ex.printStackTrace();
        }
catch(Exceptionex){
ex.printStackTrace();
        }
    }    
}

执行上述示例,输出如下所示。

ReturnType=java.lang.StringParameterizedType=intgetModifiers=0getName=arg0getType=intret=item2getGenericReturnType=java.lang.Stringtype=java.util.List<java.lang.String>typeArgClass=classjava.lang.String

关于反射还有非常多的知识点可以讲解,比如利用反射技术实现插件的动态加载等。反射的效率问题,可以通过使用高效的第三方反射库,或者加入缓冲机制来解决,这里不再赘述。

相关文章
|
4天前
|
设计模式 安全 Java
Java编程中的单例模式:理解与实践
【10月更文挑战第31天】在Java的世界里,单例模式是一种优雅的解决方案,它确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的实现方式、使用场景及其优缺点,同时提供代码示例以加深理解。无论你是Java新手还是有经验的开发者,掌握单例模式都将是你技能库中的宝贵财富。
12 2
|
6天前
|
Java API Apache
Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
【10月更文挑战第29天】Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
35 5
|
1天前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
78 53
|
1天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
15 1
|
4天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
5天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
30 4
|
5天前
|
消息中间件 供应链 Java
掌握Java多线程编程的艺术
【10月更文挑战第29天】 在当今软件开发领域,多线程编程已成为提升应用性能和响应速度的关键手段之一。本文旨在深入探讨Java多线程编程的核心技术、常见问题以及最佳实践,通过实际案例分析,帮助读者理解并掌握如何在Java应用中高效地使用多线程。不同于常规的技术总结,本文将结合作者多年的实践经验,以故事化的方式讲述多线程编程的魅力与挑战,旨在为读者提供一种全新的学习视角。
27 3
|
4天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。
|
6天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
19 1
|
10天前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
18 3