反射(一)类成分的获取和使用
1.Class类对类的一种抽象,包含类的公共特性如类都有类名,包名,方法,属性等
2.Class类型是类的字节码:获取类的字节码方式有三种:(如person类)
Person.class
new Person().getClass()
Class.forName("Person")
3. 基本类型判断,基本类型和包装类型不是同一类型,以及类型的判断
4.反射就是把类的成分映射成各种类,可以通过字节码获取和使用它们( 类的成分类型Field、Method、 Contructor、Package等等)
4.1.Constructor类代表构造方法(通过字节码获取够准确)
得到某个类所有的构造方法:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法: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"));//通常创建对象需要先获取构造器类(该构造器类一般是有参数的而不是默认无参的)
直接使用默认构造器:
String obj = (String)Class.forName("java.lang.String").newInstance(); //该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
4.2Field类代表某个类中的一个成员变量(获取对象的属性值:a.得到类字节码 b.得到字段类 c.调用字段的get方法传入对象参数得到属性值)
ReflectPoint point = new ReflectPoint(1,7);
Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");抛出异常;因为属性x是私有的。需要使用getDeclareFiled();
Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");//这只是获取特定的字段类
x.setAccessible(true);//要想获取私有属性的值,需要设置其是可访问的
System.out.println(x.get(point));//获取对象中字段的值
将一个对象字段值修改的例子:(将‘b'用’a'替换)
4.3Method类
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和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即charAt.invoke(“str”, new Object[]{1})形式。
关于方法参数本身是数组类型时:而反射默认方法的参数是数组,表示参数的个数;会自动拆分参数。比如反射调用main方法时:
mainMethod=getMethod("main", String[].class);
mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行 理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题.
解决办法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx",“xx”}});
mainMethod.invoke(null,(Object)new String[]{"xxx","xx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
4.4数组(基本类型不是继承object所以是不能直接向上转型的但是基本类型的数组是继承object可以自动向上转型的)
判断数组是否是同一类型:(相同的类型具有相同的class对象)
具有相同维数和元素类型的数组属于同一个类型(与长度无关)。
基本类型的一维数组可以被当作Object类型使用(数组继承object),不能当作Object[](一个存放objec类型t的数组)类型使用;
非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用
(二位数组就是存放数组的数组)
将一个数组转换成列表时候:(编译器首先1.4判断是否是对象数组,不是的话,采用1.5可变参数方式处理)
Arrays.asList(T.. t)方法参数是一个可变参数,也当成Object[]来处理的:处理参数是int[]和String[]时有差异的
如果是int[]会当成一个object类型来处理(因为int[]不能直接转换成object[]类型,就当一个可变参数)变成一个元素是数组的列表
如果是Sting[]会作为一个object[]方式来处理,变成的是元素是字符串的列表
Array工具类用于完成对数组的反射操作:
注意:关于数组的类型,如果是object[]那么具体元素的类型可能不一样,直接通过获取数组类型并不能精确知道每个具体元素的类型;