反射机制(二)与内省

/**

 * 第二十五集   数组的反射及应用
 * 具有相同维数和元素的数组属于同一类型,即具有相同的Class实例对象。
 * 代表数组的Class实例对象的getSuperClass()方法返回父类为Object类对应的Class。
 * 基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;
 * 非基本类型的一位数组,既可以当做Object类型,又可以当做Object类型使用。
 * Arrays.asList()方法处理int[]和String[]时的差异
 * Array工具用于完成对数组的反射操作。
 * 思考题:怎么得到数组中的元素类型? 
 * 
 */


/**
 * 第二十六集
 * ArrayList_HashSet的比较及HashCode分析
 * hashCode和HashSet类
 * 如果想查找一个集合中是否包含某个对象,大概程序的代码是这样写呢?
 * 你通常是逐一的取出每个元素与要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法比较的
 * 结果相等时,则停止继续查找并返回肯定信息,否则,返回否定信息。如果一个集合中有很多个元素,譬如有一万个
 * 元素,并且没有包含查找的元素时,则意味着你得程序需要从该集合中取出一万个元素进行逐一比较才能得到结论。
 * 有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象
 * 可以计算出个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定
 * 该对象应该存储在哪个区域,
 * 
 * HashSet就是采用哈希算法存取对象的集合,它内部采用对某个数字n进行取余的方式对哈希码进行分组,和划分对象的
 * 存储区域。Object类中定义了一个HashCode()方法来返回每个Java对象的哈希码,当从HashSet集合中查找某
 * 个对象,java系统首先调用对象的hashCode()方法获得该对象的哈希码。然后根据哈希码找到相应的区域,最后取出
 * 该存储区域内的每个元素与该对象进行equals方法比较,这样不用遍历集合中的所有元素就可以得到结论。
 * 可见,HashSet集合具有很好的对象检索性能,
 * 
 * 只有类的实例对象要被采用哈希算法进行存储和检索时,这个类才需要按要求覆盖hashCode方法。即使程序可能暂时
 * 不会用到当前类的hashCode方法。但是为了它提供了一个hashCode方法也不会有什么不好,没准以后什么时候又
 * 用到这个方法了,所以,通常要求hashCode方法和equals方法一并被同时覆盖。
 *     1.通常说,一个类的两个实例对象用equals方法进行比较的结果相等时,他们的哈希码也必须相等,但反之则不成立。
 * 即equals方法比较结果不相等的对象可以有相同的哈希码、或者说哈希码相等的两个对象的equals方法比较结果可以
 * 不相等 。
 *     2.当一个对象被存储到HashSet集合以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象
 * 修改后的哈希值与最初存进去hashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对
 * 像的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也将导致无法从HashSet集合中
 * 单独删除当前,从而造成内存泄露。。
 * 
 */


/**
 * 第二十七集 框架的概念及用反射技术开发框架的原理
 * 反射的作用---->实现框架功能
 * --框架的核心问题
 * ----我做房子卖给住户住,用户自己安装门窗和空调,我做的房子就是框架,用户
 * 需要使用我们的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类
 * 被用户类调用,而框架是调用用户提供的类。
 * --框架要解决的核心问题
 * --我在写框架(房子)的时候,你这个用户可能还在上小学,还不会写程序呢?我写的框架
 * 程序怎样才能调用到你以后写的类(门窗)呢?
 * --因为在写程序时无法知道要被调用的类名,所以,在程序中直接new某个类的实例对象,
 * 而要用反射方式来做
 * --综合案例
 * ----先直接用new语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成
 * ReflectPoint类的equals和hashcode方法,比较两个集合的运行结果差异。
 * ----然后改为采用配置文件加反射的方式创建ArrayList和Hashset的实例对象,比较
 * 观察运行的结果差异。
 * 引入了eclipse对资源文件的管理方式的讲解

 * 第二十五集   数组的反射及应用
 * 具有相同维数和元素的数组属于同一类型,即具有相同的Class实例对象。
 * 代表数组的Class实例对象的getSuperClass()方法返回父类为Object类对应的Class。
 * 基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;
 * 非基本类型的一位数组,既可以当做Object类型,又可以当做Object类型使用。
 * Arrays.asList()方法处理int[]和String[]时的差异
 * Array工具用于完成对数组的反射操作。
 * 思考题:怎么得到数组中的元素类型? 
 * 
 */


