byte和bit是什么意思
bit:二进制位数的缩写,意为“位”或者“比特”,是计算机运算的基础。取值范围0或1
byte:是计算机文件大小的基本计算单位
关系:1byte=8bit
Java中的基本数据类型以及数据范围
整数类型:
- byte:8位 -128~127
- short:16位 -2^15 ~ 2^15-1
- int:32位 -2^31 ~ 2^31-1
- long:64位 -2^63 ~2^63-1
浮点数类型:
- float:32位 ,后缀F或f。
- double:64位,默认位double
字符类型:
- char:16位 0 ~ 2^16-1
注意事项:不能为0个字符
布尔类型:
- boolean:true和false
类型转换: char–> 自动转换:byte–>short–>int–>long–>float–>double
Java中的锁,sychnorize关键字。同步锁加在不同方法和静态方法有什么不同
java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。</br>
java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。
synchronized的用法:synchronized修饰方法和synchronized修饰代码块。
修饰方法
Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。
方法一:
public synchronized void method()
{
// todo
}
方法二:
public void method()
{
synchronized(this) {
// todo
}
}
写法一修饰的是一个方法,写法二修饰的是一个代码块,但写法一与写法二是等价的,都是锁定了整个方法时的内容。
synchronized关键字不能继承。
注意:
- 在定义接口方法时不能使用synchronized关键字。
- 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。
修饰代码块
-
一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞
-
当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。
-
指定要给某个对象加锁 ,synchronized (account)
修饰静态方法
静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。
修饰一个类
synchronized(ClassName.class),给class加锁和上例的给静态方法加锁是一样的,所有对象公用一把锁
总结:
A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
线程、线程池
线程池原理:在面向对象编程中,对象创建和销毁是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是对一些很耗资源的对象创建和销毁。
最好是看看《java并发实战》
jvm的内存结构
数据库的隔离级别
- 未提交读(READ UNCOMMITTED)
事务中的修改,即使没有提交,对其它事务也是可见的。
- 提交读(READ COMMITTED)
一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
- 可重复读(REPEATABLE READ)
保证在同一个事务中多次读取同样数据的结果是一样的。
- 可串行化(SERIALIZABLE)
强制事务串行执行。
隔离级别 | 脏读 | 不可重复读 | 幻影读 | 加锁读 |
---|---|---|---|---|
未提交读 | √ | √ | √ | × |
提交读 | × | √ | √ | × |
可重复读 | × | × | √ | × |
可串行化 | × | × | × | √ |
脏数据
T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。
不可重复读
T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
幻影读
T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。
索引
集合
Collection
1. Set
- TreeSet:基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。
- HashSet:基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
- LinkedHashSet:具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。
2. List
- ArrayList:基于动态数组实现,支持随机访问。
- Vector:和 ArrayList 类似,但它是线程安全的。
- LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。
3. Queue
- LinkedList:可以用它来实现双向队列。
- PriorityQueue:基于堆结构实现,可以用它来实现优先队列。
Map
- TreeMap:基于红黑树实现。
- HashMap:基于哈希表实现。
- HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。
- LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。
hashmap在jdk1.8之前和之后的区别?为什么用红黑树
1.7中主要是数组+链表实现,1.8开始采用数组+红黑树实现
算法:求一个数组中逆对的个数
private long cnt = 0;
private int[] tmp; // 在这里声明辅助数组,而不是在 merge() 递归函数中声明
public int InversePairs(int[] nums) {
tmp = new int[nums.length];
mergeSort(nums, 0, nums.length - 1);
return (int) (cnt % 1000000007);
}
private void mergeSort(int[] nums, int l, int h) {
if (h - l < 1)
return;
int m = l + (h - l) / 2;
mergeSort(nums, l, m);
mergeSort(nums, m + 1, h);
merge(nums, l, m, h);
}
private void merge(int[] nums, int l, int m, int h) {
int i = l, j = m + 1, k = l;
while (i <= m || j <= h) {
if (i > m)
tmp[k] = nums[j++];
else if (j > h)
tmp[k] = nums[i++];
else if (nums[i] < nums[j])
tmp[k] = nums[i++];
else {
tmp[k] = nums[j++];
this.cnt += m - i + 1; // nums[i] >= nums[j],说明 nums[i...mid] 都大于 nums[j]
}
k++;
}
for (k = l; k <= h; k++)
nums[k] = tmp[k];
}
总结
总的来说,这次头条面试不难。但是很多细节上的问题。这是春招的第一个面试,也当作是对自己的一个敲门砖吧,可以知道自己的不足之处,也能更好地复习。面试官推荐平时学习多看书,可以看看《Java并发实战》