本文正在参加「金石计划」
本文主要分享自己总结的近几天面试问到的问题(附答案)
反射
首先,反射的前提条件是必须获得这个类的字节码文件。反射机制就是在程序运行的过程中,对于任意的类和对象,都能得到和调用它们的属性和方法。 简单来说,就是把java类中的各种成分映射成一个个的java对象,这种动态获取类的信息以及动态调用对象方法的功能称为java的反射机制。
获取对象的三种方式:
- 通过全类名来获取,即
Class clazz=Class.forName("com.rg.entity.Bill"); - 通过类名.class获取,即
Class clazz=Bill.class; - 通过对象.getClass()来获取,即
Student s =new Student(); Class clazz =s.getClass();
Mysql索引类型
普通索引,主键索引,唯一索引,全文索引,组合索引。
普通索引:最基本的索引,没有任何限制。
组合索引:在多个字段上创建的索引,在查询条件中,应满足最左匹配原则,即在使用时应保证查询条件使用组合索引中左边的字段。
主键索引:根据主键创建的索引,每个表只有一个主键索引,值是唯一并且不允许有空值的。
唯一索引:创建索引列的值必须唯一,但是索引列允许有空值,如果是组合索引,则列值的组合必须唯一。
全文索引:全文索引主要用于检索文本中查找的关键字,在定义全文索引的列上支持值的全文查找,允许索引列上为重复值或空值。全文索引可以在char,varchar,或text类型的列上创建。
聚簇索引与非聚簇索引
聚簇索引是一种数据存储方式,其默认是根据主键创建的一颗B+树,聚簇索引的叶子节点存放了表中的所有数据,而非叶子节点不保存具体的数据,由于在InnoDB存储引擎中,每个数据页的大小都是16kb的,所以非叶子节点能够保存更多的索引。一张表中只能拥有一个聚簇索引(默认为主键)。
对于非聚簇索引,也叫辅助索引(二级索引),它的叶子节点存放的是索引的键值和该索引键指向的主键。因此非聚簇索引一般需要进行回表查询,比如在一张表中,主键索引为学生id,唯一索引为学生name,如果通过学生name查询学生的id,那么通过非聚簇索引可直接获取,效率比较高,且不需要回表。如果想要通过学生name查询到学生的具体信息(年龄,课程……),这个时候会先通过非聚簇索引查询到学生的id(主键),然后根据主键进行回表查询出具体信息。
下面是从网上找到的一张图,比较透彻。
BIO,NIO,AIO
BIO(Blocking IO):同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善,实现对线程的复用,不过没有从根本上解决同步阻塞问题。
NIO(New IO):同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 对于NIO来讲,它的实现主要有Channel通道,Selector选择器,Buffer缓冲区实现的。
Selector(选择器):它允许单个线程处理多个客户端的连接,即使用单个线程处理多个通道。因此,它需要较少的线程来处理这些通道。
Buffer(缓冲区):是一个对象,它包含一些要写入或者要读出的数据Selector(选择器):选择器用于使用单个线程处理多个通道。
Channel(通道):所有的NIO都是从Channel中读取数据的。通道是双向的,可以从Buffer中读数据也可以写数据到Buffer中,而对于流,它的读写是单向的。无论读写,Channel通道只能和Buffer交互。因为有了Buffer的存在,通道可以进行异步地读写。
AIO(NIO2):异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
BIO、NIO、AIO适用场景: BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。 NIO方式适用于连接数目多且连接比较短(轻损作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。 AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
线程中的run和start方法区别
run()方法被称为线程执行体,它的方法体代表了线程需要完成的任务,而start()方法用来启动线程。 调用start()方法启动线程时,系统会把该run()方法当成线程执行体来处理。但如果直接调用线程对象的run()方法,则run()方法立即就会被执行,而且在run()方法返回之前其他线程无法并发执行。也就是说,如果直接调用线程对象的run()方法,系统把线程对象当成一个普通对象,而run()方法也是一个普通方法,而不是线程执行体。
快速排序
快速排序的实现方式有很多种,这里介绍一种实现方法--填坑法,即每次让key指向数组的第一个下标(此操作会空出一个空位),然后从右边寻找小于key的数,找到后将其放在左边的坑位上,然后左边指针开始移动,找到比key大的数后放到右边空缺的位置,最后将key放到空缺的坑位上,依此循环。
public class QuickSort {
public static void main(String[] args) {
int [] arr={ 7, 3, 2, 10, 8, 1, 9, 5, 4, 6 };
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr,int begin,int end){
if(begin>=end){
return;
}
//key为数组的首个元素,此时空出第一个坑位
int left=begin,right=end,key=arr[begin];
//从右边往前开始找比key小的数
while(begin<end){
while(arr[end]>key && begin<end){
end--;
}
//找到后将右边数字填到左边的坑位上
arr[begin]=arr[end];
//从左边往前开始找比key大的数
while (arr[begin]<key && begin<end){
begin++;
}
//找到后将左边数字填到右边的坑位上
arr[end]=arr[begin];
}
//将key放到遍历到的begin下标处
arr[begin]=key;
//改变key的值,方便后续循环遍历
int key1=arr[left];
quickSort(arr,left,key1-1);
quickSort(arr,key1+1,right);
}
}
求根结点到叶子节点数字之和
题目: 给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字:
例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。 计算从根节点到叶节点生成的 所有数字之和 。
例如:
输入:root = [1,2,3] 输出:25 解释: 从根到叶子节点路径 1->2 代表数字 12 从根到叶子节点路径 1->3 代表数字 13 因此,数字总和 = 12 + 13 = 25
思路:本题可以采用回溯的思想来解析,若为叶子节点,说明遍历到底部了,返回上一层遍历。详细代码如下:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int res = 0;
public int sumNumbers(TreeNode root) {
dfs(root,0);
return res;
}
public void dfs(TreeNode root,int cur){
if(root==null) return;
cur=cur*10+root.val;
if(root.left==null&&root.right==null){
res+=cur;
}
dfs(root.left,cur);
dfs(root.right,cur);
}
}