声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权;凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记。
Class管理类的其他方法
类的结构如下:
这里将类的结构划分开来了,m表示方法method,f表示属性filed,没有标识返回值的方法表示是构造器
绿色的锁表示权限公开public,红色的锁表示权限私有private
接下来将使用反射机制对类的构造(f,m)全部操作一遍,不论属性或方法,私有或其他权限!
代码示例:
第一件事情,加载类:
Class cls = Class.forName("com.kaige123.daomu.Dog");
可以做的操作:
实例化对象:
1:Object obj = cls.newInstance();
得到的是Dog类对象
2:查看这个实例化对象的方法,如果是有参构造器怎么解决?
解决办法:
Constructor constructor = cls.getConstructor(String.class, String.class, int.class);
Object object = constructor.newInstance("小黑狗", "旺财", 1);
getConstructor搜索到某个构造器,返回构造器对象,newInstance实例化出对象
搜索构造器的参数,需要明确:
构造器以及方法不能靠名字搜索因为:重载。需要靠名字以及参数类型和个数定位到方法
这里面是集合参数Class
如果是基本数据类型,也是传int.class....其他基本数据类型也是
返回构造器对象后,通过构造器对象.newInstance实例化Dog对象
这里介绍了两种实例化对象的方法:有参无无参构造器。无参newInstance实例化,有参找到构造器newInstance实例化。
对象可以实例化出一份。但forName加载的类只有一份。类模板只有一份,重复加载,也是使用加载过的这个类。但对象可以创建出多个。这两个观点不冲突。
操作方法:
实例化出Dog对象,如何操作方法?返回的实例对象Object要转子类吗?并不是的,如果转子类,又回到死代码。
如果子类有独特的方法,父类没有声明,怎么办?需要明确一点,转了子类,再次依赖子类,死代码。想办法划分多个声明,转子类的父类声明也比转子类强。这里按照情况斟酌办法。
这里操作方法代码是:
cls.getMethod指定搜索某个方法,返回方法对象。
这里也需要明确:方法不能仅靠名字判断,因为重载。还需要参数类型以及参数个数。
getMethod(name,paramter...)方法的名字,集合参数(可以传递n个参数)根据方法的名字,参数的类型以及个数,定位到方法,类型以及个数可以避免方法重载的情况
搜索到方法后,返回的方法对象method。
如果是静态方法,发起调用method.invoke(null,null)
如果是实例方法,发起调用method.invoke(object,null)
如果是实例有参方法,发起调用method.invoke(object,param...)
invoke调用方法:前面是调用谁的方法,后面是给方法传递的参数(需:方法有参)
如果是静态,当cls.getmethod在invoke,第一个参数传递null,表示cls类的调用 如果是实例,当cls.getmethod在invoke,第一个参数传递对象,表示对象的调用 如果是有参,invoke传递参数,无参则null。传递的参数给方法形参接收,不是传class
静态传null,表示是类发起调用。如果是实例方法,就需要传递对象,使用对象发起调用。当然静态传对象也行,表示用对象调用静态方法。
如果方法有返回值,前面写上接收的变量即可。 如果方法有形参,paramterType写上类型.class。便于从重载中定位。
我们不难发现,在搜索构造器与方法都有一个参数,那就是paramterType...参数类型(集合)。因为单靠名字不敢搜索,还需要参数的类型以及个数。
这个paramterType是集合参数,我们可以一直往后面添加类型.class。但我们这样直接往后面添加参数可取吗?
不可取!这样直接写不可变!这个应该随要搜索的方法走,要搜索出不同的方法,这里要变!这里的集合参数,不仅是可以直接往后面传参,集合要求可以接收数组类型!我们为了灵活应该使用数组,随需求可以改改,这样可以搜索出不同的方法,如果写参数就定死了!
实例:
Class[] classes = new Class[]{String.class, String.class, int.class};
Constructor constructor = cls.getConstructor(classes);
避免直接给方法传类型.class,来寻找。这样是死的,很明显。当我们使用数组,数组可以随我们改变,于是这样后就可以随着数组的class及个数变化而能搜索出不同的方法。
操作私有属性:
我们从类的构造中,可以得到age属性是私有的
从类中搜索属性:
搜索公开属性:cls.getField(name)
搜索私有属性:cls.getDeclaredField(name)
于属性来说,没有像方法一样,方法可能存在方法重载,需要靠方法名字以及方法参数类型及个数来定位方法。而属性在整个类来说,凭属性名就能定位到属性了。
于是搜索只需要属性名即可,从Class类中搜索,不依靠不对Object操作。这是Dog类这也是Object类。如果直接对Object也便于于以后操作。这里使用反射还能灵活的搜不同属性。
从Class类Dog类的管理类搜索出属性后,这是一个私有属性,如何操作?
首先需要setAccessible,true设置对私有属性产生操作
当设置这个后,就可以直接操作这个搜索出来的属性了。返回的Field对象
比如:
能不经过setAge方法直接给属性赋值。Set(object,value);
能不经过getAge方法获得属性赋值。get(object);
如果是静态属性,则不需要object获得的实例对象。表示从类发起操作
如果是实例属性。则需要传入实例的对象。表示从对象发起操作
操作公开也是这样,不过方法改为getFiled即可。也不需要设置setAccessible权限。
操作私有方法:
操作私有方法也是雷同,getDeclanMethod搜索私有方法,setAccessible设置权限,在操作。
总结:
实例化对象,分有参无参构造器:
无参可以直接使用Class类的管理类调用实例化方法,从类中加载一个对象出来。
有参需要从Class管理类(=Doge)类搜索到指定构造器,由构造器对象构造对象出来。
操作公开方法:
Class.getMethod(name,...)根据名字与参数类型个数,拿到找到的方法对象
Invoke(obj,value)调用方法:如果是静态可以不传对象(由类)。如果是实例需要传对象(由对象)。
如果方法有返回值,在invoke前准备接收的变量接收。
操作私有的方法,与前面大致一致,额外改变:
由getMethod改成getDeclareMethod。在invoke前Method.setAccessible让私有允许调用
操作公开属性,与操作方法一致,额外改变:
getField(name);根据属性名就能搜到属性,返回Field对象。
属性可以Field.get(obj)得到值,可以Field.set(obj,value)设置值。
如果是静态属性,则不需要obj由类对Filed对象发起操作
如果是实例属性,则需要obj由对象对Filed对象发起操作
操作私有属性,与前面大致一致,额外改变:
由getField改成getDeclareField。在操作前Field.setAccessible让私有属性允许操作
观察,实例化返回Object基本没有任何操作。我们的操作是通过Class锁定要操作的对象,进行操作。Object让他一直保持父类,由类的管理类操作对象。
当给实例属性或方法赋值,才需要obj实例后对象帮忙。因为一个加载的类,可以构建出多个对象,不知道给哪个对象赋值。我们赋值也保持Object父类,通过Class管理类传递对象,给对象上赋值,将一直保持Object类,通过Class对Object对象及类操作。都是为了灵活。
搜索方法是集合参数,因为单靠名字无法确定方法,构造器也是。需要靠方法需求的参数类型.class再靠集合参数的个数定位到方法,构造器也是。集合参数我们不直接写参数的class,通常是传递写好的Class[]过去。为了可变,改变数组引用,就能搜索不同的实例。
需要明确一点,我们不直接操作对象,通过Class管理类,搜索到操作的对象。如果是静态,直接操作表示由Class操作。如果是实例传递obj对象,表示要操作这个对象的这个实例。传递对象因为一个类可以造多个对象。需要明确类模板只有一个。
他静态与实例的关系如下图:
如果是null,反正搜索我Field的对象是Class,表示是得到Class身上的静态属性
如果是obj,表示是得到对象身上的这个Field对象属性的值。
这就是传null与obj的区别,一个是从类操作,一个从传递的对象身上操作对象。