Java面试题

Java基础

IO流分类

  • 按数据单位:字节流、字符流
  • 按数据流向:输入流、输出流
  • 按流的角色:节点流、处理流

字节输入流:InputStream

字节输出流:OutputStream

字符输入流:Reader

字符输出流:Writer

File类常用API

  • getAbsolutePath():获取绝对路径
  • getPath():获取路径
  • getName():获取文件名
  • getParet():获取上层文件目录
  • length():获取长度
  • renameTo():重命名
  • isDirectory():是否为目录
  • isFile():是否为文件
  • exists():是否存在
  • canRead()/canWrite():是否可读/写
  • isHidden():是否隐藏

String类

  1. String被final修饰,因此是最终类,不能被继承,内部没有get、set方法,所以值也不能改变

  2. JDK9之前,存储在char型数组中,JDK9及之后,存储在byte数组。

    char=>byte的原因:节省空间,提高性能。用char存储时,每个字符都会占用2个字节,内部使用encoding判断使用那种编码(utf-16/Latin-l)

  3. 重写了equals方法,调用equals方法对比的是值是否相同

  4. 实现了serializable接口,可以序列化

  5. 实现了Comparable接口,可以比较大小

String a = “abc” 和 String a = new String(“abc”)的区别,分别创建了几个对象?

  • String a = “abc” 在常量池中创建了1个对象
  • String a = new String(“abc”)在常量池中创建了1个对象,在堆里创建了一个对象

如果是先String a = “abc” ,再new String(“abc”),则总共创建了两个对象

String a = “abc” 和 String b = “abc” ,a和b的地址值相同,都指向常量池

String常用方法

  • length():获取字符串长度
  • spilt():切割字符串为数组
  • equals():对比字符串值是否相同
  • replace():替换字符串
  • toLawerCase()/toUooerCase():字符串全部变为小写/大写
  • subString():截取字符串

装箱和拆箱

  • 装箱:基本数据类型转包装类,使用包装类的构造器,构造器内部调用valueOf()方法
  • 拆箱:包装类转基本数据类型:使用包装类的XXValue()方法

String转不同类型

  • String转基本数据类型、包装类:包装类的parse()方法
  • 基本数据类型、包装类转String:String的value()方法

数组与集合转换

  • 数组转集合:Arrays.asList()方法
  • 集合转数组:数组的toArray()方法

String、StringBuffer、StringBuilder的区别

  • 相同点:都是字符串
  • 不同点:
    • String是final修饰的,值是不能改变的
    • StringBuffer、StringBuilder是可以改变的
      • StringBuffer是线程安全的(方法用synchronize修饰)
      • StringBuilder是线程非安全的

Java比较器

实现Comparable或Comparator接口,实现compareTo()方法

多线程的创建

  1. 继承Thread类:重写run方法,调用Thread的start方法
  2. 实现Runable接口:实现run方法,把Runable作为参数传进Thread类,调用Thread的start方法
  3. 实现Callable接口:实现call方法,把Callable作为参数传进FeatureTask,再把FeatureTask作为参数传进Thread类,调用Thread的start方法
  4. 线程池

FeatureTask对象

FeatureTask同时实现了Runable和Feature接口,既可以被线程执行,也可以获取Callable接口的返回值。Feature接口可以对具体Runable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。

方法的重载和重写

范围不同:重写一般在子类与父类之间,重载在同一个类

参数不同:重写的参数必须相同,重载一般用不同的参数来区分调用那个方法方法

返回类型不同:重载可以不同,重写必须相同或者是他的子类

异常与访问权限:重写时,异常不能大于父类,访问权限不能小于父类,重载与异常、访问权限无关

多态性不同:重载在编译时决定,重写在运行时决定

为什么使用线程池

  1. 减少了创建线程的时间,提高响应速度
  2. 重复利用线程池中的线程,减少创建、销毁线程的次数,降低资源消耗
  3. 便于管理线程,可以控制线程池的大小、最大线程数等

继承Thread类和实现Runable接口,选哪个?

  • 实现Runable接口,因为Java是单继承的,如果继承了Thread类,这个类就没法继承其他类了,但是接口可以同时实现多个。
  • 多个线程如果有共享数据,继承Thread类时,多个线程共用的属性必须是static的,而实现Runable接口则不用

