Java反射
Java反射
1 什么是反射
- Java反射机制是在运行状态中
- 对于任意一个类,都能知道这个类的所以属性和方法;
- 对于任何一个对象,都能够调用它的任何一个方法和属性;
- 这样动态获取新的以及动态调用对象方法的功能就叫做反射。
1 Class类
- Class可以说是反射能够实现的基础
- class关键字是在声明java类时使用的;而Class 是java JDK提供的一个类,完整路径为 java.lang.Class
- 对于每一种类,Java虚拟机都会初始化出一个Class类型的实例,每当我们编写并且编译一个新创建的类就会产生一个对应Class对象,并且这个Class对象会被保存在同名.class文件里。
- 当我们new一个新对象或者引用静态成员变量时,Java虚拟机(JVM)中的类加载器系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值。
构造器是私有的,只有JVM才可以调用这个构造函数创建Class的对象- 每个class(注意class是小写,代表普通类)类,无论创建多少个实例对象,在JVM中都对应同一个Class对象。
- Class是反射能够实现的基础的另一个原因是:Java反射包
java.lang.reflect
中的所有类都没有public构造方法,要想获得这些类实例,只能通过Class类获取。所以说如果想使用反射,必须得获得Class对象。
private Class(ClassLoader loader) {
classLoader = loader;
}
- 通过已有的类得到一个Class对象的几种方式:
1. 通过对象实例获取对应Class对象Object.getClass()--对于基本类型无法使用这种方法
//Returns the Class for String
Class c = "foo".getClass();
enum E { A, B }
//Returns the Class corresponding to the enumeration type E.
Class c = A.getClass();
byte[] bytes = new byte[1024];
//Returns the Class corresponding to an array with component type byte.
Class c = bytes.getClass();
Set<String> s = new HashSet<String>();
//Returns the Class corresponding to java.util.HashSet.
Class c = s.getClass();
2.通过类的类型获取Class对象,基本类型同样可以使用这种方法
//The `.class` syntax returns the Class corresponding to the type `boolean`.
Class c = boolean.class;
//Returns the Class for String
Class c = String.class;
3. 通过类的全限定名获取Class对象, 基本类型无法使用此方法
Class c = Class.forName("java.lang.String");//通过Class.forName()方法加载的类,采用的是系统类加载器
//对于数组比较特殊
Class cDoubleArray = Class.forName("[D"); //相当于double[].class
Class cStringArray = Class.forName("[[Ljava.lang.String;"); //相当于String[][].class
5.基本类型和void 类型的包装类可以使用TYPE字段获取
TYPE Field for Primitive Type Wrappers
Class c = Double.TYPE; //等价于 double.class.
Class c = Void.TYPE;
6. 另外还有一些反射方法可以获取Class对象,但前提是你已经获取了一个Class对象。
Class.getSuperclass()//获得给定类的父类Class
Class.getClasses()
Class.getDeclaredClasses()
Class.getDeclaringClass()
Class.getEnclosingClass()
java.lang.reflect.Field.getDeclaringClass()
java.lang.reflect.Method.getDeclaringClass()
java.lang.reflect.Constructor.getDeclaringClass()
一般博客都说用这三种
Class c1 = Test.class; //这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的()
Class c2 = test.getClass();// test是Test类的一个对象,这种方式是通过一个类的对象的getClass()方法获得的 (对于基本类型无法使用这种方法)
Class c3 = Class.forName("com.catchu.me.reflect.Test"); //这种方法是Class类调用forName方法,通过一个类的全量限定名获得(基本类型无法使用此方法)
例子:通过Class获取类修饰符和类型
11314732-2db9f6308ace3d6a.png
public class TestReflection {
private static final String TAG = "Reflection";
public void testReflection() {
Class<?> c = HashMap.class;
//获取类名
Log.d(TAG, "Class : " + c.getCanonicalName());
//获取类限定符
Log.d(TAG, "Modifiers : " + Modifier.toString(c.getModifiers()));
//获取类泛型信息
TypeVariable[] tv = c.getTypeParameters();
if (tv.length != 0) {
StringBuilder parameter = new StringBuilder("Parameters : ");
for (TypeVariable t : tv) {
parameter.append(t.getName());
parameter.append(" ");
}
Log.d(TAG, parameter.toString());
} else {
Log.d(TAG, " -- No Type Parameters --");
}
//获取类实现的所有接口
Type[] intfs = c.getGenericInterfaces();
if (intfs.length != 0) {
StringBuilder interfaces = new StringBuilder("Implemented Interfaces : ");
for (Type intf : intfs){
interfaces.append(intf.toString());
interfaces.append(" ");
}
Log.d(TAG, interfaces.toString());
} else {
Log.d(TAG, " -- No Implemented Interfaces --");
}
//获取类继承数上的所有父类
List<Class> l = new ArrayList<>();
printAncestor(c, l);
if (l.size() != 0) {
StringBuilder inheritance = new StringBuilder("Inheritance Path : ");
for (Class<?> cl : l){
inheritance.append(cl.getCanonicalName());
inheritance.append(" ");
}
Log.d(TAG, inheritance.toString());
} else {
Log.d(TAG, " -- No Super Classes --%n%n");
}
//获取类的注解(只能获取到 RUNTIME 类型的注解)
Annotation[] ann = c.getAnnotations();
if (ann.length != 0) {
StringBuilder annotation = new StringBuilder("Annotations : ");
for (Annotation a : ann){
annotation.append(a.toString());
annotation.append(" ");
}
Log.d(TAG, annotation.toString());
} else {
Log.d(TAG, " -- No Annotations --%n%n");
}
}
private static void printAncestor(Class<?> c, List<Class> l) {
Class<?> ancestor = c.getSuperclass();
if (ancestor != null) {
l.add(ancestor);
printAncestor(ancestor, l);
}
}
}
//打印结果如下
03-29 15:04:23.070 27826-27826/com.example.ming.testproject D/Reflection: Class : java.util.HashMap
03-29 15:04:23.070 27826-27826/com.example.ming.testproject D/Reflection: Modifiers : public
03-29 15:04:23.071 27826-27826/com.example.ming.testproject D/Reflection: Parameters : K V
03-29 15:04:23.071 27826-27826/com.example.ming.testproject D/Reflection: Implemented Interfaces : java.util.Map<K, V> interface java.lang.Cloneable interface java.io.Serializable
03-29 15:04:23.071 27826-27826/com.example.ming.testproject D/Reflection: Inheritance Path : java.util.AbstractMap java.lang.Object
03-29 15:04:23.071 27826-27826/com.example.ming.testproject D/Reflection: -- No Annotations --
2 Member
Reflection defines an interface java.lang.reflect.Member
which is implemented by java.lang.reflect.Field
, java.lang.reflect.Method
, and java.lang.reflect.Constructor
.
类成员主要包括构造函数
,变量
和方法
,Java中的操作基本都和这三者相关,而Member的这三个实现类就分别对应他们。
java.lang.reflect.Field
:对应类变量
java.lang.reflect.Method
:对应类方法
java.lang.reflect.Constructor
:对应类构造函数
反射就是通过这三个类才能在运行时改变对象状态。
突破java的权限检测
Java运行时会进行访问权限检查,private类型的变量无法进行直接访问
java.lang.reflect.AccessibleObject
AccessibleObject
为我们提供了一个方法 setAccessible(boolean flag),该方法的作用就是可以取消 Java 语言访问权限检查。所以任何继承AccessibleObject
的类的对象都可以使用该方法取消 Java 语言访问权限检查。
所以任何继承AccessibleObject
的类的对象都可以使用该方法取消 Java 语言访问权限检查。(final类型变量也可以通过这种办法访问)
public final class Field extends AccessibleObject implements Member
Field
、Method
和Constructor
都是继承AccessibleObject
2.0 例子
建一个测试类
public class Cat {
public static final String TAG = Cat.class.getSimpleName();
private String name;
@Deprecated
public int age;
public Cat(String name, int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public void eat(String food){
Log.d(TAG, "eat food " + food);
}
public void eat(String... foods){
StringBuilder s = new StringBuilder();
for(String food : foods){
s.append(food);
s.append(" ");
}
Log.d(TAG, "eat food " + s.toString());
}
public void sleep(){
Log.d(TAG, "sleep");
}
@Override
public String toString() {
return "name = " + name + " age = " + age;
}
}
2.1 Field
通过Field你可以访问给定对象的类变量,包括获取变量的类型、修饰符、注解、变量名、变量的值或者重新设置变量值,即使变量是private的。
获取Field
Class提供了4种方法获得给定类的Field。
getDeclaredField(String name)
获取指定的变量(只要是声明的变量都能获得,包括private)
getField(String name)
获取指定的变量(只能获得public的)
getDeclaredFields()
获取所有声明的变量(包括private)
getFields()
获取所有的public变量
获取变量类型、修饰符、注解
public void testField(){
Class c = Cat.class;
Field[] fields = c.getDeclaredFields();
for(Field f : fields){
StringBuilder builder = new StringBuilder();
//获取名称
builder.append("filed name = ");
builder.append(f.getName());
//获取类型
builder.append(" type = ");
builder.append(f.getType());
//获取修饰符
builder.append(" modifiers = ");
builder.append(Modifier.toString(f.getModifiers()));
//获取注解
Annotation[] ann = f.getAnnotations();
if (ann.length != 0) {
builder.append(" annotations = ");
for (Annotation a : ann){
builder.append(a.toString());
builder.append(" ");
}
} else {
builder.append(" -- No Annotations --");
}
Log.d(TAG, builder.toString());
}
}
打印结果:
filed name = age type = int modifiers = public annotations = @java.lang.Deprecated()
filed name = name type = class java.lang.String modifiers = private -- No Annotations --
filed name = TAG type = class java.lang.String modifiers = public static final -- No Annotations --
获取、设置变量值
通过反射获取并改变Cat的name和age.
public void testField(){
Cat cat = new Cat("Tom", 2);
Class c = cat.getClass();
try {
//注意获取private变量时,需要用getDeclaredField
Field fieldName = c.getDeclaredField("name");
Field fieldAge = c.getField("age");
fieldName.setAccessible(true);
//反射获取名字, 年龄
String name = (String) fieldName.get(cat);
int age = fieldAge.getInt(cat);
Log.d(TAG, "before set, Cat name = " + name + " age = " + age);
//反射重新set名字和年龄
fieldName.set(cat, "Timmy");
fieldAge.setInt(cat, 3);
Log.d(TAG, "after set, Cat " + cat.toString());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
2.2 Method
获取Method
Class依然提供了4种方法获取Method:
getDeclaredMethod(String name, Class... parameterTypes)
根据方法名获得指定的方法, 参数name为方法名,参数parameterTypes为方法的参数类型,如 getDeclaredMethod(“eat”, String.class)
getMethod(String name, Class... parameterTypes)
根据方法名获取指定的public方法,其它同上
getDeclaredMethods()
获取所有声明的方法
getMethods()
获取所有的public方法
- 注意:获取带参数方法时,如果参数类型错误会报NoSuchMethodException,对于参数是泛型的情况,泛型须当成Object处理(Object.class)
获取方法返回类型
getReturnType()
获取目标方法返回类型对应的Class对象
getGenericReturnType()
获取目标方法返回类型对应的Type对象
这两个方法有啥区别呢?
- getReturnType()返回类型为Class,getGenericReturnType()返回类型为Type; Class实现Type。
- 返回值为普通简单类型如Object, int, String等,getGenericReturnType()返回值和getReturnType()一样
例如public String function1()
那么各自返回值为:
getReturnType() : class java.lang.String
getGenericReturnType() : class java.lang.String
- 返回值为泛型
例如public T function2()
那么各自返回值为:
getReturnType() : class java.lang.Object
getGenericReturnType() : T
- 返回值为参数化类型
例如public Class function3()
那么各自返回值为:
getReturnType() : class java.lang.Class
getGenericReturnType() : java.lang.Class
其实反射中所有形如getGenericXXX()
的方法规则都与上面所述类似。
- 获取方法参数类型
getParameterTypes()
获取目标方法各参数类型对应的Class对象
getGenericParameterTypes()
获取目标方法各参数类型对应的Type对象
返回值为数组,它俩区别同上 “方法返回类型的区别” 。 - 获取方法声明抛出的异常的类型
getExceptionTypes()
获取目标方法抛出的异常类型对应的Class对象
getGenericExceptionTypes()
获取目标方法抛出的异常类型对应的Type对象
返回值为数组,区别同上 - 获取方法参数名称
.class文件中默认不存储方法参数名称,如果想要获取方法参数名称,需要在编译的时候加上-parameters
参数。(构造方法的参数获取方法同样)
//这里的m可以是普通方法Method,也可以是构造方法Constructor
//获取方法所有参数
Parameter[] params = m.getParameters();
for (int i = 0; i < params.length; i++) {
Parameter p = params[i];
p.getType(); //获取参数类型
p.getName(); //获取参数名称,如果编译时未加上`-parameters`,返回的名称形如`argX`, X为参数在方法声明中的位置,从0开始
p.getModifiers(); //获取参数修饰符
p.isNamePresent(); //.class文件中是否保存参数名称, 编译时加上`-parameters`返回true,反之flase
}
- 获取方法修饰符
方法与Filed等类似
method.getModifiers();
- 几个Method方法
method.isVarArgs()
//判断方法参数是否是可变参数
public Constructor<T> getConstructor(Class<?>... parameterTypes) //返回true
public Constructor<T> getConstructor(Class<?> [] parameterTypes) //返回flase
method.isSynthetic()
//判断是否是复合方法,个人理解复合方法是编译期间编译器生成的方法,并不是源代码中有的方法method.isBridge()
//判断是否是桥接方法,桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法。可以参考https://www.cnblogs.com/zsg88/p/7588929.html
- 通过反射调用方法
反射通过Method的invoke()
方法来调用目标方法。第一个参数为需要调用的目标类对象,如果方法为static的,则该参数为null。后面的参数都为目标方法的参数值,顺序与目标方法声明中的参数顺序一致。
public native Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
- 注意:如果方法是private的,可以使用method.setAccessible(true)方法绕过权限检查
Class<?> c = Cat.class;
try {
//构造Cat实例
Constructor constructor = c.getConstructor(String.class, int.class);
Object cat = constructor.newInstance( "Jack", 3);
//调用无参方法
Method sleep = c.getDeclaredMethod("sleep");
sleep.invoke(cat);
//调用定项参数方法
Method eat = c.getDeclaredMethod("eat", String.class);
eat.invoke(cat, "grass");
//调用不定项参数方法
//不定项参数可以当成数组来处理
Class[] argTypes = new Class[] { String[].class };
Method varargsEat = c.getDeclaredMethod("eat", argTypes);
String[] foods = new String[]{
"grass", "meat"
};
varargsEat.invoke(cat, (Object)foods);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
- 被调用的方法本身所抛出的异常在反射中都会以
InvocationTargetException
抛出。换句话说,反射调用过程中如果异常InvocationTargetException
抛出,说明反射调用本身是成功的,因为这个异常是目标方法本身所抛出的异常。
2.3 Constructor
这节主要介绍如何通过反射访问构造方法并通过构造方法构建新的对象。
获取构造方法
和Method一样,Class也为Constructor提供了4种方法获取
getDeclaredConstructor(Class... parameterTypes)
获取指定构造函数,参数parameterTypes为构造方法的参数类型
getConstructor(Class... parameterTypes)
获取指定public构造函数,参数parameterTypes为构造方法的参数类型
getDeclaredConstructors()
获取所有声明的构造方法
getConstructors()
获取所有的public构造方法
构造方法的名称、限定符、参数、声明的异常等获取方法都与Method类似,请参照Method
创建对象
通过反射有两种方法可以创建对象:
java.lang.reflect.Constructor.newInstance()
Class.newInstance()
一般来讲,我们优先使用第一种方法;那么这两种方法有何异同呢?
Class.newInstance()
仅可用来调用无参的构造方法;Constructor.newInstance()
可以调用任意参数的构造方法Class.newInstance()
会将构造方法中抛出的异常不作处理原样抛出;Constructor.newInstance()
会将构造方法中抛出的异常都包装成InvocationTargetException
抛出。Class.newInstance()
需要拥有构造方法的访问权限;Constructor.newInstance()
可以通过setAccessible(true)
方法绕过访问权限访问private构造方法。
例子在Method一节已经写过,这里直接截取过来
Class<?> c = Cat.class;
try {
Constructor constructor = c.getConstructor(String.class, int.class);
Cat cat = (Cat) constructor.newInstance( "Jack", 3);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
注意:反射不支持自动封箱,传入参数时要小心(自动封箱是在编译期间的,而反射在运行期间)
2.4 数组和枚举
数组和枚举也是对象,但是在反射中,对数组和枚举的创建、访问和普通对象有那么一丢丢的不同,所以Java反射为数组和枚举提供了一些特定的API接口。
2.4.1 数组
数组类型
数组类型:数组本质是一个对象,所以它也有自己的类型。
数组类型:数组本质是一个对象,所以它也有自己的类型。
例如对于int[] intArray,数组类型为class [I。数组类型中的[个数代表数组的维度,例如[代表一维数组,[[代表二维数组;[后面的字母代表数组元素类型,I代表int,一般为类型的首字母大写(long类型例外,为J)。
class [B //byte类型一维数组
class [S //short类型一维数组
class [I //int类型一维数组
class [C //char类型一维数组
class [J //long类型一维数组,J代表long类型,因为L被引用对象类型占用了
class [F //float类型一维数组
class [D //double类型一维数组
class [Lcom.dada.Season //引用类型一维数组
class [[Ljava.lang.String //引用类型二维数组
//获取一个变量的类型
Class<?> c = field.getType();
//判断该变量是否为数组
if (c.isArray()) {
//获取数组的元素类型
c.getComponentType()
}
创建和初始化数组
Java反射为我们提供了java.lang.reflect.Array
类用来创建和初始化数组。
//创建数组, 参数componentType为数组元素的类型,后面不定项参数的个数代表数组的维度,参数值为数组长度
Array.newInstance(Class<?> componentType, int... dimensions)
//设置数组值,array为数组对象,index为数组的下标,value为需要设置的值
Array.set(Object array, int index, int value)
//获取数组的值,array为数组对象,index为数组的下标
Array.get(Object array, int index)
例子,用反射创建int[] array = new int[]{1, 2}
Object array = Array.newInstance(int.class, 2);
Array.setInt(array , 0, 1);
Array.setInt(array , 1, 2);
注意:反射支持对数据自动加宽,但不允许数据narrowing(变窄?真难翻译)。意思是对于上述set方法,你可以在int类型数组中 set short类型数据,但不可以set long类型数据,否则会报IllegalArgumentException。
- 多维数组
Java反射没有提供能够直接访问多维数组元素的API,但你可以把多维数组当成数组的数组处理。
Object matrix = Array.newInstance(int.class, 2, 2);
Object row0 = Array.get(matrix, 0);
Object row1 = Array.get(matrix, 1);
Array.setInt(row0, 0, 1);
Array.setInt(row0, 1, 2);
Array.setInt(row1, 0, 3);
Array.setInt(row1, 1, 4);
或者
Object matrix = Array.newInstance(int.class, 2);
Object row0 = Array.newInstance(int.class, 2);
Object row1 = Array.newInstance(int.class, 2);
Array.setInt(row0, 0, 1);
Array.setInt(row0, 1, 2);
Array.setInt(row1, 0, 3);
Array.setInt(row1, 1, 4);
Array.set(matrix, 0, row0);
Array.set(matrix, 1, row1);
2.4.2 枚举
枚举隐式继承自java.lang.Enum
,Enum继承自Object,所以枚举本质也是一个类,也可以有成员变量,构造方法,方法等;对于普通类所能使用的反射方法,枚举都能使用;另外java反射额外提供了几个方法为枚举服务。
枚举隐式继承自java.lang.Enum
,Enum继承自Object,所以枚举本质也是一个类,也可以有成员变量,构造方法,方法等;对于普通类所能使用的反射方法,枚举都能使用;另外java反射额外提供了几个方法为枚举服务。
Class.isEnum()
Indicates whether this class represents an enum type
Class.getEnumConstants()
Retrieves the list of enum constants defined by the enum in the order they're declared
java.lang.reflect.Field.isEnumConstant()
Indicates whether this field represents an element of an enumerated type
2.5 其它方法
注解中常用的方法:
Annotation[] annotations = (Annotation[]) class1.getAnnotations();//获取class对象的所有注解
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);//获取class对象指定注解
Type genericSuperclass = class1.getGenericSuperclass();//获取class对象的直接超类的
Type Type[] interfaceTypes = class1.getGenericInterfaces();//获取class对象的所有接口的type集合
获取Class对象其它信息的方法:
boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型
boolean isArray = class1.isArray();//判断是否是集合类
boolean isAnnotation = class1.isAnnotation();//判断是否是注解类
boolean isInterface = class1.isInterface();//判断是否是接口类
boolean isEnum = class1.isEnum();//判断是否是枚举类
boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰
String className = class1.getName();//获取class名字 包含包名路径
Package aPackage = class1.getPackage();//获取class的包信息
String simpleName = class1.getSimpleName();//获取class类名
int modifiers = class1.getModifiers();//获取class访问权限
Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类
Class<?> declaringClass = class1.getDeclaringClass();//外部类
ClassLoader ClassLoader = class1.getClassLoader() 返回类加载器
getSuperclass():获取某类所有的父类
getInterfaces():获取某类所有实现的接口
2.6 静态元素
静态的类,方法,字段和实例类,方法,字段完全不一样,因为它无需初始化类就可以直接使用。
3 反射缺点
- 性能问题。因为反射是在运行时而不是在编译时,所有不会利用到编译优化,同时因为是动态生成,因此,反射操作的效率要比那些非反射操作低得多。
- 安全问题。使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了。
- 代码问题。由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
4. 反射的意义
1.增加程序的灵活性,避免将程序写死到代码里。
例:定义了一个接口,实现这个接口的类有20个,程序里用到了这个实现类的地方有好多地方,如果不使用配置文件手写的话,代码的改动量很大,因为每个地方都要改而且不容易定位,如果你在编写之前先将接口与实现类的写在配置文件里,下次只需改配置文件,利用反射(java API已经封装好了,直接用就可以用 Class.newInstance())就可完成。
2.代码简洁,提高代码的复用率,外部调用方便
package cn.yonyong.reflection.testdemo;
interface Fruit { //水果接口
public void eat() ; //吃水果
}
class Apple implements Fruit{ //定义苹果
public void eat() {
System.out.println("**吃苹果。");
}
}
class Orange implements Fruit{
public void eat() {
System.out.println("**吃橘子。");
}
}
class Factory{
public static Fruit getInstance(String className){
Fruit fruit = null ;
try{
fruit = (Fruit) Class.forName(className).newInstance() ;
}catch(Exception e ){
e.printStackTrace() ;
}
return fruit ;
}
}
public class FactoryDemo{
public static void main(String args[]){
//通过工厂类取得接口实例,传入完整的包.类名称
Fruit f = Factory.getInstance("cn.yonyong.reflection.testdemo.Apple") ;
if(f!=null){ //判断是否取得接口实例
f.eat() ;
}
}
}
如果不用反射,那么我们如果再加一个西瓜类,就得在Factory里判断,每添加一个类都要修改一次Factory,但用了反射只用在调用的时候传入完整的类名就可完成。结果:用反射,修改一处代码;不用反射,修改两处代码。
3.对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
反射的缺点
性能问题
1.使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
2.反射包括了一些动态类型,所以JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程序中使用反射。
使用反射会模糊程序内部逻辑
程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
安全限制
使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了
内部暴露
由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
Java反射可以访问和修改私有成员变量,那封装成private还有意义么?
既然小偷可以访问和搬走私有成员家具,那封装成防盗门还有意义么?这是一样的道理,并且Java从应用层给我们提供了安全管理机制——安全管理器,每个Java应用都可以拥有自己的安全管理器,它会在运行阶段检查需要保护的资源的访问权限及其它规定的操作权限,保护系统免受恶意操作攻击,以达到系统的安全策略。所以其实反射在使用时,内部有安全控制,如果安全设置禁止了这些,那么反射机制就无法访问私有成员。
反射是否真的会让你的程序性能降低?
1.反射大概比直接调用慢50~100倍,但是需要你在执行100万遍的时候才会有所感觉
2.判断一个函数的性能,你需要把这个函数执行100万遍甚至1000万遍
3.如果你只是偶尔调用一下反射,请忘记反射带来的性能影响
4.如果你需要大量调用反射,请考虑缓存。
5.你的编程的思想才是限制你程序性能的最主要的因素