Java基础知识

1、介绍一下java的集合类?分别适合什么场景?

2、简述hashtable的get和put函数的实现。

3、举例final的使用场景?

4、简述public、private、protected的作用域。

5、简述接口与抽象类的区别。

6、简述java的序列化与反序列化的原理。

7、用java serversocket编写一个服务器向客户端发送 “hello“,编写客户端接收数据并打印出来。

-----------------------------------------------------------------------------------------------------------------------

如果你能快速回答上述问题,以下的就不用看了。

----------------------------------------------------------------------------------------------------------------------- 

1、介绍一下java的集合类?分别适合什么场景?

  Java集合类有两类,一是Collection,二是Map,两个都是接口。其中Collection迭代器Iterable接口,所以Collection集合对象都有foreach特性。Collection有Set、List、Queue接口,Map有HashMap、Hashtable、SortedMap、WeakHashMap、IdentityHashMap、EnumMap。

Collection
    Set                   // 接口。注意:为Set集合里的元素实现equals(Object)方法;对Set的构造函数,传入的Collection不能包含重复的元素
        HashSet           // 存入元素时,HashSet会调用对象的hashCode()方法来得到对象的hashCode值,再根据HashCode决定该对象在HashSet中的存储位置
            LinkedHashSet // 用链表维护元素的次序
        SortedSet         // 接口。此接口主要用于排序操作
            TreeSet       // 确保集合元素处于排序状态
        EnumSet
    List                  // 接口。表示有序、可重复的集合
        ArrayList
        Vector            // 老SDK中的集合对象,跟ArrayList差不多
            Stack
        LinkedList    
Queue // 接口。队列 PriorityQueue // 优先队列,每次取出的是具有最高优先权的元素 Deque // 双端队列 ArrayDeque // 一个基于数组的双端队列 LinkedList
Map                       // Map的key不允许重复
    HashMap               // 不保证key-value对的顺序。非线程安全,允许null key, null value
        LinkedHashMap     // 使用双向链表来维护key-value对的次序
    Hashtable             // 古老的Map实现类。线程安全,不允许null key, null value
        Properties        // 可以把Map对象和属性文件关联起来
    SortedMap             // 接口
        TreeMap           // 红黑树数据结构,保证所有的key-value对处于有序状态
    WeakHashMap           // 保留对实际对象的弱引用
    IdentityHashMap       // 当两个key严格相等(key1 == key2)时,IdentityHashMap才认为两个key相等
    EnumMap               // 所有key都必须是单个枚举类的枚举值
  • TreeSet支持两种排序方式: 自然排序、定制排序,因为它必须实现Comparable接口。
  • HashSet、TreeSet、EnumSet都是非线程安全,不过可以通过以下方式解决:
SortedSet sortedSet = Collections.synchronizedSortedSet(new TreeSet(...));
  • List就是一个"线性表接口",ArrayList基于数组、LinkedList基于链表是线性表的两种典型实现。ArrayList通过ensureCapacity(int minCapacity)增加数组长度,从而减少重新分配的次数,提供性能。trimToSize()调整Object[]数组长度为当前元素的个数,减少ArrayList集合对象占用的内存空间。Stack保证了后进先出,LinkedList同时表现出了双端队列、栈的用法。PriorityQueue支持的排序和TreeSet一致。Deque代表了双端队列。
  • HashMap和Hashtable的差异见注释。TreeMap通常比HashMap、Hashtable要慢,因为TreeMap底层采用红黑树来管理key-value对。使用TreeMap的一个好处是:TreeMap中的key-value对总是处于有序状态,无须专门进行排序操作。
2、简述hashtable的get和put函数的实现

  Hashtable的get() 获取key对应的value,没有就返回null;put() 对外提供接口,让Hashtable对象可以通过put()将“key-value”添加到Hashtable中。

get()原理:

  • 首先通过key的hashCode计算索引
  • 通过key对应的Entry链表找出哈希值、键值和key都相等的元素
public synchronized V get(Object key) {
    Entry tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            return e.value;
        }
    }
    return null;
}

