秋招面经【华为 | 大疆 | 快手】【java】(三)

秋招也逐渐接近为尾声,通过一篇博客来记录秋招过程中的一些面经,主要来源自己实习、提前批、秋招面经和niuke整理。

华为

1、项目中的难点

2、volatile和synchronized的区别

两者特点

线程安全的两个方面:执行控制内存可见

执行控制:控制代码执行顺序及是否可以并发执行;

内存可见:控制线程执行结果在内存中对其他线程的可见性。

Java内存模型的实现:线程在具体执行时,会先拷贝主存数据到线程本地(CPU缓存),操作完成后再把结果从线程本地刷到主存。

  • synchronized解决的是执行控制的问题,它会阻止其他线程获取当前对象的监控锁,这样使得当前对象中被synchronized关键字保护的代码块无法被其他线程访问,也就是无法并发执行。synchronized还会创建一个内存屏障,该指令保证了所有操作结果都会直接刷到贮存中,从而保证了操作的内存可见性

  • volatile解决的是内存可见性的问题,会使得所有对volatile变量的读写都会直接刷到主存,及保证了变量的可见性。这样就能满足一些对变量可见性有要求而对读写顺序没有要求的需求。

两者区别
  1. volatile本质是告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取;synchronized是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞。
  2. volatile仅能使用在变量级别;synchronized则可以使用在变量、方法和类级别。
  3. volatile仅能实现变量的修改可见性,不能保证原子性;synchronized则可以保证变量的修改可见和原子性。
  4. volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

3、大顶堆小顶堆怎么删除根节点

堆是一个完全二叉树结构:

  • 它的父节点位置在i/2
  • 它的左子结点位置在2i
  • 它的右子结点位置在2i+1

删除根节点:把最末的元素替换堆顶再进行堆化,这样得到的新堆肯定是符合其自身特点的。很明显,这里是从上往下的堆化过程。

img

4、CSRF攻击是什么?怎么预防?

CSRF(Cross-Site Request Forgery),也被称为 one-click attack 或者 session riding,即跨站请求伪造攻击

CSRF原理

1、用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;

2、在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;

3、用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;

4、网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;

5、浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。

防御方法
  1. CSRF漏洞检测

检测CSRF漏洞最简单的方法就是抓取一个正常请求的数据包,去掉Referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞。

Referer字段:HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器该网页是从哪个页面链接过来的,服务器因此可以获得一些信息用于处理。

  1. 服务端防御

遵循标准的GET动作:只允许GET请求检索数据,但是不允许它修改服务器上的任何数据。

为页面增加随机数:当用户访问站点时,该站点应该生成一个(密码上很强壮的)伪随机值,并在用户的计算机上将其设为cookie。站点应该要求每个表单都包含该伪随机 值(作为表单值和cookie值)。当一个POST请求被发给站点时,只有表单值和cookie值相同时,该请求才会被认为是有效的。

  1. 在非 GET 请求中增加 token并验证

​ 可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

  1. 检查 HTTP Referer 字段

​ HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。

5、进程间的通信方式

管道(pipe)

管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系(父子进程关系)的进程间使用。

有名管道(namepipe)

有名管道也是半双工的通信方式,但是他允许无亲缘关系进程间的通信。

信号量(semophore)

信号量是一个计数器,可以用来控制多个进程对共享资源的访问。通话吃那个作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

消息队列(messagequeue)

消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少的缺点。

信号(signal)

信号时一种比较复杂的通信方式,用于通知接收进程某个事件已经发生

共享内存(shared memory)

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。

套接字(sokect)

套接字也是一种进程间通信机制,与其他进程机制不同的是,它可用于不同设备及其间的进程通信。

6、线程间的通信方式

锁机制:包括互斥锁、条件变量、读写锁
  • 互斥锁提供了以排他方式防止数据结构被并发修改的方法
  • 读写锁允许多个线程同时读共享数据,而对写操作是互斥的
  • 条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。
信号量机制:包括无名线程信号量和命名线程信号量
信号机制:类似进程间的信号处理

