面试问题汇总+解答(含Java、计算机系统、大数据和Linux)

JAVA

静态方法和非静态方法的区别

  1. 静态方法可以不用创建对象来访问,非静态方法需要创建对象后访问。
  2. 静态方法只能调用静态变量,非静态方法可以调用静态变量和成员变量。

反射动态调用

  1. 通过调用Class类的forname方法获取类,创建该类的对象。
  2. 通过调用该类的getMethod方法获取方法,并通过invoke调用该方法。
  3. 好处是可以通过读文件创建不同的类对象。

单例模式

  1. 懒汉式,该单例是static成员变量,初始值为空,在调用getInstance方法时创建该对象,在多线程情况下可加锁。
  2. 饿汉式,在申明static成员变量时就创建该对象,再调用getInstance方法获取该对象,但是会占内存空间。
  3. 静态内部类式,在getInstance方法中获取该静态内部类的final static成员变量,不调用该方法就不会创建该实例(final的引用地址不会改变)。

stringbuilder和stringbuffer的区别

  1. stringbuffer需要创建对象,可以对字符串进行添加或删除字符。
  2. stringbuffer实现原理:
    • 初始化时指定分配给该对象的实体的初始容量size。
    • 在进行字符串append添加的时候,会先计算添加后字符串大小判断是否需要扩容。
    • 若需要扩容,则先尝试将新容量扩为大小变成2倍+2,容量如果不够,直接扩充到需要的容量大小。
  3. stringbuffer线程安全,stringbuilder线程不安全,但是速度快。
  4. jvm对于string的+操作:new一个StringBuffer对象来处理字符串的连接。

线程池

  1. 可以让线程进行复用,避免重复的创建线程。当有工作来,就会向线程池拿一个线程,当工作完成后,并不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用。
  2. 核心池corePoolSize表示允许线程池中允许同时运行的最大线程数。
  3. 最大线程数maximumPoolSize表示线程池最多能创建多少个线程。

hashmap

  1. 列表+链表
  2. 列表+红黑树

编译型语言和解释型语言的区别

  1. 编译型语言用编译器将源代码一次性编译成机器语言。以后运行会非常方便
  2. 解释型语言用解释器将源代码解释成机器语言,以后运行时还是需要重新解释。这样效率会低一些,必须依赖解释器,但是跨平台性好。
  3. Java:将源代码(.java 文件)编译生成字节码(.class 文件),再通过 JVM(java 虚拟机)运行生成机器指令,由机器运行机器码

动态语言和静态语言

  1. 动态类型语言,是指数据类型的检查是在运行时做的。用动态类型语言编程时,不用给变量指定数据类型,该语言会在你第一次赋值给变量时,在内部记录数据类型。
  2. 静态类型语言,是指数据类型的检查是在运行前(如编译阶段)做的。
  3. 动态类型语言的优点是不需要写多种数据类型的代码,代码相对简洁一些,方便代码阅读。缺点是不方便调试,代码命名也容易混淆。
  4. 静态类型语言的优点是方便调试,代码相对规范。缺点是需要写很多数据类型相关的代码,代码不够简洁。

计算机系统

垃圾回收

  1. 对象的引用存放在链表中,当一个对象的引用次数为零时,便回收该对象。
  2. 在回收了一个对象后,从函数调用栈中获取该对象调用的其他对象,将这些对象的引用次数减一。
  3. 递归调用直到一个对象没有再引用其他对象为止。
  4. JVM分代:新生代、老年代,以及永生代。
    • 新对象会被分配在新生代内存。一旦新生代内存满了,就会开始对死掉的对象,进行小型垃圾回收过程。一片新生代内存里,死掉的越多,回收过程就越快;至于那些还活着的对象,此时就会老化,并最终老到进入老年代内存。
    • 老年代用来保存长时间存活的对象。通常,设置一个阈值,当达到该年龄时,年轻代对象会被移动到老年代。最终老年代也会被回收。这个事件成为 Major GC。
    • 永久代包含JVM用于描述应用程序中类和方法的元数据。永久代是由JVM在运行时根据应用程序使用的类来填充的。此外,Java SE类库和方法也存储在这里。

进程和线程的区别

  1. 进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
  2. 线程:进程的一个实体,是CPU调度和分派的基本单位,可与同属一个进程的其他的线程共享进程所拥有的全部资源。
  3. 区别:进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响;线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。
  4. 多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。线程执行开销小,但不利于资源的管理和保护;进程则相反。
  5. fork新的子进程时,操作系统会把当前进程的数据复制一遍,然后程序就分两个进程继续运行后面的代码,子进程的pid是0,父进程的pid是1

