java的反射与注解

简介: java的反射与注解

一、反射

1.1、反射概述

JAVA反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何

类的内部信息,并能直接操作任意对象的内部属性及方法。

反射的功能

反射的应用

1. 通过使用类全名创建类实例来使用外部用户定义的类。

例如Java数据库开发中,需要在运行时使用JDBC驱动包中的驱动类,可以通过反射机制在运行中获取。

Class.forName( “com.mysql.cj.jdbc.Driver” );

2. 开发类浏览器和智能IDE。

例如Eclipse工具,左侧的包浏览器可以查看类的结构,右侧代码编辑区,如果启用了提示功能,在对象后输入“.”运算符,会自动提示该对象所属类的所有可用属性和方法。这些IDE工具的功能需要反射机制实现。

3. 在测试工具中用于检测类的内部结构。

例如Java的单元测试框架Junit就是基于反射和注解实现的

4. 在框架开发中用于实现配置信息的处理。

例如Java Web开发中要学习的Struts2,Spring的框架功能的实现都需要用到反射

5. 实现Java的动态代理。

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

/**
 *
 * 动态代理的举例
 *
 * @author shkstart
 * @create 2019 上午 10:18
 */
interface Human{
    String getBelief();
    void eat(String food);
}
//被代理类
class SuperMan implements Human{
    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }
    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃" + food);
    }
}
class HumanUtil{
    public void method1(){
        System.out.println("====================通用方法一====================");
    }
    public void method2(){
        System.out.println("====================通用方法二====================");
    }
}
class ProxyFactory{
    //调用此方法,返回一个代理类的对象。解决问题一
    public static Object getProxyInstance(Object obj){//obj:被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}
class MyInvocationHandler implements InvocationHandler{
    private Object obj;//需要使用被代理类的对象进行赋值
    public void bind(Object obj){
        this.obj = obj;
    }
    //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        HumanUtil util = new HumanUtil();
        util.method1();
        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj,args);
        util.method2();
        //上述方法的返回值就作为当前类中的invoke()的返回值。
        return returnValue;
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("四川麻辣烫");
        System.out.println("*****************************");
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}

最后思考体会反射的动态性

框架 = 反射 + 注解 + 设计模式。

1.2、使用反射

1.2.1 Class类

java.lang.Class类是所有Reflection API的切入点,是所有反射操作的入口。

在Java程序运行过程中,对程序中每种类型的对象,Java虚拟机都会实例化一个不可变的java.lang.Class实例,每个对象都是引用或者原始类型。

反射机制里主要会用到以下四种类

1.java.lang.Class.java:类对象;

2.java.lang.reflect.Constructor.java:类的构造器对象;

3.java.lang.reflect.Method.java:类的方法对象;

4.java.lang.reflect.Field.java:类的属性对象;

1.获取Class实例的三种方法,最常用的是第3种;

  1. 对象.getClass()
  2. 类型名.class
  3. Class.forName()

(示例代码于code/ClassEx.java)

2.获取类的成员

这时候就用到了java.lang.reflect.Field.java类。一个Field提供类或接口中一个成员变量(属性)的信息,也可以动态访问。

Class中提供了两类用于访问成员变量,成员方法和构造方法的方法:

  1. 列出所有成员的方法
  2. 根据名字搜索特定成员的方法
public Field getDeclaredField(String name)
//根据名字获取类中定义的成员变量,不包括继承父类的成员
public Field getField(String name)
//根据名字获取类中定义的公有成员变量,包括继承父类的成员
public Field[] getDeclaredFields()
//获取类中声明的所有成员变量,返回Field[]类型,不含继承父类的成员
    
public Field[] getFields()
//获取类中声明的所有公有成员变量,返回Field[]类型
• 1

(示例代码于 code/RefEx)

3.获取类的成员方法

public Method getDeclaredMethod(String name,
                                Class<?>... parameterTypes)
//根据名字获取类中定义的成员方法,不包括继承父类的成员方法
public Method getMethod(String name,
                        Class<?>... parameterTypes)
//根据名字获取类中定义的公有成员方法,包括继承父类的成员方法
public Method[] getDeclaredMethods()
//获取类中声明的所有成员方法,返回Method[]类型,不含继承父类的成员
public Method[] getMethods()
//获取类中声明的所有公有成员方法,返回Method[]类型

(示例代码于 code/RefEx)

4.获取类的构造方法

public Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes)
//根据参数类型列表获取类中定义的构造方法
public Constructor<T> getConstructor(Class<?>… parameterTypes)
//根据参数类型列表获取类中定义的公有构造方法
public Constructor<?>[] getDeclaredConstructors()
//获取类中声明的所有构造方法,返回Constructor[]类型
public Constructor<?>[] getConstructors()
//获取类中声明的所有公有构造方法,返回Constructor[]类型

5.调用成员方法

**Method.invoke(Object obj, Object… args)**实现方法调用,多用于不得不用反射的的情况下。

第一个参数是调用这个方法的类实例,如果该方法是静态的,第一个参数为null

后面几个参数是该方法的参数,如果方法没有参数,可以省略

调用成员方法的案例:示例代码见 code/Deet.java

