Spring boot相关知识点


Springboot中如何解决跨域问题?

在脚本进行http请求的时候要满足同源协议,即url的协议,域名和端口要相同才能才能发起请求。浏览器在脚本发出跨域请求后,会拦截返回的结果,所以需要配置跨域。

当用户退出或者token过期时,拦截器和跨域的顺序配置会导致跨域问题,一个http请求,先走filter,到达servlet后才进行拦截器的处理。一般在前后端分离部署时要解决跨域问题,通过cors来实现跨域问题。

如果不是前后端分离部署的项目。可以通过实现WebMvcConfigurer然后重写addCorsMappings方法来实现跨域配置。

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .maxAge(3600);
    }

}

把cors放在filter里面,就可以在拦截器之前进行。

@Configuration
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }

}

SpringBoot的核心配置文件有哪几个?他们的区别是什么?

Spring boot的核心配置文件有两个,application和bootstrap。application主要用于spring boot的自动化配置。spring boot使用@EnableConfigurationProperties注解映射application中的参数和POJO的关系。

bootstrap配置文件的加载要优于application,且里面配置的属性不能被覆盖。使用spring Cloud config 配置中心时,这时需要在bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息。

Spring boot的异常处理

Spring Boot类BasicErrorController

这是一个基础全局错误controller,Spring Boot自带的,看到这个RequestMapping地址,这是一个相当于三元写法,如果你在配置文件配置了server.error.path的话,就会使用你配置的异常处理地址,如果没有就会使用你配置的error.path路径地址,如果还是没有,默认使用/error来作为发生异常的处理地址。

默认的类在你的请求返回文本或者json数据的时候,当发生错误的话,如果你在配置文件里面配置了模板引擎并且有error的页面,这个类就会返回你的error页面。返回的数据通过getErrorAttributes方法封装你的路径,状态,信息,时间戳等信息。

可以通过自定义一个bean来实现ErrorController来屏蔽默认的异常处理。如果不想全部屏蔽,可以自定义一个bean来继承BasicErrorController接口来使用部分功能,并自定义自己的错误映射地址。

统一异常处理

package com.riemann.springbootdemo.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;


@ControllerAdvice
public class GlobalExceptionHandle extends RuntimeException{

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandle.class);

    @ExceptionHandler(value = Exception.class)
    public ModelAndView exceptionHandle(HttpServletRequest request, Exception exception) {
        String url = request.getRequestURL().toString();
        logger.error("URL: " + url);
        logger.error("Excption: ", exception);
        ModelAndView mv = new ModelAndView();
        mv.addObject("exception", exception);
        mv.addObject("url", url);
        mv.setViewName("error");
        return mv;
    }

}

统一处理Exception异常,只需要在类上标注ControllerAdvice这个注解,然后在类方法上标注好对应的ExceptionHandler及异常类。ControllerAdvice可以用来进行全局异常处理,全局数据绑定和全局数据预处理。

Spring Boot与Spring的区别

Spring

spring框架为开发Java应用程序提供了全面的基础架构支持,它包含一些很好的功能,如依赖注入和开箱即用的模块。

Spring JDBC

spring中的JDBC主要为数据库资源管理和错误处理,简化开发人员对数据库的操作。其中,JDBC Template是JDBC的核心类,使用核心类提供的方法来操作数据库。

Spring MVC

Spring MVC将传统的模型层拆分为了业务层(Service)和数据访问层(DAO)。在 Service 下可以通过 Spring 的声明式事务操作数据访问层,而在业务层上还允许我们访问 NoSQL。其中,M即模型,数据,V代表视图,C为控制器,将不同的数据显示在不同的视图上。

Spring Security

Spring Security的核心功能为认证,授权和攻击防护。核心为使用Basic Authentication Filter过滤器认证用户身份。

Spring AOP

不想多说

Spring ORM

对象关系映射,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。

Spring Test

并不重要

Spring Boot

Spring Boot基本上是Spring框架的扩展,它消除了设置Spring应用程序所需的复杂例行配置。

  • 通过starter这一个依赖,以简化构建和复杂的应用程序配置
  • 可以直接main函数启动,嵌入式web服务器,避免了应用程序部署的复杂性
  • Metrics度量,Helth check健康检查和外部化配置
  • 自动化配置Spring功能

两者的区别

maven依赖

Spring创建Web应用程序所需的最小依赖项:

org.springframework
spring-web
5.1.0.RELEASE

org.springframework
spring-webmvc
5.1.0.RELEASE

与Spring不同,Spring Boot只需要一个依赖项来启动和运行Web应用程序:

org.springframework.boot
spring-boot-starter-web
2.0.5.RELEASE

在构建期间,所有其他依赖项将自动添加到最终归档中。spring-boot-starter-web包自动帮我们引入了web模块开发需要的相关jar包。spring-boot-starter-web会自动引入spring-webmvc,spring-boot-starter-validation,spring-boot-starter,spring-boot-starter-json,spring-boot-starter-tomcat5个基础依赖。

MVC配置

通过实现WebApplicationInitializer,在其中可以添加servlet,listener等,在加载Web项目的时候会加载这个接口实现类,从而起到web.xml相同的作用

package org.springframework.web;
 
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
 
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }
 
    public void onStartup(Set> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        List initializers = new LinkedList();
        Iterator var4;
        if(webAppInitializerClasses != null) {
            var4 = webAppInitializerClasses.iterator();
 
            while(var4.hasNext()) {
                Class waiClass = (Class)var4.next();
                if(!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer)waiClass.newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }
 
        if(initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
        } else {
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            var4 = initializers.iterator();
 
            while(var4.hasNext()) {
                WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                initializer.onStartup(servletContext);
            }
 
        }
    }
}

判断webAppInitializerClasses这个Set是否为空。如果不为空的话,找到这个set中不是接口,不是抽象类,并且是WebApplicationInitializer接口实现类的类,将它们保存到list中。当这个list为空的时候,抛出异常。不为空的话就按照一定的顺序排序,并将它们按照一定的顺序实例化。调用其onStartup方法执行。