并行和并发的区别

  1. 并发:应用能够交替执行不同的任务。单个CPU(也可以多个CPU)将多个线程中的每个线程(多个进程中的每个进程)按时间分为一个一个的时间片,每一个时刻只执行某个线程(进程)的时间片,时间片过期后转而执行下一个线程(进程)的时间片。
  2. 并行:应用能够同时执行不同的任务。当有多核CPU时才有可能实现并行,并行就是多个线程或者多个进程同时运行。

死锁

  1. 产生原因:当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。
  2. 特殊例子:递归死锁,解决方法是避免在递归链上加锁。
  3. 避免死锁:
    • 加锁顺序,所有线程按照相同的顺序获得锁。
    • 加锁时限,在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求,加锁超时后可以先继续运行干点其它事情,再回头来重复之前加锁的逻辑。
    • 死锁检测,当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。之后给这些线程设置优先级,让一个(或几个)线程回退。

乐观锁和悲观锁

  1. 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。Java里面的synchronized关键字的实现也是悲观锁。缺点是:
    • 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换调度延时,引起性能问题。
    • 一个线程持有锁会导致其它所有需要此锁的线程挂起。如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险
  2. 乐观锁:乐观锁假设认为数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如何去做。主要有两个实现步骤:冲突检测数据更新。其实现方式有一种比较典型的就是CAS
  3. CAS的实现方式:我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。缺点是:
    • ABA问题,当前引用不等于预期引用。
    • 循环时间长开销大,自旋CAS(不成功,就一直循环执行,直到成功)。
    • 只能保证一个共享变量的原子操作。JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。

TCP的三次握手与四次挥手

  1. 序列号 seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;序列号seq就是这个报文段中的第一个字节的数据编号
  2. 确认号 ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号,因此当前报文段最后一个字节的编号+1即为确认号。
  3. 确认 ACK:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效。
  4. 同步 SYN:连接建立时用于同步序号。SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0。
  5. 终止 FIN:用来释放一个连接。FIN=1表示此报文段的发送方的数据已经发送完毕,并要求释放运输连接。
  6. 四次挥手中,TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态:有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。2MSL就是一个发送和一个回复所需的最大时间,如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
  7. 三次握手中,Client在Server的应答分组在传输中被丢失的情况下,Client认为连接还未建立成功,将忽略S发来的任何数据分组,只等待连接确认应答分组。而Server在发出的分组超时后,重复发送同样的分组,这样就形成了死锁。

大数据

事务的基本要素

  1. A(Atomicity)原子性:事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。
  2. C(Consistency)一致性:事务开始前和结束后,数据库的完整性约束没有被破坏。不同事务读取数据库的某个值,得到结果都是相同的。
  3. I(Isolation)隔离性:同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。
  4. D(Durability)持久性:事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

事务的并发问题

  1. 脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
  2. 不可重复读:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
  3. 幻读:事务A读的时候读出了15条记录,事务B在事务A执行的过程中增加了1条,事务A再读的时候就变成了16条,这种情况就叫做幻影读。
  4. 区别:
    • 脏读是某一事务读取了另外一个事务未提交的数据,不可重复读是读取了其他事务提交的数据。
    • 幻读和不可重复读都是读取了另一条已经提交的事务,所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

事务的隔离级别

  1. 读未提交:最低的事务隔离级别,一个事务还没提交时,它做的变更就能被别的事务看到。会引起脏读+不可重复读+幻读问题。
  2. 读已提交:保证一个事物提交后才能被另外一个事务读取。另外一个事务不能读取该事物未提交的数据。实现方式是写数据时锁住行。会引起不可重复读+幻读问题。
  3. 可重复读:多次读取同一范围的数据会返回第一次查询的快照,即使其他事务对该数据做了更新修改。实现方式是MVCC(多版本并发控制)。会引起幻读问题。
  4. 串行化:读写数据都会锁住表,当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。不会引起问题。

B树和B+树

  1. B和B+树的区别在于,B+树的非叶子结点只包含导航信息,不包含实际的值,所有的叶子结点和相连的节点使用链表相连,便于区间查找和遍历。
  2. B树的优点在于:由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。
  3. B+树的优点在于:
    • 由于B+树在内部节点上不包含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子节点上关联的数据也具有更好的缓存命中率。
    • B+树的叶子结点都是相链的,因此对整棵树的便利只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。
  4. 对磁盘的访问时间分为寻道时间旋转时间,以及传送时间。由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。
  5. 在B+树中,内节点只存储导航用到的key,并不存储具体值,这样内节点个数较少,能够全部读取到主存中,外接点存储key及值,并且顺序排列,具有良好的空间局部性。

