腾讯云智一面---后台开发(凉经)

发布于:2025-07-18 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、java的特性是什么?

二、什么是封装、继承、多态(面向对象的角度,最好用项目中 / 生活中的例子),贯穿始终,看你能不能讲清楚)?再讲一下什么是抽象?(我举了汽车的例子)

三、面向对象 和 面向过程有什么区别?

四、for循环有多少中写法?(常见的写法,不包含迭代器)

五、for循环分成好几部分,讲它的每一部分和每一部分的作用。

六、数组,有五个元素【1,2,3,4,5】,如何通过for循环打印1  3  5 用for循环怎么写?

while循环打印 数组【1,2,3,4,5】怎么写?

七、多线程了解吗?举一个多线程的例子?什么情况下用多线程做事情?用多线程要注意什么问题?

八、比如说有两个线程、一个文件,一个线程要更新文件中的内容,另一个线程要读取里面的内容,这个时候代码的执行的循序是什么?,包括加锁、写文件、解锁这些顺序是什么?

九、线程之间,两个线程在并行做事情,那两个线程有时候也要进行一些通信,如何进行通信呢? 

十、private、protect、public 三种修饰符有什么区别?

十一、栈和队列有什么区别?举个生活中的例子?(讲清楚,讲完全) 

十二、二叉树什么是平衡二叉树?堆排序中堆是一种什么二叉树?

十三、你知道的排序算法有多少种?(说至少五种),选一种讲清楚?

十四、MySQL中有一张表,

A:学号 姓名 
B:学号 性别

①连表查出来学号,姓名,性别 ?

②如果要清空一个表的数据用什么命令?

③更新某个学生的姓名,怎么更新?

④删一个表命令

十五、redis是一个什么样的数据库?和别的数据库比它的显著特征?

十六、redis一般作为缓存系统,因为读写速度比较快,那它的缺陷是什么?

十七、计算机网络分层结构,七层,从下往上是什么?

十八、linux关于目录和文件的基本命令?(说十个)

查文件中的某个词用什么命令?

只想查看前十行?

后十行?

十九、①查看一个机器的 ip 地址,想连接一个机器,登录一个机器用什么命令?

②看当前到别的服务器网络通不通用什么命令?

③看他的端口号能不能连接上这个用什么命令?

二十、域名解析具体是做什么事情?可以举个例子吗?

二十一、前后端协作开发,后端写的接口文档内容有什么?

二十二、讲一下项目经历?每段控制在3分钟,两个都介绍一下

(项目背景、为什么做这个项目、有什么功能、项目的架构、是怎么设计这个项目的、这个项目过程中间,分模块设计,设计过程中有没有遇到什么问题,开发完了后是要测试,你具体怎么测的、有没有分场景、写用例,最后这个项目的背景亮点在哪,用到的核心技术)

二十三、事务的话和普通的SQL有什么区别?

二十四、k8s 和 docker容器听说过吗? docker部署几步就能把mysql数据库部署起来? 

二十五、docker中容器运行期间,你有没有看过容器的日志?

二十六、docker的网络你知道吗,网桥的名字叫什么,docker0你知道吗

有没有听过容器的主机网络模式?

二十七、有没有了解过什么是云,腾讯云嘛,云包含几层?有没有听过 IaaS、PaaS、SaaS?

CI/CD?

一、java的特性是什么?

封装、继承、多态

二、具体什么是封装、继承、多态(面向对象的角度,最好用项目中 / 生活中的例子),贯穿始终,看你能不能讲清楚)?再讲一下什么是抽象?(我举了汽车的例子)

那我拿汽车举一个例子,

封装:的话就是将发动机、变速箱这些隐藏在内部,对外只保留接口,比如说油门呀,刹车呀。

作用:这么做不仅能保护发动机和变速箱,还能方便用户操作,不需要理解发动机原理就能踩油门加速。

继承是什么呢?比如汽车这个大类,那么新能源电动汽车、燃油车这些就可以继承汽车这个大类,比如说继承它的轮子、外壳、方向盘。比如说电动车还可以实现自己的电池、充电这些特性。

作用:实现了共性的抽取和复用

多态呢?不同对象调用同一个方法,表现出不同的行为。汽车实例化的奔驰、宝马、特斯拉,按喇叭时都发出 “哔~” 声,但具体音色可能不同。接口 / 抽象类:定义统一方法(如honk())。动态绑定:运行时根据实际对象类型决定调用哪个实现。可扩展性:新增车型(如BMW)时,无需修改honkAll()方法。

  • 通过父类引用指向子类对象实现多态。

抽象:就是提取共性,用抽象类或接口定义规范。比如这里汽车就可以定义为一个抽象类。它可以规定 “必须有方向盘、四个轮子。但不规定具体用什么材料做的。

关键点

 
  • 抽象类 / 接口:不能实例化,仅作为规范(如Car不能直接new Car())。
  • 强制实现:抽象方法(如start())必须由子类实现。
  • 分离设计与实现:上层代码只依赖抽象(如Car接口),不依赖具体类。

官方定义
接口是一种抽象类型,它只定义方法签名(名称、参数、返回值),但不包含方法体。实现接口的类必须实现接口中所有的方法。

 

接口呢?如电动车中的充电接口、只要符合这个接口标准,任何充电桩都能充电。

 
 

关键作用

汽车场景中的作用

 
  1. 标准化:所有电动车和充电桩必须遵循同一接口标准(如国标),否则无法兼容。
  2. 解耦:车厂不用关心充电桩的实现,充电桩厂商也不用关心车的品牌。
  3. 扩展性:未来可以新增接口标准(如无线充电接口),而不影响现有系统。

Java中接口的作用:

  1. 标准化:规定类必须实现的方法,确保不同类遵循统一标准。
  2. 实现多继承:一个类可同时实现多个接口,弥补单继承的局限。
  3. 解耦代码:通过面向接口编程,降低模块间依赖,提高扩展性。
  4. 支持回调:接口可作为参数传递,实现代码的灵活调用(如事件监听)。

 

那我举一个汽车的例子

1. 封装(Encapsulation)

定义:将数据(属性)和操作数据的方法(行为)捆绑在一起,并隐藏内部实现细节,仅对外提供必要接口。
类比:汽车的发动机舱。

通俗解释

封装的话就是比如把发动机、变速箱藏在引擎盖下面,只留给用户油门、刹车等来控制速度。

关键作用

 
  • 保护数据:用户不能直接修改发动机转速,只能通过油门间接控制。
  • 简化操作:用户不需要懂发动机原理,踩油门就能加速。

代码示例

public class Car {
    private int speed; // 隐藏速度,不让外部直接修改
    
    // 提供公共方法控制速度
    public void pressAccelerator(int force) {
        if (force > 0) {
            speed += force;
            if (speed > 200) speed = 200; // 限速保护
        }
    }
    
    public int getSpeed() {
        return speed;
    }
}

2. 继承(Inheritance)