/**
 * 第二十六集
 * ArrayList_HashSet的比较及HashCode分析
 * hashCode和HashSet类
 * 如果想查找一个集合中是否包含某个对象,大概程序的代码是这样写呢?
 * 你通常是逐一的取出每个元素与要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法比较的
 * 结果相等时,则停止继续查找并返回肯定信息,否则,返回否定信息。如果一个集合中有很多个元素,譬如有一万个
 * 元素,并且没有包含查找的元素时,则意味着你得程序需要从该集合中取出一万个元素进行逐一比较才能得到结论。
 * 有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象
 * 可以计算出个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定
 * 该对象应该存储在哪个区域,
 * 
 * HashSet就是采用哈希算法存取对象的集合,它内部采用对某个数字n进行取余的方式对哈希码进行分组,和划分对象的
 * 存储区域。Object类中定义了一个HashCode()方法来返回每个Java对象的哈希码,当从HashSet集合中查找某
 * 个对象,java系统首先调用对象的hashCode()方法获得该对象的哈希码。然后根据哈希码找到相应的区域,最后取出
 * 该存储区域内的每个元素与该对象进行equals方法比较,这样不用遍历集合中的所有元素就可以得到结论。
 * 可见,HashSet集合具有很好的对象检索性能,
 * 
 * 只有类的实例对象要被采用哈希算法进行存储和检索时,这个类才需要按要求覆盖hashCode方法。即使程序可能暂时
 * 不会用到当前类的hashCode方法。但是为了它提供了一个hashCode方法也不会有什么不好,没准以后什么时候又
 * 用到这个方法了,所以,通常要求hashCode方法和equals方法一并被同时覆盖。
 *     1.通常说,一个类的两个实例对象用equals方法进行比较的结果相等时,他们的哈希码也必须相等,但反之则不成立。
 * 即equals方法比较结果不相等的对象可以有相同的哈希码、或者说哈希码相等的两个对象的equals方法比较结果可以
 * 不相等 。
 *     2.当一个对象被存储到HashSet集合以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象
 * 修改后的哈希值与最初存进去hashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对
 * 像的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也将导致无法从HashSet集合中
 * 单独删除当前,从而造成内存泄露。。
 * 
 */


/**
 * 第二十七集 框架的概念及用反射技术开发框架的原理
 * 反射的作用---->实现框架功能
 * --框架的核心问题
 * ----我做房子卖给住户住,用户自己安装门窗和空调,我做的房子就是框架,用户
 * 需要使用我们的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类
 * 被用户类调用,而框架是调用用户提供的类。
 * --框架要解决的核心问题
 * --我在写框架(房子)的时候,你这个用户可能还在上小学,还不会写程序呢?我写的框架
 * 程序怎样才能调用到你以后写的类(门窗)呢?
 * --因为在写程序时无法知道要被调用的类名,所以,在程序中直接new某个类的实例对象,
 * 而要用反射方式来做
 * --综合案例
 * ----先直接用new语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成
 * ReflectPoint类的equals和hashcode方法,比较两个集合的运行结果差异。
 * ----然后改为采用配置文件加反射的方式创建ArrayList和Hashset的实例对象,比较
 * 观察运行的结果差异。
 * 引入了eclipse对资源文件的管理方式的讲解

 */

package cn.itcast.day1;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;

public class ReflectTest2 {

/**
* 用类加载器的方式管理资源和配置文件
*/


/**
* @param args
* @throws IOException 
* @throws ClassNotFoundException 
* @throws IllegalAccessException 
* @throws InstantiationException 
*/
public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
/**
* ArrayList和HashSet
* ArrayList有顺序,按排队的先后顺序
* HashSet放进去先判断里面有没有,如果元素已经存在,就不再放进去了
* 现在打印的ArrayList得到4 HashSet得到3
* 当覆盖hashCode()和equals()方法后
* 打印的ArrayList得到4 HashSet得到2这时pt1和pt3也相等

* 新建一个file配置文件起名config.properties
* className=java.util.ArrayList
* 以后改名字的时候在配置文件里面改就行
* 首先要加载properties文件
* 尽量面向父类或借口编程
* Properties对象就等于一个HashMap内存里面装的是key--Value
* 但是它在HashMap上扩展了一点功能 他可以把内存里面的键值对存到硬盘里面去
* 它也可以在初始化的时候将键值对加载进来,上来便可以带来一堆

* Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
* 属性列表中每个键及其对应值都是一个字符串。 
* load()方法:: 从输入流中读取属性列表(键和元素对)。
* load后
* 要马上关门。。这里有一点小小的内存泄露,不是对象不被释放,而是对象关联的系统资源
* 没被释放。在被垃圾回收之前先把物理资源干掉.把操作系统的干掉
*/
/**
* getRealPath();金山词霸/内部
* 一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的。
*/

//InputStream ips = new FileInputStream("config.properties");
//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
/**
* 这个只需要写上配置文件
*/
InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/resources/config.properties");
//InputStream ips = ReflectTest2.class.getResourceAsStream("resources/config.properties");
Properties props = new Properties();
props.load(ips);
ips.close();
String className = props.getProperty("className");
/**
* 得到是一个对象,对象一定是Collection类型,要强制转换
*/
Collection collections = (Collection)Class.forName(className).newInstance();


//Collection collections = new HashSet();
//Collection collections = new ArrayList();
ReflectPoint pt1 = new ReflectPoint(3,3);
ReflectPoint pt2 = new ReflectPoint(3,5);
ReflectPoint pt3 = new ReflectPoint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);