6.用反射的方式给对象的属性设置值,获取对象的属性值

  • 给定类的实例,可以使用反射来设置该类实例中成员变量的值。
  • 通常是以常规方式无法设置的情况下才这样操作。
  • 因为这种访问违反了类的封装性的设计意图,耗费额外的系统开销,所以应该尽可能的酌情使用。

(示例代码见 code/Test.java)

7**.通过Constructor实例创建对象。**

创建类实例(类对象)(重点掌握):

常规情况下是使用new操作符调用类的构造方法来创建类实例:

Date date = new Date();

使用反射创建类实例有两种方法:

Class.newInstance()
//只能调用类的无参数的非私有构造方法
//抛出构造方法的异常
Constructor.newInstance(Object... initargs)
//可以调用类的任何构造方法
//用InvocationTargetException封装异常来抛出

使用反射的注意事项

反射是强大的,但不应滥用。如果可以在不使用反射的情况下进行操作,则优选避免使用反射。

反射增加了JVM的系统开销,性能上比不使用反射慢。

反射可能违反某些安全策略。

反射允许访问私有成员,打破了封装,可能破坏可移植性。

二、注解

2.1 什么是注解

注解,一种元数据形式,提供有关程序的数据,该数据不属于程序本身。注释对其注释的代码的操作没有直接影响。

注解的语法:

@注解类型名

注释有多种用途,其中包括:

  • 编译前:为编译器提供编译检查的依据,辅助检查代码错误或抑制检查异常。
  • 编译中或发布时:给编译器提供信息生成代码或给其他工具提供信息生成文档等。
  • 运行时:在运行过程中提供信息给解释器,辅助程序执行。
  • 注解经常和反射结合起来用,多用于框架中。

2.2 内置的注解

2.2.1 Override
  • 加在方法前
  • 表示重写(覆盖)父类的方法;
  • 如果重写(覆盖)有错误,则报错。
  • 因此重写父类方法,请加上@Override注解。
class Parent {
    String name;
    public Parent(){}
    public Parent(String name)
    {
        this.name = name;
    }
    public void sayHello()
    {
        System.out.println("你好,我是" + name );
    }
}
public class Child extends Parent
{
    String name;
    public Child{}
    @Override
    public void sayHello(String str)
    {
        //这里会报错
        super.sayHello();
        System.out.println("Hello,im Datawhale");
    }
}
/**
本段代码则不会提示错误
public class Child extends Parent
{
    String name;
    public Child{}
    @Override
    public void sayHello()
    {
        super.sayHello();
        System.out.println("Hello,im Datawhale");
    }
}
**/

2.3 自定义注解

日常开发中我们使用class、interface比较多,而注解和它们一样,也是一种类的类型,他是用的修饰符为 @interface @元注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,语法如下:

public @interface MyTestAnnotation {
     注解属性  [default 默认值]

2.4 元注解

元注解负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。

@Target,

@Retention,

@Documented,

@Inherited

@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。

 取值(ElementType)有:

  1.CONSTRUCTOR:用于描述构造器

  2.FIELD:用于描述域

  3.LOCAL_VARIABLE:用于描述局部变量

  4.METHOD:用于描述方法

  5.PACKAGE:用于描述包

  6.PARAMETER:用于描述参数

  7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

@Target(ElementType.TYPE)
public @interface Table {
 }
@Target(ElementType.FIELD)
public @interface NoDBColumn {
}

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。

ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中(如:泛型声明。

ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中。

最后如果本文对您有所帮助,望一键三连哦,非常感谢。

相关文章
|
2月前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
91 43
Java学习十六—掌握注解:让编程更简单
|
1月前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
50 14
|
1月前
|
前端开发 Java
[Java]讲解@CallerSensitive注解
本文介绍了 `@CallerSensitive` 注解及其作用,通过 `Reflection.getCallerClass()` 方法返回调用方的 Class 对象。文章还详细解释了如何通过配置 VM Options 使自定义类被启动类加载器加载,以识别该注解。涉及的 VM Options 包括 `-Xbootclasspath`、`-Xbootclasspath/a` 和 `-Xbootclasspath/p`。最后,推荐了几篇关于 ClassLoader 的详细文章,供读者进一步学习。
32 12
|
2月前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
22 0
[Java]反射
|
3月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
67 9
Java——反射&枚举
|
2月前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
26 2
|
25天前
|
Java 编译器
Java进阶之标准注解
Java进阶之标准注解
29 0
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
2月前
|
JSON Java 数据库
java 常用注解大全、注解笔记
关于Java常用注解的大全和笔记,涵盖了实体类、JSON处理、HTTP请求映射等多个方面的注解使用。
37 0
java 常用注解大全、注解笔记
|
3月前
|
Java 编译器 程序员
Java注解,元注解,自定义注解的使用
本文讲解了Java中注解的概念和作用,包括基本注解的用法(@Override, @Deprecated, @SuppressWarnings, @SafeVarargs, @FunctionalInterface),Java提供的元注解(@Retention, @Target, @Documented, @Inherited),以及如何自定义注解并通过反射获取注解信息。
Java注解,元注解,自定义注解的使用