《黑马程序员》反射机制《一》

来源:互联网 发布:数据库开发软件 编辑:程序博客网 时间:2024/06/05 00:59
------- android培训、java培训、期待与您交流! ----------

/**
 * 第十七集  透彻分析反射的基础Class类
 * 反射的基石--》Class类
 * Java程序中的各个Java类属于同一事物,描述这类事物的java类名就是Class
 * 对比提问:众多的人用一个什么类来表示?众多的Java类用什么来表示?
 * 人————>Person
 * java————>Class
 * 对比提问:Person类代表人,他的实例就像是张三,李四这样一个个具体的人,
 * Class类代表java类,它的各个具体对象有分别是什么呢?
 * --对应各个类在内存中的字节码,例如,Preson类的字节码,ArrayList类的字节码,等等。
 * --一个类被类加载器加载到内存中,占用一篇储存空间,这个空间里面的内容就是类的字节码
 * 不同的类的字节码是不同的,所以他们在内存中的内容量是不同的,这一个个空间可分别用一个个的
 * 对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?
 * 如何得到各个字节码的实例对象类(Class类型)
 * --类名.class,例如,System.class
 * 对象.getClass(),例如,new Date().getClass()
 * Class.forName("类名"),例如,Class.forName("java.util.Date").
 * 九个预定义Class实例对象:
 * --参看Class.isPrivate方法的帮助
 * int.class==Integer.TYPE
 * 数组类型的Class实例对象
 * --Class。isArray()
 * 总之,只要是在源程序中出现的类型,都有各自的Class实力类型,例如,int[].void
 * 
 * 
 * 
 * Java类是用于描述一个事物的共性,该类事物有什么属性,没什么属性,至于这个属性的值是什么
 * 则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。java程序中的各个java类
 * 他们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class
 * 要注意鱼小邪class关键字的区别哦。Class类描述了哪些方面的信息?类的名字,类的访问属性,
 * 类的所属的包名,字段名称的列表,方法名称,等等。
 * 学习反射,首先要明白Class这个类
 */


/**
 *第十八集 理解反射的概念
 *
 * 一个学生总结的很精辟
 * “反射就是把JAVA类中的各个成分映射成相应的java类”。这句话比很多书上讲解得都透彻,都精辟
 * 反射就是把java类中各种成分映射成相应的java类。
 * 例如,一个java类中用一个Class类的对象来表示,一个类中组成部分成员变量,方法
 * 构造方法,包等等信息也用一个个的java类来表示,就像汽车是个类,汽车中的发动机,
 * 变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得
 * 其中的变量,方法
 * 
 * @author Administrator
 *
 */


/**
 * 第十九集
 * 一个Class代表一个字节码,一个Method代表一个字节码里面的方法
 * Constructor类代表某个类中的一个构造方法
 * 得到某个类所有的构造方法
 * --例子:Constructor[] construtors=
 * Class.forName("java.lang.String").getConstructor();
 * 得到某一个构造方法
 * --例子: Constructor constructor =
 * Class.forName("java.lang.String").getConstructor(StringBuffer.class)
 * --获得党发的时候要用到类型
 * 创建实例对象
 * --通常方式:String str = new String(new StringBuffer("abc"));
 * --反射方式:String str = (String)constructor.newinstance(new StringBuffer("abc"));
 * --调用方法的时候要用到上面相同类型的实例对象
 * 
 * Class.newInstance()方法:
 * --例子:String obj = (String)Class.forName("java.lang.String").newInstance();
 * --该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
 * --该方法内部的具体代码是怎么样写的呢?用到了缓存机制来保存默认构造方法的实例对象
 *
 * @author Administrator
 *
 */


/**
 * 第二十集  成员变量的反射
 * Field类代表某个类中的一个成员变量
 * 演示用eclipse自动生成java类的构造方法
 * 问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?
 * 类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联那个对象呢?
 * 所以字段FieldX代表的是x的定义,而不是具体的x的变量。 
 * 
 */




