反射是JAVA中重要的特性,大部分的开发框架以及应用技术中都是基于反射技术的应用。
什么是反射
在构建类的实例化对象过程中,需要先确定使用的类,然后使用new进行实例化对象构建。但是如果要通过对象取得此对象所在类的信息,就需要通过Object类中的getClass方法实现。
public final Class<?> getClass()
这里的?就是不明确指明类是哪个。
public class Demo {
public static void main(String[] args) throws Exception {
String str = "Hello world";
System.out.println(str.getClass());
}
}
执行结果为:
class java.lang.String
该方法的返回值为Class<?>,然后又进行了打印。
Class类对象实例化
当时用getClass方法时,返回的类型为java.lang.Class。既然该方法的返回值为Class<?>,那么就可以用该返回值实例化对象,但是具体要怎么构建实例化对象。
调用Object类中的getClass方法
使用此类操作必须要有实例化对象。
public class Demo {
public static void main(String[] args) throws Exception {
String str = "Hello world";
Class<?> cls = str.getClass();
System.out.println(cls.getName());
}
}
执行结果为:
java.lang.String
使用“类.class”取得
此时可以不需要通过指定类的实例化对象取得。
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls = java.lang.String.class;
System.out.println(cls.getName());
}
}
执行结果为:
java.lang.String
调用Class类提供的方法
public static Class<?> forName(Module module, String name)
示例代码为:
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("java.lang.String");
System.out.println(cls.getName());
}
}
执行结果为:
java.lang.String
使用Class类中的forName方法前提是此类确实存在,如果不存在则会抛出“ClassNotFoundException”异常。
反射实例化对象
在Class类中存在如下几种常用方法:
static Class<?> forName(Module module, String name) // Returns the Class with the given binary name in the given module.
static Class<?> forName(String className) // Returns the Class object associated with the class or interface with the given string name.
static Class<?> forName(String name, boolean initialize, ClassLoader loader) // Returns the Class object associated with the class or interface with the given string name, using the given class loader.
Class<?>[] getInterfaces() // Returns the interfaces directly implemented by the class or interface represented by this Class object.
String getName() // Returns the name of the entity (class, interface, array class, primitive type, or void) represented by this Class object.
Package getPackage() // Gets the package of this class.
String getSimpleName() // Returns the simple name of the underlying class as given in the source code.
Class<? super T> getSuperclass() // Returns the Class representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class.
boolean isArray() // Determines if this Class object represents an array class.
boolean isEnum() // Returns true if and only if this class was declared as an enum in the source code.
boolean isInterface() // Determines if this Class object represents an interface type.
T newInstance() // Deprecated. This method propagates any exception thrown by the nullary constructor, including a checked exception.
通过上述的几种方法可以看出,在反射操作中类或接口都是利用Class来进行包装的,同时利用Class类可以表示任意类、枚举、接口、数组等引用操作的类型。
而知道了实例化对象的类信息后,是要构建实例化对象的,此时使用newInstance方法可以构建实例化对象,而不用使用new关键字。
class A {
public A() {
System.out.println("Hello world");
}
}
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("A");
Object obj = cls.newInstance();
}
}
执行结果为:
Hello world
上面的代码中,没有使用new进行实例化对象构建,反而使用Class的newInstance方法进行实例化对象,此时默认调用类A的无参构造方法。同时从上面代码可以看到,newInstance方法返回的对象类型是Object,若有需要也可进行向下转型。
而至于为什么使用反射机制,因为使用这种反射机制重新实例化对象好像有点麻烦,这是因为使用new需要明确指明构造方法,尤其是构造方法重载。而使用反射机制则只需要将字符串形式的类名传入即可,然后对于反射得到的实例化对象进行向下转型即可。比如在工厂设计模式中,就可以使用反射机制将工厂类中的代码利用反射机制统一,而不用在每次新增接口子类时都需要更新工厂类的判断。
反射调用构造
之前利用Class的newInstance方法进行实例化对象,但是该操作只能使用无参构造。而如果想要使用有参构造方法时,就需要通过java.lang.reflect.Constructor类实现对象的反射实例化操作。
public Constructor<?>[] getConstructors() throws SecurityException
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException
返回的类型为:
public final class Constructor<T> extends Executable
利用这两种方法可以获得java.lang.reflect.Constructor,其常用方法为:
Class<?>[] getExceptionTypes() // Returns an array of Class objects that represent the types of exceptions declared to be thrown by the underlying executable represented by this object.
int getModifiers() // Returns the Java language modifiers for the executable represented by this object.
String getName() // Returns the name of this constructor, as a string.
int getParameterCount() // Returns the number of formal parameters (whether explicitly declared or implicitly declared or neither) for the executable represented by this object.
Class<?>[] getParameterTypes() // Returns an array of Class objects that represent the formal parameter types, in declaration order, of the executable represented by this object.
T newInstance(Object... initargs) // Uses the constructor represented by this Constructor object to create and initialize a new instance of the constructor's declaring class, with the specified initialization parameters.
这里newInstance方法定义了可变参数的形式,这样的话就可以先找到执行参数类型的构造方法,再利用该方法进行实例化对象的参数来调用指定的构造方法。
而从上面的方法来看,getModifiers方法和getParameterCount方法返回的都是int值,这是因为JAVA用数字的形式来表示修饰符,同时在java.lang.reflect.Modifer中也存在将数字转换为指定修饰符的toString方法。
import java.lang.reflect.Constructor;
class A {
private String name;
private int num;
public A() {
System.out.println("Hello world");
}
public A(String name) {
this.name = name;
}
public A(String name,int num) {
this.name = name;
this.num = num;
}
@Override
public String toString() {
return "Name is " + name + ", num is " + num;
}
}
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("A");
Constructor<?> con1 = cls.getConstructor(String.class, int.class);
Constructor<?> con2 = cls.getConstructor(String.class);
Object obj1 = con1.newInstance("Tom",10);
System.out.println(obj1);
Object obj2 = con2.newInstance("Tom");
System.out.println(obj2);
}
}
执行结果为:
Name is Tom, num is 10
Name is Tom, num is 0
上面的代码中,通过Class类获取了类本身,之后通过getConstructor分别获取两种构造方法,然后使用newInstance调用该构造方法。
反射调用方法
既然可以反射调用构造方法,那么反射调用方法也是可以的,其原理都是类似的。可以利用Class类取得方法:
Method getMethod(String name, Class<?>... parameterTypes) // Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object.
Method[] getMethods() // Returns an array containing Method objects reflecting all the public methods of the class or interface represented by this Class object, including those declared by the class or interface and those inherited from superclasses and superinterfaces.
返回的类型为:
public final class Method extends Executable
也就是说反射调用方法可以通过java.lang.reflect.Method类表示,Method类的常用方法有:
int getModifiers() // Returns the Java language modifiers for the executable represented by this object.
Class<?> getReturnType() // Returns a Class object that represents the formal return type of the method represented by this Method object.
int getParameterCount() // Returns the number of formal parameters (whether explicitly declared or implicitly declared or neither) for the executable represented by this object.
Class<?>[] getParameterTypes() // Returns an array of Class objects that represent the formal parameter types, in declaration order, of the executable represented by this object.
Object invoke(Object obj, Object... args) // Invokes the underlying method represented by this Method object, on the specified object with the specified parameters.
Class<?>[] getExceptionTypes() // Returns an array of Class objects that represent the types of exceptions declared to be thrown by the underlying executable represented by this object.
通过Method类的常用方法来看,方法和方法的基本格式都是一致的,比如返回值、参数、抛出异常。和之前通过newInstance方法调用构造方法一样,调用普通方法则需要通过invoke方法实现。
而在调用invoke方法中的第一个参数也是类的实例化对象,而该实例化对象也是Object。
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
class A {
private String name;
private int num;
public A() {
System.out.println("Hello world");
}
public A(String name) {
this.name = name;
}
public A(String name,int num) {
this.name = name;
this.num = num;
}
@Override
public String toString() {
return "Name is " + name + ", num is " + num;
}
public void func(String tmp) {
System.out.println("Func " + tmp);
}
}
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("A");
Constructor<?> con = cls.getConstructor(String.class, int.class);
Object obj = con.newInstance("Tom",10);
Method m = cls.getMethod("func",String.class);
m.invoke(obj,"abc");
}
}
执行结果为:
Func abc
从上面的代码可以看出,反射调用方法的逻辑和调用构造方法的逻辑都差不多。
反射调用成员
既然可以利用反射调用构造方法,也可以反射调用普通方法,那么反射调用成员应该也是可以的,逻辑也是相似的。Class类对此也存在相关的方法:
Field getDeclaredField(String name) // Returns a Field object that reflects the specified declared field of the class or interface represented by this Class object.
Field[] getDeclaredFields() // Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object.
Field getField(String name) // Returns a Field object that reflects the specified public member field of the class or interface represented by this Class object.
Field[] getFields() // Returns an array containing Field objects reflecting all the accessible public fields of the class or interface represented by this Class object.
返回类型为:
public final class Field extends AccessibleObject implements Member
而Field的相关方法为:
Class<?> getType() // Returns a Class object that identifies the declared type for the field represented by this Field object.
Object get(Object obj) // Returns the value of the field represented by this Field, on the specified object.
void set(Object obj, Object value) // Sets the field represented by this Field object on the specified object argument to the specified new value.
void setAccessible(boolean flag) // Set the accessible flag for this reflected object to the indicated boolean value.
这里比较特殊的方法是setAccessible,因为类中的属性大部分都是私有的,因此如果想要在外部进行访问,就应该首先取消该属性的封装。
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
class A {
private String name;
private int num;
public A() {
System.out.println("Hello world");
}
public A(String name) {
this.name = name;
}
public A(String name,int num) {
this.name = name;
this.num = num;
}
@Override
public String toString() {
return "Name is " + name + ", num is " + num;
}
public void func(String tmp) {
System.out.println("Func " + tmp);
}
}
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("A");
Constructor<?> con = cls.getConstructor(String.class, int.class);
Object obj = con.newInstance("Tom",10);
Method m = cls.getMethod("func",String.class);
m.invoke(obj,"abc");
Field f = cls.getDeclaredField("name");
f.setAccessible(true);
f.set(obj,"Jerry");
System.out.println(obj);
}
}
执行结果为:
Func abc
Name is Jerry, num is 10
上边的代码示例并不严谨,因为属性封装正是为了不被外界访问,这里直接进行修改赋值的操作可能会带来其它的风险。因此最好还是通过setter、getter方法进行获取修改赋值操作。