本帖最后由 等风来_________ 于 2014-3-16 20:55 编辑
hashCode方法的作用:
总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。
你知道它们的区别吗?前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。
那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?
这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。
也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。
哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。
初学者可以这样理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。
这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。
如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,
就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。
所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
所以,Java对于eqauls方法和hashCode方法是这样规定的:
1、如果两个对象相同,那么它们的hashCode值一定要相同;2、如果两个对象的hashCode相同,它们并不一定相同 上面说的对象相同指的是用eqauls方法比较。
你当然可以不按要求去做了,但你会发现,相同的对象可以出现在Set集合中。同时,增加新元素的效率会大大下降。
内存泄露:
指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
反射的作用
实现框架功能
因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的对象,而要用反射的方法来做。
例子:
//一定要用完整的路径,但完整的路径不是硬编码,而是运算出来的
InputStream is=new FileInputStream("config.properties");
//调用配置文件的方法。
InputStream is=ReflectTest2.class.getClassLoader().getResourceAsStream("com\\itheima\\day01\\config.properties");
InputStream is=ReflectTest2.class.getResourceAsStream("config.properties");
InputStream is=ReflectTest2.class.getResourceAsStream("/com/itheima/day01/resource/config.properties");
InputStream is=new FileInputStream("config.properties");
Properties prop=new Properties();
prop.load(is);
is.close();
String className=prop.getProperty("className");
Collection collections=(Collection) Class.forName(className).newInstance();
内省
为什么要学内省?
开发框架时,经常使用java对象的属性来封装程序的数据,每次都使用反射技术来完成此类操作过于麻烦。
什么是java对象的属性和属性的读写方法?
内省访问JavaBean属性的两种方式:
通过PropertyDescriptor类操作Bean的属性。
通过Introspector类获得Bean对象的BeanInfo,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个描述器就可以获取某个属性对应的getter/setter方法。然后通过反射机制来调用这些方法。
例子:
private static Object getProperties(Object rp1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException
{
//简单的方法
/*PropertyDescriptor pd=new PropertyDescriptor(propertyName,rp1.getClass());
Method methodGetX= pd.getReadMethod();
Object retVal=methodGetX.invoke(rp1);*/
//复杂的方法
BeanInfo beanInfo=Introspector.getBeanInfo(rp1.getClass());
PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();
Object retVal=null;
for (PropertyDescriptor pd : pds)
{
if(pd.getName().equals(propertyName))
{
Method methodGetX=pd.getReadMethod();
retVal=methodGetX.invoke(rp1);
break;
}
}
return retVal;
}
Beanutils工具包:
特点:
方便快捷。
可以以属性链的形式存在。
例子:
/*
//JDK1.7的新特性
Map map={name:"zxx",age:18};
BeanUtils.setProperty(map,"name","lhm");
*/
System.out.println(BeanUtils.getProperty(rp1,"x"));
System.out.println(BeanUtils.getProperty(rp1,"x").getClass().getName());
Beanutils.setProperty(rp1,"x","9");
System.out.println(rp1.getX());
BeanUtils.setProperty(rp1,"birthday.time","111");
System.out.println(BeanUtils.getProperty(rp1,"birthday.time"));
//BeanUtils对对象的属性进行操作使用字符串的形式。
PropertyUtils.setProperty(rp1,"x",9);
System.out.println(PropertyUtils.getProperty(rp1,"x"));
//PropertyUtils对对象的属性进行操作是以属性本身的类型进行操作。
类加载器
概述和作用:
加载类的工具
java虚拟机可以安装多个类加载器,系统默认三个主要类加载器,每个类加载器负责加载特定位置的类
BootStrap,ExtClassLoader,AppClassLoader
类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,正是BootStrap。
java虚拟机中的所有类装载器采用父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其制定一个父级类装载器对象或默认使用采用系统类装载器为其父类加载。
委托机制:
当java虚拟机要加载一个类,到底要派出那个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,java虚拟机将使用加载类A的类加载器来加载类B
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类
每个类加载器加载类时,先委托给其上级类加载器
当所有父类的加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的子类,因为没有getChild()方法,即使有,不知道找哪一个。
|