目录
摘要:
Java 反射机制是 Java 语言的一个重要特性。
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法或者为属性赋值。例如:在主流的 ORM 框架的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。
Java 反射机制主要提供了以下功能,这些功能都位于java.lang.reflect包。
●在运行时判断任意一个对象所属的类。
●在运行时构造任意一个类的对象。
●在运行时判断任意一个类所具有的成员变量和方法。
●在运行时调用任意一个对象的方法。
●生成动态代理。
Class类
以String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后,为String类创建一个Class实例并关联起来:
Class cls = new Class(String);
这个Class实例是JVM内部创建的,如果我们查看JDK源码,可以发现Class类的构造方法是private,只有JVM能创建Class实例,我们自己的Java程序是无法创建Class实例的。
由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段(成员变量)等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。这种通过Class实例获取class信息的方法称为反射(Reflection)。
如何获取一个class的Class实例?有三个方法:
//Class对象的三种创建方式
public static void main(String[] args) throws ClassNotFoundException {
Class stringclass1=String.class;
String s="";
Class stringclass2=s.getClass();
Class stringclass3=Class.forName("java.lang.String");
//方式一:通过类名访问class
System.out.println(stringclass1.hashCode());
//方式二:通过实例访问getClass()
System.out.println(stringclass2.hashCode());
//方式三:通过Class类的静态方法forName(类名)
System.out.println(stringclass3.hashCode());
}
因为Class实例在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例。可以看他们的hashCode:
结果: 要从Class实例获取获取的基本信息,参考下面的代码:
public static void main(String[] args) {
Class clsss=String.class;
printClassInfo(clsss);
}
public static void printClassInfo(Class cls) {
System.out.println("类(接口)的名称:"+cls.getSimpleName());
System.out.println("完全限定名:"+cls.getName());
System.out.println("类(接口)的类型名称:"+cls.getTypeName());
//父类
Class superCls=cls.getSuperclass();
System.out.println("类的父类:"+superCls.toString());
//实现的接口
Class[] interfaceCls=cls.getInterfaces();
System.out.println("当前类实现的接口:");
for(Class icls:interfaceCls) {
System.out.println(icls);
}
//package包
Package pck=cls.getPackage();
if(pck!=null)
{
System.out.println("类所在的包:"+pck.getName());
}
//判断类型
System.out.println("是否为接口:"+cls.isInterface());
System.out.println(cls.isArray());
System.out.println(cls.isEnum());
}
Class常用方法
类型 | 访问方法 | 返回值类型 | 说明 |
包路径 | getPackage() | Package 对象 | 获取该类的存放路径 |
类名称 | getName() | String 对象 | 获取该类的名称 |
继承类 | getSuperclass() | Class 对象 | 获取该类继承的类 |
实现接口 | getlnterfaces() | Class 型数组 | 获取该类实现的所有接口 |
构造方法 | getConstructors() | Constructor 型数组 | 获取所有权限为 public 的构造方法 |
getDeclaredContruectors() | Constructor 对象 | 获取当前对象的所有构造方法 | |
方法 | getMethods() | Methods 型数组 | 获取所有权限为 public 的方法 |
getDeclaredMethods() | Methods 对象 | 获取当前对象的所有方法 | |
成员变量 | getFields() | Field 型数组 | 获取所有权限为 public 的成员变量 |
getDeclareFileds() | Field 对象 | 获取当前对象的所有成员变量 |
调用构造方法
newInstance()方法
如果通过反射来创建新的实例,可以调用Class提供的newInstance()方法:
Person p = Person.class.newInstance();
调用Class.newInstance()的局限是,它只能调用该类的public无参数构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用。
Constructor类
为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象是一个构造方法,调用结果总是返回实例:
public static void main(String[] args) throws Exception {
// 获取构造方法Integer(int):
Constructor cons1 = Integer.class.getConstructor(int.class);
// 调用构造方法:
Integer n1 = (Integer) cons1.newInstance(123);
System.out.println(n1);
// 获取构造方法Integer(String)
Constructor cons2 = Integer.class.getConstructor(String.class);
Integer n2 = (Integer) cons2.newInstance("456");
System.out.println(n2);
}
获取继承关系
获取父类的Class
有了Class实例,我们还可以获取它的父类的Class:
public static void main(String[] args) throws Exception {
Class i = Integer.class;
Class n = i.getSuperclass();
System.out.println(n);
Class o = n.getSuperclass();
System.out.println(o);
System.out.println(o.getSuperclass());
}
获取 interface
由于一个类可能实现一个或多个接口,通过Class我们就可以查询到实现的接口类型。例如:查询Integer实现的接口:
public static void main(String[] args) throws Exception {
Class s = Integer.class;
Class[] is = s.getInterfaces();
for (Class i : is) {
System.out.println(i);
}
}
继承关系
当我们判断一个实例是否是某个类型时,正常情况下,使用instanceof操作符:
//instanceof运算符:判断引用和类型之间的关系
Object obj=Integer.valueOf(5698);
System.out.println("是否为Integer?"+(obj instanceof Integer));
System.out.println("是否为Object?"+(obj instanceof Object));
System.out.println("是否为Integer?"+(obj instanceof Double));
System.out.println("是否为Comparable?"+(obj instanceof Comparable));
如果是两个Class实例,要判断一个向上转型是否成立,可以调用isAssignableFrom():
public static void main(String[] args) {
//Number a=new Integer(565);
//isAssignableFrom()方法:判断类型和类型之间的关系
System.out.println("Integer<=Integer? "+Integer.class.isAssignableFrom(Integer.class));
System.out.println("Integer<=Double? "+Integer.class.isAssignableFrom(Double.class));
System.out.println("Integer<=Number? "+Integer.class.isAssignableFrom(Number.class));
System.out.println("Number<=Integer? "+Integer.class.isAssignableFrom(Integer.class));
System.out.println("Comparable<=Integer? "+Comparable.class.isAssignableFrom(Integer.class));
}
访问字段
获取Field 字段
对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息。比如通过一个Class实例获取字段信息。Class类提供了以下几个方法来获取字段:
●Field getField(name):根据字段名获取当前类中某个public的field(包括父类)
●Field getDeclaredField(name):根据字段名获取当前类中定义的某个field(不包括父类)
●Field[] getFields():获取所有public的field(包括父类)
●Field[] getDeclaredFields():获取当前类中定义的所有field(不包括父类)
public static void main(String[] args) {
Class cls=Book.class;
// Field[] field=cls.getFields();
Field[] fields=cls.getDeclaredFields();
for (Field field2 : fields) {
System.out.println("访问修饰符:"+field2.getModifiers());
System.out.println("访问修饰符:"+Modifier.toString(field2.getModifiers()));
System.out.println("成员变量类型:"+field2.getType());
System.out.println("成员变量名称:"+field2.getName());
System.out.println();
}
}
}
class Book{
public String bookName;
public int number;
private String age;
private String price;
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public int radomncreat() {
return this.radomncreat(100);
}
public int radomncreat(int a) {
return (int) ((Math.random())*a);
}
@Override
public String toString() {
return "图书信息:Book [bookName=" + bookName + ", number=" + number + ", age=" + age + ", price=" + price + "]";
}
使用反射的方式完成成员变量存值
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
//运行期
//使用反射的方式
Class cls=Book.class;//获取Class类型对象
Object book1=cls.newInstance();//通过反射创建Book类的对象
Field field1=cls.getDeclaredField("bookName");//获取指定字段
field1.set(book1, "西游记");//参数一:目标对象;参数二:存入的值
System.out.println(book1);
}
使用反射的方式输出参数对象的所有成员变量和值
public static void main(String[] args) {
printInfo(book);
public static void printInfo(Object obj) throws IllegalArgumentException,
IllegalAccessException {
//输出参数对象的所有成员变量和值
Class cls=obj.getClass();
Field[] fields=cls.getDeclaredFields();
for (Field field : fields) {
System.out.println("成员变量名称:"+field.getName());
if(!field.isAccessible()) {
field.setAccessible(true);
}
System.out.println("成员变量内容:"+field.get(obj));
}
}
}
调用方法
Method类
我们已经能通过Class实例获取所有Field对象,同样的,可以通过Class实例获取所有方法(Method类型的对象)。Class类提供了以下几个方法来获取Method:
●Method getMethod(name, Class...):获取某个public的Method(包括父类)
●Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
●Method[] getMethods():获取所有public的Method(包括父类)
●Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
//反射操作:获取方法
//每个方法都会被封装成一个Method对象
public static void main(String[] args) {
Class book =Book.class;
//获取所有Public方法(包含父类)
// Method[] methods= book.getMethods();
Method[] methods=book.getDeclaredMethods();
for (Method method : methods) {
System.out.println("方法的名字:"+method.getName());
System.out.println("方法的返回值类型:"+method.getReturnType());
System.out.println("方法的访问修饰符:"+Modifier.toString(method.getModifiers()));
Parameter[] parms=method.getParameters();
for (Parameter parm : parms) {
System.out.println(parm.getName());
System.out.println(parm.getType());
System.out.println("-----------------------------");
}
System.out.println();
}
}
调用方法:
当我们获取到一个Method对象时,就可以对它进行调用。
//反射操作调用方法
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//获取Class对象
Class book=Book.class;
// 按照方法名称和参数类型获取Method方法对象
Object book1=book.newInstance();
Method meod1=book.getDeclaredMethod("radomncreat");
int a1=(int) meod1.invoke(book1);
System.out.println(a1);
// 按照方法名称和参数类型获取Method方法对象
Method meod2=book.getDeclaredMethod("radomncreat",int.class);
int a2=(int) meod2.invoke(book1,10000);
System.out.println(a2);
}
调用静态方法:
如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null。我们以Integer.parseInt(String)为例:
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class cls=Math.class;
Method log10=cls.getMethod("log10",double.class);
System.out.println(log10.invoke(null, 324544));
}