定义:子类(派生类)继承父类(基类)的属性和方法,实现代码复用和扩展。
类比:汽车的分类体系。

  • 父类Car(有speedstart()等通用属性和方法)。
  • 子类ElectricCarGasCar继承Car,并扩展各自特性。

通俗解释
所有汽车(子类)都继承 “汽车” 这个大类的基本特性(轮子、方向盘),但可以添加自己的特色(如给汽车的充电功能)。

// 父类:普通汽车
public class Car {
    public void drive() {
        System.out.println("开车前进");
    }
}

// 子类:电动车
public class ElectricCar extends Car {
    public void charge() { // 新增充电功能
        System.out.println("新增充电功能");
    }
    
    @Override
    public void drive() { // 修改父类行为
        System.out.println("电动引擎安静地启动");
    }
}

3. 多态(Polymorphism)

定义:同一方法调用,根据对象类型不同表现出不同行为。
类比:汽车的喇叭。

 
  • 无论是什么品牌的汽车(奔驰、宝马、特斯拉),按喇叭时都发出 “哔~” 声,但具体音色可能不同。

关键点

 
  • 接口 / 抽象类:定义统一方法(如honk())。
  • 动态绑定:运行时根据实际对象类型决定调用哪个实现。
  • 可扩展性:新增车型(如BMW)时,无需修改honkAll()方法。

代码示例

// 接口:定义汽车的行为
public interface Car {
    void honk(); // 按喇叭方法
}

// 奔驰车
public class Benz implements Car {
    @Override
    public void honk() {
        System.out.println("优雅的:哔~");
    }
}

// 特斯拉
public class Tesla implements Car {
    @Override
    public void honk() {
        System.out.println("电子合成音:哔~");
    }
}

// 测试多态
public class Main {
    public static void main(String[] args) {
        Car benz = new Benz();
        Car tesla = new Tesla();
        
        honkAll(benz, tesla); // 统一调用按喇叭方法
    }
    
    public static void honkAll(Car... cars) {
        for (Car car : cars) {
            car.honk(); // 不同汽车的喇叭声自动区分
        }
    }
}

4. 抽象(Abstraction)

定义:忽略细节,提取共性,用抽象类定义规范。
类比:汽车设计蓝图。

通俗解释
汽车设计图只规定 “必须有四个轮子”“必须能启动”,但不规定具体用什么材料(钢轮还是铝轮)。

关键点

 
  • 抽象类 / 接口:不能实例化,仅作为规范(如Car不能直接new Car())。
  • 强制实现:抽象方法(如start())必须由子类实现。
  • 分离设计与实现:上层代码只依赖抽象(如Car接口),不依赖具体类。
// 抽象类:汽车设计蓝图
public abstract class Car {
    public abstract void start(); // 抽象方法:必须启动
    
    public void stop() { // 具体方法:默认实现
        System.out.println("踩刹车停车");
    }
}

// 燃油车实现
public class GasCar extends Car {
    @Override
    public void start() {
        System.out.println("点火启动燃油发动机");
    }
}

// 电动车实现
public class ElectricCar extends Car {
    @Override
    public void start() {
        System.out.println("按下启动按钮,电动引擎就绪");
    }
}

代码示例

// 抽象类:汽车设计蓝图
public abstract class Car {
    public abstract void start(); // 抽象方法:必须启动
    
    public void stop() { // 具体方法:默认实现
        System.out.println("踩刹车停车");
    }
}

// 燃油车实现
public class GasCar extends Car {
    @Override
    public void start() {
        System.out.println("点火启动燃油发动机");
    }
}

// 电动车实现
public class ElectricCar extends Car {
    @Override
    public void start() {
        System.out.println("按下启动按钮,电动引擎就绪");
    }
}

5. 接口(Interface)

官方定义
接口是一种抽象类型,它只定义方法签名(名称、参数、返回值),但不包含方法体。实现接口的类必须实现接口中所有的方法。

 

汽车类比

 
  • 充电接口(如国标、特斯拉超充)就是一种物理接口,它规定:
    1. 必须有几个针脚(如直流正极、负极、通信线)。
    2. 每个针脚的功能和电压标准。
    3. 插头的形状和尺寸。
  • 不规定
    • 充电桩内部如何转换电能(用 IGBT 模块还是 SiC 模块)。
    • 具体用什么品牌的充电桩。

通俗解释
汽车的 “充电接口”(如 Type-C、国标快充口),只要符合这个接口标准,任何充电桩都能充电。

 

关键作用

汽车场景中的作用

 
  1. 标准化:所有电动车和充电桩必须遵循同一接口标准(如国标),否则无法兼容。
  2. 解耦:车厂不用关心充电桩的实现,充电桩厂商也不用关心车的品牌。
  3. 扩展性:未来可以新增接口标准(如无线充电接口),而不影响现有系统。

3. 代码示例:汽车充电接口

// 充电接口(规定必须有充电方法)
public interface Chargeable {
    void charge();
}

// 特斯拉实现了充电接口
public class Tesla implements Chargeable {
    public void charge() {
        System.out.println("插入Type-C充电桩,开始充电");
    }
}

// 比亚迪也实现了充电接口
public class BYD implements Chargeable {
    public void charge() {
        System.out.println("插入国标充电桩,开始充电");
    }
}

总结对比 

三、面向对象 和 面向过程有什么区别?

首先,

面向对象:是一种以对象为中心的编程风格,把类或对象作为基本单元来组织代码。

并且运用提来出来的:封装、继承、多态来作为代码设计指导。

面向过程:是一种以过程或函数为中心的编码风格。以过程作为基本单元来组织代码。

主要区别如下:

1.思维方式:

面向对象:通过定义对象的属性和行为来解决问题,关注对象之间的关系和交互

面向过程:通过函数或过程一步一步实现业务逻辑,关注执行的步骤和顺序。

2.数据与方法的关系:

面向对象:数据和方法封装在对象内部,数据操作由对象方法进行管理。

面向过程:数据和方法是分离的,函数对数据进行操作。

3.可扩展性和复用性:

面向对象:通过继承、接口、多态等机制支持代码的高复用性和扩展性

面向过程:复用性较低,扩展需要修改已有代码,影响整体稳定性。

4.适用场景

面向对象:适合处理复杂的系统和模块化设计,便于维护和发展

面向过程:适用于一些简单的,顺序性强的小型程序,开发效率较高。

四、for循环有多少种写法?(常见的写法,不包含迭代器)

①普通for循环

这是最基本的for循环形式,通过设置循环变量、循环条件以及更新循环变量的方式来控制循环的执行。

// 示例:计算1到10的整数之和
public class ForLoopExample {
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 1; i <= 10; i++) {
            sum += i;
        }
        System.out.println("1到10的整数之和为:" + sum);
    }
}

②增强for循环(for-each循环) 

增强for循环主要用于遍历数组或实现了Iterable接口的集合(如ListSet等),它的语法更加简洁,不需要显式地操作索引。

// 示例:遍历数组并打印每个元素
public class ForEachLoopExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
        for (int num : numbers) {
            System.out.print(num + " ");
        }
    }
}

