Java基础学习21之反射
「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」。
关于作者
- 作者介绍
🍓 博客主页:作者主页
🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉🏆
🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨💻
反射机制
反射机制如果只是针对普通开发者而言意义不大,一般都是作为一些系统的构架设计去使用的,包括以后学习的开源框架,那么几乎都是反射机制。
认识反射
反射指的是对象的反向处理操作,就首先观察以下“正”的操作,在默认情况下,必须先导入一个包才能产生类的实例化对象。
所谓的“反”根据对象来取得对象的来源信息,“反”的操作的本来来源就是Object的一个方法。
- 取得class对象:public final 类<?> getClass() 该方法返回的一个Class类的对象,这个Class描述的就是类。
1 | java复制代码package com.day16.demo; |
此时通过对象的确对象的来源,就是“反”的本质。在反射的背后不在是一个对象,而是对象身后的来源。
而这个getClass()方法返回的对象是Class类对象,所以这个Class就是所有反射操作的源头,但是在讲解其真正使用之前还有一个需要先解释的问题,既然Class是所有反射操作的源头,那么这个类肯定是最为重要的,而如果要想取得这个类的实例化对象,java中定义了三种方式:
方式一:通过Object类的getClass()方法取得
1 | java复制代码package com.day16.demo; |
方式二:通过“类.Class”取得
1 | java复制代码package com.day16.demo; |
方式三:使用Class内部定义的一个static方法
1 | java复制代码package com.day16.demo; |
在以上给出的三个方法会发现一个神奇的地方,除了第一种形式会产生Date实例化对象,而第二种和第三种没有
实例化取得对象。于是取得Class类对象有一个最直接的好处:可以直接通过反射实例化对象,在Class类中有一个方法:
- 通过反射实例化对象:public T newInstance() throws InstantiationException IllegalAccessException
反射实例化对象
1 | java复制代码package com.day16.demo; |
现在可以发现,对于对象的实例化操作,除了使用关键字new之外又多了一个反射机制操作,而且这个操作要比之前使用的new复杂一些,可是有什么用呢?
对于程序的开发模式之前一直强点:尽量减少耦合,而减少耦合的最好的做法是使用接口,但是就算使用了接口也逃不出关键字new,多以实际上new是耦合的关键元凶。
取得父类信息
反射可以做出一个对象所具备的所有操作行为,而且最关键的是这一切的操作都可以基于Object类型进行。
在Java里面任何的程序类实际上都一定会有一个父类,在Class类里面就可以通过此类方式来取得父类或者是实现的父接口,有如下两个方法提供:
public 软件包 getPackage() | 取得类的包名称 |
---|---|
public 类<? super T> getSuperclass() | 取得父类的Class对象 |
public 类<?>[] getInterfaces() | 取得父接口 |
取得类的相关信息
1 | java复制代码package com.day16.demo; |
通过我们反射可以取得类结构上的所有关键信息。
反射调用构造
一个类可以存在多个构造方法,如果我们要想取得类中构造的调用,我们就可以使用Class类提供的两个方法
public Constructor getConstructor(类<?>… parameterTypes) throws NoSuchMethodException,SecurityException | 取得指定参数类型的构造方法 |
---|---|
public Constructor<?>[] getConstructors()throws SecurityException | 取得类中的所有构造 |
以上两个方法的返回类型都是java.lang.reflect.Constructor类的实例化对象,这个类里面关注一个方法,实例化对象:public T newInstance(Object… initargs) throws InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException
取得类中所有构造方法的信息—利用Constructor类中的toString()方法取得了构造方法的完整信息
1 | java复制代码package com.day16.demo; |
如果使用getName()方法就比较麻烦
自己拼凑构造方法操作
1 | java复制代码package com.day16.demo; |
学习Constructor类目的并不是分析方法的组成,最需要的关注就是问题的结论:在定义简单的java类一定要保留一个无参构造。
观察没有无参构造的方法
1 | java复制代码package com.day16.demo; |
1 | java复制代码Exception in thread "main" java.lang.InstantiationException: com.day16.demo.Person |
此时运行的时候出现了错误提示“java.lang.InstancetiationException”因为以上的方式使用反射实例化对象时需要的是类之中提供无参构造方法,但是现在既然没有了无参构造方法,那么就必须明确的找到一个构造方法。
通过Constructor类实例化对象
1 | java复制代码package com.day16.demo; |
一行些简单Java类要写无参构造,以上内容就只需要了解就可以了。
反射调用方法
当取得了一个类之中的实例化对象之后,下面最需要调用的肯定是类之中的方法,所以可以继续使用Class类取得一个类中所定义的方法定义:
public Method[] getMethods() throws SecurityException | 取得全部方法 |
---|---|
public Method getMethod(String name,Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException | 取得指定方法 |
发现以上的方法返回的都是java.lang.Method类的对象。
取得一个类之中全部定义的方法
1 | java复制代码package com.day16.demo; |
但是取得类Method类对象最大的作用不在于方法的列出(方法的列出都在开发工具上使用了),但是对于取得了Method类对象之后还有一个最大的功能,就是可以利用反射调用类的方法:
调用方法:public Object invoke(Object obj,Object… args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException
之前调用类中的方法的时候使用的都是“对象.方法”,但是现在有了反射之后,可以直接利用Object类调用指定子类的操作方法。(同事解释一下,为什么setter,和getter方法的命名要求如此严格)。
利用反射调用Student类之中的setName(),getName()方法。
1 | java复制代码package com.day16.demo; |
在日后所有的技术开发中,简单Java类都是如此应用,多以必须按照标准进行。
反射调用成员
个组成部分就是成员(Field,也可以称为属性),如果要通过反射取得类的成员可以使用方法如下:
取得本类的全部成员:public Field[] getDeclaredFields() throws SecurityException
取得指定成员:public Field getDeclaredField(String name)throws NoSuchFieldException, SecurityException
取得本类全部成员
1 | java复制代码package com.day16.demo; |
但是找到了Field实际上就找到了一个很有意思的操作,在Field类之中提供了两个方法:
设置属性内容(类似于:对象.属性=内容):public void set(Object obj,Object value) throws IllegalArgumentException,IllegalAccessException
取得属性内容(类似于:对象.属性):public Object get(Object obj) throws IllegalArgumentException,IllegalAccessException
可是从类的开发要求而言,一直都强调类之中的属性必须封装,所以现在调用之前要想办法解除封装。
- 解除封装(重点):public void setAccessible(boolean flag)throws SecurityException
利用反射操作类中的属性
1 | java复制代码package com.day16.demo; |
虽然反射机制运行直接操作类之中的属性,可是不会有任何一种程序直接操作属性,都会通过setter,getter方法。
反射与简单Java类—单级VO操作原理
如果现在又一个简单Java类,那么这个简单Java类中的属性按照原始的做法一定要通过setter才可以设置,取得肯定继续使用getter(不关注此处)。
Emp.java
1 | java复制代码package com.day16.vo; |
EmpAction.java
1 | java复制代码package com.day16.action; |
EmpDemo.java
1 | java复制代码package com.day16.demo; |
单极自动VO设置实现
现在所有操作都是通过TestDemo类调用EmpAciton类实现的,而EmpAciton类的主要作用在于定位要操作属性的类型。同时该程序应该符合所有的简单Java类开发形式,也就意味着我们的设计必须有一个单独的类来实现。
由于Bean的处理操作肯定需要重复出去对象信息,所以我们还需要准备两个程序类:StringUtils,负责字符串的操作,毕竟属性的首字母需要大写处理,而后在写一个对象的具体操作(取得对象、设置对象内容)。
工具类—BeanOperation.java
1 | java复制代码package com.day16.util; |
工具类—StringUtils.java
1 | java复制代码package com.day16.util; |
工具类—ObjectUtils.java
1 | java复制代码package com.day16.util; |
EmpAction.java
1 | java复制代码package com.day16.action; |
反射与简单Java类—多级VO设置实现
现在假设一个雇员属于一个部门,一个部门属于一个公司,一个公司属于一个城市,一个城市属于一个省份,一个省份属于一个国家,这种类似关系都可以通过字符串实现多级配置。
修改Dept.java类
1 | java复制代码public class Dept { |
修改Emp.java类
1 | java复制代码public class Emp { |
此时所有的引用关系上都自动进行了对象实例化。而现在程序希望可以满足于单级和多级。
修改BeanOperation.java
1 | java复制代码package com.day16.util; |
定义TestEmpDemo.java
1 | java复制代码package com.day16.demo; |
这样的程序才可以正常使用,属于无限级配置。
本文转载自: 掘金