java 反射

 

 

 

 

1. 什么是反射?

反射是一种动态加载指定类的技术,通过反射获取类中的所有内容,将类的字节码文件封装成了对象,同时也将类中的各个成员封装成了对象,以便操作类的成员。


2.Class类是对java类的抽象描述封装类
通过Class实例可以得到实例类字节码文件中的各个属性与成员,如getName获得类名,getFields获得类中的成员变量getMethods获得成员方法。
获得实例类字节码对象的三种方法
1.Class c=Class.forName(String className);根据完整类名获得,jvm虚拟机中之前有没有加载该字节码文件都可以
2.Class c=className.Class;根据类名获得;jvm虚拟机加载了该字节码文件,只是去查询
3.Class c=对象.getClass();根据对象获得;jvm加载了该字节码文件,并且持有该类的对象实例。
第一种方法较为常用,因为经常并不知道所要加载的类名和对象,只是传进了一个字符串参数。

Class中常用的方法
1.boolean isPrimitive();判断字节码对象是否是基本数据类型的字节码文件
2.Method [] getMethods();返回该字节码中的所有方法;返回类型是Method对象数据类型为元素的数组
3.Filed [] getFields();返回该字节码中的所有成员属性;返回类型是Filed对象数据类型为元素的数组
4.Method getMethod(String methodname,Class<?>....param);根据方法名和参数类型字节码文件返回指定的方法。后面为可变参数,因为不确定参数列表的数量。
5.Field getFiled(String fieldname);根据成员变量名名返回对应的成员变量对象。
6.Constructor[] getConstructors();返回该字节码中的所有构造函数;返回类型是一个Constructor对象数据类型的数组
7.Constructor getConstructor(Class<?>....param)根据参数列表返回对象的构造函数对象。
8.newInstance();创建一个实例对象,空参的构造函数,需要进行强制转换。
9.getDeclaredFiled(String filedname);返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。与getFiled的区别在于,他是暴力反射只要在类内部声明就可以获取不管是什么访问权限对外可不可见,而getFiled只可以获取对外公共权限可见的字段。
反射就是把Java类中的各种属性与成员映射成相应的java类。如java类本身作为Class类的实例,成员变量作为Field类的对象,构造函数作为Constructor类的实例对象等。

2.Constructor类是对类中构造函数的封装类

Constructor类中的常用方法
newInstance(Object... initargs);返回一个Object类型实例对象,需要进行强制转换成所需要的对象。参数即为参数对象,构造函数需要传递的数据类型对象。
toString();
equals();比较构造方法对象
getName();或得构造函数名臣,字符串形式

3.Filed类对类中成员变量的封装类
Filed类代表类中的成员变量类,他的实例filed对象是指指定类中的成员变量对象,并不是具体的成员变量值。
常用方法
Object get(Object obj);获取指定对象上该字段的值。即具体的filed对象所对应的成员变量值。参数为指定对象。
如 Filed f=Person.Class.getFiled("name");
此处的f是指name变量封装成的对象,
String name=f.get(person);此处即是获取指定对象中变量字段对象的值。如果此处,name是私有的字段则需要使用暴力反射Filed f=Person.getDeclaredFiled("name");f.setAccessible(true);String name=f.get(person);中间需要设置访问权限
AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。

4.Method类对类中成员方法的封装类
Method类代表类中的成员方法类,与对象无关,是与类的字节码文件有关的,一般方法的调用需要通过对象来完成,但是反射时通过字节码文件获得其方法,然后再指定对象与参数。
常用方法
invoke(Object obj,Param);指定对象指定实际参数执行此方法。当方法是静态方法时,指定对象为null
如main方法的调用;

Class c=Class.forName(String classname);
Method mainMethod=c.getMethod("main",String[].class);
mainMethod.invoke(null,(Object)String[] s);//此处是为了防止编译器将数组参数拆包,因为JDK1.4版本接收数组参数会对其中的参数进行拆包,每一个元素作为一个参数,而main方法只接收一个字符串数组类型参数,所以需要对其提前进行打包,可以用Objec强制转换成一个对象,也可以new Object[]{String [] s}将数组参数整体作为一个Object数组的元素,对其拆包得到String[]数组。

 

  

5.数组类对数组类型的封装类
Array类
元素同一个数据类型,维度同样的数组是属于同一个数组类的对象的,所获得的字节码文件是同一个。
数组的父类都是Object,数组类型可以转换成Object类型,如
int [] a1=new int[2];
Object obj1=a1;此处就是将数组类型转换成Object类型
Object [] obj1=a1;此处是不行的编译不通过的,因为此处相当于把int类型转换成Object类型,但是int类型属于基本数据类型父类不是Object是不能转换的,注意与上一句的不同,上一句是将数组类型转换成Object类型。
String []s=new String[3];
Object obj=s;此处是将字符串数组类型转换成Object类型
Object [] obj=s;此处是可以编译通过的,因为String类型不是基本数据类型,他的父类是Object所以可以转换,将字符串数组转换成了Object数组。

数组反射常用方法
getLength(Object array);获得指定数组对象的长度;
get(Object array, int index) ;或得指定数组对象指定索引位置的元素,返回类型为Object

6.hashcode()方法详解
hashcode()方法可以或得对象的哈希码值,哈西马对应着其存储区域,最后取出该存储区域内的每个对象同equals方法进行比较来确定对象是否相同,当然只在哈希表数据结构中有此意义。注意当对象添加进内存中时,其hashcode()值就被确定了当存储到哈希数据结构的集合容器中时,以其哈希码值作为内存划分区域的根据;当确定存储区域后,如果更该对象参与哈希值运算的属性会改变其哈希码值,对象被存储到其他内存区域,但是引用所指向的内存区域是不会变的,这就是内存泄露的情况。

Collection coll=new HashSet();
Person p1=new Person("张三");
Person p2=new Person("张三");
Person p3=new Person("李四");
coll.add(p1);
coll.add(p2);
coll.add(p3);
p1.name="王五";
remove(p1);//如此处由于在确定内存分配之后,更改了对象的哈希值,被划分到其他内存区域,由于引用指向的内存区域没变,找不到对象所以删除不成功。

 

  

6.反射的作用
反射使实现框架功能的基础。

假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。

Java的反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。当我们构建出一个对象的时候,去调用该对象的方法和属性的时候。编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。

 

7.反射的使用步骤

1.获取类的字节码文件对象

2.获取类中的各个成员

3.通过获取到的成员进行一系列操作

//在一个Integer泛型的list集合类中放入String类型数据
//
//思路:泛型是提供给Javac编译器看的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,
// 编译器编译带参数类型说明的集合时会去除掉“类型”信息,使程序运行不受影响,
// 对于参数化的泛型类型,通过反射getClass()方法的返回值和原始类型完全一样,由于编译生成的字节码会去掉泛型的类型信息,因此只要能跳过编译器,直接对字节码进行操作就可以往某个泛型集合中加入其他类型的数据。

import java.lang.reflect.*; import java.util.*; class Test{ public static void main(String[]args)throws Exception{ List<Integer> list=new ArrayList<Integer>();//先创建制定对象 Method me=list.getClass().getMethod("add",Object.class);//通过反射获取到该类的字节码文件对象并获取对应的add方法对象 me.invoke(list,"adsda");//指定list对象执行该方法 System.out.println(list); } }

 

你可能感兴趣的:(java 反射)