首页 文章 精选 留言 我的

精选列表

搜索[面试],共4916篇文章
优秀的个人博客,低调大师

Java面试中常问的Spring方面问题(涵七大方向共55道题,含答案)

Spring Framework 现在几乎已成为 Java Web 开发的标配框架。那么,作为 Java 程序员,你对 Spring 的主要技术点又掌握了多少呢?不妨用本文的问题来检测一下。 1、一般问题 1.1. 不同版本的 Spring Framework 有哪些主要功能? VersionFeatureSpring 2.5发布于 2007 年。这是第一个支持注解的版本。Spring 3.0发布于 2009 年。它完全利用了 Java5 中的改进,并为 JEE6 提供了支持。Spring 4.0发布于 2013 年。这是第一个完全支持 JAVA8 的版本。 1.2. 什么是 Spring Framework? Spring 是一个开源应用框架,旨在降低应用程序开发的复杂度。 它是轻量级、松散耦合的。 它具有分层体系结构,允许用户选择组件,同时还为 J2EE 应用程序开发提供了一个有凝聚力的框架。 它可以集成其他框架,如 Structs、Hibernate、EJB 等,所以又称为框架的框架。 1.3. 列举 Spring Framework 的优点。 由于 Spring Frameworks 的分层架构,用户可以自由选择自己需要的组件。 Spring Framework 支持 POJO(Plain Old Java Object) 编程,从而具备持续集成和可测试性。 由于依赖注入和控制反转,JDBC 得以简化。 它是开源免费的。 1.4. Spring Framework 有哪些不同的功能? 轻量级 - Spring 在代码量和透明度方面都很轻便。 IOC - 控制反转 AOP - 面向切面编程可以将应用业务逻辑和系统服务分离,以实现高内聚。 容器 - Spring 负责创建和管理对象(Bean)的生命周期和配置。 MVC - 对 web 应用提供了高度可配置性,其他框架的集成也十分方便。 事务管理 - 提供了用于事务管理的通用抽象层。Spring 的事务支持也可用于容器较少的环境。 JDBC 异常 - Spring 的 JDBC 抽象层提供了一个异常层次结构,简化了错误处理策略。 1.5. Spring Framework 中有多少个模块,它们分别是什么? 欢迎加入QQ群架构华山论剑:836442475 Spring 核心容器 – 该层基本上是 Spring Framework 的核心。它包含以下模块: Spring Core Spring Bean SpEL (Spring Expression Language) Spring Context 数据访问/集成 – 该层提供与数据库交互的支持。它包含以下模块: JDBC (Java DataBase Connectivity) ORM (Object Relational Mapping) OXM (Object XML Mappers) JMS (Java Messaging Service) Transaction Web – 该层提供了创建 Web 应用程序的支持。它包含以下模块: Web Web – Servlet Web – Socket Web – Portlet AOP– 该层支持面向切面编程 Instrumentation– 该层为类检测和类加载器实现提供支持。 Test– 该层为使用 JUnit 和 TestNG 进行测试提供支持。 几个杂项模块: Messaging – 该模块为 STOMP 提供支持。它还支持注解编程模型,该模型用于从 WebSocket 客户端路由和处理 STOMP 消息。 Aspects – 该模块为与 AspectJ 的集成提供支持。 1.6. 什么是 Spring 配置文件? Spring 配置文件是 XML 文件。该文件主要包含类信息。它描述了这些类是如何配置以及相互引入的。但是,XML 配置文件冗长且更加干净。如果没有正确规划和编写,那么在大项目中管理变得非常困难。 1.7. Spring 应用程序有哪些不同组件? Spring 应用一般有以下组件: 接口- 定义功能。 Bean 类- 它包含属性,setter 和 getter 方法,函数等。 Spring 面向切面编程(AOP)- 提供面向切面编程的功能。 Bean 配置文件- 包含类的信息以及如何配置它们。 用户程序- 它使用接口。 1.8. 使用 Spring 有哪些方式? 使用 Spring 有以下方式: 作为一个成熟的 Spring Web 应用程序。 作为第三方 Web 框架,使用 Spring Frameworks 中间层。 用于远程使用。 作为企业级 Java Bean,它可以包装现有的 POJO(Plain Old Java Objects)。 2、依赖注入(Ioc) 2.1. 什么是 Spring IOC 容器? Spring 框架的核心是 Spring 容器。容器创建对象,将它们装配在一起,配置它们并管理它们的完整生命周期。Spring 容器使用依赖注入来管理组成应用程序的组件。容器通过读取提供的配置元数据来接收对象进行实例化,配置和组装的指令。该元数据可以通过 XML,Java 注解或 Java 代码提供。 私信555获取免费架构资料 2.2. 什么是依赖注入? 在依赖注入中,您不必创建对象,但必须描述如何创建它们。您不是直接在代码中将组件和服务连接在一起,而是描述配置文件中哪些组件需要哪些服务。由 IoC 容器将它们装配在一起。 2.3. 可以通过多少种方式完成依赖注入? 通常,依赖注入可以通过三种方式完成,即: 构造函数注入 setter 注入 接口注入 在 Spring Framework 中,仅使用构造函数和 setter 注入。 2.4. 区分构造函数注入和 setter 注入。 构造函数注入setter 注入没有部分注入有部分注入不会覆盖 setter 属性会覆盖 setter 属性任意修改都会创建一个新实例任意修改不会创建一个新实例适用于设置很多属性适用于设置少量属性 2.5. spring 中有多少种 IOC 容器? BeanFactory - BeanFactory 就像一个包含 bean 集合的工厂类。它会在客户端要求时实例化 bean。 ApplicationContext - ApplicationContext 接口扩展了 BeanFactory 接口。它在 BeanFactory 基础上提供了一些额外的功能。 2.6. 区分 BeanFactory 和 ApplicationContext。 BeanFactoryApplicationContext它使用懒加载它使用即时加载它使用语法显式提供资源对象它自己创建和管理资源对象不支持国际化支持国际化不支持基于依赖的注解支持基于依赖的注解 2.7. 列举 IoC 的一些好处。 IoC 的一些好处是: 它将最小化应用程序中的代码量。 它将使您的应用程序易于测试,因为它不需要单元测试用例中的任何单例或 JNDI 查找机制。 它以最小的影响和最少的侵入机制促进松耦合。 它支持即时的实例化和延迟加载服务。 2.8. Spring IoC 的实现机制。 Spring 中的 IoC 的实现原理就是工厂模式加反射机制。 示例: 欢迎加入QQ群架构华山论剑:836442475 3. Beans 3.1. 什么是 spring bean? 它们是构成用户应用程序主干的对象。 Bean 由 Spring IoC 容器管理。 它们由 Spring IoC 容器实例化,配置,装配和管理。 Bean 是基于用户提供给容器的配置元数据创建。 3.2. spring 提供了哪些配置方式? 基于 xml 配置 bean 所需的依赖项和服务在 XML 格式的配置文件中指定。这些配置文件通常包含许多 bean 定义和特定于应用程序的配置选项。它们通常以 bean 标签开头。例如: 基于注解配置 您可以通过在相关的类,方法或字段声明上使用注解,将 bean 配置为组件类本身,而不是使用 XML 来描述 bean 装配。默认情况下,Spring 容器中未打开注解装配。因此,您需要在使用它之前在 Spring 配置文件中启用它。例如: 基于 Java API 配置 Spring 的 Java 配置是通过使用 @Bean 和 @Configuration 来实现。 @Bean 注解扮演与 元素相同的角色。 @Configuration 类允许通过简单地调用同一个类中的其他 @Bean 方法来定义 bean 间依赖关系。 例如: @ConfigurationpublicclassStudentConfig{@BeanpublicStudentBeanmyStudent(){returnnewStudentBean(); } } 3.3. spring 支持集中 bean scope? Spring bean 支持 5 种 scope: Singleton- 每个 Spring IoC 容器仅有一个单实例。 Prototype- 每次请求都会产生一个新的实例。 Request- 每一次 HTTP 请求都会产生一个新的实例,并且该 bean 仅在当前 HTTP 请求内有效。 Session- 每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效。 Global-session- 类似于标准的 HTTP Session 作用域,不过它仅仅在基于 portlet 的 web 应用中才有意义。Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portlet 所共享。在 global session 作用域中定义的 bean 被限定于全局 portlet Session 的生命周期范围内。如果你在 web 中使用 global session 作用域来标识 bean,那么 web 会自动当成 session 类型来使用。 仅当用户使用支持 Web 的 ApplicationContext 时,最后三个才可用。 3.4. spring bean 容器的生命周期是什么样的? spring bean 容器的生命周期流程如下: Spring 容器根据配置中的 bean 定义中实例化 bean。 Spring 使用依赖注入填充所有属性,如 bean 中所定义的配置。 如果 bean 实现 BeanNameAware 接口,则工厂通过传递 bean 的 ID 来调用 setBeanName()。 如果 bean 实现 BeanFactoryAware 接口,工厂通过传递自身的实例来调用 setBeanFactory()。 如果存在与 bean 关联的任何 BeanPostProcessors,则调用 preProcessBeforeInitialization() 方法。 如果为 bean 指定了 init 方法( 的 init-method 属性),那么将调用它。 最后,如果存在与 bean 关联的任何 BeanPostProcessors,则将调用 postProcessAfterInitialization() 方法。 如果 bean 实现 DisposableBean 接口,当 spring 容器关闭时,会调用 destory()。 如果为 bean 指定了 destroy 方法( 的 destroy-method 属性),那么将调用它。 欢迎加入QQ群架构华山论剑:836442475 3.5. 什么是 spring 的内部 bean? 只有将 bean 用作另一个 bean 的属性时,才能将 bean 声明为内部 bean。为了定义 bean,Spring 的基于 XML 的配置元数据在 或 中提供了 元素的使用。内部 bean 总是匿名的,它们总是作为原型。 例如,假设我们有一个 Student 类,其中引用了 Person 类。这里我们将只创建一个 Person 类实例并在 Student 中使用它。 Student.java public class Student { private Person person; //Setters and Getters } public class Person { private String name; private String address; //Setters and Getters } bean.xml 3.6. 什么是 spring 装配 当 bean 在 Spring 容器中组合在一起时,它被称为装配或 bean 装配。 Spring 容器需要知道需要什么 bean 以及容器应该如何使用依赖注入来将 bean 绑定在一起,同时装配 bean。 3.7. 自动装配有哪些方式? Spring 容器能够自动装配 bean。也就是说,可以通过检查 BeanFactory 的内容让 Spring 自动解析 bean 的协作者。 自动装配的不同模式: no- 这是默认设置,表示没有自动装配。应使用显式 bean 引用进行装配。 byName- 它根据 bean 的名称注入对象依赖项。它匹配并装配其属性与 XML 文件中由相同名称定义的 bean。 byType- 它根据类型注入对象依赖项。如果属性的类型与 XML 文件中的一个 bean 名称匹配,则匹配并装配属性。 构造函数- 它通过调用类的构造函数来注入依赖项。它有大量的参数。 autodetect- 首先容器尝试通过构造函数使用 autowire 装配,如果不能,则尝试通过 byType 自动装配。 3.8. 自动装配有什么局限? 覆盖的可能性 - 您始终可以使用 和 设置指定依赖项,这将覆盖自动装配。 基本元数据类型 - 简单属性(如原数据类型,字符串和类)无法自动装配。 令人困惑的性质 - 总是喜欢使用明确的装配,因为自动装配不太精确。 4、注解 4.1. 什么是基于注解的容器配置 不使用 XML 来描述 bean 装配,开发人员通过在相关的类,方法或字段声明上使用注解将配置移动到组件类本身。它可以作为 XML 设置的替代方案。例如: Spring 的 Java 配置是通过使用 @Bean 和 @Configuration 来实现。 @Bean 注解扮演与 元素相同的角色。 @Configuration 类允许通过简单地调用同一个类中的其他 @Bean 方法来定义 bean 间依赖关系。 例如: @ConfigurationpublicclassStudentConfig{@BeanpublicStudentBeanmyStudent(){returnnewStudentBean(); } } 4.2. 如何在 spring 中启动注解装配? 默认情况下,Spring 容器中未打开注解装配。因此,要使用基于注解装配,我们必须通过配置 元素在 Spring 配置文件中启用它。 4.3. @Component, @Controller, @Repository, @Service 有何区别? @Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。 @Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中。 @Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。 @Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。 4.4. @Required 注解有什么用? @Required 应用于 bean 属性 setter 方法。此注解仅指示必须在配置时使用 bean 定义中的显式属性值或使用自动装配填充受影响的 bean 属性。如果尚未填充受影响的 bean 属性,则容器将抛出 BeanInitializationException。 示例: publicclassEmployee{privateString name;@RequiredpublicvoidsetName(String name){this.name=name; }publicstringgetName(){returnname; } } 4.5. @Autowired 注解有什么用? @Autowired 可以更准确地控制应该在何处以及如何进行自动装配。此注解用于在 setter 方法,构造函数,具有任意名称或多个参数的属性或方法上自动装配 bean。默认情况下,它是类型驱动的注入。 publicclassEmployee{privateString name;@AutowiredpublicvoidsetName(String name){this.name=name; }publicstringgetName(){returnname; } } 4.6. @Qualifier 注解有什么用? 当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。 例如,这里我们分别有两个类,Employee 和 EmpAccount。在 EmpAccount 中,使用@Qualifier 指定了必须装配 id 为 emp1 的 bean。 Employee.javapublicclassEmployee{privateString name;@AutowiredpublicvoidsetName(String name){this.name=name; }publicstringgetName(){returnname; } } EmpAccount.javapublicclassEmpAccount{privateEmployee emp;@Autowired@Qualifier(emp1)publicvoidshowName(){ System.out.println(“Employee name : ”+emp.getName); } } 4.7. @RequestMapping 注解有什么用? @RequestMapping 注解用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别: 类级别:映射请求的 URL 方法级别:映射 URL 以及 HTTP 请求方法 5、数据访问 私信555获取免费架构资料 5.1. spring DAO 有什么用? Spring DAO 使得 JDBC,Hibernate 或 JDO 这样的数据访问技术更容易以一种统一的方式工作。这使得用户容易在持久性技术式工作。这使得用户容易在持久性技术之间切换。它还允许您在编写代码时,无需考虑捕获每种技术不同的异常。 5.2. 列举 Spring DAO 抛出的异常。 欢迎加入QQ群架构华山论剑:836442475 5.3. spring JDBC API 中存在哪些类? JdbcTemplate SimpleJdbcTemplate NamedParameterJdbcTemplate SimpleJdbcInsert SimpleJdbcCall 5.4. 使用 Spring 访问 Hibernate 的方法有哪些? 我们可以通过两种方式使用 Spring 访问 Hibernate: 使用 Hibernate 模板和回调进行控制反转 扩展 HibernateDAOSupport 并应用 AOP 拦截器节点 5.5. 列举 spring 支持的事务管理类型 Spring 支持两种类型的事务管理: 程序化事务管理:在此过程中,在编程的帮助下管理事务。它为您提供极大的灵活性,但维护起来非常困难。 声明式事务管理:在此,事务管理与业务代码分离。仅使用注解或基于 XML 的配置来管理事务。 5.6. spring 支持哪些 ORM 框架 Hibernate iBatis JPA JDO OJB 6、AOP 6.1. 什么是 AOP? AOP(Aspect-Oriented Programming), 即 面向切面编程 , 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角. 在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面) 6.2. 什么是 Aspect? aspect 由 pointcount 和 advice 组成, 它既包含了横切逻辑的定义, 也包括了连接点的定义. Spring AOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑编织到切面所指定的连接点中. AOP 的工作重心在于如何将增强编织目标对象的连接点上, 这里包含两个工作: 如何通过 pointcut 和 advice 定位到特定的 joinpoint 上 如何在 advice 中编写切面代码. 可以简单地认为, 使用 @Aspect 注解的类就是切面. 6.3. 什么是切点(JoinPoint) 程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理. 在 Spring AOP 中, join point 总是方法的执行点。 6.4. 什么是通知(Advice)? 特定 JoinPoint 处的 Aspect 所采取的动作称为 Advice。Spring AOP 使用一个 Advice 作为拦截器,在 JoinPoint “周围”维护一系列的拦截器。 6.5. 有哪些类型的通知(Advice)? Before- 这些类型的 Advice 在 joinpoint 方法之前执行,并使用 @Before 注解标记进行配置。 After Returning- 这些类型的 Advice 在连接点方法正常执行后执行,并使用@AfterReturning 注解标记进行配置。 After Throwing- 这些类型的 Advice 仅在 joinpoint 方法通过抛出异常退出并使用 @AfterThrowing 注解标记配置时执行。 After (finally)- 这些类型的 Advice 在连接点方法之后执行,无论方法退出是正常还是异常返回,并使用 @After 注解标记进行配置。 Around- 这些类型的 Advice 在连接点之前和之后执行,并使用 @Around 注解标记进行配置。 6.6. 指出在 spring aop 中 concern 和 cross-cutting concern 的不同之处。 concern 是我们想要在应用程序的特定模块中定义的行为。它可以定义为我们想要实现的功能。 cross-cutting concern 是一个适用于整个应用的行为,这会影响整个应用程序。例如,日志记录,安全性和数据传输是应用程序几乎每个模块都需要关注的问题,因此它们是跨领域的问题。 6.7. AOP 有哪些实现方式? 实现 AOP 的技术,主要分为两大类: 静态代理 - 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强; 编译时编织(特殊编译器实现) 类加载时编织(特殊的类加载器实现)。 动态代理 - 在运行时在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。 JDK 动态代理 CGLIB 6.8. Spring AOP and AspectJ AOP 有什么区别? Spring AOP 基于动态代理方式实现;AspectJ 基于静态代理方式实现。 Spring AOP 仅支持方法级别的 PointCut;提供了完全的 AOP 支持,它还支持属性级别的 PointCut。 6.9. 如何理解 Spring 中的代理? 将 Advice 应用于目标对象后创建的对象称为代理。在客户端对象的情况下,目标对象和代理对象是相同的。 Advice + Target Object = Proxy 6.10. 什么是编织(Weaving)? 为了创建一个 advice 对象而链接一个 aspect 和其它应用类型或对象,称为编织(Weaving)。在 Spring AOP 中,编织在运行时执行。请参考下图: 7、MVC 7.1. Spring MVC 框架有什么用?私信555获取免费架构资料 Spring Web MVC 框架提供 模型-视图-控制器 架构和随时可用的组件,用于开发灵活且松散耦合的 Web 应用程序。 MVC 模式有助于分离应用程序的不同方面,如输入逻辑,业务逻辑和 UI 逻辑,同时在所有这些元素之间提供松散耦合。 7.2. 描述一下 DispatcherServlet 的工作流程 DispatcherServlet 的工作流程可以用一幅图来说明: 欢迎加入QQ群架构华山论剑:836442475 向服务器发送 HTTP 请求,请求被前端控制器 DispatcherServlet 捕获。 DispatcherServlet 根据 -servlet.xml 中的配置对请求的 URL 进行解析,得到请求资源标识符(URI)。然后根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截器),最后以HandlerExecutionChain 对象的形式返回。 DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的 preHandler(...)方法)。 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作: HttpMessageConveter: 将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的响应信息。 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等。 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。 Handler(Controller)执行完成后,向 DispatcherServlet 返回一个ModelAndView 对象; 根据返回的ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到 Spring 容器中的ViewResolver)返回给DispatcherServlet。 ViewResolver 结合Model和View,来渲染视图。 视图负责将渲染结果返回给客户端。 私信555获取免费架构资料 7.3. 介绍一下 WebApplicationContext WebApplicationContext 是 ApplicationContext 的扩展。它具有 Web 应用程序所需的一些额外功能。它与普通的 ApplicationContext 在解析主题和决定与哪个 servlet 关联的能力方面有所不同。

优秀的个人博客,低调大师

面试官问金字塔思维如何应用在技术系统,我们聊了三十分钟

欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习 0 文章概述 大家想一想工作中有没有遇到以下情况:一位同事用了很长时间罗列了很多事实和数据向你说明一件事情,但是你听完根本不知道他想要说什么。另一位同事用了大量笔墨编写了技术方案,不仅有文字还有图表,但是你看完也不知道这个方案到底要解决什么问题以及如何落地。 上述情况的出现大概率是因为表述者没有使用结构化方法进行阐释,信息看似非常丰富但是杂乱无章,让人很难抓住重点,所以我们需要引入结构化思维方法论。 金字塔原理就是一个由芭芭拉·明托女士提出的结构化思维方法论,风靡世界五十年并在各个行业都取得过很好的效果。本文我们就运用金字塔原理分析一个技术系统优化方案,并看看如何落地执行,最终总结出一个闭环工具。 1 怎么样提好一个问题 我们在工作中做的所有事情,本质上都是为问题提供解决方案。无论我们是提出一个方案,还是优化一种场景,或者是本身就是在解决问题,所以想要阐释清楚我们在做什么,第一步明确我们正在解决什么问题非常关键。 那么应该如何阐释清楚问题呢?我们可以使用金字塔原理提出的结构化表达SCQA工具,这四个字母分别代表背景、冲突、疑问、回答。 背景(Situation)是这个问题出现的环境,我们对环境的描述必须是真实和朴素的,大家对这个描述都是认可的,甚至是可以产生共鸣的。 冲突(Complication)是在上述背景下出现了哪些矛盾,常见的类型有三种:哪些预期未达标、哪些流程不顺畅、哪些隐患还存在。 疑问(Question)是根据背景和冲突自然而然提出的问题,常见的类型有四种:应该做什么、应该如何做、是否应该做、为什么会发生。 回答(Answer)是针对上述四类问题给出的答案,而回答就是金字塔结构的中心思想。 假设现在你想要向同事说明白技术方案应该如何实施,那么第一步就应该阐释清楚到底是在解决什么问题,等到大家对问题达成一定共识之后再谈技术方案: 背景:业务数据量越来越大 冲突:响应经常超时现有系统难以支撑 疑问:应该做什么才能够支撑后续业务 回答:我们应该优化系统 此时技术方案准备解决什么问题就清楚了,而对这个问题的回答就是金字塔结构的中心思想。下面我们就要对这个中心思想使用金字塔原理进行结构化组织。 2 结构化方式组织回答 金字塔原理的核心思想并不复杂:一件事情可以总结出一个中心思想,这个中心思想可以由三至七个论点支持,每个论点再可以由三至七个论据支持,基本结构图如下: 对于金字塔原理仅仅分析到这里是不够的,我们还应该进一步去分析金字塔原理的内在结构,而内在结构可以从纵向和横向两个维度分析:纵向结构体现了结论先行和以上统下两个原则,横向结构体现了归类分组和逻辑递进两个原则。 2.1 纵向结构 2.1.1 结论先行 结论先行是指开宗明义地展示中心思想,让听众一开始就明白沟通主旨,而如果把中心思想隐藏在沟通过程中,听众可能因为走神或者沟通信息太多而失焦,根本不知道你在说什么。结论先行具体有以下六个方面: 先重要后次要 先框架后细节 先总体后细分 先论点后论据 先结论后原因 先结果后过程 假设一个同事代码发布上线后导致系统故障,如果不使用结构化方法是这么表述的: 我看监控发现数据库负载升高,可能是没有加索引导致的。我又发现频繁收到重复消息,是不是消息中间件有什么问题?监控还显示创建了大量线程,是不是线程池使用不当导致的?问题排查很难短时间得到结论,我们还是先回滚代码至上一个版本吧 这位同事中心思想是问题原因比较难排查,应该先回滚代码再分析问题,但是他把最重要的观点放在最后,不听到最后不知道他要做什么,而如果结论先行应该怎么表述呢? 我们应立即当回滚代码,因为问题排查比较复杂,还是先恢复系统再排查问题。可能的问题分三类:第一可能是索引使用不当导致的数据库问题,第二可能是中间件问题导致大量重复接收消息,第三可能是线程池使用不当导致线程大量被创建。等到恢复正常之后我们依次排查这些问题 我们比较两段表述不难发现,第二段表述结构清晰很多,信息传达效率显著提升,这就是结论先行的优势所在。 2.1.2 以上统下 以上统下是指任何一个层的思想必须是其下一层思想的总结概括,我们分析一个例子进行说明:小王今天需要买牛肉、鸡蛋、萝卜、果汁、白菜、牛奶、青菜、鸡肉、酸奶,但这么多菜品他记不住,请你想办法帮助小王。 第一步我们要对菜品自下而上进行聚合归纳,这是一个找规律的过程。第二步再以上统下进行结构化表达从而帮助记忆。 自下而上聚合我们不难发现,牛肉、鸡肉、鸡蛋属于肉蛋类,白菜、青菜、萝卜属于蔬菜类,牛奶、果汁、酸奶属于饮品类,这样聚合之后我们再以上统下进行结构化表达。 上述实例比较简单,因为元素之间的关联性比较容易寻找,但是真实场景是不会这么简单的,元素之间关联性并不容易建立,那么我们应该如何从中心思想展开至第二层? 金字塔原理推荐使用疑问-回答式对话,通过设问的方式向下展开结构。那么应该问哪几个问题从而涵盖中心思想的要点?我们可以参考5W2H分析法,尽量做到要点不缺失: What:是什么、做什么 Why:为什么、什么原因 Where:在哪里、从哪开始 When:开始结束时间、里程碑 Who:谁负责、谁来做、谁验收 How:怎么做、什么方法、从哪切入 How Much:做多少、各项指标是多少 在这个模型基础上我们可以进行简化从而减少要素数量,这样更加有利于结构化表达和记忆。我们一般选取What、Why、How这三个核心要素组成2W1H模型。 2.1.3 实际应用 我们看看结论先行和以上统下的实际应用。根据第一章节我们使用SCQA工具得到了回答,这个回答就是金字塔结构中心思想,根据结论先行原则在金字塔顶端就要开门见山地展示中心思想。 我们再用2W1H模型组织论点,划分为什么、为什么和怎么做三个维度,这样金字塔的纵向结构就已经搭建完成。 2.2 横向结构 2.2.1 归类分组 (1) 归纳推理 我们一般用归纳推理和演绎推理两种方法进行归类分组,我们先看归纳推理。 归纳推理是指把观察到的事实、规律归纳总结为理论。这种推理方法是不严谨的,因为只要观察事实和信息是有限的,那么归纳推理出来的结论就不一定是正确的。这就是逻辑错误中常见的一种:错误归因。 欧洲人看到的天鹅都是白色的,那么他们就归纳总结说所有的天鹅都是白色的。当一只黑天鹅出现时,这个结论就被证明是错误的,这就是黑天鹅事件。 当然我们不可能观察到所有事实,收集到所有信息,而一般是为了解决某个具体问题,我们会收集侧重于某个角度的信息,建立特定模型去分析解决问题,这也不失为一种有效方法。 金字塔原理归纳推理一般有以下四种维度:时间维度、结构维度、程度维度、经验维度。时间维度是根据天然时间线进行归纳,结构维度根据组织结构进行归纳,程度维度是根据程度级别进行归纳,经验维度是根据已有经验进行归纳。我们分别来看上述四种维度的几种常见类型: (1) 时间维度 事前、事中、事后 短期、中期、长期 (2) 结构维度 信息部、行政部、人力部 开发组、测试组、运维组 (3) 程度维度 高级、中级、初级 重要、次要、不要 (4) 经验维度 市场战略3C理论 市场决策4P理论 高扩展、高可用、高性能 我们选取时间维度和结构维度分析一个实例:怎样减少代码上线故障。从时间维度分析:事前需要做好代码测试,事中需要监控关键指标,事后需要进行分析复盘。 从结构维度分析:开发人员需要完备单元测试,测试人员需要做好边界测试,运维人员需要完善监控平台。 (2) 演绎推理 演绎推理是指根据公理、定理或者自己相信的观念,做出推理或者判断,得到结论。 这种方法从逻辑上来说是严谨的。命题A是真的,推理出命题B也是真的,那是因为命题B的真实性包含在命题A中。 需要注意在逻辑上严谨,不是说结论一定是正确的。例如自己相信的观念最终被证明是错误的,那么得到结论也就是错误的。 这是一种自上而下的推理方法,由已知的公理、定理或者观念向下推理。使用这种方法,需要在出现问题的领域有一定的经验和积累。 标准式演绎推理分为大前提、小前提和结论:所有小鸟都会飞,这是一只小鸟,所以它会飞。 演绎推理还可以分为现象、原因和解决方案三个要素:现象是开发代码质量不高,原因是没有统一代码规约,解决方案是制定统一代码规约。 2.2.2 逻辑递进 逻辑递进是指每种思想需要按照一定顺序进行排列,时间维度按照事前、事中、事后进行排列,程度级别按照高级、中级、初级进行排列。例如时间维度我们还可以继续使用怎样减少代码上线故障案例,按照事前、事中、事后时间线进行排列,这种顺序更加符合理解和记忆习惯。 3 综合实例 前面章节我们介绍了SCQA和金字塔原理,本章节我们用一个实例将结构化思维方法论落地,并总结出一个闭环工具。 3.1 提出问题 我们还是以系统优化案例进行说明,第一步是使用SCQA提出问题从而引出回答。背景是业务数据量越来越大,冲突是响应经常超时现有系统难以支撑,疑问是应该做什么才能够支撑后续业务,回答是我们应该优化系统,而回答就是金字塔结构的中心思想。 3.2 给出回答 第二步是使用金字塔原理组织回答,在纵向结构上我们通过设问的方式提出2W1H三个问题。 在横向结构上我们按照一定维度去组织论据,我选取了经验维度,在怎么做优化这个问题上选取3H理论指导优化工作,这个理论包含高扩展、高可用、高性能三个维度。 高扩展强调系统可扩展性,包含划分清晰的领域边界,使用合适的设计模式,进行合理的组件抽象,遵守统一的代码规约。 高可用强调对系统的保护,面对大流量可能带来的非线性压力,我们要做好降级、延时、隔离、冗余、告警和响应防止系统崩溃。 高性能强调系统的性能表现,我们首先通过分析时延、负载、压测表现等指标了解系统能力,再从数据层、缓存层、服务层、WEB层、前端层、客户端、代理层等层级思考优化方案。 因为需要组织和表达信息较多,我们可以选择将金字塔旋转90度,这样做只是为了展示信息更加方便,在逻辑上并不改变金字塔结构。 3.3 落地执行 我们已经将回答进行了结构化表达,现在需要思考如何将回答落地执行,我认为可以从以下四个方面思考。 团队(Team):有多少人可以投入这个项目,人员构成是什么,例如有几个服务端和几个前端,人力的投入直接影响执行节奏。 切点(Pointcut):从哪里入手这是需要明确的,例如我们做系统优化,可以先从一个耗时最长,性能最差的接口入手,用这个接口验证技术方案的可行性。 节奏(Rhythm):受限于开发人数和项目排期等因素,系统优化很难一步到位,所以我们可以设置多个里程碑节点,规划短期、中期、长期目标,这样大目标就被拆成几个小目标逐一实现。 结果(Result):系统优化的结果怎么样,投入了这么多时间和精力到底产出是什么,这是必须要回答和面对的问题。我们可以把哪些预期未达标、哪些流程不顺畅、哪些隐患还存在等问题拿出来逐一检视。 我们把SCQA和上述四个维度进行合并,组成SCQATPRR闭环工具,这个工具可以帮助设计方案减少缺失环节从而形成闭环。 4 文章总结 本文首先分析了SCQA结构化工具,提好一个问题是解决问题的第一步。在初步给出答案后我们可以进行第二步使用金字塔结构组织答案,纵向结构要符合结论先行、以上统下原则,横向结构要符合归纳分组、逻辑递进原则。第三步在落地执行层面,我们提出团队、切点、节奏和结果这四个维度,结合SCQA工具进而组成闭环工具。 需要说明的是结构化思考工具只是一个工具,解决问题的关键还是我们要在本专业领域钻研和精进,也要在平时注意将零散的专业知识进行归纳整理,这样专业知识和工具相结合才会发挥更大的作用,希望本文对大家有所帮助。 欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习

优秀的个人博客,低调大师

左神算法、牛客网算法、剑指java、Java后台面试宝典----个人总结(实习、春招、秋招)

第一节课 第一题:题意与leetcode354的问题相同算法原型 最长递增子序列问题 * 题意:求出给定序列的最长递增子序列的长度,给定序列不是有序的,子序列不是子数组,元素在原数组中不必是连续的 * */ /* * solutions1: 时间复杂度O(n^2),空间复杂度O(n) * 新建一个辅助数组h,h[i]表示以nums[i]结尾的最长递增子序列的长度 * h[i]值的确定,需要nums[i]与nums[j](0<=j<i)做比较,h[i] = h[j] + 1 (when nums[j] < nums[i]),找出这些值中的最大值即最终的nums[i] * 如果nums[i]最小的话,则h[i] = 1; * 因为h[i]值的确定需要nums[i]进行最多i-1次比较,所以时间复杂度为O(n^2) */ /* * solution2: 时间复杂度O(n*log(n)),空间复杂度O(n) * 新建一个辅助数组h,h[i]表示遍历到当前nums[j]位置时长度为i+1的递增子序列的最小末尾 * h填入值的部分称作有效区,还未填入值的部分称作无效区 * h[i]值的确定,h[0] = nums[0],在遍历nums 的同时,在h[i]中找到第一个大于等于nums[j]的数,并将其替换为为nums[j],如果没找到则将h有效区后一个元素置为nums[j] * h[i]会一直保持有序状态,所以找第一个大于等于nums[j]的数可以用二分法,最后返回h有效区的长度即可 * 由于在遍历的同时采用了时间复杂度为log(n)的二分查找,故时间复杂度为O(n*log(n)) */ /* * 实现solution2 */ public class LongestSubSequence { public int getLonggestLen(int[] nums) { int maxLen = 0; if (nums.length == 0) return maxLen; int[] h = new int[nums.length]; h[0] = nums[0]; maxLen = 1; for (int i = 1; i < nums.length; i++) { int pos = getPos(h, 0, maxLen - 1,nums[i]); if (pos == -1) { h[maxLen++] = nums[i]; } else h[pos] = nums[i]; } return maxLen; } public int getPos(int[] h, int left, int right, int num) { if (left <= right) { int mid = left + (right - left) / 2; if (h[mid] >= num) { int pos = getPos(h, left, mid - 1, num); if (pos == -1) return mid; return pos; } else { return getPos(h, mid + 1, right, num); } } return -1; } public static void main(String[] args){ LongestSubSequence l = new LongestSubSequence(); int[] nums = {1,3,6,7,9,4,10,5,6}; l.getLonggestLen(nums); } } 第二题 * 题意:详见2016 《牛课堂第一节课课件.pdf》第2题,leetcode42原题 */ /* * solution1:时间复杂度 O(n) 空间复杂度O(n) * 首先简化一下题意:如果能求得当前位置格子上的水量,那么总水量就是每个位置水量之和 * 当前格子上所能存储的水量 = 当前格子左边最大值与右边最大值之间的较小值 - 当前格子高度 * 所以要先求出当前格子左边的最大值与右边最大值,对于右边最大值用数组r来辅助存储,从右往左遍历一下原数组即可得到r * 对于左边的最大值在遍历原数组的同时用一个全局变量记录下来就行,此时时间复杂度为O(n) 空间复杂度O(n),还没达到最优解 * * solution2:时间复杂度 O(n) 空间复杂度O(1) * 为了不使用辅助数组,我们采用双指针的方法 * 双指针left和right分别指向数组的第二个元素和倒数第二个元素,由题意可知第一个元素和最后一个元素的储水量都是零 * 当前元素的遍历则从第二个和倒数第二个元素开始,用leftMax和rightMax分别记录左边最大值和右边最大值,用一个全局变量totalWater 更新总的储水量 * 若leftMax < rightMax则结算左指针height[left],leftMax > height[left]时,totalWater += leftMax - height[left++],反之更新左边最大值leftMax = height[left++],左指针向右移一位; * 反之,结算右边的当前元素,过程与左边类似 */ public class Question1 { public int getWater(int[] height){ if (height == null || height.length < 3) return 0; int totalWater = 0; int left = 1, right = height.length - 2; int leftMax = height[0], rightMax = height[height.length - 1]; while (left <= right) { if (leftMax < rightMax) { if (leftMax > height[left]) { totalWater += leftMax - height[left++] ; } else { leftMax = height[left++]; } } else { if (rightMax > height[right]) { totalWater += rightMax - height[right--] ; } else { rightMax = height[right--]; } } } return totalWater; } public static void main(String[] args){ Question1 q = new Question1(); int[] height = {0,1,0,2,1,0,1,3,2,1,2,1}; q.getWater(height); } }

优秀的个人博客,低调大师

即将到来的金三银四,这10道springboot常见面试题你需要了解下

1.什么是Spring Boot? 多年来,随着新功能的增加,spring变得越来越复杂。只需访问https://spring.io/projects页面,我们就会看到可以在我们的应用程序中使用的所有Spring项目的不同功能。 如果必须启动一个新的Spring项目,我们必须添加构建路径或添加Maven依赖关系,配置应用程序服务器,添加spring配置。 因此,开始一个新的spring项目需要很多努力,因为我们现在必须从头开始做所有事情。 Spring Boot是解决这个问题的方法。Spring Boot已经建立在现有spring框架之上。使用spring启动,我们避免了之前我们必须做的所有样板代码和配置。 因此,Spring Boot可以帮助我们以最少的工作量,更加健壮地使用现有的Spring功能。 2.Spring Boot有哪些优点? · 减少开发,测试时间和努力。 · 使用JavaConfig有助于避免使用XML。 · 避免大量的Maven导入和各种版本冲突。 · 提供意见发展方法。 · 通过提供默认值快速开始开发。 · 没有单独的Web服务器需要。这意味着你不再需要启动Tomcat,Glassfish或其他任何东西。 · 更少的配置 因为没有web.xml文件。只需添加用@ Configuration注释的类,然后添加用@Bean注释的方法,Spring将自动加载对象并像以前 一样对其进行管理。您甚至可以将@Autowired添加到bean方法中,以使Spring自动装入需要的依赖关系中。 · 基于环境的配置 使用这些属性,您可以将您正在使用的环境传递到应用程序:-Dspring.profiles.active = {enviornment}。在加载主应用程序属性文件后,Spring将在(application{environment} .properties)中加载后续的应用程序属性文件。 3.什么是JavaConfig? Spring JavaConfig是Spring社区的产品,它提供了配置Spring IoC容器的纯Java方法。因此它有助于避免使用XML配置。使用JavaConfig的优点在于: 面向对象的配置。由于配置被定义为JavaConfig中的类,因此用户可以充分利用Java中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean方法等。 减少或消除XML配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在XML和Java之间来回切换。 JavaConfig为开发人员提供了一种纯Java方法来配置与XML配置概念相似的Spring容器。 从技术角度来讲,只使用JavaConfig配置类来配置容器是可行的,但实际上很多人认为将JavaConfig与XML混合匹配是理想的。 类型安全和重构友好。JavaConfig提供了一种类型安全的方法来配置Spring容器。由于Java 5.0对泛型的支持,现在可以按类型而不是按名称检索bean,不需要任何强制转换或基于字符串的查找。 4.如何重新加载Spring Boot上的更改,而无需重新启动服务器? 这可以使用DEV工具来实现。通过这种依赖关系,您可以节省任何更改,嵌入式tomcat将重新启动。 Spring Boot有一个开发工具(DevTools)模块,它有助于提高开发人员的生产力。Java开发人员面临的一个主要挑战是将文件更改自动部署到服务器并自动重启服务器。 开发人员可以重新加载Spring Boot上的更改,而无需重新启动服务器。这将消除每次手动部署更改的需要。Spring Boot在发布它的第一个版本时没有这个功能。 这是开发人员最需要的功能。DevTools模块完全满足开发人员的需求。该模块将在生产环境中被禁用。它还提供H2数据库控制台以更好地测试应用程序。 5.Spring Boot中的监视器是什么? Spring boot actuator是spring启动框架中的重要功能之一。Spring boot监视器可帮助您访问生产环境中正在运行的应用程序的当前状态。 有几个指标必须在生产环境中进行检查和监控。即使一些外部应用程序可能正在使用这些服务来向相关人员触发警报消息。监视器模块公开了一组可直接作为HTTP URL访问的REST端点来检查状态。 6.如何在Spring Boot中禁用Actuator端点安全性? 默认情况下,所有敏感的HTTP端点都是安全的,只有具有ACTUATOR角色的用户才能访问它们。 安全性是使用标准的HttpServletRequest.isUserInRole方法实施的。 我们可以使用management.security.enabled = false 来禁用安全性。只有在执行机构端点在防火墙后访问时,才建议禁用安全性。 如何在自定义端口上运行Spring Boot应用程序? 为了在自定义端口上运行Spring Boot应用程序,您可以在application.properties中指定端口。 server.port = 8090 7.什么是YAML? YAML是一种人类可读的数据序列化语言。它通常用于配置文件。 与属性文件相比,如果我们想要在配置文件中添加复杂的属性,YAML文件就更加结构化,而且更少混淆。可以看出YAML具有分层配置数据。 8.如何实现Spring Boot应用程序的安全性? 为了实现Spring Boot的安全性,我们使用 spring-boot-starter-security依赖项,并且必须添加安全配置。它只需要很少的代码。配置类将必须扩展WebSecurityConfigurerAdapter并覆盖其方法。 9.如何集成Spring Boot和ActiveMQ? 对于集成Spring Boot和ActiveMQ,我们使用spring-boot-starter-activemq 依赖关系。 它只需要很少的配置,并且不需要样板代码。 10.如何使用Spring Boot实现分页和排序? 使用Spring Boot实现分页非常简单。使用Spring Data-JPA可以实现将可分页的org.springframework.data.domain.Pageable传递给存储库方法。

优秀的个人博客,低调大师

面试官问一个数据表字段怎么表示多种业务含义?我愣了五分钟

欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习 1 需求背景 在系统中用户一共有三种角色:普通用户,管理员,超级管理员,现在需要设计一张用户角色表记录这类信息。我们不难设计出如下方案: 我们使用1表示是,0表示否,那么观察上表不难得出,用户一有用超级管理员角色,用户二具有管理员角色,用户三具有普通用户角色,用户四同时具有三种角色。 如果此时新增加一种角色呢?那么新增一个字段即可: 2 发现问题 按照上述做法进行表设计功能上是没有问题的,优点是容易理解结构清晰,但是我们想一想有没有什么问题?笔者遇到过如下问题: 在复杂业务环境一份数据可能会使用在不同的场景,例如上述数据存储在MySQL数据库,这一份数据还会被用在如下场景: 检索数据需要同步一份到ES 业务方使用此表通过Flink计算业务指标 业务方订阅此表Binlog消息进行业务处理 如果表结构发生变化,数据源之间就要重新进行对接,业务方也要进行代码修改,这样开发成本比较非常高。有没有办法避免此类问题? 3 解决方案 我们可以通过位图法,这样同一个字段可以表示多个含义。首先设计如下数据表,userFlag字段先不填。 我们使用位图法每一个bit表示一种角色 我们使用位图法表示如下数据表 用户一位图如下十进制数值是4 用户二位图如下十进制数值是2 用户三位图如下十进制数值是1 用户四位图如下十进制数值是7 这时我们可以补齐数据表 4 位图法详解 本章节我们分析位图法方案一些关键节点。 4.1 枚举定义 定义枚举时不要直接定义为1、2、4这类数字,而是采用位移方式定义,这样使用者可以明白设计者意图。 /** * 用户角色枚举 * * @author JAVA前线 * */ public enum UserRoleEnum { // 1 -> 00000001 NORMAL(1, "普通用户"), // 2 -> 00000010 MANAGER(1 << 1, "管理员"), // 4 -> 00000100 SUPER(1 << 2, "超级管理员") ; private int code; private String description; private UserRoleEnum(Integer code, String description) { this.code = code; this.description = description; } public String getDescription() { return description; } public int getCode() { return this.code; } } 4.2 维护角色 假设用户已经具有普通用户角色,我们需要为其增加管理员角色,这就是新增角色,与之对应还有删除角色和查询角色,这些操作需要用到为位运算,说明详见代码注释。 /** * 用户角色枚举 * * @author JAVA前线 * */ public enum UserRoleEnum { // 1 -> 00000001 NORMAL(1, "普通用户"), // 2 -> 00000010 MANAGER(1 << 1, "管理员"), // 4 -> 00000100 SUPER(1 << 2, "超级管理员") ; // 新增角色 -> 位或操作 // oldRole -> 00000001 -> 普通用户 // addRole -> 00000010 -> 新增管理员 // newRole -> 00000011 -> 普通用户和管理员 public static Integer addRole(Integer oldRole, Integer addRole) { return oldRole | addRole; } // 删除角色 -> 位异或操作 // oldRole -> 00000011 -> 普通用户和管理员 // delRole -> 00000010 -> 删除管理员 // newRole -> 00000001 -> 普通用户 public static Integer removeRole(Integer oldRole, Integer delRole) { return oldRole ^ delRole; } // 是否有某种角色 -> 位与操作 // allRole -> 00000011 -> 普通用户和管理员 // qryRole -> 00000001 -> 是否有管理员角色 // resRole -> 00000001 -> 有普通用户角色 public static boolean hasRole(Integer role, Integer queryRole) { return queryRole == (role & queryRole); } private int code; private String description; private UserRoleEnum(Integer code, String description) { this.code = code; this.description = description; } public String getDescription() { return description; } public int getCode() { return this.code; } public static void main(String[] args) { System.out.println(addRole(1, 2)); System.out.println(removeRole(3, 1)); System.out.println(hasRole(3, 1)); } } 4.3 数据查询 假设在运营后台查询界面中,需要查询具有普通用户角色的用户数据,我们可以使用SQL语句如下 select * from user_role where (user_flag & 1) = user_flag; select * from user_role where (user_flag & b'0001') = user_flag; 使用MyBatis语句如下 <select id="selectByUserRole" resultMap="BaseResultMap" parameterType="java.util.Map"> select * from user_role where user_flag & #{userFlag} = #{userFlag} </select> <select id="selectByUserIdAndRole" resultMap="BaseResultMap" parameterType="java.util.Map"> select * from user_role where id = #{userId} and user_flag & #{userFlag} = #{userFlag} </select> 5 文章总结 本文我们从一个简单案例开始,分析了直接新增字段优缺点,使用新增字段方法,笔者遇到最多的问题就是在复杂业务场景中,如果新增字段数据需要新增对接工作量,增加了开发成本。 我们介绍了位图法,这样一个字段就可以表示多个含义,减少了字段冗余,节省了对接开发成本。当然位图法也有缺点,其增加了代码理解成本,数据库字段含义不直观,需要进行转义,大家可以根据需求场景选择使用。 欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。