阿里Java后端面经【记录牛客】(上)

简介: 阿里Java后端面经【记录牛客】

一、项目问的很细,问数据库表的设计?如果数据量太大了怎么办?(水平拆分、垂直拆分)怎么拆?


垂直拆分是指数据表列的拆分:按照业务交易数据库、用户数据库、商品数据库、店铺数据库等进行拆分

拆分原则

1.把不常用的字段单独放在一张表;,

2.把text,blob等大字段拆分出来放在附表中

3.经常组合查询的列放在一张表中

优点

1.拆分后业务清晰,拆分规则明确。

2.系统之间整合或扩展容易。

3.数据维护简单。

缺点

1.部分表关联无法在数据库级别完成,需要在程序中完成

2. 事务处理相对更为复杂

3. 切分达到一定程度之后,扩展性会遇到限制

垂直拆分后遇到单机瓶颈,可以使用水平拆分。

水平拆分:水平拆分的典型场景就是大家熟知的分库分表(hash到不同的机器)。

按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中。

垂直拆分是把不同的表拆到不同的数据库中,而水平拆分是把同一个表拆到不同的数据库中。

优点

1.表关联基本能够在数据库端全部完成;

2.不会存在某些超大型数据量和高负载的表遇到瓶颈的问题;

3.事务处理相对简单


二、kafka 广播、单播、消息丢失、重复消费?


1.广播:主题消费Topic

2.单播:生产者 消费者

3.消息丢失:生产者在发送消息时,会有一个ack机制,当ack=0 或者 ack=1时,都可能会丢消息

acks=0

表示producer不需要等待任何broker确认收到消息的回复,就可以继续发送下一条消息。性能最高,但是最容易丢消息。大数据统计报表场景,对性能要求很高,对数据丢失不敏感的情况可以用这种。

acks=1

至少要等待leader已经成功将数据写入本地log,但是不需要等待所有follower是否成功写入。就可以继续发送下一条消息。这种情况下,如果follower没有成功备份数据,而此时leader又挂掉,则消息会丢失。


4.消息重复消费:幂等性的问题,发送消息如果配置了重试机制,比如由于网络波动,生产者未得到broker收到了消息的响应,就会触发重试机制,3秒后再次发送此消息。

新版kafka的broker幂等性

kafka每次发送消息会生成PID和Sequence Number,并将这两个属性一起发送给broker,broker会将PID和Sequence Number跟消息绑定一起存起来,下次如果生产者重发相同消息,broker会检查PID和Sequence Number,如果相同不会再接收


5.顺序消息

kafka想要保证消息顺序,是需要牺牲一定性能的,方法就是一个消费者,消费一个分区,可以保证消费的顺序性。但也仅限于消费端消费消息的有序性,无法保证生产者发送消息有序。

kafka的顺序消息仅仅是通过partitionKey,将某类消息写入同一个partition,一个partition只能对应一个消费线程,以保证数据有序。

比如:如果发送端配置了重试机制,kafka不会等之前那条消息完全发送成功才去发送下一条消息,这样可能会出现,发送了1,2,3条消息,第一条超时了,后面两条发送成功,再重试发送第1条消息,这时消息在broker端的顺序就是2,3,1了。发送端消息发送已经乱序,到了消费端消费时,自然无法保证顺序!

如果一定要保证生产-消费全链路消息有序,发送端需要同步发送,ack回调不能设置为0。且只能有一个分区,一个消费者进行消费,但这样明显有悖于kafka的高性能理论!


如何在多个分区中保证消息顺序和消息处理效率呢?

首先使用多个分区,消息可以被发送端发送至多个分区,保证消息发送的效率。

然后在消费端在拉消息时使用ConutdownLunch来记录一组有序消息的个数。如果达到个数,说明已拉取到完整的一组有序消息。然后在消费端根据消息序号进行排序,消费端将排好序的消息发到内存队列(可以搞多个),一个内存队列开启一个线程顺序处理消息。即可最大程度上既保证顺序又保证效率!


6.消息积压

线上有时因为发送方发送消息速度过快,或者消费方处理消息过慢,可能会导致broker积压大量未消费消息。

解决方案:此种情况如果积压了上百万未消费消息需要紧急处理,可以修改消费端程序,让其将收到的消息快速转发到其他topic(可以设置很多分区),然后再启动多个消费者同时消费新主题的不同分区。


7.延时消息

延时队列存储的对象是延时消息。所谓的“延时消息”是指消息被发送以后,并不想让消费者立刻获取,而是等待特定的时间后,消费者才能获取这个消息进行消费,延时队列的使用场景有很多

