八股文——java相关问题

hashmap的相关问题?

解决hash冲突的方法?
链接
1、hash冲突方法
2、链地址法
3、再哈希法

鏈接
JDK1.8版本的,内部使用数组 + 链表红黑树;
在这里插入图片描述
1、判断数组是否为空,为空进行初始化;
2、不为空,计算 k 的 hash 值,通过(n - 1) & hash计算应当存放在数组中的下标 index;
3、查看 table[index] 是否存在数据,没有数据就构造一个Node节点存放在 table[index] 中;
4、存在数据,说明发生了hash冲突(存在二个节点key的hash值一样), 继续判断key是否相等,相等,用新的value替换原数据(onlyIfAbsent为false);
5、如果不相等,判断当前节点类型是不是树型节点,如果是树型节点,创造树型节点插入红黑树中;
6、如果不是树型节点,创建普通Node加入链表中;判断链表长度是否大于 8, 大于的话链表转换为红黑树;
7、插入完成之后判断当前节点数是否大于阈值,如果大于开始扩容为原数组的二倍。

那HashMap设定初始容量大小:
一般如果new HashMap() 不传值,默认大小是16,负载因子是0.75, 如果自己传入初始大小k,初始化大小为 大于k的 2的整数次方,例如如果传10,大小为16。(补充说明:实现代码如下)

HashMap的哈希函数设计:
在这里插入图片描述
1、得到hash值,代码如下:高16位与低16位进行异或操作其实是用到“扰动函数”方法,目的是为增加hash的随机性。
在这里插入图片描述
2、(cap-1) &hash 其实就是取余操作,这里用的比较精妙,因为cap为2次幂,减掉一后与hash取并操作刚好和取余操作结果等价。

jdk 1.8对hashmap的优化:
1、数组+链表改成了数组+链表或红黑树;
防止发生hash冲突,链表长度过长,将时间复杂度由O(n)降为O(logn);

2、链表的插入方式从头插法改成了尾插法
因为1.7头插法扩容时,头插法会使链表发生反转,多线程环境下会产生环;

3、扩容的时候1.7需要对原数组中的元素进行重新hash定位在新数组的位置,1.8采用更简单的判断逻辑,位置不变或索引+旧容量大小;
由于是扩容是扩大两倍,用(cap)&hash得到的结果就是二进制数字前面多的那个数字,是‘0’则是原来位置,是‘1’数组索引就是n*2(用到了‘&’的性质,任何数与1取&结果都是自己)。

解决hashmap线程不安全的问题:
Java中有HashTable、Collections.synchronizedMap、以及ConcurrentHashMap可以实现线程安全的Map。

HashTable是直接在操作方法上加synchronized关键字,锁住整个数组,粒度比较大,Collections.synchronizedMap是使用Collections集合工具的内部类,通过传入Map封装出一个SynchronizedMap对象,内部定义了一个对象锁,方法内通过对象锁实现;ConcurrentHashMap使用分段锁,降低了锁粒度,让并发度大大提高。

ConcurrentHashMap的分段锁的实现原理:
ConcurrentHashMap成员变量使用volatile 修饰,免除了指令重排序,同时保证内存可见性,另外使用CAS操作和synchronized结合实现赋值操作,多线程操作只会锁住当前操作索引的节点。

如下图,线程A锁住A节点所在链表,线程B锁住B节点所在链表,操作互不干涉。

在这里插入图片描述

HashMap内部节点是有序的吗?LinkedHashMap如何保证有序的?
不是, LinkedHashMap 和 TreeMap是有序的
LinkedHashMap内部维护了一个单链表,有头尾节点,同时LinkedHashMap节点Entry内部除了继承HashMap的Node属性,还有before 和 after用于标识前置节点和后置节点。可以实现按插入的顺序或访问顺序排序。

TreeMap是按照Key的自然顺序或者Comprator的顺序进行排序,内部是通过红黑树来实现。所以要么key所属的类实现Comparable接口,或者自定义一个实现了Comparator接口的比较器,传给TreeMap用户key的比较。