下面这段代码展示了使用增强for循环遍历List集合的过程。

import java.util.ArrayList;
import java.util.List;

public class ForEachListExample {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        fruits.add("apple");
        fruits.add("banana");
        fruits.add("cherry");

        for (String fruit : fruits) {
            System.out.println(fruit);
        }
    }
}

③ 标签for循环 

标签for循环用于在嵌套循环中,更灵活地控制循环的执行和跳出。可以给外层循环添加一个标签,然后使用breakcontinue语句配合标签,实现直接跳出外层循环或者直接进入外层循环的下一次迭代。

// 示例:使用标签for循环跳出嵌套循环
public class LabeledForLoopExample {
    public static void main(String[] args) {
        outerLoop:
        for (int i = 1; i <= 3; i++) {
            for (int j = 1; j <= 3; j++) {
                if (i == 2 && j == 2) {
                    break outerLoop; // 直接跳出外层循环
                }
                System.out.println("i = " + i + ", j = " + j);
            }
        }
    }
}

在上述代码中:

 
  • outerLoop::这是给外层for循环定义的标签。
  • break outerLoop;:当满足条件i == 2 && j == 2时,直接跳出被标记为outerLoop的外层循环,而不是仅仅跳出内层循环。

在这个例子中,当满足条件i == 2 && j == 2时,continue outerLoop;语句会让程序跳过内层循环剩余的代码,直接进入外层循环的下一次迭代 。

五、for循环分成好几部分,讲它的每一部分和每一部分的作用。

for (初始化表达式; 循环条件; 迭代表达式) {
    循环体
}

六、数组,有五个元素【1,2,3,4,5】,如何通过for循环打印1  3  5 用for循环怎么写?

int[] arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.length; i += 2) {  // i每次递增2
    System.out.println(arr[i]);
}

while循环打印 数组【1,2,3,4,5】怎么写?

先定义一个变量 比如说 i = 0 

然后 i < 数组 长度

打印数字

i++

进入下一次循环。

int[] arr = {1, 2, 3, 4, 5};
int i = 0;  // 初始化索引

while (i < arr.length) {
    System.out.println(arr[i]);  // 打印当前元素
    i++;  // 索引递增
}

七、多线程了解吗?举一个多线程的例子?什么情况下用多线程做事情?用多线程要注意什么问题?

①、多线程的例子(用做饭类比)

假设你要做一顿饭,需要完成三件事:

  1. 煮米饭(需要 10 分钟)
  2. 炒青菜(需要 5 分钟)
  3. 煎鸡蛋(需要 3 分钟)

单线程方式

  • 先煮米饭 → 等米饭熟了再炒青菜 → 再煎鸡蛋,总耗时10+5+3=18分钟

多线程方式

  • 启动一个线程煮米饭(同时进行),主线程先炒青菜,再煎鸡蛋。
  • 总耗时≈10 分钟(米饭煮好的时间,其他事情在等待米饭时完成)。

②、什么情况下用多线程?

简单说:需要 “同时做多个事情” 来提高效率,或避免程序 “卡住”
常见场景:

  1. 耗时操作不阻塞主线程
    • 如 App 加载时,后台线程下载图片,主线程继续响应用户点击(否则界面会卡顿)。
  2. 充分利用 CPU 资源
    • 处理大量数据(如统计多个文件的字数),用多线程同时处理不同文件,比单线程逐个处理快。
  3. 并发任务处理
    • 服务器同时接收多个用户的请求(如微信同时接收多条消息),每个请求用一个线程处理。

③、用多线程要注意的问题

  1. 线程安全问题

    • 多个线程同时操作共享数据(如多个线程同时修改一个变量),可能导致结果错误。
    • 例:两个线程同时给账户存钱,原本存 2 次 100 元,结果可能只存了 100 元。
    • 解决:用synchronized锁或Lock保证同一时间只有一个线程操作共享资源。
  2. 死锁

    • 两个线程互相等待对方释放资源,导致都卡住。
    • 例:线程 A 持有锁 1,等待锁 2;线程 B 持有锁 2,等待锁 1,永远僵持。
    • 解决:按固定顺序获取锁,避免循环等待。
  3. 线程过多消耗资源

    • 每个线程占用内存,线程太多会导致内存不足或 CPU 切换频繁(“上下文切换” 耗时)。
    • 解决:用线程池(如ThreadPoolExecutor)控制线程数量。
  4. 线程通信复杂

    • 线程间需要协作(如 “生产者 - 消费者” 模型),若处理不好会导致逻辑混乱。
    • 解决:用wait()/notify()BlockingQueue等工具协调线程。

八、比如说有两个线程、一个文件,一个线程要更新文件中的内容,另一个线程要读取里面的内容,这个时候代码的执行的循序是什么?,包括加锁、写文件、解锁这些顺序是什么?

加解锁的一个线程:

只读锁(共享的):多个读线程可以同时读取文件。

若一个读的线程先获取到资源,那么就要加一个只读锁、

在只读锁没有被释放的时候,别的线程就不能去更新(写)这个文件。

如果A线程把这个文件读完了,这个时候就可以把锁给释放掉。 

就是说这个文件我不用了,

写锁(排他的):同一时间只允许一个线程写入,且写入时不允许其他线程读取。

解锁之后更新的线程可以获取一把写锁

他告诉别人我正在写,你们看着读,写完之后他把锁一解。

九、线程之间,两个线程在并行做事情,那两个线程有时候也要进行一些通信,如何进行通信呢? 

线程间通信是多线程编程核心概念,在多线程编程中,有时候需要让线程之间进行数据交换,协作工作。以下是几种线程间的通信方式: 

1.共享内存:线程之间可以通过访问同一块共享内存区域来实现数据交换

2.消息队列:一个线程向消息队列中放入一条消息,另一个线程从消息队列中取出消息

3.管道(Pipe):管道是一种半双工的通信方式,一个进程可以向管道中写入数据,另一个线程可以从管道中取出数据

4.信号(signal):信号是一种异步通信方式,进程收到信号后,会根据信号的类型做出相应的判断

5.互斥锁(Mutex):用于同步访问共享资源,防止多个线程同时访问共享资源,产生冲突

6.条件变量:用于线程之间的协调和通信,一个线程可以通过条件变量等待某个条件的出现,另一个线程可以通过条件变量通知正在等待的线程。

7.RPC调用:远程过程调用(RPC)是一种跨网络进行远程调用,可以实现在不用的线程或机器之间进行信息交换。

还可以延伸到你在编程时怎么让多线程共享变量,避免冲突。 

十、private、protect、public 三种修饰符有什么区别?

十一、栈和队列有什么区别?举个生活中的例子?(讲清楚,讲完全) 

  • :后进先出(LIFO),
  • 队列:先进先出(FIFO),类似排队,先到的人先服务。

栈,可以类似一摞书,最后放上去的最先被拿走。

队列,类似过安检,先到的人先出。

十二、二叉树:

什么是平衡二叉树?堆排序中堆是一种什么二叉树?