在订单系统中, 一个用户下单之后通常有 30 分钟的时间进行支付,如果 30分钟之内没有支付成功,那么这个订单将进行异常处理,这时就可以使用延时队列来处理这些订单了


8. 消息回溯

如果某段时间对已消费消息计算的结果觉得有问题,可能是由于程序bug导致的计算错误,当程序bug修复后,这时可能需要对之前已消费的消息重新消费,可以指定从多久之前的消息回溯消费,这种可以用consumer的offsetsForTimes、seek等方法指定从某个offset偏移的消息开始消费,完成消息的回溯消费!


三、数据库事务四大特性?


四、隔离性原理?


五、redis数据类型?(项目中点赞用到了set)数据量太大了怎么办?


六、redis为什么快?


七、常用的设计模式?主要说了双重校验单例模式的细节、观察者、发布-订阅(结合kafka聊了几句)


八、抽象类&接口&多态?


1.抽象类和抽象方法都必须使用abstract关键修饰。

2.子类继承抽象类【抽象类一定是父类】时,要重写抽象类中的所有抽象方法,如果没有重写所有的抽象方法,则该子类也必须定义为抽象类

3.抽象类不能创建对象,抽象类中有没有构造方法


多态【正是程序运行时判断执行其中哪个方法的能力】

一、要有继承(或者接口的实现);

二、要有重写;

三、父类引用指向子类对象。


我们在程序中定义的引用变量所指向的具体类型和通过该引用变量的方法调用在编程的时候并不确定,当处于运行期间才确定。就是这个引用变量究竟指向哪一个实例对象,在编译期间是不确定的,只有运行期才能确定,这样不用修改源码就可以把变量绑定到不同的类实例上,让程序拥有了多个运行状态,这就是多态


必须要有继承的情况存在;

在继承中必须要有方法覆盖;

必须由基类的引用指向派生类的实例,并且通过基类的引用调用被覆盖的方法;

public class Animal {
    static {
        System.out.println("Animal的静态方法");
    }
    public Animal() {
    }
    public void eat() {
        System.out.println("吃东西...");
    } 
}
public class Cat extends Animal {
    static {
        System.out.println("Cat的静态方法");
    }
    // 腹泻的方法
    @Override
    public void eat() {
        System.out.println("小猫吃东西...");
    }
    //特有的方法
    public void catchMouse() {
        System.out.println("抓老鼠...");
    }
    public static void main(String[] args) {
        // 1.Animal的静态方法
        // 2.Cat的静态方法
        Animal animal = new Cat();
        animal.eat(); //可以调用【因为复写:小猫吃东西...】
        //animal.catchMouse(); //不可以调用
        Cat cat = new Cat();
        cat.catchMouse();//可以调用【抓老鼠...】
        cat.eat();//小猫吃东西...
    }
}

九、spring AOP?

十、springboot自动配置原理?常用注解?


@SpringBootApplication:

包含@SpringbootConfiguration、@EnableAutoConfiguration、@ComponentScan

@Controller//标明这是一个SpringMVC的Controller控制器;

@Configuration//这是一个配置Spring的配置类;

@Configuration // 加载外部配置

@PropertySource(value = { “classpath:jdbc.properties”, “classpath:env.properties”, “classpath:httpclient.properties” }) // 读取配置文件数据

@ComponentScan(basePackages = “com.demo”) // 扫描包

@ImportResource(value = “classpath:dubbo/dubbo-consumer.xml”)//导入dubbo资源

@SpringBootApplication Spring Boot项目的核心注解,主要目的是开启自动配置,Spring Boot来自动选择并且完成web的相关加载工作。

@Repository:用于标注数据访问组件,即DAO组件。

@Service:用于标注业务层组件。

@RestController:用于标注控制层组件(如struts中的action),包含 @Controller@ResponseBody。

@ResponseBody:表示该方法的返回结果直接写入HTTP response body中

一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@responsebody后,会直接返回json数据。

@Component:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

@ComponentScan:组件扫描。个人理解相当于context:component-scan,如果扫描到有@Component @Controller @Service等这些注解的类,则把这些类注册为bean。

@Configuration:指出该类是 Bean 配置的信息源,相当于XML中的,一般加在主类上。

@Bean:相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。

@EnableAutoConfiguration:让 Spring Boot 根据应用所声明的依赖来对 Spring 框架进行自动配置,一般加在主类上。

@AutoWired:byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。

当加上(required=false)时,就算找不到bean也不报错。

@Qualifier:当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。与@Autowired配合使用

