这些面试题有网上的,也有自己碰到的,也有自己总结归纳的。
部分来源网址https://blog.csdn.net/weixin_43495390/article/details/86533482
5.4号
1.JAVA中的几种基本类型,各占用多少字节?
java共八种基本类型
1:boolean:1字节8位
2:byte:1字节8位有符号整数
3:short:2字节16位有符号整数
4:int:4字节32位有符号整数
5:long:8字节64位有符号整数
6:char:2字节16位unicode字符
7:float:4字节32位浮点数
8:double:8字节64位浮点数
2.String能被继承吗?为什么?
不可以,因为string类有final修饰符,被final修饰的类不能被继承,实现细节不允许被改变。
3.String, StringBuffer, StringBuilder 的区别。
- string是字符串常量,被final修饰,不可被继承,创建之后不可被修改。
创建之后不可被修改指的是内存中创建的对象不可被更改,比如:
String new = "old"
new = "new"
system.out.printf(new)
这段代码输出的是new,但是开始创建时new指向的内存中值为“old”的对象还在,这段代码产生了两个对象,一个是old一个是new
2. StringBuffer是字符串变量,线程安全,同时也被final修饰,不可被继承。绝大多数的方法都使用了sync同步处理,tostring方法也会进行对象缓存,减少元素的复制开销。
在stringbuffer中又引出了一个问题,就是
***为什么stringbuffer是线程安全的?线程安全又是什么?***线程安全通俗点说指的是内存中的一个共享资源只能同时被一个线程访问,而stringbuffer中的sync同步方法就保证了在访问内存中的变量不会被其他线程中途插入访问造成内存错误。
3. StringBuilder是字符串变量,非线程安全,除了方法没有使用sync修饰之外基本和StringBuffer一样,同样被final修饰,不可被继承。tostring方法直接返回的是新String对象。
4.ArrayList 和 LinkedList 有什么区别。
这两个都是基于List接口的实现,不同:
- ArraryList是基于索引的数据接口,底层是数组,可以以时间负责度O(1)随机访问;而LinkedList是以元素列表的方式存储数据,每个元素都和它的上一个元素和下一个元素对应,所以它的时间复杂度是O(n)
- 相对于ArrayList,LinkedList在插入和删除更快,因为当元素被添加到任意集合时候,LinkedList不需要重新计算数组大小或者更新索引
- LinkedList比ArrayList更占内存,因为LinkedList每一个节点需要保存两个引用,一个指向前一个元素,一个指向下一个元素
5.5号
5.讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当 new 的时候, 他们的执行顺序。
父类静态变量
父类静态代码块
子类静态变量
子类静态代码块
父类非静态变量
父类构造函数
子类非静态变量
子类构造函数
6.HashMap
HashMap是面试中非常非常常见的问题,日常使用很简单,就是一个key一个value,但是深层次的理解需要花比较多的时间
6.1说说了解的HashMap?
- HashMap的数据结构特性是数组加链表,它继承了数组的线性查找和链表的寻址修改提高效率
- HashMap存储的是key-value键值对映射
- HashMap是非线程安全的,在不保证线程安全的前提下,速度较快
- HashMap的key和value都可以为null,而Hashtable则不能,因为Hashtable使用equals方法会产生空指针异常,而HashMap经过API处理,不会 出现这种情况
6.2 HashMap的工作原理
HashMap使用的是hashing的原理,我们使用put(key,value)的方式存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们会先对key的hashCode做hash运算,返回的hashcode用于找到哈希桶中的索引值存储Entry对象
6.3 put()和get()的工作原理
5.6号
put():
对key的hashCode做hash运算,计算index; 如果没碰撞直接放到哈希桶里; 如果碰撞了,节点已经存在就直接覆盖原节点;属于红黑树节点就增加一个红黑树节点;如果都不是就遍历插入链表中,这时如果长度大于8,就把链表转换成红黑树(JDK1.8中的改动); 如果如果哈希桶满了,进行扩容。
get():
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
//如果表为null或长度为0,或者经过hash算法后得到的哈希桶为null,则直接返回null
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
//如果链表中的第一个节点元素相等则直接返回该Node
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
//第二个节点不为空时继续往后找
if ((e = first.next) != null) {
//判断是否为红黑树,是则交给红黑树去查找
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
//否则循环链表找到对应相等的元素,直到找到或下个节点为null
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
get方法比较简单,其实就是hash算法后得到哈希桶的索引值,再对桶内的链表进行比较hash,key是否相等
6.4 hashmap中的扩容
hashmap中的扩容是比较消耗资源的操作,首先要把原来的容量扩大为两倍,再对原先的节点重新计算hash值生成新索引放入哈希桶中。在平常运用中尽量要避免让hashmap进行扩容,若已知hashmap中的元素数量,则一开始初始化hashmap时指定容量,这样就减少了hashmap扩容次数。
5.7号
7:继承和实现的区别
继承是一个类或接口继承另外一个类或接口的功能,通常称为子类继承父类,子类可以继承父类的功能,继承是类与类、接口与接口之间最常见的关系。通过关键字extends明确表示,设计时没有争议性。
实现是类实现一个或多个接口,实现是类与接口中最常见的关系,通过implements明确表示,在设计时没有争议性。
8:讲讲你理解的 bio和 nio 的区别是啥
BIO:是blockio的缩写,是同步阻塞io,也就是我们平常使用的传统IO,特点是模式简单使用方便,但是处理并发能力低。
NIO:是new io的缩写,是同步非阻塞io,传统io的升级。
9:final 的用途
final 修饰的类叫最终类,该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
10:单例模式
10.1单例模式特点(什么是单例模式)?
a.单例类只能有一个实例。
b.单例类必须自己创建自己的唯一实例。
c.单例类必须给所有其他对象提供这一实例。
10.2单例模式的作用(用单例模式的目的)?
Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。
10.3一般Singleton模式通常有几种种形式:
第一种形式: 饿汉式单例类
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton {
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
第二种形式:懒汉式单例类
public class Singleton {
private Singleton(){}
private static Singleton instance = null;
public static Singleton getInstance() {
if (instance==null)instance=new Singleton();
return instance;
}
}
10.4哪一种模式更安全?为什么?
第一种形式要更加安全些
instance = new Singleton();
static属于类的资源,类资源在jvm加载类的时候就加载好了,instance一直引用这new Singleton(),所以永远都不会释放一直存在与内存中直到程序结束运行
第2种的话如果两个线程同一时刻去访问getInstance的时候就可能创建两个实例,所以不安全
解决办法(加上同步锁)
11 Redis
11.1 什么是redis
redis是C语言开发的一个开源的高性能键值对(key-value)的内存数据库,可以用作数据库,缓存,消息中间件等。
它是一种NoSQL的数据库
redis作为一个内存数据库性能优秀,支持10W QPS,单进程单线程,是线程安全的,采用IO多路复用机制。redis中有常见的五种数据类型:string、list、set、sorted set(有序集合)、hash
11.2 Redis 为什么是单线程的
官方文档表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了
11.3 单线程的redis为什么这么快
- 纯内存操作
- 单线程操作,避免了频繁的上下文切换
- 采用了非阻塞I/O多路复用机制
5.9号
12:JDK 和 JRE 有什么区别?
JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。
具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。
13:== 和 equals 的区别是什么?
== 解读:
对于基本类型和引用类型 == 的作用效果是不同的,如下所示:
基本类型:比较的是值是否相同;
引用类型:比较的是引用是否相同;
实例:
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true,引用相同
System.out.println(x==z); // false,==:string比较引用,开辟了新的堆内存空间,所以false
System.out.println(x.equals(y)); // true,equals:string:比较值,相同
System.out.println(x.equals(z)); // true,equals:string比较值,相同
equals 解读:
equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。
首先来看默认情况下 equals 比较一个(有相同值的对象),代码如下:
public class Cat {
private String name;
public Cat(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
Cat c1 = new Cat("cat1");//c1是Cat的实例化对象,c2同理
Cat c2 = new Cat("cat2");
String s1 = new String("隔壁老王");
String s2 = new String("隔壁老王");
System.out.println(c1.equals(c2));//false,equals在比较的类对象的时候比较的是引用
System.out.println(s1.equals(s2)); //true,而在比较string的时候,因为重写了equals方法,和基本数据类型一样,比较的是值,所以为true
}
总结 :== 对于基本类型来说是值比较(不难理解,八种基本数据类型是可以有确定值的),对于引用类型来说是比较的是引用(数组、类、接口没有确定值);而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。
14:List、Set、Map 之间的区别是什么?
14.1 写一个Map的循环遍历
Iterator<Entry<String, Object>> it = map.entrySet().iterator();
while(it.hasNext()){
Entry<String, Object> entry = it.next();
System.out.println("key:"+entry.getKey()+" key:"+entry.getValue());
}
15:如何实现数组和 List 之间的转换?
List转换成为数组:调用ArrayList的toArray方法。
数组转换成为List:调用Arrays的asList方法。
16. ArrayList 和 Vector 的区别是什么?
- Vector是同步的,而ArrayList不是。
- ArrayList比Vector快,它是异步,不会过载。
- ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。
17.Array 和 ArrayList 有何区别?
-
Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
-
Array是指定大小的,而ArrayList初始大小是固定的。
-
Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。
18.在 Queue 中 poll()和 remove()有什么区别?
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。
19. 哪些集合类是线程安全的?
vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
statck:堆栈类,先进后出。
hashtable:就比hashmap多了个线程安全。
enumeration:枚举,相当于迭代器。
20.迭代器 Iterator 是什么?
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
21.Iterator 怎么使用?有什么特点?
迭代器的实际使用参考14.1
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。
(2) 使用hasNext()检查序列中是否还有元素。
(3) 使用next()获得序列中的下一个元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
22. Iterator 和 ListIterator 有什么区别?
- Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List,见名知意,Set并不能使用ListIterator
- Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
- ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
5.16 以后这篇文章会配着脑图的顺序走,细碎的知识点会在这篇文章,比较复杂的知识点会重新写一篇文章
23.&和&&的区别
当&&左边的为false时,不再执行右边;而对于&来说,无论左边是否为false,都执行右边
24.如何用最有效的方法计算2*8
一个数向左移n位,就相当于乘以2的n次方,cpu是直接支持位运算的,所以最快的方式是2<<3
25.过滤器明天单开文章,今天太累了