Spring 系列问题

什么是spring?

spring是一个轻量级的实现控制反转(IOC) 和切面编程(AOP)的容器框架。

  • 从大小和开销方面spring都是轻量级的
  • 通过控制反转IOC的技术达到轻耦合的目的
  • 通过面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
  • 包含并管理应用对象(Bean)的配置和生命周期,这个意义上是个容器
  • 将简单的组合配置组合为复杂的应用,这个意义上是个框架

1、说说Spring 里用到了哪些设计模式?

连接
单例模式:Spring 中的 Bean 默认情况下都是单例的。

工厂模式:工厂模式主要是通过 BeanFactory 和 ApplicationContext 来生产 Bean 对象。
两者对比:
BeanFactory :延迟注入(使用到某个 bean 的时候才会注入),相比于BeanFactory来说会占用更少的内存,程序启动速度更快。
ApplicationContext :容器启动的时候,不管你用没用到,一次性创建所有 bean 。BeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory ,除了有BeanFactory的功能还有额外更多功能,所以一般开发人员使用ApplicationContext会更多。

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext(
                "C:/work/IOC Containers/springframework.applicationcontext/src/main/resources/bean-factory-config.xml");

        HelloApplicationContext obj = (HelloApplicationContext) context.getBean("helloApplicationContext");
        obj.getMsg();
    }
}

代理模式:最常见的 AOP 的实现方式就是通过代理来实现,Spring主要是使用 JDK 动态代理和 CGLIB 代理。
链接

  • JDK动态代理是面向接口的。

  • cglib是通过字节码底层继承被代理类,然后重写父类方法(如果被代理类继承final则会失败)

  • 如果被代理的对象是一个实现类(例如UserDaoImpl),那么Spring AOP会默认用JDK动态代理,否则使用cglib。

如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理,CGLib采用底层的字节码技术,全称是:Code Generation Library,CGLib可以为一个类创建一个子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。如下图所示:
在这里插入图片描述

使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。

Spring AOP 和 AspectJ AOP 有什么区别?
Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。

Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单,

如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多。

模板方法模式:主要是一些对数据库操作的类用到,比如 JdbcTemplate、JpaTemplate,因为查询数据库的建立连接、执行查询、关闭连接几个过程,非常适用于模板方法。
观察者模式
观察者模式是一种对象行为型模式。它表示的是一种对象与对象之间具有依赖关系,当一个对象发生改变的时候,这个对象所依赖的对象也会做出反应。Spring 事件驱动模型就是观察者模式很经典的一个应用。Spring 事件驱动模型非常有用,在很多场景都可以解耦我们的代码。比如我们每次添加商品的时候都需要重新更新商品索引,这个时候就可以利用观察者模式来解决这个问题。
定义一个事件: 实现一个继承自 ApplicationEvent,并且写相应的构造函数;
定义一个事件监听者:实现 ApplicationListener 接口,重写 onApplicationEvent() 方法;
使用事件发布者发布消息: 可以通过 ApplicationEventPublisher 的 publishEvent() 方法发布消息。

适配器模式
我们知道 Spring AOP 的实现是基于代理模式,但是 Spring AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是AdvisorAdapter 。Advice 常用的类型有:BeforeAdvice(目标方法调用前,前置通知)、AfterAdvice(目标方法调用后,后置通知)、AfterReturningAdvice(目标方法执行结束后,return之前)等等。每个类型Advice(通知)都有对应的拦截器:MethodBeforeAdviceInterceptor、AfterReturningAdviceAdapter、AfterReturningAdviceInterceptor。Spring预定义的通知要通过对应的适配器,适配成 MethodInterceptor接口(方法拦截器)类型的对象(如:MethodBeforeAdviceInterceptor 负责适配 MethodBeforeAdvice)。

spring MVC中的适配器模式
在Spring MVC中,DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由HandlerAdapter 适配器处理。HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类。

2、谈谈你对IOC 和 AOP 的理解?他们的实现原理是什么?

IOC叫做控制反转,通过容器来管理对象的创建、配置和生命周期,这样相当于把控制权交给了Spring,不需要人工来管理对象之间复杂的依赖关系,这样做的好处就是解耦。

ioc容器实际是map(key,value),里面存放的是各种对象(在xml里配置的bean节点、@repository、@service、@Controller、@component),在项目启动时,spring通过读取配置文件里的bean节点,使用反射创建对象存入map中。
接下来使用对象时候,再通过DI注入(autowired、resource),根据类型或者id注入。
通过容器来管理对象的创建、配置和生命周期,这样相当于把控制权交给了Spring,不需要人工来管理对象之间复杂的依赖关系,这样做的好处就是解耦。

在Spring里面,主要提供了 BeanFactory 和 ApplicationContext 两种 IOC 容器,通过他们来实现对 Bean 的管理。

AOP 叫做面向切面编程,目的就是提高代码的模块性,对方法进行增强。允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。

Srping AOP 基于动态代理的方式实现,如果是实现了接口的话就会使用 JDK 动态代理,反之则使用 CGLIB 代理,Spring中 AOP 的应用主要体现在 事务、日志、异常处理等方面,通过在代码的前后做一些增强处理,可以实现对业务逻辑的隔离,提高代码的模块化能力,同时也是解耦。Spring主要提供了 Aspect 切面、JoinPoint 连接点、PointCut 切入点、Advice 增强等实现方式。

3、 JDK 动态代理和 CGLIB 代理有什么区别?

1)、代码如何实现AOP,切面增强?

1)使用@Aspect 注解
2)使用@Around、@Pointcut、@Before、@After、@afterReturning环绕代码块块 ,执行顺序 Around --> Before --> Around --> After --> AfterReturning --> AfterThrowing
3)创建bean 的时候符合这个 PointCut 规则的,就用动态代理(JDK Proxy、CGLib)的方式创建代理对象作为 bean放到容器中。
4)当我们从 bean 容器中获取代理对象 bean 并调用它的方法的时候,因为这个bean是通过代理的方式创建的,所以必然会走org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept() 方法,而这个方法也必然会执行org.springframework.aop.framework.ReflectiveMethodInvocation#proceed() 这个方法,而这个方法就会根据上面说的执行过程依次执行不同的 MethodInterceptor 子类对象的 invoke() 方法,这个方法会根据元数据信息通过反射的方式调用代理对象对应的真正的对象的方法

2). aop的实际应用场景
是打印日志的时候可以使用
在这里插入图片描述

@Aspect
@Component
public class HelloAspect {

  @Before("execution(* com.test.service.impl.HelloServiceImpl.sayHello(..))")
  public void sayHello(){
    System.out.println("hello Java编程技术乐园!");
  }

}

3)实现动态代理的方式JDK 动态代理和 CGLIB 代理有什么区别?
这个以后再补充吧。21.7.13.

4、Spring 循环依赖说一下?spring 允许循环依赖吗?

链接

[链接] (https://mp.weixin.qq.com/s/S2sQw7ejcc7K4nO9UaNQ7A)

循环依赖:说白是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用。
在这里插入图片描述
(spring中单例和多例的区别:链接)
spring内部有三级缓存:singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例。(我们直拿到完整的bean的地方,)earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例
singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。

这三个缓存本质其实就是三个Map,本质就是先去缓存里找Bean,没有则实例化当前的Bean放到Map,如果有需要依赖当前Bean的,就能从Map取到。二级缓存和三级缓存都属于cache,一旦建立好依赖,二级缓存和三级缓存数据会被清理掉。
在这里插入图片描述
为什么要使用三级缓存,而不只使用二个级别的缓存就够了?
因为第一级缓存存储的是完全初始化好的bean,而二级缓存数据解决了循环依赖,但是数据并没有经过初始化,因此需要将初始化好后,二级缓存bean放入一级缓存bean。

5、BeanFactory和applicationcontext有什么区别?

链接
该类是SpringIOC容器是创建Bean的一种形式

ApplicationContext是BeanFactory的子接口。

ApplicationContext提供了更完整的功能:
1、继承MessageSource,支持国际化
2、统一的资源文件访问方式
3、提供在监视器中注册bean的事件
4、同时加载多个配置文件
5、载入多个(有继承关系)上下文,使得每个上下文都专注于一个特定的层次,比如应用web层。

不同:
beanFactory是懒加载
applicationContext是运行时加载,好处是运行就能知道依赖注入是否准确,坏处是比较消耗内存

6.Spring Bean的生命周期说说?

连接
1、解析类得到BeanDefinition
2、如果有多个构造方法,则要推断构造方法。
3、确定好构造方法后,进行实例化一个对象。
4、对对象中加了@Autowired注解的属性进行属性填充。
5、回调Aware方法
6、调用BeanPostProcessor的初始化前的方法
7、调用初始化方法
8、调用BeanPostProcessor的初始化后的方法,进行AOP
9、如果创建的Bean是单例的则会加Bean加入单例池
10、使用bean
11、spring容器关闭时调用DisposableBean中的destroy方法。

spring的bean的几种作用域

  • 单例模式(默认):每个容器中只有一个bean实例,单例的模式是由BeanFactory自身来维护,生命周期与IOC容易一致。
  • prototype(原生模式):让每一个bean请求提供一个实例,每次注入都会有新对象。
  • request:bean被定义为每个http请求创建一个单例对象
  • session:与request类似
  • application:bean被定义为在servletContext的生命周期中复用一个单例对象。
    …还有几个

spring事务的实现方式和原理以及隔离级别?

使用 @Transactional 注解实现spring的事务

@Service
public class TestService {

    @Autowired
    private EmployeeRepository employeeRepository;
    
    @Transactional
    public Employee addEmployee() throws Exception {

        employeeRepository.deleteAll();

        Employee employee = new Employee("3y", 23);

        // 模拟异常
        int i = 1 / 0;

        return employee;
    }

}

事务这个概念其实是数据库层面的,spring是基于数据库中的事务进行了扩展。
比如我们可以通过在方法是加是@Transactional注解,就可以启动事务,这个方法中所有的sql都会在一个事务中,进行,统一成功和失败。

在一个方法中加了@Transactional后,spring会基于这个类生成一个代理对象,会将这个代理对象作为bean,当在使用这个代理对象时,存在@Transactional的方法上若出现异常,那么代理逻辑中就会回滚,否则提交。

spring的事务基于AOP的动态代理来实现的。、
在这里插入图片描述

@Transactional 注解不生效原因

csdn

spring的隔离级别:(和数据库隔离级别一致)

事务的传播性:
知乎
默认是required,还有support;

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}
@Transactional(propagation = Propagation.REQUIRED)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}

required事务依赖如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务
support:当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行

前端发起请求spring交互流程

链接
(太多了,以后再说)
抵达 Spring MVC 的所有 HTTP 请求均由前置分发器 DispatcherServlet 统一分发

spring/springboot启动过程

springboot与spring的区别:

Spring Boot实现了auto-configuration自动配置(另外三大神器actuator监控,cli命令行接口,starter依赖),降低了项目搭建的复杂度。它主要是为了解决使用Spring框架需要进行大量的配置太麻烦的问题,所以它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具;同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box)。

springboot自动配置原理

链接
springboot是约定大于配置的。自动配置类由各个的starter提供,使用@Configration+@Bean定义配置类,放到META-INF/spring.factories下,使用spring spi扫描META-INF/spring.factories下的配置类。
使用@import导入自动配置类。
在这里插入图片描述
starter包如何运行的?
在这里插入图片描述

Spring单例模式如何保证线程安全?Spring如何解决并发访问线程安全?引申懒汉和饿汉相关

@RestController 和 @Controller的区别?

@RestController是@Controller和@ResponseBody的结合体。@ResponseBody返回json类型,适用于前后端分离的场景中。

控制层需要返回视图或者其他数据类型,那就用@Controller,

Java反射机制?调用反射的几种方法

