金山面经

如何实现优先队列,(我忘记了,这个用的少,就随便扯了两句,就有了下一个问题,真的一环扣一环!!!)

优先队列是队列的一种,不过它可以按照自定义的一种方式(数据的优先级)来对队列中的数据进行动态的排序。每次的push和pop操作,队列都会动态的调整,以达到我们预期的方式来存储,就是优先权最大的排在队列的头部。一般都是用堆实现有先队列的,

3、如何用堆实现优先队列 4、排序用过哪些,哪些稳定排序

二叉树使用场景,跟链表区别,举例

  • 海量数据并发查询,二叉树复杂度是O(K+LgN)。二叉排序树就既有链表的好处,也有数组的好处, 在处理大批量的动态的数据是比较有用。
  • B+-Tree在文件系统中的目录应用

  • 链表是由一个个节点组成的,就像是一个火车,每个节点都可以类似的看成是一个车厢。而节点里面储存的则是数据对象还有下一个节点的地址。

  • 树有着树根还有树叶,枝干等等。树也是由节点构成的,节点分为三种,分别是叶子节点,根节点还有既不是叶子节点,也不是根节点的节点。树可以有多个分支,多个叶子,但根只能有一个,相当于链表的头节点。

如何遍历二叉树,如何 实现层序遍历

前中后序遍历还有层序遍历

进程和线程的区别

进程是系统运行程序的基本单位

线程是一个比进程小的执行单位,一个进程运行的过程中可以产生多个线程

简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。

线程同步方式

  • 同步方法
  • 同步代码块
  • 使用特殊域变量(volatile)实现线程同步
  • 使用重入锁实现线程同步
  • 使用阻塞队列实现线程同步
  • 使用局部变量实现线程同步

https://www.cnblogs.com/XHJT/p/3897440.html

cookie和session是什么,有什么区别

  • cookie
    cookie是由Web服务器保存在用户浏览器上的文件(key-value格式),可以包含用户相关的信息。客户端向服务器发起请求,就提取浏览器中的用户信息由http发送给服务器
  • session
    session 是浏览器和服务器会话过程中,服务器会分配的一块储存空间给session。

服务器默认为客户浏览器的cookie中设置 sessionid,这个sessionid就和cookie对应,浏览器在向服务器请求过程中传输的cookie 包含 sessionid ,服务器根据传输cookie 中的 sessionid 获取出会话中存储的信息,然后确定会话的身份信息。

  • cookie与session区别 cookie数据存放在客户端上,安全性较差,session数据放在服务器上,安全性相对更高

单个cookie保存的数据不能超过4K,session无此限制

session一定时间内保存在服务器上,当访问增多,占用服务器性能,考虑到服务器性能方面,应当使用cookie。

java垃圾回收机制

https://www.cnblogs.com/kylinxxx/p/13773399.html#B4AijsdP

innoDB和myISAM的区别,那你平时怎么去配置mysql的...

存储过程解释一下,它和事务的区别是什么

  • 存储过程:是完成一定功能的可重复调用的程序。可以理解成一个函数。其中可以包含多个事务。

  • 事务:是可以整个撤消的一段操作,是记录的一系列的操作和变化。可能是一个或几个存储过程,也可能是一条或几条指令。

你有多少种办法复制一个关系表

解释一下你所理解的http协议

红黑树的原理

  • 每个节点都只能是红色或黑色的;
  • 根节点是黑色的;
  • 每个叶节点(空节点)是黑色的;
  • 如果一个节点是红色的,那么他的子节点都是黑色的;
  • 从任意一个节点到其每个子节点的路径都有相同数目的黑色节点;

map怎么插入数据,有几种方式

put 和 putall

synchronized关键词的使用场合,修饰方法和代码块的区别

  • 修饰实例方法,用于当前实例加锁,进入同步代码前要获得当前实例的锁
  • 修饰静态方法,用于给当前类对象加锁,进入同步代码块前要获得当前类对象的锁
  • 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码前要获得执行对象的锁

