一、java基础
1、java有哪几种数据类型?
- 基本数据类型:byte(1),char(2),short(2),int(4),long(8),double(8),float(4),boolean(1)
- 引用数据类型:各种类和接口,枚举,数组
2、 面向对象和面向过程的区别?
面向对象和面向过程都是一种开发思想。
- 面向过程就是根据解决问题所需要的步骤,具体化的一步一步的去实现。
- 面向对象就是把数据及对数据的操作方法放在一起,作为一个整体,也就是对象,若干个这样的整体组成一个系统去解决实际问题。
- 面向过程只用函数实现,性能比较快因为不需要进行实例化,但是不容易扩展、维护,复用
- 面向对象通过类去实现功能模块,代码安全性高,容易扩展和复用,比较灵活且便于维护
篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】即可免费获取
3、JDK/JRE/JVM三者的关系
JVM指的是Java的虚拟机,Java程序运行需要在JVM虚拟机上,不同平台都有自己的JVM虚拟机,所以说java语言实现了跨平台
JRE指的是Java的运行时环境,包含了java核心内库和JVM虚拟机
JDK就是 Java 开发工具包,里面包含了工java运行时需要的工具类和运行时环境,比如javac.exe和其他的一系列编译调试打包工具
4、什么是字节码文件
我们使用java编译命令就能将java源文件编译对应成字节码文件(.class),字节码文件是一种八位数据的二进制流文件,可以被JVM快速加载到内存中运行
5、面向对象有哪些特性?
面向对象有三大特性:封装,继承和多态
- 封装就是将类信息隐藏在类内部,不允许外部程序直接访问,而是通过调用类的方法去访问,良好的封装能减少耦合,增加安全性。
- 继承就是从已有的类派生出新的类,新的类继承父类的属性和方法,并扩展新的能力,极大提高了代码的复用行和易维护性。
- 多态是指同一个行为具有多种形式的表现,具体体现在继承,重写,重载,父类引用指向子类对象等,多态体现出面向对象的灵活性。
- 另外面向对象还有抽象的特性,指把客观事物用代码抽象出来。
6、StringBuilder和StringBuffer的区别
它们都继承于abstractStringBuilder,相对于String来说都是可变,StringBuffer添加了syncronized所以是线程安全的,而StringBuilder是线程不安全的
线程安全指,多个线程访问一个对象时,每个线程调用该对象的行为都能获得正确的结果
7、为什么静态方法不能调用非静态方法和变量?
类在加载的时候,会将所有被static修饰的内容(包括方法和属性)初始化,但此时非静态方法和变量还没有经过初始化,也就是没有内存空间,调用的时候就会出错。
8、异常,error和exception的区别,运行时异常和非运行时异常(checked)的区别?
JAVA异常的层次结构如图:
Throwable类是java所有异常和错误的顶类,有两个重要的子类
Error:处理JVM的内部严重问题,如资源不足,无法恢复
Exception: 可以恢复的异常,分为RuntimeException(unchecked Exception)和其他Exception(checked Exception)
RuntimeException:运行时异常,处理或者不处理都可以(try/catch throw操作)
其他Exception:必须捕获或者声明抛出的异常
9、java内存模型JMM
JMM是一个抽象的规范,它定义了共享内存中多线程程序读写规范,即变量如何存储在内存中又如何从内存中读取(这里的变量指实例字段,静态变量,构成数组的元素)
JMM将内存分为主内存和工作内存
- 主内存是线程共享的公共区域,里面存储了共享变量
- 工作内存是线程私有的工作区域,里面存储了共享变量的副本
- 线程通常是对工作内存中的共享变量进行修改,然后同步到主内存,然后其他线程从主内存中同步更新自己的变量,也即,线程之间的交互是通过主内存的。
JMM对Java编程提供了原子性,可见性,有序性的保障
原子性:原子性就是说一个操作是不可分割的,不可中断的,要么执行完成,要么不执行,在JMM中通过lock操作实现,它保证了一个变量在同一时刻只能被一个线程访问,不会被其他线程影响而中断操作,对应关键字Syncronized
可见性:可见性指线程对一个变量的操作对其他线程来说是立即可见的,在JMM中通过在修改变量后变量同步到主内存,其他线程读取时刷新变量值来实现,对应关键字volatile,Syncronized
volatile
关键字!作用:它可以用来修饰成员变量和静态变量(放在主存线程共享的变量),它可以避免线程从自己的工作内存中查找读取变量的值,而是每次都从主内存中去读取,线程操作
volatile
修饰的变量都是直接操作主存的。
有序性:指本线程的所有操作都是有序的
篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】即可免费获取
在多线程中会有指令重排序的问题, 通过volatile关键字解决
volatile
的底层原理是内存屏障,Memory Barrier(Memory Fence)
写屏障:对 volatile 变量的写指令后会加入写屏障,
写屏障保证在该屏障之前的,对共享变量的改动,都同步到主存当中
写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后
读屏障:对 volatile 变量的读指令前会加入读屏障
- 读屏障保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据
- 读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前
10、常见的集合
Java中的集合主要由Collection(单列集合)和Map(双列集合)这两个接口派生而来
Collection下有子接口List,Queue,Set(无序)
List接口的实现类有ArrayList,vector(线程安全),stack,LinkedList(双向链表)
Set接口的实现类有hashset(底层是hashMap),sortedSet(接口)、treeSet(底层是treeMap,遍历有序),LinkedHashSet
线程安全的集合:Vector、HashTable、ConcurrentHashMap、Stack(继承Vector)、ArrayBlockingQueue、ConcurrentLinkedQueue
11、重写与重载的区别
1.定义不同---重载是定义相同的方法名,参数不同;重写是子类重写父类的方法。
2.范围不同---重载是在一个类中,重写是子类与父类之间的。
3.多态不同---重载是编译时的多态性,重写是运行时的多态性。
4.返回不同---重载对返回类型没有要求,而重写要求返回类型只能与父类一致或者是父类方法返回类型的子类。
5.参数不同---重载的参数个数、参数类型、参数顺序可以不同,而重写要求参数列表一致
6.修饰不同---重载对访问修饰没有特殊要求,重写访问修饰符的访问权限一定要大于被重写方法的访问修饰符。
12、HashMap底层,插入,扩容
前置知识:
二叉树:每个节点至多只有两个子节点的树
搜索二叉树:满足当前节点的左子树上的节点不能大于当前节点,右子树上的节点不能小于当前节点的二叉树
红黑树:一种自平衡的搜索二叉树,能保证遍历,插入,删除的时间复杂度永远是O(logn)
红黑树规则:
红黑:节点只有红黑两种颜色,根节点和叶节点一定是黑色,红节点的子节点一定是黑色
树:叶节点一定是null,任一节点到叶节点的所有路径包含相同数量的黑节点
散列表:又称Hash表,是一种kv存储结构,存储时先计算k经过哈希函数运算后的值,然后根据这个值将v存放在对应的位置
篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】即可免费获取
HashMap底层实现是数组+链表/红黑树
添加数据时,根据key的hash值来确定value所在的数组下标
如果数组下标对应的链表/红黑树中有相同的key,就会替换value,否则加入到链表/红黑树中
jdk1.8之前采用的拉链法 数组+链表
jdk1.8之后采用的数组+链表+红黑树法,链表长度大于8时,先尝试扩容数组,如果数组长度此时大于64则会从链表转化为红黑树。
插入操作(简化):
1、判断数组是否为空,如果为空调用resize方法,初始化一个容量16的数组
2、对key进行hash运算,得到数组中的索引,table下标i=(table.length- 1) & ((h = key.hashCode()) ^ (h >>> 16)),如果索引指向的位置为空,则直接插入一个新的node
3、如果不为空,则判断当前key是否存在,如果存在直接进行替换value操作
4、如果不存在,判断当前Node节点是否是红黑树结构,如果是树类型,则按照树的方式去追加节点,否则在链表尾部插入数据
5、最后判断链表长度是否大于8,如果大于8,会先尝试扩容,判断数组长度是否小于64,是就扩容,否则链表转换为红黑树。
6、插入操作结束后,判断如果hashmap的元素个数超过了负载因子*容量的阈值,则需要进行扩容操作
7、扩容操作会将原来数组的元素分配到新的数组中,重新每个元素在数组中的下标
8、扩容操作完成后,再将元素插入到新的数组中
扩容操作:
1、在添加元素或初始化时扩容,执行resize方法,第一次初始化长度为16,以后每次在元素达到容量*负载因子(0.75)时,进行扩容
2、每次扩容操作后,容量都是之前的2倍
3、扩容之后,会创建一个新的数组,然后把老的数组中的元素移动到新的数组中
4、如果是链表,则计算每个链表元素的下标,然后移动到新的数组
5、如果是红黑树,则走红黑树的添加。
13、对象的创建方式
1、new
2、将类继承Cloneable接口,然后实现clone方法,调用clone方法即可复制对象
3、反射(类派发),Student.class.newInstance()
4、反射(动态加载),Class.forName("entity.Student").newInstance()
5、反射(构造方法),Student.class.getConstructor().newInstance()
6、Spring容器启动时将Bean注入
14、四种访问修饰符
1)public :公共权限,可以被任意类访问,不同包不同类依然可以访问,
可修饰:类、成员变量、方法,内部类
2)protected:受保护的权限,可以被同包类访问,如果不是同包类,必须是该类的子类才可以访问
可修饰:成员变量、方法、内部类
3)default:默认的(无),同包权限,只能被同包的类访问
可修饰:类、成员变量、方法,内部类
4)private:私有权限,只能在本类中访问,同包其他类也不能访问