1、Java 常用集合及特点?
List:ArrayList、LinkedList、Vector、Stack Set:LinkedSet、HashSet、TreeSet Queue->Deque->LinkedList。
Map:HashMap、LinkedHashMap、TreeMap Dictionary->HashTable->Properties。 Vector: 底层数据结构是数组,查询快,增删慢,线程安全,效率低,默认长度为10,超过会100%延长,变成20,浪费空间。
ArrayList :基于数组,便于按 index 访问,超过数组需要扩容,扩容成本较高。
LinkedList:使用链表实现,无需扩容。
HashSet:底层数据结构是哈希表(无序,唯一),通过hashcode()和equals()保证元素唯一。
LinkedHashSet: 底层数据结构是链表和哈希表(FIFO插入有序,唯一),由链表保证元素有序,由哈希表保证元素唯一。
TreeSet:底层数据结构是红黑树(唯一,有序),通过自然排序和比较器排序保证元素有序,根据比较返回值是否是0来保证元素唯一性。
TreeMap是有序的。
HashMap :空间换时间,哈希冲突不大的情况下查找数据性能很高。
LinkedHashMap 基本特点:继承自 HashMap,对 Entry 集合添加了一个双向链表。
2、开启一个线程的方法?
⚫ 继承Thread类,新建一个当前类对象,并且运行其start()方法
⚫ 实现Runnable接口,然后新建当前类对象,接着新建Thread对象时把当前类对象传进 去,最后运行Thread对象的start()方法
⚫ 实现Callable接口,新建当前类对象,在新建FutureTask类对象时传入当前类对象,接 着新建Thread类对象时传入FutureTask类对象,最后运行Thread对象的start()方法
3、Java 面向对象包括哪些特性,怎么理解的?
⚫ 封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义 的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类 中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封 装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
⚫ 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超 类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一 定的延续性,同时继承也是封装程序中可变因素的重要手段。
⚫ 多态:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同 样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时 的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释 为:当 A系统访问B系统提供的服务时,B 系统有多种提供服务的方式,但一切对 A 系 统来说都是透明的。方法重载(overload)实现的是编译时的多态性(也称为前绑定), 而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面 向对象最精髓的东西,要实现多态需要做两件事:
第一:方法重写(子类继承父类并重写父类中已有的或抽象的方法);
第二:对象造型(用父类型引用指向子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
⚫ 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两 方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
4、Java 如何保证线程安全?
⚫ 使用同步代码块
⚫ 使用同步方法
⚫ 使用Lock锁机制, 通过创建Lock对象,采用lock()加锁,unlock()解锁,来保护指定 的代码块。
5、介绍 Spring MVC 的工作流程 ?
⚫ 用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet。
⚫ DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该 请求该由哪个Controller来处理(并未调用Controller,只是得知)
⚫ DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执 行哪个Controller
⚫ HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视 图),并层层返回给DispatcherServlet
⚫ DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真 正的视图。
⚫ DispatcherServlet将模型数据填充到视图中
⚫ DispatcherServlet将结果响应给用户
6、Spring 框架中用到了哪些设计模式?
⚫ 工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
⚫ 代理设计模式 : Spring AOP 功能的实现。
⚫ 单例设计模式 : Spring 中的 Bean 默认都是单例的。
⚫ 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的 对数据库操作的类,它们就使用到了模板模式。
⚫ 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需 要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据 源。
⚫ 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
⚫ 适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中 也是用到了适配器模式适配Controller。
7、Redis 的特点是什么?
Redis本质上是一个Key-Value类型的内存数据库,很像Memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。 因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。
Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像 Memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能。
比方说用他的List来做FIFO双向链表,实现一个轻量级的高性 能消息队列服务,用他的Set可以做高性能的tag系统等等。另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的Memcached来用。
Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
8、为什么使用 Redis,有什么好处?
⚫ 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的 时间复杂度都是O(1)
⚫ 支持丰富数据类型,支持string,list,set,sorted set,hash
⚫ 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不 执行
⚫ 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
9、Redis 雪崩和击穿了解吗?
缓存击穿
⚫ 问题:某个KEY失效的时候,正好有大量并发请求访问这个KEY。
⚫ 分析:跟穿透其实很像,属于比较偶然的。
⚫ 解决办法:KEY的更新操作添加全局互斥锁。完全以缓存为准,使用延迟异步加载的策略 (异步线程负责维护缓存的数据,定期或根据条件触发更新),这样就不会触发更新。
缓存雪崩
⚫ 问题:当某一时刻发生大规模的缓存失效的情况,导致大量的请求无法获取数据,从而将 流量压力传导到数据库上,导致数据库压力过大甚至宕机。
⚫ 原因:一般而言,缓存雪崩有2种可能性:大量的数据同一个时间失效:比如业务关系强 相关的数据要求同时失效Redis 宕机
⚫ | 分析:一般来说,由于更新策略、或者数据热点、缓存服务宕机等原因,可能会导致缓存 |
数据同一个时间点大规模不可用,或者都更新。所以,需要我们的更新策略要在时间上合适,数据要均匀分享,缓存服务器要多台高可用。
⚫ 解决办法:更新策略在时间上做到比较平均。如果数据需要同一时间失效,可以给这批数 据加上一些随机值,使得这批数据不要在同一个时间过期,降低数据库的压力。使用的热 数据尽量分散到不同的机器上。多台机器做主从复制或者多副本,实现高可用。做好主从 的部署,当主节点挂掉后,能快速的使用从结点顶上。实现熔断限流机制,对系统进行负 载能力控制。对于非核心功能的业务,拒绝其请求,只允许核心功能业务访问数据库获取 数据。服务降价:提供默认返回值,或简单的提示信息。
10、什么是面向对象,谈谈你的理解?
世间万物都可以看成一个对象。每个物体包括动态的行为和静态的属性,这些就构成了一个对象。
11、访问数据库除了 JDBC 还有什么?
⚫ 自己封装JDBC的工具类
⚫ Commons-Dbutils+dbcp【QueryRunner】
⚫ SpringJDBC【JdbcTemplate】
⚫ JPA【配置文件、domain实体类+注解、EntityManager】
⚫ SpringDataJpa
⚫ Hibernate框架
⚫ Mybatis
12、你知道有哪些设计原则?
⚫ 遵循单一职责原则
⚫ 开放-封闭原则
⚫ 里氏代换原则(LSP)
⚫ 依赖倒置原则
⚫ 接口隔离原则(Interface Segregation Principle)
⚫ 迪米特法则(Law of Demeter)
13、在生产环境 Linux服务器上,发现某台运行Java服务的服务器的
CPU100%,不借助任何可视化工具,怎么进行问题的定位?
⚫ ⚫ ⚫ ⚫ | top 找出进程CPU比较高 PID |
14、JDK 里面带的工具你知道哪些?
⚫ jstat:虚拟机进程状况工具
⚫ jinfo:Java 配置信息工具
⚫ jmap:Java 内存映像工具
⚫ jhat:虚拟机堆转储快照分析工具
⚫ jstack:Java堆栈跟踪工具
⚫ JConsole: Java 监视与管理控制台
⚫ VisualVM: 多合一故障处理工具
15、基本数据类型 bit 长度?
⚫ byte:1*8
⚫ short:2*8
⚫ int: 4*8
⚫ long: 8*8
⚫ float: 4*8
⚫ double: 8*8
⚫ char: 2*8
⚫ boolean: 1*8
16、char 能不能存中文?
可以,不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。
17、谈谈你对泛型的理解?
Java 中的泛型有 3 种形式,泛型方法,泛型类,泛型接口。Java 通过在编译时类型擦除的方式来实现泛型。擦除时使用 Object 或者界定类型替代泛型,同时在要调用具体类型方法或者成员变量的时候插入强转代码,为了保证多态特性,Java 编译器还会为泛型类的子类生成桥
接方法。类型信息在编译阶段被擦除之后,程序在运行期间无法获取类型参数所对应的具体类型。
18、Java 程序是怎样运行的?
⚫ 首先通过 Javac 编译器将 .java 转为 JVM 可加载的 .class 字节码文件。
⚫ Javac 是由 Java 编写的程序,编译过程可以分为:① 词法解析,通过空格分割出单词、 操作符、控制符等信息,形成 token 信息流,传递给语法解析器。② 语法解析,把 token 信息流按照 Java 语法规则组装成语法树。③ 语义分析,检查关键字使用是否合 理、类型是否匹配、作用域是否正确等。④ 字节码生成,将前面各个步骤的信息转换为字 节码。
⚫ 字节码必须通过类加载过程加载到 JVM 后才可以执行,执行有三种模式,解释执行、JIT 编译执行、JIT 编译与解释器混合执行(主流 JVM 默认执行的方式)。混合模式的优势在 于解释器在启动时先解释执行,省去编译时间。
⚫ 之后通过即时编译器 JIT 把字节码文件编译成本地机器码。
⚫ Java 程序最初都是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块的运行特 别频繁,就会认定其为"热点代码",热点代码的检测主要有基于采样和基于计数器两种方 式,为了提高热点代码的执行效率,虚拟机会把它们编译成本地机器码,尽可能对代码优 化,在运行时完成这个任务的后端编译器被称为即时编译器。
⚫ 还可以通过静态的提前编译器 AOT 直接把程序编译成与目标机器指令集相关的二进制代 码。
19、GC root 有哪些?
⚫ Thread-存活的线程。
⚫ Java虚拟机栈中的引用的对象。
⚫ 方法区中的类静态属性引用的对象。 (一般指被static修饰的对象,加载类的时候就加载 到内存中。)
⚫ 方法区中的常量引用的对象。
⚫ 本地方法栈中的JNI(native方法)引用的对象。
⚫ Monitor Used-用于同步监控的对象。
20、栈帧的大小什么时候确定?
有时候编译期能够确定,有些时候函数的栈帧的大小在编译期并不确定。比如用了VLA。所以一般会有两个寄存器(IA-32上就是ebp和esp)来记录栈帧的首尾地址。当进入一个函数时,首先把上个栈帧的首尾地址分别保存起来(一般做法是将ebp压栈、并将esp写入ebp),接着再分配新的栈帧大小(先给esp减一个常数,如果需要动态分配再接着减)。
21、静态 filed 声明和构造器哪个先执行?
filed声明先执行。
22、线程创建方式是什么?
⚫ 通过继承Thread类创建线程类
⚫ 实现Runnable接口创建线程类
⚫ 通过Callable和Future接口创建线程
23、传统 I/O 跟 NIO 的区别?
⚫ 所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节。 流 I/O 用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对 象。传统流IO的好处是使用简单,将底层的机制都抽象成流,但缺点就是性能不足。而 且IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻 塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。
⚫ 原来的 I/O 库(在 java.io.*中) 与 NIO 最重要的区别是数据打包和传输的方式。 原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。
⚫ NIO性能的优势就来源于缓冲的机制(buffer机制),不管是读或者写都需要以块的形式 写入到缓冲区中。NIO实际上让我们对IO的操作更接近于操作系统的实际过程。
⚫ NIO作为非阻塞式的IO,它的优点就在于,1、它由一个专门的线程去处理所有的IO事 件,并负责分发;2、事件驱动,只有事件到了才会触发,而不是同步的监听这个事件; 3、线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓 的线程切换。
⚫ 当我们在执行持续性的操作(如上传下载)时,IO的方式是要优于 NIO的。分清情况, 合理选用。
⚫ NIO相对于IO流的优势:
非阻塞
buffer机制
流替代块
24、消息队列的在各种场景下如何选型?
⚫ | 优先级队列;队列设置最大的优先级,之后每条消息设置对应的优先级,队列根据消息优 |
先级进行消费,(在有可能队列堆积的情况才有意义);应用场景:不同业务消息推送。
⚫ 延迟队列:消息发送后,并不想让消费者立即拿到消息,等待特定的事件后,消费者才能 拿到并消费;应用场景:订单系统中订单支付30分钟内没有支付成功,那么将这个订单 进行异常处理;远程操作智能设备在指定时间进行工作等。(rabbit中没有延迟队列,但 可以借助死信队 列与TTL设置来完成)
⚫ 死信队列:当消息在一个队列中变成死信之后,它能被重新被发送到另一个交换器(DLX 交换器)中,绑定DLX的队列就称为死信队列。
⚫ 重试队列:消费端,一直不回传消费的结果,rocketmq认为消息没收到,consumer下 一次拉取,broker依然会发送该消息(有次数限制)。重试队列其实可以看成是一种回 退队列,具体指消费端消费消息失败时,为防止消息无故丢失而重新将消息回滚到 Broker中。
⚫ 消费模式: 推模式:对于kafka而言,由Broker主动推送消息至消费端,实时性较好, 不过需要一定的流 制机制来确保服务端推送过来的消息不会压垮消费端。拉模式:对于 kafka而言,消费端主动向Broker端请求拉取(一般是定时或者定量)消息,实时性较推模 式差,但是可以根据自身的处理能力而控制拉取的消息量。
⚫ 消息回溯:重置消息 offset(如:kafka、rokcetMq) 一般消息在消费完成之后就被处 理了,之后再也不能消费到该条消息。消息回溯正好相反,是指消息在消费完成之后,还 能消费到之前被消费掉的消息。对于消息而言,经常面临的问题是“消息丢失”,至于是 真正由于消息中间件的缺陷丢失还是由于使用方的误用而丢失一般很难追查,如果消息中 间件本身具备消息回溯功能的话,可以通过回溯消费复现“丢失的”消息 进而查出问题的 源头之所在。消息回溯的作用远不止与此,比如还有索引恢复、本地缓存重建,有些业务 补偿方案也可以采用回溯的方式来实现。
⚫ 消息堆积:流量削峰是消息中间件的一个非常重要的功能,而这个功能其实得益于其消息 堆积能力。从某种意义上来讲,如果一个消息中间件不具备消息堆积的能力,那么就不能 把它看做是一个合格的消息中间件。消息堆积分内存式堆积和磁盘式堆积。
⚫ 消息持久化:持久化确保MQ的使用不只是一个部分场景的辅助工具,而是让MQ能像 数据库一样存储核心的数据。有些功能是默认不开启的,需要进行配置。
⚫ 多租户: 也可以称为多重租赁技术,是一种软件架构技术,主要用来实现多用户的环境下 公用相同的系统或程序组件,并且仍可以确保各用户间数据的隔离性。RabbitMQ就能够 支持多租户技术,每一个租户表示为一个vhost,其本质上是一个独立的小型RabbitMQ 服务器,又有自己独立 的队列、交换器及绑定关系等,并且它拥有自己独立的权限。 vhost就像是物理机中的虚拟机 一样,它们在各个实例间提供逻辑上的分离,为不同程序 安全保密地允许数据,它既能将同一 个RabbitMQ中的众多客户区分开,又可以避免队 列和交换器等命名冲突。
⚫ | 跨语言支持: 对很多公司而言,其技术栈体系中会有多种编程语言,如C/C++、JAVA、 |
Go、PHP等,消息 中间件本身具备应用解耦的特性,如果能够进一步的支持多客户端语言,那么就可以将此特性 的效能扩大。跨语言的支持力度也可以从侧面反映出一个消息中间件的流行程度。
⚫ 消息顺序消息:先进先出、 逐条进行消费顾名思义,消息顺序性是指保证消息有序。这个 功能有个很常见的应用场景就是 CDC(Change Data Chapture),以MySQL为例,如 果其传输的binlog的顺序出错,比如原本是先对一条数据加1,然后再乘以2,发送错序 之后就变成了先乘以2后加1了,造成了数据不一致。
⚫ 安全机制: 在Kafka 0.9版本之后就开始增加了身份认证和权限控制两种安全机制。身份 认证是指客户端与服务端连接进行身份认证,包括客户端与Broker之间、Broker与 Broker之间、Broker与ZooKeeper之间的连接认证,目前支持SSL、SASL等认证机 制。权限控制是指对客户端的读写操作进行权限控制,包括对消息或Kafka集群操作权限 控制。权限控制是可插拔的,并支持与外部的授权服务进行集成。对于RabbitMQ而 言,其同样提供身份认证(TLS/SSL、SASL)和 权限控制(读写操作)的安全机制。
⚫ 事务支持: 事务本身是一个并不陌生的词汇,事务是由事务开始(Begin Transaction) 和事务结束(End Transaction)之间执行的全体操作组成。支持事务的消息中间件并不 在少数,Kafka和RabbitMQ都支持,不过此两者的事务是指生产者发生消息的事务,要 么发送成功,要么发送失败。消息中间件可以作为用来实现分布式事务的一种手段,但其 本身并不提供全局分布式事务的功能。
25、Java 的安全性体现在哪里?
⚫ Java SE 安全性概述 Java SE
⚫ 平台基于一个动态、可扩展、基于标准、可互操作的安全架构。加密、身份验证和授权、 公共密钥基础架构等安全特性是内置的。Java
⚫ 安全模型基于一个可定制的“沙盒”,Java 软件程序可在其中安全运行,对系统或用户无 潜在风险。
⚫ Java 编译器和虚拟机强制实施的内置的语言安全特性:
⚫ 强大的数据类型管理
⚫ 自动内存管理
⚫ 字节码验证
⚫ 安全的类加载
26、static 方法怎么访问非 static 变量?
创建对象,通过对象的引用就可以访问对象内部的非静态变量
27、讲下你理解的 Java 多继承?
Java是不支持多继承的:
⚫ 若子类继承的父类中拥有相同的成员变量,子类在引用该变量时将无法判别使用哪个父类 的成员变量
⚫ 若一个子类继承的多个父类拥有相同方法,同时子类并未覆盖该方法(若覆盖,则直接使 用子类中该方法),那么调用该方法时将无法确定调用哪个父类的方法。
28、Java 基本类型有哪些?
⚫ byte 1
⚫ short 2
⚫ int 4
⚫ long 8
⚫ float 4
⚫ double 8
⚫ char 2
⚫ boolean 1
29、线程池如果满了会怎么样?
⚫ 如果使用的是无界队列Linke dBlockingQueue,也就是无界队列的话,没关系,继续添 加任务到阻塞队列中等待执行,因为LinkedBlockingQueue 可以近乎认为是一个无穷大 的队列,可以无限存放任务
⚫ 如果使用的是有界队列比如ArrayBlockingQueue , 任务首先会被添加到
ArrayBlockingQueue 中,ArrayBlockingQueue 满了,会根据maximumPoolSize 的 值增加线程数量,如果增加了线程数量还是处理不过来,ArrayBlockingQueue 继续满, 那么则会使用拒绝策略RejectedExecutionHandler 处理满了的任务,默认是
AbortPolicy。
30、什么是双亲委派机制,它有什么作用?
双亲委派机制的意思是除了顶层的启动类加载器以外,其余的类加载器,在加载之前,都会委派给它的父加载器进行加载。这样一层层向上传递,直到祖先们都无法胜任,它才会真正的加载。
⚫ 通过带有优先级的层级关可以避免类的重复加载;
⚫ 保证 Java 程序安全稳定运行,Java 核心 API 定义类型不会被随意替换。