------- android培训、java培训、期待与您交流! ----------
一,Java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class类。反射的基石:Class类。
二,对比提问:众多的人用一个什么类表示?众多的java类用一个什么类表示?比如以后要学习到的三大框架都要用到反射的技术。
1,人 -----> Person ;
2,java类--> Class。
三,对比提问:Person类代表人这一个类,它的实例对象就是张三,李四这样的一个个具体的人,Class类代表java类,它的各个实例对象又分别对应什么呢?
1,对应各个类在内存中的字节码【张老师说,*.class文件只有被类加载器加载到内存之后才称之为字节码】,例如,Person类的字节码,ArrayList类的字节码,等等。
2,一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?
四,以后学习框架要用到反射的知识。
五,一个类所属的类的名字:Class(注意,这里是大写的),一个定义类的关键字:class。
六,java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例有不同的属性值。java程序中的各个java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class关键字的区别哦。Class类描述了哪些方面的信息呢?类的名字、类的访问属性、类所属于哪个包、字段的名称列表、方法名称列表、以及它的父类等等。学习反射,首先就要明白Class这个类。
七,如何得到各个字节码对应的实例对象(Class类型):
1,类名.class,例如:System.class。这是固定的写法,没有为什么。
2,对象.getClass(),例如:new Date().getClass()。这是得到创建该对象的Class实例【即指向类Date的字节码的引用】。
3,Class.forName("类名"),例如,Class.forName("java.util.Date")。这里要写完整的,导入包是没用的。这个静态方法,如果JVM里没有所要类名的类,则会用类加载器加载到jvm里缓存起来,在进行Class实例的获取;如果存在,则就不加载,直接获取。反射的时候主要用这种。这也是一个面试题!如果已经加载过,就不加载了!如果没有加载,则就加载。因此,可以说是查询(要加载的类的字节码已经存在)或加载(要加载的字节码不存在于JVM中)。在写程序的时候一般用这种,因为在写程序的时候还不知道类的名字。
八,九个预定义的Class实例对象:
1,基本的8个 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
2,int.class==Integer.TYPE。TYPE代表包装类型所对应的基本类型的字节码(Class实例)。
3,Class的实例.isPrimitive方法的帮助。该方法,如果其Class类型是基本数据类型的则反回true,void类型的也反回true,否则返回false。
4,public boolean isPrimitive()判定指定的 Class 对象是否表示一个基本类型。 有九种预定义的 Class 对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean、byte、char、short、int、long、float 和 double。 这些对象仅能通过下列声明为 public static final 的变量访问,也是使此方法返回 true 的仅有的几个 Class 对象。 返回:当且仅当该类表示一个基本类型时,才返回 true。从以下版本开始: JDK1.1 另请参见:Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE。
九,public boolean isArray()判定此 Class 对象是否表示一个数组类。 返回:如果此对象表示一个数组类,则返回 true;否则返回false。从以下版本开始: JDK1.1 。
十,总之,只要在源程序中出现的类型,都有各自的Class实例对象,例如,int[] ,void。
十一,示例代码:
package itheima.zhao;
public class ReflectTest {
public static void main(String[] args) throws Exception {
String str = "abc";
Class c1 = String.class;
Class c2 = str.getClass();
Class c3 = Class.forName("java.lang.String");// 会抛出异常。
System.out.println(c1 == c2);// true
System.out.println(c1 == c3);// true
System.out.println(c1.isPrimitive());// false
System.out.println(void.class.isPrimitive());// true
System.out.println(int.class.isPrimitive());// true
System.out.println(int.class == Integer.class);// false
System.out.println(char.class == Character.TYPE);// true
System.out.println(int[].class.isPrimitive());// false
System.out.println(int[].class.isArray());// true
}
}
十二,反射就是把java类中的各种成份映射成相应的java类。例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等信息也用一个个java类来表示,就象汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class 类显然要提供一系列的方法来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是:Field , Method ,Constructor ,Package等等。
十三,java的反射就是把java类的成份解析成相应的类。
十四,一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。
十五,class类---->Class对象---->比如:Field对象--->某个成员变量。
十六,比如,一个类中所有方法属于Method类,而一个个具体的方法则就是一个Method类的一个个实例。
十七,利用反射获取构造函数:
1,public Constructor<?>[] getConstructors()
throws SecurityException返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。如果该类没有公共构造方法,或者该类是一个数组类,或者该类反映一个基本类型或 void,则返回一个长度为 0 的数组。 注意,此方法返回 Constructor<T> 对象的数组(即取自此类构造方法的数组)时,此方法的返回类型是 Constructor<?>[],不是 预期的 Constructor<T>[]。此少量信息的返回类型是必需的,因为从此方法返回之后,该数组可能被修改以保存不同类的 Constructor 对象,而这将违反 Constructor<T>[] 的类型保证。 返回:表示此类公共构造方法的 Constructor 对象数组 。抛出: SecurityException - 如果存在安全管理器 s,并满足下列任一条件: 调用 s.checkMemberAccess(this, Member.PUBLIC) 拒绝访问该类中的构造方法 调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包 从以下版本开始: JDK1.1 。
2, public Constructor<T> getConstructor(Class<?>... parameterTypes)//可变参数的方法。因此说参数是数组。
throws NoSuchMethodException,
SecurityException返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。parameterTypes 参数是 Class 对象的一个数组,这些 Class 对象按声明顺序标识构造方法的形参类型。 如果此 Class 对象表示非静态上下文中声明的内部类,则形参类型作为第一个参数包括显示封闭的实例。 要反映的构造方法是此 Class 对象所表示的类的公共构造方法,其形参类型与 parameterTypes 所指定的参数类型相匹配。 参数:parameterTypes - 参数数组 返回:与指定的 parameterTypes 相匹配的公共构造方法的 Constructor 对象。
抛出:
NoSuchMethodException - 如果找不到匹配的方法。
SecurityException - 如果存在安全管理器 s,并满足下列任一条件:
调用 s.checkMemberAccess(this, Member.PUBLIC) 拒绝访问构造方法
调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包
从以下版本开始:
JDK1.1
3,这个是Constructor类里的方法:public T newInstance(Object... initargs)
throws InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。个别参数会自动解包,以匹配基本形参,必要时,基本参数和引用参数都要进行方法调用转换。
十八,Constructor类代表某个类中的一个构造方法。
1,得到某个类所有的构造方法:(数组的中括号最好放在类型的后面,这样一看就知道是某类型的数组)
例子:Constructor[] constructors=Class.forName("java.lang.String").getConstructors();得到的所有的构造方法会装在constructor[]数组里面。
2,得到某一个构造方法:
例子:Constructor constructor =Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//获得具体的构造方法时要用到类型。这个方法会抛出异常。
public Constructor<T> getConstructor(Class<?>... parameterTypes)//JDK1.4及以前的版本是这里不是用可变参数来表示,而是用数组来表示的!
throws NoSuchMethodException,
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。parameterTypes 参数是 Class 对象的一个数组,这些 Class 对象按声明顺序标识构造方法的形参类型。 如果此 Class 对象表示非静态上下文中声明的内部类,则形参类型作为第一个参数包括显示封闭的实例。 要反映的构造方法是此 Class 对象所表示的类的公共构造方法,其形参类型与 parameterTypes 所指定的参数类型相匹配。
参数:
parameterTypes- 参数数组
返回:
与指定的 parameterTypes 相匹配的公共构造方法的 Constructor 对象
抛出:
NoSuchMethodException- 如果找不到匹配的方法。
SecurityException- 如果存在安全管理器 s,并满足下列任一条件:
§ 调用 s.checkMemberAccess(this, Member.PUBLIC) 拒绝访问构造方法
§ 调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包
3,创建实例对象:
通常方式:String str=new String(new StringBuffer("abc"));
反射方式:String str=(String)constructor.newInstance(new StringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象。
4,Class的实例.newInstance()方法:如果不用这个方法也不会影响Java的开发。
1,例子:String obj=(String)Class.forName("java.lang.String").newInstance();
2,该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
3,该方法内部的具体代码是怎么写呢?用到了缓存机制来保存默认构造方法的实例对象。
4,如果想要得到没有参数的对象,则用这个比较好。反射比较耗时!!!导致程序性能下降。因此,如果要调用无参的构造方法则用这个方法比较好。
5,示例代码:
package itheima;
import java.lang.reflect.*;
public class ReflectTest {
public static void main(String[] args) throws Exception {
// new String(new StringBuffer("abc"));
Constructor constructor = // 编译器只看代码的定义,不看代码的执行。编译时,和运行时。
Class.forName("java.lang.String").getConstructor(StringBuffer.class);// StringBuffer表示选择哪个构造方法。
// 意思是:用constructor这个构造方法来创建一个String类型的对象,newStringBuffer("abc")是
// 给constructor这个构造方法传递一个具体的参数。因为没有用泛型,所以得进行强转。返回的是Object。每调用一次,就创建一个对象。如果:String
// str=(String)constructor.newInstance("abc");这样是错误的,因为该构造方法要接收的是StringBuffer类的实例。因此,类型得匹配。
String str = (String) constructor.newInstance(new StringBuffer("abc"));// 传一个StringBuffer的对象!
System.out.println(constructor);// public
// java.lang.String(java.lang.StringBuffer)
System.out.println(str.charAt(2));// c
Class cl = constructor.getDeclaringClass();// 得到该构造方法所属的类的字节码实例。
System.out.println(cl);// class java.lang.String
}
}
十九,成员变量的反射:
package test;
import java.lang.reflect.*;
public class ReflectTest {
public static void main(String[] args)throws Exception {
ReflectPoint r1=new ReflectPoint(3,5);
Field fieldY =r1.getClass().getField("y");//这里写成员变量名。
//filedY不代表某个对象上的字段值,它只是表示ReflectPoint这个类里的一个成员变量。
//如果想获取某个对象上的值则应该采用下面的方法。
System.out.println(fieldY);//public int zhao.tai.ReflectPoint.y 完整的类名.成员变量。
System.out.println(fieldY.get(r1));//5 传递get的参数得是某一个类的具体实例。比如,这里的r1。也就是说,get方法在不同的对象上所取出来的值是不一样的。
Field fieldX=r1.getClass().getDeclaredField("x");//强力得到私有的。
System.out.println(fieldX);//private int zhao.tai.ReflectPoint.x 完整的类名.成员变量。
fieldX.setAccessible(true);//可以强力获取得到r1对象上私有成员变量x的值。暴力反射!!!
System.out.println(fieldX.get(r1));//3
}
}
class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {// 这是用MyEclipse自动生生成的构造方法。
super();
this.x = x;
this.y = y;
}
}
二十,成员方法的反射:
package test;
import java.lang.reflect.*;
public class ReflectTest {
public static void main(String[] args)throws Exception {
//返回的methodCharAt是一个Stirng类里的方法,chaAt方法的参数也得用int.class类型来表示。
String str="abc";
//str.charAt(1);
Method methodCharAt=String.class.getMethod("charAt", int.class);
Object ch1=methodCharAt.invoke(str,1);//str对象调用String类中的methodCharAt方法。
if(ch1 instanceof Character){
Character ch=(Character)ch1;
System.out.println(ch);//b
}
//new Object[]{new Integer(2)}//因为可以自动装箱。JDK1.4形式的。new Object[]{2};和前面的是一样的。
System.out.println(methodCharAt.invoke(str, new Object[]{2}));//c
}
}