public class MyWebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext container) {
        AnnotationConfigWebApplicationContext context
          = new AnnotationConfigWebApplicationContext();
        context.setConfigLocation("com.test.package");
        container.addListener(new ContextLoaderListener(context));
        ServletRegistration.Dynamic dispatcher = container
          .addServlet("dispatcher", new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }
}

我们还需要将@EnableWebMvc注解添加到@Configuration注解类,并定义一个视图解析器来解析从控制器返回的视图。在使用该注解后配置一个继承于WebMvcConfigurerAdapter的配置类即可配置好Spring WebMVC。

@EnableWebMvc
@Configuration
public class ClientWebConfig implements WebMvcConfigurer {
   @Bean
   public ViewResolver viewResolver() {
      InternalResourceViewResolver bean
        = new InternalResourceViewResolver();
      bean.setViewClass(JstlView.class);
      bean.setPrefix("/WEB-INF/view/");
      bean.setSuffix(".jsp");
      return bean;
   }
}

与所有这些相比,一旦我们添加了Spring boot web starter,Spring Boot只需要一些属性来使上面的事情正常工作。

spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

上面的所有Spring配置都是通过一个名为auto-configuration的进程添加Boot web starter来自动包含的。这意味着Spring Boot将自动扫描应用程序中存在的依赖项,属性和bean,并根据这些内容启用相应的配置。

SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties 或者 @ConfigurationProperties 等几个注解来进行自动配置完成的。

@EnableAutoConfiguration 开启自动配置,主要作用就是调用 Spring-Core 包里的 loadFactoryNames(),将 autoconfig 包里的已经写好的自动配置加载进来。

@Conditional 条件注解,通过判断类路径下有没有相应配置的 jar 包来确定是否加载和自动配置这个类。

@EnableConfigurationProperties 的作用就是,给自动配置提供具体的配置参数,只需要写在 application.properties 中,就可以通过映射写入配置类的 POJO 属性中

模板引擎

在Spring中,我们需要为视图解析器添加 thymeleaf-spring5依赖项和一些配置

@Configuration
@EnableWebMvc
public class MvcWebConfig implements WebMvcConfigurer {

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public SpringResourceTemplateResolver templateResolver() {
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setApplicationContext(applicationContext);
        templateResolver.setPrefix("/WEB-INF/views/");
        templateResolver.setSuffix(".html");
        return templateResolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver());
        templateEngine.setEnableSpringELCompiler(true);
        return templateEngine;
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        registry.viewResolver(resolver);
    }

ApplicationContext是spring继BeanFactory之外的另一个核心接口或容器,允许容器通过应用程序上下文环境创建、获取、管理bean。为应用程序提供配置的中央接口。

Spring Boot 只需要spring-boot-starter-thymeleaf的依赖项 来启用Web应用程序中的Thymeleaf支持。一旦依赖关系添加成功后,我们就可以将模板添加到src / main / resources / templates文件夹中,Spring Boot将自动显示它们。具体过程也参考上方的自动配置。

安全配置

Spring需要标准的 spring-security-web和spring-security-config 依赖项来在应用程序中设置Security。接下来,我们需要添加一个扩展WebSecurityConfigurerAdapter的类,并使用@EnableWebSecurity注解

