1. Java基础
Java 基础
《Java编程思想》、《疯狂Java:突破程序员基本功的16课.修订版》,这里省略了一些非常基础的知识点
请你解释为什么会出现 4.0 - 3.6=0.40000001 这种现象
二进制小数无法精确表达十进制小数,计算机在计算十进制小数的过程中要先转换为二进制进行计算,这个过程中出现了误差
请你讲讲一个十进制的数在内存中是怎么存的?
以二进制补码形式存储,最高位是符号位,正数的补码是它的原码,负数的补码是它的反码加1,在求反码时符号位不变,其他取反,1表示负数,0正数
接口和抽象类的区别是什么 ?
接口中的所有方法隐含的都是抽象的,而抽象类则可以同时包含抽象和非抽象方法
类可以实现很多个接口,但只能继承一个抽象类
类可以不实现抽象类和接口声明的所有方法,但这种情况下,该类必须声明成抽象的
接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
JDK8后,接口中可以包含default方法,抽象类中不可以
面向对象开发的六个基本原则,在项目中用过哪些原则
- 六个基本原则
- 单一原则
一个类只做它该做的事情(高内聚)。在面向对象中,如果只让一个类完成它该做的事,而不涉及与它无关的领域就是践行了高内聚的原则,这个类就只有单一原则 - 开闭原则
软件实体应当对扩展开发,对修改关闭,要做到开闭有两个要点:
1. 抽象是关键,一个系统中如果没有抽象类或接口系统就没有扩展点
2. 封装可变性,将系统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一起,系统将变的复杂而繁乱 - 里氏替换原则
任何时候都可以用子类型替换掉父类型,子类一定是增加父类的能力而不是减少父类的能力 - 依赖倒置原则
面向接口编程。高层模块不应该依赖底层模块,两者都应该依赖其抽象,尽可能使用抽象类型而不用具体类型,因为抽象类型可以被它的任何一个子类型所替代。 - 接口隔离原则
类间的依赖关系应该建立在最小的接口上,不能大而全,接口表示能力,一个接口只应该描述一种能力,接口也应该高度内聚 - 迪米特法则
由叫最少知识原则,一个对象应该对其他对象有尽可能少的了解
- 单一原则
- 根据自己的项目来说
详细的可以看这里:https://www.cnblogs.com/qifengshi/p/5709594.html
关于网络方面的问题,面试官应该只会问一题,不会多问
HTTP请求的GET与POST方式的区别
- GET在浏览器回退是无害的,而POST会再次提交请求
- GET请求会被浏览器主动cache,而POST不会,除非手动设置
- GET请求只能进行URL编码,而POST支持多种编码
- GET请求参数会被完整保留在浏览器历史记录中,而POST中的参数不会被保留
- GET请求在URL中传送参数是有大小限制的,不能大于2KB,而POST可以说没有
- GET只接受ASCII字符,而POST没有限制
- GET参数直接暴露在URL上,而POST将数据放在request body中
TCP 三次握手,四次挥手
可见该文:https://blog.csdn.net/qq_38950316/article/details/81087809
TCP 和 UDP区别
- 区别:
UDP是无连接的,即发送数据之前不需要建立连接
UDP使用尽最大努力交付,即不保证可靠交付,同时也不使用拥塞控制
UDP是面向报文的,没有拥塞控制,适合多媒体通信要求
UDP支持一对一,一对多,多对一和多对多的交互通信
UDP首部开销小,只有8个字节
TCP是面向连接的运输层协议
TCP只能一对一连接
TCP提供可靠的交付服务,提供全双工通信
TCP 面向字节流,头部最低20个字节
从输入网址到获取页面的过程
- 查询DNS, 获取域名对应的IP地址
- 浏览器搜索自身的DNS缓存
- 搜索操作系统的DNS缓存
- 读取本地的HOST文件
- 发起一个DNS系统调用(宽带运营服务器查看本身缓存,运营服务器发起一个迭代DNS解析请求)
- 浏览器获得域名对应的IP地址后,发起HTTP三次握手
- TCP/IP建立连接后,浏览器可以向服务器发送HTTP请求了
- 服务器接收到请求后,根据路径参数,经过后端处理将页面返回给浏览器
- 浏览器渲染页面,和外部资源,最终将完整的页面呈现给用户
Session与Cookie区别
Session:
服务器端会为每个访问服务端的请求分配一个会话Session,其数据存储在服务器端,不依赖浏览器端环境,因此高效安全
Cookie:
数据已文件形式存在用户浏览器端,用户可以通过浏览器禁用Cookie,用户可以对Cookie进行查看,修改,和删除
列出自己常用的JDK包
常用的包:
java.lang 包装类,线程等都在该包
java.match 有BigDecimal 精确数字类型
java.util 并发,集合等都在该包内
equals与==的区别
- equals 比较两个实体值是否相同,可以被覆盖,但需要遵循几个约定:
自反性:对于任何非null的引用值x, x.equals(x)必须返回true
对称性:对于任何非null的引用值x和y,当y.equals(x)返回true时,x.equlas(y)必须返回true
传递性:对于任何非null的引用值x、y、z,如果x.equals(y)返回true,并且y.equals(x)也返回true,那么x.equals(z)也必须返回true
一致性:对于任何非null的引用值x和y,只要比较对象中的所有信息没有被修改,多次调用equals一致返回true,或者false - == 比较两个实体的引用地址是否相等,不能覆盖,如果引用地址相等,那认为两个实体为同一个实体
hashCode和equals方法的区别与联系
对于覆盖了equals方法的类中,同样也要覆盖hashCode方法,如果不这样该类在散列集合中会有问题,比如HashMap中,计算key的索引位置,会调用到key.hashCode
什么是Java序列化和反序列化,如何实现Java序列化?或者请解释Serializable 接口的作用
序列化是一种用来处理对象流的机制,也就是将对象的内容转化成二进制流,可以将对象持久化或者网络传输
反序列化是将二进制流还原为对象的过程
实现Java序列化,通过实现Serializable即可
Object类中常见的方法,为什么wait notify会放在Object里边?
因为Java提供的锁是对象级的,每个对象都有对象头,用来存储锁
解释一下extends 和super泛型限定符
称为 上界限定符,list只能get,不能add(除了add null值),通常用于读 称为 下界限定符,list只能add,不能get(只能用Object接收),通过用于写 ### 请列举你所知道的Object类的方法并简要说明 Object()默认构造方法 clone()创建并返回此对象的一个副本 equals(Object obj) 当前对象是否与obj对象相同 finalize()当垃圾收集器确定该对象可以回收时,由垃圾收集器调用此方法 getClass返回一个对象的运行时类 hashCode()返回该对象的哈希码值 notify()唤醒此对象监视器上等待的单个线程 notifyAll()唤醒在此对象监视器上等待的所有线程 toString()返回该对象的字符串表示 wait()使当前线程等待,直到其他线程调用此对象的notify()或者notifyAll()方法 wait(long timeout)导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。wait(long timeout, int nanos) 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。 ### 创建线程的方式 1. 继承Thread类创建线程,并重写run方法,调用实例对象的start()方法启动线程 2. 实现Runnable接口,并实现run方法,将实现Runnable的类传入Thread构造函数中,并调用Thread实例对象的start方法启动线程 3. 实现Callable接口,并实现call方法,创建Callable实现类的实例,使用FutureTask包装Callable对象,使用FutureTask对象传入Thread中,调用start方法启动线程,使用FutureTask对象的get方法获取线程的返回值 ### ArrayList 与 LinkedList 区别 ArrayList 是一种顺序存储的线性表,底层使用数组实现 LinkedList是一种链式存储的线性表,本质是一个双向链表,实现了List、Deque接口,可以当成双向链表、队列、栈使用 ### 自定义注解 1. 声明注解的保留期限类型@Retention(RetentionPolicy.RUNTIME)表示该注解可以在运行期保留
保留期限类型:java.lang.annotation.Retention
SOURCE: 注解信息仅保留在目标类源代码文件中,对应的字节码文件不会保留
CLASS: 注解信息存在于源代码、字节码文件中,但运行期JVM不能获得该注解信息
RUNTIME: 注解信息存在于源代码、字节码文件、运行期JVM中,能够通过反射机制获取注解类信息 2. 声明注解的可以使用的目标类型
[@Target(ElementType.METHOD) ](/ElementType.METHOD) 表示这个注解只能在方法上使用
目标类型:java.lang.annotation.ElementType
TYPE: 类、接口、注解类、Enum
FIELD: 类成员变量或常量
METHOD: 方法
PARAMETER: 参数
CONSTRUCTOR: 构造器
LOCAL_VARIABLE: 局部变量
ANNOTATION_TYPE: 注解
PACKAGE: 包 3. 使用[@interface ]() 修饰类 4. 声明注解成员
成员无入参、不能抛出异常;
可以通过default成员指定默认值
成员类型只能使用基本数据类型、String、Class、enums、注解类型,及上述类型的数组类型。如ForumService value()是非法的
如果注解只有一个成员,则成员名必须取名为value(),再使用时可以忽略成员名和赋值号,如果注解类拥有多个成员时,
对value成员赋值,可以省略value和赋值号,如果是多个成员赋值,必须使用赋值号 ### ArrayList扩容机制是怎么样的? 详细说一下。 在往ArrayList add元素的时候,如果ArrayList 已有元素数量+1 大于 ArrayList 存储元素的总长度,就会触发扩容。 首先ArrayList会计算新数组的长度,长度为老数组的0.5倍,如果新数组长度小于插入元素需要的最小长度,那么新数组长度赋值为最小长度,如果超过ArrayList允许的最大长度Integer.MAX_VALUE(![](https://g.yuque.com/gr/latex?2%5E31%5E%20-%201#card=math&code=2%5E31%5E%20-%201&height=19&width=43)),那么新数组长度为Integer.MAX_VALUE,否则为Integer.MAX_VALUE - 8 最后将原数组元素拷贝到新数组进行扩容 ### HashMap 1.7 和 1.8 的区别 - 1.7,在发生hash冲突的时候,数据结构只有链表; - 1.8,数据结构有链表和红黑树,使用红黑树是为了能够提高查询效率。在链表长度达到7时(bingCount >= TREEIFY_THRESHOLD - 1),并且hash tab[]数组长度大于等于64时,将链表转换成红黑树,如果数组长度小于64,只是对数组进行扩容
[https://blog.csdn.net/qq_21251983/article/details/90056067](https://blog.csdn.net/qq_21251983/article/details/90056067) ### HashMap中的key可以是任何对象或数据类型吗 - 可以是null,但不能是可变对象,如果是可变对象,对象中的属性改变,则对象的HashCode也相应改变,导致下次无法查找到已存在Map中的数据 - 如果要可变对象当着键,必须保证其HashCode在成员属性改变的时候保持不变 ### HashMap 初始容量 计算方法 如果在new HashMap的时候,没有指定初始initialCapacity,则初始initialCapacity为16,负载因子为0.75,下次扩容阈值为 16*0.75=12 `这个` `这个初始容量 不一定等于初始化完成后底层数组实际的容量,因为存在阈值的计算,方法如下;也不是初始容量是多少开始就能存多少个元素,因为存在负载因子,在底层数组还没满的时候就会进行扩容` 阈值计算方法为: ```java static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; } ``` 该方法计算大于等于输入参数并最接近参数的2的整数次幂的数,如10,返回16 cap -1 ,-1是为了在计算的时候能得到大于等于输入参数的值 在HashMap 进行put方法的时候,如果判断已有的元素大于 阈值就会触发扩容计算,扩容步骤 ```java final Node
[https://www.iteye.com/blog/hwl-sz-1897468](https://www.iteye.com/blog/hwl-sz-1897468) - HashTable: 通过synchronized来修饰,效率低,多线程put的时候,只能有一个线程成功,其他线程都处于阻塞状态 - ConcurrentHashMap:
1.7 采用锁分段技术提高并发访问率
1.8 数据依旧是分段存储,但锁采用了synchronized,内部采用Node数组+链表+红黑树的结构存储,当单个链表存储数量达到红黑树阈值8时(此时链表已有元素7),并且数组长度大于64时,存储结构转换为红黑树来存储,否则只进行数组的扩容
[https://www.cnblogs.com/banjinbaijiu/p/9147434.html](https://www.cnblogs.com/banjinbaijiu/p/9147434.html) ### HashMap 为什么不用平衡树,而用红黑树 红黑树也是一种平衡树,但不是严格平衡,平衡树是左右子树高度差不超过1,红黑树可以是2倍 红黑树在插入、删除的时候旋转的概率比平衡树低很多,效率比平衡树高 查找时间复杂度都维持在O(logN) > 关于HashMap的其他文章:[https://blog.csdn.net/login_sonata/article/details/76598675](https://blog.csdn.net/login_sonata/article/details/76598675) ### 常见异常分为哪两种(Exception,Error),区别是什么,了解受检异常和非受检异常吗 Exception,Error有共同的父类Throwable Error: 表示程序发生错误,是程序无法处理的,不可恢复的,如OutOfMemoryError Exception: 表示程序可处理的异常,又分为CheckedException(受检异常)、UncheckedException(非受检异常),受检异常发生在编译期,必须要使用try...catch 或者 throws捕获或者抛出异常,否则编译不通过;非受检异常发生在运行期,具有不确定性,主要由程序的逻辑问题引起的,在程序设计的时候要认真考虑,尽量处理异常 ### 实现一个LRU 可以直接使用LinkedHashMap实现 [《LinkedHashMap源码分析》](https://github.com/jujunchen/Java-interview-question/blob/master/%E8%AF%A6%E7%BB%86%E5%88%86%E6%9E%90/LinkedHashMap%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md) ```java public class LRUCache