【Java校招面试】实战面经(八)


前言

“实战面经”是本专栏的第二个部分,本篇博文是第八篇博文,如有需要,可:

  1. 点击这里,返回本专栏的索引文章
  2. 点击这里,返回上一篇《【Java校招面试】实战面经(七)》
  3. 点击这里,前往下一篇《【Java校招面试】实战面经(九)》

一、线程几种状态以及什么时候产生调用sleep()后线程处于什么状态?

  1. 线程有: 创建运行就绪等待超时等待阻塞终止7种状态

  2. 调用Thread.sleep()后线程会进入阻塞状态。
    在这里插入图片描述


二、loadClass和forName的区别

  1. Class.forName得到的class是已经初始化完成的;

  2. ClassLoader.loadClass得到的class是还没有链接的;

  3. 应用场景:
      1) 有的类有静态代码块(如mysql-connector),因此需要使用Class.forName;
      2) Spring中由于IoC大量使用LazyLoad技术,即延时加载,在bean真正被调用时才完成初始化。这里用到的是ClassLoader.loadClass。


三、字符流和字节流的区别

  1. 字节流在操作时不会用到缓冲区(内存),是直接对文件本身进行操作的。而字符流在操作时使用了缓冲区,再通过缓冲区操作文件。

  2. 字节流对应InputStreamOutputStream,字符流对应ReaderWriter


四、观察者模式

观察者模式即观察者注册到被观察者上,被观察者发现有新的消息时,推送给所有观察者。

  1. 观察者接口
	public interface Observer {
	    void update(String message);
	}
  1. 被观察者(可观察的)
	public interface Observable {
	    void register(Observer o);
	    void unregister(Observer o);
	    void notifyRegistered();
	}
  1. 服务器(被观察者实现类)
	public class Server implements Observable{
	    private static final LinkedList<Observer> USERS = new LinkedList<>();
	    @Override
	    public void register(Observer o) {
	        USERS.add(o);
	    }
	    @Override
	    public void unregister(Observer o) {
	        USERS.remove(o);
	    }
	    @Override
	    public void notifyRegistered() {
	        USERS.forEach(user -> user.update("New Message Recieved."));
	    }
	}
  1. 用户(观察者实现类)
	public class User implements Observer{
	    private String userName;
	    public User(String userName){
	        this.userName = userName;
	    }
	 
	    @Override
	    public void update(String message) {
	        System.out.println(userName + ": " + message);
	    }
	}
  1. 测试类
	public class Main {
	    public static void main(String[] args) {
	        Server server = new Server();
	        User user1 = new User("User1");
	        User user2 = new User("User2");
	        server.register(user1);
	        server.register(user2);
	        server.notifyRegistered();
	    }
	}

五、模板模式

子类继承父类套用其方法模板,关键在于父类指定了所有模板方法的执行顺序

  1. 抽象模板类
	public abstract class Template {
	    protected abstract void step1();
	    protected abstract void step2();
	    protected abstract void step3();
	    protected final void execute(){
	        step1();
	        step2();
	        step3();
	    }
	}
  1. 套用模板的子类
	public class TemplateImpl extends Template{
	    @Override
	    protected void step1() {
	        System.out.println("Executing step 1.");
	    }
	    @Override
	    protected void step2() {
	        System.out.println("Executing step 2.");
	    }
	    @Override
	    protected void step3() {
	        System.out.println("Executing step 3.");
	    }
	}
  1. 测试类
	public class Main {
	    public static void main(String[] args) {
	        TemplateImpl templateImpl = new TemplateImpl();
	        templateImpl.execute();
	    }
	}

六、协程是什么?

协程用来实现线程内的并发,即在遇到IO的时候通过yield中断并保存上下文环境,并让出CPU去执行其他的子程序,然后在CPU再次切换回来的时候,如果IO结束了就继续执行下面的程序。

优点: 避免了线程切换对CPU资源的耗费,在IO阻塞时充分利用CPU,提高了CPU的利用率。


七、RPC框架和普通http有什么区别和优势? 基于Tcp封装还是http封装的?

  1. RPC要比http更快,虽然底层都是socket,但是http协议的信息往往比较臃肿。

  2. RPC实现较为复杂,http相对比较简单。

  3. RPC要求服务的提供方和消费方使用同样的RPC框架,Http方式更灵活,没有规定API和语言。

  4. RPC可以基于TCP也可以基于HTTP,主流的RPC框架基于TCP。

RPC的主要应用如天气预报查询等。


八、RPC是长连接吗?如果要传输一个特别大的文件底层还是基于流吗?NIO是一个什么IO模型?

  1. RPC有长连接的实现也有短连接的实现,长连接的框架有DubboNetty,短连接的框架有RESTfulSOAP

  2. 传输大文件时底层基于缓冲区。

  3. NIO是基于缓冲区的同步非阻塞IO,见《实战面经(一)》第十一题


九、GitHub的watch、star和fork的作用

  1. watch类似于关注,该仓库如果有更新,watch的人会受到邮件。

  2. star类似于喜欢或者点赞,可以在自己的stars里找到“点赞”过的仓库

  3. fork相当于复制仓库,如果我们发现源仓库的代码有Bug,就可以在自己的拷贝上修改,然后向源仓库发起Pull Request,如果源仓库的作者同意,就会被merge


十、Exception和Error的区别,OOM是Error还是Exception?

  1. Exception和Error都继承了Throwable
      Exception: 程序可以处理的异常,捕获后可能恢复
      Error: 程序无法处理的系统错误,编译器不做检查

  2. OOM属于Error,常见的Error有:
      1) NoClassDefFoundError: 找不到class定义的异常
      2) StackOverflowError: 栈溢出异常
      3) OutOfMemoryError: 内存溢出异常


十一、拥塞控制算法有哪些?

  1. 慢启动: 初始窗口大小cwnd = 1,每收到一个ACK就cwnd++,每过一个RTT(往返时延)就cwnd *= 2,到达阈值后进入拥塞避免阶段。

  2. 拥塞避免: 每收到一个ACK, c w n d = c w n d + 1 c w n d cwnd = cwnd + \frac{1}{cwnd} cwnd=cwnd+cwnd1,每过一个RTT就cwnd++

  3. 拥塞控制: 分两种情况
      1) RTO(重传计时器超时): 阈值降为cwnd的一半,cwnd置为1,重新开始慢启动。
      2) 收到三个重复的ACK: 阈值降为cwnd的一半,进入快速恢复。

  4. 快速恢复: cwnd = 阈值 + 3 * MSS(最大报文段长度),重传重复ACK对应的包,如果再收到重复ACK,cwnd++。如果收到新的ACK,cwnd = 阈值,进入拥塞避免。


十二、流量控制协议

  1. 停等协议: 发送方发一帧,接收方收到后确认,然后发送方再发下一帧。

  2. 滑动窗口协议: 发送方连续发送多个帧,发送方收到一个帧后确认,发送方如果对连续的帧都收到确认,就向后滑动窗口。


十三、Ping命令做了什么?基于哪一层?

Ping命令使用ICMP(Internet Control Message Protocol)协议,向目标主机发送ICMP报文,通过目标主机的响应来判断其是否可达。ICMP属于网络层协议。


十四、哪些类实现了乐观锁?分析他们的源码

ConcurrentHashMapputVal方法中调用了casTabAt方法,当向表中插入键值的时候,如果发现对应的桶是空的,就调用casTabAt方法,而它又调用了Unsafe类的compareAndSwapObject方法将新的Node添到桶中。


十五、利用快排的找数组中的第K大(小),要求快排使用非递归方式

  1. 总控函数
    public static int findKthMax(int[] nums, int k, int left, int right) {
        int index, target = nums.length - k;
        while ((index = partition(nums, left, right)) != target){
            if (index > target)
                right = index - 1;
            else
                left = index + 1;
        }
        return nums[index];
    }
  1. 快排划分函数
    private static int partition(int[] nums, int left, int right) {
        int key = nums[left];
        while (left < right) {
            while (left < right && key < nums[right])
                right--;
            if (left < right)
                nums[left++] = nums[right];
            while (left < right && key > nums[left])
                left++;
            if (left < right)
                nums[right--] = nums[left];
        }
        nums[left] = key;
        return left;
    }

