在文章开头,我们先看看反射是什么?
Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法 可以简单理解为,在运行状态下,控制台输入一个(全)类名就可以得到一个该类对象,或者获取到该类的信息可以实现类的动态加载对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。 而这也是Java被视为动态或准动态语言的一个关键性质。(为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)既然反射这么nb,那么Java是怎么实现反射的呢?答:依靠Class对象。
Java有两种对象,第一种是实例对象,第二种是Class对象,每一个类运行的类型信息就是用Class对象表示的,每一个对象都有一个到 java.lang.Class(用于描述对象的结构)的实例的引用。
Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
Class对象有三种获取方式:
Class.forName(“全类名”) 将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中,读取文件,加载类。 类名.class 通过类名的属性class获取。多用于参数的传递。 对象.getClass() getclass方法在object类中定义。多用与对象的获取字节码的方式。这里再强调一次,同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,所以无论通过哪一种方式获取的class对象都是一个。三种方式的代码示例如下:
public class Demo { public static void main(String[] args) throws ClassNotFoundException { // 1.通过Class.forName获取Class对象 Class aClass = Class.forName("com.xupt.yzh.entity.Persion"); // 2.通过类名.class获取Class对象 Class bClass = Persion.class; // 3.通过对象.getClass获取Class对象 Persion persion = new Persion(); Class cClass = persion.getClass(); Persion persion1= new Persion(); Class dClass = persion.getClass(); persion1.getClass(); System.out.println(persion==persion1); // false System.out.println(aClass==bClass); // true System.out.println(aClass==dClass); // true System.out.println(aClass==cClass); // true System.out.println(cClass==dClass); // true } }Class中的方法有很多,但是最常用的就三种:获取字段(Filed对象),获取构造器(Constructor对象),获取方法(Method对象)。下面就通过代码示例来逐个说明…
// Person的Class对象是后面示例的主角 public class Person { public String name; private int age; // 两个构造方法,带参和空参 public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } // get、set public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // 两个额外方法 public void eat(){ System.out.println("eat...."); } public void hello(String a){ System.out.println("say"+ a); } }可以看到Person有两个成员变量,还有一个是private私有的,但是在反射面前,没有什么私有共有之分,向下看!
示例代码如下:
public class TestField { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException{ // 通过类名拿到Class对象 Class p = Person.class; // Class#getFields:获取public 字段Filed[] Field[] fields = p.getFields(); for (Field f:fields) { System.out.println(f); } System.out.println("-------------------------"); // Class#getField(name): 获取指定字段Field Field field = p.getField("name"); Person person = new Person(); // Field#get(Obj):获取指定对象的当前字段值 Object name = field.get(person); System.out.println(name); // Field#set(obj,value):设置一个对象的当前字段值 field.set(person,"zhangsan"); System.out.println(person); // Class#getDeclaredFields:获取所有成员变量,不考虑修饰符Field[] Field[] declaredFields = p.getDeclaredFields(); for (Field field1:declaredFields) { System.out.println(field1); } System.out.println("-------------------------"); // Class#getDeclaredField(name):获取指定private字段Field Field field1 = p.getDeclaredField("age"); // Filed#setAccessible:暴力反射,在对private字段操作前必须设置Accessible为true field1.setAccessible(true); Person person1 =new Person(); Object o = field1.get(person1); System.out.println(o); } }运行结果:
示例代码:
public class TestConstructor{ public static void main(String[] args) throws Exception { // 通过对象获取Class对象 Class personClass = Person.class; // Class#getConstructor(Class paramType...):获取指定构造器Constructor,要传入参数类型的Class对象 Constructor constructor = personClass.getConstructor(String.class, int.class); System.out.println(constructor); // Constructor#newInstance(param):用构造方法创建一个对象 Object asd = constructor.newInstance("asd", 10); System.out.println(asd); // Class#newInstance:空参构造方法创建对象 Object o = personClass.newInstance(); System.out.println(o); } }运行结果:
示例代码:
public class TestMethod{ public static void main(String[] args) throws Exception { // 获取Class对象 Class<Person> personClass = Person.class; // Class#getMethod(name):同过方法名获取方法Method Method method = personClass.getMethod("eat"); Person person = new Person(); // Method#invoke(obj):执行方法 method.invoke(person); System.out.println("----------------"); Method hello = personClass.getMethod("hello", String.class); // Method#invoke(obj,param):执行方法,并传入参数 hello.invoke(person,"hello"); } }运行结果:
