您的位置:首页 > 编程语言 > Java开发

Java基础知识之——反射

2017-10-09 15:04 309 查看
决定从现在开始养成写博客记录学习过程的习惯,所以把最近学的反射相关知识整理一下。

首先,什么是反射?

从概念上说,反射主要是指程序可以访问、检测和修改它自身状态或行为的一种能力。

而我的理解是:常规操作一个类需要实例化它的对象,然后操作它的数据成员或成员函数,在逻辑是是由上而下的;

   而反射不同,它是反过来获取类的类类型(Class Type),而后再进行操作,有一种由下而上的感觉。

区别在哪里呢?

打个比方,大多数包装好的类他们的对象都是一个宝库,但我们往往只会用到其中的一部分,由多变少;

而反射则是通过仅仅一个对象获取到整个类的全部信息,由少变多。

换个说法:对象照了下镜子,反射 出了它作为一个类的全貌。

同时,反射机制在java中是动态的,你完全可以写一个可能根本不存在的class交给编译器处理,而且能够通过编译。

即:程序运行时,允许改变程序结构或变量类型

java虽然一般归类为静态语言,但是它有反射这样一个动态的机制。

其次,反射具体是什么?

java里万事万物皆对象,这句话大家应该都知道,Object类是这句话的一个保证,它是java里所有类的父类。

Class类是Object的一个特别的子类,是一个描述类自身的类,也是反射(Reflection)的起源。

一个类应当有些什么呢?数据成员?成员函数?构造器?

Class类封装了描述数据成员的Field、描述方法的Method和描述构造器的Constructor等属性。



java.lang.Class

java.lang.reflect.Field

java.lang.reflect.Method

java.lang.reflect.Constructor

java.lang.reflect.Modifier

五个类的API中我们可以了解到反射的绝大多数操作方法。

再次,反射有什么用?

从最简单的开始,作为一个菜鸟程序员,我很好奇编写java的大牛是怎么定义String的

那么我首先要得到String类的类类型,有三种方法可以做到这一点:

//通过Class的forName方法
Class c1 = Class.forName("String");
//通过每个类的class成员获取
Class c2 = String.class;
//通过该类的一个对象获取
Class c3 = new String().getClass();

“现在,我的手里掌握了String的全部。”

/*
* 万事万物皆对象,数据成员当然也是变量,java.lang.reflect.Field类封装了关于成员变量的操作
* 想看String的数据成员?可以用getField方法 获取所有public的数据成员
* 但是往往private的东西是我们更想看的,所以用getDeclaredFields获取所有自己声明的数据成员
*/
Field[] fs = c.getDeclaredFields();
//现在Field的数组fs中就包含了String类的所有数据成员的信息
//通过getType获取它们的类型
Class fieldType = field.getType();
//通过getType获取它们的名称
Class fieldName = field.getName();

/*
* 或者也可以写一个遍历来输出它们的类型和名称
* for(Field field:fs){
*	Class fieldType = field.getType();
*	//输出成员变量的类型以及名称
*	System.out.println(fieldType.getName()+" "+field.getName());
* }
*/


类似的,可以通过Method类等类封装的方法来操作该类的成员方法等,在此不再赘述。

最后,通过反射深入研究泛型。

随意声明一个只能限制String类型的ArrayList

ArrayList<String> list = new ArrayList<String>();
//我们可以自由地往里面加字符串
list.add("233");
//但是不能往里加其他类型的对象,如int
//list.add(233); 报错


让我们做个实验,再声明一个无限制的ArrayList

ArrayList arr = new ArrayList();
//获取它们俩的类类型
Class c1 = list.getClass();
Class c2 = arr.getClass();
//输出它们是否相同
System.out.println(c1==c2);
//结果为true(可自行验证)
事实上反射都是在编译后的操作(运行时处理), 即程序通过编译过后运行时泛型是不泛型的,java的泛型仅是防止错误输入。

仅在编译阶段有效 。

而反射获取的是.class,可以理解为获取了编译之后的字节码文件,也就是说:反射是在编译之后的操作(运行时处理)

这也反映了反射的动态性质,通过这个性质,我们可以绕过编译(为所欲为)。
try{
//通过Method类的getMethod方法获取ArrayList的add方法
Method m = c1.getMethod("add", Object.class);

//再通过invoke方法,反向操作list对象,使其添加非String元素
m.invoke(list, 233);	//为所欲为!
System.out.println(list);
//但是不能通过for each来遍历现在的list,会有类型转换的错误
} catch (Exception e) {
e.printStackTrace();
}


(水平尚浅,有何错误、不妥之处欢迎指教)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 反射