Java反射产生的背景:Java一开始是静态语言,静态语言就是程序在运行时,可以根据某些条件改变自身结构。Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型(这个也叫运行时类)的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。
Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!
1.使用反射访问对象的属性,方法,创建对象。
Class clazz = Person.class;//这个就叫运行时类,每个类只有一个运行时类 //1.通过反射,创建Person类的对象 Constructor cons = clazz.getConstructor(String.class,int.class); Object obj = cons.newInstance("Tom", 12);
Person p = (Person) obj;
//上面两步可以合并为一步,如下
//Person person = (Person)clazz.getConstructor(String.class, int.class).newInstance("Tom", 12);
//上面是通过带参构造器使用反射创建的对象,对于无参构造器,如下:
Person person = (Person)clazz.getConstructor().newInstance();//该方法要求实体类中必须存在无参构造器
System.out.println(p.toString());
//2.通过反射,调用对象指定的属性、方法 //调用属性 Field age = clazz.getDeclaredField("age"); age.set(p,10); System.out.println(p.toString()); //调用方法 Method show = clazz.getDeclaredMethod("show"); show.invoke(p); System.out.println("*******************************"); //通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性 //调用私有的构造器 Constructor cons1 = clazz.getDeclaredConstructor(String.class); cons1.setAccessible(true); Person p1 = (Person) cons1.newInstance("Jerry"); System.out.println(p1); //调用私有的属性 Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p1,"HanMeimei"); System.out.println(p1); //调用私有的方法 Method showNation = clazz.getDeclaredMethod("showNation", String.class); showNation.setAccessible(true); String nation = (String) showNation.invoke(p1,"中国");//相当于String nation = p1.showNation("中国") System.out.println(nation);
//调用私有属性,方法等需要设置访问权限为true
//调用方法:首先用反射得到一个Method对象,用该方法调用invoke(),如果有参数就把参数放入invoke()中,但是invoke()方法第一个参数必须是对象
2.理解Class类
Object类中有一个方法public final Class getClass();该方法返回一个Class类的对象,Class类是Java反射的源头。通过对象反射求出类的名称。
对于每个类而言,JRE 都为其保留一个不变的Class 类型的对象。一个Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
Class本身也是一个类,Class对象只能由系统建立对象,一个加载的类在JVM 中只会有一个Class实例,每个类的实例都会记得自己是由哪个Class 实例所生成,通过Class可以完整地得到一个类中的所有被加载的结构,
Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
Class类的常用方法:
static Class forName(String name):返回指定类名name的Class对象
Object newInstance():调用无参构造器,返回该Class对象的一个实例
getName():返回此Class对象所表示的实体(类,接口,数组类,基本类型或void)名称
Class getSuperClass():返回当前Class对象的父类的Class对象
Class[] getInterfaces():获取当前Class对象的接口
ClassLoader getClassLoader():返回该类的类加载器
Class getSuperclass():返回表示此Class所表示的实体的超类的Class。
Constructor[] getConstructors():返回一个包含某些Constructor对象的数组
Constructor getConstrucetor(Class..clazz):返回指定参数类型的构造器
Field[] getDeclaredFields():返回Field对象的一个数组
Field getDeclaredField(String name):返回指定name的属性
Method getMethods(String name, Class... paramType):返回一个Method对象,此对象的形参类型为paramType
3.获取Class类的实例
1).Class clazz = String.class;
2).Class clazz = "www.atguigu.com".getClass();
3).Class clazz = Class.forName("www.atguigu.com");
4).Class clazz = this.getClass().getClassLoader().loadClass("类的全类名");
4.类的加载过程
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
换句话说,Class的实例就对应着一个运行时类。
加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
通过反射创建对应的运行时类的对象
newInstance():调用此方法,创建对应的运行时类的对象,内部调用了运行时类的空参的构造器。
要想此方法正常的创建运行时类的对象,要求:
(1)运行时类必须提供空参的构造器
(2)空参的构造器的访问权限得够,通常设置为public
在javabean中要求提供一个public的空参构造器。原因:
(1)便于通过反色和,创建运行时类的对象
(2)便于子类继承此运行时类时,默认调用super()时,保证弗雷有此构造器