Java工程师面试
- 面试之前:笔试题测试
- 笔试题:基础知识+基础算法+MySQL笔试题
- (基础)什么是Java的跨平台性?原理是什么?
- (基础)Java的八种基本数据类型
- (基础)Java创建的几种方式
- (基础)抽象类和接口的区别
- (基础)重载和重写的区别
- (基础)==和equals的区别
- (基础)简述equals和hashCode的联系和区别
- (集合)java的集合有哪些?请详细罗列。
- (线程)什么是死锁?死锁是怎么产生的?如何解决和避免发生死锁?
- (简答题)父类、子类静态代码块,父类子类构造器,父类子类代码块的加载顺序
- (设计模式)手写一个单例模式。
- (排序)冒泡排序
- (Mybatis)#{}和${}的区别
- (Mybatis)Mybatis用嵌套ResultMap实现一对多映射
- (Mybatis)Mybatis如何批量插入数据?
- (MySQL)给定几张数据表,如何设计SQL满足查询条件?
- 第一阶段:自我介绍
- 第二阶段:离职原因
- 第三阶段:项目描述
- 第四阶段:技术面试
- 超频面试题
- String,StringBuffer,StringBuild的区别
- HashMap在JDK1.8前后的区别
- HashMap和ConcurrentHashMap的区别
- HashMap和HashTable的区别
- 线程的几种实现方式
- 线程的几种状态
- 线程池有所了解吗?有哪几种类型线程池的创建方式?
- 了解哪些锁?sychornized和lock有什么区别?
- sychornized的锁升级有所了解吗?
- JVM虚拟机的内存模型了解吗?具体讲讲?
- 创建对象的过程
- GC原理讲讲。
- 什么是双亲委托机制?
- MySQL的索引有哪几种?什么是索引?主键索引是唯一索引吗?有什么区别?索引有哪几种数据结构?有什么区别?
- MySQL的查询优化有哪些?分库分表是什么意思?主从复制如何实现?如何得知一个Sql很慢?
- 如果有10G的日志数据存储在MySQL的五张表中,如何将它在零点的时候写入磁盘中并统一后缀为.txt文件的格式?详细描述过程(大数据并发的构思,这类面试题大同小异)
- Redis有哪几种数据结构?为什么Redis很快?Redis持久化了解吗?Redis集群了解吗?Redis的缓存雪崩,缓存穿透是什么意思?如何解决?
- 分布式事务了解吗?实现原理能讲讲吗?
- 分布式锁了解吗?具体讲讲。
- 基于restful的开发模式有哪些规范?具体讲讲?
- Linux命令你常用的有哪些?
- Maven和Git有所了解吗?知道Git和SVN的区别是什么?
- 最后阶段:你与HR的简单沟通:薪资+是否加班+福利+期望值
面试之前:笔试题测试
笔试题:基础知识+基础算法+MySQL笔试题
(基础)什么是Java的跨平台性?原理是什么?
跨平台性,是指java语言编写的程序,一次编译后可以在多个系统平台上运行。
实现原理:Java程序是通过java虚拟机在系统平台上运行的,只要该系统可以安装相应的java虚拟机,该系统就可以运行java程序。
(基础)Java的八种基本数据类型
整数的数据类型:
关键字 | 数据类型 | 字节 | 取值范围 | 包装类 |
---|---|---|---|---|
布尔型 | boolean | 不定值 | true,false | Boolean |
字节型 | byte | 1字节 | -128~127 | Byte |
短整型 | short | 2字节 | -215 ~ 215-1 | Short |
整型 | int | 4字节 | -231~231-1 | Integer |
长整形 | long | 8字节 | -263~263-1 | Long |
单精度浮点型 | float | 4字节 | 1.4E-45~3.4028235E38 | Float |
双精度浮点型 | Double | 8字节 | 4.9E-324~1.7976931348623157E308 | Double |
字符型 | char | 2字节 | 0~65535 | Character |
注意:
- 如果一个整数没有加上任何的标识的时候,那么默认是int类型的数据。
- 如果需要把该数据表示成一个long类型的数据,那么需要加数据后面加上L表示,L不区分大小写的,但是建议使用大写(易区分)不加L,javac编译不通过。
- 小数的数据类型:
如果一个小数没有加上任何标识的时候,那么该小数默认是double类型的数据,如果需要表示成float类型,那么需要在小数的后面加上f表示。f不区分大小写的。不加f javac编译不通过。 - 布尔数据类型:布尔数据类型只有两个值,true或者false
如果使用boolean声明一个基本类型的变量时,那么该变量占4个字节,如果使用boolean声明一个数组类型的时候,那么每个数组的元素占一个字节。
(基础)Java创建的几种方式
- 使用new关键字
这是我们最常见的也是最简单的创建对象的方式,通过这种方式我们还可以调用任意的够赞函数(无参的和有参的)。比如:Student student = new Student();
- 使用Class类的newInstance方法
我们也可以使用Class类的newInstance方法创建对象,这个newInstance方法调用无参的构造器创建对象,如:Student student2 = (Student)Class.forName(“根路径.Student”).newInstance(); 或者:Student stu = Student.class.newInstance();
- 使用Constructor类的newInstance方法
本方法和Class类的newInstance方法很像,java.lang.relect.Constructor类里也有一个newInstance方法可以创建对象。我们可以通过这个newInstance方法调用有参数的和私有的构造函数。如: Constructor constructor = Student.class.getInstance(); Student stu = constructor.newInstance(); 这两种newInstance的方法就是大家所说的反射,事实上Class的newInstance方法内部调用Constructor的newInstance方法。这也是众多框架Spring、Hibernate、Struts等使用后者的原因。
- 使用Clone的方法
无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去,用clone方法创建对象并不会调用任何构造函数。要使用clone方法,我们必须先实现Cloneable接口并实现其定义的clone方法。如:Student stu2 = stu.clone();这也是原型模式的应用。
- 使用反序列化
当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象,在反序列化时,JVM创建对象并不会调用任何构造函数。为了反序列化一个对象,我们需要让我们的类实现Serializable接口。如:ObjectInputStream in = new ObjectInputStream (new FileInputStream(“data.obj”)); Student stu3 = (Student)in.readObject();
(基础)抽象类和接口的区别
类型 | 接口 | 抽象类 |
---|---|---|
关键字 | interface | abstract |
实现方式 | 通过implments实现接口 | 通过继承的方式,重写其抽象方法 |
构造器 | 没有 | 有 |
访问修饰符 | 默认是public,并且不允许定义为private,protected | 任意访问修饰符 |
多继承 | 一个类可以实现多个接口 | 一个类只能继承一个抽象类 |
字段声明 | 默认为public static final | 任意字段声明 |
(基础)重载和重写的区别
重载 | 重写 |
---|---|
发生在同一个类中 | 发生在子类和父类中 |
方法名相同,参数列表不同,与返回值,修饰符无关 | 子类重写父类方法,方法名,参数列表相同,返回类型相同,访问修饰符不小于父类 |
(基础)==和equals的区别
对于基本数类型,比较的是值,对于引用数据类型,比较的是内存地址。
equals:只能比较引用数据类型,没有重写equals比较的是内存地址,重写后的equals比较的是值本身。
(基础)简述equals和hashCode的联系和区别
equals | hashcode |
---|---|
定义在Object中 | 定义在Object中 |
重写前比较内存地址,重写后比较值 | 比较对象通过hash算法生成的定长的hash值 |
- equals相等,在同一hash算法下hash值一定相等。
- hash值相等,equals不一定相等,可能存在hash冲突。
(集合)java的集合有哪些?请详细罗列。
(线程)什么是死锁?死锁是怎么产生的?如何解决和避免发生死锁?
死锁的定义:
多个线程抢占同一资源并请求锁定对方资源导致恶性循环,并在无外力情况下无法运行下去的现象。
产生死锁的四个必要条件:
- 互斥条件
当资源被一个线程使用(占有)时,别的线程不能使用 - 请求与保持条件
占有资源的线程二次请求被占用的资源失败后对其所持的资源保持不放。 - 不可剥夺条件
任一线程不可强行占有和剥夺已占有资源的线程的资源。 - 循环与等待条件
即存在一个等待队列,线程等待形成了一个闭路,造成永久阻塞。
如何解决和避免发生死锁?
A :死锁预防
破坏产生死锁的任一条件
- 破坏互斥条件,线程加锁本身就使得资源进行互斥,无需破坏。
- 破坏请求与保持,一次性请求所需所有资源
- 破坏不可剥夺,若已持有资源并再次请求新的资源失败后则释放原持有的资源。
- 破坏循环等待,按序分配资源,并反序释放。
提供阅读参考
有序资源分配法 这种算法资源按某种规则系统中的所有资源统一编号(例如打印机为1、磁带机为2、磁盘为3、等等),申请时必须以上升的次序。系统要求申请进程:
1、对它所必须使用的而且属于同一类的所有资源,必须一次申请完;
2、在申请不同类资源时,必须按各类设备的编号依次申请。例如:进程PA,使用资源的顺序是R1,R2;
进程PB,使用资源的顺序是R2,R1;若采用动态分配有可能形成环路条件,造成死锁。 采用有序资源分配法:R1的编号为1,R2的编号为2;
PA:申请次序应是:R1,R2 PB:申请次序应是:R1,R2 这样就破坏了环路条件,避免了死锁的发生
银行家算法 避免死锁算法中最有代表性的算法是Dijkstra E.W 于1968年提出的银行家算法: 银行家算法是避免死锁的一种重要方法,防止死锁的机构只能确保上述四个条件之一不出现,则系统就不会发生死锁。通过这个算法可以用来解决生活中的实际问题,如银行贷款等。
程序实现思路银行家算法顾名思义是来源于银行的借贷业务,一定数量的本金要应多个客户的借贷周转,为了防止银行家资金无法周转而倒闭,对每一笔贷款,必须考察其是否能限期归还。在操作系统中研究资源分配策略时也有类似问题,系统中有限的资源要供多个进程使用,必须保证得到的资源的进程能在有限的时间内归还资源,以供其他进程使用资源。如果资源分配不得到就会发生进程循环等待资源,则进程都无法继续执行下去的死锁现象。
把一个进程需要和已占有资源的情况记录在进程控制中,假定进程控制块PCB其中“状态”有就绪态、等待态和完成态。当进程在处于等待态时,表示系统不能满足该进程当前的资源申请。“资源需求总量”表示进程在整个执行过程中总共要申请的资源量。显然,每个进程的资源需求总量不能超过系统拥有的资源总数,
银行算法进行资源分配可以避免死锁。
(简答题)父类、子类静态代码块,父类子类构造器,父类子类代码块的加载顺序
public class Test {
public static void main(String[] args) {
Son son = new Son();
}
}
class Father{
static {
System.out.println("父静态代码块"); //1
}
{
System.out.println("父构造代码块"); //3
}
Father(){
System.out.println("父构造方法"); //4
}
}
class Son extends Father{
static {
System.out.println("子静态代码块"); //2
}
{
System.out.println("子构造代码块"); //5
}
Son(){
System.out.println("子构造方法"); //6
}
|
(设计模式)手写一个单例模式。
单例模式分为8种方式,可采纳的有2种饿汉式,双重检查机制,静态内部类,枚举类,提供我的博客链接可具体学习这几种方式。
单例模式:https://blog.youkuaiyun.com/JavaKobeBryant/article/details/108249098
//最常用的饿汉式
public class SingletonTest01 {
private SingletonTest01() {
}
private final static SingletonTest01 singleton = new SingletonTest01();
public static SingletonTest01 getSingleton() {
return singleton;
}
}
(排序)冒泡排序
1 /**
2 * 冒泡排序
3 *
4 * @param array
5 * @return
6 */
7 public static int[] bubbleSort(int[] array) {
8 if (array.length == 0)
9 return array;
10 for (int i = 0; i < array.length; i++)
11 for (int j = 0; j < array.length - 1 - i; j++)
12 if (array[j + 1] < array[j]) {
13 int temp = array[j + 1];
14 array[j + 1] = array[j];
15 array[j] = temp;
16 }
17 return array;
18 }
(Mybatis)#{}和${}的区别
- mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值,完成的是简单的字符串拼接。
- #{}将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号;$将传入的数据直接显示生成在sql中.
- #{}可以防止sql注入。
(Mybatis)Mybatis用嵌套ResultMap实现一对多映射
<resultMap type="Course" id="courseResult">
<result column="course_id" property="id" />
<result column="course_name" property="name" />
</resultMap>
<resultMap type="Tutor" id="tutorResult">
<result column="tutor_id" property="id" />
<result column="tutor_name" property="name" />
<collection property="courses" resultMap="Course" />
</resultMap>
(Mybatis)Mybatis如何批量插入数据?
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.center.manager.mapper.FundMapper">
<insert id="insertForeach" parameterType="java.util.List" useGeneratedKeys="false">
insert into fund
(
id,
fund_name,
fund_code,
date_x,
data_y,
create_by,
create_date,
update_by,
update_date,
remarks,
del_flag
)
values
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.id},
#{item.fundName},
#{item.fundCode},
#{item.dateX},
#{item.dataY},
#{item.createBy},
#{item.createDate},
#{item.updateBy},
#{item.updateDate},
#{item.remarks},
#{item.delFlag}
)
</foreach>
</insert>
</mapper>
(MySQL)给定几张数据表,如何设计SQL满足查询条件?
自己有一份sql题找不到了,请参考该博主:https://www.cnblogs.com/caiyishuai/p/10648909.html
第一阶段:自我介绍
您好,我来自xxx城市的xxx大学的xxx专业的xxx届学生。
我是一个xxx的人,曾在xxx中获得过xxx。
我的成绩xxx。
(没有可以不提,不好可以不提,优秀必须提)
在大学期间做过哪些项目?
(important)项目:详细描述项目背景、技术、功能、所承担的角色、遇到的问题和困难。
(simple)项目: 简单概括:我在大二期间简单做了一个关于xxx的项目,当时只是一个java的初步实践
在实习中担任的角色
我的上一任公司所经营的业务范围是xxxx,我在xx部门担任java开发岗。当时是做xxx项目,我负责xxx、xxx和xxx模块,书写接口文档完成增删改查的开发,并xxx(讲述自己承担的其他重要角色)。
在xx月的时候,xxxx(此处讲述自己一次重大的上线经历)。
第二阶段:离职原因
我离职的原因
- 上一家公司负荷过大且无任何晋升空间;
- 自己已经成长为更好的人,希望能够薪水与自己的能力相匹配。
- 公司价值观与个人发展预期不符,希望能够找到与价值观相符的公司。
- 想换个环境,当前的工作已经无法满足我未来的职业规划。
…
第三阶段:项目描述
你能详细讲述一下自己所做的项目吗?
举例:电商项目,不完整只做参考。
首先我介绍一下我项目用到的一些技术点:
前端页面采用的框架是vue.js+node.js,在后端查询到的json数据,通过vue动态绑定到页面。
请求的转发和域名的反向代理我采用的是nginx,通过在config中配置一些规则来减轻请求转发的压力。
登录微服务我采用的是jwt登录鉴权,在auth微服务中将username和password处理生成一个私钥返回给客户端,在其他微服务中配置公钥后然后通过封装好的Util去解析cookie中的user实体。
主页面的搜索框架采用的是ElasticSearch,通过封装好的复制商品参数的方法,将必要搜索字段存储到ElasticSearch的索引中,一个搜索内容就是一个分词字段,然后在java中采用ElasticSearch工具类ElasticsearchRepository查询数据,在ElasticSearch中分词采用的是ik分词器。
以及为了保证微服务中各服务之间数据的一致性,我采用消息中间件RabbitMQ连接了搜索微服务,商品微服务和item微服务之间的消息传递。当item中更新数据则商品和搜索微服务也会更新消息,消息队列采用的是Topic订阅模式,提供了RoutingKey和交换机绑定的方式传递消息。
商品图片我采用的文件服务器是fastdfs,它是一个踪迹服务器和一个存储服务器,但是我将两个服务器放在同一个linux下了,没有配置集群。
购物车微服务我是将购物车中的信息划分为登录和未登录状态,未登录状态下的数据则存放在localStorage中,如果登录状态下则以map嵌套map的格式存储在redis中。
大概上述是我用到的中间件以及一些解决方案。
下面我说一下我微服务用到了哪些组件。
首先我搭建了一个eureka注册中心,将网关及其他微服务通过配置文件的方式注册在eureka中,并在eureka和其他微服务下配置了心跳时间,拉取服务的时间,请求的过期时间。
然后我配置了一个网关(zuul)服务,所有的外部请求通过nginx进行拦截,然后如果要访问微服务则要通过网关,我在网关中配置了一些路由规则,不同的微服务转发到不同路由下,并写了一个过滤器去过滤一些需要获取登录状态的请求,而一些不需要登录过滤的请求,例如商品的查询这些则在配置中添加了白名单。
而微服务之间的访问我采用的是基于restful风格的openfeign组件,通过在引导类中配置@EnableFeignClients 在client包下配置所要请求的微服务名称使用@FeignClients+微服务名。
而且每个微服务我都配置了熔断和负载均衡,通过熔断来保持微服务之间的通信机制并使得微服务之间获知彼此的状态,以便第一时间做出相应的策略。
大概我的项目就是这些。
你的项目的数据库表是如何设计的?
该博主写的很好:自我总结语言概述你的数据库表设计思想:
https://www.cnblogs.com/tesla-turing/p/12036655.html
在项目中你哪个业务涉及到了分布式,能详细讲讲思路吗?
答案思路:
1 简述项目中用到的分布式架构(描述架构图)
2 在业务中用到的分布式中间件(详细描述业务在生产环境下会考虑遇到哪些问题所以使用该中间件)
3 单纯的描述该中间件的应用场景,正好与项目业务场景契合。
为什么使用xxx,而不直接xxx?举个例子,我在项目中使用RabbitMQ实现了MySQL和ElasticSearch索引库的同步,面试官问:为什么不直接微服务通信,而要使用RabbitMQ?
你的Redis存储了哪些数据?用的哪种数据类型?为什么使用Redis?
答案思路:
Redis本身的定义是 提供小数据量的高性能读写,它有以下几个应用场景:
- 为数据库分压的缓存,对首次从数据库中查询出来的数据写入到Redis中,在二次查询中先到Redis中查找,为数据库读写分担压力。
- 热频数据的存储,可以为Redis中的数据设置不定时过期时间,一方面防止Redis穿透,造成数据库压力读写压力过重,另一方面在正常运行情况下可以分担数据库压力。因为Redis本身是少数据量的高性能读写中间件。
- 短信消息验证码,将生成的6位验证码存储到Redis中,以备用户校验验证码
- 分布式锁实现
在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。
第四阶段:技术面试
超频面试题
String,StringBuffer,StringBuild的区别
HashMap在JDK1.8前后的区别
HashMap和ConcurrentHashMap的区别
HashMap和HashTable的区别
线程的几种实现方式
线程的几种状态
线程池有所了解吗?有哪几种类型线程池的创建方式?
了解哪些锁?sychornized和lock有什么区别?
sychornized的锁升级有所了解吗?
JVM虚拟机的内存模型了解吗?具体讲讲?
创建对象的过程
GC原理讲讲。
什么是双亲委托机制?
MySQL的索引有哪几种?什么是索引?主键索引是唯一索引吗?有什么区别?索引有哪几种数据结构?有什么区别?
MySQL的查询优化有哪些?分库分表是什么意思?主从复制如何实现?如何得知一个Sql很慢?
如果有10G的日志数据存储在MySQL的五张表中,如何将它在零点的时候写入磁盘中并统一后缀为.txt文件的格式?详细描述过程(大数据并发的构思,这类面试题大同小异)
Redis有哪几种数据结构?为什么Redis很快?Redis持久化了解吗?Redis集群了解吗?Redis的缓存雪崩,缓存穿透是什么意思?如何解决?
分布式事务了解吗?实现原理能讲讲吗?
分布式锁了解吗?具体讲讲。
基于restful的开发模式有哪些规范?具体讲讲?
Linux命令你常用的有哪些?
Maven和Git有所了解吗?知道Git和SVN的区别是什么?
最后阶段:你与HR的简单沟通:薪资+是否加班+福利+期望值
HR:你对我们公司有了解吗?你有什么想问我的?
HR:你对我们公司有了解吗?你有什么想问我的?
回答版本一:您好,我想让您对我刚刚的面试做一个评价,因为感觉自己在基础方面(如果问项目就说项目,如果问基础就说基础,如果问实操就说实操)回答的还蛮好的,希望能够从您这边的角度来获取对自己的一个定位。(肯定自己的知识技能,又谦逊的表明看看HR的看法)
回答版本二:您好,我觉得我自身技能还不充足,想听一下您对我刚刚的面试的一个评价。(如果答得不好,首先表明学习的态度,有了态度,HR更愿意和你接下来的沟通。)
HR态度一:你回答的还可以,…(省略)请问你还有什么想问的?
回答:谢谢您的肯定,我主要想问一下咱们公司的加班情况如何?以及包括我进了公司是怎样的一个角色?是否会有一个科学的过渡空间?(小公司画饼,大公司会有明确的管培制度,自己分辨好坏)
HR态度二:你的基础也就马马虎虎,…(省略)请问你还有什么想问的?
回答:谢谢您的评价,确实我自身还有很多需要补充的技能,接下来我会去根据您的建议补充这方面的知识。(如果HR给了建议,就这样说,如果没给建议,尝试去询问是否能够给予一些经验和帮助)
紧接着,回答第二个问题:请问你还有什么想问的?
PS:因为答得不好,这个时候就要开始献殷情了!
我感觉咱们公司的规模、以及待遇都是挺好的,我想知道我如果进了公司会担任什么角色?是否能够有一个学习空间去循序渐进的成长起来,以便帮助到公司的发展。
HR:你的期望薪资是多少?是否能够接受加班?
回答:如果学历,水平一般,能要求低点就低点。如果有足够的实力去找下家,直接回答就OK。