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(); } } }
参考: