就因为没看这篇文章面试失败了
前言
熬夜整理了一份java基础面试题,希望大家支持,如果文中有错误希望大家指正;
公众号:知识追寻者
知识追寻者(Inheriting the spirit of open source, Spreading technology knowledge;)
一 java基础面试
1.1面向对象和面向过程的区别
- 面向过程:
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗 资源
应用场景:单片机、嵌入式开发、Linux/Unix ;
缺点:没有面向对象易维护、易复用、易扩展
- 面向对象
优点:因为面向对象有封装、继承、多态性的特 性,可以设计出低耦合的系统,故易维护、易复用、易扩展;
应用场景:网页开发,后台开发等;
缺点:性能比面向过程低
1.2 面向对象的特性
- 封装: 将一个对象的属性私有化,并提供一个对外访问的方法;
- 继承:在已有类的基础上建立新类;提供继承信息的类被称为父类(超类、基类);得到继 承信息的类被称为子类(派生类)
- 多态:一个对象的多种表现形态;用同样的对象引用调用同样的方法但是做了不同的事情;可以向上转型和向下转型
多态实现形式:
重写:子类对父类方法的重写;
覆盖:实现接口,并覆盖方法;
1.3 抽象
抽象是指将对象抽象为具体类的过程;抽象只关注对象有哪些属性和行为(方法);
1.4 Java语言特点
- 简单易学;
- 面向对象(封装,继承,多态);
- 跨平台(Java虚拟机实现平台无关性);
- 可靠性;
- 安全性;
- 支持多线程
- 支持编译与解释;
- 支持网络编程
1.5 JDK,JRE,JVM之间的区别
JDK: java开发工具包;包含JRE, javac等调优诊断工具;能够创建和编译程序;
JRE: java运行环境; 包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件;不能创建程序;
JVM:java虚拟机,提供运行字节码文件(.class)的环境支持;
jdk 包含jre ; jre 包含 jvm
1.6 面向对象五大基本原则
- 单一职责原则SRP(Single Responsibility Principle);设计类时要功能单一;
- 开放封闭原则OCP(Open-Close Principle);一个模块对于拓展是开放的,对于修改是封闭;
- 里式替换原则LSP(the Liskov Substitution Principle LSP);子类可以替换父类;
- 依赖倒置原则DIP(the Dependency Inversion Principle DIP);高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象;
- 接口分离原则ISP(the Interface Segregation Principle ISP);设计类时,功能接口拆分为多个;
开发设计类时需考虑的事情,面试中如果碰见能答几个就几个;
1.7 什么是java主类
java主类是java代码运行的入口,即包含 main方法的类;
1.8 构造器能否被重写
子类无法继承父类的构造器,所以构造器不能被重写(overidde),但可以被重载(overload);
1.9 重载和重写的区别
重写
:
-
发生在父类与子类之间
-
方法名,参数列表,必须相同,返回值小于等于父类;
-
访问修饰符大于等于父类(public>protected>default>private);父类方法为private,则无法重写;
-
重抛出异常的范围要小于等于父类异常;
重载
:
- 发生在同一个类中
- 方法名相同参数列表不同(参数类型不同、个数不同、顺序不同);
- 方法返回值和访问修饰符可以不同;
1.10 equals与==的区别
== : 判定是否是相同一个对象 ,比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,;
equals: equals用来比较的是两个对象的内容是否相等; 但Object中的equals方法返回的却是==的判断;
1.11什么是hashCode
hashCode() 的作用是获取哈希码,也称为散列码;它实际上一个int整数;哈希码的作用是确定该对象在哈希表中的索引位置;散列表存储的是键值对(key-value),即能根据键获取值;
当一个对象插入散列表时先会比较对象与散列表中已有的对象hash值,若不同,则直接插入散列表,若相同(hash碰撞),则会调用equals 方法检查是否真的相同, 如果equal()判断不相等,直接将该元素放入集合中,否则不放入;
对象中hashCode()与equals()的关系
- 如果两个对象相等,则hashcode也相等;
- 两个对象相等,对两个对象分别调用equals方法都返回true
- 两个对象有相同的hashcode值,它们不一定是同一个对象;
- equals 方法被覆盖过,则 hashCode 方法也必须被覆盖;
1.12 什么是值传递和引用传递
值传递:传递了对象的一个副本,即使副本被改变,也不会影响源对象;
引用传递:着传递的并不是实际的对象,而是对象的引用;对外部对象的改变会反映到实际对象;
一般认为,Java 内的传递都是值传递,Java 中实例对象的传递是引用传递;
1.13 什么是抽象类与接口
-
抽象类是对类的抽象,是一种模板设计; 而接口是行为的抽象,可以理解为行为的规范;
-
抽象类中可以包含非抽象方法;而接口是绝对的抽象方法;
-
接口默认是public 方法,java8中接口支持默认(default)方法;
-
一个类可以实现多个接口,但只能实现一个抽象类;
-
抽象类不能使用final修饰(final修饰的类为固定类,无法被继承)
1.14String、String StringBuffer 和 StringBuilder 的区别
- String是只读字符串,并不是基本数据类型,而是一个对象;其无法改变;每次操作都会产生新对象;
- StringBuilder 并没有对方法进行加同步锁,线程不安全;
- StringBuffer 对方法加了同步锁,线程安全;
tip: 数据量少的时候使用 String; 单线程使用StringBuilder ; 多线程使用 StringBuffer ; 使用StringBuilder 性能比 StringBuffer 性能 提升大概有 10%~15%;
1.15 用最有效率的方法计算 2 乘以 8
2 << 3 = 8 ; 2 左移3位
1.16 & 与 && 的区别
& 运算符 按位与;
&& 运算符是短路与:&& 左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算
1.17 static 关键字
static 变量在 Java 中属于类的,它在所有的实例中具有相同的值。当类被 Java 虚拟机载入的时候,会对 static 变量进行初始化;故需要用实例来访问 static 变量;
1.18 Java 支持的数据类型
- 整数值型:byte,short,int,long;
- 字符型:char;
- 浮点类型:float,double;
- 布尔型:boolean;
类型 | 位数 | 字节数 |
---|---|---|
short | 2 | 16 |
int | 4 | 32 |
long | 8 | 64 |
float | 4 | 32 |
double | 8 | 64 |
char | 2 | 16 |
1.19 final, finally, finalize 的区别
- final:用于声明属性,方法和类, 分别表示属性不可变, 方法不可覆盖, 类不可继承.
- finally:异常处理语句结构的一部分,表示最后总是会执行.
- finalize:Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法;
1.20 instanceof 关键字
instanceof 严格来说是Java中的一个双目运算符,用来检查一个对象是否为一个类的实例;
1.21 为什么不能用浮点型表示金额
浮点数为非精确值,应该使用BigDecimal来修饰金额;
1.22 自动装箱与拆箱
- 装箱:自动将基本数据类型转换为包装器类型,调用 Integer的valueOf(int) 方法;
- 拆箱:自动将包装器类型转换为基本数据类型。调用Integer的intValue方法
tip: int 是基础数据类型,占用空间小; Integer 是对象占用空间大;
1.23 switch中能否使用string做参数
在idk 1.7之前,switch只能支持byte, short, char, int或者其对应的封装类以及Enum类型。从idk 1.7之后switch开始支持String。
可以用在byte上,但是不能用在long上。
1.24 java 创建对象的几种方式
- 采用new
- 通过反射
- 采用clone
- 通过序列化机制
1.25 如何将byte转为String
可以使用 String 接收 byte[] 参数的构造器来进行转换, 但编码必须正确;
1.26 final有哪些用法
1.被final修饰的类不可以被继承
2.被final修饰的方法不可以被重写
3.被final修饰的变量不可以被改变。
5.被final修饰的常量,在编译阶段会存入常量池中。
1.27 java当中的四种引用
- 强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。
- 软引用:在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。
- 弱引用:具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。
- 虚引用:如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。
1.28 Math. round(-1. 5) 等于多少?
Math.round(-1.5)的返回值是-1。四舍五入的原理是在参数上加0.5然后做向下取整
1.29 String str="i"与 String str=new String("i")一样吗?
tring str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中;所以一个是常量,一个是对象,不一样;
1.30char 型变量中能不能存储一个中文汉字
char 类型可以存储一个中文汉字,因为 Java 中使用的编码是 Unicode,一个 char 类型占 2 个字节,所以能放一个中文。
1.31 break 和 continue 的区别
- break 跳出整个循环。
- continue 用于跳过本次循环,执行下次循环。
1.32 内部类与静态内部类的区别
内部类:
1、内部类中的变量和方法不能声明为静态。
2、内部类实例化:B是A的内部类,实例化B:A.B b = new A().new B()
。
3、内部类可以引用外部类的静态或者非静态属性及方法。
静态内部类:
1、静态内部类属性和方法可以声明为静态的或者非静态的。
2、实例化静态内部类:B是A的静态内部类,A.B b = new A.B()
。
3、静态内部类只能引用外部类的静态的属性及方法。
1.33 throw和throws的区别?
- throw用于主动抛出java.lang.Throwable 类的一个实例化对象,即通过关键字 throw 抛出一个 Error 或者 一个Exception;
- throws 的作用是作为方法声明和签名的一部分;
1.34 error和exception的区别
- Error类和Exception类的父类都是throwable类
- Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。直接终止程序即可;
- Exception类表示程序可以处理的异常,可以捕获和有可能恢复。需要程序员手动处理;
1.35 什么时候用断言(assert)
断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为true;如果表达式的值为false,那么系统会报告一个AssertionError。
断言有两种形式:
assert Expression1
;表示一个boolean表达式assert Expression1 : Expression2
; Expression2表示一个基本类型、表达式或者是一个Object,用于在失败时输出错误信息
assert false;
assert i == 0:"123";// 当 i不等于0 时会输出错误信息
1.36 常见的五种运行时异常
- ClassCastException(类转换异常)
- IndexOutOfBoundsException(数组越界)
- NullPointerException(空指针异常)
- ArrayStoreException(数据存储异常,操作数组是类型不一致)
- BufferOverflowException(缓存溢出异常)
二 IO流面试
2.1 序列化的含义
- 序列化:将对象写入到IO流中
- 反序列化:从IO流中恢复对象
序列化机制将序列化的Java对象转换为位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象,通常被序列化的要实现Serializable接口,并指定序列值
Externalizable 可以控制整个序列化过程,指定特定的二进制格式,增加安全机制
2.2 java 中 IO 流分类
-
按照流的流向分,可以分为输入流和输出流;
-
按照操作单元划分,可以划分为字节流和字符流;
-
按照流的角色划,可以分为节点流和处理流。
-
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
-
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
图片来源网络:
2.3 BIO、NIO、AIO的区别
- BIO 就是传统的
java.io
包,它是基于流模型实现的,交互的方式是同步、阻塞方式IO; 在读入输入流或者输出流时,在读写完成之前,线程会一直处于阻塞状态。 - NIO (New IO)是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序。
- AIO(Asynchronous IO) 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO,异步 IO 是基于事件和回调机制实现。
三 集合面试
3.1 java 集合架构
3.2 Collection 和 Collections的区别
Collection:是java.uitl 下的接口,他是各种集合的父接口;
Conllecitons:是个java.util下的类,是针对集合的工具类,提供一系列静态方法对集合的搜索、查找、同步等操作;
3.3 ArrayList和Vector的区别
- 共同点:都实现了List接口,为有序的集合,底层基于数组实现,通过索引取值,允许元素重复和为null;都是实现 fail-fast机制;
- 区别:ArrayList不同步,Vector是同步(ArrayList 比 Vector 快,不会过载);Vector扩容原来的一倍,ArrayList扩容原来的0.5倍;
3.4List,Set,Map 三者的区别?
List
: 存储的元素有序可重复。Set
: 存储的元素是无序的、不可重复的。Map
: 使用键值对(kye-value)存储,通过key 获取value; key 不能重复;
3.5 ArrayList,LinkedList区别
共同点:
ArrayList
和 LinkedList
都是线程不安全;
区别:
- ArrayList的底层是数组实现,LinkedList的底层是双向链表实现。
- ArrayList 通过索引获取值,查询快;LinkedList通过遍历链表获取值查询慢;
- ArrayList 增删慢,LinkedList 增删快;
3.6Enumeration和iterator接口的区别
- iterator是Enumeration接口的替代品,只提供了遍历vector和HashTable类型集合元素的功能;
- Enumeration速度是iterator的2倍,同时占用更少的内存。
- terator有fail-fast机制,比Enumeration更安全
- Iterator能够删除元素,Enumeration并不能删除元素
3.7 简述Iterator 迭代器
Iterator迭代器可以对集合进行遍历, 遍历方式都是 hasNext()
和next()
方法,,在当前遍历集合元素被更改的时候,就会抛出 ConcurrentModificationException
异常;
3.8 HashMap和Hashtable的区别
共同点: 都实现 Map接口;
区别:
- HashMap不同步;Hashtable同步;HashMap效率比Hashtable高;
- HashMap允许为null ;Hashtable不允许为null
- Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1,HashMap 默认的初始化大小为 16;
- HashMap 在 jdk1.8 改变了数据结构为 数组 + 链表 + 红黑树方式; HashTable使用全表锁,效率低下;
3.9HashSet 和 HashMap 的区别
- HashMap 实现了Map接口;HashSet实现了Set接口
- HashMap储存键值对;HashSet仅仅存储对象
- HashMap使用put()方法将元素放入map中;HashSet使用add()方法将元素放入set中;
- HashMap中使用键对象来计算hashcode值;ashSet使用成员对象来计算hashcode值;
- HashSet较HashMap来说比较慢;
3.10 HashMap和TreeMap区别
TreeMap实现SortMap接口,能够将记录根据键排序(默认升序排序),也可以指定排序的比较器Comparator,当用Iterator 遍历TreeMap时得到排序后的结果;
对于插入、删除和定位元素等操作,选择HashMap;如果对一个有序的key集合进行遍历,选择TreeMap
3.11 并发集合和普通集合区别
并发集合常见的有 ConcurrentHashMap
、ConcurrentLinkedQueue
、ConcurrentLinkedDeque
等。并发集合位于 java.util.concurrent 包下,是 jdk1.5 之后才有;其相比与普通集合添加了 synchronized 同步锁,线程安全,但效率低;
3.12 ConcurrentHashMap1.7和1.8的区别
jdk1.8的实现不再使用jdk1.7的Segment+ HashEntry分段锁机制实现,利用Node数组
+CAS+Synchronized
来保证线程安全;底层采用数组+链表+红黑树
的存储结构;
ConcurrentHashMap1.7
dk1.7
ConcurrentHashMap1.8
3.13HashMap 的长度为什么是2的幂次方
目的是为了能让 HashMap 存取高效,尽量减少Hash碰撞,尽量使Hash算法的结果均匀分布,每个链表/红黑树长度大致相同;
算法实际是取模,hash%length,计算机中求余效率不如位移运算,源码中做了优化hash&(length-1);
hash%length==hash&(length-1)的前提是length是2的n次方
3.14 ArrayList集合如何高效加入10万条数据
直接在初始化的时候就指定ArrayList的容量值;
3.15 如何选用集合
根据集合的特点来选用集合;根据键获取值选用 Map
接口下的集合;需要排序选择 TreeMap
,不需要排序选择 HashMap
,需要线程安全选 ConcurrentHashMap
。
只存放元素值时,就选择实现Collection
接口的集合;需要元素唯一时选择实现 Set
接口的集合,如TreeSet或
HashSet; 不需要元素唯一性就选择实现 List 接口,如 ArrayList
或 LinkedList
;
3.16 快速失败(fail-fast)和安全失败(fail-safe)的区别是什么
-
快速失败:当你在迭代一个集合的时候,如果有另一个线程正在修改你正在访问的那个集合时,就会抛出一个 ConcurrentModification 异常。 在 java.util 包下的都是快速失败。
-
安全失败:你在迭代的时候会去底层集合做一个拷贝,所以你在修改上层集合的时候是不会受影响的,不会抛出 ConcurrentModification 异常。在java.util.concurrent 包下的全是安全失败的。
附:
HashMap 源码分析: https://blog.csdn.net/youku1327/article/details/105332136;
ArrayList源码分析https://blog.csdn.net/youku1327/article/details/105314040
tip: 需要懂 HashMap,ArrayList,LinkedList,ConcurrentHashMap底层实现原理