java中的常见问题
Posted SSimeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中的常见问题相关的知识,希望对你有一定的参考价值。
java中的常见问题(一)
1. java中的常用容器
- 常用的List包括ArrayList、LinkedList、Vector;
- 常用的Set包括HashSet、TreeSet、LinkedHashSet;
- 常用的Map包括HashMap、TreeMap、SortMap、LinkedHashMap、ConcurrentHashMap等;
- Queue包括Deque和PriorityQueue;
框图:
2.List和Set的区别,以及什么是不可重复
区别:
- list是一个有序的容器,保持了每个元素的插入顺序。而set是无序容器,无法保证每个元素的存储顺序;
- list可以放重复的对象,而set不允许重复;
- list可以插入多个null元素,而set只允许插入一个null元素
不可重复:
Set底层使用的是Map结构,但是其map结构对应的value值为null,因此每添加一个对象就作为map的key,而Map中的key不允许重复,所以Set也就不允许重复了。判断两个对象是否相等,要判断hashCode相等,并且equals()方法返回值为true才能证明两个对象相等。
3.HashMap是否线程安全,如果想用线程安全的HashMap怎么做?
(1)HashMap不是线程安全的,在多线程环境下不建议使用。
(2)想用线程安全的HashMap可以利用ConcurrentHashMap。
- JDK1.8 中的ConcurrentHashMap 选择了与 HashMap 相同的Node数组+链表+红黑树结构;在锁的实现上,抛弃了原有的 Segment 分段锁,采用CAS + synchronized实现更加细粒度的锁,保证了安全
- 将锁的级别控制在了更细粒度的哈希桶数组元素级别,也就是说只需要锁住这个链表头节点(红黑树的根节点),就不会影响其他的哈希桶数组元素的读写,大大提高了并发度。
4.编写一个单例模式,常见的单例有哪些,分别列举?
(1)懒汉式:
public class Slacker {
/*
* 懒汉式单例:用的时候再初始化
*/
private static Slacker instance = null;//定义
private void slacker() {
}
public static Slacker getInstance() {
if (instance == null) {
instance = new Slacker();//初始化
}
return instance;
}
}
(2)饿汉式:
public class Hungry {
/*
饿汉式:一上来就初始化
*/
private static final Hungry instance=new Hungry(); //初始化
private void hungry() {
}
public static Hungry getInstance() {
return instance;
}
}
(3)懒汉加锁:
/*
加锁的懒汉式
*/
public class Lock_up {
private static Lock_up instance = null;
private void Lock_up() {
}
private static Lock_up getinstance() {
synchronized (Lock_up.class) {
// 加锁保证一次运行一个
if (instance == null) {
synchronized (Lock_up.class) {
// 加锁保证instance为空时,创建一个实例
instance = new Lock_up();
}
}
}
return instance;
}
}
(4)枚举式:
/**
*枚举式单例模式
*/
public enum Singleton{
INSTANCE;
public Singleton getInstance(){
return INSTANCE;
}
}
5.有哪些排序算法,写出快速排序算法的实现
常用的排序算法:冒泡、插入、选择、快速。
快速排序的代码实现:
public static void quickSort(int[] arr,int left,int right) {
if (left > right) return;//左边要比右边小,不然就终止
int base = arr[left];//每次以左边的数作为基准数
int i = left,int j = right;
// 不相遇
while (i != j) {
// 以基准数为标准,右找小,左找大
while (arr[j] >= base && i < j) {
j--;
}
while (arr[i] <= base && i < j) {
i++;
}
//左边比base大,右边比base小,停下,直接交换
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
// 此时,i=j 相遇位置的数赋值给基准数
arr[left] = arr[i];
// 把基准数赋值给相遇位置的数
arr[i] = base;
quickSort(arr, left, i - 1);
quickSort(arr, i + 1, right);
}
}
6.给一个二叉树,使用递归和非递归完成先序,中序和后序的遍历
- 前序遍历
/**
* 二叉树的前序遍历(递归)
*
* @param root
*/
public void preOrder1(TreeNode root) {
if (root == null)
return;
System.out.print(root.data + " ");
preOrder1(root.left);
preOrder1(root.right);
}
/**
* 二叉树的前序遍历(迭代)
* @param root
*/
public void preOrder2(TreeNode root) {
Stack<TreeNode> stack=new Stack<TreeNode>();
TreeNode currNode=root;
if(currNode!=null) {
stack.push(currNode);//将根节点入栈
while(!stack.isEmpty()) {
currNode=stack.pop();
System.out.println(currNode.data+" ");
if(currNode.right!=null) {
stack.push(currNode.right);//由于栈结构,先压右节点
}
if(currNode.left!=null) {
stack.push(currNode.left);
}
}
}
}
- 中序遍历
/**
* 二叉树的中序遍历(递归)
*
* @param root
*/
public void inOrder1(TreeNode root) {
if (root == null)
return;
inOrder1(root.left);
System.out.print(root.data + " ");
inOrder2(root.right);
}
/**
* 二叉树的中序遍历(迭代)
*
* @param root
*/
public void inOrder2(TreeNode root) {
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode currNode=root;
while(currNode !=null || !stack.isEmpty()) {
while(currNode!=null) {
stack.push(currNode);
currNode=currNode.left;
}
if(!stack.isEmpty()) {
currNode=stack.pop();//弹出左结点
System.out.print(currNode.data+" ");
currNode=currNode.right;
}
}
}
- 后序遍历
/**
* 二叉树的后序遍历(递归)
*
* @param root
*/
public void afterOrder1(TreeNode root) {
if (root == null)
return;
afterOrder1(root.left);
afterOrder1(root.right);
System.out.print(root.data + " ");
}
/**
* 二叉树的后序遍历(迭代)
*
* @param root
*/
public void afterOrder2(TreeNode root) {
Stack<TreeNode> stack = new Stack<TreeNode>();
Stack<TreeNode> stack2=new Stack<TreeNode>();
TreeNode curNode=root;
while(curNode!=null || !stack.isEmpty()) {
while(curNode!=null) {
stack.push(curNode);
stack2.push(curNode);
curNode=curNode.right;
}
if(!stack.isEmpty()) {
curNode=stack.pop();
curNode=curNode.left;
}
}
while(!stack2.isEmpty()) {
curNode=stack2.pop();
System.out.print(curNode.data+" ");
}
}
7.数据库的事务的四大特性及数据库的隔离级别
四大特性:
- 原子性:指事务包含的所有操作要么全部成功,要么全部失败回滚
- 隔离性:当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
- 一致性:指事务必须使数据库从一个一致性状态变换到另一个一致性状态。
- 持久性:是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的。
隔离级别:
① Serializable (串行化):只有等一个事务结束,才能进行下一个事务。可避免脏读、不可重复读、幻读的发生。
② Repeatable read (可重复读):在一个事务中直到事务结束之前,都可以反复的读到事务刚开始看到的数据,并且一直不会发生变化,有效的防止了脏读和不可重复读。可避免脏读、不可重复读的发生。
③ Read committed (读已提交):在一个事务中可以读取到其他事务已经提交的数据变化。可防止脏读现象,无法限制不可重复读和幻读。
④ Read uncommitted (读未提交):在一个事务中,读到了其他事务没有提交的数据。会产生数据脏读现象,不能限制不可重复读和幻读。最低级别,任何情况都无法保证。
8.TCP的三次握手和四次挥手
(1)三次握过程:
-
第一次握手:建立连接时,客户端发送SYN包,报文首部中的同步序号SYN=1,同时选择一个初始序列号 seq=x 到服务器,并进入SYN_SENT状态,等待服务器确认。
-
第二次握手:TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN_RCVD(同步收到)状态。
-
第三次握手:客户端收到服务器的SYN+ACK包后,还要向服务器发送确认报文(ACK=1,ack=y+1,自己的序列号seq=x+1),此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态此包发送完毕,完成三次握手。
(2)四次挥手
-
客户端发出连接断开报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=x,此时,客户端进入FIN-WAIT-1(终止等待1)状态。
-
服务器收到连接释放报文,发出确认报文,ACK=1,ack=x+1,并且带上自己的序列号seq=z,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。 -
服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=x+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=y,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
-
客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=y+1,而自己的序列号是seq=x+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
9. GET/POST的区别,除了GET/POST还有哪些?
- GET:向指定的资源发出“显示”请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中。
- POST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源。
此外还有PUT 和 DELETE请求
以上是关于java中的常见问题的主要内容,如果未能解决你的问题,请参考以下文章
ASP.net MVC 代码片段问题中的 Jqgrid 实现
片段 A 的列表视图中的片段 B 中的新列表视图,单击 A 的列表项
如何从 Firebase 获取数据到 Recyclerview 中的片段?