连接
Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载。
获取class类对象:
通过java.lang.Class类获取对象。
1、 使用类对象的getClass()方法
2、使用 Class.forName(classname)
3、 .class 方法

通过Class的 newInstance() 方法实例化对象

对象的克隆,深克隆和浅克隆

克隆:拷贝对象。浅克隆:对象的基本类型(int,char等)创建新地址,其他类型(String,自定义类)为共享地址。深克隆:所有类型都重新new地址

浅克隆:implements Cloneable
深克隆 其他类型也implements Cloneable或者主类序列化implements Serializable

java中有几种IO模型?

同步/异步(响应是否需要马上处理)指server服务端,阻塞/非阻塞(是否能切换线程)是client客户端
BIO(block io) 同步阻塞IO,客户端发请求后一直等服务端相应
NIO(new io, jdk1.4引出) 同步非阻塞IO,客户端发请求后,就去干别的事情,时不时的过来检查服务端是否给出相应。(最常用的)
AIO 异步非阻塞IO:

以下内容链接
BIO 为同步阻塞IO,客户端向服务端发送请求一直要等待响应,而且一个线程只能处理一个IO接口。效率非常低。
在这里插入图片描述
NIO为同步非阻塞IO,通过Selector、Channel、Buffer来实现。一个线程通过Selector轮询访问Channel,若Buffer中的数据有IO读写需求,则进行读写,否则跳过。NIO的优化:(1)不必对每个连接分别创建线程;(2)数据读写非阻塞。
(个人理解,linux文件系统中的select、poll函数都是NIO)
在这里插入图片描述
AIO:异步非阻塞IO。NIO的基础上优化了轮询机制。轮询去检查Buffer读写效率不高。对于客户端来说,应该让服务器将读写的内容完成,客户端线程直接将数据拿走,可以提高IO效率。即异步。
(个人认为就是linux 的 epoll )

java中的IO中用到的设计模式

csdn
1、适配器模式
//file 为已定义好的文件流
FileInputStream fileInput = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(fileInput);

以上就是适配器模式的体现,FileInputStream是字节流,而并没有字符流读取字符的一些api,因此通过InputStreamReader将其转为Reader子类,因此有了可以操作文本的文件方法。换句话说,就是将FileInputStream读取一个字节流的方法扩展转换为InputStreamReader读取一个字符流的功能。
2、装饰者模式

BufferedReader bufferedReader=new BufferedReader(inputStreamReader);

构造了缓冲字符流,将FileInputStream字节流包装为BufferedReader过程就是装饰的过程,刚开始的字节流FileInputStream只有read一个字节的方法,包装为inputStreamReader后,就有了读取一个字符的功能,在包装为BufferedReader后,就拥有了read一行字符的功能。

http的get和post的区别?getMapping和postMapping的区别?

  • http协议上的request里的method参数。 http 协议的请求可以分成三部分,request
    line、headers、body。Method是request
    line里的一个参数,这个参数可以设置为post、get等,所以post和get都是可以带body的,只是网页编程中get是从服务器上获取数据,post是向服务器传送数据。而网页发送数据没有body,所以网页发送的get请求是没有body的。

  • @GetMapping是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。该注解将HTTP Get 映射到 特定的处理方法上。

  • 同理PostMapping也是一个组合注解,是@RequestMapping(method = RequestMethod.POST)的缩写。

cookie与session的区别,java如何创建一个session对象

链接
session是服务端用来保存用户数据的。由于http是无状态协议,因此客户端会用session用于保存、跟踪客户。在服务端保存Session的方法很多,内存、数据库、文件都有
cookie的客户端(浏览器)用来记录用户信息的。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。

总结一下:

  • Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;
  • Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。

java的消息中间件?消息队列的作用?

链接
解耦、异步、削峰

String的equals和==的区别

’=='其实是比较栈中的值。基本变量(如int、long、char)等值都是存在栈区,而String等对象则存在堆区,存在栈区的是引用地址, 因此就算两个String变量的值相同,但是地址不同,‘==’的结果肯定是false

