Java面试必备八股文一、Java基础篇
1.1)Java有哪几种数据类型
基本数据类型:byte(1字节) short(2字节) int(4字节) long(8字节) float(4字节) double(8字节) char(2字节) boolean(1字节)
引用数据类型:String 类 接口 抽象类 枚举 数组
1.2)JVM、JRE和JDK的关系
JVM指的是Java的虚拟机,Java程序需要运行在虚拟机上,不同的平台都有自己的虚拟机,所以Java语言实现了跨平台。
JRE指的是Java的运行时环境,包括需要的大量的类库和Java的虚拟机。
JDK指的运行时候的需要的一些工具类和运行时环境,比如包括javac.exe ,javadoxc.exe 一系列用于编译字节码工具 打包文档的工具
1.3)Switch支持的数据类型?
jdk1.5之前 byte、short、int、char
jdk5 ~ jdk1.7 加入了enum
jdk1.7之后 加入了String
*注意 不支持long类型
1.4)为什么float=3.4报错
3.4 默认是浮点double类型的,如果赋值给float是向下转型,会出现精度缺失,,需要强制转换
1.5)final 有什么用?
用于修饰类,方法,变量(属性):
如果修饰类,就是不能够继承
如果修饰方法就是不能够重写
修饰变量:修饰的变量是不能够修改的,如果是基本类型那么就是数值不能修改,如果是引用类型就是地址不能够修改。
成员变量:必须实现赋值
局部变量:声明的时候可以不赋值,后续允许一次赋值,赋值之后不能够修改
1.6)String有哪些特性
首先String是 private final char[]
数组 所以长度不可变
final 不能够被继承
private 内容不能够修改
1.6)Stringbuffer和 Stringbuilder有什么不同?
首先他们都是继承AbstractStringBuilder,相对于String来说是可变的字符串,只不过Stringbuffer加了synchronized所以是线程安全的,而Stringbuilder是线程非安全的
1.7)== 和 equals 的区别
== 默认是比较两个对象的引用地址的,而equals默认情况是==比较规则,但是不同的类会重写掉Object类的equals从而改变了equals的比较规则,比如String类的equals方法就是比较他们两个的内容是否相等
1.8)hashCode和equals
两个对象相等他们的hashCode和equals一定相等,但是hashCode相等的两个对象未必相等
1.9)方法重载和方法重写区别
实现方式:方法重载是在一个类当中;而方法重写是在父子类中实现的
方法重载是方法名字相同,参数个数,参数类型不同。和访问修饰符 和 返回类型无关;方法重写是方法名字相同,参数个数,参数类型必须相同,子类的返回值,抛出的异常必须小于等于父类。子类的访问修饰符大于等于父类。
1.10)面向对象和面向过程的区别
面向过程:就是具体化的一步一步的去实现,优点 就是性能比较快因为不需要进行实例化。 缺点就是不容易进行维护,扩展和复用
面向对象:因为面向对象的三大特点就是 封装、继承和多态
封装就是增加了代码的安全性,开发者不用在乎细节,只知道如何使用就可以了
继承就是代码的复用,结果设计模式就可以达到已于扩展和复用的特点
多态使用比较灵活,可以设计出低耦合的系统从而便于维护
1.11)ArrayList 和LinkedList 的区别
数据结构:
ArrayList是底层是数组存储的,用于一大块连续的空间进行存储的,默认的初始容量是10 一般情况下当容量不够的时候会进行1.5 倍的一个扩容,因为是数组形式长度是不变的,所以在扩容的时候需要数据的搬迁,从而导致频繁的扩容会导致性能效率,所以我们在使用时候最好指定好大小。 因为有索引所以在查找、修改数据时候都是O(1)时间复杂度,而数据的增加删除确要移动后面的元素,所以时间复杂度是O(n)。
LinkedList是双向链表的形式存储的,可以充分用碎片化的空间进行存储,查找,修改数据的时间复杂度都是O(n),因为都是要遍历整个链表。插入和删除除非是在指定的指针后面下进行插入和删除不然还是要遍历整个链表。所以说我们通常使用ArrayList进行数据存储。
1.12)说一说HashMap数据结构
HashMap通常是key-value键值对的方式进行存储,在JDK1.7的时候就是通过数组+链表的形式存储,每个数组存储的是一个一个的Entity组成的链表,这些链表上的数字都有一个特点就是Hash值相同,当数据量比较大的时候链表的长度比较大,这个时候查找的时间复杂度就比较大所以。在JDK1.8的时候引入了红黑树概念,当一个链表上的长度大于8的时候并且数组长度大于64这个时候链表就自动转换成红黑树,如果数组长度没有达到64那么此时就对数组进行1倍的扩容。
而且在JDK1.7和1.8还有一个改动就是插入的方式,由于JDK1.7的时候插入方式是头插法,在多线程的方式下会出选循环链表情况,所以1.8改为了尾插法方式。
扩容有三个地方
初始化数组长度为0
元素个数达到了 最大个数*0.75
单个链表的长度大于8并且数组长度大于64
HashMap是线程不安全的,如果要保证线程安全那么可以使用ConcurrentHashMap
1.13)ConcurrentHashMap原如何保证的线程安全?
JDK 1.7 时候是使用分成16个Seagment段,每个Seagment里面存储着一个HashMap和一个锁,所以说1.7能支持最大的并发量也就是16个线程
JDK1.8采用的CAS + Synchronized,每次插入的时候判断是否是第一次插入,是就通过CAS插入,然后判断f.hash是否=-1,如果是的那么其他线程在扩容,当前线程也会参与扩容;删除方法用了synchronized修饰,保障并发下删除元素的安全
1.14)抽象类和接口的区别
继承:抽象类是只能单继承的,接口是可以多实现的
属性:属性修饰符也不一样,抽象累可以是public protect 默认不写 还有final 修饰,但是接口只能是public static final修饰
构造方法:抽象类是可以进行实例化的,而接口是不能进行实例化
方法:抽象类既可以抽象的方法,也可以具体的方法,接口只有抽象的方法,而且子类必须实现