java反射的基本使用

java反射的基本使用

  • 作者:Geticsen
  • 时间:2020-08-31
  • 67人已阅读
简介 java反射的基本使用,例如如何获取到类的所有方法如何通过反射得到一个实例化对象

什么是反射?

反射就是把java类中的各种成分映射成一个个的Java对象,例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。

反射机制的功能

Java反射机制主要提供了以下功能:

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

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

  • 在运行时判断任意一个类所具有的成员变量和方法。

  • 在运行时调用任意一个对象的方法。

  • 生成动态代理。

实现反射机制的类

Java中主要由以下的类来实现Java反射机制:

  • Class类:代表一个类。

  • Field类:代表类的成员变量(成员变量也称为类的属性)。

  • Method类:代表类的方法。

  • Constructor类:代表类的构造方法。

  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

  • Modifier类:类的修饰符

  这些类都位于java.lang.reflect包中

image.png

常用方法介绍

名称获取

此类方法用于获取类的名称信息
getName()方法

  • 如果是一个实体类,则会返回完整包名路径名称,

  • 如果是一个数组类型,则返回内部嵌套深度的一个或多个"["字符,后面拼接上基本数据类型的二进制名称,二进制名称表如下:

Element TypeEncoding
booleanZ
byteB
charC
class or interfaceLclassname
doubleD
floatF
intI
longJ
shortS

注解相关

可获取此类是否是注解,是否包含某注解并获取到其对象,获取全部注解

方法作用
getAnnotation(Class<A> annotationClass)获取传入的注解对象,如果不存在,则返回null
getAnnotations()返回此类上的所有注解
getAnnotationsByType(Class<A> annotationClass)返回传入的注解对象数组,与getAnnotation()的区别是检测传入的注解是否是重复元素
isAnnotation()判断这个类是否是一个注解类
isAnnotationPresent(Class<? extends Annotation> annotationClass)是否包含传入的注解类,效果与getAnnotation()!=null相同

构造方法相关

在介绍构造方法之前,先介绍一个类Constructor,它的作用是提供一个类的单个构造方法的信息访问,如果一个类有两个构造方法,那么这个类就会对应有两个Constructor类,它可以使用newInstance方法来进行类的构造方法实现并进行扩展,但如果发生缩小转换则会抛出IllegalArgumentException异常,比如这个类有两个构造参数却只传入一个,就会抛异常。
获取Constructor信息的方法有:

方法作用
getConstructors()返回这个类的公共构造函数的 Constructor对象的数组
getConstructor(Class<?>..parameterTypes)传入一个指定的参数类型来获取特定的公共构造方法类
getDeclaredConstructors()与getConstructors的区别是返回所有的构造方法数组,不限于public protected private
getDeclaredConstructor(Class<?>..parameterTypes)与getConstructor的区别是会返回所有类型的构造方法

字段相关

此类型方法是用的最多的一种,希望小伙伴们可以熟练掌握
在介绍字段方法之前,先介绍一个类Field,它用于保存,修改字段的信息,甚至可以修改字段的访问权限,常用的方法如下:

方法作用
getName()获取字段名称
get()传入需要获取的值的类的对象,获取该字段的值,返回object类型,使用的时候需要做类型判断
getBoolean(),getInt()...获取指定类型的字段值
set(),setBoolean()...将指定的类的指定值设置为新值
isAccessible()判断此字段是否有访问权限
setAccessible()设置字段的权限,为true代表可以访问

使用Class来获取字段信息:

方法作用
getFields()获取所有的公共字段
getField()传入字段的名称,返回公共字段对象
getDeclaredFields()获取所有字段,返回一个Field数组
getDeclaredField传入字段的名称,返回字段对象,无访问限制

方法相关

此类型方法也是用的比较多的一种,是反射调用方法的关键
class中关于方法的封装都是给Method类来操作的,它可以获取,修改,执行类的方法,核心方法就是invoke(),作用是执行方法,第一个参数是需要执行的方法的类对象,随后是需要执行的方法参数值。
获取Method对象有四个方法:

方法作用
getMethods()获取类的所有公共方法
getMethod()获取指定公共方法,传入一个方法的名称与参数的类型
getDeclaredMethods()获取类的所有方法
getDeclaredMethod()获取类的指定方法,传入参数同getMethod()