/**
 * 第二十一集成员变量反射的综合实例
 * 将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”;
 * changeStringValue(pt1);
 *  if(field.getType().equals(String.class)){
 *  上面这个不爽 用equals语义不准确
 *  这个里应该用双等号,都是拿一份同一个字节码,比较字节码应该用双等号
 *  改完之后打印出来
 *  为了看到改变后的效果,你还有必要覆盖toString方法
 *  
 * @author Administrator
 *
 */


/**
 * 第二十二集 成员方法的反射
 * 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对象对应的是一个静态方法!
 * jdk1.4和1.5的invoke的区别:
 * jdk1.5:public Object invoke(Object obj, Object...args)
 * jdk1.4:public Object invoke(Object obj,Object[] args),
 * 既按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被
 * 
 * @author Administrator
 *
 */


/**
 * 第二十三集  对接收数组成员方法进行反射 Main
 * 用反射方式执行某个类中的main方法
 * 目标:
 * 写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法
 * 问题:
 * 启动java程序中的main方法的参数是一个字符串数组,既public static void main(String[] args)
 * 通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数
 * 而按jdk1.4的语法,数组每一个元素对应一个参数,当把一个字符串数组作为参数传递给jdk1.4的语法时,
 * 会按jdk1.4的语法处理,即把数组打散成为若干个单独的参数。所以,再给main方法传递参数时,不能使用代码
 * main Method。invoke(null, new String[]{"xxx"}),javac只把它当做jdk1.4的语法进行理解
 * 而不把它当做jdk1.5的语法解释,因此会出现参数不对的问题。
 * 解决办法:
 * --main Method.invoke(null, new Object[]{new String[]{"xxx"}})
 * --main Method.invoke(null, Object({new String[]{"xxx"}),编译器会作为特殊处理,编译器编译时
 * 不把参数当做数组看待,也就不会把数组打散成若干个参数了
 */


/**

 * 第二十四集 数组与Object的更关系及反射类型

/**
 * 第二十五集   数组的反射及应用
 * 具有相同维数和元素的数组属于同一类型,即具有相同的Class实例对象。
 * 代表数组的Class实例对象的getSuperClass()方法返回父类为Object类对应的Class。
 * 基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;
 * 非基本类型的一位数组,既可以当做Object类型,又可以当做Object类型使用。
 * Arrays.asList()方法处理int[]和String[]时的差异
 * Array工具用于完成对数组的反射操作。
 * 思考题:怎么得到数组中的元素类型? 
 * 
 */


package cn.itcast.day1;


import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class ReflectTest {


/**
* @param args
* @throws ClassNotFoundException 
* @throws NoSuchMethodException 
* @throws SecurityException 
* @throws InvocationTargetException 
* @throws IllegalAccessException 
* @throws InstantiationException 
* @throws IllegalArgumentException 
* @throws NoSuchFieldException 
*/
public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
// TODO Auto-generated method stub
String str1 = "abc";
/**
* str1是字符串对象,str1.getClass()就是得到String它的字节码
*/
Class cls1 = str1.getClass();
/**
* 这个也是String的字节码
*/
Class cls2 = String.class;
/**
* 也是String的字节码
*/
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2);
System.out.println(cls1 == cls3);

/**
* 是否是原始类型
* String并不是基本类型,虽然经常用,它是一个类
*/
System.out.println(cls1.isPrimitive());
/**
* int它的字节码是一个基本的类型
*/
System.out.println(int.class.isPrimitive());
/**
* Integer是一个包装类 和int不是一个类型 各有各的字节码
*/
System.out.println(int.class == Integer.class);
/**
* Integer里面专门定义了一个常量叫TYPE,这个TYPE就代表
* 包装类所包装的基本的类型的字节码。
*/
System.out.println(int.class == Integer.TYPE);
/**
* 数组是一个类型但不是原始类型
*/
System.out.println(int[].class.isPrimitive());