注意: 快排划分函数中间有两个if判断


十六、手写创建索引的MySQL代码

  1. 普通索引:
   CREATE INDEX idx_name ON table_name (columns)
  1. 唯一索引:
   CREATE UNIQUE INDEX idx_name ON table_name (columns)
  1. 添加主键索引:
   ALTER TABLE table_name ADD PRIMARY KEY (columns)

十七、让多个线程有顺序得执行一个对象中的多个方法(LeetCode-1114)

  1. waitnotifyAll方法
	class Foo {
	    private int counter = 0;
	    public void first(Runnable printFirst) throws InterruptedException {
	        synchronized (this) {
	            printFirst.run();
	            counter++;
	            this.notifyAll();
	        }
	    }
	 
	    public void second(Runnable printSecond) throws InterruptedException {
	        synchronized (this) {
	            while (counter < 1)
	                this.wait();
	            printSecond.run();
	            counter++;
	            this.notifyAll();
	        }
	    }
	 
	    public void third(Runnable printThird) throws InterruptedException {
	        synchronized (this) {
	            while (counter < 2)
	                this.wait();
	            printThird.run();
	            counter++;
	        }
	    }
	}

注:notify不用notifyAll会发生死锁,比如获取锁的顺序是:3 -> 2 -> 1,此时2和3 wait,1执行完调用notify,如果唤醒了2皆大欢喜;如果唤醒了3,发现counter < 2,2和3继续wait,没有线程可以继续唤醒它们。

  1. CountDownLatch方法
	class Foo {
	    private CountDownLatch latch1;
	    private CountDownLatch latch2;
	    public Foo() {
	        latch1 = new CountDownLatch(1);
	        latch2 = new CountDownLatch(1);
	    }
	 
	    public void first(Runnable printFirst) throws InterruptedException {
	        printFirst.run();
	        latch1.countDown();
	    }
	 
	    public void second(Runnable printSecond) throws InterruptedException {
	        latch1.await();
	        printSecond.run();
	        latch2.countDown();
	    }
	 
	    public void third(Runnable printThird) throws InterruptedException {
	        latch2.await();
	        printThird.run();
	    }
	}

注: CountDownLatchawait方法作用是等待计数为0时再执行后面的代码。


十八、linux常见指令

  1. 输出文件”file.txt”的第10行
      1) sed命令: sed -n '10p' file.txt
      2) awk命令: awk 'NR==10' file.txt
      3) head和tail组合命令: tail -n+10 file.txt | head -1

其中tail -n+10显示从第10行开始到文件结束的所有行,head - 1显示第一行

  1. 输出文件”file.txt”中符合(xxx) xxx-xxxx或xxx-xxx-xxxx格式的电话号码
      grep命令: grep -E ‘(^([0-9]{3}) |^[0-9]{3}-)[0-9]{3}-[0-9]{4}$’ file.txt

  2. 将文件内容转置(按列输出)
      awk命令:

	awk '{
	    for (i=1; i <= NF; i++) {
	        if (NR == 1) {
	            res[i] = $i
	        }
	        else {
	            res[i] = res[i]" "$i
	        }
	    }
	} END {
	    for(j=1; j <= NF; j++) {
	        print res[j]
	    }
	}' file.txt

其中NF是列号(1 <= NF <= n),NR是行号(1 <= NR <= m)$i表示当前行第i列 的内容。


十九、死锁检测算法?

资源分配图法
在这里插入图片描述

图中P1P2表示进程,r1r2表示资源,一个圆圈代表这个资源的一个实例。Pr的边为请求边,rP的边为分配边。

类似于拓扑排序法,先取一个请求可以得到满足的进程Pi,消掉它的所有请求边和分配边,以此类推,直到最后图中没有任何边,则表明不会死锁,否则就可能死锁。
在这里插入图片描述


后记

本篇面经题量不算法,但涉及的需要写代码的部分占比较大。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IMplementist

你的鼓励,是我继续写文章的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值