@Resource(name=“name”,type=“type”):没有括号内内容的话,默认byName。与@Autowired干类似的事。

@RequestMapping:RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

@RequestParam:用在方法的参数前面。

@RequestParam String a =request.getParameter(“a”)。

@PathVariable:路径变量。参数与大括号里的名字一样要相同。

@ConfigurationProperties Spring Boot将尝试校验外部的配置,默认使用JSR-303(如果在classpath路径中)。你可以轻松的为你的@ConfigurationProperties类添加JSR-303 javax.validation约束注解:

@ControllerAdvice:包含@Component。可以被扫描到。

统一处理异常。

@ExceptionHandler(Exception.class):用在方法上面表示遇到这个异常就执行以下方法。


@EnableAutoConfiguration 就是 为SpringBoot实现自动配置的核心注解。它的意思就是开启自动配置功能。也就是说我们之前需要配置的东西,现在都不需要配置了 而在 @EnableAutoConfiguration 的内部又有两个非常重要的注解,分别为 @AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class) 。

进入 @AutoConfigurationPackag 的内部,我们发现这个该注解是由一个 @Import注解来完成的

@AutoConfigurationPackag 的作用就是将SpringBoot 主配置类所在的包 及其 下面的所有子包里面的所有组件 扫描到 Spring 容器中

而import,AutoConfigurationImportSelector中的方法getCandidateConfigurations,得到待配置的class的类名集合,这个集合就是所有需要进行自动配置的类,而是是否配置的关键在于META-INF/spring.factories文件中是否存在该配置信息

SpringBoot 在启动的时候就从类路径下的 META-INF/spring.factories 中获取 EnableAutoConfiguration指定的值,并将这些值加载到自动配置类导入到容器中,自动配置类 就生效,帮助我们进行自动配置功能


十一、进程、线程、协程关系?主要说了切换的时候消耗、同步的还是异步的……


十二、cookie和session的区别?


十三、实现一个cache?刚开始想用LinkedHashMap ,就自己写了个双向链表+HashMap实现的 ;


// 一、节点类
class Node {
    public int key, val;
    public Node next, prev; //下一个和前面一个
    public Node (int key, int val) {
        this.key = key;
        this.val = val;
    }
}
// 二、双端链表
class DoubleLinked {
    private Node head, tail;//头结点和尾结点
    private int size;//链表的元素数目
    public DoubleLinked() {
        head = new Node(0, 0);
        tail = new Node(0, 0);
        head.next = tail;
        tail.prev = head;
        size = 0;
    }
    // 在头部插入node结点
    public void addFirst(Node x) {
        x.prev = head;
        x.next = head.next;
        head.next.prev = x;
        head.next = x;
        size++;
    }
    // 移除指定结点
    public void remove(Node x) {
        x.prev.next = x.next;
        x.next.prev = x.prev;
        size--;
    }
    // 删除链表的第一个结点,并返回该结点
    public Node removeLast() {
        if(head.next == tail) return null;//返回空
        Node last = tail.prev;
        remove(last);//删除尾结点;
        return last;
    }
    public int size() {
        return size;
    }
}
// 三、构造hashMap映射
class LRUCache {
    HashMap<Integer, Node> map;
    DoubleLinked linked;
    int cap; //容量
    public LRUCache(int capacity) {
        map = new  HashMap<>();
        linked = new DoubleLinked();
        this.cap = capacity;
    }
    public int get(int key) {
        if(!map.containsKey(key)) return -1;
        int val = map.get(key).val;
        put(key, val);//放入头结点
        return val;
    }
    public void put(int key, int value) {
        Node x = new Node(key, value);
        if(map.containsKey(key)) {
            linked.remove(map.get(key));//移除结点
            linked.addFirst(x);
            map.put(key, x);
        }else {
            if(cap == cache.size()) {
                Node last = linked.removeLast();
                map.remove(last.key);
            }
            linked.addFirst(x);
            map.put(key, x);
        }
    }
}


1、说一下hash函数的好处


HASH函数必须具备两个基本特征:单向性 和 碰撞约束

单向性是指其的操作方向的不可逆性,在HASH函数中是指 只能从输入推导出输出,而不能从输出计算出输入;

碰撞约束是指 不能找到一个输入使其输出结果等于一个已知的输出结果 或者 不能同时找到两个不同的输入使其输出结果完全一致


2、StringBuilder的append方法和“+”有什么区别,底层实现


1.在Java中,String可以直接使用+和+=实现两个字符串的链接。这是Java编译器提供的支持。但其实在编译器的背后呢,Java编译器还是会生成StringBuilder,+和+=还是会转化成append。

