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

黑马程序员_高新技术Java反射机制

2014-01-23 23:32 246 查看
一、反射的基石-----Class类(JDK1.2)
在介绍反射的时候,必须了解反射最基本的Class类,要注意小心class关键字的区别。class是一个关键字,用来定义类的.
     Class
没有公共构造方法。
Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的
defineClass
方法自动构造的。

java程序中的各个Java类都属于同一个事物,表述这类事物的Java类名就是Class.例如:
众多的人------Person(类的实例)
众多的java类------Class(java类,代表一份字节码文件)
Java类用于描述一类的事物的共性,该类事物有什么属性,没有什么属性,至于该属性是什么,是由这个类的实例对象来确定的,不同的实例对象有不同的属性值.
Java程序中的各个类,他们属于同一类事物,用一个类可以来描述它们,这个类就是Class.
Class描述了类的名字,类的访问属性,类所属的包名,字段名称的列表、方法名称的列表等.
Class.forName()的作用:得到一个类的字节码
方式有两种:1.已经加载过:直接获得类的字节码
   2.没有加载过:则先通过类加载器将字节吗放入虚拟机内存中,再获取.
得到各字节码对应的实例对象三种方式:
String str = "abc";
1. Class clazz = str.getClass(); ------------对象名.getClass();
2. Class clazz = String.class;    ------------类名.class;
3. Class clazz = Class.forName("java.ang.String"); ------------反射都用这种方式

九个预定义Class实例对象:八个基本类型+void 它们都有对应的Class对象;
public class ReflectTest_反射 {
public static void main(String[] args) throws Exception {
String str1 = "abc";
Class clazz1 = str1.getClass();
Class clazz2 = String.class;
Class clazz3 = Class.forName("java.lang.String");
Object obj = clazz3.newInstance();//创建这个实例对象
System.out.println(clazz1+"----"+clazz2+"=========="+clazz3);//class java.lang.String
System.out.println(clazz1==clazz2);//true
System.out.println(clazz1==clazz3);//true
System.out.println(clazz3.isPrimitive());//判定指定的 Class 对象是否表示一个基本类型
System.out.println(int.class.isPrimitive());//true
System.out.println(int.class==Integer.class);//false 类型不同,字节码不同
System.out.println(int.class==Integer.TYPE);//true 类型相同 Integer.TYPE代表包装类型包装的基本类型字节码

System.out.println(int[].class.isPrimitive());//任何类型都有字节码,但是不是基本类型

//总之,只要在源程序中出现的类型,都有各自的Class实例对象,例如int[],void
}
}


二、反射机制的概念
反射机制就是把一个Java类中的各个成分映射成相应的Java类。例如:一个Java类是用一个Class对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等信息,它们也是由一个Java类来表示的。就像汽车是一个类,汽车中有发动机,变速箱等也是一个类。
表示Java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是:Field、Method、Contructor、Packpage等等。
1.构造方法的反射应用(Constructor类)
public class Constructor_构造方法的反射应用 {
public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
/*new String(new SringBuffer("abc"));
通过反射将得到String这个参数类型的构造方法
想到的String构造方法中参数类型是StringBuffer的构造方法
那么就得到String这个类,通过getConstructor()方法将参数写进去
*/
//这个只是得到了一份字节码,还不知道是String类型
Constructor constructor = String.class.getConstructor(StringBuffer.class);//这里面的参数是可变参数

/*第一个是String这个类的字节码
第二个是拿着这个字节码创建出一个对象
现在只知道有一个constructor,到底是哪个构造方法我们还不知道
第一个StringBuffer表示选择哪个构造方法,是把你翻译成class,然后去运行
第二个StringBuffer是创建这个对象的参数
*/
String str2 = (String) constructor.newInstance(/*"abc"*/new StringBuffer("abc"));
System.out.println(str2.charAt(2));
//得到方法需要类型,去调用这个方法的时候也需要调用同样类型的实例对象(关键)

//在这里Class也有一个newInstance()方法
String obj = (String) Class.forName("java.lang.String").newInstance();
/*		这个方法是简化了通过constructor去得到这个构造方法,直接先得到了
* 会发现这个对象创建,他没有参数,使用的是默认的构造方法.
* 原理:该方法的内部先得到了默认的构造方法,用到了缓存机制将它缓存起来,下一次就
* 直接从缓存中得到,这个只适合无参的构造函数.
* 这从侧面说明了一个问题:反射比较消耗性能,得到这个构造方法的过程比较慢,要不至于他不会缓存
* */

}
}



4000

2.成员变量的反射(Field类)和成员方法的反射(Method类)
public class ReflectPoint_反射成员变量的测试 {
private int  x;
public int y;
public String str1= "ball";
public String str2 = "basketball";
public String str3 = "itcsat";
public ReflectPoint_反射成员变量的测试(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "ReflectPoint_反射成员变量的测试 [str1=" + str1 + ", str2=" + str2
+ ", str3=" + str3 + ", x=" + x + ", y=" + y + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint_反射成员变量的测试 other = (ReflectPoint_反射成员变量的测试) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}

public class Field_成员变量的反射应用 {

public static void main(String[] args) throws SecurityException,
NoSuchFieldException, IllegalArgumentException,
IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
// 创建了该类的对象
ReflectPoint_反射成员变量的测试 pt1 = new ReflectPoint_反射成员变量的测试(3, 5);
ReflectPoint_反射成员变量的测试 pt2 = new ReflectPoint_反射成员变量的测试(3, 6);
/*
* 想知道创建对象参数所对应的变量是什么 通过反射获得该对象变量的值是多少
*/
Field fieldY = pt1.getClass().getField("y");
/*
* fieldy的值是5吗??错!! fieldy不代表一个具体的值,只代表一个类身上的变量
* 用fieldy这个变量获得某个对象参数是"y"的值
*/
System.out.println(fieldY.get(pt1));// 5
System.out.println(fieldY.get(pt2));// 6
/*
* 同样是通过pt1这个引用对象获得,但获取的对象不同,值也不同 Field fieldX =
* pt1.getClass().getField("x");
* 这里x这个成员变量时私有的,getField无法得到,有个方法getDeclaredField
*/
Field fieldAllX = pt1.getClass().getDeclaredField("x");// 返回这个x这个字段,只要是声明过的
/*
* 但是得到了这个字段却依然无法访问,无法拿到 将这个变量对象设置成可以访问,就能取出来了,这个过程称之为暴力反射.
*/
fieldAllX.setAccessible(true);
System.out.println(fieldAllX.get(pt1));

/*
* 题目: 将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
*/
changeStringValue(pt1);
System.out.println(pt1);

// 方法的反射,调用某个类中的方法
// Method代表某个类中的成员方法,下面用反射去调用String类的charAt方法
char []str1 = {'6','5','5','1','1'};
Method method = String.class.getMethod("valueOf", char[].class);//方法名,方法参数类型字节码
// invoke对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
System.out.println(method.invoke(null, str1));// 对象,方法参数....如果第一个参数为null说明该方法是静态的方法
}

private static void changeStringValue(Object obj)
throws IllegalArgumentException, IllegalAccessException {
// 获得传来的对象的字节码文件,再得到所有的字段
Field[] fields = obj.getClass().getFields();
// 对得到的的字段迭代
for (Field field : fields) {
// 获得这个字段的类型,并判断是否是String的
// 这里最好用"=="符号,不能用equels,如果获得类型是String,那么是同一份字节码用==比
if (field.getType() == String.class) {
String oldValue = (String) field.get(obj);
String newValue = oldValue.replace('b', 'a');// 替换字符串中所有b的字符
field.set(obj, newValue);// 对这个对象的值重新设置
}
}
}
}

