反射 ( reflect 反射不是JDK 1.5的新特性,是java1.2开始有的。)
透彻分析反射的基础-Class
1 java 程序中的各个java类 属于同一类事物,描述这类事物的java类名就是 Class。
2 Class类 代表java类,它的各个实例对象分别对应什么呢?
^对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码....
^一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不 同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个空间分别用一个个的对 象来表示,这些对象具有了相同的类型Class.
3 如何得到各个字节码对应的实例对象(Class类型)
^类名.class,例如 System.class
^对象。getClass(),例如new Daye().getClass
^Class.forName("类名"),例如,Class.forName("java.lang.Date");
面试题:Class.forName("")的作用是什么?
答:返回字节码。有两种方式:
1)如果字节码在java虚拟机中已经存在,则在内存中找到并返回该字节码
2)如果字节码在java虚拟机中不存在,则类加载器将其加载到内存后 返回该字节码。
4 九个预定义的Class对象
八个基本类型 和void (int.class 、 void.class...)
参看Class.isPrimitive方法的帮助 (是不是基本数据类型)
Int.class==Integer.TYPE
5 数组类型的Class实例对象
Class.isArray()
总之只要在源程序中出现的类型,都有各自的Class实例对象,例如:int[],void...
反射 : 就是把java类中的各种成分映射成相应的java类
Constructor类 代表某个类中的一个构造方法
得到某个类的构造方法:
Constructor [] constructor=
Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象:
通常:String str=new String(new StringBuffer("abc"));
反射:String str=(String)constructor.newInstance(new StringBuffer("abc"));
//调用方法时 要用到与上面相同类型的实例对象
Class.newInstance()方法:
例子:String obj=(String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法(无参的构造方法被缓存了),然后用该构造方法创建实例对象。
该方法用到了缓存机制来 保存默认的构造方法的实例对象。
Field 类 代表某个类中的一个成员变量
问题:得到的Field对象是对应到类上的成员变量,还是对应到对象上的成员变量?
类只有一个,而该类的实例对象有多个,所以Field对象关联的是类上的成员变量。
所以字段fieldX代表X的定义,而不是具体的X变量。
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}}
public class ReflectTest{
public static void main(String[] args){
try {
ReflectPoint flect1=new ReflectPoint(4,6);
Field fieldY=flect1.getClass().getField("y");
//fieldY的值是多少?5错:fieldY是类的变量,不是对象的。
System.out.println(fieldY.get(flect1));
/*Field fieldX=flect1.getClass().getField("x");
* x是私有的外部类不能访问,要想获得X只能强制反射
*/
Field fieldX=flect1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(flect1));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();} } }
作业:将任意一个对象中的所有String类型的成员变量所有对应的字符串内容中的“b”改为“a”
import java.lang.reflect.Field;
public class ReflectTest{
ReflectPoint flect1=new ReflectPoint();
changeStringValue(flect1);
System,out.print(flect1);
public void changeStringValue(Object obj)throws Exception{
Field[] fields=obj.getClass().getFields();
for(Field field:fields){
if(field.getType==String.class){
String oldString=field.get(obj);
String newString=oldString.replace("b","a");
field.set(obj,newString);
}
}
}
}
public class ReflectPoint {
private int x;
public int y;
public String str1="boy";
public String str2="field";
public String str3="hello";
@Override
public String toString(){
return str1+":"+str2+":"+str3;
}
}
Method类 代表某个类中一个成员方法
得到类中某一个方法:
Method methodCharAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
调用方法:
通常:System.out.print(str.charAt(1));
反射:System.out.print(methodCharAt.invoke(str,1));
如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法
(因为静态方法调用时 对象还没有产生)System.out.print(methodCharAt.invoke(null,1));
jdk1.4和 jdk1.5的区别:
jdk1.5: public Object invoke(Object obj,Object ... args)
jdk1.4: public Object invoke(Object obj,Object[] args)
按jdk1.4的语法需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法的一个参数,所有 调用charAt方法的代码也可以用jdk1.4改写methodCharAt(str,new Object[]{1});
对接收数组参数的成员方法进行反射
用反射的方式执行某个类中的main方法:
目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类的main()。那么我们为什么要用反 射的方式调用呢?
问题:启动java程序的main方法的参数时一个字符串数组,通过反射调用main方法时,如何为invoke 方法传递参数呢?按jdk1.5,整个数组是一个参数;按jdk1.4,数组中的每个元素对应一个参数 jdk1.5肯定要兼容jdk1.4的语法,把数组打散成若干个单独的参数。所以给main方法传递参数时 不能使用代码。
解决办法:
1 mainMethod.invoke(null,new Object[]{new String[]{"xxx"}})
将数组作为另一个数组的元素。
2 mainMethod.invoke(null,(object)new String[]{"xxx"});
编译器会作特殊处理,编译时不把参数当做数组看待。
目标程序:(运行这段代码时 我们在运行对话框里 给类传递参数(要运行main方法类的完整名称)这样主 函数才能找到类名进行反射)
import java.lang.reflect.Method;
public class MainMethodRunTest {
public static void main(String[] args) {
String mainStaticMethod = args[0];
try {
Method mainMethod=
Class.forName(mainStaticMethod).getMethod("main",String[].class);
mainMethod.invoke(null,new Object[]{new String[]{"12","43","34"}});
//mainMethod.invoke(null, (Object)new String[]{"12","43","34"});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();}}}
class MainMethodTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
for(String arg:args){
System.out.println(arg);
} } }
数组与Object的关系及其反射类型
1 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象
2 代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
3 基本类型的一位数组可以被当做Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当作Object类型也可当作Object[]类型使用。
4 Arrays.asList()方法处理int[]和String[]时的差异
jdk1.4中Arrays.asList(Object[] a) 它将String[]的元素转化为List对象,就可以打出了;而
int[] 属于Object类型 不属于Object[] 只能由jdk1.5处理。jdk1.5中,Arrays.asList(T ... a)
int[] 被当作一个Object 所以不能打出int[]的各个元素。
int [] a1=new int[]{1,2,8};
String [] a2=new String[]{"a","b","d"};
System.out.println(Arrays.asList(a1)); //结果[[I@C17164]
System.out.println(Arrays.asList(a2)); //结果[a,b,d]
5 Array工具类用于完成对数组的反射操作
例:打印任意对象
import java.util.Arrays;
public class AyyayReflectTest{
public static void main(String [] args){
int [] a1=new int[]{1,5,8};
String [] a2=new String[]{"v","fd","h"};
printObject(a1);
printObject(a2);
printObject("xyz");
}
private static void printObject(Object obj) {
// TODO Auto-generated method stub
Class clazz=obj.getClass();
if(clazz.isArray()){
int len=Array.getLength(obj);
for(int i=0;i<len;i++){
System.out.println(Array.get(obj, i));
}
}else{
System.out.println(obj);
} } }
ArrayList、HashSet的比较及Hashcode分析
ArrayList按顺序存储元素,可以重复
HashSet存储的元素不可以重复,当存储元素时先比较如果存在该元素则不存放。
它们都可以实现Collection接口
**分析Hashcode:
如果要查找一个集合中是否有某个对象,通常我们会逐一取出每个元素与查找的对象比较,直到找到那个用equals方法比较相等的值,停止查找并返回肯定的信息,如果有一万个元素并且不包含要查找的对象,那么equals方法会被调用一万次。有人发明了哈希算法,计算出每个元素的哈希值,并将集合分为若干区域,这样查找对象时 只要计算出它的哈希值所对应的区域 在对这个区域进行查找 ,大大的提高了程序的运行效率。当然Hashcode只对用哈希算法存储的集合有价值,例如HashSet.
注意(内存泄露):当一个对象被存入HashSet集合以后,就不能修改这个对象中的那些参与计算哈希值 的字段了,否则,对象修改后的哈希值与最初存入的不同,这时即使在contains方法使用该对象当前引 用作为参数去HashSet集合检索对象,也将 返回 找不到对象的结果,这也会导致无法从HashSet集合中 单独删除当前对象,造成内存泄露。
反射的作用——>实现框架功能
框架与工具类的区别:工具类被用户调用;框架则是调用用户提供的类
框架要解决的核心问题:写程序时无法知道要被调用的类名,所以在程序中无法直接new某个类的实例对象,而要用反射方式来做
综合案例:采用配置文件夹反射的方式创建ArrayList和HashSet的实例对象。
工作间workspacethree 类名ReflectPoint.java和ReflectTest2.java 配置文件config.properties * 配置文件一定要用完整的目录,这个目录不是硬编码,是运算出来的。
* 在javaWeb中 我们通过getRealPath();来获得绝对路径 将配置文件放在一个目录下,之后让用户 * 配置这个目录
* 另一个获得资源文件的方式,用类加载器
ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast.day1/config.properties")
ReflectTest2.class.getResourceStream("config.peoperties")
内省-->了解JavaBean
IntroSpector(内省)-->JavaBean-->特殊的java类
JavaBean:是一种特殊的java类主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段, 且方法名符合某种命名规则
如果在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object简称VO),这些信息在类中用私有字段来存储,称为类的属性,
属性是由方法来判断的,去掉get或set后 就是JavaBean的属性,如果第二个字母是小写则第一个字母变成小写getTime-->time、gettime-->time、getCPU-->CPU
JavaBean类的好处:
1 在javaEE开发中,经常要使用JavaBean,很多环境要求按JavaBean的方式操作。
2 JDK中提供了对JavaBean进行操作的一些API,这套API 称为内省,使用内省操作JavaBean比普通类 的方式更方便
写一段代码 读取javaBean属性,然后再设置javaBean属性
代码1: 用PropertyDescriptor类更简单(workspace2/javaenhance/cn/itcast/day1/IntroSpectTest.java)
代码2:采用遍历BeanInfo的所有属性方式查找和设置某个ReflectPoint对象的x属性。在程序中把一个类当作javaBean来看,就是调用IntroSpector.getBeanInfo(),得到的BeanInfo对象封装了把这个类当作JavaBean看的结果信息
**BeanUtils工具包
由于javaBean的属性设置和获取使用的非常多,有人开发出了Beanutils工具包。
将beanutils包的最大jar包commons-beanutils-1.8.3放入工程中(在工程里新建一个文件夹lib),之 后build path;因为beanutils包使用到了commons-logging.jar,所以将这个jar同样的导入工程中。
(1)BeanUtils(获得设置javabean属性)
get()返回的属性结果为字符串,set可接受任意类型对象,通常为字符串。
BeanUtils.setProperty(Object,propertyName,value)
BeanUtils.getProperty(object,peopertyName)
例:
BeanUtils.setProperty(rp, "x", "8");
BeanUtils.getProperty(rp,"x");
*操作符合属性,支持属性的级联操作
BeanUtils.setProperty(rp,"birthday.time" ,"111");
System.out.println(BeanUtils.getProperty(rp, "birthday.time"));
*java7新特性:beanutils可以对MAP进行操作。提供了Map与javabean 的转换
Map map={name:"lisi",age:17};
BeanUtils.setProperty(map,"name","wangsan");
(2)PropertyUtils(另一个获得设置javabean属性的简单类 )
get()返回的属性为bean本身类型,set只接受bean属性的本身类型。
PropertyUtils.setProperty(bean,propertyName ,value);
System.out.println(PropertyUtils.getProperty(bean, propertyName));
例如:
PropertyUtils.setProperty(rp, "x", 5);
System.out.println(PropertyUtils.getProperty(rp, "x"));