常见面试题

接口和抽象类有什么共同点和区别?

共同点 :
都不能被实例化。
都可以包含抽象方法。
都可以有默认实现的方法(Java 8 可以用 default 关键字在接口中定义默认方法)。

区别 :
接口主要用于对类的行为进行约束,实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系。
一个类只能继承一个类,但是可以实现多个接口。
接口中的成员变量只能是 public static final 类型的,不能被修改且必须有初始值,而抽象类的成员变量默认 default,可在子类中被重新定义,也可被重新赋值

深拷贝和浅拷贝区别了解吗?什么是引用拷贝?

浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
深拷贝 :深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
引用拷贝: 简单来说,引用拷贝就是两个不同的引用指向同一个对象。
在这里插入图片描述

Object 类的常见方法有哪些?

/**
 * native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。
 */
public final native Class<?> getClass()
/**
 * native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的HashMap。
 */
public native int hashCode()
/**
 * 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等。
 */
public boolean equals(Object obj)
/**
 * naitive 方法,用于创建并返回当前对象的一份拷贝。
 */
protected native Object clone() throws CloneNotSupportedException
/**
 * 返回类的名字实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法。
 */
public String toString()
/**
 * native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
 */
public final native void notify()
/**
 * native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
 */
public final native void notifyAll()
/**
 * native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间。
 */
public final native void wait(long timeout) throws InterruptedException
/**
 * 多了 nanos 参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 毫秒。。
 */
public final void wait(long timeout, int nanos) throws InterruptedException
/**
 * 跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
 */
public final void wait() throws InterruptedException

String、StringBuffer、StringBuilder 的区别?

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
总结:
操作少量的数据: 适用 String
单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

Java IO 流了解吗?

IO 即 Input/Output,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。

Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的:

  1. InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  2. OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

Java 只有值传递

方法接收的是实参值的拷贝,会创建副本。即便是引用类型也是对象的地址副本,只有通过这个地址去修改堆中对象,对象才会变化,如果仅仅对地址进行交换,因为交换的仅仅是副本,作用范围就只在方法之内。

什么是线程死锁?

线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁(锁对象),然后通过Thread.sleep(1000);让线程 A 休眠 1s 为的是等线程 B 得到CPU执行权然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。

产生锁的四个必要条件:

  1. 互斥条件:该资源任意一个时刻只由一个线程占用。
  2. 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
  4. 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。

如何预防和避免线程死锁?

如何预防死锁?破坏死锁的产生的必要条件即可:

  1. 破坏请求与保持条件 :一次性申请所有的资源。
  2. 破坏不剥夺条件 :占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
  3. 破坏循环等待条件 :靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

为什么 wait() 方法不定义在 Thread 中?而sleep() 方法定义在 Thread 中?

wait() 是让获得对象锁的线程实现等待,会自动释放当前线程占有的对象锁。每个对象(Object)都拥有对象锁,既然要释放当前线程占有的对象锁并让其进入 WAITING 状态,自然是要操作对应的对象(Object)而非当前的线程(Thread)。

而 sleep() 是让当前线程暂停执行,不涉及到对象类,也不需要获得对象锁。

可以直接调用 Thread 类的 run 方法吗?

new 一个 Thread,线程进入了新建状态。调用 start()方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 但是,直接执行 run() 方法,会把 run() 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。
总结: 调用 start() 方法方可启动线程并使线程进入就绪状态,直接执行 run() 方法的话不会以多线程的方式执行。

synchronized 和 ReentrantLock 的区别

可重入性:就是一个线程不用释放,可以重复的获取一个锁n次,只是在释放的时候,也需要相应的释放n次。(简单来说:A线程在某上下文中或得了某锁,当A线程想要在次获取该锁时,不会应为锁已经被自己占用,而需要先等到锁的释放)假使A线程即获得了锁,又在等待锁的释放,就会造成死锁。
两者都是可重入锁,“可重入锁” 指的是自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果是不可重入锁的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增 1,所以要等到锁的计数器下降为 0 时才能释放锁。

synchronized:无需释放锁,synchronized会自动释放锁。
reentrantlock:上几次锁( reentrantLock.lock()),就需要手动释放几次unlock()。

ReentrantLock 比 synchronized 增加了一些高级功能

等待可中断 : ReentrantLock提供了一种能够中断等待锁的线程的机制,通过 lock.lockInterruptibly() 来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
可实现公平锁 : ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来制定是否是公平的。
可实现选择性通知(锁可以绑定多个条件): synchronized关键字与wait()和notify()/notifyAll()方法相结合可以实现等待/通知机制。ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition()方法。

Runnable 接口和 Callable 接口的区别

Runnable 接口 不会返回结果或抛出检查异常,但是 Callable 接口 可以。所以,如果任务不需要返回结果或抛出异常推荐使用 Runnable 接口 ,这样代码看起来会更加简洁。

多线程的内存划分

线程私有的:
程序计数器
虚拟机栈
本地方法栈

线程共享的:

方法区
直接内存 (非运行时数据区的一部分)


栈(Java 虚拟机栈)绝对算的上是 JVM 运行时数据区域的一个核心,除了一些 Native 方法调用是通过本地方法栈实现的(后面会提到),其他所有的 Java 方法调用都是通过栈来实现的(也需要和其他运行时数据区域比如程序计数器配合)。
虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。

方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

java获取对象的几种手段

new关键字;clone();反序列化;反射机制

mysql底层数据存储

B+树,每个节点大约能存16kb,一个元素(索引8字节+指针6字节)只有14字节,所以极大的增大了索引个数,叶子节点中的一个元素是索引+数据(数据假设1kb,索引8字节)。‘show global status like ‘innodb_page_size’’可以查看mysql中每个节点的大小即为16KB。B+树每增加一层就增加一次磁盘IO,mysql一启动就会将根节点加载进内存,所以磁盘io次数减一。

避免回表查询影响性能,采用覆盖索引的方式来避免,即将select *替换成select 索引字段。(并且select后必须全部为索引字段,否则都会回表)。可以用explain来查看SQL语句执行时是否使用了哪些索引以及是否回表查询了。

show global status like ‘Innodb_rows%’ 语句可以查看数据库是查询密集型和修改密集型。查询较多的建议使用mysql索引优化,但是增删改多(如商品订单)的不建议使用索引,因为增删改均要更新索引。修改型数据库建议使用es优化。查看索引:show index from 表名。mysql不使用hash索引的原因:一是因为大量的重复元素导致哈希冲突,形成链表导致性能变低,二是因为hash只支持等值查询比较快,如果带范围如where age>5 会很慢。

① 普通索引 (仅提高搜索效率)
create table t_dept(
    no int not null primary key,
    name varchar(20) null,
    sex varchar(2) null,
    info varchar(20) null,
    index index_no(no)
  )
  
② 唯一索引 (唯一约束+提高搜索效率)
create table t_dept(
       no int not null primary key,
       name varchar(20) null,
       sex varchar(2) null,
       info varchar(20) null,
       unique index index_no(no)
     )