如何理解Callable比Runable强大

  • Callable接口可以有返回值
  • 实现Callable接口,实现call方法可以抛出异常,被外面的操作捕获,获取异常信息
  • Callable支持泛型

Thread类常用方法

  • start():启动线程,并执行run方法
  • run():线程被调度时执行的操作
  • yield():释放cpu的执行权,一但释放,其他线程就能抢到执行权
  • sleep():线程睡眠,线程进入阻塞状态
  • isAlive():判断当前线程是否存活
  • currentThread():返回当前线程
  • getName()/setName():获取/修改线程名字

线程的调度

时间片,抢占式:优先高优先级的,优先级相同时,先进来的先执行

线程的生命周期

  • 新建:当一个Thread类或其子类的对象被声明时
  • 就绪:处于新建状态的线程被调用start方法后,进入线程队列等待执行权
  • 运行:当就绪的线程获取执行权后便进入运行状态
  • 阻塞:在某种情况下,被人为挂起或执行输入输出操作时并临时终止自己的执行,进入阻塞状态
  • 死亡:线程已经执行完或者被强制终止、出现异常等

sleep方法和wait方法异同

  • 相同点:都能使线程进入阻塞状态
  • 不同点:
    • 声明位置不同:sleep是Threa类的一个静态方法,wait是Object类的方法
    • 调用要求不同:sleep方法可以在任何地方调用,wait只能在同步代码块或者同步方法中
    • 是否会释放锁:如果两个方法同时在同步代码块或者同步方法中,sleep不会释放,wait会释放

线程的通信

  • wait():调用wait方法,线程进入阻塞状态并释放锁,让其他线程先执行
  • notify():唤醒被wait的线程,如果有多个线程被wait,优先唤醒优先级最高的。
  • notifyAll():唤醒全部被wait的线程。

线程同步如何解决线程安全问题

1、同步代码块

2、同步方法

3、同步锁(Lock)

怎么避免线程死锁

  • 避免使用多个锁
  • 尽可能减少同步代码长度
  • 尝试改变锁的顺序,避免线程之间互相等待
  • 使用定时锁超时释放

sychronize和Lock的异同

  • 相同点:都可以解决线程安全问题
  • 不同点:synchronize在执行完同步代码块之后,自动释放锁,Lock需要手动调用unlock解锁,但是更加灵活

Java集合分类

  • Collection接口
    • List接口
      • ArrayList
      • LinkedList
      • Vector
    • Set接口
      • HashSet
      • LinkedHashSet
      • ThreeSet
  • Map接口
    • HashMap
    • LinkedHashMap
    • HashTable
    • TreeMap

ArrayList和和HashMap的扩容

  • ArrayList:
    • JDK8之前,默认创建长度为10,扩容1.5倍
    • JDK8之后,根据第一次调用add方法的长度进行创建,再次调用才会扩容
  • HashMap:
    • 创建长度为16的数组,默认加载因子为0.75,扩容临界值为:16 * 0.75 = 12

ArrayList和LinkedList区别

  • 相同点:都是List接口实现类
  • 不同点:
    • ArrayList底层是数组,查找和遍历更快
    • LinkedList底层是双向链表,插入和删除更快

HashMap与HashTable的区别

  • 相同点:都是Map接口的实现类,存储的都是key-value键值对数据
  • 不同点:
    • HashMap是线程非安全的,执行效率高,可以存储null值
    • HashTable是线程安全的,执行效率低,不能存储null值

异常的分类以及常见异常

  • Error
  • Exception
    • IOException:IO异常
    • ClassNotFoundException:类未发现异常
    • NullPointerException:空指针异常
    • IndexOutofBoundsException:角标超越异常
    • ClassCastException:类型转换异常
    • NumberFormatException:数值转换异常

悲观锁与乐观锁

  • 悲观锁:认为线程安全一定发生,因此在操作之前先获取锁。例如:synchronize、Lock
  • 乐观锁:认为线程安全不一定发生,因此不加锁。例如:版本号

final修饰

  • 修饰基本数据类型,被称为常量,值无法被修改
  • 修饰引用数据类型,对象本身内容可以修改,但是引用地址不能修改
  • 修饰类的成员变量,必须当场赋值,否则编译报错
  • 修饰方法:被称为最终方法,方法不能被子类重写,但是可以被继承
  • 修饰类:被称为最终类,无法被继承

Mysql

索引失效

  • 使用不等于时
  • 对索引列进行函数操作
  • 使用like且通配符开头
  • 使用的or但是or的两边不全是索引列
  • 使用了is null 或者 is not null

慢sql优化

  • 避免使用select *
  • union all 代替 union
  • 小表驱动大表
  • in中的值不能太多
  • join的表不宜太多
  • 用join代替子查询
  • 选择合理的字段类型

explain列说明

  • type:链接类型
  • key:实际用到的索引
  • key-len:实际索引长度

Mysql索引分类

Mysql默认B+tree

  • 按数据结构分类:B+Tree、Hash索引、Full-Text
  • 按物理存储分类:聚集索引、非聚集索引
  • 按字段特性分类:主键索引、唯一索引、普通索引、全文索引
  • 按字段个数分类:单列索引、联合索引(组合索引)

事务的特性(ACID)

  • 原子性:事务的所有操作作为一个不可分割的单元,要么都执行,要么都不执行
  • 一致性:保证事务开始前和开始后的数据的完整和一致
  • 隔离性:事务的执行不会受其他事务的影响
  • 持久性:事务的命令操作修改后,会被持久化保存,且不会回滚

事务的隔离级别

  • 未提交读:最低级别的隔离,可能会读取到其他未提交的更改
  • 提交读:只能读取到其他事物已提交的修改,但事务内多次读取可能结果不同
  • 可重复读(默认):保证同一事务内多次读取一致,但某些情况下可能产生幻读
  • 串行读:最高的隔离级别,通过锁表保证完全隔离,但会严重影响数据库性能

事务并发问题(脏读、幻读、不可重复读)

  • 脏读:一个事务读到了另一个事务回滚之前的数据。
  • 幻读:一个事务在前后两次查询中,另一个事务新增了几条数据,后面一次查到了前一次没有的行。
  • 不可重复读:一个事务获取了值,另一个事务也获取值并进行修改,事务再次获取的值和第一次不同。

大分页

通过子查询先获取id,然后再关联查询

Spring

Spring Ioc和Aop

Ioc:工厂模式,aop:代理模式

  • ioc思想:控制反转,强调创建bean的权利交给bean工厂
  • aop思想:面向切面编程,功能横线抽取
  • DI思想:依赖注入,强调bean之间的关系

BeanFactory和ApplicationContext的关系

  • BeanFactory成为Bean工厂,ApplicationContext为Spring容器
  • BeanFactory主要负责Bean的产生,ApplicationContext在BeanFactory的基础上进行拓展,对BeanFactory的API进行封装
  • BeanFactory在调用getBean方法时才会创建Bean,ApplicationContext在加载配置文件时全部初始化好
  • ApplicationContext内部继承了BeanFactory,内部维护着BeanFactory的引用,既有继承关系,也有融合关系

@Transaction注解失效

  • 在方法中异常被 try catch 捕获并处理
  • 方法不是public的
  • 调用了本类的方法
  • @Transaction的属性rollbackFor设置错误
  • 父容器和子容器都管理了事务,子容器的事务不受父容器的影响

@Autowired和@Resource的区别

  • 来源不同:@Autowired是Spring的,@Resource是Java的

  • 注入方式不同:@Autowired是先根据类型再根据名称注入,@Resource是先根据名称再根据类型注入

  • 用法:@Autowired支持构造器,属性和setter注入,@Resource只支持属性和setter注入

    spring推荐setter注入

Spring Bean的生命周期

Bean加载配置,进入Bean工厂后置处理器,实例化,Bean后置处理器,加载到单例池,调用,销毁

Spring Aop的5种增强类型

  • before:前置通知,目标方法执行前
  • after-before:后置通知,目标方法执行后,方法异常时不会执行
  • around:环绕通知,目标方法执行前后执行,方法异常时,环绕后方法不执行
  • after-throwing:异常通知,目标方法抛出异常时执行
  • after:最终通知,不管方法是否异常,最终都会执行

Spring MVC工作流程

用户发起请求,web服务器将请求转发给前端控制器(DispatcherServlet),调用HandlerMapping确定是哪个controller,controller执行对应的业务逻辑,返回一个ModleAndView对象,再通过视图解析器解析为具体的view,view根据model进行渲染,最后返回给客户端

过滤器与拦截器的区别

  • 运行顺序不同:先过滤器,再拦截器
  • 依赖不同:过滤器依赖于servlet,拦截器依赖于Spring
  • 范围不同:过滤器只能对request和response响应,拦截器还能对SpringMVC生态下的组件做处理

SpringBoot

SpringBoot和SpringMVC的区别

  • SpringMVC大量繁琐的配置,需要单独的Tomcat
  • SpringBoot约定大于配置,自动装配,内置Tomcat

SpringBoot自动装配原理

主要通过@SpringBootApplication注解,@SpringBootApplication是由

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

三个注解组成的,@SpringBootConfiguration是代表这个类是SpringBoot的启动配置类,@EnableAutoConfiguration代表开启自动装配,@ComponentScan指定要扫描哪些包。

Mybatis

#和$的区别

  • #是预编译占位符,可以防止sql注入,转换成sql时参数带双引号
  • $是字符串替换,直接作为字符串拼接,有sql注入风险,转换成sql时参数不带双引号

Mybatis分页插件原理

通过拦截器来实现的,将分页参数传给插件,插件先查总条数,再把分页参数拼到sql中,最后吧数据和总条数一起返回。

Redis

Redis数据类型

  • string 字符串
  • hash 类似HashMap
  • list 类似list
  • set 类似set
  • sortedset 可排序set

Redis三大问题

  • 缓存穿透
    • 原因:数据库和缓存中都不存在,每次请求都直接访问数据库并返回空
    • 解决:
      • 缓存空对象,如果没有则缓存空值,并设置过期时间
      • 布隆过滤器,在客户端请求时,加一层过滤器来过滤请求
  • 缓存雪崩
    • 原因:大量缓存同时到期,导致大量请求同时访问数据库
    • 解决:设置不同过期时间,或者建立集群
  • 缓存击穿
    • 原因:热key问题,热key突然消失,请求大量访问数据库
    • 解决:
      • 互斥锁,基于set nx命令,线程1使用set nx查询缓存,未命中则拿到锁,开始查询数据库然后加入缓存,线程2未命中进入休眠模式
      • 逻辑过期,在互斥锁的基础上,缓存一个逻辑过期时间,获取锁成功后,直接吧数据返回,然后开启一个新的线程更新数据。

Redis缓存更新策略

  • 内存淘汰,利用redis的内存淘汰机制,当内存不足时自动淘汰部分数据,下次查询时更新缓存
  • 超时剔除,给缓存数据添加过期时间,到期后自动删除
  • 主动更新,编写业务逻辑,在修改数据库的同时,更新缓存

主动更新策略

  • 由缓存调用者,在更新数据库时同时更新缓存
  • 缓存与数据库整合成一个服务,由服务来维护一致性,调用者调用该服务,无需关心缓存一致性问题
  • 调用者只操作缓存,由其他线程异步的将缓存持久化到数据库,保证最终一致

Redis持久化

  • RDB:将数据直接记录在磁盘
    • save(默认):服务停止时创建新的RDB文件并保存到磁盘
    • bgsave:在配置中配置save 60 1000,然后则会触发当60秒内修改1000次则更新RDB文件
  • AOF:记录执行过的命令,根据命令进行恢复

Redis主从

实现读写分离

Redis哨兵

自动故障恢复

哨兵的作用
  1. 监控:sentinel会不断检查master和slave是否按预期工作。
  2. 自动恢复故障:如果master故障,sentinel会将一个slave提升为master。当故障实例恢复后也已新的master为主
  3. 通知:sentinel充当Redis客户端的服务发现来源,当集群发生故障偏移是,会将最新的消息推送给Redis的客户端。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值