java基础学习笔记——反射机制
开始捣鼓java的反射机制了,好了.....那开始吧!
一、什么是java的反射机制
大家都知道的啦,在运行java程序的过程中,会产生以后缀名.class结尾的文件,我们称其为字节码文件。那么,当JVM拿到这些字节码文件会去干什么呢???我们想一下肯定能想得到,JVM肯定是对此文件进行了剖析。这种在程序的动态运行过程中,对于编译所产生的任意一个.class文件能够动态的知道此类的属性和方法,动态获取类的相关信息的这种机制,就叫做java的反射机制。 OK,从上面的解释可以看出,使用反射机制就可以提高应用程序的扩展性,因为程序可以在动态的运行过程中动态的分析和加载某些内容,这些内容通常是对程序功能的扩展。那么,既然java的反射机制是通过对.class文件进行剖析之后进而读取其中的相关信息的,是不是JVM或者调用者首先得想办法得到.class文件呢???首先我们建立练习文件,方便反射机制的演示。二、建立Person类for练习反射机制
建立练习文件,代码如下:/ * 首先我们建立一个练习反射机制的Person类。
* 程序代码如下:
*/
public class Person {
privateint age;
privateString name;
//有参构造函数
publicPerson(int age, String name) {
super();
this.age= age;
this.name= name;
System.out.println("Person(int,String)run...."+this.name+":"+this.age);
}
//空参构造函数
publicPerson() {
super();
System.out.println("Person()run....");
}
//公有方法
publicvoid publicMethod() {
System.out.println("publicMethodrun...");
}
//私有方法
privatevoid privateMethod() {
System.out.println("privateMethodrun...");
}
//有参方法
publicvoid parameterMethod(String name, int age) {
System.out.println("parameterMethodrun..." + age + name);
}
//静态方法
publicstatic void staticMethod(){
System.out.println("staticMethodrun...");
}
//完善类的初始化方法
publicint getAge() {
returnage;
}
publicvoid setAge(int age) {
this.age= age;
}
publicString getName() {
returnname;
}
publicvoid setName(String name) {
this.name= name;
}
@Override
publicString toString() {
return"Person [age=" + age + ", name=" + name + "]";
}
}
三、获取class文件的方法
public class ReflectDemo {
/**
* 获取字节码文件的三种方式: 1.Object类中的getClass()方法。
* 2. 每一个类都具备静态属性,即.class。
* 3.通过指定的类的字符串名称,使用Class类的静态方法。
* @throws ClassNotFoundException
*/
publicstatic void main(String[] args) throws ClassNotFoundException {
getClass_1();
getClass_2();
getClass_3();
}
/*
* 使用Class类中的public static Class<?> forName(String className)throwsClassNotFoundException方法。
* 注意:需要抛出异常。同时,此处由于使用的是默认的包,因此,字符串类的名字直接就是类名,否则就要指定的类的全名。即加上包名。
*/
publicstatic void getClass_3() throws ClassNotFoundException {
Classc1 = Class.forName("Person");
Classc2 = Class.forName("Person");
System.out.println(c1);
System.out.println(c2);
}
/*
* 使用任何一个类具备的.class的静态属性。
* 缺点是:要使用到类中的静态成员,扩展性弱。
*/
publicstatic void getClass_2() {
Classc1 = Person.class;
Classc2 = Person.class;
System.out.println(c1);
System.out.println(c2);
}
/*
* 使用Object类中的public final Class<?> getClass()方法。
* 缺点是:需要创建对象。
*/
publicstatic void getClass_1() {
Personp1 = new Person();
Classc1 = p1.getClass();
Personp2 = new Person();
Classc2 = p2.getClass();
System.out.println(c1);
System.out.println(c2);
}
}
四、利用反射机制获取构造函数
1.了解两个异常:
在java.lang包中,有两个与java反射机制相关的异常,分别是:
public class InstantiationException extends Exception和public class IllegalAccessException。
这两个异常类发生的情况是:InstantiationException发生在类似于在利用某个构造函数去实例化一个对象的时候,但是类中却不存在这样的构造函数,例如在利用空参构造函数时,却没有空参构造函数。
IllegalAccessException发生在类似于在类中是有构造函数的,但是构造函数被私有化了,访问不到,则会发生此异常。
2.反射机制获取空参数构造函数对象
public class ReflectDemo {
publicstatic void main(String[] args) throws ClassNotFoundException,
InstantiationException,IllegalAccessException {
//使用字节码文件对象的方法创建实例。
getConstructorDemo();
}
publicstatic void getConstructorDemo() throws ClassNotFoundException,
InstantiationException,IllegalAccessException {
//1.获取字节码文件对象
Stringname = "Person";
Class<Person>c = (Class<Person>) Class.forName(name);
//2.利用字节码文件对象使用空参数的构造函数创建对象。
Person obj = c.newInstance();
}
}
3.反射机制获取有参数构造函数对象
import java.lang.reflect.Constructor;
importjava.lang.reflect.InvocationTargetException;
public class ReflectDemo {
publicstatic void main(String[] args) throws ClassNotFoundException,
InstantiationException,IllegalAccessException,
NoSuchMethodException,SecurityException, IllegalArgumentException,
InvocationTargetException{
getConstructorDemo();
}
publicstatic void getConstructorDemo() throws ClassNotFoundException,
NoSuchMethodException,SecurityException, InstantiationException,
IllegalAccessException,IllegalArgumentException,
InvocationTargetException{
//1.获取字节码文件对象
Stringname = "Person";
Class<Person>c = (Class<Person>) Class.forName(name);
//2.利用字节码文件对象获取其对象中的构造函数。
Constructor con = c.getConstructor(int.class, String.class);
//3.创建对象
Personobj = (Person) con.newInstance(25, "wanxi");
}
}
其中,Constructor是java.lang.reflect包中的一类,并且此类带有泛型。此类的完整继承关系如下:
public final class Constructor<T> extends AccessibleObject implements GenericDeclaration, Member
官方对此类的解释是:Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。Constructor 允许在将实参与带有底层构造方法的形参的 newInstance() 匹配时进行扩展转换,但是如果发生收缩转换,则抛出IllegalArgumentException。
五、利用反射机制获取字段
import java.lang.reflect.Field;
public class ReflectDemo3 {
/**
* @author Administrator
* @param args
* @throws ClassNotFoundException
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalAccessException
* @throws InstantiationException
*
*/
publicstatic void main(String[] args) throws ClassNotFoundException,
NoSuchFieldException,SecurityException, InstantiationException, IllegalAccessException {
getFieldDemo();
}
publicstatic void getFieldDemo() throws ClassNotFoundException,
NoSuchFieldException,SecurityException, InstantiationException, IllegalAccessException {
//获取字节码文件对象。
String name = "Person";
Class c = Class.forName(name);
/*
* 使用字节码文件对象的方法获取字段集合。
* Fieldfield =c.getField("age");利用此方法会出现错误,因为在我们的Person类中的字段是私有的,
* 就是说getField(String str)只能获取公有的方法。
* 而getDeclaredField(String str)可以获取私有的字段,但是只能获取本类中的字段,父类中的是获取不到的。
*/
Field field = c.getDeclaredField("age");
//在上述方法中,虽然我们拿到了field对象,但是由于其是私有的,因此如果我们使用get(Object obj)方法,则会出现无效的访问异常。因为有Private修饰。
//解决上述问题的办法:让程序在运行的时候对私有字段的方法取消权限的检查。
field.setAccessible(true);
//利用方法获取值,其中参数的意思是明确要获取哪个对象的age属性的值,因此首先创建对象。
Object obj = c.newInstance();
Object val = field.get(obj);
System.out.println(val);
//利用方法设置值,其中参数的意思是明确要给哪个对象的age属性设置值。
field.set(obj,25);
Object val1 = field.get(obj);
System.out.println(val1);
}
}
六、利用反射机制获取方法
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo {
publicstatic void main(String[] args) throws Exception {
getPrivateMethodDemo();
getAllMethodDemo();
getSingleMethodDemo();
getSingleParameterMethodDemo();
}
/*
* 利用java的反射机制获取对象自己的所有方法,包括私有方法。
*/
publicstatic void getPrivateMethodDemo() throws Exception {
//获取字节码文件对象。
Stringname = "Person";
Classc = Class.forName(name);
//获取对象中自己的所有方法,包括私有的方法。
Method[]methods = c.getDeclaredMethods();
for(Method method:methods){
System.out.println(method);
}
}
/*
*利用java的反射机制获取对象中的所有方法。
*/
publicstatic void getAllMethodDemo() throws Exception {
//获取字节码文件对象。
String name = "Person";
Class c = Class.forName(name);
//获取对象中的所有方法,包括父类中的方法。
Method[] methods = c.getMethods();
for(Method method:methods){
System.out.println(method);
}
}
/*
* 获取对象中指定的空参数的方法,并且运行获取的方法。
*/
publicstatic void getSingleMethodDemo() throws Exception {
//获取字节码文件对象。
String name = "Person";
Class c = Class.forName(name);
//获取指定的方法。
Method method = c.getMethod("staticMethod", null);
//创建对象,指定获取的方法是属于哪个对象的。
Object obj = c.newInstance();
//运行obj这个对象的方法。
method.invoke(obj,null);
}
/*
* 获取对象中指定的带参数的方法,并且运行获取的方法。
*/
publicstatic void getSingleParameterMethodDemo() throws Exception {
//获取字节码文件对象。
String name = "Person";
Class c = Class.forName(name);
//获取指定的带参数的方法。
Method method = c.getMethod("parameterMethod", String.class,int.class);
//创建对象,利用有参数的构造方法进行创建,指定获取的方法是属于哪个对象的。
Constructor constructor = c.getConstructor(int.class,String.class);
Object obj = constructor.newInstance(30,"wanxi");
//运行obj这个对象的方法。
method.invoke(obj,"xiaoqiang",32);
}
}
关于反射机制的应用,经常是用来增强程序的扩展性,使用者只需要实现源程序暴露出来的接口,然后将程序的相关信息写在一个配置文件中去(经常是XML文件),然后主程序会在动态的运行过程中去读取配置文件,从而拿到新扩展的程序的相关信息,对其进行剖析(反射机制原理)加以运行即可。因此,程序不需要改动源代码即可实现自身功能的扩展。
可以看出,利用java的反射机制,可以很好的解析字节码文件,从而进行程序的动态扩展性。(后续会继续完善......)
=======================================================
注: 转载注明出处: http://blog.csdn.net/xzpd1518148553
=======================================================