前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >美团研发岗的薪酬一览表。。

美团研发岗的薪酬一览表。。

作者头像
沉默王二
发布2024-05-14 18:54:47
990
发布2024-05-14 18:54:47
举报
文章被收录于专栏:沉默王二沉默王二

请说说 String、StringBuilder、StringBuffer的区别,为什么这么设计?

StringStringBuilderStringBuffer在 Java 中都是用于处理字符串的,它们之间的区别是,String 是不可变的,平常开发用得最多,当遇到大量字符串连接时,就用 StringBuilder,它不会生成很多新的对象,StringBuffer 和 StringBuilder 类似,但每个方法上都加了 synchronized 关键字,所以是线程安全的。

为什么这么设计?
  • String:适用于字符串内容不会改变的场景,比如说作为 HashMap 的 key。
  • StringBuilder:适用于单线程环境下需要频繁修改字符串内容的场景,比如在循环中拼接或修改字符串,是 String 的完美替代品。
  • StringBuffer:现在已经不怎么用了,因为一般不会在多线程场景下去频繁的修改字符串内容。

String不可变吗?为什么不可变?有什么好处?怎么保证不可变。

String 是不可变的,这意味着一旦一个 String 对象被创建,其存储的文本内容就不能被改变。这是因为:

①、不可变性使得 String 对象在使用中更加安全。因为字符串经常用作参数传递给其他 Java 方法,例如网络连接、打开文件等。

如果 String 是可变的,这些方法调用的参数值就可能在不知不觉中被改变,从而导致网络连接被篡改、文件被莫名其妙地修改等问题。

②、不可变的对象因为状态不会改变,所以更容易进行缓存和重用。字符串常量池的出现正是基于这个原因。

当代码中出现相同的字符串字面量时,JVM 会确保所有的引用都指向常量池中的同一个对象,从而节约内存。

③、因为 String 的内容不会改变,所以它的哈希值也就固定不变。这使得 String 对象特别适合作为 HashMap 或 HashSet 等集合的键,因为计算哈希值只需要进行一次,提高了哈希表操作的效率。

如何保证 String 不可变?

第一,String 类内部使用一个私有的字符数组来存储字符串数据。这个字符数组在创建字符串时被初始化,之后不允许被改变。

代码语言:javascript
复制
private final char value[];

第二,String 类没有提供任何可以修改其内容的公共方法,像 concat 这些看似修改字符串的操作,实际上都是返回一个新创建的字符串对象,而原始字符串对象保持不变。

代码语言:javascript
复制
public String concat(String str) {
    if (str.isEmpty()) {
        return this;
    }
    int len = value.length;
    int otherLen = str.length();
    char buf[] = Arrays.copyOf(value, len + otherLen);
    str.getChars(buf, len);
    return new String(buf, true);
}

第三,String 类本身被声明为 final,这意味着它不能被继承。这防止了子类可能通过添加修改方法来改变字符串内容的可能性。

代码语言:javascript
复制
public final class String

抽象类能写构造方法吗(能)接口能吗(不能)为什么二者有这样的区别

抽象类可以定义构造方法吗?

可以,抽象类可以有构造方法。

代码语言:javascript
复制
abstract class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public abstract void makeSound();
}

public class Dog extends Animal {
    private int age;

    public Dog(String name, int age) {
        super(name);  // 调用抽象类的构造函数
        this.age = age;
    }

    @Override
    public void makeSound() {
        System.out.println(name + " says: Bark");
    }
}
接口可以定义构造方法吗?

不能,接口主要用于定义一组方法规范,没有具体的实现细节。

二哥的 Java 进阶之路:接口不能定义构造方法

为什么有这样的区别?

抽象类更多地是用来为多个相关的类提供一个共同的基础框架,包括状态的初始化,而接口则是定义一套行为标准,让不同的类可以实现同一接口,提供行为的多样化实现。

为什么重写equals,建议必须重写hashCode方法

维护 equals()hashCode()之间的一致性是至关重要的,因为基于哈希的集合类(如 HashSet、HashMap、Hashtable 等)依赖于这一点来正确存储和检索对象。

具体地说,这些集合通过对象的哈希码将其存储在不同的“桶”中(底层数据结构是数组,哈希码用来确定下标),当查找对象时,它们使用哈希码确定在哪个桶中搜索,然后通过 equals()方法在桶中找到正确的对象。

如果重写了 equals()方法而没有重写 hashCode()方法,那么被认为相等的对象可能会有不同的哈希码,从而导致无法在集合中正确处理这些对象。

HashMap的内部结构,1.7和1.8的区别,有什么改进

JDK 8 中 HashMap 的数据结构是数组+链表+红黑树

三分恶面渣逆袭:JDK 8 HashMap 数据结构示意图

相比较 JDK 7,JDK 8 的 HashMap 主要做了四点优化:

①、底层数据结构由数组 + 链表改成了数组 + 链表或红黑树的结构。

原因:如果多个键映射到了同一个哈希值,链表会变得很长,在最坏的情况下,当所有的键都映射到同一个桶中时,性能会退化到 O(n),而红黑树的时间复杂度是 O(logn)。

②、链表的插入方式由头插法改为了尾插法。

原因:头插法虽然简单快捷,但扩容后容易改变原来链表的顺序。

③、扩容的时机由插入时判断改为插入后判断。

原因:可以避免在每次插入时都进行不必要的扩容检查,因为有可能插入后仍然不需要扩容。

④、优化了哈希算法。

JDK 7 进行了多次移位和异或操作来计算元素的哈希值。

二哥的 Java 进阶之路:JDK 7 的 hash 方法

JDK 8 优化了这个算法,只进行了一次异或操作,但仍然能有效地减少冲突。

二哥的 Java 进阶之路:JDK 8 的 hash 方法

并且能够保证扩容后,元素的新位置要么是原位置,要么是原位置加上旧容量大小。

三分恶面渣逆袭:扩容后的元素位置

进程间的通信方式,代码使用匿名管道使两个进程通信

进程间通信(IPC,Inter-Process Communication)的方式有管道、信号、消息队列、共享内存、信号量和套接字。

编程十万问:进程间通信

简单说说管道:

管道可以理解成不同进程之间的传话筒,一方发声,一方接收,声音的介质可以是空气或者电缆。

进程间的管道就是内核中的一串缓存,从管道的一端写入数据,另一端读取。数据只能单向流动,遵循先进先出(FIFO)的原则。

编程十万问:管道

匿名管道:允许具有亲缘关系的进程(如父子进程)进行通信。

三分恶面渣逆袭:“奉先我儿”

使用 C 语言在 Unix/Linux 环境下通过匿名管道实现两个进程(通常是父子进程)之间通信的示例:

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main() {
    int pipefd[2];
    pid_t cpid;
    char buf;

    // 创建管道
    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    // 创建子进程
    cpid = fork();
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (cpid == 0) {    /* 子进程 */
        close(pipefd[1]);  // 关闭写端

        // 从管道读取数据
        while (read(pipefd[0], &buf, 1) > 0)
            write(STDOUT_FILENO, &buf, 1);

        write(STDOUT_FILENO, "\n", 1);
        close(pipefd[0]);
        exit(EXIT_SUCCESS);
    } else {            /* 父进程 */
        close(pipefd[0]);  // 关闭读端

        // 向管道写入数据
        write(pipefd[1], "Hello, Child!", 13);
        close(pipefd[1]);  // 关闭写端,触发EOF
        wait(NULL);        // 等待子进程退出
        exit(EXIT_SUCCESS);
    }
}

MySQL ACID 哪些机制来保证

MySQL 通过事务、undo log、redo log 来确保 ACID。

二哥的 Java 进阶之路:ACID 的保证机制

如何保证原子性?

MySQL 通过 undo log 来确保原子性(Atomicity)。

当事务开始时,MySQL 会在undo log中记录事务开始前的旧值。如果事务执行失败,MySQL 会使用undo log中的旧值来回滚事务开始前的状态;如果事务执行成功,MySQL 会在某个时间节点将undo log删除。

如何保证一致性?

如果其他三个特性都得到了保证,那么一致性(Consistency)就自然而然得到保证了。

如何保证隔离性?

MySQL 定义了多种隔离级别,通过 MVCC 来确保每个事务都有专属自己的数据版本,从而实现隔离性(Isolation)。

在 MVCC 中,每行记录都有一个版本号,当事务尝试读取记录时,会根据事务的隔离级别和记录的版本号来决定是否可以读取。

如何保证持久性?

redo log 是一种物理日志,当执行写操作时,MySQL 会先将更改记录到 redo log 中。当 redo log 填满时,MySQL 再将这些更改写入数据文件中。

如果 MySQL 在写入数据文件时发生崩溃,可以通过 redo log 来恢复数据文件,从而确保持久性(Durability)。

redo log、 bin log

binlog,即二进制日志,对所有存储引擎都可用,是 MySQL 服务器级别的日志,用于数据的复制、恢复和备份。而 redo log 主要用于保证事务的持久性,是 InnoDB 存储引擎特有的日志类型。

binlog 记录的是逻辑 SQL 语句,而 redo log 记录的是物理数据页的修改操作,不是具体的 SQL 语句。

redo log 是固定大小的,通常配置为一组文件,使用环形方式写入,旧的日志会在空间需要时被覆盖。binlog 是追加写入的,新的事件总是被添加到当前日志文件的末尾,当文件达到一定大小后,会创建新的 binlog 文件继续记录。

BeanFactory和ApplicationContext

可以这么比喻,BeanFactory 是 Spring 的“心脏”,而 ApplicantContext 是 Spring 的完整“身躯”。

  • BeanFactory 主要负责配置、创建和管理 bean,为 Spring 提供了基本的依赖注入(DI)支持。
  • ApplicationContext 是 BeanFactory 的子接口,在 BeanFactory 的基础上添加了企业级的功能支持。

三分恶面渣逆袭:BeanFactory和ApplicantContext

详细说说 BeanFactory

BeanFactory 位于整个 Spring IoC 容器的顶端,ApplicationContext 算是 BeanFactory 的子接口。

三分恶面渣逆袭:Spring5 BeanFactory继承体系

它最主要的方法就是 getBean(),这个方法负责从容器中返回特定名称或者类型的 Bean 实例。

来看一个 XMLBeanFactory(已过时) 获取 bean 的例子:

代码语言:javascript
复制
class HelloWorldApp{
   public static void main(String[] args) {
      BeanFactory factory = new XmlBeanFactory (new ClassPathResource("beans.xml"));
      HelloWorld obj = (HelloWorld) factory.getBean("itwanger");
      obj.getMessage();
   }
}
请详细说说 ApplicationContext

ApplicationContext 继承了 HierachicalBeanFactory 和 ListableBeanFactory 接口,算是 BeanFactory 的自动挡版本,是 Spring 应用的默认方式。

三分恶面渣逆袭:Spring5 ApplicationContext部分体系类图

ApplicationContext 会在启动时预先创建和配置所有的单例 bean,并支持如 JDBC、ORM 框架的集成,内置面向切面编程(AOP)的支持,可以配置声明式事务管理等。

这是 ApplicationContext 的使用例子:

代码语言:javascript
复制
class MainApp {
    public static void main(String[] args) {
        // 使用 AppConfig 配置类初始化 ApplicationContext
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 从 ApplicationContext 获取 messageService 的 bean
        MessageService service = context.getBean(MessageService.class);

        // 使用 bean
        service.printMessage();
    }
}

通过 AnnotationConfigApplicationContext 类,我们可以使用 Java 配置类来初始化 ApplicationContext,这样就可以使用 Java 代码来配置 Spring 容器。

代码语言:javascript
复制
@Configuration
@ComponentScan(basePackages = "com.github.paicoding.forum.test.javabetter.spring1") // 替换为你的包名
public class AppConfig {
}

bean加工有哪些方法?

Spring 提供了 4 种不同的方式来实例化 Bean,以满足不同场景下的需求。

说说构造方法的方式

在类上使用@Component(或@Service、@Repository 等特定于场景的注解)标注类,然后通过构造方法注入依赖。

代码语言:javascript
复制
@Component
public class ExampleBean {
    private DependencyBean dependency;

    @Autowired
    public ExampleBean(DependencyBean dependency) {
        this.dependency = dependency;
    }
}
说说静态工厂的方式

在这种方式中,Bean 是由一个静态方法创建的,而不是直接通过构造方法。

代码语言:javascript
复制
public class ClientService {
    private static ClientService clientService = new ClientService();

    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}
说说实例工厂方法实例化的方式

与静态工厂方法相比,实例工厂方法依赖于某个类的实例来创建 Bean。这通常用在需要通过工厂对象的非静态方法来创建 Bean 的场景。

代码语言:javascript
复制
public class ServiceLocator {
    public ClientService createClientServiceInstance() {
        return new ClientService();
    }
}
说说 FactoryBean 接口实例化方式

FactoryBean 是一个特殊的 Bean 类型,可以在 Spring 容器中返回其他对象的实例。通过实现 FactoryBean 接口,可以自定义实例化逻辑,这对于构建复杂的初始化逻辑非常有用。

代码语言:javascript
复制
public class ToolFactoryBean implements FactoryBean<Tool> {
    private int factoryId;
    private int toolId;

    @Override
    public Tool getObject() throws Exception {
        return new Tool(toolId);
    }

    @Override
    public Class<?> getObjectType() {
        return Tool.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    // setter and getter methods for factoryId and toolId
}

设计模式,策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列的算法,将每个算法封装起来,使得它们可以相互替换。这种模式通常用于实现不同的业务规则或策略,其中每种策略封装了特定的行为或算法。

特别适合用于优化程序中的复杂条件分支语句(if-else),将不同的分支逻辑封装到不同的策略类中,然后通过上下文类来选择不同的策略。

在策略模式中,有三个角色:上下文(Context)、策略接口(Strategy Interface)和具体策略(Concrete Strategy)。

  • 策略接口:定义所有支持的算法的公共接口。策略模式的核心。
  • 具体策略:实现策略接口的类,提供具体的算法实现。
  • 上下文:使用策略的类。通常包含一个引用指向策略接口,可以在运行时改变其具体策略。

技术派教程

比如说在技术派中,用户可以自由切换 AI 服务,服务端可以通过 if/esle 进行判断,但如果后续需要增加新的 AI 服务,就需要修改代码,这样不够灵活。

因此,我们使用了策略模式,将不同的 AI 服务封装成不同的策略类,通过工厂模式创建不同的 AI 服务实例,从而实现 AI 服务的动态切换。

代码语言:javascript
复制
@Service
public class PaiAiDemoServiceImpl extends AbsChatService {

    @Override
    public AISourceEnum source() {
        return AISourceEnum.PAI_AI;
    }
}

@Slf4j
@Service
public class ChatGptAiServiceImpl extends AbsChatService {
    @Override
    public AISourceEnum source() {
        return AISourceEnum.CHAT_GPT_3_5;
    }
}

@Slf4j
@Service
public class XunFeiAiServiceImpl extends AbsChatService {
    @Override
    public AISourceEnum source() {
        return AISourceEnum.XUN_FEI_AI;
    }
}

为什么三次握手,有什么缺点,洪泛攻击,半连接服务拒绝,让你重新设计,怎么设计

使用三次握手可以建立一个可靠的连接。这一过程的目的是确保双方都知道对方已准备好进行通信,并同步双方的序列号,从而保持数据包的顺序和完整性。

  • 第一次握手:客户端发送 SYN 包(连接请求)给服务器,如果这个包延迟了,客户端不会一直等待,它可能会重试并发送一个新的连接请求。
  • 第二次握手:服务器收到 SYN 包后,发送一个 SYN-ACK 包(确认接收到连接请求)回客户端。
  • 第三次握手:客户端收到 SYN-ACK 包后,再发送一个 ACK 包给服务器,确认收到了服务器的响应。
什么是泛洪攻击?

泛洪攻击(SYN Flood Attack)是一种常见的 DoS(拒绝服务)攻击,攻击者会发送大量的伪造的 TCP 连接请求,导致服务器资源耗尽,无法处理正常的连接请求。

如果让你重新设计,怎么设计?

如果重新设计 TCP 的连接建立过程,可以考虑引入 SYN cookies,这种技术通过在 SYN-ACK 响应中编码连接信息,从而在不占用大量资源的情况下验证客户端。

参考链接

  • 三分恶的面渣逆袭:https://javabetter.cn/sidebar/sanfene/nixi.html
  • 二哥的 Java 进阶之路:https://javabetter.cn
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 沉默王二 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 请说说 String、StringBuilder、StringBuffer的区别,为什么这么设计?
    • 为什么这么设计?
    • String不可变吗?为什么不可变?有什么好处?怎么保证不可变。
      • 如何保证 String 不可变?
      • 抽象类能写构造方法吗(能)接口能吗(不能)为什么二者有这样的区别
        • 抽象类可以定义构造方法吗?
          • 接口可以定义构造方法吗?
            • 为什么有这样的区别?
            • 为什么重写equals,建议必须重写hashCode方法
            • HashMap的内部结构,1.7和1.8的区别,有什么改进
            • 进程间的通信方式,代码使用匿名管道使两个进程通信
              • 简单说说管道:
              • MySQL ACID 哪些机制来保证
                • 如何保证原子性?
                  • 如何保证一致性?
                    • 如何保证隔离性?
                      • 如何保证持久性?
                      • redo log、 bin log
                      • BeanFactory和ApplicationContext
                        • 详细说说 BeanFactory
                          • 请详细说说 ApplicationContext
                          • bean加工有哪些方法?
                            • 说说构造方法的方式
                              • 说说静态工厂的方式
                                • 说说实例工厂方法实例化的方式
                                  • 说说 FactoryBean 接口实例化方式
                                  • 设计模式,策略模式
                                  • 为什么三次握手,有什么缺点,洪泛攻击,半连接服务拒绝,让你重新设计,怎么设计
                                    • 什么是泛洪攻击?
                                      • 如果让你重新设计,怎么设计?
                                      • 参考链接
                                      相关产品与服务
                                      云数据库 MySQL
                                      腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
                                      领券
                                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档