//pt1.y = 7;
//collections.remove(pt1);

System.out.println(collections.size());
}
}

/**
 * 第二十九集 由内省引出JavaBean的讲解
 * IntroSpection-->JavaBean-->Java类
 *  IntroSpection汉语意思为内省
 * JavaBean是一种特殊的java类,主要用于传递数据信息,这种java类中的
 * 主要方法要用于访问私有字段,且方法名符合某种命名规则。
 * 如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,
 * 这种javaBean的实例对象通常称之为值对象(Value Object 简称VO)。
 * 这些信息在类中用私有字段来存储,如果方法名为setId,中文的意思是设置id,
 * 至于你把它存储到哪个变量上,用管吗?如果方法名给getId,中文意思即为获取
 * id,至于你用哪个变量上取,用管吗?去掉set前缀,剩余的部分就是属性名。
 * 如果剩余部分的第二个字母是小写,则把剩余部分改成小写。
 * 
 * --setId-->id
 * --isLast-->last
 * 如果第二个字母为大写,则不变
 * getCPU-->CPU
 * getUPS-->UPS
 * 总之一个类被当做javaBean使用时,JavaBean的属性是根据方法名推断出来的,
 * 他根本看不到java类内部的成员变量
 * 
 * 一个符合javaBean特点的类可以当做普通类一样使用,但把它当javaBean用
 * 肯定需要带来一些额外的好处,我们才会去了解和应用JavaBean!
 * 好处如下
 * --在javaEE开发中,经常要用到JavaBean。很多环境就要求用JavaBean
 * 方式进行操作,别人都这么用和要求这么做,那你就没有什么挑选的余地!
 * --JDK中提供了对JavaBean进行操作的Api,这套?API就成为内省。如果
 * 你要通过getX方法来访问私有x,怎么做,有一定难度,用内省这套api造作
 * JavaBean比用普通类方式更加方便.

 * 
 * javaBean是给外界操作的
 * private int x;
 * 
 * public int getAge(){
 * return x;
 * }
 * void setAge(int age){
 * this.x = age;
 * }
 */
package cn.itcast.day1;


import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;


/**
 * 第三十一集 JavaBean复杂的内省操作
 * 内省的综合应用
 * 演示用eclipse自动生成ReflectPoint类的setter和getter方法。
 * description描述,形容,种类,类型
 * Property特性,属性,财产,道具所有权
 * 直接new一个PropertyDescription对象来让大家了解JavaBean API
 * 的价值,先用一段代码读取JavaBean的属性,然后再用一段代码设置JavaBean的属性
 * 演示用eclipse将读取属性和设置属性的流水账代码跟别抽取成方法:
 * --只要调用这个方法,并给这个方法传递一个对象,属性名称和设置值,
 * 他就能完成属性的修改的功能
 * --得到BeanInfo最好采用"obj.getClass()"方式,
 * 而不要采用“类名.class”方式,这样程序更通用。
 * 采用遍历BeanInfo的多有属性方式来查找和设置某个RefectPoint
 * 对象的x属性。这程序中把一个类当做JavaBean来看就是
 * 调用IntroSpector.getBeanInfo方法,得到BeanInfo对象
 * 封装了把这个类当做JavaBean看的结果信息。。

 * 
 * @author Administrator
 */


