几个老的JAVA问题

1、interface与abstract class的区别
2、collection中类的层次结构

 

7.7
.    抽象类 
1 ) Abstract(抽象)可以修饰类、方法
如果将一个类声明为abstract,此类不能生成对象,只能被继承使用。
Abstract类的设计是将子类的共性最大限度的抽取出来,以提高程序的统一性。

2 ) 一个类中包含有抽象方法必须声明为抽象类;
如果一个类中有一个抽象方法,那么这个类一定为一个抽象类。
反之,如果一个类为抽象类,那么其中可能有非抽象的方法。

3 ) 抽象类不能实例化,但仍可以声明;
Abstract类可以作为编译时类型,但不能作为运行时类型。

4 ) 子类继承抽象类必须实现其中抽象方法
当abstract用于修饰方法时,此时该方法为抽象方法,此时方法不需要实现,实现留给子类覆盖,子类覆盖该方法之后方法才能够生效。
注意比较:
private void print(){};此语句表示方法的空实现。
Abstract
void print(); 此语句表示方法的抽象,无实现


7.8 . 接口 (模板方法模式)
1 ) 接口是抽象类的另外一种形式(没有实例变量的抽象类);
2 ) 在一个接口中所有方法都是抽象方法;
3 ) 接口中所有变量都必须被定义为final static
4 ) 接口可以继承多个接口。
5 ) 可插入性的保障――工程模式
6 ) 是规范的制订者和规范的实现者分开。――JDBC
注:
1 ) 接口中的方法自动被置为public, 因经,在接口中声明方法并不需要提供public关键字。但在实现接口时,必须把方法声明为public。

接口与抽象类的区别:
接口 抽象类
无实现方法 可以有非抽象方法――实现代码
可以把子类的共有代码
提取出来放在抽象类中

在JAVA中的接口分类:
普通
----- 有方法和属性
常量
----- 存常量
标记
----- 没有方法,属性,只为了做编译类型的标记
实例:
简单工厂模式(接口和多态的实际使用)
public interface Car {
public void brake();
public void grade();
public void engineer();

}
public class BMW implements Car{
private String name;

public String getName() {
return name;
}
public void setName(String name) {
this .name = name;
}
public void brake() {
// TODO Auto-generated method stub
System.out.println( " BMWcar's brake() " );
}
public void engineer() {
// TODO Auto-generated method stub
System.out.println( " BMWcar's engieer() " );
}
public void grade() {
// TODO Auto-generated method stub
System.out.println( " BMWcar's greade() " );
}
/**
*
@param args
*/
}
Public
class Banz implements Car{
……
}
Public
class Factory{
Public
static Car factory(String name){
If(name.equals(“BMW”){
Return
new BMWCar(); // 返回一个引用
}
If(name.equals(“Banz”){
Return
new BanzCar();
}
}
}
注意:
1 这个问题不是对象有没有toString方法的问题,而是用接口作为编译时类型能否调用的问题.
2 接口就是接口,尽管本质上是抽象类,但是总归不是类,接口没有父类,Object类绝不是接口的父类,这种理解大错而特错 !!!

3 .我们来看看虚拟机规范是怎么说的
JVM会解析类型为CONSTANT_InterfaceMethodref_info的常量池的入口,会按照如下步骤执行接口方法解析:
1 ) 检查接口是否具有用户调用的方法
2 ) 检查接口的父接口是否具有用户调用的方法
3 ) 检查java.lang.Object是否具有用户调用的方法
4 ) 抛出NoSuchMethodException

因此,不是接口类型有toString方法,用反射是看不到这个方法的,而是虚拟机在作方法调用连接时,会自动去找Object类中的方法.虚拟机就是这么工作的.

所以,对以接口作为编译时类型的方法调用,根据以上说的第三点,Object类中的方法都可以得到调用

7.12 . 1 . 集合接口类层次
位于package java.util.
* ;
Collection

| ˉˉˉˉˉˉ |
Set List Map
↑ ↑
| |
SortedSet SortedMap

1 ) Set: 集合类中不允许有重复对象;
2 ) SortedSet: 和Set接口同,但元素按升序排列;
3 ) List: 元素加载和移出时按照顺序,可以保存重复对象。
4 ) Map: (key - value对)存储了唯一关键字辨识和对应的值。
5 ) SortedMap: 和Map类同,但对象按他们关键字的升序排列。

7.12 . 2 . 集合类层次
(注:JAVA1.5对JAVA1.4的最大改进就是增加了对范型的支持)

Collection

