来源不详了
原文地址: Java 基础面试题-20211228
欢迎访问我的博客: http://blog.duhbb.com/
题目
- 转发和重定向的区别
- HashMap 和 HashTable 的区别
- 什么是死锁, 产生死锁的必要条件是什么? 如何避免死锁的产生?
- 深拷贝和浅拷贝的区别
- Java 中注释的代码一定不会执行吗? 没有注释的代码一定会执行吗?
转发和重定向的区别
- 请求转发: 客户浏览器发送 http 请求,web 服务器接受此请求, 调用内部的一个方法在容器内部完成请求处理和转发动作, 将目标资源发送给客户;在这里, 转发的路径必须是同一个 web 容器下的 url, 其不能转向到其他的 web 路径上去, 中间传递的是自己的容器内的 request. 在客户浏览器路径栏显示的仍然是其第一次访问的路径, 也就是说客户是感觉不到服务器做了转发的. 转发行为是浏览器只做了一次访问请求.
- 重定向过程: 客户浏览器发送 http 请求,web 服务器接受后发送 302 状态码响应及对应新的 location 给客户浏览器, 客户浏览器发现是 302 响应, 则自动再发送一个新的 http 请求, 请求 url 是新的 location 地址, 服务器根据此请求寻找资源并发送给客户. 在这里 location 可以重定向到任意 URL, 既然是浏览器重新发出了请求, 则就没有什么 request 传递的概念了. 在客户浏览器路径栏显示的是其重定向的路径, 客户可以观察到地址的变化的. 重定向行为是浏览器做了至少两次的访问请求的.
下面通过一张图来对比两者的区别:
区别 | 重定向 | 请求转发 |
---|---|---|
在哪里完成 | 客户端完成 (可以在不同的服务器下完成) | 服务器端完成 (必须是在同一台服务器下完成) |
浏览器发送请求的次数 | 2 次或者 2 次以上 | 1 次 |
地址栏 URL 是否发生改变 | 地址栏发生变化 | 地址栏的地址不变 |
是否共享 request | 不共享数据 (经过重定向后,request 内的对象将无法使用) | 共享数据 (以前的 request 中存放的变量不会失效, 就像把两个页面拼到了一起) |
第二次请求发起者 | 浏览器 | 服务器 |
第二次的请求路径方式 | 绝对路径 | 相对路径 |
速度 | 因为还要浏览器发送第二次请求, 重定向相对慢一点 | 快 |
语句 | response.sendRedirect("success.jsp"); | request.getRequestDispatcher("success.jsp").forward(request,response); |
原文链接:https://blog.csdn.net/Weixiaohuai/article/details/103991831
HashMap 和 HashTable 的区别
- HashMap 不是线程安全的 HashMap 是 map 接口的实现类, 是将键映射到值的对象, 其中键和值都是对象, 并且不能包含重复键, 但可以包含重复值.HashMap 允许 null key 和 null value, 而 HashTable 不允许.
- HashTable 是线程安全 Collection. HashMap 是 HashTable 的轻量级实现, 他们都完成了 Map 接口, 主要区别在于 HashMap 允许 null key 和 null value, 由于非线程安全, 效率上可能高于 Hashtable.
区别如下:
- HashMap 允许将 null 作为一个 entry 的 key 或者 value, 而 Hashtable 不允许.
- HashMap 把 Hashtable 的 contains 方法去掉了, 改成 containsValue 和 containsKey. 因为 contains 方法容易让人引起误解.
- HashTable 继承自 Dictionary 类, 而 HashMap 是 Java1.2 引进的 Map interface 的一个实现.
- HashTable 的方法是 Synchronize 的, 而 HashMap 不是, 在多个线程访问 Hashtable 时, 不需要自己为它的方法实现同步, 而 HashMap 就必须为之提供外同步.
- Hashtable 和 HashMap 采用的 hash/rehash 算法都大概一样, 所以性能不会有很大的差异.
作者:hunter886 链接:https://www.jianshu.com/p/5c34133ed372
来源: 简书
著作权归作者所有. 商业转载请联系作者获得授权, 非商业转载请注明出处.
那为什么 HashTable 的 key 或者 value 不能为 null 呢?
以下回答摘自 stackoverflow: Hashtable 是较古老的类, 通常不鼓励使用它.
在之后的使用中, 设计人员发现开发中通常需要一个空键或者空值, 于是就在 HashMap 中增加了对 null 的支持.
HashMap 最为 HashTable 之后实现的类, 具有更高级的功能, 这基本上只是对 Hashtable 功能的改进.
创建 HashMap 时, 它专门设计为将空值作为键处理并将其作为特殊情况处理.
补充:JDK 源码中作者的注释:
To successfully store and retrieve objects from a Hashtable, the objects used as keys must implement the hashCode method and the equals method.
要从 Hashtable 成功存储和检索对象, 用作键的对象必须实现 hashCode 方法和 equals 方法.
由于 null 不是对象, 因此不能在其上调用 equals() 或 hashCode(), 因此 Hashtable 无法将其计算哈希值以用作键.
作者: 王李红
链接:https://www.zhihu.com/question/264749854/answer/773203452
来源: 知乎
著作权归作者所有. 商业转载请联系作者获得授权, 非商业转载请注明出处.
死锁
什么是死锁?
所谓死锁, 是指多个进程在运行过程中因争夺资源而造成的一种僵局, 当进程处于这种僵持状态时, 若无外力作用, 它们都将无法再向前推进. 因此我们举个例子来描述, 如果此时有一个线程 A, 按照先锁 a 再获得锁 b 的的顺序获得锁, 而在此同时又有另外一个线程 B, 按照先锁 b 再锁 a 的顺序获得锁.
产生死锁的原因?
可归结为如下两点:
竞争资源
系统中的资源可以分为两类:
- 可剥夺资源, 是指某进程在获得这类资源后, 该资源可以再被其他进程或系统剥夺,CPU 和主存均属于可剥夺性资源;
- 另一类资源是不可剥夺资源, 当系统把这类资源分配给某进程后, 再不能强行收回, 只能在进程用完后自行释放, 如磁带机, 打印机等.
产生死锁中的竞争资源之一指的是竞争不可剥夺资源 (例如: 系统中只有一台打印机, 可供进程 P1 使用, 假定 P1 已占用了打印机, 若 P2 继续要求打印机打印将阻塞)
产生死锁中的竞争资源另外一种资源指的是竞争临时资源 (临时资源包括硬件中断, 信号, 消息, 缓冲区内的消息等), 通常消息通信顺序进行不当, 则会产生死锁
进程间推进顺序非法
若 P1 保持了资源 R1,P2 保持了资源 R2, 系统处于不安全状态, 因为这两个进程再向前推进, 便可能发生死锁.
例如, 当 P1 运行到 P1:Request(R2) 时, 将因 R2 已被 P2 占用而阻塞;当 P2 运行到 P2:Request(R1) 时, 也将因 R1 已被 P1 占用而阻塞, 于是发生进程死锁
死锁产生的 4 个必要条件?
产生死锁的必要条件:
- 互斥条件: 进程要求对所分配的资源进行排它性控制, 即在一段时间内某资源仅为一进程所占用.
- 请求和保持条件: 当进程因请求资源而阻塞时, 对已获得的资源保持不放.
- 不剥夺条件: 进程已获得的资源在未使用完之前, 不能剥夺, 只能在使用完时由自己释放.
- 环路等待条件: 在发生死锁时, 必然存在一个进程--资源的环形链.
解决死锁的基本方法
预防死锁:
- 资源一次性分配: 一次性分配所有资源, 这样就不会再有请求了:(破坏请求条件)
- 只要有一个资源得不到分配, 也不给这个进程分配其他的资源:(破坏请保持条件)
- 可剥夺资源: 即当某进程获得了部分资源, 但得不到其它资源, 则释放已占有的资源 (破坏不可剥夺条件)
- 资源有序分配法: 系统给每类资源赋予一个编号, 每一个进程按编号递增的顺序请求资源, 释放则相反 (破坏环路等待条件)
1 以确定的顺序获得锁
如果必须获取多个锁, 那么在设计的时候需要充分考虑不同线程之前获得锁的顺序.
针对两个特定的锁, 开发者可以尝试按照锁对象的 hashCode 值大小的顺序, 分别获得两个锁, 这样锁总是会以特定的顺序获得锁, 那么死锁也不会发生. 问题变得更加复杂一些, 如果此时有多个线程, 都在竞争不同的锁, 简单按照锁对象的 hashCode 进行排序 (单纯按照 hashCode 顺序排序会出现"环路等待"), 可能就无法满足要求了, 这个时候开发者可以使用银行家算法, 所有的锁都按照特定的顺序获取, 同样可以防止死锁的发生, 该算法在这里就不再赘述了, 有兴趣的可以自行了解一下.
2 超时放弃
当使用 synchronized 关键词提供的内置锁时, 只要线程没有获得锁, 那么就会永远等待下去, 然而 Lock 接口提供了 boolean tryLock(long time, TimeUnit unit) throws InterruptedException 方法, 该方法可以按照固定时长等待锁, 因此线程可以在获取锁超时以后, 主动释放之前已经获得的所有的锁. 通过这种方式, 也可以很有效地避免死锁.
避免死锁
预防死锁的几种策略, 会严重地损害系统性能. 因此在避免死锁时, 要施加较弱的限制, 从而获得 较满意的系统性能. 由于在避免死锁的策略中, 允许进程动态地申请资源. 因而, 系统在进行资源分配之前预先计算资源分配的安全性. 若此次分配不会导致系统进入不安全的状态, 则将资源分配给进程;否则, 进程等待. 其中最具有代表性的避免死锁算法是银行家算法.
银行家算法: 首先需要定义状态和安全状态的概念. 系统的状态是当前给进程分配的资源情况. 因此, 状态包含两个向量 Resource(系统中每种资源的总量) 和 Available(未分配给进程的每种资源的总量) 及两个矩阵 Claim(表示进程对资源的需求) 和 Allocation(表示当前分配给进程的资源). 安全状态是指至少有一个资源分配序列不会导致死锁. 当进程请求一组资源时, 假设同意该请求, 从而改变了系统的状态, 然后确定其结果是否还处于安全状态. 如果是, 同意这个请求;如果不是, 阻塞该进程知道同意该请求后系统状态仍然是安全的.
检测死锁
- 首先为每个进程和每个资源指定一个唯一的号码;
- 然后建立资源分配表和进程等待表.
死锁检测的工具
- Jstack 命令 jstack 是 java 虚拟机自带的一种堆栈跟踪工具.jstack 用于打印出给定的 java 进程 ID 或 core file 或远程调试服务的 Java 堆栈信息. Jstack 工具可以用于生成 java 虚拟机当前时刻的线程快照. 线程快照是当前 java 虚拟机内每一条线程正在执行的方法堆栈的集合, 生成线程快照的主要目的是定位线程出现长时间停顿的原因, 如线程间死锁, 死循环, 请求外部资源导致的长时间等待等. 线程出现停顿的时候通过 jstack 来查看各个线程的调用堆栈, 就可以知道没有响应的线程到底在后台做什么事情, 或者等待什么资源.
- JConsole 工具 Jconsole 是 JDK 自带的监控工具, 在 JDK/bin 目录下可以找到. 它用于连接正在运行的本地或者远程的 JVM, 对运行在 Java 应用程序的资源消耗和性能进行监控, 并画出大量的图表, 提供强大的可视化界面. 而且本身占用的服务器内存很小, 甚至可以说几乎不消耗.
解除死锁:
当发现有进程死锁后, 便应立即把它从死锁状态中解脱出来, 常采用的方法有:
- 剥夺资源: 从其它进程剥夺足够数量的资源给死锁进程, 以解除死锁状态;
- 撤消进程: 可以直接撤消死锁进程或撤消代价最小的进程, 直至有足够的资源可用, 死锁状态. 消除为止;所谓代价是指优先级, 运行代价, 进程的重要性和价值等.
原文链接: https://www.cnblogs.com/crazymakercircle/p/14323919.html
深拷贝和浅拷贝的区别
所谓深浅拷贝, 都是进行复制, 那么区别主要在于复制出来的新对象和原来的对象是否会互相影响, 改一个, 另一个也会变.
- 浅拷贝: 对于仅仅是复制了引用 (地址), 换句话说, 复制了之后, 原来的变量和新的变量指向同一个东西, 彼此之间的操作会互相影响, 为 浅拷贝.
- 深拷贝: 而如果是在堆中重新分配内存, 拥有不同的地址, 但是值是一样的, 复制后的对象与原来的对象是完全隔离, 互不影响, 为深拷贝.
深浅拷贝的主要区别就是: 复制的是引用 (地址) 还是复制的是实例.
https://github.com/YvetteLau/Step-By-Step/issues/17#issuecomment-497601234
Java 注释
感觉这个问题有点弱智:
- 注释了的代码坑定不会执行啊, 要是能执行那才是奇了怪了;
- 没有注释的代码不一定会执行的
if (1 == 2) {
System.out.println("never get here!");
}
你说这个会执行吗?
原文地址: Java 基础面试题-20211228
欢迎访问我的博客: http://blog.duhbb.com/