2.使用+链接和使用StringBuilder效果是一样的。但是在循环的情况下,可能使用StringBuilder效率会更高。


3.因为在循环的情况下,String的每一次+操作都会生成一个StringBuilder对象

3、http的常见状态码以及含义


4、tcp和udp的区别


UDP报文没有可靠性保证、顺序保证和流量控制。超时重传、拥塞控制 字段等,可靠性较差。但是正因为UDP协议的控制选项较少,在数据传输过程中延迟小、数据传输效率高,适合对可靠性要求不高的应用程序,或者可以保障可靠性的应用程序,如短消息、广播信息等。


5、udp不面向连接数据怎么传输的呢


1.UDP服务器同样以报文形式做出响应,如果服务器未收到 此请求,客户端不会进行重发,因此报文的传输是不可靠的


2.UDP只会把想发的数据报文一股脑的丢给对方,并不在意数据有无安全完整到达。

3.在UDP方式下,客户端并不与服务器建立连接,它只负责调用发送函数向服务器发出数据报。类似地,服务器也不从客户端接收连接,只负责调用接收函数,等待来自某客户端的数据到达。


6、基于tcp和udp的应用层有哪些(比如http)

7、讲一下cookie和session的区别和作用


1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。

3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。

4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

5、可以考虑将登陆信息等重要信息存放为session,其他信息如果需要保留,可以放在cookie中。

目录
相关文章
|
2月前
|
人工智能 Java 开发者
阿里出手!Java 开发者狂喜!开源 AI Agent 框架 JManus 来了,初次见面就心动~
JManus是阿里开源的Java版OpenManus,基于Spring AI Alibaba框架,助力Java开发者便捷应用AI技术。支持多Agent框架、网页配置、MCP协议及PLAN-ACT模式,可集成多模型,适配阿里云百炼平台与本地ollama。提供Docker与源码部署方式,具备无限上下文处理能力,适用于复杂AI场景。当前仍在完善模型配置等功能,欢迎参与开源共建。
1419 58
阿里出手!Java 开发者狂喜!开源 AI Agent 框架 JManus 来了,初次见面就心动~
|
7月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
486 70
|
5月前
|
SQL Java 数据库连接
阿里腾讯互联网公司校招 Java 面试题总结及答案解析
本文总结了阿里巴巴和腾讯等互联网大厂的Java校招面试题及答案,涵盖Java基础、多线程、集合框架、数据库、Spring与MyBatis框架等内容。从数据类型、面向对象特性到异常处理,从线程安全到SQL优化,再到IOC原理与MyBatis结果封装,全面梳理常见考点。通过详细解析,帮助求职者系统掌握Java核心知识,为校招做好充分准备。资源链接:[点击下载](https://pan.quark.cn/s/14fcf913bae6)。
186 2
|
8月前
|
Java API Docker
在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境
以上内容是一个简单的实现在Java后端中通过DockerClient操作Docker生成python环境并执行代码,最后销毁的案例全过程,也是实现一个简单的在线编程后端API的完整流程,你可以在此基础上添加额外的辅助功能,比如上传文件、编辑文件、查阅文件、自定义安装等功能。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境
|
9月前
|
监控 前端开发 Java
构建高效Java后端与前端交互的定时任务调度系统
通过以上步骤,我们构建了一个高效的Java后端与前端交互的定时任务调度系统。该系统使用Spring Boot作为后端框架,Quartz作为任务调度器,并通过前端界面实现用户交互。此系统可以应用于各种需要定时任务调度的业务场景,如数据同步、报告生成和系统监控等。
370 9
|
8月前
|
JavaScript Java Docker
干货含源码!如何用Java后端操作Docker(命令行篇)
只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
4月前
|
人工智能 Java API
后端开发必看:零代码实现存量服务改造成MCP服务
本文介绍如何通过 **Nacos** 和 **Higress** 实现存量 Spring Boot 服务的零代码改造,使其支持 MCP 协议,供 AI Agent 调用。全程无需修改业务代码,仅通过配置完成服务注册、协议转换与工具映射,显著降低改造成本,提升服务的可集成性与智能化能力。
1335 1
|
4月前
|
前端开发 Java 数据库连接
后端开发中的错误处理实践:原则与实战
在后端开发中,错误处理是保障系统稳定性的关键。本文介绍了错误分类、响应设计、统一处理机制及日志追踪等实践方法,帮助开发者提升系统的可维护性与排障效率,做到防患于未然。

热门文章

最新文章

下一篇
oss云网关配置