diff --git a/docs/.nojekyll b/.nojekyll similarity index 100% rename from docs/.nojekyll rename to .nojekyll diff --git a/docs/HomePage.md b/HomePage.md similarity index 64% rename from docs/HomePage.md rename to HomePage.md index 43c740363e2..43a338951fc 100644 --- a/docs/HomePage.md +++ b/HomePage.md @@ -1,55 +1,79 @@ -Java后端技术交流群(限工作一年及以上,架构视频免费领取) :[![QQ群](https://img.shields.io/badge/QQ%E7%BE%A4-869815609-red.svg)](https://jq.qq.com/?_wv=1027&k=5QqyxIx) - -点击订阅[Java面试进阶指南](https://xiaozhuanlan.com/javainterview?rel=javaguide)(专为Java面试方向准备)。[为什么要弄这个专栏?](https://shimo.im/docs/9BJjNsNg7S4dCnz3/) +点击订阅[Java面试进阶指南](https://xiaozhuanlan.com/javainterview?rel=javaguide)(专为Java面试方向准备)。[为什么要弄这个专栏?](https://shimo.im/./9BJjNsNg7S4dCnz3/)

Java 学习/面试指南

-

-

Special Sponsors

-

- - - -

+## 目录 + +- [Java](#java) + - [基础](#基础) + - [容器](#容器) + - [并发](#并发) + - [JVM](#jvm) + - [I/O](#io) + - [Java 8](#java-8) + - [编程规范](#编程规范) +- [网络](#网络) +- [操作系统](#操作系统) + - [Linux相关](#linux相关) +- [数据结构与算法](#数据结构与算法) + - [数据结构](#数据结构) + - [算法](#算法) +- [数据库](#数据库) + - [MySQL](#mysql) + - [Redis](#redis) +- [系统设计](#系统设计) + - [设计模式(工厂模式、单例模式 ... )](#设计模式) + - [常用框架(Spring、Zookeeper ... )](#常用框架) + - [数据通信(消息队列、Dubbo ... )](#数据通信) + - [网站架构](#网站架构) +- [面试指南](#面试指南) + - [备战面试](#备战面试) + - [常见面试题总结](#常见面试题总结) + - [面经](#面经) +- [工具](#工具) + - [Git](#git) + - [Docker](#Docker) +- [资料](#资料) + - [书单](#书单) + - [Github榜单](#Github榜单) +- [待办](#待办) +- [说明](#说明) ## Java ### 基础 * [Java 基础知识回顾](java/Java基础知识.md) +* [Java 基础知识疑难点总结](java/Java疑难点.md) * [J2EE 基础知识回顾](java/J2EE基础知识.md) -* [Collections 工具类和 Arrays 工具类常见方法](java/Basis/Arrays%2CCollectionsCommonMethods.md) -* [Java常见关键字总结:static、final、this、super](java/Basis/final、static、this、super.md) ### 容器 -* **常见问题总结:** - * [这几道Java集合框架面试题几乎必问](java/这几道Java集合框架面试题几乎必问.md) - * [Java 集合框架常见面试题总结](java/Java集合框架常见面试题总结.md) -* **源码分析:** - * [ArrayList 源码学习](java/ArrayList.md) - * [【面试必备】透过源码角度一步一步带你分析 ArrayList 扩容机制](java/ArrayList-Grow.md) - * [LinkedList 源码学习](java/LinkedList.md) - * [HashMap(JDK1.8)源码学习](java/HashMap.md) +* [Java容器常见面试题/知识点总结](java/collection/Java集合框架常见面试题.md) +* [ArrayList 源码学习](java/collection/ArrayList.md) +* [LinkedList 源码学习](java/collection/LinkedList.md) +* [HashMap(JDK1.8)源码学习](java/collection/HashMap.md) ### 并发 + * [Java 并发基础常见面试题总结](java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md) * [Java 并发进阶常见面试题总结](java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md) -* [并发编程面试必备:synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReentrantLock 的对比](java/synchronized.md) -* [并发编程面试必备:乐观锁与悲观锁](essential-content-for-interview/面试必备之乐观锁与悲观锁.md) -* [并发编程面试必备:JUC 中的 Atomic 原子类总结](java/Multithread/Atomic.md) -* [并发编程面试必备:AQS 原理以及 AQS 同步组件总结](java/Multithread/AQS.md) * [并发容器总结](java/Multithread/并发容器总结.md) +* [乐观锁与悲观锁](essential-content-for-interview/面试必备之乐观锁与悲观锁.md) +* [JUC 中的 Atomic 原子类总结](java/Multithread/Atomic.md) +* [AQS 原理以及 AQS 同步组件总结](java/Multithread/AQS.md) ### JVM - -* [可能是把Java内存区域讲的最清楚的一篇文章](java/可能是把Java内存区域讲的最清楚的一篇文章.md) -* [搞定JVM垃圾回收就是这么简单](java/搞定JVM垃圾回收就是这么简单.md) -* [《深入理解Java虚拟机》第2版学习笔记](java/Java虚拟机(jvm).md) +* [一 Java内存区域](java/jvm/Java内存区域.md) +* [二 JVM垃圾回收](java/jvm/JVM垃圾回收.md) +* [三 JDK 监控和故障处理工具](java/jvm/JDK监控和故障处理工具总结.md) +* [四 类文件结构](java/jvm/类文件结构.md) +* [五 类加载过程](java/jvm/类加载过程.md) +* [六 类加载器](java/jvm/类加载器.md) ### I/O @@ -76,7 +100,7 @@ Java后端技术交流群(限工作一年及以上,架构视频免费领取) ### Linux相关 * [后端程序员必备的 Linux 基础知识](operating-system/后端程序员必备的Linux基础知识.md) -* [Shell 编程入门](operating-system/Shell.md) +* [Shell 编程入门](operating-system/Shell.md) ## 数据结构与算法 @@ -87,8 +111,8 @@ Java后端技术交流群(限工作一年及以上,架构视频免费领取) ### 算法 - [算法学习资源推荐](dataStructures-algorithms/算法学习资源推荐.md) -- [算法总结——几道常见的子符串算法题 ](dataStructures-algorithms/几道常见的子符串算法题.md) -- [算法总结——几道常见的链表算法题 ](dataStructures-algorithms/几道常见的链表算法题.md) +- [几道常见的字符串算法题总结 ](dataStructures-algorithms/几道常见的子符串算法题.md) +- [几道常见的链表算法题总结 ](dataStructures-algorithms/几道常见的链表算法题.md) - [剑指offer部分编程题](dataStructures-algorithms/剑指offer部分编程题.md) - [公司真题](dataStructures-algorithms/公司真题.md) - [回溯算法经典案例之N皇后问题](dataStructures-algorithms/Backtracking-NQueens.md) @@ -100,7 +124,7 @@ Java后端技术交流群(限工作一年及以上,架构视频免费领取) * [MySQL 学习与面试](database/MySQL.md) * [一千行MySQL学习笔记](database/一千行MySQL命令.md) * [MySQL高性能优化规范建议](database/MySQL高性能优化规范建议.md) -* [搞定数据库索引就是这么简单](database/MySQL%20Index.md) +* [数据库索引总结](database/MySQL%20Index.md) * [事务隔离级别(图文详解)](database/事务隔离级别(图文详解).md) * [一条SQL语句在MySQL中如何执行的](database/一条sql语句在mysql中如何执行的.md) @@ -120,21 +144,24 @@ Java后端技术交流群(限工作一年及以上,架构视频免费领取) #### Spring -- [Spring 学习与面试](system-design/framework/Spring学习与面试.md) -- [Spring中bean的作用域与生命周期](system-design/framework/SpringBean.md) -- [SpringMVC 工作原理详解](system-design/framework/SpringMVC%20%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E8%AF%A6%E8%A7%A3.md) +- [Spring 学习与面试](system-design/framework/spring/Spring.md) +- [Spring 常见问题总结](system-design/framework/spring/SpringInterviewQuestions.md) +- [Spring中bean的作用域与生命周期](system-design/framework/spring/SpringBean.md) +- [SpringMVC 工作原理详解](system-design/framework/spring/SpringMVC-Principle.md) +- [Spring中都用到了那些设计模式?](system-design/framework/spring/Spring-Design-Patterns.md) #### ZooKeeper -- [可能是把 ZooKeeper 概念讲的最清楚的一篇文章](system-design/framework/ZooKeeper.md) -- [ZooKeeper 数据模型和常见命令了解一下,速度收藏!](system-design/framework/ZooKeeper数据模型和常见命令.md) +- [ZooKeeper 相关概念总结](system-design/framework/ZooKeeper.md) +- [ZooKeeper 数据模型和常见命令](system-design/framework/ZooKeeper数据模型和常见命令.md) ### 数据通信 -- [数据通信(RESTful、RPC、消息队列)相关知识点总结](system-design/data-communication/数据通信(RESTful、RPC、消息队列).md) +- [数据通信(RESTful、RPC、消息队列)相关知识点总结](system-design/data-communication/summary.md) - [Dubbo 总结:关于 Dubbo 的重要知识点](system-design/data-communication/dubbo.md) -- [消息队列总结:新手也能看懂,消息队列其实很简单](system-design/data-communication/message-queue.md) -- [一文搞懂 RabbitMQ 的重要概念以及安装](system-design/data-communication/rabbitmq.md) +- [消息队列总结](system-design/data-communication/message-queue.md) +- [RabbitMQ 入门](system-design/data-communication/rabbitmq.md) +- [RocketMQ的几个简单问题与答案](system-design/data-communication/RocketMQ-Questions.md) ### 网站架构 @@ -157,7 +184,7 @@ Java后端技术交流群(限工作一年及以上,架构视频免费领取) * [第一周(2018-8-7)](essential-content-for-interview/MostCommonJavaInterviewQuestions/第一周(2018-8-7).md) (为什么 Java 中只有值传递、==与equals、 hashCode与equals) * [第二周(2018-8-13)](essential-content-for-interview/MostCommonJavaInterviewQuestions/第二周(2018-8-13).md)(String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的?、什么是反射机制?反射机制的应用场景有哪些?......) -* [第三周(2018-08-22)](java/这几道Java集合框架面试题几乎必问.md) (Arraylist 与 LinkedList 异同、ArrayList 与 Vector 区别、HashMap的底层实现、HashMap 和 Hashtable 的区别、HashMap 的长度为什么是2的幂次方、HashSet 和 HashMap 区别、ConcurrentHashMap 和 Hashtable 的区别、ConcurrentHashMap线程安全的具体实现方式/底层具体实现、集合框架底层数据结构总结) +* [第三周(2018-08-22)](java/collection/Java集合框架常见面试题.md) (Arraylist 与 LinkedList 异同、ArrayList 与 Vector 区别、HashMap的底层实现、HashMap 和 Hashtable 的区别、HashMap 的长度为什么是2的幂次方、HashSet 和 HashMap 区别、ConcurrentHashMap 和 Hashtable 的区别、ConcurrentHashMap线程安全的具体实现方式/底层具体实现、集合框架底层数据结构总结) * [第四周(2018-8-30).md](essential-content-for-interview/MostCommonJavaInterviewQuestions/第四周(2018-8-30).md) (主要内容是几道面试常问的多线程基础题。) ### 面经 @@ -198,12 +225,6 @@ Java后端技术交流群(限工作一年及以上,架构视频免费领取) - [ ] Netty 总结(---正在进行中---) - [ ] 数据结构总结重构(---正在进行中---) -## 联系我 - -添加我的微信备注“Github”,回复关键字 **“加群”** 即可入群。 - -![我的微信](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-2/JavaGuide.jpg) - ## 公众号 - 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 diff --git a/README.md b/README.md index e0cc2b627cb..6050a14f5fc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -点击订阅[Java面试进阶指南](https://xiaozhuanlan.com/javainterview?rel=javaguide)(专为Java面试方向准备)。[为什么要弄这个专栏?](https://shimo.im/docs/9BJjNsNg7S4dCnz3/) +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 + +**[推荐一下阿里云双11的活动:云服务器1折起,仅86元/年,限量抢购!](https://www.aliyun.com/1111/2019/group-buying-share?ptCode=32AE103FC8249634736194795A3477C4647C88CF896EF535&userCode=hf47liqn&share_source=copy_link)** (仅限新人,老用户可以考虑使用家人或者朋友账号购买,推荐799/3年 2核4G 这个性价比和适用面更广。不懂怎么使用云服务器的朋友可以看这篇[阿里云服务器使用经验](docs/tools/阿里云服务器使用经验.md))。

@@ -12,19 +14,17 @@ 公众号 公众号 投稿 + 投稿

-

Special Sponsors

- +

Sponsor

- - - + + +

-推荐一下我的另外一个正在维护的项目:[programmer-advancement](https://github.com/Snailclimb/programmer-advancement) (技术人员成长必备!) - -推荐使用 在线阅读(访问速度慢的话,请使用 ),在线阅读内容本仓库同步一致。这种方式阅读的优势在于:有侧边栏阅读体验更好,Gitee pages 的访问速度相对来说也比较快。 +推荐使用 https://snailclimb.top/JavaGuide/ 在线阅读(访问速度慢的话,请使用 https://snailclimb.gitee.io/javaguide ),在线阅读内容本仓库同步一致。这种方式阅读的优势在于:有侧边栏阅读体验更好,Gitee pages 的访问速度相对来说也比较快。 ## 目录 @@ -35,7 +35,7 @@ - [JVM](#jvm) - [I/O](#io) - [Java 8](#java-8) - - [编程规范](#编程规范) + - [优雅 Java 代码必备实践(Java编程规范)](#优雅-java-代码必备实践java编程规范) - [网络](#网络) - [操作系统](#操作系统) - [Linux相关](#linux相关) @@ -46,18 +46,20 @@ - [MySQL](#mysql) - [Redis](#redis) - [系统设计](#系统设计) - - [设计模式](#设计模式) - - [常用框架](#常用框架) - - [数据通信](#数据通信) + - [常用框架(Spring/SpringBoot、Zookeeper ... )](#常用框架) + - [数据通信(消息队列、Dubbo ... )](#数据通信) + - [权限认证](#权限认证) + - [设计模式(工厂模式、单例模式 ... )](#设计模式) - [网站架构](#网站架构) - [面试指南](#面试指南) - [备战面试](#备战面试) - [常见面试题总结](#常见面试题总结) - [面经](#面经) +- [Java学习常见问题汇总](#java学习常见问题汇总) - [工具](#工具) - [Git](#git) - [Docker](#Docker) -- [资料](#资料) +- [资源](#资源) - [书单](#书单) - [Github榜单](#Github榜单) - [待办](#待办) @@ -67,37 +69,35 @@ ### 基础 -* [Java 基础知识回顾](docs/java/Java基础知识.md) +* **[Java 基础知识回顾](docs/java/Java基础知识.md)** +* **[Java 基础知识疑难点/易错点](docs/java/Java疑难点.md)** +* **[一些重要的Java程序设计题](docs/java/Java程序设计题.md)** * [J2EE 基础知识回顾](docs/java/J2EE基础知识.md) -* [Collections 工具类和 Arrays 工具类常见方法](docs/java/Basis/Arrays%2CCollectionsCommonMethods.md) -* [Java常见关键字总结:static、final、this、super](docs/java/Basis/final、static、this、super.md) ### 容器 -* **常见问题总结:** - * [这几道Java集合框架面试题几乎必问](docs/java/这几道Java集合框架面试题几乎必问.md) - * [Java 集合框架常见面试题总结](docs/java/Java集合框架常见面试题总结.md) -* **源码分析:** - * [ArrayList 源码学习](docs/java/ArrayList.md) - * [【面试必备】透过源码角度一步一步带你分析 ArrayList 扩容机制](docs/java/ArrayList-Grow.md) - * [LinkedList 源码学习](docs/java/LinkedList.md) - * [HashMap(JDK1.8)源码学习](docs/java/HashMap.md) +* **[Java容器常见面试题/知识点总结](docs/java/collection/Java集合框架常见面试题.md)** +* [ArrayList 源码学习](docs/java/collection/ArrayList.md) +* [LinkedList 源码学习](docs/java/collection/LinkedList.md) +* [HashMap(JDK1.8)源码学习](docs/java/collection/HashMap.md) ### 并发 -* [Java 并发基础常见面试题总结](docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md) -* [Java 并发进阶常见面试题总结](docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md) -* [并发编程面试必备:synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReentrantLock 的对比](docs/java/synchronized.md) -* [并发编程面试必备:乐观锁与悲观锁](docs/essential-content-for-interview/面试必备之乐观锁与悲观锁.md) -* [并发编程面试必备:JUC 中的 Atomic 原子类总结](docs/java/Multithread/Atomic.md) -* [并发编程面试必备:AQS 原理以及 AQS 同步组件总结](docs/java/Multithread/AQS.md) +* **[Java 并发基础常见面试题总结](docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md)** +* **[Java 并发进阶常见面试题总结](docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md)** * [并发容器总结](docs/java/Multithread/并发容器总结.md) +* [乐观锁与悲观锁](docs/essential-content-for-interview/面试必备之乐观锁与悲观锁.md) +* [JUC 中的 Atomic 原子类总结](docs/java/Multithread/Atomic.md) +* [AQS 原理以及 AQS 同步组件总结](docs/java/Multithread/AQS.md) ### JVM -* [可能是把Java内存区域讲的最清楚的一篇文章](docs/java/可能是把Java内存区域讲的最清楚的一篇文章.md) -* [搞定JVM垃圾回收就是这么简单](docs/java/搞定JVM垃圾回收就是这么简单.md) -* [《深入理解Java虚拟机》第2版学习笔记](docs/java/Java虚拟机(jvm).md) +* **[一 Java内存区域](docs/java/jvm/Java内存区域.md)** +* **[二 JVM垃圾回收](docs/java/jvm/JVM垃圾回收.md)** +* [三 JDK 监控和故障处理工具](docs/java/jvm/JDK监控和故障处理工具总结.md) +* [四 类文件结构](docs/java/jvm/类文件结构.md) +* **[五 类加载过程](docs/java/jvm/类加载过程.md)** +* [六 类加载器](docs/java/jvm/类加载器.md) ### I/O @@ -108,10 +108,11 @@ * [Java 8 新特性总结](docs/java/What's%20New%20in%20JDK8/Java8Tutorial.md) * [Java 8 学习资源推荐](docs/java/What's%20New%20in%20JDK8/Java8教程推荐.md) +* [Java8 forEach 指南](docs/java/What's%20New%20in%20JDK8/Java8foreach指南.md) -### 编程规范 +### 优雅 Java 代码必备实践(Java编程规范) -- [Java 编程规范](docs/java/Java编程规范.md) +* [Java 编程规范以及优雅Java代码实践总结](docs/java/Java编程规范.md) ## 网络 @@ -124,7 +125,7 @@ ### Linux相关 * [后端程序员必备的 Linux 基础知识](docs/operating-system/后端程序员必备的Linux基础知识.md) -* [Shell 编程入门](docs/operating-system/Shell.md) +* [Shell 编程入门](docs/operating-system/Shell.md) ## 数据结构与算法 @@ -135,8 +136,8 @@ ### 算法 - [算法学习资源推荐](docs/dataStructures-algorithms/算法学习资源推荐.md) -- [算法总结——几道常见的子符串算法题 ](docs/dataStructures-algorithms/几道常见的子符串算法题.md) -- [算法总结——几道常见的链表算法题 ](docs/dataStructures-algorithms/几道常见的链表算法题.md) +- [几道常见的字符串算法题总结 ](docs/dataStructures-algorithms/几道常见的子符串算法题.md) +- [几道常见的链表算法题总结 ](docs/dataStructures-algorithms/几道常见的链表算法题.md) - [剑指offer部分编程题](docs/dataStructures-algorithms/剑指offer部分编程题.md) - [公司真题](docs/dataStructures-algorithms/公司真题.md) - [回溯算法经典案例之N皇后问题](docs/dataStructures-algorithms/Backtracking-NQueens.md) @@ -145,10 +146,11 @@ ### MySQL -* [MySQL 学习与面试](docs/database/MySQL.md) -* [一千行MySQL学习笔记](docs/database/一千行MySQL命令.md) +* **[【推荐】MySQL/数据库 知识点总结](docs/database/MySQL.md)** +* **[阿里巴巴开发手册数据库部分的一些最佳实践](docs/database/阿里巴巴开发手册数据库部分的一些最佳实践.md)** +* **[一千行MySQL学习笔记](docs/database/一千行MySQL命令.md)** * [MySQL高性能优化规范建议](docs/database/MySQL高性能优化规范建议.md) -* [搞定数据库索引就是这么简单](docs/database/MySQL%20Index.md) +* [数据库索引总结](docs/database/MySQL%20Index.md) * [事务隔离级别(图文详解)](docs/database/事务隔离级别(图文详解).md) * [一条SQL语句在MySQL中如何执行的](docs/database/一条sql语句在mysql中如何执行的.md) @@ -160,29 +162,47 @@ ## 系统设计 -### 设计模式 - -- [设计模式系列文章](docs/system-design/设计模式.md) - ### 常用框架 -#### Spring +#### Spring/SpringBoot -- [Spring 学习与面试](docs/system-design/framework/Spring学习与面试.md) -- [Spring中bean的作用域与生命周期](docs/system-design/framework/SpringBean.md) -- [SpringMVC 工作原理详解](docs/system-design/framework/SpringMVC%20%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E8%AF%A6%E8%A7%A3.md) +- [Spring 学习与面试](docs/system-design/framework/spring/Spring.md) +- **[Spring 常见问题总结](docs/system-design/framework/spring/SpringInterviewQuestions.md)** +- **[SpringBoot 指南/常见面试题总结](https://github.com/Snailclimb/springboot-guide)** +- [Spring中bean的作用域与生命周期](docs/system-design/framework/spring/SpringBean.md) +- [SpringMVC 工作原理详解](docs/system-design/framework/spring/SpringMVC-Principle.md) +- [Spring中都用到了那些设计模式?](docs/system-design/framework/spring/Spring-Design-Patterns.md) #### ZooKeeper -- [可能是把 ZooKeeper 概念讲的最清楚的一篇文章](docs/system-design/framework/ZooKeeper.md) -- [ZooKeeper 数据模型和常见命令了解一下,速度收藏!](docs/system-design/framework/ZooKeeper数据模型和常见命令.md) +- [ZooKeeper 相关概念总结](docs/system-design/framework/ZooKeeper.md) +- [ZooKeeper 数据模型和常见命令](docs/system-design/framework/ZooKeeper数据模型和常见命令.md) ### 数据通信 -- [数据通信(RESTful、RPC、消息队列)相关知识点总结](docs/system-design/data-communication/数据通信(RESTful、RPC、消息队列).md) +- [数据通信(RESTful、RPC、消息队列)相关知识点总结](docs/system-design/data-communication/summary.md) + +#### Dubbo + - [Dubbo 总结:关于 Dubbo 的重要知识点](docs/system-design/data-communication/dubbo.md) -- [消息队列总结:新手也能看懂,消息队列其实很简单](docs/system-design/data-communication/message-queue.md) -- [一文搞懂 RabbitMQ 的重要概念以及安装](docs/system-design/data-communication/rabbitmq.md) + +#### 消息中间件 + +- [消息队列总结](docs/system-design/data-communication/message-queue.md) +- [RabbitMQ 入门](docs/system-design/data-communication/rabbitmq.md) +- [RocketMQ的几个简单问题与答案](docs/system-design/data-communication/RocketMQ-Questions.md) +- [Kafka入门看这一篇就够了](docs/system-design/data-communication/Kafka入门看这一篇就够了.md) +- [Kafka系统设计开篇-面试看这篇就够了](docs/system-design/data-communication/Kafka系统设计开篇-面试看这篇就够了.md) + +### 权限认证 + +- **[权限认证基础:区分Authentication,Authorization以及Cookie、Session、Token](docs/system-design/authority-certification/basis-of-authority-certification.md)** +- **[JWT 优缺点分析以及常见问题解决方案](docs/system-design/authority-certification/JWT-advantages-and-disadvantages.md)** +- **[适合初学者入门 Spring Security With JWT 的 Demo](https://github.com/Snailclimb/spring-security-jwt-guide)** + +### 设计模式 + +- [设计模式系列文章](docs/system-design/设计模式.md) ### 网站架构 @@ -194,18 +214,19 @@ ### 备战面试 -* [【备战面试1】程序员的简历就该这样写](docs/essential-content-for-interview/PreparingForInterview/程序员的简历之道.md) -* [【备战面试2】初出茅庐的程序员该如何准备面试?](docs/essential-content-for-interview/PreparingForInterview/interviewPrepare.md) -* [【备战面试3】7个大部分程序员在面试前很关心的问题](docs/essential-content-for-interview/PreparingForInterview/JavaProgrammerNeedKnow.md) -* [【备战面试4】Github上开源的Java面试/学习相关的仓库推荐](docs/essential-content-for-interview/PreparingForInterview/JavaInterviewLibrary.md) -* [【备战面试5】如果面试官问你“你有什么问题问我吗?”时,你该如何回答](docs/essential-content-for-interview/PreparingForInterview/如果面试官问你“你有什么问题问我吗?”时,你该如何回答.md) -* [【备战面试6】美团面试常见问题总结(附详解答案)](docs/essential-content-for-interview/PreparingForInterview/美团面试常见问题总结.md) +* **[【备战面试1】程序员的简历就该这样写](docs/essential-content-for-interview/PreparingForInterview/程序员的简历之道.md)** +* **[【备战面试2】初出茅庐的程序员该如何准备面试?](docs/essential-content-for-interview/PreparingForInterview/interviewPrepare.md)** +* **[【备战面试3】7个大部分程序员在面试前很关心的问题](docs/essential-content-for-interview/PreparingForInterview/JavaProgrammerNeedKnow.md)** +* **[【备战面试4】Github上开源的Java面试/学习相关的仓库推荐](docs/essential-content-for-interview/PreparingForInterview/JavaInterviewLibrary.md)** +* **[【备战面试5】如果面试官问你“你有什么问题问我吗?”时,你该如何回答](docs/essential-content-for-interview/PreparingForInterview/如果面试官问你"你有什么问题问我吗?"时,你该如何回答.md)** +* **[【备战面试6】美团面试常见问题总结(附详解答案)](docs/essential-content-for-interview/PreparingForInterview/美团面试常见问题总结.md)** +* **[【备战面试7】一些刁难的面试问题总结](https://xiaozhuanlan.com/topic/9056431872)** ### 常见面试题总结 * [第一周(2018-8-7)](docs/essential-content-for-interview/MostCommonJavaInterviewQuestions/第一周(2018-8-7).md) (为什么 Java 中只有值传递、==与equals、 hashCode与equals) * [第二周(2018-8-13)](docs/essential-content-for-interview/MostCommonJavaInterviewQuestions/第二周(2018-8-13).md)(String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的?、什么是反射机制?反射机制的应用场景有哪些?......) -* [第三周(2018-08-22)](docs/java/这几道Java集合框架面试题几乎必问.md) (Arraylist 与 LinkedList 异同、ArrayList 与 Vector 区别、HashMap的底层实现、HashMap 和 Hashtable 的区别、HashMap 的长度为什么是2的幂次方、HashSet 和 HashMap 区别、ConcurrentHashMap 和 Hashtable 的区别、ConcurrentHashMap线程安全的具体实现方式/底层具体实现、集合框架底层数据结构总结) +* [第三周(2018-08-22)](docs/java/collection/Java集合框架常见面试题.md) (Arraylist 与 LinkedList 异同、ArrayList 与 Vector 区别、HashMap的底层实现、HashMap 和 Hashtable 的区别、HashMap 的长度为什么是2的幂次方、HashSet 和 HashMap 区别、ConcurrentHashMap 和 Hashtable 的区别、ConcurrentHashMap线程安全的具体实现方式/底层具体实现、集合框架底层数据结构总结) * [第四周(2018-8-30).md](docs/essential-content-for-interview/MostCommonJavaInterviewQuestions/第四周(2018-8-30).md) (主要内容是几道面试常问的多线程基础题。) ### 面经 @@ -214,6 +235,11 @@ - [蚂蚁金服2019实习生面经总结(已拿口头offer)](docs/essential-content-for-interview/BATJrealInterviewExperience/蚂蚁金服实习生面经总结(已拿口头offer).md) - [2019年蚂蚁金服、头条、拼多多的面试总结](docs/essential-content-for-interview/BATJrealInterviewExperience/2019alipay-pinduoduo-toutiao.md) +## Java学习常见问题汇总 + +- [Java学习路线和方法推荐](docs/questions/java-learning-path-and-methods.md) +- [Java培训四个月能学会吗?](docs/questions/java-training-4-month.md) + ## 工具 ### Git @@ -222,16 +248,24 @@ ### Docker -* [Docker 入门](docs/tools/Docker.md) +* [Docker 基本概念解读](docs/tools/Docker.md) * [一文搞懂 Docker 镜像的常用操作!](docs/tools/Docker-Image.md) -## 资料 +### 其他 + +- [阿里云服务器使用经验](docs/tools/阿里云服务器使用经验.md) + +## 资源 ### 书单 - [Java程序员必备书单](docs/data/java-recommended-books.md) -### Github榜单 +### 实战项目推荐 + +- [Github 上热门的 Spring Boot 项目实战推荐](docs/data/spring-boot-practical-projects.md) + +### Github 历史榜单 - [Java 项目月榜单](docs/github-trending/JavaGithubTrending.md) @@ -239,16 +273,13 @@ ## 待办 -- [x] [Java 8 新特性总结](docs/java/What's%20New%20in%20JDK8/Java8Tutorial.md) -- [x] [Java 8 新特性详解](docs/java/What's%20New%20in%20JDK8/Java8教程推荐.md) - [ ] Java 多线程类别知识重构(---正在进行中---) -- [x] [BIO,NIO,AIO 总结 ](docs/java/BIO-NIO-AIO.md) - [ ] Netty 总结(---正在进行中---) - [ ] 数据结构总结重构(---正在进行中---) ## 说明 -### 介绍 +### JavaGuide介绍 * **对于 Java 初学者来说:** 本文档倾向于给你提供一个比较详细的学习路径,让你对于Java整体的知识体系有一个初步认识。另外,本文的一些文章 也是你学习和复习 Java 知识不错的实践; @@ -258,6 +289,12 @@ Markdown 格式参考:[Github Markdown格式](https://guides.github.com/featur 利用 docsify 生成文档部署在 Github pages: [docsify 官网介绍](https://docsify.js.org/#/) +### 作者的其他开源项目推荐 + +1. [springboot-guide](https://github.com/Snailclimb/springboot-guide) : 适合新手入门以及有经验的开发人员查阅的 Spring Boot 教程(业余时间维护中,欢迎一起维护)。 +2. [programmer-advancement](https://github.com/Snailclimb/programmer-advancement) : 我觉得技术人员应该有的一些好习惯! +3. [spring-security-jwt-guide](https://github.com/Snailclimb/spring-security-jwt-guide) :从零入门 !Spring Security With JWT(含权限验证)后端部分代码。 + ### 关于转载 如果你需要转载本仓库的一些文章到自己的博客的话,记得注明原文地址就可以了。 @@ -280,16 +317,22 @@ Markdown 格式参考:[Github Markdown格式](https://guides.github.com/featur 添加我的微信备注“Github”,回复关键字 **“加群”** 即可入群。 -![我的微信](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-2/JavaGuide.jpg) +![个人微信](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/wechat3.jpeg) ### Contributor 下面是笔主收集的一些对本仓库提过有价值的pr或者issue的朋友,人数较多,如果你也对本仓库提过不错的pr或者issue的话,你可以加我的微信与我联系。下面的排名不分先后! - - + + + + + + + + @@ -323,6 +366,12 @@ Markdown 格式参考:[Github Markdown格式](https://guides.github.com/featur + + + + + + ### 公众号 @@ -330,6 +379,6 @@ Markdown 格式参考:[Github Markdown格式](https://guides.github.com/featur **《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! -**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。 +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 -![我的公众号](https://user-gold-cdn.xitu.io/2018/11/28/167598cd2e17b8ec?w=258&h=258&f=jpeg&s=27334) +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/_coverpage.md b/_coverpage.md similarity index 81% rename from docs/_coverpage.md rename to _coverpage.md index 612cd2160ec..9e45fbab3cf 100644 --- a/docs/_coverpage.md +++ b/_coverpage.md @@ -8,3 +8,6 @@ [GitHub]() [开始阅读](#java) +![](./media/pictures/rostyslav-savchyn-5joK905gcGc-unsplash.jpg) + + diff --git a/docs/data/java-recommended-books.md b/docs/data/java-recommended-books.md index 7f387ce46ae..7bdadce50be 100644 --- a/docs/data/java-recommended-books.md +++ b/docs/data/java-recommended-books.md @@ -27,6 +27,7 @@ - [《Java 核心技术卷 1+卷 2》](https://book.douban.com/subject/25762168/)(推荐): 很棒的两本书,建议有点 Java 基础之后再读,介绍的还是比较深入的,非常推荐。这两本书我一般也会用来巩固知识点,是两本适合放在自己身边的好书。 - [《JAVA 网络编程 第 4 版》](https://book.douban.com/subject/26259017/): 可以系统的学习一下网络的一些概念以及网络编程在 Java 中的使用。 - [《Java 编程思想 (第 4 版)》](https://book.douban.com/subject/2130190/)(推荐,豆瓣评分 9.1,3.2K+人评价):大部分人称之为Java领域的圣经,但我不推荐初学者阅读,有点劝退的味道。稍微有点基础后阅读更好。 +- [《Java性能权威指南》](https://book.douban.com/subject/26740520/)(推荐,豆瓣评分 8.2,0.1K+人评价):O'Reilly 家族书,性能调优的入门书,我个人觉得性能调优是每个 Java 从业者必备知识,这本书的缺点就是太老了,但是这本书可以作为一个实战书,尤其是 JVM 调优!不适合初学者。前置书籍:《深入理解 Java 虚拟机》 ### 并发 @@ -81,8 +82,9 @@ ### 设计模式 -- [《设计模式 : 可复用面向对象软件的基础》 ](https://book.douban.com/subject/1052241/) (推荐,豆瓣评分 9.1):设计模式的经典! +- [《设计模式 : 可复用面向对象软件的基础》](https://book.douban.com/subject/1052241/) (推荐,豆瓣评分 9.1):设计模式的经典! - [《Head First 设计模式(中文版)》](https://book.douban.com/subject/2243615/) (推荐,豆瓣评分 9.2):相当赞的一本设计模式入门书籍。用实际的编程案例讲解算法设计中会遇到的各种问题和需求变更(对的,连需求变更都考虑到了!),并以此逐步推导出良好的设计模式解决办法。 +- [《大话设计模式》](https://book.douban.com/subject/2334288/) (推荐,豆瓣评分 8.3):本书通篇都是以情景对话的形式,用多个小故事或编程示例来组织讲解GOF(即《设计模式 : 可复用面向对象软件的基础》这本书)),但是不像《设计模式 : 可复用面向对象软件的基础》难懂。但是设计模式只看书是不够的,还是需要在实际项目中运用,结合[设计模式](docs/system-design/设计模式.md)更佳! ### 常用框架 @@ -93,6 +95,7 @@ - [《RabbitMQ 实战指南》](https://book.douban.com/subject/27591386/):《RabbitMQ 实战指南》从消息中间件的概念和 RabbitMQ 的历史切入,主要阐述 RabbitMQ 的安装、使用、配置、管理、运维、原理、扩展等方面的细节。如果你想浅尝 RabbitMQ 的使用,这本书是你最好的选择;如果你想深入 RabbitMQ 的原理,这本书也是你最好的选择;总之,如果你想玩转 RabbitMQ,这本书一定是最值得看的书之一 - [《Spring Cloud 微服务实战》](https://book.douban.com/subject/27025912/):从时下流行的微服务架构概念出发,详细介绍了 Spring Cloud 针对微服务架构中几大核心要素的解决方案和基础组件。对于各个组件的介绍,《Spring Cloud 微服务实战》主要以示例与源码结合的方式来帮助读者更好地理解这些组件的使用方法以及运行原理。同时,在介绍的过程中,还包含了作者在实践中所遇到的一些问题和解决思路,可供读者在实践中作为参考。 - [《第一本 Docker 书》](https://book.douban.com/subject/26780404/):Docker 入门书籍! +- [《Spring Boot编程思想(核心篇)》](https://book.douban.com/subject/33390560/)(推荐,豆瓣评分 6.2):SpringBoot深入书,不适合初学者。书尤其的厚,评分低的的理由是书某些知识过于拖沓,评分高的理由是书中对SpringBoot内部原理讲解很清楚。作者小马哥:Apache Dubbo PMC、Spring Cloud Alibaba项目架构师。B站作者地址:https://space.bilibili.com/327910845?from=search&seid=17095917016893398636。 ### 网站架构 @@ -107,6 +110,7 @@ ## 其他 - [《黑客与画家》](https://read.douban.com/ebook/387525/?dcs=subject-rec&dcm=douban&dct=2243615):这本书是硅谷创业之父,Y Combinator 创始人 Paul Graham 的文集。之所以叫这个名字,是因为作者认为黑客(并非负面的那个意思)与画家有着极大的相似性,他们都是在创造,而不是完成某个任务。 +- [《图解密码技术》](https://book.douban.com/subject/26265544/)(推荐,豆瓣评分 9.1,0.3K+人评价):本书以**图配文**的形式,第一部分讲述了密码技术的历史沿革、对称密码、分组密码模式(包括ECB、CBC、CFB、OFB、CTR)、公钥、混合密码系统。第二部分重点介绍了认证方面的内容,涉及单向散列函数、消息认证码、数字签名、证书等。第三部分讲述了密钥、随机数、PGP、SSL/TLS 以及密码技术在现实生活中的应用。关键字:JWT 前置知识、区块链密码技术前置知识。属于密码知识入门书籍。 diff --git a/docs/data/spring-boot-practical-projects.md b/docs/data/spring-boot-practical-projects.md new file mode 100644 index 00000000000..046af88a271 --- /dev/null +++ b/docs/data/spring-boot-practical-projects.md @@ -0,0 +1,66 @@ +最近经常被读者问到有没有 Spring Boot 实战项目可以学习,于是,我就去 Github 上找了 10 个我觉得还不错的实战项目。对于这些实战项目,有部分是比较适合 Spring Boot 刚入门的朋友学习的,还有一部分可能要求你对 Spring Boot 相关技术比较熟悉。需要的朋友可以根据个人实际情况进行选择。如果你对 Spring Boot 不太熟悉的话,可以看我最近开源的 springboot-guide:https://github.com/Snailclimb/springboot-guide 入门(还在持续更新中)。 + +### mall + +- **Github地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) +- **star**: 22.9k +- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 + +### jeecg-boot + +- **Github地址**:[https://github.com/zhangdaiscott/jeecg-boot](https://github.com/zhangdaiscott/jeecg-boot) +- **star**: 6.4k +- **介绍**: 一款基于代码生成器的JAVA快速开发平台!采用最新技术,前后端分离架构:SpringBoot 2.x,Ant Design&Vue,Mybatis,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任何代码,绝对是全栈开发福音!! JeecgBoot的宗旨是提高UI能力的同时,降低前后分离的开发成本,JeecgBoot还独创在线开发模式,No代码概念,一系列在线智能开发:在线配置表单、在线配置报表、在线设计流程等等。 + +### eladmin + +- **Github地址**:[https://github.com/elunez/eladmin](https://github.com/elunez/eladmin) +- **star**: 3.9k +- **介绍**: 项目基于 Spring Boot 2.1.0 、 Jpa、 Spring Security、redis、Vue的前后端分离的后台管理系统,项目采用分模块开发方式, 权限控制采用 RBAC,支持数据字典与数据权限管理,支持一键生成前后端代码,支持动态路由。 + +### paascloud-master + +- **Github地址**:[https://github.com/paascloud/paascloud-master](https://github.com/paascloud/paascloud-master) +- **star**: 5.9k +- **介绍**: spring cloud + vue + oAuth2.0全家桶实战,前后端分离模拟商城,完整的购物流程、后端运营平台,可以实现快速搭建企业级微服务项目。支持微信登录等三方登录。 + +### vhr + +- **Github地址**:[https://github.com/lenve/vhr](https://github.com/lenve/vhr) +- **star**: 10.6k +- **介绍**: 微人事是一个前后端分离的人力资源管理系统,项目采用SpringBoot+Vue开发。 + +### One mall + +- **Github地址**:[https://github.com/YunaiV/onemall](https://github.com/YunaiV/onemall) +- **star**: 1.2k +- **介绍**: mall 商城,基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。 + +### Guns + +- **Github地址**:[https://github.com/stylefeng/Guns](https://github.com/stylefeng/Guns) +- **star**: 2.3k +- **介绍**: Guns基于SpringBoot 2,致力于做更简洁的后台管理系统,完美整合springmvc + shiro + mybatis-plus + beetl!Guns项目代码简洁,注释丰富,上手容易,同时Guns包含许多基础模块(用户管理,角色管理,部门管理,字典管理等10个模块),可以直接作为一个后台管理系统的脚手架! + +### SpringCloud + +- **Github地址**:[https://github.com/YunaiV/onemall](https://github.com/YunaiV/onemall) +- **star**: 1.2k +- **介绍**: mall 商城,基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。 + +### SpringBoot-Shiro-Vue + +- **Github地址**:[https://github.com/Heeexy/SpringBoot-Shiro-Vue](https://github.com/Heeexy/SpringBoot-Shiro-Vue) +- **star**: 1.8k +- **介绍**: 提供一套基于Spring Boot-Shiro-Vue的权限管理思路.前后端都加以控制,做到按钮/接口级别的权限。 + +### newbee-mall + +最近开源的一个商城项目。 + +- **Github地址**:[https://github.com/newbee-ltd/newbee-mall](https://github.com/newbee-ltd/newbee-mall) +- **star**: 50 +- **介绍**: newbee-mall 项目是一套电商系统,包括 newbee-mall 商城系统及 newbee-mall-admin 商城后台管理系统,基于 Spring Boot 2.X 及相关技术栈开发。 前台商城系统包含首页门户、商品分类、新品上线、首页轮播、商品推荐、商品搜索、商品展示、购物车、订单结算、订单流程、个人订单管理、会员中心、帮助中心等模块。 后台管理系统包含数据面板、轮播图管理、商品管理、订单管理、会员管理、分类管理、设置等模块。 + + + diff --git "a/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\345\255\220\347\254\246\344\270\262\347\256\227\346\263\225\351\242\230.md" "b/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\345\255\220\347\254\246\344\270\262\347\256\227\346\263\225\351\242\230.md" index 938f4a48f1a..8489082b2f3 100644 --- "a/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\345\255\220\347\254\246\344\270\262\347\256\227\346\263\225\351\242\230.md" +++ "b/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\345\255\220\347\254\246\344\270\262\347\256\227\346\263\225\351\242\230.md" @@ -112,7 +112,7 @@ public class Main { public static String replaceSpace(String[] strs) { // 如果检查值不合法及就返回空串 - if (!chechStrs(strs)) { + if (!checkStrs(strs)) { return ""; } // 数组长度 @@ -137,7 +137,6 @@ public class Main { private static boolean chechStrs(String[] strs) { boolean flag = false; - // 注意:=是赋值,==是判断 if (strs != null) { // 遍历strs检查元素值 for (int i = 0; i < strs.length; i++) { @@ -145,6 +144,7 @@ public class Main { flag = true; } else { flag = false; + break; } } } @@ -463,7 +463,7 @@ public class Main { return 0; } } - return flag == 1 ? res : -res; + return flag != 2 ? res : -res; } diff --git "a/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" "b/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" index 79b74441deb..85e2934e407 100644 --- "a/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" +++ "b/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" @@ -225,7 +225,7 @@ public class Solution { while (node1 != null) { node1 = node1.next; count++; - if (k < 1 && node1 != null) { + if (k < 1) { node2 = node2.next; } k--; diff --git "a/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" index 6bb19abaaa1..dfb5bc18586 100644 --- "a/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" +++ "b/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" @@ -47,7 +47,7 @@ Queue 用来存放 等待处理元素 的集合,这种场景一般用于缓冲 ### 什么是 Set Set 继承于 Collection 接口,是一个不允许出现重复元素,并且无序的集合,主要 HashSet 和 TreeSet 两大实现类。 -在判断重复元素的时候,Set 集合会调用 hashCode()和 equal()方法来实现。 +在判断重复元素的时候,HashSet 集合会调用 hashCode()和 equal()方法来实现;TreeSet 集合会调用compareTo方法来实现。 ### 补充:有序集合与无序集合说明 - 有序集合:集合里的元素可以根据 key 或 index 访问 (List、Map) @@ -83,8 +83,8 @@ Set 继承于 Collection 接口,是一个不允许出现重复元素,并且 ### ArrayList 和 LinkedList 源码学习 -- [ArrayList 源码学习](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/ArrayList.md) -- [LinkedList 源码学习](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/LinkedList.md) +- [ArrayList 源码学习](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/ArrayList.md) +- [LinkedList 源码学习](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/LinkedList.md) ### 推荐阅读 @@ -104,7 +104,7 @@ Set 继承于 Collection 接口,是一个不允许出现重复元素,并且 (1)[完全二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。 - (2)[满二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。 + (2)[满二叉树](https://baike.baidu.com/item/%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91)——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。 (3)[平衡二叉树](https://baike.baidu.com/item/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91/10421057)——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。 diff --git a/docs/database/MySQL Index.md b/docs/database/MySQL Index.md index e2e9dc534db..b25589fc7d2 100644 --- a/docs/database/MySQL Index.md +++ b/docs/database/MySQL Index.md @@ -70,7 +70,7 @@ select * from user where city=xx ; // 无法命中索引 冗余索引指的是索引的功能相同,能够命中 就肯定能命中 ,那么 就是冗余索引如(name,city )和(name )这两个索引就是冗余索引,能够命中后者的查询肯定是能够命中前者的 在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。 -MySQLS.7 版本后,可以通过查询 sys 库的 `schema_redundant_indexes` 表来查看冗余索引 +MySQL 5.7 版本后,可以通过查询 sys 库的 `schema_redundant_indexes` 表来查看冗余索引 ### Mysql如何为表字段添加索引??? diff --git a/docs/database/MySQL.md b/docs/database/MySQL.md index 2351f825743..64dfad2fd79 100644 --- a/docs/database/MySQL.md +++ b/docs/database/MySQL.md @@ -1,171 +1,328 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 + +- [书籍推荐](#书籍推荐) +- [文字教程推荐](#文字教程推荐) +- [视频教程推荐](#视频教程推荐) +- [常见问题总结](#常见问题总结) + - [什么是MySQL?](#什么是mysql) + - [存储引擎](#存储引擎) + - [一些常用命令](#一些常用命令) + - [MyISAM和InnoDB区别](#myisam和innodb区别) + - [字符集及校对规则](#字符集及校对规则) + - [索引](#索引) + - [查询缓存的使用](#查询缓存的使用) + - [什么是事务?](#什么是事务) + - [事物的四大特性(ACID)](#事物的四大特性acid) + - [并发事务带来哪些问题?](#并发事务带来哪些问题) + - [事务隔离级别有哪些?MySQL的默认隔离级别是?](#事务隔离级别有哪些mysql的默认隔离级别是) + - [锁机制与InnoDB锁算法](#锁机制与innodb锁算法) + - [大表优化](#大表优化) + - [1. 限定数据的范围](#1-限定数据的范围) + - [2. 读/写分离](#2-读写分离) + - [3. 垂直分区](#3-垂直分区) + - [4. 水平分区](#4-水平分区) + - [一条SQL语句在MySQL中如何执行的](#一条sql语句在mysql中如何执行的) + - [MySQL高性能优化规范建议](#mysql高性能优化规范建议) + - [一条SQL语句执行得很慢的原因有哪些?](#一条sql语句执行得很慢的原因有哪些) + + + +## 书籍推荐 + +- 《SQL基础教程(第2版)》 (入门级) +- 《高性能MySQL : 第3版》 (进阶) + +## 文字教程推荐 + +- [SQL Tutorial](https://www.w3schools.com/sql/default.asp) (SQL语句学习,英文)、[SQL Tutorial](https://www.w3school.com.cn/sql/index.asp)(SQL语句学习,中文)、[SQL语句在线练习](https://www.w3schools.com/sql/exercise.asp) (非常不错) +- [Github-MySQL入门教程(MySQL tutorial book)](https://github.com/jaywcjlove/mysql-tutorial) (从零开始学习MySQL,主要是面向MySQL数据库管理系统初学者) +- [官方教程](https://dev.mysql.com/doc/refman/5.7/) +- [MySQL 教程(菜鸟教程)](http://www.runoob.com/MySQL/MySQL-tutorial.html) + +## 相关资源推荐 + +- [中国5级行政区域mysql库](https://github.com/kakuilan/china_area_mysql) + +## 视频教程推荐 +**基础入门:** [与MySQL的零距离接触-慕课网](https://www.imooc.com/learn/122) -Java面试通关手册(Java学习指南,欢迎Star,会一直完善下去,欢迎建议和指导):[https://github.com/Snailclimb/Java_Guide](https://github.com/Snailclimb/Java_Guide) +**MySQL开发技巧:** [MySQL开发技巧(一)](https://www.imooc.com/learn/398)  [MySQL开发技巧(二)](https://www.imooc.com/learn/427)  [MySQL开发技巧(三)](https://www.imooc.com/learn/449) -> ## 书籍推荐 +**MySQL5.7新特性及相关优化技巧:** [MySQL5.7版本新特性](https://www.imooc.com/learn/533)  [性能优化之MySQL优化](https://www.imooc.com/learn/194) -**《高性能MySQL : 第3版》** +[MySQL集群(PXC)入门](https://www.imooc.com/learn/993)  [MyCAT入门及应用](https://www.imooc.com/learn/951) -> ## 文字教程推荐 +## 常见问题总结 -[MySQL 教程(菜鸟教程)](http://www.runoob.com/mysql/mysql-tutorial.html) +### 什么是MySQL? -[MySQL教程(易百教程)](https://www.yiibai.com/mysql/) +MySQL 是一种关系型数据库,在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展。阿里巴巴数据库系统也大量用到了 MySQL,因此它的稳定性是有保障的。MySQL是开放源代码的,因此任何人都可以在 GPL(General Public License) 的许可下下载并根据个性化的需要对其进行修改。MySQL的默认端口号是**3306**。 -> ## 视频教程推荐 +### 存储引擎 +#### 一些常用命令 -**基础入门:** [与MySQL的零距离接触-慕课网](https://www.imooc.com/learn/122) +**查看MySQL提供的所有存储引擎** -**Mysql开发技巧:** [MySQL开发技巧(一)](https://www.imooc.com/learn/398)  [MySQL开发技巧(二)](https://www.imooc.com/learn/427)  [MySQL开发技巧(三)](https://www.imooc.com/learn/449) +```sql +mysql> show engines; +``` -**Mysql5.7新特性及相关优化技巧:** [MySQL5.7版本新特性](https://www.imooc.com/learn/533)  [性能优化之MySQL优化](https://www.imooc.com/learn/194) +![查看MySQL提供的所有存储引擎](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/mysql-engines.png) -[MySQL集群(PXC)入门](https://www.imooc.com/learn/993)  [MyCAT入门及应用](https://www.imooc.com/learn/951) +从上图我们可以查看出 MySQL 当前默认的存储引擎是InnoDB,并且在5.7版本所有的存储引擎中只有 InnoDB 是事务性存储引擎,也就是说只有 InnoDB 支持事务。 + +**查看MySQL当前默认的存储引擎** + +我们也可以通过下面的命令查看默认的存储引擎。 + +```sql +mysql> show variables like '%storage_engine%'; +``` + +**查看表的存储引擎** + +```sql +show table status like "table_name" ; +``` + +![查看表的存储引擎](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/查看表的存储引擎.png) + +#### MyISAM和InnoDB区别 + +MyISAM是MySQL的默认数据库引擎(5.5版之前)。虽然性能极佳,而且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。不过,5.5版本之后,MySQL引入了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。 + +大多数时候我们使用的都是 InnoDB 存储引擎,但是在某些情况下使用 MyISAM 也是合适的比如读密集的情况下。(如果你不介意 MyISAM 崩溃恢复问题的话)。 + +**两者的对比:** + +1. **是否支持行级锁** : MyISAM 只有表级锁(table-level locking),而InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。 +2. **是否支持事务和崩溃后的安全恢复: MyISAM** 强调的是性能,每次查询具有原子性,其执行速度比InnoDB类型更快,但是不提供事务支持。但是**InnoDB** 提供事务支持事务,外部键等高级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。 +3. **是否支持外键:** MyISAM不支持,而InnoDB支持。 +4. **是否支持MVCC** :仅 InnoDB 支持。应对高并发事务, MVCC比单纯的加锁更高效;MVCC只在 `READ COMMITTED` 和 `REPEATABLE READ` 两个隔离级别下工作;MVCC可以使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。推荐阅读:[MySQL-InnoDB-MVCC多版本并发控制](https://segmentfault.com/a/1190000012650596) +5. ...... + +《MySQL高性能》上面有一句话这样写到: + +> 不要轻易相信“MyISAM比InnoDB快”之类的经验之谈,这个结论往往不是绝对的。在很多我们已知场景中,InnoDB的速度都可以让MyISAM望尘莫及,尤其是用到了聚簇索引,或者需要访问的数据都可以放入内存的应用。 + +一般情况下我们选择 InnoDB 都是没有问题的,但是某些情况下你并不在乎可扩展能力和并发能力,也不需要事务支持,也不在乎崩溃后的安全恢复问题的话,选择MyISAM也是一个不错的选择。但是一般情况下,我们都是需要考虑到这些问题的。 + +### 字符集及校对规则 + +字符集指的是一种从二进制编码到某类字符符号的映射。校对规则则是指某种字符集下的排序规则。MySQL中每一种字符集都会对应一系列的校对规则。 + +MySQL采用的是类似继承的方式指定字符集的默认值,每个数据库以及每张数据表都有自己的默认值,他们逐层继承。比如:某个库中所有表的默认字符集将是该数据库所指定的字符集(这些表在没有指定字符集的情况下,才会采用默认字符集) PS:整理自《Java工程师修炼之道》 + +详细内容可以参考: [MySQL字符集及校对规则的理解](https://www.cnblogs.com/geaozhang/p/6724393.html#MySQLyuzifuji) + +### 索引 + +MySQL索引使用的数据结构主要有**BTree索引** 和 **哈希索引** 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。 + +MySQL的BTree索引使用的是B树中的B+Tree,但对于主要的两种存储引擎的实现方式是不同的。 + +- **MyISAM:** B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。 +- **InnoDB:** 其数据文件本身就是索引文件。相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。**在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。** **因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。** PS:整理自《Java工程师修炼之道》 + +**更多关于索引的内容可以查看文档首页MySQL目录下关于索引的详细总结。** + +### 查询缓存的使用 + +> 执行查询语句的时候,会先查询缓存。不过,MySQL 8.0 版本后移除,因为这个功能不太实用 + +my.cnf加入以下配置,重启MySQL开启查询缓存 +```properties +query_cache_type=1 +query_cache_size=600000 +``` + +MySQL执行以下命令也可以开启查询缓存 + +```properties +set global query_cache_type=1; +set global query_cache_size=600000; +``` +如上,**开启查询缓存后在同样的查询条件以及数据情况下,会直接在缓存中返回结果**。这里的查询条件包括查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息。因此任何两个查询在任何字符上的不同都会导致缓存不命中。此外,如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、MySQL库中的系统表,其查询结果也不会被缓存。 + +缓存建立之后,MySQL的查询缓存系统会跟踪查询中涉及的每张表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。 + +**缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做一次缓存操作,失效后还要销毁。** 因此,开启缓存查询要谨慎,尤其对于写密集的应用来说更是如此。如果开启,要注意合理控制缓存空间大小,一般来说其大小设置为几十MB比较合适。此外,**还可以通过sql_cache和sql_no_cache来控制某个查询语句是否需要缓存:** +```sql +select sql_no_cache count(*) from usr; +``` + +### 什么是事务? + +**事务是逻辑上的一组操作,要么都执行,要么都不执行。** + +事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。 + +### 事物的四大特性(ACID) + +![事物的特性](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/事务特性.png) + +1. **原子性(Atomicity):** 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用; +2. **一致性(Consistency):** 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的; +3. **隔离性(Isolation):** 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的; +4. **持久性(Durability):** 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。 + +### 并发事务带来哪些问题? + +在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。 + +- **脏读(Dirty read):** 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。 +- **丢失修改(Lost to modify):** 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。 +- **不可重复读(Unrepeatableread):** 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。 +- **幻读(Phantom read):** 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。 + +**不可重复读和幻读区别:** + +不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了。 + +### 事务隔离级别有哪些?MySQL的默认隔离级别是? + +**SQL 标准定义了四个隔离级别:** + +- **READ-UNCOMMITTED(读取未提交):** 最低的隔离级别,允许读取尚未提交的数据变更,**可能会导致脏读、幻读或不可重复读**。 +- **READ-COMMITTED(读取已提交):** 允许读取并发事务已经提交的数据,**可以阻止脏读,但是幻读或不可重复读仍有可能发生**。 +- **REPEATABLE-READ(可重复读):** 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,**可以阻止脏读和不可重复读,但幻读仍有可能发生**。 +- **SERIALIZABLE(可串行化):** 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,**该级别可以防止脏读、不可重复读以及幻读**。 + +------ + +| 隔离级别 | 脏读 | 不可重复读 | 幻影读 | +| :--------------: | :--: | :--------: | :----: | +| READ-UNCOMMITTED | √ | √ | √ | +| READ-COMMITTED | × | √ | √ | +| REPEATABLE-READ | × | × | √ | +| SERIALIZABLE | × | × | × | + +MySQL InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)**。我们可以通过`SELECT @@tx_isolation;`命令来查看 + +```sql +mysql> SELECT @@tx_isolation; ++-----------------+ +| @@tx_isolation | ++-----------------+ +| REPEATABLE-READ | ++-----------------+ +``` + +这里需要注意的是:与 SQL 标准不同的地方在于 InnoDB 存储引擎在 **REPEATABLE-READ(可重读)** +事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server) +是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)** 已经可以完全保证事务的隔离性要求,即达到了 + SQL标准的 **SERIALIZABLE(可串行化)** 隔离级别。因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 **READ-COMMITTED(读取提交内容)** ,但是你要知道的是InnoDB 存储引擎默认使用 **REPEAaTABLE-READ(可重读)** 并不会有任何性能损失。 + +InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到 **SERIALIZABLE(可串行化)** 隔离级别。 + +### 锁机制与InnoDB锁算法 + +**MyISAM和InnoDB存储引擎使用的锁:** + +- MyISAM采用表级锁(table-level locking)。 +- InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁 + +**表级锁和行级锁对比:** + +- **表级锁:** MySQL中锁定 **粒度最大** 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。 +- **行级锁:** MySQL中锁定 **粒度最小** 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。 + +详细内容可以参考: MySQL锁机制简单了解一下:[https://blog.csdn.net/qq_34337272/article/details/80611486](https://blog.csdn.net/qq_34337272/article/details/80611486) + +**InnoDB存储引擎的锁的算法有三种:** + +- Record lock:单个行记录上的锁 +- Gap lock:间隙锁,锁定一个范围,不包括记录本身 +- Next-key lock:record+gap 锁定一个范围,包含记录本身 + +**相关知识点:** + +1. innodb对于行的查询使用next-key lock +2. Next-locking keying为了解决Phantom Problem幻读问题 +3. 当查询的索引含有唯一属性时,将next-key lock降级为record key +4. Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生 +5. 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1 + +### 大表优化 + +当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下: + +#### 1. 限定数据的范围 + +务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内; + +#### 2. 读/写分离 + +经典的数据库拆分方案,主库负责写,从库负责读; + +#### 3. 垂直分区 + + **根据数据库里面数据表的相关性进行拆分。** 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。 + + **简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。** 如下图所示,这样来说大家应该就更容易理解了。 + ![数据库垂直分区](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/数据库垂直分区.png) + +- **垂直拆分的优点:** 可以使得列数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。 +- **垂直拆分的缺点:** 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂; + +#### 4. 水平分区 + +**保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。** + + 水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。 + +![数据库水平拆分](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/数据库水平拆分.png) + +水平拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 **水平拆分最好分库** 。 + +水平拆分能够 **支持非常大的数据量存储,应用端改造也少**,但 **分片事务难以解决** ,跨节点Join性能较差,逻辑复杂。《Java工程师修炼之道》的作者推荐 **尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度** ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。 + +**下面补充一下数据库分片的两种常见方案:** + +- **客户端代理:** **分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。** 当当网的 **Sharding-JDBC** 、阿里的TDDL是两种比较常用的实现。 +- **中间件代理:** **在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。** 我们现在谈的 **Mycat** 、360的Atlas、网易的DDB等等都是这种架构的实现。 + +详细内容可以参考: MySQL大表优化方案: [https://segmentfault.com/a/1190000006158186](https://segmentfault.com/a/1190000006158186) + +### 解释一下什么是池化设计思想。什么是数据库连接池?为什么需要数据库连接池? + +池话设计应该不是一个新名词。我们常见的如java线程池、jdbc连接池、redis连接池等就是这类设计的代表实现。这种设计会初始预设资源,解决的问题就是抵消每次获取资源的消耗,如创建线程的开销,获取远程连接的开销等。就好比你去食堂打饭,打饭的大妈会先把饭盛好几份放那里,你来了就直接拿着饭盒加菜即可,不用再临时又盛饭又打菜,效率就高了。除了初始化资源,池化设计还包括如下这些特征:池子的初始值、池子的活跃值、池子的最大值等,这些特征可以直接映射到java线程池和数据库连接池的成员属性中。——这篇文章对[池化设计思想](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485679&idx=1&sn=57dbca8c9ad49e1f3968ecff04a4f735&chksm=cea24724f9d5ce3212292fac291234a760c99c0960b5430d714269efe33554730b5f71208582&token=1141994790&lang=zh_CN#rd)介绍的还不错,直接复制过来,避免重复造轮子了。 + +数据库连接本质就是一个 socket 的连接。数据库服务端还要维护一些缓存和用户权限信息之类的 所以占用了一些内存。我们可以把数据库连接池是看做是维护的数据库连接的缓存,以便将来需要对数据库的请求时可以重用这些连接。为每个用户打开和维护数据库连接,尤其是对动态数据库驱动的网站应用程序的请求,既昂贵又浪费资源。**在连接池中,创建连接后,将其放置在池中,并再次使用它,因此不必建立新的连接。如果使用了所有连接,则会建立一个新连接并将其添加到池中。**连接池还减少了用户必须等待建立与数据库的连接的时间。 + +### 分库分表之后,id 主键如何处理? + +因为要是分成多个表之后,每个表都是从 1 开始累加,这样是不对的,我们需要一个全局唯一的 id 来支持。 + +生成全局 id 有下面这几种方式: + +- **UUID**:不适合作为主键,因为太长了,并且无序不可读,查询效率低。比较适合用于生成唯一的名字的标示比如文件的名字。 +- **数据库自增 id** : 两台数据库分别设置不同步长,生成不重复ID的策略来实现高可用。这种方式生成的 id 有序,但是需要独立部署数据库实例,成本高,还会有性能瓶颈。 +- **利用 redis 生成 id :** 性能比较好,灵活方便,不依赖于数据库。但是,引入了新的组件造成系统更加复杂,可用性降低,编码更加复杂,增加了系统成本。 +- **Twitter的snowflake算法** :Github 地址:https://github.com/twitter-archive/snowflake。 +- **美团的[Leaf](https://tech.meituan.com/2017/04/21/mt-leaf.html)分布式ID生成系统** :Leaf 是美团开源的分布式ID生成器,能保证全局唯一性、趋势递增、单调递增、信息安全,里面也提到了几种分布式方案的对比,但也需要依赖关系数据库、Zookeeper等中间件。感觉还不错。美团技术团队的一篇文章:https://tech.meituan.com/2017/04/21/mt-leaf.html 。 +- ...... + +### 一条SQL语句在MySQL中如何执行的 + +[一条SQL语句在MySQL中如何执行的]() + +### MySQL高性能优化规范建议 + +[MySQL高性能优化规范建议]() + +### 一条SQL语句执行得很慢的原因有哪些? + +[腾讯面试:一条SQL语句执行得很慢的原因有哪些?---不看后悔系列](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485185&idx=1&sn=66ef08b4ab6af5757792223a83fc0d45&chksm=cea248caf9d5c1dc72ec8a281ec16aa3ec3e8066dbb252e27362438a26c33fbe842b0e0adf47&token=79317275&lang=zh_CN#rd) + +## 公众号 +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! -> ## 常见问题总结 - -- ### ①存储引擎 - - [MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇](https://juejin.im/post/5b1685bef265da6e5c3c1c34) - -- ### ②字符集及校对规则 - - 字符集指的是一种从二进制编码到某类字符符号的映射。校对规则则是指某种字符集下的排序规则。Mysql中每一种字符集都会对应一系列的校对规则。 - - Mysql采用的是类似继承的方式指定字符集的默认值,每个数据库以及每张数据表都有自己的默认值,他们逐层继承。比如:某个库中所有表的默认字符集将是该数据库所指定的字符集(这些表在没有指定字符集的情况下,才会采用默认字符集) PS:整理自《Java工程师修炼之道》 - - 详细内容可以参考: [MySQL字符集及校对规则的理解](https://www.cnblogs.com/geaozhang/p/6724393.html#mysqlyuzifuji) - -- ### ③索引相关的内容(数据库使用中非常关键的技术,合理正确的使用索引可以大大提高数据库的查询性能) - -   Mysql索引使用的数据结构主要有**BTree索引** 和 **哈希索引** 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。 - -   Mysql的BTree索引使用的是B数中的B+Tree,但对于主要的两种存储引擎的实现方式是不同的。 - -   **MyISAM:** B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。 - -   **InnoDB:** 其数据文件本身就是索引文件。相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。**在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。** **因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。** PS:整理自《Java工程师修炼之道》 - - 详细内容可以参考: - - [干货:mysql索引的数据结构](https://www.jianshu.com/p/1775b4ff123a) - - [MySQL优化系列(三)--索引的使用、原理和设计优化](https://blog.csdn.net/Jack__Frost/article/details/72571540) - - [数据库两大神器【索引和锁】](https://juejin.im/post/5b55b842f265da0f9e589e79#comment) - -- ### ④查询缓存的使用 - - my.cnf加入以下配置,重启Mysql开启查询缓存 - ``` - query_cache_type=1 - query_cache_size=600000 - ``` - - Mysql执行以下命令也可以开启查询缓存 - - ``` - set global query_cache_type=1; - set global query_cache_size=600000; - ``` - 如上,**开启查询缓存后在同样的查询条件以及数据情况下,会直接在缓存中返回结果**。这里的查询条件包括查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息。因此任何两个查询在任何字符上的不同都会导致缓存不命中。此外,如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、Mysql库中的系统表,其查询结果也不会被缓存。 - - 缓存建立之后,Mysql的查询缓存系统会跟踪查询中涉及的每张表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。 - - **缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做一次缓存操作,失效后还要销毁。** 因此,开启缓存查询要谨慎,尤其对于写密集的应用来说更是如此。如果开启,要注意合理控制缓存空间大小,一般来说其大小设置为几十MB比较合适。此外,**还可以通过sql_cache和sql_no_cache来控制某个查询语句是否需要缓存:** - ``` - select sql_no_cache count(*) from usr; - ``` - -- ### ⑤事务机制 - - **关系性数据库需要遵循ACID规则,具体内容如下:** - -![事务的特性](https://user-gold-cdn.xitu.io/2018/5/20/1637b08b98619455?w=312&h=305&f=png&s=22430) - - 1. **原子性:** 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用; - 2. **一致性:** 执行事务前后,数据库从一个一致性状态转换到另一个一致性状态。 - 3. **隔离性:** 并发访问数据库时,一个用户的事物不被其他事务所干扰,各并发事务之间数据库是独立的; - 4. **持久性:** 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库 发生故障也不应该对其有任何影响。 - - **为了达到上述事务特性,数据库定义了几种不同的事务隔离级别:** - -- **READ_UNCOMMITTED(未提交读):** 最低的隔离级别,允许读取尚未提交的数据变更,**可能会导致脏读、幻读或不可重复读** -- **READ_COMMITTED(提交读):** 允许读取并发事务已经提交的数据,**可以阻止脏读,但是幻读或不可重复读仍有可能发生** -- **REPEATABLE_READ(可重复读):** 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,**可以阻止脏读和不可重复读,但幻读仍有可能发生。** -- **SERIALIZABLE(串行):** 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,**该级别可以防止脏读、不可重复读以及幻读**。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 - - 这里需要注意的是:**Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.** - - 事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVCC(多版本并发控制),通过行的创建时间和行的过期时间来支持并发一致性读和回滚等特性。 - - 详细内容可以参考: [可能是最漂亮的Spring事务管理详解](https://blog.csdn.net/qq_34337272/article/details/80394121) - -- ### ⑥锁机制与InnoDB锁算法 - **MyISAM和InnoDB存储引擎使用的锁:** - - - MyISAM采用表级锁(table-level locking)。 - - InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁 - - **表级锁和行级锁对比:** - - - **表级锁:** Mysql中锁定 **粒度最大** 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。 - - **行级锁:** Mysql中锁定 **粒度最小** 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。 - - 详细内容可以参考: - [Mysql锁机制简单了解一下](https://blog.csdn.net/qq_34337272/article/details/80611486) - - **InnoDB存储引擎的锁的算法有三种:** - - Record lock:单个行记录上的锁 - - Gap lock:间隙锁,锁定一个范围,不包括记录本身 - - Next-key lock:record+gap 锁定一个范围,包含记录本身 - - **相关知识点:** - 1. innodb对于行的查询使用next-key lock - 2. Next-locking keying为了解决Phantom Problem幻读问题 - 3. 当查询的索引含有唯一属性时,将next-key lock降级为record key - 4. Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生 - 5. 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1 - -- ### ⑦大表优化 - - 当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下: - - 1. **限定数据的范围:** 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内; - 2. **读/写分离:** 经典的数据库拆分方案,主库负责写,从库负责读; - 3 . **垂直分区:** - - **根据数据库里面数据表的相关性进行拆分。** 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。 - - **简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。** 如下图所示,这样来说大家应该就更容易理解了。 - ![](https://user-gold-cdn.xitu.io/2018/6/16/164084354ba2e0fd?w=950&h=279&f=jpeg&s=26015) - - **垂直拆分的优点:** 可以使得列数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。 - - **垂直拆分的缺点:** 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂; - - 4. **水平分区:** - - - **保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。** - - 水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。 - - ![数据库水平拆分](https://user-gold-cdn.xitu.io/2018/6/16/164084b7e9e423e3?w=690&h=271&f=jpeg&s=23119) - - 水平拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 **水平拆分最好分库** 。 - - 水平拆分能够 **支持非常大的数据量存储,应用端改造也少**,但 **分片事务难以解决** ,跨节点Join性能较差,逻辑复杂。《Java工程师修炼之道》的作者推荐 **尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度** ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。 - - **下面补充一下数据库分片的两种常见方案:** - - **客户端代理:** **分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。** 当当网的 **Sharding-JDBC** 、阿里的TDDL是两种比较常用的实现。 - - **中间件代理:** **在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。** 我们现在谈的 **Mycat** 、360的Atlas、网易的DDB等等都是这种架构的实现。 - - - 详细内容可以参考: - [MySQL大表优化方案](https://segmentfault.com/a/1190000006158186) - +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git "a/docs/database/MySQL\351\253\230\346\200\247\350\203\275\344\274\230\345\214\226\350\247\204\350\214\203\345\273\272\350\256\256.md" "b/docs/database/MySQL\351\253\230\346\200\247\350\203\275\344\274\230\345\214\226\350\247\204\350\214\203\345\273\272\350\256\256.md" index dcfc2edd210..f079ad77593 100644 --- "a/docs/database/MySQL\351\253\230\346\200\247\350\203\275\344\274\230\345\214\226\350\247\204\350\214\203\345\273\272\350\256\256.md" +++ "b/docs/database/MySQL\351\253\230\346\200\247\350\203\275\344\274\230\345\214\226\350\247\204\350\214\203\345\273\272\350\256\256.md" @@ -311,7 +311,7 @@ select name,phone from customer where id = '111'; ### 4. 数据库设计时,应该要对以后扩展进行考虑 -### 5. 程序连接不同的数据库使用不同的账号,进制跨库查询 +### 5. 程序连接不同的数据库使用不同的账号,禁止跨库查询 - 为数据库迁移和分库分表留出余地 - 降低业务耦合度 diff --git a/docs/database/Redis/Redis.md b/docs/database/Redis/Redis.md index fa64bd60565..33790b266a4 100644 --- a/docs/database/Redis/Redis.md +++ b/docs/database/Redis/Redis.md @@ -1,3 +1,4 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 @@ -18,7 +19,6 @@ - [缓存雪崩和缓存穿透问题解决方案](#缓存雪崩和缓存穿透问题解决方案) - [如何解决 Redis 的并发竞争 Key 问题](#如何解决-redis-的并发竞争-key-问题) - [如何保证缓存与数据库双写时的数据一致性?](#如何保证缓存与数据库双写时的数据一致性) -- [参考:](#参考) @@ -32,7 +32,7 @@ **高性能:** -假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可! +假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可! ![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-24/54316596.jpg) @@ -54,6 +54,21 @@ 使用 redis 或 memcached 之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。缺点是需要保持 redis 或 memcached服务的高可用,整个程序架构上较为复杂。 +### redis 的线程模型 + +> 参考地址:https://www.javazhiyin.com/22943.html + +redis 内部使用文件事件处理器 `file event handler`,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。 + +文件事件处理器的结构包含 4 个部分: + +- 多个 socket +- IO 多路复用程序 +- 文件事件分派器 +- 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器) + +多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个 socket,会将 socket 产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。 + ### redis 和 memcached 的区别 @@ -147,9 +162,7 @@ Redis中有个设置时间过期的功能,即对存储在 redis 数据库中 - **惰性删除** :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈! -但是仅仅通过设置过期时间还是有问题的。我们想一下:如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了。怎么解决这个问题呢? - -**redis 内存淘汰机制。** +但是仅仅通过设置过期时间还是有问题的。我们想一下:如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了。怎么解决这个问题呢? **redis 内存淘汰机制。** ### redis 内存淘汰机制(MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?) @@ -164,13 +177,17 @@ redis 配置文件 redis.conf 中有相关注释,我这里就不贴了,大 5. **allkeys-random**:从数据集(server.db[i].dict)中任意选择数据淘汰 6. **no-eviction**:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧! +4.0版本后增加以下两种: + +7. **volatile-lfu**:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰 +8. **allkeys-lfu**:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key **备注: 关于 redis 设置过期时间以及内存淘汰机制,我这里只是简单的总结一下,后面会专门写一篇文章来总结!** ### redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进行恢复) -很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后回复数据),或者是为了防止系统故障而将数据备份到一个远程位置。 +很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了防止系统故障而将数据备份到一个远程位置。 Redis不同于Memcached的很重一点就是,Redis支持持久化,而且支持两种不同的持久化操作。**Redis的一种持久化方式叫快照(snapshotting,RDB),另一种方式是只追加文件(append-only file,AOF)**。这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。 @@ -234,6 +251,10 @@ Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。 在传统的关系式数据库中,常常用 ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务总是具有原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation),并且当 Redis 运行在某种特定的持久化模式下时,事务也具有持久性(Durability)。 +补充内容: + +> 1. redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。(来自[issue:关于Redis事务不是原子性问题](https://github.com/Snailclimb/JavaGuide/issues/452) ) + ### 缓存雪崩和缓存穿透问题解决方案 **缓存雪崩** @@ -281,13 +302,14 @@ Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。 串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。 -**参考:** +**参考:** Java工程师面试突击第1季(可能是史上最好的Java面试突击课程)-中华石杉老师!公众号后台回复关键字“1”即可获取该视频内容。 + +## 公众号 -- Java工程师面试突击第1季(可能是史上最好的Java面试突击课程)-中华石杉老师。视频地址见下面! - - 链接: https://pan.baidu.com/s/18pp6g1xKVGCfUATf_nMrOA - - 密码:5i58 +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 -### 参考: +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! -- redis设计与实现(第二版) +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) \ No newline at end of file diff --git "a/docs/database/Redis/Redlock\345\210\206\345\270\203\345\274\217\351\224\201.md" "b/docs/database/Redis/Redlock\345\210\206\345\270\203\345\274\217\351\224\201.md" index b1742f2fbf9..86a15ff6faf 100644 --- "a/docs/database/Redis/Redlock\345\210\206\345\270\203\345\274\217\351\224\201.md" +++ "b/docs/database/Redis/Redlock\345\210\206\345\270\203\345\274\217\351\224\201.md" @@ -28,7 +28,7 @@ end 算法很易懂,起 5 个 master 节点,分布在不同的机房尽量保证可用性。为了获得锁,client 会进行如下操作: -1. 得到当前的时间,微妙单位 +1. 得到当前的时间,微秒单位 2. 尝试顺序地在 5 个实例上申请锁,当然需要使用相同的 key 和 random value,这里一个 client 需要合理设置与 master 节点沟通的 timeout 大小,避免长时间和一个 fail 了的节点浪费时间 3. 当 client 在大于等于 3 个 master 上成功申请到锁的时候,且它会计算申请锁消耗了多少时间,这部分消耗的时间采用获得锁的当下时间减去第一步获得的时间戳得到,如果锁的持续时长(lock validity time)比流逝的时间多的话,那么锁就真正获取到了。 4. 如果锁申请到了,那么锁真正的 lock validity time 应该是 origin(lock validity time) - 申请锁期间流逝的时间 diff --git "a/docs/database/\344\270\200\345\215\203\350\241\214MySQL\345\221\275\344\273\244.md" "b/docs/database/\344\270\200\345\215\203\350\241\214MySQL\345\221\275\344\273\244.md" index acbfda3d7ac..1ddc9ade2c6 100644 --- "a/docs/database/\344\270\200\345\215\203\350\241\214MySQL\345\221\275\344\273\244.md" +++ "b/docs/database/\344\270\200\345\215\203\350\241\214MySQL\345\221\275\344\273\244.md" @@ -104,7 +104,7 @@ SHOW VARIABLES -- 显示系统变量信息 -- 查看所有表 SHOW TABLES[ LIKE 'pattern'] SHOW TABLES FROM 库名 --- 查看表机构 +-- 查看表结构 SHOW CREATE TABLE 表名 (信息更详细) DESC 表名 / DESCRIBE 表名 / EXPLAIN 表名 / SHOW COLUMNS FROM 表名 [LIKE 'PATTERN'] SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern'] @@ -363,7 +363,7 @@ set(val1, val2, val3...) 字段不能再分,就满足第一范式。 -- 2NF, 第二范式 满足第一范式的前提下,不能出现部分依赖。 - 消除符合主键就可以避免部分依赖。增加单列关键字。 + 消除复合主键就可以避免部分依赖。增加单列关键字。 -- 3NF, 第三范式 满足第二范式的前提下,不能出现传递依赖。 某个字段依赖于主键,而有其他字段依赖于该字段。这就是传递依赖。 @@ -590,7 +590,7 @@ CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW view_name ```mysql 事务是指逻辑上的一组操作,组成这组操作的各个单元,要不全成功要不全失败。 - 支持连续SQL的集体成功或集体撤销。 - - 事务是数据库在数据晚自习方面的一个功能。 + - 事务是数据库在数据完整性方面的一个功能。 - 需要利用 InnoDB 或 BDB 存储引擎,对自动提交的特性支持完成。 - InnoDB被称为事务安全型引擎。 -- 事务开启 diff --git "a/docs/database/\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253(\345\233\276\346\226\207\350\257\246\350\247\243).md" "b/docs/database/\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253(\345\233\276\346\226\207\350\257\246\350\247\243).md" index 4cd950d5357..fd066837e01 100644 --- "a/docs/database/\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253(\345\233\276\346\226\207\350\257\246\350\247\243).md" +++ "b/docs/database/\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253(\345\233\276\346\226\207\350\257\246\350\247\243).md" @@ -3,7 +3,7 @@ - [事务隔离级别(图文详解)](#事务隔离级别图文详解) - [什么是事务?](#什么是事务) - - [事物的特性(ACID)](#事物的特性acid) + - [事务的特性(ACID)](#事务的特性acid) - [并发事务带来的问题](#并发事务带来的问题) - [事务隔离级别](#事务隔离级别) - [实际情况演示](#实际情况演示) @@ -24,11 +24,10 @@ 事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。 -### 事物的特性(ACID) +### 事务的特性(ACID) + +![事务的特性](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/事务特性.png) -
- -
1. **原子性:** 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用; 2. **一致性:** 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的; @@ -70,7 +69,7 @@ | REPEATABLE-READ | × | × | √ | | SERIALIZABLE | × | × | × | -MySQL InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)**。我们可以通过`SELECT @@tx_isolation;`命令来查看 +MySQL InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)**。我们可以通过`SELECT @@tx_isolation;`命令来查看,MySQL 8.0 该命令改为`SELECT @@transaction_isolation;` ```sql mysql> SELECT @@tx_isolation; diff --git "a/docs/database/\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\346\261\240.md" "b/docs/database/\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\346\261\240.md" new file mode 100644 index 00000000000..3e84dfc8efd --- /dev/null +++ "b/docs/database/\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\346\261\240.md" @@ -0,0 +1,21 @@ +- 公众号和Github待发文章:[数据库:数据库连接池原理详解与自定义连接池实现](https://www.fangzhipeng.com/javainterview/2019/07/15/mysql-connector-pool.html) +- [基于JDBC的数据库连接池技术研究与应用](http://blog.itpub.net/9403012/viewspace-111794/) +- [数据库连接池技术详解](https://juejin.im/post/5b7944c6e51d4538c86cf195) + +数据库连接本质就是一个 socket 的连接。数据库服务端还要维护一些缓存和用户权限信息之类的 所以占用了一些内存 + +连接池是维护的数据库连接的缓存,以便将来需要对数据库的请求时可以重用这些连接。为每个用户打开和维护数据库连接,尤其是对动态数据库驱动的网站应用程序的请求,既昂贵又浪费资源。**在连接池中,创建连接后,将其放置在池中,并再次使用它,因此不必建立新的连接。如果使用了所有连接,则会建立一个新连接并将其添加到池中。**连接池还减少了用户必须等待建立与数据库的连接的时间。 + +操作过数据库的朋友应该都知道数据库连接池这个概念,它几乎每天都在和我们打交道,但是你真的了解 **数据库连接池** 吗? + +### 没有数据库连接池之前 + +我相信你一定听过这样一句话:**Java语言中,JDBC(Java DataBase Connection)是应用程序与数据库沟通的桥梁**。 + + + + + + + + diff --git "a/docs/database/\351\230\277\351\207\214\345\267\264\345\267\264\345\274\200\345\217\221\346\211\213\345\206\214\346\225\260\346\215\256\345\272\223\351\203\250\345\210\206\347\232\204\344\270\200\344\272\233\346\234\200\344\275\263\345\256\236\350\267\265.md" "b/docs/database/\351\230\277\351\207\214\345\267\264\345\267\264\345\274\200\345\217\221\346\211\213\345\206\214\346\225\260\346\215\256\345\272\223\351\203\250\345\210\206\347\232\204\344\270\200\344\272\233\346\234\200\344\275\263\345\256\236\350\267\265.md" new file mode 100644 index 00000000000..7eb84001c00 --- /dev/null +++ "b/docs/database/\351\230\277\351\207\214\345\267\264\345\267\264\345\274\200\345\217\221\346\211\213\345\206\214\346\225\260\346\215\256\345\272\223\351\203\250\345\210\206\347\232\204\344\270\200\344\272\233\346\234\200\344\275\263\345\256\236\350\267\265.md" @@ -0,0 +1,41 @@ +# 阿里巴巴Java开发手册数据库部分的一些最佳实践总结 + +## 模糊查询 + +对于模糊查询阿里巴巴开发手册这样说到: + +> 【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。 +> +> 说明:索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。 + +## 外键和级联 + +对于外键和级联,阿里巴巴开发手册这样说到: + +>【强制】不得使用外键与级联,一切外键概念必须在应用层解决。 +> +>说明:以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风 险;外键影响数据库的插入速度 + +为什么不要用外键呢?大部分人可能会这样回答: + +> 1. **增加了复杂性:** a.每次做DELETE 或者UPDATE都必须考虑外键约束,会导致开发的时候很痛苦,测试数据极为不方便;b.外键的主从关系是定的,假如那天需求有变化,数据库中的这个字段根本不需要和其他表有关联的话就会增加很多麻烦。 +> 2. **增加了额外工作**: 数据库需要增加维护外键的工作,比如当我们做一些涉及外键字段的增,删,更新操作之后,需要触发相关操作去检查,保证数据的的一致性和正确性,这样会不得不消耗资源;(个人觉得这个不是不用外键的原因,因为即使你不使用外键,你在应用层面也还是要保证的。所以,我觉得这个影响可以忽略不计。) +> 3. 外键还会因为需要请求对其他表内部加锁而容易出现死锁情况; +> 4. **对分不分表不友好** :因为分库分表下外键是无法生效的。 +> 5. ...... + +我个人觉得上面这种回答不是特别的全面,只是说了外键存在的一个常见的问题。实际上,我们知道外键也是有很多好处的,比如: + +1. 保证了数据库数据的一致性和完整性; +2. 级联操作方便,减轻了程序代码量; +3. ...... + +所以说,不要一股脑的就抛弃了外键这个概念,既然它存在就有它存在的道理,如果系统不涉及分不分表,并发量不是很高的情况还是可以考虑使用外键的。 + +我个人是不太喜欢外键约束,比较喜欢在应用层去进行相关操作。 + +## 关于@Transactional注解 + +对于`@Transactional`事务注解,阿里巴巴开发手册这样说到: + +>【参考】@Transactional事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。 diff --git "a/docs/essential-content-for-interview/MostCommonJavaInterviewQuestions/\347\254\254\344\270\200\345\221\250\357\274\2102018-8-7\357\274\211.md" "b/docs/essential-content-for-interview/MostCommonJavaInterviewQuestions/\347\254\254\344\270\200\345\221\250\357\274\2102018-8-7\357\274\211.md" index 595a73e28b0..300f1fd6c11 100644 --- "a/docs/essential-content-for-interview/MostCommonJavaInterviewQuestions/\347\254\254\344\270\200\345\221\250\357\274\2102018-8-7\357\274\211.md" +++ "b/docs/essential-content-for-interview/MostCommonJavaInterviewQuestions/\347\254\254\344\270\200\345\221\250\357\274\2102018-8-7\357\274\211.md" @@ -78,7 +78,7 @@ num2 = 20 ![example 2](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/3825204.jpg) -array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的时同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。 +array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的是同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。 **通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。** diff --git a/docs/essential-content-for-interview/PreparingForInterview/JavaInterviewLibrary.md b/docs/essential-content-for-interview/PreparingForInterview/JavaInterviewLibrary.md index 4901f88932d..835b6a54fa0 100644 --- a/docs/essential-content-for-interview/PreparingForInterview/JavaInterviewLibrary.md +++ b/docs/essential-content-for-interview/PreparingForInterview/JavaInterviewLibrary.md @@ -1,78 +1,89 @@ -最近浏览 Github ,收藏了一些还算不错的 Java面试/学习相关的仓库,分享给大家,希望对你有帮助。我暂且按照目前的 Star 数量来排序。 +昨天我整理了公众号历史所有和面试相关的我觉得还不错的文章:[整理了一些有助于你拿Offer的文章]() 。今天分享一下最近逛Github看到了一些我觉得对于Java面试以及学习有帮助的仓库,这些仓库涉及Java核心知识点整理、Java常见面试题、算法、基础知识点比如网络和操作系统等等。 -本文由 SnailClimb 整理,如需转载请联系作者。 +## 知识点相关 -### 1. interviews +### 1.JavaGuide -- Github地址: [https://github.com/kdn251/interviews/blob/master/README-zh-cn.md](https://github.com/kdn251/interviews/blob/master/README-zh-cn.md) -- star: 31k -- 介绍: 软件工程技术面试个人指南。 -- 概览: - - ![interviews](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-24/47663247.jpg) +- Github地址: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide) +- star: 64.0k +- 介绍: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。 + +### 2.CS-Notes + +- Github 地址: +- Star: 68.3k +- 介绍: 技术面试必备基础知识、Leetcode 题解、后端面试、Java 面试、春招、秋招、操作系统、计算机网络、系统设计。 -### 2. JCSprout +### 3. advanced-java + +- Github地址:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) +- star: 23.4k +- 介绍: 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务等领域知识,后端同学必看,前端同学也可学习。 + +### 4.JCSprout - Github地址:[https://github.com/crossoverJie/JCSprout](https://github.com/crossoverJie/JCSprout) -- star: 17.7k +- star: 21.2k - 介绍: Java Core Sprout:处于萌芽阶段的 Java 核心知识库。 -- 概览: - - ![ JCSprout](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-24/85903384.jpg) -### 3. JavaGuide +### 5.toBeTopJavaer -- Github地址: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide) -- star: 17.4k -- 介绍: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。 -- 概览: +- Github地址:[https://github.com/hollischuang/toBeTopJavaer](https://github.com/hollischuang/toBeTopJavaer) +- star: 4.0 k +- 介绍: Java工程师成神之路。 + +### 6.architect-awesome - ![JavaGuide](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-24/1352784.jpg) +- Github地址:[https://github.com/xingshaocheng/architect-awesome](https://github.com/xingshaocheng/architect-awesome) +- star: 34.4 k +- 介绍:后端架构师技术图谱。 -### 4. technology-talk +### 7.technology-talk - Github地址: [https://github.com/aalansehaiyang/technology-talk](https://github.com/aalansehaiyang/technology-talk) -- star: 4.2k +- star: 6.1k - 介绍: 汇总java生态圈常用技术框架、开源中间件,系统架构、项目管理、经典架构案例、数据库、常用三方库、线上运维等知识。 -### 5. fullstack-tutorial +### 8.fullstack-tutorial - Github地址: [https://github.com/frank-lam/fullstack-tutorial](https://github.com/frank-lam/fullstack-tutorial) -- star: 2.8k -- 介绍: Full Stack Developer Tutorial,后台技术栈/全栈开发/架构师之路,秋招/春招/校招/面试。 from zero to hero。 -- 概览: +- star: 4.0k +- 介绍: fullstack tutorial 2019,后台技术栈/架构师之路/全栈开发社区,春招/秋招/校招/面试。 - ![fullstack-tutorial](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-24/67104534.jpg) +### 9.3y + +- Github地址:[https://github.com/ZhongFuCheng3y/3y](https://github.com/ZhongFuCheng3y/3y) +- star: 1.9 k +- 介绍: Java 知识整合。 -### 6. java-bible +### 10.java-bible - Github地址:[https://github.com/biezhi/java-bible](https://github.com/biezhi/java-bible) -- star: 1.9k +- star: 2.3k - 介绍: 这里记录了一些技术摘要,部分文章来自网络,本项目的目的力求分享精品技术干货,以Java为主。 -- 概览: - ![ java-bible](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-24/90223588.jpg) +### 11.interviews -### 7. EasyJob +- Github地址: [https://github.com/kdn251/interviews/blob/master/README-zh-cn.md](https://github.com/kdn251/interviews/blob/master/README-zh-cn.md) +- star: 35.3k +- 介绍: 软件工程技术面试个人指南(国外的一个项目,虽然有翻译版,但是不太推荐,因为很多内容并不适用于国内)。 -- Github地址:[https://github.com/it-interview/EasyJob](https://github.com/it-interview/EasyJob) -- star: 1.9k -- 介绍: 互联网求职面试题、知识点和面经整理。 +## 算法相关 -### 8. advanced-java +### 1.LeetCodeAnimation -- Github地址:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) -- star: 1k -- 介绍: 互联网 Java 工程师进阶知识完全扫盲 +- Github 地址: +- Star: 33.4k +- 介绍: Demonstrate all the questions on LeetCode in the form of animation.(用动画的形式呈现解LeetCode题目的思路)。 -### 9. 3y +### 2.awesome-java-leetcode -- Github地址:[https://github.com/ZhongFuCheng3y/3y](https://github.com/ZhongFuCheng3y/3y) -- star: 0.4 k -- 介绍: Java 知识整合。 +- Github地址:[https://github.com/Blankj/awesome-java-leetcode](https://github.com/Blankj/awesome-java-leetcode) +- star: 6.1k +- 介绍: LeetCode 上 Facebook 的面试题目。 -除了这九个仓库,再推荐几个不错的学习方向的仓库给大家。 +### 3.leetcode -1. Star 数高达 4w+的 CS 笔记-CS-Notes:[https://github.com/CyC2018/CS-Notes](https://github.com/CyC2018/CS-Notes) -2. 后端(尤其是Java)程序员的 Linux 学习仓库-Linux-Tutorial:[https://github.com/judasn/Linux-Tutorial](https://github.com/judasn/Linux-Tutorial)( Star:4.6k) -3. 两个算法相关的仓库,刷 Leetcode 的小伙伴必备:①awesome-java-leetcode:[https://github.com/Blankj/awesome-java-leetcode](https://github.com/Blankj/awesome-java-leetcode);②LintCode:[https://github.com/awangdev/LintCode](https://github.com/awangdev/LintCode) +- Github地址:[https://github.com/azl397985856/leetcode](https://github.com/azl397985856/leetcode) +- star: 12.0k +- 介绍: LeetCode Solutions: A Record of My Problem Solving Journey.( leetcode题解,记录自己的leetcode解题之路。) \ No newline at end of file diff --git "a/docs/essential-content-for-interview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md" "b/docs/essential-content-for-interview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md" index f0627de96e2..7feead7d8bd 100644 --- "a/docs/essential-content-for-interview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md" +++ "b/docs/essential-content-for-interview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md" @@ -102,7 +102,7 @@ ## 排版注意事项 1. 尽量简洁,不要太花里胡哨; -2. 一些技术名词不要弄错了大小写比如MySQL不要写成mysql,Java不要写成Java。这个在我看来还是比较忌讳的,所以一定要注意这个细节; +2. 一些技术名词不要弄错了大小写比如MySQL不要写成mysql,Java不要写成java。这个在我看来还是比较忌讳的,所以一定要注意这个细节; 3. 中文和数字英文之间加上空格的话看起来会舒服一点; ## 其他的一些小tips diff --git "a/docs/essential-content-for-interview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" "b/docs/essential-content-for-interview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" index 46996cdb3e9..da3e04d78e4 100644 --- "a/docs/essential-content-for-interview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" +++ "b/docs/essential-content-for-interview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" @@ -32,7 +32,7 @@ - [2.1 两者的对比](#21-两者的对比) - [2.2 关于两者的总结](#22-关于两者的总结) - [3 聊聊 Java 中的集合吧!](#3-聊聊-java-中的集合吧) - - [3.1 Arraylist 与 LinkedList 有什么不同?\(注意加上从数据结构分析的内容\)](#31-arraylist-与-linkedlist-有什么不同注意加上从数据结构分析的内容) + - [3.1 ArrayList 与 LinkedList 有什么不同?\(注意加上从数据结构分析的内容\)](#31-arraylist-与-linkedlist-有什么不同注意加上从数据结构分析的内容) - [3.2 HashMap的底层实现](#32-hashmap的底层实现) - [1)JDK1.8之前](#1jdk18之前) - [2)JDK1.8之后](#2jdk18之后) @@ -108,7 +108,7 @@ request.getRequestDispatcher("login_success.jsp").forward(request, response); ``` -**重定向(Redirect)** 是利用服务器返回的状态吗来实现的。客户端浏览器请求服务器的时候,服务器会返回一个状态码。服务器通过HttpServletRequestResponse的setStatus(int status)方法设置状态码。如果服务器返回301或者302,则浏览器会到新的网址重新请求该资源。 +**重定向(Redirect)** 是利用服务器返回的状态码来实现的。客户端浏览器请求服务器的时候,服务器会返回一个状态码。服务器通过HttpServletRequestResponse的setStatus(int status)方法设置状态码。如果服务器返回301或者302,则浏览器会到新的网址重新请求该资源。 1. **从地址栏显示来说**:forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器。浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址。redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址。所以地址栏显示的是新的URL。 2. **从数据共享来说**:forward:转发页面和转发到的页面可以共享request里面的数据。redirect:不能共享数据。 @@ -450,7 +450,7 @@ TransactionDefinition 接口中定义了五个表示隔离级别的常量: ### 1.3 介绍一下你知道哪几种消息队列,该如何选择呢? -| 特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafaka | +| 特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka | | :---------------------- | -----------------------------------------------------------: | -----------------------------------------------------------: | -----------------------------------------------------------: | -----------------------------------------------------------: | | 单机吞吐量 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 10万级,RocketMQ也是可以支撑高吞吐的一种MQ | 10万级别,这是kafka最大的优点,就是吞吐量高。一般配合大数据类的系统来进行实时数据计算、日志采集等场景 | | topic数量对吞吐量的影响 | | | topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降这是RocketMQ的一大优势,在同等机器下,可以支撑大量的topic | topic从几十个到几百个的时候,吞吐量会大幅度下降。所以在同等机器下,kafka尽量保证topic数量不要过多。如果要支撑大规模topic,需要增加更多的机器资源 | @@ -484,7 +484,7 @@ TransactionDefinition 接口中定义了五个表示隔离级别的常量: ### 2.2 关于两者的总结 -MyISAM更适合读密集的表,而InnoDB更适合写密集的的表。 在数据库做主从分离的情况下,经常选择MyISAM作为主库的存储引擎。 +MyISAM更适合读密集的表,而InnoDB更适合写密集的表。 在数据库做主从分离的情况下,经常选择MyISAM作为主库的存储引擎。 一般来说,如果需要事务支持,并且有较高的并发读取频率(MyISAM的表锁的粒度太大,所以当该表写并发量较高时,要等待的查询就会很多了),InnoDB是不错的选择。如果你的数据量很大(MyISAM支持压缩特性可以减少磁盘的空间占用),而且不需要支持事务时,MyISAM是最好的选择。 diff --git "a/docs/essential-content-for-interview/\351\235\242\350\257\225\345\277\205\345\244\207\344\271\213\344\271\220\350\247\202\351\224\201\344\270\216\346\202\262\350\247\202\351\224\201.md" "b/docs/essential-content-for-interview/\351\235\242\350\257\225\345\277\205\345\244\207\344\271\213\344\271\220\350\247\202\351\224\201\344\270\216\346\202\262\350\247\202\351\224\201.md" index b6f40fbc31b..00aaecd8c5b 100644 --- "a/docs/essential-content-for-interview/\351\235\242\350\257\225\345\277\205\345\244\207\344\271\213\344\271\220\350\247\202\351\224\201\344\270\216\346\202\262\350\247\202\351\224\201.md" +++ "b/docs/essential-content-for-interview/\351\235\242\350\257\225\345\277\205\345\244\207\344\271\213\344\271\220\350\247\202\351\224\201\344\270\216\346\202\262\350\247\202\351\224\201.md" @@ -1,3 +1,22 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 + + + +- [何谓悲观锁与乐观锁](#何谓悲观锁与乐观锁) + - [悲观锁](#悲观锁) + - [乐观锁](#乐观锁) + - [两种锁的使用场景](#两种锁的使用场景) +- [乐观锁常见的两种实现方式](#乐观锁常见的两种实现方式) + - [1. 版本号机制](#1-版本号机制) + - [2. CAS算法](#2-cas算法) +- [乐观锁的缺点](#乐观锁的缺点) + - [1 ABA 问题](#1-aba-问题) + - [2 循环时间长开销大](#2-循环时间长开销大) + - [3 只能保证一个共享变量的原子操作](#3-只能保证一个共享变量的原子操作) +- [CAS与synchronized的使用情景](#cas与synchronized的使用情景) + + + ### 何谓悲观锁与乐观锁 > 乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。 @@ -64,8 +83,6 @@ JDK 1.5 以后的 `AtomicStampedReference 类`就提供了此种能力,其中 CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5开始,提供了`AtomicReference类`来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用`AtomicReference类`把多个共享变量合并成一个共享变量来操作。 - - ### CAS与synchronized的使用情景 > **简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多)** @@ -73,10 +90,17 @@ CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 1. 对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。 2. 对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。 - 补充: Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为 **“重量级锁”** 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 **偏向锁** 和 **轻量级锁** 以及其它**各种优化**之后变得在某些情况下并不是那么重了。synchronized的底层实现主要依靠 **Lock-Free** 的队列,基本思路是 **自旋后阻塞**,**竞争切换后继续竞争锁**,**稍微牺牲了公平性,但获得了高吞吐量**。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。 +## 公众号 + +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 + +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"面试突击"** 即可免费领取! + +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/github-trending/2019-4.md b/docs/github-trending/2019-4.md new file mode 100644 index 00000000000..713a76da642 --- /dev/null +++ b/docs/github-trending/2019-4.md @@ -0,0 +1,98 @@ +以下涉及到的数据统计与 2019 年 5 月 1 日 12 点,数据来源: 。 + +下面的内容从 Java 学习文档到最热门的框架再到热门的工具应有尽有,比如下面推荐到的开源项目 Hutool 就是近期比较热门的项目之一,它是 Java 工具包,能够帮助我们简化代码!我觉得下面这些项目对于学习 Java 的朋友还是很有帮助的! + + +### 1. JavaGuide + +- **Github 地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide) +- **Star**: 37.9k (5,660 stars this month) +- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。 + +### 2. advanced-java + +- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) +- **Star**: 15.1k (4,654 stars this month) +- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。 + +### 3. CS-Notes + +- **Github 地址**: +- **Star**: 59.2k (4,012 stars this month) +- **介绍**: 技术面试必备基础知识。 + +### 4. ghidra + +- **Github 地址**: +- **Star**: 15.0k (2,995 stars this month) +- **介绍**: Ghidra是一个软件逆向工程(SRE)框架。 + +### 5. mall + +- **Github 地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) +- **star**: 11.6 k (2,100 stars this month) +- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 + +### 6. ZXBlog + +- **Github 地址**: +- **star**: 2.1 k (2,086 stars this month) +- **介绍**: 记录各种学习笔记(算法、Java、数据库、并发......)。 + +### 7.DoraemonKit + +- **Github地址**: +- **Star**: 7.6k (1,541 stars this month) +- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。 + +### 8. spring-boot + +- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot) +- **star:** 37.3k (1,489 stars this month) +- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。 + +**Spring Boot官方的介绍:** + +> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可) + +### 9. spring-boot-examples + +- **Github 地址**:[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples) +- **Star**: 12.8k (1,453 stars this month) +- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。 + +### 10. seata + +- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata) +- **star**: 8.4 k (1441 stars this month) +- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。 + +### 11. litemall + +- **Github 地址**:[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples) +- **Star**: 6.0k (1,427 stars this month) +- **介绍**: 又一个小商城。litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端。 + +### 12. skywalking + +- **Github 地址**: +- **Star**: 8.0k (1,381 stars this month) +- **介绍**: 针对分布式系统的应用性能监控,尤其是针对微服务、云原生和面向容器的分布式系统架构。 + +### 13. elasticsearch + +- **Github 地址** [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch) +- **Star**: 4.0k (1,068stars this month) +- **介绍**: 开源,分布式,RESTful 搜索引擎。 + +### 14. arthas + +- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas) +- **star**: 12.6 k (1,080 stars this month) +- **介绍**: Arthas 是Alibaba开源的Java诊断工具。 + +### 15. hutool + +- **Github地址**: +- **star**: 4.5 k (1,031 stars this month) +- **介绍**: Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。Hutool最初是我项目中“util”包的一个整理,后来慢慢积累并加入更多非业务相关功能,并广泛学习其它开源项目精髓,经过自己整理修改,最终形成丰富的开源工具集。官网: 。 \ No newline at end of file diff --git a/docs/github-trending/2019-5.md b/docs/github-trending/2019-5.md new file mode 100644 index 00000000000..1ac28b22cd7 --- /dev/null +++ b/docs/github-trending/2019-5.md @@ -0,0 +1,125 @@ +以下涉及到的数据统计与 2019 年 6 月 1 日 18 点,数据来源: 。下面推荐的内容从 Java 学习文档到最热门的框架再到热门的工具应有尽有,建议收藏+在看! + +### 1.LeetCodeAnimation + +- **Github 地址**: +- **Star**: 29.0k (11,492 stars this month) +- **介绍**: Demonstrate all the questions on LeetCode in the form of animation.(用动画的形式呈现解LeetCode题目的思路)。 + +### 2.CS-Notes + +- **Github 地址**: +- **Star**: 64.4k (5513 stars this month) +- **介绍**: 技术面试必备基础知识、Leetcode 题解、后端面试、Java 面试、春招、秋招、操作系统、计算机网络、系统设计。 + +### 3.JavaGuide + +- **Github 地址**: +- **Star**: 42.0k (4,442 stars this month) +- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。 + +### 4.mall + +- **Github 地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) +- **star**: 14.6 k (3,086 stars this month) +- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 + +### 5.advanced-java + +- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) +- **Star**: 20.8k (2,394 stars this month) +- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。 + +### 6.spring-boot + +- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot) +- **star:** 38.5k (1,339 stars this month) +- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。 + +**Spring Boot官方的介绍:** + +> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可) + +### 7. Java + +- **Github 地址**: +- **Star**:14.3k (1,334 stars this month) +- **介绍**: All Algorithms implemented in Java。 + +### 8.server + +- **Github 地址**: +- **star**: 2.2 k (1,275 stars this month) +- **介绍**: 全开源即时通讯(IM)系统。 + +### 9.litemall + +- **Github 地址**: +- **Star**: 7.1k (1,114 stars this month) +- **介绍**: 又一个小商城。litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端。 + +### 10.Linkage-RecyclerView + +- **Github 地址**: +- **Star**: 10.0k (1,093 stars this month) +- **介绍**: 即使不用饿了么订餐,也请务必收藏好该库!🔥 一行代码即可接入,二级联动订餐列表 - Even if you don't order food by PrubHub, be sure to collect this library, please! 🔥 This secondary linkage list widget can be accessed by only one line of code. Supporting by RecyclerView & AndroidX. + +### 11.toBeTopJavaer + +- **Github 地址** : +- **Star**: 3.3k (1,007 stars this month) +- **介绍**: To Be Top Javaer - Java工程师成神之路 + +### 12.elasticsearch + +- **Github 地址** : [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch) +- **Star**: 48.0k (968 stars this month) +- **介绍**: Design patterns implemented in Java。 + +### 13.java-design-patterns + +- **Github 地址** : +- **Star**: 41.5k (955 stars this month) +- **介绍**: 开源,分布式,RESTful 搜索引擎。 + +### 14.apollo + +- **Github 地址** : +- **Star**: 14.5k (927 stars this month) +- **介绍**: Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。 + +### 15.arthas + +- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas) +- **star**: 13.5 k (933 stars this month) +- **介绍**: Arthas 是Alibaba开源的Java诊断工具。 + +### 16.dubbo + +- **Github地址**: +- **star**: 26.9 k (769 stars this month) +- **介绍**: Apache Dubbo是一个基于Java的高性能开源RPC框架。 + +### 17.DoraemonKit + +- **Github地址**: +- **Star**: 8.5k (909 stars this month) +- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。 + +### 18.halo + +- **Github地址**: +- **Star**: 4.1k (829 stars this month) +- **介绍**: Halo 可能是最好的 Java 博客系统。 + +### 19.seata + +- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata) +- **star**: 9.2 k (776 stars this month) +- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。 + +### 20.hutool + +- **Github地址**: +- **star**: 5,3 k (812 stars this month) +- **介绍**: Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。Hutool最初是我项目中“util”包的一个整理,后来慢慢积累并加入更多非业务相关功能,并广泛学习其它开源项目精髓,经过自己整理修改,最终形成丰富的开源工具集。官网: 。 \ No newline at end of file diff --git a/docs/github-trending/2019-6.md b/docs/github-trending/2019-6.md new file mode 100644 index 00000000000..2a395e160d6 --- /dev/null +++ b/docs/github-trending/2019-6.md @@ -0,0 +1,119 @@ +### 1.CS-Notes + +- **Github 地址**:https://github.com/CyC2018/CS-Notes +- **Star**: 69.8k +- **介绍**: 技术面试必备基础知识、Leetcode 题解、后端面试、Java 面试、春招、秋招、操作系统、计算机网络、系统设计。 + +### 2.toBeTopJavaer + +- **Github 地址:**[https://github.com/hollischuang/toBeTopJavaer](https://github.com/hollischuang/toBeTopJavaer) +- **Star**: 4.7k +- **介绍**: To Be Top Javaer - Java工程师成神之路。 + +### 3.p3c + +- **Github 地址:** [https://github.com/alibaba/p3c](https://github.com/alibaba/p3c) +- **Star**: 16.6k +- **介绍**: Alibaba Java Coding Guidelines pmd implements and IDE plugin。Eclipse 和 IDEA 上都有该插件,推荐使用! + +### 4.SpringCloudLearning + +- **Github 地址:** [https://github.com/forezp/SpringCloudLearning](https://github.com/forezp/SpringCloudLearning) +- **Star**: 8.7k +- **介绍**: 史上最简单的Spring Cloud教程源码。 + +### 5.dubbo + +- **Github地址**: +- **star**: 27.6 k +- **介绍**: Apache Dubbo是一个基于Java的高性能开源RPC框架。 + +### 6.jeecg-boot + +- **Github地址**: [https://github.com/zhangdaiscott/jeecg-boot](https://github.com/zhangdaiscott/jeecg-boot) +- **star**: 3.3 k +- **介绍**: 一款基于代码生成器的JAVA快速开发平台!全新架构前后端分离:SpringBoot 2.x,Ant Design&Vue,Mybatis,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任何代码,绝对是全栈开发福音!! JeecgBoot的宗旨是提高UI能力的同时,降低前后分离的开发成本,JeecgBoot还独创在线开发模式,No代码概念,一系列在线智能开发:在线配置表单、在线配置报表、在线设计流程等等。 + +### 7.advanced-java + +- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) +- **Star**: 24.2k +- **介绍**: 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务等领域知识,后端同学必看,前端同学也可学习。 + +### 8.FEBS-Shiro + +- **Github 地址**:[https://github.com/wuyouzhuguli/FEBS-Shiro](https://github.com/wuyouzhuguli/FEBS-Shiro) +- **Star**: 2.6k +- **介绍**: Spring Boot 2.1.3,Shiro1.4.0 & Layui 2.5.4 权限管理系统。预览地址:http://49.234.20.223:8080/login。 + +### 9.SpringAll + +- **Github 地址**: [https://github.com/wuyouzhuguli/SpringAll](https://github.com/wuyouzhuguli/SpringAll) +- **Star**: 5.4k +- **介绍**: 循序渐进,学习Spring Boot、Spring Boot & Shiro、Spring Cloud、Spring Security & Spring Security OAuth2,博客Spring系列源码。 + +### 10.JavaGuide + +- **Github 地址**: +- **Star**: 47.2k +- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。 + +### 11.vhr + +- **Github 地址**:[https://github.com/lenve/vhr](https://github.com/lenve/vhr) +- **Star**: 4.9k +- **介绍**: 微人事是一个前后端分离的人力资源管理系统,项目采用SpringBoot+Vue开发。 + +### 12. tutorials + +- **Github 地址**:[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials) +- **star**: 15.4 k +- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖 Java 生态系统中单一且定义明确的开发领域。 当然,它们的重点是 Spring Framework - Spring,Spring Boot 和 Spring Securiyt。 除了 Spring 之外,还有以下技术:核心 Java,Jackson,HttpClient,Guava。 + +### 13.EasyScheduler + +- **Github 地址**:[https://github.com/analysys/EasyScheduler](https://github.com/analysys/EasyScheduler) +- **star**: 1.1 k +- **介绍**: Easy Scheduler是一个分布式工作流任务调度系统,主要解决“复杂任务依赖但无法直接监控任务健康状态”的问题。Easy Scheduler以DAG方式组装任务,可以实时监控任务的运行状态。同时,它支持重试,重新运行等操作... 。https://analysys.github.io/easyscheduler_docs_cn/ + +### 14.thingsboard + +- **Github 地址**:[https://github.com/thingsboard/thingsboard](https://github.com/thingsboard/thingsboard) +- **star**: 3.7 k +- **介绍**: 开源物联网平台 - 设备管理,数据收集,处理和可视化。 [https://thingsboard.io](https://thingsboard.io/) + +### 15.mall-learning + +- **Github 地址**: [https://github.com/macrozheng/mall-learning](https://github.com/macrozheng/mall-learning) +- **star**: 0.6 k +- **介绍**: mall学习教程,架构、业务、技术要点全方位解析。mall项目(16k+star)是一套电商系统,使用现阶段主流技术实现。 涵盖了SpringBoot2.1.3、MyBatis3.4.6、Elasticsearch6.2.2、RabbitMQ3.7.15、Redis3.2、Mongodb3.2、Mysql5.7等技术,采用Docker容器化部署。 https://github.com/macrozheng/mall + +### 16. flink + +- **Github地址**:[https://github.com/apache/flink](https://github.com/apache/flink) +- **star**: 9.3 k +- **介绍**: Apache Flink是一个开源流处理框架,具有强大的流和批处理功能。 + +### 17.spring-cloud-kubernetes + +- **Github地址**:[https://github.com/spring-cloud/spring-cloud-kubernetes](https://github.com/spring-cloud/spring-cloud-kubernetes) +- **star**: 1.4 k +- **介绍**: Kubernetes 集成 Spring Cloud Discovery Client, Configuration, etc... + +### 18.springboot-learning-example + +- **Github地址**:[https://github.com/JeffLi1993/springboot-learning-example](https://github.com/JeffLi1993/springboot-learning-example) +- **star**: 10.0 k +- **介绍**: spring boot 实践学习案例,是 spring boot 初学者及核心技术巩固的最佳实践。 + +### 19.canal + +- **Github地址**:[https://github.com/alibaba/canal](https://github.com/alibaba/canal) +- **star**: 9.3 k +- **介绍**: 阿里巴巴 MySQL binlog 增量订阅&消费组件。 + +### 20.react-native-device-info + +- **Github地址**:[https://github.com/react-native-community/react-native-device-info](https://github.com/react-native-community/react-native-device-info) +- **star**: 4.0 k +- **介绍**: React Native iOS和Android的设备信息。 \ No newline at end of file diff --git a/docs/github-trending/JavaGithubTrending.md b/docs/github-trending/JavaGithubTrending.md index 0639622212f..91d544ed37e 100644 --- a/docs/github-trending/JavaGithubTrending.md +++ b/docs/github-trending/JavaGithubTrending.md @@ -1,4 +1,8 @@ - [2018 年 12 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2018-12.md) - [2019 年 1 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-1.md) - [2019 年 2 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-2.md) +- [2019 年 3 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-3.md) +- [2019 年 4 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-4.md) +- [2019 年 5 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-5.md) +- [2019 年 6 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-6.md) diff --git a/docs/java/BIO-NIO-AIO.md b/docs/java/BIO-NIO-AIO.md index c5ec6dddd04..ee8e751cd6d 100644 --- a/docs/java/BIO-NIO-AIO.md +++ b/docs/java/BIO-NIO-AIO.md @@ -42,7 +42,7 @@ - **阻塞:** 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。 - **非阻塞:** 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。 -举个生活中简单的例子,你妈妈让你烧水,小时候你比较笨啊,在哪里傻等着水开(**同步阻塞**)。等你稍微再长大一点,你知道每次烧水的空隙可以去干点其他事,然后只需要时不时来看看水开了没有(**同步非阻塞**)。后来,你们家用上了水开了会发出声音的壶,这样你就只需要听到响声后就知道水开了,在这期间你可以随便干自己的事情,你需要去倒水了(**异步非阻塞**)。 +举个生活中简单的例子,你妈妈让你烧水,小时候你比较笨啊,在那里傻等着水开(**同步阻塞**)。等你稍微再长大一点,你知道每次烧水的空隙可以去干点其他事,然后只需要时不时来看看水开了没有(**同步非阻塞**)。后来,你们家用上了水开了会发出声音的壶,这样你就只需要听到响声后就知道水开了,在这期间你可以随便干自己的事情,你需要去倒水了(**异步非阻塞**)。 ## 1. BIO (Blocking I/O) @@ -73,7 +73,7 @@ BIO通信(一请求一应答)模型图如下(图源网络,原出处不明) 采用线程池和任务队列可以实现一种叫做伪异步的 I/O 通信框架,它的模型图如上图所示。当有新的客户端接入时,将客户端的 Socket 封装成一个Task(该任务实现java.lang.Runnable接口)投递到后端的线程池中进行处理,JDK 的线程池维护一个消息队列和 N 个活跃线程,对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。 -伪异步I/O通信框架采用了线程池实现,因此避免了为每个请求都创建一个独立线程造成的线程资源耗尽问题。不过因为它的底层任然是同步阻塞的BIO模型,因此无法从根本上解决问题。 +伪异步I/O通信框架采用了线程池实现,因此避免了为每个请求都创建一个独立线程造成的线程资源耗尽问题。不过因为它的底层仍然是同步阻塞的BIO模型,因此无法从根本上解决问题。 ### 1.3 代码示例 @@ -202,13 +202,13 @@ NIO 通过Channel(通道) 进行读写。 通道是双向的,可读也可写,而流的读写是单向的。无论读写,通道只能和Buffer交互。因为 Buffer,通道可以异步地读写。 -#### 4)Selectors(选择器) +#### 4)Selector (选择器) NIO有选择器,而IO没有。 选择器用于使用单个线程处理多个通道。因此,它需要较少的线程来处理这些通道。线程之间的切换对于操作系统来说是昂贵的。 因此,为了提高系统效率选择器是有用的。 -![一个单线程中Slector维护3个Channel的示意图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-2/Slector.png) +![一个单线程中Selector维护3个Channel的示意图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-2/Slector.png) ### 2.3 NIO 读数据和写数据方式 通常来说NIO中的所有IO都是从 Channel(通道) 开始的。 @@ -273,8 +273,7 @@ public class NIOServer { if (key.isAcceptable()) { try { - // (1) - // 每来一个新连接,不需要创建一个线程,而是直接注册到clientSelector + // (1) 每来一个新连接,不需要创建一个线程,而是直接注册到clientSelector SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept(); clientChannel.configureBlocking(false); clientChannel.register(clientSelector, SelectionKey.OP_READ); diff --git "a/docs/java/J2EE\345\237\272\347\241\200\347\237\245\350\257\206.md" "b/docs/java/J2EE\345\237\272\347\241\200\347\237\245\350\257\206.md" index ced017ab47b..c1dff59bd00 100644 --- "a/docs/java/J2EE\345\237\272\347\241\200\347\237\245\350\257\206.md" +++ "b/docs/java/J2EE\345\237\272\347\241\200\347\237\245\350\257\206.md" @@ -1,3 +1,5 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 + - [Servlet总结](#servlet总结) @@ -26,7 +28,7 @@ ## Servlet总结 -在Java Web程序中,**Servlet**主要负责接收用户请求**HttpServletRequest**,在**doGet()**,**doPost()**中做相应的处理,并将回应**HttpServletResponse**反馈给用户。Servlet可以设置初始化参数,供Servlet内部使用。一个Servlet类只会有一个实例,在它初始化时调用**init()方法**,销毁时调用**destroy()方法**。**Servlet需要在web.xml中配置**(MyEclipse中创建Servlet会自动配置),**一个Servlet可以设置多个URL访问**。**Servlet不是线程安全**,因此要谨慎使用类变量。 +在Java Web程序中,**Servlet**主要负责接收用户请求 `HttpServletRequest`,在`doGet()`,`doPost()`中做相应的处理,并将回应`HttpServletResponse`反馈给用户。**Servlet** 可以设置初始化参数,供Servlet内部使用。一个Servlet类只会有一个实例,在它初始化时调用`init()`方法,销毁时调用`destroy()`方法**。**Servlet需要在web.xml中配置(MyEclipse中创建Servlet会自动配置),**一个Servlet可以设置多个URL访问**。**Servlet不是线程安全**,因此要谨慎使用类变量。 ## 阐述Servlet和CGI的区别? @@ -55,11 +57,11 @@ ## Servlet接口中有哪些方法及Servlet生命周期探秘 Servlet接口定义了5个方法,其中**前三个方法与Servlet生命周期相关**: -- **void init(ServletConfig config) throws ServletException** -- **void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException** -- **void destory()** -- java.lang.String getServletInfo() -- ServletConfig getServletConfig() +- `void init(ServletConfig config) throws ServletException` +- `void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException` +- `void destroy()` +- `java.lang.String getServletInfo()` +- `ServletConfig getServletConfig()` **生命周期:** **Web容器加载Servlet并将其实例化后,Servlet生命周期开始**,容器运行其**init()方法**进行Servlet的初始化;请求到达时调用Servlet的**service()方法**,service()方法会根据需要调用与请求对应的**doGet或doPost**等方法;当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的**destroy()方法**。**init方法和destroy方法只会执行一次,service方法客户端每次请求Servlet都会执行**。Servlet中有时会用到一些需要初始化与销毁的资源,因此可以把初始化资源的代码放入init方法中,销毁资源的代码放入destroy方法中,这样就不需要每次处理客户端的请求都要初始化与销毁资源。 @@ -93,7 +95,7 @@ Form标签里的method的属性为get时调用doGet(),为post时调用doPost() **转发是服务器行为,重定向是客户端行为。** -**转发(Forword)** +**转发(Forward)** 通过RequestDispatcher对象的forward(HttpServletRequest request,HttpServletResponse response)方法实现的。RequestDispatcher可以通过HttpServletRequest 的getRequestDispatcher()方法获得。例如下面的代码就是跳转到login_success.jsp页面。 ```java request.getRequestDispatcher("login_success.jsp").forward(request, response); @@ -143,13 +145,11 @@ Response.setHeader("Refresh","5;URL=http://localhost:8080/servlet/example.htm"); JSP是一种Servlet,但是与HttpServlet的工作方式不太一样。HttpServlet是先由源代码编译为class文件后部署到服务器下,为先编译后部署。而JSP则是先部署后编译。JSP会在客户端第一次请求JSP文件时被编译为HttpJspPage类(接口Servlet的一个子类)。该类会被服务器临时存放在服务器工作目录里面。下面通过实例给大家介绍。 工程JspLoginDemo下有一个名为login.jsp的Jsp文件,把工程第一次部署到服务器上后访问这个Jsp文件,我们发现这个目录下多了下图这两个东东。 .class文件便是JSP对应的Servlet。编译完毕后再运行class文件来响应客户端请求。以后客户端访问login.jsp的时候,Tomcat将不再重新编译JSP文件,而是直接调用class文件来响应客户端请求。 -![JSP工作原理](https://user-gold-cdn.xitu.io/2018/3/31/1627bee073079a28?w=675&h=292&f=jpeg&s=133553) +![JSP工作原理](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/1.png) 由于JSP只会在客户端第一次请求的时候被编译 ,因此第一次请求JSP时会感觉比较慢,之后就会感觉快很多。如果把服务器保存的class文件删除,服务器也会重新编译JSP。 开发Web程序时经常需要修改JSP。Tomcat能够自动检测到JSP程序的改动。如果检测到JSP源代码发生了改动。Tomcat会在下次客户端请求JSP时重新编译JSP,而不需要重启Tomcat。这种自动检测功能是默认开启的,检测改动会消耗少量的时间,在部署Web应用的时候可以在web.xml中将它关掉。 - - 参考:《javaweb整合开发王者归来》P97 ## JSP有哪些内置对象、作用分别是什么 @@ -195,31 +195,31 @@ JSP有9个内置对象: ## request.getAttribute()和 request.getParameter()有何区别 **从获取方向来看:** -getParameter()是获取 POST/GET 传递的参数值; +`getParameter()`是获取 POST/GET 传递的参数值; -getAttribute()是获取对象容器中的数据值; +`getAttribute()`是获取对象容器中的数据值; **从用途来看:** -getParameter用于客户端重定向时,即点击了链接或提交按扭时传值用,即用于在用表单或url重定向传值时接收数据用。 +`getParameter()`用于客户端重定向时,即点击了链接或提交按扭时传值用,即用于在用表单或url重定向传值时接收数据用。 -getAttribute用于服务器端重定向时,即在 sevlet 中使用了 forward 函数,或 struts 中使用了 +`getAttribute()` 用于服务器端重定向时,即在 sevlet 中使用了 forward 函数,或 struts 中使用了 mapping.findForward。 getAttribute 只能收到程序用 setAttribute 传过来的值。 -另外,可以用 setAttribute,getAttribute 发送接收对象.而 getParameter 显然只能传字符串。 -setAttribute 是应用服务器把这个对象放在该页面所对应的一块内存中去,当你的页面服务器重定向到另一个页面时,应用服务器会把这块内存拷贝另一个页面所对应的内存中。这样getAttribute就能取得你所设下的值,当然这种方法可以传对象。session也一样,只是对象在内存中的生命周期不一样而已。getParameter只是应用服务器在分析你送上来的 request页面的文本时,取得你设在表单或 url 重定向时的值。 +另外,可以用 `setAttribute()`,`getAttribute()` 发送接收对象.而 `getParameter()` 显然只能传字符串。 +`setAttribute()` 是应用服务器把这个对象放在该页面所对应的一块内存中去,当你的页面服务器重定向到另一个页面时,应用服务器会把这块内存拷贝另一个页面所对应的内存中。这样`getAttribute()`就能取得你所设下的值,当然这种方法可以传对象。session也一样,只是对象在内存中的生命周期不一样而已。`getParameter()`只是应用服务器在分析你送上来的 request页面的文本时,取得你设在表单或 url 重定向时的值。 **总结:** -getParameter 返回的是String,用于读取提交的表单中的值;(获取之后会根据实际需要转换为自己需要的相应类型,比如整型,日期类型啊等等) +`getParameter()`返回的是String,用于读取提交的表单中的值;(获取之后会根据实际需要转换为自己需要的相应类型,比如整型,日期类型啊等等) -getAttribute 返回的是Object,需进行转换,可用setAttribute 设置成任意对象,使用很灵活,可随时用 +`getAttribute()`返回的是Object,需进行转换,可用`setAttribute()`设置成任意对象,使用很灵活,可随时用 ## include指令include的行为的区别 **include指令:** JSP可以通过include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。 语法格式如下: <%@ include file="文件相对 url 地址" %> -i**nclude动作:** 动作元素用来包含静态和动态的文件。该动作把指定文件插入正在生成的页面。语法格式如下: +i**nclude动作:** ``动作元素用来包含静态和动态的文件。该动作把指定文件插入正在生成的页面。语法格式如下: ## JSP九大内置对象,七大动作,三大指令 @@ -232,11 +232,9 @@ JSP中的四种作用域包括page、request、session和application,具体来 - **session**代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。 - **application**代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。 - - ## 如何实现JSP或Servlet的单线程模式 对于JSP页面,可以通过page指令进行设置。 -<%@page isThreadSafe=”false”%> +`<%@page isThreadSafe=”false”%>` 对于Servlet,可以让自定义的Servlet实现SingleThreadModel标识接口。 @@ -294,12 +292,20 @@ if(cookies !=null){ 在所有会话跟踪技术中,HttpSession对象是最强大也是功能最多的。当一个用户第一次访问某个网站时会自动创建 HttpSession,每个用户可以访问他自己的HttpSession。可以通过HttpServletRequest对象的getSession方 法获得HttpSession,通过HttpSession的setAttribute方法可以将一个值放在HttpSession中,通过调用 HttpSession对象的getAttribute方法,同时传入属性名就可以获取保存在HttpSession中的对象。与上面三种方式不同的 是,HttpSession放在服务器的内存中,因此不要将过大的对象放在里面,即使目前的Servlet容器可以在内存将满时将HttpSession 中的对象移到其他存储设备中,但是这样势必影响性能。添加到HttpSession中的值可以是任意Java对象,这个对象最好实现了 Serializable接口,这样Servlet容器在必要的时候可以将其序列化到文件中,否则在序列化时就会出现异常。 ## Cookie和Session的的区别 -1. 由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。 -2. 思考一下服务端如何识别特定的客户?这个时候Cookie就登场了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。 -3. Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。所以,总结一下:Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。 +Cookie 和 Session都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。 + + **Cookie 一般用来保存用户信息** 比如①我们在 Cookie 中保存已经登录过得用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了;②一般的网站都会有保持登录也就是说下次你再访问网站的时候就不需要重新登录了,这是因为用户登录的时候我们可以存放了一个 Token 在 Cookie 中,下次登录的时候只需要根据 Token 值来查找用户即可(为了安全考虑,重新登录一般要将 Token 重写);③登录一次网站后访问网站其他页面不需要重新登录。**Session 的主要作用就是通过服务端记录用户的状态。** 典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。 + +Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。 + +Cookie 存储在客户端中,而Session存储在服务器上,相对来说 Session 安全性更高。如果使用 Cookie 的一些敏感信息不要写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。 + +## 公众号 + +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 -参考: +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! -https://www.zhihu.com/question/19786827/answer/28752144 +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 -《javaweb整合开发王者归来》P158 Cookie和Session的比较 +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) \ No newline at end of file diff --git "a/docs/java/Java IO\344\270\216NIO.md" "b/docs/java/Java IO\344\270\216NIO.md" index 905df527c40..74bd850e696 100644 --- "a/docs/java/Java IO\344\270\216NIO.md" +++ "b/docs/java/Java IO\344\270\216NIO.md" @@ -27,12 +27,12 @@ **(1) 按操作方式分类结构图:** -![按操作方式分类结构图:](https://user-gold-cdn.xitu.io/2018/5/16/16367d4fd1ce1b46?w=720&h=1080&f=jpeg&s=69522) +![IO-操作方式分类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/IO-操作方式分类.png) **(2)按操作对象分类结构图** -![按操作对象分类结构图](https://user-gold-cdn.xitu.io/2018/5/16/16367d673b0e268d?w=720&h=535&f=jpeg&s=46081) +![IO-操作对象分类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/IO-操作对象分类.png) ### [二 java IO体系的学习总结](https://blog.csdn.net/nightcurtis/article/details/51324105) 1. **IO流的分类:** @@ -92,7 +92,7 @@ - 写入数据到缓冲区(Writing Data to a Buffer) **写数据到Buffer有两种方法:** - + 1.从Channel中写数据到Buffer ```java int bytesRead = inChannel.read(buf); //read into buffer. @@ -103,7 +103,7 @@ ``` 4. **Buffer常用方法测试** - + 说实话,NIO编程真的难,通过后面这个测试例子,你可能才能勉强理解前面说的Buffer方法的作用。 diff --git "a/docs/java/Java\345\237\272\347\241\200\347\237\245\350\257\206.md" "b/docs/java/Java\345\237\272\347\241\200\347\237\245\350\257\206.md" index 69bc85d590e..42f37d1f365 100644 --- "a/docs/java/Java\345\237\272\347\241\200\347\237\245\350\257\206.md" +++ "b/docs/java/Java\345\237\272\347\241\200\347\237\245\350\257\206.md" @@ -1,73 +1,73 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 - - + - [1. 面向对象和面向过程的区别](#1-面向对象和面向过程的区别) - - [面向过程](#面向过程) - - [面向对象](#面向对象) -- [2. Java 语言有哪些特点](#2-java-语言有哪些特点) +- [2. Java 语言有哪些特点?](#2-java-语言有哪些特点) - [3. 关于 JVM JDK 和 JRE 最详细通俗的解答](#3-关于-jvm-jdk-和-jre-最详细通俗的解答) - - [JVM](#jvm) - - [JDK 和 JRE](#jdk-和-jre) + - [JVM](#jvm) + - [JDK 和 JRE](#jdk-和-jre) - [4. Oracle JDK 和 OpenJDK 的对比](#4-oracle-jdk-和-openjdk-的对比) -- [5. Java和C++的区别](#5-java和c的区别) -- [6. 什么是 Java 程序的主类 应用程序和小程序的主类有何不同](#6-什么是-java-程序的主类-应用程序和小程序的主类有何不同) -- [7. Java 应用程序与小程序之间有那些差别](#7-java-应用程序与小程序之间有那些差别) -- [8. 字符型常量和字符串常量的区别](#8-字符型常量和字符串常量的区别) -- [9. 构造器 Constructor 是否可被 override](#9-构造器-constructor-是否可被-override) +- [5. Java和C++的区别?](#5-java和c的区别) +- [6. 什么是 Java 程序的主类 应用程序和小程序的主类有何不同?](#6-什么是-java-程序的主类-应用程序和小程序的主类有何不同) +- [7. Java 应用程序与小程序之间有哪些差别?](#7-java-应用程序与小程序之间有哪些差别) +- [8. 字符型常量和字符串常量的区别?](#8-字符型常量和字符串常量的区别) +- [9. 构造器 Constructor 是否可被 override?](#9-构造器-constructor-是否可被-override) - [10. 重载和重写的区别](#10-重载和重写的区别) - [11. Java 面向对象编程三大特性: 封装 继承 多态](#11-java-面向对象编程三大特性-封装-继承-多态) - - [封装](#封装) - - [继承](#继承) - - [多态](#多态) -- [12. String StringBuffer 和 StringBuilder 的区别是什么 String 为什么是不可变的](#12-string-stringbuffer-和-stringbuilder-的区别是什么-string-为什么是不可变的) + - [封装](#封装) + - [继承](#继承) + - [多态](#多态) +- [12. String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?](#12-string-stringbuffer-和-stringbuilder-的区别是什么-string-为什么是不可变的) - [13. 自动装箱与拆箱](#13-自动装箱与拆箱) -- [14. 在一个静态方法内调用一个非静态成员为什么是非法的](#14-在一个静态方法内调用一个非静态成员为什么是非法的) +- [14. 在一个静态方法内调用一个非静态成员为什么是非法的?](#14-在一个静态方法内调用一个非静态成员为什么是非法的) - [15. 在 Java 中定义一个不做事且没有参数的构造方法的作用](#15-在-java-中定义一个不做事且没有参数的构造方法的作用) -- [16. import java和javax有什么区别](#16-import-java和javax有什么区别) -- [17. 接口和抽象类的区别是什么](#17-接口和抽象类的区别是什么) -- [18. 成员变量与局部变量的区别有那些](#18-成员变量与局部变量的区别有那些) +- [16. import java和javax有什么区别?](#16-import-java和javax有什么区别) +- [17. 接口和抽象类的区别是什么?](#17-接口和抽象类的区别是什么) +- [18. 成员变量与局部变量的区别有哪些?](#18-成员变量与局部变量的区别有哪些) - [19. 创建一个对象用什么运算符?对象实体与对象引用有何不同?](#19-创建一个对象用什么运算符对象实体与对象引用有何不同) - [20. 什么是方法的返回值?返回值在类的方法里的作用是什么?](#20-什么是方法的返回值返回值在类的方法里的作用是什么) -- [21. 一个类的构造方法的作用是什么 若一个类没有声明构造方法,该程序能正确执行吗 ?为什么?](#21-一个类的构造方法的作用是什么-若一个类没有声明构造方法该程序能正确执行吗-为什么) -- [22. 构造方法有哪些特性](#22-构造方法有哪些特性) +- [21. 一个类的构造方法的作用是什么? 若一个类没有声明构造方法,该程序能正确执行吗? 为什么?](#21-一个类的构造方法的作用是什么-若一个类没有声明构造方法该程序能正确执行吗-为什么) +- [22. 构造方法有哪些特性?](#22-构造方法有哪些特性) - [23. 静态方法和实例方法有何不同](#23-静态方法和实例方法有何不同) -- [24. 对象的相等与指向他们的引用相等,两者有什么不同?](#24-对象的相等与指向他们的引用相等两者有什么不同) -- [25. 在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?](#25-在调用子类构造方法之前会先调用父类没有参数的构造方法其目的是) -- [26. == 与 equals\(重要\)](#26--与-equals重要) -- [27. hashCode 与 equals \(重要\)](#27-hashcode-与-equals-重要) - - [hashCode()介绍](#hashcode()介绍) - - [为什么要有 hashCode](#为什么要有-hashcode) - - [hashCode()与equals()的相关规定](#hashcode()与equals()的相关规定) -- [28. 为什么Java中只有值传递](#28-为什么java中只有值传递) -- [29. 简述线程,程序、进程的基本概念。以及他们之间关系是什么](#29-简述线程程序进程的基本概念以及他们之间关系是什么) +- [24. 对象的相等与指向他们的引用相等,两者有什么不同?](#24-对象的相等与指向他们的引用相等两者有什么不同) +- [25. 在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?](#25-在调用子类构造方法之前会先调用父类没有参数的构造方法其目的是) +- [26. == 与 equals(重要)](#26--与-equals重要) +- [27. hashCode 与 equals (重要)](#27-hashcode-与-equals-重要) + - [hashCode()介绍](#hashcode介绍) + - [为什么要有 hashCode](#为什么要有-hashcode) + - [hashCode()与equals()的相关规定](#hashcode与equals的相关规定) +- [28. 为什么Java中只有值传递?](#28-为什么java中只有值传递) +- [29. 简述线程、程序、进程的基本概念。以及他们之间关系是什么?](#29-简述线程程序进程的基本概念以及他们之间关系是什么) - [30. 线程有哪些基本状态?](#30-线程有哪些基本状态) - [31 关于 final 关键字的一些总结](#31-关于-final-关键字的一些总结) - [32 Java 中的异常处理](#32-java-中的异常处理) - - [Java异常类层次结构图](#java异常类层次结构图) - - [Throwable类常用方法](#throwable类常用方法) - - [异常处理总结](#异常处理总结) -- [33 Java序列化中如果有些字段不想进行序列化 怎么办](#33-java序列化中如果有些字段不想进行序列化-怎么办) -- [34 获取用键盘输入常用的的两种方法](#34-获取用键盘输入常用的的两种方法) + - [Java异常类层次结构图](#java异常类层次结构图) + - [Throwable类常用方法](#throwable类常用方法) + - [异常处理总结](#异常处理总结) +- [33 Java序列化中如果有些字段不想进行序列化,怎么办?](#33-java序列化中如果有些字段不想进行序列化怎么办) +- [34 获取用键盘输入常用的两种方法](#34-获取用键盘输入常用的两种方法) +- [35 Java 中 IO 流](#35-java-中-io-流) + - [Java 中 IO 流分为几种?](#java-中-io-流分为几种) + - [既然有了字节流,为什么还要有字符流?](#既然有了字节流为什么还要有字符流) + - [BIO,NIO,AIO 有什么区别?](#bionioaio-有什么区别) +- [36. 常见关键字总结:static,final,this,super](#36-常见关键字总结staticfinalthissuper) +- [37. Collections 工具类和 Arrays 工具类常见方法总结](#37-collections-工具类和-arrays-工具类常见方法总结) - [参考](#参考) - [公众号](#公众号) - - + ## 1. 面向对象和面向过程的区别 -### 面向过程 - -**优点:** 性能比面向对象高。因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发 +- **面向过程** :**面向过程性能比面向对象高。** 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发。但是,**面向过程没有面向对象易维护、易复用、易扩展。** +- **面向对象** :**面向对象易维护、易复用、易扩展。** 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,**面向对象性能比面向过程低**。 -**缺点:** 没有面向对象易维护、易复用、易扩展 +参见 issue : [面向过程 :面向过程性能比面向对象高??](https://github.com/Snailclimb/JavaGuide/issues/431) -### 面向对象 - -**优点:** 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护 - -**缺点:** 性能比面向过程低 +> 这个并不是根本原因,面向过程也需要分配内存,计算内存偏移量,Java性能差的主要原因并不是因为它是面向对象语言,而是Java是半编译语言,最终的执行代码并不是可以直接被CPU执行的二进制机械码。 +> +> 而面向过程语言大多都是直接编译成机械码在电脑上执行,并且其它一些面向过程的脚本语言性能也并不一定比Java好。 ## 2. Java 语言有哪些特点? @@ -98,7 +98,9 @@ Java虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM有针对不同 > HotSpot采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是JIT所需要编译的部分。JVM会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9引入了一种新的编译模式AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了JIT预热等各方面的开销。JDK支持分层编译和AOT协作使用。但是 ,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。 -总结:Java虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语言“一次编译,随处可以运行”的关键所在。 +**总结:** + +Java虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语言“一次编译,随处可以运行”的关键所在。 ### JDK 和 JRE @@ -118,9 +120,9 @@ JRE 是 Java运行时环境。它是运行已编译 Java 程序所需的所有 > > 答:非常接近 - 我们的Oracle JDK版本构建过程基于OpenJDK 7构建,只添加了几个部分,例如部署代码,其中包括Oracle的Java插件和Java WebStart的实现,以及一些封闭的源代码派对组件,如图形光栅化器,一些开源的第三方组件,如Rhino,以及一些零碎的东西,如附加文档或第三方字体。展望未来,我们的目的是开源Oracle JDK的所有部分,除了我们考虑商业功能的部分。 -总结: +**总结:** -1. Oracle JDK版本将每三年发布一次,而OpenJDK版本每三个月发布一次; +1. Oracle JDK大概每6个月发一次主要版本,而OpenJDK版本大概每三个月发布一次。但这不是固定的,我觉得了解这个没啥用处。详情参见:https://blogs.oracle.com/java-platform-group/update-and-faq-on-the-java-se-release-cadence。 2. OpenJDK 是一个参考模型并且是完全开源的,而Oracle JDK是OpenJDK的一个实现,并不是完全开源的; 3. Oracle JDK 比 OpenJDK 更稳定。OpenJDK和Oracle JDK的代码几乎相同,但Oracle JDK有更多的类和一些错误修复。因此,如果您想开发企业/商业软件,我建议您选择Oracle JDK,因为它经过了彻底的测试和稳定。某些情况下,有些人提到在使用OpenJDK 可能会遇到了许多应用程序崩溃的问题,但是,只需切换到Oracle JDK就可以解决问题; 4. 在响应性和JVM性能方面,Oracle JDK与OpenJDK相比提供了更好的性能; @@ -141,14 +143,14 @@ JRE 是 Java运行时环境。它是运行已编译 Java 程序所需的所有 一个程序中可以有多个类,但只能有一个类是主类。在 Java 应用程序中,这个主类是指包含 main()方法的类。而在 Java 小程序中,这个主类是一个继承自系统类 JApplet 或 Applet 的子类。应用程序的主类不一定要求是 public 类,但小程序的主类要求必须是 public 类。主类是 Java 程序执行的入口点。 -## 7. Java 应用程序与小程序之间有那些差别? +## 7. Java 应用程序与小程序之间有哪些差别? -简单说应用程序是从主线程启动(也就是 main() 方法)。applet 小程序没有main方法,主要是嵌在浏览器页面上运行(调用init()线程或者run()来启动),嵌入浏览器这点跟 flash 的小游戏类似。 +简单说应用程序是从主线程启动(也就是 `main()` 方法)。applet 小程序没有 `main()` 方法,主要是嵌在浏览器页面上运行(调用`init()`或者`run()`来启动),嵌入浏览器这点跟 flash 的小游戏类似。 ## 8. 字符型常量和字符串常量的区别? 1. 形式上: 字符常量是单引号引起的一个字符; 字符串常量是双引号引起的若干个字符 -2. 含义上: 字符常量相当于一个整形值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置) +2. 含义上: 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置) 3. 占内存大小 字符常量只占2个字节; 字符串常量占若干个字节(至少一个字符结束标志) (**注意: char在Java中占两个字节**) > java编程思想第四版:2.2.2节 @@ -160,9 +162,8 @@ JRE 是 Java运行时环境。它是运行已编译 Java 程序所需的所有 ## 10. 重载和重写的区别 -**重载:** 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。    - -**重写:** 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。 +- **重载:** 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。    +- **重写:** 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。 ## 11. Java 面向对象编程三大特性: 封装 继承 多态 @@ -189,7 +190,6 @@ JRE 是 Java运行时环境。它是运行已编译 Java 程序所需的所有 ## 12. String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的? **可变性** -  简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串,`private final char value[]`,所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串`char[]value` 但是没有用 final 关键字修饰,所以这两种对象都是可变的。 @@ -211,52 +211,53 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { **线程安全性** -String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。 -   +String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。  **性能** 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。 **对于三者使用的总结:** + 1. 操作少量的数据: 适用String 2. 单线程操作字符串缓冲区下操作大量数据: 适用StringBuilder 3. 多线程操作字符串缓冲区下操作大量数据: 适用StringBuffer ## 13. 自动装箱与拆箱 -**装箱**:将基本类型用它们对应的引用类型包装起来; -**拆箱**:将包装类型转换为基本数据类型; +- **装箱**:将基本类型用它们对应的引用类型包装起来; +- **拆箱**:将包装类型转换为基本数据类型; ## 14. 在一个静态方法内调用一个非静态成员为什么是非法的? 由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。 ## 15. 在 Java 中定义一个不做事且没有参数的构造方法的作用 -Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。 + +Java 程序在执行子类的构造方法之前,如果没有用 `super() `来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 `super() `来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。   ## 16. import java和javax有什么区别? -刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,但是,将扩展从 javax 包移动到 java 包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准API的一部分。 +刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准API的一部分。 所以,实际上java和javax没有区别。这都是一个名字。 ## 17. 接口和抽象类的区别是什么? 1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。 -2. 接口中的实例变量默认是 final 类型的,而抽象类中则不一定。 -3. 一个类可以实现多个接口,但最多只能实现一个抽象类。 -4. 一个类实现接口的话要实现接口的所有方法,而抽象类不一定。 -5. 接口不能用 new 实例化,但可以声明,但是必须引用一个实现该接口的对象。从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。 +2. 接口中除了static、final变量,不能有其他变量,而抽象类中则不一定。 +3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过extends关键字扩展多个接口。 +4. 接口方法默认修饰符是public,抽象方法可以有public、protected和default这些修饰符(抽象方法就是为了被重写所以不能使用private关键字修饰!)。 +5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。 备注:在JDK8中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。(详见issue:[https://github.com/Snailclimb/JavaGuide/issues/146](https://github.com/Snailclimb/JavaGuide/issues/146)) -## 18. 成员变量与局部变量的区别有那些? +## 18. 成员变量与局部变量的区别有哪些? 1. 从语法形式上看:成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。 -2. 从变量在内存中的存储方式来看:如果成员变量是使用`static`修饰的,那么这个成员变量是属于类的,如果没有使用使用`static`修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。 +2. 从变量在内存中的存储方式来看:如果成员变量是使用`static`修饰的,那么这个成员变量是属于类的,如果没有使用`static`修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。 3. 从变量在内存中的生存时间上看:成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。 -4. 成员变量如果没有被赋初值:则会自动以类型的默认值而赋值(一种情况例外被 final 修饰的成员变量也必须显示地赋值),而局部变量则不会自动赋值。 +4. 成员变量如果没有被赋初值:则会自动以类型的默认值而赋值(一种情况例外:被 final 修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。 ## 19. 创建一个对象用什么运算符?对象实体与对象引用有何不同? @@ -296,7 +297,7 @@ new运算符,new创建对象实例(对象实例在堆内存中),对象 **equals()** : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况: - 情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。 -- 情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。 +- 情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来比较两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。 **举个例子:** @@ -322,11 +323,10 @@ public class test1 { ``` **说明:** + - String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。 - 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。 - - ## 27. hashCode 与 equals (重要) 面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?” @@ -364,7 +364,7 @@ hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返 **程序**是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。 -**进程**是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 +**进程**是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。从另一角度来说,进程属于操作系统的范畴,主要是同一段时间内,可以同时执行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段。 ## 30. 线程有哪些基本状态? @@ -383,7 +383,7 @@ Java 线程在运行的生命周期中的指定时刻只可能处于下面6种 线程创建之后它将处于 **NEW(新建)** 状态,调用 `start()` 方法后开始运行,线程这时候处于 **READY(可运行)** 状态。可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 **RUNNING(运行)** 状态。 -> 操作系统隐藏 Java虚拟机(JVM)中的 RUNNABLE 和 RUNNING 状态,它只能看到 RUNNABLE 状态(图源:[HowToDoInJava](https://howtodoinjava.com/):[Java Thread Life Cycle and Thread States](https://howtodoinjava.com/java/multi-threading/java-thread-life-cycle-and-thread-states/)),所以 Java 系统一般将这两个状态统称为 **RUNNABLE(运行中)** 状态 。 +> 操作系统隐藏 Java虚拟机(JVM)中的 READY 和 RUNNING 状态,它只能看到 RUNNABLE 状态(图源:[HowToDoInJava](https://howtodoinjava.com/):[Java Thread Life Cycle and Thread States](https://howtodoinjava.com/java/multi-threading/java-thread-life-cycle-and-thread-states/)),所以 Java 系统一般将这两个状态统称为 **RUNNABLE(运行中)** 状态 。 ![RUNNABLE-VS-RUNNING](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3/RUNNABLE-VS-RUNNING.png) @@ -402,7 +402,10 @@ final关键字主要用在三个地方:变量、方法、类。 ### Java异常类层次结构图 ![Java异常类层次结构图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-2/Exception.png) - 在 Java 中,所有的异常都有一个共同的祖先java.lang包中的 **Throwable类**。Throwable: 有两个重要的子类:**Exception(异常)** 和 **Error(错误)** ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。 + + + +在 Java 中,所有的异常都有一个共同的祖先java.lang包中的 **Throwable类**。Throwable: 有两个重要的子类:**Exception(异常)** 和 **Error(错误)** ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。 **Error(错误):是程序无法处理的错误**,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。 @@ -410,20 +413,21 @@ final关键字主要用在三个地方:变量、方法、类。 **Exception(异常):是程序本身可以处理的异常**。Exception 类有一个重要的子类 **RuntimeException**。RuntimeException 异常由Java虚拟机抛出。**NullPointerException**(要访问的变量没有引用任何对象时,抛出该异常)、**ArithmeticException**(算术运算异常,一个整数除以0时,抛出该异常)和 **ArrayIndexOutOfBoundsException** (下标越界异常)。 -**注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。** +**注意:异常和错误的区别:异常能被程序本身处理,错误是无法处理。** ### Throwable类常用方法 -- **public string getMessage()**:返回异常发生时的详细信息 -- **public string toString()**:返回异常发生时的简要描述 -- **public string getLocalizedMessage()**:返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以声称本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同 +- **public string getMessage()**:返回异常发生时的简要描述 +- **public string toString()**:返回异常发生时的详细信息 +- **public string getLocalizedMessage()**:返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同 - **public void printStackTrace()**:在控制台上打印Throwable对象封装的异常信息 ### 异常处理总结 -- **try 块:**用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。 -- **catch 块:**用于处理try捕获到的异常。 -- **finally 块:**无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。 +- **try 块:** 用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。 +- **catch 块:** 用于处理try捕获到的异常。 +- **finally 块:** 无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return +语句时,finally语句块将在方法返回之前被执行。 **在以下4种特殊情况下,finally块不会被执行:** @@ -434,15 +438,21 @@ final关键字主要用在三个地方:变量、方法、类。 下面这部分内容来自issue:。 -**关于返回值:** +**注意:** 当try语句和finally语句中都有return语句时,在方法返回之前,finally语句的内容将被执行,并且finally语句的返回值将会覆盖原始的返回值。如下: -如果try语句里有return,返回的是try语句块中变量值。 -详细执行过程如下: +```java + public static int f(int value) { + try { + return value * value; + } finally { + if (value == 2) { + return 0; + } + } + } +``` -1. 如果有返回值,就把返回值保存到局部变量中; -2. 执行jsr指令跳到finally语句里执行; -3. 执行完finally语句后,返回之前保存在局部变量表里的值。 -4. 如果try,finally语句里均有return,忽略try的return,而使用finally的return. +如果调用 `f(2)`,返回值将是0,因为finally语句的返回值覆盖了try语句块的返回值。 ## 33 Java序列化中如果有些字段不想进行序列化,怎么办? @@ -450,7 +460,7 @@ final关键字主要用在三个地方:变量、方法、类。 transient关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被transient修饰的变量值不会被持久化和恢复。transient只能修饰变量,不能修饰类和方法。 -## 34 获取用键盘输入常用的的两种方法 +## 34 获取用键盘输入常用的两种方法 方法1:通过 Scanner @@ -467,6 +477,48 @@ BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); String s = input.readLine(); ``` +## 35 Java 中 IO 流 + +### Java 中 IO 流分为几种? + + - 按照流的流向分,可以分为输入流和输出流; + - 按照操作单元划分,可以划分为字节流和字符流; + - 按照流的角色划分为节点流和处理流。 + +Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。 + + - InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。 + - OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。 + +按操作方式分类结构图: + +![IO-操作方式分类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/IO-操作方式分类.png) + + +按操作对象分类结构图: + +![IO-操作对象分类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/IO-操作对象分类.png) + +### 既然有了字节流,为什么还要有字符流? + +问题本质想问:**不管是文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么 I/O 流操作要分为字节流操作和字符流操作呢?** + +回答:字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。 + +### BIO,NIO,AIO 有什么区别? + +- **BIO (Blocking I/O):** 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。 +- **NIO (New I/O):** NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 `Socket` 和 `ServerSocket` 相对应的 `SocketChannel` 和 `ServerSocketChannel` 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发 +- **AIO (Asynchronous I/O):** AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。 + +## 36. 常见关键字总结:static,final,this,super + +详见笔主的这篇文章: + +## 37. Collections 工具类和 Arrays 工具类常见方法总结 + +详见笔主的这篇文章: + ## 参考 - https://stackoverflow.com/questions/1906445/what-is-the-difference-between-jdk-and-jre @@ -481,4 +533,5 @@ String s = input.readLine(); **Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 -![我的公众号](https://user-gold-cdn.xitu.io/2018/11/28/167598cd2e17b8ec?w=258&h=258&f=jpeg&s=27334) +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) + diff --git "a/docs/java/Java\347\226\221\351\232\276\347\202\271.md" "b/docs/java/Java\347\226\221\351\232\276\347\202\271.md" new file mode 100644 index 00000000000..1a10e9586ef --- /dev/null +++ "b/docs/java/Java\347\226\221\351\232\276\347\202\271.md" @@ -0,0 +1,373 @@ + + +- [1. 基础](#1-基础) + - [1.1. 正确使用 equals 方法](#11-正确使用-equals-方法) + - [1.2. 整型包装类值的比较](#12-整型包装类值的比较) + - [1.3. BigDecimal](#13-bigdecimal) + - [1.3.1. BigDecimal 的用处](#131-bigdecimal-的用处) + - [1.3.2. BigDecimal 的大小比较](#132-bigdecimal-的大小比较) + - [1.3.3. BigDecimal 保留几位小数](#133-bigdecimal-保留几位小数) + - [1.3.4. BigDecimal 的使用注意事项](#134-bigdecimal-的使用注意事项) + - [1.3.5. 总结](#135-总结) + - [1.4. 基本数据类型与包装数据类型的使用标准](#14-基本数据类型与包装数据类型的使用标准) +- [2. 集合](#2-集合) + - [2.1. Arrays.asList()使用指南](#21-arraysaslist使用指南) + - [2.1.1. 简介](#211-简介) + - [2.1.2. 《阿里巴巴Java 开发手册》对其的描述](#212-阿里巴巴java-开发手册对其的描述) + - [2.1.3. 使用时的注意事项总结](#213-使用时的注意事项总结) + - [2.1.4. 如何正确的将数组转换为ArrayList?](#214-如何正确的将数组转换为arraylist) + - [2.2. Collection.toArray()方法使用的坑&如何反转数组](#22-collectiontoarray方法使用的坑如何反转数组) + - [2.3. 不要在 foreach 循环里进行元素的 remove/add 操作](#23-不要在-foreach-循环里进行元素的-removeadd-操作) + + + +# 1. 基础 + +## 1.1. 正确使用 equals 方法 + +Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。 + +举个例子: + +```java +// 不能使用一个值为null的引用类型变量来调用非静态方法,否则会抛出异常 +String str = null; +if (str.equals("SnailClimb")) { + ... +} else { + .. +} +``` + +运行上面的程序会抛出空指针异常,但是我们把第二行的条件判断语句改为下面这样的话,就不会抛出空指针异常,else 语句块得到执行。: + +```java +"SnailClimb".equals(str);// false +``` +不过更推荐使用 `java.util.Objects#equals`(JDK7 引入的工具类)。 + +```java +Objects.equals(null,"SnailClimb");// false +``` +我们看一下`java.util.Objects#equals`的源码就知道原因了。 +```java +public static boolean equals(Object a, Object b) { + // 可以避免空指针异常。如果a==null的话此时a.equals(b)就不会得到执行,避免出现空指针异常。 + return (a == b) || (a != null && a.equals(b)); + } +``` + +**注意:** + +Reference:[Java中equals方法造成空指针异常的原因及解决方案](https://blog.csdn.net/tick_tock97/article/details/72824894) + +- 每种原始类型都有默认值一样,如int默认值为 0,boolean 的默认值为 false,null 是任何引用类型的默认值,不严格的说是所有 Object 类型的默认值。 +- 可以使用 == 或者 != 操作来比较null值,但是不能使用其他算法或者逻辑操作。在Java中`null == null`将返回true。 +- 不能使用一个值为null的引用类型变量来调用非静态方法,否则会抛出异常 + +## 1.2. 整型包装类值的比较 + +所有整型包装类对象值的比较必须使用equals方法。 + +先看下面这个例子: + +```java +Integer x = 3; +Integer y = 3; +System.out.println(x == y);// true +Integer a = new Integer(3); +Integer b = new Integer(3); +System.out.println(a == b);//false +System.out.println(a.equals(b));//true +``` + +当使用自动装箱方式创建一个Integer对象时,当数值在-128 ~127时,会将创建的 Integer 对象缓存起来,当下次再出现该数值时,直接从缓存中取出对应的Integer对象。所以上述代码中,x和y引用的是相同的Integer对象。 + +**注意:**如果你的IDE(IDEA/Eclipse)上安装了阿里巴巴的p3c插件,这个插件如果检测到你用 ==的话会报错提示,推荐安装一个这个插件,很不错。 + +## 1.3. BigDecimal + +### 1.3.1. BigDecimal 的用处 + +《阿里巴巴Java开发手册》中提到:**浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals 来判断。** 具体原理和浮点数的编码方式有关,这里就不多提了,我们下面直接上实例: + +```java +float a = 1.0f - 0.9f; +float b = 0.9f - 0.8f; +System.out.println(a);// 0.100000024 +System.out.println(b);// 0.099999964 +System.out.println(a == b);// false +``` +具有基本数学知识的我们很清楚的知道输出并不是我们想要的结果(**精度丢失**),我们如何解决这个问题呢?一种很常用的方法是:**使用使用 BigDecimal 来定义浮点数的值,再进行浮点数的运算操作。** + +```java +BigDecimal a = new BigDecimal("1.0"); +BigDecimal b = new BigDecimal("0.9"); +BigDecimal c = new BigDecimal("0.8"); +BigDecimal x = a.subtract(b);// 0.1 +BigDecimal y = b.subtract(c);// 0.1 +System.out.println(x.equals(y));// true +``` + +### 1.3.2. BigDecimal 的大小比较 + +`a.compareTo(b)` : 返回 -1 表示小于,0 表示 等于, 1表示 大于。 + +```java +BigDecimal a = new BigDecimal("1.0"); +BigDecimal b = new BigDecimal("0.9"); +System.out.println(a.compareTo(b));// 1 +``` +### 1.3.3. BigDecimal 保留几位小数 + +通过 `setScale`方法设置保留几位小数以及保留规则。保留规则有挺多种,不需要记,IDEA会提示。 + +```java +BigDecimal m = new BigDecimal("1.255433"); +BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN); +System.out.println(n);// 1.255 +``` + +### 1.3.4. BigDecimal 的使用注意事项 + +注意:我们在使用BigDecimal时,为了防止精度丢失,推荐使用它的 **BigDecimal(String)** 构造方法来创建对象。《阿里巴巴Java开发手册》对这部分内容也有提到如下图所示。 + +![《阿里巴巴Java开发手册》对这部分BigDecimal的描述](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019/7/BigDecimal.png) + +### 1.3.5. 总结 + +BigDecimal 主要用来操作(大)浮点数,BigInteger 主要用来操作大整数(超过 long 类型)。 + +BigDecimal 的实现利用到了 BigInteger, 所不同的是 BigDecimal 加入了小数位的概念 + +## 1.4. 基本数据类型与包装数据类型的使用标准 + +Reference:《阿里巴巴Java开发手册》 + +- 【强制】所有的 POJO 类属性必须使用包装数据类型。 +- 【强制】RPC 方法的返回值和参数必须使用包装数据类型。 +- 【推荐】所有的局部变量使用基本数据类型。 + +比如我们如果自定义了一个Student类,其中有一个属性是成绩score,如果用Integer而不用int定义,一次考试,学生可能没考,值是null,也可能考了,但考了0分,值是0,这两个表达的状态明显不一样. + +**说明** :POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。 + +**正例** : 数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。 + +**反例** : 比如显示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调用不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线。所以包装数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。 + +# 2. 集合 + +## 2.1. Arrays.asList()使用指南 + +最近使用`Arrays.asList()`遇到了一些坑,然后在网上看到这篇文章:[Java Array to List Examples](http://javadevnotes.com/java-array-to-list-examples) 感觉挺不错的,但是还不是特别全面。所以,自己对于这块小知识点进行了简单的总结。 + +### 2.1.1. 简介 + +`Arrays.asList()`在平时开发中还是比较常见的,我们可以使用它将一个数组转换为一个List集合。 + +```java +String[] myArray = { "Apple", "Banana", "Orange" }; +List myList = Arrays.asList(myArray); +//上面两个语句等价于下面一条语句 +List myList = Arrays.asList("Apple","Banana", "Orange"); +``` + +JDK 源码对于这个方法的说明: + +```java +/** + *返回由指定数组支持的固定大小的列表。此方法作为基于数组和基于集合的API之间的桥梁,与 Collection.toArray()结合使用。返回的List是可序列化并实现RandomAccess接口。 + */ +public static List asList(T... a) { + return new ArrayList<>(a); +} +``` + +### 2.1.2. 《阿里巴巴Java 开发手册》对其的描述 + +`Arrays.asList()`将数组转换为集合后,底层其实还是数组,《阿里巴巴Java 开发手册》对于这个方法有如下描述: + +![阿里巴巴Java开发手-Arrays.asList()方法](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/阿里巴巴Java开发手-Arrays.asList()方法.png) + +### 2.1.3. 使用时的注意事项总结 + +**传递的数组必须是对象数组,而不是基本类型。** + +`Arrays.asList()`是泛型方法,传入的对象必须是对象数组。 + +```java +int[] myArray = { 1, 2, 3 }; +List myList = Arrays.asList(myArray); +System.out.println(myList.size());//1 +System.out.println(myList.get(0));//数组地址值 +System.out.println(myList.get(1));//报错:ArrayIndexOutOfBoundsException +int [] array=(int[]) myList.get(0); +System.out.println(array[0]);//1 +``` +当传入一个原生数据类型数组时,`Arrays.asList()` 的真正得到的参数就不是数组中的元素,而是数组对象本身!此时List 的唯一元素就是这个数组,这也就解释了上面的代码。 + +我们使用包装类型数组就可以解决这个问题。 + +```java +Integer[] myArray = { 1, 2, 3 }; +``` + +**使用集合的修改方法:`add()`、`remove()`、`clear()`会抛出异常。** + +```java +List myList = Arrays.asList(1, 2, 3); +myList.add(4);//运行时报错:UnsupportedOperationException +myList.remove(1);//运行时报错:UnsupportedOperationException +myList.clear();//运行时报错:UnsupportedOperationException +``` + +`Arrays.asList()` 方法返回的并不是 `java.util.ArrayList` ,而是 `java.util.Arrays` 的一个内部类,这个内部类并没有实现集合的修改方法或者说并没有重写这些方法。 + +```java +List myList = Arrays.asList(1, 2, 3); +System.out.println(myList.getClass());//class java.util.Arrays$ArrayList +``` + +下图是`java.util.Arrays$ArrayList`的简易源码,我们可以看到这个类重写的方法有哪些。 + +```java + private static class ArrayList extends AbstractList + implements RandomAccess, java.io.Serializable + { + ... + + @Override + public E get(int index) { + ... + } + + @Override + public E set(int index, E element) { + ... + } + + @Override + public int indexOf(Object o) { + ... + } + + @Override + public boolean contains(Object o) { + ... + } + + @Override + public void forEach(Consumer action) { + ... + } + + @Override + public void replaceAll(UnaryOperator operator) { + ... + } + + @Override + public void sort(Comparator c) { + ... + } + } +``` + +我们再看一下`java.util.AbstractList`的`remove()`方法,这样我们就明白为啥会抛出`UnsupportedOperationException`。 + +```java +public E remove(int index) { + throw new UnsupportedOperationException(); +} +``` + +### 2.1.4. 如何正确的将数组转换为ArrayList? + +stackoverflow:https://dwz.cn/vcBkTiTW + +**1. 自己动手实现(教育目的)** + +```java +//JDK1.5+ +static List arrayToList(final T[] array) { + final List l = new ArrayList(array.length); + + for (final T s : array) { + l.add(s); + } + return (l); +} +``` + +```java +Integer [] myArray = { 1, 2, 3 }; +System.out.println(arrayToList(myArray).getClass());//class java.util.ArrayList +``` + +**2. 最简便的方法(推荐)** + +```java +List list = new ArrayList<>(Arrays.asList("a", "b", "c")) +``` + +**3. 使用 Java8 的Stream(推荐)** + +```java +Integer [] myArray = { 1, 2, 3 }; +List myList = Arrays.stream(myArray).collect(Collectors.toList()); +//基本类型也可以实现转换(依赖boxed的装箱操作) +int [] myArray2 = { 1, 2, 3 }; +List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList()); +``` + +**4. 使用 Guava(推荐)** + +对于不可变集合,你可以使用[`ImmutableList`](https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/ImmutableList.java)类及其[`of()`](https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/ImmutableList.java#L101)与[`copyOf()`](https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/ImmutableList.java#L225)工厂方法:(参数不能为空) + +```java +List il = ImmutableList.of("string", "elements"); // from varargs +List il = ImmutableList.copyOf(aStringArray); // from array +``` +对于可变集合,你可以使用[`Lists`](https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/Lists.java)类及其[`newArrayList()`](https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/Lists.java#L87)工厂方法: + +```java +List l1 = Lists.newArrayList(anotherListOrCollection); // from collection +List l2 = Lists.newArrayList(aStringArray); // from array +List l3 = Lists.newArrayList("or", "string", "elements"); // from varargs +``` + +**5. 使用 Apache Commons Collections** + +```java +List list = new ArrayList(); +CollectionUtils.addAll(list, str); +``` + +## 2.2. Collection.toArray()方法使用的坑&如何反转数组 + +该方法是一个泛型方法:` T[] toArray(T[] a);` 如果`toArray`方法中没有传递任何参数的话返回的是`Object`类型数组。 + +```java +String [] s= new String[]{ + "dog", "lazy", "a", "over", "jumps", "fox", "brown", "quick", "A" +}; +List list = Arrays.asList(s); +Collections.reverse(list); +s=list.toArray(new String[0]);//没有指定类型的话会报错 +``` + +由于JVM优化,`new String[0]`作为`Collection.toArray()`方法的参数现在使用更好,`new String[0]`就是起一个模板的作用,指定了返回数组的类型,0是为了节省空间,因为它只是为了说明返回的类型。详见: + +## 2.3. 不要在 foreach 循环里进行元素的 remove/add 操作 + +如果要进行`remove`操作,可以调用迭代器的 `remove `方法而不是集合类的 remove 方法。因为如果列表在任何时间从结构上修改创建迭代器之后,以任何方式除非通过迭代器自身`remove/add`方法,迭代器都将抛出一个`ConcurrentModificationException`,这就是单线程状态下产生的 **fail-fast 机制**。 + +> **fail-fast 机制** :多个线程对 fail-fast 集合进行修改的时,可能会抛出ConcurrentModificationException,单线程下也会出现这种情况,上面已经提到过。 + +`java.util`包下面的所有的集合类都是fail-fast的,而`java.util.concurrent`包下面的所有的类都是fail-safe的。 + +![不要在 foreach 循环里进行元素的 remove/add 操作](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019/7/foreach-remove:add.png) + + + diff --git "a/docs/java/Java\347\250\213\345\272\217\350\256\276\350\256\241\351\242\230.md" "b/docs/java/Java\347\250\213\345\272\217\350\256\276\350\256\241\351\242\230.md" new file mode 100644 index 00000000000..46c9c16994b --- /dev/null +++ "b/docs/java/Java\347\250\213\345\272\217\350\256\276\350\256\241\351\242\230.md" @@ -0,0 +1,125 @@ +## 泛型的实际应用 + +### 实现最小值函数 + +自己设计一个泛型的获取数组最小值的函数.并且这个方法只能接受Number的子类并且实现了Comparable接口。 + +```java +//注意:Number并没有实现Comparable +private static > T min(T[] values) { + if (values == null || values.length == 0) return null; + T min = values[0]; + for (int i = 1; i < values.length; i++) { + if (min.compareTo(values[i]) > 0) min = values[i]; + } + return min; +} +``` + +测试: + +```java +int minInteger = min(new Integer[]{1, 2, 3});//result:1 +double minDouble = min(new Double[]{1.2, 2.2, -1d});//result:-1d +String typeError = min(new String[]{"1","3"});//报错 +``` + +## 数据结构 + +### 使用数组实现栈 + +**自己实现一个栈,要求这个栈具有`push()`、`pop()`(返回栈顶元素并出栈)、`peek()` (返回栈顶元素不出栈)、`isEmpty()`、`size()`这些基本的方法。** + +提示:每次入栈之前先判断栈的容量是否够用,如果不够用就用`Arrays.copyOf()`进行扩容; + +```java +public class MyStack { + private int[] storage;//存放栈中元素的数组 + private int capacity;//栈的容量 + private int count;//栈中元素数量 + private static final int GROW_FACTOR = 2; + + //TODO:不带初始容量的构造方法。默认容量为8 + public MyStack() { + this.capacity = 8; + this.storage=new int[8]; + this.count = 0; + } + + //TODO:带初始容量的构造方法 + public MyStack(int initialCapacity) { + if (initialCapacity < 1) + throw new IllegalArgumentException("Capacity too small."); + + this.capacity = initialCapacity; + this.storage = new int[initialCapacity]; + this.count = 0; + } + + //TODO:入栈 + public void push(int value) { + if (count == capacity) { + ensureCapacity(); + } + storage[count++] = value; + } + + //TODO:确保容量大小 + private void ensureCapacity() { + int newCapacity = capacity * GROW_FACTOR; + storage = Arrays.copyOf(storage, newCapacity); + capacity = newCapacity; + } + + //TODO:返回栈顶元素并出栈 + private int pop() { + count--; + if (count == -1) + throw new IllegalArgumentException("Stack is empty."); + + return storage[count]; + } + + //TODO:返回栈顶元素不出栈 + private int peek() { + if (count == 0){ + throw new IllegalArgumentException("Stack is empty."); + }else { + return storage[count-1]; + } + } + + //TODO:判断栈是否为空 + private boolean isEmpty() { + return count == 0; + } + + //TODO:返回栈中元素的个数 + private int size() { + return count; + } + +} + +``` + +验证 + +```java +MyStack myStack = new MyStack(3); +myStack.push(1); +myStack.push(2); +myStack.push(3); +myStack.push(4); +myStack.push(5); +myStack.push(6); +myStack.push(7); +myStack.push(8); +System.out.println(myStack.peek());//8 +System.out.println(myStack.size());//8 +for (int i = 0; i < 8; i++) { + System.out.println(myStack.pop()); +} +System.out.println(myStack.isEmpty());//true +myStack.pop();//报错:java.lang.IllegalArgumentException: Stack is empty. +``` \ No newline at end of file diff --git "a/docs/java/Java\347\274\226\347\250\213\350\247\204\350\214\203.md" "b/docs/java/Java\347\274\226\347\250\213\350\247\204\350\214\203.md" index 0845c21b0a3..2f719bc1df2 100644 --- "a/docs/java/Java\347\274\226\347\250\213\350\247\204\350\214\203.md" +++ "b/docs/java/Java\347\274\226\347\250\213\350\247\204\350\214\203.md" @@ -1,12 +1,30 @@ - - -根据各位建议加上了这部分内容,我暂时只是给出了两个资源,后续可能会对重要的点进行总结,然后更新在这里,如果你总结过这类东西,欢迎与我联系! +讲真的,下面推荐的文章或者资源建议阅读 3 遍以上。 ### 团队 -- **阿里巴巴Java开发手册(详尽版)** -- **Google Java编程风格指南:** +- **阿里巴巴Java开发手册(详尽版)** +- **Google Java编程风格指南:** ### 个人 - **程序员你为什么这么累:** + +### 如何写出优雅的 Java 代码 + +1. 使用 IntelliJ IDEA 作为您的集成开发环境 (IDE) +1. 使用 JDK 8 或更高版本 +1. 使用 Maven/Gradle +1. 使用 Lombok +1. 编写单元测试 +1. 重构:常见,但也很慢 +1. 注意代码规范 +1. 定期联络客户,以获取他们的反馈 + +上述建议的详细内容:[八点建议助您写出优雅的Java代码](docs/八点建议助您写出优雅的Java代码.md)。 + +更多代码优化相关内容推荐: + +- [业务复杂=if else?刚来的大神竟然用策略+工厂彻底干掉了他们!](https://juejin.im/post/5dad23685188251d2c4ea2b6) +- [一些不错的 Java 实践!推荐阅读3遍以上!](http://lrwinx.github.io/2017/03/04/%E7%BB%86%E6%80%9D%E6%9E%81%E6%81%90-%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BC%9A%E5%86%99java%E5%90%97/) +- [[解锁新姿势] 兄dei,你代码需要优化了](https://juejin.im/post/5dafbc02e51d4524a0060bdd) +- [消灭 Java 代码的“坏味道”](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485599&idx=1&sn=d83ff4e6b1ee951a0a33508a10980ea3&chksm=cea24754f9d5ce426d18b435a8c373ddc580c06c7d6a45cc51377361729c31c7301f1bbc3b78&token=1328169465&lang=zh_CN#rd) \ No newline at end of file diff --git "a/docs/java/Java\350\231\232\346\213\237\346\234\272\357\274\210jvm\357\274\211.md" "b/docs/java/Java\350\231\232\346\213\237\346\234\272\357\274\210jvm\357\274\211.md" deleted file mode 100644 index 9be88bc89c3..00000000000 --- "a/docs/java/Java\350\231\232\346\213\237\346\234\272\357\274\210jvm\357\274\211.md" +++ /dev/null @@ -1,59 +0,0 @@ - -下面是按jvm虚拟机知识点分章节总结的一些jvm学习与面试相关的一些东西。一般作为Java程序员在面试的时候一般会问的大多就是**Java内存区域、虚拟机垃圾算法、虚拟垃圾收集器、JVM内存管理**这些问题了。这些内容参考周的《深入理解Java虚拟机》中第二章和第三章就足够了对应下面的[深入理解虚拟机之Java内存区域:](https://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s%3F__biz%3DMzU4NDQ4MzU5OA%3D%3D%26mid%3D2247483910%26idx%3D1%26sn%3D246f39051a85fc312577499691fba89f%26chksm%3Dfd985467caefdd71f9a7c275952be34484b14f9e092723c19bd4ef557c324169ed084f868bdb%23rd)和[深入理解虚拟机之垃圾回收](https://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s%3F__biz%3DMzU4NDQ4MzU5OA%3D%3D%26mid%3D2247483914%26idx%3D1%26sn%3D9aa157d4a1570962c39783cdeec7e539%26chksm%3Dfd98546bcaefdd7d9f61cd356e5584e56b64e234c3a403ed93cb6d4dde07a505e3000fd0c427%23rd)这两篇文章。 - - -> ### 常见面试题 - -[深入理解虚拟机之Java内存区域](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484960&idx=1&sn=ff3739fe849030178346bef28a4556c3&chksm=cea249ebf9d5c0fdbde7c86155d0d7ac8925153742aff472bcb79e5e9d400534a855bad38375&token=1082669959&lang=zh_CN#rd) - -1. 介绍下Java内存区域(运行时数据区)。 - -2. 对象的访问定位的两种方式。 - - -[深入理解虚拟机之垃圾回收](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484959&idx=1&sn=9ac740edba59981b7c89482043776280&chksm=cea249d4f9d5c0c21703382510a47d4bb387932bd814ac891fd214b92cead5d2cf0ee2dff797&token=1082669959&lang=zh_CN#rd) - -1. 如何判断对象是否死亡(两种方法)。 - -2. 简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)。 - -3. 垃圾收集有哪些算法,各自的特点? - -4. HotSpot为什么要分为新生代和老年代? - -5. 常见的垃圾回收器有那些? - -6. 介绍一下CMS,G1收集器。 - -7. Minor Gc和Full GC 有什么不同呢? - - - - [虚拟机性能监控和故障处理工具](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484957&idx=1&sn=713ed6003d23ef883ded14cb43e9ebb7&chksm=cea249d6f9d5c0c0ce0854a03f0d02fcacc8a46e29c2fd4f085a375b00e1cd1b632937a9895e&token=1082669959&lang=zh_CN#rd) - -1. JVM调优的常见命令行工具有哪些? - - [深入理解虚拟机之类文件结构](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484956&idx=1&sn=05f46ccacacdbce7c43de594d3fe93db&chksm=cea249d7f9d5c0c1ef6d29b0fbbf0701acd28490deb0974ae71b4d23ae793bec0b0993a4c829&token=1082669959&lang=zh_CN#rd) - -1. 简单介绍一下Class类文件结构(常量池主要存放的是那两大常量?Class文件的继承关系是如何确定的?字段表、方法表、属性表主要包含那些信息?) - -[深入理解虚拟机之虚拟机字节码执行引擎](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484952&idx=1&sn=d0ec9443600dc5b2a81782b7ae0691d5&chksm=cea249d3f9d5c0c50642f1829fd6fe9e35d155bbbb6718611330c7c46c7158279275b533181e&token=1082669959&lang=zh_CN#rd) - -1. 简单说说类加载过程,里面执行了哪些操作? - -2. 对类加载器有了解吗? - -3. 什么是双亲委派模型? - -4. 双亲委派模型的工作过程以及使用它的好处。 - - - - - -> ### 推荐阅读 - - [《深入理解 Java 内存模型》读书笔记](http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/) (非常不错的文章) - [全面理解Java内存模型(JMM)及volatile关键字 ](https://blog.csdn.net/javazejian/article/details/72772461) - - diff --git "a/docs/java/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/docs/java/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" deleted file mode 100644 index cb0bd1fe0e3..00000000000 --- "a/docs/java/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" +++ /dev/null @@ -1,353 +0,0 @@ - - -1. [List,Set,Map三者的区别及总结](#list,setmap三者的区别及总结) -1. [Arraylist 与 LinkedList 区别](#arraylist-与-linkedlist-区别) -1. [ArrayList 与 Vector 区别(为什么要用Arraylist取代Vector呢?)](#arraylist-与-vector-区别) -1. [HashMap 和 Hashtable 的区别](#hashmap-和-hashtable-的区别) -1. [HashSet 和 HashMap 区别](#hashset-和-hashmap-区别) -1. [HashMap 和 ConcurrentHashMap 的区别](#hashmap-和-concurrenthashmap-的区别) -1. [HashSet如何检查重复](#hashset如何检查重复) -1. [comparable 和 comparator的区别](#comparable-和-comparator的区别) - 1. [Comparator定制排序](#comparator定制排序) - 1. [重写compareTo方法实现按年龄来排序](#重写compareto方法实现按年龄来排序) -1. [如何对Object的list排序?](#如何对object的list排序) -1. [如何实现数组与List的相互转换?](#如何实现数组与list的相互转换) -1. [如何求ArrayList集合的交集 并集 差集 去重复并集](#如何求arraylist集合的交集-并集-差集-去重复并集) -1. [HashMap 的工作原理及代码实现](#hashmap-的工作原理及代码实现) -1. [ConcurrentHashMap 的工作原理及代码实现](#concurrenthashmap-的工作原理及代码实现) -1. [集合框架底层数据结构总结](#集合框架底层数据结构总结) - 1. [- Collection](#--collection) - 1. [1. List](#1-list) - 1. [2. Set](#2-set) - 1. [- Map](#--map) -1. [集合的选用](#集合的选用) -1. [集合的常用方法](#集合的常用方法) - - - - -## List,Set,Map三者的区别及总结 -- **List:对付顺序的好帮手** - - List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象 -- **Set:注重独一无二的性质** - - 不允许重复的集合。不会有多个元素引用相同的对象。 - -- **Map:用Key来搜索的专家** - - 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。 - - -## Arraylist 与 LinkedList 区别 -Arraylist底层使用的是数组(存读数据效率高,插入删除特定位置效率低),LinkedList 底层使用的是双向链表数据结构(插入,删除效率特别高)(JDK1.6之前为循环链表,JDK1.7取消了循环。注意双向链表和双向循环链表的区别:); 详细可阅读JDK1.7-LinkedList循环链表优化。学过数据结构这门课后我们就知道采用链表存储,插入,删除元素时间复杂度不受元素位置的影响,都是近似O(1)而数组为近似O(n),因此当数据特别多,而且经常需要插入删除元素时建议选用LinkedList.一般程序只用Arraylist就够用了,因为一般数据量都不会蛮大,Arraylist是使用最多的集合类。 - -## ArrayList 与 Vector 区别 -Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector -,代码要在同步操作上耗费大量的时间。Arraylist不是同步的,所以在不需要同步时建议使用Arraylist。 - -## HashMap 和 Hashtable 的区别 -1. HashMap是非线程安全的,HashTable是线程安全的;HashTable内部的方法基本都经过synchronized修饰。 - -2. 因为线程安全的问题,HashMap要比HashTable效率高一点,HashTable基本被淘汰。 -3. HashMap允许有null值的存在,而在HashTable中put进的键值只要有一个null,直接抛出NullPointerException。 - -Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java5或以上的话,请使用ConcurrentHashMap吧 - -## HashSet 和 HashMap 区别 -![HashSet 和 HashMap 区别](https://user-gold-cdn.xitu.io/2018/3/2/161e717d734f3b23?w=896&h=363&f=jpeg&s=205536) - -## HashMap 和 ConcurrentHashMap 的区别 -[HashMap与ConcurrentHashMap的区别](https://blog.csdn.net/xuefeng0707/article/details/40834595) - -1. ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。(JDK1.8之后ConcurrentHashMap启用了一种全新的方式实现,利用CAS算法。) -2. HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。 - -## HashSet如何检查重复 -当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。(摘自我的Java启蒙书《Head fist java》第二版) - -**hashCode()与equals()的相关规定:** -1. 如果两个对象相等,则hashcode一定也是相同的 -2. 两个对象相等,对两个equals方法返回true -3. 两个对象有相同的hashcode值,它们也不一定是相等的 -4. 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖 -5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。 - -**==与equals的区别** - -1. ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同 -2. ==是指对内存地址进行比较 equals()是对字符串的内容进行比较 -3. ==指引用是否相同 equals()指的是值是否相同 - -## comparable 和 comparator的区别 -- comparable接口实际上是出自java.lang包 它有一个 compareTo(Object obj)方法用来排序 -- comparator接口实际上是出自 java.util 包它有一个compare(Object obj1, Object obj2)方法用来排序 - -一般我们需要对一个集合使用自定义排序时,我们就要重写compareTo方法或compare方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写compareTo方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的Collections.sort(). - -### Comparator定制排序 -```java -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; - -/** - * TODO Collections类方法测试之排序 - * @author 寇爽 - * @date 2017年11月20日 - * @version 1.8 - */ -public class CollectionsSort { - - public static void main(String[] args) { - - ArrayList arrayList = new ArrayList(); - arrayList.add(-1); - arrayList.add(3); - arrayList.add(3); - arrayList.add(-5); - arrayList.add(7); - arrayList.add(4); - arrayList.add(-9); - arrayList.add(-7); - System.out.println("原始数组:"); - System.out.println(arrayList); - // void reverse(List list):反转 - Collections.reverse(arrayList); - System.out.println("Collections.reverse(arrayList):"); - System.out.println(arrayList); -/* - * void rotate(List list, int distance),旋转。 - * 当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 - * list的前distance个元素整体移到后面。 - - Collections.rotate(arrayList, 4); - System.out.println("Collections.rotate(arrayList, 4):"); - System.out.println(arrayList);*/ - - // void sort(List list),按自然排序的升序排序 - Collections.sort(arrayList); - System.out.println("Collections.sort(arrayList):"); - System.out.println(arrayList); - - // void shuffle(List list),随机排序 - Collections.shuffle(arrayList); - System.out.println("Collections.shuffle(arrayList):"); - System.out.println(arrayList); - - // 定制排序的用法 - Collections.sort(arrayList, new Comparator() { - - @Override - public int compare(Integer o1, Integer o2) { - return o2.compareTo(o1); - } - }); - System.out.println("定制排序后:"); - System.out.println(arrayList); - } - -} - -``` -### 重写compareTo方法实现按年龄来排序 -```java -package map; - -import java.util.Set; -import java.util.TreeMap; - -public class TreeMap2 { - - public static void main(String[] args) { - // TODO Auto-generated method stub - TreeMap pdata = new TreeMap(); - pdata.put(new Person("张三", 30), "zhangsan"); - pdata.put(new Person("李四", 20), "lisi"); - pdata.put(new Person("王五", 10), "wangwu"); - pdata.put(new Person("小红", 5), "xiaohong"); - // 得到key的值的同时得到key所对应的值 - Set keys = pdata.keySet(); - for (Person key : keys) { - System.out.println(key.getAge() + "-" + key.getName()); - - } - } -} - -// person对象没有实现Comparable接口,所以必须实现,这样才不会出错,才可以使treemap中的数据按顺序排列 -// 前面一个例子的String类已经默认实现了Comparable接口,详细可以查看String类的API文档,另外其他 -// 像Integer类等都已经实现了Comparable接口,所以不需要另外实现了 - -class Person implements Comparable { - private String name; - private int age; - - public Person(String name, int age) { - super(); - this.name = name; - this.age = age; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - /** - * TODO重写compareTo方法实现按年龄来排序 - */ - @Override - public int compareTo(Person o) { - // TODO Auto-generated method stub - if (this.age > o.getAge()) { - return 1; - } else if (this.age < o.getAge()) { - return -1; - } - return age; - } -} -``` - -## 如何对Object的list排序 -- 对objects数组进行排序,我们可以用Arrays.sort()方法 -- 对objects的集合进行排序,需要使用Collections.sort()方法 - - -## 如何实现数组与List的相互转换 -List转数组:toArray(arraylist.size()方法;数组转List:Arrays的asList(a)方法 -```java -List arrayList = new ArrayList(); - arrayList.add("s"); - arrayList.add("e"); - arrayList.add("n"); - /** - * ArrayList转数组 - */ - int size=arrayList.size(); - String[] a = arrayList.toArray(new String[size]); - //输出第二个元素 - System.out.println(a[1]);//结果:e - //输出整个数组 - System.out.println(Arrays.toString(a));//结果:[s, e, n] - /** - * 数组转list - */ - List list=Arrays.asList(a); - /** - * list转Arraylist - */ - List arrayList2 = new ArrayList(); - arrayList2.addAll(list); - System.out.println(list); -``` -## 如何求ArrayList集合的交集 并集 差集 去重复并集 -需要用到List接口中定义的几个方法: - -- addAll(Collection c) :按指定集合的Iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾 -实例代码: -- retainAll(Collection c): 仅保留此列表中包含在指定集合中的元素。 -- removeAll(Collection c) :从此列表中删除指定集合中包含的所有元素。 -```java -package list; - -import java.util.ArrayList; -import java.util.List; - -/** - *TODO 两个集合之间求交集 并集 差集 去重复并集 - * @author 寇爽 - * @date 2017年11月21日 - * @version 1.8 - */ -public class MethodDemo { - - public static void main(String[] args) { - // TODO Auto-generated method stub - List list1 = new ArrayList(); - list1.add(1); - list1.add(2); - list1.add(3); - list1.add(4); - - List list2 = new ArrayList(); - list2.add(2); - list2.add(3); - list2.add(4); - list2.add(5); - // 并集 - // list1.addAll(list2); - // 交集 - //list1.retainAll(list2); - // 差集 - // list1.removeAll(list2); - // 无重复并集 - list2.removeAll(list1); - list1.addAll(list2); - for (Integer i : list1) { - System.out.println(i); - } - } - -} - -``` - -## HashMap 的工作原理及代码实现 - -[集合框架源码学习之HashMap(JDK1.8)](https://juejin.im/post/5ab0568b5188255580020e56) - -## ConcurrentHashMap 的工作原理及代码实现 - -[ConcurrentHashMap实现原理及源码分析](http://www.cnblogs.com/chengxiao/p/6842045.html) - - -## 集合框架底层数据结构总结 -### - Collection - -#### 1. List - - Arraylist:数组(查询快,增删慢 线程不安全,效率高 ) - - Vector:数组(查询快,增删慢 线程安全,效率低 ) - - LinkedList:链表(查询慢,增删快 线程不安全,效率高 ) - -#### 2. Set - - HashSet(无序,唯一):哈希表或者叫散列集(hash table) - - LinkedHashSet:链表和哈希表组成 。 由链表保证元素的排序 , 由哈希表证元素的唯一性 - - TreeSet(有序,唯一):红黑树(自平衡的排序二叉树。) - -### - Map - - HashMap:基于哈希表的Map接口实现(哈希表对键进行散列,Map结构即映射表存放键值对) - - LinkedHashMap:HashMap 的基础上加上了链表数据结构 - - HashTable:哈希表 - - TreeMap:红黑树(自平衡的排序二叉树) - - -## 集合的选用 -主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用Map接口下的集合,需要排序时选择TreeMap,不需要排序时就选择HashMap,需要保证线程安全就选用ConcurrentHashMap.当我们只需要存放元素值时,就选择实现Collection接口的集合,需要保证元素唯一时选择实现Set接口的集合比如TreeSet或HashSet,不需要就选择实现List接口的比如ArrayList或LinkedList,然后再根据实现这些接口的集合的特点来选用。 - -2018/3/11更新 -## 集合的常用方法 -今天下午无意看见一道某大厂的面试题,面试题的内容就是问你某一个集合常见的方法有哪些。虽然平时也经常见到这些集合,但是猛一下让我想某一个集合的常用的方法难免会有遗漏或者与其他集合搞混,所以建议大家还是照着API文档把常见的那几个集合的常用方法看一看。 - -会持续更新。。。 - -**参考书籍:** - -《Head first java 》第二版 推荐阅读真心不错 (适合基础较差的) - - 《Java核心技术卷1》推荐阅读真心不错 (适合基础较好的) - - 《算法》第四版 (适合想对数据结构的Java实现感兴趣的) - diff --git a/docs/java/Multithread/AQS.md b/docs/java/Multithread/AQS.md index f405db1f17d..287116d7da2 100644 --- a/docs/java/Multithread/AQS.md +++ b/docs/java/Multithread/AQS.md @@ -1,5 +1,5 @@ -**目录:** +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 - [1 AQS 简单介绍](#1-aqs-简单介绍) @@ -23,10 +23,6 @@ > 常见问题:AQS原理?;CountDownLatch和CyclicBarrier了解吗,两者的区别是什么?用过Semaphore吗? -**本节思维导图:** - -![并发编程面试必备:AQS 原理以及 AQS 同步组件总结](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-31/61115865.jpg) - ### 1 AQS 简单介绍 AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包下面。 @@ -37,7 +33,7 @@ AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效 ### 2 AQS 原理 -> 在面试中被问到并发知识的时候,大多都会被问到“请你说一下自己对于AQS原理的理解”。下面给大家一个示例供大家参加,面试不是背题,大家一定要加入自己的思想,即使加入不了自己的思想也要保证自己能够通俗的讲出来而不是背出来。 +> 在面试中被问到并发知识的时候,大多都会被问到“请你说一下自己对于AQS原理的理解”。下面给大家一个示例供大家参考,面试不是背题,大家一定要加入自己的思想,即使加入不了自己的思想也要保证自己能够通俗的讲出来而不是背出来。 下面大部分内容其实在AQS类注释上已经给出了,不过是英语看着比较吃力一点,感兴趣的话可以看看源码。 @@ -128,7 +124,7 @@ tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true ### 3 Semaphore(信号量)-允许多个线程同时访问 -**synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。**示例代码如下: +**synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。** 示例代码如下: ```java /** @@ -471,6 +467,12 @@ CyclicBarrier和CountDownLatch的区别这部分内容参考了如下两篇文 ReentrantLock 和 synchronized 的区别在上面已经讲过了这里就不多做讲解。另外,需要注意的是:读写锁 ReentrantReadWriteLock 可以保证多个线程可以同时读,所以在读操作远大于写操作的时候,读写锁就非常有用了。 -由于篇幅问题,关于 ReentrantLock 和 ReentrantReadWriteLock 详细内容可以查看我的这篇原创文章。 +## 公众号 + +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 + +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"面试突击"** 即可免费领取! + +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 -- [ReentrantLock 和 ReentrantReadWriteLock](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483745&idx=2&sn=6778ee954a19816310df54ef9a3c2f8a&chksm=fd985700caefde16b9970f5e093b0c140d3121fb3a8458b11871e5e9723c5fd1b5a961fd2228&token=1829606453&lang=zh_CN#rd) +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) \ No newline at end of file diff --git a/docs/java/Multithread/Atomic.md b/docs/java/Multithread/Atomic.md index 785ac34ec33..7f832568f65 100644 --- a/docs/java/Multithread/Atomic.md +++ b/docs/java/Multithread/Atomic.md @@ -1,8 +1,28 @@ -> 个人觉得这一节掌握基本的使用即可! +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 -**本节思维导图:** +> 个人觉得这一节掌握基本的使用即可! -![](https://user-gold-cdn.xitu.io/2018/10/30/166c58b785368234?w=1200&h=657&f=png&s=49615) + + +- [1 Atomic 原子类介绍](#1-atomic-原子类介绍) +- [2 基本类型原子类](#2-基本类型原子类) + - [2.1 基本类型原子类介绍](#21-基本类型原子类介绍) + - [2.2 AtomicInteger 常见方法使用](#22-atomicinteger-常见方法使用) + - [2.3 基本数据类型原子类的优势](#23-基本数据类型原子类的优势) + - [2.4 AtomicInteger 线程安全原理简单分析](#24-atomicinteger-线程安全原理简单分析) +- [3 数组类型原子类](#3-数组类型原子类) + - [3.1 数组类型原子类介绍](#31-数组类型原子类介绍) + - [3.2 AtomicIntegerArray 常见方法使用](#32-atomicintegerarray-常见方法使用) +- [4 引用类型原子类](#4-引用类型原子类) + - [4.1 引用类型原子类介绍](#41--引用类型原子类介绍) + - [4.2 AtomicReference 类使用示例](#42-atomicreference-类使用示例) + - [4.3 AtomicStampedReference 类使用示例](#43-atomicstampedreference-类使用示例) + - [4.4 AtomicMarkableReference 类使用示例](#44-atomicmarkablereference-类使用示例) +- [5 对象的属性修改类型原子类](#5-对象的属性修改类型原子类) + - [5.1 对象的属性修改类型原子类介绍](#51-对象的属性修改类型原子类介绍) + - [5.2 AtomicIntegerFieldUpdater 类使用示例](#52-atomicintegerfieldupdater-类使用示例) + + ### 1 Atomic 原子类介绍 @@ -12,7 +32,7 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是 并发包 `java.util.concurrent` 的原子类都存放在`java.util.concurrent.atomic`下,如下图所示。 -![ JUC 原子类概览](https://user-gold-cdn.xitu.io/2018/10/30/166c4ac08d4c5547?w=317&h=367&f=png&s=13267) +![JUC原子类概览](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/JUC原子类概览.png) 根据操作的数据类型,可以将JUC包中的原子类分为4类 @@ -36,7 +56,7 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是 **引用类型** - AtomicReference:引用类型原子类 -- AtomicStampedRerence:原子更新引用类型里的字段原子类 +- AtomicReferenceFieldUpdater:原子更新引用类型里的字段 - AtomicMarkableReference :原子更新带有标记位的引用类型 **对象的属性修改类型** @@ -44,6 +64,82 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是 - AtomicIntegerFieldUpdater:原子更新整型字段的更新器 - AtomicLongFieldUpdater:原子更新长整型字段的更新器 - AtomicStampedReference :原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。 +- AtomicMarkableReference:原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来,也可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。 + +**CAS ABA 问题** +- 描述: 第一个线程取到了变量 x 的值 A,然后巴拉巴拉干别的事,总之就是只拿到了变量 x 的值 A。这段时间内第二个线程也取到了变量 x 的值 A,然后把变量 x 的值改为 B,然后巴拉巴拉干别的事,最后又把变量 x 的值变为 A (相当于还原了)。在这之后第一个线程终于进行了变量 x 的操作,但是此时变量 x 的值还是 A,所以 compareAndSet 操作是成功。 +- 例子描述(可能不太合适,但好理解): 年初,现金为零,然后通过正常劳动赚了三百万,之后正常消费了(比如买房子)三百万。年末,虽然现金零收入(可能变成其他形式了),但是赚了钱是事实,还是得交税的! +- 代码例子(以``` AtomicInteger ```为例) + +```java +import java.util.concurrent.atomic.AtomicInteger; + +public class AtomicIntegerDefectDemo { + public static void main(String[] args) { + defectOfABA(); + } + + static void defectOfABA() { + final AtomicInteger atomicInteger = new AtomicInteger(1); + + Thread coreThread = new Thread( + () -> { + final int currentValue = atomicInteger.get(); + System.out.println(Thread.currentThread().getName() + " ------ currentValue=" + currentValue); + + // 这段目的:模拟处理其他业务花费的时间 + try { + Thread.sleep(300); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + boolean casResult = atomicInteger.compareAndSet(1, 2); + System.out.println(Thread.currentThread().getName() + + " ------ currentValue=" + currentValue + + ", finalValue=" + atomicInteger.get() + + ", compareAndSet Result=" + casResult); + } + ); + coreThread.start(); + + // 这段目的:为了让 coreThread 线程先跑起来 + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Thread amateurThread = new Thread( + () -> { + int currentValue = atomicInteger.get(); + boolean casResult = atomicInteger.compareAndSet(1, 2); + System.out.println(Thread.currentThread().getName() + + " ------ currentValue=" + currentValue + + ", finalValue=" + atomicInteger.get() + + ", compareAndSet Result=" + casResult); + + currentValue = atomicInteger.get(); + casResult = atomicInteger.compareAndSet(2, 1); + System.out.println(Thread.currentThread().getName() + + " ------ currentValue=" + currentValue + + ", finalValue=" + atomicInteger.get() + + ", compareAndSet Result=" + casResult); + } + ); + amateurThread.start(); + } +} +``` + +输出内容如下: + +``` +Thread-0 ------ currentValue=1 +Thread-1 ------ currentValue=1, finalValue=2, compareAndSet Result=true +Thread-1 ------ currentValue=2, finalValue=1, compareAndSet Result=true +Thread-0 ------ currentValue=1, finalValue=2, compareAndSet Result=true +``` 下面我们来详细介绍一下这些原子类。 @@ -60,7 +156,7 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是 上面三个类提供的方法几乎相同,所以我们这里以 AtomicInteger 为例子来介绍。 **AtomicInteger 类常用方法** - + ```java public final int get() //获取当前的值 public final int getAndSet(int newValue)//获取当前的值,并设置新的值 @@ -210,7 +306,7 @@ public class AtomicIntegerArrayTest { 基本类型原子类只能更新一个变量,如果需要原子更新多个变量,需要使用 引用类型原子类。 - AtomicReference:引用类型原子类 -- AtomicStampedRerence:原子更新引用类型里的字段原子类 +- AtomicStampedReference:原子更新引用类型里的字段原子类 - AtomicMarkableReference :原子更新带有标记位的引用类型 上面三个类提供的方法几乎相同,所以我们这里以 AtomicReference 为例子来介绍。 @@ -268,7 +364,121 @@ class Person { Daisy 20 ``` +#### 4.3 AtomicStampedReference 类使用示例 + +```java +import java.util.concurrent.atomic.AtomicStampedReference; + +public class AtomicStampedReferenceDemo { + public static void main(String[] args) { + // 实例化、取当前值和 stamp 值 + final Integer initialRef = 0, initialStamp = 0; + final AtomicStampedReference asr = new AtomicStampedReference<>(initialRef, initialStamp); + System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp()); + + // compare and set + final Integer newReference = 666, newStamp = 999; + final boolean casResult = asr.compareAndSet(initialRef, newReference, initialStamp, newStamp); + System.out.println("currentValue=" + asr.getReference() + + ", currentStamp=" + asr.getStamp() + + ", casResult=" + casResult); + + // 获取当前的值和当前的 stamp 值 + int[] arr = new int[1]; + final Integer currentValue = asr.get(arr); + final int currentStamp = arr[0]; + System.out.println("currentValue=" + currentValue + ", currentStamp=" + currentStamp); + + // 单独设置 stamp 值 + final boolean attemptStampResult = asr.attemptStamp(newReference, 88); + System.out.println("currentValue=" + asr.getReference() + + ", currentStamp=" + asr.getStamp() + + ", attemptStampResult=" + attemptStampResult); + + // 重新设置当前值和 stamp 值 + asr.set(initialRef, initialStamp); + System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp()); + + // [不推荐使用,除非搞清楚注释的意思了] weak compare and set + // 困惑!weakCompareAndSet 这个方法最终还是调用 compareAndSet 方法。[版本: jdk-8u191] + // 但是注释上写着 "May fail spuriously and does not provide ordering guarantees, + // so is only rarely an appropriate alternative to compareAndSet." + // todo 感觉有可能是 jvm 通过方法名在 native 方法里面做了转发 + final boolean wCasResult = asr.weakCompareAndSet(initialRef, newReference, initialStamp, newStamp); + System.out.println("currentValue=" + asr.getReference() + + ", currentStamp=" + asr.getStamp() + + ", wCasResult=" + wCasResult); + } +} +``` +输出结果如下: +``` +currentValue=0, currentStamp=0 +currentValue=666, currentStamp=999, casResult=true +currentValue=666, currentStamp=999 +currentValue=666, currentStamp=88, attemptStampResult=true +currentValue=0, currentStamp=0 +currentValue=666, currentStamp=999, wCasResult=true +``` + +#### 4.4 AtomicMarkableReference 类使用示例 + +``` java +import java.util.concurrent.atomic.AtomicMarkableReference; + +public class AtomicMarkableReferenceDemo { + public static void main(String[] args) { + // 实例化、取当前值和 mark 值 + final Boolean initialRef = null, initialMark = false; + final AtomicMarkableReference amr = new AtomicMarkableReference<>(initialRef, initialMark); + System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked()); + + // compare and set + final Boolean newReference1 = true, newMark1 = true; + final boolean casResult = amr.compareAndSet(initialRef, newReference1, initialMark, newMark1); + System.out.println("currentValue=" + amr.getReference() + + ", currentMark=" + amr.isMarked() + + ", casResult=" + casResult); + + // 获取当前的值和当前的 mark 值 + boolean[] arr = new boolean[1]; + final Boolean currentValue = amr.get(arr); + final boolean currentMark = arr[0]; + System.out.println("currentValue=" + currentValue + ", currentMark=" + currentMark); + + // 单独设置 mark 值 + final boolean attemptMarkResult = amr.attemptMark(newReference1, false); + System.out.println("currentValue=" + amr.getReference() + + ", currentMark=" + amr.isMarked() + + ", attemptMarkResult=" + attemptMarkResult); + + // 重新设置当前值和 mark 值 + amr.set(initialRef, initialMark); + System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked()); + + // [不推荐使用,除非搞清楚注释的意思了] weak compare and set + // 困惑!weakCompareAndSet 这个方法最终还是调用 compareAndSet 方法。[版本: jdk-8u191] + // 但是注释上写着 "May fail spuriously and does not provide ordering guarantees, + // so is only rarely an appropriate alternative to compareAndSet." + // todo 感觉有可能是 jvm 通过方法名在 native 方法里面做了转发 + final boolean wCasResult = amr.weakCompareAndSet(initialRef, newReference1, initialMark, newMark1); + System.out.println("currentValue=" + amr.getReference() + + ", currentMark=" + amr.isMarked() + + ", wCasResult=" + wCasResult); + } +} +``` + +输出结果如下: +``` +currentValue=null, currentMark=false +currentValue=true, currentMark=true, casResult=true +currentValue=true, currentMark=true +currentValue=true, currentMark=false, attemptMarkResult=true +currentValue=null, currentMark=false +currentValue=true, currentMark=true, wCasResult=true +``` ### 5 对象的属性修改类型原子类 @@ -335,3 +545,12 @@ class User { 23 ``` +## 公众号 + +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 + +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"面试突击"** 即可免费领取! + +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 + +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md b/docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md index 21fe5034d70..0044f0dca71 100644 --- a/docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md +++ b/docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md @@ -1,3 +1,5 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 + - [Java 并发进阶常见面试题总结](#java-并发进阶常见面试题总结) @@ -52,10 +54,10 @@ synchronized关键字解决的是多个线程之间访问资源的同步性,sy **synchronized关键字最主要的三种使用方式:** - **修饰实例方法:** 作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁 -- **修饰静态方法:** :也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,**因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁**。 +- **修饰静态方法:** 也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,**因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁**。 - **修饰代码块:** 指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 -**总结:** synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。synchronized 关键字加到静态方法上是给对象实例上锁。尽量不要使用 synchronized(String a) 因为JVM中,字符串常量池具有缓存功能! +**总结:** synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。synchronized 关键字加到实例方法上是给对象实例上锁。尽量不要使用 synchronized(String a) 因为JVM中,字符串常量池具有缓存功能! 下面我以一个常见的面试题为例讲解一下 synchronized 关键字的具体使用。 @@ -93,7 +95,7 @@ uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueIns 2. 初始化 uniqueInstance 3. 将 uniqueInstance 指向分配的内存地址 -但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。 +但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。 使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。 @@ -116,7 +118,7 @@ public class SynchronizedDemo { 通过 JDK 自带的 javap 命令查看 SynchronizedDemo 类的相关字节码信息:首先切换到类的对应目录执行 `javac SynchronizedDemo.java` 命令生成编译后的 .class 文件,然后执行`javap -c -s -v -l SynchronizedDemo.class`。 -![synchronized 关键字原理](https://user-gold-cdn.xitu.io/2018/10/26/166add616a292bcf?w=917&h=633&f=png&s=21863) +![synchronized关键字原理](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/synchronized关键字原理.png) 从上面我们可以看出: @@ -133,7 +135,7 @@ public class SynchronizedDemo2 { ``` -![synchronized 关键字原理](https://user-gold-cdn.xitu.io/2018/10/26/166add6169fc206d?w=875&h=421&f=png&s=16114) +![synchronized关键字原理](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/synchronized关键字原理2.png) synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。 @@ -144,7 +146,7 @@ JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、 锁主要存在四种状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。 -关于这几种优化的详细信息可以查看:[synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484539&idx=1&sn=3500cdcd5188bdc253fb19a1bfa805e6&chksm=fd98521acaefdb0c5167247a1fa903a1a53bb4e050b558da574f894f9feda5378ec9d0fa1ac7&token=1604028915&lang=zh_CN#rd) +关于这几种优化的详细信息可以查看笔主的这篇文章: ### 1.5. 谈谈 synchronized和ReentrantLock 的区别 @@ -174,15 +176,15 @@ synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团 ### 2.1. 讲一下Java内存模型 -在 JDK1.2 之前,Java的内存模型实现总是从**主存**(即共享内存)读取变量,是不需要进行特别的注意的。而在当前的 Java 内存模型下,线程可以把变量保存**本地内存**比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成**数据的不一致**。 +在 JDK1.2 之前,Java的内存模型实现总是从**主存**(即共享内存)读取变量,是不需要进行特别的注意的。而在当前的 Java 内存模型下,线程可以把变量保存**本地内存**(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成**数据的不一致**。 -![数据的不一致](https://user-gold-cdn.xitu.io/2018/10/30/166c46ede4423ba2?w=273&h=166&f=jpeg&s=7268) +![数据不一致](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/数据不一致.png) 要解决这个问题,就需要把变量声明为**volatile**,这就指示 JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。 说白了, **volatile** 关键字的主要作用就是保证变量的可见性然后还有一个作用是防止指令重排序。 -![volatile关键字的可见性](https://user-gold-cdn.xitu.io/2018/10/30/166c46ede4b9f501?w=474&h=238&f=jpeg&s=9942) +![volatile关键字的可见性](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/volatile关键字的可见性.png) ### 2.2. 说说 synchronized 关键字和 volatile 关键字的区别 @@ -204,7 +206,7 @@ synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团 再举个简单的例子: -比如有两个人去宝屋收集宝物,这两个共用一个袋子的话肯定会产生争执,但是给他们两个人每个人分配一个袋子的话就不会出现这样的问题。如果把这两个人比作线程的话,那么ThreadLocal就是用来这两个线程竞争的。 +比如有两个人去宝屋收集宝物,这两个共用一个袋子的话肯定会产生争执,但是给他们两个人每个人分配一个袋子的话就不会出现这样的问题。如果把这两个人比作线程的话,那么ThreadLocal就是用来避免这两个线程竞争的。 ### 3.2. ThreadLocal示例 @@ -319,17 +321,17 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; } ``` -通过上面这些内容,我们足以通过猜测得出结论:**最终的变量是放在了当前线程的 `ThreadLocalMap` 中,并不是存在 `ThreadLocal` 上,ThreadLocal 可以理解为只是ThreadLocalMap的封装,传递了变量值。** +通过上面这些内容,我们足以通过猜测得出结论:**最终的变量是放在了当前线程的 `ThreadLocalMap` 中,并不是存在 `ThreadLocal` 上,`ThreadLocal` 可以理解为只是`ThreadLocalMap`的封装,传递了变量值。** `ThrealLocal` 类中可以通过`Thread.currentThread()`获取到当前线程对象后,直接通过`getMap(Thread t)`可以访问到该线程的`ThreadLocalMap`对象。 -**每个`Thread`中都具备一个`ThreadLocalMap`,而`ThreadLocalMap`可以存储以`ThreadLocal`为key的键值对。这里解释了为什么每个线程访问同一个`ThreadLocal`,得到的确是不同的数值。另外,`ThreadLocal` 是 map结构是为了让每个线程可以关联多个 `ThreadLocal`变量。** +**每个`Thread`中都具备一个`ThreadLocalMap`,而`ThreadLocalMap`可以存储以`ThreadLocal`为key的键值对。** 比如我们在同一个线程中声明了两个 `ThreadLocal` 对象的话,会使用 `Thread`内部都是使用仅有那个`ThreadLocalMap` 存放数据的,`ThreadLocalMap`的 key 就是 `ThreadLocal`对象,value 就是 `ThreadLocal` 对象调用`set`方法设置的值。`ThreadLocal` 是 map结构是为了让每个线程可以关联多个 `ThreadLocal`变量。这也就解释了 ThreadLocal 声明的变量为什么在每一个线程都有自己的专属本地变量。 `ThreadLocalMap`是`ThreadLocal`的静态内部类。 -![ThreadLocal内部类](https://ws1.sinaimg.cn/large/006rNwoDgy1g2f47u9li2j30ka08cq43.jpg) +![ThreadLocal内部类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ThreadLocal内部类.png) ### 3.4. ThreadLocal 内存泄露问题 -`ThreadLocalMap` 中使用的 key 为 `ThreadLocal` 的弱引用,而 value 是强引用。所以,如果 `ThreadLocal` 没有被外部强引用的情况下,在垃圾回收的时候会 key 会被清理掉,而 value 不会被清理掉。这样一来,`ThreadLocalMap` 中就会出现key为null的Entry。假如我们不做任何措施的话,value 永远无法被GC 回收,这个时候就可能会产生内存泄露。ThreadLocalMap实现中已经考虑了这种情况,在调用 `set()`、`get()`、`remove()` 方法的时候,会清理掉 key 为 null 的记录。使用完 `ThreadLocal`方法后 最好手动调用`remove()`方法 +`ThreadLocalMap` 中使用的 key 为 `ThreadLocal` 的弱引用,而 value 是强引用。所以,如果 `ThreadLocal` 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。这样一来,`ThreadLocalMap` 中就会出现key为null的Entry。假如我们不做任何措施的话,value 永远无法被GC 回收,这个时候就可能会产生内存泄露。ThreadLocalMap实现中已经考虑了这种情况,在调用 `set()`、`get()`、`remove()` 方法的时候,会清理掉 key 为 null 的记录。使用完 `ThreadLocal`方法后 最好手动调用`remove()`方法 ```java static class Entry extends WeakReference> { @@ -383,7 +385,7 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; > - **CachedThreadPool 和 ScheduledThreadPool** : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。 **方式一:通过构造方法实现** -![通过构造方法实现](https://user-gold-cdn.xitu.io/2018/10/30/166c4a5baac923e9?w=925&h=158&f=jpeg&s=29190) +![ThreadPoolExecutor构造方法](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ThreadPoolExecutor构造方法.png) **方式二:通过Executor 框架的工具类Executors来实现** 我们可以创建三种类型的ThreadPoolExecutor: @@ -392,7 +394,7 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; - **CachedThreadPool:** 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。 对应Executors工具类中的方法如图所示: -![通过Executor 框架的工具类Executors来实现](https://user-gold-cdn.xitu.io/2018/10/30/166c4a5baa9ca5e9?w=645&h=222&f=jpeg&s=31710) +![Executor框架的工具类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/Executor框架的工具类.png) ## 5. Atomic 原子类 @@ -405,7 +407,7 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是 并发包 `java.util.concurrent` 的原子类都存放在`java.util.concurrent.atomic`下,如下图所示。 -![ JUC 原子类概览](https://user-gold-cdn.xitu.io/2018/10/30/166c4ac08d4c5547?w=317&h=367&f=png&s=13267) +![JUC原子类概览](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/JUC原子类概览.png) ### 5.2. JUC 包中的原子类是哪4类? @@ -429,7 +431,7 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是 **引用类型** - AtomicReference:引用类型原子类 -- AtomicStampedRerence:原子更新引用类型里的字段原子类 +- AtomicStampedReference:原子更新引用类型里的字段原子类 - AtomicMarkableReference :原子更新带有标记位的引用类型 **对象的属性修改类型** @@ -504,7 +506,7 @@ CAS的原理是拿期望的值和原本的一个值作比较,如果相同则 AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包下面。 -![enter image description here](https://user-gold-cdn.xitu.io/2018/10/30/166c4bb575d4a690?w=317&h=338&f=png&s=14122) +![AQS类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/AQS类.png) AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。当然,我们自己也能利用AQS非常轻松容易地构造出符合我们自己需求的同步器。 @@ -512,14 +514,12 @@ AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效 AQS 原理这部分参考了部分博客,在5.2节末尾放了链接。 -> 在面试中被问到并发知识的时候,大多都会被问到“请你说一下自己对于AQS原理的理解”。下面给大家一个示例供大家参加,面试不是背题,大家一定要假如自己的思想,即使加入不了自己的思想也要保证自己能够通俗的讲出来而不是背出来。 +> 在面试中被问到并发知识的时候,大多都会被问到“请你说一下自己对于AQS原理的理解”。下面给大家一个示例供大家参加,面试不是背题,大家一定要加入自己的思想,即使加入不了自己的思想也要保证自己能够通俗的讲出来而不是背出来。 下面大部分内容其实在AQS类注释上已经给出了,不过是英语看着比较吃力一点,感兴趣的话可以看看源码。 #### 6.2.1. AQS 原理概览 - - **AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。** > CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。 @@ -527,7 +527,7 @@ AQS 原理这部分参考了部分博客,在5.2节末尾放了链接。 看个AQS(AbstractQueuedSynchronizer)原理图: -![enter image description here](https://user-gold-cdn.xitu.io/2018/10/30/166c4bbe4a9c5ae7?w=852&h=401&f=png&s=21797) +![AQS原理图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/AQS原理图.png) AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。 @@ -612,4 +612,14 @@ tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true - 《Java并发编程的艺术》 - http://www.cnblogs.com/waterystone/p/4920797.html - https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html -- \ No newline at end of file +- + +## 公众号 + +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 + +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"面试突击"** 即可免费领取! + +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 + +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md b/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md index f013fba71ef..ddf3e8805e1 100644 --- a/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md +++ b/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md @@ -1,3 +1,5 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 + - [Java 并发基础常见面试题总结](#java-并发基础常见面试题总结) @@ -34,7 +36,7 @@ 如下图所示,在 windows 中通过查看任务管理器的方式,我们就可以清楚看到 window 当前运行的进程(.exe 文件的运行)。 -![进程 ](https://images.gitbook.cn/a0929b60-d133-11e8-88a4-5328c5b70145) +![进程示例图片-Windows](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/进程示例图片-Windows.png) ### 1.2. 何为线程? @@ -75,9 +77,11 @@ public class MultiThread { ### 2.1. 图解进程和线程的关系 -下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。如果你对 Java 内存区域 (运行时数据区) 这部分知识不太了解的话可以阅读一下这篇文章:[《可能是把 Java 内存区域讲的最清楚的一篇文章》](https://snailclimb.gitee.io/javaguide/#/java/可能是把 Java 内存区域讲的最清楚的一篇文章) +下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。如果你对 Java 内存区域 (运行时数据区) 这部分知识不太了解的话可以阅读一下这篇文章:[《可能是把 Java 内存区域讲的最清楚的一篇文章》]() -![](https://diycode.b0.upaiyun.com/photo/2019/ff96fed0e2a354bb16bbc84dcedf503a.png) +
+ +
从上图可以看出:一个进程中可以有多个线程,多个线程共享进程的**堆**和**方法区 (JDK1.8 之后的元空间)**资源,但是每个线程有自己的**程序计数器**、**虚拟机栈** 和 **本地方法栈**。 @@ -100,8 +104,8 @@ public class MultiThread { ### 2.3. 虚拟机栈和本地方法栈为什么是私有的? -- **虚拟机栈:**每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。 -- **本地方法栈:**和虚拟机栈所发挥的作用非常相似,区别是: **虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。** 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。 +- **虚拟机栈:** 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。 +- **本地方法栈:** 和虚拟机栈所发挥的作用非常相似,区别是: **虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。** 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。 所以,为了**保证线程中的局部变量不被别的线程访问到**,虚拟机栈和本地方法栈是线程私有的。 @@ -148,13 +152,13 @@ Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种 ![RUNNABLE-VS-RUNNING](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3/RUNNABLE-VS-RUNNING.png) -当线程执行 `wait()`方法之后,线程进入 **WAITING(等待)**状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 **TIME_WAITING(超时等待)** 状态相当于在等待状态的基础上增加了超时限制,比如通过 `sleep(long millis)`方法或 `wait(long millis)`方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。线程在执行 Runnable 的` run() `方法之后将会进入到 **TERMINATED(终止)** 状态。 +当线程执行 `wait()`方法之后,线程进入 **WAITING(等待)** 状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 **TIME_WAITING(超时等待)** 状态相当于在等待状态的基础上增加了超时限制,比如通过 `sleep(long millis)`方法或 `wait(long millis)`方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。线程在执行 Runnable 的` run() `方法之后将会进入到 **TERMINATED(终止)** 状态。 ## 7. 什么是上下文切换? 多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。 -概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换会这个任务时,可以再加载这个任务的状态。**任务从保存到再加载的过程就是一次上下文切换**。 +概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。**任务从保存到再加载的过程就是一次上下文切换**。 上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。 @@ -168,7 +172,7 @@ Linux 相比与其他操作系统(包括其他类 Unix 系统)有很多的 如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。 -![线程死锁示意图 ](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-4/2019-4 死锁 1.png) +![线程死锁示意图 ](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-4/2019-4%E6%AD%BB%E9%94%811.png) 下面通过一个例子来说明线程死锁,代码模拟了上图的死锁的情况 (代码来源于《并发编程之美》): @@ -290,7 +294,8 @@ Process finished with exit code 0 - 两者最主要的区别在于:**sleep 方法没有释放锁,而 wait 方法释放了锁** 。 - 两者都可以暂停线程的执行。 - Wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。 -- wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。 +- wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。或者可以使用wait(long timeout)超时后线程会自动苏醒。 + ## 10. 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法? @@ -299,3 +304,13 @@ Process finished with exit code 0 new 一个 Thread,线程进入了新建状态;调用 start() 方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。 **总结: 调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。** + +## 公众号 + +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 + +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"面试突击"** 即可免费领取! + +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 + +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/java/Multithread/ThredLocal.md b/docs/java/Multithread/ThredLocal.md index 590cf754a2e..84d619df728 100644 --- a/docs/java/Multithread/ThredLocal.md +++ b/docs/java/Multithread/ThredLocal.md @@ -129,7 +129,19 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; 通过上面这些内容,我们足以通过猜测得出结论:**最终的变量是放在了当前线程的 `ThreadLocalMap` 中,并不是存在 `ThreadLocal` 上,ThreadLocal 可以理解为只是ThreadLocalMap的封装,传递了变量值。** -**每个`Thread`中都具备一个`ThreadLocalMap`,而`ThreadLocalMap`可以存储以`ThreadLocal`为key的键值对。这里解释了为什么每个线程访问同一个`ThreadLocal`,得到的确是不同的数值。另外,`ThreadLocal` 是 map结构是为了让每个线程可以关联多个 `ThreadLocal`变量。** +**每个Thread中都具备一个ThreadLocalMap,而ThreadLocalMap可以存储以ThreadLocal为key的键值对。** 比如我们在同一个线程中声明了两个 `ThreadLocal` 对象的话,会使用 `Thread`内部都是使用仅有那个`ThreadLocalMap` 存放数据的,`ThreadLocalMap`的 key 就是 `ThreadLocal`对象,value 就是 `ThreadLocal` 对象调用`set`方法设置的值。`ThreadLocal` 是 map结构是为了让每个线程可以关联多个 `ThreadLocal`变量。这也就解释了ThreadLocal声明的变量为什么在每一个线程都有自己的专属本地变量。 + +```java +public class Thread implements Runnable { + ...... +//与此线程有关的ThreadLocal值。由ThreadLocal类维护 +ThreadLocal.ThreadLocalMap threadLocals = null; + +//与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护 +ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; + ...... +} +``` `ThreadLocalMap`是`ThreadLocal`的静态内部类。 diff --git a/docs/java/synchronized.md b/docs/java/Multithread/synchronized.md similarity index 100% rename from docs/java/synchronized.md rename to docs/java/Multithread/synchronized.md diff --git "a/docs/java/Multithread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" "b/docs/java/Multithread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" index 22873b3d9fd..243001efe59 100644 --- "a/docs/java/Multithread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" +++ "b/docs/java/Multithread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" @@ -1,3 +1,4 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 @@ -37,12 +38,10 @@ JDK提供的这些容器大部分在 `java.util.concurrent` 包中。 所以就有了 HashMap 的线程安全版本—— ConcurrentHashMap 的诞生。在ConcurrentHashMap中,无论是读操作还是写操作都能保证很高的性能:在进行读操作时(几乎)不需要加锁,而在写操作时通过锁分段技术只对所操作的段加锁而不影响客户端对其它段的访问。 -关于 ConcurrentHashMap 相关问题,我在 [《这几道Java集合框架面试题几乎必问》](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98%E6%80%BB%E7%BB%93.md) 这篇文章中已经提到过。下面梳理一下关于 ConcurrentHashMap 比较重要的问题: - -- [ConcurrentHashMap 和 Hashtable 的区别](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/%E8%BF%99%E5%87%A0%E9%81%93Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E9%9D%A2%E8%AF%95%E9%A2%98%E5%87%A0%E4%B9%8E%E5%BF%85%E9%97%AE.md#concurrenthashmap-%E5%92%8C-hashtable-%E7%9A%84%E5%8C%BA%E5%88%AB) -- [ConcurrentHashMap线程安全的具体实现方式/底层具体实现](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/%E8%BF%99%E5%87%A0%E9%81%93Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E9%9D%A2%E8%AF%95%E9%A2%98%E5%87%A0%E4%B9%8E%E5%BF%85%E9%97%AE.md#concurrenthashmap%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E5%85%B7%E4%BD%93%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F%E5%BA%95%E5%B1%82%E5%85%B7%E4%BD%93%E5%AE%9E%E7%8E%B0) - +关于 ConcurrentHashMap 相关问题,我在 [Java集合框架常见面试题](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98.md) 这篇文章中已经提到过。下面梳理一下关于 ConcurrentHashMap 比较重要的问题: +- [ConcurrentHashMap 和 Hashtable 的区别](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98.md#concurrenthashmap-%E5%92%8C-hashtable-%E7%9A%84%E5%8C%BA%E5%88%AB) +- [ConcurrentHashMap线程安全的具体实现方式/底层具体实现](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98.md#concurrenthashmap%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E5%85%B7%E4%BD%93%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F%E5%BA%95%E5%B1%82%E5%85%B7%E4%BD%93%E5%AE%9E%E7%8E%B0) ## 三 CopyOnWriteArrayList @@ -214,10 +213,18 @@ PriorityBlockingQueue 并发控制采用的是 **ReentrantLock**,队列为无 使用跳表实现Map 和使用哈希算法实现Map的另外一个不同之处是:哈希并不会保存元素的顺序,而跳表内所有的元素都是排序的。因此在对跳表进行遍历时,你会得到一个有序的结果。所以,如果你的应用需要有序性,那么跳表就是你不二的选择。JDK 中实现这一数据结构的类是ConcurrentSkipListMap。 - - ## 七 参考 - 《实战Java高并发程序设计》 - https://javadoop.com/post/java-concurrent-queue - https://juejin.im/post/5aeebd02518825672f19c546 + +## 公众号 + +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 + +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"面试突击"** 即可免费领取! + +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 + +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git "a/docs/java/Multithread/1\345\271\266\345\217\221\347\274\226\347\250\213\345\237\272\347\241\200\347\237\245\350\257\206.md" "b/docs/java/Multithread/\345\271\266\345\217\221\347\274\226\347\250\213\345\237\272\347\241\200\347\237\245\350\257\206.md" similarity index 98% rename from "docs/java/Multithread/1\345\271\266\345\217\221\347\274\226\347\250\213\345\237\272\347\241\200\347\237\245\350\257\206.md" rename to "docs/java/Multithread/\345\271\266\345\217\221\347\274\226\347\250\213\345\237\272\347\241\200\347\237\245\350\257\206.md" index 2e2c8c399f9..68509cdc066 100644 --- "a/docs/java/Multithread/1\345\271\266\345\217\221\347\274\226\347\250\213\345\237\272\347\241\200\347\237\245\350\257\206.md" +++ "b/docs/java/Multithread/\345\271\266\345\217\221\347\274\226\347\250\213\345\237\272\347\241\200\347\237\245\350\257\206.md" @@ -57,7 +57,7 @@ public class MultiThread { #### 1.3.1 图解进程和线程的关系 -下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。如果你对 Java 内存区域 (运行时数据区) 这部分知识不太了解的话可以阅读一下我的这篇文章:[《可能是把 Java 内存区域讲的最清楚的一篇文章》](https://github.com/Snailclimb/JavaGuide/blob/master/Java 相关/可能是把 Java 内存区域讲的最清楚的一篇文章.md) +下图是 Java 内存区域,通过下图我们从 JVM 的角度来说一下线程和进程之间的关系。如果你对 Java 内存区域 (运行时数据区) 这部分知识不太了解的话可以阅读一下我的这篇文章:[《可能是把 Java 内存区域讲的最清楚的一篇文章》]()
@@ -340,7 +340,7 @@ Thread[线程 2,5,main]waiting get resource1 1. 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。 1. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 -### 如何避免线程死锁? +### 如何预防线程死锁? 我们只要破坏产生死锁的四个条件中的其中一个就可以了。 diff --git "a/docs/java/What's New in JDK8/JDK8\346\216\245\345\217\243\350\247\204\350\214\203-\351\235\231\346\200\201\343\200\201\351\273\230\350\256\244\346\226\271\346\263\225.md" "b/docs/java/What's New in JDK8/JDK8\346\216\245\345\217\243\350\247\204\350\214\203-\351\235\231\346\200\201\343\200\201\351\273\230\350\256\244\346\226\271\346\263\225.md" deleted file mode 100644 index ee1dd8c2448..00000000000 --- "a/docs/java/What's New in JDK8/JDK8\346\216\245\345\217\243\350\247\204\350\214\203-\351\235\231\346\200\201\343\200\201\351\273\230\350\256\244\346\226\271\346\263\225.md" +++ /dev/null @@ -1,163 +0,0 @@ -JDK8接口规范 -=== -在JDK8中引入了lambda表达式,出现了函数式接口的概念,为了在扩展接口时保持向前兼容性(比如泛型也是为了保持兼容性而失去了在一些别的语言泛型拥有的功能),Java接口规范发生了一些改变。。 ---- -## 1.JDK8以前的接口规范 -- JDK8以前接口可以定义的变量和方法 - - 所有变量(Field)不论是否显式 的声明为```public static final```,它实际上都是```public static final```的。 - - 所有方法(Method)不论是否显示 的声明为```public abstract```,它实际上都是```public abstract```的。 -```java -public interface AInterfaceBeforeJDK8 { - int FIELD = 0; - void simpleMethod(); -} -``` -以上接口信息反编译以后可以看到字节码信息里Filed是public static final的,而方法是public abstract的,即是你没有显示的去声明它。 -```java -{ - public static final int FIELD; - descriptor: I - flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 0 - - public abstract void simpleMethod(); - descriptor: ()V - flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT -} -``` -## 2.JDK8之后的接口规范 -- JDK8之后接口可以定义的变量和方法 - - 变量(Field)仍然必须是 ```java public static final```的 - - 方法(Method)除了可以是public abstract之外,还可以是public static或者是default(相当于仅public修饰的实例方法)的。 -从以上改变不难看出,修改接口的规范主要是为了能在扩展接口时保持向前兼容。 -
下面是一个JDK8之后的接口例子 -```java -public interface AInterfaceInJDK8 { - int simpleFiled = 0; - static int staticField = 1; - - public static void main(String[] args) { - } - static void staticMethod(){} - - default void defaultMethod(){} - - void simpleMethod() throws IOException; - -} -``` -进行反编译(去除了一些没用信息) -```java -{ - public static final int simpleFiled; - flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL - - public static final int staticField; - flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL - - public static void main(java.lang.String[]); - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - - public static void staticMethod(); - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - - public void defaultMethod(); - flags: (0x0001) ACC_PUBLIC - - public abstract void simpleMethod() throws java.io.IOException; - flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT - Exceptions: - throws java.io.IOException -} -``` -可以看到 default关键字修饰的方法是像实例方法一样定义的,所以我们来定义一个只有default的方法并且实现一下试一试。 -```java -interface Default { - default int defaultMethod() { - return 4396; - } -} - -public class DefaultMethod implements Default { - public static void main(String[] args) { - DefaultMethod defaultMethod = new DefaultMethod(); - System.out.println(defaultMethod.defaultMethod()); - //compile error : Non-static method 'defaultMethod()' cannot be referenced from a static context - //! DefaultMethod.defaultMethod(); - } -} -``` -可以看到default方法确实像实例方法一样,必须有实例对象才能调用,并且子类在实现接口时,可以不用实现default方法,也可以覆盖该方法。 -这有点像子类继承父类实例方法。 -
-接口静态方法就像是类静态方法,唯一的区别是**接口静态方法只能通过接口名调用,而类静态方法既可以通过类名调用也可以通过实例调用** -```java -interface Static { - static int staticMethod() { - return 4396; - } -} - ... main(String...args) - //!compile error: Static method may be invoked on containing interface class only - //!aInstanceOfStatic.staticMethod(); - ... -``` -另一个问题是多继承问题,大家知道Java中类是不支持多继承的,但是接口是多继承和多实现(implements后跟多个接口)的, -那么如果一个接口继承另一个接口,两个接口都有同名的default方法会怎么样呢?答案是会像类继承一样覆写(@Override),以下代码在IDE中可以顺利编译 -```java -interface Default { - default int defaultMethod() { - return 4396; - } -} -interface Default2 extends Default { - @Override - default int defaultMethod() { - return 9527; - } -} -public class DefaultMethod implements Default,Default2 { - public static void main(String[] args) { - DefaultMethod defaultMethod = new DefaultMethod(); - System.out.println(defaultMethod.defaultMethod()); - } -} - -输出 : 9527 -``` -出现上面的情况时,会优先找继承树上近的方法,类似于“短路优先”。 -
-那么如果一个类实现了两个没有继承关系的接口,且这两个接口有同名方法的话会怎么样呢?IDE会要求你重写这个冲突的方法,让你自己选择去执行哪个方法,因为IDE它 -还没智能到你不告诉它,它就知道你想执行哪个方法。可以通过```java 接口名.super```指针来访问接口中定义的实例(default)方法。 -```java -interface Default { - default int defaultMethod() { - return 4396; - } -} - -interface Default2 { - default int defaultMethod() { - return 9527; - } -} -//如果不重写 -//compile error : defaults.DefaultMethod inherits unrelated defaults for defaultMethod() from types defaults.Default and defaults.Default2 -public class DefaultMethod implements Default,Default2 { -@Override - public int defaultMethod() { - System.out.println(Default.super.defaultMethod()); - System.out.println(Default2.super.defaultMethod()); - return 996; - } - public static void main(String[] args) { - DefaultMethod defaultMethod = new DefaultMethod(); - System.out.println(defaultMethod.defaultMethod()); - } -} - -运行输出 : -4396 -9527 -996 -``` diff --git a/docs/java/What's New in JDK8/Java8Tutorial.md b/docs/java/What's New in JDK8/Java8Tutorial.md index cee4d4e4f0a..56e7f52d5b0 100644 --- a/docs/java/What's New in JDK8/Java8Tutorial.md +++ b/docs/java/What's New in JDK8/Java8Tutorial.md @@ -1,3 +1,5 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 + 随着 Java 8 的普及度越来越高,很多人都提到面试中关于Java 8 也是非常常问的知识点。应各位要求和需要,我打算对这部分知识做一个总结。本来准备自己总结的,后面看到Github 上有一个相关的仓库,地址: [https://github.com/winterbe/java8-tutorial](https://github.com/winterbe/java8-tutorial)。这个仓库是英文的,我对其进行了翻译并添加和修改了部分内容,下面是正文了。 @@ -442,15 +444,15 @@ optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b" 首先看看Stream是怎么用,首先创建实例代码的用到的数据List: ```java -List stringCollection = new ArrayList<>(); -stringCollection.add("ddd2"); -stringCollection.add("aaa2"); -stringCollection.add("bbb1"); -stringCollection.add("aaa1"); -stringCollection.add("bbb3"); -stringCollection.add("ccc"); -stringCollection.add("bbb2"); -stringCollection.add("ddd1"); +List stringList = new ArrayList<>(); +stringList.add("ddd2"); +stringList.add("aaa2"); +stringList.add("bbb1"); +stringList.add("aaa1"); +stringList.add("bbb3"); +stringList.add("ccc"); +stringList.add("bbb2"); +stringList.add("ddd1"); ``` Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。下面几节将详细解释常用的Stream操作: @@ -492,7 +494,7 @@ forEach 是为 Lambda 而设计的,保持了最紧凑的风格。而且 Lambda 中间操作 map 会将元素根据指定的 Function 接口来依次将元素转成另外的对象。 -下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。 +下面的示例展示了将字符串转换为大写字符串。你也可以通过map来将对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。 ```java // 测试 Map 操作 @@ -916,9 +918,16 @@ System.out.println(hints2.length); // 2 @interface MyAnnotation {} ``` +## Where to go from here? +关于Java 8的新特性就写到这了,肯定还有更多的特性等待发掘。JDK 1.8里还有很多很有用的东西,比如`Arrays.parallelSort`, `StampedLock`和`CompletableFuture`等等。 -## Whete to go from here? +## 公众号 -关于Java 8的新特性就写到这了,肯定还有更多的特性等待发掘。JDK 1.8里还有很多很有用的东西,比如`Arrays.parallelSort`, `StampedLock`和`CompletableFuture`等等。 +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 + +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! + +**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。 +![我的公众号](https://user-gold-cdn.xitu.io/2018/11/28/167598cd2e17b8ec?w=258&h=258&f=jpeg&s=27334) diff --git "a/docs/java/What's New in JDK8/Java8foreach\346\214\207\345\215\227.md" "b/docs/java/What's New in JDK8/Java8foreach\346\214\207\345\215\227.md" new file mode 100644 index 00000000000..48cda3abfd9 --- /dev/null +++ "b/docs/java/What's New in JDK8/Java8foreach\346\214\207\345\215\227.md" @@ -0,0 +1,139 @@ +> 本文由 JavaGuide 翻译,原文地址:https://www.baeldung.com/foreach-java + +## 1 概述 + +在Java 8中引入的*forEach*循环为程序员提供了一种新的,简洁而有趣的迭代集合的方式。 + +在本文中,我们将看到如何将*forEach*与集合*一起*使用,它采用何种参数以及此循环与增强的*for*循环的不同之处。 + +## 2 基础知识 + +```Java +public interface Collection extends Iterable +``` + +Collection 接口实现了 Iterable 接口,而 Iterable 接口在 Java 8开始具有一个新的 API: + +```java +void forEach(Consumer action)//对 Iterable的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。 +``` + +使用*forEach*,我们可以迭代一个集合并对每个元素执行给定的操作,就像任何其他*迭代器一样。* + +例如,迭代和打印字符串集合*的*for循环版本: + +```java +for (String name : names) { + System.out.println(name); +} +``` + +我们可以使用*forEach*写这个 : + +```java +names.forEach(name -> { + System.out.println(name); +}); +``` + +## 3.使用forEach方法 + +### 3.1 匿名类 + +我们使用 *forEach*迭代集合并对每个元素执行特定操作。**要执行的操作包含在实现Consumer接口的类中,并作为参数传递给forEach 。** + +所述*消费者*接口是一个功能接口(具有单个抽象方法的接口)。它接受输入并且不返回任何结果。 + +Consumer 接口定义如下: + +```java +@FunctionalInterface +public interface Consumer { + void accept(T t); +} +``` +任何实现,例如,只是打印字符串的消费者: + +```java +Consumer printConsumer = new Consumer() { + public void accept(String name) { + System.out.println(name); + }; +}; +``` + +可以作为参数传递给*forEach*: + +```java +names.forEach(printConsumer); +``` + +但这不是通过消费者和使用*forEach* API 创建操作的唯一方法。让我们看看我们将使用*forEach*方法的另外2种最流行的方式: + +### 3.2 Lambda表达式 + +Java 8功能接口的主要优点是我们可以使用Lambda表达式来实例化它们,并避免使用庞大的匿名类实现。 + +由于 Consumer 接口属于函数式接口,我们可以通过以下形式在Lambda中表达它: + +```java +(argument) -> { body } +name -> System.out.println(name) +names.forEach(name -> System.out.println(name)); +``` + +### 3.3 方法参考 + +我们可以使用方法引用语法而不是普通的Lambda语法,其中已存在一个方法来对类执行操作: + +```java +names.forEach(System.out::println); +``` + +## 4.forEach在集合中的使用 + +### 4.1.迭代集合 + +**任何类型Collection的可迭代 - 列表,集合,队列 等都具有使用forEach的相同语法。** + +因此,正如我们已经看到的,迭代列表的元素: + +```java +List names = Arrays.asList("Larry", "Steve", "James"); + +names.forEach(System.out::println); +``` + +同样对于一组: + +```java +Set uniqueNames = new HashSet<>(Arrays.asList("Larry", "Steve", "James")); + +uniqueNames.forEach(System.out::println); +``` + +或者让我们说一个*队列*也是一个*集合*: + +```java +Queue namesQueue = new ArrayDeque<>(Arrays.asList("Larry", "Steve", "James")); + +namesQueue.forEach(System.out::println); +``` + +### 4.2.迭代Map - 使用Map的forEach + +Map没有实现Iterable接口,但它**提供了自己的forEach 变体,它接受BiConsumer**。* + +```java +Map namesMap = new HashMap<>(); +namesMap.put(1, "Larry"); +namesMap.put(2, "Steve"); +namesMap.put(3, "James"); +namesMap.forEach((key, value) -> System.out.println(key + " " + value)); +``` + +### 4.3.迭代一个Map - 通过迭代entrySet + +```java +namesMap.entrySet().forEach(entry -> System.out.println(entry.getKey() + " " + entry.getValue())); +``` \ No newline at end of file diff --git "a/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md" "b/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md" index 43e4539ccac..7de58352a5f 100644 --- "a/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md" +++ "b/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md" @@ -1,5 +1,3 @@ - - ### 书籍 - **《Java8 In Action》** diff --git "a/docs/java/What's New in JDK8/Lambda\350\241\250\350\276\276\345\274\217.md" "b/docs/java/What's New in JDK8/Lambda\350\241\250\350\276\276\345\274\217.md" deleted file mode 100644 index 359c4714473..00000000000 --- "a/docs/java/What's New in JDK8/Lambda\350\241\250\350\276\276\345\274\217.md" +++ /dev/null @@ -1,235 +0,0 @@ -JDK8--Lambda表达式 -=== -## 1.什么是Lambda表达式 -**Lambda表达式实质上是一个可传递的代码块,Lambda又称为闭包或者匿名函数,是函数式编程语法,让方法可以像普通参数一样传递** - -## 2.Lambda表达式语法 -```(参数列表) -> {执行代码块}``` -
参数列表可以为空```()->{}``` -
可以加类型声明比如```(String para1, int para2) -> {return para1 + para2;}```我们可以看到,lambda同样可以有返回值. -
在编译器可以推断出类型的时候,可以将类型声明省略,比如```(para1, para2) -> {return para1 + para2;}``` -
(lambda有点像动态类型语言语法。lambda在字节码层面是用invokedynamic实现的,而这条指令就是为了让JVM更好的支持运行在其上的动态类型语言) - -## 3.函数式接口 -在了解Lambda表达式之前,有必要先了解什么是函数式接口```(@FunctionalInterface)```
-**函数式接口指的是有且只有一个抽象(abstract)方法的接口**
-当需要一个函数式接口的对象时,就可以用Lambda表达式来实现,举个常用的例子: -
-```java - Thread thread = new Thread(() -> { - System.out.println("This is JDK8's Lambda!"); - }); -``` -这段代码和函数式接口有啥关系?我们回忆一下,Thread类的构造函数里是不是有一个以Runnable接口为参数的? -```java -public Thread(Runnable target) {...} - -/** - * Runnable Interface - */ -@FunctionalInterface -public interface Runnable { - public abstract void run(); -} -``` -到这里大家可能已经明白了,**Lambda表达式相当于一个匿名类或者说是一个匿名方法**。上面Thread的例子相当于 -```java - Thread thread = new Thread(new Runnable() { - @Override - public void run() { - System.out.println("Anonymous class"); - } - }); -``` -也就是说,上面的lambda表达式相当于实现了这个run()方法,然后当做参数传入(个人感觉可以这么理解,lambda表达式就是一个函数,只不过它的返回值、参数列表都 -由编译器帮我们推断,因此可以减少很多代码量)。 -
Lambda也可以这样用 : -```java - Runnable runnable = () -> {...}; -``` -其实这和上面的用法没有什么本质上的区别。 -
至此大家应该明白什么是函数式接口以及函数式接口和lambda表达式之间的关系了。在JDK8中修改了接口的规范, -目的是为了在给接口添加新的功能时保持向前兼容(个人理解),比如一个已经定义了的函数式接口,某天我们想给它添加新功能,那么就不能保持向前兼容了, -因为在旧的接口规范下,添加新功能必定会破坏这个函数式接口[(JDK8中接口规范)]() -
-除了上面说的Runnable接口之外,JDK中已经存在了很多函数式接口 -比如(当然不止这些): -- ```java.util.concurrent.Callable``` -- ```java.util.Comparator``` -- ```java.io.FileFilter``` -
**关于JDK中的预定义的函数式接口** - -- JDK在```java.util.function```下预定义了很多函数式接口 - - ```Function {R apply(T t);}``` 接受一个T对象,然后返回一个R对象,就像普通的函数。 - - ```Consumer {void accept(T t);}``` 消费者 接受一个T对象,没有返回值。 - - ```Predicate {boolean test(T t);}``` 判断,接受一个T对象,返回一个布尔值。 - - ```Supplier {T get();} 提供者(工厂)``` 返回一个T对象。 - - 其他的跟上面的相似,大家可以看一下function包下的具体接口。 -## 4.变量作用域 -```java -public class VaraibleHide { - @FunctionalInterface - interface IInner { - void printInt(int x); - } - public static void main(String[] args) { - int x = 20; - IInner inner = new IInner() { - int x = 10; - @Override - public void printInt(int x) { - System.out.println(x); - } - }; - inner.printInt(30); - - inner = (s) -> { - //Variable used in lambda expression should be final or effectively final - //!int x = 10; - //!x= 50; error - System.out.print(x); - }; - inner.printInt(30); - } -} -输出 : -30 -20 -``` -对于lambda表达式```java inner = (s) -> {System.out.print(x);};```,变量x并不是在lambda表达式中定义的,像这样并不是在lambda中定义或者通过lambda的参数列表()获取的变量成为自由变量,它是被lambda表达式捕获的。 -
lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值, -同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该 -不难理解。 -## 5.方法引用 -**只需要提供方法的名字,具体的调用过程由Lambda和函数式接口来确定,这样的方法调用成为方法引用。** -
下面的例子会打印list中的每个元素: -```java -List list = new ArrayList<>(); - for (int i = 0; i < 10; ++i) { - list.add(i); - } - list.forEach(System.out::println); -``` -其中```System.out::println```这个就是一个方法引用,等价于Lambda表达式 ```(para)->{System.out.println(para);}``` -
我们看一下List#forEach方法 ```default void forEach(Consumer action)```可以看到它的参数是一个Consumer接口,该接口是一个函数式接口 -```java -@FunctionalInterface -public interface Consumer { - void accept(T t); -``` -大家能发现这个函数接口的方法和```System.out::println```有什么相似的么?没错,它们有着相似的参数列表和返回值。 -
我们自己定义一个方法,看看能不能像标准输出的打印函数一样被调用 -```java -public class MethodReference { - public static void main(String[] args) { - List list = new ArrayList<>(); - for (int i = 0; i < 10; ++i) { - list.add(i); - } - list.forEach(MethodReference::myPrint); - } - - static void myPrint(int i) { - System.out.print(i + ", "); - } -} - -输出: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -``` -可以看到,我们自己定义的方法也可以当做方法引用。 -
到这里大家多少对方法引用有了一定的了解,我们再来说一下方法引用的形式。 -- 方法引用 - - 类名::静态方法名 - - 类名::实例方法名 - - 类名::new (构造方法引用) - - 实例名::实例方法名 -可以看出,方法引用是通过(方法归属名)::(方法名)来调用的。通过上面的例子已经讲解了一个`类名::静态方法名`的使用方法了,下面再依次介绍其余的几种 -方法引用的使用方法。
-**类名::实例方法名**
-先来看一段代码 -```java - String[] strings = new String[10]; - Arrays.sort(strings, String::compareToIgnoreCase); -``` -**上面的String::compareToIgnoreCase等价于(x, y) -> {return x.compareToIgnoreCase(y);}**
-我们看一下`Arrays#sort`方法`public static void sort(T[] a, Comparator c)`, -可以看到第二个参数是一个Comparator接口,该接口也是一个函数式接口,其中的抽象方法是`int compare(T o1, T o2);`,再看一下 -`String#compareToIgnoreCase`方法,`public int compareToIgnoreCase(String str)`,这个方法好像和上面讲方法引用中`类名::静态方法名`不大一样啊,它 -的参数列表和函数式接口的参数列表不一样啊,虽然它的返回值一样? -
是的,确实不一样但是别忘了,String类的这个方法是个实例方法,而不是静态方法,也就是说,这个方法是需要有一个接收者的。所谓接收者就是 -instance.method(x)中的instance, -它是某个类的实例,有的朋友可能已经明白了。上面函数式接口的`compare(T o1, T o2)`中的第一个参数作为了实例方法的接收者,而第二个参数作为了实例方法的 -参数。我们再举一个自己实现的例子: -```java -public class MethodReference { - static Random random = new Random(47); - public static void main(String[] args) { - MethodReference[] methodReferences = new MethodReference[10]; - Arrays.sort(methodReferences, MethodReference::myCompare); - } - int myCompare(MethodReference o) { - return random.nextInt(2) - 1; - } -} -``` -上面的例子可以在IDE里通过编译,大家有兴趣的可以模仿上面的例子自己写一个程序,打印出排序后的结果。 -
**构造器引用**
-构造器引用仍然需要与特定的函数式接口配合使用,并不能像下面这样直接使用。IDE会提示String不是一个函数式接口 -```java - //compile error : String is not a functional interface - String str = String::new; -``` -下面是一个使用构造器引用的例子,可以看出构造器引用可以和这种工厂型的函数式接口一起使用的。 -```java - interface IFunctional { - T func(); -} - -public class ConstructorReference { - - public ConstructorReference() { - } - - public static void main(String[] args) { - Supplier supplier0 = () -> new ConstructorReference(); - Supplier supplier1 = ConstructorReference::new; - IFunctional functional = () -> new ConstructorReference(); - IFunctional functional1 = ConstructorReference::new; - } -} -``` -下面是一个JDK官方的例子 -```java - public static , DEST extends Collection> - DEST transferElements( - SOURCE sourceCollection, - Supplier collectionFactory) { - - DEST result = collectionFactory.get(); - for (T t : sourceCollection) { - result.add(t); - } - return result; - } - - ... - - Set rosterSet = transferElements( - roster, HashSet::new); -``` - -**实例::实例方法** -
-其实开始那个例子就是一个实例::实例方法的引用 -```java -List list = new ArrayList<>(); - for (int i = 0; i < 10; ++i) { - list.add(i); - } - list.forEach(System.out::println); -``` -其中System.out就是一个实例,println是一个实例方法。相信不用再给大家做解释了。 -## 总结 -Lambda表达式是JDK8引入Java的函数式编程语法,使用Lambda需要直接或者间接的与函数式接口配合,在开发中使用Lambda可以减少代码量, -但是并不是说必须要使用Lambda(虽然它是一个很酷的东西)。有些情况下使用Lambda会使代码的可读性急剧下降,并且也节省不了多少代码, -所以在实际开发中还是需要仔细斟酌是否要使用Lambda。和Lambda相似的还有JDK10中加入的var类型推断,同样对于这个特性需要斟酌使用。 diff --git a/docs/java/What's New in JDK8/README.md b/docs/java/What's New in JDK8/README.md deleted file mode 100644 index fa71e907410..00000000000 --- a/docs/java/What's New in JDK8/README.md +++ /dev/null @@ -1,556 +0,0 @@ -JDK8新特性总结 -====== -总结了部分JDK8新特性,另外一些新特性可以通过Oracle的官方文档查看,毕竟是官方文档,各种新特性都会介绍,有兴趣的可以去看。
-[Oracle官方文档:What's New in JDK8](https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html) ------ -- [Java语言特性](#JavaProgrammingLanguage) - - [Lambda表达式是一个新的语言特性,已经在JDK8中加入。它是一个可以传递的代码块,你也可以把它们当做方法参数。 - Lambda表达式允许您更紧凑地创建单虚方法接口(称为功能接口)的实例。](#LambdaExpressions) - - - [方法引用为已经存在的具名方法提供易于阅读的Lambda表达式](#MethodReferences) - - - [默认方法允许将新功能添加到库的接口,并确保与为这些接口的旧版本编写的代码的二进制兼容性。](#DefaultMethods) - - - [改进的类型推断。](#ImprovedTypeInference) - - - [方法参数反射(通过反射获得方法参数信息)](#MethodParameterReflection) - -- [流(stream)](#stream) - - [新java.util.stream包中的类提供Stream API以支持对元素流的功能样式操作。流(stream)和I/O里的流不是同一个概念 - ,使用stream API可以更方便的操作集合。]() - -- [国际化]() - - 待办 -- 待办 -___ - - - - - - - -##              Lambda表达式 -### 1.什么是Lambda表达式 -**Lambda表达式实质上是一个可传递的代码块,Lambda又称为闭包或者匿名函数,是函数式编程语法,让方法可以像普通参数一样传递** - -### 2.Lambda表达式语法 -```(参数列表) -> {执行代码块}``` -
参数列表可以为空```()->{}``` -
可以加类型声明比如```(String para1, int para2) -> {return para1 + para2;}```我们可以看到,lambda同样可以有返回值. -
在编译器可以推断出类型的时候,可以将类型声明省略,比如```(para1, para2) -> {return para1 + para2;}``` -
(lambda有点像动态类型语言语法。lambda在字节码层面是用invokedynamic实现的,而这条指令就是为了让JVM更好的支持运行在其上的动态类型语言) - -### 3.函数式接口 -在了解Lambda表达式之前,有必要先了解什么是函数式接口```(@FunctionalInterface)```
-**函数式接口指的是有且只有一个抽象(abstract)方法的接口**
-当需要一个函数式接口的对象时,就可以用Lambda表达式来实现,举个常用的例子: -
-```java - Thread thread = new Thread(() -> { - System.out.println("This is JDK8's Lambda!"); - }); -``` -这段代码和函数式接口有啥关系?我们回忆一下,Thread类的构造函数里是不是有一个以Runnable接口为参数的? -```java -public Thread(Runnable target) {...} - -/** - * Runnable Interface - */ -@FunctionalInterface -public interface Runnable { - public abstract void run(); -} -``` -到这里大家可能已经明白了,**Lambda表达式相当于一个匿名类或者说是一个匿名方法**。上面Thread的例子相当于 -```java - Thread thread = new Thread(new Runnable() { - @Override - public void run() { - System.out.println("Anonymous class"); - } - }); -``` -也就是说,上面的lambda表达式相当于实现了这个run()方法,然后当做参数传入(个人感觉可以这么理解,lambda表达式就是一个函数,只不过它的返回值、参数列表都 -由编译器帮我们推断,因此可以减少很多代码量)。 -
Lambda也可以这样用 : -```java - Runnable runnable = () -> {...}; -``` -其实这和上面的用法没有什么本质上的区别。 -
至此大家应该明白什么是函数式接口以及函数式接口和lambda表达式之间的关系了。在JDK8中修改了接口的规范, -目的是为了在给接口添加新的功能时保持向前兼容(个人理解),比如一个已经定义了的函数式接口,某天我们想给它添加新功能,那么就不能保持向前兼容了, -因为在旧的接口规范下,添加新功能必定会破坏这个函数式接口[(JDK8中接口规范)]() -
-除了上面说的Runnable接口之外,JDK中已经存在了很多函数式接口 -比如(当然不止这些): -- ```java.util.concurrent.Callable``` -- ```java.util.Comparator``` -- ```java.io.FileFilter``` -
**关于JDK中的预定义的函数式接口** - -- JDK在```java.util.function```下预定义了很多函数式接口 - - ```Function {R apply(T t);}``` 接受一个T对象,然后返回一个R对象,就像普通的函数。 - - ```Consumer {void accept(T t);}``` 消费者 接受一个T对象,没有返回值。 - - ```Predicate {boolean test(T t);}``` 判断,接受一个T对象,返回一个布尔值。 - - ```Supplier {T get();} 提供者(工厂)``` 返回一个T对象。 - - 其他的跟上面的相似,大家可以看一下function包下的具体接口。 -### 4.变量作用域 -```java -public class VaraibleHide { - @FunctionalInterface - interface IInner { - void printInt(int x); - } - public static void main(String[] args) { - int x = 20; - IInner inner = new IInner() { - int x = 10; - @Override - public void printInt(int x) { - System.out.println(x); - } - }; - inner.printInt(30); - - inner = (s) -> { - //Variable used in lambda expression should be final or effectively final - //!int x = 10; - //!x= 50; error - System.out.print(x); - }; - inner.printInt(30); - } -} -输出 : -30 -20 -``` -对于lambda表达式```java inner = (s) -> {System.out.print(x);};```,变量x并不是在lambda表达式中定义的,像这样并不是在lambda中定义或者通过lambda -的参数列表()获取的变量成为自由变量,它是被lambda表达式捕获的。 -
lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值,同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该不难理解。 - -### 5.方法引用 -**只需要提供方法的名字,具体的调用过程由Lambda和函数式接口来确定,这样的方法调用成为方法引用。** -
下面的例子会打印list中的每个元素: -```java -List list = new ArrayList<>(); - for (int i = 0; i < 10; ++i) { - list.add(i); - } - list.forEach(System.out::println); -``` -其中```System.out::println```这个就是一个方法引用,等价于Lambda表达式 ```(para)->{System.out.println(para);}``` -
我们看一下List#forEach方法 ```default void forEach(Consumer action)```可以看到它的参数是一个Consumer接口,该接口是一个函数式接口 -```java -@FunctionalInterface -public interface Consumer { - void accept(T t); -``` -大家能发现这个函数接口的方法和```System.out::println```有什么相似的么?没错,它们有着相似的参数列表和返回值。 -
我们自己定义一个方法,看看能不能像标准输出的打印函数一样被调用 -```java -public class MethodReference { - public static void main(String[] args) { - List list = new ArrayList<>(); - for (int i = 0; i < 10; ++i) { - list.add(i); - } - list.forEach(MethodReference::myPrint); - } - - static void myPrint(int i) { - System.out.print(i + ", "); - } -} - -输出: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -``` -可以看到,我们自己定义的方法也可以当做方法引用。 -
到这里大家多少对方法引用有了一定的了解,我们再来说一下方法引用的形式。 -- 方法引用 - - 类名::静态方法名 - - 类名::实例方法名 - - 类名::new (构造方法引用) - - 实例名::实例方法名 -可以看出,方法引用是通过(方法归属名)::(方法名)来调用的。通过上面的例子已经讲解了一个`类名::静态方法名`的使用方法了,下面再依次介绍其余的几种 -方法引用的使用方法。
-**类名::实例方法名**
-先来看一段代码 -```java - String[] strings = new String[10]; - Arrays.sort(strings, String::compareToIgnoreCase); -``` -**上面的String::compareToIgnoreCase等价于(x, y) -> {return x.compareToIgnoreCase(y);}**
-我们看一下`Arrays#sort`方法`public static void sort(T[] a, Comparator c)`, -可以看到第二个参数是一个Comparator接口,该接口也是一个函数式接口,其中的抽象方法是`int compare(T o1, T o2);`,再看一下 -`String#compareToIgnoreCase`方法,`public int compareToIgnoreCase(String str)`,这个方法好像和上面讲方法引用中`类名::静态方法名`不大一样啊,它 -的参数列表和函数式接口的参数列表不一样啊,虽然它的返回值一样? -
是的,确实不一样但是别忘了,String类的这个方法是个实例方法,而不是静态方法,也就是说,这个方法是需要有一个接收者的。所谓接收者就是 -instance.method(x)中的instance, -它是某个类的实例,有的朋友可能已经明白了。上面函数式接口的`compare(T o1, T o2)`中的第一个参数作为了实例方法的接收者,而第二个参数作为了实例方法的 -参数。我们再举一个自己实现的例子: -```java -public class MethodReference { - static Random random = new Random(47); - public static void main(String[] args) { - MethodReference[] methodReferences = new MethodReference[10]; - Arrays.sort(methodReferences, MethodReference::myCompare); - } - int myCompare(MethodReference o) { - return random.nextInt(2) - 1; - } -} -``` -上面的例子可以在IDE里通过编译,大家有兴趣的可以模仿上面的例子自己写一个程序,打印出排序后的结果。 -
**构造器引用**
-构造器引用仍然需要与特定的函数式接口配合使用,并不能像下面这样直接使用。IDE会提示String不是一个函数式接口 -```java - //compile error : String is not a functional interface - String str = String::new; -``` -下面是一个使用构造器引用的例子,可以看出构造器引用可以和这种工厂型的函数式接口一起使用的。 -```java - interface IFunctional { - T func(); -} - -public class ConstructorReference { - - public ConstructorReference() { - } - - public static void main(String[] args) { - Supplier supplier0 = () -> new ConstructorReference(); - Supplier supplier1 = ConstructorReference::new; - IFunctional functional = () -> new ConstructorReference(); - IFunctional functional1 = ConstructorReference::new; - } -} -``` -下面是一个JDK官方的例子 -```java - public static , DEST extends Collection> - DEST transferElements( - SOURCE sourceCollection, - Supplier collectionFactory) { - - DEST result = collectionFactory.get(); - for (T t : sourceCollection) { - result.add(t); - } - return result; - } - - ... - - Set rosterSet = transferElements( - roster, HashSet::new); -``` - -**实例::实例方法** -
-其实开始那个例子就是一个实例::实例方法的引用 -```java -List list = new ArrayList<>(); - for (int i = 0; i < 10; ++i) { - list.add(i); - } - list.forEach(System.out::println); -``` -其中System.out就是一个实例,println是一个实例方法。相信不用再给大家做解释了。 -### 总结 -Lambda表达式是JDK8引入Java的函数式编程语法,使用Lambda需要直接或者间接的与函数式接口配合,在开发中使用Lambda可以减少代码量, -但是并不是说必须要使用Lambda(虽然它是一个很酷的东西)。有些情况下使用Lambda会使代码的可读性急剧下降,并且也节省不了多少代码, -所以在实际开发中还是需要仔细斟酌是否要使用Lambda。和Lambda相似的还有JDK10中加入的var类型推断,同样对于这个特性需要斟酌使用。 - - -___ - - -##              JDK8接口规范 -### 在JDK8中引入了lambda表达式,出现了函数式接口的概念,为了在扩展接口时保持向前兼容性(JDK8之前扩展接口会使得实现了该接口的类必须实现添加的方法,否则会报错。为了保持兼容性而做出妥协的特性还有泛型,泛型也是为了保持兼容性而失去了在一些别的语言泛型拥有的功能),Java接口规范发生了一些改变。 -### 1.JDK8以前的接口规范 -- JDK8以前接口可以定义的变量和方法 - - 所有变量(Field)不论是否显式 的声明为```public static final```,它实际上都是```public static final```的。 - - 所有方法(Method)不论是否显示 的声明为```public abstract```,它实际上都是```public abstract```的。 -```java -public interface AInterfaceBeforeJDK8 { - int FIELD = 0; - void simpleMethod(); -} -``` -以上接口信息反编译以后可以看到字节码信息里Filed是public static final的,而方法是public abstract的,即是你没有显示的去声明它。 -```java -{ - public static final int FIELD; - descriptor: I - flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 0 - - public abstract void simpleMethod(); - descriptor: ()V - flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT -} -``` -### 2.JDK8之后的接口规范 -- JDK8之后接口可以定义的变量和方法 - - 变量(Field)仍然必须是 ```java public static final```的 - - 方法(Method)除了可以是public abstract之外,还可以是public static或者是default(相当于仅public修饰的实例方法)的。 -从以上改变不难看出,修改接口的规范主要是为了能在扩展接口时保持向前兼容。 -
下面是一个JDK8之后的接口例子 -```java -public interface AInterfaceInJDK8 { - int simpleFiled = 0; - static int staticField = 1; - - public static void main(String[] args) { - } - static void staticMethod(){} - - default void defaultMethod(){} - - void simpleMethod() throws IOException; - -} -``` -进行反编译(去除了一些没用信息) -```java -{ - public static final int simpleFiled; - flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL - - public static final int staticField; - flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL - - public static void main(java.lang.String[]); - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - - public static void staticMethod(); - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - - public void defaultMethod(); - flags: (0x0001) ACC_PUBLIC - - public abstract void simpleMethod() throws java.io.IOException; - flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT - Exceptions: - throws java.io.IOException -} -``` -可以看到 default关键字修饰的方法是像实例方法(就是普通类中定义的普通方法)一样定义的,所以我们来定义一个只有default方法的接口并且实现一下这个接口试一 -试。 -```java -interface Default { - default int defaultMethod() { - return 4396; - } -} - -public class DefaultMethod implements Default { - public static void main(String[] args) { - DefaultMethod defaultMethod = new DefaultMethod(); - System.out.println(defaultMethod.defaultMethod()); - //compile error : Non-static method 'defaultMethod()' cannot be referenced from a static context - //! DefaultMethod.defaultMethod(); - } -} -``` -可以看到default方法确实像实例方法一样,必须有实例对象才能调用,并且子类在实现接口时,可以不用实现default方法,也可以选择覆盖该方法。 -这有点像子类继承父类实例方法。 -
-接口静态方法就像是类静态方法,唯一的区别是**接口静态方法只能通过接口名调用,而类静态方法既可以通过类名调用也可以通过实例调用** -```java -interface Static { - static int staticMethod() { - return 4396; - } -} - ... main(String...args) - //!compile error: Static method may be invoked on containing interface class only - //!aInstanceOfStatic.staticMethod(); - ... -``` -另一个问题是多继承问题,大家知道Java中类是不支持多继承的,但是接口是多继承和多实现(implements后跟多个接口)的, -那么如果一个接口继承另一个接口,两个接口都有同名的default方法会怎么样呢?答案是会像类继承一样覆写(@Override),以下代码在IDE中可以顺利编译 -```java -interface Default { - default int defaultMethod() { - return 4396; - } -} -interface Default2 extends Default { - @Override - default int defaultMethod() { - return 9527; - } -} -public class DefaultMethod implements Default,Default2 { - public static void main(String[] args) { - DefaultMethod defaultMethod = new DefaultMethod(); - System.out.println(defaultMethod.defaultMethod()); - } -} - -输出 : 9527 -``` -出现上面的情况时,会优先找继承树上近的方法,类似于“短路优先”。 -
-那么如果一个类实现了两个没有继承关系的接口,且这两个接口有同名方法的话会怎么样呢?IDE会要求你重写这个冲突的方法,让你自己选择去执行哪个方法,因为IDE它还没智能到你不告诉它,它就知道你想执行哪个方法。可以通过```java 接口名.super```指针来访问接口中定义的实例(default)方法。 -```java -interface Default { - default int defaultMethod() { - return 4396; - } -} - -interface Default2 { - default int defaultMethod() { - return 9527; - } -} -//如果不重写 -//compile error : defaults.DefaultMethod inherits unrelated defaults for defaultMethod() from types defaults.Default and defaults.Default2 -public class DefaultMethod implements Default,Default2 { -@Override - public int defaultMethod() { - System.out.println(Default.super.defaultMethod()); - System.out.println(Default2.super.defaultMethod()); - return 996; - } - public static void main(String[] args) { - DefaultMethod defaultMethod = new DefaultMethod(); - System.out.println(defaultMethod.defaultMethod()); - } -} - -运行输出 : -4396 -9527 -996 -``` - - -___ - - -##              改进的类型推断 -### 1.什么是类型推断 -类型推断就像它的字面意思一样,编译器根据你显示声明的已知的信息 推断出你没有显示声明的类型,这就是类型推断。 -看过《Java编程思想 第四版》的朋友可能还记得里面讲解泛型一章的时候,里面很多例子是下面这样的: -```java - Map map = new Map(); -``` -而我们平常写的都是这样的: -```java - Map map = new Map<>(); -``` -这就是类型推断,《Java编程思想 第四版》这本书出书的时候最新的JDK只有1.6(JDK7推出的类型推断),在Java编程思想里Bruce Eckel大叔还提到过这个问题 -(可能JDK的官方人员看了Bruce Eckel大叔的Thinking in Java才加的类型推断,☺),在JDK7中推出了上面这样的类型推断,可以减少一些无用的代码。 -(Java编程思想到现在还只有第四版,是不是因为Bruce Eckel大叔觉得Java新推出的语言特性“然并卵”呢?/滑稽) -
-在JDK7中,类型推断只有上面例子的那样的能力,即只有在使用**赋值语句**时才能自动推断出泛型参数信息(即<>里的信息),下面的官方文档里的例子在JDK7里会编译 -错误 -```java - List stringList = new ArrayList<>(); - stringList.add("A"); - //error : addAll(java.util.Collection)in List cannot be applied to (java.util.List) - stringList.addAll(Arrays.asList()); -``` -但是上面的代码在JDK8里可以通过,也就说,JDK8里,类型推断不仅可以用于赋值语句,而且可以根据代码中上下文里的信息推断出更多的信息,因此我们需要些的代码 -会更少。加强的类型推断还有一个就是用于Lambda表达式了。 -
-大家其实不必细究类型推断,在日常使用中IDE会自动判断,当IDE自己无法推断出足够的信息时,就需要我们额外做一下工作,比如在<>里添加更多的类型信息, -相信随着Java的进化,这些便利的功能会越来越强大。 - - -____ - - -##              通过反射获得方法的参数信息 -JDK8之前 .class文件是不会存储方法参数信息的,因此也就无法通过反射获取该信息(想想反射获取类信息的入口是什么?当然就是Class类了)。即是是在JDK11里 -也不会默认生成这些信息,可以通过在javac加上-parameters参数来让javac生成这些信息(javac就是java编译器,可以把java文件编译成.class文件)。生成额外 -的信息(运行时非必须信息)会消耗内存并且有可能公布敏感信息(某些方法参数比如password,JDK文档里这么说的),并且确实很多信息javac并不会为我们生成,比如 -LocalVariableTable,javac就不会默认生成,需要你加上 -g:vars来强制让编译器生成,同样的,方法参数信息也需要加上 --parameters来让javac为你在.class文件中生成这些信息,否则运行时反射是无法获取到这些信息的。在讲解Java语言层面的方法之前,先看一下javac加上该 -参数和不加生成的信息有什么区别(不感兴趣想直接看运行代码的可以跳过这段)。下面是随便写的一个类。 -```java -public class ByteCodeParameters { - public String simpleMethod(String canUGetMyName, Object yesICan) { - return "9527"; - } -} -``` -先来不加参数编译和反编译一下这个类javac ByteCodeParameters.java , javap -v ByteCodeParameters: -```java - //只截取了部分信息 - public java.lang.String simpleMethod(java.lang.String, java.lang.Object); - descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=3, args_size=3 - 0: ldc #2 // String 9527 - 2: areturn - LineNumberTable: - line 5: 0 - //这个方法的描述到这里就结束了 -``` -接下来我们加上参数javac -parameters ByteCodeParameters.java 再来看反编译的信息: -```java - public java.lang.String simpleMethod(java.lang.String, java.lang.Object); - descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=3, args_size=3 - 0: ldc #2 // String 9527 - 2: areturn - LineNumberTable: - line 8: 0 - MethodParameters: - Name Flags - canUGetMyName - yesICan -``` -可以看到.class文件里多了一个MethodParameters信息,这就是参数的名字,可以看到默认是不保存的。 -
下面看一下在Intelj Idea里运行的这个例子,我们试一下通过反射获取方法名 : -```java -public class ByteCodeParameters { - public String simpleMethod(String canUGetMyName, Object yesICan) { - return "9527"; - } - - public static void main(String[] args) throws NoSuchMethodException { - Class clazz = ByteCodeParameters.class; - Method simple = clazz.getDeclaredMethod("simpleMethod", String.class, Object.class); - Parameter[] parameters = simple.getParameters(); - for (Parameter p : parameters) { - System.out.println(p.getName()); - } - } -} -输出 : -arg0 -arg1 -``` -???说好的方法名呢????别急,哈哈。前面说了,默认是不生成参数名信息的,因此我们需要做一些配置,我们找到IDEA的settings里的Java Compiler选项,在 -Additional command line parameters:一行加上-parameters(Eclipse 也是找到Java Compiler选中Stoer information about method parameters),或者自 -己编译一个.class文件放在IDEA的out下,然后再来运行 : -```java -输出 : -canUGetMyName -yesICan -``` -这样我们就通过反射获取到参数信息了。想要了解更多的同学可以自己研究一下 [官方文档] -(https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html) -
-## 总结与补充 -在JDK8之后,可以通过-parameters参数来让编译器生成参数信息然后在运行时通过反射获取方法参数信息,其实在SpringFramework -里面也有一个LocalVariableTableParameterNameDiscoverer对象可以获取方法参数名信息,有兴趣的同学可以自行百度(这个类在打印日志时可能会比较有用吧,个人感觉)。 - -____ - - - - -___ diff --git a/docs/java/What's New in JDK8/Stream.md b/docs/java/What's New in JDK8/Stream.md deleted file mode 100644 index de7c86e3f2e..00000000000 --- a/docs/java/What's New in JDK8/Stream.md +++ /dev/null @@ -1,75 +0,0 @@ -Stream API 旨在让编码更高效率、干净、简洁。 - -### 从迭代器到Stream操作 - -当使用 `Stream` 时,我们一般会通过三个阶段建立一个流水线: - -1. 创建一个 `Stream`; -2. 进行一个或多个中间操作; -3. 使用终止操作产生一个结果,`Stream` 就不会再被使用了。 - -**案例1:统计 List 中的单词长度大于6的个数** - -```java -/** -* 案例1:统计 List 中的单词长度大于6的个数 -*/ -ArrayList wordsList = new ArrayList(); -wordsList.add("Charles"); -wordsList.add("Vincent"); -wordsList.add("William"); -wordsList.add("Joseph"); -wordsList.add("Henry"); -wordsList.add("Bill"); -wordsList.add("Joan"); -wordsList.add("Linda"); -int count = 0; -``` -Java8之前我们通常用迭代方法来完成上面的需求: - -```java -//迭代(Java8之前的常用方法) -//迭代不好的地方:1. 代码多;2 很难被并行运算。 -for (String word : wordsList) { - if (word.length() > 6) { - count++; - } -} -System.out.println(count);//3 -``` -Java8之前我们使用 `Stream` 一行代码就能解决了,而且可以瞬间转换为并行执行的效果: - -```java -//Stream -//将stream()改为parallelStream()就可以瞬间将代码编程并行执行的效果 -long count2=wordsList.stream() - .filter(w->w.length()>6) - .count(); -long count3=wordsList.parallelStream() - .filter(w->w.length()>6) - .count(); -System.out.println(count2); -System.out.println(count3); -``` - -### `distinct()` - -去除 List 中重复的 String - -```java -List list = list.stream() - .distinct() - .collect(Collectors.toList()); -``` - -### `map` - -map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数: - -```java -List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); -// 获取 List 中每个元素对应的平方数并去重 -List squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList()); -System.out.println(squaresList.toString());//[9, 4, 49, 25] -``` - diff --git "a/docs/java/What's New in JDK8/\346\224\271\350\277\233\347\232\204\347\261\273\345\236\213\346\216\250\346\226\255.md" "b/docs/java/What's New in JDK8/\346\224\271\350\277\233\347\232\204\347\261\273\345\236\213\346\216\250\346\226\255.md" deleted file mode 100644 index b5cff7bb0c0..00000000000 --- "a/docs/java/What's New in JDK8/\346\224\271\350\277\233\347\232\204\347\261\273\345\236\213\346\216\250\346\226\255.md" +++ /dev/null @@ -1,30 +0,0 @@ -##              改进的类型推断 -### 1.什么是类型推断 -类型推断就像它的字面意思一样,编译器根据你显示声明的已知的信息 推断出你没有显示声明的类型,这就是类型推断。 -看过《Java编程思想 第四版》的朋友可能还记得里面讲解泛型一章的时候,里面很多例子是下面这样的: -```java - Map map = new Map(); -``` -而我们平常写的都是这样的: -```java - Map map = new Map<>(); -``` -这就是类型推断,《Java编程思想 第四版》这本书出书的时候最新的JDK只有1.6(JDK7推出的类型推断),在Java编程思想里Bruce Eckel大叔还提到过这个问题 -(可能JDK的官方人员看了Bruce Eckel大叔的Thinking in Java才加的类型推断,☺),在JDK7中推出了上面这样的类型推断,可以减少一些无用的代码。 -(Java编程思想到现在还只有第四版,是不是因为Bruce Eckel大叔觉得Java新推出的语言特性“然并卵”呢?/滑稽) -
-在JDK7中,类型推断只有上面例子的那样的能力,即只有在使用**赋值语句**时才能自动推断出泛型参数信息(即<>里的信息),下面的官方文档里的例子在JDK7里会编译 -错误 -```java - List stringList = new ArrayList<>(); - stringList.add("A"); - //error : addAll(java.util.Collection)in List cannot be applied to (java.util.List) - stringList.addAll(Arrays.asList()); -``` -但是上面的代码在JDK8里可以通过,也就说,JDK8里,类型推断不仅可以用于赋值语句,而且可以根据代码中上下文里的信息推断出更多的信息,因此我们需要些的代码 -会更少。加强的类型推断还有一个就是用于Lambda表达式了。 -
-大家其实不必细究类型推断,在日常使用中IDE会自动判断,当IDE自己无法推断出足够的信息时,就需要我们额外做一下工作,比如在<>里添加更多的类型信息, -相信随着Java的进化,这些便利的功能会越来越强大。 - - diff --git "a/docs/java/What's New in JDK8/\351\200\232\350\277\207\345\217\215\345\260\204\350\216\267\345\276\227\346\226\271\346\263\225\347\232\204\345\217\202\346\225\260\344\277\241\346\201\257.md" "b/docs/java/What's New in JDK8/\351\200\232\350\277\207\345\217\215\345\260\204\350\216\267\345\276\227\346\226\271\346\263\225\347\232\204\345\217\202\346\225\260\344\277\241\346\201\257.md" deleted file mode 100644 index a1d91c4b2fe..00000000000 --- "a/docs/java/What's New in JDK8/\351\200\232\350\277\207\345\217\215\345\260\204\350\216\267\345\276\227\346\226\271\346\263\225\347\232\204\345\217\202\346\225\260\344\277\241\346\201\257.md" +++ /dev/null @@ -1,79 +0,0 @@ -##              通过反射获得方法的参数信息 -JDK8之前 .class文件是不会存储方法参数信息的,因此也就无法通过反射获取该信息(想想反射获取类信息的入口是什么?当然就是Class类了)。即是是在JDK11里 -也不会默认生成这些信息,可以通过在javac加上-parameters参数来让javac生成这些信息(javac就是java编译器,可以把java文件编译成.class文件)。生成额外 -的信息(运行时非必须信息)会消耗内存并且有可能公布敏感信息(某些方法参数比如password,JDK文档里这么说的),并且确实很多信息javac并不会为我们生成,比如 -LocalVariableTable,javac就不会默认生成,需要你加上 -g:vars来强制让编译器生成,同样的,方法参数信息也需要加上 --parameters来让javac为你在.class文件中生成这些信息,否则运行时反射是无法获取到这些信息的。在讲解Java语言层面的方法之前,先看一下javac加上该 -参数和不加生成的信息有什么区别(不感兴趣想直接看运行代码的可以跳过这段)。下面是随便写的一个类。 -```java -public class ByteCodeParameters { - public String simpleMethod(String canUGetMyName, Object yesICan) { - return "9527"; - } -} -``` -先来不加参数编译和反编译一下这个类javac ByteCodeParameters.java , javap -v ByteCodeParameters: -```java - //只截取了部分信息 - public java.lang.String simpleMethod(java.lang.String, java.lang.Object); - descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=3, args_size=3 - 0: ldc #2 // String 9527 - 2: areturn - LineNumberTable: - line 5: 0 - //这个方法的描述到这里就结束了 -``` -接下来我们加上参数javac -parameters ByteCodeParameters.java 再来看反编译的信息: -```java - public java.lang.String simpleMethod(java.lang.String, java.lang.Object); - descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=3, args_size=3 - 0: ldc #2 // String 9527 - 2: areturn - LineNumberTable: - line 8: 0 - MethodParameters: - Name Flags - canUGetMyName - yesICan -``` -可以看到.class文件里多了一个MethodParameters信息,这就是参数的名字,可以看到默认是不保存的。 -
下面看一下在Intelj Idea里运行的这个例子,我们试一下通过反射获取方法名 : -```java -public class ByteCodeParameters { - public String simpleMethod(String canUGetMyName, Object yesICan) { - return "9527"; - } - - public static void main(String[] args) throws NoSuchMethodException { - Class clazz = ByteCodeParameters.class; - Method simple = clazz.getDeclaredMethod("simpleMethod", String.class, Object.class); - Parameter[] parameters = simple.getParameters(); - for (Parameter p : parameters) { - System.out.println(p.getName()); - } - } -} -输出 : -arg0 -arg1 -``` -???说好的方法名呢????别急,哈哈。前面说了,默认是不生成参数名信息的,因此我们需要做一些配置,我们找到IDEA的settings里的Java Compiler选项,在 -Additional command line parameters:一行加上-parameters(Eclipse 也是找到Java Compiler选中Stoer information about method parameters),或者自 -己编译一个.class文件放在IDEA的out下,然后再来运行 : -```java -输出 : -canUGetMyName -yesICan -``` -这样我们就通过反射获取到参数信息了。想要了解更多的同学可以自己研究一下 [官方文档] -(https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html) -
-## 总结与补充 -在JDK8之后,可以通过-parameters参数来让编译器生成参数信息然后在运行时通过反射获取方法参数信息,其实在SpringFramework -里面也有一个LocalVariableTableParameterNameDiscoverer对象可以获取方法参数名信息,有兴趣的同学可以自行百度(这个类在打印日志时可能会比较有用吧,个人感觉)。 diff --git a/docs/java/ArrayList-Grow.md b/docs/java/collection/ArrayList-Grow.md similarity index 99% rename from docs/java/ArrayList-Grow.md rename to docs/java/collection/ArrayList-Grow.md index 6dd4cc93daf..06fa5388d76 100644 --- a/docs/java/ArrayList-Grow.md +++ b/docs/java/collection/ArrayList-Grow.md @@ -270,7 +270,6 @@ public class ArrayscopyOfTest { 10 ``` - ### 3.3 两者联系和区别 **联系:** @@ -281,8 +280,6 @@ public class ArrayscopyOfTest { `arraycopy()` 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 `copyOf()` 是系统自动在内部新建一个数组,并返回该数组。 - - ## 四 `ensureCapacity`方法 ArrayList 源码中有一个 `ensureCapacity` 方法不知道大家注意到没有,这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的,那么这个方法有什么作用呢? @@ -341,7 +338,6 @@ public class EnsureCapacityTest { ``` 使用ensureCapacity方法前:4637 使用ensureCapacity方法后:241 - ``` 通过运行结果,我们可以很明显的看出向 ArrayList 添加大量元素之前最好先使用`ensureCapacity` 方法,以减少增量重新分配的次数 diff --git a/docs/java/ArrayList.md b/docs/java/collection/ArrayList.md similarity index 98% rename from docs/java/ArrayList.md rename to docs/java/collection/ArrayList.md index c3e8dd47896..f6578a7a784 100644 --- a/docs/java/ArrayList.md +++ b/docs/java/collection/ArrayList.md @@ -660,7 +660,7 @@ public class ArrayList extends AbstractList (3)private class SubList extends AbstractList implements RandomAccess (4)static final class ArrayListSpliterator implements Spliterator ``` -  ArrayList有四个内部类,其中的**Itr是实现了Iterator接口**,同时重写了里面的**hasNext()**,**next()**,**remove()**等方法;其中的**ListItr**继承**Itr**,实现了**ListIterator接口**,同时重写了**hasPrevious()**,**nextIndex()**,**previousIndex()**,**previous()**,**set(E e)**,**add(E e)**等方法,所以这也可以看出了 **Iterator和ListIterator的区别:**ListIterator在Iterator的基础上增加了添加对象,修改对象,逆向遍历等方法,这些是Iterator不能实现的。 +  ArrayList有四个内部类,其中的**Itr是实现了Iterator接口**,同时重写了里面的**hasNext()**, **next()**, **remove()** 等方法;其中的**ListItr** 继承 **Itr**,实现了**ListIterator接口**,同时重写了**hasPrevious()**, **nextIndex()**, **previousIndex()**, **previous()**, **set(E e)**, **add(E e)** 等方法,所以这也可以看出了 **Iterator和ListIterator的区别:** ListIterator在Iterator的基础上增加了添加对象,修改对象,逆向遍历等方法,这些是Iterator不能实现的。 ### ArrayList经典Demo ```java diff --git a/docs/java/HashMap.md b/docs/java/collection/HashMap.md similarity index 98% rename from docs/java/HashMap.md rename to docs/java/collection/HashMap.md index 716bb1f34a5..80cd8b571bc 100644 --- a/docs/java/HashMap.md +++ b/docs/java/collection/HashMap.md @@ -56,7 +56,7 @@ static int hash(int h) { 所谓 **“拉链法”** 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。 -![jdk1.8之前的内部结构](https://user-gold-cdn.xitu.io/2018/3/20/16240dbcc303d872?w=348&h=427&f=png&s=10991) +![jdk1.8之前的内部结构](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/jdk1.8之前的内部结构.png) ### JDK1.8之后 相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。 @@ -170,7 +170,9 @@ static final class TreeNode extends LinkedHashMap.Entry { ``` ## HashMap源码分析 ### 构造方法 -![四个构造方法](https://user-gold-cdn.xitu.io/2018/3/20/162410d912a2e0e1?w=336&h=90&f=jpeg&s=26744) + +HashMap 中有四个构造方法,它们分别如下: + ```java // 默认构造函数。 public HashMap() { @@ -237,9 +239,7 @@ HashMap只提供了put用于添加元素,putVal方法只是给put方法调用 - ①如果定位到的数组位置没有元素 就直接插入。 - ②如果定位到的数组位置有元素就和要插入的key比较,如果key相同就直接覆盖,如果key不相同,就判断p是否是一个树节点,如果是就调用`e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value)`将元素添加进入。如果不是就遍历链表插入(插入的是链表尾部)。 - - -![put方法](https://user-gold-cdn.xitu.io/2018/9/2/16598bf758c747e6?w=999&h=679&f=png&s=54486) +![put方法](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/put方法.png) ```java public V put(K key, V value) { diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" new file mode 100644 index 00000000000..db10324fcc3 --- /dev/null +++ "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" @@ -0,0 +1,456 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 + + + +- [剖析面试最常见问题之Java基础知识](#剖析面试最常见问题之java基础知识) + - [说说List,Set,Map三者的区别?](#说说listsetmap三者的区别) + - [Arraylist 与 LinkedList 区别?](#arraylist-与-linkedlist-区别) + - [补充内容:RandomAccess接口](#补充内容randomaccess接口) + - [补充内容:双向链表和双向循环链表](#补充内容双向链表和双向循环链表) + - [ArrayList 与 Vector 区别呢?为什么要用Arraylist取代Vector呢?](#arraylist-与-vector-区别呢为什么要用arraylist取代vector呢) + - [说一说 ArrayList 的扩容机制吧](#说一说-arraylist-的扩容机制吧) + - [HashMap 和 Hashtable 的区别](#hashmap-和-hashtable-的区别) + - [HashMap 和 HashSet区别](#hashmap-和-hashset区别) + - [HashSet如何检查重复](#hashset如何检查重复) + - [HashMap的底层实现](#hashmap的底层实现) + - [JDK1.8之前](#jdk18之前) + - [JDK1.8之后](#jdk18之后) + - [HashMap 的长度为什么是2的幂次方](#hashmap-的长度为什么是2的幂次方) + - [HashMap 多线程操作导致死循环问题](#hashmap-多线程操作导致死循环问题) + - [ConcurrentHashMap 和 Hashtable 的区别](#concurrenthashmap-和-hashtable-的区别) + - [ConcurrentHashMap线程安全的具体实现方式/底层具体实现](#concurrenthashmap线程安全的具体实现方式底层具体实现) + - [JDK1.7(上面有示意图)](#jdk17上面有示意图) + - [JDK1.8 (上面有示意图)](#jdk18-上面有示意图) + - [comparable 和 Comparator的区别](#comparable-和-comparator的区别) + - [Comparator定制排序](#comparator定制排序) + - [重写compareTo方法实现按年龄来排序](#重写compareto方法实现按年龄来排序) + - [集合框架底层数据结构总结](#集合框架底层数据结构总结) + - [Collection](#collection) + - [1. List](#1-list) + - [2. Set](#2-set) + - [Map](#map) + - [如何选用集合?](#如何选用集合) + + + +# 剖析面试最常见问题之Java基础知识 + +## 说说List,Set,Map三者的区别? + +- **List(对付顺序的好帮手):** List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象 +- **Set(注重独一无二的性质):** 不允许重复的集合。不会有多个元素引用相同的对象。 +- **Map(用Key来搜索的专家):** 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。 + +## Arraylist 与 LinkedList 区别? + +- **1. 是否保证线程安全:** `ArrayList` 和 `LinkedList` 都是不同步的,也就是不保证线程安全; + +- **2. 底层数据结构:** `Arraylist` 底层使用的是 **`Object` 数组**;`LinkedList` 底层使用的是 **双向链表** 数据结构(JDK1.6之前为循环链表,JDK1.7取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!) + +- **3. 插入和删除是否受元素位置的影响:** ① **`ArrayList` 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。** 比如:执行`add(E e) `方法的时候, `ArrayList` 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(`add(int index, E element) `)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② **`LinkedList` 采用链表存储,所以对于`add(E e)`方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置`i`插入和删除元素的话(`(add(int index, E element)`) 时间复杂度应为`o(n))`因为需要新创立一个新的链表,复制前i-1个元素并在第i位加入新的元素,最后附上n-i个元素。** + +- **4. 是否支持快速随机访问:** `LinkedList` 不支持高效的随机元素访问,而 `ArrayList` 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index) `方法)。 + +- **5. 内存空间占用:** ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。 + +### **补充内容:RandomAccess接口** + +```java +public interface RandomAccess { +} +``` + +查看源码我们发现实际上 `RandomAccess` 接口中什么都没有定义。所以,在我看来 `RandomAccess` 接口不过是一个标识罢了。标识什么? 标识实现这个接口的类具有随机访问功能。 + +在 `binarySearch(`)方法中,它要判断传入的list 是否 `RamdomAccess` 的实例,如果是,调用`indexedBinarySearch()`方法,如果不是,那么调用`iteratorBinarySearch()`方法 + +```java + public static + int binarySearch(List> list, T key) { + if (list instanceof RandomAccess || list.size() MAXIMUM_CAPACITY) + initialCapacity = MAXIMUM_CAPACITY; + if (loadFactor <= 0 || Float.isNaN(loadFactor)) + throw new IllegalArgumentException("Illegal load factor: " + + loadFactor); + this.loadFactor = loadFactor; + this.threshold = tableSizeFor(initialCapacity); + } + public HashMap(int initialCapacity) { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } +``` + +下面这个方法保证了 HashMap 总是使用2的幂作为哈希表的大小。 + +```java + /** + * Returns a power of two size for the given target capacity. + */ + 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; + } +``` + +## HashMap 和 HashSet区别 + +如果你看过 `HashSet` 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了 `clone() `、`writeObject()`、`readObject()`是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。 + +| HashMap | HashSet | +| :------------------------------: | :----------------------------------------------------------: | +| 实现了Map接口 | 实现Set接口 | +| 存储键值对 | 仅存储对象 | +| 调用 `put()`向map中添加元素 | 调用 `add()`方法向Set中添加元素 | +| HashMap使用键(Key)计算Hashcode | HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性, | + +## HashSet如何检查重复 + +当你把对象加入`HashSet`时,HashSet会先计算对象的`hashcode`值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用`equals()`方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。(摘自我的Java启蒙书《Head fist java》第二版) + +**hashCode()与equals()的相关规定:** + +1. 如果两个对象相等,则hashcode一定也是相同的 +2. 两个对象相等,对两个equals方法返回true +3. 两个对象有相同的hashcode值,它们也不一定是相等的 +4. 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖 +5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。 + +**==与equals的区别** + +1. ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同 +2. ==是指对内存地址进行比较 equals()是对字符串的内容进行比较 +3. ==指引用是否相同 equals()指的是值是否相同 + +## HashMap的底层实现 + +### JDK1.8之前 + +JDK1.8 之前 `HashMap` 底层是 **数组和链表** 结合在一起使用也就是 **链表散列**。**HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。** + +**所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。** + +**JDK 1.8 HashMap 的 hash 方法源码:** + +JDK 1.8 的 hash方法 相比于 JDK 1.7 hash 方法更加简化,但是原理不变。 + +```java + static final int hash(Object key) { + int h; + // key.hashCode():返回散列值也就是hashcode + // ^ :按位异或 + // >>>:无符号右移,忽略符号位,空位都以0补齐 + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); + } +``` + +对比一下 JDK1.7的 HashMap 的 hash 方法源码. + +```java +static int hash(int h) { + // This function ensures that hashCodes that differ only by + // constant multiples at each bit position have a bounded + // number of collisions (approximately 8 at default load factor). + + h ^= (h >>> 20) ^ (h >>> 12); + return h ^ (h >>> 7) ^ (h >>> 4); +} +``` + +相比于 JDK1.8 的 hash 方法 ,JDK 1.7 的 hash 方法的性能会稍差一点点,因为毕竟扰动了 4 次。 + +所谓 **“拉链法”** 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。 + +![jdk1.8之前的内部结构-HashMap](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/jdk1.8之前的内部结构-HashMap.jpg) + +### JDK1.8之后 + +相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。 + +![jdk1.8之后的内部结构-HashMap](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/JDK1.8之后的HashMap底层数据结构.jpg) + +> TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。 + +**推荐阅读:** + +- 《Java 8系列之重新认识HashMap》 : + +## HashMap 的长度为什么是2的幂次方 + +为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648到2147483647,前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ `(n - 1) & hash`”。(n代表数组长度)。这也就解释了 HashMap 的长度为什么是2的幂次方。 + +**这个算法应该如何设计呢?** + +我们首先可能会想到采用%取余的操作来实现。但是,重点来了:**“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。”** 并且 **采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。** + +## HashMap 多线程操作导致死循环问题 + +主要原因在于 并发下的Rehash 会造成元素之间会形成一个循环链表。不过,jdk 1.8 后解决了这个问题,但是还是不建议在多线程下使用 HashMap,因为多线程下使用 HashMap 还是会存在其他问题比如数据丢失。并发环境下推荐使用 ConcurrentHashMap 。 + +详情请查看: + +## ConcurrentHashMap 和 Hashtable 的区别 + +ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。 + +- **底层数据结构:** JDK1.7的 ConcurrentHashMap 底层采用 **分段的数组+链表** 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 **数组+链表** 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的; +- **实现线程安全的方式(重要):** ① **在JDK1.7的时候,ConcurrentHashMap(分段锁)** 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 **到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化)** 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② **Hashtable(同一把锁)** :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。 + +**两者的对比图:** + +图片来源: + +**HashTable:** + +![HashTable全表锁](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/HashTable全表锁.png) + +**JDK1.7的ConcurrentHashMap:** + +![JDK1.7的ConcurrentHashMap](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ConcurrentHashMap分段锁.jpg) + +**JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点):** + +![JDK1.8的ConcurrentHashMap](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/JDK1.8-ConcurrentHashMap-Structure.jpg) + +## ConcurrentHashMap线程安全的具体实现方式/底层具体实现 + +### JDK1.7(上面有示意图) + +首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。 + +**ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成**。 + +Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。 + +```java +static class Segment extends ReentrantLock implements Serializable { +} +``` + +一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。 + +### JDK1.8 (上面有示意图) + +ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。Java 8在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为O(N))转换为红黑树(寻址时间复杂度为O(log(N))) + +synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。 + +## comparable 和 Comparator的区别 + +- comparable接口实际上是出自java.lang包 它有一个 `compareTo(Object obj)`方法用来排序 +- comparator接口实际上是出自 java.util 包它有一个`compare(Object obj1, Object obj2)`方法用来排序 + +一般我们需要对一个集合使用自定义排序时,我们就要重写`compareTo()`方法或`compare()`方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写`compareTo()`方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的 `Collections.sort()`. + +### Comparator定制排序 + +```java + ArrayList arrayList = new ArrayList(); + arrayList.add(-1); + arrayList.add(3); + arrayList.add(3); + arrayList.add(-5); + arrayList.add(7); + arrayList.add(4); + arrayList.add(-9); + arrayList.add(-7); + System.out.println("原始数组:"); + System.out.println(arrayList); + // void reverse(List list):反转 + Collections.reverse(arrayList); + System.out.println("Collections.reverse(arrayList):"); + System.out.println(arrayList); + + // void sort(List list),按自然排序的升序排序 + Collections.sort(arrayList); + System.out.println("Collections.sort(arrayList):"); + System.out.println(arrayList); + // 定制排序的用法 + Collections.sort(arrayList, new Comparator() { + + @Override + public int compare(Integer o1, Integer o2) { + return o2.compareTo(o1); + } + }); + System.out.println("定制排序后:"); + System.out.println(arrayList); +``` + +Output: + +``` +原始数组: +[-1, 3, 3, -5, 7, 4, -9, -7] +Collections.reverse(arrayList): +[-7, -9, 4, 7, -5, 3, 3, -1] +Collections.sort(arrayList): +[-9, -7, -5, -1, 3, 3, 4, 7] +定制排序后: +[7, 4, 3, 3, -1, -5, -7, -9] +``` + +### 重写compareTo方法实现按年龄来排序 + +```java +// person对象没有实现Comparable接口,所以必须实现,这样才不会出错,才可以使treemap中的数据按顺序排列 +// 前面一个例子的String类已经默认实现了Comparable接口,详细可以查看String类的API文档,另外其他 +// 像Integer类等都已经实现了Comparable接口,所以不需要另外实现了 + +public class Person implements Comparable { + private String name; + private int age; + + public Person(String name, int age) { + super(); + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + /** + * TODO重写compareTo方法实现按年龄来排序 + */ + @Override + public int compareTo(Person o) { + // TODO Auto-generated method stub + if (this.age > o.getAge()) { + return 1; + } else if (this.age < o.getAge()) { + return -1; + } + return age; + } +} + +``` + +```java + public static void main(String[] args) { + TreeMap pdata = new TreeMap(); + pdata.put(new Person("张三", 30), "zhangsan"); + pdata.put(new Person("李四", 20), "lisi"); + pdata.put(new Person("王五", 10), "wangwu"); + pdata.put(new Person("小红", 5), "xiaohong"); + // 得到key的值的同时得到key所对应的值 + Set keys = pdata.keySet(); + for (Person key : keys) { + System.out.println(key.getAge() + "-" + key.getName()); + + } + } +``` + +Output: + +``` +5-小红 +10-王五 +20-李四 +30-张三 +``` + +## 集合框架底层数据结构总结 + +### Collection + +#### 1. List + +- **Arraylist:** Object数组 +- **Vector:** Object数组 +- **LinkedList:** 双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环) + +#### 2. Set + +- **HashSet(无序,唯一):** 基于 HashMap 实现的,底层采用 HashMap 来保存元素 +- **LinkedHashSet:** LinkedHashSet 继承于 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 HashMap 实现一样,不过还是有一点点区别的 +- **TreeSet(有序,唯一):** 红黑树(自平衡的排序二叉树) + +### Map + +- **HashMap:** JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间 +- **LinkedHashMap:** LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:[《LinkedHashMap 源码详细分析(JDK1.8)》](https://www.imooc.com/article/22931) +- **Hashtable:** 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的 +- **TreeMap:** 红黑树(自平衡的排序二叉树) + +## 如何选用集合? + +主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用Map接口下的集合,需要排序时选择TreeMap,不需要排序时就选择HashMap,需要保证线程安全就选用ConcurrentHashMap.当我们只需要存放元素值时,就选择实现Collection接口的集合,需要保证元素唯一时选择实现Set接口的集合比如TreeSet或HashSet,不需要就选择实现List接口的比如ArrayList或LinkedList,然后再根据实现这些接口的集合的特点来选用。 + +## 公众号 + +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 + +**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! + +**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 + +![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/java/LinkedList.md b/docs/java/collection/LinkedList.md similarity index 99% rename from docs/java/LinkedList.md rename to docs/java/collection/LinkedList.md index 983c1fae0d0..d26bc752267 100644 --- a/docs/java/LinkedList.md +++ b/docs/java/collection/LinkedList.md @@ -458,7 +458,7 @@ public class LinkedListDemo { linkedList.add(3); linkedList.removeFirstOccurrence(3); // 从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表) System.out.println("After removeFirstOccurrence(3):" + linkedList); - linkedList.removeLastOccurrence(3); // 从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表) + linkedList.removeLastOccurrence(3); // 从此列表中移除最后一次出现的指定元素(从尾部到头部遍历列表) System.out.println("After removeFirstOccurrence(3):" + linkedList); /************************** 遍历操作 ************************/ diff --git "a/docs/java/jvm/JDK\347\233\221\346\216\247\345\222\214\346\225\205\351\232\234\345\244\204\347\220\206\345\267\245\345\205\267\346\200\273\347\273\223.md" "b/docs/java/jvm/JDK\347\233\221\346\216\247\345\222\214\346\225\205\351\232\234\345\244\204\347\220\206\345\267\245\345\205\267\346\200\273\347\273\223.md" new file mode 100644 index 00000000000..ad0a6545287 --- /dev/null +++ "b/docs/java/jvm/JDK\347\233\221\346\216\247\345\222\214\346\225\205\351\232\234\345\244\204\347\220\206\345\267\245\345\205\267\346\200\273\347\273\223.md" @@ -0,0 +1,337 @@ +点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 + + + +- [JDK 监控和故障处理工具总结](#jdk-监控和故障处理工具总结) + - [JDK 命令行工具](#jdk-命令行工具) + - [`jps`:查看所有 Java 进程](#jps查看所有-java-进程) + - [`jstat`: 监视虚拟机各种运行状态信息](#jstat-监视虚拟机各种运行状态信息) + - [` jinfo`: 实时地查看和调整虚拟机各项参数](#-jinfo-实时地查看和调整虚拟机各项参数) + - [`jmap`:生成堆转储快照](#jmap生成堆转储快照) + - [**`jhat`**: 分析 heapdump 文件](#jhat-分析-heapdump-文件) + - [**`jstack`** :生成虚拟机当前时刻的线程快照](#jstack-生成虚拟机当前时刻的线程快照) + - [JDK 可视化分析工具](#jdk-可视化分析工具) + - [JConsole:Java 监视与管理控制台](#jconsolejava-监视与管理控制台) + - [连接 Jconsole](#连接-jconsole) + - [查看 Java 程序概况](#查看-java-程序概况) + - [内存监控](#内存监控) + - [线程监控](#线程监控) + - [Visual VM:多合一故障处理工具](#visual-vm多合一故障处理工具) + + + +# JDK 监控和故障处理工具总结 + +## JDK 命令行工具 + +这些命令在 JDK 安装目录下的 bin 目录下: + +- **`jps`** (JVM Process Status): 类似 UNIX 的 `ps` 命令。用户查看所有 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息; +- **`jstat`**( JVM Statistics Monitoring Tool): 用于收集 HotSpot 虚拟机各方面的运行数据; +- **`jinfo`** (Configuration Info for Java) : Configuration Info forJava,显示虚拟机配置信息; +- **`jmap`** (Memory Map for Java) :生成堆转储快照; +- **`jhat`** (JVM Heap Dump Browser ) : 用于分析 heapdump 文件,它会建立一个 HTTP/HTML 服务器,让用户可以在浏览器上查看分析结果; +- **`jstack`** (Stack Trace for Java):生成虚拟机当前时刻的线程快照,线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合。 + +### `jps`:查看所有 Java 进程 + +`jps`(JVM Process Status) 命令类似 UNIX 的 `ps` 命令。 + +`jps`:显示虚拟机执行主类名称以及这些进程的本地虚拟机唯一 ID(Local Virtual Machine Identifier,LVMID)。`jps -q` :只输出进程的本地虚拟机唯一 ID。 + +```powershell +C:\Users\SnailClimb>jps +7360 NettyClient2 +17396 +7972 Launcher +16504 Jps +17340 NettyServer +``` + +`jps -l`:输出主类的全名,如果进程执行的是 Jar 包,输出 Jar 路径。 + +```powershell +C:\Users\SnailClimb>jps -l +7360 firstNettyDemo.NettyClient2 +17396 +7972 org.jetbrains.jps.cmdline.Launcher +16492 sun.tools.jps.Jps +17340 firstNettyDemo.NettyServer +``` + +`jps -v`:输出虚拟机进程启动时 JVM 参数。 + +`jps -m`:输出传递给 Java 进程 main() 函数的参数。 + +### `jstat`: 监视虚拟机各种运行状态信息 + +jstat(JVM Statistics Monitoring Tool) 使用于监视虚拟机各种运行状态信息的命令行工具。 它可以显示本地或者远程(需要远程主机提供 RMI 支持)虚拟机进程中的类信息、内存、垃圾收集、JIT 编译等运行数据,在没有 GUI,只提供了纯文本控制台环境的服务器上,它将是运行期间定位虚拟机性能问题的首选工具。 + +**`jstat` 命令使用格式:** + +```powershell +jstat -