平衡二叉树:

每个节点的左右子树高度差不超过 1,且左右子树均为平衡二叉树。

这种结构能保证树的高度始终保持在 O(log n),从而实现高效的插入、删除和查询操作(时间复杂度均为 O(log n))。

关键特点:

  • 任何节点的左右子树高度差(平衡因子)只能是 -1、0、1
  • 插入或删除节点后,可能需要通过 旋转操作(左旋、右旋、左右旋、右左旋)来重新平衡树。

示例:

    3           // 平衡因子:左子树高度2 - 右子树高度1 = 1(平衡)
   / \
  1   4
   \
    2           // 所有子树的平衡因子均为0或±1

堆排序中的堆(Heap)

定义:
堆是一种 完全二叉树,分为两种类型:

 
  1. 大根堆(Max-Heap):每个节点的值都 大于或等于 其子节点的值,根节点为最大值。
  2. 小根堆(Min-Heap):每个节点的值都 小于或等于 其子节点的值,根节点为最小值。
 

关键特点:

 
  • 完全二叉树:除最后一层外,每一层都被完全填充,且最后一层的节点都靠左排列。
  • 高效的堆操作
    • 插入元素:O (log n)
    • 删除堆顶元素:O (log n)
    • 构建堆:O (n)

示例(大根堆):

       9         // 根节点值最大
      / \
     7   8       // 每个节点的值 ≥ 子节点的值
    / \ / \
   5  6 3  4

示例(小根堆):

       1         // 根节点最小
      / \
     3   2       // 父节点1 ≤ 子节点3、2;父节点3 ≤ 子节点5、4
    / \ /
   5  4 6       // 所有父节点均满足 ≤ 子节点

堆排序原理:
利用堆的特性,每次取出堆顶元素(最大值 / 最小值),并调整剩余元素为新堆,重复此过程得到有序序列。

对比总结:

十三、你知道的排序算法有多少种?(说至少五种),选一种讲清楚?

常见的排序算法包括:冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序、希尔排序、基数排序等。

1.冒泡排序(稳定)(交换)

(Bubble Sort)核心思想:重复遍历数组,比较相邻元素,若顺序错误则交换,每一轮将最大(或最小)元素 “冒泡” 到末尾,直到整个数组有序。

步骤(以升序为例)
  1. 初始状态:整个数组视为未排序。
  2. 第 1 轮:从第 1 个元素开始,比较相邻元素,若前一个 > 后一个,则交换。遍历结束后,最大元素 “冒泡” 到末尾。
  3. 第 2 轮:重复上述过程,但只遍历到倒数第 2 个元素(因最后一个已排好)。
  4. 重复:每轮减少遍历范围,直到未排序部分只剩 1 个元素。
​
    // 1. 冒泡排序(稳定,O(n²))
    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }

​

示例(数组:[5, 3, 8, 4, 6])

初始:[5, 3, 8, 4, 6]  
第1轮:比较5-3→交换,3-8不变,8-4→交换,8-6→交换 → [3, 5, 4, 6, 8](最大元素8移至末尾)  
第2轮:比较3-5不变,5-4→交换,5-6不变 → [3, 4, 5, 6, 8](次大元素6移至倒数第2位)  
第3轮:比较3-4不变,4-5不变 → [3, 4, 5, 6, 8]  
第4轮:比较3-4不变 → [3, 4, 5, 6, 8](已完成)  
最终排序:[3, 4, 5, 6, 8]  
关键特点
  • 时间复杂度
    • 最坏 / 平均:O (n²)(需 n (n-1)/2 次比较)。
    • 最好:O (n)(数组已有序时,通过设置 “交换标志” 提前终止)。
  • 空间复杂度:O (1),原地排序。
  • 稳定性稳定(相等元素不交换,相对顺序不变)。

2.快速排序(不稳定)(交换)

核心思想

先选取一个基准元素,再用分区:将所有比这个基准元素小的数放在基准元素左边,大的放在右边。再这样递归对左边右边两部分分别排序,最终使整个数组有序。

步骤(以升序为例)
  1. 选择基准值:从数组中任选一个元素作为 “基准”(通常选第一个、最后一个或中间元素)。
  2. 分区操作:将数组中小于基准值的元素放在基准左侧,大于基准值的元素放在右侧(等于基准值的可放左或右)。
  3. 递归排序:对基准左侧和右侧的子数组重复上述步骤,直到子数组长度为 1(天然有序)。

// 4. 快速排序(不稳定,O(n log n))
    public static void quickSort(int[] arr) {
        quickSort(arr, 0, arr.length - 1);
    }

    private static void quickSort(int[] arr, int low, int high) {
        if (low < high) {
            int pivotIndex = partition(arr, low, high);
            quickSort(arr, low, pivotIndex - 1);
            quickSort(arr, pivotIndex + 1, high);
        }
    }

    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int i = low - 1;
        for (int j = low; j < high; j++) {
            if (arr[j] < pivot) {
                i++;
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        int temp = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp;
        return i + 1;
    }

示例(数组:[3, 6, 8, 10, 1, 2, 1])

1.选基准值为 3,分区后
左侧为 [1, 2, 1](均 < 3),
右侧为 [6, 8, 10](均 > 3),
数组变为 [1, 2, 1, 3, 6, 8, 10]。

递归处理左侧 [1, 2, 1]:
选基准 1,
分区后左侧为空,右侧为 [2, 1](均 >= 1),
再对 [2, 1] 分区(基准 2,左侧为 [1],右侧为空),
最终左侧子数组排序为 [1, 1, 2]。

递归处理右侧 [6, 8, 10]:
选基准 6,分区后右侧为 [8, 10],
继续递归排序,
最终右侧为 [6, 8, 10]。

合并结果:[1, 1, 2, 3, 6, 8, 10]。

关键特点
  • 时间复杂度:平均 O (n log n),最坏 O (n²)(如有序数组选两端为基准),可通过随机选基准优化。
  • 空间复杂度:O (log n)(递归栈开销),不稳定排序(相等元素可能交换位置)。
  • 优势:实际应用中效率高,是工业界常用的排序算法(如 C++ 的std::sort部分场景采用)。

3.选择排序(不稳定)(选择)

核心思想

1.从未排序序列中找到最小(或最大)元素

2.将其放到已排序序列的末尾

重复上述过程,直到所有元素排序完毕

步骤(以升序为例)
  1. 初始状态:整个数组视为未排序。
  2. 第 1 轮:从未排序部分(全体元素)中找到最小值,与第 1 个元素交换。
  3. 第 2 轮:从未排序部分(第 2 个元素至末尾)中找到最小值,与第 2 个元素交换。
  4. 重复:以此类推,直到未排序部分只剩最后一个元素(天然有序)。
// 2. 选择排序(不稳定,O(n²))
    public static void selectionSort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < n; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }
            int temp = arr[minIndex];
            arr[minIndex] = arr[i];
            arr[i] = temp;
        }
    }

 示例(数组:[64, 25, 12, 22, 11])