| ˉˉˉˉˉˉ |
HashSet LinkedList Hashtable
(Set) Vector, ArrayList Hashmap
(List) (Map)
↑ ↑
| |
TreeSet TreeMap
(SortedSet) (SortedMap)
Collection接口的方法:
add(Object o)
addAll(Collection c)
contains(Object o)
containsAll(Collection c)
remove(Object o)
removeAll(Collection c)
clear()
equals(Object o)
isEmpty()
iterator()
size()
toArray()
toArray(Object[] o)


7.12 . 3 . 五个最常用的集合类之间的区别和联系
1 .ArrayList: 元素单个,效率高,多用于查询
2 .Vector: 元素单个,线程安全,多用于查询
3 .LinkedList:元素单个,多用于插入和删除
4 .HashMap: 元素成对,元素可为空
5 .HashTable: 元素成对,线程安全,元素不可为空

ArrayList
底层是Object数组,所以ArrayList具有数组的查询速度快的优点以及增删速度慢的缺点。
而在LinkedList的底层是一种双向循环链表。在此链表上每一个数据节点都由三部分组成:前指针(指向前面的节点的位置),数据,后指针(指向后面的节点的位置)。最后一个节点的后指针指向第一个节点的前指针,形成一个循环。
双向循环链表的查询效率低但是增删效率高。
ArrayList和LinkedList在用法上没有区别,但是在功能上还是有区别的。

LinkedList
经常用在增删操作较多而查询操作很少的情况下:队列和堆栈。
队列:先进先出的数据结构。
栈:后进先出的数据结构。
注意:使用栈的时候一定不能提供方法让不是最后一个元素的元素获得出栈的机会。

Vector
(与ArrayList相似,区别是Vector是重量级的组件,使用使消耗的资源比较多。)
结论:在考虑并发的情况下用Vector(保证线程的安全)。
在不考虑并发的情况下用ArrayList(不能保证线程的安全)。

面试经验(知识点):
java.util.stack(stack即为堆栈)的父类为Vector。可是stack的父类是最不应该为Vector的。因为Vector的底层是数组,且Vector有get方法(意味着它可能访问到并不属于最后一个位置元素的其他元素,很不安全)。
对于堆栈和队列只能用push类和get类。
Stack类以后不要轻易使用。
实现栈一定要用LinkedList。
(在JAVA1.5中,collection有queue来实现队列。)

Set
- HashSet实现类:
遍历一个Set的方法只有一个:迭代器(interator)。
HashSet中元素是无序的(这个无序指的是数据的添加顺序和后来的排列顺序不同),而且元素不可重复。
在Object中除了有finalize(),toString(),equals(),还有hashCode()。
HashSet底层用的也是数组。
当向数组中利用add(Object o)添加对象的时候,系统先找对象的hashCode:
int hc = o.hashCode(); 返回的hashCode为整数值。
Int I
= hc % n;(n为数组的长度),取得余数后,利用余数向数组中相应的位置添加数据,以n为6为例,如果I = 0则放在数组a[ 0 ]位置,如果I = 1 ,则放在数组a[ 1 ]位置。如果equals()返回的值为true,则说明数据重复。如果equals()返回的值为false,则再找其他的位置进行比较。这样的机制就导致两个相同的对象有可能重复地添加到数组中,因为他们的hashCode不同。
如果我们能够使两个相同的对象具有相同hashcode,才能在equals()返回为真。
在实例中,定义student对象时覆盖它的hashcode。
因为String类是自动覆盖的,所以当比较String类的对象的时候,就不会出现有两个相同的string对象的情况。
现在,在大部分的JDK中,都已经要求覆盖了hashCode。
结论:如将自定义类用hashSet来添加对象,一定要覆盖hashcode()和equals(),覆盖的原则是保证当两个对象hashcode返回相同的整数,而且equals()返回值为True。
如果偷懒,没有设定equals(),就会造成返回hashCode虽然结果相同,但在程序执行的过程中会多次地调用equals(),从而影响程序执行的效率。

我们要保证相同对象的返回的hashCode一定相同,也要保证不相同的对象的hashCode尽可能不同(因为数组的边界性,hashCode还是可能相同的)。

例子:
public int hashCode(){
return name.hashcode() + age;
}
这个例子保证了相同姓名和年龄的记录返回的hashCode是相同的。

使用hashSet的优点:
hashSet的底层是数组,其查询效率非常高。而且在增加和删除的时候由于运用的hashCode的比较开确定添加元素的位置,所以不存在元素的偏移,所以效率也非常高。因为hashSet查询和删除和增加元素的效率都非常高。
但是hashSet增删的高效率是通过花费大量的空间换来的:因为空间越大,取余数相同的情况就越小。HashSet这种算法会建立许多无用的空间。
使用hashSet类时要注意,如果发生冲突,就会出现遍历整个数组的情况,这样就使得效率非常的低。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值