失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 【反射机制】Java中的反射机制 使用反射机制创建对象 访问属性 方法 构造方法等

【反射机制】Java中的反射机制 使用反射机制创建对象 访问属性 方法 构造方法等

时间:2019-02-06 04:45:16

相关推荐

【反射机制】Java中的反射机制 使用反射机制创建对象 访问属性 方法 构造方法等

这篇文章主要是整理了Java中的反射机制,包括:反射机制概念、反射机制访问构造方法、反射机制访问普通方法、反射机制访问属性,反射机制访问修饰符。

目录

一、反射机制概念

二、反射机制使用

(1)加载Class类对象的几种方式

(2)准备测试类

(3)反射访问属性

(4)反射访问方法

(5)反射访问构造方法

(6)反射创建对象

(7)反射访问私有属性或方法

三、反射机制的应用

四、反射机制优缺点

(1)优点

(2)缺点

文章包含的源代码地址如下:

本篇文章涉及的案例代码在Gitee仓库,点击【反射机制案例代码】可以查看。

一、反射机制概念

什么是反射???

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。

在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

Java中的反射机制,简单理解就是可以在程序运行的过程中,获取到某个类的内部结构信息,例如:类名称、属性、方法、构造方法等,并且可以动态的创建某个具体的对象实例的能力。

二、反射机制使用

(1)加载Class类对象的几种方式

我们在使用反射机制之前,第一步就是需要加载Class类对象,只有获取到Class类对象之后,我们才能根据Class类对象去访问相应类的内部结构信息,比如:属性、方法、构造方法、类名称、修饰符等等内容。

获取Class类对象一般有如下几种方式:

第一种方式:通过类的class属性获取Class类对象