初始:[64, 25, 12, 22, 11]  
第1轮:最小值11,与64交换 → [11, 25, 12, 22, 64]  
第2轮:最小值12,与25交换 → [11, 12, 25, 22, 64]  
第3轮:最小值22,与25交换 → [11, 12, 22, 25, 64]  
第4轮:最小值25,无需交换 → [11, 12, 22, 25, 64]  
最终排序:[11, 12, 22, 25, 64]  
关键特点
  • 时间复杂度:始终为 O(n²),无论数组初始状态如何。
  • 空间复杂度:O (1),原地排序(仅需常数级额外空间)。
  • 稳定性不稳定(例如 [5, 8, 5, 2] 中两个 5 的相对顺序可能改变)。
  • 优势:简单直观,适合小规模数据或内存受限场景。

4、堆排序(不稳定)

(Heap Sort)核心思想:利用 “堆” 这种数据结构(完全二叉树)的特性,先将数组构建为大根堆(或小根堆,此处以大根堆为例),再通过反复提取堆顶元素(最大值)并调整堆结构,最终得到有序数组。

步骤(以升序为例)
  1. 构建大根堆
    • 从最后一个非叶子节点开始(索引为 n//2 - 1),对每个节点执行 “下沉” 操作(若节点值小于子节点,与较大子节点交换,直到满足大根堆性质:父节点≥子节点);
    • 最终数组转化为大根堆,堆顶为最大值。
  2. 排序阶段
    • 将堆顶元素(最大值)与数组末尾元素交换,此时末尾元素为有序部分;
    • 缩小堆的范围(忽略已排序的末尾元素),对新堆顶执行 “下沉” 操作,重建大根堆;
    • 重复上述步骤,直到堆的范围缩小至 1,数组完全有序。
// 6. 堆排序(不稳定,O(n log n))
    public static void heapSort(int[] arr) {
        int n = arr.length;

        // 构建大顶堆
        for (int i = n / 2 - 1; i >= 0; i--) {
            heapify(arr, n, i);
        }

        // 一个个交换元素
        for (int i = n - 1; i > 0; i--) {
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;

            // 重新调整堆结构
            heapify(arr, i, 0);
        }
    }

    private static void heapify(int[] arr, int n, int i) {
        int largest = i;
        int left = 2 * i + 1;
        int right = 2 * i + 2;

        if (left < n && arr[left] > arr[largest]) {
            largest = left;
        }

        if (right < n && arr[right] > arr[largest]) {
            largest = right;
        }

        if (largest != i) {
            int swap = arr[i];
            arr[i] = arr[largest];
            arr[largest] = swap;

            heapify(arr, n, largest);
        }
    }

示例(数组:[4, 10, 3, 5, 1])

(索引对应关系:根节点为索引 0,左子 = 2i+1,右子 = 2i+2)
构建大根堆:
        4          (根节点,索引0)
       / \
     10   3        (左子索引1,右子索引2)
    / \
   5   1           (左子索引3,右子索引4)
从非叶子节点 10 开始(无需调整),
再处理根节点 4:
根节点 4 的左子 10(>4)、右子 3(<4),与较大的左子 10 交换:
        10         (交换后,根节点变为10)
       / \
     4   3         (左子变为4,右子仍为3)
    / \
   5   1
原左子 4(现在索引 1)的左子 5(>4)、右子 1(<4),与左子 5 交换:
        10         (最终大根堆)
       / \
     5   3         (左子变为5,满足5≥4和1)
    / \
   4   1
(大根堆构建完成)。
排序阶段:
堆顶 10 与末尾 1 交换→[1,5,3,4,10](10 有序);对根节点 1 下沉→[5,4,3,1,10];
堆顶 5 与倒数第 2 位 1 交换→[1,4,3,5,10](5 有序);
对根节点 1 下沉→[4,1,3,1,10]→[4,1,3,1,10] 
调整为 [4,1,3,1,10](实际步骤略);
重复后最终有序:[1,3,4,5,10]。

关键特点
  • 时间复杂度
    • 构建堆的时间为 O (n),排序阶段每次调整堆为 O (log n),共 n-1 次→总时间复杂度 O(n log n)(最坏、平均、最好情况均为此值)。
  • 空间复杂度:O (1),原地排序(仅需常数级额外空间)。
  • 稳定性不稳定(交换堆顶与末尾元素时,可能改变相等元素的相对顺序)。
适用场景
  • 对时间复杂度要求高且空间有限的场景(如嵌入式系统、内存紧张的环境);
  • 无需稳定性的排序需求(如单纯对数值排序);
  • top-k 问题(如从海量数据中找前 k 个最大值,堆排序可高效实现)。

 5.插入排序(稳定)(插入)

核心思想:将数组划分为已排序部分和待排序部分,每次选取待排序中的一个数有序的插入到已排序的列表中。

步骤(以升序为例)
  1. 初始状态:第一个元素视为 “已排序” 部分,其余为 “未排序” 部分。
  2. 第 1 次插入:取未排序部分的第一个元素(第 2 个元素),与已排序部分的元素从后往前比较,插入到正确位置。
  3. 重复插入:依次处理未排序部分的每个元素,每次将当前元素插入到已排序部分的对应位置(确保已排序部分始终有序)。
  4. 结束条件:未排序部分为空,整个数组有序。
// 3. 插入排序(稳定,O(n²))
    public static void insertionSort(int[] arr) {
        int n = arr.length;
        for (int i = 1; i < n; i++) {
            int key = arr[i];
            int j = i - 1;
            while (j >= 0 && arr[j] > key) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = key;
        }
    }
初始:已排序[5],未排序[2, 4, 6, 1, 3]  
第1次插入(元素2):  
比较2与5(2<5),插入到5左侧 → 已排序[2, 5],未排序[4, 6, 1, 3]  

第2次插入(元素4):  
比较4与5(4<5)→ 继续比较4与2(4>2),插入到2和5之间 → 已排序[2, 4, 5],未排序[6, 1, 3]  

第3次插入(元素6):  
比较6与5(6>5),直接插入到5右侧 → 已排序[2, 4, 5, 6],未排序[1, 3]  

第4次插入(元素1):  
比较1与6(1<6)→ 1<5 → 1<4 → 1<2,插入到2左侧 → 已排序[1, 2, 4, 5, 6],未排序[3]  

第5次插入(元素3):  
比较3与6 → 3<5 → 3<4 → 3>2,插入到2和4之间 → 已排序[1, 2, 3, 4, 5, 6]  

最终排序:[1, 2, 3, 4, 5, 6]  
关键特点
  • 时间复杂度
    • 最坏情况(逆序数组):O (n²)(需比较和移动大量元素)。
    • 最好情况(已排序数组):O (n)(只需遍历一次,无需移动元素)。
    • 平均情况:O(n²)。
  • 空间复杂度:O (1),原地排序(仅需常数级额外空间)。
  • 稳定性稳定(相等元素插入时会放在原有相等元素的右侧,保持相对顺序)。

 6、希尔排序(不稳定)(插入)

(Shell Sort)核心思想:作为插入排序的改进版,通过 “分组” 的方式减少元素移动的距离。先将数组按一定 “步长” 分为多个子数组,对每个子数组用插入排序;逐步缩小步长(最终步长为 1),最后一次按步长 1 执行插入排序,此时数组已接近有序,效率大幅提升。

步骤(以升序为例)
  1. 选择步长序列:常用步长如 n/2, n/4, ..., 1(折半步长),或更优的 Knuth 序列 (3^k - 1)/2(如 1, 4, 13, 40...)。
  2. 按步长分组排序
    • 对每个步长 gap,将数组分为 gap 个子数组(索引 0 与 gap、2gap… 为一组,1 与 1+gap、1+2gap… 为另一组等);
    • 对每个子数组执行插入排序(子数组内元素已部分有序)。
  3. 缩小步长重复:直到步长为 1,此时数组接近有序,最后一次插入排序效率极高,完成最终排序。
// 7. 希尔排序(不稳定,O(n^1.3))
    public static void shellSort(int[] arr) {
        int n = arr.length;

        // 初始步长为数组长度的一半,逐步减半
        for (int gap = n / 2; gap > 0; gap /= 2) {
            // 对每个步长进行插入排序
            for (int i = gap; i < n; i++) {
                int temp = arr[i];
                int j;
                for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
                    arr[j] = arr[j - gap];
                }
                arr[j] = temp;
            }
        }
    }