当然还有与获取接口相关的方法,这里不做详细说明。

反射可以用来做什么?

反射的应用场合

在编译时根本无法知道该对象或类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息.

反射的作用

通过反射可以使程序代码访问装载到JVM 中的类的内部信息

获取已装载类的成员变量信息

获取已装载类的方法

获取已装载类的构造方法信息

如何使用反射?

使用反射很简单只需引用反射的包即可:

import java.lang.reflect.*;

我们以一些简单的例子理解反射的实际运用

先建一个Dog类如下:

import com.geticsen.annotation.MyAnnotation;

@MyAnnotation
public class Dog {
    private String name;
    private Integer age;
    public String color;
    public Dog() {
    }

    public Dog(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
    private void initSomething(){
        //todo something
    }
    @Override
    public String toString() {
        return "{name:"+name+",age:"+age+"}";
    }
}

其中MyAnnotation是一个自定义注解(它没有任何用处就只是一个自己写的注解而已)

public @interface MyAnnotation {
    String [] name() default "unknown";
}

获取到一个类的所有成员字段

获取公有成员属性

Class reflectDog = Dog.class;
Field [] dogPublicMember =reflectDog.getFields();
for(Field field:dogPublicMember){
    System.out.println("field.getName() = " + field.getName());
}

// field.getName() = color

获取所有成员属性

Class reflectDog = Dog.class;
Field []dogAllMember = reflectDog.getDeclaredFields();
for(Field field:dogAllMember){
    System.out.println("field.getName() = " + field.getName());
}

//field.getName() = name
//field.getName() = age
//field.getName() = color

获取到类的所有方法

获取公有方法

Class reflectDog = Dog.class;
Method [] dogPublicMethod =reflectDog.getMethods();
for (Method method:dogPublicMethod) {
    System.out.println("method = " + method);
}
//method = public java.lang.String com.geticsen.reflect.Dog.toString()
//method = public java.lang.String com.geticsen.reflect.Dog.getName()
//method = public void com.geticsen.reflect.Dog.setName(java.lang.String)
//method = public java.lang.Integer com.geticsen.reflect.Dog.getAge()
//method = public java.lang.String com.geticsen.reflect.Dog.getColor()
//method = public void com.geticsen.reflect.Dog.setColor(java.lang.String)
//method = public void com.geticsen.reflect.Dog.setAge(java.lang.Integer)
//method = public final void java.lang.Object.wait() throws java.lang.InterruptedException
//method = public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
//method = public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
//method = public boolean java.lang.Object.equals(java.lang.Object)
//method = public native int java.lang.Object.hashCode()
//method = public final native java.lang.Class java.lang.Object.getClass()
//method = public final native void java.lang.Object.notify()
//method = public final native void java.lang.Object.notifyAll()

获取所有方法

Class reflectDog = Dog.class;
Method [] dogAllMethod =reflectDog.getDeclaredMethods();
for (Method method:dogAllMethod) {
    System.out.println("method = " + method);
}
//method = public java.lang.String com.geticsen.reflect.Dog.toString()
//method = public java.lang.String com.geticsen.reflect.Dog.getName()
//method = public void com.geticsen.reflect.Dog.setName(java.lang.String)
//method = public java.lang.Integer com.geticsen.reflect.Dog.getAge()
//method = public void com.geticsen.reflect.Dog.setAge(java.lang.Integer)
//method = public java.lang.String com.geticsen.reflect.Dog.getColor()
//method = public void com.geticsen.reflect.Dog.setColor(java.lang.String)
//method = private void com.geticsen.reflect.Dog.initSomething()

这里我们要注意的是,getMethods 方法会获取到类继承的所有public方法以及被反射类自身的public方法而不能获取到私有以及保护方法

但是getDeclaredMethods就可以获取当前被反射的类的所有方法(不包括继承来的方法)

获取到类的所有构造方法

根据参数列表获取构造函数

Class reflectDog = Dog.class;
System.out.println("dogconstructor = " + dogconstructor);
//dogconstructor = public com.geticsen.reflect.Dog(java.lang.String,java.lang.Integer)

获取所有构造函数

Class reflectDog = Dog.class;
Constructor dogAllConstructor[] = reflectDog.getDeclaredConstructors();
for (Constructor constructor :dogAllConstructor){
    System.out.println("constructor = " + constructor);
}
//constructor = public com.geticsen.reflect.Dog()
//constructor = public com.geticsen.reflect.Dog(java.lang.String,java.lang.Integer)

获取某个函数的参数列表

这里我们以有参的构造函数为例其实任何一个函数你都可以获取到其参数列表

Class reflectDog = Dog.class;
//因为只有连个构造函数其第二个就是有参构造函数
//这里也可以通过参数列表来获取
Parameter[]dogParam = reflectDog.getConstructors()[1].getParameters();

for(Parameter item:dogParam){
    System.out.println("item = " + item);
}
//item = java.lang.String arg0
//item = java.lang.Integer arg1


//Dog的有参构造函数:public Dog(String name, Integer age)

利用反射获取到的构造函数构新建一个实例

Class reflectDog = Dog.class;
//没有指定构造函数默认是无参构造函数
Constructor dogconstructor = reflectDog.getConstructor();
System.out.println("dogconstructor = " + dogconstructor);
//利用构造函数新建一个实例
Dog dogNew = (Dog) dogconstructor.newInstance();
System.out.println(dogNew);

//dogconstructor = public com.geticsen.reflect.Dog()
//{name:null,age:null}

这里要注意下如果你没有写无参构造函数这里直接去获取构造函数会出现错误如下:

Exception in thread "main" java.lang.NoSuchMethodException: com.geticsen.reflect.Dog.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.getConstructor(Class.java:1825)

我们在spring boot中写一个model 如果写了有参构造函数又忘记补上无参构造函数,其实也会遇上差不多的问题,因为其使用的也是反射机制来映射出对象的其会出现如下错误:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.geticsen.model.Company` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (PushbackInputStream); line: 2, column: 5]
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.11.0.jar:2.11.0]
	at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1611) ~[jackson-databind-2.11.0.jar:2.11.0]
	at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400) ~[jackson-databind-2.11.0.jar:2.11.0]
	at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1077) ~[jackson-databind-2.11.0.jar:2.11.0]
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1320) ~[jackson-databind-2.11.0.jar:2.11.0]
	..........

利用反射对实例字段赋值

上面新建的dog的成员是没有值的我们有两种方式赋值

1.可以用获取到的field 来赋值,用field的来赋值的时候要设置一下访问权限否则会报java.lang.IllegalAccessException错误

Class reflectDog = Dog.class;
//没有指定构造函数默认是无参构造函数
Constructor dogconstructor = reflectDog.getConstructor();
System.out.println("dogconstructor = " + dogconstructor);
//利用构造函数新建一个实例
Dog dogNew = (Dog) dogconstructor.newInstance();
System.out.println(dogNew);
//利用反射的字段赋值
Field age = reflectDog.getDeclaredField("age");
Field name = reflectDog.getDeclaredField("name");
name.setAccessible(true);
name.set(dogNew,"dog two");
age.setAccessible(true);
age.set(dogNew,26);
System.out.println(dogNew);

//dogconstructor = public com.geticsen.reflect.Dog()
//{name:null,age:null}
//{name:dog two,age:26}

2.也可以用获取到的method来赋值

Class reflectDog = Dog.class;
//没有指定构造函数默认是无参构造函数
Constructor dogconstructor = reflectDog.getConstructor();
System.out.println("dogconstructor = " + dogconstructor);
//利用构造函数新建一个实例
Dog dogNew = (Dog) dogconstructor.newInstance();
System.out.println(dogNew);
//利用反射的方法进行赋值
Method setName = reflectDog.getDeclaredMethod("setName",String.class);
Method setAge = reflectDog.getDeclaredMethod("setAge", Integer.class);
setName.invoke(dogNew,"dog one");
setAge.invoke(dogNew,15);
System.out.println("dogNew = " + dogNew);

//dogconstructor = public com.geticsen.reflect.Dog()
//{name:null,age:null}
//dogNew = {name:dog one,age:15}

通过以上的例子以及资料其实就可以用反射完成基本自己想要的功能了。

例子先到这里,ok继续探索中



相关文章:

1 . java反射的基本使用

文章评论

Top