黑马程序员 Java基础难点总结day3

来源:互联网 发布:军人网络保密准则 编辑:程序博客网 时间:2024/06/07 06:54

 ------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------

21. "=="和equals方法究竟有什么区别?

    1.  ==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。

 

如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如Objet obj = new Object();变量obj是一个内存,new Object()是另一个内存,此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就需要用==操作符进行比较。

 

    2.    equals方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。例如,对于下面的代码:

 

     String a=new String("foo");

 

     String b=new String("foo");

 

     两条new语句创建了两个对象,然后用a,b这两个变量分别指向了其中一个对象,这是两个不同的对象,它们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式a==b将返回false,而这两个对象中的内容是相同的,所以,表达式a.equals(b)将返回true。

 

     在实际开发中,我们经常要比较传递进行来的字符串内容是否等,例如,String input = …;input.equals(“quit”),许多人稍不注意就使用==进行比较了,这是错误的,随便从网上找几个项目实战的教学视频看看,里面就有大量这样的错误。记住,字符串的比较基本上都是使用equals方法。

 

     如果一个类没有自己定义equals方法,那么它将继承Object类的equals方法,Object类的equals方法的实现代码如下:

 

     boolean equals(Object o){

 

     return this==o;

 

}

 

     这说明,如果一个类没有自己定义equals方法,它默认的equals方法(从Object 类继承的)就是使用==操作符,也是在比较两个变量指向的对象是否是同一对象,这时候使用equals和使用==会得到同样的结果,如果比较的是两个独立的对象则总返回false。如果你编写的类希望能够比较该类创建的两个实例对象的内容是否相同,那么你必须覆盖equals方法,由你自己写代码来决定在什么情况即可认为两个对象的内容是相同的。

 

22. Integer与int的区别

 

    int是java提供的8种原始数据类型之一。Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。在JSP开发中,Integer的默认为null,所以用el表达式在文本框中显示时,值为空白字符串,而int默认的默认值为0,                

所以用el表达式在文本框中显示时,结果为0,所以,int不适合作为web层的表单数据的类型。

    在Hibernate中,如果将OID定义为Integer类型,那么Hibernate就可以根据其值是否为null而判断一个对象是否是临时的,如果将OID定义为了int类型,还需要在hbm映射文件中设置其unsaved-value属性为0。

    另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。

 

 

23.hashCode与equals的区别

     一般来讲,equals这个方法是给用户调用的,如果你想判断2个对象是否相等,你可以重写equals方法,然后在代码中调用,就可以判断他们是否相等了。简单来讲,equals方法主要是用来判断从表面上看或者从内容上看,2个对象是不是相等。举个例子,有个学生类,属性只有姓名和性别,那么我们可以认为只要姓名和性别相等,那么就说这2个对象是相等的。

 

hashcode方法一般用户不会去调用,比如在hashmap中,由于key是不可以重复的,他在判断key是不是重复的时候就判断了hashcode这个方法,而且也用到了equals方法。这里不可以重复是说equals和hashcode只要有一个不等就可以了!所以简单来讲,hashcode相当于是一个对象的编码,就好像文件中的md5,他和equals不同就在于他返回的是int型的,比较起来不直观。我们一般在覆盖equals的同时也要覆盖hashcode,让他们的逻辑一致。举个例子,还是刚刚的例子,如果姓名和性别相等就算2个对象相等的话,那么hashcode的方法也要返回姓名的hashcode值加上性别的hashcode值,这样从逻辑上,他们就一致了。

 

要从内容上判断2个对象是否相等,用==就可以了。

 

 

24. 接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承具体类(concrete class)? 抽象类中是否可以有静态的main方法?

    接口可以继承接口。抽象类可以实现(implements)接口,抽象类是否可继承具体类。抽象类中可以有静态的main方法。

    只有记住抽象类与普通类的唯一区别就是不能创建实例对象和允许有abstract方法。

 

 

25. java中实现多态的机制是什么?

 

     靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

 

 