synchronized修饰代码块底层原理

  • 定义一个synchronized修饰的同步代码块,用javap反编译后得到字节码指令
  • 同步代码块的实现使用的是monitorenter和monitorexit指令,其中monitorenter指令指向同步代码块开始位置,monitorexit指令指向同步代码块结束位置
  • 执行monitor指令时,当前线程试图获取objectref(即对象锁)所对应的monitor的持有权,当objectref的monitor的计数器count为0,那线程可以成功取得monitor,并将计数器设置为1,取锁成功。
  • 如果当前线程已经拥有了objectref的monitor的持有权,那它可以重入这个monitor,重入时monitor的计数器count也会+1
  • 如果其他线程已经拥有的objectref的monitor的持有权,那当前线程会被阻塞,直到正在执行的线程执行完毕,即monitorexit指令被执行,执行线程释放monitor并设置计数器count为0,其他线程将有机会持有monitor。
  • 编译器会确保无论代码块通过何种方式完成,每条调用过的monitorenter指令都要执行对应的monitorexit指令,无论代码块中是正常结束还是异常结束。为了保证这一点,编译器会自动产生一个异常处理器,它的目的就是执行monitorexit指令。从字节码中也可以看出多了一个monitorexit指令,它就是异常结束时被执行的释放monitor的指令。

synchronized修饰方法底层原理

  • 定义一个synchronized修饰的方法,用javap反编译后得到字节码指令
  • 方法级的同步是隐式的,无需通过字节码指令来控制。

mysql存储引擎 myIsam和innodb的区别

MySQL有多种存储引擎,MyISAM和InnoDB是其中常用的两种。这里介绍关于这两种引擎的一些基本概念(非深入介绍)。

MyISAM存储引擎,基于传统的ISAM类型,支持全文搜索,但不是事务安全的,而且不支持外键。每张MyISAM表存放在三个文件中:frm 文件存放表格定义;数据文件是MYD (MYData);索引文件是MYI (MYIndex)。

InnoDB是事务型引擎,支持回滚、崩溃恢复能力、多版本并发控制、ACID事务,支持行级锁定(InnoDB表的行锁不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,如like操作时的SQL语句),以及提供与Oracle类型一致的不加锁读取方式。InnoDB存储它的表和索引在一个表空间中,表空间可以包含数个文件。

  • 核心区别

MyISAM是非事务安全型的,而InnoDB是事务安全型的。

MyISAM锁的粒度是表级,而InnoDB支持行级锁定。

MyISAM支持全文类型索引,而InnoDB不支持全文索引。

MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用MyISAM。

MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦。

InnoDB表比MyISAM表更安全,可以在保证数据不会丢失的情况下,切换非事务表到事务表(alter table tablename type=innodb)。

应用场景

MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MyISAM是更好的选择。

InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERT或UPDATE操作,则应该使用InnoDB,这样可以提高多用户并发操作的性能。

Java内存模型

Java 内存模型主要由以下三部分构成:1 个主内存、n 个线程、n 个工作内存(与线程一一对应) 1. java内存模型规定了所有的变量都存储在主内存。线程间变量值的传递均需要通过主内存来完成 2. 每条线程还有自己的工作内存

arraylist与linkedlist的区别,数组与链表的区别,栈如何实现

treemap了解吗

你对数据库进行查询,发现查询很慢,对代码排查,代码没问题,你怎么对数据库进行排查

给你一个数据库,数据库里面数据很大(TB级),你怎么解决查询慢(性能优化)的问题;(分区技术)

socket编程,怎么实现一个多人聊天室;(怎么设计、怎么实现)

HTTP请求方法(GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE)

GET与POST请求区别(根据笔试题的回答提问),POST请求运用,GET幂等的理解,GET请求URL显示,GET请求URL中为什么有“?”

get请求:

①请求参数会显示在地址栏中(如下图,第一行中)

②请求体是空的 ③请求参数显示在请求首行中(GET /?name=12a HTTP/1.1)

post请求:

①请求参数会不显示在地址栏中

②请求体是请求参数

③ 请求参数不显示在请求首行中(POST/ HTTP/1.1)

其他区别

get请求 请求参数会显示在地址栏中  不安全
post请求 地址栏只显示请求资源的url 不显示请求参数 相对安全

get请求 请求参数存放在请求首行中 http对请求首行限制1kb
post请求 请求参数存放在请求体中 请求体没有大小限制,post可以发送大数据


POST 一般用来向服务端提交数据 - text/xml - application/json - application/x-www-form-urlencoded - multipart/form-data


我对幂等的理解是:如果我将GET操作调用1次或1万次(或任何次数),结果应该是相同的.

