java 中的一些惯用法
鸟宿池边树,僧敲月下门。
java7中的try-resource语法语义自动关闭InputStream和JDBC Connection,就不需要在finally中close了。
java中的锁是如何实现的
在java里锁是通过cas把当前线程id刷新到对象的头信息里,在获取锁时会去头信息里拿这个信息,如果没有则会cas刷新进去,刷新成功就获取到锁;刷新失败就表明有别的线程也在尝试刷新这个信息。
在操作系统层面有pv操作保证原子性,而pv操作也是利用cpu中原语指令,在获取锁时保证不会被别的指令打断(或被重排序)。
白名单问题
java中1/0抛出算术异常,而1/0.0结果为无穷大。
一家信用卡公司中的账号是成千上万的,我们将用户账号保存在一个文件中,这个文件就是白名单。认为给定一个账户,我们需要检查该账号是否在白名单中。一个程序可用往往是不够的,如果没有二分查找或者归并排序这样的高效算法解决此类问题是不可能的。
关于除法运算
无论正负,a / b
的商会向0取整。而余数满足这个公式:(a/b) * b + a % b == a
。例如-14 / 3 == -4,-14 % 3 == -2;14 / -3 == -4,14 % -3 == 2.
为什么数组索引从0开始
这个习惯来源于机器语言。那时计算一个数组元素的地址需要将数组的起始地址加上该元素的索引。将初始索引设置为1要么会浪费数组的第一个元素的空间,要么会花费额外的时间将索引减1。
java中对象数组为什么保存的是引用
如果对象非常大,那么移动它们只需要操作引用本身而非对象本身,这就会大大提高效率;反之如果对象非常小,每次获取信息的时候都需要通过引用反而会降低效率。
共变数组和类型擦除
数组的协变性(covariant)是指:如果类Base是类Sub的基类,那么Base[]
就是Sub[]
的基类。而泛型是不可变的(invariant),List
数组的协变性可能会导致一些错误,比如下面的代码:
1 | Object[] array = new String[10]; |
它是可以编译通过的,因为数组是协变的,Object[]
类型的引用可以指向一个String[]
类型的对象,但是运行的时候是会报出如下异常的:
1 | Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer |
但是对于泛型就不会出现这种情况了:
1 | List<Object> list = new ArrayList<String>(); |
以上代码在编译的时候都不能通过!
数组是具体化的(reified),而泛型在运行时是被擦除的(erasure)。数组是在运行时才去判断数组元素的类型约束,而泛型正好相反,在运行时,泛型的类型信息是会被擦除的,只有编译的时候才会对类型进行强化。所以上面的例子中,数组的方法会在运行时报出ArrayStoreException,而泛型根本无法通过编译。
虽然将集合看作是数组的抽象会有所帮助,但是数组还有一些集合不具备的特殊性质。
Java 语言中的数组是协变的(covariant),也就是说,如果 Integer扩展了 Number(事实也是如此),那么不仅 Integer是 Number,而且 Integer[]也是 Number[],在要求 Number[]的地方完全可以传递或者赋予 Integer[]。(更正式地说,如果 Number是 Integer的超类型,那么 Number[]也是 Integer[]的超类型)。
您也许认为这一原理同样适用于泛型类型 —— List
不允许这样做有一个很充分的理由:
这样做将破坏要提供的类型安全泛型。如果能够将 List
1 | List<Integer> li = new ArrayList<Integer>(); |
因为 ln是 List
java中Boolean值用一个字节表示(而不是位),因为计算机访问内存都是以字节为单位。
长度可变的数组
利用可变参和泛型
1 | public class Test { |
Kafka的使用场景
- 日志收集中心
- 消息系统
- 网站活性追踪
- 流处理
- 事件源
常见工具
- 线程、堆分配、gc情况:visualvm