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

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

相关文章
|
9月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
412 83
|
6月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
268 6
|
6月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
360 1
|
7月前
|
SQL Java 数据库
2025 年 Java 从零基础小白到编程高手的详细学习路线攻略
2025年Java学习路线涵盖基础语法、面向对象、数据库、JavaWeb、Spring全家桶、分布式、云原生与高并发技术,结合实战项目与源码分析,助力零基础学员系统掌握Java开发技能,从入门到精通,全面提升竞争力,顺利进阶编程高手。
1180 2
|
6月前
|
安全 前端开发 Java
从反射到方法句柄:深入探索Java动态编程的终极解决方案
从反射到方法句柄,Java 动态编程不断演进。方法句柄以强类型、低开销、易优化的特性,解决反射性能差、类型弱、安全性低等问题,结合 `invokedynamic` 成为支撑 Lambda 与动态语言的终极方案。
265 0
|
8月前
|
安全 Java 数据库连接
2025 年最新 Java 学习路线图含实操指南助你高效入门 Java 编程掌握核心技能
2025年最新Java学习路线图,涵盖基础环境搭建、核心特性(如密封类、虚拟线程)、模块化开发、响应式编程、主流框架(Spring Boot 3、Spring Security 6)、数据库操作(JPA + Hibernate 6)及微服务实战,助你掌握企业级开发技能。
1021 3
|
7月前
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
561 100
|
7月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
478 16
|
7月前
|
NoSQL Java 关系型数据库
超全 Java 学习路线,帮你系统掌握编程的超详细 Java 学习路线
本文为超全Java学习路线,涵盖基础语法、面向对象编程、数据结构与算法、多线程、JVM原理、主流框架(如Spring Boot)、数据库(MySQL、Redis)及项目实战等内容,助力从零基础到企业级开发高手的进阶之路。
514 1