示例(数组:[12, 34, 54, 2, 3],步长序列 5→2→1)

步长 5:数组长度 5,步长 5 时每组只有 1 个元素,无需排序。
步长 2:
分组:[12,54,3] 和 [34,2];
对第一组插入排序→[3,54,12](原数组对应位置更新为 [3,34,54,2,12]);
对第二组插入排序→[2,34](原数组更新为 [3,2,54,34,12])。
步长 1:对整个数组执行插入排序→[2,3,12,34,54]。
关键特点
  • 时间复杂度
    • 依赖步长序列,平均情况约为 O (n^1.3)(折半步长),最优步长下可接近 O (n log²n);
    • 最坏情况:O (n²)(如折半步长处理某些特殊数组)。
  • 空间复杂度:O (1),原地排序。
  • 稳定性不稳定(分组排序时,相等元素可能被分到不同组,导致相对顺序改变)。
优势与适用场景
  • 比插入排序高效:通过大步长减少元素移动次数,尤其对中等规模数据(n=1000~10000)性能较好。
  • 实现简单:在插入排序基础上增加步长逻辑即可,适合对性能有一定要求但无需极致优化的场景。
  • 不适合对稳定性有要求的场景,也不如归并 / 堆排序适合大规模数据。

7.归并排序(稳定)(归并)

核心思想:基于 “分治” 策略,将数组不断 “拆分” 为两个等长(或近似等长)的子数组,直到子数组长度为 1(天然有序),再通过 “合并” 两个有序子数组的方式,逐步向上整合为一个完整的有序数组。

步骤(以升序为例)
  1. 拆分阶段
    • 将原数组从中间分为左、右两个子数组;
    • 递归拆分左、右子数组,直到每个子数组只有 1 个元素(终止条件)。
  2. 合并阶段
    • 准备一个临时数组,用于存放合并后的结果;
    • 比较两个有序子数组的元素,按从小到大的顺序依次放入临时数组;
    • 将临时数组的元素复制回原数组的对应位置,完成合并;
    • 递归合并上层子数组,最终得到完整的有序数组。
// 5. 归并排序(稳定,O(n log n))
    public static void mergeSort(int[] arr) {
        if (arr.length > 1) {
            int mid = arr.length / 2;
            int[] left = Arrays.copyOfRange(arr, 0, mid);
            int[] right = Arrays.copyOfRange(arr, mid, arr.length);

            mergeSort(left);
            mergeSort(right);
            merge(arr, left, right);
        }
    }

    private static void merge(int[] arr, int[] left, int[] right) {
        int i = 0, j = 0, k = 0;
        while (i < left.length && j < right.length) {
            if (left[i] <= right[j]) {
                arr[k] = left[i];
                i++;
            } else {
                arr[k] = right[j];
                j++;
            }
            k++;
        }
        while (i < left.length) {
            arr[k] = left[i];
            i++;
            k++;
        }
        while (j < right.length) {
            arr[k] = right[j];
            j++;
            k++;
        }
    }

示例(数组:[8, 4, 5, 7, 1, 3, 6, 2])

拆分:[8,4,5,7] 和 [1,3,6,2] → 
继续拆分至 [8]、[4]、[5]、[7]、[1]、[3]、[6]、[2];
合并:
[8] 与 [4] 合并为 [4,8];
[5] 与 [7] 合并为 [5,7];
[4,8] 与 [5,7] 合并为 [4,5,7,8];
同理,右侧子数组合并为 [1,2,3,6];
最终合并 [4,5,7,8] 与 [1,2,3,6] → [1,2,3,4,5,6,7,8]。
关键特点
  • 时间复杂度
    • 无论最好、最坏还是平均情况,均为 O(n log n)(拆分次数为 log n 层,每层合并总耗时 O (n))。
  • 空间复杂度O(n)(需额外临时数组存储合并结果,递归调用栈深度为 log n,可忽略)。
  • 稳定性稳定(合并时若两元素相等,会优先放入左侧子数组的元素,保持相对顺序)。
