赞
踩
基础知识主要涉及java语法及常见知识点。包括IO、类加载,反射、注解、异常,集合类等。下面以这几个大类分别做汇总,并引出其中若干小问题。
以下就分别从这几个方面来讲讲java基础知识。
上图是java Collection主要类成员。基本上Collection
为集合框架的最抽象接口。其下根据功能需要又分为Set
、List
和Queue
3个主要接口。分别表示无序不重复集合(Set)、有序集合和队列。
HashMap
完成。HashMap的key即为HashSet的值。value则为一固定的Object对象。 // 部分HashSet源码
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
LinkedHashSet
实现由LinkedHashMap
完成。而LinkedHashMap
是HashMap和LinkedList的结合体。由HashMap保证Map属性,再由链表将Map中的元素以插入顺序链起来。LinkedHashSet调用了HashSet的构造函数。 // 此构造函数专为LinkedHashSet使用
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
TreeSet
由TreeMap
实现,同上一样,TreeSet调用了TreeMap的方法。 // TreeSet部分源码
private static final Object PRESENT = new Object();
public TreeSet() {
this(new TreeMap<E,Object>());
}
可以看到,和之前HashSet之于HashMap是类似的处理。TreeMap是一个有序的Map结构,底层通过红黑树实现。
Queue
队列数据结构可简单分为双端队列(Deque)和阻塞队列(BlockingQueue)和优先级队列(PriorityQueue)。主要类结构图已如上图所示。CopyOnWriteArrayList
写时复制数组。操作 | 返回特殊值 | 抛出异常 |
---|---|---|
插入(队尾) | offer(e) | add(e) |
删除(队首) | poll() | remove() |
查询(获取队首元素) | peek() | element() |
对于阻塞队列(BlockingQueue
)而言,又多了阻塞和超时的方法。
操作 | 返回特殊值 | 抛出异常 | 阻塞 | 超时退出 |
---|---|---|---|---|
插入(队尾) | offer(e) | add(e) | put(e) | offer(e,timeout,unit) |
删除(队首) | poll() | remove() | take() | poll(timeout,unit) |
查询(获取队首元素) | peek() | element() | 无 | 无 |
以上是Map的类结构图。除了现已不再推荐使用的Hashtable
以外,主要有3类Map。ConcurrentHashMap
基于Hash表和分段锁实现,线程安全,在多线程环境最常使用的Map。HashMap
基于Hash表实现,日常使用最广的Map。TreeMap
,基于红黑树实现。插入元素排序。
有关java集合的问题挺多,常见如HashMap底层数据结构和实现原理,ConcurrentHashMap原理等。这里总结下以备忘,常看常新。
类的加载过程是: 加载->连接(验证,准备,解析)->初始化->使用->卸载。具体解析如下:
加载 : 将class二进制字节流(文件或jar包或字节流等各种形式)加载到内存中。
验证 :包括文件格式、元数据,以及字节码等验证,看是否合法。
准备 : 为类变量分配内存,设初始值(零值)等。
解析 : 符号引用替换为直接引用。加载这个类所引用的其他所有类。
初始化: 初始化类变量或其他资源。是执行方法的过程。JVM会保证在执行时,其父类的被执行(接口则不会执行父类cinit,除非被用到)。是线程安全的,由JVM保证。
注意:
三种类加载器,分别是BootstrapClassLoader
、ExtClassLoader
和AppClassLoader
加载器。从顶到下排列。当前类加载器会先将加载任务委托给父类加载,父类一级一级加载,直到BootstrapClassLoader
加载不了(加载不了的意思是指其classpath中没有要加载的类),则向下流转。直到其父类都不能加载,自己才尝试加载。
字节流、字符流四个主要接口。为InputStream、 OutputStream、Writer和Reader。前2个为字节流,后2个为字符流。主要方法为write(int b)
和read()
。
对于字节流InputStream,read()
方法返回int值。
public abstract int read() throws IOException;
返回的int值范围在-1 - 255
之间,-1表示流结束。
同理,OutputStream , write(int b)
方法参数为int,
public abstract void write(int b) throws IOException;
传入的参数值只有低8位有效,其余16为会被忽略,也即不能超过255。
讨论为什么传统IO会阻塞?
在系统内核,有关于socket的2个缓存区比较重要。分别是socket的读缓存区(read buffer)和写缓冲区(write buffer)。
流程如下:首先通过操作socket写入数据,先写入写缓冲区(write buffer)系统有一个专门的线程用于检查写缓冲区,有数据后将数据送入网卡设备经网络传送出去。
同时,系统内核有线程会将收到的数据拷贝到socket读缓冲区供用户空间使用。
两个缓冲区的大小是有限的,当写缓冲区写满后,会阻塞写操作,当读缓冲区为空后,会阻塞读操作。
所以,当有线程专门用于读取时,若读不到指定大小的字节,会阻塞该线程。
所以总结来说:
当使用IO读取数据时,实际上底层使用的是socket操作的数据。读取时读取socket的 read buffer。若socket读缓冲区没有足够的数据时,读取操作就会阻塞,直到有了足够数据。
其他IO参考文章
java的异常分为错误和异常。
错误(Error)指发生非常严重的异常,通常是JVM发生的。如
内存溢出(OutOfMemoryError)、线程死亡(ThreadDeath)等。这些异常发生时,JVM一般会终止程序,所以一般也不捕获。
Exception指程序发生的异常。分为运行时异常(RuntimeException及其子类)和非运行时异常(其他Exception子类)。运行时异常有空指针异常(NullPointerException)、下标越界异常(IndexOutOfBoundsException)、类型转换异常(ClassCastException) 等。一般是程序错误引起,在运行时才能发现的异常。
非运行时异常也称为受检异常。指必须捕获的异常,如IOException和SQLException等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。