@Configuration
@EnableWebSecurity
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
          .withUser("user1")
            .password(passwordEncoder()
            .encode("user1Pass"))
          .authorities("ROLE_USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
          .anyRequest().authenticated()
          .and()
          .httpBasic();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

WebSecurityConfigurerAdapter 类是个适配器, 在配置的时候,需要我们自己写个配置类去继承他,然后编写自己所特殊需要的配置。

Spring Boot也需要这些依赖项才能使其工作。但是我们只需要定义spring-boot-starter-security的依赖关系,它会自动将所有相关的依赖项添加到类路径中。

应用引导Application Bootstrap

web.xml引导方法

  1. Servlet容器(服务器)读取web.xml
  2. web.xml中定义的DispatcherServlet由容器实例化
  3. DispatcherServlet通过读取WEB-INF / {servletName} -servlet.xml来创建WebApplicationContext
  4. 最后,DispatcherServlet注册在应用程序上下文中定义的bean

servlet 3+引导方法

在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。

  1. 容器搜索实现ServletContainerInitializer的 类并执行
  2. SpringServletContainerInitializer找到实现类WebApplicationInitializer的子类
  3. WebApplicationInitializer创建会话使用XML或上下文@Configuration类
  4. WebApplicationInitializer创建DispatcherServlet,使用先前创建的上下文。

Spring Boot引导

详情参考Spring Boot启动过程的文章。

参考文章:https://mp.weixin.qq.com/s/0qk2kaCKLdAViVzsw401sg

Shiro知识点

shiro和核心概念为subject,securityManager和realm。subject即为与当前应用交互的主体,主体将交互请求委托给securityManager。securityManager通过realm判断当前用户认证信息,并授权用户的身份和角色信息,判断是否具有相关权限。

自定义relam

AuthorizingRealm中的doGetAuthenticationInfo用于身份验证,doGetAuthorizationInfo用于授权。

身份认证过程

主体subject调用subject.login(token),将请求委托给securityManager。

Authenticator获取token信息并传入realm,从realm获取身份验证信息。

realm根据加密方式查询数据库进行验证,验证成功返回成功信息。可以单个realm验证,也可以多realm验证,根据不同策略判断是否需要多个realm同时验证成功。

doGetAuthenticationInfo获取身份验证相关信息:首先根据传入的用户名获取User信息;如果user为空,那么抛出没找到账号异常UnknownAccountExecption;如果user找到但却被锁定了抛出锁定异常LockedAccountException;最后生成AuthenticationInfo信息,交给间接父类AuthenticatingRealm使用CredentialsMatcher进行判断密码是否匹配,如果不匹配将抛出密码错误异常信息IncorrectCredentialsException;如果密码重试次数太多将抛出超出重试次数异常ExcessiveAttemptsException;在组装SimpleAuthenticationInfo信息时,需要传入:身份信息(用户名)、凭据(密文密码)、盐(username+salt),CredentialsMatcher使用盐加密传入的明文密码和此处的密文密码进行匹配。

授权过程

身份验证成功,通过Authorization进行授权。可以在后台使用逻辑代码判断是否具有权限和角色,也可以使用注解给接口授权,也可以使用前台标签的形式进行授权。

同时可以在shiro的配置文件中放开登录接口和静态资源的访问权限。

doGetAuthorizationInfo获取授权信息:PrincipalCollection是一个身份集合,因为只用到了一个Realm,所以直接调用getPrimaryPrincipal得到之前传入的用户名即可;然后根据用户名调用UserService接口获取角色及权限信息。

Session Manager

Session Manager是securityManager的父类,维护subject主体的相关信息。Session manager一般将会话委托也就是继承的方式给securityManager进行处理。

JWT知识点

jwt和security的对比

jwt使用token验证,jwt无需在服务器端存储用户数据,减轻服务端压力。security是将用户数据保存在服务器端。

jwt是可以跨语言的,不想security针对某个语言进行开发一个适用的框架。

随之而来的就是对token的控制问题。

如何解决token注销问题

减少token的有效期,使token尽快失效。

使用redis,前端发起请求时,携带的jwt数据信息,把信息中的唯一的id存到redis里面,验证的时候查询redis中是否有这个id,在token注销的时候删除redis里面的id即可。

怎么解决token失效后的续签问题

在用户登录验证的时候根据相关标准判断当前的token是否要过期。如果token快要过期,重新生成token就可以。

解决cookie被盗用问题

如果收到XSS攻击的话,设置一下cookie的httponly为true,防止脚本攻击。

对于抓包的话使用https协议即可。

对于CSRF攻击,在请求头中加个随机码。

cookie被禁用怎么办

判断一下用户的cookie是否禁用,如果禁用提示用户打开即可。

解决cookie被篡改问题

cookie被篡改,但是jwt验证是通过签名进行验证的,签名不对,后台的jwt验证就通过不了。

服务端微服务地址不小心暴露了,用户就可以绕过网关,直接访问微服务,怎么办

一般来说微服务都是使用nginx代理的,一般暴露的都是nginx的地址。

如果担心接口暴露,那也可以在微服务之间通信也使用jwt验证即可。

jwt流程分析

在前端,jwt头部记录信息和加密方式,然后进行编码。载荷记录token的对象和时间等信息,也进行编码。以句号拼接两部分的编码内容使用密钥和算法进行加密形成签名。服务器端以相同的方式即相同的密码和算法进行签名得到一直的结果验证成功返回客户端jwt信息,客户端请求接口时携带jwt信息就可以了。

redis知识点

redis的特点

redis的数据存在于内存之中,因此redis的读写速度很快,所以常用作缓存。

支持AOF和RDB两种数据持久化方式。

支持事务,redis的所有操作都是原子性的。

支持主从复制,主机可以将数据同步到从机。

redis数据存在于内存中,所以不适合海量数据的读写。

redis对扩容的兼容性较差。

为什么要使用redis

redis是运行在内存的,当一个项目多户发起多次请求访问数据库时,访问速度很慢,如果将数据库的数据放在redis中用作缓存,那么用户访问redis中的数据即可。当数据库的数据变更时,同步一下redis中的数据即可。加快访问速度,提供系统的性能。

当使用用户较多时,多用户同时访问数据库,数据库的压力较大,可能承受不住,而运行在内存中的redis支持高并发,因此使用redis可以减轻数据库的压力,也可以提升系统性能。

redis属于分布式缓存,多实例公用一份缓存数据,缓存具有一致性。

redis为什么访问速度快

redis运行在内存之中所以访问速度快。

redis使用的是单线程,不存在线程切换带来的性能消耗。

数据结构简单,有string,list,set,zset,hash五种数据结构。io采用多路复用方式。

redis的应用场景

用作计数器,在新闻中,对于热门新闻的点击,可以使用redis的string进行记录,速度快。

缓存,对于经常访问到的热点数据,可以同步到redis中,减轻数据库的压力。

存储会话信息,使用jwt的时候,可以用来存储jwt的唯一id,在登录逻辑中进行判断,注销token时删除redis中的id就行。

对于交集并集数据,查找共同好友或者笔记共同的分类,可以使用redis的set集合实现。

redis的持久化

持久化就是将数据持久化到硬盘之中,防止宕机之后的数据丢失。redis提供了AOF和RDB两种数据持久化方式。

RDB是redis的默认的持久化方式,按照时间持久化数据,时间可在配置中自己定义,效率比较高。且持久化时主进程处理命令,子进程负责io操作,保证了redis的高性能。但是因为是按照时间进行持久化,会发生数据丢失。

AOF将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。AOF通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。数据安全,每进行一次 命令操作就记录到 aof 文件中一次。AOF的文件较大,且效率较低。

通常应该同时使用AOF和RDB两种持久化方式,因为AOF的持久化方式数据较为完整,但是使用RDB的持久化文件可以作为数据库的备份来使用,因为他恢复较快。

redis过期键的删除策略

一般来说内存较大或者对内存的占用性能影响不大的情况下可以使用惰性过期,在访问key的时候在判断当前的key是否过期,如果过期的话就删除。

如果数据较多,会严重占用内存,考虑定期过期,每隔一段时间扫描key,然后清楚过期的key即可。

一般不使用定时过期,因为会浪费cpu资源,影响redis性能。

redis的内存淘汰

当redis中的数据满,而数据库的数据仍然在持续增加时。

如果系统的需求是当前redis中的数据已经满足使用,则不进行淘汰,不插入新的数据到redis中。

如果像新闻需要实时更新热门新闻的话,可以将当前内存中不常使用的key进行删除。

而类似于普通的用户信息,不存在优先级问题,也可以随机删除key。

redis的内存优化

对于redis,不应按照平常面向对象的习惯使用key-value的形式存储数据,占用的空间会很大。在redis中,hash占用的空间较小,所以应将信息存储到hash中。

redis事务

事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。redis的事务也是如此。

redis使用multi事务开始,然后命令入队,然后exec执行。事务执行时服务端收到有EXEC、DISCARD、WATCH、MULTI之外的请求,将会把请求放入队列中排。

一般事务具有原子性,隔离性,一致性和持久性。redis的事务具有一致性和隔离性,使用持久化时,具有持久性。在redis中,命令的执行是原子性的,但是事务不是,也无法进行回滚。

rabbitmq知识点

为什么使用MQ

可以进行异步处理,用户的请求需要多个系统写入数据,如果延迟过高,用户体验较差。使用MQ,我把请求放在mq里,让延迟快的优先反应并返回信息给用户,让其他系统订阅mq,取消息在保存到数据库即可。

解耦,当前系统产生的数据可能被其他多个系统使用,让其他系统请求,当前系统分发的话,有可能产生不一致问题,那么使用MQ把数据放到MQ里,谁使用谁订阅并且取数据即可。

流量削峰,对于那些某时刻会有大量请求的系统,使用MQ的话,可以将请求先放在MQ中,先由MQ处理,之后在慢慢交给后台,防止数据量过大,服务器直接奔溃。

对于电商,用户下单,没有付款的话,使用死信队列,使当前的订单超过半个小时自动关闭。

使用MQ的时候除了他优点,要非常注意数据一致性问题,即把MQ当成中间件,要确保后面的数据写入要同步完成。

为什么使用rabbitMQ

rabbitmq对高并发的处理和适配比较好,所以一般使用这个,而且使用的公司比较多,社区的更新频繁,bug少。

保证消息的顺序

通过MQ自身的实现的话可能会影响MQ的性能,所以可以在业务层面保证消息消费的顺序,或者使无序的消息消费顺序不影响系统的使用。

保证消息的重复性

保证接收端的幂等性即可。可以使用日志文件记录消息的唯一id,消费信息后更新日志id,再此消费时判断一下当前的日志中是否存在该消息的id即可。

消息的路由方式

通过队列的路由键将队列绑定到对应的交换器上,消息到达交换器时,根据消息创建时携带的路由和交换器中队列的路由进行匹配即可。

消息基于什么传输

由于 TCP 连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ 使用信道的方式来传输数据。信道是建立在真实的 TCP 连接内的虚拟连接,且每条 TCP 连接上的信道数量没有限制。

确保消息被消费

将信道设置为confirm方式,发送方发送的消息携带唯一的id,接收方消费完成之后需要返回确认给发送方,发送方才可以删除消息。这个过程是异步的,不会影响发送方继续发送消息。如果没有接收到确认方的信息,接收方断开或者取消订阅,就把消息送给下一个接收方。如果没有断开也没有返回确认信息,发送方判断接收方忙且不在发送数据。

如何确保消息的可靠传输

对于发送方来说除了信道设置confirm方式采用唯一id等待确认方式之外,还可以使用事务,如果出错,事务回滚确保数据的安全。

队列的话可以设置一下持久化,将数据保存到磁盘,数据丢失可以进行恢复。

接收方通过业务逻辑手动回复确认即可。

如何解决MQ中的消息积压问题

新建一个 topic模式(通配符的方式),分区是原来的 10 倍,临时建立好原先 10 倍的队列 数量。然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的 10 倍数量的 队列中。接着临时征用 10 倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将 queue 资源和 consumer 资源扩大 10 倍,以正常的 10 倍速度来消费数据。等快速消费完积压数据之后,得恢复原先部署的架构,重新用原先的 consumer 机器来消费消息。

mysql面试题

数据库的三大范式

第一范式保证原子性,即当前字段不可在分割,当前字段不能由多条信息组成。

第二范式是不应存在联合主键,每条记录要有唯一性。

第三范式保证数据库的单挑数据的字段中不存在依赖关系,字段之间应保证独立。

MySQL的binlog有有几种录入格式?分别有什么区别?

binlog为mysql的二进制日志文件,记录数据库的操作语句,方便数据库进行主从复制时保证数据的一致性和产生宕机后的数据恢复问题。

binlog使用statement方式录入,会记录修改数据的sql语句,减少日志文件的大小。使用row方式录入,会记录表中每一行的改动,产生的日志文件较大,记录较全。mixed混合录入方式对于普通的操作语句使用statement方式录入,若无法使用statement,使用row方式录入。

什么是索引?

索引相当于目录,建立索引可以方便数据的查找。索引是一种文件系统,占用物理空间,一般通过B树或者B+树来实现。

索引的使用场景

一般使用最多的是主键索引,通过主键id进行查询。新建的字段一般查询时都没有索引,当数据量较大时,可以考虑使用add index为字段添加索引来提高查询效率。

使用order by对数据库某个字段进行排序时,当字段没有索引的时候,系统会将所有的数据读入到内存中,再利用内部排序进行排序最后将每个部分进行合并,很大的浪费了性能。通过对排序字段添加索引,可以根据索引的顺序进行逐条读取数据。

建立所以可以提高连接查询的效率。

select和select *的区别,如果要查询的字段已经建立了索引,那么查询时就会根据索引查询,而不是访问原数据。所以推荐使用select加字段的方式,而select * 则会全表扫描。

索引有哪几种类型

使用最多的就是主键索引,唯一id,数据项不能重复且不能为空。

数据项不能重复且可以为空的为唯一索引。

数据项可以重复可以为空的为普通索引。

对于搜索引擎还有一种全文索引。

索引的实现原理

对字段添加索引,数据库会对添加索引的字段进行排序生成排序表,在排序表上记录数据的地址。对字段进行查询的时候会先找到排序表,在通过排序表中的数据地址获取数据。

索引设计的原则

如果你的查询条件中有>,<,between等条件,对于这些条件以后的字段不应设置索引,会失效,或者将带有这些条件的字段放在最后,因为mysql是默认向右匹配到这些条件就停止的。

建立索引用于提高查询效率,因此被频繁查询的字段可以添加索引,但是频繁更新的字段不应该建立索引。

对于已经有索引的表,添加新的字段索引的时候应有限扩展索引,即在当前的基础上扩展。

如果该列作为外键使用不应建立索引。

由于建立索引要进行排序,对于字段中的数据大量重复的情形或者字段较长的也不应该进行设置索引。设置短索引。

创建索引的方式

在创建表时添加索引。

已经表的基础上通过add index或者create index的方式创建索引。

使用索引查询一定能提高查询的性能吗?为什么

索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时,索引本身也会被修改。 这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5 次的磁盘I/O。 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。使用索引查询不一定能提高查询性能。

一般来说当你根据字段查询的数据为当前表的三分之一的时候性能提升较大。或者非唯一性索引进行查询性能提升也较大。

对于百万级的数据的删除

对于有着百万数据的表中的字段索引在删除数据前,应该先删除索引,删除索引后进行数据的删除。数据删除完毕之后重新创建索引较为安全。

什么是聚簇索引?何时使用聚簇索引与非聚簇索引

聚簇索引就是将数据和索引放到一起,找到索引即找到了数据。

非聚簇索引数据和索引不在一起,优先将索引缓存到内存,查询数据先找到索引在读取磁盘找到数据。

联合索引是什么?为什么需要注意联合索引中的顺序?

联合索引是将多个字段的索引组合,联合索引是严格按照索引的排序顺序进行查询的,即先以第一个索引为基础排序,再以第二个排序。所以要严格按照联合索引的顺序使用。

什么是脏读?幻读?不可重复读?

脏读就是读到不该读的数据,A事务进行回滚前的数据被B事务读到了。

幻读就是没读全,A事务在插入数据前已经被B事务读了,A事务插入数据完成,B事务在读发现有一部分没读到。

不可重复读就是数据被别人动了,A事务进行逻辑处理的时候,B事务更新了数据,A事务使用的数据不是原来的。

什么是事务的隔离级别?MySQL的默认隔离级别是什么?

数据库定义的隔离级别阻止脏读幻读和不可重复读的发生。

read-uncommited读取未提交,最低的隔离级别,容易导致脏读幻读和不可重复读。

read-commited读取已提交,读取事务已经提交的数据,防止脏读。

repeatable-read可重复读,对同一个字段的读取结果是一致的,除非自身的事务更新了数据,可以防止脏读和不可重复读。

serializable:可串行化,满足事务的四个性质,最高隔离级别,可以防止脏读,幻读和不可重复读。

mysql的默认级别是可重复读,在使用innoDB引擎的时候分布式事务使用可串行化。

隔离级别与锁的关系

read-uncommited读取未提交读操作不使用共享锁。

read-commited读取已提交,读操作是使用共享锁,读操作完成之后释放锁。

repeatable-read可重复读,读操作使用共享锁,读操作完成之后不释放锁,事务完成之后在释放锁。

serializable对于键一直保持锁,直到事务结束。

按照锁的粒度分数据库锁有哪些?锁机制与InnoDB锁算法

数据库的锁分为行级锁,表级锁和页级锁。

其中mysql的MyISAM引擎使用表级锁。

InnoDB使用行级锁和表级锁,默认使用行级锁。

行级锁是粒度最细的锁,对行进行加锁,减少数据库操作的冲突,但是因为是对每行加锁,所以开销较大,也较为安全。

表级锁是对整张表加锁,粒度最大,发生的概率也最大,消耗较低。

页级锁兼容上面两种,做到中消耗,中冲突,每次锁定相邻的一组记录。

从锁的类别上分MySQL都有哪些锁呢?

锁的类别区分mysql有共享锁和排他锁。

共享锁即读操作时使用的锁,数据可以被多个语句读,可以被加多个共享锁。

排他锁是写操作时使用的锁,只能加一个,和其他的所有锁互斥。

MySQL中InnoDB引擎的行锁是怎么实现的?

使用索引来实现行锁。使用for update

select * from tab_with_index where id = 1 for update;

且for update查询的字段要有索引,否则会使用表级锁。

数据库的乐观锁和悲观锁是什么?

在数据库操作并发时使用悲观锁和乐观锁两种手段确保数据安全。

悲观锁是在事务查询数据的时候就加锁,而乐观锁是在事务最后修改数据的时候在加锁。

当读操作角度的时候使用乐观锁可以提高系统性能。

而在写操作多的时候保证数据的安全使用悲观锁。

为什么要使用视图?什么是视图?

视图和表相似但是是一种虚拟表,在进行查询的时候根据从基本表即实表中查询的数据动态生成一个含有这些数据的视图。

在使用数据库时,只是用某几张表中的部分数据,可以使用视图,确保其他数据的安全性。

视图有哪些特点?

视图是一种虚表。

视图中的内容可以由来自不同的实表组合生成。

对于查询视图的操作不会影响的原表。

对于数据更新的操作则会更新原表,如果视图是由多个实表中的数据组合,不允许进行数据更改。

视图的使用场景有哪些?

所使用到的数据来自多个表,且是多个表中的部分字段。

用户权限细分的时候,只给有具体权限的用户展示具体的数据。

重用sql语句时,多个业务逻辑都用到当前的sql语句查询的结果。

由于要生成视图,所以性能上会有所消耗。

什么是存储过程?有哪些优缺点?

存储过程是预编译的sql语句,可以进行模块化设计,使用的时候直接调用存储过程即可。

存储过程是预编译的且存在于数据库中,所以速度较快。同时重用性可以减少开发人员的工作量。可以进行权限的划分。

使用存储过程之后大量的存储过程维护较为麻烦。

什么是触发器?触发器的使用场景有哪些?

触发器即触发某个操作时自动执行的代码。

可以进行级联更改。

由当前数据生成其他数据的相关信息。

关联查询

内连接inner join

外连接,left join,左连接以左表为基础,右连接以右表为基础,没有的字段信息使用null。

mysql中 in 和 exists 区别

in语句将外表和内表做hash连接,exists对外表进行loop循环。每次循环过程中在对内表进行查询。

在查询的表大小差不多时,in和exists的效率相同。

当查询的表大小差别大时,子查询的表为大表使用exists,小表使用in

使用not语句,not exists一定比not in效率高,因为not exists可以使用索引。

varchar与char的区别

char的长度是固定的,为255,空白用空格填充,查询的速度较快。

varchar的长度是可变的,数据多长,varchar可以设置多长,不会浪费空间。

如何定位及优化SQL语句的性能问题?创建的索引有没有被使用到?或者说怎么才可以知道这条语句运行很慢的原因?

使用explain命令查看语句的执行计划,通过type判断当前运行的语句是否使用了索引。eq_ref是在join中使用了索引关联,ref使用了非唯一索引,index_subquery在子查询中使用非唯一索引,index为遍历索引,ALL为全表扫描。

如果possible_keys为null应该考虑sql优化,因为该字段显示查询时所查询字段的索引。以及key字段显示当前使用的索引。

SQL的生命周期?

应用服务器和数据库服务器建立连接,数据库服务器获取应用服务器的sql语句,生成执行计划,查询数据并读取到内存进行逻辑处理,最后将数据返回给应用服务器,断开连接,释放资源。

大表数据查询,怎么优化

使用索引或者使用缓存技术。

超大分页怎么处理?

使用缓存或者使用带索引的字段进行子查询。

为什么要尽量设定一个主键?

主键是一张表中一行数据唯一性的保证。在增删改查的时候,使用主键更快且能保证数据的安全和隐蔽。

主键使用自增ID还是UUID?

推荐使用自增id,因为mysql使用的算法是B+树,叶子节点上按照顺序存放主键索引,若是自增id,插入数据进行往下排列即可。如果是uuid,若新的id与已有的id大小不一致会导致数据的移动,产生内存碎片消耗性能。

优化查询过程中的数据访问

避免查询过多的不必要的数据,可以使用limit字段。尽量避免使用select *而应该使用select具体字段,尤其是在多表关联查询的时候更应该指定列名。对于热点数据的查询,应优先考虑使用缓存。

优化特定类型的查询语句

使用count(*)会直接统计列树,不建议使用count(列名)。当对count没有确切的需求是可以用explain近似值代替count。使用limit可以记录上一次查询的最大id。

优化WHERE子句

在where子句中应避免使用null,!=和大于小于操作符。避免使用in,not in,避免使用函数,参数和表达式求值等。都会导致全表扫描。

MySQL数据库cpu飙升到500%的话他怎么处理?

查看当前的进程判断是否是mysqld的进程导致的cpu增加。如果是使用show processlist查看sql语句的性能消耗,找到消耗大的sql语句查看执行计划,进行sql优化。

springboot知识点

Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

springboot的核心注解是springbootApplication,它包含三个注解分别是springbootConfiguration,EnableAutoConfiguration和ComponentScan。其中SpringbootConfiguration用于实现配置文件,EnableAutoConfiguration用于实现自动化配置,ComponentScan实现组件扫描。

什么是 JavaConfig?

javaconfig提供配置容器的纯java方法,从而避免使用繁杂的xml配置。可以直接使用类的方式来进行容器配置。通过泛型按照类型的方式来配置更加安全,便于重构。

Spring Boot 是否可以使用 XML 配置 ?

可以使用@importResource注解来使用xml配置

mybatis知识

为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?

全自动ORM hibernate可以根据对象关系映射获取数据,而mybatis半自动映射需要自己书写sql语句。

mybatis的优势

mybatis使用线程池,使用的时候去线程池中取即可,避免了创建释放导致的资源消耗。

mybatis将java代码和sql语句分离开来,配置在mapper.xml中,对系统进行解耦。

mybatis可以将类对象直接映射到sql语句,反之根据返回类型的设置可以将数据映射到类。

MyBatis的工作原理

框架启动加载mybatis的配置文件,读取项目中的对应的mapper.xml中的sql语句。构造会话工厂sqlsessionfactory,创建sqlsession会话信息。使用exec执行器创建mappedstatement对象,mapped statement读取传入的参数,并将返回的数据映射到类对象。

为什么需要预编译

预编译的sql语句在数据库拿到之后可以直接执行,减少数据库对复杂sql语句编译的消耗。

对于预编译产生的statement对象可以重复利用从而减少开销。

使用预编译可以防止sql注入。

Mybatis都有哪些Executor执行器?它们之间的区别是什么?

mybatis有三种执行器,simple,reuse和batch

simple每执行一次查询或者更新创建一个statement对象,使用完后关闭。

reuse创建statement对象后,使用完不关闭,放入Map中存储进行重复使用。

batch执行更新时使用批处理,将所有sql添加到批处理之后统一执行。

Mybatis中如何指定使用哪一种Executor执行器?

可以在mybatis的配置文件中设置执行器。也可以在创建sqlsessionfactory工厂时将执行器的类型作为参数指定。

Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

在mybatis中对于association和collection是支持延迟加载的,可以在配置文件中通过lazyLoadingEnable开启延迟加载。

使用cglib创建代理对象,当进行方法调用的时候发现值为空时,根据实现保存的sql查询到当前为空的值并进行赋值,然后在进行方法调用。

#{}和${}的区别

#{}会对sql语句进行预处理可以防止sql注入。而${}是直接将字符串进行替换。

模糊查询like语句该怎么写

一般用concat(’%’,#{},’%’)的形式拼接查询。

也可以在sql中使用bind标签,指定name和value,其中value进行组合查询字段,sql语句中使用bind标签定义的name。

在mapper中如何传递多个参数

可以使用占位符的方式,即0,1

可以在dao层的接口中使用Param注解,指定传入参数的名称

可以封装map,将map中的key与字段进行直接映射。

使用javabean,通过parameterType传入指定的类的地址进行类和字段的映射。

Mybatis如何执行批量操作

使用foreach进行迭代处理。

如何获取生成的主键

开启useGeneratedKeys标签,使用keyProperty指定返回的主键字段名称。

当实体类中的属性名和表中的字段名不一样 ,怎么办

在sql语句中可以定义别名。

使用resultMap定义映射关系,在sql标签中指定定义的resultMap进行映射。

什么是MyBatis的接口绑定?有哪些实现方式?

将接口和sql语句绑定,使用接口即可调用sql进行数据查询。

使用注解的方式来绑定,但是耦合度太高。

一般使用namespace指定dao类的方式进行接口绑定。

使用MyBatis的mapper接口调用时有哪些要求?

方法名和sqlid要相同。

传入参数和返回参数的类型要在sql中通过parameterType和resultType进行定义。

Mybatis映射文件中,如果A标签通过include引用了B标签的内容,请问,B标签能否定义在A标签的后面,还是说必须定义在A标签的前面?

顺序没有关系,当a标签调用b标签时,若a标签解析时发现b不存在,则会暂停a标签解析,解析需要的标签,需要的标签解析完毕之后,在解析a标签。

MyBatis实现一对一,一对多有几种方式,怎么操作的?

通过association标签实现一对一,通过collection标签实现一对多。

Spring知识点

版本迭代

4.x 增加了lamda表达式,同时为了适配restfui接口格式,添加了restController接口。

5.x支持函数和响应式编程

spring核心

spring核心包括IOC,AOP和DI

其中IOC是控制反转,spring框架起到了中介的作用,程序员只需要编程自己的需求,相应的提供由spring框架来完成。实现了可插拔式接口,降低了代码的耦合度。

为了提升组件重用的概率,使用DI,容器在系统运行期间进行依赖注入。通过DI来实现spring的IOC的功能。

常见的注入模式一般为setter注入和构造方法注入和注解注入。

使用setter方式注入一般使用xml的形式,使用bean标签进行类的注入。

使用注解注入一般使用autowired,service等注解实现。

aop是切面编程,一般使用注解的方式after,before,pointcut等注解实现,使用springboot之后进行日志记录或者接口防刷等,自定义注解后,在具体的方法上使用自己的注解即可。

@Component 和 @Bean 有什么区别?

@Component注解主要运用于类上面,使用SomponentScan进行扫描并注册到spring容器之中,而@bean主要运用于方法,标志某个类的实例,需要时在使用。

Spring 中 bean 的作用域有几种类型?

单例模式整个应用程序只创建一个bean。原型模式,每次注入都会创建一个bean。会话模式,每次会话创建一个bean,但只在web系统中有效。请求模式,每次请求创建一个bean,只在web系统中有效。

spring实现事务的方式

使用编程式或者注解的方法。一般使用主机的方法使用事务。声明式事务,底层是建立在 Spring AOP 的基础上,在方式执行前后进行拦截,并在目标方法开始执行前创建新事务或加入一个已存在事务,最后在目标方法执行完后根据情况提交或者回滚事务。
声明式事务的优点:不需要编程,减少了代码的耦合,在配置文件中配置并在目标方法上添加 @Transactional 注解来实现。

Spring 声明式事务无效可能的原因有哪些?

mysql使用MyISAM引擎。

注解只支持public方法。

Spring 中的 Bean 是线程安全的吗?

bean默认是单例模式,所以是非线程安全的。可以设置为原型模式,每次注入创建一个bean可以保证线程安全。

Spring 中都是用了哪些设计模式?

在listen中进行监听,动作触发进行通知使用了观察者模式。

通过beanFactory或者ApplicationContext创建bean使用的是工厂模式。

bean的单例或者原型使用单例原型模式。

在创建代理类时,根据代理的是不是接口选择不同的代理方式使用的是策略模式。

SpringMVC知识点

Spring MVC的主要组件?

DispatcherServlet前端控制器,主要用于接收请求和响应结果。

HandlerMapping映射器,根据对应的映射路径找到handler。

HandlerAdapter适配器,用于执行handler。

handler程序员开发,相当于方法。

ViewResolver视图解析器,用于解析视图。

View视图,即程序员编写的前端页面。

请描述Spring MVC的工作流程?描述一下 DispatcherServlet 的工作流程?

用户发起请求至DispatcherServlet,DispatcherServlet拿到请求之后调用handlerMapping映射器请求获取handler。HandlerMapping根据url映射地址找到handler后生成处理器对象返回给DispatcherServlet。DispatcherServlet拿到对象之后调用adapter适配器,适配器调用具体的handler处理逻辑,最终返回modelandView。DispatcherServlet将ModelAndView传给ViewResolver视图解析器解析。解析完成后送给view页面。DispatcherServlet渲染页面,将数据显示到视图中,最终响应给用户。

MVC是什么?MVC设计模式的好处有哪些

MVC是一种设计模式,采用三层架构的方式。将系统进行解耦,提高开发效率。

Spring MVC常用的注解有哪些?

@RequestMapping用于映射url和类中的方法。

@RequestBody将http请求中接收到的json数据转换为系统中的类。

@ResponseBody将返回的类数据转换为json返回给前端。

@Controller注解的作用

@Controller负责接收DispatcherServlet的请求,然后交由对应的方法处理,最终再把返回的ModelAndView交给视图。在类上标注注解并在配置文件中加载对应的controller类的bean对象即可。

Spring MVC怎么样设定重定向和转发的?

转发使用forward,不改变页面路径。而重定向使用redirect,改变页面url。

如何解决POST请求中文乱码问题,GET的又如何处理呢?

POST请求在web.xml文件中配置一下CharacterEncodeingFilter的属性为utf-8即可。

GET请求修改一下tomcat服务器的配置文件。

javaSE

JVM、JRE和JDK的关系

jvm是虚拟机,java程序是运行在虚拟机上的,不同的平台有不同的虚拟机,因此java是可以跨平台的。

jre是虚拟机和java程序运行的必不可少的类库和各种包。

jdk是程序员使用的开发工具,包括jre和编译工具,打包工具等。

什么是字节码?采用字节码的最大好处是什么

字节码是java代码通过虚拟机编译后产生的只面向虚拟机的.class文件。

使用字节码,由于字节码是面向虚拟机的,因此移植的时候安装具体的虚拟机即可,不用考虑平台。使用字节码,虚拟机解释语言的效率也比较高。

Java有哪些数据类型

byte 1字节,int 4字节,boolean 一字节,double 8字节,long 8字节,char 2字节,short 2字节。float 4字节。

对应的包装类,Byte,Integer,Boolean,Double,Long,Character,Short,Float

switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上

java7之后switch是可以支持String的,byte是一只支持的。long是不支持的。

用最有效率的方法计算 2 乘以 8

a<<b,<<相当于乘,>>相当于除,a为底数,b为次方。

Math.round(11.5) 等于多少?Math.round(-11.5)等于多少

round函数对于正数来讲向上取整,对于负数来说也是向上取整。

short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗

+=符号会自动进行转型,不会发生错误。

访问修饰符 public,private,protected,以及不写(默认)时的区别

public是同包下和其他包下,private是本类,protect是本包,子类。default是本包。

&和&&的区别

和| ||相同,&只是前一个成功,后一个就忽略。而&&是双方必须都成功。

final 有什么用?

final修饰变量无法更改。修饰方法无法重写。修饰类无法继承。

final修饰变量时,若变量是引用变量,地址无法更改,而内容可以更改。

final finally finalize区别

finally用于异常捕获,无论异常是否发生都会执行,且在方法return之前执行。finalize是Object的方法,所有的类都继承,如果实现了该方法,在第一次垃圾回收该对象时,先调用该方法。第二次则进行标记直接回收。

static存在的主要意义

即便没有创建对象,也可以使用类名调用变量和方法。

使用静态代码块来优化性能,在类中而不是方法中是无法进行变量运算的,可以使用静态代码块。

static的独特之处

不属于创建的实例对象,而是被类的实例对象共享。

共享对象值的变化是通用的。

break ,continue ,return 的区别及作用

break是跳出当前的循环体。continue是跳出当前循环,进行下一轮。return是返回,屏蔽return以后的语句。

抽象类和接口的对比

抽象类和接口都不能被实例化。其中包括的方法子类必须实现。

接口没有构造函数。抽象类的方法没有方法体。

接口只能被定义为public,抽象类只能继承一个。

接口的字段默认都是static和final的。

在Java中定义一个不做事且没有参数的构造方法的作用

当调用子类的时候,子类没有使用super来调用父类的构造方法,就会默认调用父类的无参构造器。而如果父类没有无参构造器,则编译错误。

内部类

内部类为静态内部类,成员内部类,局部内部类和匿名内部类

静态内部类是类中的静态类,静态内部类无法访问外部的非静态变量。成员内部类是类内部定义在成员位置上的非静态类,成员内部类可以访问外部的静态和非静态变量,私有和非私有变量。局部内部类是定义在方法中的内部类,可以访问外部的所有方法和所有变量。匿名内部类是没有名字的内部类,首先匿名内部类实现接口或者继承一个类,匿名内部类中不能定义静态方法和静态变量,匿名内部类用到的外部参数需要设置为final类型。

匿名内部类使用参数为什么要设置为final

在类中定义的成员变量存储在栈中,当方法销毁时会销毁成员变量,而匿名内部类仍然保有对成员变量的引用,因此如果不把变量设置为final,则会被销毁,匿名内部类会出错。

重写和重载

父类的构造函数无法继承因此无法被重写,可以被重载。重载意味着方法名相同,其他可以不同。而重写方法名相同,参数列表相同。返回值小于父类,可见性大于父类。

== 和 equals 的区别是什么

==对于对象的判断是判断引用地址,对于基本变量的判断是判断值。equals判断的是变量的值。

对于String类型的数据使用equals进行比较,由于String的equals方法被重写过,因此比较的是String的值。在创建String时,若在常量池中存在当前的字符串,则直接引用,否则重新创建对象。

hashCode 与 equals

hashcode是数据存储的索引,存储数据时根据hashcode的值进行存储,若存在相同的值,则不存,若hashcode计算相同,值不相同则发生冲突在进行散列。

两个对象相等,则hashcode一定相等。若hashcode相等,则两个对象不一定相等。

hashcode是对堆上的对象产生独特值,若不重写,则两个class类永远不会相等。

对象的相等与指向他们的引用相等,两者有什么不同?

对象的相等指值的相等。而引用相等,意味着所在的内存地址相等。

值传递

java中只存在值传递。当一个基本数据变量通过参数使用时,参数只是一份原先数据的拷贝,对于其他方法中对变量的操作不会影响到原变量。当对象引用作为参数传递时,方法中对参数的更改会更改原对象的相关数据。

java 中 IO 流分为几种?

输入输出流,字节字符流

什么是反射机制?

反射即在程序运行期间动态的获取当前类的属性和方法。可以使用指定位置或者forClassName指定类名的方式获取对应的属性和方法。

String真的是不可变的吗?

不可变,String是用final修饰的无法被继承,底层使用的是char型的数组。

在使用 HashMap 的时候,用 String 做 key 有什么好处?

String是不可变的,因此使用String作为key得到的hashcode被缓存后不需要再此计算。

Integer a= 127 与 Integer b = 127相等吗

当int型变量在-128到127之间时,会在常量池中调用已有的变量值。当超过这个范围,会创建新的对象。

String和StringBuffer、StringBuilder的区别是什么?

当字符串使用较多且不存在多线程的时候建议使用StringBuilder,存在缓冲区,减少了String频繁的性能消耗。使用多线程下使用StringBuffer,StringBuffer是线程安全的。


文章作者: dinggc
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 dinggc !
评论
 上一篇
个人类库工具文档(持续更新) 个人类库工具文档(持续更新)
个人类库工具文档(持续更新) 文件操作类 方法名称 方法注释 传入参数 参数注释 返回结果 结果注释 FileUtils.ImgUpload() 文件上传工具类 file MultipartFile文件类 Map 返回Map类
2021-03-31
下一篇 
opencv学习笔记一 opencv学习笔记一
opencv学习笔记一 opencv模块 core:实现了最核心的数据结构及其基本运算,如绘图函数,数组操作相关函数等。 highgui:实现了视频与图像的读取,显示,存储等接口 imgproc:实现了图像处理的基础方法,包括图像滤波,图像
2021-03-29
  目录