26. String 和StringBuffer的区别

 

     JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。String类表示内容不可改变的字符串。而StringBuffer类表示内容可以被修改的字符串。当你知道字符数据要改变的时候你就可以使用StringBuffer。典型地,你可以使用StringBuffers来动态构造字符数据。另外,String实现了equals方法,new String(“abc”).equals(new String(“abc”)的结果为true,而StringBuffer没有实现equals方法,所以,new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的结果为false。

 

接着要举一个具体的例子来说明,我们要把1到100的所有数字拼起来,组成一个串。

 

StringBuffer sbf = new StringBuffer(); 

 

for(int i=0;i<100;i++)

 

{

 

sbf.append(i);

 

}

 

上面的代码效率很高,因为只创建了一个StringBuffer对象,而下面的代码效率很低,因为创建了101个对象。

 

String str = new String(); 

 

for(int i=0;i<100;i++)

 

{

 

str = str + i;

 

}

 

    在讲两者区别时,应把循环的次数搞成10000,然后用endTime-beginTime来比较两者执行的时间差异,最后还要讲讲StringBuilder与StringBuffer的区别。

 

     String覆盖了equals方法和hashCode方法,而StringBuffer没有覆盖equals方法和hashCode方法,所以,将StringBuffer对象存储进Java集合类中时会出现问题。

 

27. StringBuffer与StringBuilder的区别

 

     StringBuffer和StringBuilder类都表示内容可以被修改的字符串,StringBuilder是线程不安全的,运行效率高,如果一个字符串变量是在方法里面定义,这种情况只可能有一个线程访问它,不存在不安全的因素了,则用StringBuilder。如果要在类里面定义成员变量,并且这个类的实例对象会在多线程环境下使用,那么最好用StringBuffer。

 

28. 运行时异常与一般异常有何异同?

 

     异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。

29. error和exception有什么区别?

 

     error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。

 

 

30. Java中的异常处理机制的简单原理和应用。

 

    异常是指java程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,现实生活中的事件可以包含事件发生的时间、地点、人物、情节等信息,可以用一个对象来表示,Java使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。

    Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception,Error 表示应用程序本身无法克服和恢复的一种严重问题,程序只有死的份了,例如,说内存溢出和线程死锁等系统问题。Exception表示程序还能够克服和恢复的问题,其中又分为系统异常和普通异常,系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件死掉,例如,数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。

    java为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须try..catch处理或用throws声明继续抛给上层调用方法处理,所以普通异常也称为checked异常,而系统异常可以处理也可以不处理,所以,编译器不强制用try..catch处理或用throws声明,所以系统异常也称为unchecked异常。

 

 

31. 多线程有几种实现方法?同步有几种实现方法?

 

    多线程有两种实现方法,分别是继承Thread类与实现Runnable接口

    同步的实现方面有两种,分别是synchronized,wait与notify

    wait():使一个线程处于等待状态,并且释放所持有的对象的lock。

    sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。

notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

    Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

 

32.HashMap和Hashtable的区别

    HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下,效率要高于Hashtable。

     HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。

     HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。

     Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。

    最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。

     Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

     就HashMap与HashTable主要从三方面来说。

一.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现 。

二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的 。

三.值:只有HashMap可以让你将空值作为一个表的条目的key或value 。

 

33. 字节流与字符流的区别

 

    要把一片二进制数据数据逐一输出到某个设备中,或者从某个设备中逐一读取一片二进制数据,不管输入输出设备是什么,我们要用统一的方式来完成这些操作,用一种抽象的方式进行描述,这个抽象描述方式起名为IO流,对应的抽象类为OutputStream和InputStream ,不同的实现类就代表不同的输入和输出设备,它们都是针对字节进行操作的。

     在应用中,经常要完全是字符的一段文本输出去或读进来,用字节流可以吗?计算机中的一切最终都是二进制的字节形式存在。对于“中国”这些字符,首先要得到其对应的字节,然后将字节写入到输出流。读取时,首先读到的是字节,可是我们要把它显示为字符,我们需要将字节转换成字符。由于这样的需求很广泛,人家专门提供了字符流的包装类。

    底层设备永远只接受字节数据,有时候要写字符串到底层设备,需要将字符串转成字节再进行写入。字符流是字节流的包装,字符流则是直接接受字符串,它内部将串转成字节,再写入底层设备,这为我们向IO设别写入或读取字符串提供了一点点方便。

    字符向字节转换时,要注意编码的问题,因为字符串转成字节数组,其实是转成该字符的某种编码的字节形式,读取也是反之的道理。

 

 

 

 

 

Set ( 无序的集合 )

Set 下分别有2个小弟 : HashSet , TreeSet

Set 特点:元素是无序的,元素不可以重复(不可以存入同一样的元素),存入跟取出的顺序不一定一致。

 

 

HashSet :底层数据结构是哈希表结构, 它是基于HashMap实现的。

    特点:通过元素的 hashCode 和 equals 方法 判断元素的唯一性。

    系统先调用hashCode 方法,只有hashCode相同,才会执行equals 方法,反之不执行。

 

 

因为集合存储的是对象,而对象不一定具备比较性行为,所以往往通过HashSet存入多个对象时,明明是相同的对象,结果还是存进去了。这样违背了Set 集合的特点。

因此,为了让对象自身具备比较性,我们需要通过复写对象中的 HashCode方法和 equals方法,

因为HashSet 底层就是调用这个方法来判断元素是否是同一个元素,如何相同,不写入,否则,写入。

HashSet: 示例:

import java.util.*; 

class Person 

    String name; 

    int age; 

    Person(String name, int age) 

    { 

        this.name = name; 

        this.age  = age; 

    } 

    public int hashCode() //复写hashCode方法,返回一个自定义的哈希值。 

    { 

        return name.hashCode() + age * 12; 

    } 

    public boolean equals(Object obj) //复写eqausl方法,自定义一套比较规则。 

    { 

        if(!(obj instanceof Person)) 

            return false; 

        Person p = (Person) obj; 

        return this.name.equals(p.name) && this.age == p.age; 

    } 

    public String toString() 

    { 

        return "Name:"+name+" Age:"+age; 

    } 

class HashSetDemo 

    public static void main(String[] args)  

    { 

        HashSet hs = new HashSet(); 

        hs.add(new Person("张三",18)); 

        hs.add(new Person("张三",18));  

        hs.add(new Person("李四",20));   

        Iterator it = hs.iterator(); 

        while(it.hasNext()) 

        { 

            Person p = (Person) it.next(); 

            System.out.println(p.toString()); 

        } 

    } 

 

 

TreeSet:底层数据结构是二叉树。

特点:可以对元素进行排序,保证元素的唯一性的依据:通过comparTo 方法 return 0,不过一般情况重写comparTo方法。

TreeSet 集合排序的两种方式 :

第一种:让元素自身具备比较性。

    方法:元素实现 Comparable 接口,并且复写里面的 comparTo 方法 。

第二种:当元素自身不具备比较性,或者具备的比较性不是所需要的,这时可以让集合自身具备比较性。

    方法:定义一个类,实现 Comparator 接口,并且复写里面的 compare 方法,使用方式如下

注意:根据 comparTo方法的返回值为负整数、零或正整数,来断定对象是小于、等于还是大于指定对象。

第一种排序方式:

import java.util.*; 

class Person implements Comparable   //实现Comparable 接口 

    String name; 

    int age; 

    Person(String name, int age) 

    { 

        this.name = name; 

        this.age  = age; 

    } 

    public int compareTo(Object obj)  //复写compartTo方法,制定一套排序方案 

    { 

        if(!(obj instanceof Person)) 

            throw new RuntimeException("不是Psrson对象!"); 

        Person p = (Person) obj; 

        if(this.age > p.age) 

            return 1; 

        if(this.age == p.age) 

        { 

            return this.name.compareTo(p.name); 

        } 

        return -1;  

    } 

    public String toString() 

    { 

        return "Name:"+name+" Age:"+age; 

    } 

class HashSetDemo 

    public static void main(String[] args)  

    { 

        TreeSet ts = new TreeSet(); 

        ts.add(new Person("张三",18)); 

        ts.add(new Person("张四",18));  

        ts.add(new Person("李四",20));   

        Iterator it = ts.iterator(); 

        while(it.hasNext()) 

        { 

            Person p = (Person) it.next(); 

            System.out.println(p.toString()); 

        } 

    } 

}

 

 

第二种排序方式:

[java] view plaincopy

import java.util.*; 

class Person 

    String name; 

    int age; 

    Person(String name, int age) 

    { 

        this.name = name; 

        this.age  = age; 

    } 

    public String toString() 

    { 

        return "Name:"+name+" Age:"+age; 

    } 

class MyCompare implements Comparator 

    public int compare(Object o1, Object o2) 

    { 

        Person p1 = (Person) o1; 

        Person p2 = (Person) o2; 

        int name = p1.name.compareTo(p2.name); 

        if(name == 0) 

        { 

            if(p1.age > p2.age) 

                return 1; 

            if(p1.age == p2.age) 

                return 0; 

            return -1; 

        } 

        return name; 

    } 

    public static void main(String[] args)  

    { 

        TreeSet ts = new TreeSet(new MyCompare()); 

        ts.add(new Person("张三",18)); 

        ts.add(new Person("王五",17));  

        ts.add(new Person("李四",20));   

        Iterator it = ts.iterator(); 

        while(it.hasNext()) 

        { 

            Person p = (Person) it.next(); 

            System.out.println(p.toString()); 

        } 

    } 

 

 

0 0