黑马程序员--Java基础加强(高新技术)学习第二天

二十六、ArryList、HashSet的比较及 hashCode()分析

1、hashCode()

(1)首先引入哈希表的概念:

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

(2)对于Set类,要求元素是无序的、不重复的。怎么保证元素不重复?判断两个元素是否重复的依据是什么?添加一个元素时先计算元素的hashcode定位它放置的物理地址,如果这个位置上没有元素,直接存放。如果这个位置有元素,调用它的equals()方法与新元素进行比较,若相同,不保存;若不相同,就散列到其他地址保存。

(3)所以Java对于equals()方法和hashCode()方法就有这样的规定:

1)如果两个对象相同,那么他们的hashcode值一定相同。

2)如果两个对象的hashcode相同,它们并不一定相同。(相同指的就是equals()的比较)

2、equals()

(1) '=='是用来比较两个变量(基本类型和对象类型)的值是否相等的, 如果两个变量是基本类型的,那很容易,直接比较值就可以了。如果两个变量是对象类型的,那么它还是比较值,只是它比较的是这两个对象在栈中的引用(即地址)。

对象是放在堆中的,栈中存放的是对象的引用(地址)。由此可见'=='是对栈中的值进行比较的。如果要比较堆中对象的内容是否相同,那么就要重写equals方法了。

(2) Object类中的equals方法就是用'=='来比较的,所以如果没有重写equals方法,equals和==是等价的。

通常我们会重写equals方法,让equals比较两个对象的内容,而不是比较对象的引用(地址)因为往往我们觉得比较对象的内容是否相同比比较对象的引用(地址)更有意义。

(3)Object类中的hashCode是返回对象在内存中地址转换成的一个int值(可以就当做地址看)。所以如果没有重写hashCode方法,任何对象的hashCode都是不相等的。通常在集合类的时候需要重写hashCode方法和equals方法,因为如果需要给集合类(比如:HashSet)添加对象,那么在添加之前需要查看给集合里是否已经有了该对象,比较好的方式就是用hashCode。

(4) 注意的是String、Integer、Boolean、Double等这些类都重写了equals和hashCode方法,这两个方法是根据对象的内容来比较和计算hashCode的。(详细可以查看jdk下的String.java源代码),所以只要对象的基本类型值相同,那么hashcode就一定相同。

(5) equals()相等的两个对象,hashcode()一般是相等的,最好在重写equals()方法时,重写hashcode()方法; equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。 反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。在object类中,hashcode()方法是本地方法,返回的是对象的引用(地址值),而object类中的equals()方法比较的也是两个对象的引用(地址值),如果equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了。

3、ArrayList和HashSet的比较,代码如下:

import java.util.*;

 

public class HashDemo

{

    public static void main(String[] args)

    {

        String s1=new String("aaa");

        String s2=new String("bbb");

        String s3=new String("ccc");

        String s4=new String("bbb");

       

        Collection  c1=new ArrayList();

        c1.add(s1);

        c1.add(s2);

        c1.add(s3);

        c1.add(s4);

        System.out.println(c1);//[aaa, bbb, ccc, bbb]

       

        Collection  c2=new HashSet();

        c2.add(s1);

        c2.add(s2);

        c2.add(s3);

        c2.add(s4);

        System.out.println(c2);//[aaa, ccc, bbb]

       

        s2=new String("ddd");

        c2.remove(s2);

        System.out.println(c2);//[aaa, ccc, bbb],这里并没有删除成c2中s2原来的值

                               //因为s2的哈希值已经变了,HashSet中没有保存现在的哈希值。

    }

}

二十七、框架的概念及用反射技术开发框架的原理

二十八、用类加载器的方式管理资源和配置文件

import java.io.*;

import java.util.Collection;

import java.util.Properties;

 

public class LoaderResDemo

{

    public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException

    {

        InputStream ips=new FileInputStream("config.properties");//config.properties文件放在项目目录下。

        //使用Class的类加载器

        //InputStream ips=LoaderResDemo.class.getClassLoader().getResourceAsStream("cn/itcast/day02/config.properties");//bin\cn\itcast\day02目录下

        //InputStream ips=LoaderResDemo.class.getResourceAsStream("config.properties");//和LoaderResDemo类在同一个包中

        //InputStream ips=LoaderResDemo.class.getResourceAsStream("res/config.properties");//在LoaderResDemo类所在的子包res包中。

        //InputStream ips=LoaderResDemo.class.getResourceAsStream("/cn/itcast/day02/res/config.properties");//在LoaderResDemo类所在的子包res包中,使用绝对路径

        Properties props=new Properties();

        props.load(ips);

        ips.close();

        String className=props.getProperty("className");

       

        Collection c=(Collection) Class.forName(className).newInstance();

        c.add(new String("aaa"));

        c.add(new String("abc"));

        System.out.println(c);

    }

}

二十九、由内省引出JavaBean的讲解

JavaBean是一种特殊的Java类,主要用于传递数据信息,这种Java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。

如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问。JavaBean的属性就是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。如果方法名是setId,中文意思即为设置id。

一个类被当做JavaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到Java类内部的成员变量。

三十、对JavaBean的简单内省操作

import java.beans.*;

import java.lang.reflect.InvocationTargetException;

 

public class IntrospectorDemo

{

    public static void main(String[] args) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException

    {

        String propertyName="x";

        Point pt=new Point(3,5);

       

        Object obj = getProperty(propertyName, pt);

        System.out.println(obj);

       

        setProperty(pt,propertyName,100);

        System.out.println(pt.getX());

    }

 

    //使用java.beans.PropertyDescriptor类

    private static void setProperty(Object obj,String propertyName, Object value)

            throws IllegalAccessException, InvocationTargetException, IntrospectionException {

        PropertyDescriptor dp=new PropertyDescriptor(propertyName,obj.getClass());

        dp.getWriteMethod().invoke(obj, value);

    }

 

    private static Object getProperty(String propertyName, Object obj)

            throws IntrospectionException, IllegalAccessException,

            InvocationTargetException {

        PropertyDescriptor dp=new PropertyDescriptor(propertyName,obj.getClass());

        return dp.getReadMethod().invoke(obj);

    }

}

三十一、对JavaBean的复杂内省操作

    private static Object getProperty(String propertyName, Object obj)

            throws IntrospectionException, IllegalAccessException,

            InvocationTargetException {

        BeanInfo bi=Introspector.getBeanInfo(obj.getClass());

        PropertyDescriptor[] pds=bi.getPropertyDescriptors();

        Object value=null;

        for(PropertyDescriptor dp:pds)

        {

            if(dp.getName().equals(propertyName))

            {

                value=dp.getReadMethod().invoke(obj);

            }

        }

        return value;

    }

三十二、使用BeanUtils工具包操作JavaBean

import java.lang.reflect.InvocationTargetException;

 

import org.apache.commons.beanutils.BeanUtils;

import org.apache.commons.beanutils.PropertyUtils;

 

public class BeanUtilsDemo

{

    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException

    {

        Point pt=new Point(2,8);

        String x=BeanUtils.getProperty(pt, "x");

        System.out.println(x);

       

        BeanUtils.setProperty(pt, "x", "100");

        System.out.println(pt.getX());

       

        BeanUtils.setProperty(pt, "date.time", "111");

        System.out.println(pt.getDate().getTime());

       

        PropertyUtils.setProperty(pt, "y", 99);//

        System.out.println(PropertyUtils.getProperty(pt, "y"));

    }

}

三十三、了解和入门注解的应用

java.lang包中提供3个注解类型(Annotation Types):

1、@SuppressWarnings取消警告

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})

@Retention(RetentionPolicy.SOURCE)

public @interface SuppressWarnings {

    String[] value();

}

deprecation:使用了不赞成使用的类或方法时的警告

unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型。

fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告。

path:在类路径、源文件路径等中有不存在的路径时的警告。

serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告。

finally:任何 finally 子句不能正常完成时的警告。 all 关于以上所有情况的警告。

