-----------
反射的基石---Class类
一个类有多个组成部分,例如:成员变量,成员方法,构造函数等。反射就是加载类,并解剖出类的各个组成部分
加载:硬盘上的有用程序调到内存中的整个过程,一般来说我们在启动程序或安装程序时都会有加载。
1. java类用于描述一类事物的共性,该类事物该有什么属性,不该有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。Java程
序中的各个java类是否也属于同一类事物,是不是可以用一个类来描述这个类事物呢?这个类的名称就是Class。Class类描述了那些方面的信息呢?类的名称,类的访问属性,类所属的包名
,字段名称的列表、方法名称的列表。
2. Class的实例对象代表内存中的一份字节码。
当我们在源程序中用到一个叫person的类时,首先要从硬盘上把这个类的二进制代码(一个类编译成class后,就是存放在硬盘上的一些二进制代码)加载到内存中来,接着才可以用这些代码
去创建一个实例对象。当我们源程序中用到了很多个类时,例如Date Math等等这些类时,我们的内存中就会加载这些类的二进制字节码,也就是说内存中会存在相应个数的类的字节码个数。
那么!每一个字节码就是Class的一个实例对象。
如何获得内存中的字节码(获得Class类的实例对象)
Class clazz = Person.class
Person p1 = new Person();
Class clazz = p1.getClass();
Class.forName(“java.util.Date”)做反射时主要用这种方法返回字节码
[面试题]Class.forName()的作用:返回字节码,而返回的方式有两种,第一种是要返回的字节码曾经被加载过,已经存在于java虚拟机中了直接返回;第二种是java虚拟机中还没有这个份字节码,这时就用java 虚拟机去加载,将加载进内存的字节码缓存在虚拟机中,以后要得到这份字节码就不用再加载了。
九个预定义的Class实例对象:
八个基本类型+void
判断一份字节码是否是基本类型的字节码
Class的isPrimitive() -->true false
基本类型包装类
int.class == Integer.TYPE --> true
判断是否是数组类型的字节码:
Class的isArray() --> true false
理解反射的概念:
java类来表示,就像汽车是个类,汽车中的发动机,变速箱等等也是一个个类。表示java类得Class类显然要提供一系列的方法,来获得其中的变来那个方法,构造方法,修饰符,包等信息,
这些信息就是用相应的类得实例对象来表示,他们是Field.Meshod.Contructor.Package等等。
2. 一个类中的每个成员都可以用相应的反射API类得一个实例对象来表示,通过调用Class类得方法可以得到这些实例对象后,得到这些实例对象有什么用呢?怎么用呢》这真是学习和应用反
射的要点。
Construct类:
1. constructor类代表某个类中的一个构造方法
2. 得到某个类所有的构造方法:
例子:Construct[] constggrctors = Class.forName(“java.lang.String”).getConstructors();
3. 得到某一个构造方法:
例子:Constructor constructor =
Class.forName(java.lang.String).getConstructor(StringBuffer.class);
4. 代码实例:
用Construct来实现 new String (new StringBuffer(“abc”));
Constructor constructor = String.class.getConstructor(StringBurrer.class);
String str = (String) constructor.newInstance(new StringBuffer(“abc”));
说明:这边为什么要强制转换:因为constructor的newInstance方法返回的结果是object类型。
5. Class类中同样有一个newInstance()。这其实是调用了Constructor中的无参构造方法。
Filed类:
1. 表示某个类中的成员变量
2. 得到的Filed对象是对应到雷山面得成员变量,还是对应到对象上的成员变量?类只有一个,而该类得实例对象有多个,如果是对象关联,那关联的是哪个对象呢》所以字段filedX
代表的是类中的定义。而不是对象。
3. 实例代码:
Public class ReflectPoint
{
Public int x;
Public int y;
ReflectPoint(int x, int y)
{
This.x = x;
This.y = y;
}
}
Main(){
ReflectPoint p1 = new ReflectPoint (3,5);
Filed filedX = p1.getClass().getFiled(“x”);//这个地方获得的并不是P1的X值而是P1所属的类得X字段。
System.out.println(filedX .get(p1));//这个地方通过指定对象P1。获得P1的X值。
}
Method类:
1. Method类代表某个类中的一个成员方法
2. 得到类中的一个方法:
例子:Method charAt =
Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);
通常方式:System.out.println(str.chaAt(1));
反射方式:System.out.println(charAt.invoke(str,1));
4. 代码实例:
String str1 = “abc”;
Method methodCharAt = String.class.getMethod(“charAt”,int.class);
System.out.println(methodCharAt.invoke(str1,1));
输出b
6. 如果invoke方法的第一个参数为Null那么这个方法为静态方法。
目标:
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通的方式调用完以后,要明白为什么要用反射的方式去调用。
问题:
启动java程序的main()方法的阐述是一个字符串,,即public static void main(String[]args),
通过反射方式调用这个main方法时,如何为 invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中格的每个元素对应一个参数,当把一个字符串数组作
为参数传递给jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以在给main方法传递参数时不能使用代码
mainMethod.invoke(null,newString[]{xxx}),javac只把他当做jdk1.4来理解,不把他当成1.5运作所以会出现参数不对的错误
解决办法’:
mainMethod.invoke(null,newObject[]{new String[]{“xxx”}});
mainMethod.invoke(null,(Object)new String[]{“XXX”}); 编译器会做特殊处理,编译时不把参数当做数组看待,也就不会数组打散成若干个字符串了。
示例代码:
Class ReflectTest
{
Public static void main(String [] args )
{
String startingClassName = args[0]; //这里在运行的时候才将要运行的类得名字传进来编写代码的时候并不知道要运行的类是什么。
Method mainMethod =
class.forName(startingClassName).getMethod(“main”,String[].class);
mainMethod.invokedf(null,(object)new String[]{“ 111”,”222”,”333”});
}
}