如何让python class methodmethod只允许使用用类对象来调用

Objective-C Runtime 运行时之三:方法与消息
招聘信息:
前面我们讨论了Runtime中对,及对。这一章,我们就要开始讨论Runtime中最有意思的一部分:消息处理机制。我们将详细讨论消息的发送及消息的转发。不过在讨论消息之前,我们先来了解一下与方法相关的一些内容。基础数据类型SELSEL又叫选择器,是表示一个方法的selector的指针,其定义如下:typedef&struct&objc_selector&*SEL;objc_selector结构体的详细定义没有在头文件中找到。方法的selector用于表示运行时方
法的名字。Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL。如下
代码所示:SEL&sel1&=&@selector(method1);
NSLog(@"sel&:&%p",&sel1);上面的输出为:&18:40:07.518&RuntimeTest[]&sel&:&0x两个类之间,不管它们是父类与子类的关系,还是之间没有这种关系,只要方法名相同,那么方法的SEL就是一样的。每一个方法都对应着一个SEL。所以在
Objective-C同一个类(及类的继承体系)中,不能存在2个同名的方法,即使参数类型不同也不行。相同的方法只能对应一个SEL。这也就导致
Objective-C在处理相同方法名且参数个数相同但类型不同的方法方面的能力很差。如在某个类中定义以下两个方法:-&(void)setWidth:(int)
-&(void)setWidth:(double)当然,不同的类可以拥有相同的selector,这个没有问题。不同类的实例对象执行相同的selector时,会在各自的方法列表中去根据selector去寻找自己对应的IMP。工程中的所有的SEL组成一个Set集合,Set的特点就是唯一,因此SEL是唯一的。因此,如果我们想到这个方法集合中查找某个方法时,只需要去
找到这个方法对应的SEL就行了,SEL实际上就是根据方法名hash化了的一个字符串,而对于字符串的比较仅仅需要比较他们的地址就可以了,可以说速度
上无语伦比!!但是,有一个问题,就是数量增多会增大hash冲突而导致的性能下降(或是没有冲突,因为也可能用的是perfect
hash)。但是不管使用什么样的方法加速,如果能够将总量减少(多个方法可能对应同一个SEL),那将是最犀利的方法。那么,我们就不难理解,为什么
SEL仅仅是函数名了。本质上,SEL只是一个指向方法的指针(准确的说,只是一个根据方法名hash化了的KEY值,能唯一代表一个方法),它的存在只是为了加快方法的查询速度。这个查找过程我们将在下面讨论。我们可以在运行时添加新的selector,也可以在运行时获取已存在的selector,我们可以通过下面三种方法来获取SEL:1. sel_registerName函数2. Objective-C编译器提供的@selector()3. NSSelectorFromString()方法IMPIMP实际上是一个函数指针,指向方法实现的首地址。其定义如下:id&(*IMP)(id,&SEL,&...)这个函数使用当前CPU架构实现的标准的C调用约定。第一个参数是指向self的指针(如果是实例方法,则是类实例的内存地址;如果是类方法,则是指向元类的指针),第二个参数是方法选择器(selector),接下来是方法的实际参数列表。前面介绍过的SEL就是为了查找方法的最终实现IMP的。由于每个方法对应唯一的SEL,因此我们可以通过SEL方便快速准确地获得它所对应的
IMP,查找过程将在下面讨论。取得IMP后,我们就获得了执行这个方法代码的入口点,此时,我们就可以像调用普通的C语言函数一样来使用这个函数指针
了。通过取得IMP,我们可以跳过Runtime的消息传递机制,直接执行IMP指向的函数实现,这样省去了Runtime消息传递过程中所做的一系列查找操作,会比直接向对象发送消息高效一些。Method介绍完SEL和IMP,我们就可以来讲讲Method了。Method用于表示类定义中的方法,则定义如下:typedef&struct&objc_method&*M
struct&objc_method&{
&&&&SEL&method_name&&&&&&&&&&&&&&&&&OBJC2_UNAVAILABLE;&&//&方法名
&&&&char&*method_types&&&&&&&&&&&&&&&&&&OBJC2_UNAVAILABLE;
&&&&IMP&method_imp&&&&&&&&&&&&&&&&&&&&&&OBJC2_UNAVAILABLE;&&//&方法实现
}我们可以看到该结构体中包含一个SEL和IMP,实际上相当于在SEL和IMP之间作了一个映射。有了SEL,我们便可以找到对应的IMP,从而调用方法的实现代码。具体操作流程我们将在下面讨论。objc_method_descriptionobjc_method_description定义了一个Objective-C方法,其定义如下:struct&objc_method_description&{&SEL&&char&*&};方法相关操作函数Runtime提供了一系列的方法来处理与方法相关的操作。包括方法本身及SEL。本节我们介绍一下这些函数。方法方法操作相关函数包括下以://&调用指定方法的实现
id&method_invoke&(&id&receiver,&Method&m,&...&);
//&调用返回一个数据结构的方法的实现
void&method_invoke_stret&(&id&receiver,&Method&m,&...&);
//&获取方法名
SEL&method_getName&(&Method&m&);
//&返回方法的实现
IMP&method_getImplementation&(&Method&m&);
//&获取描述方法参数和返回值类型的字符串
const&char&*&method_getTypeEncoding&(&Method&m&);
//&获取方法的返回值类型的字符串
char&*&method_copyReturnType&(&Method&m&);
//&获取方法的指定位置参数的类型字符串
char&*&method_copyArgumentType&(&Method&m,&unsigned&int&index&);
//&通过引用返回方法的返回值类型字符串
void&method_getReturnType&(&Method&m,&char&*dst,&size_t&dst_len&);
//&返回方法的参数的个数
unsigned&int&method_getNumberOfArguments&(&Method&m&);
//&通过引用返回方法指定位置参数的类型字符串
void&method_getArgumentType&(&Method&m,&unsigned&int&index,&char&*dst,&size_t&dst_len&);
//&返回指定方法的方法描述结构体
struct&objc_method_description&*&method_getDescription&(&Method&m&);
//&设置方法的实现
IMP&method_setImplementation&(&Method&m,&IMP&imp&);
//&交换两个方法的实现
void&method_exchangeImplementations&(&Method&m1,&Method&m2&);● method_invoke函数,返回的是实际实现的返回值。参数receiver不能为空。这个方法的效率会比method_getImplementation和method_getName更快。● method_getName函数,返回的是一个SEL。如果想获取方法名的C字符串,可以使用sel_getName(method_getName(method))。● method_getReturnType函数,类型字符串会被拷贝到dst中。● method_setImplementation函数,注意该函数返回值是方法之前的实现。方法选择器选择器相关的操作函数包括://&返回给定选择器指定的方法的名称
const&char&*&sel_getName&(&SEL&sel&);
//&在Objective-C&Runtime系统中注册一个方法,将方法名映射到一个选择器,并返回这个选择器
SEL&sel_registerName&(&const&char&*str&);
//&在Objective-C&Runtime系统中注册一个方法
SEL&sel_getUid&(&const&char&*str&);
//&比较两个选择器
BOOL&sel_isEqual&(&SEL&lhs,&SEL&rhs&);● sel_registerName函数:在我们将一个方法添加到类定义时,我们必须在Objective-C Runtime系统中注册一个方法名以获取方法的选择器。方法调用流程在Objective-C中,消息直到运行时才绑定到方法实现上。编译器会将消息表达式[receiver message]转化为一个消息函数的调用,即objc_msgSend。这个函数将消息接收者和方法名作为其基础参数,如以下所示:objc_msgSend(receiver,&selector)如果消息中还有其它参数,则该方法的形式如下所示:objc_msgSend(receiver,&selector,&arg1,&arg2,&...)这个函数完成了动态绑定的所有事情:1. 首先它找到selector对应的方法实现。因为同一个方法可能在不同的类中有不同的实现,所以我们需要依赖于接收者的类来找到的确切的实现。2. 它调用方法实现,并将接收者对象及方法的所有参数传给它。3. 最后,它将实现返回的值作为它自己的返回值。消息的关键在于我们前面章节讨论过的结构体objc_class,这个结构体有两个字段是我们在分发消息的关注的:1. 指向父类的指针2. 一个类的方法分发表,即methodLists。当我们创建一个新对象时,先为其分配内存,并初始化其成员变量。其中isa指针也会被初始化,让对象可以访问类及类的继承体系。下图演示了这样一个消息的基本框架:当消息发送给一个对象时,objc_msgSend通过对象的isa指针获取到类的结构体,然后在方法分发表里面查找方法的selector。如果
没有找到selector,则通过objc_msgSend结构体中的指向父类的指针找到其父类,并在父类的分发表里面查找方法的selector。依
此,会一直沿着类的继承体系到达NSObject类。一旦定位到selector,函数会就获取到了实现的入口点,并传入相应的参数来执行方法的具体实
现。如果最后没有定位到selector,则会走消息转发流程,这个我们在后面讨论。为了加速消息的处理,运行时系统缓存使用过的selector及对应的方法的地址。这点我们在前面讨论过,不再重复。隐藏参数objc_msgSend有两个隐藏参数:1. 消息接收对象2. 方法的selector这两个参数为方法的实现提供了调用者的信息。之所以说是隐藏的,是因为它们在定义方法的源代码中没有声明。它们是在编译期被插入实现代码的。虽然这些参数没有显示声明,但在代码中仍然可以引用它们。我们可以使用self来引用接收者对象,使用_cmd来引用选择器。如下代码所示:-&strange
&&&&id&&target&=&getTheReceiver();
&&&&SEL&method&=&getTheMethod();
&&&&if&(&target&==&self&||&method&==&_cmd&)
&&&&&&&&return&
&&&&return&[target&performSelector:method];
}当然,这两个参数我们用得比较多的是self,_cmd在实际中用得比较少。获取方法地址Runtime中方法的动态绑定让我们写代码时更具灵活性,如我们可以把消息转发给我们想要的对象,或者随意交换一个方法的实现等。不过灵活性的提
升也带来了性能上的一些损耗。毕竟我们需要去查找方法的实现,而不像函数调用来得那么直接。当然,方法的缓存一定程度上解决了这一问题。我们上面提到过,如果想要避开这种动态绑定方式,我们可以获取方法实现的地址,然后像调用函数一样来直接调用它。特别是当我们需要在一个循环内频繁地调用一个特定的方法时,通过这种方式可以提高程序的性能。NSObject类提供了methodForSelector:方法,让我们可以获取到方法的指针,然后通过这个指针来调用实现代码。我们需要将methodForSelector:返回的指针转换为合适的函数类型,函数参数和返回值都需要匹配上。我们通过以下代码来看看methodForSelector:的使用:void&(*setter)(id,&SEL,&BOOL);
setter&=&(void&(*)(id,&SEL,&BOOL))[target
&&&&methodForSelector:@selector(setFilled:)];
for&(&i&=&0&;&i&<&1000&;&i++&)
&&&&setter(targetList[i],&@selector(setFilled:),&YES);这里需要注意的就是函数指针的前两个参数必须是id和SEL。当然这种方式只适合于在类似于for循环这种情况下频繁调用同一方法,以提高性能的情况。另外,methodForSelector:是由Cocoa运行时提供的;它不是Objective-C语言的特性。消息转发当一个对象能接收一个消息时,就会走正常的方法调用流程。但如果一个对象无法接收指定消息时,又会发生什么事呢?默认情况下,如果是以
message]的方式调用方法,如果object无法响应message消息时,编译器会报错。但如果是以perform…的形式来调用,则需要等到运
行时才能确定object是否能接收message消息。如果不能,则程序崩溃。通常,当我们不能确定一个对象是否能接收某个消息时,会先调用respondsToSelector:来判断一下。如下代码所示:if&([self&respondsToSelector:@selector(method)])&{
&&&&[self&performSelector:@selector(method)];
}不过,我们这边想讨论下不使用respondsToSelector:判断的情况。这才是我们这一节的重点。当一个对象无法接收某一消息时,就会启动所谓”消息转发(message forwarding)“机制,通过这一机制,我们可以告诉对象如何处理未知的消息。默认情况下,对象接收到未知的消息,会导致程序崩溃,通过控制台,我们可以看到以下异常信息:-[SUTRuntimeMethod&method]:&unrecognized&selector&sent&to&instance&0x
***&Terminating&app&due&to&uncaught&exception&&#39;NSInvalidArgumentException&#39;,&reason:&&#39;-[SUTRuntimeMethod&method]:&unrecognized&selector&sent&to&instance&0x&#39;这段异常信息实际上是由NSObject的”doesNotRecognizeSelector”方法抛出的。不过,我们可以采取一些措施,让我们的程序执行特定的逻辑,而避免程序的崩溃。消息转发机制基本上分为三个步骤:1. 动态方法解析2. 备用接收者3. 完整转发下面我们详细讨论一下这三个步骤。动态方法解析对象在接收到未知的消息时,首先会调用所属类的类方法+resolveInstanceMethod:(实例方法)或
者+resolveClassMethod:(类方法)。在这个方法中,我们有机会为该未知消息新增一个”处理方法”“。不过使用该方法的前提是我们已经
实现了该”处理方法”,只需要在运行时通过class_addMethod函数动态添加到类里面就可以了。如下代码所示:void&functionForMethod1(id&self,&SEL&_cmd)&{
&&&NSLog(@"%@,&%p",&self,&_cmd);
+&(BOOL)resolveInstanceMethod:(SEL)sel&{
&&&&NSString&*selectorString&=&NSStringFromSelector(sel);
&&&&if&([selectorString&isEqualToString:@"method1"])&{
&&&&&&&&class_addMethod(self.class,&@selector(method1),&(IMP)functionForMethod1,&"@:");
&&&&return&[super&resolveInstanceMethod:sel];
}不过这种方案更多的是为了实现@dynamic属性。备用接收者如果在上一步无法处理消息,则Runtime会继续调以下方法:-&(id)forwardingTargetForSelector:(SEL)aSelector如果一个对象实现了这个方法,并返回一个非nil的结果,则这个对象会作为消息的新接收者,且消息会被分发到这个对象。当然这个对象不能是self自身,否则就是出现无限循环。当然,如果我们没有指定相应的对象来处理aSelector,则应该调用父类的实现来返回结果。使用这个方法通常是在对象内部,可能还有一系列其它对象能处理该消息,我们便可借这些对象来处理消息并返回,这样在对象外部看来,还是由该对象亲自处理了这一消息。如下代码所示:@interface&SUTRuntimeMethodHelper&:&NSObject
-&(void)method2;
@implementation&SUTRuntimeMethodHelper
-&(void)method2&{
&&&&NSLog(@"%@,&%p",&self,&_cmd);
#pragma&mark&-
@interface&SUTRuntimeMethod&()&{
&&&&SUTRuntimeMethodHelper&*_
@implementation&SUTRuntimeMethod
+&(instancetype)object&{
&&&&return&[[self&alloc]&init];
-&(instancetype)init&{
&&&&self&=&[super&init];
&&&&if&(self&!=&nil)&{
&&&&&&&&_helper&=&[[SUTRuntimeMethodHelper&alloc]&init];
&&&&return&
-&(void)test&{
&&&&[self&performSelector:@selector(method2)];
-&(id)forwardingTargetForSelector:(SEL)aSelector&{
&&&&NSLog(@"forwardingTargetForSelector");
&&&&NSString&*selectorString&=&NSStringFromSelector(aSelector);
&&&&//&将消息转发给_helper来处理
&&&&if&([selectorString&isEqualToString:@"method2"])&{
&&&&&&&&return&_
&&&&return&[super&forwardingTargetForSelector:aSelector];
@end这一步合适于我们只想将消息转发到另一个能处理该消息的对象上。但这一步无法对消息进行处理,如操作消息的参数和返回值。完整消息转发如果在上一步还不能处理未知消息,则唯一能做的就是启用完整的消息转发机制了。此时会调用以下方法:-&(void)forwardInvocation:(NSInvocation&*)anInvocation运行时系统会在这一步给消息接收者最后一次机会将消息转发给其它对象。对象会创建一个表示消息的NSInvocation对象,把与尚未处理的消息
有关的全部细节都封装在anInvocation中,包括selector,目标(target)和参数。我们可以在forwardInvocation
方法中选择将消息转发给其它对象。forwardInvocation:方法的实现有两个任务:1. 定位可以响应封装在anInvocation中的消息的对象。这个对象不需要能处理所有未知消息。2. 使用anInvocation作为参数,将消息发送到选中的对象。anInvocation将会保留调用结果,运行时系统会提取这一结果并将其发送到消息的原始发送者。不过,在这个方法中我们可以实现一些更复杂的功能,我们可以对消息的内容进行修改,比如追回一个参数等,然后再去触发消息。另外,若发现某个消息不应由本类处理,则应调用父类的同名方法,以便继承体系中的每个类都有机会处理此调用请求。还有一个很重要的问题,我们必须重写以下方法:-&(NSMethodSignature&*)methodSignatureForSelector:(SEL)aSelector消息转发机制使用从这个方法中获取的信息来创建NSInvocation对象。因此我们必须重写这个方法,为给定的selector提供一个合适的方法签名。完整的示例如下所示:-&(NSMethodSignature&*)methodSignatureForSelector:(SEL)aSelector&{
&&&&NSMethodSignature&*signature&=&[super&methodSignatureForSelector:aSelector];
&&&&if&(!signature)&{
&&&&&&&&if&([SUTRuntimeMethodHelper&instancesRespondToSelector:aSelector])&{
&&&&&&&&&&&&signature&=&[SUTRuntimeMethodHelper&instanceMethodSignatureForSelector:aSelector];
&&&&return&
-&(void)forwardInvocation:(NSInvocation&*)anInvocation&{
&&&&if&([SUTRuntimeMethodHelper&instancesRespondToSelector:anInvocation.selector])&{
&&&&&&&&[anInvocation&invokeWithTarget:_helper];
}NSObject的forwardInvocation:方法实现只是简单调用了doesNotRecognizeSelector:方法,它不会转发任何消息。这样,如果不在以上所述的三个步骤中处理未知消息,则会引发一个异常。从某种意义上来讲,forwardInvocation:就像一个未知消息的分发中心,将这些未知的消息转发给其它对象。或者也可以像一个运输站一样将所有未知消息都发送给同一个接收对象。这取决于具体的实现。消息转发与多重继承回过头来看第二和第三步,通过这两个方法我们可以允许一个对象与其它对象建立关系,以处理某些未知消息,而表面上看仍然是该对象在处理消息。通过这
种关系,我们可以模拟“多重继承”的某些特性,让对象可以“继承”其它对象的特性来处理一些事情。不过,这两者间有一个重要的区别:多重继承将不同的功能
集成到一个对象中,它会让对象变得过大,涉及的东西过多;而消息转发将功能分解到独立的小的对象中,并通过某种方式将这些对象连接起来,并做相应的消息转
发。不过消息转发虽然类似于继承,但NSObject的一些方法还是能区分两者。如respondsToSelector:和isKindOfClass:只能用于继承体系,而不能用于转发链。便如果我们想让这种消息转发看起来像是继承,则可以重写这些方法,如以下代码所示:-&(BOOL)respondsToSelector:(SEL)aSelector&&&{
&&&&&&&if&(&[super&respondsToSelector:aSelector]&)
&&&&&&&&&&&&&&&&return&YES;&&&&&
&&&&&&&else&{
&&&&&&&&&&&&&&&&&/*&Here,&test&whether&the&aSelector&message&can
&&&&&&&&&&&&&&&&&&*&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&*&be&forwarded&to&another&object&and&whether&that&&
&&&&&&&&&&&&&&&&&&*&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&*&object&can&respond&to&it.&Return&YES&if&it&can.&&
&&&&&&&&&&&&&&&&&&*/&&&&&&
&&&&&&&return&NO;&&
}小结在此,我们已经了解了Runtime中消息发送和转发的基本机制。这也是Runtime的强大之处,通过它,我们可以为程序增加很多动态的行为,虽
然我们在实际开发中很少直接使用这些机制(如直接调用objc_msgSend),但了解它们有助于我们更多地去了解底层的实现。其实在实际的编码过程中,我们也可以灵活地使用这些机制,去实现一些特殊的功能,如hook操作等。
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量6265点击量4590点击量4565点击量3500点击量2909点击量2409点击量2381点击量2351点击量2333
&2016 Chukong Technologies,Inc.
京公网安备89python中:如何让classmethod只允许使用用类对象来调用
我的图书馆
python中:如何让classmethod只允许使用用类对象来调用
原blog链接:
我们知道在python中使用classmethod可以把一个方法处理为类方法,这样就可以直接使用类对象来调用它了。但是,它也有一个不算问题的问题,就是也可以使用实例来调用,比如:
class&A(object):&&&&&&@classmethod&&&&&&def&p(cls):&&&&&&&&&&print&'call&p()'&&a&=&A()&&A.p()&&a.p()&&但
是有时,我们可能希望这个方法只能被类对象来调用,而不能使用实例来调用。为什么会有这样的需求?举一个例子,数据库操作中,Model有
delete()和remove()方法。其中delete()是用在实例上的,删除一个记录,而remove()是定义为classmethod的,删
除整个表的所有记录。但是有时在实例上误写为record.remove(),这样的结果是会将表全部删除,而不是只删除这一条记录。所以这里
remove()具有一定的危险性。所以我想有这么一种限制,即类方法只能用类对象来调用。当然,并不是所有的类方法都需要这样,只对有破坏性或危险性的
方法这样处理。那么怎么做呢?可以通过写了一个descriptor的类,可以干这件事,同时它可以像classmethod一样作为decorator
来使用。代码如下:
class&classonlymethod(object):&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&def&__init__(self,&func):&&&&&&&&&&self._func&=&func&&&&&&&&def&__get__(self,&model_instance,&model_class):&&&&&&&&&&if&model_instance&is&not&None:&&&&&&&&&&&&&&raise&AttributeError("This&method&can&only&be&called&with&class&object.")&&&&&&&&&&return&super(classonlymethod,&self).__get__(model_instance,&model_class)&&它使用了descriptor的特性,能过定义 __get__ 方法来区分类对象调用还是实例对象调用。
关于descriptor,可以参考:
TA的最新馆藏[转]&[转]&[转]&[转]&[转]&[转]&
喜欢该文的人也喜欢欢迎加入我们,一同切磋技术。 &
用户名: &&&
密 码: &
共有 3710 人关注过本帖
标题:使用Class对象实例化一个A类,并列出A类的全部构造方法、成员变量和方法的名 ...
等 级:论坛游侠
帖 子:101
专家分:106
结帖率:77.78%
&&已结贴√
&&问题点数:20&&回复次数:4&&&
使用Class对象实例化一个A类,并列出A类的全部构造方法、成员变量和方法的名称
使用Class对象实例化一个A类,并列出A类的全部构造方法、成员变量和方法的名称:
import java.lang.reflect.*;
&&&&&&&&x = 12;
&&&&&&&&y=12.901f;
&&&&&&&&z=0.123456;&&&&&&&&
&&& A(int x, float y, double z) {
&&&&&&&&this.x =
&&&&&&&&this.y =
&&&&&&&&this.z =
&&& public double getSum() {
&&&&&&&&return x+y+z;
&&& public void setX(int x) {
&&&&&&&&this.x =
&&& public void setY(int y) {
&&&&&&&&this.y =
&&& public void setZ(double z) {
&&&&&&&&this.z =
public class Ex4_3 {
&&& public static void main(String args[]) {
&&&&&&&&try {
&&&&&&&&&&&&Class cs = 【代码1】;&&& //&&获得A类的Class对象;
&&&&&&&&&&&&A a = 【代码2】;&&& // 利用A类的Class对象实例化A类的对象;
&&&&&&&&&&&&System.out.println(&a对象中成员变量的和:& + a.getSum());
&&&&&&&&&&&&String className = 【代码3】;&&& // 获得A类的名称;
&&&&&&&&&&&&Constructor[] con = 【代码4】;&&& // 获得A类的构造方法;
&&&&&&&&&&&&Field[] field = 【代码5】;&&& // 获得A类的成员变量;
&&&&&&&&&&&&Method[] method =【代码6】;&&& // 获得A类的方法;
&&&&&&&&&&&&System.out.println(&类的名字:& + className);
&&&&&&&&&&&&System.out.println(&类中有如下的构造方法:&);
&&&&&&&&&&&&for(int i = 0; i & con. i++) {
&&&&&&&&&&&&&&& System.out.println(con[i].toString());
&&&&&&&&&&&&}
&&&&&&&&&&&&System.out.println(&类中有如下的成员变量:&);
&&&&&&&&&&&&for(int i = 0; i & field. i++) {
&&&&&&&&&&&&&&& System.out.println(field[i].toString());
&&&&&&&&&&&&}
&&&&&&&&&&&&System.out.println(&类中有如下的方法:&);
&&&&&&&&&&&&for(int i = 0; i & method. i++) {
&&&&&&&&&&&&&&& System.out.println(method[i].toString());
&&&&&&&&&&&&}
&&&&&&&&} catch(Exception e) {
&&&&&&&&&&&&System.out.println(e.getStackTrace());
搜索更多相关主题的帖子:
&&&&&&&&&&
等 级:版主
威 望:54
帖 子:2508
专家分:6424
&&得分:20&
import java.lang.reflect.*;
&&&&&&&&x = 12;
&&&&&&&&y=12.901f;
&&&&&&&&z=0.123456;&&&&&&&&
&&& A(int x, float y, double z) {
&&&&&&&&this.x =
&&&&&&&&this.y =
&&&&&&&&this.z =
&&& public double getSum() {
&&&&&&&&return x+y+z;
&&& public void setX(int x) {
&&&&&&&&this.x =
&&& public void setY(int y) {
&&&&&&&&this.y =
&&& public void setZ(double z) {
&&&&&&&&this.z =
public class Ex4_3 {
&&& public static void main(String args[]) {
&&&&&&&&try {
&&&&&&&&&&&&Class cs = A.class;&&& //&&获得A类的Class对象;
&&&&&&&&&&&&A a = (A)cs.newInstance();&&& // 利用A类的Class对象实例化A类的对象;
&&&&&&&&&&&&System.out.println(&a对象中成员变量的和:& + a.getSum());
&&&&&&&&&&&&String className = cs.getName();&&& // 获得A类的名称;
&&&&&&&&&&&&Constructor[] con = cs.getConstructors();&&& // 获得A类的构造方法;
&&&&&&&&&&&&Field[] field = cs.getFields();&&& // 获得A类的成员变量;
&&&&&&&&&&&&Method[] method = cs.getMethods();&&& // 获得A类的方法;
&&&&&&&&&&&&System.out.println(&类的名字:& + className);
&&&&&&&&&&&&System.out.println(&类中有如下的构造方法:&);
&&&&&&&&&&&&for(int i = 0; i & con. i++) {
&&&&&&&&&&&&&&& System.out.println(con[i].toString());
&&&&&&&&&&&&}
&&&&&&&&&&&&System.out.println(&类中有如下的成员变量:&);
&&&&&&&&&&&&for(int i = 0; i & field. i++) {
&&&&&&&&&&&&&&& System.out.println(field[i].toString());
&&&&&&&&&&&&}
&&&&&&&&&&&&System.out.println(&类中有如下的方法:&);
&&&&&&&&&&&&for(int i = 0; i & method. i++) {
&&&&&&&&&&&&&&& System.out.println(method[i].toString());
&&&&&&&&&&&&}
&&&&&&&&} catch(Exception e) {
&&&&&&&&&&&&System.out.println(e.getStackTrace());
你的优秀和我的人生无关!!!!
    我要过的,是属于我自己的生活~~~
等 级:论坛游侠
帖 子:101
专家分:106
回复 2楼 lampeter123
我查了一下jdk帮助文档知道:
Constructor&T& Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。
Field Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
Method Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
但是还是很不明白。能不能讲一下、
等 级:版主
威 望:54
帖 子:2508
专家分:6424
以下是引用guchao2009在 19:33:25的发言:
我查了一下jdk帮助文档知道:
Constructor Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。
Field Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
Method Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
但是还是很不明白。能不能讲一下、 Struts, Spring框架用到反射,反射的作用就是不用修改原程序,只要改配置文件就能达到修改程序的效果
一句话来概括反射机制就是:动态地获取类的一切信息,并利用这些信息做一些你想做的事情
Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说&自审&,并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。 Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。
JavaBean 是 reflection 的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。
1. 一个简单的例子
考虑下面这个简单的例子,让我们看看 reflection 是如何工作的。
import java.lang.reflect.*;&&
public class DumpMethods {&&
&&&public static void main(String args[]) {&&
&&&&&&try {&&
&&&&&&&&&&&Class c = Class.forName(&java.util.Stack&);&&
&&&&&&&&&&&Method m[] = c.getDeclaredMethods();&&
&&&&&&&&&&&&
&&&&&&&&&&&for (int i = 0; i & m. i++)&&
&&&&&&&&&&&&&&&System.out.println(m[i].toString());&&
&&&&&&catch (Throwable e){&&
&&&&&&&&&&&&System.err.println(e);&&
它的结果输出为:
public synchronized java.lang.Object java.util.Stack.pop()
public java.lang.Object java.util.Stack.push(java.lang.Object)
public boolean java.util.Stack.empty()
public synchronized java.lang.Object java.util.Stack.peek()
public synchronized int java.util.Stack.search(java.lang.Object)
这样就列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。
这个程序使用 Class.forName 载入指定的类,然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。
2.开始使用 Reflection
用于 reflection 的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。
下面就是获得一个 Class 对象的方法之一:
Class c = Class.forName(&java.lang.String&);
这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:
Class c = int. 或者 Class c = Integer.TYPE;
它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。
第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。
一旦取得这个信息,就可以进行第三步了——使用 reflection API 来操作这些信息,如下面这段代码:
Class c = Class.forName(&java.lang.String&);&&
Method m[] = c.getDeclaredMethods();&&
System.out.println(m[0].toString());&&
它将以文本方式打印出 String 中定义的第一个方法的原型。
在下面的例子中,这三个步骤将为使用 reflection 处理特殊应用程序提供例证。
模拟 instanceof 操作符
得到类信息之后,通常下一个步骤就是解决关于 Class 对象的一些基本的问题。例如,Class.isInstance 方法可以用于模拟 instanceof 操作符:
class S {&&
public class IsInstance {&&
&&&public static void main(String args[]) {&&
&&&&&&try {&&
&&&&&&&&&&&Class cls = Class.forName(&S&);&&
&&&&&&&&&&&boolean b1 = cls.isInstance(new Integer(37));&&
&&&&&&&&&&&System.out.println(b1);&&
&&&&&&&&&&&boolean b2 = cls.isInstance(new S());&&
&&&&&&&&&&&System.out.println(b2);&&
&&&&&&catch (Throwable e) {&&
&&&&&&&&&&&System.err.println(e);&&
在这个例子中创建了一个S 类的 Class 对象,然后检查一些对象是否是S的实例。Integer(37) 不是,但 new S()是。
3.找出类的方法
找出一个类中定义了些什么方法,这是一个非常有价值也非常基础的 reflection 用法。下面的代码就实现了这一用法:
import java.lang.reflect.*;&&&
public class Method1 {&&
&&&private int f1(Object p, int x) throws NullPointerException {&&
&&&if (p == null)&&
&&&&&&throw new NullPointerException();&&
&&&public static void main(String args[]) {&&
&&&&&& try {&&
&&&&&&&&&&&Class cls = Class.forName(&Method1&);&&
&&&&&&&&&&&Method methlist[] = cls.getDeclaredMethods();&&
&&&&&&&&&&&for (int i = 0; i & methlist. i++) {&&
&&&&&&&&&&&&&&&Method m = methlist[i];&&
&&&&&&&&&&&&&&&System.out.println(&name = & + m.getName());&&
&&&&&&&&&&&&&&&System.out.println(&decl class = & + m.getDeclaringClass());&&
&&&&&&&&&&&&&&&Class pvec[] = m.getParameterTypes();&&
&&&&&&&&&&&&&&&for (int j = 0; j & pvec. j++)&&
&&&&&&&&&&&&&&&&&& System.out.println(&param #& + j + & & + pvec[j]);&&
&&&&&&&&&&&&&&&Class evec[] = m.getExceptionTypes();&&
&&&&&&&&&&&&&&&for (int j = 0; j & evec. j++)&&
&&&&&&&&&&&&&&&&&& System.out.println(&exc #& + j + & & + evec[j]);&&
&&&&&&&&&&&&&&&System.out.println(&return type = & + m.getReturnType());&&
&&&&&&&&&&&&&&&System.out.println(&-----&);&&
&&&&&&&&&&&}&&
&&&&&& }&&
&&&&&& catch (Throwable e) {&&
&&&&&&&&&&&System.err.println(e);&&
&&&&&& }&&
这个程序首先取得 method1 类的描述,然后调用 getDeclaredMethods 来获取一系列的 Method 对象,它们分别描述了定义在类中的每一个方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 来代替 getDeclaredMethods,你还能获得继承来的各个方法的信息。
取得了 Method 对象列表之后,要显示这些方法的参数类型、异常类型和返回值类型等就不难了。这些类型是基本类型还是类类型,都可以由描述类的对象按顺序给出。
输出的结果如下:
name = f1&&
decl class = class method1&&
param #0 class java.lang.Object&&
param #1 int&&
exc #0 class java.lang.NullPointerException&&
return type = int
name = main&&
decl class = class method1&&
param #0 class [Ljava.lang.S&&
return type = void
4.获取构造器信息
获取类构造器的用法与上述获取方法的用法类似,如:
import java.lang.reflect.*;
public class Constructor1 {&&
&&&public Constructor1() {&&
&&&protected Constructor1(int i, double d) {&&
&&&public static void main(String args[]) {&&
&&&&&&try {&&
&&&&&&&&&&&Class cls = Class.forName(&Constructor1&);&&
&&&&&&&&&&&Constructor ctorlist[] = cls.getDeclaredConstructors();&&
&&&&&&&&&&&for (int i = 0; i & ctorlist. i++) {&&
&&&&&&&&&&&&&&Constructor ct = ctorlist[i];&&
&&&&&&&&&&&&&&System.out.println(&name = & + ct.getName());&&
&&&&&&&&&&&&&&System.out.println(&decl class = & + ct.getDeclaringClass());&&
&&&&&&&&&&&&&&Class pvec[] = ct.getParameterTypes();&&
&&&&&&&&&&&&&&for (int j = 0; j & pvec. j++)&&
&&&&&&&&&&&&&&&&&System.out.println(&param #& + j + & & + pvec[j]);&&
&&&&&&&&&&&&&&Class evec[] = ct.getExceptionTypes();&&
&&&&&&&&&&&&&&for (int j = 0; j & evec. j++)&&
&&&&&&&&&&&&&&&&&System.out.println(&exc #& + j + & & + evec[j]);&&
&&&&&&&&&&&&&&System.out.println(&-----&);&&
&&&&&&&&&&&}&&
&&&&&&catch (Throwable e) {&&
&&&&&&&&&&&System.err.println(e);&&
这个例子中没能获得返回类型的相关信息,那是因为构造器没有返回类型。
这个程序运行的结果是:
name = Constructor1
decl class = class Constructor1
param #0 int
param #1 double
name = Constructor1
decl class = class Constructor1
5.获取类的字段(域)
找出一个类中定义了哪些数据字段也是可能的,下面的代码就在干这个事情:
import java.lang.reflect.*;&&&
public class Field1 {&&
&&&public static final int i = 37;&&
&&&String s = &testing&;&&&
&&&public static void main(String args[]) {&&
&&&&&&try {&&
&&&&&&&&&&&Class cls = Class.forName(&Field1&);&&
&&&&&&&&&&&Field fieldlist[] = cls.getDeclaredFields();&&
&&&&&&&&&&&for (int i = 0; i & fieldlist. i++) {&&
&&&&&&&&&&&&&&Field fld = fieldlist[i];&&
&&&&&&&&&&&&&&System.out.println(&name = & + fld.getName());&&
&&&&&&&&&&&&&&System.out.println(&decl class = & + fld.getDeclaringClass());&&
&&&&&&&&&&&&&&System.out.println(&type = & + fld.getType());&&
&&&&&&&&&&&&&&int mod = fld.getModifiers();&&
&&&&&&&&&&&&&&System.out.println(&modifiers = & + Modifier.toString(mod));&&
&&&&&&&&&&&&&&System.out.println(&-----&);&&
&&&&&&&&&&&}&&
&&&&&&catch (Throwable e) {&&
&&&&&&&&&&&System.err.println(e);&&
这个例子和前面那个例子非常相似。例中使用了一个新东西 Modifier,它也是一个 reflection 类,用来描述字段成员的修饰语,如“private int”。这些修饰语自身由整数描述,而且使用 Modifier.toString 来返回以“官方”顺序排列的字符串描述 (如“static”在“final”之前)。这个程序的输出是:
decl class = class Field1
type = double
modifiers = private
decl class = class Field1
type = int
modifiers = public static final
decl class = class Field1
type = class java.lang.String
modifiers =&&
和获取方法的情况一下,获取字段的时候也可以只取得在当前类中申明了的字段信息 (getDeclaredFields),或者也可以取得父类中定义的字段 (getFields) 。
6.根据方法的名称来执行方法
文本到这里,所举的例子无一例外都与如何获取类的信息有关。我们也可以用 reflection 来做一些其它的事情,比如执行一个指定了名称的方法。下面的示例演示了这一操作:
import java.lang.reflect.*;&&
public class Method2 {&&
&&&public int add(int a, int b) {&&
&&&&&&return a +&&
&&&public static void main(String args[]) {&&
&&&&&&try {&&
&&&&&&&&&&&Class cls = Class.forName(&Method2&);&&
&&&&&&&&&&&Class partypes[] = new Class[2];&&
&&&&&&&&&&&partypes[0] = Integer.TYPE;&&
&&&&&&&&&&&partypes[1] = Integer.TYPE;&&
&&&&&&&&&&&Method meth = cls.getMethod(&add&, partypes);&&
&&&&&&&&&&&Method2 methobj = new Method2();&&
&&&&&&&&&&&Object arglist[] = new Object[2];&&
&&&&&&&&&&&arglist[0] = new Integer(37);&&
&&&&&&&&&&&arglist[1] = new Integer(47);&&
&&&&&&&&&&&Object retobj = meth.invoke(methobj, arglist);&&
&&&&&&&&&&&Integer retval = (Integer)&&
&&&&&&&&&&&System.out.println(retval.intValue());&&
&&&&&&catch (Throwable e) {&&
&&&&&&&&&&&System.err.println(e);&&
假如一个程序在执行的某处的时候才知道需要执行某个方法,这个方法的名称是在程序的运行过程中指定的 (例如,JavaBean 开发环境中就会做这样的事),那么上面的程序演示了如何做到。
上例中,getMethod用于查找一个具有两个整型参数且名为 add 的方法。找到该方法并创建了相应的Method 对象之后,在正确的对象实例中执行它。执行该方法的时候,需要提供一个参数列表,这在上例中是分别包装了整数 37 和 47 的两个 Integer 对象。执行方法的返回的同样是一个 Integer 对象,它封装了返回值 84。
7.创建新的对象
对于构造器,则不能像执行方法那样进行,因为执行一个构造器就意味着创建了一个新的对象 (准确的说,创建一个对象的过程包括分配内存和构造对象)。所以,与上例最相似的例子如下:
import java.lang.reflect.*;&&&
public class Constructor2 {&&
&&&public Constructor2() {&&
&&&public Constructor2(int a, int b) {&&
&&&&&&System.out.println(&a = & + a + & b = & + b);&&
&&&public static void main(String args[]) {&&
&&&&&&try {&&
&&&&&&&&&&&Class cls = Class.forName(&Constructor2&);&&
&&&&&&&&&&&Class partypes[] = new Class[2];&&
&&&&&&&&&&&partypes[0] = Integer.TYPE;&&
&&&&&&&&&&&partypes[1] = Integer.TYPE;&&
&&&&&&&&&&&Constructor ct = cls.getConstructor(partypes);&&
&&&&&&&&&&&Object arglist[] = new Object[2];&&
&&&&&&&&&&&arglist[0] = new Integer(37);&&
&&&&&&&&&&&arglist[1] = new Integer(47);&&
&&&&&&&&&&&Object retobj = ct.newInstance(arglist);&&
&&&&&&catch (Throwable e) {&&
&&&&&&&&&&&System.err.println(e);&&
根据指定的参数类型找到相应的构造函数并执行它,以创建一个新的对象实例。使用这种方法可以在程序运行时动态地创建对象,而不是在编译的时候创建对象,这一点非常有价值。
8.改变字段(域)的值
reflection 的还有一个用处就是改变对象数据字段的值。reflection 可以从正在运行的程序中根据名称找到对象的字段并改变它,下面的例子可以说明这一点:
import java.lang.reflect.*;&&&
public class Field2 {&&
&&&public static void main(String args[]) {&&
&&&&&&try {&&
&&&&&&&&&&&Class cls = Class.forName(&Field2&);&&
&&&&&&&&&&&Field fld = cls.getField(&d&);&&
&&&&&&&&&&&Field2 f2obj = new Field2();&&
&&&&&&&&&&&System.out.println(&d = & + f2obj.d);&&
&&&&&&&&&&&fld.setDouble(f2obj, 12.34);&&
&&&&&&&&&&&System.out.println(&d = & + f2obj.d);&&
&&&&&&catch (Throwable e) {&&
&&&&&&&&&&&System.err.println(e);&&
这个例子中,字段 d 的值被变为了 12.34。
9.使用数组
本文介绍的 reflection 的最后一种用法是创建的操作数组。数组在 Java 语言中是一种特殊的类类型,一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的:
import java.lang.reflect.*;&&&
public class Array1 {&&
&&&public static void main(String args[]) {&&
&&&&&&try {&&
&&&&&&&&&&&Class cls = Class.forName(&java.lang.String&);&&
&&&&&&&&&&&Object arr = Array.newInstance(cls, 10);&&
&&&&&&&&&&&Array.set(arr, 5, &this is a test&);&&
&&&&&&&&&&&String s = (String) Array.get(arr, 5);&&
&&&&&&&&&&&System.out.println(s);&&
&&&&&&catch (Throwable e) {&&
&&&&&&&&&&&System.err.println(e);&&
例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,最后将这个字符串从数组中取得并打印了出来。
下面这段代码提供了一个更复杂的例子:
import java.lang.reflect.*;&&&
public class Array2 {&&
&&&public static void main(String args[]) {&&
&&&&&&int dims[] = new int[]{5, 10, 15};&&
&&&&&&Object arr = Array.newInstance(Integer.TYPE, dims);&&
&&&&&&Object arrobj = Array.get(arr, 3);&&
&&&&&&Class cls = arrobj.getClass().getComponentType();&&
&&&&&&System.out.println(cls);&&
&&&&&&arrobj = Array.get(arrobj, 5);&&
&&&&&&Array.setInt(arrobj, 10, 37);&&
&&&&&&int arrcast[][][] = (int[][][])&&
&&&&&&System.out.println(arrcast[3][5][10]);&&
例中创建了一个 5 x 10 x 15 的整型数组,并为处于 [3][5][10] 的元素赋了值为 37。注意,多维数组实际上就是数组的数组,例如,第一个 Array.get 之后,arrobj 是一个 10 x 15 的数组。进而取得其中的一个元素,即长度为 15 的数组,并使用 Array.setInt 为它的第 10 个元素赋值。
注意创建数组时的类型是动态的,在编译时并不知道其类型。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/justinavril/archive//2873664.aspx
[ 本帖最后由 lampeter123 于
21:19 编辑 ]
你的优秀和我的人生无关!!!!
    我要过的,是属于我自己的生活~~~
等 级:论坛游侠
帖 子:101
专家分:106
版权所有,并保留所有权利。
Powered by , Processed in 0.060663 second(s), 7 queries.
Copyright&, BCCN.NET, All Rights Reserved}

我要回帖

更多关于 class.getmethod参数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信