HTTP GET 方法,用于获取资源,不管调用多少次接口,结果都不会改变,所以是幂等的。只是查询数据,不会影响到资源的变化,因此我们认为它幂等。

说说RESTful架构

  • 无状态。确保服务是无状态的。这是为了解除客户端请求与指定某台服务器的耦合。
  • 资源识别。服务提供资源的方式应该是在URL可识别的,包括资源名称,文件格式等。这是为了解除客户端寻找资源的逻辑与服务器端提供资源的逻辑的耦合。
  • 操作资源的方式。操作资源的方式应该是表示的,例如POST,DELETE,UPDATE。这是为了解除客户端操作资源的逻辑与服务器端的耦合。 -自我描述的信息。每个HTTP请求都能够自我解释请求什么资源,格式是什么,操作什么资源,等等。这样一来,资源相关的逻辑就与服务器解耦了。

RESTFUL服务的目的还是为了解耦,解除客户端与服务器之间的耦合。在软件中,抽象再怎么强调都不过分,无论是什么编程(过程式,面向对象,函数式等等),抽象都是核心。这个RESTful也一样. 可以把restful看作是客户端与服务器之间交互的抽象,定义了一些规则: URL中表达出请求什么资源(以及格式),还有POST DELETE UPDATE PUT定义对资源的操作方式等等 .它们最大化的解除了客户端请求和服务器资源的耦合,开发上前后端的耦合度也降低了,提高了开发效率。

接口跟抽象类的区别

重载重写,java隐藏的区别

  • 重载:同一个类中方法名相同,参数列表必须不同, 并且返回值修饰符也可以不同
  • 重写:是子类方法对父类方法的重新改造
  • 隐藏:父类和子类拥有相同名字的属性或者方法(方法隐藏只有一种形式,就是父类和子类存在相同的静态方法)时,父类的同名的属性或者方法形式上不见了,实际是还是存在的。隐藏是对于静态方法和成员变量而言的,属性只能被隐藏,不能被覆盖
  • 覆盖:子类重写父类的方法,要求方法名和参数类型完全一样,返回值和异常比父类小或者相同(即为父类的子类),访问修饰符比父类大或者相同。

隐藏和覆盖的区别

(1)被隐藏的属性,在子类被强制转换成父类后,访问的是父类中的属性

在无强制转换时子类要访问父类的属性使用super关键字

(2)被覆盖的方法,在子类被强制转换成父类后,调用的还是子类自身的方法

子类要是想访问父类的方法,可以使用super关键字

==与equals区别

equals和hashcode

https://images.cnblogs.com/cnblogs_com/kylinxxx/1675669/o_201022013201hashcode%E2%80%94%E2%80%94equals.jpg

内存泄露内存溢出区别

内存泄漏是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。

内存溢出指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。

二者的关系:

  1. 内存泄漏的堆积最终会导致内存溢出
  2. 内存溢出就是你要的内存空间超过了系统实际分配给你的空间,此时系统相当于没法满足你的需求,就会报内存溢出的错误。
  3. 内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。就相当于你租了个带钥匙的柜子,你存完东西之后把柜子锁上之后,把钥匙丢了或者没有将钥匙还回去,那么结果就是这个柜子将无法供给任何人使用,也无法被垃圾回收器回收,因为找不到他的任何信息。
  4. 内存溢出:一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出。比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出。说白了就是我承受不了那么多,那我就报错。

内存泄漏的分类(按发生方式来分类)

  • 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
  • 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
  • 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
  • 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

内存溢出原因:

  1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  2. 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
  3. 代码中存在死循环或循环产生过多重复的对象实体;
  4. 使用的第三方软件中的BUG;
  5. 启动参数内存值设定的过小

内存溢出的解决方案:

第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)

第二步,检查错误日志,查看“OutOfMemory”错误前是否有其 它异常或错误。

第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。 重点排查以下几点: 1.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。

2.检查代码中是否有死循环或递归调用。

3.检查是否有大循环重复产生新对象实体。

4.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。

5.检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。

第四步,使用内存查看工具动态查看内存使用情况

http和https区别,https过程

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