put()原理:

  • 若Hashtable中已存在键为key的键值对, 则用新的value替换旧的value
  • 若Hashtable中不存在键为key的键值对,将修改统计数modCount+1
  • 若Hashtable实际容量 > 阈值, 则调整Hashtable的大小
  • 将Hashtable中index位置的Entry链表保存
  • 创建新的Entry节点,并将新的Entry插入Hashtable的index位置,并设置新的Entry的下一个元素
  • 将Hashtable的实际容量+1
public synchronized V put(K key, V value) {
    // Hashtable中不允许null value
    if (value == null) {
        throw new NullPointerException();
    }

    Entry tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            V old = e.value;
            e.value = value;
            return old;
        }
    }

    modCount++;
    if (count >= threshold) {
        // Rehash the table if the threshold is exceeded
        rehash();

        tab = table;
        index = (hash & 0x7FFFFFFF) % tab.length;
    }

    Entry<K,V> e = tab[index];
    tab[index] = new Entry<K,V>(hash, key, value, e);
    count++;
    return null;
}
3、举例final的使用场景

  final关键字用于修饰类时表示该类不允许被继承,修饰方法时表示该方法在派生类里不允许被覆写(override),修饰变量时(静态变量、实例成员变量、形式参数和局部变量)表示该变量的值不可变。

  • final保证只被赋值一次
  • 可以让匿名类直接进行引用
  • JVM可以对final变量的使用进行优化(且不用考虑线程间可见性等问题)
4、简述public、private、protected的作用域
作用域 当前类 同一package 子类 其它package
public
protected ×
private × × ×
5、简述接口与抽象类的区别

  在抽象类中可以有自己的数据成员,也可以有非抽象的成员方法;而在接口中,只能有静态的不能被修改的数据成员(static final类 型),所有的成员方法默认都是共有、抽象的。 《Effective Java》告诉我们,接口优于抽象类:

  • 现有的类易于更新,以实现新的接口
  • 接口是定义混合类型的理想选择
  • 接口允许我们构造非层次结构的类型框架
6、简述java的序列化与反序列化的原理

  把对象转换为字节流的过程称为对象的序列化,相反的过程叫反序列化。Java中,实现Serializable或者Externalizable接口的类才能被序列化。其中Externalizable接口继承 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而实现Serializable接口的类可以采用默认的序列化方式。

对象序列化步骤:

  • 创建一个对象输出流
  • 通过对象输出流的writeObject()方法写对象

对象反序列化步骤:

  • 创建一个对象输入流
  • 通过对象输入流的readObject()方法读取对象
private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject();
    out.writeInt(25);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    int age = in.readInt();
}
7、用java serversocket编写一个服务器向客户端发送 “hello“,编写客户端接收数据并打印出来

服务端:

public class SocketServer {
    public static void main(String args[]) {
        try {
            // 1、指定服务器端的端口号
            ServerSocket serverSocket = new ServerSocket(7777);
            while (true) {
                // 2、建立连接
                Socket socket = serverSocket.accept();
                // 3、打开输出流
                OutputStream outputStream = socket.getOutputStream();
                // 4、封装输出流
                DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
                // 5、向客户端发送数据
                dataOutputStream.writeUTF("hello");
                // 6、关闭打开的输出流
                dataOutputStream.close();
                // 7、关闭打开的socket对象
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端:

public class SocketClient {
    public static void main(String args[]) {
        try {
            // 1、创建本地socket对象
            Socket socket = new Socket("127.0.0.1", 7777);
            // 2、打开输入流
            InputStream inputStream = socket.getInputStream();
            // 3、封装输入流
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            // 4、打印服务器端发送过来hello
            System.out.println(dataInputStream.readUTF());
            // 5、关闭输入流
            dataInputStream.close();
            // 6、关闭打开的socket对象
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

参考:

Java集合类: Set、List、Map、Queue使用场景梳理

posted @ 2016-01-14 13:51  LeoLiang  阅读(753)  评论(0编辑  收藏  举报