【面试】某公司记录一次面试题

文章目录

框架类

1. Spring boot与 spring 架相比,好在哪里?

  • 传统Spring框架存在的弊端:
    • Spring事物管理,MVC,启用第三方库都需要XML或Java进行显示配置,配置过重
    • 写配置挤占了实际写应用的逻辑的时间
    • 项目依赖管理,要考虑用那些库,还要知道哪些版本和库不会有冲突,影响开发效率
  • SpringBoot的优势:
    • 自动配置:针对很多Spring常见的应用功能,SpringBoot能自动提供相关配置
    • 起步依赖:告诉SpringBoot需要什么功能,它就能引入需要的库
    • CLI命令行界面:通过SpringBootCLI,借此你只需写代码就能完成完整的应用程序,无须传统项目构建
    • Actuator: 提供在运行时检视应用程序内部情况的能力

2. Spring boot以及 Spring MVC 常用注解(如requestingMapping,responseBody 等)

  • @Autowired

  • @Resource

  • @RequestMapping

  • @RequestBody

  • @GetMapping

  • @PostMapping

  • @PutMapping

  • @DeleteMapping

  • @PatchMapping

  • @ControllerAdvice

  • @ResponseBody

  • @ExceptionHandler

  • @ResponseStatus

  • @PathVariable

  • @RequestParam

  • @Controller

  • @RestController

  • @ModelAttribute

  • @CrossOrigin

  • @InitBinder

  • @SpringBootApplication

  • @EnableAutoConfiguration

  • @ConditionalOnClass与@ConditionalOnMissingClass

  • @ConditionalOnBean与@ConditionalOnMissingBean

  • @ConditionalOnProperty

  • @ConditionalOnResource

  • @ConditionalOnWebApplication与@ConditionalOnNotWebApplication

  • @ConditionalExpression

  • @Conditional

3. 常用的java 设计模式,spring 中用到哪些设计模式

  • 单例模式

  • 工厂模式

  • 原型模式

  • 建造者模式

  • 装饰器模式

  • 模板方法模式

  • 观察者模式

  • 简单工厂(非23种设计模式中的一种)

  • 工厂方法

  • 单例模式

  • 适配器模式

  • 装饰器模式

  • 代理模式

  • 观察者模式

  • 策略模式

  • 模版方法模式

4. SpringIOC是什么,如何理解

IOC是inverse of control的简写,译为控制反转,是一种创建对象的思想。那什么又是控制反转呢?就是将创建对象的权力交给Spring容器,其实就是让Spring容器帮你创建对象,而你不需要在javel代码中new对象了。在没学IOC之前,在java代码中,如果你想创建类名为ClassA的对象classa,你是怎么创建对象的呢?是不是这样:ClassA classa=new ClassA();
但是,学了IOC之后,我们想要获取对象classa就不需要写以上语句来获取了,而是加载Spring容器,Spring容器就会创建classa对象,我们直接拿过来使用就可以了

5. AOP中的PointCut是什么,如何理解advisor?

PointCut是指哪些方法需要被执行"AOP"PointCut表达式可以有一下几种方式

5.1 execution:

一般用于指定方法的执行
格式:execution( 方法类型(public等,可省略) 方法的返回值类型 包路径(可省略) 方法的名称(参数) 异常类型(可省略) )

  • 方法类型包含Public,Protected等,可省略
  • 方法返回值类型,*可以包含所有的返回值类型
  • 包路径,如“com.demo…*”,表示"com.demo"包以及该包之下子包的所有类型
  • 方法名称,如“add*”,表示所有以add开头的方法,参数:(*)表示任意一个参数,(…)表示所有参数
  • 异常类型,如execution(* *(…) throws Exception)”匹配所有抛出Exception的方法。

5.2.2 within:

是用来指定类型的,指定类型中的所有方法将被拦截是用来指定类型的,指定类型中的所有方法将被拦截

  • within(com.demo.service.impl.UserServiceImpl) 匹配UserServiceImpl类对应对象的所有方法调用,并且只能是UserServiceImpl对象,不能是它的子对象
  • within(com.demo…*)匹配com.demo包及其子包下面的所有类的所有方法的外部调用。

5.2.3. this:

SpringAOP是基于代理的,this就代表代理对象,语法是this(type),当生成的代理对象可以转化为type指定的类型时表示匹配。

  • this(com.demo.service.IUserService)匹配生成的代理对象是IUserService类型的所有方法的外部调用

5.2.4. target:

SpringAOP是基于代理的,target表示被代理的目标对象,当被代理的目标对象可以转换为指定的类型时则表示匹配。

  • target(com.demo.service.IUserService) 匹配所有被代理的目标对象能够转化成IuserService类型的所有方法的外部调用。

5.2.5 args:

args用来匹配方法参数

  • args() 匹配不带参数的方法
  • args(java.lang.String) 匹配方法参数是String类型的
  • args(…) 带任意参数的方法
  • args(java.lang.String,…) 匹配第一个参数是String类型的,其他参数任意。最后一个参数是String的同理。

5.2.6 @within 和 @target

带有相应标注的所有类的任意方法,比如@Transactional

@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)

5.2.7 @annotation:

带有相应标注的任意方法,比如@Transactional

@annotation(org.springframework.transaction.annotation.Transactional)

@within和@target针对类的注解,@annotation针对方法的注解

5.2.8 @args:

参数带有相应标注的任意方法,比如@Transactional

@args(org.springframework.transaction.annotation.Transactional)

5.3.PointCut使用

5.3.1基本使用

//PointCut表达式
@Pointcut("execution(public * com.example.demo.controller.UserController.*(..))")
//PointCut签名
public void log(){
    
    
}

//下面直接调用log(),相当于直接使用上面的表达式,简化代码
@After("log()")
public void doAfter(){
    
    
	logger.info("222222222");
}

5.3.2PointCut中的运算符

PointCut中可以使用&&、||、!运算

	@Pointcut("execution(public * com.example.demo.controller.UserController.*(..))")
	public void cutController(){
    
    
	}
	
	@Pointcut("execution(public * com.example.demo.Service.UserService.*(..))")
	public void cutService(){
    
    
	}
	
   //使用  && 运算符,则cutAll()的作用等同于  cutController  和 cutService 之和
	@Pointcut("cutController() && cutService()")
	public void cutAll(){
    
    
	}

Spring Aop的代理主要分为三个步骤:获取所有的Advisor,过滤可应用到当前bean的Adivsor和使用Advisor为当前bean生成代理对象

6. SOA的概念

SOA(面向服务的架构)定义了一种可通过服务接口复用软件组件并实现其互操作的方法。 服务使用公共接口标准和架构模式,因此可以快速整合到新应用中。 这让应用开发人员无需像之前那样重新开发或复制现有功能,也不必了解如何连接现有功能或提供与现有功能的互操作性 。

7. 微服务接口调用超时处理,怎么定位微服务调用出现的问题

  • 把超时时间设置足够长。
  • 如果还超时,就要检查是哪个模块出现异常导致响应超时。
  • 如果所有模块都没有异常,则可能是某个方法响应时间过长,此时可以通过链路追踪查看每个微服务的响应时间,然后做出响应的优化。
  • 对数据库进行优化。

8. 什么是WSDL

WSDL 是基于 XML 的用于描述 Web Services 以及如何访问 Web Services 的语言。

9. 如何理解 webservice, dubbo等

Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的独立的通讯技术。是:通过SOAP在Web上提供的软件服务,使用WSDL文件进行说明,并通过UDDI进行注册。

最开始是应用于淘宝网,由阿里巴巴开源的一款优秀的高性能服务框架,由Java开发,后来贡献给了Apache组织

10. 服务器端404,301,302,400,500都是什么错误

  • 404 Not Found:请求资源不存在
  • 301 Moved Permanently
    永久性定向。该状态码表示请求的资源已被分配了新的URI,以后应使用资源现在所指的URI。
  • 302 Found
    临时性重定向。该状态码表示请求的资源已被分配了新的URI,希望用户(本次)能使用新的URI访问。和301相似,但302表示的资源不是永久移动,只是临时性的。

11. autowaire 和 resource 的区别

  • @Autowired注解是Spring提供的,而@Resource注解是J2EE本身提供的
  • @Autowird注解默认通过byType方式注入,而@Resource注解默认通过byName方式注入
  • @Autowired注解注入的对象需要在IOC容器中存在,否则需要加上属性required=false,表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错。

数据库

1. 事务的传播特性有几种,项目中使用过哪几种?

  • PROPAGATION_REQUIRED:支持当前事务,如果不存在就新建一个(默认) ;

  • PROPAGATION_REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起;

  • PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。

  • PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务;

  • PROPAGATION_MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常;

  • PROPAGATION_NEVER:以非事务方式运行,如果有事务存在,抛出异常;

  • PROPAGATION_NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于封装事务进行提交或回滚。如果封装事务不存在,行为就像PROPAGATION_REQUIRES一样。

2. mysql实现分页写,mysql的执行计划

关键字 LIMIT
explain select 投影列 FROM 表名 WHERE 条件

3. Oracle的rownum 用法,以及常用的字符串函数

rownum:伪列。顾名思义:是数据库自己创建出来的字段。
字符大小写转换函数 UPPER LOWER INITCAP
去除空白及字符串函数 TRIM,LTRIM,RTRIM
substr 、 substrb截取字符函数
length、lengthb查询字符串长度
concat、||字符串连接
instr:字符查找函数
ASCII、CHR:ascii
lpad、rpad字符填充函数
regexp_replace、translate:替换函数
extract,to_char提取日期及获取时间差

4. redis 缓存实现

redis缓存
1.缓存概述:
流程图:
代码逻辑
2.缓存方式
2.1 不设置过期时间
数据同步:
内存消耗:
2.2设置过期时间
内存资源
过期时间设置
3.名称解释
缓存穿透
缓存雪崩
缓存击穿

5. Sql加行锁的语句

SELECT * FROM cliente with(ROWLOCK) where idCliente='850'

6. 行锁,表锁的区别

  • 行锁

    • 支持的存储引擎:Innodb;

    • InnoDB行锁是通过给索引上的索引项加锁来实现的,意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

    • 适用场景:有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用

    • 特点:开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高

    • 分析:show status like ‘innodb_row_lock%’;分析系统上行锁的争夺情况如果发现锁争用比较严重,如InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比较高,还可以通过设置InnoDB Monitors来进一步观察发生锁冲突的表、数据行等,并分析锁争用的原因。

  • 表锁

    • 支持的存储引擎:Innodb、MYIsam

    • 适用场景:以查询为主,只有少量按索引条件更新数据的应用

    • 特点:开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低

    • 两种模式:

      • 表共享读锁
      • 表独占写锁
    • 对两张表显示加锁、解锁

      ​ Lock tables orders read local, order_detail read local;

      ​ Select sum(total) from orders;

      ​ Select sum(subtotal) from order_detail;

      ​ Unlock tables;

    • MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁

7. Oracle 中 varchar2 类型的好处

要存储可变长度的字符串,可以使用Oracle VARCHAR2数据类型。 VARCHAR2列可以存储1到4000字节的值。 这意味着对于单字节字符集,最多可以在VARCHAR2列中存储4000个字符。

8. 如何理解分库,分表

对于MySQL,InnoDB存储引擎的话,单表最多可以存储10亿级数据。但是的话,如果真的存储这么多,性能就会非常差。一般数据量千万级别,B+树索引高度就会到3层以上了,查询的时候会多查磁盘的次数,SQL就会变慢。

阿里巴巴的《Java开发手册》提出:

单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。

那我们是不是等到数据量到达五百万,才开始分库分表呢?

不是这样的,我们应该提前规划分库分表,如果估算3年后,你的表都不会到达这个五百万,则不需要分库分表。

MySQL服务器如果配置更好,是不是可以超过这个500万这个量级,才考虑分库分表?

虽然配置更好,可能数据量大之后,性能还是不错,但是如果持续发展的话,还是要考虑分库分表

一般什么类型业务表需要才分库分表?

通用是一些流水表、用户表等才考虑分库分表,如果是一些配置类的表,则完全不用考虑,因为不太可能到达这个量级。

分库分表15道面试题

持久化

1. mybatis实现原理

MyBatis应用程序根据XML配置文件创建SqlSessionFactory,SqlSessionFactory在根据配置,配置来源于两个地方,一处是配置文件,一处是Java代码的注解,获取一个SqlSession。SqlSession包含了执行sql所需要的所有方法,可以通过SqlSession实例直接运行映射的sql语句,完成对数据的增删改查和事务提交等,用完之后关闭SqlSession。

2. mybatis的 mapperxml中,变量替换时,$ 和 #的区别

  • #{}方式能够很大程度防止sql注入(安全),${}方式无法防止Sql注入
  • 在JDBC能使用占位符的地方,最好优先使用#{}
  • 在JDBC不支持使用占位符的地方,就只能使用${},典型情况就是 动态参数

3. mybatis的mapperxml中都会定义什么内容

在这里插入图片描述

4. mybatis 如何实现批量更新

case when
foreach成多条sql
ON DUPLICATE KEY UPDATE (mysql)
replace into (mysql)

Java基础

1. Hashmap key vlue可以是null吗 key有重复吗

HashMap对象的key、value值均可为null。
HahTable对象的key、value值均不可为null。
且两者的的key值均不能重复,若添加key相同的键值对,后面的value会自动覆盖前面的value,但不会报错。

2. 常用的集合,它们有什么区别,哪些是线程安全的哪些不是

线性安全的
Vector:只要是关键性的操作,方法前面都加了synchronized关键字,来保证线程的安全性
Hashtable:使用了synchronized关键字,所以相较于Hashmap是线程安全的。
ConcurrentHashMap:使用锁分段技术确保线性安全,是一种高效但是线程安全的集合。
Stack:栈,也是线程安全的,继承于Vector。

线性不安全的
Hashmap
Arraylist
LinkedList
HashSet
TreeSet
TreeMap

值得注意的是:为了保证集合是线程安全的,相应的效率也比较低;线程不安全的集合效率相对会高一些。

3. Hashmap的底层原理,18之后的底层数据结构,CurrentHashMap 底层原理

  • Hashmap的底层原理
    HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
    当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。

  • 18之后的底层数据结构
    数组+单线链表+红黑树(1.8)

  • CurrentHashMap的实现原理
    ConcurrentHashMap的出现主要为了解决hashmap在并发环境下不安全,JDK1.8ConcurrentHashMap的设计与实现非常精巧,大量的利用了volatile,CAS等乐观锁技术来减少锁竞争对于性能的影响,ConcurrentHashMap保证线程安全的方案是:
    JDK1.8:synchronized+CAS+HashEntry+红黑树
    JDK1.7:ReentrantLock+Segment+HashEntry。

4. json 解析常用的方法,用过哪些json解析jar包

FastJson GSON解析
Gson:https://github.com/google/gson 【谷歌开发的 JSON 库,功能十分全面】
FastJson:https://github.com/alibaba/fastjson 【阿里巴巴开发的 JSON 库,性能十分优秀】
Jackson:https://github.com/FasterXML/jackson 【社区十分活跃且更新速度很快】

5. 抽象类和接口区别,抽象类可以是静态的吗

相同点:
(1)都不能被实例化
(2)接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。

不同点:
(1)接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
(2)实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
(3)接口强调特定功能的实现,而抽象类强调所属关系。
(4)接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。
(5)接口被用于常用的功能,便于日后维护和添加删除,而抽象类更倾向于充当公共类的角色,不适用于日后重新对立面的代码修改。功能需要累积时用抽象类,不需要累积时用接口。

不可以

6. 构造函数可以 private 吗,单例模式的构造函数可以 private 吗

可以,但是自己写的类是不可以的,不然就没办法new对象
可以的情况:API中的Math类

在单例模式构造函数是可以private的,用静态成员函数GetInstance来获得实例。

7. 冒泡排序实现原理

  • 比较相邻元素,如果元素1大于元素2,则交换。
  • 依次向后对每一个相邻元素做同样的工作,直到队列末尾,这时最大的元素就位于最后一个元素位置了。
  • 重复以上步骤,直到最后一个元素位置的前一位为止(因为最后一位已经排好了)。
  • 持续每次对越来越少的元素重复上面步骤,直到没有任何一个数字需要比较位置,排序结束。

8. 并行与并发的区别

并发和并行之间的区别。
并发(concurrency):把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。
并行(parallelism):把每一个任务分配给每一个处理器独立完成。在同一时间点,任务一定是同时运行。

并发不是并行。并行是让不同的代码片段同时在不同的物理处理器上执行。并行的关键是同时做很多事情,而并发是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情了。

在很多情况下,并发的效果比并行好,因为操作系统和硬件的总资源一般很少,但能支持系统同时做很多事情。这种“使用较少的资源做更多的事情”的哲学,也是指导 Go语言设计的哲学。

9. 多层嵌套循环,直接跳出来

方式一:通过break标签跳出多重循环
方式二:循环条件限制
方式三:内层循环抛出异常结束多重循环

10. 日期格式M和m代表什么年用什么表示4个Y是什么

1.M m是为了区分“月”与“分”,M:月,m:分
2.H h是为了区分12小时制与24小时制,H是24小时制,h是12小时制。
y
Week year意思是当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,那么这周就算入下一年。例如2017年12月31日,本周(2017年12月31日-2018年1月6日)本周跨年了。就进入了下一年。

11. 字符串有英文有数字怎么去掉数字

private void testString(){
    
    
		String string="0079527大白菜";
		char foodName[]=string.toCharArray();
		StringBuilder stringBuilder=new StringBuilder();
		for (int i = 0; i < foodName.length; i++) {
    
    
			char c=foodName[i];
			boolean isDigit=Character.isDigit(c);
			if (!isDigit) {
    
    
				stringBuilder.append(c);
			}
		}
		System.out.println("原来的字符串:"+string+",改后的字符串:"+stringBuilder.toString());
	}

