反射机制(大大提高了程序的扩展性、且使用户更简单的使用)Java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意一个方法和属性。
这种动态获取信息以及动态调用方法的功能成为java语言的反射机制。即能动态获取一个类中的信息,称为java的反射机制。可以理解为对类的解剖。
反射机制可以用来:
1、在运行中分析类的能力。 2、在运行中查看对象 3、实现通用的数组操作代码
4、利用Method对象,这个对象很像C++中的函数指针。
二、Class对象
一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。
在程序的运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。可以通过专门的Java类访问这些信息。保存这些信息的类被称为Class。Object类中的getClass()方法将会返回一个Class类型的实例。
获取Class方法:
最常用的Class方法是getName。这个方法将返回类的名字。(若类在一个包里,包名也作为类名的一部分)
例:Class c1 = d.getClass();
还能调用静态方法forName获得类名对应Class对象Class c1 = Class.forName(“className")
如果类名保存在字符串中,并可在运行中改变,就可以使用这个方法。此方法只能在
className是类名或接口名时才能够执行。否则,forName方法将抛出一个chenkedexception
异常。无论何时使用这个方法,都应该提供一个异常处理器。
获得Class类对象的第三种方法,如果T是任意的Java类型,T.class代表匹配的类对象。示例:
publicclass ReflectDemo{
public static void main(String[] args)
{
getClassObject_1();
}
// 1、Object类中的getClass()方法。用这种方式,必须明确具体的类,并创建对象。麻烦。
public static void getClassObject_1(){
Person p = new Person();
Class clazz = p.getClass();
Person p1 = new Person();
Class clazz1 = p.getClass();
System.out.println(clazz==clazz1); // 相等,因为都是依赖一个class建立的。
}
// 2、任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。
此方法相对简单,但仍要明确类中的静态成员(.class)。还是不够扩展
public static void getClassObject_2(){
Class clazz = Person.class;
Class clazz1 = Person.class;
System.out.println(clazz==clazz1); // 相等
}
// 3、只要通过给定的类的字符串名称就可以获取该类,更为扩展。可使用Class类中的方法完成。
public static void getClassObject_3()throws ClassNotFoundException{
String className = “cn.itcast.bean.Person”; // 注意路径名要打完整。即类的全名,和导包无关系。
Class clazz =Class.forName(className);
System.out.println(clazz);
}
}
三、反射相关方法
虚拟机为每个类型管理一个Class对象。故可以利用==运算符实现两个类对象比较操作。
getName方法在应用于数组类型的时候会返回一个很奇怪的名字。
newInstance()方法可以用来创建一个类的实例。 e.getClass().newInstance() 创建了一个与e具有相同类类型的实例。
newInstance方法调用默认的构造器(无参数)初始化新创建的对象。若这个类没有默认的构造器,会抛出异常。
forName与newInstance配合起来可以根据存储在字符串中的类名创建一个对象
例如:String s = “java.util.Date”; Object m = Class.forName(s).newInstance();若希望按名称创建的类的构造器提供参数,必须使用Constructor类的newInstance方法。
示例:
publicclass ReflectDemo2{
public static void main(String[] args)
{
}
publicstatic void createNewObject() throws ClassNotFoundExceptio,InstantiationException,IllegalAccessException
{
// 早期:new时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存。并创建该字
节码文件对象,并接着创建该字节文件的对应的Person对象。(写法简单)
cn.itcast.bean.Person p = newcn.itcast.bean.Person();
//现在
String name = “cn.itcast.bean.Person”;
// 找寻该名称类文件,并接着创建该字节文件的对应的Person对象。(扩展性强)
Classclazz = Class.forName(name);
// 如何产生该类的对象呢
Object obj = clazz.newInstance() // 创建实例同样会执行初始化即调用构造参数。
// 注意,当类中没有无参数构造时,或无参构造私有时,创建空参对象时,会产生异常。
}
public static void createNewObject_2()throws Exception
{
// cn.itcast.bean.Person p =new cn.itcast.bean.Person(“小强”,39);
// 当获取指定名称对应类中的所体现的对象时,而该对象初始化不使用空参数构造该怎么办呢?
既然是通过指定的构造函数进行对象的初始化,所以应该先获取到该构造函数。通过字节码文件
对象即可完成。该方法是:getConstructors(); 返回所有公有的构造函数的数组。
getDeclaredConstructors(class<?>paramType);方法:
返回该class对象的所有构造函数,包括私有,保护默认等。
String name = “cn.itcast.bean.Person”;
Class clazz =Class.forName(name);
Constructor constructor = clazz.getConstructor(String.class,int.class);
// 通过该构造器对象的newInstance方法进行对象的初始化
Object obj =constructor.newInstance(“小明”,38);
}
}
四、利用反射分析类的能力
getName、getType、getModifiers方法
在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。这三个类都有一
个叫做getName的方法,用来返回项目的名称。Field类有一个getType方法,用来返回描述域所属类型的Class对象。
Method和Constructor类有能够报告参数类型的方法,Method类还有一个可以报告返回类型的方法。这三个类还有一
个叫做getModifiers的方法,它将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用状况。可以
使用Modifier类中的isPublic、isPrivate或isFinal判断方法或构造器是否是public、private或final。还可以利用
Modifier.toString方法将修饰符打印出来。
getFields、getMethods和getConstructors方法
Class类中的getFields、getMethods和getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中
包括超类的共有成员。Class类的getDeclareField、 getDeclareMethods和getDeclaredConstructors方法将分别返回
类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。
setAccessible方法
反射机制的默认行为受限于Java的访问控制(私有)。如果一个Java程序没有受到安全管理器的控制,就可以覆盖访
问控制。为了达到这个目的,需要调用Field、Method或Constructor对象的setAccessible方法
setAccessible方法是AccessibleObject类中的一个方法,它是Field、Meethod和Constructor
类的公共超类。这个特性是为调试、持久存储和相似机制提供的。
invoke方法
在Method类中有一个invoke方法,它允许调用包装在当前Method对象中的方法。
invoke方法的签名是:Object invoke(Object obj,Object…args); 第一个参数是隐式参数,其余的对象提供了显式参数。(Java SE 5.0以前的版本,必须传递一个对象数组,如果没有显式参数就传递一个null)。
示例1、publicclass ReflectDemo
{
public static void main(String[] args)
{
getFieldDemo();
}
// 获取字节码文件中的字段
public static void getFieldDemo()
{
Class clazz = Class.forName(“cn.itcast.bean.Person”);
// Field field =clazz.getField(“age”); // 不能拿到私有的
Field field = clazz.getDeclaredField(“age”); // 只获取本类,包含私有。
// 对私有字段的访问权限取消检查,暴力访问
field.setAccessible(true);
Object obj =clazz.newInstance();
field.set(obj,89);
Object o = file.get(obj); // 获取值所属的对象
System.out.println(o);
}
}
示例2、获取Class中的方法publicclass ReflectDemo4
{
public ReflectDemo4()
{
}
public static void main(String[] args)
{
getMethodDemo();
}
public static void getMethodDemo_3()throws Exception
{
Class clazz = Class.forName(“cn.itcast.bean.Person”); // 给出类名
// 只获取指定方法名的方法
Method method =clazz.getDeclaredMethod(“paramMethod”,String.class,int.class); // 给出方法名参数
Object obj =clazz.newInstance();
method.invoke(obj,”小强”,89); // 运行方法
}
public static void getMethodDemo_2()throws Exception
{
Class clazz = Class.forName(“cn.itcast.bean.Person”);
Method method = clazz.getDeclaredMethod(“show”,null); // 只获取指定方法名的方法
Object obj = clazz.newInstance();
Constructor constructor =clazz.getConstructor(String.class,int.class);
Object obj =constructor.newInstance(“小名”,37);
method.invoke(obj,null);
}
示例3、获取指定Class中的公共函数public static void getMethodDemo()
{
Class clazz = Class.forName(“cn.itcast.bean.Person”);
// Method[] methods =clazz.getMethods(); // 获取的都是public方法
Method[] methods =clazz.getDeclaredMethods(); // 只获取本类中所有方法,包含私有
for(Method method : methods)
{ System.out.println(method); }
}
}
五、反射方法详述
java.lang.Class 1.0
Field[]getField() 返回一个包含Field对象的数组,这些对象记录了此类或其超类的共有域
Field[]getDeclaredFields() 这些对象记录此类或其超类的所有域若没有返回长度0的数组
Method[]getMethods() 返回包含Method对象的数组,(公有方法)
Method[]getDeclareMethods() 返回这个类或接口的全部方法,不包括由超类继承的方法
Constructor[]getConstructors() 返回包含Constructor对象的数组,公有构造器
Constructor[]getDeclaredConstructors() 返回全部构造器数组
116、java.lang.reflect.Constructor 1.1
ClassgetDeclaringClass() 返回一个用于描述类中定义的构造器、方法或域的Class对象
Class[]getExceptionTypes() 返回一个用于描述方法抛出的异常类型的Class对象数组
intgetModifiers() 返回一个用于描述构造器、方法或域的修饰符的整型数值。
StringgetName() 返回一个用于描述构造器、方法或域名的字符串。
Class[]getParameterTypes() 返回一个用于描述参数类型的Class对象数组。
ClassgetReturnType() 返回一个用于描述返回类型的Class对象。
117、java.lang.reflect.Modifier 1.1
static String toString(intmodifiers) 返回对应modifiers中位设置的修饰符的字符串表示。
static booleanisAbstract(int modifiers)
static booleanisFinal(int modifiers)
static booleanisInterface(int modifiers)
static booleanisNative(int modifiers) ?
static booleanisPrivate(int modifiers)
static booleanisProtected(int modifiers)
static booleanisPublic(int modifiers)
static booleanisStatic(int modifiers)
static booleanisStrict(int modifiers) ?
static booleanisSynchronized(int modifiers) ?
static booleanisVolatile(int modifiers) ?
这些方法检测方法名中对应的修饰符在modifiers值中的位
查看任意对象的数据域名称和类型:
1、获得对应Class对象 2、通过Class对象调用getDeclaredFields______________________________
java.lang.reflect.AccessibleObject 1.2
voidsetAccessible(boo flag)为反射对象设置可访问标志flag为true表明屏蔽java语言检测
boolean isAccessible() 返回反射对象的可访问标志的值
static voidsetAccessible(AccessibleObject[] array,boolean flag)
是一种设置对象数组可访问标志的快捷方法。
______________________________
java.lang.Class 1.1
Field getField(Stringname)
Field[]getField() 返回指定名称的公有域,或包含所有域的数组。
FieldgetDeclaredField(String name)
Field[]getDeclaredFields() 返回类中声明的给定名称的域,或者包含声明的全部域的数组。
______________________________
java.lang.reflect.Field 1.1
Objectget(Object obj) 返回obj对象中用Field对象表示的域值
void set(Objectobj,Object newValue) 用一个新值设置Obj对象中Field对象表示的域。
______________________________
java.lang.reflect包中的Array类允许动态地创建数组。 Array类静态方法newInstance:
ObjectnewArray = Array.newInstance(componentType,newLength),参数一个是数组的元素类型,一个是数组长度。
______________________________
可以通过调用Array.getLength(a)获得数组的长度,也可以通过Array类的静态getLength方法的返回值得到任意数组的长度。而要获得新数组元素类型,就需要进行以下工作:
1、首先获得a数组的类对象 2、确认他是一个数组
3、使用Class类的getComponentType方法确定数组对应的类型。
______________________________
java.lang.reflect.Array 1.1
static Objectget(Object array,int index)
static xxxgetxxx(Object array,int index)
(xxx是任意一种基本数据类型)这些方法将返回存储在给定位置上的给定数组的内容。
static voidset(Object array,int index,Object newValue)
staticsetxxx(Object array,int index,xxx newValue)
(xxx是任意一种基本数据类型)这些方法将一个新值存储到给定位置上的给定数组中。
static intgetLength(Object array) 返回数组长度
static ObjectnewInstance(Class componentType,int length)
static ObjectnewInstance(Class componentType,int[] lengths)
返回一个具有给定类型、给定维数的新数组
______________________________
六、反射应用
反射练习:电脑运行
packagecn.itcast.reflect.test;
publicclass MainBoard
{
public void run();
{ System.out.println(“mainboardrun…”); }
// public void useSound(SoundCard c)
// { c.open(); c.close(); }
public void usePCI(PCI p)
{
if(p!=null)
{
p.open(); p.close();
}
}
}
packagecn.itcast.reflect.test;
publicclass ReflectTest
{
public static void main(String[] args)
{
MainBoard mb = newMainBoard();
mb.run();
mb.usePCI(null);
// 每次添加一个设备都需要修改代码传递一个新创建的对象
mb.usePCI(new SoundCard());
// 能不能不修改代码就可以完成这个动作
// 不用new来完成,而是只获取其class文件在内部实现创建对象动作。
File configFile = new File(“pci.properties”); // 将属性文件封装成文件
Properties prop = newProperties();
FileInputStream fis = newFileInputStream(configFile); // 文件输入流
prop.load(fis);
for(intx=0;x<prop.size();x++)
{
String pciName =prop.getProperty(“pci”+(x+1));
Class clazz =Class.forName(pciName); // 用Class去加载这个pci子类。
PCI p = (PCI)clazz.newInstance();
mb.userPCI(p);
}
fis.close();
}
}
扩展:
packagecn.itcast.reflect.test;
publicclass SoundCard implements PCI
{
pulibc void open()
{ System.out.println(“soundopen” }
pulibc void close()
{ System.out.println(“soundclose” }
}
扩展性好需要接口。
packagecn.itcast.reflect.test;
publicinterface PCI
{
pulibc void open();
pulibc void close();
}
创建配置文件:
pci.properties文件
pci1= packagecn.itcast.reflect.test.SoundCard;
pci1= packagecn.itcast.reflect.test.NetCard;
// 在引入一个网卡
packagecn.itcast.reflect.test;
publicclass NetCard implements PCI
{
pulibc void open()
{ System.out.println(“NetCardopen” }
pulibc void close()
{ System.out.println(“NetCardclose” }
}