https过程

  (1)客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。

  (2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。

  (3)客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。

  (4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。

  (5)Web服务器利用自己的私钥解密出会话密钥。

  (6)Web服务器利用会话密钥加密与客户端之间的通信。

hashmap讲讲put的原理

第一步首先将k,v封装到Node对象当中(节点)。第二步它的底层会调用K的hashCode()方法得出hash值。第三步通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。

mysql防止注入,安全性了解吗

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

防止的方式:

  • 像mybatis有一个用#{}去代替${}的方式。前者解析传递进来的参数数据,并且进行一个预编译处理,而后者是字符串替换。

  • 其次我个人觉得在一些比较敏感的语句中,我们可以把用户自定义的输入改成我们定义好的按钮选择,这样的话就可以防止恶意注入。

  • 还有就是可以在数据传输过程中加上一个过滤器,利用注入的语句具有的特征,过滤掉一些有危险的语句,提示用户重新输入。

讲一讲B+树

一级索引和二级索引

1、一级索引
索引和数据存储在一起,都存储在同一个B+tree中的叶子节点。一般主键索引都是一级索引。

2、二级索引
二级索引树的叶子节点存储的是主键而不是数据。也就是说,在找到索引后,得到对应的主键,再回到一级索引中找主键对应的数据记录。

关系:一级索引可以单独存在,二级索引不能单独存在,必须依附于一级索引

哨兵机制

如果主服务器挂了,我们可以将从服务器升级为主服务器,等到旧的主服务器(挂掉的那个)重连上来,会将它(挂掉的主服务器)变成从服务器。

  • Sentinel本质上只是一个运行在特殊模式下的Redis服务器。
  • Sentinel在初始化的时候并不会载入AOF/RDB文件,因为Sentinel根本就不用数据库
  • 然后,在启动的时候会将普通Redis服务器的代码替换成Sentinel专用代码。(所以Sentinel虽然作为Redis服务器,但是它不能执行SET、DBSIZE等等命令,因为命令表的代码被替换了)
  • 初始化Sentinel的状态,并根据给定的配置文件初始化Sentinel监视的主服务器列表。
  • 最后,Sentinel会创建两个连向主服务器的网络连接:
    • 命令连接(发送和接收命令)
    • 订阅连接(订阅主服务器的sentinel:hello频道)
  • Sentinel通过主服务器发送INFO命令来获得主服务器属下所有从服务器的地址信息,并为这些从服务器创建相应的实例结构。
  • 当发现有新的从服务器出现时,除了创建对应的从服务器实例结构,Sentinel还会创建命令连接和订阅连接。
  • 在Sentinel运行的过程中,通过命令连接会以每两秒一次的频率向监视的主从服务器的sentinel:hello频道发送命令(主要发送Sentinel本身的信息,监听主从服务器的信息),并通过订阅连接接收sentinel:hello频道的信息。
  • 这样一来一回,我们就可以更新每个Sentinel实例结构的信息。

判断下线:

  • 主观下线 Sentinel会以每秒一次的频率向与它创建命令连接的实例(包括主从服务器和其他的Sentinel)发送PING命令,通过PING命令返回的信息判断实例是否在线 如果一个主服务器在down-after-milliseconds毫秒内连续向Sentinel发送无效回复,那么当前Sentinel就会主观认为该主服务器已经下线了。
  • 客观下线 当Sentinel将一个主服务器判断为主观下线以后,为了确认该主服务器是否真的下线,它会向同样监视该主服务器的Sentinel询问,看它们是否也认为该主服务器是否下线。 如果足够多的Sentinel认为该主服务器是下线的,那么就判定该主服务为客观下线,并对主服务器执行故障转移操作。

选举头Sentinel和故障转移:

当一个主服务器认为为客观下线以后,监视这个下线的主服务器的各种Sentinel会进行协商,选举出一个领头的Sentinel,领头的Sentinel会对下线的主服务器执行故障转移操作。

选举领头Sentinel的规则也比较多,总的来说就是先到先得(哪个快,就选哪个)

  • 在已下线主服务器属下的从服务器中,挑选一个转换为主服务器
  • 让已下线主服务器属下的所有从服务器改为复制新的主服务器
  • 已下线的主服务器重新连接时,让他成为新的主服务器的从服务器
  • 挑选某一个从服务器作为主服务器也是有策略的,大概如下:

    跟master断开连接的时长 slave优先级 复制offset run id(上一个主服务器id)

posted @ 2020-10-22 09:47  无名客nameless  阅读(139)  评论(0)    收藏  举报