Java反射和注解

反射

反射是指在运行的状态,对于任意一个类,都能够知道类里面的所有的属性和方法,并能够进行属性的赋值和方法的调用 。 在java中使用java.lang下面的Class来表示**类型的”类” ** ,在JDK中定义接口如下

其中T 表示运行时类的类型,如果不知道类型可以使用Class<?>,Class表示的实例表示正在运行的 Java 应用程序中的类(包含枚举) 和接口 , 所有的反射出来的结果都共享一个基类Class。

获得类型、方法、属性和构造器

在java中有三种方法可以反射定制类的Class(以String类型为例):

1. 通过Class.from("java.lang.String")    

2. 通过String.class    

3. 通过类的实例对像中的getClass()方法 :"abc".getClass()

为了演示反射的功能,我们首先定义一个类型:

class Person
{
    private String userName;
    private String userCode;
    public  String Sex;//字段
    
    public String getUserName() {
        return userName;
    }
    
    public void setUserName(String userName) {
        this.userName = userName;
    }
    
    public String getUserCode() {
        return userCode;
    }
    
    public void setUserCode(String userCode) {
        this.userCode = userCode;
    }
    
    public String GetUserNameWithUserCode(){
        return this.getUserName()+"_"+getUserCode();
    }
    
    public String GetUserNameWithUserCode(String prefix){
        return prefix+"_"+ this.getUserName()+"_"+getUserCode();
    }
    
    public static String GetClassName(String prefix){
        return prefix+"_Person";
    }
}

获得Person类中的方法、属性和构造器,代码如下:

try {
    Class<?> clazz = Class.forName("Person");
    
    System.out.println("--------------Person的方法如下----------------");
    Method[] methods = clazz.getMethods();
    for (Method m : methods) {
        System.out.println(m);
    }
    
    
    System.out.println("--------------Person字段如下----------------");
    Field[] fields = clazz.getFields();
    for (Field f : fields) {
        System.out.println(f);
    }
    
    System.out.println("--------------Person构造函数如下----------------");
    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    for (Constructor c : constructors) {
        System.out.println(c);
    }
} catch (Exception e) {
    e.printStackTrace();
}

运行结果:

--------------Person的方法如下----------------
public java.lang.String Person.getUserName()
public java.lang.String Person.GetUserNameWithUserCode()
public java.lang.String Person.GetUserNameWithUserCode(java.lang.String)
public void Person.setUserName(java.lang.String)
public java.lang.String Person.getUserCode()
public void Person.setUserCode(java.lang.String)
public static java.lang.String Person.GetClassName(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
--------------Person字段如下----------------
public java.lang.String Person.Sex
--------------Person构造函数如下----------------
Person()

方法的调用和属性的赋值

上面的代码我们能够获得对应的属性和方法,下面就解决如何去调用当前方法和属性进行赋值:

try {
        Class clazz =Class.forName("Person");
        Object obj=clazz.newInstance();//创建对象
        
        //set方法的获得
        Method setUserName=clazz.getMethod("setUserName",String.class);
        Method setUserCode=clazz.getMethod("setUserCode",String.class);
        //set方法的调用
        setUserName.invoke(obj,"fuwei");
        setUserCode.invoke(obj,"F0001");
        
        //一般方法的获得和调用
        Method m1=clazz.getMethod("GetUserNameWithUserCode");
        String s1= m1.invoke(obj,null).toString();
        
        Method m2=clazz.getMethod("GetUserNameWithUserCode",String.class);
        String s2= m2.invoke(obj,new Object[]{"Test"}).toString();
        
        //静态方法的调用
        Method m3=clazz.getMethod("GetClassName",String.class);
        String s3= m3.invoke(null,new Object[]{"Test"}).toString();
        
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
   } 
catch (Exception e) {
     e.printStackTrace();
   }

输出结果:

fuwei_F0001
Test_fuwei_F0001
Test_Person

使用反射实现动态代理

为了更好的理解动态代理,首先要理解静态代理的逻辑,具体的实现示意图:

具体实现类代码如下:

interface ProxyInterface {
    public void doSomething();
}

//被代理者
class RealObj implements ProxyInterface {
    @Override
    public void doSomething() {
        System.out.println("doSomething");
    }

}

//代理者
class ProxyObj implements ProxyInterface {
    @Override
    public void doSomething() {
        System.out.println("before Exector");
        new RealObj().doSomething();
        System.out.println("after Exector");
    }

}

对应的调用代码:

@Test
public  void TestProxy(){
    CallMethod(new ProxyObj());

}

public  void CallMethod(ProxyInterface pi){
    pi.doSomething();
}

运行结果:

before Exector
doSomething
after Exector

在静态的代理中,可以看到是在代理类的内部使用了真实类的实例,来实现代理的功能,但这样就只能代理指定的类的类型,丧失了灵活性。如果我们能够在代理的时候把数据传入到代理类中,就可以动态的实现类的代理。 代码实现原理如下:

interface ProxyInterface {
    public void doSomething();
}

//被代理者
class RealObj implements ProxyInterface {
    @Override
    public void doSomething() {
        System.out.println("doSomething");
    }

}

//代理者
class ProxyObj implements InvocationHandler {
    
    private Object subject;
    public ProxyObj(Object subject)
    {
        this.subject = subject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("Before Exector");
        Object obj = method.invoke(subject, args);
        System.out.println("After Exector");

        return obj;
    }
}

调用的实现:

    public  void TestProxy()
    {
        RealObj real = new RealObj();
        ProxyInterface proxy = (ProxyInterface) Proxy.newProxyInstance(
                ProxyInterface.class.getClassLoader(),
                new Class[]{ProxyInterface.class},
                new ProxyObj(real));
        proxy.doSomething();
    }

对于这个实现的原理可以参考这篇博客细说JDK动态代理的实现原理 , 生成了一个继承自Proxy和实现了ProxyInterface方法的一个对象,具体的示意为:

class proxy extends Proxy implements HelloWorld{
    ....
}

注解

Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容,常用的注解有可以参考:Java注释Override、Deprecated、SuppressWarnings详解

自定义注解

注解的大多使用情况都是结合反射,在Spring框架中也有很多都是使用反射+注解的方法来实现,下面为了更深入了解注解,我们可以自定义一个注解,注解在Java中的实现很简单:

public @interface MyAnno   
{

}

只需要这样定义就可以直接使用这个注解 ,但这个是没有任何的实际意义。在这个注解中,可以添加对应的内部方法:

public @interface MyAnno  
{

    String value();
}

在使用的时候,我们就可以对value进行赋值:

@MyAnno(value = "test")
public  void TestReflect()
{

}

对于上面的代码,注解没有干涉内部代码的逻辑,但也没有起任何的作用。对于注解的使用,我们可以根据上面说到反射来实现我们的目的。比如,我们想实现一个注解为test的方法调用,可以先获得一个方法的所有注解,然后根据注解的value值来判断是否调用 ,下面是获得所有注解的方法:

Method[] methods = clazz.getMethods();     
for (Method m : methods) {
    Annotation[] ans= m.getDeclaredAnnotations();
    for (Annotation an : ans)
    {
        System.out.println(an);
    }
}

(本文完)

作者:老付 如果觉得对您有帮助,可以下方的订阅,或者选择右侧捐赠作者,如果有问题,请在捐赠后咨询,谢谢合作 如有任何知识产权、版权问题或理论错误,还请指正。 自由转载-非商用-非衍生-保持署名,请遵循:创意共享3.0许可证 交流请加群113249828: 点击加群 或发我邮件 laofu_online@163.com

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Netty01--- Netty实现简单通信

    这个Demo的功能是客户端向服务端发送一个Hello Netty的消息,然后服务端又把消息返回给客户端

    付威
  • 设计模式--02.命令模式

    命令模式是把对象的操作方法分成一个命令,分别去执行。在分布式环境中,熔断和降级组件使用的设计模式就是命令模式。

    付威
  • Java中实现顺序IO

    对于磁盘的读写分为两种模式,顺序IO和随机IO。 随机IO存在一个寻址的过程,所以效率比较低。而顺序IO,相当于有一个物理索引,在读取的时候不需要寻找地址,效率...

    付威
  • 20190312_浅谈go&java差异(一)

    java中对于大量的比较耗时的任务多采用多线程对方式对任务进行处理,同时由于进程和线程 本身是通过宿主机OS进行管理的,当在cpu核数较少或线程分配不当 会导...

    上帝
  • 注解:为计算机而做的注释

    编写文档:通过代码里标识的注解生成文档【生成文档doc文档】 代码分析:通过代码里标识的注解对代码进行分析【使用反射】 编译检查:通过代码里标识的注解让编译器能...

    BWH_Steven
  • 【刨根问底】java注解--上

    从JDK1.5开始,Java增加了对元数据的支持,也就是Annotation,首先说明注释和注解不是同一回事,是有区别的。本次分享的注解,其实就是代码里的特色标...

    用户4143945
  • java学习:调用 java web service

    先写一个java的class:AwbModel(相当于要在web service中传输的实体对象) package webservicesample; pub...

    菩提树下的杨过
  • Java描述设计模式(06):建造者模式

    知了一笑
  • 通过例子介绍架构

    首先一个 APP 软件是一个大的系统,我们通常可以把这个大的系统划分为许多个小的模块,比如:登录注册功能,首页展示功能、个人信息功能等等某个具体的模块的功能。然...

    开发者
  • Android-实用的MVP

    code_horse

扫码关注云+社区

领取腾讯云代金券