public class IntroSpectorTest {

/**
* @param args
* @throws IntrospectionException 
* @throws InvocationTargetException 
* @throws IllegalAccessException 
* @throws IllegalArgumentException 
* @throws NoSuchMethodException 
*/
public static void main(String[] args) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
// TODO Auto-generated method stub
ReflectPoint pt1 = new ReflectPoint(3, 5);

String propertyName = "x";
//"x"-->"X"-->"getX"-->"MethodGetX"-->

/**
* 在哪个方法上调?在pt1上调
* 起变量的名字 大师的代码 
*/
Object retVal = getProperty(pt1, propertyName);
System.out.println(retVal);


Object value = 7;
setProperty(pt1, propertyName, value);
/**
* 要用到别人jar,日志开发包 commons-logging-1.1.1.jar
* 看看BeanUtils它的结果类型。。String
*/
System.out.println(BeanUtils.getProperty(pt1, "x").getClass().getName());
/**
* 设置它的值,这个9是字符串
* set x的属性是int 但是用BeanUtils去操作的时候设置的时候是String
* 返回时也是String,在Web开发中,通过浏览器年龄多大?填写用户9,
* 填写的时候是数字,传给服务器的时候是字符串,
* 自己写的时候是先把字符串转化成数字,然后setBean
* 这个BeanUtils就会进行自动转换,目标是整数 ,来的是字符串
* 交给工具,他帮你转换,转换后再set进去。。显示出去也是String
* 这个转换他可以自动完成

*/
BeanUtils.setProperty(pt1, "x", "9");
System.out.println(pt1.getX());

/**
* java7的新特性
*/
  /*Map map = (name:"zxx",age:18);
BeanUtils.setProperty(map, "name", "lhm");*/

/**
* Date下面有一个setTime方法
* 还有time方法
* 直接运行会报异常 No bean specified
* specified---规定的;详细说明的v. 指定;详细说明(specify的过去分词)
* 对birthday需要赋初值Date birthday = new Date();
* 支持属性的级联操作。脑袋上的眼睛,眼睛上的眼珠,眼珠的颜色
*/
BeanUtils.setProperty(pt1, "brithday.time", "111");
System.out.println(BeanUtils.getProperty(pt1, "brithday.time"));
/**
* Method invocation failed
* invocation 祈祷;乞求;乞灵;乞求神助
* argument type mismatch 参数类型不匹配
* argumen --n.争论,争吵;论据;[数]幅角;主题,情节
* mismatch--vt.使配错,使配合不当n.错配,失谐
* PropertyUtils.setProperty(pt1, "x", "9");
* 这里出现错误   应当直接将9设置为整数  返回java.lang.Integer
* get回来的结果是整数 。
* BeanUtil是以字符串的形式进行set和get
* PropertyUtils是以属性本身的类型进行操作
*/
PropertyUtils.setProperty(pt1, "x", 9);
System.out.println(PropertyUtils.getProperty(pt1, "x").getClass().getName());
}


private static void setProperty(ReflectPoint pt1, String propertyName,
Object value) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName, pt1.getClass());
Method methodSetX = pd2.getWriteMethod();
/**
* 因为java5有自动装箱功能把它装成7
*/
methodSetX.invoke(pt1,value);
}


private static Object getProperty(Object pt1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
/**
* 第二个属性是javaBean类,把类传进去当做javaBean
*/
/*PropertyDescriptor pd = new PropertyDescriptor(propertyName, pt1.getClass());
*//**
* 得到get方法 返回方法
*//*
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt1);*/
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor pd : pds){
if(pd.getName().equals(propertyName)){
Method methodGetX = pd.getReadMethod();
retVal = methodGetX.invoke(pt1);
break;
}
}
return retVal;
}
}

/**
 * 第三十二集  使用BeanUtils工具包操作JavaBean
 * BeanUtils 工具包
 * 演示用eclipse如何导入jar包,先只是引入beanutil包,
 * 等程序出错后再引入logging包。
 * 在前面内省例子的基础上,用BeanUtils类先get原来设置
 * 好的属性,再将其set为一个新值
 * --get属性时通常返回的结果为字符串,set属性时可以接受
 * 任意类型的对象,通常是用字符串。
 * 用PropertyUtils类先get原来设置好的属性,再将其
 * set为一个新值。get属性时返回的结果为该属性本来的类型,
 * set属性时只接受属性本来的类型
 * 
 */


package cn.itcast.day1;


import java.util.Date;


public class ReflectPoint {

private Date brithday = new Date();;

private int x;
public int y;
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "itcast";

public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}

/**
* 为了看到打印后的效果你还有必要覆盖toString方法
* @return 
*/

@Override
public String toString(){
return str1 + ":" + str2 + ":" + str3;
}

/**
* 覆盖hashCode()和equals()方法
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}


/**
* 往往会写成equals(ReflectPoint obj)
* 这时候没有调用equals。。这时候不是对父类的覆盖,而是重载
* @Override是表示覆盖
* 以后遇到此类问题防止写错 要加上@Override 加上一个注解。。
* 如果错误就会报错
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}


public int getX() {
return x;
}


public void setX(int x) {
this.x = x;
}


public int getY() {
return y;
}


public void setY(int y) {
this.y = y;
}


public Date getBrithday() {
return brithday;
}


public void setBrithday(Date brithday) {
this.brithday = brithday;
}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值