Java是一门静态语言,但反射机制使其具有了动态语言的很多特征,所以我认为Java可以看作一门准动态语言。那么,我们再看看什么是Java的反射机制?
Java的反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
首先,Java是一门面向对象语言,Java中的类也是一个对象。所有类都是java.lang.Class类的一个对象,这个关于该类的Class类的对象也被称为该类的类类型(class type)
-
如何获取一个类的类类型?有三种方式
package test; public class Test { public static void main(String [] args){ //第一种通过类名获取类类型 Class c1=Test.class; //第二种已知该类对象获取类类型 Test t=new Test(); Class c2=t.getClass(); //第三种通过全限定名获取类类型 Class c3=null; try { c3=Class.forName("test.Test"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
那么通过这三种方式获取到Test类的类类型,是否相等?答案是肯定的,因为他们是属于同一个类的类类型。我们可以通过代码测试一下
boolean b1=(c1==c2)?true:false;
boolean b2=(c1==c3)?true:false;
boolean b3=(c2==c3)?true:false;
System.out.println(b1+","+b2+","+b3);
结果如下图所示:
其中Class.forName(“类的全限定名”)不但代表该类的类类型,还代表着动态加载类。(编译时加载类属于静态加载类,运行时加载类属于动态加载,使用new关键字创建一个类的实例对象时,属于静态加载类)相比静态加载(在编译时需要所有可能用到的类构建完毕),动态加载可以看作一种按需运行,未构建的类不影响已构建的类的使用,这样在一个庞杂的项目中,即使有些功能(类)未完成,我们依旧可以使用那些已经完成的功能(类),这并不影响整个项目的正常运行。同时,动态加载类有利于代码重用和维护,我们可以对某些相近功能的类定义一个公共的规范接口,有利于程序的扩展和维护。这就是动态加载类的好处。
-
我们可以通过如下代码动态的获取一个类的对象
package test; public class Test { public static void main(String [] args){ Class c=null; try { c=Class.forName("test.Test"); } catch (ClassNotFoundException e) { e.printStackTrace(); } try { //通过Test类的类类型c生成Test类的实例对象 Test t=(Test)c.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
-
Class类常用api,通过这些api来获取一个类的全部信息,下面我以String类作为示例
1.首先获取String类中的相关方法信息,这些方法其实也是一个对象,是 java.lang.reflect.Method类的一个对象
package test;
import java.lang.reflect.Method;
public class Test {
public static void main(String [] args){
Class c1=String.class;
//1.获取该类的全限定名
System.out.println(c1.getName());
//2.返回该类的简称
System.out.println(c1.getSimpleName());
//3.获取该类的所有public方法,包括父类继承而来的
c1.getMethods();
//4.获取该类声明的所有的方法,不包括父类继承的
Method[] method=c1.getDeclaredMethods();
for (int i=0;i<method.length;i++){
//5.获取该方法的返回值类型
//getReturnType()得到的是返回值类型的类类型,如返回值是String,那么得到的就是String的类类型
Class returnType=method[i].getReturnType();
System.out.print(returnType.getSimpleName()+" ");
//6.获取该方法的名称
System.out.print(method[i].getName()+" ");
//7.获取该方法的参数类型
Class [] paramTypes=method[i].getParameterTypes();
for (int j=0;j<paramTypes.length;j++){
System.out.print(paramTypes[j].getSimpleName()+",");
}
System.out.println();
}
}
}
2.获取String类中的成员变量,这些成员变量其实也是一个对象,是 java.lang.reflect.Field类的一个对象
package test;
import java.lang.reflect.Field;
public class Test {
public static void main(String [] args){
Class c1=String.class;
//1.获取该类中所有public的成员变量
Field[] fields1=c1.getFields();
//2.获取该类中所有声明的成员变量
Field[] fields=c1.getDeclaredFields();
for(int i=0;i<fields.length;i++){
//3.获取该成员变量的数据类型
Class Type = fields[i].getType();
System.out.print(Type.getSimpleName()+" ");
//4.获取该成员变量的名字
System.out.println(fields[i].getName());
}
}
}
3.获取String类中的构造方法,这些构造方法其实也是一个对象,是 java.lang.reflect.Constructor类的一个对象
package test;
import java.lang.reflect.Constructor;
public class Test {
public static void main(String [] args){
Class c1=String.class;
//1.获取所有的构造方法
Constructor [] constructors=c1.getDeclaredConstructors();
for(int i=0;i<constructors.length;i++){
//2.获取构造方法的名字
System.out.print(constructors[i].getName());
//3.获取构造方法的参数类型
Class [] paramTypes=constructors[i].getParameterTypes();
for(int j=0;j<paramTypes.length;j++){
System.out.print(paramTypes[j].getName()+",");
}
System.out.println();
}
}
}
方法的反射操作
package test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
public static void main(String [] args){
//1.正常方法调用过程,使用类的对象调用方法
A a=new A();
a.print(3,5);
//2.通过反射调用方法
Class c1=A.class;
try {
//获取声明的方法其中参数为方法名称,及参数类型的类类型
Method method=c1.getDeclaredMethod("print", int.class, int.class);
try {
//通过method对象反过来操作对象a调用指定方法
method.invoke(a,3,5);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
class A{
public void print(int a,int b){
System.out.println(a+b);
}
public void print(String a,String b){
System.out.println(a+"和"+b);
}
}
程序结果如图,可以看到,正常的方法调用和通过反射调用方法的结果是相同的。