内部排序和外部排序的区别

  1. 内部排序:待排序记录存放在内存进行的排序过程。排序方法有插入排序、快速排序、选择排序、归并排序、基数排序等。
  2. 外部排序:待排序记录的数量很大,以致于内存不能一次容纳全部记录,所以在排序过程中需要对外存进行访问的排序过程。外部排序的过程为:
    • 首先,按可用内存大小,将外存上的文件分成若干长度的子文件或段,依次读入内存并利用有效的内部排序方法对他们进行排序,并将排序后得到的有序子文件重新写入外存,通常称这些有序子文件为归并段。
    • 对这些归并段进行逐趟归并,使归并段逐渐由小至大,直至得到整个有序文件为止。排序方法有多路平衡归并和置换-选择排序。
  3. 置换-选择排序:
    • 将每个文件最开始的数读入(由于有序,所以为该文件最小数),存放在一个大小为子文件数的first_data数组中。
    • 选择first_data数组中最小的数min_data,及其对应的文件索引index。
    • 将first_data数组中最小的数写入文件result,然后更新数组first_data(根据index读取该文件下一个数代替min_data)。
    • 循环该过程直到所有数据读取完毕。

数据仓库

  1. 数据仓库的特点:
    • 主题性:数据仓库是针对某个主题来进行组织,比如滴滴出行,司机行为分析就是一个主题,所以可以将多种不同的数据源进行整合。而传统的数据库主要针对某个项目而言,数据相对分散和孤立。
    • 集成性:数据仓库需要将多个数据源的数据存到一起,但是这些数据以前的存储方式不同,所以需要经过抽取、清洗、转换的过程。
    • 稳定性:保存的数据是一系列历史快照,不允许修改,只能分析。
    • 时变性:会定期接收到新的数据,反应出最新的数据变化。
  2. ELT:
    • Extract:数据抽取,就是把数据从数据源读出来。
    • Transform:数据转换,就是把数据转换为特定的格式。
    • Load:数据加载,把处理后的数据加载到目标处。
  3. Hive可以对存储在HDFS的文件数据进行查询、分析。Hive对外可以提供HiveQL,这是类似于SQL语言的一种查询语言。在查询时可以将HiveQL语句转换为MapReduce任务,在Hadoop层进行执行。Hive主要针对的是OLAP分析型应用,需要从已有的数据库或日志进行同步最终入到hdfs文件系统中,当前要做到增量实时同步都相当困难。
  4. HBase是key-value型数据库,针对的是OLTP事务型应用,实时性要求高,数据量不是很大。

NoSQL

  1. CAP理论
    • C(Consistency):一致性。它是指任何一个读操作总是能够读到之前完成的写操作的结果,也就是在分布式环境中,多点的数据是一致的。
    • A(Availability):可用性。它是指快速获取数据,可以在确定的时间内返回操作结果。
    • P(Tolerance of Network Partiton):分区容忍性。它是指当出现网络分区的情况时(即系统中的一部分节点无法和其他节点进行通信),分离的系统也能够正常运行。
    • 选择CA,放弃P:MySQL,SQL Server。最简单的做法是把所有与事务相关的内容都放到同一台机器上,这种做法会严重影响系统的可扩展性。
    • 选择CP,放弃A:MongoDB,HBase,Redis等NoSQL数据库。当出现网络分区的情况时,受影响的服务需要等待数据一致,因此在等待期间就无法对外提供服务。
    • 选择AP,放弃C:Dynamo等NoSQL数据库。允许系统返回不一致的数据,如Web 2.0网站,用户首先关注的是网站服务是否可用。
  2. BASE理论
    • BA(Basically Available):基本可用是指一个分布式系统的一部分发生问题变得不可用时,其他部分仍然可以正常使用,也就是允许分区失败的情形出现。
    • S(Soft-state):软状态是指状态可以有一段时间不同步,具有一定的滞后性。如银行转移资金。
    • E(Eventually consistency):一致性的类型包括强一致性和弱一致性,二者的主要区别在于高并发的数据访问操作下,后续操作是否能够获取最新的数据。最终一致性是弱一致性的一种特例,也是ACID的最终目的。

Linux编程

  1. 查看所有的进程和端口使用情况:netstat –apn。
  2. 查看端口被哪个进程占用:netstat -apn | grep portno。
  3. ps 命令用于查看当前正在运行的进程,grep 是搜索,-aux 显示所有状态,kill 命令用于终止进程。
  4. git命令:init,clone,add,commit,pull,push,branch(查看位置),checkout(改动位置),status(查看改动情况)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值