2、@Deprecated已过时

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})

public @interface Deprecated {

}

3、@Override覆盖

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.SOURCE)

public @interface Override {

}

三十四、注解的定义与反射调用

1、自定义annotation

import java.lang.annotation.*;

 

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface MyAnnotation {

    String value();

}

2、使用自定义annotation

@MyAnnotation("abc")

public class Program

{

    public static void main(String[] args)

    {

    }

}

3、对“应用了注解类的类”进行反射操作

@MyAnnotation("abc")

public class Program

{

    public static void main(String[] args)

    {

        if(Program.class.isAnnotationPresent(MyAnnotation.class))

        {

            MyAnnotation annotation=Program.class.getAnnotation(MyAnnotation.class);

            System.out.println(annotation.value());

        }

    }

}

4、java.lang.annotation包

(1)Annotation接口

(2)ElementType枚举

ANNOTATION_TYPE 注释类型声明

CONSTRUCTOR 构造方法声明

FIELD 字段声明(包括枚举常量)

LOCAL_VARIABLE 局部变量声明

METHOD 方法声明

PACKAGE 包声明

PARAMETER 参数声明

TYPE 类、接口(包括注释类型)或枚举声明

(3)RetentionPolicy枚举

CLASS 编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释。

RUNTIME 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。

SOURCE 编译器要丢弃的注释。

(4)注视类型:Documented、Inherited、Retention、Target

三十五、为注解添加属性

1、为注解添加属性和应用属性

2、为属性指定缺省值

3、数组类型的属性

如果数据属性中只有一个元素,属性值部分可以省略大括号。

4、枚举类型的属性

5、注解类型的属性

6、用反射的方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法

7、注解的属性类型只能是:基本数据类型、String类型、Class类型、枚举类型、注解类型和以上类型的数组。

三十六、入门泛型的基本应用

1、ArrayList al=new ArrayList();

al.add(1);

al.add(1L);

int i=(Integer)al.get(1);//编译要强制类型转换且运行时出错!

2、ArrayList<Integer> al=new ArrayList<Integer>();

al.add(1);

//al.add(1L);

//al.add(“abc”);//这两行编译时报告语法错误

Int i=al.get(0);//不需要再进行类型转换

3、泛型是提供给javac编译器使用的,可以先定集合中的输入类型,让编译器阻止源程序中的非法输入,编译器编译带参数说明的集合时会去掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据。

三十七、泛型的内部原理及更深应用

1、使用反射跳过编译器

public class GenericDemo

{

    public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException

    {

        ArrayList<String> al1=new ArrayList<String>();

        Constructor c=String.class.getConstructor(StringBuffer.class);

        String s=(String)c.newInstance(new StringBuffer("abc"));

        System.out.println(s);

       

        ArrayList<Integer> al2=new ArrayList<Integer>();

        System.out.println(al1.getClass());

        System.out.println(al2.getClass());

        System.out.println(al1.getClass()==al2.getClass());//true

       

        al2.getClass().getMethod("add", Object.class).invoke(al2, "aaa");

        System.out.println(al2.get(0));

    }

}

2、了解泛型

(1)参数化类型与原始类型的兼容性:

1)参数化类型可以引用一个原始类型的对象,编译报告警告,例如

Collection<String> c=new Vector();

2)原始类型可以引用一个参数化类型的对象,编译报告警告,例如

Collection c=new Vector<String>();

原来的方法接受一个集合参数,新的类型也要能传进去。

(2)参数化类型不考虑类型参数的继承关系:

Vector<String> v=new Vector<Object>();//错误

Vector<Object> v=new Vector<String>();//错误

(3)在创建数组实例时,数组的元素不能使用参数化的类型,下面语句有错误:

Vector<Integer> v[]=new Vector<Integer>[10];

(4)Vector v1=new Vector<String>();//没有错误

Vector<Object> v2=v1; //没有错误

posted on 2012-03-29 00:53  黑马程序员  阅读(246)  评论(0编辑  收藏  举报

导航