diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\345\246\202\344\275\225\345\207\206\345\244\207\347\256\227\346\263\225/\345\246\202\344\275\225\345\207\206\345\244\207\347\256\227\346\263\225.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\345\246\202\344\275\225\345\207\206\345\244\207\347\256\227\346\263\225/\345\246\202\344\275\225\345\207\206\345\244\207\347\256\227\346\263\225.md" index 01f4180..5b82053 100644 --- "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\345\246\202\344\275\225\345\207\206\345\244\207\347\256\227\346\263\225/\345\246\202\344\275\225\345\207\206\345\244\207\347\256\227\346\263\225.md" +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\345\246\202\344\275\225\345\207\206\345\244\207\347\256\227\346\263\225/\345\246\202\344\275\225\345\207\206\345\244\207\347\256\227\346\263\225.md" @@ -1,6 +1,6 @@ # 如何准备算法 -作者:[石皮幼鸟](https://juejin.cn/user/3437526047270291) +作者:石皮幼鸟 [掘金](https://juejin.cn/user/3437526047270291) | [B 站](https://space.bilibili.com/30915729) 这篇文章是专门为计算机相关专业或想走这一行的同学朋友们准备的,主要是为了帮助你们更好地准备算法,提高你们的算法能力,从而更好地应对秋招春招的挑战。 diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/SpringBoot/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/SpringBoot/index.md" new file mode 100644 index 0000000..385bf56 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/SpringBoot/index.md" @@ -0,0 +1,138 @@ +**本模块适合 `Java后端研发(业务岗、架构岗)`的同学。** + +## 讲讲 SpringBoot 的优点 + +SpringBoot 的优点主要有以下几点: + +1. 简化开发:SpringBoot 提供了自动配置、快速启动、依赖管理等功能,简化了开发流程,提高了开发效率。 +2. 零配置:SpringBoot 采用约定大于配置的方式,减少了配置文件,提高了代码的可读性、可维护性、可扩展性。 +3. 内嵌服务器:SpringBoot 内置了 Tomcat、Jetty、Undertow 等服务器,方便部署和运行。 +4. 微服务架构:SpringBoot 支持微服务架构,提供了 RESTful API、消息队列、服务注册、服务发现等功能,适合微服务架构。 +5. 生态系统:SpringBoot 有庞大的生态系统,提供了大量的插件、工具、框架等,方便开发和部署。 + +## 为什么选择 SpringBoot,不选择 Nodejs?(简历写全栈的同学必定会被问到) + +SpringBoot 是一种 Java 框架,主要用于开发企业级应用,支持事务管理、安全认证、数据访问等,适合大型项目。 + +Nodejs 是一种 JavaScript 框架,主要用于开发 Web 应用,支持异步 IO、事件驱动、非阻塞 IO 等,适合小型项目。 + +选择 SpringBoot 的主要原因有以下几点: + +1. Java 生态系统:SpringBoot 是基于 Java 生态系统,有庞大的生态系统,提供了大量的插件、工具、框架等,方便开发和部署。 +2. 企业级应用:SpringBoot 支持事务管理、安全认证、数据访问等,适合开发大型项目,提高代码的可读性、可维护性、可扩展性。 +3. 性能稳定:SpringBoot 是基于 Java 虚拟机,性能稳定,适合高并发场景,提高系统的性能和可靠性。 + +因此,根据不同的需求选择合适的框架,SpringBoot 适合开发大型项目,Nodejs 适合开发小型项目。 + +## 讲讲 SpringBoot 的自动装配原理 + +SpringBoot 的自动装配原理主要有以下几个步骤: + +1. SpringBoot 启动时,会扫描所有的类路径,查找`@SpringBootApplication`注解。 +2. SpringBoot 会根据`@SpringBootApplication`注解的配置,自动装配`@Configuration`、`@ComponentScan`、`@EnableAutoConfiguration`等注解。 +3. SpringBoot 会根据`@EnableAutoConfiguration`注解的配置,自动装配`spring-boot-autoconfigure`模块中的配置类。 +4. SpringBoot 会根据`spring.factories`文件中的配置,自动装配`spring-boot-starter`模块中的依赖。 + +SpringBoot 的自动装配原理是为了简化开发流程,提高开发效率,减少配置文件,提高代码的可读性、可维护性、可扩展性。 + +## 什么叫 AOP,SpringBoot 底层如何实现的 AOP? + +AOP(Aspect-Oriented Programming)是一种面向切面编程的方法,亦是代理模式的直接实现。 + +AOP 提供另一个角度来考虑程序的结构,主要是为了解耦业务逻辑和横切关注点,提高代码的可读性、可维护性、可扩展性。 + +使用 AOP 的好处包括: + +1. 代码重用:将横切关注点抽象成切面,提高代码的可重用性。 +2. 代码解耦:将业务逻辑和横切关注点解耦,提高代码的可维护性。 +3. 代码扩展:将横切关注点动态织入业务逻辑,提高代码的可扩展性。 + +AOP 主要包含如下几个概念: + +1. 切面(Aspect):横切关注点,包括通知、切点、连接点等。 +2. 通知(Advice):在连接点上执行的动作,包括前置通知、后置通知、环绕通知、异常通知、最终通知等。 +3. 切点(Pointcut):连接点的集合,用于匹配连接点。 +4. 连接点(Joinpoint):程序执行的某个特定点,如方法调用、方法返回、方法异常等。 +5. 织入(Weaving):将切面应用到目标对象的过程,包括编译时织入、类加载时织入、运行时织入等。 +6. 代理(Proxy):代理对象,用于控制对目标对象的访问,包括静态代理、动态代理、CGLIB 代理等。 +7. 目标对象(Target):被代理的对象,包括业务逻辑、数据访问等。 + +实现 AOP 主要包含 JDK 动态代理和 CGLIB 动态代理两种方式: + +1. JDK 动态代理:基于接口的代理,通过`java.lang.reflect.Proxy`类实现,要求目标对象必须实现接口。 +2. CGLIB 动态代理:基于类的代理,通过`net.sf.cglib.proxy.Enhancer`类实现,不要求目标对象必须实现接口。 + +其原理很简单,调用`Method.invoke()`方法时,会先调用`MethodInterceptor.intercept()`方法,最后将参数传递给目标对象。 + +## 什么是 IOC 和 DI? + +IOC(Inversion of Control)是一种控制反转的思想,主要是将对象的创建和管理交给容器,提高代码的可读性、可维护性、可扩展性。 + +DI(Dependency Injection)是一种依赖注入的方式,主要是将对象的依赖关系交给容器,提高代码的可读性、可维护性、可扩展性。 + +SpringBoot 的 IOC 和 DI 主要有以下几个概念: + +1. Bean:SpringBoot 的核心对象,由容器管理,包括配置类、组件类、代理类等。 +2. Configuration:SpringBoot 的配置类,用于配置 Bean 的创建和管理,包括`@Configuration`、`@ComponentScan`、`@EnableAutoConfiguration`等。 +3. Component:SpringBoot 的组件类,用于定义 Bean 的依赖关系,包括`@Component`、`@Service`、`@Repository`、`@Controller`等。 +4. Autowired:SpringBoot 的依赖注入,用于注入 Bean 的依赖关系,包括`@Autowired`、`@Qualifier`、`@Resource`、`@Inject`等。 +5. BeanFactory:SpringBoot 的容器类,用于管理 Bean 的创建和管理,包括`BeanFactory`、`ApplicationContext`、`AnnotationConfigApplicationContext`等。 + +SpringBoot 的 IOC 和 DI 主要是为了解耦对象的创建和使用,提高代码的可读性、可维护性、可扩展性,根据不同的需求选择合适的配置类、组件类、代理类等。 + +## 讲讲 SpringBoot 的启动流程 + +SpringBoot 的启动流程主要有以下几个步骤: + +1. 加载配置:SpringBoot 会加载`application.properties`、`application.yml`等配置文件,读取配置信息,初始化配置类。 +2. 扫描注解:SpringBoot 会扫描`@SpringBootApplication`、`@Configuration`、`@ComponentScan`、`@EnableAutoConfiguration`等注解,初始化配置类。 +3. 自动装配:SpringBoot 会根据`@EnableAutoConfiguration`注解的配置,自动装配`spring-boot-autoconfigure`模块中的配置类,初始化 Bean 对象。 +4. 启动容器:SpringBoot 会启动`Tomcat`、`Jetty`、`Undertow`等服务器,监听端口,接收请求,处理响应。 +5. 处理请求:SpringBoot 会根据`@Controller`、`@RestController`、`@RequestMapping`等注解,处理请求,返回响应。 + +SpringBoot 的启动流程是为了简化开发流程,提高开发效率,减少配置文件,提高代码的可读性、可维护性、可扩展性。 + +## 什么是 Starter? + +Starter 是 SpringBoot 的一个插件,本质是对依赖的集合,主要是为了简化依赖管理,提高代码的可读性、可维护性、可扩展性。 + +Starter 主要包含如下几个概念: + +1. Starter:SpringBoot 的插件,用于管理依赖关系,提供一组依赖的集合,方便开发和部署。 +2. Autoconfigure:SpringBoot 的自动配置,用于自动装配依赖关系,提供一组配置的集合,方便开发和部署。 +3. Starter Parent:SpringBoot 的父项目,用于管理依赖关系,提供一组依赖的集合,方便开发和部署。 +4. Starter Test:SpringBoot 的测试插件,用于测试依赖关系,提供一组测试的集合,方便开发和部署。 + +Starter 是为了简化依赖管理,提高代码的可读性、可维护性、可扩展性,根据不同的需求选择合适的 Starter。 + +值得注意的是,如果是官方的 Starter,一般是以`spring-boot-starter-`开头,如`spring-boot-starter-web`、`spring-boot-starter-data-jpa`等,如果是三方的 Starter,一般是以`xxx-spring-boot-starter`开头,如`mybatis-spring-boot-starter`、`dubbo-spring-boot-starter`等。 + +## SpringBoot 在注入时如何解决循环依赖?(不常考,但如果说自己读过源码,必问) + +循环依赖是一个非常棘手的问题,SpringBoot 是通过三级缓存解决的,具体来说,有如下三级缓存: + +1. singletonObjects:存放完全实例化好的 Bean,可以直接使用。类型是`ConcurrentHashMap`。 +2. earlySingletonObjects:存放早期暴露出来的 Bean,还没有完成属性注入和初始化的 Bean,但是可以提前暴露出来给其他 Bean 使用。类型是`ConcurrentHashMap`。 +3. singletonFactories:存放未完成属性注入和初始化的 Bean 的工厂,用于解决循环依赖问题。类型是`ConcurrentHashMap>`。 + +在注册和依赖注入过程中,SpringBoot 首先会调用构造函数创建 Bean 的实例。创建完之后,会先将 Bean 的名称作为 Key,一个获得提前暴露的半成品 Bean 的工厂方法作为 value 放入三级框架中。注册完成后,进入属性注入阶段。 + +正常情况下,属性注入会先从一级缓存中获取 Bean,如果没有,再从二级缓存中获取 Bean,如果还没有,一般就出现了循环依赖问题,直接从三级缓存中获取工厂方法,得到半成品,移入二级缓存,删除三级缓存当中对应的方法,再进行属性注入。这个过程中,如果对象允许被创建代理,那么放入二级缓存的是代理对象。 + +完成注入后,再将 Bean 移入一级缓存,删除二级和三级缓存中对应的内容,这就是总体的注册和依赖注入过程。 + +## SpringBoot 底层是如何解决基于里氏替换原则实现类型注入的?(不常考,但如果说自己读过源码,必问) + +先说说里氏替换原则,里氏替换原则是面向对象设计的一个重要原则,主要是为了保证子类可以替换父类,提高代码的可读性、可维护性、可扩展性。 + +简单可以理解为,如果 A 是 B 的子类,那么在任何使用 B 的地方,都可以替换成 A,而不影响程序的正确性,即大装小,如:`List list = new ArrayList();`。 + +既然要实现基于里氏替换原则的类型注入,就意味着,我们必须从 Bean 对应类型的实现链和继承链中找到对应的类型,然后再进行注入。 + +但这样会导致每次类型注入都要遍历整个所有 Bean 的实现链和继承链,如果有 m 个字段,n 个 Bean,那么复杂度就是 O(mn),这种设计是不可取的。 + +SpringBoot 为了定位 Bean 对应的类型的父类和接口,维护了`allBeanNamesByType`和`singletonBeanNamesByType`两个 Map,类型是`ConcurrentHashMap`,key 是 Bean 的类型,包括接口和父类,value 是 Bean 的名称数组。在 Bean 刚刚实例化的时候,会将 Bean 的类型和名称,以及整个继承链和实现链上的类型和名称都放入这两个 Map 中。 + +在依赖注入阶段,如果三级缓存都拿不到对应的 Bean,就意味着这个注入是基于类型的,那么就会从这两个 Map 中找到对应的 Bean 名称数组,然后又从一级缓存中找到对应的 Bean,进行注入。 + +这样就实现了基于里氏替换原则的类型注入,时间复杂度从 O(mn) 降低到了近乎 O(1)(因为哈希表会出现哈希冲突导致在链表或红黑树上查找,所以不是完全的 O(1))。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/index.md" new file mode 100644 index 0000000..ae68770 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/index.md" @@ -0,0 +1 @@ +**本模块适合 `Java后端研发(业务岗、架构岗)`、`Java安卓原生研发`等 Java 相关岗位的同学。** \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/jvm/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/jvm/index.md" new file mode 100644 index 0000000..ed44181 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/jvm/index.md" @@ -0,0 +1,153 @@ +**本模块适合 `Java后端研发(业务岗、架构岗)`、`Java安卓原生研发`等 Java 相关岗位的同学。** + +## 讲讲 jvm 的内存结构,细分到 JDK 7 和 JDK 8 的区别 + +Java 虚拟机(JVM)的内存结构主要分为以下几个区域: + +1. 程序计数器(Program Counter Register):线程私有,用来存储当前线程执行的字节码指令地址。 +2. Java 虚拟机栈(Java Virtual Machine Stacks):线程私有,每个线程都有一个栈,用来存储方法的局部变量、操作数栈、动态链接、方法出口等信息。 +3. 本地方法栈(Native Method Stack):线程私有,用来存储本地方法的信息。 +4. Java 堆(Java Heap):所有线程共享,用来存储对象实例。 +5. 方法区(Method Area):所有线程共享,用来存储类的结构信息、常量、静态变量等。 +6. 运行时常量池(Runtime Constant Pool):方法区的一部分,用来存储编译期生成的字面量和符号引用。 +7. 直接内存(Direct Memory):不是虚拟机运行时数据区的一部分,但是也会被频繁使用,主要是 NIO 中使用的堆外内存。 + +JDK 7 和 JDK 8 的内存结构区别主要在于永久代的移除和元空间的引入。 + +- JDK 7 中,方法区是永久代(Perm)的实现,存储类的结构信息、常量、静态变量等。永久代的大小是有限的,当类的数量增多时,会导致永久代内存溢出。永久代的垃圾回收主要是通过`Full GC`来进行,效率较低。 +- JDK 8 中,永久代被移除,取而代之的是元空间(Metaspace),元空间是使用本地内存来存储一些元数据(常量池和静态变量则被移动至 Java 堆中)。元空间的大小是受限于本地内存,当类的数量增多时,会导致本地内存溢出。元空间的垃圾回收主要是通过`Full GC`来进行,效率较高。 + +JDK 8 中的元空间相比于 JDK 7 中的永久代有以下几个优点: + +1. 元空间使用本地内存,不受永久代大小的限制,可以动态调整。 +2. 元空间的垃圾回收效率高,不会出现永久代内存溢出的情况。 +3. 元空间的内存分配和回收更加灵活,可以避免内存泄漏。 + +## 讲讲 JVM 的垃圾回收时的可达性检测算法 + +Java 虚拟机的垃圾回收可达性分析算法主要采用的是根搜索算法。 + +根搜索算法是通过一系列的`GC Roots`对象作为起始点,从这些节点开始向下搜索,搜索过程中所走过的路径称为引用链,当一个对象到`GC Roots`没有任何引用链相连时,则证明此对象是不可达的,即为垃圾对象。 + +`GC Roots`对象包括以下几种: + +1. 虚拟机栈中引用的对象。 +2. 本地方法栈中引用的对象。 +3. 方法区中静态属性引用的对象。 +4. 方法区中常量引用的对象。 + +根搜索算法的优点是不需要暂停用户线程,可以并发执行,不会影响用户线程的执行。 + +可达性检测算法还包含了引用计数算法,但是引用计数算法无法解决循环引用的问题,会导致内存泄漏。 + +## 讲讲 JVM 的垃圾回收算法 + +先说说常见的垃圾回收算法: + +1. 标记 - 清除算法(Mark-Sweep):标记 - 清除算法是一种基础的垃圾回收算法,分为标记和清除两个阶段。首先标记所有存活对象,然后清除所有未标记的对象。标记 - 清除算法优点是实现简便,不需要移动对象,但由于算法过程中需要暂停应用,且会产生内存碎片,导致如果需要分配大对象时,可能会因为内存碎片不足而提前触发垃圾回收。 +2. 复制算法(Copying):复制算法是一种高效的垃圾回收算法,将内存空间划分为两块,每次只使用其中一块,当这一块内存用完时,将存活对象复制到另一块内存中,然后清除当前内存。复制算法优点是实现简单,不会产生内存碎片,但缺点是需要额外的内存空间。 +3. 标记 - 整理算法(Mark-Compact):标记 - 整理算法是一种高效的垃圾回收算法,结合了标记 - 清除算法和复制算法的优点,标记存活对象,然后将存活对象向一端移动,清除另一端的内存。标记 - 整理算法优点是不会产生内存碎片,但缺点是需要移动对象,可能会影响性能。 +4. 分代收集算法(Generational):分代收集算法是一种高效的垃圾回收算法,根据对象的存活周期将内存划分为不同的代,一般分为新生代和老年代。新生代使用复制算法,老年代使用标记 - 整理算法。分代算法优点是根据对象的存活周期选择合适的算法,提高了垃圾回收效率。 + +Java 虚拟机的垃圾回收算法主要采用的是分代收集算法。 + +在新生代中,分为 Eden 区、Survivor 区的 From 区和 Survivor 区的 To 区,比例为 8:1:1,使用复制算法。首先将 Eden 区存活的对象复制到 From 区,然后将 Eden 清空。当 From 区满时,将存活的对象复制到 To 区,清空 Eden 和 From 区,然后将 From 区和 To 区交换。循环往复直到 To 区放不下存活的对象。新生代发生的 GC 称为 Minor GC。 + +在老年代中,使用标记 - 整理算法,标记存活对象,然后将存活对象向一端移动,清除另一端的内存。老年代发生的 GC 称为 Major GC 或 Full GC。Major GC 的触发会连带触发 Minor GC。 + +## 讲讲 JVM 的垃圾回收器(不常考) + +Java 虚拟机的垃圾回收器主要分为以下几种: + +1. 串行垃圾回收器(Serial GC):串行垃圾回收器是一种单线程的垃圾回收器,使用复制算法,适用于单核 CPU 的环境。串行垃圾回收器在新生代使用复制算法,在老年代使用标记 - 整理算法。 +2. 并行垃圾回收器(Parallel GC):并行垃圾回收器是一种多线程的垃圾回收器,使用复制算法,适用于多核 CPU 的环境。并行垃圾回收器在新生代使用复制算法,在老年代使用标记 - 整理算法。 +3. CMS 垃圾回收器(Concurrent Mark-Sweep GC):CMS 垃圾回收器是一种并发的垃圾回收器,使用标记 - 清除算法,适用于对响应时间有要求的场景。CMS 垃圾回收器在新生代使用复制算法,在老年代使用标记 - 清除算法。 +4. G1 垃圾回收器(Garbage-First GC):G1 垃圾回收器是一种面向服务端的垃圾回收器,使用分代收集算法,适用于大内存、多核 CPU 的环境。G1 垃圾回收器将堆内存划分为多个区域,每个区域独立进行垃圾回收,可以控制垃圾回收的时间和内存占用。 + +不同的垃圾回收器适用于不同的场景,可以根据应用的特点和需求选择合适的垃圾回收器。 + +## 讲讲常见的引用类型(不常考) + +Java 中的引用类型主要分为以下几种: + +1. 强引用(Strong Reference):强引用是最常见的引用类型,只要存在强引用,垃圾回收器就不会回收对象。例如: + +```java +Object obj = new Object(); +``` + +2. 软引用(Soft Reference):软引用是一种相对弱化的引用类型,只有在内存不足时,垃圾回收器才会回收对象。例如: + +```java +SoftReference softRef = new SoftReference<>(new Object()); +``` + +3. 弱引用(Weak Reference):弱引用是一种更弱化的引用类型,只要发生垃圾回收,就会回收对象。例如: + +```java +WeakReference weakRef = new WeakReference<>(new Object()); +``` + +4. 虚引用(Phantom Reference):虚引用是一种最弱化的引用类型,无法通过虚引用获取对象,主要用来跟踪对象被回收的状态。例如: + +```java +ReferenceQueue queue = new ReferenceQueue<>(); +PhantomReference phantomRef = new PhantomReference<>(new Object(), queue); +``` + +引用类型的主要作用是帮助开发者更好地管理内存,避免内存泄漏和内存溢出。 + +## 讲讲 JVM 的类加载机制 + +Java 虚拟机的类加载机制主要分为以下几个步骤:(3、4、5 统称为链接) + +1. 加载(Loading):加载是指将类的字节码文件加载到内存中,并生成一个`java.lang.Class`对象。 +2. 验证(Verification):验证是指对字节码文件进行验证,确保字节码文件的正确性和安全性。 +3. 准备(Preparation):准备是指为类的静态变量分配内存,并初始化为默认值。 +4. 解析(Resolution):解析是指将类的符号引用解析为直接引用。 +5. 初始化(Initialization):初始化是指执行类的构造方法,初始化类的静态变量和静态代码块。 + +类加载机制主要遵循以下几个原则: + +1. 双亲委派模型(常见的代理模式):类加载器之间存在父子关系,子类加载器会委托父类加载器加载类,在父类加载器加载不了类时,子类加载器才会加载类,如果都加载不了类,会抛出`ClassNotFoundException`异常。(自下向上委托,自上向下加载) +2. 全盘负责原则:类加载器负责加载类的所有依赖类,如果依赖类没有加载,会先加载依赖类,除非显式调用另一个类加载器加载类。 +3. 缓存机制:类加载器会缓存加载过的类,避免重复加载。 + +此外,例如 Tomcat 采用的加载方式虽然是一种代理模式,但是并不是双亲委派模型,而是先尝试自己加载,加载不到再委托父类加载器加载,和双亲委派模型有所区别。 + +类加载机制的主要作用是实现类的动态加载和动态链接,提高程序的灵活性和可扩展性。 + +## 什么时候才会触发类的初始化 + +Java 虚拟机在初始化一个类时,会按照以下顺序主动执行类的初始化: + +1. 当创建类的实例时,会触发类的初始化。 +2. 当访问类的静态变量时,会触发类的初始化。 +3. 当调用类的静态方法时,会触发类的初始化。 +4. 当使用`java.lang.reflect`包的方法对类进行反射调用时,会触发类的初始化。 +5. 当初始化一个类时,如果发现其父类还没有初始化,则会先触发其父类的初始化。 + +类的初始化主要是执行类的构造方法,初始化类的静态变量和静态代码块。其中,main 方法所在类被初始化属于第一种情况。 + +## 讲讲 JVM 的类加载器 + +Java 虚拟机的类加载器主要分为以下几种: + +1. 启动类(引导类)加载器(Bootstrap ClassLoader):启动类加载器是 Java 虚拟机的内置类加载器,负责加载`JAVA_HOME/lib`目录下的核心类库,如`rt.jar`、`charsets.jar`等。 +2. 扩展类加载器(Extension ClassLoader):扩展类加载器是 Java 虚拟机的内置类加载器,负责加载`JAVA_HOME/lib/ext`目录下的扩展类库,如`junit.jar`、`mysql-connector-java.jar`等。 +3. 应用类加载器(Application ClassLoader):应用类加载器是 Java 虚拟机的内置类加载器,负责加载应用程序的类路径(classpath)下的类库,如`-classpath`或`-cp`参数指定的类库。 +4. 自定义类加载器(Custom ClassLoader):自定义类加载器是开发者自定义的类加载器,继承自`ClassLoader`类,可以实现自定义的类加载逻辑。 + +类加载器主要负责加载类的字节码文件,并生成`java.lang.Class`对象,类加载器之间存在父子关系,子类加载器会委托父类加载器加载类,只有在父类加载器加载不了类时,子类加载器才会加载类。 + +## 简单讲讲 jvm 调优 *(面试时尽可能说没做过,绝大部分学生不可能接触到 jvm 调优,会引发真实性问题)* + +Java 虚拟机的调优主要分为以下几个方面: + +1. 内存调优:主要是调整堆内存大小、新生代和老年代比例、永久代大小等,可以通过`-Xms`、`-Xmx`、`-Xmn`、`-XX:PermSize`、`-XX:MaxPermSize`等参数进行调优。 +2. 垃圾回收调优:主要是调整垃圾回收器的类型、堆内存分代比例、垃圾回收线程数等,可以通过`-XX:+UseSerialGC`、`-XX:+UseParallelGC`、`-XX:+UseConcMarkSweepGC`、`-XX:+UseG1GC`等参数进行调优。 +3. 线程调优:主要是调整线程池的大小、线程栈大小、线程优先级等,可以通过`-Xss`、`-Xmx`、`-Xms`、`-XX:ThreadStackSize`、`-XX:ThreadPriority`等参数进行调优。 +4. 类加载调优:主要是调整类加载器的加载路径、类加载器的委托关系等,可以通过`-classpath`、`-cp`等参数进行调优。 +5. JIT 调优:主要是调整即时编译器的编译级别、编译触发条件等,可以通过`-XX:+TieredCompilation`、`-XX:+PrintCompilation`、`-XX:CompileThreshold`等参数进行调优。 + +Java 虚拟机的调优主要是根据应用的特点和需求,调整虚拟机的参数,提高程序的性能和稳定性。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\237\272\347\241\200\346\225\260\346\215\256\347\261\273\345\236\213/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\237\272\347\241\200\346\225\260\346\215\256\347\261\273\345\236\213/index.md" new file mode 100644 index 0000000..1b09e4a --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\237\272\347\241\200\346\225\260\346\215\256\347\261\273\345\236\213/index.md" @@ -0,0 +1,72 @@ +**本模块适合 `Java后端研发(业务岗、架构岗)`、`Java安卓原生研发`等 Java 相关岗位的同学。** + +## Java 中的基本数据类型有哪些?它们的大小和取值范围是多少? + +Java 中的基本数据类型有 8 种,分别是`byte`、`short`、`int`、`long`、`float`、`double`、`char`、`boolean`。 + +- `byte`:1 字节,取值范围是`-128~127`。 +- `short`:2 字节,取值范围是`-32768~32767`。 +- `int`:4 字节,取值范围是`-2147483648~2147483647`。 +- `long`:8 字节,取值范围是`-9223372036854775808~9223372036854775807`。 +- `float`:4 字节,取值范围是`-3.4028235E38~3.4028235E38`。 +- `double`:8 字节,取值范园是`-1.7976931348623157E308~1.7976931348623157E308`。 +- `char`:2 字节,取值范围是`0~65535`。 +- `boolean`:1 字节,取值范围是`true`和`false`。 + +首先要理解二进制转十进制的计算方法,这是计算机中最基础的运算。 + +对于二进制数`1101`,我们可以以 i 作为下标,x 作为当前位置上的数,从零开始,从右往左依次递增编号。则每一位可以转换为`x*2^i`,然后将所有位的值相加即可得到十进制数,即`1*2^3+1*2^2+0*2^1+1*2^0=13`。 + +接下来我们可以得到记忆各类型位数的最简单的方法: + +一个字节就是 8 位,第一位如果是符号位,那么剩下的 7 位就是数值位,所以`byte`的取值范围是`-2^7~2^7-1`,`short`的取值范围是`-2^15~2^15-1`,`int`的取值范围是`-2^31~2^31-1`,`long`的取值范围是`-2^63~2^63-1`。 + +对于浮点数,采用`IEEE 754`标准,`float`是 32 位,包含 1 个符号位、8 个指数位和 23 个尾数位,`double`是 64 位,包含 1 个符号位、11 个指数位和 52 个尾数位。 + +对于`char`,采用 Unicode 编码,2 字节,取值范围是 0~2^16-1。 + +对于`boolean`,1 字节,取值范围是`true`和`false`。 + +## Java 中的基本数据类型和包装类有什么区别? + +Java 中的基本数据类型和包装类的区别主要有以下几点: + +1. 基本数据类型是 Java 语言的一部分,而包装类是 Java 类库中的类。 +2. 基本数据类型是值类型,包装类是引用类型。 +3. 基本数据类型在内存中占用的空间较小,包装类在内存中占用的空间较大。 +4. 基本数据类型的默认值是 0,包装类的默认值是 null。 +5. 基本数据类型和包装类之间可以自动装箱和拆箱。 + +## 什么是自动装箱和拆箱? + +自动装箱(Boxing)和拆箱(Unboxing)是 JDK 5 中引入的特性,用于基本数据类型和包装类之间的转换,在此之前只能使用`Object`进行装箱和拆箱。(目前C#等语言仍旧使用`object`进行装箱和拆箱,因此可以说 Java 的包装类设计解决了自动拆装箱问题) + +自动装箱是指将基本数据类型自动转换为包装类,比如`int`转换为`Integer`,`double`转换为`Double`,`boolean`转换为`Boolean`等。 + +自动拆箱是指将包装类自动转换为基本数据类型,比如`Integer`转换为`int`,`Double`转换为`double`,`Boolean`转换为`boolean`等。 + +自动装箱和拆箱可以简化代码,提高代码的可读性和可维护性。 + +## 判断题:`Integer a = 10, b = 10; System.out.println(a == b);` 输出为`true`。 + +正确。因为 Java 中对于`-128~127`之间的整数,会在常量池当中进行缓存,所以 a 和 b 指向的是同一个对象,超出这个范围就不会进行缓存,会创建新的对象。 + +## 讲讲常量池 + +常量池是 Java 虚拟机中的一块内存区域,用于存放字符串常量、基本数据类型常量和符号引用。常量池分为运行时常量池和类常量池。 + +运行时常量池是每个类的一部分,用于存放编译期生成的字面量和符号引用。类常量池是每个类的一部分,用于存放编译期生成的字面量和符号引用。 + +常量池的作用是减少内存的占用,提高性能。常量池中的常量是唯一的,可以减少内存的占用,提高性能。 + +常量池中的常量是不可变的,一旦创建就不能修改。常量池中的常量是共享的,可以被多个对象共享。 + +## 讲讲各基础类型的上下行转换规则 + +Java 中的基本数据类型之间的转换规则如下: + +1. `byte、short、char`之间可以相互转换,转换时会自动提升为`int`类型。 +2. `int`可以自动转换为`long、float、double`,但是不能自动转换为`byte、short、char`。 +3. `long`可以自动转换为`float、double`,但是不能自动转换为`byte、short、char、int`。 +4. `float`可以自动转换为`double`,但是不能自动转换为`byte、short、char、int、long`。 +5. `double`不能自动转换为其他基本数据类型。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\244\232\347\272\277\347\250\213\345\222\214\351\224\201/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\244\232\347\272\277\347\250\213\345\222\214\351\224\201/index.md" new file mode 100644 index 0000000..2cb083f --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\244\232\347\272\277\347\250\213\345\222\214\351\224\201/index.md" @@ -0,0 +1,304 @@ +**本模块适合 `Java后端研发(业务岗、架构岗)`等 Java 相关岗位的同学。非后端相关岗位的同学仅做了解即可** + +## 讲讲 Java 四种实现多线程的方法 + +Java 中实现多线程的方法有四种,分别是继承`Thread`类、实现`Runnable`接口、实现`Callable`接口和使用线程池。 + +1. 继承`Thread`类:继承`Thread`类,重写`run`方法,创建线程对象,调用`start`方法启动线程。 + +```java +public class MyThread extends Thread { + @Override + public void run() { + // 线程执行的代码 + } +} + +public class Main { + public static void main(String[] args) { + MyThread myThread = new MyThread(); + myThread.start(); + } +} +``` + +缺点是 Java 是单继承的,继承`Thread`类后无法继承其他类。 + +2. 实现`Runnable`接口:实现`Runnable`接口,重写`run`方法,创建`Thread`对象,将`Runnable`对象作为参数传递给`Thread`对象,调用`start`方法启动线程。 + +```java +public class MyRunnable implements Runnable { + @Override + public void run() { + // 线程执行的代码 + } +} + +public class Main { + public static void main(String[] args) { + MyRunnable myRunnable = new MyRunnable(); + Thread thread = new Thread(myRunnable); + thread.start(); + } +} +``` + +优点是可以继承其他类,实现接口。 + +3. 实现`Callable`接口:实现`Callable`接口,重写`call`方法,创建`FutureTask`对象,将`Callable`对象作为参数传递给`FutureTask`对象,创建`Thread`对象,将`FutureTask`对象作为参数传递给`Thread`对象,调用`start`方法启动线程。 + +```java +public class MyCallable implements Callable { + @Override + public Integer call() throws Exception { + // 线程执行的代码 + return 1; + } +} + +public class Main { + public static void main(String[] args) { + MyCallable myCallable = new MyCallable(); + FutureTask futureTask = new FutureTask<>(myCallable); + Thread thread = new Thread(futureTask); + thread.start(); + try { + Integer result = futureTask.get(); // 可以返回数据,实际上可以实现异步回调 + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } +} +``` + +4. 使用线程池:使用`ExecutorService`接口和`ThreadPoolExecutor`类,创建线程池,提交任务,执行任务。 + +```java +public class Main { + public static void main(String[] args) { + ExecutorService executorService = Executors.newFixedThreadPool(10); + executorService.execute(() -> { + // 线程执行的代码 + }); + executorService.shutdown(); + } +} +``` + +线程池本质其实是一种对象池,可以重复利用线程,减少线程创建和销毁的开销。 + +## 讲讲线程池的关键属性 + +线程池是一种特殊的对象池,用来管理线程的创建、销毁和复用,可以提高线程的利用率和性能。 + +线程池的关键属性如下: + +1. `corePoolSize`:核心线程数,线程池中保持存活的线程数量,即使线程处于空闲状态也不会被销毁。 +2. `maximumPoolSize`:最大线程数,线程池中允许的最大线程数量,当任务队列满了且核心线程数已满时,会创建新的线程。 +3. `keepAliveTime`:线程空闲时间,当线程空闲时间超过该值时,会被销毁。 +4. `unit`:时间单位,用来设置`keepAliveTime`的时间单位。 +5. `workQueue`:任务队列,用来存储等待执行的任务,有`SynchronousQueue`、`LinkedBlockingQueue`、`ArrayBlockingQueue`等实现类。 +6. `threadFactory`:线程工厂,用来创建新的线程。 +7. `handler`:拒绝策略,用来处理任务队列满了且线程池已满时的情况。 + +## 讲讲线程池的拒绝策略 + +线程池的拒绝策略是用来处理任务队列满了且线程池已满时的情况,有以下几种策略: + +1. `AbortPolicy`:默认策略,抛出`RejectedExecutionException`异常。 +2. `CallerRunsPolicy`:调用线程执行任务。 +3. `DiscardPolicy`:丢弃任务,不抛出异常。 +4. `DiscardOldestPolicy`:丢弃队列中最旧的任务,将新任务加入队列。(本质算法是 FIFO) +5. 自定义策略:实现`RejectedExecutionHandler`接口,自定义拒绝策略。 + +## 讲讲常见的线程池有哪些 + +Java 中常见的线程池有以下几种: + +1. `FixedThreadPool`:固定大小线程池,核心线程数和最大线程数相等,适用于执行长期的任务。 + +```java +ExecutorService executorService = Executors.newFixedThreadPool(10); + +public static ExecutorService newFixedThreadPool(int nThreads) { + return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); // 默认创建了一个链表阻塞队列 +} +``` + +2. `CachedThreadPool`:缓存线程池,核心线程数为 0,最大线程数为`Integer.MAX_VALUE`,适用于执行大量短期的任务。 + +```java +ExecutorService executorService = Executors.newCachedThreadPool(); + +public static ExecutorService newCachedThreadPool() { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue());// 默认创建了一个同步队列 +} +``` + +3. `SingleThreadPool`:单线程线程池,核心线程数和最大线程数为 1,适用于执行顺序执行的任务。 + +```java +ExecutorService executorService = Executors.newSingleThreadExecutor(); + +public static ExecutorService newSingleThreadExecutor() { + return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); +} +``` + +可以理解成会弱化为单线程串行执行,不释放锁,适用于顺序执行的任务。 + +4. `ScheduledThreadPool`:定时线程池,可以定时执行任务。 + +```java +ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10); + +public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { + return new ScheduledThreadPoolExecutor(corePoolSize); +} +``` + +5. `WorkStealingPool`:工作窃取线程池,适用于执行大量独立任务。(了解即可,是 JDK 8 新增的线程池) + +```java +ExecutorService executorService = Executors.newWorkStealingPool(); + +public static ExecutorService newWorkStealingPool() { + return new ForkJoinPool(Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); +} +``` + +## 总结下 Java 当中有哪几种锁的划分类型 + +从底层实现方面来讲,Java 中真正的锁实现只有隐式锁和显式锁两种。 + +1. 隐式锁:隐式锁是指 Java 中的`synchronized`关键字,是一种悲观锁,通过`monitorenter`和`monitorexit`指令来实现,是 JVM 层面的锁,对用户不可见。 + +如果修饰的是具体的对象,锁的是对象,如果修饰的是成员方法,锁的是当前对象`this`,如果修饰的是静态方法,锁的是当前类的`Class`对象。 + +例如: + +```java +public class MyObject { + // 修饰成员方法 + public synchronized void method() { + // 临界区 + } + + public void method2(Object lock) { + // 修饰对象 + synchronized (lock) { + // 临界区 + } + } + + // 修饰静态方法 + public static synchronized void method3() { + // 临界区 + } +} +``` + +2. 显式锁:显式锁是指 Java 中的`Lock`接口,以及各种实现类,如`ReentrantLock`、`ReadWriteLock`、`StampedLock`等,其中包括有悲观锁和乐观锁,如`ReentrantLock`、`ReadWriteLock`是悲观锁,`StampedLock`是乐观锁。 + +显式锁是一种更加灵活的锁,可以实现更多的功能,如可重入锁、读锁、写锁、公平锁、自旋锁、分段锁等。 + +例如: + +```java +public class MyObject { + private Lock lock = new ReentrantLock(); + + public void method() { + lock.lock(); + try { + // 临界区 + } finally { + lock.unlock(); + } + } +} +``` + + +实际上,Lock 的实现基本都是通过聚合了一个同步器 AQS(AbstractQueuedSynchronizer)来实现的,AQS 是一种基于 CLH 队列的锁实现,是一种非常高效的锁实现。其目的是为了保证锁的可重入性和线程安全性,底层算法必然是 FIFO 队列。 + +AQS 的主要使用方式是继承,通过继承 AQS,实现自己的同步器,然后通过`Lock`接口的实现类来使用。在使用过程中,对同步状态进行更改主要有如下几个方法: + +1. `protected final int getState()`:获取同步状态。 +2. `protected final void setState(int newState)`:设置同步状态。 +3. `protected final boolean compareAndSetState(int expect, int update)`:CAS 设置同步状态。 + +从特性方面来讲,分为乐观锁和悲观锁。 + +1. 乐观锁:乐观锁是一种乐观思想,认为读操作远远多于写操作,所以读操作不加锁,只在写操作时加锁,例如`CAS`操作。实际上业务中最常见的乐观锁设计反而是在数据库层面中,通过版本号或时间戳来实现。 +2. 悲观锁:悲观锁是一种悲观思想,认为写操作远远多于读操作,所以读操作和写操作都加锁,例如`synchronized`关键字和`ReentrantLock`类。 + +按照锁的顺序来讲,分为公平锁和非公平锁。 + +1. 公平锁:公平锁是指按照线程请求的顺序来获取锁,先到先得,不会产生饥饿现象。如`ReentrantLock`的构造方法可以传入`true`来创建公平锁。 +2. 非公平锁:非公平锁是指不按照线程请求的顺序来获取锁,可能会产生饥饿现象。如`syncronized`关键字就是无法指定公平性的。 + +按照持有性来讲,分为独占锁和共享锁。 + +1. 独占锁(排他锁):独占锁是指在同一时刻只能有一个线程持有锁,其他线程必须等待。如`synchronized`关键字和`ReentrantLock`类。 +2. 共享锁:共享锁是指在同一时刻可以有多个线程持有锁,适用于读多写少的场景。如`ReadWriteLock`接口和`ReentrantReadWriteLock`类的读锁(写锁是独占锁)。 + +但要注意,独占锁和共享锁更多时候是一种广义的概念,具体实现还是互斥锁和读写锁。 + +## 简单对比下显式锁和隐式锁,以及其优缺点 + +显式锁和隐式锁是 Java 中的两种锁实现,各有优缺点。 + +1. 显式锁(`Lock`接口): + - 优点:显式锁是一种更加灵活的锁,可以实现更多的功能,如可重入锁、读锁、写锁、公平锁、自旋锁、分段锁等。 + - 缺点:显式锁需要手动加锁和解锁,容易出现死锁、忘记解锁等问题,使用复杂,代码量较多。 + +2. 隐式锁(`synchronized`关键字): + - 优点:隐式锁是一种简单易用的锁,不需要手动加锁和解锁,JVM 会自动帮助加锁和解锁,使用方便,没有内存泄漏的风险。 + - 缺点:隐式锁功能较为简单,只能实现基本的加锁和解锁,不支持可重入锁、读锁、写锁、公平锁等功能,不可以跨方法调用,不可以设置等待时间不能监控锁状态。 + +总的来说,显式锁更加灵活,功能更加丰寶,但使用复杂,代码量较多;隐式锁简单易用,但功能较为简单,只能实现基本的加锁和解锁。 + +## 讲讲什么是`CAS`操作 + +`CAS`(Compare And Swap)操作是一种乐观锁技术,是一种无锁算法,用来解决多线程并发访问共享变量的问题。需要注意到是,这不是一个 Java 级别的锁,而是一种硬件级别的原子操作。 + +`CAS`操作的原理是先比较当前内存值和期望值是否相等,如果相等则更新为新值,否则不更新。 + +在 Java 的`java.util.concurrent.atomic`包中提供了一些原子类,如`AtomicInteger`、`AtomicLong`、`AtomicReference`等,这些类底层就是通过`CAS`操作来保证线程安全的。 + +`CAS`操作的步骤如下: + +1. 读取内存值。 +2. 比较内存值和期望值是否相等。 +3. 如果相等,则更新内存值为新值。 +4. 如果不相等,则不更新内存值。 + +`CAS`操作是一种乐观锁技有,不需要加锁,可以提高并发性能。但是`CAS`操作有三大问题: + +1. ABA 问题:如果一个值原来是 A,后来被改成了 B,又被改回了 A,那么使用`CAS`进行检查时会发现没有变化,但实际上已经发生了变化。 +2. 循环时间长开销大:如果一直检查到变量没有发生变化,会一直循环检查,会消耗大量的 CPU 时间。 +3. 只能保证一个共享变量的原子操作:`CAS`只能保证一个共享变量的原子操作,对于多个共享变量操作,需要加锁保证原子性。 + +## 讲讲`volatile`关键字的作用 + +很多人说`volatile`关键字是 Java 中的轻量级锁,这个说法是不准确的。`volatile`关键字是一种内存屏障,用来保证变量的可见性和有序性,不是一种锁。 + +`volatile`关键字是 Java 中的修饰符,用来修饰变量,保证变量的可见性和有序性。 + +`volatile`关键字的作用是为了解决多线程并发访问共享变量的问题,保证变量的可见性和有序性。 + +`volatile`关键字的特点如下: + +1. 可见性:`volatile`关键字保证变量的修改对其他线程是可见的,一个线程修改了`volatile`变量的值,其他线程可以立即看到修改后的值。 +2. 有序性:`volatile`关键字保证变量的读写操作是有序的,一个线程写入了`volatile`变量的值,其他线程可以立即看到写入的值。 + +`volatile`关键字的使用场景是在多线程并发访问共享变量的情况下,保证变量的可见性和有序性。 + +其具体实现涉及到了 Java 内存模型(JMM)的相关知识。JMM 中规定,所有共享变量都存储在主内存中,每个线程都有自己的工作内存,线程对共享变量的操作都是在工作内存中进行的,线程之间的共享变量不可见。 + +为了保障可见性,除了使用`synchronized`关键字外,还可以使用`volatile`关键字。`volatile`关键字修饰的变量在被进行读写操作时,会直接越过工作内存,在主内存中进行操作,从而保证了变量的可见性。 + +但设计同一块主内存操作时,常常需要确保缓存数据一致性问题,此时遵循了各处理器的缓存一致性协议,如 MSI、MESI、MOSI 等。 + +其中最重要的一点就是禁止指令重排序。一般情况下,为了提高性能,编译器和处理器会对指令进行重排序,但是在多线程环境下,这种重排序可能会导致线程安全问题。`volatile`关键字可以禁止指令重排序,保证了变量的有序性。我们用双重校验锁来实现单例模式时,就需要用到`volatile`关键字来保证线程安全。 diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\255\227\347\254\246\344\270\262/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\255\227\347\254\246\344\270\262/index.md" new file mode 100644 index 0000000..23f7575 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\255\227\347\254\246\344\270\262/index.md" @@ -0,0 +1,87 @@ +**本模块适合 `Java后端研发(业务岗、架构岗)`、`Java安卓原生研发`等 Java 相关岗位的同学。** + +## `String a = "abc"; String b = "abc";` 会创建几个对象? + +`String a = "abc"; String b = "abc";` 会创建一个对象,因为 Java 中的字符串常量是存储在常量池中的,当创建一个字符串常量时,会先在常量池中查找是否存在相同的字符串常量,如果存在,则直接返回引用,如果不存在,则创建一个新的字符串常量。 + +在这个例子中,`"abc"` 是一个字符串常量。第一次执行 `String a = "abc";` 时,常量池中没有 `"abc"`,所以会创建一个新的字符串对象并将其放入常量池中。第二次执行 `String b = "abc";` 时,常量池中已经有了 `"abc"`,所以 `b` 会直接引用常量池中的这个对象,而不会创建新的对象。因此,`a` 和 `b` 都引用同一个字符串对象。 + +## 讲讲`String`和`StringBuffer`、`StringBuilder`的区别 + +`String`、`StringBuffer`和`StringBuilder`都是 Java 中的字符串类,它们之间的区别如下: + +1. `String`:`String`是不可变的字符串类,一旦创建就不能被修改,每次修改都会创建一个新的字符串对象,效率较低。 +2. `StringBuffer`:`StringBuffer`是可变的字符串类,可以修改字符串的内容,是线程安全的,效率较低。 +3. `StringBuilder`:`StringBuilder`是可变的字符串类,可以修改字符串的内容,是非线程安全的,效率较高。 + +`StringBuffer`和`StringBuilder`的区别是`StringBuffer`是线程安全的,`StringBuilder`是非线程安全的,所以在单线程环境下,推荐使用`StringBuilder`,在多线程环境下,推荐使用`StringBuffer`。 + +## 讲讲`String`的常用方法 API + +`String`类是 Java 中的字符串类,提供了一些常用的方法 API,如下: + +1. `public int length()`:返回字符串的长度。 +2. `public char charAt(int index)`:返回字符串中指定位置的字符。 +3. `public int indexOf(String str)`:返回字符串中指定子串的位置。 +4. `public String substring(int beginIndex)`:返回字符串中从指定位置开始到末尾的子串。 +5. `public String substring(int beginIndex, int endIndex)`:返回字符串中从指定位置开始到指定位置结束的子串。 +6. `public String replace(char oldChar, char newChar)`:返回字符串中指定字符替换后的字符串。 +7. `public String replace(CharSequence target, CharSequence replacement)`:返回字符串中指定子串替换后的字符串。 +8. `public String toUpperCase()`:返回字符串中所有字符转换为大写的字符串。 +9. `public String toLowerCase()`:返回字符串中所有字符转换为小写的字符串。 +10. `public String trim()`:返回字符串中去除前后空格的字符串。 +11. `public boolean equals(Object obj)`:判断两个字符串是否相等。 +12. `public boolean equalsIgnoreCase(String anotherString)`:忽略大小写判断两个字符串是否相等。 +13. `public boolean startsWith(String prefix)`:判断字符串是否以指定前缀开头。 +14. `public boolean endsWith(String suffix)`:判断字符串是否以指定后缀结尾。 +15. `public boolean contains(CharSequence s)`:判断字符串是否包含指定子串。 +16. `public String[] split(String regex)`:返回字符串根据指定分隔符分割后的字符串数组。 +17. `public byte[] getBytes()`:返回字符串的字节数组。 +18. `public char[] toCharArray()`:返回字符串的字符数组。 +19. `public int compareTo(String anotherString)`:比较字符串的大小。 +20. `public int compareToIgnoreCase(String str)`:忽略大小写比较字符串的大小。 +21. `public boolean isEmpty()`:判断字符串是否为空。 + +## 讲讲`StringBuffer`的常用方法 API + +`StringBuffer`类是 Java 中的可变字符串类,提供了一些常用的方法 API,如下: + +1. `public synchronized int length()`:返回字符串的长度。 +2. `public synchronized int capacity()`:返回字符串的容量。 +3. `public synchronized char charAt(int index)`:返回字符串中指定位置的字符。 +4. `public synchronized int indexOf(String str)`:返回字符串中指定子串的位置。 +5. `public synchronized StringBuffer append(String str)`:追加字符串。 +6. `public synchronized StringBuffer insert(int offset, String str)`:插入字符串。 +7. `public synchronized StringBuffer delete(int start, int end)`:删除字符串。 +8. `public synchronized StringBuffer replace(int start, int end, String str)`:替换字符串。 +9. `public synchronized StringBuffer reverse()`:反转字符串。 +10. `public synchronized String substring(int start)`:返回字符串中从指定位置开始到末尾的子串。 +11. `public synchronized String substring(int start, int end)`:返回字符串中从指定位置开始到指定位置结束的子串。 +12. `public synchronized String toString()`:返回字符串的字符串表示。 +13. `public synchronized void setCharAt(int index, char ch)`:设置字符串中指定位置的字符。 +14. `public synchronized void setLength(int newLength)`:设置字符串的长度。 +15. `public synchronized void ensureCapacity(int minimumCapacity)`:确保字符串的容量。 +16. `public synchronized void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)`:将字符串中的字符复制到目标数组中。 +17. `public synchronized void trimToSize()`:将字符串的容量调整为字符串的长度。 + +## 讲讲`StringBuilder`的常用方法 API + +`StringBuilder`类是 Java 中的可变字符串类,提供了一些常用的方法 API,如下: + +1. `public int length()`:返回字符串的长度。 +2. `public int capacity()`:返回字符串的容量。 +3. `public char charAt(int index)`:返回字符串中指定位置的字符。 +4. `public int indexOf(String str)`:返回字符串中指定子串的位置。 +5. `public StringBuilder append(String str)`:追加字符串。 +6. `public StringBuilder insert(int offset, String str)`:插入字符串。 +7. `public StringBuilder delete(int start, int end)`:删除字符串。 +8. `public StringBuilder replace(int start, int end, String str)`:替换字符串。 +9. `public StringBuilder reverse()`:反转字符串。 +10. `public String substring(int start)`:返回字符串中从指定位置开始到末尾的子串。 +11. `public String substring(int start, int end)`:返回字符串中从指定位置开始到指定位置结束的子串。 +12. `public String toString()`:返回字符串的字符串表示。 +13. `public void setCharAt(int index, char ch)`:设置字符串中指定位置的字符。 +14. `public void setLength(int newLength)`:设置字符串的长度。 +15. `public void ensureCapacity(int minimumCapacity)`:确保字符串的容量。 +16. `public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)`:将字符串中的字符复制到目标数组中。 +17. `public void trimToSize()`:将字符串的容量调整为字符串的长度。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\256\271\345\231\250\347\261\273/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\256\271\345\231\250\347\261\273/index.md" new file mode 100644 index 0000000..5f5b913 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\256\271\345\231\250\347\261\273/index.md" @@ -0,0 +1,268 @@ +**本模块适合 `Java后端研发(业务岗、架构岗)`、`Java安卓原生研发`等 Java 相关岗位的同学。但线程安全相关内容非后端相关岗位的同学仅做了解即可** + +## 讲讲 List 接口类常用 API + +`List`接口是 Java 中的有序集合类,继承自`Collection`接口,提供了一些常用的方法 API,如下: + +1. `public boolean add(E e)`:向列表中添加元素。 +2. `public void add(int index, E element)`:在列表中指定位置添加元素。 +3. `public boolean addAll(Collection c)`:向列表中添加集合。 +4. `public boolean addAll(int index, Collection c)`:在列表中指定位置添加集合。 +5. `public void clear()`:清空列表。 +6. `public boolean contains(Object o)`:判断列表中是否包含指定元素。 +7. `public boolean containsAll(Collection c)`:判断列表中是否包含指定集合。 +8. `public boolean equals(Object o)`:判断列表是否相等。 +9. `public E get(int index)`:获取列表中指定位置的元素。 +10. `public int hashCode()`:返回列表的哈希码值。 +11. `public int indexOf(Object o)`:返回列表中指定元素的位置。 +12. `public boolean isEmpty()`:判断列表是否为空。 +13. `public Iterator iterator()`:返回列表的迭代器。 +14. `public int lastIndexOf(Object o)`:返回列表中指定元素的最后一个位置。 +15. `public ListIterator listIterator()`:返回列表的列表迭代器。 +16. `public ListIterator listIterator(int index)`:返回列表的列表迭代器。 +17. `public E remove(int index)`:移除列表中指定位置的元素。 +18. `public boolean remove(Object o)`:移除列表中指定元素。 +19. `public boolean removeAll(Collection c)`:移除列表中指定集合。 +20. `public boolean retainAll(Collection c)`:保留列表中指定集合。 +21. `public E set(int index, E element)`:设置列表中指定位置的元素。 + +## 讲讲 Set 接口类常用 API + +`Set`接口是 Java 中的无序集合类,继承自`Collection`接口,提供了一些常用的方法 API,如下: + +1. `public boolean add(E e)`:向集合中添加元素。 +2. `public boolean addAll(Collection c)`:向集合中添加集合。 +3. `public void clear()`:清空集合。 +4. `public boolean contains(Object o)`:判断集合中是否包含指定元素。 +5. `public boolean containsAll(Collection c)`:判断集合中是否包含指定集合。 +6. `public boolean equals(Object o)`:判断集合是否相等。 +7. `public int hashCode()`:返回集合的哈希码值。 +8. `public boolean isEmpty()`:判断集合是否为空。 +9. `public Iterator iterator()`:返回集合的迭代器。 +10. `public boolean remove(Object o)`:移除集合中指定元素。 +11. `public boolean removeAll(Collection c)`:移除集合中指定集合。 +12. `public boolean retainAll(Collection c)`:保留集合中指定集合。 + +## 讲讲 Map 接口类常用 API + +`Map`接口是 Java 中的映射类,提供了一些常用的方法 API,如下: + +1. `public void clear()`:清空映射。 +2. `public boolean containsKey(Object key)`:判断映射中是否包含指定键。 +3. `public boolean containsValue(Object value)`:判断映射中是否包含指定值。 +4. `public Set> entrySet()`:返回映射的键值对集合。 +5. `public boolean equals(Object o)`:判断映射是否相等。 +6. `public V get(Object key)`:获取映射中指定键的值。 +7. `public int hashCode()`:返回映射的哈希码值。 +8. `public boolean isEmpty()`:判断映射是否为空。 +9. `public Set keySet()`:返回映射的键集合。 +10. `public V put(K key, V value)`:向映射中添加键值对。 +11. `public void putAll(Map m)`:向映射中添加映射。 +12. `public V remove(Object key)`:移除映射中指定键的值。 +13. `public int size()`:返回映射的大小。 +14. `public Collection values()`:返回映射的值集合。 + +## 讲讲`Deque`接口类常用 API + +`Deque`接口是 Java 中的双端队列类,继承自`Queue`接口,提供了一些常用的方法 API,如下: + +1. `public void addFirst(E e)`:向队列头部添加元素。 +2. `public void addLast(E e)`:向队列尾部添加元素。 +3. `public boolean offerFirst(E e)`:向队列头部添加元素。 +4. `public boolean offerLast(E e)`:向队列尾部添加元素。 +5. `public E removeFirst()`:移除队列头部的元素。 +6. `public E removeLast()`:移除队列尾部的元素。 +7. `public E pollFirst()`:移除队列头部的元素。 +8. `public E pollLast()`:移除队列尾部的元素。 +9. `public E getFirst()`:获取队列头部的元素。 +10. `public E getLast()`:获取队列尾部的元素。 +11. `public E peekFirst()`:获取队列头部的元素。 +12. `public E peekLast()`:获取队列尾部的元素。 + +## 讲讲一个元素存储进`HashMap`时经历了哪些步骤? + +一个元素存储进`HashMap`时经历了以下步骤: + +1. 计算键的哈希码值:调用键的`hashCode`方法计算键的哈希码值。如果用户没有重写`hashCode`方法,则默认返回对象的内存地址。 +2. 计算哈希值:为了减少哈希冲突,调用扰动函数对哈希码值进行扰动,然后计算哈希值。具体是返回`(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)`。 +3. 计算索引:通过哈希值和数组长度计算索引,具体是返回`hash & (length - 1)`。 +4. 存储元素:将键值对存储到索引位置,如果索引位置已经有元素,则存储为链表,当链表长度大于 8 时,链表转换为红黑树,当红黑树节点小于 6 时,红黑树转换为链表。 + +## 讲讲`Collection`和`Map`的区别 + +`Collection`和`Map`是 Java 中的容器类,它们之间的区别如下: + +1. `Collection`:`Collection`是集合类,用来存储一组对象,是一个接口,继承自`Iterable`接口,包括`List`、`Set`和`Queue`等子接口。 +2. `Map`:`Map`是映射类,用来存储键值对,是一个接口,包括`HashMap`、`TreeMap`和`LinkedHashMap`等实现类。 + +`Collection`和`Map`的区别是`Collection`是用来存储一组对象的,`Map`是用来存储键值对的。 + +## 讲讲`List`和`Set`的区别 + +`List`和`Set`是 Java 中的集合类,它们之间的区别如下: + +1. `List`:`List`是有序的集合类,可以存储重复的元素,可以通过索引访问元素,包括`ArrayList`、`LinkedList`和`Vector`等实现类。 +2. `Set`:`Set`是无序的集合类,不可以存储重复的元素,不可以通过索引访问元素,包括`HashSet`、`TreeSet`和`LinkedHashSet`等实现类。 + +`List`和`Set`的区别是`List`是有序的,可以存储重复的元素,可以通过索引访问元素,`Set`是无序的,不可以存储重复的元素,不可以通过索引访问元素。 + +## 讲讲`ArrayList`和`LinkedList`的区别 + +`ArrayList`和`LinkedList`是 Java 中的集合类,它们之间的区别如下: + +1. `ArrayList`:`ArrayList`是基于数组实现的集合类,支持随机访问,插入和删除元素效率较低,适合查询操作。创建时初始化一个长度为 10 的数组,当数组容量不足时会进行扩容,通常是将容量翻倍。(要和其他的集合类区分开,很多默认长度是 2 的幂次方,这样可以通过位运算来代替取模运算,提高效率) +2. `LinkedList`:`LinkedList`是基于链表实现的集合类,支持插入和删除元素效率较高,适合增删操作。 + +可以说,一般情况下,`ArrayList`从头插入和删除元素效率较低,因为需要移动大量元素。 + +但从中间位置开始,由于`LinkedList`需要遍历到中间位置,所以`ArrayList`效率较高。 + +对于结尾位置,`ArrayList`和`LinkedList`效率都很高。 + +具体用表格来表示: + +| 操作 | `ArrayList` | `LinkedList` | +| -------- | ----------- | ------------ | +| 头插入 | 极慢 | 极快 | +| 中间插入 | 较慢 | 极慢 | +| 尾插入 | 极快 | 极快 | +| 头删除 | 极慢 | 极快 | +| 中间删除 | 较慢 | 极慢 | +| 尾删除 | 极快 | 极快 | +| 查询 | 快 | 慢 | + +## 讲讲为什么栈推荐用`Deque`而不是`Stack` + +`Stack`是 Java 中的栈类,继承自`Vector`类,但是`Stack`是线程安全的,效率较低,不推荐使用。 + +`Deque`是 Java 中的双端队列类,继承自`Queue`接口,可以实现栈的功能,是线程不安全的,效率较高,推荐使用。 + +`Deque`是一个双端队列,可以实现栈的功能,包括`ArrayDeque`和`LinkedList`等实现类。 + +## 讲讲`HashMap`和`Hashtable`的区别 + +`HashMap`和`Hashtable`是 Java 中的映射类,它们之间的区别如下: + +1. `HashMap`:`HashMap`是非线程安全的映射类,可以存储`null`键和`null`值,效率较高,是`Hashtable`的替代品。 +2. `Hashtable`:`Hashtable`是线程安全的映射类,不可以存储`null`键和`null`值,效率较低,不推荐使用。 + +`HashMap`和`Hashtable`的区别是`HashMap`是非线程安全的,可以存储`null`键和`null`值,效率较高,`Hashtable`是线程安全的,不可以存储`null`键和`null`值,效率较低。 + +## 为什么`HashMap`线程不安全? + +`HashMap`是非线程安全的,因为`HashMap`的`put`方法不是原子操作,当多个线程同时调用`put`方法时,可能会导致数据丢失或覆盖。 + +`HashMap`的`put`方法是通过计算键的哈希码值和散列函数来确定键值对的位置,如果两个键的哈希码值相同,会发生哈希冲突,导致数据丢失或覆盖。 + +此外,在 JDK 7 当中,`HashMap`的`get`操作可能因为`resize`操作采用的是头插法而导致链表逆序,从而导致死循环。 + +为了解决`HashMap`的线程安全问题,可以使用`ConcurrentHashMap`类,它是线程安全的映射类,通过分段锁和 CAS 操作来保证线程安全。 + +## 讲讲 JDK 7 和 8 中`HashMap`底层实现的区别 + +在 JDK 7 中,`HashMap`的底层实现是数组 + 链表(链地址法),初始化时创建一个长度为 16 的数组。 + +插入元素时,`HashMap`会根据键的哈希值计算出数组索引,如果该索引位置已经有元素,则将新元素插入到链表的头部。 + +当`HashMap`中的元素数量超过阈值(容量 * 负载因子,负载因子默认是 0.75)时,会进行扩容,通常是将容量翻倍。扩容时需要重新计算所有元素的哈希值并重新分配到新的数组中。扩容算法采用的是头插法,多线程情况下可能会导致链表逆序,造成死循环。 + +在 JDK 8 中,`HashMap`默认仍然使用数组 + 链表的方式,但是当链表长度超过 8 时,会将链表转换为红黑树,以提高查询效率。初始化与 JDK 7 一致。 + +插入元素时,`HashMap`会根据键的哈希值计算出数组索引。如果该索引位置已经有元素且链表长度超过阈值,则将链表转换为红黑树。 + +扩容条件与 JDK 7 一致,但是在扩容时,红黑树结构不会变化,只会重新计算哈希值并重新分配到新的数组中。算法采用的是尾插法,不会造成链表逆序。(本质是红黑树带来的优化) + +## 详细讲讲为什么头插法在多线程状态下会造成死循环 + +假设现在有线程 1 和线程 2,目前桶数为 2 的哈希表中,索引为 1 的位置有一个链表,其中元素 A。 + +现在线程 1 插入 B 元素,紧接在 A 元素后面,此时触发扩容机制,`resize`方法调用了`transfer`方法,其中有`Entry[] src`变量接收了原哈希表。在循环遍历中,此时用了一个`Entry e`指针指向了`src[1]`也就是索引 1 的位置,但时间片已经用完,线程 1 挂起,注意这里并没有把`src[1]`引用释放掉。 + +此时,线程 2 插入 C 元素,紧接在 B 元素后面,继续触发扩容机制,这里线程 2 就直接把扩容流程做完了。因为是头插法,所以会导致逆序。 + +我们假设 A 和 B 经过哈希计算后会需要放到新索引 3 的位置,C 还是在新索引 1 的位置。此时新哈希表状态如下: + +```markdown +新索引 0: null +新索引 1: C +新索引 2: null +新索引 3: B -> A +``` + +此时,线程 1 被唤醒,继续执行扩容操作,此时操作的新表是线程 2 刚刚扩容好的哈希表。接下来重点关注这几个步骤,这几个步骤是反复执行的: + +```java +// 1. 将当前节点的 next 指向头节点,然后将当前节点设置为头节点 +Entry next = e.next; + +// 2. 将当前节点指针指向下一个节点 +int i = indexFor(e.hash, newCapacity); + +// 3. 将当前节点的 next 指向头节点,然后将当前节点设置为头节点 +e.next = newTable[i]; +newTable[i] = e; + +// 4. 将当前节点指针指向下一个节点 +e = next; +``` + +A 线程中,原本的 e 指针指向 A,但在线程 2 扩容之后,A 位于尾节点。 + +在执行`e.next = newTable[i];`时,A 的 next 指向了 B,这时候就形成了一个环。执行完这串代码后哈希表状态如下: + +```markdown +新索引 3: A -> B -> A -> ... +``` + +这样就形成了一个环,导致了死循环。 + +## 详细讲讲`ConcurrentHashMap`的实现原理,为什么它是线程安全的?再讲讲 JDK 8 中的实现比 JDK 7 中的实现好在哪里? + +`ConcurrentHashMap`是一个线程安全的映射类,通过分段锁和 CAS 操作来保证线程安全。 + +在 JDK 7 中,其底层实现利用了分段原理。`ConcurrentHashMap`底层结构是一个`Segment`数组,而不是`Object`数组。 + +`Segment`继承了`ReentrantLock`,每个`Segment`就是一个小的`HashMap`。其中有一个`volatile`修饰的`HashEntry`数组,`HashEntry`是`ConcurrentHashMap`的内部类,用来存储键值对。此外,还维护了一个`volatile`修饰的`count`变量,用来记录`Segment`中的元素个数。 + +通过这种设计,就可以在需要进行并发写操作时(读操作不上锁),只锁定对应的`Segment`,而不是整个`ConcurrentHashMap`,从而提高了并发性能。但扩容时需要锁定所有的`Segment`,会导致长时间的全表锁定,降低了并发性能。 + +在 JDK 8 中,`ConcurrentHashMap`的底层实现采用了`CAS`操作和`synchronized`关键字来保证线程安全。 + +`ConcurrentHashMap`底层此时不再使用`Segment`数组,而是使用了`Node`数组,每个`Node`是一个链表节点,用来存储键值对,当链表长度超过 8 时,会将链表转换为红黑树。 + +在读操作时,采用`volatile`修饰的`Node`数组来保证可见性,再使用`CAS`操作来保证原子性,不需要加锁。写操作则在`CAS`操作基础上使用`synchronized`关键字来保证线程安全。 + +在扩容时,使用分段迁移避免长时间的全表锁定,提高了并发性能。 + +总的来说,JDK 8 通过细化锁的粒度,更换数据结构并修改扩容机制,提高了并发性能。 + +## 讲讲你还认识哪些线程安全的集合类 + +Java 中的线程安全的集合类有`Vector`、`HashTable`、`Collections.synchronizedList`、`Collections.synchronizedSet`、`Collections.synchronizedMap`、`ConcurrentHashMap`、`CopyOnWriteArrayList`、`CopyOnWriteArraySet`等。 + +1. `Vector`:`Vector`是线程安全的动态数组类,是`ArrayList`的线程安全版本。 +2. `HashTable`:`HashTable`是线程安全的映射类,是`HashMap`的线程安全版本。 +3. `Collections.synchronizedList`:`Collections.synchronizedList`是线程安全的列表类,是`ArrayList`的线程安全版本。 +4. `Collections.synchronizedSet`:`Collections.synchronizedSet`是线程安全的集合类,是`HashSet`的线程安全版本。 +5. `Collections.synchronizedMap`:`Collections.synchronizedMap`是线程安全的映射类,是`HashMap`的线程安全版本。 +6. `ConcurrentHashMap`:`ConcurrentHashMap`是线程安全的映射类,通过分段锁和`CAS`操作来保证线程安全。 +7. `CopyOnWriteArrayList`:`CopyOnWriteArrayList`是线程安全的动态数组类,通过写时复制机制来保证线程安全。 +8. `CopyOnWriteArraySet`:`CopyOnWriteArraySet`是线程安全的集合类,通过写时复制机制来保证线程安全。 +9. `ConcurrentLinkedQueue`:`ConcurrentLinkedQueue`是线程安全的队列类,通过`CAS`操作来保证线程安全。 +10. `ConcurrentLinkedDeque`:`ConcurrentLinkedDeque`是线程安全的双端队列类,通过`CAS`操作来保证线程安全。 + +其中大部分的线程安全集合类都位于`java.util.concurrent`包中,通过分段锁、`CAS`操作、写时复制机制等技术来保证线程安全。 + +## Java 提供了这么多线程安全集合类,为什么平时做 SpringBoot 项目中却很难用上? + +Spring 框架本身提供了许多线程安全的机制和工具,例如依赖注入、事务管理等,开发者通常不需要直接处理线程安全问题。Spring 的各种组件(如@Service、@Repository 等)默认是单例的,Spring 会确保它们的线程安全。 + +在 Spring Boot 项目中,业务逻辑通常被分离到不同的服务层和数据访问层中,减少了共享状态的使用。通过合理的设计,避免了多个线程同时访问和修改同一个数据结构的情况。 + +Spring Boot 项目通常使用数据库来持久化数据,数据库本身提供了事务管理和并发控制。通过使用 Spring 的事务管理(@Transactional),可以确保数据的一致性和隔离性,减少了对线程安全集合类的需求。 + +Spring Boot 项目通常采用无状态设计,特别是在 Web 应用中,每个请求都是独立的,不会共享状态。无状态设计减少了并发访问共享数据的需求,从而减少了对线程安全集合类的使用。 + +在需要高并发访问的场景下,Spring Boot 项目通常会使用缓存(如 Redis)和消息队列(如 RabbitMQ、Kafka)来处理。这些工具本身提供了线程安全和高并发支持,开发者不需要自己实现线程安全的集合类。 + +Java 提供了许多并发工具类(如 ExecutorService、CountDownLatch、Semaphore 等),这些工具类可以帮助管理并发任务,而不需要直接使用线程安全的集合类。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\270\270\347\224\250\345\205\263\351\224\256\345\255\227/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\270\270\347\224\250\345\205\263\351\224\256\345\255\227/index.md" new file mode 100644 index 0000000..2f2f9e4 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\345\270\270\347\224\250\345\205\263\351\224\256\345\255\227/index.md" @@ -0,0 +1,176 @@ +**本模块适合 `Java后端研发(业务岗、架构岗)`、`Java安卓原生研发`等 Java 相关岗位的同学。** + +## 讲讲常见的访问修饰关键字 + +Java 中的访问修饰符有四种,分别是`private`、`default`、`protected`和`public`。 + +1. `private`:私有的,只能在本类中访问,其他类无法访问。 +2. `default`:默认的,只能在本类、同包类中访问,其他类无法访问。 +3. `protected`:受保护的,只能在本类、同包类和子类中访问,其他类无法访问。 +4. `public`:公共的,可以在任何地方访问。 + +## 讲讲`abstract`、`interface`关键字的作用和区别 + +`abstract`和`interface`关键字都是用来定义抽象类和接口的。此外,`abstract`还可以用来定义抽象方法。 + +1. `abstract`:抽象类是一种不能被实例化的类,只能被继承,可以包含抽象方法和非抽象方法,抽象方法没有方法体,必须在子类中实现。 +2. `interface`:接口是一种不能被实例化的类,只能被实现,可以包含抽象方法和常量,抽象方法没有方法体,必须在实现类中实现。 + +区别如下: + +1. 抽象类可以包含成员变量和非抽象方法,接口只能包含常量和抽象方法。 +2. 一个类只能继承一个抽象类,但是可以实现多个接口。 +3. 抽象类可以有构造方法,接口不能有构造方法。 +4. 抽象类可以有访问修饰符,接口的成员变量和方法默认是`public static final`和`public abstract`。 +5. 抽象类是一种模板设计模式,接口是一种规范设计模式。 + +Java 8 中引入了默认方法和静态方法,接口中可以包含默认方法和静态方法。 + +默认方法是指接口中可以包含有方法体的方法,实现类可以不实现默认方法,直接继承接口中的默认方法。静态方法是指接口中可以包含静态方法,实现类不能继承接口中的静态方法,要使用接口中的静态方法,需要通过接口名调用。 + +```java +public class Solution { + static interface A { + default void test() { + System.out.println("test"); + } + static void test1() { + System.out.println("test1"); + } + } + + static class B implements A { + } + + static class C implements A { + @Override + public void test() { + System.out.println("test in C"); + } + } + + public static void main(String[] args) { + B b = new B(); + b.test(); // test + A.test1(); // test1 + + C c = new C(); + c.test(); // test in C + } +} +``` + +接口中的方法默认是`public abstract`,默认方法是`public default`,静态方法是`public static`,所以不需要添加额外的访问修饰符或`abstract`关键字,加了也没错。 + +接口还可以拥有变量,但是变量默认是`public static final`,所以不需要添加额外的访问修饰符,也不可以修改。 + +## 讲讲`default`关键字的作用 + +`default`关键字是 Java 8 中引入的新特性,用来定义接口中的默认方法。默认方法是指接口中可以包含有方法体的方法,实现类可以不实现默认方法,直接继承接口中的默认方法。 + +默认方法的作用是为了解决接口的升级问题,当接口中需要添加新的方法时,实现类不需要修改代码,直接继承接口中的默认方法即可。 + +默认方法的定义格式如下: + +```java +public interface Test { + default void test() { + System.out.println("test"); + } +} +``` + +## 讲讲`final`关键字的作用 + +`final`关键字是 Java 中的修饰符,可以用来修饰类、方法和变量。 + +1. 修饰类:`final`修饰的类不能被继承,是最终的类。 +2. 修饰方法:`final`修饰的方法不能被重写,是最终的方法。 +3. 修饰变量:`final`修饰的变量是常量,只能被赋值一次,是最终的变量。 + +`final`关键字的作用是为了保护类、方法和变量,防止被继承、重写和修改。 + +## 讲讲`static`关键字的作用 + +`static`关键字是 Java 中的修饰符,可以用来修饰类、方法和变量。 + +1. 修饰类:`static`修饰的类是静态内部类,可以直接通过类名访问,不需要实例化。 +2. 修饰方法:`static`修饰的方法是静态方法,可以直接通过类名访问,不需要实例化。 +3. 修饰变量:`static`修饰的变量是静态变量,是类变量,所有实例共享,可以直接通过类名访问。 + +`static`关键字的作用是为了实现代码的复用和提高代码的性能,静态方法和静态变量是类级别的,不依赖于实例,可以直接通过类名访问。 + +## 讲讲`this`和`super`关键字的作用 + +`this`和`super`关键字都是 Java 中的关键字,用来引用当前对象和父类对象。 + +1. `this`:`this`关键字是用来引用当前对象的,可以用来引用当前对象的属性和方法,也可以用来调用当前对象的构造方法。 +2. `super`:`super`关键字是用来引用父类对象的,可以用来引用父类的属性和方法,也可以用来调用父类的构造方法。 + +`this`和`super`关键字的作用是为了区分局部变量和成员变量、子类方法和父类方法,实现代码的复用和提高代码的可读性。 + +在子类的构造方法中,如果要使用`super()`和`this()`构造器,在 JDK 22 之前,必须放在第一行,否则会报错。但在 JDK 22 的预览功能中,构造器调用可以放在任意位置,但编译器会严格检查是否违反运行限制,比如在调用构造器前就访问了成员变量。 + +## 讲讲异常捕获相关关键字 + +Java 中的异常捕获相关关键字有`try`、`catch`、`finally`、`throw`和`throws`。 + +1. `try`:`try`关键字用来捕获异常,`try`块中包含可能抛出异常的代码。 +2. `catch`:`catch`关键字用来处理异常,`catch`块中包含处理异常的代码。 +3. `finally`:`finally`关键字用来释放资源,`finally`块中包含释放资源的代码,无论是否发生异常,`finally`块中的代码都会执行。 +4. `throw`:`throw`关键字用来抛出异常,`throw`关键字后面跟一个异常对象。 +5. `throws`:`throws`关键字用来声明方法可能抛出的异常,`throws`关键字后面跟一个异常类。 + +异常捕获的原则是捕获异常应该尽早,处理异常应该尽轻,释放资源应该尽快。 + +## 下面函数的返回值究竟是什么? + +```java +public int test() { + int i = 0; + try { + i = 1; + return i; + } catch (Exception e) { + i = 2; + return i; + } finally { + i = 3; + } +} +``` + +返回值是 1。`try`块中的`return`语句会先执行,然后`finally`块中的`i = 3`会执行,但是不会影响`return`语句的返回值。 + +## 下面函数的返回值究竟是什么? + +```java +public int test() { + try { + return 1; + } catch (Exception e) { + return 2; + } finally { + return 3; + } +} +``` + +返回值是 3。`finally`块中的`return`语句会覆盖`try`块中的`return`语句。 + +## 说出几个常见的异常类和具体含义 + +Java 中的异常类有很多,常见的异常类有`NullPointerException`、`ArrayIndexOutOfBoundsException`、`ClassCastException`、`ArithmeticException`、`NumberFormatException`、`FileNotFoundException`、`IOException`、`SQLException`等。 + +1. `NullPointerException`:空指针异常,当一个对象为`null`时调用它的方法或访问它的属性会抛出空指针异常。 +2. `ArrayIndexOutOfBoundsException`:数组下标越界异常,当访问数组的索引超出数组的范围时会抛出数组下标越界异常。 +3. `ClassCastException`:类型转换异常,当一个对象不能被强制转换为另一个类型时会抛出类型转换异常。 +4. `ArithmeticException`:算术异常,当一个数除以 0 时会抛出算术异常。 +5. `NumberFormatException`:数字格式异常,当一个字符串不能被解析为数字时会抛出数字格式异常。 +6. `FileNotFoundException`:文件未找到异常,当一个文件不存在时会抛出文件未找到异常。 +7. `IOException`:输入输出异常,当一个输入输出操作失败时会抛出输入输出异常。 +8. `SQLException`:SQL 异常,当一个 SQL 操作失败时会抛出 SQL 异常。 +9. `NoSuchMethodException`:方法未找到异常,当一个方法不存在时会抛出方法未找到异常。 +10. `java.lang.OutofMemoryError`:内存溢出异常,当内存不足时会抛出内存溢出异常。 +11. `java.lang.StackOverflowError`:栈溢出异常,当栈空间不足时会抛出栈溢出异常。 +12. `java.lang.ClassNotFoundException`:类未找到异常,当一个类不存在时会抛出类未找到异常。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200/index.md" new file mode 100644 index 0000000..2f4df1d --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/Java\345\237\272\347\241\200/\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200/index.md" @@ -0,0 +1,215 @@ +**本模块适合 `Java后端研发(业务岗、架构岗)`、`Java安卓原生研发`等 Java 相关岗位的同学。** + +## Java 是完全的面向对象语言吗? + +Java 是一种面向对象的编程语言,但不是完全的面向对象语言。Java 中有 8 种基本数据类型,这些基本数据类型不是对象,不具有面向对象的特性。另外,Java 中还有一些关键字,比如 static、final、synchronized 等,这些关键字也不是面向对象的。 + +## 讲讲 Object 的常用方法 API + +Java 中的 Object 类是所有类的父类,提供了一些常用的方法 API,如下: + +1. `public String toString()`:返回对象的字符串表示。 +2. `public boolean equals(Object obj)`:判断两个对象是否相等。 +3. `public int hashCode()`:返回对象的哈希码值。 +4. `protected Object clone()`:创建并返回此对象的一个副本。 +5. `public final Class getClass()`:返回对象的运行时类。(反射) +6. `protected void finalize()`:当对象被垃圾回收器回收时调用。 +7. `public final void notify()`:唤醒在此对象监视器上等待的单个线程。 +8. `public final void notifyAll()`:唤醒在此对象监视器上等待的所有线程。 +9. `public final void wait()`:导致当前线程等待,直到另一个线程调用此对象的`notify()`方法或`notifyAll()`方法。 +10. `public final void wait(long timeout)`:导致当前线程等待,直到另一个线程调用此对象的`notify()`方法或`notifyAll()`方法,或者指定的时间已经过去。 +11. `public final void wait(long timeout, int nanos)`:导致当前线程等待,直到另一个线程调用此对象的`notify()`方法或`notifyAll()`方法,或者其他某个线程中断当前线程,或者已经过去的时间超过指定的时间。 + +## 简单讲讲`equals()`重写的原则 + +在 Java 中,`equals()`方法是用来判断两个对象是否相等的,但是默认情况下,`equals()`方法是继承自 Object 类的,它只是比较两个对象的引用是否相等,即比较的是内存地址。 + +为了实现自定义的相等判断,需要重写`equals()`方法,一般遵循以下原则: + +1. 自反性:对于任何非 null 的引用值 x,x.equals(x) 应该返回 true。 +2. 对称性:对于任何非 null 的引用值 x 和 y,如果 x.equals(y) 返回 true,则 y.equals(x) 也应该返回 true。 +3. 传递性:对于任何非 null 的引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,则 x.equals(z) 也应该返回 true。 +4. 一致性:对于任何非 null 的引用值 x 和 y,只要 equals 的比较操作在对象中所用的信息没有被修改,多次调用 x.equals(y) 应该一致地返回 true 或 false。 +5. 非空性:对于任何非 null 的引用值 x,x.equals(null) 应该返回 false。 + +## 例题:重写`equals()`方法,让 a 和 b 相等 + +```java +public class Test { + private int age; + private String name; + + public Test(int age, String name) { + this.age = age; + this.name = name; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Test test = (Test) obj; + return age == test.age && Objects.equals(name, test.name); + } + + @Override + public int hashCode() { + return Objects.hash(age, name); + } + + public static void main(String[] args) { + Test a = new Test(18, "Tom"); + Test b = new Test(18, "Tom"); + System.out.println(a.equals(b)); // true + } +} +``` + +## 为什么要在重写`equals()`方法的时候同时重写`hashCode()`方法? + +在 Java 中,`hashCode()`方法是用来计算对象的哈希码值的,哈希码值是用来确定对象在哈希表中的位置的。如果两个对象相等,那么它们的哈希码值也应该相等,否则会导致哈希表中的冲突。 + +所以在重写`equals()`方法的时候,一般也要重写`hashCode()`方法,保证相等的对象具有相等的哈希码值。 + +## 讲讲`clone()`方法的实现原理 + +`clone()`方法是用来创建并返回对象的一个副本的,它是`Object`类的一个`protected`方法,需要实现`Cloneable`接口。 + +`clone()`方法的实现原理如下: + +1. 首先,`clone()`方法是一个`protected`方法,只能在子类中调用,所以如果要使用`clone()`方法,需要重写`clone()`方法,并将其改为`public`。 +2. 其次,`clone()`方法是浅拷贝,即只拷贝对象的引用,而不拷贝对象的内容。如果对象中包含引用类型的成员变量,那么拷贝的对象和原对象中的引用会指向同一个对象。 +3. 最后,如果要实现深拷贝,需要在`clone()`方法中手动拷贝对象的内容,包括基本数据类型和引用类型。 + +## 简单手写一下浅拷贝,不用`clone()`方法和使用`clone()`方法都写一遍 + +```java +public static T shallowCopy(T obj) { + if (obj == null) { + return null; + } + try { + T copy = (T) obj.getClass().getDeclaredConstructor().newInstance(); + Field[] fields = obj.getClass().getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + field.set(copy, field.get(obj)); + } + return copy; + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + return null; + } +} +``` + +```java +public class Test implements Cloneable { + private int age; + private String name; + + public Test(int age, String name) { + this.age = age; + this.name = name; + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + } + + public static void main(String[] args) { + Test a = new Test(18, "Tom"); + Test b = (Test) a.clone(); + System.out.println(a.equals(b)); // false + } +} +``` + +## 如何改写`clone()`方法实现深拷贝? + +```java +public class Test implements Cloneable { + private int age; + private String name; + private List list; + + public Test(int age, String name, List list) { + this.age = age; + this.name = name; + this.list = list; + } + + @Override + public Object clone() { + try { + Test copy = (Test) super.clone(); + copy.list = new ArrayList<>(); + for (T t : list) + copy.list.add(t.clone()); // 这里要求 T 也实现 Cloneable 接口 + return copy; + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + } + + public static void main(String[] args) { + List list = new ArrayList<>(); + list.add("a"); + list.add("b"); + Test a = new Test<>(18, "Tom", list); + Test b = (Test) a.clone(); + System.out.println(a.equals(b)); // false + } +} +``` + +## 什么是协变和逆变? + +协变和逆变是支持泛型的语言才有的概念。 + +协变是指子类对象可以赋值给父类引用,逆变是指父类对象可以赋值给子类引用。 + +换句话说,协变是符合里氏替换原则的,逆变是违反里氏替换原则的。 + +举个例子: + +```java +List list1 = new ArrayList(); // 协变 +List list2 = new ArrayList(); // 逆变 +``` + +在协变中,`List`表示可以接受任何继承自 Number 的子类,所以`ArrayList`可以赋值给`List`。 + +在逆变中,`List`表示可以接受任何父类为 Integer 的类,所以`ArrayList`可以赋值给`List`。 + +## 讲讲面向对象的四大特性 + +面向对象的四大特性是封装、继承、多态和抽象。 + +1. 封装:封装是指将对象的属性和方法封装在一个类中,对外部隐藏对象的内部实现细节,只提供公共的访问接口。封装可以提高代码的可维护性和可复用性,降低代码的耦合度。 +2. 继承:继承是指一个类可以继承另一个类的属性和方法,从而实现代码的复用。子类可以继承父类的属性和方法,也可以重写父类的方法,实现多态。 +3. 多态:多态是指同一个方法可以根据不同的对象调用不同的实现。多态可以提高代码的灵活性和可扩展性,降低代码的耦合度。 +4. 抽象:抽象是指将对象的共同特征提取出来,形成一个抽象类或接口,用来定义对象的行为和属性。抽象类不能被实例化,只能被继承,接口定义了对象的行为,实现了多态。 + +## 讲讲面向对象的七大原则 + +面向对象的七大原则是设计模式的基础,包括单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则和合成复用原则。 + +1. 单一职责原则:一个类只负责一项职责,降低类的复杂度,提高类的可维护性。 +2. 开闭原则:对扩展开放,对修改关闭,通过抽象和多态实现代码的可扩展性。 +3. 里氏替换原则:子类可以替换父类,子类对象可以赋值给父类引用,实现代码的灵活性和可扩展性。 +4. 依赖倒置原则:高层模块不应该依赖低层模块,两者都应该依赖抽象,抽象不应该依赖细节,细节应该依赖抽象。 +5. 接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上,不应该依赖不需要的接口。 +6. 迪米特法则:一个对象应该对其他对象有最少的了解,降低对象之间的耦合度。 +7. 合成复用原则:尽量使用合成/聚合,而不是继承,通过组合的方式实现代码的复用。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/index.md" new file mode 100644 index 0000000..2210370 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/index.md" @@ -0,0 +1,5 @@ +这个板块将会按照类别存放计算机相关岗位常考八股,在公共课类别会详细标注各模块适合哪些岗位,方便各位针对自己的岗位进行复习。 + +该模块过于功利性,建议作为笔记复习或路线指引使用,不建议作为主要学习材料,否则对你的编程技能提升没有任何帮助,背完就忘。 + +同时,欢迎各位对该模块进行补充,让更多人受益。***但本模块应该存储的是常考八股,覆盖 90% 的考点即可,剩下 10% 的量可能会比这 90% 的量还要多,但是考察频率很低,不适合放在这里,学起来也是事倍功半。你要知道,市面上光是常见的 Java 后端八股文总结就已经到达十几万字。*** \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\223\215\344\275\234\347\263\273\347\273\237/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\223\215\344\275\234\347\263\273\347\273\237/index.md" new file mode 100644 index 0000000..9d7a42d --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\223\215\344\275\234\347\263\273\347\273\237/index.md" @@ -0,0 +1,111 @@ +**本模块是公共类课程,适合绝大部分计算机岗位。** + +## 讲讲进程、线程、协程的区别 + +进程、线程、协程是操作系统中的三个重要概念,主要区别如下: + +1. 进程(Process):进程是操作系统中的一个程序执行实例,是系统资源分配的基本单位,每个进程都有自己的独立内存空间,进程之间相互独立,通信需要通过进程间通信(IPC)来实现。进程的切换开销较大,需要切换内存空间、上下文等,进程之间的切换需要通过系统调用来实现。 +2. 线程(Thread):线程是进程中的一个执行单元,是 CPU 调度的基本单位,同一个进程中的多个线程共享进程的内存空间,线程之间相互独立,通信需要通过共享内存来实现。线程的切换开销较小,只需要切换上下文即可,线程之间的切换是通过线程调度器来实现。 +3. 协程(Coroutine):协程是一种用户态的轻量级线程,是一种协作式的多任务处理机制,协程之间相互协作,通过协程调度器来实现。协程的切换开销较小,只需要切换上下文即可,协程之间的切换是通过协程调度器来实现。 + +总的来说,进程是操作系统资源分配的基本单位,线程是 CPU 调度的基本单位,协程是用户态的轻量级线程。 + +## 什么是同步和互斥 + +同步和互斥是操作系统中的两个重要概念,主要区别如下: + +1. 同步(Synchronization):同步是指多个线程之间的协调和合作,保证线程之间的有序执行。同步是一种概念,可以通过锁、信号量、条件变量等机制来实现。同步的目的是为了避免多个线程之间的竞争条件,保证线程之间的有序执行。(本质基于互斥,只是加上了排队等待的机制) +2. 互斥(Mutual Exclusion):互斥是指多个线程之间的互斥访问共享资源,保证同一时刻只有一个线程访问共享资源。互斥是一种机制,可以通过锁来实现。互斥的目的是为了避免多个线程同时访问共享资源,保证共享资源的一致性。 + +同步和互斥是多线程编程中的重要概念,可以通过锁、信号量、条件变量等机制来实现。 + +## 讲讲如何实现线程间同步 + +1. 临界区(Critical Section):临界区是指本质是对多线程的串行化来访问公共资源或一段代码,同一时刻只能有一个线程访问,Java 中可以通过`synchronized`关键字或`ReentrantLock`类来实现,由进程控制。 +2. 信号量(Semaphore):信号量是一种更加通用的同步机制,本质是一个非负整型变量,通过 wait 和 signal 原子操作进行访问,可以控制多个线程同时访问共享资源,Java 中可以通过`Semaphore`类来实现,但需要控制最大线程数量。 +3. 互斥量(Mutex):互斥量实际上是信号量的一种特殊情况,只能有一个线程访问共享资源,值只能为 0 或 1,Java 中可以通过`Mutex`类来实现。 +4. 事件(Event):事件是一种通知同步机制,本质是一个二值信号量,Java 中可以通过`Event`类来实现,可以控制线程的执行顺序,比较优先级。 + +线程间同步主要是为了避免多个线程之间的竞争条件,保证线程之间的有序执行。 + +## 讲讲进程间通信的方式 + +进程间通信(Inter-Process Communication,IPC)是指不同进程之间进行数据交换和共享资源的过程,主要有以下几种方式: + +1. 管道(Pipe):管道是一种半双工的通信方式,只能在父子进程或兄弟进程之间使用,可以通过`pipe`系统调用来实现,但速度慢,容量有限。 +2. 命名管道(Named Pipe):命名管道是一种全双工的通信方式,可以在不同进程之间使用,可以通过`mkfifo`系统调用来实现。(不常考) +3. 信号(Signal):信号是一种异步的通信方式,用来通知进程发生了某种事件,可以通过`kill`系统调用来发送信号。 +4. 消息队列(Message Queue):消息队列是一种消息的链表,用来在不同进程之间传递数据,可以通过`msgget`、`msgsnd`、`msgrcv`系统调用来实现,但容量受到系统限制,要考虑上一次消息是否被处理。 +5. 共享内存(Shared Memory):共享内存是一种共享内存空间的通信方式,可以在不同进程之间共享数据,可以通过`shmget`、`shmat`、`shmdt`、`shmctl`系统调用来实现。 +6. 信号量(Semaphore):信号量是一种控制多个进程对共享资源的访问的同步机制,可以通过`semget`、`semop`、`semctl`系统调用来实现。***(和线程间同步的信号量不同)*** +7. 套接字(Socket):套接字是一种网络通信的方式,可以在不同主机之间进行通信,可以通过`socket`、`bind`、`listen`、`accept`、`connect`、`send`、`recv`等系统调用来实现。 + +进程间通信主要是为了实现不同进程之间的数据交换和共享资源,提高程序的灵活性和可扩展性。 + +## 讲讲线程间通信的方式 + +线程间通信(Inter-Thread Communication,ITC)是指不同线程之间进行数据交换和共享资源的过程,主要有以下几种方式: + +1. 锁机制:锁机制是一种最基本的线程同步机制,如互斥锁、条件变量、读写锁等。 +2. 信号量(Semaphore):信号量是一种控制多个线程对共享资源的访问的同步机制,可以控制多个线程同时访问共享资源。 +3. 信号(Signal):信号是一种异步的通信方式,用来通知线程发生了某种事件,类似进程间通信的信号。 + +## 讲讲死锁的原因和解决方法 + +死锁是指两个或多个线程互相持有对方需要的资源,导致线程无法继续执行的情况,主要原因有以下几种: + +1. 互斥条件:一个资源只能被一个线程持有。 +2. 请求与保持条件:一个线程请求一个资源而阻塞时,不释放已经持有的资源。 +3. 不剥夺条件:一个线程持有的资源不能被其他线程剥夺。 +4. 循环等待条件:多个线程之间形成一个循环等待资源的关系。 + +解决死锁的方法主要有以下几种: + +1. 预防死锁:通过破坏死锁的四个必要条件之一来预防死锁,如破坏互斥条件(一般破坏不了)、破坏请求与保持条件、破坏不剥夺条件、破坏循环等待条件。 +2. 避免死锁:通过银行家算法等方法来避免死锁,如资源分配图、安全序列等。 +3. 检测死锁:通过死锁检测算法来检测死锁,如有向图算法、等待图算法等。 + +## 讲讲进程调度算法 + +进程调度算法是操作系统中的一个重要概念,主要有以下几种算法: + +1. 先来先服务(First Come First Serve,FCFS):按照进程到达的先后顺序进行调度,先到达的进程先执行,适用于长作业。 +2. 短作业优先(Shortest Job First,SJF):按照进程的执行时间进行调度,执行时间短的进程先执行,适用于短作业。 +3. 高响应比优先(Highest Response Ratio Next,HRRN):按照进程的响应比进行调度,响应比高的进程先执行,适用于响应时间短的进程。 +4. 优先级调度(Priority Scheduling):按照进程的优先级进行调度,优先级高的进程先执行,适用于优先级高的进程。 +5. 时间片轮转(Round Robin,RR):按照时间片的大小进行调度,每个进程执行一个时间片,然后切换到下一个进程,适用于时间片相对均匀的进程。劣势是长作业需要多次排队。 +6. 多级反馈队列(Multilevel Feedback Queue):按照多个队列的优先级进行调度,优先级高的队列先执行,适用于不同优先级的进程。 + +进程调度算法主要是为了提高系统的性能和资源利用率,根据不同的场景选择合适的调度算法。 + +## 什么是段、页 + +段(Segment)和页(Page)是操作系统中的两个重要概念,主要区别如下: + +1. 段(Segment):段是一种逻辑上的地址空间,是程序的一个逻辑单位,包含了一组相关的逻辑信息,如代码段、数据段、堆栈段等。段的大小是不固定的,可以根据程序的需要进行调整。分段的作业地址空间是二维的,有段号和段内地址两个部分。 +2. 页(Page):页是一种物理上的地址空间,是内存的一个物理单位,包含了一组连续的物理地址,大小是固定的,通常为 4KB 或 8KB。一个系统只有一种大小的页。分页的作业地址空间是一维的,只有页号一个部分,即是单一的线性地址空间。 + +段和页主要是为了实现虚拟内存和内存保护,提高内存的利用率和安全性。 + +## 讲讲虚拟内存 + +虚拟内存(Virtual Memory)是一种计算机系统的内存管理技术,主要有以下几个特点: + +1. 虚拟内存是一种逻辑上的内存空间,是程序的一个逻辑单位,包含了一组连续的逻辑地址,大小是不固定的,可以根据程序的需要进行调整。(换句话说,虚拟内存不需要完全在内存中,可以部分在内存中,部分在磁盘中) +2. 虚拟内存是一种分页的内存管理技术,将物理内存划分为若干个固定大小的页,将逻辑内存划分为若干个固定大小的段,通过页表将逻辑地址映射到物理地址。 +3. 虚拟内存是一种内存保护的技术,通过页表将逻辑地址映射到物理地址,可以实现内存的隔离和保护,防止程序之间的干扰。 +4. 虚拟内存是一种内存共享的技术,多个程序可以共享同一块物理内存,提高内存的利用率。 + +虚拟内存主要是为了提高内存的利用率和安全性,实现内存的隔离和保护。 + +## 讲讲页面置换算法 + +页面置换算法(Page Replacement Algorithm)是虚拟内存管理中的一个重要概念,主要有以下几种算法: + +1. 先进先出(First In First Out,FIFO):按照页面进入内存的先后顺序进行置换,先进入内存的页面先被置换出去。 +2. 最近最少使用(Least Recently Used,LRU):按照页面最近一次被访问的时间进行置换,最近一次被访问时间最久的页面被置换出去。(以时间为参考) +3. 最近使用次数(Least Frequently Used,LFU):按照页面被访问的频率进行置换,访问频率最低的页面被置换出去。(以次数为参考) +4. 时钟(Clock):按照页面的访问位进行置换,访问位为 0 的页面被置换出去,访问位为 1 的页面被保留。(不常考) +5. 最优置换(Optimal,OPT):按照页面未来一段时间内不再被访问的时间进行置换,未来一段时间内不再被访问的页面被置换出去。 + +页面置换算法主要是为了提高内存的利用率和性能,根据不同的场景选择合适的置换算法。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/MySQL/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/MySQL/index.md" new file mode 100644 index 0000000..ad67c16 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/MySQL/index.md" @@ -0,0 +1,29 @@ +**本模块是公共类课程,适合`后端开发`、`嵌入式开发`、`移动端原生开发(SQLite、Realm)`等需要使用数据库进行数据持久化的岗位。** + +## 讲讲 MyISAM 和 InnoDB 的区别 + +MyISAM 和 InnoDB 是 MySQL 中的两种存储引擎,主要区别如下: + +1. 存储结构:MyISAM 存储引擎是表级锁,InnoDB 存储引擎是行级锁。 +2. 事务支持:MyISAM 存储引擎不支持事务,InnoDB 存储引擎支持事务。 +3. 外键支持:MyISAM 存储引擎不支持外键,InnoDB 存储引擎支持外键。 +4. 全文索引:MyISAM 存储引擎支持全文索引,InnoDB 存储引擎不支持全文索引。 +5. 表空间:MyISAM 存储引擎不支持表空间,InnoDB 存储引擎支持表空间。 +6. 性能:MyISAM 存储引擎读取速度快,写入速度慢,适合读多写少的场景;InnoDB 存储引擎读取速度慢,写入速度快,适合读写频繁的场景。 + +MyISAM 和 InnoDB 的主要区别是存储结构、事务支持、外键支持、全文索引、表空间和性能,根据不同的需求选择合适的存储引擎。 + +## 讲讲 MySQL 和 MongoDB 的区别是什么?指出最大的区别 + +MySQL 和 MongoDB 是两种不同类型的数据库,主要区别如下: + +1. 数据库类型:MySQL 是关系型数据库,MongoDB 是非关系型数据库。 +2. 数据结构:MySQL 是表结构,MongoDB 是文档结构。 +3. 数据模型:MySQL 是静态模式,MongoDB 是动态模式。 +4. 查询语言:MySQL 是 SQL 语言,MongoDB 是 JSON 语言。 +5. 事务支持:MySQL 支持事务,隔离级别为可重复读,MongoDB 在 4.0 版本后支持事务,隔离级别为读提交。 +6. 复杂查询:MySQL 支持复杂查询,MongoDB 不支持复杂查询。 +7. 数据库引擎:MySQL 支持多种存储引擎,MongoDB 只支持一种存储引擎。 +8. 数据库性能:MySQL 读取速度快,写入速度慢,MongoDB 读取速度慢,写入速度快。 + +MySQL 和 MongoDB 的最大区别是数据库类型,MySQL 是关系型数据库,MongoDB 是非关系型数据库。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/Redis/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/Redis/index.md" new file mode 100644 index 0000000..0d7426b --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/Redis/index.md" @@ -0,0 +1,84 @@ +**本模块是公共类课程,适合`后端开发`等需要使用 Redis 进行缓存和并发处理的岗位。** + +## 讲讲什么是 Redis,有什么优缺点? + +Redis 是一种内存数据库,主要用于缓存、会话管理、消息队列等,支持的数据结构有字符串`string`、列表`list`、集合`set`、有序集合`zset`、哈希表`hash`等。 + +主要优缺点如下: + +1. 优点: + - 高性能:Redis 是基于内存的数据库,读写速度非常快,适合高并发场景。 + - 支持多种数据结构:Redis 支持多种数据结构,如字符串、列表、集合、有序集合、哈希表等。 + - 支持持久化:Redis 支持持久化,可以将数据保存到磁盘,防止数据丢失。 + - 支持主从复制:Redis 支持主从复制,可以实现读写分离,提高数据库的性能和可靠性。 + - 支持集群模式:Redis 支持集群模式,可以实现分布式存储,提高数据库的性能和可靠性。 + +2. 缺点: + - 内存消耗大:Redis 是基于内存的数据库,内存消耗大,不适合存储大量数据。 + - 不支持事务:Redis 不支持事务,不支持回滚操作,不适合复杂的业务逻辑。 + - 不支持复杂查询:Redis 不支持复杂查询,只支持简单的查询操作,不适合复杂的查询操作。 + - 单线程模型:Redis 是单线程模型,只能使用一个 CPU 核心,不适合多核 CPU。 + +Redis 是一种高性能的内存数据库,适合高并发场景,但是内存消耗大,不支持事务,不支持复杂查询,是一种适合缓存、会话管理、消息队列等场景的数据库。 + +## MongoDB 也支持作为内存数据库,为什么选择 Redis? + +MongoDB 也支持作为内存数据库,但是 MongoDB 是一种文档数据库,适合存储大量的文档数据,不适合存储大量的键值对数据,因此选择 Redis。 + +Redis 是一种内存数据库,适合存储大量的键值对数据,支持多种数据结构,如字符串、列表、集合、有序集合、哈希表等,适合缓存、会话管理、消息队列等场景。 + +MongoDB 是一种文档数据库,适合存储大量的文档数据,支持复杂的查询操作,适合存储大量的文档数据,不适合存储大量的键值对数据。 + +因此,根据不同的需求选择合适的数据库,MongoDB 适合存储大量的文档数据,Redis 适合存储大量的键值对数据。 + +## Redis 的数据结构有哪些?分别适用于什么场景? + +Redis 支持多种数据结构,主要有以下几种数据结构: + +1. 字符串(string):字符串是最基本的数据结构,适合存储简单的键值对数据,如缓存、会话管理等。 +2. 列表(list):列表是一种有序的数据结构,适合存储有序的数据,如消息队列、任务队列等。 +3. 集合(set):集合是一种无序的数据结构,适合存储无序的数据,如共同好友、共同关注等。 +4. 有序集合(zset):有序集合是一种有序的数据结构,适合存储有序的数据,如排行榜、热门文章等。 +5. 哈希表(hash):哈希表是一种键值对的数据结构,适合存储键值对的数据,如用户信息、商品信息等。 + +Redis 的数据结构适合不同的场景,字符串适合存储简单的键值对数据,列表适合存储有序的数据,集合适合存储无序的数据,有序集合适合存储有序的数据,哈希表适合存储键值对的数据。 + +## Redis 的持久化机制是什么?各自有什么优缺点? + +Redis 的持久化机制主要有 RDB 快照和 AOF 日志,各自有以下优缺点: + +1. RDB 快照: + - 优点:RDB 快照是将内存数据保存到磁盘,恢复速度快,适合备份和恢复数据。 + - 缺点:RDB 快照是定时保存数据,可能会丢失部分数据,不适合实时数据。 + +2. AOF 日志: + - 优点:AOF 日志是将写操作保存到磁盘,恢复数据更加可靠,适合实时数据。 + - 缺点:AOF 日志是实时保存数据,可能会影响性能,不适合大量写操作。 + +Redis 的持久化机制是为了保证数据的可靠性和持久性,根据不同的需求选择合适的持久化机制。 + +## 讲讲什么是缓存穿透和缓存雪崩,以及如何解决? + +缓存穿透是指查询一个不存在的数据,导致缓存失效,请求直接访问数据库,增加数据库的压力,解决方法是使用布隆过滤器、缓存空对象、设置短暂过期时间等。但更好的解决办法是直接把错误数据也缓存起来,但是要设置一个较短的过期时间。 + +缓存雪崩是指缓存集中失效,导致请求直接访问数据库,增加数据库的压力,解决方法是使用分布式锁、设置随机过期时间、设置热点数据永不过期等。 + +缓存穿透和缓存雪崩都会导致数据库的压力增加,影响系统的性能和可靠性,需要根据不同的情况选择合适的解决方法。 + +## 讲讲大 Key 问题,以及如何解决? + +大 Key 问题是指一个键值对的数据量非常大,导致读写性能下降,解决方法是使用拆分多个小 Key、数据压缩、数据分片等。 + +大 Key 问题会导致读写性能下降,影响系统的性能和可靠性,需要根据不同的情况选择合适的解决方法。 + +## 为什么现在很少使用 Redis 作为消息队列? + +Redis 是一种内存数据库,主要用于缓存、会话管理、消息队列等,支持多种数据结构,如字符串、列表、集合、有序集合、哈希表等,适合缓存、会话管理、消息队列等场景。 + +Redis 的消息队列主要有以下几个问题: + +1. **持久化**:Redis 的持久化机制(RDB 和 AOF)虽然可以减少数据丢失,但都不是实时的,可能会丢失部分数据,不适合需要严格持久化的场景。 +2. **高可用**:Redis 的高可用机制(主从复制和哨兵)可以提高数据库的性能和可靠性,但在主从切换期间可能会有短暂的不可用时间,不适合需要高可用性的场景。 +3. **集群**:Redis Cluster 支持分布式存储和水平扩展,但在分片和重分片过程中可能会影响性能,不适合需要大规模分布式的场景。 + +因此,根据不同的需求选择合适的消息队列,Redis 适合作为轻量级、高性能的消息队列,但对于需要严格持久化、高可用性和大规模分布式的场景,专门的消息队列系统(如 Kafka、RabbitMQ 等)可能更合适。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/index.md" new file mode 100644 index 0000000..dab754c --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/index.md" @@ -0,0 +1 @@ +**本模块是公共类课程,适合`后端开发`、`嵌入式开发`、`移动端原生开发(SQLite、Realm)`等需要使用数据库进行数据持久化的岗位。** \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/\351\200\232\350\257\206\345\237\272\347\241\200/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/\351\200\232\350\257\206\345\237\272\347\241\200/index.md" new file mode 100644 index 0000000..950bb61 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\346\225\260\346\215\256\345\272\223/\351\200\232\350\257\206\345\237\272\347\241\200/index.md" @@ -0,0 +1,105 @@ +**本模块是公共类课程,适合`后端开发`、`嵌入式开发`、`移动端原生开发(SQLite、Realm)`等需要使用数据库进行数据持久化的岗位。** + +## 讲讲数据库的三范式 + +数据库的三范式是数据库设计的一种规范,主要有以下三个范式: + +1. 第一范式(1NF):属性不可再分。每个属性都是不可再分的基本数据项,不可再分是指属性不能再分解为更小的数据项,如学生表的学号、姓名、性别等。 +2. 第二范式(2NF):属性完全依赖于主键。每个非主属性完全依赖于主键,而不依赖于主键的任何一部分,如学生表的学号、课程号、成绩等。 +3. 第三范式(3NF):属性不依赖于其他非主属性。每个非主属性不依赖于其他非主属性,如学生表的学号、姓名、性别等。 + +数据库的三范式是为了提高数据库的性能和可靠性,减少数据冗余和数据不一致,实际开发中根据需求选择合适的范式,没有必要必须满足三范式。 + +## 讲讲数据库的索引 + +数据库的索引是一种数据结构,用于提高数据库的查询性能,主要有以下几种索引: + +1. 普通索引:普通索引是最基本的索引,没有任何限制,可以加速查询,但不会唯一。 +2. 唯一索引:唯一索引是保证索引列的唯一性,可以加速查询,保证数据的唯一性。 +3. 主键索引:主键索引是一种唯一索引,用于保证表的唯一性,主键索引是一种特殊的唯一索引。 +4. 复合索引:复合索引是多个列组合在一起的索引,可以加速多列的查询,提高查询性能。 +5. 全文索引:全文索引是对文本的索引,可以加速文本的查询,提高查询性能。 +6. 空间索引:空间索引是对空间数据的索引,可以加速空间数据的查询,提高查询性能。 + +数据库的索引是为了提高数据库的查询性能,根据不同的需求选择合适的索引,索引的实现常常是 B 树(MongoDB、PostgreSQL)、B+ 树(MySQL InnoDB)、哈希表(MySQL Memory)等。 + +索引的优点是可以加快检索速度,使用优化隐藏器,提高系统性能;缺点是占用磁盘空间,降低写入速度,增加了维护成本。 + +## 讲讲数据库的事务和事务等级 + +数据库的事务是一组操作单元,要么全部成功,要么全部失败,主要有以下四个特性: + +1. 原子性(Atomicity):事务是一个不可分割的工作单位,要么全部成功,要么全部失败。 +2. 一致性(Consistency):事务执行前后,数据库的完整性约束没有被破坏。 +3. 隔离性(Isolation):事务之间是相互隔离的,一个事务的执行不会影响其他事务。 +4. 持久性(Durability):事务一旦提交,对数据库的修改是永久性的,不会被回滚。 + +数据库的事务是为了保证数据的一致性和完整性,提高数据库的可靠性和可靠性。 + +数据库的事务等级主要有以下四个等级: + +1. 读未提交(Read Uncommitted):一个事务可以读取另一个事务未提交的数据,最低的隔离级别。 +2. 读提交(Read Committed):一个事务只能读取另一个事务已提交的数据,避免脏读,不可重复读。 +3. 可重复读(Repeatable Read):一个事务在执行期间,不会看到其他事务的更新,避免脏读和不可重复读,但不能防止幻读。 +4. 串行化(Serializable):一个事务在执行期间,不会看到其他事务的更新,避免脏读、不可重复读和幻读,最高的隔离级别。 + +数据库的事务等级是为了保证数据的一致性和完整性,提高数据库的可靠性和可靠性。 + +MySQL 的默认事务等级是可重复读(Repeatable Read),MongoDB 的默认事务等级是读提交(Read Committed),ClickHouse 的默认事务等级是读未提交(Read Uncommitted)。 + +## 讲讲数据库的锁和锁的类型 + +数据库的锁是为了保证数据的一致性和完整性,主要有以下几种锁: + +1. 共享锁(S 锁):共享锁是读锁,多个事务可以同时获取共享锁,但是不能获取排他锁,用于读取数据。 +2. 排他锁(X 锁):排他锁是写锁,只有一个事务可以获取排他锁,其他事务不能获取共享锁和排他锁,用于修改数据。 +3. 行级锁:行级锁是对数据行加锁,只锁定需要修改的数据行,其他数据行不受影响,提高并发性能。 +4. 表级锁:表级锁是对整个表加锁,锁定整个表,其他事务不能访问表,降低并发性能。 +5. 乐观锁:乐观锁是通过版本号或时间戳实现,不加锁,通过版本号或时间戳判断数据是否被修改。 +6. 悲观锁:悲观锁是通过加锁实现,锁定数据,其他事务不能访问数据,提高数据的一致性和完整性。(select ... for update) + +数据库的锁是为了保证数据的一致性和完整性,提高数据库的可靠性和可靠性,根据不同的需求选择合适的锁。 + +## 单靠乐观锁可以解决秒杀并发问题吗? + +很多八股文说,单靠乐观锁可以解决秒杀并发问题,实际上这种说法纯粹是误人子弟。你若听信他的胡扯,那你这辈子也是真的有了。 + +乐观锁通过版本号或时间戳实现,不加锁。在修改数据时,先读取数据,然后比较版本号或时间戳,如果一致,则修改数据,否则抛出异常。只从这个层面看,似乎乐观锁解决了悲观锁出现的阻塞、死锁等问题。 + +但秒杀并发问题并不单纯指代数据层面的并发修改问题,还会包括并发引起的系统压力等问题,需要综合考虑多种因素。 + +一般情况下,秒杀系统的乐观锁都处于数据库层面,会增加一个冗余字段专门存储版本号或时间戳。 + +眼看单靠乐观锁可以解决秒杀并发问题,但是此时所有请求都会到达数据库,数据库的压力会非常大,容易导致数据库宕机,因此还需要进行流量削峰、限流等操作,比如使用令牌桶算法、漏桶算法,让请求直接到达应用业务层就被打回,降低数据库的压力。 + +而且在实际业务中,一般会建立一个评估团队,对秒杀活动进行评估,根据评估结果进行调整,比如集群扩容、数据库优化、代码优化等。 + +如果单靠乐观锁就可以解决秒杀并发问题,那这软件程序未免也太好写了,这么好写为什么大家天天加班呢? + +笔者写了一篇文章专门研究过这个问题,有兴趣的可以看看:[为什么仅靠简单的乐观锁和悲观锁不能处理秒杀系统业务](https://juejin.cn/post/7396921416464105487) + +## 如何定位慢 SQL(SQL 调优) + +定位慢 SQL 主要有以下几种方法: + +1. 慢查询日志:开启慢查询日志,记录执行时间超过阈值的 SQL 语句,通过分析慢查询日志,找出慢 SQL。(上线前关闭) +2. explain 命令:使用 explain 命令,查看 SQL 语句的执行计划,分析索引、表连接、排序等情况,找出慢 SQL。 +3. show profile 命令:使用 show profile 命令,查看 SQL 语句的执行时间、IO 操作、锁等情况,找出慢 SQL。 +4. 数据库监控工具:使用数据库监控工具,监控数据库的性能指标,如 CPU、内存、磁盘、网络等,找出慢 SQL。 +5. 慢 SQL 分析工具:使用慢 SQL 分析工具,分析 SQL 语句的执行计划、IO 操作、锁等情况,找出慢 SQL。 + +定位慢 SQL 主要是通过慢查询日志、explain 命令、show profile 命令、数据库监控工具、慢 SQL 分析工具等方法,找出慢 SQL,然后进行优化。 + +## 讲讲数据库的索引失效的情况 + +数据库的索引失效主要有以下几种情况: + +1. 索引列不在查询条件中:索引列不在查询条件中,索引失效,无法加速查询。 +2. 索引列使用函数:索引列使用函数,索引失效,无法加速查询。 +3. 索引列使用运算符:索引列使用运算符,索引失效,无法加速查询。 +4. 索引列使用 or 条件:索引列使用 or 条件,索引失效,无法加速查询。 +5. 索引列使用 not 条件:索引列使用 not 条件,索引失效,无法加速查询。 +6. 索引列使用 is null 条件:索引列使用 is null 条件,索引失效,无法加速查询。 +7. 索引列使用!=条件:索引列使用!=条件,索引失效,无法加速查询。 + +数据库的索引失效主要是因为索引列不在查询条件中、索引列使用函数、索引列使用运算符、索引列使用 or 条件、索引列使用 not 条件、索引列使用 is null 条件、索引列使用!=条件等情况,根据不同的情况选择合适的索引。 diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP\343\200\201WebSocket\345\222\214TLS/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP\343\200\201WebSocket\345\222\214TLS/index.md" new file mode 100644 index 0000000..de556fe --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP\343\200\201WebSocket\345\222\214TLS/index.md" @@ -0,0 +1,78 @@ +**本模块是公共类课程,适合绝大部分计算机岗位。但服务端岗位只需要简单学习 HTTP,不需要了解 TLS 协议具体内容** + +## 讲讲 Http 和 Https 的区别 + +HTTP(HyperText Transfer Protocol)和 HTTPS(HyperText Transfer Protocol Secure)是两种网络传输协议,主要区别如下: + +1. HTTP 是明文传输,HTTPS 是加密传输:HTTP 是明文传输,数据不加密,安全性较低,容易被窃听和篡改;HTTPS 是加密传输,数据加密传输,安全性较高,可以防止窃听和篡改。 +2. HTTP 默认端口是 80,HTTPS 默认端口是 443:HTTP 默认端口是 80,HTTPS 默认端口是 443,通过端口可以区分 HTTP 和 HTTPS。 +3. HTTP 不需要证书,HTTPS 需要证书:HTTP 不需要证书,直接传输数据;HTTPS 需要证书,需要向 CA 机构申请证书,用于加密传输数据。 +4. HTTP 的连接速度快,HTTPS 的连接速度慢:HTTP 的连接速度快,数据传输快;HTTPS 的连接速度慢,数据传输慢,因为需要加密和解密数据。 +5. HTTP 和 HTTPS 使用的是不同的 URL:HTTP 使用`http://`开头的 URL,HTTPS 使用`https://`开头的 URL,通过 URL 可以区分 HTTP 和 HTTPS。 + +HTTP 和 HTTPS 的主要区别是安全性和速度,根据不同的需求选择合适的传输协议。 + +## ws 和 wss(WebSocket Secure) 有什么区别? + +WebSocket 是一种全双工通信协议,它可以在客户端和服务器之间建立持久连接,实现实时通信。WebSocket 的数据传输是明文传输的,不安全,容易被中间人攻击。wss 是 WebSocket 的加密版本,它使用 TLS/SSL 协议对数据进行加密传输,安全性更高,不易被中间人攻击。 + +在实际应用中,如果需要保护数据的安全性,可以使用 wss 协议,如果不需要保护数据的安全性,可以使用 WebSocket 协议。 + +## 讲讲`GET`和`POST`的区别 + +`GET`和`POST`是 HTTP 协议中的两种请求方法,主要区别如下: + +1. `GET`请求:`GET`请求是一种幂等的请求方法,用于从服务器获取资源,通过 URL 传递参数,参数会显示在 URL 中,有长度限制,不安全,不适合传输敏感数据。 +2. `POST`请求:`POST`请求是一种非幂等的请求方法,用于向服务器提交数据,通过请求体传递参数,参数不会显示在 URL 中,没有长度限制,安全,适合传输敏感数据。 +3. `GET`请求的参数会显示在 URL 中,`POST`请求的参数不会显示在 URL 中。 +4. `GET`请求的参数有长度限制,`POST`请求的参数没有长度限制。 +5. `GET`请求的参数不安全,`POST`请求的参数相对安全。 + +`GET`和`POST`的主要区别是传递参数的方式、参数的安全性和长度限制,根据不同的需求选择合适的请求方法。 + +## 讲讲常见的 HTTP 状态码 + +HTTP 状态码是 HTTP 协议中的一种响应状态码,主要有以下几种状态码: + +1. 1xx:信息状态码,表示请求已接收,继续处理。 +2. 2xx:成功状态码,表示请求已成功接收、理解、接受。 + - 200 OK:请求成功。 + - 201 Created:请求已创建。 + - 202 Accepted:请求已接受。 +3. 3xx:重定向状态码,表示需要进一步操作以完成请求。 + - 301 Moved Permanently:永久重定向。 + - 302 Found:临时重定向。 + - 304 Not Modified:未修改,已缓存。 +4. 4xx:客户端错误状态码,表示请求包含语法错误或无法完成请求。 + - 400 Bad Request:请求错误。 + - 401 Unauthorized:未授权。 + - 403 Forbidden:禁止访问。 + - 404 Not Found:未找到。 + - 405 Method Not Allowed:方法不允许。 +5. 5xx:服务器错误状态码,表示服务器无法完成明显有效的请求。 + - 500 Internal Server Error:服务器错误。 + - 501 Not Implemented:未实现。 + - 503 Service Unavailable:服务不可用。 + +HTTP 状态码主要是为了表示请求的处理结果,根据不同的状态码进行不同的处理。 + +## 讲讲 WebSocket 连接建立的详细过程。 + +WebSocket 连接建立的详细过程如下: + +1. 客户端发起 HTTP 请求,请求头中包含`Upgrade: websocket,Connection: Upgrade,Sec-WebSocket-Key`等字段。 +2. 服务器返回 HTTP 响应,响应头中包含`Upgrade: websocket,Connection: Upgrade,Sec-WebSocket-Accept`等字段,状态码为 101。 +3. 客户端收到 HTTP 响应,校验`Sec-WebSocket-Accept`字段,如果校验通过,连接建立成功。 +4. 客户端和服务器之间可以进行全双工通信,实时传输数据。 + +总的来说,进行了一次握手,不过如果算上 TCP 的三次握手,其实是四次握手。 + +## 讲讲 WebSocket 连接断开的详细过程。 + +WebSocket 连接断开的详细过程如下: + +1. 客户端或服务器发起关闭连接请求,请求头中包含`Connection: close`字段。 +2. 对方返回关闭连接响应,响应头中包含`Connection: close`字段。 +3. 客户端或服务器收到关闭连接响应,连接断开成功。 + +总的来说,进行了一次握手,不过如果算上 TCP 的四次挥手,其实是五次挥手。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP/index.md" new file mode 100644 index 0000000..f04266a --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/TCP/index.md" @@ -0,0 +1,79 @@ +**本模块是公共类课程,适合绝大部分计算机岗位。** + +## 讲讲 TCP 和 UDP 的区别 + +TCP(Transmission Control Protocol)和 UDP(User Datagram Protocol)是两种网络传输协议,主要区别如下: + +1. TCP 是面向连接的,UDP 是无连接的:TCP 是面向连接的传输协议,需要先建立连接,然后再传输数据;UDP 是无连接的传输协议,不需要建立连接,直接传输数据。 +2. TCP 是可靠的,UDP 是不可靠的:TCP 是可靠的传输协议,通过重传机制、校验和、滑动窗口等机制保证数据的可靠传输;UDP 是不可靠的传输协议,不保证数据的可靠传输。 +3. TCP 是面向字节流的,UDP 是面向报文的:TCP 是面向字节流的传输协议,将数据看作字节流进行传输;UDP 是面向报文的传输协议,将数据看作报文进行传输。 +4. TCP 是有序的,UDP 是无序的:TCP 是有序的传输协议,保证数据的有序传输;UDP 是无序的传输协议,不保证数据的有序传输。 +5. TCP 是重量级的,UDP 是轻量级的:TCP 是重量级的传输协议,需要建立连接、维护状态、保证可靠传输等;UDP 是轻量级的传输协议,不需要建立连接、不保证可靠传输等。 + +TCP 和 UDP 的主要区别是连接方式、可靠性、传输方式、有序性和重量级程度,根据不同的需求选择合适的传输协议。 + +## 讲讲 TCP 协议的格式 + +TCP 报文通常由长 20 字节的报文头(不计选项和填充字段的话)和报文体两部分组成。 + +报文头一般有 5 行,每行 4 个字节,共 20 个字节,包括以下字段: + +1. 第一行:源端口号(16 位):标识发送端的端口号。目的端口号(16 位):标识接收端的端口号。 +2. 第二行:seq 序列号(32 位):用来标识从 TCP 源端向目的端发送的数据字节流,发送数据时,序列号指的是数据的第一个字节的编号。 +3. 第三行:ack 确认号(32 位):是期望收到的下一次数据的第一个字节的序号,确认序号应该是上一次收到的数据字节序号 +1。只有 ACK 标志位为 1 时,确认号字段才有效。 +4. 第四行:Data Offset 数据偏移(4 位):指明 TCP 报文头部的长度,即数据从哪里开始。Reserved 保留位(6 位):保留未来使用,全部置为 0。Flags 标志位(6 位):包括 URG、ACK、PSH、RST、SYN、FIN 等标志位。Window 窗口大小(16 位):窗口大小是指发送端还能接收多少字节的数据。 +5. 第五行:Checksum 校验和(16 位):检验整个 TCP 报文段在传输过程中是否发生变化。Urgent Pointer 紧急指针(16 位):紧急指针是一个偏移量,和序列号相加表示紧急数据的最后一个字节的下一个字节。 +6. (可选)选项和填充字段:TCP 报文头还可以包含一些选项,如最大报文段长度、窗口扩大因子、时间戳等。 + +报文体是 TCP 报文的数据部分,长度不固定,可以是 0 个字节或多个字节。 + +## 讲讲 TCP 三次握手和四次挥手的过程 + +TCP 三次握手和四次挥手是 TCP 协议中的连接和断开过程,主要过程如下: + +1. 三次握手: + 1. 第一次握手:客户端发送 SYN 包,序列号 seq=x,进入 SYN-SENT 状态。 + 2. 第二次握手:服务器收到 SYN 包,发送 SYN+ACK 包,确认号 ack=x+1,序列号 seq=y,进入 SYN-RECEIVED 状态。 + 3. 第三次握手:客户端收到 SYN+ACK 包,发送 ACK 包,确认号 ack=y+1,进入 ESTABLISHED 状态。服务端收到 ACK 包,进入 ESTABLISHED 状态。 + +2. 四次挥手: + 1. 第一次挥手:客户端发送 FIN 包,序列号 seq=u,进入 FIN-WAIT-1 状态。 + 2. 第二次挥手:服务器收到 FIN 包,发送 ACK 包,确认号 ack=u+1,序列号 seq=v,进入 CLOSE-WAIT 状态。客户端收到 ACK 包,进入 FIN-WAIT-2 状态。 + 3. 第三次挥手:服务器发送 FIN 包,序列号 seq=w,进入 LAST-ACK 状态。 + 4. 第四次挥手:客户端收到 FIN 包,发送 ACK 包,确认号 ack=w+1,进入 TIME-WAIT 状态。间隔 2MSL 后,客户端进入 CLOSED 状态,服务器收到 ACK 包,进入 CLOSED 状态。 + +TCP 三次握手和四次挥手是为了建立和断开 TCP 连接,保证数据的可靠传输。 + +## 为什么四次挥手结尾要等待 2MSL(Maximum Segment Lifetime) + +1. 保证数据的可靠传输:TIME-WAIT 状态是为了保证数据的可靠传输,防止数据丢失或重复传输。 +2. 防止连接的混淆:TIME-WAIT 状态是为了防止连接的混淆,保证连接的唯一性,防止新的连接和旧的连接混淆。 +3. 保证连接的可靠关闭:TIME-WAIT 状态是为了保证连接的可靠关闭,防止连接的不完整,保证连接的完整性。 +4. 防止 TIME-WAIT 状态的冲突:TIME-WAIT 状态是为了防止 TIME-WAIT 状态的冲突,保证连接的唯一性,防止连接的冲突。 + +因此,四次挥手结尾要等待 2MSL 是为了保证数据的可靠传输,防止连接的混淆,保证连接的可靠关闭,防止 TIME-WAIT 状态的冲突。 + +## 讲讲 TCP 的滑动窗口协议流程 + +TCP 的滑动窗口协议是一种流量控制机制,主要流程如下: + +1. 发送端发送数据:发送端发送数据,接收端接收数据,接收端发送 ACK 包,确认收到的数据。 +2. 接收端发送窗口大小:接收端发送窗口大小,告诉发送端还能接收多少字节的数据。 +3. 发送端调整窗口大小:发送端根据接收端发送的窗口大小调整发送窗口大小,保证发送端和接收端的窗口大小一致。 +4. 循环进行上述流程。 + +TCP 的滑动窗口协议是为了实现流量控制,保证发送端和接收端的窗口大小一致,防止数据的丢失和重传。 + +## 讲讲 TCP 的拥塞控制机制 + +先说一个名词:拥塞窗口(Congestion Window,cwnd)。拥塞窗口是发送方可以发送的数据量,是动态变化的,取决于网络的拥塞程度。 + +发送方会让自己的发送窗口大小等于拥塞窗口和接收窗口中的较小值。 + +实现拥塞窗口动态变化主要有以下几种算法: + +1. 慢启动(Slow Start):发送端拥塞窗口从 1MSS 开始,每收到一个 ACK 包,拥塞窗口大小加 1,指数增长,直到达到慢启动阈值(Slow Start Threshold,ssthresh,一般是 65536)。 +2. 拥塞避免(Congestion Avoidance):发送端拥塞窗口达到慢启动阈值后,拥塞窗口大小线性增长,每个 RTT(往返时间)增加 1 个 MSS,直到检测到网络拥塞。 +3. 快重传(Fast Retransmit):发送端收到 3 个重复的 ACK 包,说明某个数据包丢失,立即重传丢失的数据包,而不是等待超时重传。此时慢启动阈值设置为拥塞窗口的一半,拥塞窗口大小设置为新的慢启动阈值加上 3 个 MSS(因为已经收到三个重复的 ACK 包),重新进入慢启动阶段。 + +TCP 的拥塞控制机制是为了防止网络拥塞,保证数据的可靠传输,提高网络的性能和可靠性。 diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/index.md" new file mode 100644 index 0000000..13c5bbc --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/index.md" @@ -0,0 +1 @@ +**本模块是公共类课程,适合绝大部分计算机岗位。** \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/\351\200\232\350\257\206\345\237\272\347\241\200/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/\351\200\232\350\257\206\345\237\272\347\241\200/index.md" new file mode 100644 index 0000000..c48d3aa --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/\351\200\232\350\257\206\345\237\272\347\241\200/index.md" @@ -0,0 +1,32 @@ +**本模块是公共类课程,适合绝大部分计算机岗位。** + +## 讲讲 OSI 七层模型和 TCP/IP 四层模型 + +OSI 七层模型(Open Systems Interconnection Reference Model)和 TCP/IP 四层模型(Transmission Control Protocol/Internet Protocol)是计算机网络中的两种网络模型,主要区别如下: + +1. OSI 七层模型: + 1. 物理层(Physical Layer):负责物理设备之间的链接,传输比特流,主要是传输介质、信号、时钟等。常见协议有 RS-232、V.35、RJ45 等。 + 2. 数据链路层(Data Link Layer):负责在网络设备中建立和维护数据链路,传输帧,主要是传输数据帧、差错检测、流量控制等。常见协议有以太网、PPP、HDLC 等。 + 3. 网络层(Network Layer):负责数据包的路由和转发,主要是传输数据包、路由选择、拥塞控制等。常见协议有 IP、ICMP、ARP、RIP、OSPF 等。 + 4. 传输层(Transport Layer):负责数据的传输和可靠性,主要是传输数据段、流量控制、差错检测等。常见协议有 TCP、UDP 等。 + 5. 会话层(Session Layer):负责建立、管理和终止会话,主要是传输会话标识、同步、检查点等。常见协议有 NetBIOS、RPC 等。 + 6. 表示层(Presentation Layer):负责数据的格式化和编码,主要是传输数据格式、加密、压缩等。常见协议有 JPEG、MPEG、ASCII 等。 + 7. 应用层(Application Layer):负责应用程序之间的通信,主要是传输应用数据、协议解析、用户接口等。常见协议有 HTTP、FTP、SMTP、DNS 等。 +2. TCP/IP四层模型:TCP/IP四层模型是一种实际模型,将计算机网络分为四层,分别是网络接口层、网络层、传输层、应用层。每一层都有自己的功能和协议,实现了网络的简化和高效。 + +OSI 七层模型和 TCP/IP 四层模型都是为了实现网络的分层和模块化,提高网络的可靠性和可扩展性。 + +在实际应用中,会把 OSI 七层模型和 TCP/IP 四层模型结合起来变成五层理论模型,即将物理层、数据链路层、网络层、传输层和应用层。 + +## 讲讲在浏览器中输入 uri 到页面显示的过程 + +在浏览器中输入 URI 到页面显示的过程主要分为以下几个步骤: + +1. 输入 URI:用户在浏览器中输入 URI(Uniform Resource Identifier),浏览器会解析 URI,获取协议、主机、端口、路径等信息。 +2. DNS 解析:浏览器通过 DNS 解析获取主机的 IP 地址,浏览器会先查找浏览器缓存,如果没有找到,会搜索系统的 DNS 缓存,读取本地 Hosts 文件,还是没有就会向本地 DNS 服务器发送请求,本地 DNS 服务器会向根域名服务器、顶级域名服务器、权威域名服务器依次迭代查询,直到找到主机的 IP 地址。 +3. 建立连接:浏览器通过 TCP 三次握手与服务器建立连接,浏览器会向服务器发送一个 SYN 包,服务器收到后回复一个 SYN+ACK 包,浏览器再回复一个 ACK 包,完成连接的建立。 +4. 发送请求:浏览器向服务器发送 HTTP 请求,请求包括请求行、请求头、请求体等,请求行包括请求方法、URI、协议版本等。 +5. 接收响应:服务器接收到请求后,会处理请求,生成响应,响应包括响应行、响应头、响应体等,响应行包括协议版本、状态码、状态描述等。 +6. 渲染页面:浏览器接收到响应后,会解析 HTML、CSS、JavaScript 等资源,在主线程中构建 DOM 树,计算样式,生成布局树,然后构建图层树,生成绘制列表,最后将绘制列表交给 GPU 进行绘制,显示在屏幕上。 + +页面显示的过程主要是浏览器发送请求、服务器处理请求、浏览器接收响应、浏览器渲染页面的过程。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\276\350\256\241\346\250\241\345\274\217/index.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\276\350\256\241\346\250\241\345\274\217/index.md" new file mode 100644 index 0000000..88c8fb8 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\345\260\261\344\270\232/\350\256\241\347\256\227\346\234\272\345\270\270\350\200\203\345\205\253\350\202\241\346\226\207\346\200\273\347\273\223/\350\256\276\350\256\241\346\250\241\345\274\217/index.md" @@ -0,0 +1,551 @@ +**本模块是公共类课程,适合绝大部分计算机岗位。由于 Java 受众最广且易读性强,本模块示例代码使用 Java 编写,其他语言也可参考学习。** + +## 讲讲常见的设计模式 + +常见的设计模式主要有以下几种: + +1. 创建型模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。 +2. 结构型模式:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 +3. 行为型模式:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 + +设计模式是为了提高代码的可读性、可维护性、可扩展性,根据不同的需求选择合适的设计模式。 + +## 讲讲单例模式的实现方式(只需要讲懒汉式和饿汉式) + +单例模式是一种创建型设计模式,主要有以下两种实现方式: + +1. 懒汉式:懒汉式是在第一次调用时创建实例。 + +```java +public class Singleton { + private static Singleton instance; + + private Singleton() {} + + public static Singleton getInstance() { + if (instance == null) { + instance = new Singleton(); + } + return instance; + } +} +``` + +2. 饿汉式:饿汉式是在类加载时创建实例。 + +```java +public class Singleton { + private static Singleton instance = new Singleton(); + + private Singleton() {} + + public static Singleton getInstance() { + return instance; + } +} +``` + +单例模式是为了保证一个类只有一个实例,提高代码的可读性、可维护性、可扩展性,根据不同的需求选择合适的实现方式。 + +至于双重校验锁、静态内部类、枚举(只有 Java、Python 这种枚举实例是引用类型数据的情况才能用)等实现方式,可以根据需要进行了解,但面试大概率不会问,特别是枚举实现实际上是旁门左道,踩在编程语言的特性上歪打正着做文章,不要听八股文瞎扯。笔者写了一篇文章专门研究过枚举实现单例的问题,有兴趣的可以看看:[从源码角度告诉你为什么 Java 和 Python 可以用枚举类实现单例模式](https://juejin.cn/post/7398709913570820131) + +## 什么是工厂模式?有哪些实现方式? + +工厂模式是一种创建型设计模式,主要有以下几种实现方式: + +1. 简单工厂模式:简单工厂模式是通过一个工厂类来创建实例,根据不同的参数返回不同的实例。 + +```java +public class SimpleFactory { + public static Product createProduct(String type) { + if ("A".equals(type)) { + return new ProductA(); + } else if ("B".equals(type)) { + return new ProductB(); + } + return null; + } +} +``` + +2. 工厂方法模式:工厂方法模式是通过一个工厂接口来创建实例,每个实例对应一个工厂类。 + +```java +public interface Factory { + Product createProduct(); +} + +public class FactoryA implements Factory { + @Override + public Product createProduct() { + return new ProductA(); + } +} + +public class FactoryB implements Factory { + @Override + public Product createProduct() { + return new ProductB(); + } +} +``` + +3. 抽象工厂模式:抽象工厂模式是通过一个抽象工厂接口来创建实例,每个实例对应一个工厂类,每个工厂类对应多个实例。 + +```java +public interface AbstractFactory { + ProductA createProductA(); + ProductB createProductB(); +} + +public class FactoryA implements AbstractFactory { + @Override + public ProductA createProductA() { + return new ProductA(); + } + + @Override + public ProductB createProductB() { + return new ProductB(); + } +} + +public class FactoryB implements AbstractFactory { + @Override + public ProductA createProductA() { + return new ProductA(); + } + + @Override + public ProductB createProductB() { + return new ProductB(); + } +} +``` + +工厂模式是为了解耦对象的创建和使用,提高代码的可读性、可维护性、可扩展性,根据不同的需求选择合适的实现方式。 + +此外,SpringBoot 中一般用实现`BeanFactoryAware`接口的方式来实现工厂模式,因为 SpringBoot 框架中,绝大部分类的实例化都由框架完成。 + +```java +public class ProductFactory implements BeanFactoryAware { + private BeanFactory beanFactory; + + public Product createProduct(String type) { + return beanFactory.getBean(type, Product.class); + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } +} +``` + +## 什么是适配器模式? + +适配器模式是一种结构型设计模式,主要是为了解决接口不兼容的问题,提高代码的可读性、可维护性、可扩展性。 + +```java +// 第一步:定义接口 +public interface MediaPlayer { + void play(String audioType, String fileName); +} + +public interface AdvancedMediaPlayer { + void playVlc(String fileName); + void playMp4(String fileName); +} + +// 第二步:实现接口 +public class VlcPlayer implements AdvancedMediaPlayer { + @Override + public void playVlc(String fileName) { + System.out.println("Playing vlc file. Name: " + fileName); + } + + @Override + public void playMp4(String fileName) { + // do nothing + } +} + +public class Mp4Player implements AdvancedMediaPlayer { + @Override + public void playVlc(String fileName) { + // do nothing + } + + @Override + public void playMp4(String fileName) { + System.out.println("Playing mp4 file. Name: " + fileName); + } +} + +// 第三步:实现适配器 +public class MediaAdapter implements MediaPlayer { + private AdvancedMediaPlayer advancedMediaPlayer; + + public MediaAdapter(String audioType) { + if ("vlc".equalsIgnoreCase(audioType)) { + advancedMediaPlayer = new VlcPlayer(); + } else if ("mp4".equalsIgnoreCase(audioType)) { + advancedMediaPlayer = new Mp4Player(); + } + } + + @Override + public void play(String audioType, String fileName) { + if ("vlc".equalsIgnoreCase(audioType)) { + advancedMediaPlayer.playVlc(fileName); + } else if ("mp4".equalsIgnoreCase(audioType)) { + advancedMediaPlayer.playMp4(fileName); + } + } +} + +// 第四步:使用适配器 +public class AudioPlayer implements MediaPlayer { + private MediaAdapter mediaAdapter; + + @Override + public void play(String audioType, String fileName) { + if ("mp3".equalsIgnoreCase(audioType)) { + System.out.println("Playing mp3 file. Name: " + fileName); + } else if ("vlc".equalsIgnoreCase(audioType) || "mp4".equalsIgnoreCase(audioType)) { + mediaAdapter = new MediaAdapter(audioType); + mediaAdapter.play(audioType, fileName); + } else { + System.out.println("Invalid media. " + audioType + " format not supported"); + } + } +} +``` + +## 讲讲代理模式,以及和适配器模式的区别 + +代理模式是一种结构型设计模式,主要是为了控制对对象的访问,提高代码的可读性、可维护性、可扩展性。 + +```java +// 第一步:定义接口 +public interface Image { + void display(); +} + +// 第二步:实现接口 +public class RealImage implements Image { + private String fileName; + + public RealImage(String fileName) { + this.fileName = fileName; + loadFromDisk(fileName); + } + + @Override + public void display() { + System.out.println("Displaying " + fileName); + } + + private void loadFromDisk(String fileName) { + System.out.println("Loading " + fileName); + } +} + +// 第三步:实现代理 +public class ProxyImage implements Image { + private RealImage realImage; + private String fileName; + + public ProxyImage(String fileName) { + this.fileName = fileName; + } + + @Override + public void display() { + if (realImage == null) { + realImage = new RealImage(fileName); + } + realImage.display(); + } +} + +// 第四步:使用代理 +public class ProxyPatternDemo { + public static void main(String[] args) { + Image image = new ProxyImage("test.jpg"); + image.display(); + } +} +``` + +代理模式和适配器模式的区别主要有以下几点: + +1. 目的不同:代理模式是为了控制对对象的访问,适配器模式是为了解决接口不兼容的问题。 +2. 实现方式不同:代理模式是通过代理类来控制对对象的访问,适配器模式是通过适配器类来解决接口不兼容的问题。 +3. 使用场景不同:代理模式适合控制对对象的访问,适配器模式适合解决接口不兼容的问题。 + +## 讲讲装饰器模式,以及和代理模式、适配器模式的区别 + +装饰器模式是一种结构型设计模式,主要是为了动态地给对象添加新的功能,提高代码的可读性、可维护性、可扩展性。 + +```java +// 第一步:定义接口 +public interface Shape { + void draw(); +} + +// 第二步:实现接口 +public class Circle implements Shape { + @Override + public void draw() { + System.out.println("Shape: Circle"); + } +} + +// 第三步:实现装饰器 +public abstract class ShapeDecorator implements Shape { + protected Shape shape; + + public ShapeDecorator(Shape shape) { + this.shape = shape; + } + + @Override + public void draw() { + shape.draw(); + } +} + +public class RedShapeDecorator extends ShapeDecorator { + public RedShapeDecorator(Shape shape) { + super(shape); + } + + @Override + public void draw() { + shape.draw(); + setRedBorder(shape); + } + + private void setRedBorder(Shape shape) { + System.out.println("Border Color: Red"); + } +} + +// 第四步:使用装饰器 +public class DecoratorPatternDemo { + public static void main(String[] args) { + Shape circle = new Circle(); + Shape redCircle = new RedShapeDecorator(new Circle()); + Shape redRectangle = new RedShapeDecorator(new Rectangle()); + circle.draw(); + redCircle.draw(); + redRectangle.draw(); + } +} +``` + +装饰器模式和代理模式、适配器模式的区别主要有以下几点: + +1. 目的不同:装饰器模式是为了动态地给对象添加新的功能,代理模式是为了控制对对象的访问,适配器模式是为了解决接口不兼容的问题。 +2. 实现方式不同:装饰器模式是通过装饰器类来动态地给对象添加新的功能,代理模式是通过代理类来控制对对象的访问,适配器模式是通过适配器类来解决接口不兼容的问题。 +3. 使用场景不同:装饰器模式适合动态地给对象添加新的功能,代理模式适合控制对对象的访问,适配器模式适合解决接口不兼容的问题。 + +## 什么是外观模式? + +外观模式是一种结构型设计模式,主要是为了提供一个统一的接口,隐藏系统的复杂性,提高代码的可读性、可维护性、可扩展性。有些地方也称之为门面模式,这是机翻,听起来高大上但是面试官听不懂,最好还是叫外观模式。 + +```java +// 第一步:定义接口 +public interface Shape { + void draw(); +} + +public interface Color { + void fill(); +} + +// 第二步:实现接口 +public class Circle implements Shape { + @Override + public void draw() { + System.out.println("Shape: Circle"); + } +} + +public class Red implements Color { + @Override + public void fill() { + System.out.println("Color: Red"); + } +} + +// 第三步:实现外观 +public class ShapeMaker { + private Shape circle; + private Color red; + + public ShapeMaker() { + circle = new Circle(); + red = new Red(); + } + + public void drawCircle() { + circle.draw(); + } + + public void fillRed() { + red.fill(); + } +} + +// 第四步:使用外观 +public class FacadePatternDemo { + public static void main(String[] args) { + ShapeMaker shapeMaker = new ShapeMaker(); + shapeMaker.drawCircle(); + shapeMaker.fillRed(); + } +} +``` + +## 什么是策略模式? + +策略模式是一种行为型设计模式,主要是为了定义一系列算法,将每个算法封装起来,提供一个统一的接口,提高代码的可读性、可维护性、可扩展性。 + +```java +// 第一步:定义接口 +public interface Strategy { + int doOperation(int num1, int num2); +} + +// 第二步:实现接口 +public class OperationAdd implements Strategy { + @Override + public int doOperation(int num1, int num2) { + return num1 + num2; + } +} + +public class OperationSubtract implements Strategy { + @Override + public int doOperation(int num1, int num2) { + return num1 - num2; + } +} + +public class OperationMultiply implements Strategy { + @Override + public int doOperation(int num1, int num2) { + return num1 * num2; + } +} + +// 第三步:使用策略 +public class Context { + private Strategy strategy; + + public Context(Strategy strategy) { + this.strategy = strategy; + } + + public int executeStrategy(int num1, int num2) { + return strategy.doOperation(num1, num2); + } +} + +// 第四步:使用策略 +public class StrategyPatternDemo { + public static void main(String[] args) { + Context context = new Context(new OperationAdd()); + System.out.println("10 + 5 = " + context.executeStrategy(10, 5)); + context = new Context(new OperationSubtract()); + System.out.println("10 - 5 = " + context.executeStrategy(10, 5)); + context = new Context(new OperationMultiply()); + System.out.println("10 * 5 = " + context.executeStrategy(10, 5)); + } +} +``` + +## 什么是观察者模式? + +观察者模式是一种行为型设计模式,主要是为了定义一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知,提高代码的可读性、可维护性、可扩展性。 + +值得一提的是,我们在消息队列、事件总线(事件中心)的使用中常常提到的发布订阅模式,就是观察者模式的一种特殊实现。 + +```java +// 第一步:定义接口 +public abstract class Observer { + protected Subject subject; + public abstract void update(); +} + +public class Subject { + private List observers = new ArrayList<>(); + + public void attach(Observer observer) { + observers.add(observer); + } + + public void detach(Observer observer) { + observers.remove(observer); + } + + public void notifyAllObservers() { + for (Observer observer : observers) { + observer.update(); + } + } +} + +// 第二步:实现接口 +public class BinaryObserver extends Observer { + public BinaryObserver(Subject subject) { + this.subject = subject; + this.subject.attach(this); + } + + @Override + public void update() { + System.out.println("Binary String: " + Integer.toBinaryString(subject.getState())); + } +} + +public class OctalObserver extends Observer { + public OctalObserver(Subject subject) { + this.subject = subject; + this.subject.attach(this); + } + + @Override + public void update() { + System.out.println("Octal String: " + Integer.toOctalString(subject.getState())); + } +} + +public class HexaObserver extends Observer { + public HexaObserver(Subject subject) { + this.subject = subject; + this.subject.attach(this); + } + + @Override + public void update() { + System.out.println("Hex String: " + Integer.toHexString(subject.getState()).toUpperCase()); + } +} + +// 第三步:使用观察者 +public class ObserverPatternDemo { + public static void main(String[] args) { + Subject subject = new Subject(); + new BinaryObserver(subject); + new OctalObserver(subject); + new HexaObserver(subject); + System.out.println("First state change: 15"); + subject.setState(15); + System.out.println("Second state change: 10"); + subject.setState(10); + } +} +``` \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205.md" new file mode 100644 index 0000000..8d82ab3 --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205.md" @@ -0,0 +1,154 @@ +# 如何在 IDEA 手动配置 Tomcat 以及导入导出带 Java 源码的 war 包 + +作者:石皮幼鸟 [掘金](https://juejin.cn/user/3437526047270291) | [B 站](https://space.bilibili.com/30915729) + +## 前言,以及什么是 Tomcat 和 War 包 + +如果你的课表中恰好有一门课程名为“Web 应用程序设计”,那么你就来对地方了。 + +更巧的是,如果你是软工的学生,那么你的老师也会发一篇和本文一模一样的 Word 文档,其实那篇文档就是笔者写的这篇文章。 + +在这门课程中,学校会教一门极其落伍的技术——JSP,这是一种在服务器端生成动态网页的技术,但是这种技术已经被淘汰了,现在的前端开发都是用 React、Vue、Angular 等框架,后端开发都是用 SpringBoot、Express、Django 等框架,即便是前后端不分离架构,也是用 Thymeleaf、Freemarker 等模板引擎。 + +但很明显,这门课教的 JSP 技术并没有这么先进,所以你需要手动配置 Tomcat,然后将你的项目打包成一个 war 包,然后部署到 Tomcat 上。 + +那么,什么是 Tomcat,什么是 War 包呢? + +Tomcat 是一个开源的 Servlet 容器,是一个 JSP/Servlet 容器,是 Apache 软件基金会(Apache Software Foundation)的 Jakarta 项目中的一个核心项目,由 Apache、Sun 和其他一些公司及个人共同开发维护。 + +简单来讲,Tomcat 是一个服务器,它能够运行 JSP 和 Servlet,是一个 Java 的 Web 服务器。 + +War 包是一种 Web 应用程序的打包格式,它是一种压缩文件,其中包含了 Web 应用程序的所有内容,包括 HTML、JSP、Servlet、Java 类、XML、EJB、TLD、JAR、WAR 文件等。和你常常接触的 Jar 包类似,但是 War 包是专门用来部署 Web 应用程序的。在 War 包被打包好之后,你可以将 War 包部署到 Tomcat 上,然后 Tomcat 就会解压 War 包,然后运行你的 Web 应用程序。 + +当然,如果你接触过 SpringBoot,你可能会发出疑问,SpringBoot 项目也是 Java 的 Web 项目,为什么打包出来是 Jar 包而不是 War 包呢? + +这是因为 SpringBoot 实际上对大量技术进行了封装,比如,原本需要写大量 xml 配置文件的 Spring,现在只需要一个注解就能完成,设置包括原本需要配置的 Tomcat 也在内部封装好了,所以 SpringBoot 项目打包出来是 Jar 包,而不是 War 包。 + +在这门课当中,老师会要求你手动配置 Tomcat,导入老师发给你的包含 Java 源码的 War 包,然后修改 Java 源码,重新打包包含源码的 War 包,然后再次部署到 Tomcat 上。 + +由于大家现在常用的开发工具是 IDEA、VSCode 等,配置 Tomcat 以及导入导出 War 包与 Eclipse 的差距实在过大,特别是导入 War 包的功能在当前已不流行,故 IDEA 是完全不支持的。 + +绝大部分同学都会选择直接导入 Eclipse,但是 Eclipse 实在太丑太难用了,所以我写了这篇文章,帮助大家在 IDEA 中手动配置 Tomcat,导入导出 War 包。 + +## 如何导入包含源码的 War 包,并配置 Tomcat + +IDEA 不能直接导入 war 包,比较麻烦,想要导入 war 包只能手动操作。 + +首先,将下载好的 war 包后缀名改为 rar,备用。 + +![pic01](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic01.png) + +接下来在 idea 中新建一个 web 应用项目,我们在生成器栏里选择 Jakarta EE(旧版本是 Java Enterprise),模板设置为 Web 应用程序,应用程序服务器设置为 Tomcat,如果没有,点击右边的新建按钮,选择我们解压好的 Tomcat 文件夹即可。 + +![pic02](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic02.png) + +单击下一步,确认在规范目录下已经勾选好 Servlet,单击创建。 + +![pic03](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic03.png) + +这是项目的基础目录结构。 + +![pic04](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic04.png) + +删除 webapp 文件夹下的所有内容,然后把 rar 里所有文件丢到 webapp 下。 + +![pic05](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic05.png) + +接下来处理 WEB-INF 文件夹的内容。删除`/src/main/java`文件夹下的内容,把 classes 文件夹里的.java 文件移到`/src/main/java`里面。(注意,没有特殊要求不要把 class 文件丢进去,你可以放进去之后手动删掉 class 文件) + +![pic06](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic06.png) + +接下来右击项目名称,点击打开模块设置。当然,你也可以通过 文件 - 项目结构 - 项目设置 - 模块 找到接下来的操作界面。 + +![pic07](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic07.png) + +点击“依赖”,选择“JAR 或目录…”,然后选中项目内 WEB-INF 文件夹里面的 lib 文件夹,添加后点击确定即可。 + +![pic08](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic08.png) +![pic09](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic09.png) +![pic10](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic10.png) + +现在我们来配置运行 web 项目。右上角运行按钮旁边应该会出现一个 Tomcat 服务器的运行配置。 + +![pic11](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic11.png) + +不管有没有,点开下拉栏,点击编辑配置。 + +![pic12](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic12.png) + +如果没有,就点击左上角“+”添加新配置,点击 Tomcat 服务器 - 本地。 + +![pic13](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic13.png) + +右侧栏目中,在“应用程序服务器”这一行点击配置,选择 Tomcat 解压的文件夹即可。 + +![pic14](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic14.png) + +然后单击“部署”,在“在服务器启动时部署”栏中应该会出现一个已经配置好的工件。 + +![pic15](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic15.png) + +如果没有,点击“+”然后选择工件即可。如果没有工件选项,需要去项目结构自行配置,这说明前面的流程你没有跟着我的教程走,idea 没有识别到你的 web.xml。 + +![pic16](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic16.png) + +现在点击运行,网页应该就可以正常跑起来了。 + +![pic17](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic17.png) + +## 如何导出包含源码的 War 包 + +由于我们创建的是 Maven 项目,而 Maven 已经为我们提供了轻松导出 war 包的指令,所以导出 war 包并非难事。创建项目后 IDEA 应该会自动帮我们配置 Maven,如果没有或者配置不了,可以参考这个视频:[教程](https://www.bilibili.com/video/BV16Q4y127BZ/) + +在 Maven 项目中,pom.xml(Project Object Model)对于项目的配置起着主导控制作用,我们有空可以百度自学简单了解 Maven 项目中 pom.xml 的结构与功能。 + +![pic18](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic18.png) + +我们打开项目的 pom.xml,重点看到 packaging 标签。这个标签管理 maven 项目的导出格式,有 jar、war、ear 和 pom 四种。大部分项目默认是 jar,这意味着项目会导出成 jar 包,我们把里面的值改成 war,像图中这样即可。 + +![pic19](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic19.png) + +接下来我们打开 idea 的终端,或者在 cmd 中使用 cd 指令跳转到项目目录,输入指令: + +```shell +mvn clean package +``` + +(这条指令是 clean 和 package 的组合,clean 用于清除 target 生成的内容,package 用于生成内容) + +![pic20](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic20.png) + +等待片刻,接下来我们可以看到在项目根目录/target 文件夹下出现了打包好的 war 文件。 + +![pic21](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic21.png) + +这个时候我们会发现,导出的 war 包里面不包含我们在`src/main/java`文件夹下的 java 文件,只包含生成好的 class 文件,这是为什么呢? + +这是因为,默认情况下,maven 只把我们的 resources 文件夹和 webapp 文件夹当成了资源文件夹处理。我们继续处理 pom.xml。 + +在 pom 结尾会出现 build 标签。 + +![pic22](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic22.png) + +在``标签中加入如下内容:(包括 springboot 项目也可以这样做) + +```xml + + + src/main/java + + **/*.java + + + + + +``` + +![pic23](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic23.png) + +现在我们重新执行`mvn clean package`指令。执行完后我们会发现,在生成的 war 包的`/WEB_INF/classes`里面有我们需要的 java 代码了。(这里我拿我自己写的 spring 项目生成的文件演示) + +![pic24](./如何在IDEA手动配置Tomcat以及导入导出带Java源码的war包/pic24.png) + +这样我们就成功导出了包含源码的 war 包。 \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic01.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic01.png" new file mode 100644 index 0000000..cef2826 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic01.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic02.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic02.png" new file mode 100644 index 0000000..fc7325a Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic02.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic03.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic03.png" new file mode 100644 index 0000000..dd03a78 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic03.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic04.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic04.png" new file mode 100644 index 0000000..a3b9737 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic04.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic05.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic05.png" new file mode 100644 index 0000000..b575965 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic05.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic06.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic06.png" new file mode 100644 index 0000000..2449d99 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic06.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic07.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic07.png" new file mode 100644 index 0000000..863ada3 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic07.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic08.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic08.png" new file mode 100644 index 0000000..95f6b9e Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic08.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic09.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic09.png" new file mode 100644 index 0000000..9c74ad1 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic09.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic10.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic10.png" new file mode 100644 index 0000000..c8eb9bd Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic10.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic11.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic11.png" new file mode 100644 index 0000000..a33f53a Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic11.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic12.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic12.png" new file mode 100644 index 0000000..fea00ad Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic12.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic13.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic13.png" new file mode 100644 index 0000000..2c8ed22 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic13.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic14.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic14.png" new file mode 100644 index 0000000..86df772 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic14.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic15.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic15.png" new file mode 100644 index 0000000..5ff0971 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic15.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic16.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic16.png" new file mode 100644 index 0000000..16090ea Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic16.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic17.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic17.png" new file mode 100644 index 0000000..57ddaa1 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic17.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic18.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic18.png" new file mode 100644 index 0000000..8ba6f69 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic18.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic19.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic19.png" new file mode 100644 index 0000000..829f8a3 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic19.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic20.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic20.png" new file mode 100644 index 0000000..6b2647b Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic20.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic21.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic21.png" new file mode 100644 index 0000000..224da10 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic21.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic22.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic22.png" new file mode 100644 index 0000000..d1681cc Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic22.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic23.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic23.png" new file mode 100644 index 0000000..8feec69 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic23.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic24.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic24.png" new file mode 100644 index 0000000..0d43fe5 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/Web\345\272\224\347\224\250\347\250\213\345\272\217\350\256\276\350\256\241/\345\246\202\344\275\225\345\234\250IDEA\346\211\213\345\212\250\351\205\215\347\275\256Tomcat\344\273\245\345\217\212\345\257\274\345\205\245\345\257\274\345\207\272\345\270\246Java\346\272\220\347\240\201\347\232\204war\345\214\205/pic24.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266.md" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266.md" new file mode 100644 index 0000000..3c9653e --- /dev/null +++ "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266.md" @@ -0,0 +1,100 @@ +# IDEA 如何连接数据库,执行 SQL 文件 + +作者:石皮幼鸟 [掘金](https://juejin.cn/user/3437526047270291) | [B 站](https://space.bilibili.com/30915729) + +## 前言 + +如果你的课表中恰好有一门课程名为“数据库应用技术”,那么你就来对地方了。 + +更巧的是,如果你是软工的学生,那么你的老师也会发一篇和本文一模一样的 Word 文档,其实那篇文档就是笔者写的这篇文章。 + +你是不是发现自己好像读过一模一样的话?没错,因为 Web 应用程序设计的那篇文章也是笔者写的。或许你在学校课程中遇到的很多旧技术新用教程,都是笔者在其中充当适配器角色而编写的。 + +同样,这篇文章也适合拥有逆反心理的你。 + +这门课将会教学一门名为 Oracle 的数据库,这个数据库极其怪异,在本地部署时可以选择非容器数据库部署,但是在服务器上部署时必须选择容器数据库部署。 + +但容器数据库情况下会遇到诸如连接失败、权限不足等问题,在服务器上不断折腾的你可能会因此破大防。 + +笔者在破大防之后还是硬生生吃下了这坨屎,解决了问题,所以在你放弃并部署到本地之前,还请耐心看完这篇文章。 + +当然,MySQL 等数据库也是可以连接的,本地和服务器上的皆可。 + +只是笔者在这里以 Oracle 为例,IDEA 可以连接 MySQL、SQLServer、PostgreSQL、MongoDB、Redis 等数据库,甚至连本地嵌入式数据库 SQLite 都可以连接。 +(典中典之 Realm 没人权) + +## 如何连接数据库 + +别用 Navicat,要钱的,而且不好用,IDEA 自带的数据库连接工具就够用了,当然前提是你的 IDEA 是专业版。 + +在 IDEA 右侧栏数据库按钮中连接数据库。 + +旧版 UI: + +![pic01](./IDEA如何连接数据库,执行SQL文件/pic01.png) + +新版 UI: + +![pic02](./IDEA如何连接数据库,执行SQL文件/pic02.png) + +- 在弹出的窗口当中设置数据库信息,记得先设置成全局数据源,这样切换项目时也能继续使用这个数据库。 +- 主机这一栏填写 ip 地址,如果是跑在本地上就写 localhost。Oracle 端口默认是 1521。 +- 如果使用的不是容器数据库,sid 这里填写默认的 sid 就写,比如 ORCL,反之,这里先不填。 +- 驱动程序这里选择 Thin 就行,用户名密码也不多说了。 +- 如果用的是容器数据库,在 url 这一栏,把图中我标注的冒号改成“/”,后面跟自己的 pdb 名称(这个时候你会发现 sid 那一栏也跟着变了)。 +- 如何查询这里可以用的 pdb,其实也不难,在 SQLplus 当中使用 show pdbs;指令,查看本地的 pdb 情况,有 read write 权限的 pdb 就可以直接拿来用,反之需要你自己新建一个 pdb。 + +![pic03](./IDEA如何连接数据库,执行SQL文件/pic03.png) +![pic04](./IDEA如何连接数据库,执行SQL文件/pic04.png) + +配置完成后,点击下载驱动按钮,下载成功后点击测试连接,如果一切正常,我们就可以进入下一步了。倘若下载速度较慢,可以尝试挂代理下载,因为资源服务器在国外。 + +如何辨别自己用的是不是容器数据库?在新建用户时,如果报出下面的错误,那你用的一定就是容器数据库。 + +![pic05](./IDEA如何连接数据库,执行SQL文件/pic05.png) + +至于 PDB 是什么,CDB 是什么,SQLplus 是什么,如何新建 pdb,那又是长篇大论,网上有教程,这里就不再细说了。有兴趣可以看看这篇文章:[教程](https://blog.csdn.net/weixin_47315082/article/details/131922971) + +## 如何执行 SQL 文件 + +把 sql 文件随便拖进一个项目目录里面。 + +![pic06](./IDEA如何连接数据库,执行SQL文件/pic06.png) + +打开过后,应该会出现上面这一栏。 + +![pic07](./IDEA如何连接数据库,执行SQL文件/pic07.png) + +如果没有,就右击,在弹窗中选择附加会话,然后选中 Oracle。 + +![pic08](./IDEA如何连接数据库,执行SQL文件/pic08.png) + +如果有,在右上角的 MySQL 那里切换成 Oracle 或者 Oracle SQLplus,会话这边选择默认控制台或者新建一个都行。 + +![pic09](./IDEA如何连接数据库,执行SQL文件/pic09.png) +![pic10](./IDEA如何连接数据库,执行SQL文件/pic10.png) + +现在右击非注释或空行的语句就可以执行了,这里会出现两行选项,意思从上到下分别是提问你需要执行单条语句还是全文。这是 IDEA 提供的一个非常人性化的功能,在测试 SQL 语句是否正确的时候,单条执行非常快速方便。 + +![pic11](./IDEA如何连接数据库,执行SQL文件/pic11.png) +![pic12](./IDEA如何连接数据库,执行SQL文件/pic12.png) + +MySQL 等其他类型数据库同理。此外,包括 MongoDB 这类以 JavaScript 为操作语言的非关系型数据库都可以通过这样的方式执行数据库脚本。 + +在 IDEA 的数据库工具侧栏点击这个按钮就可以打开默认控制台编写语句,非常方便。 + +![pic13](./IDEA如何连接数据库,执行SQL文件/pic13.png) +![pic14](./IDEA如何连接数据库,执行SQL文件/pic14.png) + + \ No newline at end of file diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic01.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic01.png" new file mode 100644 index 0000000..7dfb297 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic01.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic02.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic02.png" new file mode 100644 index 0000000..67a6ef0 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic02.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic03.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic03.png" new file mode 100644 index 0000000..00fc701 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic03.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic04.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic04.png" new file mode 100644 index 0000000..4857d57 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic04.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic05.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic05.png" new file mode 100644 index 0000000..161468d Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic05.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic06.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic06.png" new file mode 100644 index 0000000..b2ed961 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic06.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic07.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic07.png" new file mode 100644 index 0000000..7c09380 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic07.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic08.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic08.png" new file mode 100644 index 0000000..707a284 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic08.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic09.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic09.png" new file mode 100644 index 0000000..290be51 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic09.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic10.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic10.png" new file mode 100644 index 0000000..210216a Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic10.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic11.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic11.png" new file mode 100644 index 0000000..7a22a30 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic11.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic12.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic12.png" new file mode 100644 index 0000000..28bb829 Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic12.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic13.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic13.png" new file mode 100644 index 0000000..ede994c Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic13.png" differ diff --git "a/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic14.png" "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic14.png" new file mode 100644 index 0000000..e33fc9e Binary files /dev/null and "b/\345\255\246\344\271\240\347\233\270\345\205\263/\350\257\276\345\211\215\345\277\205\347\234\213/\346\225\260\346\215\256\345\272\223\345\272\224\347\224\250\346\212\200\346\234\257/IDEA\345\246\202\344\275\225\350\277\236\346\216\245\346\225\260\346\215\256\345\272\223\357\274\214\346\211\247\350\241\214SQL\346\226\207\344\273\266/pic14.png" differ diff --git "a/\345\256\236\351\252\214\345\256\244/\345\233\236\345\243\260\345\256\236\351\252\214\345\256\244/epochLab.md" "b/\345\256\236\351\252\214\345\256\244/\345\233\236\345\243\260\345\256\236\351\252\214\345\256\244/epochLab.md" index 52cff3b..d34000a 100644 --- "a/\345\256\236\351\252\214\345\256\244/\345\233\236\345\243\260\345\256\236\351\252\214\345\256\244/epochLab.md" +++ "b/\345\256\236\351\252\214\345\256\244/\345\233\236\345\243\260\345\256\236\351\252\214\345\256\244/epochLab.md" @@ -29,18 +29,18 @@ ## 实验室成果 -* 近几年就业形势发生严峻变化,但是只要你足够努力足够卷,还有进大厂以及毕业了获得满意 offer 的"一线生机"。 我们实验室有已经毕业了的学长学姐的内推机会。 +* 近几年就业形势发生严峻变化,但是只要你足够努力足够卷,还有进大厂以及毕业了获得满意 offer 的"一线生机"。我们实验室有已经毕业了的学长学姐的内推机会。 * 部分有统计的就业情况 - * 腾讯x1 - * 阿里巴巴x1 - * 美团x2 - * 字节跳动x3 - * 滴滴x1 - * 贝壳x2 - * 青云科技x2 - * 小黑盒x1 + * 腾讯 x1 + * 阿里巴巴 x1 + * 美团 x2 + * 字节跳动 x3 + * 滴滴 x1 + * 贝壳 x2 + * 青云科技 x2 + * 小黑盒 x1 * 部分有统计的获奖情况: @@ -66,7 +66,7 @@ ## 团建 -我们每年会在五一节举行团建,在轰趴馆进行彻夜狂欢! +我们每年会在五一节举行团建,在轰趴馆进行彻夜狂欢! 2024 团建合照: @@ -87,7 +87,7 @@ ## 我们的招新流程 招新群:834932567 -二维码: +二维码: @@ -100,9 +100,9 @@ 学期初我们会有一个招新宣讲会,主要是为了帮助各位对于开发,管理等等我们实验室的业务不太了解的小伙伴们答疑解惑。欢迎各位小杏仁来参加 - 招新宣讲一个半月后,分别对新生进行考核面试,面试通过者进入回声实验室新星组(对专业知识要求不高,主要针对学习的态度,积极性以及平时的态度进行考核) -- 在一年的培养学习后对外围成员进行第二次考核面试,面试通过者进入回声实验室核心组。(对专业知识有一定的要求,需要完成自己所学方向的相应技术,并且对实验室有归属感) +- 在一年的培养学习后对外围成员进行第二次考核面试,面试通过者进入回声实验室核心组。(对专业知识有一定的要求,需要完成自己所学方向的相应技术,并且对实验室有归属感) ### 培养方案 -- 在招新宣讲之后,根据新生人数按组分配给前后端各小组组长,通过小组的学习方式让小组成员和各个组长交流学习; 同时每周根据新生的学习进度布置一些小题目供大家练手,并且在每周日进行答疑(答疑时间和方式由组长自己决定) -- 在通过面试之后,将新生拉入回声实验室新星组的大群,并且会布置一些学习任务以及规定学习进度(不强制要求必须做),这段时间新星成员的表现会计入核心组的考核面试中,包括新星成员的学习兴趣以及热情程度 +- 在招新宣讲之后,根据新生人数按组分配给前后端各小组组长,通过小组的学习方式让小组成员和各个组长交流学习; 同时每周根据新生的学习进度布置一些小题目供大家练手,并且在每周日进行答疑 (答疑时间和方式由组长自己决定) +- 在通过面试之后,将新生拉入回声实验室新星组的大群,并且会布置一些学习任务以及规定学习进度 (不强制要求必须做),这段时间新星成员的表现会计入核心组的考核面试中,包括新星成员的学习兴趣以及热情程度