12. String 类常用的方法(substring,charAt)

语法 :字符串名.charAt(值);  返回值为 char 类型。从字符串中取出指定位置的字符

代码如下(示例):

String str="淡忘_Java";
        char c = str.charAt(3);
        System.out.println("指定字符为:" + c);
运行结果:指定字符为:J
String substring(int beginIndex,int endIndex)  截取字符串
String str = "123淡忘_Java博客456";
        System.out.println("截取后的字符为:" + str.substring(0, 3));// 截取0-3个位置的内容   不含3
        System.out.println("截取后字符为:" + str.substring(2));// 从第3个位置开始截取    含2
 运行结果:
   截取后的字符为:123
   截取后字符为:3淡忘_Java博客456

13. 数组和字符串如何取长度

string.length
string.length()

14. 在项目中,获取到一个llst 以及一个对象,首先需要做什么

实例化一个对象

15. a的ascil码多少 97

97~122

16. 不用+或者 add 实现加法

 int add(int a, int b) {
    
    
        return a-(-b);
    }

  public int add(int a, int b) {
    
    
        while (b != 0) {
    
    
            int sum = a ^ b;
            int carry = (a & b) << 1;
            a = sum;
            b = carry;
        }
        return a;
    }

17. 线程池怎么创建,创建线程池的参数都代表什么?

  • newCachedThreadPool()
  • newFixedThreadPool()
  • newScheduledThreadPool()
  • newSingleThreadExecutor()
  • newSingleThreadScheduledExecutor()
public ThreadPoolExecutor(
int corePoolSize,//线程池核心线程大小
int maximumPoolSize,//线程池最大线程数量
long keepAliveTime,//空闲线程存活时间
TimeUnit unit,//空闲线程存活时间单位,一共有七种静态属性(TimeUnit.DAYS天,TimeUnit.HOURS小时,TimeUnit.MINUTES分钟,TimeUnit.SECONDS秒,TimeUnit.MILLISECONDS毫秒,TimeUnit.MICROSECONDS微妙,TimeUnit.NANOSECONDS纳秒)
BlockingQueue<Runnable> workQueue,//工作队列
ThreadFactory threadFactory,//线程工厂,主要用来创建线程(默认的工厂方法是:Executors.defaultThreadFactory()对线程进行安全检查并命名)
RejectedExecutionHandler handler//拒绝策略(默认是:ThreadPoolExecutor.AbortPolicy不执行并抛出异常)
)

使用示例:

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 2, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5));

18. 如何创建多线程,几种方式? 4种

  • 继承Thread类创建线程
  • 实现Runnable接口创建线程
  • 使用Callable和Future创建线程
  • 使用线程池创建(使用java.util.concurrent.Executor接口

19. Walt,sleep的区别,如何理解 notifyAll

  • 区别一:语法使用不同
    wait 方法必须配合 synchronized 一起使用,不然在运行时就会抛出 IllegalMonitorStateException 的异常
  • 区别二:所属类不同
    wait 方法属于 Object 类的方法,而 sleep 属于 Thread 类的方法
  • 区别三:唤醒方式不同
    sleep 方法必须要传递一个超时时间的参数,且过了超时时间之后,线程会自动唤醒。而 wait 方法可以不传递任何参数,不传递任何参数时表示永久休眠,直到另一个线程调用了 notify 或 notifyAll 之后,休眠的线程才能被唤醒。也就是说 sleep 方法具有主动唤醒功能,而不传递任何参数的 wait 方法只能被动的被唤醒。
  • 区别四:释放锁资源不同
    wait 方法会主动的释放锁,而 sleep 方法则不会
  • 区别五:线程进入状态不同
    调用 sleep 方法线程会进入 TIMED_WAITING 有时限等待状态,而调用无参数的 wait 方法,线程会进入 WAITING 无时限等待状态

20. final可以修饰类吗?举例说明

final修饰类不可以被继承,但是可以继承其他类,使用方式跟其它类一样

Linux

1. Linux 命令怎么查看系统时间,常用命令

date

2. 如何部署,杀进程等

  • kill:根据进程号(PID)杀死进程
  • pkill:根据进程名杀死进程
  • killall:根据进程名杀死进程

3. 如何在多屏的日志下,截取 error 信息,分析错误日期

查看

猜你喜欢

转载自blog.csdn.net/u011397981/article/details/131822061