Objet的equals其实就是’==‘,因此equals需要重新,只不过是String对象的equals已经重写过了。
在这里插入图片描述

final相关

  • 修饰类:表示类不可以继承
  • 修饰方法:方法不可以被重写,可以被重载
  • 修饰变量:不可以修改变量的值。
    (1)基本值:修饰后基本值不能修改
    (2)引用值,值其实可以修改。但不能设为null
    在这里插入图片描述
    为什么局部内部类和匿名内部类只能访问局部final变量?
    在这里插入图片描述
    在编译时,局部内部类Thread()和test类都会生成各自的class文件,局部内部类Thread()会将b值拷贝一份到class文件中,jvm为了使得这两个class中的变量值相同,不得已只能用final修饰,确保b变量无法被修改。

接口和继承的区别

  • 接口的设计目的,是对类的行为进行约束(更准确的说是一种“有”约束,因为接口不能规定类不可以有什么行为),也就是提供一种机制,可以强制要求不同的类具有相同的行为。
  • 而抽象类的设计目的,是代码复用。当不同的类具有某些相同的行为(记为行为集合A),且其中一部分行为的实现方式一致时(A的非真子集,记为B),可以让这些类都派生于一个抽象类。在这个抽象类中实现了B,避免让所有的子类来实现B,这就达到了代码复用的目的。

1、抽象类只能继承一个,接口可以实现多个。
2、抽象类可以实现方法,接口不可以实现普通的方法,但是可以实现default修饰的方法。
3、接口定义的变量都是public static final的。接口就是提供一种统一的’协议’,而接口中的属性也属于’协议’中的成员.它们是公共的,静态的,最终的常量.相当于全局常量.

public interface test {
    int k =2;
    public default void test1(){
        int a = 1;
        int b = 2;
    }
    public void test2();
}

hashcode()

hashCode() 存储的是值其实是hash表的索引。jvm会维护一个哈希表,这个哈希表帮助jvm获取对象在堆中的存储位置,而索引值其实就是hashCode()。

RabbitMQ的架构设计

在这里插入图片描述
生产者、消费者、交换机、队列
交换机:负责分发消息
queue:存储消息

在这里插入图片描述
在这里插入图片描述

RabbitMQ如何确保消息发送和接受?

发送方确认机制:
在这里插入图片描述
接受方确认机制:
在这里插入图片描述
区别:生产者不需要确认也可以继续发送消息。消费者要收到消息确认,才能继续接受消息。

java的四种引用

强引用
软引用
弱引用
虚引用

Java中的hashCode,以及与equals的关系

链接

  1. Object 的 hashCode 存的是什么?
    存的是该对象的内存空间地址
  2. hashmap为何能在O(1)时间内找到元素?
    通过计算hashCode()的值,来获得存储的位置,例如散列发、链地址法
  3. 如果不重写HashCode,只重写equals,会如何?
    在这里插入图片描述
    由于hashCode不想等,则hashmap、hashSet就直接判定为新对象。无法进入equals比较,及就算重写了equals方法,也无法将equals对象放入集合。

Java语法相关问题

java的多态

多态是同一个行为具有多个不同表现形式或形态的能力。

多态就是同一个接口,使用不同的实例而执行不同操作。

class Shape {
    void draw() {}
}
 
class Circle extends Shape {
    void draw() {
        System.out.println("Circle.draw()");
    }
}
 
class Square extends Shape {
    void draw() {
        System.out.println("Square.draw()");
    }
}

调用:

  public static void main(String[] args) {
      show(new Cat());  // 以 Cat 对象调用 show 方法
      show(new Dog());  // 以 Dog 对象调用 show 方法
                
      Animal a = new Cat();  // 向上转型  
      a.eat();               // 调用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 work
  }  
            
    public static void show(Animal a)  {
      a.eat();  
        // 类型判断
        if (a instanceof Cat)  {  // 猫做的事情 
            Cat c = (Cat)a;  
            c.work();  
        } else if (a instanceof Dog) { // 狗做的事情 
            Dog c = (Dog)a;  
            c.work();  
        }  
    }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值