本章主要带来的是JavaSE的面试题,里面包含了大部分Java基础面试能碰到的问题,如果有什么遗漏,或者写的有问题的希望各位兄弟们能在评论区指出
如果有想了解Spring框架面试题的兄弟们博主这里也是为大家准备了一篇,里面涵盖了Spring、Springmvc、SpringBoot总共31题
Spring面试题合集
byte、short、int、long | 整数类型 | 默认类型是int、其余需要强转 |
float、double | 浮点数类型 | 默认类型是double,如果是float类型后面需要写F |
char | 字符类型 | 默认值为空,字符类型用单引号表示字符, |
boolean | 布尔类型 | 默认值false true、false这两个不是关键字 |
访问修饰符 | 当前类 | 同一包下其他类 | 其它包内子类 | 其它包内非子类 |
---|---|---|---|---|
private | 可以访问 | |||
default(默认的修饰符) | 可以访问 | 可以访问 | ||
protected | 可以访问 | 可以访问 | 可以访问 | |
public | 可以访问 | 可以访问 | 可以访问 | 可以访问 |
面向对象即OOP是一种编程思想,对象在我们生活中处处都存在,比如说一只小狗就是一个对象,在面向对象编程中,对象是有很多特征的,比如说品种、颜色、体型等等都是对象的属性,对象还会有一些能力,比如跑、跳、打滚,这就可以理解成对象所封装的方法,简单来说就是将功能封装到对象里,我们面向对象,让对象去完成这些功能,在java中万物皆对象
封装:将不同的功能抽取出来方便调用,减少大量代码的冗余
继承:减少了类的冗余代码,让子父类之间产生关系,为多态打下基础
多态:一个接口,多个方法。通过继承实现不同的对象调用相同的方法,进而有不同的行为
接口
public interface a{}
抽象类
abstract class a{}
接口 | 抽象类 |
---|---|
不能定义构造器 | 可以定义构造器 |
接口中所有成员都是public static final | 可以有抽象方法和具体方法 |
成员变量都是常量 | 抽象类中的成员可以是任何类型 |
接口可以多继承 | 类只能单继承 |
注意点:
抽象类如果是内部类可以使用static ,外部类不行
抽象类不能使用final修饰,因为使用后就不能被继承了,就失去了抽象类存在的意义了
注:抽象类中不一定有抽象方法,有抽象方法这个类一定是一个抽象类
抽象类:描述现实中具体的事务可以使用抽象类
接口:描述某些行为特征时可以使用接口
普通类 | 抽象类 |
---|---|
不能包含抽象方法 | 可以包含抽象方法 |
能直接实例化(new) | 不能直接实例化(需要继承重写抽象方法) |
静态变量:被static修饰符修饰的变量,也被称为类变量,它是属于类的,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝
实例变量:必须依存于某一个实例,需要先创建对象然后通过对象才能访问到它。实例变量可以实现让多个对象共享内存
重写:发生在子类和父类之间,当子类继承父类时,子类中的方法与父类方法的名称,参数个数,参数类型完全一致时,称为子类重写父类方法,子类的访问修饰符必须大于等于父类的访问修饰符
重载:一个类中的多个方法的方法名称相同,参数个数或者形参类型不同,则称为方法重载,与返回值是无关的
补充:
1.final修饰的不能重写,但是能重载
2.无论重载重写,必须保证方法名相同
构造器Constructor是不能被继承的,因此不能被重写Overriding,但可以被重载Overloading
public 类名(){}
补充:
1、构造函数是不能被继承的,构造方法只能被隐式调用
2、构造函数是不能有返回值类型的
基本数据类型 | 包装类 |
---|---|
short | Short |
int | Interger |
byte | Byte |
long | Long |
char | Character |
double | Double |
float | Float |
boolean | Boolean |
初始值不同:基本数据类型初始值int为0,boolean为false,double为0.0,包装类型的默认值都为null
使用方式不同:基本数据类型不能用于泛型,包装类可以作用于泛型
说明:POJO类属性没有初始值是提醒使用者在需要使用时,必须自己显示地进行赋值,任何NPE(NullPointerException)问题,或者入库检查,都由使用者来保证
数据查询的结果可能是null,因为自动拆箱,用基本数据类型接收有NPE风险
装箱:将基本类型转换成引用类型的过程叫做装箱(将栈中的基本数据类型放到堆里面去从而转换成一个引用类型)
int i1 =1;
Integer i2 = new Integer(i1);
自动装箱
int t =11;
Integer t2 =t;
拆箱:将包装类型转换成引用数据类型的过程叫拆箱(将栈中的基本数据类型放到堆里面去从而转换成一个引用类型)
Integer i=new Integer(1);
int i2=i.intValue();
自动拆箱
int t =11;
Integer t2 =t;
int t3 =t2.intValue();
方法介绍:
intValue()是吧Integer对象类型变成int的基本数据类型
parseInt()是吧String转化成int类型
Valueof()是把String转化成Integer类型
String
String:不可变字符序列,底层是一个数组不可扩容
StringBuffer、StringBuilder:可变字符序列,底层是可扩容的数组,底层会有一个value 和 count,当需要追加数据时,底层的数组长度不够时,它会再创建一个扩容后的新数组,然后将原来的数据复制过去,然后再在新数组后面追加
StringBuffer:是JDK1.0的时候的,线程安全
StringBuilder:是JDK1.5的时候的,线程不安全
三者的效率区别:StringBuilder>StringBuffer>String
.equals()
.substring()
.length()
.indexOf()
.toUpperCase()
还有很多自行了解:String类常用API
equals()和==最大的区别一个是方法,一个是运算符
==:如果对象时基本数据类型,则比较的是数值是否相等;如果是引用数据类型,比较的则是对象的地址值是否相等
equals():用来比较方法两个对象的内容是否相等
ArrayIndexOutOfBoundsException 数组下标越界
NullPointerException 空指针异常
ClassCastException 类转换异常
SQLException SQL异常
IllegalArgumentException 方法传参错误
try{
可能出现异常的代码
}catch{
捕获到的异常与catch中的异常类型进行匹配,执行里面的逻辑
可能有多重判断
}finally{
进行资源的关闭
}
throw
throws
其实说到底这三者并没有关联关系,不过面试题还是会考,可能因为它们长得像吧
final:用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可被继承
finally:异常处理语句结构的一部分,表示不管抛不拋异常都执行
finalize:Object 类的一个方法,在垃圾回收器执行的时候会调用被回收对象的
此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。该方法无需我们调用
流的方向:输入流,输出流
流的类型:字节流,字符流
字符流 | 字节流 | |
---|---|---|
输入流 | Reader | inputStream |
输出流 | writer | outputStream |
字节流处理的数据类型:图片、MP3、视频文件、文本数据
字符流处理的数据类型:纯文本数据
如果是文本数据优先选择字符流,除此之外都是字节流
字节流主要操作的数据类型是byte类型数据,以byte数组为准
字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串
字节流处理单元为 1 个字节,操作字节和字节数组
数组 | 集合 |
---|---|
数组的大小在创建之初就规定了,后面不能更改 | 集合的大小是不固定的,底层会自动扩容 |
数组存放的类型在创建是就规定了,只能有一种 | 集合在不添加泛型时数据类型并不是一种,是Object类型 |
1.超级接口Iterable
2.起始接口collection继承Iterable接口
3.下面就分为三大接口
4.Map在集合框架中跟collection是并列存在的
第一代安全集合类
Vector Hashtable
核心代码使用synchronized修饰符
第二代线程非安全集合类
ArrayList HashMap
线程不安全但是性能好,用来替代第一代
要变成线程安全,使用工具类Conllections,底层也是使用synchronized代码块锁,但是相比第一代性能稍有提升
Map接口 使用键值对存储(k-v),不能有重复的key,value可以重复,键要保证唯一性,键和值存在映射关系
集合名 | 存值 | 数据结构 |
---|---|---|
Hashtable | 不可以存入null键,null值 | 哈希表 |
HashMap | 允许null值和null键 | JDK1.7 数组+链表 JDK1.8 数组+链表+红黑树 |
LinkedHashMap | 允许null值和null键 | 哈希表+双向链表 |
TreeMap | key不可以为null,值可以为null | 红黑树 |
如果需要保证键的唯一性需要覆写hashCode方法,和equals方法
如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现
TreeSet底层实际使用的存储容器是TreeMap
对于TreeMap而言,它采用一种被称为红黑树的排序二叉树来保存Map中的每个Entry,每个Entry被当成红黑树的一个节点来对待
总结:
HashSet的实现原理,封装了一个HashMap对象来存储所有的集合元素,不允许集合中有重复
的值,使用该方式时需要重写 equals()和 hashCode()方法
总结:
根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
一个进程可以包含多个线程
进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃,整个进程都死掉,所以多进程要比多线程健壮
接口/类 | 方法体 | 介绍 |
---|---|---|
exteds Thread类 | run() | |
implement Runnable接口 | run() | |
implement callable接口 | call() | 方法有返回值,能够抛出异常 |
新建:new创建一个线程
就绪:.start()后,该线程处于就绪状态,只是表示可以运行,等待jvm调用
运行:线程获得cpu开始执行run()方法体
阻塞:如果其他线程抢占CPU就会阻塞,或者调用sleep()
死亡:run()、call()执行完毕,线程正常结束,或者是抛出异常、调用stop()但是容易死锁
创建线程整个过程的浪费是比较大的,而线程池的意义在于先创建线程需要的数据,当要用的时候直接去线程池中取出线程,这样节省了创建连接和关闭资源的开销
ExecutorService executorService=new ThreadPoolExecutor(3,//核心线程数
6,//最大线程数
1L,//存活时间(多余的空闲线程的存活时间)
TimeUnit.SECONDS,//时间单位(线程池维护线程所允许的空闲时间的单位)
new ArrayBlockingQueue<>(3),//任务队列
Executors.defaultThreadFactory(),//线程工厂
new ThreadPoolExecutor.AbortPolicy());//拒绝策略
任务队列:任务队列是基于阻塞队列实现的,即采用生产者消费者模式
**线程工厂:**指创建线程的方式
**拒绝策略:**4种
AbortPolicy(默认):丢弃任务并抛出 RejectedExecutionException 异常。
CallerRunsPolicy:由调用线程处理该任务。
DiscardPolicy:丢弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。
DiscardOldestPolicy:丢弃队列最早的未处理任务,然后重新尝试执行任务。
通过Exeutors(jdk1.5)提供的四种线程池
名称 | ||
---|---|---|
可缓存线程池 | newCachedThreadPool | 如果线程池长度超过处理需要,可以灵活的回收线程,若无可回收,则新建线程 |
定长线程池 | newFixedThreadPool | 可控制线程最大并发数,超出的线程会在队列中等待 |
定长线程池,定时 | newScheduledThreadPool | 支持定时及周期任务执行 |
单线程化的线程池 | newSingleThreadExecutor | 它会用唯一的工作线程来执行任务,保证所有的任务按照指定顺序执行 |
Lock是接口,synchronized是关键字
Lock可以选择性的获取锁,如果一段时间获取不到,可以放弃。Synchronized,会一直获取,借助这个特性可以规避死锁
synchronized在发生异常和同步代码块结束的时候,会自动释放锁,Lock必须手动释放,所以如果忘记释放锁,也会造成死锁
1.对象锁钥匙只能有一把才能互斥,才能保证共享变量的唯一性
2.在静态方法上的锁,和 实例方法上的锁,默认不是同样的,如果同步需要制定两把锁一样。
static synchronized是类锁
synchronized是对象锁
若类对象被lock,则类对象的所有同步方法(static synchronized func)全被lock。
若实例对象被lock,则该实例对象的所有同步方法(synchronized func)全被lock。
java的两种同步机制:同步块(或方法)和volatile变量
锁提供了两种主要特性:互斥和可见性
volatile关键字是一种轻量级的同步机制,只保证数据的可见性,而不保证数据的原子性。
非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作
synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
在传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁、写锁等,都是在操作之前先上锁(效率低)
在数据上会为其加一个版本号,在操作后数据版本号会更新,如果使用数据版本号不一致,后面就不能操作了,乐观锁适用于多读的应用类型,这样可以提高吞吐量。
List接口是Collection接口的子接口定义一个允许重复项的有序集合,可以动态的增长,最重要的是它保证维护元素特定的顺序
ArrayList 优点是随机访问元素,但插入和删除速度很慢
LinkedList 对顺序访问进行了优化,插入和删除的开销并不大,随机访问则相对较慢
是指在多线程环境下,每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的