③ 全文索引 (通过查找文本中的关键字,类似于一些搜素引擎)
create table t_dept(
       no int not null primary key,
       name varchar(20) null,
       sex varchar(2) null,
       info varchar(20) null,
       fulltext index index_no(no)

④ 组合(联合)索引  (多个字段组成索引)
create table t_dept(
       no int not null primary key,
       name varchar(20) null,
       sex varchar(2) null,
       info varchar(20) null,
       key index_no_name(no,name)
     )

索引创建原则:
1.字段字段识别度不低于70%,如姓名年龄可以创建索引,而性别就不适合
2.经常使用where搜索的建议使用索引,如user表中的 id name字段
3.经常使用表连接的字段(内、外连接),可以加快连接速度。
4.经常排序的字段order by ,因为索引已经是排过序的,这样可以利用索引的排序,更快。

索引失效原因见:http://t.csdn.cn/2JRJh
纠正补充最左原则:当索引字段均出现在where后,无需考虑顺序,索引均不会失效,只要索引字段不是全部出现,不满足最左原则,则索引全部失效,key_len=null。而当只有一三字段出现,则只有一字段索引生效,三索引失效。
补充:
1.如果模糊查询以%开头的会索引失效,但是可以通过覆盖索引可以使索引生效。
2.MySQL会自动判断SQL语句,如果走索引效率更低,那就选择不走索引。例如北京市不会走索引,而上海市会走索引。
在这里插入图片描述

3.in(‘bejing’)
优化案例:
实际开发中分页查询的优化是分库分表。
在这里插入图片描述
两种优化方案:
在这里插入图片描述

ES

在这里插入图片描述

输入URL 到页面展示到底发生了什么?(非常重要)

类似的问题:打开一个网页,整个过程会使用哪些协议?

总体来说分为以下几个过程:
1.DNS 解析
2.TCP 连接
3.发送 HTTP 请求
4.服务器处理请求并返回 HTTP 报文
5.浏览器解析渲染页面
6.连接结束

HTTP 状态码有哪些?

在这里插入图片描述

HTTP 和 HTTPS 有什么区别?(重要)

端口号 :HTTP 默认是 80,HTTPS 默认是 443。
URL 前缀 :HTTP 的 URL 前缀是 http://,HTTPS 的 URL 前缀是 https://。
安全性和资源消耗 : HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS 是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。

TCP 与 UDP 的区别(重要)

是否面向连接 :UDP 在传送数据之前不需要先建立连接。而 TCP 提供面向连接的服务,在传送数据之前必须先建立连接,数据传送结束后要释放连接。
是否是可靠传输:远地主机在收到 UDP 报文后,不需要给出任何确认,并且不保证数据不丢失,不保证是否顺序到达。TCP 提供可靠的传输服务,TCP 在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制。通过 TCP 连接传输的数据,无差错、不丢失、不重复、并且按序到达。
是否有状态 :这个和上面的“是否可靠传输”相对应。TCP 传输是有状态的,这个有状态说的是 TCP 会去记录自己发送消息的状态比如消息是否发送了、是否被接收了等等。为此 ,TCP 需要维持复杂的连接状态表。而 UDP 是无状态服务,简单来说就是不管发出去之后的事情了(这很渣男!)。
传输效率 :由于使用 TCP 进行传输的时候多了连接、确认、重传等机制,所以 TCP 的传输效率要比 UDP 低很多。
传输形式 : TCP 是面向字节流的,UDP 是面向报文的。
首部开销 :TCP 首部开销(20 ~ 60 字节)比 UDP 首部开销(8 字节)要大。
是否提供广播或多播服务 :TCP 只支持点对点通信,UDP 支持一对一、一对多、多对一、多对多;
在这里插入图片描述

OSI七层协议

OSI有七层协议,但是他既复杂也不实用 。TCP/IP是一个四层结构,但是它的网络接口层没什么具体内容。所以综合OSI和TCP/IP的优点,采用一种只有五层协议的体系结构。
在这里插入图片描述

  1. 应用层
    定义了应用进程之间的通信和交互规则。这里的进程是指主机中正在运行的程序。对于不同的网络应用需要有不同的应用层协议。如域名系统DNS,支持万维网应用的HTTP协议,支持电子邮件的 SMTP协议等。我们把应用层交互的数据单元称为报文。
  2. 运输层(传输层)
    负责向两台主机中进程之间的通信提供通用的数据传输服务。TCP协议和UDP协议。
  3. 网络层
    负责为分组交换网上的不同主机提供通信服务。把运输层产生的报文段(TCP的传输单位)或用户数据报(UDP的传输单位)封装成分组进行传输。
  4. 数据链路层
    将IP数据报组装成,在两个相邻节点间的链路上传送
  5. 物理层
    物理层上传输数据的单位是比特。物理层需要考虑用多大的电压代表“1”或“0”,以及接收方如何识别出发送方所发送的比特。以及确定连接电缆的插头应当有多少根引脚以及各引脚应如何连接。

使用 TCP 的协议有哪些?使用 UDP 的协议有哪些?

运行于 TCP 协议之上的协议 :HTTP 协议、HTTPS 协议 、FTP 协议、SMTP 协议、SSH 协议
运行于 UDP 协议之上的协议 :DHCP 协议、DNS

三次握手(重要)

在这里插入图片描述

数据传输的三种交换方式

在这里插入图片描述

路由器的作用

路由器是实现分组交换的关键构件,其任务是转发收到的分组或包,这是网络核心部分最重要的功能。分组交换采用存储转发技术,表示把一个报文(要发送的整块数据)分为几个分组后再进行传送。在发送报文之前,先把较长的报文划分成为一个个更小的等长数据段。在每个数据端的前面加上一些由必要的控制信息组成的首部后,就构成了一个分组。分组又称为包。分组是在互联网中传送的数据单元,正是由于分组的头部包含了诸如目的地址和源地址等重要控制信息,每一个分组才能在互联网中独立的选择传输路径,并正确地交付到分组传输的终点。

交换机和路由器的区别

路由器:寻址,转发(依靠 IP 地址)
交换机:过滤,转发(依靠 MAC 地址)

路由器内有一份路由表,里面有它的寻址信息(就像是一张地图),它收到网络层的数据报后,会根据路由表和选路算法将数据报转发到下一站(可能是路由器、交换机、目的主机)

交换机内有一张MAC表,里面存放着和它相连的所有设备的MAC地址,它会根据收到的数据帧的首部信息内的目的MAC地址在自己的表中查找,如果有就转发,如果没有就放弃
在这里插入图片描述
每一个路由器与其之下连接的设备,其实构成一个局域网
交换机工作在路由器之下,也就是交换机工作在局域网内
交换机用于局域网内网的数据转发
路由器用于连接局域网和外网

举例:我们每个人相当于主机,路由器相当于快递员,宿管大爷相当于交换机,学校是一个局域网。快递员根据学校地址(IP)把包裹送到学校,再根据公寓号(子网IP)把快递交给这个公寓的宿管大爷,宿管大爷根据你的名字(MAC)交给你。

交换机和路由器可不可以少一个?

交换机在局域网内工作,它根据 MAC 地址转发数据,如果没有了路由器在网络层寻址,那么我们的数据就不能发送到其他网络终端上去了

路由器内集成了交换机的功能,主机与路由器相连也可以实现数据转发,但是不足之处是:
1.可扩展的接口不如交换机多
2.交换机通常由硬件加速转发,路由器主要靠软件寻址,速度慢

实际网络数据转发过程

在这里插入图片描述
你的电脑先在应用层打包一个 HTTP报文,然后在传输层在打包成 TCP报文,然后再根据 DNS 查到的 IP 在网络层打包成 IP数据报,然后在通过链路层打包成以太网数据帧,发送给你的交换机:
在这里插入图片描述
你的交换机收到后,重新包装数据帧,再发送给你的路由器:
在这里插入图片描述
你的路由器利用 NAT(Network Address Translation),将你的主机IP(局域网IP)转换为外网IP,还会修改端口号,对外完全隐藏你的主机,再根据路由表选择一条合适的路径进行转发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值