您的位置:首页 > 职场人生

黑马程序员——Java学习之反射技术

2015-03-10 19:57 357 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

反射技术也算得上是Java基础知识里较高新的一项技术了(但反射不是java5的新特性),犹记得老师课上说过,当反射遇到Private时,会让我们深深地领悟到啥叫一“招”回到解放前。

如果将Java语言中的各种技术比喻成江湖上纷繁的门派,反射技术的存在总是给人一种隐秘而强悍的幽灵般的感觉,它让限制不再,让规矩死亡,颇有几分C中的goto给人的第一印象。今天讨论反射,首先从下面一个题开刀吧!

ArrayList list = new ArrayList(); 如何在这个泛型为Integer的ArrayList中存放一个String类型的对象。

我们来分析分析给泛型为Integer的ArrayList中添加一个String类型对象,为什么会添加不进去?是因为ArrayList只接受Integer类型,那么,我们就让它不只是接受Integer。如何让它不是只接受Integer呢?既然是接收,靠的是ArrayList中的add方法了。

所以我们需要通过反射将方法接受类型改变,

若要反射,首先要对象字节码,想获取方法,还需要一个ArrayList对象。

其实所谓的泛型,也就是在方法上对于接受的类型进行了一个限定。而反射方法和字段以及构造,字节码就是必须的。运行方法,对象也是必要的。只是构造本来就是new对象的,而获取字段和方法需要有字节码对象,运行,就要有对象参与。

但是涉及反射,一定需求注意的是反射的对象所被修饰的修饰符是什么?

如果是public修饰的,直接可以getMethod();

如果是默认的,就需要通过getDeclaredMethod();

如果是private的话,就要通过暴力反射了,就是要通getDeclaredMethod(),并设置me.setAccessible(true)来进行设置权限

上题的操作如下:

public class Test {
public static void main(String[] args) throws Exception {
ArrayList<Integer> al = new ArrayList<Integer>();
Class clazz = ArrayList.class; //获取字节码对象.
Method me = clazz.getMethod("add", Object.class); //反射方法.
me.invoke(al,"我是字符串"); //运行方法.
System.out.println(al);
}
}


联系泛型可以解释:泛型的出现阻止了形形色色的元素混入,可是,有了反射,就好比让接受重新回到解放前。反射的确是让程序重新变得不安全,因此,对于特别修饰的private,我们需求慎重来考虑一下,毕竟private自然有private的道理。

反射的概念

反射就是把java类中的各种成分映射成相应的java类。一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示。

反射的基础:Class–>用来描述java中的类

如何得到各个字节码对应的实例对象

->类名.class 或者 类名.TYPE

->对象.getClass()

->Class.forName(“类名”)

总之,只要在源程序中出现的类型,都有各自的Class实例对象

String str1 = "abc";
//三种获取字节码的方法
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2);  //true
System.out.println(cls2 == cls3);  //true


反射的作用

反射机制最重要的内容——检查类的结构。

在java.lang.reflect包中有三个类Field、Method、Constructor分别用于描述类的域、方法和构造器。

这三个类共有方法:

getModifiers //返回一个整形数值,用不同的位开关描述public和static这样的修饰符使用状况

getName //用来返回项目的名称

构造方法的反射应用

constructor代表一个构造方法,constructor对象上的方法有:得到方法名字,得到所属类,产生实例对象。

无参:Constructor[] constructors =

Class.forName(“java.lang.String”).getConstructors();

有参:Constructor constructor =

Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

成员变量的反射:

Field类代表某个类中的一个成员变量 ,通过Field调用成员变量

public class ReflectPoint {
private int x;
public int y ;
public ReflectPoint(int x, int y) {
super( );
this.x = x;
this.y = y;
}
}

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectTest {
public static void main(String[] args) throws Exception {
ReflectPoint rp = new ReflectPoint(3, 5);
Field fieldY = rp.getClass().getField( "y");
System. out.println(fieldY.get(rp));
}
}


如果想直接通过getField方法获取私有对象,会出现如下错误:java.lang.NoSuchFieldException : x

Field fieldX = rp.getClass().getDeclaredField(“x”);

fieldX.setAccessible(true);

这种方法称之为暴力反射,使用setAccessible(true)使private类型的成员变量也可以被获取。

成员方法的反射:

Method类用于描述类中的成员方法。

得到类中的某一个方法:Method charAt = Class.forName(“java.lang.String”)

.getMethod(“charAt”, int.class);

调用方法:

通常方法:System.out.println(str.charAt(1));

反射方法:System.out.println(charAt.invoke(str, 1));

注意:如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是静态方法!

框架的概念及用反射技术开发框架的原理:

框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。

框架程序怎样调用以后写的类呢?很多时候程序无法知道被调用的类名,所以,在程序中无法直接new某个类的实例对象,而要用 反射 方式来做。

public class ReflectTest {
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream("config.properties" );
Properties props = new Properties();
props.load(is);
is.close();
String className = (String)props.get( "className");
Collection collections = (Collection)Class.forName(className).newInstance();
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);

collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System. out.println(collections.size());
}
}

className = java.util.ArrayList    //4
className = java.util.HashSet    //3


用类加载器的方式管理资源和配置文件:

方式一:采用类加载器进行加载,使用相对路径的方式

InputStream is = ReflectTest.class.getClassLoader().getResourceAsStream

(“com/mytest/config.properties”);

方式二:利用Class方式进行加载,使用相对路径的方式

nputStream is = ReflectTest.class.getResourceAsStream(“config.properties”);

方式三:利用Class方式进行加载,使用绝对路径的方式

InputStream is = ReflectTest.class .getResourceAsStream(“com/mttest/config.

properties”);

Array工具类 用于完成数组的反射操作:

public class ReflectTest {
public static void main(String[] args) throws Exception {
String[] a1 = new String[]{"a" ,"b" ,"c" };
String a2 = "xyz";
printObject(a1);
printObject(a2);
}
public static void printObject(Object obj){
Class clazz = obj.getClass();
if(clazz.isArray()){
int len = Array.getLength(obj);
for(int i = 0; i < len; i++){
System. out.println(Array.get(obj, i));
}
} else{
System. out.println(obj);
}
}
}


ArrayList和HashSet的比较及Hashcode分析:

ArrayList
Collection collections = new ArrayList( );
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System. out.println(collections.size());
Collection collections = new ArrayList();
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System. out.println(collections.size());  //4

HashSet:
Collection collections = new HashSet();
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System. out.println(collections.size());  //3


分析:

由以上两示例可以看到:

当集合为ArrayList时,其实质是一个数组,因此放入4个元素后,集合size为4。

当集合为HashSet时,需要通过比较hashcode值以及equals方法是否返回true决定是否放入。

如果hashcode值相等并且equals方法返回true,那么就不会放入。因此,集合size为3。

如果想让size为2,也就是pt1与pt3作为同一个元素存入HashSet集合,那就需要覆盖ReflectPoint类的hashCode方法以及equals方法。

总结:

反射在本质上就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等,明显反射技术的基石就是其class。所以在以后的编程工作中一定要清晰头脑,做到从字节码中反射出自己的思维。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: