java面试题框架篇

1.Spring框架

1.1Spring两大核心:IOC与AOP

IOC

IOC全称Inversion of Control,即“控制反转”,这是一种设计思想。对象创建的权利由Spring框架完成,由容器管理对象的生命周期。

  1. 原来的对象的创建都是由用户自己手动创建,这样的方式耦合性肯定高,如果类发生了变化,则代码都得修改。
  2. 现在所有的对象都交给Spring容器管理,用户无需关心对象是如何实例化,容器负责对象的注入即可,以后几乎不用修改任何代码,降低了代码的耦合性。

spring容器怎么创建bean对象?
spring容器,这个容器指的就是ConCurrentHashMap
Spring的IOC有三种注入方式 :构造器注入, setter方法注入, 根据注解注入。

DI

DI的意思是依赖注入,和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖IOC容器来动态注入对象需要的外部资源。
注入方式
1.set方式注入
2.构造方法注入
3.基于配置文件注入

<bean id = "c" class = "cn.spring.Cat"/>
<bean id = "dog" class = "cn.spring.Dog">
	<property name = "token" value = "111"/> //set方法注入
	<constructor-arg name = "cat" ref = "c"/> //构造方法注入
</bean> 

4.基于注解开发
@Resource
@Autowired

AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
总结: Spring中的AOP 利用代理对象在不修改源代码的条件下,对方法进行扩展.

切面 = 切入点表达式 + 通知方法

切入点表达式:如果目标对象满足切入点表达式的判断,则spring自动为其创建代理对象
1.bean表达式
根据bean的ID拦截指定的对象
2.within表达式
按照类型匹配,可以使用通配符*号
3.execution表达式
粒度比较细,可以按照方法参数进行匹配
4.@annotation表达式
作用:可以根据用户的自定义注解进行拦截

通知方法:对目标方法进行扩展的封装方法
1.前置通知:在目标方法执行前执行
2.后置通知:在目标方法执行后执行
3.异常通知:目标方法执行报错时执行
4.最终通知:目标方法之后都要执行的通知
5.环绕通知:在目标方法执行前后都要执行,控制目标方法

关于JDK代理和CGlib代理总结(高程/架构)!!!

Spring中默认采用JDK代理,SpringBoot默认采用CGLIB代理。

  1. JDK要求必须有或者实现接口, cglib有无接口都可以创建代理对象.代理对象是目标对象的子类
  2. JDK代理工具API: Proxy.newProxyInstance(类加载器,接口数组,invocationHandler接口)
  3. CGlib代理工具API: Enhancer 增强器对象 获取代理对象 enhancer.create(); 回调接口
    MethodInterceptor接口
  4. JDK中执行目标方法
  • method.invoke(target,args);
    CGlib中执行目标方法
  • method.invoke(target,args);

AOP常用注解

  1. @Aspect 标识切面
  2. @Pointcut 标识切入点表达式 4种写法 2种常用
  3. 五个通知注解
  4. @EnableAspectJAutoProxy(proxyTargetClass = true) //开启AOP
  5. 排序注解 @Order

1.2BeanFactory(懒加载初始bean)和ApplicationContext(立即初始bean) 有什么区别

BeanFactory和ApplicationContext都是接口,并且ApplicationContext是BeanFactory的子接口。同时它们都是spring的容器对象。
BeanFactory:
Spring最顶层的接口,实现了Spring容器的最基础的一些功能, 调用起来比较麻烦, 一般面向Spring自身使用;
BeanFactory在启动的时候不会去实例化Bean,从容器中拿Bean的时候才会去实例化。
ApplicationContext:
是BeanFactory的子接口,扩展了其功能, 一般面向程序员自身使用;
ApplicationContext在启动的时候就把所有的Bean全部实例化了。

1.3Spring框架用到了哪些设计模式

  1. 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例
  2. 单例模式:Bean默认为单例模式
  3. 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
  4. 模板方法模式:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate
  5. 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener

1.4spring框架的优缺点

优点
1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦
2.可以使用容易提供的众多服务,如事务管理,消息服务等
3.容器提供单例模式支持
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供了众多的辅助类,能加快应用的开发
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器
9.spring的DI机制降低了业务对象替换的复杂性
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部
缺点
使用到了大量反射机制。反射机制非常占内存。

1.5Spring常用注解

  1. @Component(任何层) @Controller @Service @Repository(dao): 用于实例化对象
  2. @Scope : 设置Spring对象的作用域
  3. @PostConstruct @PreDestroy : 用于设置Spring创建对象在对象创建之后和销毁之前要执行的方法
  4. @Value: 简单属性的依赖注入
  5. @Autowired: 对象属性的依赖注入
  6. @Qualifier 要和@Autowired 联合使用,代表在按照类型匹配的基础上,再按照名称匹配。
  7. @Resource 按照属性名称依赖注入
  8. @ComponentScan: 组件扫描
  9. @Bean: 标在方法上,用于将方法的返回值对象放入容器
  10. @PropertySource: 用于引入其它的properties配置文件
  11. @Import: 在一个配置类中导入其它配置类的内容
  12. @Configuration: 被此注解标注的类,会被Spring认为是配置类。Spring在启动的时候会自动扫描并加载所有配置类,然后将配置类中bean放入容器
  13. @Transactional 此注解可以标在类上,也可以标在方法上,表示当前类中的方法具有事务管理功能。

2.SpringMVC框架

2.1什么是SpringMVC

SpringMVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

2.2SpringMVC的五大组件及处理流程

五大组件

1.DispatcherServlet
DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自己定义,把拦截下来的请求,依据相应的规则分发到目标Controller来处理,是配置spring MVC的第一步。
DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IOC容器无缝集成,从而可以获得Spring的所有好处。
2.HandlerMapping
HandlerMapping 是处理器映射,它的作用是请求派发,负责请求和控制器建立对应的关系。它是由 DispatcherServlet 调用,DispatcherServlet 会从容器中取出所有 HandlerMapping 实例并遍历,让 HandlerMapping 实例根据自己实现类的方式去尝试查找 Handler。也就是说,DispatcherServlet要将一个请求交给哪个特定的Controller,它需要咨询一个Bean,这个Bean的名字为“HandlerMapping”。
3.Controller
控制器,负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。
4.ModelAndView
封装数据信息和视图信息的模型。使用ModelAndView类用来存储处理完后的结果数据,以及显示该数据的视图。从名字上看ModelAndView中的Model代表模型,View代表视图,这个名字就很好地解释了该类的作用。业务处理器调用模型层处理完用户请求后,把结果数据存储在该类的model属性中,把要返回的视图信息存储在该类的view属性中,然后让该ModelAndView返回该Spring MVC框架。框架通过调用配置文件中定义的视图解析器,对该对象进行解析,最后把结果数据显示在指定的页面上。
5.ViewResolver
视图解析器,ViewResolver 的主要作用是把一个逻辑上的视图名称解析为一个真正的视图。 DispatcherServlet收到请求后,依据HandlerMapping的配置,调用相应的Controller来处理,Controller将处理结果封装成ModelAndView对象,返回给DispatcherServlet,DispatcherServlet依据ViewResolver的解析,调用相应的视图对象来生成相应的页面.

处理流程:

1.客户端向服务端发起请求
2.请求经过滤器到达 DispatcherServlet 前端控制器,并初始化Spring MVC配置。
3.前端控制器 DispatcherServlet 将请求资源 url 传递给 HandlerMapping 对象处理。
4.HandlerMapping 对象依据 URL 获取对象请求的执行链对象,然后先执行拦截器,再执行后端处理器对象。
5.将后台控制器的处理结果例如视图和数据封装到 ModelAndView 对象,最后返回给Spring MVC。
6.Spring MVC 解析控制层方法的返回结果(例如视图解析器解析视图)再进一步处理。
7.请求处理结束,返回客户端相关信息
应用优势:
更好的实现对象解耦,简化了代码编写,提高程序可读性,以及可维护性。

2.3SpringMVC的常用注解

1.@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为
父路径。
2.@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
3.@ResponseBody:注解实现将controller方法返回对象转化为json对象响应给客户。
4.@PathVariable 用户从url路径上获取指定参数,标注在参数前 @PathVariabel(“要获取的参数名”)。
5.@RequestParam: 标注在方法参数之前,用于对传入的参数做一些限制,支持三个属性:
- value:默认属性,用于指定前端传入的参数名称
- required:用于指定此参数是否必传
- defaultValue:当参数为非必传参数且前端没有传入参数时,指定一个默认值
6. @ControllerAdvice 标注在一个类上,表示该类是一个全局异常处理的类。@ControllerAdvice 配合 @ExceptionHandler 实现全局异常处理。
7. @ExceptionHandler(Exception.class) 标注在异常处理类中的方法上,表示该方法可以处理的异常类型。

2.4SpringMVC统一异常处理的思想和实现方式

使用SpringMVC之后,代码的调用者是SpringMVC框架,也就是说最终的异常会抛到框架中,然后由框架指定异常处理类进行统一处理
方式一: 创建一个自定义异常处理器(实现HandlerExceptionResolver接口),并实现里面的异常处理方法,然后将这个类交给Spring容器管理
方式二: 在类上加注解(@ControllerAdvice)表明这是一个全局异常处理类
在方法上加注解(@ExceptionHandler),在ExceptionHandler中有一个value属性,可以指定可以处理的异常类型

2.5在SpringMVC中拦截器的使用步骤是什么样的

1 定义拦截器类:
SpringMVC为我们提供了拦截器规范的接口,创建一个类实现HandlerInterceptor,重写接口中的抽象方法;
preHandle方法:在调用处理器之前调用该方法,如果该方法返回true则请求继续向下进行,否则请求不会继续向下进行,处理器也不会调用
postHandle方法:在调用完处理器后调用该方法
afterCompletion方法:在前端控制器渲染页面完成之后调用此方法
2 注册拦截器:
在SpringMVC核心配置文件中注册自定义的拦截器
mvc:interceptors
mvc:interceptor
<mvc:mapping path=“拦截路径规则”/>
<mvc:exclude-mapping path=“不拦截路径规则”/>

</mvc:interceptor>
</mvc:interceptors>

2.6SpringMVC 中如何解决GET|POST请求中文乱码问题?

(1)解决post请求乱码问题:在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8;

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
   <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter> 
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

(2) get请求中文参数出现乱码解决方法有两个:
①修改tomcat配置文件添加编码与工程编码一致,如下:

<ConnectorURIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

②另外一种方法对参数进行重新编码:

String userName= new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")

ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码。

2.7SpringMVC 的拦截器和过滤器什么区别

