Tips:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
JAVA反射(放射)机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
简单来说就是你不用具体实例化某个对象只通过一个类的名字(这个类名你可能事先不知道,具体运行的时候才能确定)来调用类的方法和修改类的属性。比如以前都是通过 import 某个类,然后 new 后取得实例化对象去调用类的方法和设置类的属性,先做个简单的测试,看能不能先通过实例化后的对象来得到类的完整名字,如以下程序:
package a.b.c.d;
class Person {
private String name;
void setName(String name) { this.name = name; }
String getName(){ return this.name; }
}
public class Reflect {
public static void main(String args[]) {
Person p = new Person();
/* 根据实例化对象获取类并获得类名打印出来 */
System.out.println(p.getClass().getName());
}
}
上面的程序直接结果是打印出了:a.b.c.d.Person
既然可以根据实例化对象来获得类名,那有没有办法通过 Person 这个类的名字来获得类对象,从而调用 Person 类里面的方法呢,是可以的,这也就是反射的概念,可以看下面的程序实现。
package a.b.c.d;
class Person {
private String name;
void setName(String name) { this.name = name; }
String getName(){ return this.name; }
}
public class Reflect {
public static void main(String args[]) {
Person p = new Person();
/* class用来描述类的 */
/* 下面用三种方法来获得class */
/* 一:通过类名 */
Class> c1 = null;
try {
c1 = Class.forName("a.b.c.d.Person");
} catch (ClassNotFoundException e) {
System.out.println(e);
}
/* 二:通过实例对象 */
Class> c2 = p.getClass();
/* 三:通过实例对象 */
Class> c3 = Person.class;
System.out.println(c1.getName());
System.out.println(c2.getName());
System.out.println(c3.getName());
}
}
上面输出的结果如下:
a.b.c.d.Person
a.b.c.d.Person
a.b.c.d.Person
输出的都是一样的,所以以上三种获得 class 的方法都是可以的。上面做了 Person 类的测试,那基本数据类型是否也能获得它的类名呢,可以使用下面的代码来进行测试。
public class Reflect {
public static void main(String args[]) {
/* 打印数组的类 */
int arr[] = {1,2,3};
int arr2[] = {1,2,3,4};
int arr3[][] = {{1,2,3,4}, {2}};
Class> c4 = arr.getClass();
Class> c5 = arr2.getClass();
Class> c6 = arr3.getClass();
System.out.println(c4.getName());
System.out.println(c5.getName());
System.out.println(c6.getName());
/* 判断是否一致 */
System.out.println((c4 == c5)); /* 一样 */
System.out.println((c4 == c6)); /* 不一样 */
/* 基本数据类型 int 是否也有类 */
Class> c7 = int.class;
System.out.println(c7.getName());/* 类类型是int */
}
}
输出结果如下:
[I
[I
[[I
true
false
int
可以看到一维数组和二维数组的类名不同,int 的类名是 int 。当然使用反射肯定不仅仅是为了获得类的名字,而是想通过类名来正常访问使用一个类,下面我们把 Person 类写到一个包里面,然后通过反射去访问他的有参和无参的构造函数,使用反射就不需要去 import Person 这个类了,代码如下:
Person类:
package a.b.c.d;
public class Person {
private String name;
void setName(String name) { this.name = name; }
String getName(){ return this.name; }
public Person(){
System.out.println("Constructor of Person");
}
public Person(String name){
this.name = name;
System.out.println("Constructor of Person, name is "+this.name);
}
}
下面使用反射来访问这个 Person 类,代码如下:
import java.lang.reflect.Constructor;
public class Reflect {
public static void main(String args[]) throws Exception{
/* 通过类名字获得一个类 */
Class> c = null;
try {
/* Class使用名字来获得Person object */
c = Class.forName("a.b.c.d.Person");//类名
} catch (ClassNotFoundException e) {
System.out.println(e);
}
/* 不能这么写,会找不到Person这个类 */
//Person p = c.newInstance();
/* 可以使用Object,Object是所有类的父类 */
Object p = null;
try {
p = c.newInstance();/* 调用类的无参构造方法 */
} catch(InstantiationException e) {
System.out.println(e);
}
/* 调用有参构造函数首先需要获得有参构造方法 */
Constructor> con = c.getConstructor(String.class);
Object p2 = con.newInstance("call the person");
}
}
上面仅通过类名就访问到了类的有参和无参构造方法,能否访问类的其它方法呢,也是可以的,下面先是调用设置的方法(setName),然后再调用获取的方法(getName)来获取设置值,看如下代码:
/* 调用 Constructor 需要import */
import java.lang.reflect.Constructor;
/* 调用 Method 需要import*/
import java.lang.reflect.Method;
public class Reflect {
public static void main(String args[]) throws Exception{
Class> c = null;
try {
/* Class使用名字来获得Person object */
c = Class.forName("a.b.c.d.Person");
} catch (ClassNotFoundException e) {
System.out.println(e);
}
/* Object是所有类的父类 */
Object p = null;
try {
p = c.newInstance();/* 调用类的无参构造方法 */
} catch(InstantiationException e) {
System.out.println(e);
}
/* 调用有参构造函数首先需要获得构造方法 */
Constructor> con = c.getConstructor(String.class);
Object p2 = con.newInstance("call the person");
/* 获得类里面的方法,方法需要传入参数String.class */
Method set = c.getMethod("setName", String.class);
/* invoke调用,这个方法是用来设置这个对象传入的参数,传入字符串 */
set.invoke(p2,"123");
set.invoke(p,"abc");
Method get = c.getMethod("getName");
/* 调用 */
System.out.println(get.invoke(p));
System.out.println(get.invoke(p2));
}
}
上面通过类名调用到类里面方法的形式去修改了类的成员变量,那能不能直接去设置这些类里面的变量呢,还有一种方式,就是使用 Field 直接去设置,如下代码:
/* 调用 Constructor 需要import */
import java.lang.reflect.Constructor;
/* 调用 Field 需要import*/
import java.lang.reflect.Field;
public class Reflect {
public static void main(String args[]) throws Exception{
Class> c = null;
try {
/* Class使用名字来获得Person object */
c = Class.forName("a.b.c.d.Person");
} catch (ClassNotFoundException e) {
System.out.println(e);
}
/* Object是所有类的父类 */
Object p = null;
try {
p = c.newInstance();/* 调用类的无参构造方法 */
} catch(InstantiationException e) {
System.out.println(e);
}
/* 调用有参构造函数首先需要获得构造方法 */
Constructor> con = c.getConstructor(String.class);
Object p2 = con.newInstance("call the person");
/* 获得本类中的属性 */
Field name = c.getDeclaredField("name");
/* 因为name为private,所以需设置为可被外部访问,破坏了类的封装性,不建议这么用 */
name.setAccessible(true);
//p、p2为实例化对象
name.set(p, "is p");
name.set(p2, "is p2");
System.out.println(name.get(p));
System.out.println(name.get(p2));
}
}
上面代码写完后,如果有个类跟 Person 类非常相似都有获取名字和设置名字的一些属性和方法,但是程序一旦编译过后,我们如何能够动态给这个程序传入不同的类名来实现不同的功能呢,也就是Tips说到的可以加载一个运行时才得知名称的class,我们现在写好了另一个类如 Student 类,那我们就可以使用反射提供的功能,在程序运行时我们传入不同的类名从而调用到不同类的属性,先写一个 Student 类来试试:
package a.b.c.d;
public class Student {
//如果设置为private,使用Filed的话要用setAccessible,否则不用
private String name;
public void setName(String name) { this.name = name; }
public String getName(){ return this.name; }
public Student(){
System.out.println("Constructor of Student");
}
public Student(String name){
this.name = name;
System.out.println("Constructor of Student, name is "+this.name);
}
}
Student 类跟 Person 类很像,只是类名不一样,打印的东西不一样,这里需要把前面的 Reflect 类改为动态传入类名,即具体调用的类名由客户输入,做如下更改即可:
/* Class使用名字来获得Person object */
c = Class.forName(args[0]);//手动输入类名
那在输入的时候只要输入不同的类名就可以实现不同的功能:
java Reflect a.b.c.d.Person
java Reflect a.b.c.d.Student