/** ** 语法格式:** Class<?> clazz = 类名称.class;*/public class ClassDemo {public static void main(String[] args) {// 1、类名称.class 获取Class类对象Class<String> clazz = String.class;// 输出看看效果System.out.println(clazz);}}

第二种方式:通过对象的getClass()方法获取Class类对象

/**** 语法格式:* Class<?> clazz = 对象名称.getClass();*/public class ClassDemo {public static void main(String[] args) {// 2、对象.getClass() 获取Class类对象String str = "";Class<? extends String> clazz2 = str.getClass();// 输出看看效果System.out.println(clazz2);}}

第三种方式:通过Class.forName()获取Class类对象

/*** 语法格式:* Class<?> clazz = Class.forName("这里写类的全限定名称");* 类的全限定名称:包名称 + 类名称*/public class ClassDemo {public static void main(String[] args) throws ClassNotFoundException {// 3、Class.forName() 获取Class类对象// 这种方式需要处理异常,因为根据包名称可能找不到类Class<?> clazz = Class.forName("java.lang.String");// 输出看看结果System.out.println(clazz);}}

(2)准备测试类

为了测试反射机制获取类的内部结构信息,我们创建两个测试类,分别是:Person和Student类,并且Student类继承自Person,源代码大致如下。

Person类代码

public class Person {// 属性private String priVal;String defVal;protected String proVal;public String pubVal;// 父类构造方法private Person(String priVal, String defVal, String proVal){}Person(String defVal){}protected Person(String proVal, String defVal){}public Person(){}// 方法private void test01(){}void test02(){}protected void test03(){}public void test04(){}}

Student类代码

public class Student extends Person {// 子类属性private String stuPriVal;String stuDefVal;protected String stuProVal;public String stuPubVal;// 子类构造方法private Student(String stuPriVal, String stuDefVal, String stuProVal){}Student(String stuDefVal){}protected Student(String stuProVal, String stuDefVal){}public Student(){}public Student(String name, Integer id){}// 子类方法private void subTest01(){System.out.println("Student类中的private私有方法");}void subTest02(){}protected void subTest03(){}{System.out.println("调用了Student类中的subTest04()方法,方法参数是:" + name);}}

(3)反射访问属性

前面我们获取到了Class类对象之后,就可以通过Class类对象,去操作类的内部结构信息。

Field对象

Java中将类中每个属性都封装成了一个【Field】对象,并且提供了一些获取这些属性的方法。Field属性,可以是当前类中的属性,也可以是父类中的属性。

反射机制获取类中的属性(Field对象):

getFields(): 获取当前类、父类中的所有public属性。getDeclaredFields(): 获取当前类中的所有属性。getField(String name): 获取当前类、父类中指定名称的一个public属性(如果不是public属性,则会抛出异常)。getDeclaredField(String name): 获取当前类中指定名称的一个属性。

下面通过代码来看下这四个方法的具体使用。

public class FieldDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {// 1、获取Class类对象Class<?> clazz = Class.forName("org.example.reflection.Student");// 2、获取类的属性// getFields(): 获取当前类、父类中的所有public方法Field[] fields = clazz.getFields();System.out.println("===== getFields(): 获取当前类、父类中的所有public方法 =====");for (Field field : fields) {System.out.println(""+field);}System.out.println();// getDeclaredFields(): 获取当前类中的所有方法Field[] declaredFields = clazz.getDeclaredFields();System.out.println("===== getDeclaredFields(): 获取当前类中的所有方法 =====");for (Field declaredField : declaredFields) {System.out.println(declaredField);}System.out.println();// getField(String name): 获取当前类、父类中指定名称的一个public属性System.out.println("===== getField(String name): 获取当前类、父类中指定名称的一个属性 =====");Field pubVal = clazz.getField("pubVal"); // 父类public属性System.out.println(pubVal);Field stuPubVal = clazz.getField("stuPubVal"); // 子类public属性System.out.println(stuPubVal);System.out.println();// getDeclaredField(String name): 获取当前类中指定名称的一个属性System.out.println("===== getDeclaredField(String name): 获取当前类中指定名称的一个属性 =====");Field stuPriVal = clazz.getDeclaredField("stuPriVal"); // 子类private属性System.out.println(stuPriVal);}}

上面代码的运行结果如下所示:

通过反射机制,我们获取到了类中的属性,那要如何给属性赋值呢???

Java中提供了如下方法,用于属性赋值和获取属性值:

set(Object obj,Object newValue)方法:用于属性赋值。参数obj表示实例对象,newValue表示赋值后的值。get(Object obj)方法:用于获取属性值。参数obj表示实例对象。

上面两种方式,只能给【非private修饰】的属性进行赋值操作,对于private的属性,不能直接操作,如何操作private属性,请看本篇文章的第(7)部分。

通过反射机制给属性赋值、获取属性值:

public class FieldDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException,InstantiationException, IllegalAccessException {// 1、获取Class类对象Class<?> clazz = Class.forName("org.example.reflection.Student");// 2、创建类对象Student student = (Student) clazz.newInstance();// 3、获取属性Field field = clazz.getDeclaredField("stuProVal");// 属性赋值field.set(student, "通过反射机制给属性赋值");// 获取属性值Object value = field.get(student);System.out.println(value);}}

程序运行结果如下所示:

(4)反射访问方法

反射机制获取类中的方法(Method对象):

getMethods(): 获取当前类、父类中所有的public方法。getDeclaredMethods(): 获取当前类中的所有方法。getMethod(String name, Class<?> paramType): 获取当前类、父类中一个指定方法名称的public方法(如果类中没有指定的方法,则会抛出异常)。getDeclaredMethod(String name, Class<?> paramType): 获取当前类中一个指定方法名称的方法(如果类中没有指定的方法,则会抛出异常)。

下面通过代码演示反射机制获取类中方法:

public class MethodDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {// 1、获取Class类对象Class<?> clazz = Class.forName("org.example.reflection.Student");// 2、获取类中方法// getMethods(): 获取当前类、父类中所有的public方法Method[] methods = clazz.getMethods();System.out.println("===== getMethods(): 获取当前类、父类中所有的public方法 =====");for (Method method : methods) {System.out.println(method);}System.out.println();// getDeclaredMethods(): 获取当前类中的所有方法Method[] declaredMethods = clazz.getDeclaredMethods();System.out.println("===== getDeclaredMethods(): 获取当前类中的所有方法 =====");for (Method declaredMethod : declaredMethods) {System.out.println(declaredMethod);}System.out.println();// getMethod(String name, Class<?> paramType): 获取当前类、父类中一个指定方法名称的public方法System.out.println("===== getMethod(String name, Class<?> paramType): 获取当前类、父类中一个指定方法名称的public方法 =====");// 方法没有参数,则设置null即可Method test04 = clazz.getMethod("test04", null);System.out.println("父类中public方法: "+test04);// 有参数,则传递参数类型Method subTest04 = clazz.getMethod("subTest04", String.class);System.out.println("子类中public方法: "+subTest04);System.out.println();// getDeclaredMethod(String name, Class<?> paramType): 获取当前类中一个指定方法名称的方法System.out.println("===== getDeclaredMethod(): 获取当前类中一个指定方法名称的方法 =====");Method subTest01 = clazz.getDeclaredMethod("subTest01", null);System.out.println("当前类中私有方法: "+subTest01);}}

上面代码最终运行结果如下图所示:

上面介绍了如何通过反射机制获取类中的方法,方法获取之后,那么要如何调用方法呢???

反射调用方法步骤如下:

获取Class类对象通过Class创建一个实例对象通过Class获取需要调用的Method对象通过Method对象的invoke()进行方法调用

反射机制调用方法代码:

public class MethodDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {// 1、获取Class类对象Class<?> clazz = Class.forName("org.example.reflection.Student");// 2、创建实例对象// 这里先默认通过构造方法创建实例对象,后面会继续介绍如何创建对象Student student = (Student) clazz.newInstance();// 3、获取需要调用的方法Method method = clazz.getDeclaredMethod("subTest04", String.class);// 调用invoke(Object obj, Object... args)方法, obj表示实例对象,args表示调用方法的参数Object ret = method.invoke(student, "这是一个方法参数");System.out.println("方法的返回值是:" + ret);}}

程序运行结果如下所示:

invoke(Object obj, Object... args)方法:

参数obj:实例对象,因为我们调用方法需要知道调用哪个具体实例对象的方法。参数args:方法参数,这是我们调用那个方法需要的参数值。

(5)反射访问构造方法

反射机制提供了如下四个方法,用于获取类的构造方法:

getConstructors(): 获取当前类中所有public的构造方法。getDeclaredConstructors(): 获取当前类中的所有构造方法(不限制修饰符)。getConstructor(Class... args): 获取当前类中指定的参数个数的public构造方法(如果没有指定的构造方法,会抛出异常)。getDeclaredConstructor(Class... args): 获取当前类中指定参数个数的构造方法(修饰符不限制如果没有指定的构造方法,会抛出异常)。

下面给出演示代码:

public class ConstructorDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {// 1、创建 Class类对象Class<?> clazz = Class.forName("org.example.reflection.Student");// 2、获取构造方法// getConstructors(): 获取当前类中所有public的构造方法System.out.println("===== getConstructors(): 获取当前类中所有public的构造方法 =====");Constructor<?>[] constructors = clazz.getConstructors();for (Constructor<?> constructor : constructors) {System.out.println(constructor);}System.out.println();// getDeclaredConstructors(): 获取当前类中的所有构造方法System.out.println("===== getDeclaredConstructors(): 获取当前类中的所有构造方法 =====");Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();for (Constructor<?> declaredConstructor : declaredConstructors) {System.out.println(declaredConstructor);}System.out.println();// getConstructor(Class... args): 获取当前类中指定的参数个数的public构造方法System.out.println("===== getConstructor(Class... args): 获取当前类中指定参数个数的public构造方法 =====");// 获取指定参数的构造方法Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);System.out.println(constructor);System.out.println();// getDeclaredConstructor(Class... args): 获取当前类中指定参数个数的构造方法(修饰符不限制)System.out.println("===== getDeclaredConstructor(Class... args): 获取当前类中指定参数个数的构造方法(修饰符不限制) =====");Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class);System.out.println(declaredConstructor);}}

代码运行结果如下图所示:

(6)反射创建对象

在前面,我们调用方法的时候,创建了一个实例对象,这里我们介绍一下通过反射机制如何创建一个对象。

创建对象,本质上就是通过new关键字,然后根据不同的构造方法创建对象实例。

反射机制中,提供了如下两种方式创建对象实例:

第一种方式:调用【newInstance()】方法(本质上是通过无参构造方法创建对象第二种方式:首先获取有参构造方法,然后通过有参构造方法去调用【newInstance(Object... args)】方法(通过有参构造方法创建对象)。

第一种方式:无参构造方法创建对象

public class InstanceDemo {public static void main(String[] args) throws ClassNotFoundException,InstantiationException, IllegalAccessException {// 1、创建Class类对象Class<?> clazz = Class.forName("org.example.reflection.Student");// 2、通过 newInstance() 创建对象// 通过 Student 类的无参构造方法创建对象Student student = (Student) clazz.newInstance();System.out.println(student);}}

注意:这种创建对象的方式,要求对应的类中具有无参构造方法,如果没有无参构造方法,则会创建失败。

第二种方式:首先获取有参构造方法,然后通过有参构造方法去调用【newInstance(Object... args)】方法

第一种方式创建对象是有限制条件的,类必须具备无参构造方法,否则无法创建,这里在介绍一下通过有参构造方法创建对象。

public class InstanceDemo {public static void main(String[] args) throws ClassNotFoundException,InstantiationException, IllegalAccessException,NoSuchMethodException, InvocationTargetException {// 1、创建Class类对象Class<?> clazz = Class.forName("org.example.reflection.Student");// 2、获取有参构造方法Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, String.class);// 通过有参构造方法创建对象Student student = (Student) constructor.newInstance("参数1", "参数2");System.out.println(student);}}

以上,就是通过反射机制创建对象的两种方式。

(7)反射访问私有属性或方法

setAccessible(boolean flag)方法。

我们前面介绍了通过反射获取属性、获取方法,但是反射对于私有属性、私有方法,是不能直接调用的。

反射机制访问私有属性

下面举个例子,我们直接操作私有的属性或者方法,然后查看运行结果:

public class AccessPrivateDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException,InstantiationException, IllegalAccessException {// 1、创建Class类对象Class<?> clazz = Class.forName("org.example.reflection.Student");// 2、创建对象Student student = (Student) clazz.newInstance();// 3、获取私有属性Field field = clazz.getDeclaredField("stuPriVal");// 给私有属性重新赋值field.set(student, "私有属性赋值新值");System.out.println("赋值后的值:" + field.get(student));}}

程序运行之后,可以发现,控制台报错了,如下所示:

可以发现报错提示中,会出现了如下内容:

上面意思,大致就是:不能够访问private修饰符的类成员。

反射机制中,我们不能直接访问私有的成员、私有的方法,需要在调用之前,解除方法的安全性检查,这样我们就可以操作私有属性、私有方法了。

解除私有属性和方法的安全性检查:

setAccessible(boolean flag):解除或者开启安全性检查。true表示解除安全性检查。

我们在访问私有属性之前,调用【setAccessible()】方法,再次查看结果。

public class AccessPrivateDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException,InstantiationException, IllegalAccessException {// 1、创建Class类对象Class<?> clazz = Class.forName("org.example.reflection.Student");// 2、创建对象Student student = (Student) clazz.newInstance();// 3、获取私有属性Field field = clazz.getDeclaredField("stuPriVal");// 解除安全性检查field.setAccessible(true); // true表示解除安全性检查// 给私有属性重新赋值field.set(student, "私有属性赋值新值");System.out.println("赋值后的值:" + field.get(student));}}

程序运行结果如下所示:

反射机制访问私有方法

访问私有方法案例代码:

public class AccessPrivateDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException,InstantiationException, IllegalAccessException,NoSuchMethodException, InvocationTargetException {// 1、创建Class类对象Class<?> clazz = Class.forName("org.example.reflection.Student");// 2、创建对象Student student = (Student) clazz.newInstance();// 3、获取私有方法Method method = clazz.getDeclaredMethod("subTest01", null);// 解除安全性检查method.setAccessible(true);// 调用方法Object ret = method.invoke(student, null);System.out.println("方法返回值:" + ret);}}

程序运行结果如下所示:

以上,就是通过反射机制访问私有属性或者方法。

三、反射机制的应用

反射机制在很多地方都被使用到了,比如下面这些场景都采用反射机制实现的。

JDBC获取数据库连接:通过反射机制加载数据库驱动程序。Spring中的IOC容器:根据xml配置文件的bean标签,然后采用反射机制创建实例对象。MyBatis中mapper接口执行SQL语句:动态代理中使用反射机制调用目标对象方法。AOP动态代理:动态代理中就需要通过反射机制创建对象。等等。。。

四、反射机制优缺点

(1)优点

反射机制提高了程序的可扩展性、灵活性。降低了程序的耦合性。

(2)缺点

反射机制会破环面向对象的封装性,因为通过反射可以访问private私有属性或方法。反射机制会降低程序的运行效率。

以上,就是我个人对反射机制相关内容的总结,包括:获取类对象、反射机制访问属性、方法、构造方法、访问私有成员、以及反射机制的应用场景和优缺点。​​​​​​​

如果觉得《【反射机制】Java中的反射机制 使用反射机制创建对象 访问属性 方法 构造方法等》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。