java #
long/double 原子性? #
- 没加 volatile 修饰符时,在 32bit 平台上,存取 long/double 变量不是原子操作(分成2步,每次操作32bit)。
- 加了 volatile 修饰符之后,存取 long/double 变量是原子操作。
volatile 修饰符作用? #
- 保证顺序(防止JVM或JTI对语句重排序)和可见性(happens-before,确保一个线程的修改对其他线程可见)。
- 保证变量读写的原子性(针对64位数据类型,如 long 和 double)。
String 类有哪些常用方法? #
Get:
- length(): int
- isEmpty(): boolean
- charAt(int index): char
- getBytes(String charsetName): byte[]
- getBytes(Charset charset): byte[]
- getBytes(): byte[]
- toCharArray(): char[]
Search:
- contains(CharSequence str): boolean
- indexOf(int ch): int
- indexOf(String str): int
- indexOf(int ch, int fromIndex): int
- indexOf(String str, int fromIndex): int
- lastIndexOf(int ch): int
- lastIndexOf(String str): int
- lastIndexOf(int ch, int fromIndex): int
- lastIndexOf(String str, int fromIndex): int
- startsWith(String prefix): boolean
- endsWith(String suffix): boolean
- matches(String regex): boolean
- compareTo(String str): int
- compareToIgnoreCase(String str): int
- equals(Object str): boolean
- equalsIgnoreCase(Object str): boolean
Transform:
- toUpperCase(): String
- toLowerCase(): String
- trim(): String
- substring(int beginIndex): String
- substring(int beginIndex, endIndex): String
- replace(CharSequence target, CharSequence replacement): String
- replaceFirst(String regex, String replacement): String
- replaceAll(String regex, String replacement): String
- split(String regex): String[]
- split(String regex, int limit): String[]
- {static} join(CharSequence delimiter, CharSequence… elements): String
- {static} format(String format, Object… args): String
java.io.File 类有哪些常用方法? #
- getName(): String
- getPath(): String
- getAbsolutePath(): String
- getParent(): String
- getParentFile(): File
- exists(): boolean
- isDirectory(): boolean
- isFile(): boolean
- list(): String[]
- listFiles(): File[]
- length(): long
- delete(): boolean
- mkdir(): boolean
- mkdirs(): boolean
- renameTo(File): boolean
- {static} createTempFile(String prefix, String suffix): File
FileDescriptor 中的 fd 和 handle 字段意义? #
- FileInputStream/FileOutputStream/RarndomAccessFile 使用 handle 表示底层的文件句柄。
- Socket/ServerSocket 使用 fd 表示底层的文件句柄。
BIO、NIO模型的区别? #
- BIO代表模型有java.io包中的输入输出流;NIO代表模型有java.nio包中的通道、缓冲区、选择器。
- BIO模型是同步阻塞的(在读写时线程阻塞);而NIO模型是同步非阻塞的(在等待就绪阶段是非阻塞的,在读写操作阶段是同步阻塞的)。
NIO模型的特性? #
- 同步非阻塞
- 多路复用
AIO模型的特性? #
- 异步非阻塞
- 事件通知机制
通道(Channel)与输入输出流(InputStream/OutputStream)的区别? #
- 通道既可以读,也可以写,而流通常是单向的。
- 通道可以异步读写,而流是同步阻塞的。
- 通道中的数据总是先读或写到缓冲区(Buffer),而输入输出流则直接操作数据。
缓冲区(java.nio.Buffer)常用方法? #
Get:
- capacity(): int
- position(): int
- limit(): int
- remaining(): int
Judge:
- hasRemaining(): boolean
- isReadOnly(): boolean
- isDirect(): boolean
Update:
- position(int): Buffer
- limit(int): Buffer
- mark(): Buffer
- reset(): Buffer
- clear(): Buffer
- flip(): Buffer
- rewind(): Buffer
字节缓冲区(java.nio.ByteBuffer)常用方法? #
除了继承自 java.nio.Buffer 中的方法外,还包括:
Get:
- get(): byte
- get(int index): byte
- get(byte[] dst, int offset, int length): ByteBuffer
- get(byte[] dst): ByteBuffer
- slice(): ByteBuffer
- duplicate(): ByteBuffer
- {static} allocateDirect(int capacity): ByteBuffer
- {static} allocate(int capacity): ByteBuffer
- {static} wrap(byte[] array, int offset, int length): ByteBuffer
- {static} wrap(byte[] array): ByteBuffer
Update:
- compact(): ByteBuffer
- put(byte): ByteBuffer
- put(int index, byte b): ByteBuffer
- put(byte[] src, int offset, int length): ByteBuffer
- put(byte[] src): ByteBuffer
- put(ByteBuffer src): ByteBuffer
About backed array:
- hasArray(): boolean
- array(): byte[]
- arrayOffset(): int
HashMap 实现原理? #
put 方法:
- HashMap 内部维护一个数组,key 经过 hash 和取余操作之后得到下标位置,找到数组对应的节点。
- 此节点是一个链表表头或树根或为空,链表或树中存放着所有哈希冲突的节点,找到 key 对应节点替换其中的数据,没有找到则插入新节点。
- 当链表长度超过阈值(TREEIFY_THRESHOLD=8),则链表会转换为红黑树(since java 8),冲突数量少于6时又退化为链表。
为什么 HashMap 里的 table 数组长度是 2 的幂次? #
优化取模计算,提高存储下标位置计算效率。
为什么 HashMap 里的 table、entrySet 成员变量用 transient 修饰? #
- transient 修饰的成员变量,在序列化时会被忽略。
- 考虑到不同平台 hash 结果可能不一样,为了保证兼容性,添加 transient 修饰符,序列化时忽略 hash 数据。
- HashMap 采用自定义方法进行序列化和反序列化,序列化时会将内部字段和键值对直接写入对象输出流中,反序列化时从对象输入流中按顺序读取字段值和键值对,详见 writeObject(ObjectOutputStream) 和 readObject(ObjectInputStream) 方法。
为什么 hashCode 方法里使用 31 作为乘数? #
- 乘数:因为经常需要将多个数据合并为一个数据,相较加法,乘数方法(对第一个数乘以一个固定值后加上第二个数)可以更大概率保留这两个数据的原始特征。
- 质数:31作为质数乘数可以使结果更加分散,减少冲突概率。
- 效率:31作为乘数,可优化为左移5位再减去原始值,提高计算效率。
外部命令? #
TODO search: java.lang.ProcessBuilder, java.lang.Process
注解 #
- 代码中的特殊标记
- 可以在编译、类加载、运⾏时被读取,执⾏相对应的处理
java自带常用注解:
- FunctionanInterface
元注解:
- ⽤来修饰注解的
- 常用的有@Retention、@Target、@Documented
@Retention:
- RetentionPolicy.SOURCE:注解只在源码阶段保留
- RetentionPolicy.CLASS:注解只被保留到编译进行的时候
- RetentionPolicy.RUNTIME:在程序运行时可以获取到它们
@Target:
- ElementType.ANNOTATION_TYPE
- ElementType.CONSTRUCTOR
- ElementType.FIELD
- ElementType.LOCAL_VARIABLE
- ElementType.METHOD
- ElementType.PACKAGE
- ElementType.PARAMETER
- ElementType.TYPE
@Inherited:
- 修饰在父类中,子类继承该父类被@Inherited修饰的注解
@Repeatble:
- 允许被修饰注解重复修饰同一处
- 在java8以前没有此元注解,只能手动添加一个数组注解
@FunctionanInterface #
- 将接口标记为函数式接口
- 编译时检查被标记的接口是否是合法的函数式接口
函数式接口:
- 只有一个抽象方法
- 可以有任意数量的默认方法、静态方法、覆盖Object类的方法
- 配合lambda表达式而设计
泛型 #
- 将类型参数化
- 编译期间匹配类型