适用场景
  • 数据量较大且对时间复杂度要求高的场景(如 n>1000);
  • 对稳定性有要求的场景(如排序对象需保持相等元素的原始顺序);
  • 外部排序(数据无法全部加载到内存时,归并排序的 “拆分 - 合并” 特性更易实现)。

      八、基数排序(分配式排序)

      (Radix Sort)核心思想:非比较型排序算法,通过 “按位排序” 实现:从元素的最低位(或最高位)开始,按每位的数值(0-9,或字符 ASCII 码等)将元素分配到对应 “桶” 中,再按桶的顺序收集元素;重复此过程直到所有位处理完毕,数组有序。

      步骤(以整数升序、按低位到高位为例)
      1. 确定最大位数:找到数组中最大元素的位数(如 [123, 45, 6] 的最大位数为 3)。
      2. 按位排序
        • 从最低位(个位)开始,对每个元素按当前位的数值(不足位数补 0)分配到 0-9 号桶中;
        • 按桶的顺序(0→9)收集元素,形成新的数组;
        • 依次处理十位、百位…… 直到最高位。
      3. 结束条件:所有位处理完毕,数组有序。
      // 8. 基数排序(稳定,O(d(n+r)))
          public static void radixSort(int[] arr) {
              if (arr.length == 0) return;
      
              // 找到最大值确定位数
              int max = arr[0];
              for (int num : arr) {
                  if (num > max) {
                      max = num;
                  }
              }
      
              // 对每一位进行排序
              for (int exp = 1; max / exp > 0; exp *= 10) {
                  countingSort(arr, exp);
              }
          }
      
          private static void countingSort(int[] arr, int exp) {
              int n = arr.length;
              int[] output = new int[n];
              int[] count = new int[10];
      
              // 统计每一位出现的次数
              for (int i = 0; i < n; i++) {
                  count[(arr[i] / exp) % 10]++;
              }
      
              // 计算累积次数
              for (int i = 1; i < 10; i++) {
                  count[i] += count[i - 1];
              }
      
              // 构建输出数组(从后向前保证稳定性)
              for (int i = n - 1; i >= 0; i--) {
                  output[count[(arr[i] / exp) % 10] - 1] = arr[i];
                  count[(arr[i] / exp) % 10]--;
              }
      
              // 复制回原数组
              System.arraycopy(output, 0, arr, 0, n);
          }

      示例(数组:[170, 45, 75, 90, 802, 24, 2, 66])
      • 最大位数:3(802 为 3 位)。
      • 按个位排序
        • 个位数值:0(170、90)、2(802、2)、4(24)、5(45、75)、6(66)。
        • 收集后:[170, 90, 802, 2, 24, 45, 75, 66]。
      • 按十位排序
        • 十位数值:0(802、2)、2(24)、4(45)、6(66)、7(170、75)、9(90)。
        • 收集后:[802, 2, 24, 45, 66, 170, 75, 90]。
      • 按百位排序
        • 百位数值:0(2、24、45、66、75、90)、1(170)、8(802)。
        • 收集后:[2, 24, 45, 66, 75, 90, 170, 802](排序完成)。
      关键特点
      • 时间复杂度
        • 设元素最大位数为 d,基数为 r(如 10 个桶),则时间复杂度为 *O(d(n + r))**(d 轮分配 + 收集,每轮 O (n + r))。
        • 当 d 为常数(如整数最多 32 位)、r≤n 时,可视为 O (n)。
      • 空间复杂度:O (n + r)(需 r 个桶存储元素,总容量为 n)。
      • 稳定性稳定(分配到桶时按元素原始顺序放入,收集时保持相对顺序)。
      适用场景
      • 整数或可按位拆分的类型(如字符串、日期等);
      • 数据范围大但位数有限的场景(如手机号、身份证号排序);
      • 不适合浮点数(位数不固定)或无法按位比较的类型。

      总结

      • 简单排序(冒泡、选择、插入):实现简单,适合小规模数据,其中插入排序在基本有序时最优。
      • 高级排序(快速、归并、堆、希尔):适合大规模数据,快速排序平均效率最高,归并排序稳定但需额外空间,堆排序空间复杂度低。
      • 基数排序:非比较类排序,适合特定场景(如整数),时间效率高但适用范围有限。

      十四、MySQL中有一张表,

      A:学号 姓名 
      B:学号 性别

      ①连表查出来学号,姓名,性别 ?

      select A.学号, A.姓名, B.性别 
      from A ,B where A.学号 = B.学号;

      ②如果要清空一个表的数据用什么命令?

      TRUNCATE TABLE 表名;

      truncate(截断,删节) table 表名;

      需要完全清空表且不需要回滚 → 使用truncate table

      /

      DELETE FROM 表名;

      清空表数据

      需要条件删除或需要事务回滚 → 使用 DELETE FROM

      需要清空表但保留自增ID → 使用 DELETE FROM

      表有外键约束时 → 通常需要使用 DELETE FROM,或先禁用外键约束

      ③更新某个学生的姓名,怎么更新?

      update A 
      set 姓名 = '新姓名' 
      where 学号 = '学生学号';

      ④删一个表命令

      DROP TABLE 表名;

      drop table 表名;

      十五、redis是一个什么样的数据库?和别的数据库比它的显著特征?

      十六、redis一般作为缓存系统,因为读写速度比较快,那它的缺陷是什么?

      十七、计算机网络分层结构,七层,从下往上是什么?

      十八、linux关于目录和文件的基本命令?(说十个)

      linux常用命令 

      文件与目录操作

      1. ls : 列出目录内容(包括文件和子目录)

      2. ls -l :(详细信息)

      3. ls -a :包括隐藏文件

      4. cd -目录名 :进入目录

      5. cd .. 返回上一级目录

      6. pwd - 显示当前工作目录

      7. mkdir 目录名:创建目录

      8. rmdir 目录名:删除空目录

      9. rm 文件名:直接删除单个文件

      10. rm -r 目录名: 递归删除目录及其内容

      11. rm -f 目录名:强制删除单个文件

      12. rm -i 文件名:交互式删除(删除前询问确认)

      13. rm -rf /  :这将尝试删除系统所有文件(通常会被系统阻止)

      14. rm -rf *  :删除当前目录下所有文件

      15. touch 文件名: 创建空文件或更新文件时间戳

      16. cp 文件名 目录:复制文件到目录

      17. cp -r : 递归复制目录

      18. mv 旧文件名 新文件名 :重命名文件

      19. mv file.txt 目录地址 :移动文件

      文件内容查看

      1. cat 文件名 :查看文件内容

      2. more/less:分页查看文件内容,less支持向上翻页,适合查看大文件

      3. tail:查看文件末尾若干行

      4. tail -f 用于实时监控内容变化,如日志文件

      5. head:查看文件开头几行

      6. vi/vim  文件名:编辑文件内容

      7. 在 Linux 中查看文件内容时,有几种快速查找特定单词/文本的方法:
        1. 使用 less 查看器(推荐) less 文件名

        查找操作
        按 / 进入搜索模式,输入要查找的单词,回车
        按 n 跳转到下一个匹配项
        按 N 跳转到上一个匹配项
        按 q 退出 less

      系统管理:

      1. ps:显示当前运行的进程列表
      2. ps aux:查看所有用户进程
      3. top/htop:实时查看系统重的进程状态和资源使用情况
      4. kill/killall:终止进程,kill后跟进程id,killall后面跟进程名称
      5. df:显示文件系统的磁盘使用情况
      6. du:统计目录或文件占用磁盘空间
      7. du -h :以人类可读的格式显示

      网络配置与调试:

      1. ping:测试与目标主机的连通性
      2. ifconfig/ip :查看和配置网络接口信息,ifconfig已经逐渐被 ip 命令替代
      3. netstat/ss:查看网络连接和端口使用情况,ss是netstat的替代品,提供更详细查询
      4. curl/wget:发送http请求或下载文件,curl更适合进行API调试,wget则用于下载文件

      文件权限和用户管理:

      1. chmod:修改文件或文件目录,常用模式如 chmod 755
      2. chown:更改文件或目录所有者
      3. useradd/userdel:添加或删除用户
      4. passwd:修改用户密码

      用cat查一个小文件,想搜索某个词用什么命令?

      cat 文件名

      grep "搜索词"

      只想查看前十行?

      head -n 10 filename

      后十行?

      tail -n 10 filename

      动态查看日志新增内容:tail -f logfile

      十九、

      ①查看一个机器的 ip 地址,想连接一个机器,登录一个机器用什么命令?

      查看本机 IP 地址

      ip addr show

      登录远程机器

      ssh 用户名@目标IP或域名

      # 基本用法

      示例:

      ssh root@192.168.1.100

      ssh user@example.com -p 2222 # 指定非默认端口(22)

      ②看当前到别的服务器网络通不通用什么命令?

      ping 目标IP或域名 
      示例:
      ping 8.8.8.8          # 测试Google DNS
      ping example.com      # 测试域名解析

      ③看他的端口号能不能连接上这个用什么命令?

      在 Linux 中检查目标服务器的端口是否可连接,常用以下命令:

      1.telnet(简单测试 TCP 端口)

      telnet <目标IP或域名> <端口号> 

      示例

      tenet 113.44.83.92 1208

      2.curl(测试 HTTP/应用层协议)

      curl -v http://<目标IP或域名>:<端口>  # 测试 Web 服务
      curl -v telnet://<目标IP>:<端口>      # 测试任意 TCP 端口
      示例:
      curl -v telnet://113.44.83.92:1208

      curl -v telnet://113.44.83.92:1208

      1. 命令与 URL 处理

      • curl -v http://113.44.83.92:1208 中,-v 表示输出详细日志(包括请求头、响应头、连接过程等)。
      • * Rebuilt URL to: http://113.44.83.92:1208/:curl 自动补全了 URL 末尾的 /(标准 HTTP 路径格式)。

      2. 连接建立过程

      • * Trying 113.44.83.92...:开始尝试连接目标 IP 113.44.83.92
      • * TCP_NODELAY set:启用 TCP 无延迟选项(减少网络传输延迟)。
      • * Connected to 113.44.83.92 (113.44.83.92) port 1208 (#0):成功与目标 IP 的 1208 端口建立 TCP 连接。

      3. 发送 HTTP 请求

      • > GET / HTTP/1.1:发送 HTTP GET 请求,路径为 /,协议版本为 HTTP/1.1
      • > Host: 113.44.83.92:1208:指定请求的主机和端口(用于服务器识别虚拟主机)。
      • > User-Agent: curl/7.61.1:告知服务器客户端是 curl 工具,版本为 7.61.1
      • > Accept: */*:表示客户端接受任意类型的响应数据。

      4. 服务器响应结果

      • < HTTP/1.1 401:服务器返回的 HTTP 状态码为 401,表示 “未授权”(需要身份验证才能访问该资源)。
      • 响应头信息:
        • < Vary: Origin 等:服务器告知客户端,响应内容可能因请求的来源、方法、头信息不同而变化。
        • < Content-Length: 0:响应体长度为 0(无实际内容)。
        • < Date: Wed, 16 Jul 2025 17:10:29 GMT:服务器处理请求的时间。

      5. 连接结束

      • * Connection #0 to host 113.44.83.92 left intact:请求完成后,TCP 连接保持(未立即关闭)。

      总结

      本次请求的核心结果是:客户端成功与 113.44.83.92:1208 建立连接,但服务器返回 401 未授权,说明访问该地址需要提供用户名和密码等身份验证信息,否则无法获取资源。

      二十、域名解析具体是做什么事情?可以举个例子吗?

      二十一、前后端协作开发,后端写的接口文档内容有什么?

      前后端协作中,后端接口文档通常包含:
      以我的博客登录接口为例:/user/login

      1. 接口基础信息(及描述)



      2. 请求参数说明


      Body 参数(JSON)

      {
        "username": "admin",     // 必填,4-20位字符
        "password": "a1b2c3d4",  // 必填,前端RSA加密后密文
        "captcha": "3845"        // 可选,登录失败3次后必填
      }

      字段规则:

      password 加密逻辑:前端使用固定公钥MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC... 进行RSA加密

      captcha 需调用 /captcha/generate 接口获取

      3. 响应体


      成功响应(HTTP 200)

      {
        "code": 200,
        "data": {
          "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
          "expire_in": 7200,          // token有效期(秒)
          "user_info": {
            "user_id": 10001,
            "username": "admin",
            "avatar": "https://xxx.com/avatar.jpg"
          }
        },
        "message": "登录成功"
      }


      失败响应示例



      4. 安全控制


      请求头要求:

      http

      Authorization: Bearer <token>  // 后续接口需携带
      X-Request-ID: <uuid>          // 请求唯一标识


      密码加密流程:

      javascript
       

      // 前端加密示例(Web)
      const encryptedPwd = RSA.encrypt('明文密码', '公钥');


      5. 调用示例


      cURL 请求
      bash

      curl -X POST 'https://api.example.com/user/login' \
        -H 'Content-Type: application/json' \
        -d '{
          "username": "admin",
          "password": "a1b2c3d4..."
        }'


      JavaScript Fetch
      javascript

       

      fetch('/user/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          username: 'admin',
          password: encryptedPwd
        })
      })


      6. 辅助信息


      Mock 数据(供前端开发使用)
      json
       

      {
        "code": 200,
        "data": {
          "token": "mock_token_123",
          "user_info": {
            "user_id": 999,
            "username": "mock_user"
          }
        }
      }


      版本变更记录


      7. 注意事项


      Token 刷新:

      Token 过期前调用 /user/token/refresh 续期

      错误重试:

      连续3次失败后必须要求验证码

      日志记录:

      所有登录失败会记录IP和设备信息

      为什么需要这些细节?
      避免联调问题:
      明确加密规则防止前后端加密方式不一致

      提高安全性:标注敏感字段处理方式(如密码加密)

      快速定位错误:完备的状态码说明减少沟通成本

      这样的文档能确保前后端开发者像阅读说明书一样明确交互逻辑。实际开发中推荐使用 Swagger 或 YAPI 自动生成交互式文档。

      ## 用户登录接口
      - URL: /api/v1/login
      - Method: POST
      - 请求参数:
        {
          "username": "string",
          "password": "string"
        }
      - 成功响应:
        {
          "code": 200,
          "data": {
            "token": "xxxxxx",
            "userInfo": {...}
          }
        }
      - 错误响应:
        {
          "code": 401,
          "message": "用户名或密码错误"
        }

      二十二、讲一下项目经历?每段控制在3分钟,两个都介绍一下

      (项目背景、为什么做这个项目、有什么功能、项目的架构、是怎么设计这个项目的、这个项目过程中间,分模块设计,设计过程中有没有遇到什么问题,开发完了后是要测试,你具体怎么测的、有没有分场景、写用例,最后这个项目的背景亮点在哪,用到的核心技术)

      二十三、事务的话和普通的SQL有什么区别?

      二十四、k8s 和 docker容器听说过吗? docker部署几步就能把mysql数据库部署起来? 

      二十五、docker中容器运行期间,你有没有看过容器的日志?

      二十六、docker的网络你知道吗,网桥的名字叫什么,docker0你知道吗

      有没有听过容器的主机网络模式?

      二十七、有没有了解过什么是云,腾讯云嘛,云包含几层?有没有听过 IaaS、PaaS、SaaS?CI/CD?


      网站公告

      今日签到

      点亮在社区的每一天
      去签到