3.数组的反射
public class ArrayReflect_数组的反射 {
public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
//正常调用方法
TestArry.main(new String[]{"111","222"});
//用反射执行某个类中的main方法
String className = args[0];
System.out.println(className);//com.itcast.advanced.TestArry 在运行配置 自变量中写的参数传进来
Class clazz = Class.forName(className);
Method method = clazz.getMethod("main",String[].class);
method.invoke(null, (Object)new String[]{"111","222","333"});
//思考一下为什么要用反射的方式调用
/*
* 因为我们一开始是不知道类名的,这个类名是变化的,
* 通过别的地方获取到这个类的名称,获取其中的main方法
* 并执行main方法
*/

int [] a = {1,12,1};
String [] a1=  {"11","11"};
System.out.println(Arrays.asList(a));
System.out.println(Arrays.asList(a1));
//1.4jdk中接收的是Object[] a类型  1.5jdk中是T...a
//String []符合1.4的,就把String每一个元素取出来放在Object中,
//int[] 不符合1.4的,就找1.5的,结果将整个int[]都当做一个Object

//那么就写一个对数组的反射
Object obj1 = new String[]{"1","2"};
Object obj2 = new int[]{11,11,22,33};
printObject(obj1);
printObject(obj2);
printObject("111");
//思考一下:如果想获得一个数组的类型怎么做?int[]目前没有办法,只能获得数组每个元素对应的类型

int [][] obj3 = {{1},{2},{3}};
Object [] obj4 = {"123",1};
printObjectType(obj3);
printObjectType(obj4);

}

private static void printObjectType(Object[] obj) {
int len = Array.getLength(obj);
for (int i = 0; i <len; i++) {
System.out.println(obj[i].getClass().getName());//循环每一个元素,并获得字节码,得到他们的类型
}
}

private 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);
}
}

}
class TestArry{
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}


4.ArrayList---HashSet的比较以及HashCode的作用(并且利用实现框架的原理)
框架的概念以及用反射技术开发框架的原理,把我们要调用的类,放在配置文件中,在源程序中不出现类名.以动态方式获取.
public class ReflectTest_反射的框架原理 {
public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
InputStream input = new FileInputStream("config.properties");//通过字节流读取流读取配置文件信息
Properties properties = new Properties();
properties.load(input);

//String className1 = properties.getProperty("className1");//通过配置文件的键获取对应的值---ArrayLiat
String className2 = properties.getProperty("className2");//通过配置文件的键获取对应的值---HashSet
Collection collection = (Collection) Class.forName(className2).newInstance();//创建这个字节码对象

ReflectPoint_反射成员变量的测试  point1 = new ReflectPoint_反射成员变量的测试(3, 4);
ReflectPoint_反射成员变量的测试  point2 = new ReflectPoint_反射成员变量的测试(3, 5);
ReflectPoint_反射成员变量的测试  point3 = new ReflectPoint_反射成员变量的测试(3, 6);
ReflectPoint_反射成员变量的测试  point4 = new ReflectPoint_反射成员变量的测试(3, 4);
/*当集合石HashSet时,这个对象在内存数组中就会通过一个哈希算法得到一个哈希值,当将这对象再存一遍的时候,会
找这个对象的哈希值是否存在,不存在,就将对象存入HashSet中,如果存在,就不存入这个对象
如果要判断对象的值也要唯一,那么就要覆盖HashCode方法和equels方法,自己去写equels方法,Objecj的equels默认比较的是内存地址*/
collection.add(point1);
collection.add(point2);
collection.add(point3);
collection.add(point4);//覆盖了equels方法这个对象就无法存入HashSet集合中
/*着这里要注意,一但对象存入了Hash类型的集合后,他就有自己的一个哈希值,
如果将这个对象参数修改后,这个对象就会重新产生一个哈希值,也将无法返回这个对象的结果
例如:将一个值修改后,再将它去除,一开始结果是3,去除这个对象,应该是2,但是输出结果依然是3(重点细节)*/
point1.y = 7;
point2.y = 7;
point3.y = 7;
collection.removeAll(collection);
/*	我将这三个集合全部修改后,我直接移除这个集合所有的元素,可怕的的是结果依然是3
原因是:修改后的对象又会产生一个哈希值,存在集合中,那么这个对象在集合中存储的位置发生了变化,
而我们想删除的那个集合的哈希值已经不在了,调用删除方法的时候依然还是用的原来的哈希值在寻找这个集合
所以得出一个结论是:当对象存入Hash类型的集合当中时,这个对象就不能去修改
否则,导致内存泄露*/
System.out.println(collection.size());//输出结果是3
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: