Java 反射机制

Java 反射机制是构建框架技术的基础。

Java 反射机制是指在运行状态中,动态获取信息以及动态调用对象方法的功能。

java 反射有3个动态性质:

> 运行时生成对象实例,

> 运行期间调用方法,

> 运行时更改属性。

如何理解java反射机制?首先回顾一下JAVA程序的执行过程,想程序运行,Java类必须被java虚拟机加载。运行的程序在编译时就已经加载了所需要的类。

eg: Person.java > 编译器 >Person.class >JVM >运行程序

java 反射机制在编译时并不确定是哪个类被加载,而是在程序运行的时候才加载,探知,使用。这特点就是反射。在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

java反射机智能够知道类的基本结构,这种对java类结构探知的能力,称为Java类的“自审”,如java代码的 自动提示功能(对自己行为的描述,self representation),就是利用了java反射的原理,是对所创建对象的探知和自审( 检测)。

通过Java 反射,可以实现以下功能:

> 在运行时判断任意一个对象所属的类。

> 在运行时构造任意一个类的对象

> 在运行时判断任意一个类所具有的方法和属性

> 在运行是调用任意一个对象的方法

Java反射常用API:

Class 类,反射的核心类,反射所有的操作都是围绕该类类生成的。通过Class类,可以获取类的属性,方法等内容信息。

Field类,表示类的属性,可以获取和设置类中属性的值。

Method类,表示类的方法,它可以用来获取类中方法的信息,或者执行方法。

Constructor类:表示类的构造方法。

在java程序中使用反射的基本步骤如下:

 a. 导入java.lang.reflect.*;

 b. 获取需要操作的类的Java.lang.Class对象,

 c. 调用Class 的方法获取Field,Method等对象,

 d. 使用反射API 进行操作 (The Reflection API https://docs.oracle.com/javase/tutorial/reflect/)

反射的应用

获取类的信息:通过反射获取类的信息分为两步:首先获取Class对象,然后通过Class对象获取信息。

1.获取Class对象

每个类被加载后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。Java程序中获得Class对象通常由如下3中方式。

1)调用对象的getClass()方法

getClass()方法是java.lang.Object类中的一个方法,所有的java 对象都可以调用该方法,该方法会返回该对象所属类对应的Class对象。

eg Studendt p = new Student();   Class cla = p.getClass();   

2. 调用对象的class属性。

调用某个类的class属性可以获取该类对应的Class 对象,这种方式需要在编译期就知道类的名称,eg: Class cla = Student.class

3. 使用Class 类的forName()静态方法

使用Class 类的forName()静态方法也可以获取该类对应的Class 对象,该方法需要传入字符串参数,该字符串参数的值是某个类的全名,即要在类名前添加完整的包名。

eg Class cla = Class.forName("com.project.lwh.reflection.Student");       //正确

eg Class cla = Class.forName("Student");       //错误

如果传入的字符串不是类的全名,就会抛出一个ClassNotFoundException异常。

后两种方式都是直接根据类来获取该类的Class 对象,相比之下调用某个类的class属性来获取该类对应的Class对象这种方式更有优势,原因如下两点:

a. 代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在。

b.程序性能更高,因为这种方式无须调用方法,所以性能更好。

因此,大部分时候都应该使用调用某个类的class属性的方式来获取指定类的Class 对象。

2.从Class 对象获取信息

在获得了某个类所对应的Class对象之后,程序就可以调用Class 对象的方法来获得该类的真实信息。Class类提供了大量实例方法来或取Class对象所对应类的详细信息。

1)访问Class 对应的类所包含的构造方法

访问Class 对应的类所包含的构造方法常用方法由:

a. 方法: Constructor getConstructor(Class[] params) 

    说明:返回此Class对象所表示的类的指定的public 构造方法,params参数是按声明顺序标志该方法参数类型的Class对象的一个数组。构造方法的参数类型与params所指定的参数类型所匹配。eg: Constructor co = c.getConstructor(String.class,List.class);

Constructor[] getConstructor()  返回此Class对象所表示的类的所有public 构造方法

Constructor getDeclaredConstructor(Class[] params) 返回此Class 对象所表示的类的指定构造方法,与构造方法的访问级别无关。

Constructor[]  getDeclaredConstructors() 返回此Class对象所表示的所有构造方法,与构造方法的访问级别无关。

 

2) 访问Class 对应的类所包含的方法

访问Class 对用的类所报的方法常用的方法有:

方法: Method getMethod(String name,Class[] params),    

说明:返回 此Class对象所表示的类的指定public 方法,name 参数用于指定方法的名称,params 参数是 按声明顺序标志该方法参数类型的Class对象的一个数组,例如:c.getMethod("info",String.class); //c 为某Class 对象;  c.getMethod("info",String.class,Integer.class);

Method[] getMethods();     返回此Class对象所表示的类的所有public 方法

Method getDeclaredMethod(String name, Class[] params)    返回此Class对象所表示的类的指定的方法,与方法的访问级别无关。

Method[] getDeclaredMethods()  返回此Class对象所表示的类的全部方法,与方法的访问级别无关。

2)访问Class对应的类所包含的方法

方法:Method getMethod(String name,Class[] params) 

说明: 返回此Class对象所表示的类的指定的public方法,name 参数用于指定方法名称, params 参数是按声明顺序标志该方法参数类型的,Class对象的一个数组。例如:c.getMethod("info",String.class);// c为某Class对象。

//c.getMethod("info",String.class,Integer.class)

Method[] getMethods()  返回此Class对象所表示的类的所有public 方法。

Method[] getDeclaredMethod(String name, Class[] params)  返回此Class对象所表示的类的指定方法,与方法的访问级别无关。

Method[] getDeclaredMethods()     返回此Class 对象所表示的类的全部方法,与方法的访问级别无关。

3)访问类包含属性的常用方法

方法: Field getField(String name)  

说明:返回此Class对象所表示的类的指定的public 属性,name参数用于指定属性名称, 如:c.getField("age"); //c为某Class对象,age为属性名。

Field[] getFields()  返回此Class对象所表示的类的所有public 属性。

Field[] getDeclaredFiled(String name)  返回此Class对象所表示的类的的指定属性,与属性的访问级别无关。

Field[] getDeclaredFields() 返回此Class对象所表示的类的全部属性,与属性的访问级别无关。

4)访问Class对应的类所包含的注释

访问Class对应的类所包含的注释的常用方法:

访问类包含注释的常用方法

方法:<A extends Annotation>A  getAnnotation(Class<A> annotationClass)  

说明:试图获取该Class对象所表示类上指定类型的注释,如果该类型的注释不存在则返回null值,其中 annotationClass参数对应于注释类型的Class对象。

Annotation[] getAnnotations()   返回此元素上存在的所有注释

Annotation[] getDeclaredAnnotations()  返回直接存在于此元素上的所有注释
Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。

boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.

访问Class对应 的类的其他信息

访问Class对应的类的其他一些常用方法的定义

Class[] getDeclaredClasses() 返回该Class对象的对应类里包含的全部内部类

Class[] getDeclaringClass() 访问Class对应的类所在的外部类

Class[] getInterfaces[]   返回该Class对应类所实现的全部接口

int getModifiers()  返回此类或接口的所有修饰符,返回的修饰符由public ,protected,private,final,static 和abstract等对应的常量组成,返回的整数应使用Modifier工具类的方法来解码,才可以获取真实的修饰符。

Package getPackage() 获取此类的包

String getName()此字符串形式返回此Class对象所表示的类的名称

String getSimpleName() 此字符串形式返回此Class对象所表示的类的简称

Class  getSuperclass()  返回该Class所表示的类的超类对应的Class对象。

 Method, Constructor,Field这3个类都定义在java.lang.reflect下,并实现了java.lang.reflect.Member接口,程序可以通过 Method对象来执行对应的方法,通过 Constructor对象来调用相应的构造方法创建对象,通过Field对象直接访问并修改对象的属性。

https://blog.csdn.net/DaJian35/article/details/79705193

在使用枚举类的时候,建议用getDeclaringClass返回枚举类。但是为什么不用getClass呢?下面来看看代码:

    public enum FruitEnum{
        BANANA,APPLE;

        public static void main(String[] args) {
            System.out.println(BANANA.getDeclaringClass());
            System.out.println(BANANA.getClass());
        }
    }

    # 运行结果
        class FruitEnum
        class FruitEnum
    }
  •  
    public enum FruitEnum{
        BANANA{
            String getName() {
                return "香蕉";
            }
        },APPLE{
            String getName() {
                return "苹果";
            }
        };

        abstract String getName();

        public static void main(String[] args) {
            System.out.println(BANANA.getDeclaringClass());
            System.out.println(BANANA.getClass());
        }
    }

    # 运行结果
    class FruitEnum
    class FruitEnum$1
  • 20这种情况下就不同了。因为此时BANANAAPPLE相当于FruitEnum的内部类。下面来看看Enum的源码:
    public final Class<E> getDeclaringClass() {
        Class var1 = this.getClass();
        Class var2 = var1.getSuperclass(); // 获取上一级的父类
        return var2 == Enum.class?var1:var2;
    }
  •  

当上一级的父类不是Enum,则返回上一级的class。因此对枚举类进行比较的时候,使用getDeclaringClass是万无一失的。

 

通过反射来创建对象的方式有两种:

1)使用Class对象的newInstance()方法创建对象

使用Class对象的newInstance()方法来创建Class对象对应类的实例,这种方式要求该Class对象的对应类由默认构造方法,而执行newInstance()方法时实际上是利用默认构造方法来创建该类的实例。

eg: Class cla= Date.class;  

       Date d =(Date)cla.newInstance();

2)使用Constructor对象创建对象

使用Constructor对象创建对象,要先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用某个类的指定构造方法来创建实例。

eg: Class cla = Date.class;

Constructor cu = cla.getConstructor(long.class);

Date d= (Date) cu.newInstance(1987);

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值