1).SpringMVC中的拦截器interceptor用于对控制器controller进行预处理和后处理的技术;
2).可以定义拦截器链,拦截器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。
3).拦截器interceptor与过滤器filter的区别
1.过滤器是servlet中的,任何框架都可以使用过滤器技术;
2.拦截器是SpringMVC所独有的
3.过滤器设置/*可以拦截任何资源
4.拦截器只对控制器controller中的方法进行拦截
4).拦截器利用的是AOP思想;
5).自定义拦截器,需要实现HandlerInterceptor接口

3.Mybatis框架

MyBatis
优点: 基于配置文件+注解, 可以灵活的书写原生sql语句
缺点: 在单表操作时,需要写sql语句,比较麻烦

3.1MyBatis应用中#与$有什么区别

1.#{ }是占位符,预编译处理,MyBatis在处理#{ }时,它会将sql中的#{ }替换为"?",然后调用PreparedStatement的set方法来赋值;
使用#{}可以有效的防止SQL注入,提高系统安全性。
2.

是字符串替换,

M

y

B

a

t

i

s

在处理

{ }是字符串替换,MyBatis在处理

是字符串替换,MyBatis在处理{ }时,它会将sql中的${ }替换为变量的值。

3.2Mybatis的工作原理

本质上是封装了JDBC,简化了jdbc的操作并且是一个非常优秀的ORM框架。
1, mybatis应用程序通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件
(也可以用Java文件配置的方式,需要添加@Configuration)中构建出SqlSessionFactory(SqlSessionFactory是线程安全的)
2, SqlSessionFactory的实例直接开启一个SqlSession
3, 再通过SqlSession实例获得Mapper对象
4, 执行Mapper映射的SQL语句,完成对数据库的CRUD和事务提交
5, 关闭SqlSession
说明:SqlSession是单线程对象,因为它是非线程安全的,是持久化操作的独享对象,类似jdbc中的Connection,底层就封装了jdbc连接。

3.3MyBatis中resultType和resultMap的区别

若是实体类的字段和数据库的字段一一对应,则可直接用resultType。
如果数据库字段和实体类字段不是一一对应的,则需要采用resultMap属性,两者字段之间进行映射。
resultType和resultMap都是返回对象的,resultType实体类字段必须和数据库字段一一对应,而resultMap如果两者字段名不同,则可通过定义resultMap进行字段的映射。

3.4Mybatis中的动态SQL的标签

  1. if是为了判断传入的值是否符合某种规则,比如是否不为空.
  2. where标签可以用来做动态拼接查询条件,当和if标签配合的时候,不用显示的声明类型where 1 = 1这种无用的条件
  3. foreach标签可以把传入的集合对象进行遍历,然后把每一项的内容作为参数传到sql语句中.
  4. include可以把大量的重复代码整理起来,当使用的时候直接include即可,减少重复代码的编写;
  5. 适用于更新中,当匹配某个条件后,才会对该字段进行跟新操作

3.5mybatis缓存机制

Mybatis有两级缓存,一级缓存是SqlSession级别的,默认开启,无法关闭;二级缓存是Mapper级别的,二级缓存默认是没有开启的,需要手动开启。
当我们的配置文件配置了cacheEnabled=true时,就会开启二级缓存,二级缓存是mapper级别的,如果你配置了二级缓存,那么查询数据的顺序应该为:二级缓存→一级缓存→数据库。

4.SpringBoot框架

4.1什么是SpringBoot

  • 用来简化spring应用的初始搭建以及开发过程 使用特定的方式来进行配置(properties或yml文件)
  • 创建独立的spring引用程序 main方法运行
  • 嵌入的Tomcat 无需部署war文件
  • 简化maven配置
  • 自动配置spring添加对应功能starter(启动器)自动化配置

4.2SpringBoot有哪些优点

SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。
版本锁定:解决是maven依赖版本容易冲突的问题,集合了常用的并且测试过的所有版本
使用了Starter(启动器)管理依赖并能对版本进行集中控制,如下的父工程带有版本号, 就是对版本进行了集中控制.

 <!--引入父工程-->
	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
   </parent>

起步依赖 :解决了完成某一个功能要整合的jar包过多的问题,集合了常用的jar包
自动配置:解决了整合框架或者技术的配置文件过多,集合了所有的约定的默认配置
内置Tomcat:通过内置的tomcat,无需再用其他外置的Tomcat就直接可以运行javaEE程序
总之:人们把Spring Boot 称为搭建程序的脚手架。其最主要作用就是帮我们快速的构建庞大的spring项目,并且尽可能的减少一切xml配置,做到开箱即用,迅速上手,让我们关注于业务而非配置。

4.3SpringBoot的启动器starter

4.3.1什么是Starter

starter启动器,可以通过启动器集成其他的技术,比如说: web, mybatis, redis等等.可以提供对应技术的开发和运行环境.
比如: pom中引入spring-boot-starter-web, 就可以进行web开发。

  • 如果启动器是以spring-boot-starter-x开头的, 那就表示该启动器是springBoot封装的
  • 如果启动器是以x-spring-boot-starter开头的,那就表示该启动器不是springBoot封装的,是第三方封装的

4.3.2starter执行原理

  • SpringBoot在启动时候会去扫描jar包中的一个名为spring.factories.
  • 根据文件中的配置,去加载自动配置类. 配置文件格式是key=value, value中配置了很多需要Spring加载的类.
  • Spring会去加载这些自动配置类,Spring读取后,就会创建这些类的对象,放到Spring容器中.后期就会从Spring容器中获取这些类对象.

4.4SpringBoot构建的项目如何部署和运行

  • 开发环境: 直接启动的应用类, 启动我们的项目
  • 生成环境: 项目开发完毕,要上线了.
    方式一: springboot默认是jar包, 通过 java的jar启动命令, 来部署和运行项目.
    方式二: springboot打成war包(排除springboot内嵌的tomcat), 把war包部署到tomcat服务器运行.

SpringBoot常用注解

@SpringBootApplication:它封装了核心的@SpringBootConfiguration +@EnableAutoConfiguration +@ComponentScan这三个类,大大节省了程序员配置时间,这就是SpringBoot的核心设计思想.
@EnableScheduling是通过@Import将Spring调度框架相关的bean定义都加载到IOC容器
@MapperScan:spring-boot支持mybatis组件的一个注解,通过此注解指定mybatis接口类的路径,即可完成对mybatis接口的扫描
@RestController 是@Controller 和@ResponseBody的结合,一个类被加上@RestController注解,数据接口中就不再需要添加 @ ResponseBody,更加简洁。
@RequestMapping,我们都需要明确请求的路径.
@GetMappping,@PostMapping, @PutMapping, @DeleteMapping 结合@RequestMapping使用, 是Rest风格的, 指定更明确的子路径.
@PathVariable:路径变量注解,用{}来定义url部分的变量名.
@Service这个注解用来标记业务层的组件,我们会将业务逻辑处理的类都会加上这个注解交给spring容器。事务的切面也会配置在这一层。当让 这个注解不是一定要用。有个泛指组件的注解,当我们不能确定具体作用的时候 可以用泛指组件的注解托付给spring容器
@Component和spring的注解功能一样, 注入到IOC容器中.
@ControllerAdvice 和 @ExceptionHandler 配合完成统一异常拦截处理.

开箱即用原理

SpingBoot官网将主流框架进行整合,用户无需多余的配置,即拿来就用,及开箱就用。

常见跨域方式

1).JSONP方式 但是JSONP只可以发送GET请求,无法发送其他类型的请求
2). Restful风格 利用/user/xx实现数据传递
3).CORS,(Cross-origin resource sharing)跨域资源共享 来解决跨域问题。

MVVM框架是什么?

MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。

v-if 和 v-show 的区别

v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。
v-show 会被编译成指令,条件不满足时控制样式将此节点隐藏(display:none)
使用场景
v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景。
v-show 适用于需要非常频繁切换条件的场景。

vue中对数组操作的方法有哪些,并说明其作用

1.push 在结尾新增一个元素
2.shift 删除第一个元素
3.pop, 删除最后一个元素
4.splice 替换元素
5.unshift 在第一位新增元素
6.sort 排序元素
7.reverse 反转元素

请简述什么是Nginx

Nginx是一个 Web服务器、反向代理服务器,用于 HTTP、HTTPS、SMTP、POP3和IMAP协议。
目前使用的最多的 Web服务器或者代理服务器,像淘宝、新浪、网易、迅雷等都在使用。
Nginx的主要功能如下:
1.反向代理服务器
2.实现负载均衡
3.虚拟主机

NGINX 常见命令

启动nginx: start nginx
重启nginx: nginx -s reload
停止nginx: nginx -s stop

重载配置 ./sbin/nginx -s reload(平滑重启) 或 service nginx reload
重载指定配置文件 .nginx -c /usr/local/nginx/conf/nginx.conf
查看 nginx 版本 nginx -v
检查配置文件是否正确 nginx -t
显示帮助信息 nginx -h

简述什么是正向代理/反向代理

反向代理(必须掌握):
反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无须在用户端作任何设定。反向代理服务器通常可用来作为Web加速,即使用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率。
总结:

  1. 代理服务器位于用户和服务器之间
  2. 用户以为代理服务器就是目标服务器.
  3. 用户无需了解真实服务器地址.
  4. 反向代理服务器保护了服务器端的信息 (服务器端代理)

正向代理(了解)
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从目标服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。
特点:

  1. 代理服务器位于用户与服务器之间
  2. 用户了解访问的服务器到底是谁.
  3. 只有用户可以使用正向代理 (客户端代理)
  4. 正向代理保护了用户的信息.

小结:

  1. 反向代理保护了服务器信息. 称之为服务器端代理(业务数据获取)
  2. 正向代理保护了用户的信息. 称之为客户端代理. (网络出口)
  3. 用户每次请求几乎都有正向和反向代理的影子.
版权声明:程序员胖胖胖虎阿 发表于 2022年11月22日 上午5:32。
转载请注明:java面试题框架篇 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...