7、mysql常用的数据结构

  • 整型(xxxint
  • 位类型(bit)
  • 浮点型(floatdoublereal
  • 定点数(decimal,numeric
  • 日期时间类型(date,time,datetime,year
  • 字符串(char,varchar,xxxtext
  • 二进制数据(xxxBlobxxbinary
  • 枚举(enum
  • 集合(set

引用链接

8、单例模式

懒汉式(用的时候再new)

1、双重锁volatile & synchronized

public class DoubleCheckSingleton {
    
    
    private static volatile DoubleCheckSingleton instance;
    private DoubleCheckSingleton() {
    
    }
    public static synchronized DoubleCheckSingleton getInstance() {
    
    
        if(instance == null) {
    
     //判断是否实例化
            synchronized (DoubleCheckSingleton.class) {
    
    
                if(instance == null) {
    
    
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance; //否则直接return
    }
}

2、单重锁(synchronized

public class SafeSingleton {
    
    
    private static SafeSingleton instance;
    private SafeSingleton() {
    
    }
    //synchronized同步处理
    public static synchronized SafeSingleton getInstance() {
    
    
        if(instance == null) {
    
    
            instance = new SafeSingleton();
        }
        return instance;
    }
}

3、不安全模式

public class NoneSafeSingleton {
    
    
    private static NoneSafeSingleton instance;
    private NoneSafeSingleton() {
    
    }
    public static NoneSafeSingleton getInstance() {
    
      //调用时才实例化对象,懒汉式
        if(instance == null) {
    
    
            instance = new NoneSafeSingleton();
        }
        return instance;
    }
}
饿汉式(先new一个实例)

1、静态变量形式

public class Singleton_1 {
    
    
    //私有化构造器
    private Singleton_1() {
    
    }
    //内部创建对象实例
    private final static Singleton_1 instance = new Singleton_1();
    //对外公有的静态方法
    public static Singleton_1 getInstance() {
    
    
        return instance;
    }
}

2、静态代码块模式

public class Singleton_2 {
    
    
    //私有化构造器
    private Singleton_2() {
    
    }
    //内部创建对象实例
    private static Singleton_2 instance;
    static {
    
     // 在静态代码块中,创建单例对象
        instance = new Singleton_2();
    }
    //对外公有的静态方法
    public static Singleton_2 getInstance() {
    
    
        return instance;
    }
}

9、进程和线程的区别

进程

​ 一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。

线程

​ 线程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。

快手

1、前k个高频元素

public static int[] method(int[] nums, int k){
    
    
    int[] topK = new int[k];
    Map<Integer, Integer> map = new HashMap<>();
    PriorityQueue<int[]> queue = new PriorityQueue<>((a, b) -> (a[1] - b[1]));
    for(int num : nums){
    
    
        map.put(num, map.getOrDefault(num, 0) +1);
    }
    for(Map.Entry<Integer, Integer> mapEntry : map.entrySet()){
    
    
        int temp = mapEntry.getKey();
        int count = mapEntry.getVaule();
        if(queue.size() < k) queue.offer(new int[]{
    
    temp, count});
        else{
    
    
            if(queue.peek()[1] < count){
    
    
                queue.poll();
                queue.offer(new int[]{
    
    temp, count});
            }
        }
    }
    int i = 0;
    while(!queue.isEmpty()) topK[i] = queue.poll()[0];
    return topK;
}

2、某一时间段的新增用户数

SELECT DATE_FORMAT(create_date, "%Y-%m-%d") AS time, count(*) AS total
FROM survey_user
WHERE time BETWEEN "2021-11-21 00:00:00" AND "2022-11-21 00:00:00"
GROUP BY time
ORDER BY total ASC;

3、局域网下抓iphone的包

使用 Charles 抓取 iPhone 的手机报文需要进行以下步骤:
1.配置 Charles 代理:在 Charles 中选择“Proxy”菜单,然后选择“Proxy Settings”,在“Proxy Settings”对话框中选择“Proxies”选项卡,在“Proxies”选项卡中勾选“Enable macOS Proxy”和“Enable iOS Simulator Proxy”(如果你使用的是 iOS 模拟器)。

2.在 iPhone 上配置代理:打开 iPhone 的设置,选择“无线局域网”,然后选择当前连接的 Wi-Fi 网络,在网络详情页面中选择“配置代理”,选择“手动”,在“服务器”和“端口”输入 Charles 的 IP 地址和端口号(默认为8888)。

3.安装 Charles 的 SSL 证书:在 Charles 中选择“Help”菜单,然后选择“SSL Proxying”和“Install Charles Root Certificate”,然后按照提示安装证书。

4.开始抓取手机报文:在 Charles 中选择“Proxy”菜单,然后选择“SSL Proxying Settings”,在“SSL Proxying Settings”对话框中选择“Add”按钮,将需要抓取的域名添加到“Location”列表中,然后在 iPhone上打开需要抓取的应用程序,Charles 就会开始抓取该应用程序的手机报文。

5.需要注意的是,有些应用程序可能使用了 HTTPS 加密,这时需要在 Charles 中对 HTTPS 流量进行解密才能抓取到明文数据。在“Proxy”菜单中选择“SSL Proxying”和“Install Charles Root Certificate on a Mobile Device or Remote Browser”,然后按照提示安装证书并在 iPhone中信任该证书即可。

大疆

1、使用自动化编写测试用例的流程

  • 了解需求和规格

在编写测试用例之前,测试人员需要充分了解软件的需求和规格,以确保测试用例能够覆盖所有的功能和场景。包括阅读需求文档分析规格确认需求细节点

  • 编写测试用例

在了解需求之后,就可以编写测试用例。测试用例应该能够覆盖软件的各种功能和场景,以确保软件质量。包括选择测试工具确定测试场景编写测试用例确认测试用例分类测试用例

2、测试用例包括哪些部分

  • 用例编号
  • 所属模块
  • 用例标题
  • 前提条件
  • 操作步骤
  • 测试数据
  • 期望结果
  • 实际结果

3、如何评判测试用例的好坏

  • 完整性

检查用例是否覆盖了各种场景、边界值和异常情况,确保测试的全面性和完整性

  • 可读性

测试用例应该具有明确的描述和步骤,以便测试人员能够轻松理解并执行测试

  • 一致性

测试用例应该遵循一致的格式、命名规范和约定,以便于管理和执行

  • 可重复性

测试用例应该能够在不同的环境和条件下重复执行,并产生相同的结果

  • 独立性

测试用例应该相互独立,不依赖于其他测试用例的执行结果

  • 可测量性

确定测试用例是否具有明确的预期结果和度量标准,以便能够准确评估测试结果

  • 效率

测试用例应该能够在合理的时间内执行完成,一边提高测试的笑和效果

  • 异常处理

测试用例应该能够覆盖各种异常情况,并验证系统在异常情况下的行为和响应

  • 强壮性

测试用例能够处理各种极端和负面的情况,并验证系统的稳定性和鲁棒性

  • 可维护性

测试用例应该易于维护和更新,以应对需求和系统的变更。

4、静态测试和动态测试

  • 静态测试

是指不运行被测试的软件系统,而是采用其他手段和技术对被测软件进行检测的一种技术。例如:代码走读、文档评审、程序分析。常用的静态分析技术包括:控制流、信息流和数据流,现在很多问题在编辑器的时候就解决了。在我们进行测试过程中,关于静态测试用的最多的是对文档进行评审。

  • 动态测试

是通过观察代码运行时的动作,来提供执行跟踪、时间分析及测试覆盖度方面的信息、动态测试通过真正运行程序发现错误。通过有效的测试用例,对应的输入输出关系来分析被测程序的运行情况。

5、消息中间件

接受某一方的消息同时,其他地方也可以从这里拿到消息。

举例:菜鸟驿站:每一个小区都有一个菜鸟驿站,每个小区的人只关心自己小区的菜鸟驿站;从网上买了东西,东西不会直接送到手上,而是送到菜鸟驿站,

1.从快递员的角度:快递员不需要将每件商品送到客户手中,在菜鸟驿站放下产品就走,提升了送货的效率;

2.从用户的角度:快递到的时候可能在上班上学,没时间接收快递,但是有了菜鸟驿站,可以根据自己的时间去取快递;

3.当有双十一等购物活动时:当订单量暴增时,因为有了菜鸟驿站快递员不会手忙脚乱,用户也只需要在菜鸟驿站排队取回自己购买的商品即可。

实际场景:

1.在微服务中,两个互不相干的系统,如果存在接口调用,可以让二者面向mq编程,让二者互不相干独立变化,调用方将请求封装成消息,直接发送到mq当中,被调用方消费mq当中的消息,处理具体的业务逻辑;这样就让多个互相调用的系统,实现了完全解耦;

2.有了mq之后,调用方发送完消息之后直接返回,也可以大大提升接口的响应速度,实现了多系统当中的异步调用;

3.在应对超高流量并发的场景,同样前端可以把请求封装成消息,发送给mq直接返回,这样前置服务可以支持更高流量的并发,而后置的耗时耗力的需要进行io操作的服务,通过消耗mq去处理它,这样就实现了削峰填谷,在大流量高并发的情况下,让前端不断的写入消息,写消息的速度是要比处理消息的速度快的多得多,而对于后端业务逻辑的处理所支持的并发是极其有限的,这样所积攒的消息,后端可以慢慢的处理。

可以降低服务与服务之间的耦合程度,提升接口的响应速度,并对大流量提供削峰解耦的能力。

猜你喜欢

转载自blog.csdn.net/weixin_55488173/article/details/134311168