----------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------------
反射(续)
一、Method类
1、l Method类代表某个类中的一个成员方法
2、l得到类中的一个方法:
例如:Method charAt=Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);
3、l调用方法
通常方式:System.out.prinltn(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1));
如果传递给Method对象的invoke()方法的第一个参数为null.这有着什么样的意义呢?
说明该Method对象对应的是一个静态方法!
4、lJdk1.4 和 jdk1.5的invoke方法的区别:
Jdk1.5: public Object invoke(Object obj,Object.....args)
JDK1.4: public Object invoke(Object obj,Object[] args)
按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。
5、专家模式:谁拥有数据,谁就是操作数据的专家。
代码
String str1=”abc”我们的目的是 str1.charAt(1);
Method methodChatAt=String.class.getMethod(“charAt”,int.class);
System.out.println(methodCharAt.invoke(str1,1));
//这就是用反射的方法,拿到一份字节码中的方法,并让该方法起作用于某个对象。
Invoke()是所取到方法对象中的方法。
例子:圆对象去画圆
Circle.draw();
面向对象:变量属性在谁身上,方法就在谁身上,这就是专家模式.
6、 对接收数组参数的成员方法进行反射
1)用反射方式执行某个类中的main方法
目标
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法
l 问题
启动java程序的main方法的参数是一个字符串数组,即public static void main(String []args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?
按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每一个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac到底会按照哪种语法进行处理呢?
Jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。
所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
l 解决方法
mainMethod.invoke(null,new Object[]{new String[]{“xxx”}});
mainMethod.invoke(null,(Object)new String[]{“xxx”});,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了。
示例:
用反射方式执行Test类中的main方法
编译时,Run As——>RunConfigurations——>Arguments——>Program arguments中添加要执行的类名
二、数组的反射
l1、具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
2、代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
3、l基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用,非基本类型的一维数组,既可以当作Object类型使用,又可以当作Object[]类型使用。
4、lArrays.asList()方法处理int[]和String[]时的差异。
Array工具类用于完成对数组的反射操作
5、1.4版本和1.5版本区别
asList(Object[]a)它接收的是一个Object[],而int是不能赋值给Object的,所以就用1.5版本处理
1.5版本
asList(T.....a)它等效接收的是一个Object对象,把一个int[]赋值给Object对象,返回的就是地址
6、演示
三、ArrayList与HashSet的比较及Hashcode分析
1、ArrayList与HashSet的比较
ArrayList 是一种有序的集合,它存放的对象的引用。如果同一个对象放进去两次,它也接收。因为相当于多一个引用变量,即便引用的是同一个对象,也没有关系。甚至可以插队,不是比较顺序,而是指先后顺序。
而HashSet,存放对象时,它先判断里面有没有这个对象,如果有,就放不进去,不是覆盖。
如果想要覆盖,必须,把原来的给删除掉,然后把现在的写进去。
2、Hashcode方法的作用
如果一个集合中有很多元素,比如有一万个,并没有包含要查找的对象时,则意味着你的程序需要从该集合中取出一万个元素进行逐一比较才得到结论。有人发明了一种哈希算法来提高从集合中查找元素的效率。这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定对象存储在哪一个区域。
HashSet就是采用哈希算法存取对象的集合,它内部采用对某个数组n进行取余的方式对哈希码进行分组和划分对象的存储区域,object定义了一个hashcode方法来返回每个java对象的哈希码。
当hashset集合中查找某个对象时,java系统首先调用对象的hashcode方法获取该对象的哈希码,然后根据哈希码找到相应区域,最后取出该存储区域中的每个元素与该对象进行equals方法比较,这样不用遍历集合中的所有元素就可以得到结论。可见hashset集合具有很好的对象检索功能。
提示:
(1)如果两个对象的equals比较相等的话,也要让他们的hashcode也相等,但反之则不成立。即equals方法比较结果不想等的对象可以拥有相同的哈希码,或者说哈希码相同的两个对象equals方法比较的结果可以不等,
(2)当一个对象被存储进hashset集合中后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进hashset集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为参数去hashset集合中检索对象,也将返回找不到对象的结果,这也会导致无法从hashset集合中单独删除当前对象,从而造成内存泄露。
3、演示
四、反射的作用——>实现框架的功能
我们使用类有两种方式,一种是使用别人的类,一种是别人使用我们的类
框架:通过反射调用Java类的一种方式。
l1、框架与框架要解决的核心问题
我要做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中,框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
l 2、框架要解决的核心问题
因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式类做。
3、 简单框架程序的步骤:
1)右击项目File命名一个配置文件如:config.properties,然后写入配置信息。如键值对:className=java.util.ArrayList,等号右边的配置键,右边是值。
2)代码实现,加载此文件:
(1)将文件读取到读取流中,要写出配置文件的绝对路径。
如:InputStream is=new FileInputStream(“配置文件”);
(2)用Properties类的load()方法将流中的数据存入集合。
(3)关闭流:关闭的是读取流,因为流中的数据已经加载进内存。
3)通过getProperty()方法获取className,即配置的值,也就是某个类名。
4)用反射的方式,创建对象newInstance()。
5)执行程序主体功能
九、类加载器
1、类加载器是将.class的文件加载进内存,也可将普通文件中的信息加载进内存。
2、 文件的加载问题:
1)eclipse会将源程序中的所有.java文件编译成.class文件,然后放到classPath指定的目录中去。并且会将非.java文件原封不动的复制到.class指定的目录中去。在运行的时候,执行的是.class文件。
2)将配置文件放到.class文件目录中一同打包,类加载器就会一同加载。
3、资源文件的加载:是使用类加载器。
1)由类加载器ClassLoader来加载进内存,即用getClassLoader()方法获取类加载器,然后用类加载器的getResourceAsStream(String name)方法,将配置文件(资源文件)加载进内存。利用类加载器来加载配置文件,需把配置文件放置的包名一起写上。这种方式只有读取功能。
2)Class类也提供getResourceAsStream方法来加载资源文件,其实它内部就是调用了ClassLoader的方法。这时,配置文件是相对类文件的当前目录的,也就是说用这种方法,配置文件前面可以省略包名。
如:类名.class.getResourceAsStream(“资源文件名”)
4、配置文件的路径问题:
1)用绝对路径,通过getRealPath()方法运算出来具体的目录,而不是内部编码出来的。
一般先得到用户自定义的总目录,在加上自己内部的路径。可以通过getRealPath()方法获取文件路径。对配置文件修改是需要要储存到配置文件中,那么就要得到它的绝对路径才行,因此,配置文件要放到程序的内部。
2)name的路径问题:
(1)如果配置文件和classPath目录没关系,就必须写上绝对路径,
(2)如果配置文件和classPath目录有关系,即在classPath目录中或在其子目录中(一般是资源文件夹resource),那么就得写相对路径,因为它自己了解自己属于哪个包,是相对于当前包而言的。
5、演示