java中的常见问题

Posted SSimeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.给一个二叉树,使用递归和非递归完成先序,中序和后序的遍历

  1. 前序遍历
/**
	 * 二叉树的前序遍历(递归)
	 * 
	 * @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);
				}
			}
		}
	}

  1. 中序遍历
    /**
	 * 二叉树的中序遍历(递归)
	 * 
	 * @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;
			}
		}

	}
  1. 后序遍历
	/**
	 * 二叉树的后序遍历(递归)
	 * 
	 * @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 实现

(转) Java中的负数及基本类型的转型详解

片段 A 的列表视图中的片段 B 中的新列表视图,单击 A 的列表项

如何从 Firebase 获取数据到 Recyclerview 中的片段?

如何使用java将数据从片段传递到android中的另一个片段?

片段中的 java.lang.IllegalStateException