/**
* 可变参数,可以传一个也可以传两个
* 参数是数组Class[]
* new String(new StringBuffer("abc"))
* Constructor代表的是一个构造方法
* 得到这个方法的时候需要类型
* 调用这个方法的时候也要传递同样类型的对象
*/
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
String str2 = (String) constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str2.charAt(2));

ReflectPoint pt1 = new ReflectPoint(3,5);
/**
* fieldY的值是多少?是5,错!
* fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值
*/
Field fieldY = pt1.getClass().getField("y");
System.out.println(fieldY.get(pt1));
/**
* getDeclaredField()无论可见不可见都能够取出来
*/
Field fieldX = pt1.getClass().getDeclaredField("x");
/**
* 设置说可以访问 暴力反射
* 出了两个错误,上面说看不见,下面是拿不到
*/
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));

changeStringValue(pt1);
/**
* 改完之后打印出来
* 为了看到改变后的效果,你还有必要覆盖toString方法
*/
System.out.println("behind change----"+ pt1);
/**
* 这个方法接受两个参数,第一个参数方法名字,这个类身上有多个方法,确定是哪个方法
* 第二个参数是,一个类里面的方法有多种重载形式,选取哪一种呢?
* str1.charAt(1);
* invoke 调用这个方法 

*/
Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println("method---"+methodCharAt.invoke(str1, 1));
System.out.println("method--"+methodCharAt.invoke(str1, new Object[]{2}));

/**
* 这就是用静态代码的方式直接调用它的方法
* 普通方式调完后,为甚么要用反射的方式调用
*/
//TestArguments.main(new String[]{"111","222","333"});
String startingClassName = args[0];
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
/**
* 因为main是静态方法,第一个参数是null
* 本是数组把多个元素打包成一个数组元素,但是java不把数组当做一个元素
* 而是打开分别作为多个参数
* 所以再打一个包Object[],在object数组中装的第一个元素是数组
* 每个数组的父类都是Object
* 如果在前面加Object对其转换就是对编译器说我给你的是一个对象,不要拆包
* mainMethod.invoke(null,(Object)new String[]{"111","222","333"});
* 等于在外面运行这个类
* run as --> run configurations -->Arguments
* 导入cn.itcast.day1.TestArguments
*/
mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});


int [] a1 = new int[3];
int [] a2 = new int[4];
int[][] a3 = new int[2][3];
String [] a4 = new String[3];
/**
* 对象要得到字节码要用方法
* Incompatible 不相容 矛盾 不能同时成立
* operand 操作数运算数
* capture 捕获
*/

System.out.println(a1.getClass() == a2.getClass());
//System.out.println(a1.getClass() == a4.getClass());
//System.out.println(a1.getClass() == a3.getClass());
/**
* 得到结果[I中括号代表数组,I代表整数
*/
System.out.println(a1.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());

Object aObj1 = a1;
Object aObj2 = a2;
//Object[] aObj3 = a1;
Object[] aObj4 = a3;
Object[] aObj5 = a4;

System.out.println(a1);
System.out.println(a4);
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
System.out.println();

Object obj = null;
printObject(a4);

printObject("xyz");
}


/**
* 你给我一个Object我就把他打印出来,
* @param obj
*/
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);
}
}


/**
* 这个程序写明白了,说不懂反射,谁相信呢?
* 好好看看本程序!
* @param obj
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException {
Field[] fields = obj.getClass().getFields();
for(Field field : fields){
/**
* if(field.getType().equals(String.class)){
* 上面这个不爽 用equals语义不准确
* 这个里应该用双等号,都是拿一份同一个字节码,比较字节码应该用双等号
*/
if(field.getType()== String.class){
String oldValue = (String)field.get(obj);
/**
* 将字符串里面的b换成a
*/
String newValue = oldValue.replace('b', 'a');
/**
* 拿到之后还要设置进去
*/
field.set(obj, newValue);
}
}

}
}


class TestArguments{
public static void main(String[] args){
for(String arg : args){
System.out.println(arg);
}
}
}





------- android培训、java培训、期待与您交流! ----------
0 0