From 6d318f3aaf7ec53ab1d627f6b545a1246f3d3845 Mon Sep 17 00:00:00 2001 From: JustALee Date: Thu, 1 Oct 2020 17:49:25 +0800 Subject: [PATCH 01/90] Fix typo --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index b382ce2..83e1fb1 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -24,7 +24,7 @@ - [容器](#容器) - [设计模式](#设计模式) - [JavaWeb](#JavaWeb) - - [Srping](#Spring) + - [Spring](#Spring) - [SpringMVC](#SpringMVC) - [SpringBoot](#SpringBoot) - [Java进阶](#Java进阶) From d7eaa6d90b1bfab52fd2e8a9815b60b4096e51f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=BB=84=E5=B0=8F=E6=96=9C?= <362294931@qq.com> Date: Fri, 14 May 2021 15:57:33 +0800 Subject: [PATCH 02/90] Update ReadMe.md --- ReadMe.md | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index b382ce2..f9a91fb 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -349,6 +349,12 @@ springboot和springcloud ## 微信公众号 +### 黄小斜学Java + +微信公众号【黄小斜学Java】 作者是蚂蚁金服Java工程师,专注分享Java领域干货,不限于BAT面试,算法、计算机基础、数据库、分布式、spring全家桶、微服务、高并发、JVM、Docker容器,ELK、大数据等相关知识,希望我们可以一起进步。 + +**500页Java面试手册PDF:** 关注公众号后回复 **”PDF“** 即可领取超级热门的Java面试宝典pdf + ### Java技术江湖 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号【Java技术江湖】一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发! @@ -358,17 +364,3 @@ springboot和springcloud **Java进阶架构师资料:** 关注公众号后回复 **”架构师“** 即可领取 Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南、Java程序员面试指南等干货资源 ![我的公众号](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众号:程序员黄小斜 -​ -黄小斜是 985 硕士,阿里巴巴Java工程师,在自学编程、技术求职、Java学习等方面有丰富经验和独到见解,希望帮助到更多想要从事互联网行业的程序员们。 -​ -作者专注于 JAVA 后端技术栈,热衷于分享程序员干货、学习经验、求职心得,以及自学编程和Java技术栈的相关干货。 -​ -黄小斜是一个斜杠青年,坚持学习和写作,相信终身学习的力量,希望和更多的程序员交朋友,一起进步和成长! - -**原创电子书:** 关注微信公众号【程序员黄小斜】后回复 **"原创电子书"** 即可领取我原创的电子书《菜鸟程序员修炼手册:从技术小白到阿里巴巴Java工程师》这份电子书总结了我2年的Java学习之路,包括学习方法、技术总结、求职经验和面试技巧等内容,已经帮助很多的程序员拿到了心仪的offer! - -**程序员3T技术学习资源:** 一些程序员学习技术的资源大礼包,关注公众号【程序员黄小斜】后,后台回复关键字 **“资料”** 即可免费无套路获取,包括Java、python、C++、大数据、机器学习、前端、移动端等方向的技术资料。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) From 04e727568631b38bb83c73a93878a718d1f4987e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=BB=84=E5=B0=8F=E6=96=9C?= <362294931@qq.com> Date: Thu, 9 Dec 2021 15:01:09 +0800 Subject: [PATCH 03/90] Update ReadMe.md --- ReadMe.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index f9a91fb..5c79614 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -349,9 +349,9 @@ springboot和springcloud ## 微信公众号 -### 黄小斜学Java +### 程序员黄小斜 -微信公众号【黄小斜学Java】 作者是蚂蚁金服Java工程师,专注分享Java领域干货,不限于BAT面试,算法、计算机基础、数据库、分布式、spring全家桶、微服务、高并发、JVM、Docker容器,ELK、大数据等相关知识,希望我们可以一起进步。 +微信公众号【程序员黄小斜】作者是前蚂蚁金服Java工程师,专注分享Java技术干货和求职成长心得,不限于BAT面试,算法、计算机基础、数据库、分布式、spring全家桶、微服务、高并发、JVM、Docker容器,ELK、大数据等 **500页Java面试手册PDF:** 关注公众号后回复 **”PDF“** 即可领取超级热门的Java面试宝典pdf From b23bd5cd9b2eda81800a9a2ffaea7f57d2dcede8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=BB=84=E5=B0=8F=E6=96=9C?= <362294931@qq.com> Date: Thu, 9 Dec 2021 15:01:39 +0800 Subject: [PATCH 04/90] Create ReadMe.md From f95f5cf2edb343d583cbe41f1bb7b9f0a8350a76 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Sun, 26 Mar 2023 17:02:46 +0800 Subject: [PATCH 05/90] do nothing --- src/main/java/md/AtxMarkdownTocFileTest.java | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/main/java/md/AtxMarkdownTocFileTest.java diff --git a/src/main/java/md/AtxMarkdownTocFileTest.java b/src/main/java/md/AtxMarkdownTocFileTest.java deleted file mode 100644 index 531617d..0000000 --- a/src/main/java/md/AtxMarkdownTocFileTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package md; - -public class AtxMarkdownTocFileTest { -} From 8b486aeac24d32e8c1408e6d8146d9e0e4e00f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=B0=8F=E6=96=9C?= <362294931@qq.com> Date: Sun, 26 Mar 2023 17:09:13 +0800 Subject: [PATCH 06/90] Update ReadMe.md --- ReadMe.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 5c79614..7df3d6d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -349,15 +349,10 @@ springboot和springcloud ## 微信公众号 -### 程序员黄小斜 - -微信公众号【程序员黄小斜】作者是前蚂蚁金服Java工程师,专注分享Java技术干货和求职成长心得,不限于BAT面试,算法、计算机基础、数据库、分布式、spring全家桶、微服务、高并发、JVM、Docker容器,ELK、大数据等 - -**500页Java面试手册PDF:** 关注公众号后回复 **”PDF“** 即可领取超级热门的Java面试宝典pdf ### Java技术江湖 -如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号【Java技术江湖】一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发! +如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号【Java技术江湖】前阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发! **Java工程师技术学习资料:** 一些Java工程师常用学习资源,关注公众号后,后台回复关键字 **“Java”** 即可免费无套路获取。 From 6e86a2cdca52b19246e8eccbd312e49e9b7d2f4a Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Fri, 31 Mar 2023 19:38:30 +0800 Subject: [PATCH 07/90] resolve distributed docs category problem --- ReadMe.md | 66 +++++++++---------- ...\200\247\343\200\2012PC\345\222\2143PC.md" | 0 ...5\237\272\347\241\2002 \357\274\232CAP.md" | 0 ...13\344\273\266\351\241\272\345\272\217.md" | 0 ...\237\272\347\241\2004\357\274\232Paxos.md" | 0 ...76\345\222\214\347\247\237\347\272\246.md" | 0 ...41\2006\357\274\232Raft\343\200\201Zab.md" | 0 ...17\350\260\203\346\234\215\345\212\241.md" | 0 ...15\345\222\214\344\274\230\345\214\226.md" | 0 ...06\344\270\216\345\256\236\350\267\265.md" | 0 ...44\270\200\350\207\264\346\200\247hash.md" | 0 ...37\346\210\220\346\226\271\346\241\210.md" | 0 ...04\351\202\243\344\272\233\344\272\213.md" | 0 ...6\210\220Redis\347\274\223\345\255\230.md" | 0 ...60\347\232\204\345\245\227\350\267\257.md" | 0 ...40\347\247\215\346\226\271\346\241\210.md" | 0 ...03\345\274\217\344\272\213\345\212\241.md" | 0 ...43\345\206\263\346\226\271\346\241\210.md" | 0 ...03\345\274\217\344\272\213\345\212\241.md" | 0 ...72\346\234\254\346\246\202\345\277\265.md" | 0 ...40\344\275\225\350\200\214\347\224\237.md" | 0 ...\201\257\346\212\200\346\234\257 Kafka.md" | 0 ...57\274\214Raft\347\256\227\346\263\225.md" | 0 ...0\203\346\234\215\345\212\241zookeeper.md" | 0 ...01\347\250\213\350\257\246\350\247\243.md" | 0 ...41\347\220\206\345\256\236\346\210\230.md" | 0 ...57\345\217\212\345\256\236\350\267\265.md" | 0 ...36\350\267\265\346\200\273\347\273\223.md" | 0 ...06\350\256\272\346\200\273\347\273\223.md" | 0 29 files changed, 33 insertions(+), 33 deletions(-) rename "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2001\357\274\232 \344\270\200\350\207\264\346\200\247\343\200\2012PC\345\222\2143PC.md" => "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2001\357\274\232 \344\270\200\350\207\264\346\200\247\343\200\2012PC\345\222\2143PC.md" (100%) rename "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2002 \357\274\232CAP.md" => "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2002 \357\274\232CAP.md" (100%) rename "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2003\357\274\232 \346\227\266\351\227\264\343\200\201\346\227\266\351\222\237\345\222\214\344\272\213\344\273\266\351\241\272\345\272\217.md" => "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2003\357\274\232 \346\227\266\351\227\264\343\200\201\346\227\266\351\222\237\345\222\214\344\272\213\344\273\266\351\241\272\345\272\217.md" (100%) rename "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2004\357\274\232Paxos.md" => "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2004\357\274\232Paxos.md" (100%) rename "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2005\357\274\232\351\200\211\344\270\276\343\200\201\345\244\232\346\225\260\346\264\276\345\222\214\347\247\237\347\272\246.md" => "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2005\357\274\232\351\200\211\344\270\276\343\200\201\345\244\232\346\225\260\346\264\276\345\222\214\347\247\237\347\272\246.md" (100%) rename "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2006\357\274\232Raft\343\200\201Zab.md" => "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2006\357\274\232Raft\343\200\201Zab.md" (100%) rename "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2008\357\274\232zookeeper\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241.md" => "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2008\357\274\232zookeeper\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241.md" (100%) rename "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\350\277\233\351\230\2667\357\274\232Paxos\345\217\230\347\247\215\345\222\214\344\274\230\345\214\226.md" => "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\350\277\233\351\230\2667\357\274\232Paxos\345\217\230\347\247\215\345\222\214\344\274\230\345\214\226.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25710\357\274\232LVS\345\256\236\347\216\260\350\264\237\350\275\275\345\235\207\350\241\241\347\232\204\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25710\357\274\232LVS\345\256\236\347\216\260\350\264\237\350\275\275\345\235\207\350\241\241\347\232\204\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25711\357\274\232\345\210\206\345\270\203\345\274\217session\350\247\243\345\206\263\346\226\271\346\241\210\344\270\216\344\270\200\350\207\264\346\200\247hash.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25711\357\274\232\345\210\206\345\270\203\345\274\217session\350\247\243\345\206\263\346\226\271\346\241\210\344\270\216\344\270\200\350\207\264\346\200\247hash.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25712\357\274\232\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25712\357\274\232\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25713\357\274\232\347\274\223\345\255\230\347\232\204\351\202\243\344\272\233\344\272\213.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25713\357\274\232\347\274\223\345\255\230\347\232\204\351\202\243\344\272\233\344\272\213.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232Spring Boot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232Spring Boot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25715\357\274\232\347\274\223\345\255\230\346\233\264\346\226\260\347\232\204\345\245\227\350\267\257.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25715\357\274\232\347\274\223\345\255\230\346\233\264\346\226\260\347\232\204\345\245\227\350\267\257.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25716\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\351\224\201\347\232\204\345\207\240\347\247\215\346\226\271\346\241\210.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25716\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\351\224\201\347\232\204\345\207\240\347\247\215\346\226\271\346\241\210.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25717\357\274\232\346\265\205\346\236\220\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25717\357\274\232\346\265\205\346\236\220\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25718\357\274\232\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\270\270\347\224\250\350\247\243\345\206\263\346\226\271\346\241\210.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25718\357\274\232\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\270\270\347\224\250\350\247\243\345\206\263\346\226\271\346\241\210.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25719\357\274\232\344\275\277\347\224\250RocketMQ\344\272\213\345\212\241\346\266\210\346\201\257\350\247\243\345\206\263\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25719\357\274\232\344\275\277\347\224\250RocketMQ\344\272\213\345\212\241\346\266\210\346\201\257\350\247\243\345\206\263\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2571\357\274\232\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\232\204\344\270\200\344\272\233\345\237\272\346\234\254\346\246\202\345\277\265.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2571\357\274\232\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\232\204\344\270\200\344\272\233\345\237\272\346\234\254\346\246\202\345\277\265.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25720\357\274\232\346\266\210\346\201\257\351\230\237\345\210\227\345\233\240\344\275\225\350\200\214\347\224\237.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25720\357\274\232\346\266\210\346\201\257\351\230\237\345\210\227\345\233\240\344\275\225\350\200\214\347\224\237.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257 Kafka.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257 Kafka.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2572\357\274\232\345\210\206\345\270\203\345\274\217\344\270\200\350\207\264\346\200\247\345\215\217\350\256\256\344\270\216Paxos\357\274\214Raft\347\256\227\346\263\225.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2572\357\274\232\345\210\206\345\270\203\345\274\217\344\270\200\350\207\264\346\200\247\345\215\217\350\256\256\344\270\216Paxos\357\274\214Raft\347\256\227\346\263\225.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2573\357\274\232\345\210\235\346\216\242\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241zookeeper.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2573\357\274\232\345\210\235\346\216\242\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241zookeeper.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2574\357\274\232ZAB\345\215\217\350\256\256\346\246\202\350\277\260\344\270\216\351\200\211\344\270\273\346\265\201\347\250\213\350\257\246\350\247\243.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2574\357\274\232ZAB\345\215\217\350\256\256\346\246\202\350\277\260\344\270\216\351\200\211\344\270\273\346\265\201\347\250\213\350\257\246\350\247\243.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2575\357\274\232Zookeeper\347\232\204\351\205\215\347\275\256\344\270\216\351\233\206\347\276\244\347\256\241\347\220\206\345\256\236\346\210\230.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2575\357\274\232Zookeeper\347\232\204\351\205\215\347\275\256\344\270\216\351\233\206\347\276\244\347\256\241\347\220\206\345\256\236\346\210\230.md" (100%) rename "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2576\357\274\232Zookeeper\345\205\270\345\236\213\345\272\224\347\224\250\345\234\272\346\231\257\345\217\212\345\256\236\350\267\265.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2576\357\274\232Zookeeper\345\205\270\345\236\213\345\272\224\347\224\250\345\234\272\346\231\257\345\217\212\345\256\236\350\267\265.md" (100%) rename "docs/distrubuted/\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\345\256\236\350\267\265\346\200\273\347\273\223.md" => "docs/distributed/\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\345\256\236\350\267\265\346\200\273\347\273\223.md" (100%) rename "docs/distrubuted/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272\346\200\273\347\273\223.md" => "docs/distributed/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272\346\200\273\347\273\223.md" (100%) diff --git a/ReadMe.md b/ReadMe.md index 5c79614..0e7377f 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -292,40 +292,40 @@ todo ## 分布式 ### 理论 -* [分布式系统理论基础开篇:从放弃到入门](docs/distrubuted/理论/分布式系统理论基础开篇:从放弃到入门.md) -* [分布式系统理论基础1: 一致性、2PC和3PC ](docs/distrubuted/理论/分布式系统理论基础1:%20一致性、2PC和3PC.md) -* [分布式系统理论基础2:CAP ](docs/distrubuted/理论/分布式系统理论基础2%20:CAP.md) -* [分布式系统理论基础3: 时间、时钟和事件顺序](docs/distrubuted/理论/分布式系统理论基础3:%20时间、时钟和事件顺序.md) -* [分布式系统理论基础4:Paxos](docs/distrubuted/理论/分布式系统理论基础4:Paxos.md) -* [分布式系统理论基础5:选举、多数派和租约](docs/distrubuted/理论/分布式系统理论基础5:选举、多数派和租约.md) -* [分布式系统理论基础6:Raft、Zab ](docs/distrubuted/理论/分布式系统理论基础6:Raft、Zab.md) -* [分布式系统理论进阶7:Paxos变种和优化 ](docs/distrubuted/理论/分布式系统理论进阶7:Paxos变种和优化.md) -* [分布式系统理论基础8:zookeeper分布式协调服务 ](docs/distrubuted/理论/分布式系统理论基础8:zookeeper分布式协调服务.md) - +* [分布式系统理论基础1: 一致性、2PC和3PC ](docs/distributed/basic/分布式系统理论基础1:%20一致性、2PC和3PC.md) +* [分布式系统理论基础2:CAP ](docs/distributed/basic/分布式系统理论基础2%20:CAP.md) +* [分布式系统理论基础3: 时间、时钟和事件顺序](docs/distributed/basic/分布式系统理论基础3:%20时间、时钟和事件顺序.md) +* [分布式系统理论基础4:Paxos](docs/distributed/basic/分布式系统理论基础4:Paxos.md) +* [分布式系统理论基础5:选举、多数派和租约](docs/distributed/basic/分布式系统理论基础5:选举、多数派和租约.md) +* [分布式系统理论基础6:Raft、Zab ](docs/distributed/basic/分布式系统理论基础6:Raft、Zab.md) +* [分布式系统理论进阶7:Paxos变种和优化 ](docs/distributed/basic/分布式系统理论进阶7:Paxos变种和优化.md) +* [分布式系统理论基础8:zookeeper分布式协调服务 ](docs/distributed/basic/分布式系统理论基础8:zookeeper分布式协调服务.md) + +* [分布式技术实践总结](docs/distributed/分布式理论总结.md) ### 技术 -* [搞懂分布式技术1:分布式系统的一些基本概念 ](docs/distrubuted/实战/搞懂分布式技术1:分布式系统的一些基本概念.md ) -* [搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法](docs/distrubuted/实战/搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法.md) -* [搞懂分布式技术3:初探分布式协调服务zookeeper ](docs/distrubuted/实战/搞懂分布式技术3:初探分布式协调服务zookeeper.md ) -* [搞懂分布式技术4:ZAB协议概述与选主流程详解 ](docs/distrubuted/实战/搞懂分布式技术4:ZAB协议概述与选主流程详解.md ) -* [搞懂分布式技术5:Zookeeper的配置与集群管理实战](docs/distrubuted/实战/搞懂分布式技术5:Zookeeper的配置与集群管理实战.md) -* [搞懂分布式技术6:Zookeeper典型应用场景及实践 ](docs/distrubuted/实战/搞懂分布式技术6:Zookeeper典型应用场景及实践.md ) -* [搞懂分布式技术7:负载均衡概念与主流方案](docs/distrubuted/实战/搞懂分布式技术7:负载均衡概念与主流方案.md) -* [搞懂分布式技术8:负载均衡原理剖析 ](docs/distrubuted/实战/搞懂分布式技术8:负载均衡原理剖析.md ) -* [搞懂分布式技术9:Nginx负载均衡原理与实践 ](docs/distrubuted/实战/搞懂分布式技术9:Nginx负载均衡原理与实践.md) -* [搞懂分布式技术10:LVS实现负载均衡的原理与实践 ](docs/distrubuted/实战/搞懂分布式技术10:LVS实现负载均衡的原理与实践.md ) -* [搞懂分布式技术11:分布式session解决方案与一致性hash](docs/distrubuted/实战/搞懂分布式技术11:分布式session解决方案与一致性hash.md) -* [搞懂分布式技术12:分布式ID生成方案 ](docs/distrubuted/实战/搞懂分布式技术12:分布式ID生成方案.md ) -* [搞懂分布式技术13:缓存的那些事](docs/distrubuted/实战/搞懂分布式技术13:缓存的那些事.md) -* [搞懂分布式技术14:Spring Boot使用注解集成Redis缓存](docs/distrubuted/实战/搞懂分布式技术14:Spring%20Boot使用注解集成Redis缓存.md) -* [搞懂分布式技术15:缓存更新的套路 ](docs/distrubuted/实战/搞懂分布式技术15:缓存更新的套路.md ) -* [搞懂分布式技术16:浅谈分布式锁的几种方案 ](docs/distrubuted/实战/搞懂分布式技术16:浅谈分布式锁的几种方案.md ) -* [搞懂分布式技术17:浅析分布式事务 ](docs/distrubuted/实战/搞懂分布式技术17:浅析分布式事务.md ) -* [搞懂分布式技术18:分布式事务常用解决方案 ](docs/distrubuted/实战/搞懂分布式技术18:分布式事务常用解决方案.md ) -* [搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务 ](docs/distrubuted/实战/搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务.md ) -* [搞懂分布式技术20:消息队列因何而生](docs/distrubuted/实战/搞懂分布式技术20:消息队列因何而生.md) -* [搞懂分布式技术21:浅谈分布式消息技术 Kafka ](docs/distrubuted/实战/搞懂分布式技术21:浅谈分布式消息技术%20Kafka.md ) - - +* [搞懂分布式技术1:分布式系统的一些基本概念 ](docs/distributed/practice/搞懂分布式技术1:分布式系统的一些基本概念.md ) +* [搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法](docs/distributed/practice/搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法.md) +* [搞懂分布式技术3:初探分布式协调服务zookeeper ](docs/distributed/practice/搞懂分布式技术3:初探分布式协调服务zookeeper.md ) +* [搞懂分布式技术4:ZAB协议概述与选主流程详解 ](docs/distributed/practice/搞懂分布式技术4:ZAB协议概述与选主流程详解.md ) +* [搞懂分布式技术5:Zookeeper的配置与集群管理实战](docs/distributed/practice/搞懂分布式技术5:Zookeeper的配置与集群管理实战.md) +* [搞懂分布式技术6:Zookeeper典型应用场景及实践 ](docs/distributed/practice/搞懂分布式技术6:Zookeeper典型应用场景及实践.md ) +* [搞懂分布式技术7:负载均衡概念与主流方案](docs/distributed/practice/搞懂分布式技术7:负载均衡概念与主流方案.md) +* [搞懂分布式技术8:负载均衡原理剖析 ](docs/distributed/practice/搞懂分布式技术8:负载均衡原理剖析.md ) +* [搞懂分布式技术9:Nginx负载均衡原理与实践 ](docs/distributed/practice/搞懂分布式技术9:Nginx负载均衡原理与实践.md) +* [搞懂分布式技术10:LVS实现负载均衡的原理与实践 ](docs/distributed/practice/搞懂分布式技术10:LVS实现负载均衡的原理与实践.md ) +* [搞懂分布式技术11:分布式session解决方案与一致性hash](docs/distributed/practice/搞懂分布式技术11:分布式session解决方案与一致性hash.md) +* [搞懂分布式技术12:分布式ID生成方案 ](docs/distributed/practice/搞懂分布式技术12:分布式ID生成方案.md ) +* [搞懂分布式技术13:缓存的那些事](docs/distributed/practice/搞懂分布式技术13:缓存的那些事.md) +* [搞懂分布式技术14:Spring Boot使用注解集成Redis缓存](docs/distributed/practice/搞懂分布式技术14:Spring%20Boot使用注解集成Redis缓存.md) +* [搞懂分布式技术15:缓存更新的套路 ](docs/distributed/practice/搞懂分布式技术15:缓存更新的套路.md ) +* [搞懂分布式技术16:浅谈分布式锁的几种方案 ](docs/distributed/practice/搞懂分布式技术16:浅谈分布式锁的几种方案.md ) +* [搞懂分布式技术17:浅析分布式事务 ](docs/distributed/practice/搞懂分布式技术17:浅析分布式事务.md ) +* [搞懂分布式技术18:分布式事务常用解决方案 ](docs/distributed/practice/搞懂分布式技术18:分布式事务常用解决方案.md ) +* [搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务 ](docs/distributed/practice/搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务.md ) +* [搞懂分布式技术20:消息队列因何而生](docs/distributed/practice/搞懂分布式技术20:消息队列因何而生.md) +* [搞懂分布式技术21:浅谈分布式消息技术 Kafka ](docs/distributed/practice/搞懂分布式技术21:浅谈分布式消息技术%20Kafka.md ) + +* [分布式理论总结](docs/distributed/分布式技术实践总结.md) ## 面试指南 todo diff --git "a/docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2001\357\274\232 \344\270\200\350\207\264\346\200\247\343\200\2012PC\345\222\2143PC.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2001\357\274\232 \344\270\200\350\207\264\346\200\247\343\200\2012PC\345\222\2143PC.md" similarity index 100% rename from "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2001\357\274\232 \344\270\200\350\207\264\346\200\247\343\200\2012PC\345\222\2143PC.md" rename to "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2001\357\274\232 \344\270\200\350\207\264\346\200\247\343\200\2012PC\345\222\2143PC.md" diff --git "a/docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2002 \357\274\232CAP.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2002 \357\274\232CAP.md" similarity index 100% rename from "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2002 \357\274\232CAP.md" rename to "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2002 \357\274\232CAP.md" diff --git "a/docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2003\357\274\232 \346\227\266\351\227\264\343\200\201\346\227\266\351\222\237\345\222\214\344\272\213\344\273\266\351\241\272\345\272\217.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2003\357\274\232 \346\227\266\351\227\264\343\200\201\346\227\266\351\222\237\345\222\214\344\272\213\344\273\266\351\241\272\345\272\217.md" similarity index 100% rename from "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2003\357\274\232 \346\227\266\351\227\264\343\200\201\346\227\266\351\222\237\345\222\214\344\272\213\344\273\266\351\241\272\345\272\217.md" rename to "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2003\357\274\232 \346\227\266\351\227\264\343\200\201\346\227\266\351\222\237\345\222\214\344\272\213\344\273\266\351\241\272\345\272\217.md" diff --git "a/docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2004\357\274\232Paxos.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2004\357\274\232Paxos.md" similarity index 100% rename from "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2004\357\274\232Paxos.md" rename to "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2004\357\274\232Paxos.md" diff --git "a/docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2005\357\274\232\351\200\211\344\270\276\343\200\201\345\244\232\346\225\260\346\264\276\345\222\214\347\247\237\347\272\246.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2005\357\274\232\351\200\211\344\270\276\343\200\201\345\244\232\346\225\260\346\264\276\345\222\214\347\247\237\347\272\246.md" similarity index 100% rename from "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2005\357\274\232\351\200\211\344\270\276\343\200\201\345\244\232\346\225\260\346\264\276\345\222\214\347\247\237\347\272\246.md" rename to "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2005\357\274\232\351\200\211\344\270\276\343\200\201\345\244\232\346\225\260\346\264\276\345\222\214\347\247\237\347\272\246.md" diff --git "a/docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2006\357\274\232Raft\343\200\201Zab.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2006\357\274\232Raft\343\200\201Zab.md" similarity index 100% rename from "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2006\357\274\232Raft\343\200\201Zab.md" rename to "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2006\357\274\232Raft\343\200\201Zab.md" diff --git "a/docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2008\357\274\232zookeeper\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2008\357\274\232zookeeper\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241.md" similarity index 100% rename from "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2008\357\274\232zookeeper\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241.md" rename to "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\345\237\272\347\241\2008\357\274\232zookeeper\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241.md" diff --git "a/docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\350\277\233\351\230\2667\357\274\232Paxos\345\217\230\347\247\215\345\222\214\344\274\230\345\214\226.md" "b/docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\350\277\233\351\230\2667\357\274\232Paxos\345\217\230\347\247\215\345\222\214\344\274\230\345\214\226.md" similarity index 100% rename from "docs/distrubuted/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\350\277\233\351\230\2667\357\274\232Paxos\345\217\230\347\247\215\345\222\214\344\274\230\345\214\226.md" rename to "docs/distributed/basic/\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\220\206\350\256\272\350\277\233\351\230\2667\357\274\232Paxos\345\217\230\347\247\215\345\222\214\344\274\230\345\214\226.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25710\357\274\232LVS\345\256\236\347\216\260\350\264\237\350\275\275\345\235\207\350\241\241\347\232\204\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25710\357\274\232LVS\345\256\236\347\216\260\350\264\237\350\275\275\345\235\207\350\241\241\347\232\204\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25710\357\274\232LVS\345\256\236\347\216\260\350\264\237\350\275\275\345\235\207\350\241\241\347\232\204\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25710\357\274\232LVS\345\256\236\347\216\260\350\264\237\350\275\275\345\235\207\350\241\241\347\232\204\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25711\357\274\232\345\210\206\345\270\203\345\274\217session\350\247\243\345\206\263\346\226\271\346\241\210\344\270\216\344\270\200\350\207\264\346\200\247hash.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25711\357\274\232\345\210\206\345\270\203\345\274\217session\350\247\243\345\206\263\346\226\271\346\241\210\344\270\216\344\270\200\350\207\264\346\200\247hash.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25711\357\274\232\345\210\206\345\270\203\345\274\217session\350\247\243\345\206\263\346\226\271\346\241\210\344\270\216\344\270\200\350\207\264\346\200\247hash.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25711\357\274\232\345\210\206\345\270\203\345\274\217session\350\247\243\345\206\263\346\226\271\346\241\210\344\270\216\344\270\200\350\207\264\346\200\247hash.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25712\357\274\232\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25712\357\274\232\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25712\357\274\232\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25712\357\274\232\345\210\206\345\270\203\345\274\217ID\347\224\237\346\210\220\346\226\271\346\241\210.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25713\357\274\232\347\274\223\345\255\230\347\232\204\351\202\243\344\272\233\344\272\213.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25713\357\274\232\347\274\223\345\255\230\347\232\204\351\202\243\344\272\233\344\272\213.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25713\357\274\232\347\274\223\345\255\230\347\232\204\351\202\243\344\272\233\344\272\213.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25713\357\274\232\347\274\223\345\255\230\347\232\204\351\202\243\344\272\233\344\272\213.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232Spring Boot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232Spring Boot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232Spring Boot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232Spring Boot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25715\357\274\232\347\274\223\345\255\230\346\233\264\346\226\260\347\232\204\345\245\227\350\267\257.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25715\357\274\232\347\274\223\345\255\230\346\233\264\346\226\260\347\232\204\345\245\227\350\267\257.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25715\357\274\232\347\274\223\345\255\230\346\233\264\346\226\260\347\232\204\345\245\227\350\267\257.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25715\357\274\232\347\274\223\345\255\230\346\233\264\346\226\260\347\232\204\345\245\227\350\267\257.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25716\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\351\224\201\347\232\204\345\207\240\347\247\215\346\226\271\346\241\210.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25716\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\351\224\201\347\232\204\345\207\240\347\247\215\346\226\271\346\241\210.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25716\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\351\224\201\347\232\204\345\207\240\347\247\215\346\226\271\346\241\210.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25716\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\351\224\201\347\232\204\345\207\240\347\247\215\346\226\271\346\241\210.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25717\357\274\232\346\265\205\346\236\220\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25717\357\274\232\346\265\205\346\236\220\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25717\357\274\232\346\265\205\346\236\220\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25717\357\274\232\346\265\205\346\236\220\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25718\357\274\232\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\270\270\347\224\250\350\247\243\345\206\263\346\226\271\346\241\210.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25718\357\274\232\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\270\270\347\224\250\350\247\243\345\206\263\346\226\271\346\241\210.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25718\357\274\232\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\270\270\347\224\250\350\247\243\345\206\263\346\226\271\346\241\210.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25718\357\274\232\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\270\270\347\224\250\350\247\243\345\206\263\346\226\271\346\241\210.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25719\357\274\232\344\275\277\347\224\250RocketMQ\344\272\213\345\212\241\346\266\210\346\201\257\350\247\243\345\206\263\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25719\357\274\232\344\275\277\347\224\250RocketMQ\344\272\213\345\212\241\346\266\210\346\201\257\350\247\243\345\206\263\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25719\357\274\232\344\275\277\347\224\250RocketMQ\344\272\213\345\212\241\346\266\210\346\201\257\350\247\243\345\206\263\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25719\357\274\232\344\275\277\347\224\250RocketMQ\344\272\213\345\212\241\346\266\210\346\201\257\350\247\243\345\206\263\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2571\357\274\232\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\232\204\344\270\200\344\272\233\345\237\272\346\234\254\346\246\202\345\277\265.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2571\357\274\232\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\232\204\344\270\200\344\272\233\345\237\272\346\234\254\346\246\202\345\277\265.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2571\357\274\232\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\232\204\344\270\200\344\272\233\345\237\272\346\234\254\346\246\202\345\277\265.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2571\357\274\232\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\347\232\204\344\270\200\344\272\233\345\237\272\346\234\254\346\246\202\345\277\265.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25720\357\274\232\346\266\210\346\201\257\351\230\237\345\210\227\345\233\240\344\275\225\350\200\214\347\224\237.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25720\357\274\232\346\266\210\346\201\257\351\230\237\345\210\227\345\233\240\344\275\225\350\200\214\347\224\237.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25720\357\274\232\346\266\210\346\201\257\351\230\237\345\210\227\345\233\240\344\275\225\350\200\214\347\224\237.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25720\357\274\232\346\266\210\346\201\257\351\230\237\345\210\227\345\233\240\344\275\225\350\200\214\347\224\237.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257 Kafka.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257 Kafka.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257 Kafka.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257 Kafka.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2572\357\274\232\345\210\206\345\270\203\345\274\217\344\270\200\350\207\264\346\200\247\345\215\217\350\256\256\344\270\216Paxos\357\274\214Raft\347\256\227\346\263\225.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2572\357\274\232\345\210\206\345\270\203\345\274\217\344\270\200\350\207\264\346\200\247\345\215\217\350\256\256\344\270\216Paxos\357\274\214Raft\347\256\227\346\263\225.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2572\357\274\232\345\210\206\345\270\203\345\274\217\344\270\200\350\207\264\346\200\247\345\215\217\350\256\256\344\270\216Paxos\357\274\214Raft\347\256\227\346\263\225.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2572\357\274\232\345\210\206\345\270\203\345\274\217\344\270\200\350\207\264\346\200\247\345\215\217\350\256\256\344\270\216Paxos\357\274\214Raft\347\256\227\346\263\225.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2573\357\274\232\345\210\235\346\216\242\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241zookeeper.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2573\357\274\232\345\210\235\346\216\242\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241zookeeper.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2573\357\274\232\345\210\235\346\216\242\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241zookeeper.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2573\357\274\232\345\210\235\346\216\242\345\210\206\345\270\203\345\274\217\345\215\217\350\260\203\346\234\215\345\212\241zookeeper.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2574\357\274\232ZAB\345\215\217\350\256\256\346\246\202\350\277\260\344\270\216\351\200\211\344\270\273\346\265\201\347\250\213\350\257\246\350\247\243.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2574\357\274\232ZAB\345\215\217\350\256\256\346\246\202\350\277\260\344\270\216\351\200\211\344\270\273\346\265\201\347\250\213\350\257\246\350\247\243.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2574\357\274\232ZAB\345\215\217\350\256\256\346\246\202\350\277\260\344\270\216\351\200\211\344\270\273\346\265\201\347\250\213\350\257\246\350\247\243.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2574\357\274\232ZAB\345\215\217\350\256\256\346\246\202\350\277\260\344\270\216\351\200\211\344\270\273\346\265\201\347\250\213\350\257\246\350\247\243.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2575\357\274\232Zookeeper\347\232\204\351\205\215\347\275\256\344\270\216\351\233\206\347\276\244\347\256\241\347\220\206\345\256\236\346\210\230.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2575\357\274\232Zookeeper\347\232\204\351\205\215\347\275\256\344\270\216\351\233\206\347\276\244\347\256\241\347\220\206\345\256\236\346\210\230.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2575\357\274\232Zookeeper\347\232\204\351\205\215\347\275\256\344\270\216\351\233\206\347\276\244\347\256\241\347\220\206\345\256\236\346\210\230.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2575\357\274\232Zookeeper\347\232\204\351\205\215\347\275\256\344\270\216\351\233\206\347\276\244\347\256\241\347\220\206\345\256\236\346\210\230.md" diff --git "a/docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2576\357\274\232Zookeeper\345\205\270\345\236\213\345\272\224\347\224\250\345\234\272\346\231\257\345\217\212\345\256\236\350\267\265.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2576\357\274\232Zookeeper\345\205\270\345\236\213\345\272\224\347\224\250\345\234\272\346\231\257\345\217\212\345\256\236\350\267\265.md" similarity index 100% rename from "docs/distrubuted/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2576\357\274\232Zookeeper\345\205\270\345\236\213\345\272\224\347\224\250\345\234\272\346\231\257\345\217\212\345\256\236\350\267\265.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\2576\357\274\232Zookeeper\345\205\270\345\236\213\345\272\224\347\224\250\345\234\272\346\231\257\345\217\212\345\256\236\350\267\265.md" diff --git "a/docs/distrubuted/\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\345\256\236\350\267\265\346\200\273\347\273\223.md" "b/docs/distributed/\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\345\256\236\350\267\265\346\200\273\347\273\223.md" similarity index 100% rename from "docs/distrubuted/\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\345\256\236\350\267\265\346\200\273\347\273\223.md" rename to "docs/distributed/\345\210\206\345\270\203\345\274\217\346\212\200\346\234\257\345\256\236\350\267\265\346\200\273\347\273\223.md" diff --git "a/docs/distrubuted/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272\346\200\273\347\273\223.md" "b/docs/distributed/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272\346\200\273\347\273\223.md" similarity index 100% rename from "docs/distrubuted/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272\346\200\273\347\273\223.md" rename to "docs/distributed/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272\346\200\273\347\273\223.md" From 3bb0926b7a594dd566ae23f7bacead1566c8be38 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Fri, 31 Mar 2023 19:54:30 +0800 Subject: [PATCH 08/90] resolve pic problem in juc --- ...\227\357\274\214CAS\346\223\215\344\275\234.md" | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2273\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" "b/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2273\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" index 4220389..b778e5e 100644 --- "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2273\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" +++ "b/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2273\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" @@ -264,7 +264,7 @@ volatile写的内存语义如下: 以上面示例程序VolatileExample为例,假设线程A首先执行writer()方法,随后线程B执行reader()方法,初始时两个线程的本地内存中的flag和a都是初始状态。下图是线程A执行volatile写后,共享变量的状态示意图: -![](https://res.infoq.com/articles/java-memory-model-4/zh/resources/2.png) +![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9UTEgzQ2ljUFZpYnJlMXJ3emFJWjJkT3VVdDVjekVBdVQ3dmRtZ0NFNjBKeTlRODRsZ0VwdzNqaWFCREJmSjE2TkxZUEQ2a1dNeHBnOTNad0tUOVY2Ym1Ydy82NDA?x-oss-process=image/format,png) 如上图所示,线程A在写flag变量后,本地内存A中被线程A更新过的两个共享变量的值被刷新到主内存中。此时,本地内存A和主内存中的共享变量的值是一致的。 @@ -274,7 +274,7 @@ volatile读的内存语义如下: 下面是线程B读同一个volatile变量后,共享变量的状态示意图: -![](https://res.infoq.com/articles/java-memory-model-4/zh/resources/3.png) +![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9UTEgzQ2ljUFZpYnJlMXJ3emFJWjJkT3VVdDVjekVBdVQ3WWcwMEt1bGVicDAza0MyU2ZHWmI3Tm5vaDcxaG5JS3dpYW5qcnJsUzBUN3dNZWljWUJySmY4YVEvNjQw?x-oss-process=image/format,png) 如上图所示,在读flag变量后,本地内存B已经被置为无效。此时,线程B必须从主内存中读取共享变量。线程B的读取操作将导致本地内存B与主内存中的共享变量的值也变成一致的了。 @@ -322,7 +322,7 @@ volatile读的内存语义如下: 下面是保守策略下,volatile写插入内存屏障后生成的指令序列示意图: -![](https://res.infoq.com/articles/java-memory-model-4/zh/resources/4.png) +![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9UTEgzQ2ljUFZpYnJlMXJ3emFJWjJkT3VVdDVjekVBdVQ3U2dxcTJ0SnF1N1pwQllpYVZmZnpTd3F5NGhDY1dSWVhNVzlzM0VWSmljRFZzODY5cWc1M1A3eEEvNjQw?x-oss-process=image/format,png) 上图中的StoreStore屏障可以保证在volatile写之前,其前面的所有普通写操作已经对任意处理器可见了。这是因为StoreStore屏障将保障上面所有的普通写在volatile写之前刷新到主内存。 @@ -330,7 +330,7 @@ volatile读的内存语义如下: 下面是在保守策略下,volatile读插入内存屏障后生成的指令序列示意图: -![](https://res.infoq.com/articles/java-memory-model-4/zh/resources/5.png) +![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9UTEgzQ2ljUFZpYnJlMXJ3emFJWjJkT3VVdDVjekVBdVQ3U2dxcTJ0SnF1N1pwQllpYVZmZnpTd3F5NGhDY1dSWVhNVzlzM0VWSmljRFZzODY5cWc1M1A3eEEvNjQw?x-oss-process=image/format,png) 上图中的LoadLoad屏障用来禁止处理器把上面的volatile读与下面的普通读重排序。LoadStore屏障用来禁止处理器把上面的volatile读与下面的普通写重排序。 @@ -354,7 +354,7 @@ volatile读的内存语义如下: 针对readAndWrite()方法,编译器在生成字节码时可以做如下的优化: -![](https://res.infoq.com/articles/java-memory-model-4/zh/resources/6.png) +![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9UTEgzQ2ljUFZpYnJlMXJ3emFJWjJkT3VVdDVjekVBdVQ3RzlISFVoNTh0MHNCVUZwMEJyZVU4Njc0OGZHMFhKdmxRTXFpYXE3dUhRNklGd1Q5a1pZQktjZy82NDA?x-oss-process=image/format,png) 注意,最后的StoreLoad屏障不能省略。因为第二个volatile写之后,方法立即return。此时编译器可能无法准确断定后面是否会有volatile读或写,为了安全起见,编译器常常会在这里插入一个StoreLoad屏障。 @@ -362,7 +362,7 @@ volatile读的内存语义如下: 前面保守策略下的volatile读和写,在 x86处理器平台可以优化成: -![](https://res.infoq.com/articles/java-memory-model-4/zh/resources/7.png) +![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9UTEgzQ2ljUFZpYnJlMXJ3emFJWjJkT3VVdDVjekVBdVQ3aWNaU1h6dURzT2pnZ0hBTmljYnljbTNPYjkwbkVVOURCTm1JaHE1Y2NVdTZMUnZxS3V1cmlhQ29nLzY0MA?x-oss-process=image/format,png) 前文提到过,x86处理器仅会对写-读操作做重排序。X86不会对读-读,读-写和写-写操作做重排序,因此在x86处理器中会省略掉这三种操作类型对应的内存屏障。在x86中,JMM仅需在volatile写后面插入一个StoreLoad屏障即可正确实现volatile写-读的内存语义。这意味着在x86处理器中,volatile写的开销比volatile读的开销会大很多(因为执行StoreLoad屏障开销会比较大)。 @@ -370,7 +370,7 @@ volatile读的内存语义如下: 在JSR-133之前的旧Java内存模型中,虽然不允许volatile变量之间重排序,但旧的Java内存模型允许volatile变量与普通变量之间重排序。在旧的内存模型中,VolatileExample示例程序可能被重排序成下列时序来执行: -![](https://res.infoq.com/articles/java-memory-model-4/zh/resources/8.png) +![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9UTEgzQ2ljUFZpYnJlMXJ3emFJWjJkT3VVdDVjekVBdVQ3TG9aamN2em13WUk3aGVJcksyemp2YWs5RGI5cHZKaFJ3NVR0cHFGMzNWUjFHb0tzd1lwNUdnLzY0MA?x-oss-process=image/format,png) 在旧的内存模型中,当1和2之间没有数据依赖关系时,1和2之间就可能被重排序(3和4类似)。其结果就是:读线程B执行4时,不一定能看到写线程A在执行1时对共享变量的修改。 From f6f1362df0adf58fde9f89cdef7680bf334887ab Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Fri, 31 Mar 2023 20:13:19 +0800 Subject: [PATCH 09/90] =?UTF-8?q?resolve=20pic=20problem=20in=20=E5=A4=9A?= =?UTF-8?q?=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\271\350\261\241\345\237\272\347\241\200.md" | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git "a/docs/java/basic/1\343\200\201\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md" "b/docs/java/basic/1\343\200\201\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md" index 3570358..20a3a96 100644 --- "a/docs/java/basic/1\343\200\201\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md" +++ "b/docs/java/basic/1\343\200\201\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md" @@ -452,9 +452,10 @@ java里的多态主要表现在两个方面:   这两句话是什么意思呢,让我们用代码来体验一下,首先我们创建一个父类Animal和一个子类Dog,在主函数里如下所示: -![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701155536086-1897896282.png) +![img](https://cdn.mianshigee.com/upload/note/thumbnails/202202211611552259.png) -  注意:我们不能使用一个子类的引用来指向父类的对象,如:![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701155839586-923083573.png)。 +  注意:我们不能使用一个子类的引用来指向父类的对象,如: +![img](https://cdn.mianshigee.com/upload/note/thumbnails/202202211611556958.png)。   这里我们必须深刻理解引用多态的意义,才能更好记忆这种多态的特性。为什么子类的引用不能用来指向父类的对象呢?我在这里通俗给大家讲解一下:就以上面的例子来说,我们能说“狗是一种动物”,但是不能说“动物是一种狗”,狗和动物是父类和子类的继承关系,它们的从属是不能颠倒的。当父类的引用指向子类的对象时,该对象将只是看成一种特殊的父类(里面有重写的方法和属性),反之,一个子类的引用来指向父类的对象是不可行的!! @@ -482,23 +483,23 @@ java里的多态主要表现在两个方面:   就以上述的父类Animal和一个子类Dog来说明,当父类的引用可以指向子类的对象时,就是**向上类型转换**。如: -![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701162630508-961507659.png) +![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2MjYzMDUwOC05NjE1MDc2NTkucG5n?x-oss-process=image/format,png)   **2. 向下类型转换(强制类型转换),是大类型转换到小类型(有风险,可能出现数据溢出)。**   将上述代码再加上一行,我们再次将父类转换为子类引用,那么会出现错误,编译器不允许我们直接这么做**,**虽然我们知道这个父类引用指向的就是子类对象,但是编译器认为这种转换是存在风险的**。**如: -![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701162926477-3857975.png) +![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2MjkyNjQ3Ny0zODU3OTc1LnBuZw?x-oss-process=image/format,png) -  那么我们该怎么解决这个问题呢,我们可以在animal前加上(Dog)来强制类型转换。如:![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701163408383-2003626729.png) +  那么我们该怎么解决这个问题呢,我们可以在animal前加上(Dog)来强制类型转换。如:![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2NDIyOTg5OS0xMDU1MTkwNzc0LnBuZw?x-oss-process=image/format,png) -  但是如果父类引用没有指向**该子类的对象**,则不能向下类型转换,虽然编译器不会报错,但是运行的时候程序会出错,如:![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701164229899-1055190774.png) +  但是如果父类引用没有指向**该子类的对象**,则不能向下类型转换,虽然编译器不会报错,但是运行的时候程序会出错,如:![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2NTEzMzI4OS03MTc0MzkzNjAucG5n?x-oss-process=image/format,png)   其实这就是上面所说的子类的引用指向父类的对象,而强制转换类型也不能转换!!   还有一种情况是父类的引用指向**其他子类的对象**,则不能通过强制转为**该子类的对象**。如: -    ![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701165133289-717439360.png) +    ![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2NTEzMzI4OS03MTc0MzkzNjAucG5n?x-oss-process=image/format,png)   这是因为我们在编译的时候进行了强制类型转换,编译时的类型是我们强制转换的类型,所以编译器不会报错,而当我们运行的时候,程序给animal开辟的是Dog类型的内存空间,这与Cat类型内存空间不匹配,所以无法正常转换。这两种情况出错的本质是一样的,所以我们在使用强制类型转换的时候要特别注意这两种错误!!下面有个更安全的方式来实现向下类型转换。。。。 @@ -508,7 +509,7 @@ java里的多态主要表现在两个方面:   我们来使用instanceof运算符来规避上面的错误,代码修改如下: -  ![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701165626571-501228254.png) +  ![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2NTYyNjU3MS01MDEyMjgyNTQucG5n?x-oss-process=image/format,png)   利用if语句和instanceof运算符来判断两个对象的类型是否一致。 From 760cc8971613257429e4bd2332151e26e9242e48 Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Fri, 31 Mar 2023 20:28:04 +0800 Subject: [PATCH 10/90] resolve pic problem in network programing --- ReadMe.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 23bde83..f8b8ed6 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -196,17 +196,17 @@ todo ### Java网络编程 -* [Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制](docs/java/jvm/Java网络编程和NIO详解1:JAVA%20中原生的%20socket%20通信机制.md) -* [Java网络编程与NIO详解2:JAVA NIO 一步步构建IO多路复用的请求模型](docs/java/jvm/Java网络编程与NIO详解2:JAVA%20NIO%20一步步构建IO多路复用的请求模型.md) -* [Java网络编程和NIO详解3:IO模型与Java网络编程模型](docs/java/jvm/Java网络编程和NIO详解3:IO模型与Java网络编程模型.md) -* [Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector](https://github.com/h2pl/Java-Tutorial/blob/master/docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E4%B8%8ENIO%E8%AF%A6%E8%A7%A34%EF%BC%9A%E6%B5%85%E6%9E%90NIO%E5%8C%85%E4%B8%AD%E7%9A%84Buffer%E3%80%81Channel%20%E5%92%8C%20Selector.md) -* [Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO](https://github.com/h2pl/Java-Tutorial/blob/master/docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%92%8CNIO%E8%AF%A6%E8%A7%A35%EF%BC%9AJava%20%E9%9D%9E%E9%98%BB%E5%A1%9E%20IO%20%E5%92%8C%E5%BC%82%E6%AD%A5%20IO.md) -* [Java网络编程和NIO详解6:Linux epoll实现原理详解](docs/java/jvm/Java网络编程和NIO详解6:Linux%20epoll实现原理详解.md) -* [Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理](Java网络编程和NIO详解7:浅谈%20Linux%20中NIO%20Selector%20的实现原理.md) -* [Java网络编程与NIO详解8:浅析mmap和Direct Buffer](https://github.com/h2pl/Java-Tutorial/blob/master/docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E4%B8%8ENIO%E8%AF%A6%E8%A7%A38%EF%BC%9A%E6%B5%85%E6%9E%90mmap%E5%92%8CDirect%20Buffer.md) -* [Java网络编程和NIO详解9:基于NIO的网络编程框架Netty](docs/java/jvm/Java网络编程和NIO详解9:基于NIO的网络编程框架Netty.md) -* [Java网络编程与NIO详解10:深度解读Tomcat中的NIO模型](https://github.com/h2pl/Java-Tutorial/blob/master/docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E4%B8%8ENIO%E8%AF%A6%E8%A7%A310%EF%BC%9A%E6%B7%B1%E5%BA%A6%E8%A7%A3%E8%AF%BBTomcat%E4%B8%AD%E7%9A%84NIO%E6%A8%A1%E5%9E%8B.md) -* [Java网络编程与NIO详解11:Tomcat中的Connector源码分析(NIO)](docs/java/jvm/Java网络编程与NIO详解11:Tomcat中的Connector源码分析(NIO).md) +* [Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制](docs/java/network-programming/Java网络编程和NIO详解1:JAVA%20中原生的%20socket%20通信机制.md) +* [Java网络编程与NIO详解2:JAVA NIO 一步步构建IO多路复用的请求模型](docs/java/network-programming/Java网络编程与NIO详解2:JAVA%20NIO%20一步步构建IO多路复用的请求模型.md) +* [Java网络编程和NIO详解3:IO模型与Java网络编程模型](docs/java/network-programming/Java网络编程和NIO详解3:IO模型与Java网络编程模型.md) +* [Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector](docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E4%B8%8ENIO%E8%AF%A6%E8%A7%A34%EF%BC%9A%E6%B5%85%E6%9E%90NIO%E5%8C%85%E4%B8%AD%E7%9A%84Buffer%E3%80%81Channel%20%E5%92%8C%20Selector.md) +* [Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO](docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%92%8CNIO%E8%AF%A6%E8%A7%A35%EF%BC%9AJava%20%E9%9D%9E%E9%98%BB%E5%A1%9E%20IO%20%E5%92%8C%E5%BC%82%E6%AD%A5%20IO.md) +* [Java网络编程和NIO详解6:Linux epoll实现原理详解](docs/java/network-programming/Java网络编程和NIO详解6:Linux%20epoll实现原理详解.md) +* [Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理](docs/java/network-programming/Java网络编程和NIO详解7:浅谈%20Linux%20中NIO%20Selector%20的实现原理.md) +* [Java网络编程与NIO详解8:浅析mmap和Direct Buffer](docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E4%B8%8ENIO%E8%AF%A6%E8%A7%A38%EF%BC%9A%E6%B5%85%E6%9E%90mmap%E5%92%8CDirect%20Buffer.md) +* [Java网络编程和NIO详解9:基于NIO的网络编程框架Netty](docs/java/network-programming/Java网络编程和NIO详解9:基于NIO的网络编程框架Netty.md) +* [Java网络编程与NIO详解10:深度解读Tomcat中的NIO模型](docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E4%B8%8ENIO%E8%AF%A6%E8%A7%A310%EF%BC%9A%E6%B7%B1%E5%BA%A6%E8%A7%A3%E8%AF%BBTomcat%E4%B8%AD%E7%9A%84NIO%E6%A8%A1%E5%9E%8B.md) +* [Java网络编程与NIO详解11:Tomcat中的Connector源码分析(NIO)](docs/java/network-programming/Java网络编程与NIO详解11:Tomcat中的Connector源码分析(NIO).md) ## 计算机基础 From bfb15b068176e70055ed8fd51bb76c6ccf7f217e Mon Sep 17 00:00:00 2001 From: h2pl <362294931@qq.com> Date: Fri, 31 Mar 2023 20:38:19 +0800 Subject: [PATCH 11/90] resolve docs in juc --- ...\233\206\345\220\210\347\261\273\346\200\273\347\273\223.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\347\261\273\346\200\273\347\273\223.md" "b/docs/java/collection/Java\351\233\206\345\220\210\347\261\273\346\200\273\347\273\223.md" index 2c05f7b..36d9432 100644 --- "a/docs/java/collection/Java\351\233\206\345\220\210\347\261\273\346\200\273\347\273\223.md" +++ "b/docs/java/collection/Java\351\233\206\345\220\210\347\261\273\346\200\273\347\273\223.md" @@ -91,7 +91,7 @@ hashmap的增删改查方式比较简单,都是遍历,替换。有一点要 ## CHM -concurrenthashmap也稍微提一下把,chm1.7使用分段锁来控制并发,每个segment对应一个segmentmask,通过key的hash值相与这个segmentmask得到segment位置,然后在找到具体的entry数组下标。所以chm需要维护多个segment,每个segment对应一个数组。分段锁使用的是reetreetlock可重入锁实现。查询时不加锁。 +concurrenthashmap也稍微提一下把,chm1.7使用分段锁来控制并发,每个segment对应一个segmentmask,通过key的hash值相与这个segmentmask得到segment位置,然后在找到具体的entry数组下标。所以chm需要维护多个segment,每个segment对应一个数组。分段锁使用的是ReentrantLock可重入锁实现。查询时不加锁。 1.8则放弃使用分段锁,改用cas+synchronized方式实现并发控制,查询时不加锁,插入时如果没有冲突直接cas到成功为止,有冲突则使用synchronized插入。 From 1d8d97d20f2cfcdf43fc1ebaf913b5eae589fa57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=B0=8F=E6=96=9C?= <362294931@qq.com> Date: Mon, 3 Apr 2023 11:57:22 +0800 Subject: [PATCH 12/90] modify catalog --- ReadMe.md | 119 +++++++++--------- .../\345\211\221\346\214\207offer.md" | 0 ...46\344\271\240\346\200\273\347\273\223.md" | 0 ...46\344\271\240\346\200\273\347\273\223.md" | 0 ...46\344\271\240\346\200\273\347\273\223.md" | 0 ...4\232SpringMVC\346\246\202\350\277\260.md" | 0 ...5\277\265\344\270\216DispatcherServlet.md" | 0 ...67\346\261\202\350\275\254\345\217\221.md" | 0 ...\243\347\241\256\347\232\204Controller.md" | 0 ...43\346\236\220\345\216\237\347\220\206.md" | 0 ...6@ResponseBody\346\263\250\350\247\243.md" | 0 ...72\346\234\254\345\216\237\347\220\206.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...\351\230\237\345\210\227 BlockingQueue.md" | 0 ...20\347\240\201\345\256\236\347\216\260.md" | 0 ...p \345\205\250\350\247\243\346\236\220.md" | 0 ...7\232\204Unsafe\345\222\214Locksupport.md" | 0 ...27\346\263\225\345\211\226\346\236\220.md" | 0 ...va\345\244\232\347\272\277\347\250\213.md" | 0 ...345\255\230\346\250\241\345\236\213JMM.md" | 0 ...357\274\214CAS\346\223\215\344\275\234.md" | 0 ...1\224\201 Lock\345\222\214synchronized.md" | 0 ...56\345\255\227\350\247\243\346\236\220.md" | 0 ...345\236\213JMM\346\200\273\347\273\223.md" | 0 ...347\261\273AQS\350\257\246\350\247\243.md" | 0 ...71\263\351\224\201\357\274\214Condtion.md" | 0 ...73\347\232\204\345\256\236\347\216\260.md" | 0 ...46\344\271\240\346\200\273\347\273\223.md" | 0 ...00\347\273\210\346\225\210\346\236\234.md" | 2 +- 29 files changed, 62 insertions(+), 59 deletions(-) rename "docs/algorithms/\345\211\221\346\214\207offer.md" => "docs/cs/algorithms/\345\211\221\346\214\207offer.md" (100%) rename "docs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\345\255\246\344\271\240\346\200\273\347\273\223.md" => "docs/cs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\345\255\246\344\271\240\346\200\273\347\273\223.md" (100%) rename "docs/operating-system/Linux\345\206\205\346\240\270\344\270\216\345\237\272\347\241\200\345\221\275\344\273\244\345\255\246\344\271\240\346\200\273\347\273\223.md" => "docs/cs/operating-system/Linux\345\206\205\346\240\270\344\270\216\345\237\272\347\241\200\345\221\275\344\273\244\345\255\246\344\271\240\346\200\273\347\273\223.md" (100%) rename "docs/operating-system/\346\223\215\344\275\234\347\263\273\347\273\237\345\255\246\344\271\240\346\200\273\347\273\223.md" => "docs/cs/operating-system/\346\223\215\344\275\234\347\263\273\347\273\237\345\255\246\344\271\240\346\200\273\347\273\223.md" (100%) rename "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2201\357\274\232SpringMVC\346\246\202\350\277\260.md" => "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2201\357\274\232SpringMVC\346\246\202\350\277\260.md" (100%) rename "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2202\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" => "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2202\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" (100%) rename "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2203\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" => "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2203\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" (100%) rename "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2204\357\274\232DispatcherServlet\345\246\202\344\275\225\346\211\276\345\210\260\346\255\243\347\241\256\347\232\204Controller.md" => "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2204\357\274\232DispatcherServlet\345\246\202\344\275\225\346\211\276\345\210\260\346\255\243\347\241\256\347\232\204Controller.md" (100%) rename "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2206\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" => "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2206\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" (100%) rename "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\211\226\346\236\2205\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" => "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\211\226\346\236\2205\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" (100%) rename "docs/java-web/\346\267\261\345\205\245JavaWeb\346\212\200\346\234\257\344\270\226\347\225\21415\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md" => "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\21415\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java \350\257\273\345\206\231\351\224\201 ReentrantReadWriteLock \346\272\220\347\240\201\345\210\206\346\236\220.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java \350\257\273\345\206\231\351\224\201 ReentrantReadWriteLock \346\272\220\347\240\201\345\210\206\346\236\220.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273 Java \351\230\273\345\241\236\351\230\237\345\210\227 BlockingQueue.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273 Java \351\230\273\345\241\236\351\230\237\345\210\227 BlockingQueue.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273 java \347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273 java \347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java \344\270\255\347\232\204 HashMap \345\222\214 ConcurrentHashMap \345\205\250\350\247\243\346\236\220.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java \344\270\255\347\232\204 HashMap \345\222\214 ConcurrentHashMap \345\205\250\350\247\243\346\236\220.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22714\357\274\232JUC\344\270\255\345\270\270\347\224\250\347\232\204Unsafe\345\222\214Locksupport.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22714\357\274\232JUC\344\270\255\345\270\270\347\224\250\347\232\204Unsafe\345\222\214Locksupport.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\347\274\226\347\250\213\346\214\207\345\215\22715\357\274\232Fork join\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22715\357\274\232Fork join\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2271\357\274\232\345\271\266\345\217\221\345\237\272\347\241\200\344\270\216Java\345\244\232\347\272\277\347\250\213.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2271\357\274\232\345\271\266\345\217\221\345\237\272\347\241\200\344\270\216Java\345\244\232\347\272\277\347\250\213.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2273\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2273\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201 Lock\345\222\214synchronized.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201 Lock\345\222\214synchronized.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2275\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2275\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2276\357\274\232Java\345\206\205\345\255\230\346\250\241\345\236\213JMM\346\200\273\347\273\223.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2276\357\274\232Java\345\206\205\345\255\230\346\250\241\345\236\213JMM\346\200\273\347\273\223.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2277\357\274\232JUC\347\232\204\346\240\270\345\277\203\347\261\273AQS\350\257\246\350\247\243.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2277\357\274\232JUC\347\232\204\346\240\270\345\277\203\347\261\273AQS\350\257\246\350\247\243.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2278\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2278\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2279\357\274\232AQS\345\205\261\344\272\253\346\250\241\345\274\217\344\270\216\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273\347\232\204\345\256\236\347\216\260.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2279\357\274\232AQS\345\205\261\344\272\253\346\250\241\345\274\217\344\270\216\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273\347\232\204\345\256\236\347\216\260.md" (100%) rename "docs/java/currency/Java\345\271\266\345\217\221\346\200\273\347\273\223.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\346\200\273\347\273\223.md" (100%) diff --git a/ReadMe.md b/ReadMe.md index f8b8ed6..f63c941 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -63,28 +63,28 @@ ### 基础知识 -* [面向对象基础](docs/java/basic/1、面向对象基础.md) -* [Java基本数据类型](docs/java/basic/2、Java基本数据类型.md) -* [string和包装类](docs/java/basic/3、string和包装类.md) -* [final关键字特性](docs/java/basic/4、final关键字特性.md) -* [Java类和包](docs/java/basic/5、Java类和包.md) -* [抽象类和接口](docs/java/basic/6、抽象类和接口.md) -* [代码块和代码执行顺序](docs/java/basic/7、代码块和代码执行顺序.md) -* [Java自动拆箱装箱里隐藏的秘密](docs/java/basic/8、Java自动拆箱装箱里隐藏的秘密.md) -* [Java中的Class类和Object类](docs/java/basic/9、Java中的Class类和Object类.md) -* [Java异常](docs/java/basic/10、Java异常.md) -* [解读Java中的回调](docs/java/basic/11、解读Java中的回调.md) -* [反射](docs/java/basic/12、反射.md) -* [泛型](docs/java/basic/13、泛型.md) -* [枚举类](docs/java/basic/14、枚举类.md) -* [Java注解和最佳实践](docs/java/basic/15、Java注解和最佳实践.md) -* [JavaIO流](docs/java/basic/16、JavaIO流.md) -* [多线程](docs/java/basic/17、多线程.md) -* [深入理解内部类](docs/java/basic/18、深入理解内部类.md) -* [javac和javap](docs/java/basic/20、javac和javap.md) -* [Java8新特性终极指南](docs/java/basic/21、Java8新特性终极指南.md) -* [序列化和反序列化](docs/java/basic/22、序列化和反序列化.md) -* [继承、封装、多态的实现原理](docs/java/basic/23、继承、封装、多态的实现原理.md) +* [1、面向对象基础](docs/java/basic/1、面向对象基础.md) +* [2、Java基本数据类型](docs/java/basic/2、Java基本数据类型.md) +* [3、string和包装类](docs/java/basic/3、string和包装类.md) +* [4、final关键字特性](docs/java/basic/4、final关键字特性.md) +* [5、Java类和包](docs/java/basic/5、Java类和包.md) +* [6、抽象类和接口](docs/java/basic/6、抽象类和接口.md) +* [7、代码块和代码执行顺序](docs/java/basic/7、代码块和代码执行顺序.md) +* [8、Java自动拆箱装箱里隐藏的秘密](docs/java/basic/8、Java自动拆箱装箱里隐藏的秘密.md) +* [9、Java中的Class类和Object类](docs/java/basic/9、Java中的Class类和Object类.md) +* [10、Java异常](docs/java/basic/10、Java异常.md) +* [11、解读Java中的回调](docs/java/basic/11、解读Java中的回调.md) +* [12、反射](docs/java/basic/12、反射.md) +* [13、泛型](docs/java/basic/13、泛型.md) +* [14、枚举类](docs/java/basic/14、枚举类.md) +* [15、Java注解和最佳实践](docs/java/basic/15、Java注解和最佳实践.md) +* [16、JavaIO流](docs/java/basic/16、JavaIO流.md) +* [17、多线程](docs/java/basic/17、多线程.md) +* [18、深入理解内部类](docs/java/basic/18、深入理解内部类.md) +* [20、javac和javap](docs/java/basic/20、javac和javap.md) +* [21、Java8新特性终极指南](docs/java/basic/21、Java8新特性终极指南.md) +* [22、序列化和反序列化](docs/java/basic/22、序列化和反序列化.md) +* [23、继承、封装、多态的实现原理](docs/java/basic/23、继承、封装、多态的实现原理.md) ### 容器 @@ -123,7 +123,7 @@ * [走进JavaWeb技术世界12:从手动编译打包到项目构建工具Maven](docs/java-web/走进JavaWeb技术世界12:从手动编译打包到项目构建工具Maven.md) * [走进JavaWeb技术世界13:Hibernate入门经典与注解式开发](docs/java-web/走进JavaWeb技术世界13:Hibernate入门经典与注解式开发.md) * [走进JavaWeb技术世界14:Mybatis入门](docs/java-web/走进JavaWeb技术世界14:Mybatis入门.md) -* [深入JavaWeb技术世界15:深入浅出Mybatis基本原理](docs/java-web/深入JavaWeb技术世界15:深入浅出Mybatis基本原理.md) +* [走进JavaWeb技术世界15:深入浅出Mybatis基本原理](docs/java-web/走进JavaWeb技术世界15:深入浅出Mybatis基本原理.md) * [走进JavaWeb技术世界16:极简配置的SpringBoot](docs/java-web/走进JavaWeb技术世界16:极简配置的SpringBoot.md) ### Spring @@ -140,12 +140,12 @@ ### SpringMVC -* [SpringMVC源码分析1:SpringMVC概述](docs/java-web/Spring/SSM/SpringMVC源码分析1:SpringMVC概述.md) -* [SpringMVC源码分析2:SpringMVC设计理念与DispatcherServlet](docs/java-web/Spring/SSM/SpringMVC源码分析2:SpringMVC设计理念与DispatcherServlet.md) -* [SpringMVC源码分析3:DispatcherServlet的初始化与请求转发 ](docs/java-web/Spring/SSM/SpringMVC源码分析3:DispatcherServlet的初始化与请求转发.md) -* [SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller ](docs/java-web/Spring/SSM/SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller.md) -* [SpringMVC源码剖析5:消息转换器HttpMessageConverter与@ResponseBody注解](docs/java-web/Spring/SSM/SpringMVC源码剖析5:消息转换器HttpMessageConverter与@ResponseBody注解.md) -* [SpringMVC源码分析6:SpringMVC的视图解析原理 ](docs/java-web/Spring/SSM/SpringMVC源码分析6:SpringMVC的视图解析原理.md) +* [SpringMVC源码分析1:SpringMVC概述](docs/java-web/SpringMVC/SpringMVC源码分析1:SpringMVC概述.md) +* [SpringMVC源码分析2:SpringMVC设计理念与DispatcherServlet](docs/java-web/SpringMVC/SpringMVC源码分析2:SpringMVC设计理念与DispatcherServlet.md) +* [SpringMVC源码分析3:DispatcherServlet的初始化与请求转发 ](docs/java-web/SpringMVC/SpringMVC源码分析3:DispatcherServlet的初始化与请求转发.md) +* [SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller ](docs/java-web/SpringMVC/SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller.md) +* [SpringMVC源码剖析5:消息转换器HttpMessageConverter与@ResponseBody注解](docs/java-web/SpringMVC/SpringMVC源码剖析5:消息转换器HttpMessageConverter与@ResponseBody注解.md) +* [SpringMVC源码分析6:SpringMVC的视图解析原理 ](docs/java-web/SpringMVC/SpringMVC源码分析6:SpringMVC的视图解析原理.md) ### SpringBoot @@ -159,22 +159,22 @@ todo ### 并发 -* [Java并发指南1:并发基础与Java多线程](docs/java/currency/Java并发指南1:并发基础与Java多线程.md) -* [Java并发指南2:深入理解Java内存模型JMM](docs/java/currency/Java并发指南2:深入理解Java内存模型JMM.md) -* [Java并发指南3:并发三大问题与volatile关键字,CAS操作](docs/java/currency/Java并发指南3:并发三大问题与volatile关键字,CAS操作.md) -* [Java并发指南4:Java中的锁 Lock和synchronized](docs/java/currency/Java并发指南4:Java中的锁%20Lock和synchronized.md) -* [Java并发指南5:JMM中的final关键字解析](docs/java/currency/Java并发指南5:JMM中的final关键字解析.md) -* [Java并发指南6:Java内存模型JMM总结](docs/java/currency/Java并发指南6:Java内存模型JMM总结.md) -* [Java并发指南7:JUC的核心类AQS详解](docs/java/currency/Java并发指南7:JUC的核心类AQS详解.md) -* [Java并发指南8:AQS中的公平锁与非公平锁,Condtion](docs/java/currency/Java并发指南8:AQS中的公平锁与非公平锁,Condtion.md) -* [Java并发指南9:AQS共享模式与并发工具类的实现](docs/java/currency/Java并发指南9:AQS共享模式与并发工具类的实现.md) -* [Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析](docs/java/currency/Java并发指南10:Java%20读写锁%20ReentrantReadWriteLock%20源码分析.md) -* [Java并发指南11:解读 Java 阻塞队列 BlockingQueue](docs/java/currency/Java并发指南11:解读%20Java%20阻塞队列%20BlockingQueue.md) -* [Java并发指南12:深度解读 java 线程池设计思想及源码实现](docs/java/currency/Java并发指南12:深度解读%20java%20线程池设计思想及源码实现.md) -* [Java并发指南13:Java 中的 HashMap 和 ConcurrentHashMap 全解析](https://github.com/h2pl/Java-Tutorial/blob/master/docs/java/currency/Java%E5%B9%B6%E5%8F%91%E6%8C%87%E5%8D%9713%EF%BC%9AJava%20%E4%B8%AD%E7%9A%84%20HashMap%20%E5%92%8C%20ConcurrentHashMap%20%E5%85%A8%E8%A7%A3%E6%9E%90.md) -* [Java并发指南14:JUC中常用的Unsafe和Locksupport](docs/java/currency/Java并发指南14:JUC中常用的Unsafe和Locksupport.md) -* [Java并发指南15:Fork join并发框架与工作窃取算法剖析](docs/java/currency/Java并发编程指南15:Fork%20join并发框架与工作窃取算法剖析.md) -* [Java并发编程学习总结](https://github.com/h2pl/Java-Tutorial/blob/master/docs/java/currency/Java%E5%B9%B6%E5%8F%91%E6%80%BB%E7%BB%93.md) +* [Java并发指南1:并发基础与Java多线程](docs/java/concurrency/Java并发指南1:并发基础与Java多线程.md) +* [Java并发指南2:深入理解Java内存模型JMM](docs/java/concurrency/Java并发指南2:深入理解Java内存模型JMM.md) +* [Java并发指南3:并发三大问题与volatile关键字,CAS操作](docs/java/concurrency/Java并发指南3:并发三大问题与volatile关键字,CAS操作.md) +* [Java并发指南4:Java中的锁 Lock和synchronized](docs/java/concurrency/Java并发指南4:Java中的锁%20Lock和synchronized.md) +* [Java并发指南5:JMM中的final关键字解析](docs/java/concurrency/Java并发指南5:JMM中的final关键字解析.md) +* [Java并发指南6:Java内存模型JMM总结](docs/java/concurrency/Java并发指南6:Java内存模型JMM总结.md) +* [Java并发指南7:JUC的核心类AQS详解](docs/java/concurrency/Java并发指南7:JUC的核心类AQS详解.md) +* [Java并发指南8:AQS中的公平锁与非公平锁,Condtion](docs/java/concurrency/Java并发指南8:AQS中的公平锁与非公平锁,Condtion.md) +* [Java并发指南9:AQS共享模式与并发工具类的实现](docs/java/concurrency/Java并发指南9:AQS共享模式与并发工具类的实现.md) +* [Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析](docs/java/concurrency/Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析.md) +* [Java并发指南11:解读 Java 阻塞队列 BlockingQueue](docs/java/concurrency/Java并发指南11:解读 Java 阻塞队列 BlockingQueue.md) +* [Java并发指南12:深度解读 java 线程池设计思想及源码实现](docs/java/concurrency/Java并发指南12:深度解读 java 线程池设计思想及源码实现.md) +* [Java并发指南13:Java 中的 HashMap 和 ConcurrentHashMap 全解析](docs/java/concurrency/Java并发指南13:Java 中的 HashMap 和 ConcurrentHashMap 全解析.md) +* [Java并发指南14:JUC中常用的Unsafe和Locksupport](docs/java/concurrency/Java并发指南14:JUC中常用的Unsafe和Locksupport.md) +* [Java并发指南15:Fork join并发框架与工作窃取算法剖析](docs/java/concurrency/Java并发指南15:Fork join并发框架与工作窃取算法剖析.md) +* [Java并发编程学习总结](docs/java/concurrency/Java并发编程学习总结.md) ### JVM @@ -196,16 +196,16 @@ todo ### Java网络编程 -* [Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制](docs/java/network-programming/Java网络编程和NIO详解1:JAVA%20中原生的%20socket%20通信机制.md) -* [Java网络编程与NIO详解2:JAVA NIO 一步步构建IO多路复用的请求模型](docs/java/network-programming/Java网络编程与NIO详解2:JAVA%20NIO%20一步步构建IO多路复用的请求模型.md) +* [Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制](docs/java/network-programming/Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制.md) +* [Java网络编程与NIO详解2:JAVA NIO 一步步构建IO多路复用的请求模型](docs/java/network-programming/Java网络编程与NIO详解2:JAVA NIO 一步步构建IO多路复用的请求模型.md) * [Java网络编程和NIO详解3:IO模型与Java网络编程模型](docs/java/network-programming/Java网络编程和NIO详解3:IO模型与Java网络编程模型.md) -* [Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector](docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E4%B8%8ENIO%E8%AF%A6%E8%A7%A34%EF%BC%9A%E6%B5%85%E6%9E%90NIO%E5%8C%85%E4%B8%AD%E7%9A%84Buffer%E3%80%81Channel%20%E5%92%8C%20Selector.md) -* [Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO](docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%92%8CNIO%E8%AF%A6%E8%A7%A35%EF%BC%9AJava%20%E9%9D%9E%E9%98%BB%E5%A1%9E%20IO%20%E5%92%8C%E5%BC%82%E6%AD%A5%20IO.md) +* [Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector](docs/java/network-programming/Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector.md) +* [Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO](docs/java/network-programming/Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO.md) * [Java网络编程和NIO详解6:Linux epoll实现原理详解](docs/java/network-programming/Java网络编程和NIO详解6:Linux%20epoll实现原理详解.md) -* [Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理](docs/java/network-programming/Java网络编程和NIO详解7:浅谈%20Linux%20中NIO%20Selector%20的实现原理.md) -* [Java网络编程与NIO详解8:浅析mmap和Direct Buffer](docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E4%B8%8ENIO%E8%AF%A6%E8%A7%A38%EF%BC%9A%E6%B5%85%E6%9E%90mmap%E5%92%8CDirect%20Buffer.md) +* [Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理](docs/java/network-programming/Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理.md) +* [Java网络编程与NIO详解8:浅析mmap和Direct Buffer](docs/java/network-programming/Java网络编程与NIO详解8:浅析mmap和Direct Buffer.md) * [Java网络编程和NIO详解9:基于NIO的网络编程框架Netty](docs/java/network-programming/Java网络编程和NIO详解9:基于NIO的网络编程框架Netty.md) -* [Java网络编程与NIO详解10:深度解读Tomcat中的NIO模型](docs/java/network-programming/Java%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E4%B8%8ENIO%E8%AF%A6%E8%A7%A310%EF%BC%9A%E6%B7%B1%E5%BA%A6%E8%A7%A3%E8%AF%BBTomcat%E4%B8%AD%E7%9A%84NIO%E6%A8%A1%E5%9E%8B.md) +* [Java网络编程与NIO详解10:深度解读Tomcat中的NIO模型](docs/java/network-programming/Java网络编程与NIO详解10:深度解读Tomcat中的NIO模型.md) * [Java网络编程与NIO详解11:Tomcat中的Connector源码分析(NIO)](docs/java/network-programming/Java网络编程与NIO详解11:Tomcat中的Connector源码分析(NIO).md) ## 计算机基础 @@ -236,7 +236,7 @@ todo ### MySQL * [Mysql原理与实践总结](docs/database/Mysql原理与实践总结.md) * [重新学习Mysql数据库1:无废话MySQL入门](docs/database/重新学习Mysql数据库1:无废话MySQL入门.md) -* [重新学习Mysql数据库2:『浅入浅出』MySQL 和 InnoDB](docs/database/重新学习Mysql数据库2:%20『浅入浅出』MySQL%20和%20InnoDB.md) +* [重新学习Mysql数据库2:『浅入浅出』MySQL 和 InnoDB](docs/database/重新学习Mysql数据库2:『浅入浅出』MySQL 和 InnoDB.md) * [重新学习Mysql数据库3:Mysql存储引擎与数据存储原理](docs/database/重新学习Mysql数据库3:Mysql存储引擎与数据存储原理.md) * [重新学习Mysql数据库4:Mysql索引实现原理和相关数据结构算法](docs/database/重新学习Mysql数据库4:Mysql索引实现原理和相关数据结构算法.md) * [重新学习Mysql数据库5:根据MySQL索引原理进行分析与优化](docs/database/重新学习Mysql数据库5:根据MySQL索引原理进行分析与优化.md) @@ -309,21 +309,24 @@ todo * [搞懂分布式技术4:ZAB协议概述与选主流程详解 ](docs/distributed/practice/搞懂分布式技术4:ZAB协议概述与选主流程详解.md ) * [搞懂分布式技术5:Zookeeper的配置与集群管理实战](docs/distributed/practice/搞懂分布式技术5:Zookeeper的配置与集群管理实战.md) * [搞懂分布式技术6:Zookeeper典型应用场景及实践 ](docs/distributed/practice/搞懂分布式技术6:Zookeeper典型应用场景及实践.md ) -* [搞懂分布式技术7:负载均衡概念与主流方案](docs/distributed/practice/搞懂分布式技术7:负载均衡概念与主流方案.md) -* [搞懂分布式技术8:负载均衡原理剖析 ](docs/distributed/practice/搞懂分布式技术8:负载均衡原理剖析.md ) -* [搞懂分布式技术9:Nginx负载均衡原理与实践 ](docs/distributed/practice/搞懂分布式技术9:Nginx负载均衡原理与实践.md) + +[//]: # (* [搞懂分布式技术7:负载均衡概念与主流方案](docs/distributed/practice/搞懂分布式技术7:负载均衡概念与主流方案.md)) + +[//]: # (* [搞懂分布式技术8:负载均衡原理剖析 ](docs/distributed/practice/搞懂分布式技术8:负载均衡原理剖析.md )) + +[//]: # (* [搞懂分布式技术9:Nginx负载均衡原理与实践 ](docs/distributed/practice/搞懂分布式技术9:Nginx负载均衡原理与实践.md)) * [搞懂分布式技术10:LVS实现负载均衡的原理与实践 ](docs/distributed/practice/搞懂分布式技术10:LVS实现负载均衡的原理与实践.md ) * [搞懂分布式技术11:分布式session解决方案与一致性hash](docs/distributed/practice/搞懂分布式技术11:分布式session解决方案与一致性hash.md) * [搞懂分布式技术12:分布式ID生成方案 ](docs/distributed/practice/搞懂分布式技术12:分布式ID生成方案.md ) * [搞懂分布式技术13:缓存的那些事](docs/distributed/practice/搞懂分布式技术13:缓存的那些事.md) -* [搞懂分布式技术14:Spring Boot使用注解集成Redis缓存](docs/distributed/practice/搞懂分布式技术14:Spring%20Boot使用注解集成Redis缓存.md) +* [搞懂分布式技术14:Spring Boot使用注解集成Redis缓存](docs/distributed/practice/搞懂分布式技术14:Spring Boot使用注解集成Redis缓存.md) * [搞懂分布式技术15:缓存更新的套路 ](docs/distributed/practice/搞懂分布式技术15:缓存更新的套路.md ) * [搞懂分布式技术16:浅谈分布式锁的几种方案 ](docs/distributed/practice/搞懂分布式技术16:浅谈分布式锁的几种方案.md ) * [搞懂分布式技术17:浅析分布式事务 ](docs/distributed/practice/搞懂分布式技术17:浅析分布式事务.md ) * [搞懂分布式技术18:分布式事务常用解决方案 ](docs/distributed/practice/搞懂分布式技术18:分布式事务常用解决方案.md ) * [搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务 ](docs/distributed/practice/搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务.md ) * [搞懂分布式技术20:消息队列因何而生](docs/distributed/practice/搞懂分布式技术20:消息队列因何而生.md) -* [搞懂分布式技术21:浅谈分布式消息技术 Kafka ](docs/distributed/practice/搞懂分布式技术21:浅谈分布式消息技术%20Kafka.md ) +* [搞懂分布式技术21:浅谈分布式消息技术 Kafka ](docs/distributed/practice/搞懂分布式技术21:浅谈分布式消息技术 Kafka.md ) * [分布式理论总结](docs/distributed/分布式技术实践总结.md) ## 面试指南 diff --git "a/docs/algorithms/\345\211\221\346\214\207offer.md" "b/docs/cs/algorithms/\345\211\221\346\214\207offer.md" similarity index 100% rename from "docs/algorithms/\345\211\221\346\214\207offer.md" rename to "docs/cs/algorithms/\345\211\221\346\214\207offer.md" diff --git "a/docs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\345\255\246\344\271\240\346\200\273\347\273\223.md" "b/docs/cs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\345\255\246\344\271\240\346\200\273\347\273\223.md" similarity index 100% rename from "docs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\345\255\246\344\271\240\346\200\273\347\273\223.md" rename to "docs/cs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\345\255\246\344\271\240\346\200\273\347\273\223.md" diff --git "a/docs/operating-system/Linux\345\206\205\346\240\270\344\270\216\345\237\272\347\241\200\345\221\275\344\273\244\345\255\246\344\271\240\346\200\273\347\273\223.md" "b/docs/cs/operating-system/Linux\345\206\205\346\240\270\344\270\216\345\237\272\347\241\200\345\221\275\344\273\244\345\255\246\344\271\240\346\200\273\347\273\223.md" similarity index 100% rename from "docs/operating-system/Linux\345\206\205\346\240\270\344\270\216\345\237\272\347\241\200\345\221\275\344\273\244\345\255\246\344\271\240\346\200\273\347\273\223.md" rename to "docs/cs/operating-system/Linux\345\206\205\346\240\270\344\270\216\345\237\272\347\241\200\345\221\275\344\273\244\345\255\246\344\271\240\346\200\273\347\273\223.md" diff --git "a/docs/operating-system/\346\223\215\344\275\234\347\263\273\347\273\237\345\255\246\344\271\240\346\200\273\347\273\223.md" "b/docs/cs/operating-system/\346\223\215\344\275\234\347\263\273\347\273\237\345\255\246\344\271\240\346\200\273\347\273\223.md" similarity index 100% rename from "docs/operating-system/\346\223\215\344\275\234\347\263\273\347\273\237\345\255\246\344\271\240\346\200\273\347\273\223.md" rename to "docs/cs/operating-system/\346\223\215\344\275\234\347\263\273\347\273\237\345\255\246\344\271\240\346\200\273\347\273\223.md" diff --git "a/docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2201\357\274\232SpringMVC\346\246\202\350\277\260.md" "b/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2201\357\274\232SpringMVC\346\246\202\350\277\260.md" similarity index 100% rename from "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2201\357\274\232SpringMVC\346\246\202\350\277\260.md" rename to "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2201\357\274\232SpringMVC\346\246\202\350\277\260.md" diff --git "a/docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2202\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" "b/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2202\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" similarity index 100% rename from "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2202\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" rename to "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2202\357\274\232SpringMVC\350\256\276\350\256\241\347\220\206\345\277\265\344\270\216DispatcherServlet.md" diff --git "a/docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2203\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" "b/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2203\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" similarity index 100% rename from "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2203\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" rename to "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2203\357\274\232DispatcherServlet\347\232\204\345\210\235\345\247\213\345\214\226\344\270\216\350\257\267\346\261\202\350\275\254\345\217\221.md" diff --git "a/docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2204\357\274\232DispatcherServlet\345\246\202\344\275\225\346\211\276\345\210\260\346\255\243\347\241\256\347\232\204Controller.md" "b/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2204\357\274\232DispatcherServlet\345\246\202\344\275\225\346\211\276\345\210\260\346\255\243\347\241\256\347\232\204Controller.md" similarity index 100% rename from "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2204\357\274\232DispatcherServlet\345\246\202\344\275\225\346\211\276\345\210\260\346\255\243\347\241\256\347\232\204Controller.md" rename to "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2204\357\274\232DispatcherServlet\345\246\202\344\275\225\346\211\276\345\210\260\346\255\243\347\241\256\347\232\204Controller.md" diff --git "a/docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2206\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" "b/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2206\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" similarity index 100% rename from "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2206\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" rename to "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\210\206\346\236\2206\357\274\232SpringMVC\347\232\204\350\247\206\345\233\276\350\247\243\346\236\220\345\216\237\347\220\206.md" diff --git "a/docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\211\226\346\236\2205\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" "b/docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\211\226\346\236\2205\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" similarity index 100% rename from "docs/java-web/SSM/SpringMVC\346\272\220\347\240\201\345\211\226\346\236\2205\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" rename to "docs/java-web/springMVC/SpringMVC\346\272\220\347\240\201\345\211\226\346\236\2205\357\274\232\346\266\210\346\201\257\350\275\254\346\215\242\345\231\250HttpMessageConverter\344\270\216@ResponseBody\346\263\250\350\247\243.md" diff --git "a/docs/java-web/\346\267\261\345\205\245JavaWeb\346\212\200\346\234\257\344\270\226\347\225\21415\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md" "b/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\21415\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md" similarity index 100% rename from "docs/java-web/\346\267\261\345\205\245JavaWeb\346\212\200\346\234\257\344\270\226\347\225\21415\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md" rename to "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\21415\357\274\232\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\345\237\272\346\234\254\345\216\237\347\220\206.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java \350\257\273\345\206\231\351\224\201 ReentrantReadWriteLock \346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java \350\257\273\345\206\231\351\224\201 ReentrantReadWriteLock \346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java \350\257\273\345\206\231\351\224\201 ReentrantReadWriteLock \346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java \350\257\273\345\206\231\351\224\201 ReentrantReadWriteLock \346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273 Java \351\230\273\345\241\236\351\230\237\345\210\227 BlockingQueue.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273 Java \351\230\273\345\241\236\351\230\237\345\210\227 BlockingQueue.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273 Java \351\230\273\345\241\236\351\230\237\345\210\227 BlockingQueue.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273 Java \351\230\273\345\241\236\351\230\237\345\210\227 BlockingQueue.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273 java \347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273 java \347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273 java \347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273 java \347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java \344\270\255\347\232\204 HashMap \345\222\214 ConcurrentHashMap \345\205\250\350\247\243\346\236\220.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java \344\270\255\347\232\204 HashMap \345\222\214 ConcurrentHashMap \345\205\250\350\247\243\346\236\220.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java \344\270\255\347\232\204 HashMap \345\222\214 ConcurrentHashMap \345\205\250\350\247\243\346\236\220.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java \344\270\255\347\232\204 HashMap \345\222\214 ConcurrentHashMap \345\205\250\350\247\243\346\236\220.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22714\357\274\232JUC\344\270\255\345\270\270\347\224\250\347\232\204Unsafe\345\222\214Locksupport.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22714\357\274\232JUC\344\270\255\345\270\270\347\224\250\347\232\204Unsafe\345\222\214Locksupport.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\22714\357\274\232JUC\344\270\255\345\270\270\347\224\250\347\232\204Unsafe\345\222\214Locksupport.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22714\357\274\232JUC\344\270\255\345\270\270\347\224\250\347\232\204Unsafe\345\222\214Locksupport.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\347\274\226\347\250\213\346\214\207\345\215\22715\357\274\232Fork join\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22715\357\274\232Fork join\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\347\274\226\347\250\213\346\214\207\345\215\22715\357\274\232Fork join\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22715\357\274\232Fork join\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2271\357\274\232\345\271\266\345\217\221\345\237\272\347\241\200\344\270\216Java\345\244\232\347\272\277\347\250\213.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2271\357\274\232\345\271\266\345\217\221\345\237\272\347\241\200\344\270\216Java\345\244\232\347\272\277\347\250\213.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2271\357\274\232\345\271\266\345\217\221\345\237\272\347\241\200\344\270\216Java\345\244\232\347\272\277\347\250\213.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2271\357\274\232\345\271\266\345\217\221\345\237\272\347\241\200\344\270\216Java\345\244\232\347\272\277\347\250\213.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243Java\345\206\205\345\255\230\346\250\241\345\236\213JMM.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2273\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2273\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2273\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2273\357\274\232\345\271\266\345\217\221\344\270\211\345\244\247\351\227\256\351\242\230\344\270\216volatile\345\205\263\351\224\256\345\255\227\357\274\214CAS\346\223\215\344\275\234.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201 Lock\345\222\214synchronized.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201 Lock\345\222\214synchronized.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201 Lock\345\222\214synchronized.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201 Lock\345\222\214synchronized.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2275\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2275\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2275\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2275\357\274\232JMM\344\270\255\347\232\204final\345\205\263\351\224\256\345\255\227\350\247\243\346\236\220.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2276\357\274\232Java\345\206\205\345\255\230\346\250\241\345\236\213JMM\346\200\273\347\273\223.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2276\357\274\232Java\345\206\205\345\255\230\346\250\241\345\236\213JMM\346\200\273\347\273\223.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2276\357\274\232Java\345\206\205\345\255\230\346\250\241\345\236\213JMM\346\200\273\347\273\223.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2276\357\274\232Java\345\206\205\345\255\230\346\250\241\345\236\213JMM\346\200\273\347\273\223.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2277\357\274\232JUC\347\232\204\346\240\270\345\277\203\347\261\273AQS\350\257\246\350\247\243.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2277\357\274\232JUC\347\232\204\346\240\270\345\277\203\347\261\273AQS\350\257\246\350\247\243.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2277\357\274\232JUC\347\232\204\346\240\270\345\277\203\347\261\273AQS\350\257\246\350\247\243.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2277\357\274\232JUC\347\232\204\346\240\270\345\277\203\347\261\273AQS\350\257\246\350\247\243.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2278\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2278\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2278\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2278\357\274\232AQS\344\270\255\347\232\204\345\205\254\345\271\263\351\224\201\344\270\216\351\235\236\345\205\254\345\271\263\351\224\201\357\274\214Condtion.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2279\357\274\232AQS\345\205\261\344\272\253\346\250\241\345\274\217\344\270\216\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273\347\232\204\345\256\236\347\216\260.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2279\357\274\232AQS\345\205\261\344\272\253\346\250\241\345\274\217\344\270\216\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273\347\232\204\345\256\236\347\216\260.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\214\207\345\215\2279\357\274\232AQS\345\205\261\344\272\253\346\250\241\345\274\217\344\270\216\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273\347\232\204\345\256\236\347\216\260.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2279\357\274\232AQS\345\205\261\344\272\253\346\250\241\345\274\217\344\270\216\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273\347\232\204\345\256\236\347\216\260.md" diff --git "a/docs/java/currency/Java\345\271\266\345\217\221\346\200\273\347\273\223.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\346\200\273\347\273\223.md" similarity index 100% rename from "docs/java/currency/Java\345\271\266\345\217\221\346\200\273\347\273\223.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\346\200\273\347\273\223.md" diff --git "a/\346\234\200\347\273\210\346\225\210\346\236\234.md" "b/\346\234\200\347\273\210\346\225\210\346\236\234.md" index dc34623..0cf3d7d 100644 --- "a/\346\234\200\347\273\210\346\225\210\346\236\234.md" +++ "b/\346\234\200\347\273\210\346\225\210\346\236\234.md" @@ -52,7 +52,7 @@ ### 并发 -* [1 面向对象基础](docs/java/currency/Java并发总结.md) +* [1 面向对象基础](docs/java/concurrency/Java并发编程学习总结.md) ### JVM From 2831f9c502018b322da022c42558248468326e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=B0=8F=E6=96=9C?= <362294931@qq.com> Date: Mon, 3 Apr 2023 12:24:40 +0800 Subject: [PATCH 13/90] modify catalog --- ReadMe.md | 38 +++++++++---------- ...272\343\200\217MySQL\345\222\214InnoDB.md" | 0 ...6\210\220Redis\347\274\223\345\255\230.md" | 0 ...6\201\257\346\212\200\346\234\257Kafka.md" | 0 ...20\347\240\201\345\210\206\346\236\220.md" | 0 ...6\351\230\237\345\210\227BlockingQueue.md" | 0 ...20\347\240\201\345\256\236\347\216\260.md" | 0 ...ap\345\205\250\350\247\243\346\236\220.md" | 0 ...27\346\263\225\345\211\226\346\236\220.md" | 0 ...51\224\201Lock\345\222\214synchronized.md" | 0 ...32\344\277\241\346\234\272\345\210\266.md" | 0 ...67\346\261\202\346\250\241\345\236\213.md" | 0 ...26\347\250\213\346\250\241\345\236\213.md" | 0 ...343\200\201Channel\345\222\214Selector.md" | 0 ...\345\222\214\345\274\202\346\255\245IO.md" | 0 ...37\347\220\206\350\257\246\350\247\243.md" | 0 ...36\347\216\260\345\216\237\347\220\206.md" | 0 ...46\236\220mmap\345\222\214DirectBuffer.md" | 0 ...7\250\213\346\241\206\346\236\266Netty.md" | 0 19 files changed, 19 insertions(+), 19 deletions(-) rename "docs/database/\351\207\215\346\226\260\345\255\246\344\271\240Mysql\346\225\260\346\215\256\345\272\2232\357\274\232\343\200\216\346\265\205\345\205\245\346\265\205\345\207\272\343\200\217MySQL \345\222\214 InnoDB.md" => "docs/database/\351\207\215\346\226\260\345\255\246\344\271\240Mysql\346\225\260\346\215\256\345\272\2232\357\274\232\343\200\216\346\265\205\345\205\245\346\265\205\345\207\272\343\200\217MySQL\345\222\214InnoDB.md" (100%) rename "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232Spring Boot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232SpringBoot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" (100%) rename "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257 Kafka.md" => "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257Kafka.md" (100%) rename "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java \350\257\273\345\206\231\351\224\201 ReentrantReadWriteLock \346\272\220\347\240\201\345\210\206\346\236\220.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java\350\257\273\345\206\231\351\224\201ReentrantReadWriteLock\346\272\220\347\240\201\345\210\206\346\236\220.md" (100%) rename "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273 Java \351\230\273\345\241\236\351\230\237\345\210\227 BlockingQueue.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273Java\351\230\273\345\241\236\351\230\237\345\210\227BlockingQueue.md" (100%) rename "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273 java \347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Java\347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" (100%) rename "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java \344\270\255\347\232\204 HashMap \345\222\214 ConcurrentHashMap \345\205\250\350\247\243\346\236\220.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java\344\270\255\347\232\204HashMap\345\222\214ConcurrentHashMap\345\205\250\350\247\243\346\236\220.md" (100%) rename "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22715\357\274\232Fork join\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22715\357\274\232ForkJoin\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" (100%) rename "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201 Lock\345\222\214synchronized.md" => "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201Lock\345\222\214synchronized.md" (100%) rename "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2431\357\274\232JAVA \344\270\255\345\216\237\347\224\237\347\232\204 socket \351\200\232\344\277\241\346\234\272\345\210\266.md" => "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2431\357\274\232JAVA\344\270\255\345\216\237\347\224\237\347\232\204socket\351\200\232\344\277\241\346\234\272\345\210\266.md" (100%) rename "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2432\357\274\232JAVA NIO \344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md" => "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2432\357\274\232JavaNIO\344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md" (100%) rename "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2433\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md" => "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2433\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md" (100%) rename "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2434\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel \345\222\214 Selector.md" => "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2434\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel\345\222\214Selector.md" (100%) rename "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2435\357\274\232Java \351\235\236\351\230\273\345\241\236 IO \345\222\214\345\274\202\346\255\245 IO.md" => "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2435\357\274\232Java\351\235\236\351\230\273\345\241\236IO\345\222\214\345\274\202\346\255\245IO.md" (100%) rename "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2436\357\274\232Linux epoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" => "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2436\357\274\232LinuxEpoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" (100%) rename "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2437\357\274\232\346\265\205\350\260\210 Linux \344\270\255NIO Selector \347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" => "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2437\357\274\232\346\265\205\350\260\210Linux\344\270\255Selector\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" (100%) rename "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2438\357\274\232\346\265\205\346\236\220mmap\345\222\214Direct Buffer.md" => "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2438\357\274\232\346\265\205\346\236\220mmap\345\222\214DirectBuffer.md" (100%) rename "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2439\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md" => "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2439\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md" (100%) diff --git a/ReadMe.md b/ReadMe.md index f63c941..54c9ba8 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -162,18 +162,18 @@ todo * [Java并发指南1:并发基础与Java多线程](docs/java/concurrency/Java并发指南1:并发基础与Java多线程.md) * [Java并发指南2:深入理解Java内存模型JMM](docs/java/concurrency/Java并发指南2:深入理解Java内存模型JMM.md) * [Java并发指南3:并发三大问题与volatile关键字,CAS操作](docs/java/concurrency/Java并发指南3:并发三大问题与volatile关键字,CAS操作.md) -* [Java并发指南4:Java中的锁 Lock和synchronized](docs/java/concurrency/Java并发指南4:Java中的锁%20Lock和synchronized.md) +* [Java并发指南4:Java中的锁Lock和synchronized](docs/java/concurrency/Java并发指南4:Java中的锁Lock和synchronized.md) * [Java并发指南5:JMM中的final关键字解析](docs/java/concurrency/Java并发指南5:JMM中的final关键字解析.md) * [Java并发指南6:Java内存模型JMM总结](docs/java/concurrency/Java并发指南6:Java内存模型JMM总结.md) * [Java并发指南7:JUC的核心类AQS详解](docs/java/concurrency/Java并发指南7:JUC的核心类AQS详解.md) * [Java并发指南8:AQS中的公平锁与非公平锁,Condtion](docs/java/concurrency/Java并发指南8:AQS中的公平锁与非公平锁,Condtion.md) * [Java并发指南9:AQS共享模式与并发工具类的实现](docs/java/concurrency/Java并发指南9:AQS共享模式与并发工具类的实现.md) -* [Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析](docs/java/concurrency/Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析.md) -* [Java并发指南11:解读 Java 阻塞队列 BlockingQueue](docs/java/concurrency/Java并发指南11:解读 Java 阻塞队列 BlockingQueue.md) -* [Java并发指南12:深度解读 java 线程池设计思想及源码实现](docs/java/concurrency/Java并发指南12:深度解读 java 线程池设计思想及源码实现.md) -* [Java并发指南13:Java 中的 HashMap 和 ConcurrentHashMap 全解析](docs/java/concurrency/Java并发指南13:Java 中的 HashMap 和 ConcurrentHashMap 全解析.md) +* [Java并发指南10:Java读写锁ReentrantReadWriteLock源码分析](docs/java/concurrency/Java并发指南10:Java读写锁ReentrantReadWriteLock源码分析.md) +* [Java并发指南11:解读Java阻塞队列BlockingQueue](docs/java/concurrency/Java并发指南11:解读Java阻塞队列BlockingQueue.md) +* [Java并发指南12:深度解读java线程池设计思想及源码实现](docs/java/concurrency/Java并发指南12:深度解读Java线程池设计思想及源码实现.md) +* [Java并发指南13:Java中的HashMap和ConcurrentHashMap全解析](docs/java/concurrency/Java并发指南13:Java中的HashMap和ConcurrentHashMap全解析.md) * [Java并发指南14:JUC中常用的Unsafe和Locksupport](docs/java/concurrency/Java并发指南14:JUC中常用的Unsafe和Locksupport.md) -* [Java并发指南15:Fork join并发框架与工作窃取算法剖析](docs/java/concurrency/Java并发指南15:Fork join并发框架与工作窃取算法剖析.md) +* [Java并发指南15:ForkJoin并发框架与工作窃取算法剖析](docs/java/concurrency/Java并发指南15:ForkJoin并发框架与工作窃取算法剖析.md) * [Java并发编程学习总结](docs/java/concurrency/Java并发编程学习总结.md) ### JVM @@ -196,16 +196,16 @@ todo ### Java网络编程 -* [Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制](docs/java/network-programming/Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制.md) -* [Java网络编程与NIO详解2:JAVA NIO 一步步构建IO多路复用的请求模型](docs/java/network-programming/Java网络编程与NIO详解2:JAVA NIO 一步步构建IO多路复用的请求模型.md) -* [Java网络编程和NIO详解3:IO模型与Java网络编程模型](docs/java/network-programming/Java网络编程和NIO详解3:IO模型与Java网络编程模型.md) -* [Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector](docs/java/network-programming/Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector.md) -* [Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO](docs/java/network-programming/Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO.md) -* [Java网络编程和NIO详解6:Linux epoll实现原理详解](docs/java/network-programming/Java网络编程和NIO详解6:Linux%20epoll实现原理详解.md) -* [Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理](docs/java/network-programming/Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理.md) -* [Java网络编程与NIO详解8:浅析mmap和Direct Buffer](docs/java/network-programming/Java网络编程与NIO详解8:浅析mmap和Direct Buffer.md) -* [Java网络编程和NIO详解9:基于NIO的网络编程框架Netty](docs/java/network-programming/Java网络编程和NIO详解9:基于NIO的网络编程框架Netty.md) -* [Java网络编程与NIO详解10:深度解读Tomcat中的NIO模型](docs/java/network-programming/Java网络编程与NIO详解10:深度解读Tomcat中的NIO模型.md) +* [Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制](docs/java/network-programming/Java网络编程与NIO详解1:JAVA中原生的socket通信机制.md) +* [Java网络编程与NIO详解2:JAVA NIO 一步步构建IO多路复用的请求模型](docs/java/network-programming/Java网络编程与NIO详解2:JavaNIO一步步构建IO多路复用的请求模型.md) +* [Java网络编程和NIO详解3:IO模型与Java网络编程模型](docs/java/network-programming/Java网络编程与NIO详解3:IO模型与Java网络编程模型.md) +* [Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector](docs/java/network-programming/Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel和Selector.md) +* [Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO](docs/java/network-programming/Java网络编程与NIO详解5:Java非阻塞IO和异步IO.md) +* [Java网络编程与NIO详解6:LinuxEpoll实现原理详解](docs/java/network-programming/Java网络编程与NIO详解6:LinuxEpoll实现原理详解.md.md) +* [Java网络编程与NIO详解7:浅谈Linux中Selector的实现原理](docs/java/network-programming/Java网络编程与NIO详解7:浅谈Linux中Selector的实现原理.md) +* [Java网络编程与NIO详解8:浅析mmap和DirectBuffer](docs/java/network-programming/Java网络编程与NIO详解8:浅析mmap和DirectBuffer.md) +* [Java网络编程与NIO详解9:基于NIO的网络编程框架Netty](docs/java/network-programming/Java网络编程与NIO详解9:基于NIO的网络编程框架Netty.md) +* [Java网络编程与NIO详解10:Java网络编程与NIO详解10](docs/java/network-programming/Java网络编程与NIO详解10:深度解读Tomcat中的NIO模型.md) * [Java网络编程与NIO详解11:Tomcat中的Connector源码分析(NIO)](docs/java/network-programming/Java网络编程与NIO详解11:Tomcat中的Connector源码分析(NIO).md) ## 计算机基础 @@ -236,7 +236,7 @@ todo ### MySQL * [Mysql原理与实践总结](docs/database/Mysql原理与实践总结.md) * [重新学习Mysql数据库1:无废话MySQL入门](docs/database/重新学习Mysql数据库1:无废话MySQL入门.md) -* [重新学习Mysql数据库2:『浅入浅出』MySQL 和 InnoDB](docs/database/重新学习Mysql数据库2:『浅入浅出』MySQL 和 InnoDB.md) +* [重新学习Mysql数据库2:『浅入浅出』MySQL和InnoDB](docs/database/重新学习Mysql数据库2:『浅入浅出』MySQL和InnoDB.md) * [重新学习Mysql数据库3:Mysql存储引擎与数据存储原理](docs/database/重新学习Mysql数据库3:Mysql存储引擎与数据存储原理.md) * [重新学习Mysql数据库4:Mysql索引实现原理和相关数据结构算法](docs/database/重新学习Mysql数据库4:Mysql索引实现原理和相关数据结构算法.md) * [重新学习Mysql数据库5:根据MySQL索引原理进行分析与优化](docs/database/重新学习Mysql数据库5:根据MySQL索引原理进行分析与优化.md) @@ -319,14 +319,14 @@ todo * [搞懂分布式技术11:分布式session解决方案与一致性hash](docs/distributed/practice/搞懂分布式技术11:分布式session解决方案与一致性hash.md) * [搞懂分布式技术12:分布式ID生成方案 ](docs/distributed/practice/搞懂分布式技术12:分布式ID生成方案.md ) * [搞懂分布式技术13:缓存的那些事](docs/distributed/practice/搞懂分布式技术13:缓存的那些事.md) -* [搞懂分布式技术14:Spring Boot使用注解集成Redis缓存](docs/distributed/practice/搞懂分布式技术14:Spring Boot使用注解集成Redis缓存.md) +* [搞懂分布式技术14:SpringBoot使用注解集成Redis缓存](docs/distributed/practice/搞懂分布式技术14:SpringBoot使用注解集成Redis缓存.md) * [搞懂分布式技术15:缓存更新的套路 ](docs/distributed/practice/搞懂分布式技术15:缓存更新的套路.md ) * [搞懂分布式技术16:浅谈分布式锁的几种方案 ](docs/distributed/practice/搞懂分布式技术16:浅谈分布式锁的几种方案.md ) * [搞懂分布式技术17:浅析分布式事务 ](docs/distributed/practice/搞懂分布式技术17:浅析分布式事务.md ) * [搞懂分布式技术18:分布式事务常用解决方案 ](docs/distributed/practice/搞懂分布式技术18:分布式事务常用解决方案.md ) * [搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务 ](docs/distributed/practice/搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务.md ) * [搞懂分布式技术20:消息队列因何而生](docs/distributed/practice/搞懂分布式技术20:消息队列因何而生.md) -* [搞懂分布式技术21:浅谈分布式消息技术 Kafka ](docs/distributed/practice/搞懂分布式技术21:浅谈分布式消息技术 Kafka.md ) +* [搞懂分布式技术21:浅谈分布式消息技术Kafka](docs/distributed/practice/搞懂分布式技术21:浅谈分布式消息技术Kafka.md ) * [分布式理论总结](docs/distributed/分布式技术实践总结.md) ## 面试指南 diff --git "a/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240Mysql\346\225\260\346\215\256\345\272\2232\357\274\232\343\200\216\346\265\205\345\205\245\346\265\205\345\207\272\343\200\217MySQL \345\222\214 InnoDB.md" "b/docs/database/\351\207\215\346\226\260\345\255\246\344\271\240Mysql\346\225\260\346\215\256\345\272\2232\357\274\232\343\200\216\346\265\205\345\205\245\346\265\205\345\207\272\343\200\217MySQL\345\222\214InnoDB.md" similarity index 100% rename from "docs/database/\351\207\215\346\226\260\345\255\246\344\271\240Mysql\346\225\260\346\215\256\345\272\2232\357\274\232\343\200\216\346\265\205\345\205\245\346\265\205\345\207\272\343\200\217MySQL \345\222\214 InnoDB.md" rename to "docs/database/\351\207\215\346\226\260\345\255\246\344\271\240Mysql\346\225\260\346\215\256\345\272\2232\357\274\232\343\200\216\346\265\205\345\205\245\346\265\205\345\207\272\343\200\217MySQL\345\222\214InnoDB.md" diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232Spring Boot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232SpringBoot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" similarity index 100% rename from "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232Spring Boot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25714\357\274\232SpringBoot\344\275\277\347\224\250\346\263\250\350\247\243\351\233\206\346\210\220Redis\347\274\223\345\255\230.md" diff --git "a/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257 Kafka.md" "b/docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257Kafka.md" similarity index 100% rename from "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257 Kafka.md" rename to "docs/distributed/practice/\346\220\236\346\207\202\345\210\206\345\270\203\345\274\217\346\212\200\346\234\25721\357\274\232\346\265\205\350\260\210\345\210\206\345\270\203\345\274\217\346\266\210\346\201\257\346\212\200\346\234\257Kafka.md" diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java \350\257\273\345\206\231\351\224\201 ReentrantReadWriteLock \346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java\350\257\273\345\206\231\351\224\201ReentrantReadWriteLock\346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java \350\257\273\345\206\231\351\224\201 ReentrantReadWriteLock \346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22710\357\274\232Java\350\257\273\345\206\231\351\224\201ReentrantReadWriteLock\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273 Java \351\230\273\345\241\236\351\230\237\345\210\227 BlockingQueue.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273Java\351\230\273\345\241\236\351\230\237\345\210\227BlockingQueue.md" similarity index 100% rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273 Java \351\230\273\345\241\236\351\230\237\345\210\227 BlockingQueue.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22711\357\274\232\350\247\243\350\257\273Java\351\230\273\345\241\236\351\230\237\345\210\227BlockingQueue.md" diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273 java \347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Java\347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" similarity index 100% rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273 java \347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22712\357\274\232\346\267\261\345\272\246\350\247\243\350\257\273Java\347\272\277\347\250\213\346\261\240\350\256\276\350\256\241\346\200\235\346\203\263\345\217\212\346\272\220\347\240\201\345\256\236\347\216\260.md" diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java \344\270\255\347\232\204 HashMap \345\222\214 ConcurrentHashMap \345\205\250\350\247\243\346\236\220.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java\344\270\255\347\232\204HashMap\345\222\214ConcurrentHashMap\345\205\250\350\247\243\346\236\220.md" similarity index 100% rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java \344\270\255\347\232\204 HashMap \345\222\214 ConcurrentHashMap \345\205\250\350\247\243\346\236\220.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22713\357\274\232Java\344\270\255\347\232\204HashMap\345\222\214ConcurrentHashMap\345\205\250\350\247\243\346\236\220.md" diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22715\357\274\232Fork join\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22715\357\274\232ForkJoin\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" similarity index 100% rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22715\357\274\232Fork join\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\22715\357\274\232ForkJoin\345\271\266\345\217\221\346\241\206\346\236\266\344\270\216\345\267\245\344\275\234\347\252\203\345\217\226\347\256\227\346\263\225\345\211\226\346\236\220.md" diff --git "a/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201 Lock\345\222\214synchronized.md" "b/docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201Lock\345\222\214synchronized.md" similarity index 100% rename from "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201 Lock\345\222\214synchronized.md" rename to "docs/java/concurrency/Java\345\271\266\345\217\221\346\214\207\345\215\2274\357\274\232Java\344\270\255\347\232\204\351\224\201Lock\345\222\214synchronized.md" diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2431\357\274\232JAVA \344\270\255\345\216\237\347\224\237\347\232\204 socket \351\200\232\344\277\241\346\234\272\345\210\266.md" "b/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2431\357\274\232JAVA\344\270\255\345\216\237\347\224\237\347\232\204socket\351\200\232\344\277\241\346\234\272\345\210\266.md" similarity index 100% rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2431\357\274\232JAVA \344\270\255\345\216\237\347\224\237\347\232\204 socket \351\200\232\344\277\241\346\234\272\345\210\266.md" rename to "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2431\357\274\232JAVA\344\270\255\345\216\237\347\224\237\347\232\204socket\351\200\232\344\277\241\346\234\272\345\210\266.md" diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2432\357\274\232JAVA NIO \344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md" "b/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2432\357\274\232JavaNIO\344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md" similarity index 100% rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2432\357\274\232JAVA NIO \344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md" rename to "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2432\357\274\232JavaNIO\344\270\200\346\255\245\346\255\245\346\236\204\345\273\272IO\345\244\232\350\267\257\345\244\215\347\224\250\347\232\204\350\257\267\346\261\202\346\250\241\345\236\213.md" diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2433\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md" "b/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2433\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md" similarity index 100% rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2433\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md" rename to "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2433\357\274\232IO\346\250\241\345\236\213\344\270\216Java\347\275\221\347\273\234\347\274\226\347\250\213\346\250\241\345\236\213.md" diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2434\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel \345\222\214 Selector.md" "b/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2434\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel\345\222\214Selector.md" similarity index 100% rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2434\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel \345\222\214 Selector.md" rename to "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2434\357\274\232\346\265\205\346\236\220NIO\345\214\205\344\270\255\347\232\204Buffer\343\200\201Channel\345\222\214Selector.md" diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2435\357\274\232Java \351\235\236\351\230\273\345\241\236 IO \345\222\214\345\274\202\346\255\245 IO.md" "b/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2435\357\274\232Java\351\235\236\351\230\273\345\241\236IO\345\222\214\345\274\202\346\255\245IO.md" similarity index 100% rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2435\357\274\232Java \351\235\236\351\230\273\345\241\236 IO \345\222\214\345\274\202\346\255\245 IO.md" rename to "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2435\357\274\232Java\351\235\236\351\230\273\345\241\236IO\345\222\214\345\274\202\346\255\245IO.md" diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2436\357\274\232Linux epoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" "b/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2436\357\274\232LinuxEpoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" similarity index 100% rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2436\357\274\232Linux epoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" rename to "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2436\357\274\232LinuxEpoll\345\256\236\347\216\260\345\216\237\347\220\206\350\257\246\350\247\243.md" diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2437\357\274\232\346\265\205\350\260\210 Linux \344\270\255NIO Selector \347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" "b/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2437\357\274\232\346\265\205\350\260\210Linux\344\270\255Selector\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" similarity index 100% rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2437\357\274\232\346\265\205\350\260\210 Linux \344\270\255NIO Selector \347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" rename to "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2437\357\274\232\346\265\205\350\260\210Linux\344\270\255Selector\347\232\204\345\256\236\347\216\260\345\216\237\347\220\206.md" diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2438\357\274\232\346\265\205\346\236\220mmap\345\222\214Direct Buffer.md" "b/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2438\357\274\232\346\265\205\346\236\220mmap\345\222\214DirectBuffer.md" similarity index 100% rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2438\357\274\232\346\265\205\346\236\220mmap\345\222\214Direct Buffer.md" rename to "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2438\357\274\232\346\265\205\346\236\220mmap\345\222\214DirectBuffer.md" diff --git "a/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2439\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md" "b/docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2439\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md" similarity index 100% rename from "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\345\222\214NIO\350\257\246\350\247\2439\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md" rename to "docs/java/network-programming/Java\347\275\221\347\273\234\347\274\226\347\250\213\344\270\216NIO\350\257\246\350\247\2439\357\274\232\345\237\272\344\272\216NIO\347\232\204\347\275\221\347\273\234\347\274\226\347\250\213\346\241\206\346\236\266Netty.md" From cfcc5bd7e56daccf03c1929491bf2735e907baac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=B0=8F=E6=96=9C?= <362294931@qq.com> Date: Mon, 3 Apr 2023 20:28:04 +0800 Subject: [PATCH 14/90] use oss picgo --- ReadMe.md | 30 +- ...70\345\277\203\346\265\201\347\250\213.md" | 0 ...40\350\275\275\350\277\207\347\250\213.md" | 0 ...4\232SpringAOP\346\246\202\350\277\260.md" | 0 ...45\350\257\206\346\270\205\345\215\225.md" | 0 ...37\347\220\206\350\257\246\350\247\243.md" | 0 ...71\350\261\241\345\237\272\347\241\200.md" | 124 +-- ...60\346\215\256\347\261\273\345\236\213.md" | 169 ++-- ...14\345\214\205\350\243\205\347\261\273.md" | 923 +++++++++--------- ...56\345\255\227\347\211\271\346\200\247.md" | 54 +- ...va\347\261\273\345\222\214\345\214\205.md" | 263 +++-- ...17\347\232\204\347\247\230\345\257\206.md" | 422 ++++---- ...43\346\236\220\345\256\236\350\267\265.md" | 0 ...14\346\234\237\344\274\230\345\214\226.md" | 0 14 files changed, 918 insertions(+), 1067 deletions(-) rename "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2202\357\274\232\345\210\235\346\216\242Spring IOC\346\240\270\345\277\203\346\265\201\347\250\213.md" => "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2202\357\274\232\345\210\235\346\216\242SpringIOC\346\240\270\345\277\203\346\265\201\347\250\213.md" (100%) rename "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2203\357\274\232Spring IOC\345\256\271\345\231\250\347\232\204\345\212\240\350\275\275\350\277\207\347\250\213.md" => "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2203\357\274\232SpringIOC\345\256\271\345\231\250\347\232\204\345\212\240\350\275\275\350\277\207\347\250\213.md" (100%) rename "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2206\357\274\232Spring AOP\346\246\202\350\277\260.md" => "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2206\357\274\232SpringAOP\346\246\202\350\277\260.md" (100%) rename "docs/java-web/Spring/\347\273\231\344\275\240\344\270\200\344\273\275Spring Boot\347\237\245\350\257\206\346\270\205\345\215\225.md" => "docs/java-web/Spring/\347\273\231\344\275\240\344\270\200\344\273\275SpringBoot\347\237\245\350\257\206\346\270\205\345\215\225.md" (100%) rename "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\2144\357\274\232Servlet \345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md" => "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\2144\357\274\232Servlet\345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md" (100%) rename "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2724\357\274\232Java class\344\273\213\347\273\215\344\270\216\350\247\243\346\236\220\345\256\236\350\267\265.md" => "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2724\357\274\232Java\345\255\227\350\212\202\347\240\201\344\273\213\347\273\215\344\270\216\350\247\243\346\236\220\345\256\236\350\267\265.md" (100%) rename "docs/java/jvm/\346\267\261\345\205\245\344\272\206\350\247\243JVM\350\231\232\346\213\237\346\234\2728\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" => "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2728\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" (100%) diff --git a/ReadMe.md b/ReadMe.md index 54c9ba8..9e8a447 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -67,6 +67,7 @@ * [2、Java基本数据类型](docs/java/basic/2、Java基本数据类型.md) * [3、string和包装类](docs/java/basic/3、string和包装类.md) * [4、final关键字特性](docs/java/basic/4、final关键字特性.md) + * [5、Java类和包](docs/java/basic/5、Java类和包.md) * [6、抽象类和接口](docs/java/basic/6、抽象类和接口.md) * [7、代码块和代码执行顺序](docs/java/basic/7、代码块和代码执行顺序.md) @@ -112,7 +113,7 @@ * [走进JavaWeb技术世界1:JavaWeb的由来和基础知识](docs/java-web/走进JavaWeb技术世界1:JavaWeb的由来和基础知识.md) * [走进JavaWeb技术世界2:JSP与Servlet的曾经与现在](docs/java-web/走进JavaWeb技术世界2:JSP与Servlet的曾经与现在.md) * [走进JavaWeb技术世界3:JDBC的进化与连接池技术](docs/java-web/走进JavaWeb技术世界3:JDBC的进化与连接池技术.md) -* [走进JavaWeb技术世界4:Servlet 工作原理详解](docs/java-web/走进JavaWeb技术世界4:Servlet%20工作原理详解.md) +* [走进JavaWeb技术世界4:Servlet工作原理详解](docs/java-web/走进JavaWeb技术世界4:Servlet工作原理详解.md) * [走进JavaWeb技术世界5:初探Tomcat的HTTP请求过程](docs/java-web/走进JavaWeb技术世界5:初探Tomcat的HTTP请求过程.md) * [走进JavaWeb技术世界6:Tomcat5总体架构剖析](docs/java-web/走进JavaWeb技术世界6:Tomcat5总体架构剖析.md) * [走进JavaWeb技术世界7:Tomcat和其他WEB容器的区别](docs/java-web/走进JavaWeb技术世界7:Tomcat和其他WEB容器的区别.md) @@ -182,11 +183,11 @@ todo * [深入理解JVM虚拟机1:JVM内存的结构与消失的永久代](docs/java/jvm/深入理解JVM虚拟机1:JVM内存的结构与消失的永久代.md) * [深入理解JVM虚拟机2:JVM垃圾回收基本原理和算法](docs/java/jvm/深入理解JVM虚拟机2:JVM垃圾回收基本原理和算法.md) * [深入理解JVM虚拟机3:垃圾回收器详解](docs/java/jvm/深入理解JVM虚拟机3:垃圾回收器详解.md) -* [深入理解JVM虚拟机4:Javaclass介绍与解析实践](docs/java/jvm/深入理解JVM虚拟机4:Javaclass介绍与解析实践.md) +* [深入理解JVM虚拟机4:Javaclass介绍与解析实践](docs/java/jvm/深入理解JVM虚拟机4:Java字节码介绍与解析实践.md) * [深入理解JVM虚拟机5:虚拟机字节码执行引擎](docs/java/jvm/深入理解JVM虚拟机5:虚拟机字节码执行引擎.md) * [深入理解JVM虚拟机6:深入理解JVM类加载机制](docs/java/jvm/深入理解JVM虚拟机6:深入理解JVM类加载机制.md) * [深入理解JVM虚拟机7:JNDI,OSGI,Tomcat类加载器实现](docs/java/jvm/深入理解JVM虚拟机7:JNDI,OSGI,Tomcat类加载器实现.md) -* [深入了解JVM虚拟机8:Java的编译期优化与运行期优化](docs/java/jvm/深入了解JVM虚拟机8:Java的编译期优化与运行期优化.md) +* [深入了解JVM虚拟机8:Java的编译期优化与运行期优化](docs/java/jvm/深入理解JVM虚拟机8:Java的编译期优化与运行期优化.md) * [深入理解JVM虚拟机9:JVM监控工具与诊断实践](docs/java/jvm/深入理解JVM虚拟机9:JVM监控工具与诊断实践.md) * [深入理解JVM虚拟机10:JVM常用参数以及调优实践](docs/java/jvm/深入理解JVM虚拟机10:JVM常用参数以及调优实践.md) * [深入理解JVM虚拟机11:Java内存异常原理与实践](docs/java/jvm/深入理解JVM虚拟机11:Java内存异常原理与实践.md) @@ -199,8 +200,8 @@ todo * [Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制](docs/java/network-programming/Java网络编程与NIO详解1:JAVA中原生的socket通信机制.md) * [Java网络编程与NIO详解2:JAVA NIO 一步步构建IO多路复用的请求模型](docs/java/network-programming/Java网络编程与NIO详解2:JavaNIO一步步构建IO多路复用的请求模型.md) * [Java网络编程和NIO详解3:IO模型与Java网络编程模型](docs/java/network-programming/Java网络编程与NIO详解3:IO模型与Java网络编程模型.md) -* [Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector](docs/java/network-programming/Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel和Selector.md) -* [Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO](docs/java/network-programming/Java网络编程与NIO详解5:Java非阻塞IO和异步IO.md) +* [Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel和Selector](docs/java/network-programming/Java网络编程与NIO详解4:浅析NIO包中的Buffer、Channel和Selector.md) +* [Java网络编程和NIO详解5:Java非阻塞IO和异步IO](docs/java/network-programming/Java网络编程与NIO详解5:Java非阻塞IO和异步IO.md) * [Java网络编程与NIO详解6:LinuxEpoll实现原理详解](docs/java/network-programming/Java网络编程与NIO详解6:LinuxEpoll实现原理详解.md.md) * [Java网络编程与NIO详解7:浅谈Linux中Selector的实现原理](docs/java/network-programming/Java网络编程与NIO详解7:浅谈Linux中Selector的实现原理.md) * [Java网络编程与NIO详解8:浅析mmap和DirectBuffer](docs/java/network-programming/Java网络编程与NIO详解8:浅析mmap和DirectBuffer.md) @@ -255,7 +256,7 @@ todo ### Redis * [Redis原理与实践总结](docs/cache/Redis原理与实践总结.md) * [探索Redis设计与实现开篇:什么是Redis](docs/cache/探索Redis设计与实现开篇:什么是Redis.md) -* [探索Redis设计与实现1:Redis 的基础数据结构概览](docs/cache/探索Redis设计与实现1:Redis%20的基础数据结构概览.md) +* [探索Redis设计与实现1:Redis的基础数据结构概览](docs/cache/探索Redis设计与实现1:Redis的基础数据结构概览.md) * [探索Redis设计与实现2:Redis内部数据结构详解——dict](docs/cache/探索Redis设计与实现2:Redis内部数据结构详解——dict.md) * [探索Redis设计与实现3:Redis内部数据结构详解——sds](docs/cache/探索Redis设计与实现3:Redis内部数据结构详解——sds.md) * [探索Redis设计与实现4:Redis内部数据结构详解——ziplist](docs/cache/探索Redis设计与实现4:Redis内部数据结构详解——ziplist.md) @@ -292,9 +293,9 @@ todo ## 分布式 ### 理论 -* [分布式系统理论基础1: 一致性、2PC和3PC ](docs/distributed/basic/分布式系统理论基础1:%20一致性、2PC和3PC.md) -* [分布式系统理论基础2:CAP ](docs/distributed/basic/分布式系统理论基础2%20:CAP.md) -* [分布式系统理论基础3: 时间、时钟和事件顺序](docs/distributed/basic/分布式系统理论基础3:%20时间、时钟和事件顺序.md) +* [分布式系统理论基础1:一致性、2PC和3PC ](docs/distributed/basic/分布式系统理论基础1:一致性、2PC和3PC.md) +* [分布式系统理论基础2:CAP ](docs/distributed/basic/分布式系统理论基础2:CAP.md) +* [分布式系统理论基础3:时间、时钟和事件顺序](docs/distributed/basic/分布式系统理论基础3:时间、时钟和事件顺序.md) * [分布式系统理论基础4:Paxos](docs/distributed/basic/分布式系统理论基础4:Paxos.md) * [分布式系统理论基础5:选举、多数派和租约](docs/distributed/basic/分布式系统理论基础5:选举、多数派和租约.md) * [分布式系统理论基础6:Raft、Zab ](docs/distributed/basic/分布式系统理论基础6:Raft、Zab.md) @@ -302,13 +303,14 @@ todo * [分布式系统理论基础8:zookeeper分布式协调服务 ](docs/distributed/basic/分布式系统理论基础8:zookeeper分布式协调服务.md) * [分布式技术实践总结](docs/distributed/分布式理论总结.md) + ### 技术 -* [搞懂分布式技术1:分布式系统的一些基本概念 ](docs/distributed/practice/搞懂分布式技术1:分布式系统的一些基本概念.md ) +* [搞懂分布式技术1:分布式系统的一些基本概念](docs/distributed/practice/搞懂分布式技术1:分布式系统的一些基本概念.md ) * [搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法](docs/distributed/practice/搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法.md) -* [搞懂分布式技术3:初探分布式协调服务zookeeper ](docs/distributed/practice/搞懂分布式技术3:初探分布式协调服务zookeeper.md ) -* [搞懂分布式技术4:ZAB协议概述与选主流程详解 ](docs/distributed/practice/搞懂分布式技术4:ZAB协议概述与选主流程详解.md ) +* [搞懂分布式技术3:初探分布式协调服务zookeeper](docs/distributed/practice/搞懂分布式技术3:初探分布式协调服务zookeeper.md ) +* [搞懂分布式技术4:ZAB协议概述与选主流程详解](docs/distributed/practice/搞懂分布式技术4:ZAB协议概述与选主流程详解.md ) * [搞懂分布式技术5:Zookeeper的配置与集群管理实战](docs/distributed/practice/搞懂分布式技术5:Zookeeper的配置与集群管理实战.md) -* [搞懂分布式技术6:Zookeeper典型应用场景及实践 ](docs/distributed/practice/搞懂分布式技术6:Zookeeper典型应用场景及实践.md ) +* [搞懂分布式技术6:Zookeeper典型应用场景及实践](docs/distributed/practice/搞懂分布式技术6:Zookeeper典型应用场景及实践.md ) [//]: # (* [搞懂分布式技术7:负载均衡概念与主流方案](docs/distributed/practice/搞懂分布式技术7:负载均衡概念与主流方案.md)) @@ -322,7 +324,7 @@ todo * [搞懂分布式技术14:SpringBoot使用注解集成Redis缓存](docs/distributed/practice/搞懂分布式技术14:SpringBoot使用注解集成Redis缓存.md) * [搞懂分布式技术15:缓存更新的套路 ](docs/distributed/practice/搞懂分布式技术15:缓存更新的套路.md ) * [搞懂分布式技术16:浅谈分布式锁的几种方案 ](docs/distributed/practice/搞懂分布式技术16:浅谈分布式锁的几种方案.md ) -* [搞懂分布式技术17:浅析分布式事务 ](docs/distributed/practice/搞懂分布式技术17:浅析分布式事务.md ) +* [搞懂分布式技术17:浅析分布式事务](docs/distributed/practice/搞懂分布式技术17:浅析分布式事务.md ) * [搞懂分布式技术18:分布式事务常用解决方案 ](docs/distributed/practice/搞懂分布式技术18:分布式事务常用解决方案.md ) * [搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务 ](docs/distributed/practice/搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务.md ) * [搞懂分布式技术20:消息队列因何而生](docs/distributed/practice/搞懂分布式技术20:消息队列因何而生.md) diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2202\357\274\232\345\210\235\346\216\242Spring IOC\346\240\270\345\277\203\346\265\201\347\250\213.md" "b/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2202\357\274\232\345\210\235\346\216\242SpringIOC\346\240\270\345\277\203\346\265\201\347\250\213.md" similarity index 100% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2202\357\274\232\345\210\235\346\216\242Spring IOC\346\240\270\345\277\203\346\265\201\347\250\213.md" rename to "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2202\357\274\232\345\210\235\346\216\242SpringIOC\346\240\270\345\277\203\346\265\201\347\250\213.md" diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2203\357\274\232Spring IOC\345\256\271\345\231\250\347\232\204\345\212\240\350\275\275\350\277\207\347\250\213.md" "b/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2203\357\274\232SpringIOC\345\256\271\345\231\250\347\232\204\345\212\240\350\275\275\350\277\207\347\250\213.md" similarity index 100% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2203\357\274\232Spring IOC\345\256\271\345\231\250\347\232\204\345\212\240\350\275\275\350\277\207\347\250\213.md" rename to "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2203\357\274\232SpringIOC\345\256\271\345\231\250\347\232\204\345\212\240\350\275\275\350\277\207\347\250\213.md" diff --git "a/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2206\357\274\232Spring AOP\346\246\202\350\277\260.md" "b/docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2206\357\274\232SpringAOP\346\246\202\350\277\260.md" similarity index 100% rename from "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2206\357\274\232Spring AOP\346\246\202\350\277\260.md" rename to "docs/java-web/Spring/Spring\346\272\220\347\240\201\345\211\226\346\236\2206\357\274\232SpringAOP\346\246\202\350\277\260.md" diff --git "a/docs/java-web/Spring/\347\273\231\344\275\240\344\270\200\344\273\275Spring Boot\347\237\245\350\257\206\346\270\205\345\215\225.md" "b/docs/java-web/Spring/\347\273\231\344\275\240\344\270\200\344\273\275SpringBoot\347\237\245\350\257\206\346\270\205\345\215\225.md" similarity index 100% rename from "docs/java-web/Spring/\347\273\231\344\275\240\344\270\200\344\273\275Spring Boot\347\237\245\350\257\206\346\270\205\345\215\225.md" rename to "docs/java-web/Spring/\347\273\231\344\275\240\344\270\200\344\273\275SpringBoot\347\237\245\350\257\206\346\270\205\345\215\225.md" diff --git "a/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\2144\357\274\232Servlet \345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md" "b/docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\2144\357\274\232Servlet\345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md" similarity index 100% rename from "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\2144\357\274\232Servlet \345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md" rename to "docs/java-web/\350\265\260\350\277\233JavaWeb\346\212\200\346\234\257\344\270\226\347\225\2144\357\274\232Servlet\345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md" diff --git "a/docs/java/basic/1\343\200\201\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md" "b/docs/java/basic/1\343\200\201\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md" index 20a3a96..0037f49 100644 --- "a/docs/java/basic/1\343\200\201\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md" +++ "b/docs/java/basic/1\343\200\201\351\235\242\345\220\221\345\257\271\350\261\241\345\237\272\347\241\200.md" @@ -16,10 +16,6 @@ * [二、封装](#二、封装) * [1、封装的概念](#1、封装的概念) * [2、封装的优点](#2、封装的优点) - * [3、封装的实现步骤](#3、封装的实现步骤) - * [A、访问修饰符](#a、访问修饰符) - * [B、this关键字](#b、this关键字) - * [C、Java 中的内部类](#c、java-中的内部类) * [三、多态](#三、多态) * [1、多态的概念](#1、多态的概念) * [2、多态的好处](#2、多态的好处) @@ -67,27 +63,23 @@ Java 是面向对象的编程语言,对象就是面向对象程序设计的核 ## 面向对象和面向过程的区别 - 面向过程: -   一种较早的编程思想,顾名思义就是该思想是站着过程的角度思考问题,强调的就是功能行为,功能的执行过程,即先后顺序,而每一个功能我们都使用函数(类似于方法)把这些步骤一步一步实现。使用的时候依次调用函数就可以了。 +一种较早的编程思想,顾名思义就是该思想是站着过程的角度思考问题,强调的就是功能行为,功能的执行过程,即先后顺序,而每一个功能我们都使用函数(类似于方法)把这些步骤一步一步实现。使用的时候依次调用函数就可以了。 - 面向过程的设计: -   最小的程序单元是函数,每个函数负责完成某一个功能,用于接受输入数据,函数对输入数据进行处理,然后输出结果数据,整个软件系统由一个个的函数组成,其中作为程序入口的函数称之为主函数,主函数依次调用其他函数,普通函数之间可以相互调用,从而实现整个系统功能。 -   面向过程最大的问题在于随着系统的膨胀,面向过程将无法应付,最终导致系统的崩溃。为了解决这一种软件危机,我们提出面向对象思想。 + 最小的程序单元是函数,每个函数负责完成某一个功能,用于接受输入数据,函数对输入数据进行处理,然后输出结果数据,整个软件系统由一个个的函数组成,其中作为程序入口的函数称之为主函数,主函数依次调用其他函数,普通函数之间可以相互调用,从而实现整个系统功能。 + 面向过程最大的问题在于随着系统的膨胀,面向过程将无法应付,最终导致系统的崩溃。为了解决这一种软件危机,我们提出面向对象思想。 - 面向过程的缺陷: -   是采用自顶而下的设计模式,在设计阶段就需要考虑每一个模块应该分解成哪些子模块,每一个子模块又细分为更小的子模块,如此类推,直到将模块细化为一个个函数。 + 是采用自顶而下的设计模式,在设计阶段就需要考虑每一个模块应该分解成哪些子模块,每一个子模块又细分为更小的子模块,如此类推,直到将模块细化为一个个函数。 -- 存在的问题 - - ​ 设计不够直观,与人类的思维习惯不一致 +- 存在的问题 + 设计不够直观,与人类的思维习惯不一致 系统软件适应新差,可拓展性差,维护性低 -- 面向对象: - - ​ 一种基于面向过程的新编程思想,顾名思义就是该思想是站在对象的角度思考问题,我们把多个功能合理放到不同对象里,强调的是具备某些功能的对象。 - -   具备某种功能的实体,称为对象。面向对象最小的程序单元是:类。面向对象更加符合常规的思维方式,稳定性好,可重用性强,易于开发大型软件产品,有良好的可维护性。 - -  在软件工程上,面向对象可以使工程更加模块化,实现更低的耦合和更高的内聚。 +- 面向对象: + 一种基于面向过程的新编程思想,顾名思义就是该思想是站在对象的角度思考问题,我们把多个功能合理放到不同对象里,强调的是具备某些功能的对象。 + 具备某种功能的实体,称为对象。面向对象最小的程序单元是:类。面向对象更加符合常规的思维方式,稳定性好,可重用性强,易于开发大型软件产品,有良好的可维护性。 + 在软件工程上,面向对象可以使工程更加模块化,实现更低的耦合和更高的内聚。 ## 面向对象的三大核心特性简介 @@ -179,10 +171,7 @@ Java 是面向对象的编程语言,对象就是面向对象程序设计的核 使用这种层次形的分类方式,是为了将多个类的通用属性和方法提取出来,放在它们的父类中,然后只需要在子类中各自定义自己独有的属性和方法,并以继承的形式在父类中获取它们的通用属性和方法即可。 - 继承是类与类的一种关系,是一种“is a”的关系。比如“狗”继承“动物”,这里动物类是狗类的父类或者基类,狗类是动物类的子类或者派生类。如下图所示: - -![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701123011243-2128400556.png) - + 继承是类与类的一种关系,是一种“is a”的关系。比如“狗”继承“动物”,这里动物类是狗类的父类或者基类,狗类是动物类的子类或者派生类。 注:java中的继承是**单继承**,即**一个类只有一个父类。** **补充:Java中的继承只能单继承,但是可以通过内部类继承其他类来实现多继承。** @@ -218,47 +207,37 @@ System.out.println("mom cook");  子类拥有父类的所有属性和方法(除了private修饰的属性不能拥有)从而实现了实现代码的复用;  -#### 3、语法规则 - -![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701123421961-647167245.png) - ------- - ##### A、方法的重写 - 子类如果对继承的父类的方法不满意(不适合),可以自己编写继承的方法,这种方式就称为**方法的重写。当调用方法时会优先调用子类的方法。** +子类如果对继承的父类的方法不满意(不适合),可以自己编写继承的方法,这种方式就称为**方法的重写。当调用方法时会优先调用子类的方法。** - **重写要注意:** +**重写要注意:** -  a、返回值类型 +a、返回值类型 -  b、方法名 +b、方法名 -  c、参数类型及个数 +c、参数类型及个数 - 都要与父类继承的方法相同,才叫方法的重写。 +都要与父类继承的方法相同,才叫方法的重写。 - **重载和重写的区别:** +**重载和重写的区别:** -  方法重载:在同一个类中处理不同数据的多个相同方法名的多态手段。 +方法重载:在同一个类中处理不同数据的多个相同方法名的多态手段。 -  方法重写:相对继承而言,子类中对父类已经存在的方法进行区别化的修改。 +方法重写:相对继承而言,子类中对父类已经存在的方法进行区别化的修改。 ------ ##### B、继承的初始化顺序 -  1、初始化父类再初始化子类 - -  2、先执行初始化对象中属性,再执行构造方法中的初始化。 +1、初始化父类再初始化子类 - 基于上面两点,我们就知道实例化一个子类,java程序的执行顺序是: +2、先执行初始化对象中属性,再执行构造方法中的初始化。 - **父类对象属性初始化---->父类对象构造方法---->子类对象属性初始化--->子类对象构造方法**    +基于上面两点,我们就知道实例化一个子类,java程序的执行顺序是: - 下面有个形象的图: - -![img](https://images2015.cnblogs.com/blog/1189312/201707/1189312-20170701144019071-1063399032.png) +**父类对象属性初始化---->父类对象构造方法---->子类对象属性初始化--->子类对象构造方法**    ------ @@ -345,55 +324,22 @@ Java 封装,说白了就是将一大坨公共通用的实现逻辑玩意,装 4. 一对一,一个功能就只为这个功能服务;避免头发绳子一块用,导致最后一团糟 -#### 3、封装的实现步骤 - -![img](https://images2015.cnblogs.com/blog/1189312/201706/1189312-20170630170717493-357592353.png) - -    需要注意:对封装的属性不一定要通过get/set方法,其他方法也可以对封装的属性进行操作。当然最好使用get/set方法,比较标准。 - ------- - - - -##### A、访问修饰符 - -![img](https://images2015.cnblogs.com/blog/1189312/201706/1189312-20170630174919274-1857293801.png) - -    从表格可以看出**从上到下封装性越来越差**。 - - - -##### B、this关键字 - - 1.this关键字**代表当前对象** - -  this.属性 操作当前对象的属性 - -  this.方法 调用当前对象的方法。 - - 2.封装对象的属性的时候,经常会使用this关键字。 - - 3.当getter和setter函数参数名和成员函数名重合的时候,**可以使用this****区别。如:** - -![img](https://images2015.cnblogs.com/blog/1189312/201706/1189312-20170630180217524-833886832.png) - - ##### C、Java 中的内部类 - 内部类( Inner Class )就是定义在另外一个类**里面**的类。与之对应,包含内部类的类被称为外部类。 +内部类( Inner Class )就是定义在另外一个类**里面**的类。与之对应,包含内部类的类被称为外部类。 - 那么问题来了:那为什么要将一个类定义在另一个类里面呢?清清爽爽的独立的一个类多好啊!! +那么问题来了:那为什么要将一个类定义在另一个类里面呢?清清爽爽的独立的一个类多好啊!! - 答:内部类的主要作用如下: +答:内部类的主要作用如下: -  1. 内部类提供了**更好的封装**,可以把内部类**隐藏**在外部类之内,**不允许**同一个包中的其他类访问该类。 +1. 内部类提供了**更好的封装**,可以把内部类**隐藏**在外部类之内,**不允许**同一个包中的其他类访问该类。 -  2. 内部类的方法可以**直接访问外部类的所有数据**,包括**私有的数据**。 +2. 内部类的方法可以**直接访问外部类的所有数据**,包括**私有的数据**。 -  3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便。 +3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便。 -  内部类可分为以下几种: +内部类可分为以下几种: - 成员内部类 @@ -401,8 +347,6 @@ Java 封装,说白了就是将一大坨公共通用的实现逻辑玩意,装 - 方法内部类 - 匿名内部类   - - ### 三、多态 #### 1、多态的概念 @@ -483,13 +427,13 @@ java里的多态主要表现在两个方面:   就以上述的父类Animal和一个子类Dog来说明,当父类的引用可以指向子类的对象时,就是**向上类型转换**。如: -![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2MjYzMDUwOC05NjE1MDc2NTkucG5n?x-oss-process=image/format,png) + ![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2MjYzMDUwOC05NjE1MDc2NTkucG5n?x-oss-process=image/format,png)   **2. 向下类型转换(强制类型转换),是大类型转换到小类型(有风险,可能出现数据溢出)。**   将上述代码再加上一行,我们再次将父类转换为子类引用,那么会出现错误,编译器不允许我们直接这么做**,**虽然我们知道这个父类引用指向的就是子类对象,但是编译器认为这种转换是存在风险的**。**如: -![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2MjkyNjQ3Ny0zODU3OTc1LnBuZw?x-oss-process=image/format,png) + ![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2MjkyNjQ3Ny0zODU3OTc1LnBuZw?x-oss-process=image/format,png)   那么我们该怎么解决这个问题呢,我们可以在animal前加上(Dog)来强制类型转换。如:![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2NDIyOTg5OS0xMDU1MTkwNzc0LnBuZw?x-oss-process=image/format,png) @@ -499,7 +443,7 @@ java里的多态主要表现在两个方面:   还有一种情况是父类的引用指向**其他子类的对象**,则不能通过强制转为**该子类的对象**。如: -    ![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2NTEzMzI4OS03MTc0MzkzNjAucG5n?x-oss-process=image/format,png) +  ![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMTE4OTMxMi8yMDE3MDcvMTE4OTMxMi0yMDE3MDcwMTE2NTEzMzI4OS03MTc0MzkzNjAucG5n?x-oss-process=image/format,png)   这是因为我们在编译的时候进行了强制类型转换,编译时的类型是我们强制转换的类型,所以编译器不会报错,而当我们运行的时候,程序给animal开辟的是Dog类型的内存空间,这与Cat类型内存空间不匹配,所以无法正常转换。这两种情况出错的本质是一样的,所以我们在使用强制类型转换的时候要特别注意这两种错误!!下面有个更安全的方式来实现向下类型转换。。。。 diff --git "a/docs/java/basic/2\343\200\201Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" "b/docs/java/basic/2\343\200\201Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" index 655b4c2..c6349c6 100644 --- "a/docs/java/basic/2\343\200\201Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" +++ "b/docs/java/basic/2\343\200\201Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" @@ -34,9 +34,6 @@ 如果对本系列文章有什么建议,或者是有什么疑问的话,也可以关注公众号【Java技术江湖】联系作者,欢迎你参与本系列博文的创作和修订。 - - - # Java 基本数据类型 @@ -53,7 +50,6 @@ - 内置数据类型 - 引用数据类型 -- - * ### 内置数据类型 @@ -128,50 +124,6 @@ Java语言提供了八种基本类型。六种数字类型(四个整数型, - char 数据类型可以储存任何字符; - 例子:char letter = 'A';。 -``` -//8位 -byte bx = Byte.MAX_VALUE; -byte bn = Byte.MIN_VALUE; -//16位 -short sx = Short.MAX_VALUE; -short sn = Short.MIN_VALUE; -//32位 -int ix = Integer.MAX_VALUE; -int in = Integer.MIN_VALUE; -//64位 -long lx = Long.MAX_VALUE; -long ln = Long.MIN_VALUE; -//32位 -float fx = Float.MAX_VALUE; -float fn = Float.MIN_VALUE; -//64位 -double dx = Double.MAX_VALUE; -double dn = Double.MIN_VALUE; -//16位 -char cx = Character.MAX_VALUE; -char cn = Character.MIN_VALUE; -//1位 -boolean bt = Boolean.TRUE; -boolean bf = Boolean.FALSE; -``` - - `127` - `-128` - `32767` - `-32768` - `2147483647` - `-2147483648` - `9223372036854775807` - `-9223372036854775808` - `3.4028235E38` - `1.4E-45` - `1.7976931348623157E308` - `4.9E-324` - `￿` - - `true` - `false` - ### 引用类型 - 在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。 @@ -202,22 +154,24 @@ char a = 'A' ## 自动拆箱和装箱(详解) Java 5增加了自动装箱与自动拆箱机制,方便基本类型与包装类型的相互转换操作。在Java 5之前,如果要将一个int型的值转换成对应的包装器类型Integer,必须显式的使用new创建一个新的Integer对象,或者调用静态方法Integer.valueOf()。 - - //在Java 5之前,只能这样做 - Integer value = new Integer(10); - //或者这样做 - Integer value = Integer.valueOf(10); - //直接赋值是错误的 - //Integer value = 10;` - +```` +//在Java 5之前,只能这样做 +Integer value = new Integer(10); +//或者这样做 +Integer value = Integer.valueOf(10); +//直接赋值是错误的 +//Integer value = 10; +```` 在Java 5中,可以直接将整型赋给Integer对象,由编译器来完成从int型到Integer类型的转换,这就叫自动装箱。 - `//在Java 5中,直接赋值是合法的,由编译器来完成转换` - `Integer value = 10;` - `与此对应的,自动拆箱就是可以将包装类型转换为基本类型,具体的转换工作由编译器来完成。` - `//在Java 5 中可以直接这么做` - `Integer value = new Integer(10);` - `int i = value;` +```` +//在Java 5中,直接赋值是合法的,由编译器来完成转换 +Integer value = 10; +与此对应的,自动拆箱就是可以将包装类型转换为基本类型,具体的转换工作由编译器来完成。 +//在Java 5 中可以直接这么做 +Integer value = new Integer(10); +int i = value; +```` 自动装箱与自动拆箱为程序员提供了很大的方便,而在实际的应用中,自动装箱与拆箱也是使用最广泛的特性之一。自动装箱和自动拆箱其实是Java编译器提供的一颗语法糖(语法糖是指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通过可提高开发效率,增加代码可读性,增加代码的安全性)。 @@ -229,43 +183,41 @@ Java 5增加了自动装箱与自动拆箱机制,方便基本类型与包装 实例方法xxxValue():将具体的包装类型对象转换成基本类型; 下面我们以int和Integer为例,说明Java中自动装箱与自动拆箱的实现机制。看如下代码: - - class Auto //code1 - { - public static void main(String[] args) - { - //自动装箱 - Integer inte = 10; - //自动拆箱 - int i = inte; - - //再double和Double来验证一下 - Double doub = 12.40; - double d = doub; - - } - - } +```` + class Auto //code1 + { + public static void main(String[] args) + { + //自动装箱 + Integer inte = 10; + //自动拆箱 + int i = inte; + + //再double和Double来验证一下 + Double doub = 12.40; + double d = doub; + + } + + } +```` 上面的代码先将int型转为Integer对象,再讲Integer对象转换为int型,毫无疑问,这是可以正确运行的。可是,这种转换是怎么进行的呢?使用反编译工具,将生成的Class文件在反编译为Java文件,让我们看看发生了什么: +```` class Auto//code2 { public static void main(String[] paramArrayOfString) { Integer localInteger = Integer.valueOf(10); - - -​ -​ int i = localInteger.intValue(); - - -​ -​ -​ Double localDouble = Double.valueOf(12.4D); -​ double d = localDouble.doubleValue(); -​ -​ } -​ } + + int i = localInteger.intValue(); + + Double localDouble = Double.valueOf(12.4D); + double d = localDouble.doubleValue(); + + } + } +```` 我们可以看到经过javac编译之后,code1的代码被转换成了code2,实际运行时,虚拟机运行的就是code2的代码。也就是说,虚拟机根本不知道有自动拆箱和自动装箱这回事;在将Java源文件编译为class文件的过程中,javac编译器在自动装箱的时候,调用了Integer.valueOf()方法,在自动拆箱时,又调用了intValue()方法。我们可以看到,double和Double也是如此。 实现总结:其实自动装箱和自动封箱是编译器为我们提供的一颗语法糖。在自动装箱时,编译器调用包装类型的valueOf()方法;在自动拆箱时,编译器调用了相应的xxxValue()方法。 @@ -276,7 +228,7 @@ Java 5增加了自动装箱与自动拆箱机制,方便基本类型与包装 Integer源码 -``` +```` public final class Integer extends Number implements Comparable { private final int value; @@ -343,7 +295,7 @@ public final class Integer extends Number implements Comparable { private IntegerCache() {} } -``` +```` 以上是Oracle(Sun)公司JDK 1.7中Integer源码的一部分,通过分析上面的代码,得到: @@ -363,21 +315,22 @@ public final class Integer extends Number implements Comparable { 5)调用intValue(),直接返回value的值。 通过3)和4)可以发现,默认情况下,在使用自动装箱时,VM会复用[-128,127]之间的Integer对象。 - - Integer a1 = 1; - Integer a2 = 1; - Integer a3 = new Integer(1); - //会打印true,因为a1和a2是同一个对象,都是Integer.cache[129] - System.out.println(a1 == a2); - //false,a3构造了一个新的对象,不同于a1,a2 - System.out.println(a1 == a3); - +```` +Integer a1 = 1; +Integer a2 = 1; +Integer a3 = new Integer(1); +//会打印true,因为a1和a2是同一个对象,都是Integer.cache[129] +System.out.println(a1 == a2); +//false,a3构造了一个新的对象,不同于a1,a2 +System.out.println(a1 == a3); +```` ### 了解基本类型缓存(常量池)的最佳实践 ``` //基本数据类型的常量池是-128到127之间。 // 在这个范围中的基本数据类的包装类可以自动拆箱,比较时直接比较数值大小。 public static void main(String[] args) { + //int的自动拆箱和装箱只在-128到127范围中进行,超过该范围的两个integer的 == 判断是会返回false的。 Integer a1 = 128; Integer a2 = -128; @@ -394,7 +347,6 @@ public static void main(String[] args) { System.out.println(b1 == b2); System.out.println(b3 == b4); - // Long c1 = 128L; Long c2 = 128L; Long c3 = -128L; @@ -411,7 +363,6 @@ public static void main(String[] args) { System.out.println(d1 == d2); System.out.println(d3 == d4); - `结果` `false` @@ -423,8 +374,6 @@ public static void main(String[] args) { `false` `true` - - Integer i = 10; Byte b = 10; //比较Byte和Integer.两个对象无法直接比较,报错 @@ -460,8 +409,6 @@ public static void main(String[] args) { 需要注意的是:“==”在没遇到算术运算时,不会自动拆箱;基本类型只会自动装箱为对应的包装类型,代码中最后一条说明的内容。 - - 在JDK 1.5中提供了自动装箱与自动拆箱,这其实是Java 编译器的语法糖,编译器通过调用包装类型的valueOf()方法实现自动装箱,调用xxxValue()方法自动拆箱。自动装箱和拆箱会有一些陷阱,那就是包装类型复用了某些对象。 (1)Integer默认复用了[-128,127]这些对象,其中高位置可以修改; @@ -574,10 +521,6 @@ System.out.println(s3 == s4); JDK1.6以及以下:false false JDK1.7以及以上:false true ``` - -![image](https://img-blog.csdn.net/20180422231916788?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E3MjQ4ODg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) - -![image](https://img-blog.csdn.net/20180422231929413?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E3MjQ4ODg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) JDK1.6查找到常量池存在相同值的对象时会直接返回该对象的地址。 JDK 1.7后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。 diff --git "a/docs/java/basic/3\343\200\201string\345\222\214\345\214\205\350\243\205\347\261\273.md" "b/docs/java/basic/3\343\200\201string\345\222\214\345\214\205\350\243\205\347\261\273.md" index 13dfe0a..064cb24 100644 --- "a/docs/java/basic/3\343\200\201string\345\222\214\345\214\205\350\243\205\347\261\273.md" +++ "b/docs/java/basic/3\343\200\201string\345\222\214\345\214\205\350\243\205\347\261\273.md" @@ -46,11 +46,8 @@ 如果对本系列文章有什么建议,或者是有什么疑问的话,也可以关注公众号【Java技术江湖】联系作者,欢迎你参与本系列博文的创作和修订。 - - - ## string基础 ### Java String 类 @@ -70,9 +67,14 @@ String greeting = "菜鸟教程"; String 类有 11 种构造方法,这些方法提供不同的参数来初始化字符串,比如提供一个字符数组参数: ### StringDemo.java 文件代码: - - public class StringDemo{ public static void main(String args[]){ char[] helloArray = { 'r', 'u', 'n', 'o', 'o', 'b'}; String helloString = new String(helloArray); System.out.println( helloString ); } } - +```` +public class StringDemo{ +public static void main(String args[]){ +char[] helloArray = { 'r', 'u', 'n', 'o', 'o', 'b'}; +String helloString = new String(helloArray); +System.out.println( helloString ); +} } +```` 以上实例编译运行结果如下: ``` @@ -87,35 +89,37 @@ runoob ### 创建String对象的常用方法 -(1) String s1 = "mpptest" +(1) String s1 = "mpptest" - (2) String s2 = new String(); +(2) String s2 = new String(); - (3) String s3 = new String("mpptest") +(3) String s3 = new String("mpptest") ### String中常用的方法,用法如图所示,具体问度娘 ![](https://img2018.cnblogs.com/blog/710412/201902/710412-20190213220237169-1966705420.png) ### 三个方法的使用: lenth() substring() charAt() - - - -
package com.mpp.string; public class StringDemo1 { public static void main(String[] args) { //定义一个字符串"晚来天欲雪 能饮一杯无"
+````
+package com.mpp.string; 
+public class StringDemo1 {
+    public static void main(String[] args) { //定义一个字符串"晚来天欲雪 能饮一杯无"
         String str = "晚来天欲雪 能饮一杯无";
         System.out.println("字符串的长度是:"+str.length()); //字符串的雪字打印输出  charAt(int index)
         System.out.println(str.charAt(4)); //取出子串  天欲
         System.out.println(str.substring(2));   //取出从index2开始直到最后的子串,包含2
         System.out.println(str.substring(2,4));  //取出index从2到4的子串,包含2不包含4  顾头不顾尾
- }
-}
- - + } +} +```` - 两个方法的使用,求字符或子串第一次/最后一次在字符串中出现的位置: indexOf() lastIndexOf() +两个方法的使用,求字符或子串第一次/最后一次在字符串中出现的位置: +indexOf() lastIndexOf() -
package com.mpp.string; public class StringDemo2 { public static void main(String[] args) {
+````
+package com.mpp.string; public class StringDemo2 { 
+    public static void main(String[] args) {
         String str = new String("赵客缦胡缨 吴钩胡缨霜雪明"); //查找胡在字符串中第一次出现的位置
         System.out.println("\"胡\"在字符串中第一次出现的位置:"+str.indexOf("胡")); //查找子串"胡缨"在字符串中第一次出现的位置
         System.out.println("\"胡缨\"在字符串中第一次出现的位置"+str.indexOf("胡缨")); //查找胡在字符串中最后一次次出现的位置
@@ -123,35 +127,40 @@ runoob
         System.out.println(str.lastIndexOf("胡缨")); //从indexof为5的位置,找第一次出现的"吴"
         System.out.println(str.indexOf("吴",5));
     }
-}
- +} +```` ### 字符串与byte数组间的相互转换 - -
package com.mpp.string; import java.io.UnsupportedEncodingException; public class StringDemo3 { public static void main(String[] args) throws UnsupportedEncodingException { //字符串和byte数组之间的相互转换
- String str = new String("hhhabc银鞍照白马 飒沓如流星"); //将字符串转换为byte数组,并打印输出
-        byte[] arrs = str.getBytes("GBK"); for(int i=0;i){
+````
+package com.mpp.string; import java.io.UnsupportedEncodingException; 
+public class StringDemo3 { 
+    public static void main(String[] args) throws UnsupportedEncodingException { 
+        
+        //字符串和byte数组之间的相互转换
+        String str = new String("hhhabc银鞍照白马 飒沓如流星"); //将字符串转换为byte数组,并打印输出
+        byte[] arrs = str.getBytes("GBK"); 
+        for(int i=0;i){
             System.out.print(arrs[i]);
-        } //将byte数组转换成字符串
- System.out.println();
+        } 
+        
+        //将byte数组转换成字符串
+        System.out.println();
         String str1 = new String(arrs,"GBK");  //保持字符集的一致,否则会出现乱码
- System.out.println(str1);
+        System.out.println(str1);
     }
-}
- +} +```` ### ==运算符和equals之间的区别: -
引用指向的内容和引用指向的地址
+引用指向的内容和引用指向的地址 -![](https://img2018.cnblogs.com/blog/710412/201902/710412-20190214223341972-1204335921.png) - - - -
package com.mpp.string; public class StringDemo5 { public static void main(String[] args) {
+````
+package com.mpp.string; public class StringDemo5 { 
+    public static void main(String[] args) {
         String str1 = "mpp";
         String str2 = "mpp";
         String str3 = new String("mpp");
@@ -160,9 +169,9 @@ runoob
         System.out.println(str1.equals(str3));   //true  内容相同
         System.out.println(str1==str2);   //true   地址相同
         System.out.println(str1==str3);   //false  地址不同
- }
-}
- + } +} +```` ### 字符串的不可变性 @@ -170,7 +179,7 @@ String的对象一旦被创建,则不能修改,是不可变的 所谓的修改其实是创建了新的对象,所指向的内存空间不变 -![](https://img2018.cnblogs.com/blog/710412/201902/710412-20190214224055939-746946317.png) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/blog/Java%E6%8A%80%E6%9C%AF%E6%B1%9F%E6%B9%96/%E4%BA%8C%E7%BB%B4%E7%A0%81/710412-20190214224055939-746946317.png) 上图中,s1不再指向imooc所在的内存空间,而是指向了hello,imooc ### String的连接 @@ -218,85 +227,86 @@ String str = "aa"+"bb"+"cc"; ## String类的源码分析 ### String类型的intern - - public void intern () { - //2:string的intern使用 - //s1是基本类型,比较值。s2是string实例,比较实例地址 - //字符串类型用equals方法比较时只会比较值 - String s1 = "a"; - String s2 = new String("a"); - //调用intern时,如果s2中的字符不在常量池,则加入常量池并返回常量的引用 - String s3 = s2.intern(); - System.out.println(s1 == s2); - System.out.println(s1 == s3); - } - +```` +public void intern () { + //2:string的intern使用 + //s1是基本类型,比较值。s2是string实例,比较实例地址 + //字符串类型用equals方法比较时只会比较值 + String s1 = "a"; + String s2 = new String("a"); + //调用intern时,如果s2中的字符不在常量池,则加入常量池并返回常量的引用 + String s3 = s2.intern(); + System.out.println(s1 == s2); + System.out.println(s1 == s3); +} +```` ### String类型的equals - - //字符串的equals方法 - // public boolean equals(Object anObject) { - // if (this == anObject) { - // return true; - // } - // if (anObject instanceof String) { - // String anotherString = (String)anObject; - // int n = value.length; - // if (n == anotherString.value.length) { - // char v1[] = value; - // char v2[] = anotherString.value; - // int i = 0; - // while (n-- != 0) { - // if (v1[i] != v2[i]) - // return false; - // i++; - // } - // return true; - // } - // } - // return false; - // } - +```` +//字符串的equals方法 +// public boolean equals(Object anObject) { +// if (this == anObject) { +// return true; +// } +// if (anObject instanceof String) { +// String anotherString = (String)anObject; +// int n = value.length; +// if (n == anotherString.value.length) { +// char v1[] = value; +// char v2[] = anotherString.value; +// int i = 0; +// while (n-- != 0) { +// if (v1[i] != v2[i]) +// return false; +// i++; +// } +// return true; +// } +// } +// return false; +// } +```` ### StringBuffer和Stringbuilder 底层是继承父类的可变字符数组value +```` +/** + +- The value is used for character storage. + */ + char[] value; + 初始化容量为16 + +/** + +- Constructs a string builder with no characters in it and an +- initial capacity of 16 characters. + */ + public StringBuilder() { + super(16); + } + 这两个类的append方法都是来自父类AbstractStringBuilder的方法 + +public AbstractStringBuilder append(String str) { + if (str == null) + return appendNull(); + int len = str.length(); + ensureCapacityInternal(count + len); + str.getChars(0, len, value, count); + count += len; + return this; +} +@Override +public StringBuilder append(String str) { + super.append(str); + return this; +} - /** - - - The value is used for character storage. - */ - char[] value; - 初始化容量为16 - - /** - - - Constructs a string builder with no characters in it and an - - initial capacity of 16 characters. - */ - public StringBuilder() { - super(16); - } - 这两个类的append方法都是来自父类AbstractStringBuilder的方法 - - public AbstractStringBuilder append(String str) { - if (str == null) - return appendNull(); - int len = str.length(); - ensureCapacityInternal(count + len); - str.getChars(0, len, value, count); - count += len; - return this; - } - @Override - public StringBuilder append(String str) { - super.append(str); - return this; - } - - @Override - public synchronized StringBuffer append(String str) { - toStringCache = null; - super.append(str); - return this; - } +@Override +public synchronized StringBuffer append(String str) { + toStringCache = null; + super.append(str); + return this; +} +```` ### append方法 Stringbuffer在大部分涉及字符串修改的操作上加了synchronized关键字来保证线程安全,效率较低。 @@ -312,19 +322,19 @@ a = a + a;时,实际上先把a封装成stringbuilder,调用append方法后 ensureCapacityInternal(count + len); 该方法是计算append之后的空间是否足够,不足的话需要进行扩容 - - public void ensureCapacity(int minimumCapacity) { - if (minimumCapacity > 0) - ensureCapacityInternal(minimumCapacity); - } - private void ensureCapacityInternal(int minimumCapacity) { - // overflow-conscious code - if (minimumCapacity - value.length > 0) { - value = Arrays.copyOf(value, - newCapacity(minimumCapacity)); - } +```` +public void ensureCapacity(int minimumCapacity) { + if (minimumCapacity > 0) + ensureCapacityInternal(minimumCapacity); +} +private void ensureCapacityInternal(int minimumCapacity) { + // overflow-conscious code + if (minimumCapacity - value.length > 0) { + value = Arrays.copyOf(value, + newCapacity(minimumCapacity)); } - +} +```` 如果新字符串长度大于value数组长度则进行扩容 扩容后的长度一般为原来的两倍 + 2; @@ -334,42 +344,39 @@ ensureCapacityInternal(count + len); 考虑两种情况 如果新的字符串长度超过int最大值,则抛出异常,否则直接使用数组最大长度作为新数组的长度。 - - private int hugeCapacity(int minCapacity) { - if (Integer.MAX_VALUE - minCapacity < 0) { // overflow - throw new OutOfMemoryError(); - } - return (minCapacity > MAX_ARRAY_SIZE) - ? minCapacity : MAX_ARRAY_SIZE; +```` +private int hugeCapacity(int minCapacity) { + if (Integer.MAX_VALUE - minCapacity < 0) { // overflow + throw new OutOfMemoryError(); } - + return (minCapacity > MAX_ARRAY_SIZE) + ? minCapacity : MAX_ARRAY_SIZE; +} +```` ### 删除 这两个类型的删除操作: 都是调用父类的delete方法进行删除 - - public AbstractStringBuilder delete(int start, int end) { - if (start < 0) - throw new StringIndexOutOfBoundsException(start); - if (end > count) - end = count; - if (start > end) - throw new StringIndexOutOfBoundsException(); - int len = end - start; - if (len > 0) { - System.arraycopy(value, start+len, value, start, count-end); - count -= len; - } - return this; - } - +```` +public AbstractStringBuilder delete(int start, int end) { + if (start < 0) + throw new StringIndexOutOfBoundsException(start); + if (end > count) + end = count; + if (start > end) + throw new StringIndexOutOfBoundsException(); + int len = end - start; + if (len > 0) { + System.arraycopy(value, start+len, value, start, count-end); + count -= len; + } + return this; +} +```` 事实上是将剩余的字符重新拷贝到字符数组value。 这里用到了system.arraycopy来拷贝数组,速度是比较快的 - - - ### system.arraycopy方法 转自知乎: @@ -386,37 +393,26 @@ ensureCapacityInternal(count + len); ## String和JVM的关系 - - - 下面我们了解下Java栈、Java堆、方法区和常量池: - - Java栈(线程私有数据区): ``` 每个Java虚拟机线程都有自己的Java虚拟机栈,Java虚拟机栈用来存放栈帧,每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 ``` - - Java堆(线程共享数据区): ``` 在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配。 ``` - - 方法区(线程共享数据区): ``` 方法区在虚拟机启动的时候被创建,它存储了每一个类的结构信息,例如运行时常量池、字段和方法数据、构造函数和普通方法的字节码内容、还包括在类、实例、接口初始化时用到的特殊方法。在JDK8之前永久代是方法区的一种实现,而JDK8元空间替代了永久代,永久代被移除,也可以理解为元空间是方法区的一种实现。 ``` - - 常量池(线程共享数据区): ``` @@ -437,23 +433,18 @@ Java堆(线程共享数据区): 使用字符串常量池,每当我们使用字面量(String s=”1”;)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就将此字符串对象的地址赋值给引用s(引用s在Java栈中)。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,并将此字符串对象的地址赋值给引用s(引用s在Java栈中)。 ``` - - ``` 使用字符串常量池,每当我们使用关键字new(String s=new String(”1”);)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么不再在字符串常量池创建该字符串对象,而直接堆中复制该对象的副本,然后将堆中对象的地址赋值给引用s,如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,然后在堆中复制该对象的副本,然后将堆中对象的地址赋值给引用s。 ``` - - - ## String为什么不可变? 翻开JDK源码,java.lang.String类起手前三行,是这样写的: - - public final class String implements java.io.Serializable, Comparable, CharSequence { - /** String本质是个char数组. 而且用final关键字修饰.*/ - private final char value[]; ... ... - } - +```` +public final class String implements java.io.Serializable, Comparable, CharSequence { + /** String本质是个char数组. 而且用final关键字修饰.*/ +private final char value[]; ... ... + } +```` 首先String类是用final关键字修饰,这说明String不可继承。再看下面,String类的主力成员字段value是个char[]数组,而且是用final修饰的。 final修饰的字段创建以后就不可改变。 有的人以为故事就这样完了,其实没有。因为虽然value是不可变,也只是value这个引用地址不可变。挡不住Array数组是可变的事实。 @@ -463,16 +454,16 @@ Array的数据结构看下图。 也就是说Array变量只是stack上的一个引用,数组的本体结构在heap堆。 String类里的value用final修饰,只是说stack里的这个叫value的引用地址不可变。没有说堆里array本身数据不可变。看下面这个例子, - - final int[] value={1,2,3} ; - int[] another={4,5,6}; - value=another; //编译器报错,final不可变 value用final修饰,编译器不允许我把value指向堆区另一个地址。 - 但如果我直接对数组元素动手,分分钟搞定。 - - final int[] value={1,2,3}; - value[2]=100; //这时候数组里已经是{1,2,100} 所以String是不可变,关键是因为SUN公司的工程师。 - 在后面所有String的方法里很小心的没有去动Array里的元素,没有暴露内部成员字段。private final char value[]这一句里,private的私有访问权限的作用都比final大。而且设计师还很小心地把整个String设成final禁止继承,避免被其他人继承后破坏。所以String是不可变的关键都在底层的实现,而不是一个final。考验的是工程师构造数据类型,封装数据的功力。 - +```` +final int[] value={1,2,3} ; +int[] another={4,5,6}; + value=another; //编译器报错,final不可变 value用final修饰,编译器不允许我把value指向堆区另一个地址。 +但如果我直接对数组元素动手,分分钟搞定。 + + final int[] value={1,2,3}; + value[2]=100; //这时候数组里已经是{1,2,100} 所以String是不可变,关键是因为SUN公司的工程师。 + 在后面所有String的方法里很小心的没有去动Array里的元素,没有暴露内部成员字段。private final char value[]这一句里,private的私有访问权限的作用都比final大。而且设计师还很小心地把整个String设成final禁止继承,避免被其他人继承后破坏。所以String是不可变的关键都在底层的实现,而不是一个final。考验的是工程师构造数据类型,封装数据的功力。 +```` ### 不可变有什么好处? 这个最简单地原因,就是为了安全。看下面这个场景(有评论反应例子不够清楚,现在完整地写出来),一个函数appendStr( )在不可变的String参数后面加上一段“bbb”后返回。appendSb( )负责在可变的StringBuilder后面加“bbb”。 @@ -485,28 +476,28 @@ String类里的value用final修饰,只是说stack里的这个叫value的引用 > 3 final修饰的char数组保证了char数组的引用不可变。但是可以通过char[0] = 'a’来修改值。不过String内部并不提供方法来完成这一操作,所以String的不可变也是基于代码封装和访问控制的。 举个例子 +```` +final class Fi { + int a; + final int b = 0; + Integer s; - final class Fi { - int a; - final int b = 0; - Integer s; - - } - final char[]a = {'a'}; - final int[]b = {1}; - @Test - public void final修饰类() { - //引用没有被final修饰,所以是可变的。 - //final只修饰了Fi类型,即Fi实例化的对象在堆中内存地址是不可变的。 - //虽然内存地址不可变,但是可以对内部的数据做改变。 - Fi f = new Fi(); - f.a = 1; - System.out.println(f); - f.a = 2; - System.out.println(f); - //改变实例中的值并不改变内存地址。 - -``` +} +final char[]a = {'a'}; +final int[]b = {1}; +@Test +public void final修饰类() { + //引用没有被final修饰,所以是可变的。 + //final只修饰了Fi类型,即Fi实例化的对象在堆中内存地址是不可变的。 + //虽然内存地址不可变,但是可以对内部的数据做改变。 + Fi f = new Fi(); + f.a = 1; + System.out.println(f); + f.a = 2; + System.out.println(f); + //改变实例中的值并不改变内存地址。 +```` +```` Fi ff = f; //让引用指向新的Fi对象,原来的f对象由新的引用ff持有。 //引用的指向改变也不会改变原来对象的地址 @@ -515,7 +506,7 @@ System.out.println(f); System.out.println(ff); } -``` +```` 这里的对f.a的修改可以理解为char[0] = 'a'这样的操作。只改变数据值,不改变内存值。 @@ -523,279 +514,283 @@ System.out.println(ff); 问题描述 很多时候我们需要对字符串进行很多固定的操作,而这些操作在JDK/JRE中又没有预置,于是我们想到了apache-commons组件,但是它也不能完全覆盖我们的业务需求,所以很多时候还是要自己写点代码的,下面就是基于apache-commons组件写的部分常用方法: - MAVEN依赖 + +```` org.apache.commons commons-lang3 ${commons-lang3.version} +```` 代码成果 - public class StringUtils extends org.apache.commons.lang3.StringUtils { - - /** 值为"NULL"的字符串 */ - private static final String NULL_STRING = "NULL"; - - private static final char SEPARATOR = '_'; - - - /** - * 满足一下情况返回true
- * ①.入参为空 - * ②.入参为空字符串 - * ③.入参为"null"字符串 - * - * @param string 需要判断的字符型 - * @return boolean - */ - public static boolean isNullOrEmptyOrNULLString(String string) { - return isBlank(string) || NULL_STRING.equalsIgnoreCase(string); - } - - /** - * 把字符串转为二进制码
- * 本方法不会返回null - * - * @param str 需要转换的字符串 - * @return 二进制字节码数组 - */ - public static byte[] toBytes(String str) { - return isBlank(str) ? new byte[]{} : str.getBytes(); - } - - /** - * 把字符串转为二进制码
- * 本方法不会返回null - * - * @param str 需要转换的字符串 - * @param charset 编码类型 - * @return 二进制字节码数组 - * @throws UnsupportedEncodingException 字符串转换的时候编码不支持时出现 - */ - public static byte[] toBytes(String str, Charset charset) throws UnsupportedEncodingException { - return isBlank(str) ? new byte[]{} : str.getBytes(charset.displayName()); - } - - /** - * 把字符串转为二进制码
- * 本方法不会返回null - * - * @param str 需要转换的字符串 - * @param charset 编码类型 - * @param locale 编码类型对应的地区 - * @return 二进制字节码数组 - * @throws UnsupportedEncodingException 字符串转换的时候编码不支持时出现 - */ - public static byte[] toBytes(String str, Charset charset, Locale locale) throws UnsupportedEncodingException { - return isBlank(str) ? new byte[]{} : str.getBytes(charset.displayName(locale)); - } - - /** - * 二进制码转字符串
- * 本方法不会返回null - * - * @param bytes 二进制码 - * @return 字符串 - */ - public static String bytesToString(byte[] bytes) { - return bytes == null || bytes.length == 0 ? EMPTY : new String(bytes); - } - - /** - * 二进制码转字符串
- * 本方法不会返回null - * - * @param bytes 二进制码 - * @param charset 编码集 - * @return 字符串 - * @throws UnsupportedEncodingException 当前二进制码可能不支持传入的编码 - */ - public static String byteToString(byte[] bytes, Charset charset) throws UnsupportedEncodingException { - return bytes == null || bytes.length == 0 ? EMPTY : new String(bytes, charset.displayName()); - } - - /** - * 二进制码转字符串
- * 本方法不会返回null - * - * @param bytes 二进制码 - * @param charset 编码集 - * @param locale 本地化 - * @return 字符串 - * @throws UnsupportedEncodingException 当前二进制码可能不支持传入的编码 - */ - public static String byteToString(byte[] bytes, Charset charset, Locale locale) throws UnsupportedEncodingException { - return bytes == null || bytes.length == 0 ? EMPTY : new String(bytes, charset.displayName(locale)); - } - - /** - * 把对象转为字符串 - * - * @param object 需要转化的字符串 - * @return 字符串, 可能为空 - */ - public static String parseString(Object object) { - if (object == null) { - return null; - } - if (object instanceof byte[]) { - return bytesToString((byte[]) object); - } - return object.toString(); - } - - /** - * 把字符串转为int类型 - * - * @param str 需要转化的字符串 - * @return int - * @throws NumberFormatException 字符串格式不正确时抛出 - */ - public static int parseInt(String str) throws NumberFormatException { - return isBlank(str) ? 0 : Integer.parseInt(str); - } - - /** - * 把字符串转为double类型 - * - * @param str 需要转化的字符串 - * @return double - * @throws NumberFormatException 字符串格式不正确时抛出 - */ - public static double parseDouble(String str) throws NumberFormatException { - return isBlank(str) ? 0D : Double.parseDouble(str); - } - - /** - * 把字符串转为long类型 - * - * @param str 需要转化的字符串 - * @return long - * @throws NumberFormatException 字符串格式不正确时抛出 - */ - public static long parseLong(String str) throws NumberFormatException { - return isBlank(str) ? 0L : Long.parseLong(str); +```` +public class StringUtils extends org.apache.commons.lang3.StringUtils { + +/** 值为"NULL"的字符串 */ +private static final String NULL_STRING = "NULL"; + +private static final char SEPARATOR = '_'; + + +/** + * 满足一下情况返回true
+ * ①.入参为空 + * ②.入参为空字符串 + * ③.入参为"null"字符串 + * + * @param string 需要判断的字符型 + * @return boolean + */ +public static boolean isNullOrEmptyOrNULLString(String string) { + return isBlank(string) || NULL_STRING.equalsIgnoreCase(string); +} + +/** + * 把字符串转为二进制码
+ * 本方法不会返回null + * + * @param str 需要转换的字符串 + * @return 二进制字节码数组 + */ +public static byte[] toBytes(String str) { + return isBlank(str) ? new byte[]{} : str.getBytes(); +} + +/** + * 把字符串转为二进制码
+ * 本方法不会返回null + * + * @param str 需要转换的字符串 + * @param charset 编码类型 + * @return 二进制字节码数组 + * @throws UnsupportedEncodingException 字符串转换的时候编码不支持时出现 + */ +public static byte[] toBytes(String str, Charset charset) throws UnsupportedEncodingException { + return isBlank(str) ? new byte[]{} : str.getBytes(charset.displayName()); +} + +/** + * 把字符串转为二进制码
+ * 本方法不会返回null + * + * @param str 需要转换的字符串 + * @param charset 编码类型 + * @param locale 编码类型对应的地区 + * @return 二进制字节码数组 + * @throws UnsupportedEncodingException 字符串转换的时候编码不支持时出现 + */ +public static byte[] toBytes(String str, Charset charset, Locale locale) throws UnsupportedEncodingException { + return isBlank(str) ? new byte[]{} : str.getBytes(charset.displayName(locale)); +} + +/** + * 二进制码转字符串
+ * 本方法不会返回null + * + * @param bytes 二进制码 + * @return 字符串 + */ +public static String bytesToString(byte[] bytes) { + return bytes == null || bytes.length == 0 ? EMPTY : new String(bytes); +} + +/** + * 二进制码转字符串
+ * 本方法不会返回null + * + * @param bytes 二进制码 + * @param charset 编码集 + * @return 字符串 + * @throws UnsupportedEncodingException 当前二进制码可能不支持传入的编码 + */ +public static String byteToString(byte[] bytes, Charset charset) throws UnsupportedEncodingException { + return bytes == null || bytes.length == 0 ? EMPTY : new String(bytes, charset.displayName()); +} + +/** + * 二进制码转字符串
+ * 本方法不会返回null + * + * @param bytes 二进制码 + * @param charset 编码集 + * @param locale 本地化 + * @return 字符串 + * @throws UnsupportedEncodingException 当前二进制码可能不支持传入的编码 + */ +public static String byteToString(byte[] bytes, Charset charset, Locale locale) throws UnsupportedEncodingException { + return bytes == null || bytes.length == 0 ? EMPTY : new String(bytes, charset.displayName(locale)); +} + +/** + * 把对象转为字符串 + * + * @param object 需要转化的字符串 + * @return 字符串, 可能为空 + */ +public static String parseString(Object object) { + if (object == null) { + return null; } - - /** - * 把字符串转为float类型 - * - * @param str 需要转化的字符串 - * @return float - * @throws NumberFormatException 字符串格式不正确时抛出 - */ - public static float parseFloat(String str) throws NumberFormatException { - return isBlank(str) ? 0L : Float.parseFloat(str); + if (object instanceof byte[]) { + return bytesToString((byte[]) object); } - - /** - * 获取i18n字符串 - * - * @param code - * @param args - * @return - */ - public static String getI18NMessage(String code, Object[] args) { - //LocaleResolver localLocaleResolver = (LocaleResolver) SpringContextHolder.getBean(LocaleResolver.class); - //HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); - //Locale locale = localLocaleResolver.resolveLocale(request); - //return SpringContextHolder.getApplicationContext().getMessage(code, args, locale); + return object.toString(); +} + +/** + * 把字符串转为int类型 + * + * @param str 需要转化的字符串 + * @return int + * @throws NumberFormatException 字符串格式不正确时抛出 + */ +public static int parseInt(String str) throws NumberFormatException { + return isBlank(str) ? 0 : Integer.parseInt(str); +} + +/** + * 把字符串转为double类型 + * + * @param str 需要转化的字符串 + * @return double + * @throws NumberFormatException 字符串格式不正确时抛出 + */ +public static double parseDouble(String str) throws NumberFormatException { + return isBlank(str) ? 0D : Double.parseDouble(str); +} + +/** + * 把字符串转为long类型 + * + * @param str 需要转化的字符串 + * @return long + * @throws NumberFormatException 字符串格式不正确时抛出 + */ +public static long parseLong(String str) throws NumberFormatException { + return isBlank(str) ? 0L : Long.parseLong(str); +} + +/** + * 把字符串转为float类型 + * + * @param str 需要转化的字符串 + * @return float + * @throws NumberFormatException 字符串格式不正确时抛出 + */ +public static float parseFloat(String str) throws NumberFormatException { + return isBlank(str) ? 0L : Float.parseFloat(str); +} + +/** + * 获取i18n字符串 + * + * @param code + * @param args + * @return + */ +public static String getI18NMessage(String code, Object[] args) { + //LocaleResolver localLocaleResolver = (LocaleResolver) SpringContextHolder.getBean(LocaleResolver.class); + //HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); + //Locale locale = localLocaleResolver.resolveLocale(request); + //return SpringContextHolder.getApplicationContext().getMessage(code, args, locale); + return ""; +} + +/** + * 获得用户远程地址 + * + * @param request 请求头 + * @return 用户ip + */ +public static String getRemoteAddr(HttpServletRequest request) { + String remoteAddr = request.getHeader("X-Real-IP"); + if (isNotBlank(remoteAddr)) { + remoteAddr = request.getHeader("X-Forwarded-For"); + } else if (isNotBlank(remoteAddr)) { + remoteAddr = request.getHeader("Proxy-Client-IP"); + } else if (isNotBlank(remoteAddr)) { + remoteAddr = request.getHeader("WL-Proxy-Client-IP"); + } + return remoteAddr != null ? remoteAddr : request.getRemoteAddr(); +} + +/** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ +public static String toCamelCase(String s, Locale locale, char split) { + if (isBlank(s)) { return ""; } - - /** - * 获得用户远程地址 - * - * @param request 请求头 - * @return 用户ip - */ - public static String getRemoteAddr(HttpServletRequest request) { - String remoteAddr = request.getHeader("X-Real-IP"); - if (isNotBlank(remoteAddr)) { - remoteAddr = request.getHeader("X-Forwarded-For"); - } else if (isNotBlank(remoteAddr)) { - remoteAddr = request.getHeader("Proxy-Client-IP"); - } else if (isNotBlank(remoteAddr)) { - remoteAddr = request.getHeader("WL-Proxy-Client-IP"); - } - return remoteAddr != null ? remoteAddr : request.getRemoteAddr(); - } - - /** - * 驼峰命名法工具 - * - * @return toCamelCase(" hello_world ") == "helloWorld" - * toCapitalizeCamelCase("hello_world") == "HelloWorld" - * toUnderScoreCase("helloWorld") = "hello_world" - */ - public static String toCamelCase(String s, Locale locale, char split) { - if (isBlank(s)) { - return ""; - } - - s = s.toLowerCase(locale); - - StringBuilder sb = new StringBuilder(); - for (char c : s.toCharArray()) { - sb.append(c == split ? Character.toUpperCase(c) : c); - } - - return sb.toString(); - } - - public static String toCamelCase(String s) { - return toCamelCase(s, Locale.getDefault(), SEPARATOR); - } - - public static String toCamelCase(String s, Locale locale) { - return toCamelCase(s, locale, SEPARATOR); - } - - public static String toCamelCase(String s, char split) { - return toCamelCase(s, Locale.getDefault(), split); - } - - public static String toUnderScoreCase(String s, char split) { - if (isBlank(s)) { - return ""; - } - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - boolean nextUpperCase = (i < (s.length() - 1)) && Character.isUpperCase(s.charAt(i + 1)); - boolean upperCase = (i > 0) && Character.isUpperCase(c); - sb.append((!upperCase || !nextUpperCase) ? split : "").append(Character.toLowerCase(c)); - } - - return sb.toString(); + + s = s.toLowerCase(locale); + + StringBuilder sb = new StringBuilder(); + for (char c : s.toCharArray()) { + sb.append(c == split ? Character.toUpperCase(c) : c); } - - public static String toUnderScoreCase(String s) { - return toUnderScoreCase(s, SEPARATOR); + + return sb.toString(); +} + +public static String toCamelCase(String s) { + return toCamelCase(s, Locale.getDefault(), SEPARATOR); +} + +public static String toCamelCase(String s, Locale locale) { + return toCamelCase(s, locale, SEPARATOR); +} + +public static String toCamelCase(String s, char split) { + return toCamelCase(s, Locale.getDefault(), split); +} + +public static String toUnderScoreCase(String s, char split) { + if (isBlank(s)) { + return ""; } - - /** - * 把字符串转换为JS获取对象值的三目运算表达式 - * - * @param objectString 对象串 - * 例如:入参:row.user.id/返回:!row?'':!row.user?'':!row.user.id?'':row.user.id - */ - public static String toJsGetValueExpression(String objectString) { - StringBuilder result = new StringBuilder(); - StringBuilder val = new StringBuilder(); - String[] fileds = split(objectString, "."); - for (int i = 0; i < fileds.length; i++) { - val.append("." + fileds[i]); - result.append("!" + (val.substring(1)) + "?'':"); - } - result.append(val.substring(1)); - return result.toString(); + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + boolean nextUpperCase = (i < (s.length() - 1)) && Character.isUpperCase(s.charAt(i + 1)); + boolean upperCase = (i > 0) && Character.isUpperCase(c); + sb.append((!upperCase || !nextUpperCase) ? split : "").append(Character.toLowerCase(c)); } + return sb.toString(); +} - } +public static String toUnderScoreCase(String s) { + return toUnderScoreCase(s, SEPARATOR); +} + +/** + * 把字符串转换为JS获取对象值的三目运算表达式 + * + * @param objectString 对象串 + * 例如:入参:row.user.id/返回:!row?'':!row.user?'':!row.user.id?'':row.user.id + */ +public static String toJsGetValueExpression(String objectString) { + StringBuilder result = new StringBuilder(); + StringBuilder val = new StringBuilder(); + String[] fileds = split(objectString, "."); + for (int i = 0; i < fileds.length; i++) { + val.append("." + fileds[i]); + result.append("!" + (val.substring(1)) + "?'':"); + } + result.append(val.substring(1)); + return result.toString(); +} + + +} +```` ## 参考文章 https://blog.csdn.net/qq_34490018/article/details/82110578 diff --git "a/docs/java/basic/4\343\200\201final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md" "b/docs/java/basic/4\343\200\201final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md" index ff1118c..5609da9 100644 --- "a/docs/java/basic/4\343\200\201final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md" +++ "b/docs/java/basic/4\343\200\201final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md" @@ -184,7 +184,7 @@ final方法的好处: 2. final变量在多线程中并发安全,无需额外的同步开销 3. final方法是静态编译的,提高了调用速度 4. **final类创建的对象是只可读的,在多线程可以安全共享** -5. + ## final关键字的最佳实践 ### final的用法 @@ -198,7 +198,7 @@ final修饰的变量有三种:静态变量、实例变量和局部变量,分  另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。   但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。 - +```` public class FinalTest { final int p; final int q=3; @@ -210,19 +210,20 @@ final修饰的变量有三种:静态变量、实例变量和局部变量,分 q=i;//不能为一个final变量赋值 } } - +```` ### final内存分配 刚提到了内嵌机制,现在详细展开。 要知道调用一个函数除了函数本身的执行时间之外,还需要额外的时间去寻找这个函数(类内部有一个函数签名和函数地址的映射表)。所以减少函数调用次数就等于降低了性能消耗。 final修饰的函数会被编译器优化,优化的结果是减少了函数调用的次数。如何实现的,举个例子给你看: - +```` public class Test{ final void func(){System.out.println("g");}; public void main(String[] args){ for(int j=0;j<1000;j++) func(); }} + 经过编译器优化之后,这个类变成了相当于这样写: public class Test{ final void func(){System.out.println("g");}; @@ -230,7 +231,7 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调 for(int j=0;j<1000;j++) {System.out.println("g");} }} - +```` 看出来区别了吧?编译器直接将func的函数体内嵌到了调用函数的地方,这样的结果是节省了1000次函数调用,当然编译器处理成字节码,只是我们可以想象成这样,看个明白。 不过,当函数体太长的话,用final可能适得其反,因为经过编译器内嵌之后代码长度大大增加,于是就增加了jvm解释字节码的时间。 @@ -243,7 +244,7 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调 见下面的测试代码,我会执行五次: - +```` public class Test { public static void getJava() @@ -283,6 +284,7 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调 } } +```` 结果为: 第一次: @@ -308,13 +310,13 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调 由以上运行结果不难看出,执行最快的是“正常的执行”即代码直接编写,而使用final修饰的方法,不像有些书上或者文章上所说的那样,速度与效率与“正常的执行”无异,而是位于第二位,最差的是调用不加final修饰的方法。 -观点:加了比不加好一点。 + 观点:加了比不加好一点。 ### 使用final修饰变量会让变量的值不能被改变吗; 见代码: - +```` public class Final { public static void main(String[] args) @@ -335,18 +337,18 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调 red blue yellow white 看!,黑色变成了白色。 - -​ -​ 在使用findbugs插件时,就会提示public static String[] color = { "red", "blue", "yellow", "black" };这行代码不安全,但加上final修饰,这行代码仍然是不安全的,因为final没有做到保证变量的值不会被修改! -​ -​ 原因是:final关键字只能保证变量本身不能被赋与新值,而不能保证变量的内部结构不被修改。例如在main方法有如下代码Color.color = new String[]{""};就会报错了。 +```` + + 在使用findbugs插件时,就会提示public static String[] color = { "red", "blue", "yellow", "black" };这行代码不安全,但加上final修饰,这行代码仍然是不安全的,因为final没有做到保证变量的值不会被修改! + + 原因是:final关键字只能保证变量本身不能被赋与新值,而不能保证变量的内部结构不被修改。例如在main方法有如下代码Color.color = new String[]{""};就会报错了。 ### 如何保证数组内部不被修改 那可能有的同学就会问了,加上final关键字不能保证数组不会被外部修改,那有什么方法能够保证呢?答案就是降低访问级别,把数组设为private。这样的话,就解决了数组在外部被修改的不安全性,但也产生了另一个问题,那就是这个数组要被外部使用的。 解决这个问题见代码: - +```` import java.util.AbstractList; import java.util.List; @@ -384,7 +386,7 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调 } - +```` 这样就OK了,既保证了代码安全,又能让数组中的元素被访问了。 @@ -397,7 +399,7 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调 规则3:父类中private final方法,子类可以重新定义,这种情况不是重写。 代码示例 - +```` 规则1代码 public class FinalMethodTest @@ -429,7 +431,7 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调 // 下面方法定义将不会出现问题 public void test(){} } - +```` ## final 和 jvm的关系 @@ -439,8 +441,8 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调 2. 初次读一个包含 final 域的对象的引用,与随后初次读这个 final 域,这两个操作之间不能重排序。 下面,我们通过一些示例性的代码来分别说明这两个规则: - -
public class FinalExample {
+````
+public class FinalExample {
     int i;                            // 普通变量 
     final int j;                      //final 变量 
     static FinalExample obj;
@@ -460,7 +462,7 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调
         int b = object.j;                // 读 final 域 
     }
 }
-
+```` 这里假设一个线程 A 执行 writer () 方法,随后另一个线程 B 执行 reader () 方法。下面我们通过这两个线程的交互来说明这两个规则。 @@ -478,7 +480,7 @@ final修饰的函数会被编译器优化,优化的结果是减少了函数调 假设线程 B 读对象引用与读对象的成员域之间没有重排序(马上会说明为什么需要这个假设),下图是一种可能的执行时序: -![](https://static001.infoq.cn/resource/image/66/3a/6628576a54f0ba625c8c3af4586cef3a.jpg) +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/6628576a54f0ba625c8c3af4586cef3a.jpg) 在上图中,写普通域的操作被编译器重排序到了构造函数之外,读线程 B 错误的读取了普通变量 i 初始化之前的值。而写 final 域的操作,被写 final 域的重排序规则“限定”在了构造函数之内,读线程 B 正确的读取了 final 变量初始化之后的值。 @@ -511,8 +513,8 @@ reader() 方法包含三个操作: 上面我们看到的 final 域是基础数据类型,下面让我们看看如果 final 域是引用类型,将会有什么效果? 请看下列示例代码: - -
public class FinalReferenceExample {
+````
+public class FinalReferenceExample {
 final int[] intArray;                     //final 是引用类型 
 static FinalReferenceExample obj;
 
@@ -535,7 +537,7 @@ public static void reader () {              // 读线程 C 执行
     }
 }
 }
-
+```` 这里 final 域为一个引用类型,它引用一个 int 型的数组对象。对于引用类型,写 final 域的重排序规则对编译器和处理器增加了如下约束: @@ -543,14 +545,14 @@ public static void reader () { // 读线程 C 执行 对上面的示例程序,我们假设首先线程 A 执行 writerOne() 方法,执行完后线程 B 执行 writerTwo() 方法,执行完后线程 C 执行 reader () 方法。下面是一种可能的线程执行时序: -![](https://static001.infoq.cn/resource/image/29/db/29b097c36fd531028991826bb7c835db.png) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/29b097c36fd531028991826bb7c835db.png) 在上图中,1 是对 final 域的写入,2 是对这个 final 域引用的对象的成员域的写入,3 是把被构造的对象的引用赋值给某个引用变量。这里除了前面提到的 1 不能和 3 重排序外,2 和 3 也不能重排序。 JMM 可以确保读线程 C 至少能看到写线程 A 在构造函数中对 final 引用对象的成员域的写入。即 C 至少能看到数组下标 0 的值为 1。而写线程 B 对数组元素的写入,读线程 C 可能看的到,也可能看不到。JMM 不保证线程 B 的写入对读线程 C 可见,因为写线程 B 和读线程 C 之间存在数据竞争,此时的执行结果不可预知。 如果想要确保读线程 C 看到写线程 B 对数组元素的写入,写线程 B 和读线程 C 之间需要使用同步原语(lock 或 volatile)来确保内存可见性。 + ## 参考文章 https://www.infoq.cn/article/java-memory-model-6 diff --git "a/docs/java/basic/5\343\200\201Java\347\261\273\345\222\214\345\214\205.md" "b/docs/java/basic/5\343\200\201Java\347\261\273\345\222\214\345\214\205.md" index 4084792..89dda2c 100644 --- "a/docs/java/basic/5\343\200\201Java\347\261\273\345\222\214\345\214\205.md" +++ "b/docs/java/basic/5\343\200\201Java\347\261\273\345\222\214\345\214\205.md" @@ -1,28 +1,24 @@ # 目录 - * [Java中的包概念](#java中的包概念) +* [Java中的包概念](#java中的包概念) * [包的作用](#包的作用) * [package 的目录结构](#package-的目录结构) * [设置 CLASSPATH 系统变量](#设置-classpath-系统变量) - * [常用jar包](#常用jar包) +* [常用jar包](#常用jar包) * [java软件包的类型](#java软件包的类型) * [dt.jar](#dtjar) * [rt.jar](#rtjar) - * [*.java文件的奥秘](#java文件的奥秘) +* [*.java文件的奥秘](#java文件的奥秘) * [*.Java文件简介](#java文件简介) * [为什么一个java源文件中只能有一个public类?](#为什么一个java源文件中只能有一个public类?) * [Main方法](#main方法) * [外部类的访问权限](#外部类的访问权限) * [Java包的命名规则](#java包的命名规则) - * [参考文章](#参考文章) - * [微信公众号](#微信公众号) +* [参考文章](#参考文章) +* [微信公众号](#微信公众号) * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) ---- - - Java类 ---- - 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -74,11 +70,7 @@ Java 使用包(package)这种机制是为了防止命名冲突,访问控 package net.java.util; public class Something{ ... } - - - - -那么它的路径应该是 **net/java/util/Something.java** 这样保存的。 package(包) 的作用是把不同的 java 程序分类保存,更方便的被其他 java 程序调用。 +那么它的路径应该是**net/java/util/Something.java**这样保存的。 package(包) 的作用是把不同的 java 程序分类保存,更方便的被其他 java 程序调用。 一个包(package)可以定义为一组相互联系的类型(类、接口、枚举和注释),为这些类型提供访问保护和命名空间管理的功能。 @@ -91,8 +83,6 @@ Java 使用包(package)这种机制是为了防止命名冲突,访问控 由于包创建了新的命名空间(namespace),所以不会跟其他包中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单。 - - ### package 的目录结构 类放在包中会有两种主要的结果: @@ -104,28 +94,18 @@ Java 使用包(package)这种机制是为了防止命名冲突,访问控 将类、接口等类型的源码放在一个文本中,这个文件的名字就是这个类型的名字,并以.java作为扩展名。例如: - - - - -// 文件名 : Car.java package vehicle; public class Car { // 类实现 } - - - - +```` +// 文件名 : Car.java +package vehicle; +public class Car { +// 类实现 +} +```` 接下来,把源文件放在一个目录中,这个目录要对应类所在包的名字。 - - - - ....\vehicle\Car.java - - - - 现在,正确的类名和路径将会是如下样子: * 类名 -> vehicle.Car @@ -134,51 +114,49 @@ Java 使用包(package)这种机制是为了防止命名冲突,访问控 通常,一个公司使用它互联网域名的颠倒形式来作为它的包名.例如:互联网域名是 runoob.com,所有的包名都以 com.runoob 开头。包名中的每一个部分对应一个子目录。 -例如:有一个 **com.runoob.test** 的包,这个包包含一个叫做 Runoob.java 的源文件,那么相应的,应该有如下面的一连串子目录: - - - - +例如:有一个**com.runoob.test**的包,这个包包含一个叫做 Runoob.java 的源文件,那么相应的,应该有如下面的一连串子目录: ....\com\runoob\test\Runoob.java - - - - 编译的时候,编译器为包中定义的每个类、接口等类型各创建一个不同的输出文件,输出文件的名字就是这个类型的名字,并加上 .class 作为扩展后缀。 例如: - - - - - -// 文件名: Runoob.java package com.runoob.test; public class Runoob { } class Google { } - - - +```` +// 文件名: Runoob.java +package com.runoob.test; +public class Runoob { } +class Google { } +```` 现在,我们用-d选项来编译这个文件,如下: - -
$javac -d .  Runoob.java
- +```` + $javac -d . Runoob.java +```` 这样会像下面这样放置编译了的文件: -
.\com\runoob\test\Runoob.class  .\com\runoob\test\Google.class
- -你可以像下面这样来导入所有** \com\runoob\test\ **中定义的类、接口等: +```` + .\com\runoob\test\Runoob.class + .\com\runoob\test\Google.class +```` -
import com.runoob.test.*;
+你可以像下面这样来导入所有**\com\runoob\test\**中定义的类、接口等: +```` + import com.runoob.test.*; +```` 编译之后的 .class 文件应该和 .java 源文件一样,它们放置的目录应该跟包的名字对应起来。但是,并不要求 .class 文件的路径跟相应的 .java 的路径一样。你可以分开来安排源码和类的目录。 -
\sources\com\runoob\test\Runoob.java \classes\com\runoob\test\Google.class
+```` + \sources\com\runoob\test\Runoob.java + \classes\com\runoob\test\Google.class +```` 这样,你可以将你的类目录分享给其他的编程人员,而不用透露自己的源码。用这种方法管理源码和类文件可以让编译器和java 虚拟机(JVM)可以找到你程序中使用的所有类型。 -类目录的绝对路径叫做 **class path**。设置在系统变量 **CLASSPATH** 中。编译器和 java 虚拟机通过将 package 名字加到 class path 后来构造 .class 文件的路径。 +类目录的绝对路径叫做**class path**。设置在系统变量**CLASSPATH**中。编译器和 java 虚拟机通过将 package 名字加到 class path 后来构造 .class 文件的路径。 -\classes 是 class path,package 名字是 com.runoob.test,而编译器和 JVM 会在 \classes\com\runoob\test 中找 .class 文件。 +```` + \classes 是 class path,package 名字是 com.runoob.test,而编译器和 JVM 会在 \classes\com\runoob\test 中找 .class 文件。 +```` 一个 class path 可能会包含好几个路径,多路径应该用分隔符分开。默认情况下,编译器和 JVM 查找当前目录。JAR 文件按包含 Java 平台相关的类,所以他们的目录默认放在了 class path 中。 @@ -222,55 +200,54 @@ java包的作用是为了区别类名的命名空间   在 animals 包中加入一个接口(interface): - package animals; - - interface Animal { - public void eat(); - public void travel(); - } -  接下来,在同一个包中加入该接口的实现: - - package animals; - - /* 文件名 : MammalInt.java */ - public class MammalInt implements Animal{ - - public void eat(){ - System.out.println("Mammal eats"); - } - - public void travel(){ - System.out.println("Mammal travels"); - } - - public int noOfLegs(){ - return 0; - } - - public static void main(String args[]){ - MammalInt m = new MammalInt(); - m.eat(); - m.travel(); - } - } - +```` +package animals; +interface Animal { + public void eat(); + public void travel(); +} +```` + +接下来,在同一个包中加入该接口的实现: + +```` +package animals; +/* 文件名 : MammalInt.java */ +public class MammalInt implements Animal{ + public void eat(){ + System.out.println("Mammal eats"); + } + public void travel(){ + System.out.println("Mammal travels"); + } + public int noOfLegs(){ + return 0; + } + public static void main(String args[]){ + MammalInt m = new MammalInt(); + m.eat(); + m.travel(); + } +} +```` import 关键字 为了能够使用某一个包的成员,我们需要在 Java 程序中明确导入该包。使用 "import" 语句可完成此功能。 在 java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为: -1 -import package1[.package2…].(classname|*); -  如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。 +```` + import package1[.package2…].(classname|*); +```` + +如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。 通常,一个公司使用它互联网域名的颠倒形式来作为它的包名.例如:互联网域名是 runoob.com,所有的包名都以 com.runoob 开头。包名中的每一个部分对应一个子目录。 例如:有一个 com.runoob.test 的包,这个包包含一个叫做 Runoob.java 的源文件,那么相应的,应该有如下面的一连串子目录: - -1 -....\com\runoob\test\Runoob.java - +``` + ....\com\runoob\test\Runoob.java +``` ## 常用jar包 ### java软件包的类型 @@ -291,36 +268,30 @@ import package1[.package2…].(classname|*); 6) java.net:包含支持网络操作的类。 ### dt.jar -> SUN对于dt.jar的定义:Also includes dt.jar, the DesignTime archive of BeanInfo files that tell interactive development environments (IDE's) how to display the Java components and how to let the developer customize them for the application。 -中文翻译过来就是:dt.jar是BeanInfo文件的DesignTime归档,BeanInfo文件用来告诉集成开发环境(IDE)如何显示Java组件还有如何让开发人员根据应用程序自定义它们。这段文字中提到了几个关键字:DesignTime,BeanInfo,IDE,Java components。其实dt.jar就是DesignTime Archive的缩写。那么何为DesignTime。 - -    何为DesignTime?翻译过来就是设计时。其实了解JavaBean的人都知道design time和runtime(运行时)这两个术语的含义。设计时(DesignTIme)是指在开发环境中通过添加控件,设置控件或窗体属性等方法,建立应用程序的时间。 -    -    与此相对应的运行时(RunTIme)是指可以象用户那样与应用程序交互作用的时间。那么现在再理解一下上面的翻译,其实dt.jar包含了swing控件中的BeanInfo,而IDE的GUI Designer需要这些信息。那让我们看一下dt.jar中到底有什么?下面是一张dt.jar下面的内容截图: +> SUN对于dt.jar的定义:Also includesdt.jar, the DesignTime archive of BeanInfo files that tell interactive development environments (IDE's) how to display the Java components and how to let the developer customize them for the application。 -![image](http://www.blogjava.net/images/blogjava_net/landon/dt-jar.jpg) +中文翻译过来就是:dt.jar是BeanInfo文件的DesignTime归档,BeanInfo文件用来告诉集成开发环境(IDE)如何显示Java组件还有如何让开发人员根据应用程序自定义它们。这段文字中提到了几个关键字:DesignTime,BeanInfo,IDE,Java components。其实dt.jar就是DesignTime Archive的缩写。那么何为DesignTime。 -    从上面的截图可以看出,dt.jar中全部是Swing组件的BeanInfo。那么到底什么是BeanInfo呢? +何为DesignTime?翻译过来就是设计时。其实了解JavaBean的人都知道design time和runtime(运行时)这两个术语的含义。设计时(DesignTIme)是指在开发环境中通过添加控件,设置控件或窗体属性等方法,建立应用程序的时间。 -    何为BeanInfo?JavaBean和BeanInfo有很大的关系。Sun所制定的JavaBean规范,很大程度上是为IDE准备的——它让IDE能够以可视化的方式设置JavaBean的属性。如果在IDE中开发一个可视化应用程序,我们需要通过属性设置的方式对组成应用的各种组件进行定制,IDE通过属性编辑器让开发人员使用可视化的方式设置组件的属性。 +与此相对应的运行时(RunTIme)是指可以象用户那样与应用程序交互作用的时间。那么现在再理解一下上面的翻译,其实dt.jar包含了swing控件中的BeanInfo,而IDE的GUI Designer需要这些信息。那让我们看一下dt.jar中到底有什么? -    一般的IDE都支持JavaBean规范所定义的属性编辑器,当组件开发商发布一个组件时,它往往将组件对应的属性编辑器捆绑发行,这样开发者就可以在IDE环境下方便地利用属性编辑器对组件进行定制工作。JavaBean规范通过java.beans.PropertyEditor定义了设置JavaBean属性的方法,通过BeanInfo描述了JavaBean哪些属性是可定制的,此外还描述了可定制属性与PropertyEditor的对应关系。 +dt.jar中全部是Swing组件的BeanInfo。那么到底什么是BeanInfo呢? -    BeanInfo与JavaBean之间的对应关系,通过两者之间规范的命名确立:对应JavaBean的BeanInfo采用如下的命名规范:BeanInfo。当JavaBean连同其属性编辑器相同的组件注册到IDE中后,当在开发界面中对JavaBean进行定制时,IDE就会根据JavaBean规范找到对应的BeanInfo,再根据BeanInfo中的描述信息找到JavaBean属性描述(是否开放、使用哪个属性编辑器),进而为JavaBean生成特定开发编辑界面。 +何为BeanInfo?JavaBean和BeanInfo有很大的关系。Sun所制定的JavaBean规范,很大程度上是为IDE准备的——它让IDE能够以可视化的方式设置JavaBean的属性。如果在IDE中开发一个可视化应用程序,我们需要通过属性设置的方式对组成应用的各种组件进行定制,IDE通过属性编辑器让开发人员使用可视化的方式设置组件的属性。 -    dt.jar里面主要是swing组件的BeanInfo。IDE根据这些BeanInfo显示这些组件以及开发人员如何定制他们。 +dt.jar里面主要是swing组件的BeanInfo。IDE根据这些BeanInfo显示这些组件以及开发人员如何定制他们。 ### rt.jar rt.jar是runtime的归档。Java基础类库,也就是Java doc里面看到的所有的类的class文件。 -![image](https://img-blog.csdnimg.cn/20181115130130739.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Z1aGFuZ2hhbmc=,size_16,color_FFFFFF,t_70) +![image](https://www.pianshen.com/images/75/856cbbdf52da90fa4f9bbb7b0597ce63.png) - rt.jar 默认就在Root Classloader的加载路径里面的,而在Claspath配置该变量是不需要的;同时jre/lib目录下的其他jar:jce.jar、jsse.jar、charsets.jar、resources.jar都在Root Classloader中。 +rt.jar 默认就在Root Classloader的加载路径里面的,而在Claspath配置该变量是不需要的;同时jre/lib目录下的其他jar:jce.jar、jsse.jar、charsets.jar、resources.jar都在Root Classloader中。 -## *.java文件的奥秘 - -### *.Java文件简介 +## java文件的奥秘 +### Java文件简介 .java文件你可以认为只是一个文本文件, 这个文件即是用java语言写成的程序,或者说任务的代码块。 @@ -335,43 +306,41 @@ rt.jar是runtime的归档。Java基础类库,也就是Java doc里面看到的 这里的.class文件在计算的体系结构中本质上对应的是一种机器语言(而这里的机器叫作JVM),所以JVM本身是可以直接运行这里的.class文件。所以 你可以进一步地认为,.java与.class与其它的编程语法一样,它们都是程序员用来描述自己的任务的一种语言,只是它们面向的对象不一样,而计算机本身只能识别它自已定义的那些指令什么的(再次强调,这里的计算机本身没有那么严格的定义) > In short: -> +> > .java是Java的源文件后缀,里面存放程序员编写的功能代码。 -> +> > .class文件是字节码文件,由.java源文件通过javac命令编译后生成的文件。是可以运行在任何支持Java虚拟机的硬件平台和操作系统上的二进制文件。 -> +> > .class文件并不本地的可执行程序。Java虚拟机就是去运行.class文件从而实现程序的运行。 - - ### 为什么一个java源文件中只能有一个public类? -  在java编程思想(第四版)一书中有这样3段话(6.4 类的访问权限): +在java编程思想(第四版)一书中有这样3段话(6.4 类的访问权限): >   1.每个编译单元(文件)都只能有一个public类,这表示,每个编译单元都有单一的公共接口,用public类来表现。该接口可以按要求包含众多的支持包访问权限的类。如果在某个编译单元内有一个以上的public类,编译器就会给出错误信息。 -> +> >   2.public类的名称必须完全与含有该编译单元的文件名相同,包含大小写。如果不匹配,同样将得到编译错误。 -> +> >   3.虽然不是很常用,但编译单元内完全不带public类也是可能的。在这种情况下,可以随意对文件命名。 总结相关的几个问题: 1、一个”.java”源文件中是否可以包括多个类(不是内部类)?有什么限制? ->   答:可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。 +> 答:可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。 2、为什么一个文件中只能有一个public的类 ->   答:编译器在编译时,针对一个java源代码文件(也称为“编译单元”)只会接受一个public类。否则报错。 +> 答:编译器在编译时,针对一个java源代码文件(也称为“编译单元”)只会接受一个public类。否则报错。 3、在java文件中是否可以没有public类 ->   答:public类不是必须的,java文件中可以没有public类。 +> 答:public类不是必须的,java文件中可以没有public类。 4、为什么这个public的类的类名必须和文件名相同 ->   答: 是为了方便虚拟机在相应的路径中找到相应的类所对应的字节码文件。 +> 答: 是为了方便虚拟机在相应的路径中找到相应的类所对应的字节码文件。 ### Main方法 @@ -400,27 +369,25 @@ rt.jar是runtime的归档。Java基础类库,也就是Java doc里面看到的 为什么要对外部类或类做修饰呢? -> 1.存在包概念:public 和 default 能区分这个外部类能对不同包作一个划分 (default修饰的类,其他包中引入不了这个类,public修饰的类才能被import) -> +> 1.存在包概念:public 和 default 能区分这个外部类能对不同包作一个划分 (default修饰的类,其他包中引入不了这个类,public修饰的类才能被import) +> > 2.protected是包内可见并且子类可见,但是当一个外部类想要继承一个protected修饰的非同包类时,压根找不到这个类,更别提几层了 -> +> > 3.private修饰的外部类,其他任何外部类都无法导入它。 - - //Java中的文件名要和public修饰的类名相同,否则会报错 - //如果没有public修饰的类,则文件可以随意命名 - public class Java中的类文件 { - - } - - //非公共开类的访问权限默认是包访问权限,不能用private和protected - //一个外部类的访问权限只有两种,一种是包内可见,一种是包外可见。 - //如果用private修饰,其他类根本无法看到这个类,也就没有意义了。 - //如果用protected,虽然也是包内可见,但是如果有子类想要继承该类但是不同包时, - //压根找不到这个类,也不可能继承它了,所以干脆用default代替。 - class A{ - - } +```` +//Java中的文件名要和public修饰的类名相同,否则会报错 +//如果没有public修饰的类,则文件可以随意命名 +public class Java中的类文件 { +} +//非公共开类的访问权限默认是包访问权限,不能用private和protected +//一个外部类的访问权限只有两种,一种是包内可见,一种是包外可见。 +//如果用private修饰,其他类根本无法看到这个类,也就没有意义了。 +//如果用protected,虽然也是包内可见,但是如果有子类想要继承该类但是不同包时, +//压根找不到这个类,也不可能继承它了,所以干脆用default代替。 +class A{ +} +```` ### Java包的命名规则 @@ -458,6 +425,6 @@ https://blog.csdn.net/qq_36626914/article/details/80627454 作者是 985 硕士,蚂蚁金服 JAVA 工程师,专注于 JAVA 后端技术栈:SpringBoot、MySQL、分布式、中间件、微服务,同时也懂点投资理财,偶尔讲点算法和计算机理论基础,坚持学习和写作,相信终身学习的力量! -**程序员3T技术学习资源:** 一些程序员学习技术的资源大礼包,关注公众号后,后台回复关键字 **“资料”** 即可免费无套路获取。 +**程序员3T技术学习资源:** 一些程序员学习技术的资源大礼包,关注公众号后,后台回复关键字 **“资料”** 即可免费无套路获取。 ![](https://img-blog.csdnimg.cn/20190829222750556.jpg) diff --git "a/docs/java/basic/8\343\200\201Java\350\207\252\345\212\250\346\213\206\347\256\261\350\243\205\347\256\261\351\207\214\351\232\220\350\227\217\347\232\204\347\247\230\345\257\206.md" "b/docs/java/basic/8\343\200\201Java\350\207\252\345\212\250\346\213\206\347\256\261\350\243\205\347\256\261\351\207\214\351\232\220\350\227\217\347\232\204\347\247\230\345\257\206.md" index c6ed3af..0408300 100644 --- "a/docs/java/basic/8\343\200\201Java\350\207\252\345\212\250\346\213\206\347\256\261\350\243\205\347\256\261\351\207\214\351\232\220\350\227\217\347\232\204\347\247\230\345\257\206.md" +++ "b/docs/java/basic/8\343\200\201Java\350\207\252\345\212\250\346\213\206\347\256\261\350\243\205\347\256\261\351\207\214\351\232\220\350\227\217\347\232\204\347\247\230\345\257\206.md" @@ -18,10 +18,6 @@ * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) - - - Java基本数据类型 ---- - 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -148,14 +144,14 @@ float fn = Float.MIN_VALUE; //64位 double dx = Double.MAX_VALUE; double dn = Double.MIN_VALUE; -//16位 -char cx = Character.MAX_VALUE; -char cn = Character.MIN_VALUE; //1位 boolean bt = Boolean.TRUE; boolean bf = Boolean.FALSE; ``` +``` +打印它们的结果可以得到 + `127` `-128` `32767` @@ -168,10 +164,9 @@ boolean bf = Boolean.FALSE; `1.4E-45` `1.7976931348623157E308` `4.9E-324` -`￿` - `true` `false` +``` ### 引用类型 @@ -203,23 +198,23 @@ char a = 'A' ## 自动拆箱和装箱(详解) Java 5增加了自动装箱与自动拆箱机制,方便基本类型与包装类型的相互转换操作。在Java 5之前,如果要将一个int型的值转换成对应的包装器类型Integer,必须显式的使用new创建一个新的Integer对象,或者调用静态方法Integer.valueOf()。 - +```` //在Java 5之前,只能这样做 Integer value = new Integer(10); //或者这样做 Integer value = Integer.valueOf(10); //直接赋值是错误的 //Integer value = 10;` - +```` 在Java 5中,可以直接将整型赋给Integer对象,由编译器来完成从int型到Integer类型的转换,这就叫自动装箱。 - -`//在Java 5中,直接赋值是合法的,由编译器来完成转换` -`Integer value = 10;` -`与此对应的,自动拆箱就是可以将包装类型转换为基本类型,具体的转换工作由编译器来完成。` -`//在Java 5 中可以直接这么做` -`Integer value = new Integer(10);` -`int i = value;` - +```` +//在Java 5中,直接赋值是合法的,由编译器来完成转换 +Integer value = 10; +与此对应的,自动拆箱就是可以将包装类型转换为基本类型,具体的转换工作由编译器来完成。 +//在Java 5 中可以直接这么做 +Integer value = new Integer(10); +int i = value; +```` 自动装箱与自动拆箱为程序员提供了很大的方便,而在实际的应用中,自动装箱与拆箱也是使用最广泛的特性之一。自动装箱和自动拆箱其实是Java编译器提供的一颗语法糖(语法糖是指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通过可提高开发效率,增加代码可读性,增加代码的安全性)。 ### 简易实现 @@ -231,6 +226,7 @@ Integer value = Integer.valueOf(10); 实例方法xxxValue():将具体的包装类型对象转换成基本类型; 下面我们以int和Integer为例,说明Java中自动装箱与自动拆箱的实现机制。看如下代码: +```` class Auto //code1 { public static void main(String[] args) @@ -246,7 +242,10 @@ class Auto //code1 } } +```` + 上面的代码先将int型转为Integer对象,再讲Integer对象转换为int型,毫无疑问,这是可以正确运行的。可是,这种转换是怎么进行的呢?使用反编译工具,将生成的Class文件在反编译为Java文件,让我们看看发生了什么: +```` class Auto//code2 { public static void main(String[] paramArrayOfString) @@ -254,17 +253,11 @@ class Auto//code2 Integer localInteger = Integer.valueOf(10); int i = localInteger.intValue(); - - - - - - - Double localDouble = Double.valueOf(12.4D); double d = localDouble.doubleValue(); } } +```` 我们可以看到经过javac编译之后,code1的代码被转换成了code2,实际运行时,虚拟机运行的就是code2的代码。也就是说,虚拟机根本不知道有自动拆箱和自动装箱这回事;在将Java源文件编译为class文件的过程中,javac编译器在自动装箱的时候,调用了Integer.valueOf()方法,在自动拆箱时,又调用了intValue()方法。我们可以看到,double和Double也是如此。 实现总结:其实自动装箱和自动封箱是编译器为我们提供的一颗语法糖。在自动装箱时,编译器调用包装类型的valueOf()方法;在自动拆箱时,编译器调用了相应的xxxValue()方法。 @@ -274,128 +267,132 @@ class Auto//code2 Integer源码 +```` public final class Integer extends Number implements Comparable { - private final int value; - - /*Integer的构造方法,接受一个整型参数,Integer对象表示的int值,保存在value中*/ - public Integer(int value) { - this.value = value; - } - - /*equals()方法判断的是:所代表的int型的值是否相等*/ - public boolean equals(Object obj) { - if (obj instanceof Integer) { - return value == ((Integer)obj).intValue(); - } - return false; - } - - /*返回这个Integer对象代表的int值,也就是保存在value中的值*/ - public int intValue() { - return value; - } - - /** - * 首先会判断i是否在[IntegerCache.low,Integer.high]之间 - * 如果是,直接返回Integer.cache中相应的元素 - * 否则,调用构造方法,创建一个新的Integer对象 - */ - public static Integer valueOf(int i) { - assert IntegerCache.high >= 127; - if (i >= IntegerCache.low && i <= IntegerCache.high) - return IntegerCache.cache[i + (-IntegerCache.low)]; - return new Integer(i); - } - - /** - * 静态内部类,缓存了从[low,high]对应的Integer对象 - * low -128这个值不会被改变 - * high 默认是127,可以改变,最大不超过:Integer.MAX_VALUE - (-low) -1 - * cache 保存从[low,high]对象的Integer对象 - */ - private static class IntegerCache { - static final int low = -128; - static final int high; - static final Integer cache[]; - - static { - // high value may be configured by property - int h = 127; - String integerCacheHighPropValue = - sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); - if (integerCacheHighPropValue != null) { - int i = parseInt(integerCacheHighPropValue); - i = Math.max(i, 127); - // Maximum array size is Integer.MAX_VALUE - h = Math.min(i, Integer.MAX_VALUE - (-low) -1); - } - high = h; - - cache = new Integer[(high - low) + 1]; - int j = low; - for(int k = 0; k < cache.length; k++) - cache[k] = new Integer(j++); - } - - private IntegerCache() {} - } + private final int value; + + /*Integer的构造方法,接受一个整型参数,Integer对象表示的int值,保存在value中*/ + public Integer(int value) { + this.value = value; + } + + /*equals()方法判断的是:所代表的int型的值是否相等*/ + public boolean equals(Object obj) { + if (obj instanceof Integer) { + return value == ((Integer)obj).intValue(); + } + return false; + } + + /*返回这个Integer对象代表的int值,也就是保存在value中的值*/ + public int intValue() { + return value; + } + + /** + * 首先会判断i是否在[IntegerCache.low,Integer.high]之间 + * 如果是,直接返回Integer.cache中相应的元素 + * 否则,调用构造方法,创建一个新的Integer对象 + */ + public static Integer valueOf(int i) { + assert IntegerCache.high >= 127; + if (i >= IntegerCache.low && i <= IntegerCache.high) + return IntegerCache.cache[i + (-IntegerCache.low)]; + return new Integer(i); + } + + /** + * 静态内部类,缓存了从[low,high]对应的Integer对象 + * low -128这个值不会被改变 + * high 默认是127,可以改变,最大不超过:Integer.MAX_VALUE - (-low) -1 + * cache 保存从[low,high]对象的Integer对象 + */ + private static class IntegerCache { + static final int low = -128; + static final int high; + static final Integer cache[]; + + static { + // high value may be configured by property + int h = 127; + String integerCacheHighPropValue = + sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); + if (integerCacheHighPropValue != null) { + int i = parseInt(integerCacheHighPropValue); + i = Math.max(i, 127); + // Maximum array size is Integer.MAX_VALUE + h = Math.min(i, Integer.MAX_VALUE - (-low) -1); + } + high = h; + + cache = new Integer[(high - low) + 1]; + int j = low; + for(int k = 0; k < cache.length; k++) + cache[k] = new Integer(j++); + } + + private IntegerCache() {} + } } -以上是Oracle(Sun)公司JDK 1.7中Integer源码的一部分,通过分析上面的代码,得到: -1)Integer有一个实例域value,它保存了这个Integer所代表的int型的值,且它是final的,也就是说这个Integer对象一经构造完成,它所代表的值就不能再被改变。 -2)Integer重写了equals()方法,它通过比较两个Integer对象的value,来判断是否相等。 -3)重点是静态内部类IntegerCache,通过类名就可以发现:它是用来缓存数据的。它有一个数组,里面保存的是连续的Integer对象。 - (a) low:代表缓存数据中最小的值,固定是-128。 - (b) high:代表缓存数据中最大的值,它可以被该改变,默认是127。high最小是127,最大是Integer.MAX_VALUE-(-low)-1,如果high超过了这个值,那么cache[ ]的长度就超过Integer.MAX_VALUE了,也就溢出了。 - (c) cache[]:里面保存着从[low,high]所对应的Integer对象,长度是high-low+1(因为有元素0,所以要加1)。 -4)调用valueOf(int i)方法时,首先判断i是否在[low,high]之间,如果是,则复用Integer.cache[i-low]。比如,如果Integer.valueOf(3),直接返回Integer.cache[131];如果i不在这个范围,则调用构造方法,构造出一个新的Integer对象。 -5)调用intValue(),直接返回value的值。 -通过3)和4)可以发现,默认情况下,在使用自动装箱时,VM会复用[-128,127]之间的Integer对象。 - -Integer a1 = 1; -Integer a2 = 1; -Integer a3 = new Integer(1); -//会打印true,因为a1和a2是同一个对象,都是Integer.cache[129] -System.out.println(a1 == a2); -//false,a3构造了一个新的对象,不同于a1,a2 -System.out.println(a1 == a3); +```` + + 以上是Oracle(Sun)公司JDK 1.7中Integer源码的一部分,通过分析上面的代码,得到: + 1)Integer有一个实例域value,它保存了这个Integer所代表的int型的值,且它是final的,也就是说这个Integer对象一经构造完成,它所代表的值就不能再被改变。 + 2)Integer重写了equals()方法,它通过比较两个Integer对象的value,来判断是否相等。 + 3)重点是静态内部类IntegerCache,通过类名就可以发现:它是用来缓存数据的。它有一个数组,里面保存的是连续的Integer对象。 + (a) low:代表缓存数据中最小的值,固定是-128。 + (b) high:代表缓存数据中最大的值,它可以被该改变,默认是127。high最小是127,最大是Integer.MAX_VALUE-(-low)-1,如果high超过了这个值,那么cache[ ]的长度就超过Integer.MAX_VALUE了,也就溢出了。 + (c) cache[]:里面保存着从[low,high]所对应的Integer对象,长度是high-low+1(因为有元素0,所以要加1)。 + 4)调用valueOf(int i)方法时,首先判断i是否在[low,high]之间,如果是,则复用Integer.cache[i-low]。比如,如果Integer.valueOf(3),直接返回Integer.cache[131];如果i不在这个范围,则调用构造方法,构造出一个新的Integer对象。 + 5)调用intValue(),直接返回value的值。 + 通过3)和4)可以发现,默认情况下,在使用自动装箱时,VM会复用[-128,127]之间的Integer对象。 + + Integer a1 = 1; + Integer a2 = 1; + Integer a3 = new Integer(1); + //会打印true,因为a1和a2是同一个对象,都是Integer.cache[129] + System.out.println(a1 == a2); + //false,a3构造了一个新的对象,不同于a1,a2 + System.out.println(a1 == a3); ### 了解基本类型缓存(常量池)的最佳实践 - //基本数据类型的常量池是-128到127之间。 - // 在这个范围中的基本数据类的包装类可以自动拆箱,比较时直接比较数值大小。 - public static void main(String[] args) { - //int的自动拆箱和装箱只在-128到127范围中进行,超过该范围的两个integer的 == 判断是会返回false的。 - Integer a1 = 128; - Integer a2 = -128; - Integer a3 = -128; - Integer a4 = 128; - System.out.println(a1 == a4); - System.out.println(a2 == a3); - - Byte b1 = 127; - Byte b2 = 127; - Byte b3 = -128; - Byte b4 = -128; - //byte都是相等的,因为范围就在-128到127之间 - System.out.println(b1 == b2); - System.out.println(b3 == b4); - - // - Long c1 = 128L; - Long c2 = 128L; - Long c3 = -128L; - Long c4 = -128L; - System.out.println(c1 == c2); - System.out.println(c3 == c4); - - //char没有负值 - //发现char也是在0到127之间自动拆箱 - Character d1 = 128; - Character d2 = 128; - Character d3 = 127; - Character d4 = 127; - System.out.println(d1 == d2); - System.out.println(d3 == d4); +```` +//基本数据类型的常量池是-128到127之间。 +// 在这个范围中的基本数据类的包装类可以自动拆箱,比较时直接比较数值大小。 +public static void main(String[] args) { + +//int的自动拆箱和装箱只在-128到127范围中进行,超过该范围的两个integer的 == 判断是会返回false的。 +Integer a1 = 128; +Integer a2 = -128; +Integer a3 = -128; +Integer a4 = 128; +System.out.println(a1 == a4); +System.out.println(a2 == a3); + +Byte b1 = 127; +Byte b2 = 127; +Byte b3 = -128; +Byte b4 = -128; +//byte都是相等的,因为范围就在-128到127之间 +System.out.println(b1 == b2); +System.out.println(b3 == b4); + +Long c1 = 128L; +Long c2 = 128L; +Long c3 = -128L; +Long c4 = -128L; +System.out.println(c1 == c2); +System.out.println(c3 == c4); + +//char没有负值 +//发现char也是在0到127之间自动拆箱 +Character d1 = 128; +Character d2 = 128; +Character d3 = 127; +Character d4 = 127; +System.out.println(d1 == d2); +System.out.println(d3 == d4); `结果` @@ -409,27 +406,29 @@ System.out.println(a1 == a3); `true` - Integer i = 10; - Byte b = 10; - //比较Byte和Integer.两个对象无法直接比较,报错 - //System.out.println(i == b); - System.out.println("i == b " + i.equals(b)); - //答案是false,因为包装类的比较时先比较是否是同一个类,不是的话直接返回false. - int ii = 128; - short ss = 128; - long ll = 128; - char cc = 128; - System.out.println("ii == bb " + (ii == ss)); - System.out.println("ii == ll " + (ii == ll)); - System.out.println("ii == cc " + (ii == cc)); - - 结果 - i == b false - ii == bb true - ii == ll true - ii == cc true - - //这时候都是true,因为基本数据类型直接比较值,值一样就可以。 +Integer i = 10; +Byte b = 10; +//比较Byte和Integer.两个对象无法直接比较,报错 +//System.out.println(i == b); +System.out.println("i == b " + i.equals(b)); +//答案是false,因为包装类的比较时先比较是否是同一个类,不是的话直接返回false. + +int ii = 128; +short ss = 128; +long ll = 128; +char cc = 128; +System.out.println("ii == bb " + (ii == ss)); +System.out.println("ii == ll " + (ii == ll)); +System.out.println("ii == cc " + (ii == cc)); + +结果 +i == b false +ii == bb true +ii == ll true +ii == cc true + +//这时候都是true,因为基本数据类型直接比较值,值一样就可以。 +```` ### 总结: @@ -439,12 +438,10 @@ System.out.println(a1 == a3); (2)当需要一个基本类型时会自动拆箱,比如int a = new Integer(10);算术运算是在基本类型间进行的,所以当遇到算术运算时会自动拆箱,比如代码中的 c == (a + b); -(3) 包装类型 == 基本类型时,包装类型自动拆箱; +(3)包装类型 == 基本类型时,包装类型自动拆箱; 需要注意的是:“==”在没遇到算术运算时,不会自动拆箱;基本类型只会自动装箱为对应的包装类型,代码中最后一条说明的内容。 - - 在JDK 1.5中提供了自动装箱与自动拆箱,这其实是Java 编译器的语法糖,编译器通过调用包装类型的valueOf()方法实现自动装箱,调用xxxValue()方法自动拆箱。自动装箱和拆箱会有一些陷阱,那就是包装类型复用了某些对象。 (1)Integer默认复用了[-128,127]这些对象,其中高位置可以修改; @@ -468,11 +465,13 @@ Boolean没有自动装箱与拆箱,它也复用了Boolean.TRUE和Boolean.FALSE 上面自动拆箱和装箱的原理其实与常量池有关。 ### 存在栈中: -public void(int a) -{ -int i = 1; -int j = 1; -} + + public void(int a) + { + int i = 1; + int j = 1; + } + 方法中的i 存在虚拟机栈的局部变量表里,i是一个引用,j也是一个引用,它们都指向局部变量表里的整型值 1. int a是传值引用,所以a也会存在局部变量表。 @@ -485,50 +484,53 @@ i是类的成员变量。类实例化的对象存在堆中,所以成员变量 3 包装类对象怎么存 其实我们说的常量池也可以叫对象池。 + 比如String a= new String("a").intern()时会先在常量池找是否有“a"对象如果有的话直接返回“a"对象在常量池的地址,即让引用a指向常量”a"对象的内存地址。 -public native String intern(); Integer也是同理。 下图是Integer类型在常量池中查找同值对象的方法。 - public static Integer valueOf(int i) { - if (i >= IntegerCache.low && i <= IntegerCache.high) - return IntegerCache.cache[i + (-IntegerCache.low)]; - return new Integer(i); - } - private static class IntegerCache { - static final int low = -128; - static final int high; - static final Integer cache[]; - - static { - // high value may be configured by property - int h = 127; - String integerCacheHighPropValue = - sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); - if (integerCacheHighPropValue != null) { - try { - int i = parseInt(integerCacheHighPropValue); - i = Math.max(i, 127); - // Maximum array size is Integer.MAX_VALUE - h = Math.min(i, Integer.MAX_VALUE - (-low) -1); - } catch( NumberFormatException nfe) { - // If the property cannot be parsed into an int, ignore it. - } +```` +public static Integer valueOf(int i) { + if (i >= IntegerCache.low && i <= IntegerCache.high) + return IntegerCache.cache[i + (-IntegerCache.low)]; + return new Integer(i); +} +private static class IntegerCache { + static final int low = -128; + static final int high; + static final Integer cache[]; + + static { + // high value may be configured by property + int h = 127; + String integerCacheHighPropValue = + sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); + if (integerCacheHighPropValue != null) { + try { + int i = parseInt(integerCacheHighPropValue); + i = Math.max(i, 127); + // Maximum array size is Integer.MAX_VALUE + h = Math.min(i, Integer.MAX_VALUE - (-low) -1); + } catch( NumberFormatException nfe) { + // If the property cannot be parsed into an int, ignore it. } - high = h; - - cache = new Integer[(high - low) + 1]; - int j = low; - for(int k = 0; k < cache.length; k++) - cache[k] = new Integer(j++); - - // range [-128, 127] must be interned (JLS7 5.1.7) - assert IntegerCache.high >= 127; } - - private IntegerCache() {} + high = h; + + cache = new Integer[(high - low) + 1]; + int j = low; + for(int k = 0; k < cache.length; k++) + cache[k] = new Integer(j++); + + // range [-128, 127] must be interned (JLS7 5.1.7) + assert IntegerCache.high >= 127; } + + private IntegerCache() {} +} +```` + 所以基本数据类型的包装类型可以在常量池查找对应值的对象,找不到就会自动在常量池创建该值的对象。 而String类型可以通过intern来完成这个操作。 @@ -537,7 +539,6 @@ JDK1.7后,常量池被放入到堆空间中,这导致intern()函数的功能 ``` -[java] view plain copy String s = new String("1"); s.intern(); String s2 = "1"; @@ -554,9 +555,6 @@ JDK1.6以及以下:false false JDK1.7以及以上:false true ``` -![image](https://img-blog.csdn.net/20180422231916788?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E3MjQ4ODg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) - -![image](https://img-blog.csdn.net/20180422231929413?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E3MjQ4ODg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) JDK1.6查找到常量池存在相同值的对象时会直接返回该对象的地址。 JDK 1.7后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。 diff --git "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2724\357\274\232Java class\344\273\213\347\273\215\344\270\216\350\247\243\346\236\220\345\256\236\350\267\265.md" "b/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2724\357\274\232Java\345\255\227\350\212\202\347\240\201\344\273\213\347\273\215\344\270\216\350\247\243\346\236\220\345\256\236\350\267\265.md" similarity index 100% rename from "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2724\357\274\232Java class\344\273\213\347\273\215\344\270\216\350\247\243\346\236\220\345\256\236\350\267\265.md" rename to "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2724\357\274\232Java\345\255\227\350\212\202\347\240\201\344\273\213\347\273\215\344\270\216\350\247\243\346\236\220\345\256\236\350\267\265.md" diff --git "a/docs/java/jvm/\346\267\261\345\205\245\344\272\206\350\247\243JVM\350\231\232\346\213\237\346\234\2728\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" "b/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2728\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" similarity index 100% rename from "docs/java/jvm/\346\267\261\345\205\245\344\272\206\350\247\243JVM\350\231\232\346\213\237\346\234\2728\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" rename to "docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2728\357\274\232Java\347\232\204\347\274\226\350\257\221\346\234\237\344\274\230\345\214\226\344\270\216\350\277\220\350\241\214\346\234\237\344\274\230\345\214\226.md" From ed040831f5e5b92008008b284f03f51218f33a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=B0=8F=E6=96=9C?= <362294931@qq.com> Date: Mon, 3 Apr 2023 22:15:41 +0800 Subject: [PATCH 15/90] modify basic mds --- ...43\200\201Java\345\274\202\345\270\270.md" | 659 ++++++++--------- ...55\347\232\204\345\233\236\350\260\203.md" | 352 +++++---- ...12\343\200\201\345\217\215\345\260\204.md" | 561 +++++++-------- ...13\343\200\201\346\263\233\345\236\213.md" | 666 +++++++++-------- ...01\346\236\232\344\270\276\347\261\273.md" | 329 ++++----- ...00\344\275\263\345\256\236\350\267\265.md" | 123 ++-- .../16\343\200\201JavaIO\346\265\201.md" | 127 ++-- ...01\345\244\232\347\272\277\347\250\213.md" | 9 - ...43\345\206\205\351\203\250\347\261\273.md" | 568 +++++++-------- ...06\346\236\266\346\242\263\347\220\206.md" | 680 +++++++++--------- .../20\343\200\201javac\345\222\214javap.md" | 439 ++++++----- ...10\346\236\201\346\214\207\345\215\227.md" | 468 ++++++------ ...15\345\272\217\345\210\227\345\214\226.md" | 610 ++++++++-------- ...36\347\216\260\345\216\237\347\220\206.md" | 531 +++++--------- ...73\345\222\214\346\216\245\345\217\243.md" | 149 ++-- ...47\350\241\214\351\241\272\345\272\217.md" | 85 ++- ...\261\273\345\222\214Object\347\261\273.md" | 166 ++--- 17 files changed, 3118 insertions(+), 3404 deletions(-) diff --git "a/docs/java/basic/10\343\200\201Java\345\274\202\345\270\270.md" "b/docs/java/basic/10\343\200\201Java\345\274\202\345\270\270.md" index c937417..7e76b60 100644 --- "a/docs/java/basic/10\343\200\201Java\345\274\202\345\270\270.md" +++ "b/docs/java/basic/10\343\200\201Java\345\274\202\345\270\270.md" @@ -19,11 +19,6 @@ * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) ---- - - - Java异常 ---- - 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -51,7 +46,7 @@ > 3、 由调用函数来分析异常,这要求程序员对库函数有很深的了解。 > - 在OO中提供的异常处理机制是提供代码健壮的强有力的方式。使用异常机制它能够降低错误处理代码的复杂度,如果不使用异常,那么就必须检查特定的错误,并在程序中的许多地方去处理它。 +在OO中提供的异常处理机制是提供代码健壮的强有力的方式。使用异常机制它能够降低错误处理代码的复杂度,如果不使用异常,那么就必须检查特定的错误,并在程序中的许多地方去处理它。 而如果使用异常,那就不必在方法调用处进行检查,因为异常机制将保证能够捕获这个错误,并且,只需在一个地方处理错误,即所谓的异常处理程序中。 @@ -61,21 +56,20 @@ ## 异常基本定义 -> 在《Think in java》中是这样定义异常的:异常情形是指阻止当前方法或者作用域继续执行的问题。在这里一定要明确一点:异常代码某种程度的错误,尽管Java有异常处理机制,但是我们不能以“正常”的眼光来看待异常,异常处理机制的原因就是告诉你:这里可能会或者已经产生了错误,您的程序出现了不正常的情况,可能会导致程序失败! +> 在《Think in java》中是这样定义异常的:异常情形是指阻止当前方法或者作用域继续执行的问题。在这里一定要明确一点:异常代码某种程度的错误,尽管Java有异常处理机制,但是我们不能以“正常”的眼光来看待异常,异常处理机制的原因就是告诉你:这里可能会或者已经产生了错误,您的程序出现了不正常的情况,可能会导致程序失败! -> 那么什么时候才会出现异常呢?只有在你当前的环境下程序无法正常运行下去,也就是说程序已经无法来正确解决问题了,这时它所就会从当前环境中跳出,并抛出异常。抛出异常后,它首先会做几件事。 +> 那么什么时候才会出现异常呢?只有在你当前的环境下程序无法正常运行下去,也就是说程序已经无法来正确解决问题了,这时它所就会从当前环境中跳出,并抛出异常。抛出异常后,它首先会做几件事。 > 首先,它会使用new创建一个异常对象,然后在产生异常的位置终止程序,并且从当前环境中弹出对异常对象的引用,这时。异常处理机制就会接管程序,并开始寻找一个恰当的地方来继续执行程序,这个恰当的地方就是异常处理程序。 -> 总的来说异常处理机制就是当程序发生异常时,它强制终止程序运行,记录异常信息并将这些信息反馈给我们,由我们来确定是否处理异常。 +> 总的来说异常处理机制就是当程序发生异常时,它强制终止程序运行,记录异常信息并将这些信息反馈给我们,由我们来确定是否处理异常。 ## 异常体系 -[外链图片转存失败(img-KNxcBTK0-1569073569353)(https://images0.cnblogs.com/blog/381060/201311/22185952-834d92bc2bfe498f9a33414cc7a2c8a4.png)] +![img](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/22185952-834d92bc2bfe498f9a33414cc7a2c8a4.png) 从上面这幅图可以看出,Throwable是java语言中所有错误和异常的超类(万物即可抛)。它有两个子类:Error、Exception。 - Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。 Throwable又派生出Error类和Exception类。 @@ -103,7 +97,7 @@ Throwable又派生出Error类和Exception类。 异常是在执行某个函数时引发的,而函数又是层级调用,形成调用栈的,因为,只要一个函数发生了异常,那么他的所有的caller都会被异常影响。当这些被影响的函数以异常信息输出时,就形成的了异常追踪栈。 异常最先发生的地方,叫做异常抛出点。 - +```` public class 异常 { public static void main (String [] args ) { @@ -130,9 +124,7 @@ Throwable又派生出Error类和Exception类。 // at com.javase.异常.异常.devide(异常.java:24) // at com.javase.异常.异常.CMDCalculate(异常.java:19) // at com.javase.异常.异常.main(异常.java:12) - - -​ + // ----欢迎使用命令行除法计算器---- // r // Exception in thread "main" java.util.InputMismatchException @@ -143,8 +135,7 @@ Throwable又派生出Error类和Exception类。 // at com.javase.异常.异常.CMDCalculate(异常.java:17) // at com.javase.异常.异常.main(异常.java:12) - -[外链图片转存失败(img-9rqUQJQj-1569073569354)(http://incdn1.b0.upaiyun.com/2017/09/0b3e4ca2f4cf8d7116c7ad354940601f.png)] +```` 从上面的例子可以看出,当devide函数发生除0异常时,devide函数将抛出ArithmeticException异常,因此调用他的CMDCalculate函数也无法正常完成,因此也发送异常,而CMDCalculate的caller——main 因为CMDCalculate抛出异常,也发生了异常,这样一直向调用栈的栈底回溯。 @@ -157,7 +148,7 @@ Throwable又派生出Error类和Exception类。 ## 异常和错误 下面看一个例子 - +```` //错误即error一般指jvm无法处理的错误 //异常是Java定义的用于简化错误处理流程和定位错误的一种工具。 public class 错误和错误 { @@ -198,7 +189,7 @@ Throwable又派生出Error类和Exception类。 // at com.javase.异常.错误.main(错误.java:11) } - +```` ## 异常的处理方式 在编写代码处理异常时,对于检查异常,有2种不同的处理方式: @@ -213,7 +204,7 @@ Throwable又派生出Error类和Exception类。 上面的例子是运行时异常,不需要显示捕获。 下面这个例子是可检查异常需,要显示捕获或者抛出。 - +```` @Test public void testException() throws IOException { @@ -309,6 +300,7 @@ Throwable又派生出Error类和Exception类。 // at com.javase.异常.异常处理方式.test(异常处理方式.java:62) throw new StringIndexOutOfBoundsException(); } +```` 其实有的语言在遇到异常后仍然可以继续运行 @@ -321,11 +313,12 @@ Throwable又派生出Error类和Exception类。 throws是另一种处理异常的方式,它不同于try…catch…finally,throws仅仅是将函数中可能出现的异常向调用者声明,而自己则不具体处理。 采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好,调用者需要为可能发生的异常负责。 - - public void foo() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN - { - //foo内部可以抛出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 类的异常,或者他们的子类的异常对象。 - } +```` +public void foo() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN +{ + //foo内部可以抛出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 类的异常,或者他们的子类的异常对象。 +} +```` ## 纠结的finally @@ -341,6 +334,7 @@ finally块不管异常是否发生,只要对应的try执行了,则它一定 3、在同一try…catch…finally块中 ,try发生异常,且匹配的catch块中处理异常时也抛出异常,那么后面的finally也会执行:首先执行finally块,然后去外围调用者中寻找合适的catch块。 +```` public class finally使用 { public static void main(String[] args) { try { @@ -357,6 +351,7 @@ finally块不管异常是否发生,只要对应的try执行了,则它一定 } } } +```` ## throw : JRE也使用的关键字 @@ -365,15 +360,14 @@ throw exceptionObject 程序员也可以通过throw语句手动显式的抛出一个异常。throw语句的后面必须是一个异常对象。 throw 语句必须写在函数中,执行throw 语句的地方就是一个异常抛出点,==它和由JRE自动形成的异常抛出点没有任何差别。== - - public void save(User user) - { - if(user == null) - throw new IllegalArgumentException("User对象为空"); - //...... - - } - +```` +public void save(User user) +{ + if(user == null) + throw new IllegalArgumentException("User对象为空"); + //...... +} +```` 后面开始的大部分内容都摘自http://www.cnblogs.com/lulipro/p/7504267.html 该文章写的十分细致到位,令人钦佩,是我目前为之看到关于异常最详尽的文章,可以说是站在巨人的肩膀上了。 @@ -389,88 +383,86 @@ throw 语句必须写在函数中,执行throw 语句的地方就是一个异 > 异常链化:以一个异常对象为参数构造新的异常对象。新的异对象将包含先前异常的信息。这项技术主要是异常类的一个带Throwable参数的函数来实现的。这个当做参数的异常,我们叫他根源异常(cause)。 查看Throwable类源码,可以发现里面有一个Throwable字段cause,就是它保存了构造时传递的根源异常参数。这种设计和链表的结点类设计如出一辙,因此形成链也是自然的了。 - - public class Throwable implements Serializable { - private Throwable cause = this; - - public Throwable(String message, Throwable cause) { - fillInStackTrace(); - detailMessage = message; - this.cause = cause; - } - public Throwable(Throwable cause) { - fillInStackTrace(); - detailMessage = (cause==null ? null : cause.toString()); - this.cause = cause; - } - - //........ +```` +public class Throwable implements Serializable { + private Throwable cause = this; + public Throwable(String message, Throwable cause) { + fillInStackTrace(); + detailMessage = message; + this.cause = cause; } - + public Throwable(Throwable cause) { + fillInStackTrace(); + detailMessage = (cause==null ? null : cause.toString()); + this.cause = cause; + } + //........ +} +```` 下面看一个比较实在的异常链例子哈 - - public class 异常链 { - @Test - public void test() { - C(); - } - public void A () throws Exception { - try { - int i = 1; - i = i / 0; - //当我注释掉这行代码并使用B方法抛出一个error时,运行结果如下 - // 四月 27, 2018 10:12:30 下午 org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines - // 信息: Discovered TestEngines with IDs: [junit-jupiter] - // java.lang.Error: B也犯了个错误 - // at com.javase.异常.异常链.B(异常链.java:33) - // at com.javase.异常.异常链.C(异常链.java:38) - // at com.javase.异常.异常链.test(异常链.java:13) - // at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - // Caused by: java.lang.Error - // at com.javase.异常.异常链.B(异常链.java:29) - - }catch (ArithmeticException e) { - //这里通过throwable类的构造方法将最底层的异常重新包装并抛出,此时注入了A方法的信息。最后打印栈信息时可以看到caused by - A方法的异常。 - //如果直接抛出,栈信息打印结果只能看到上层方法的错误信息,不能看到其实是A发生了错误。 - //所以需要包装并抛出 - throw new Exception("A方法计算错误", e); - } - +```` +public class 异常链 { + @Test + public void test() { + C(); + } + public void A () throws Exception { + try { + int i = 1; + i = i / 0; + //当我注释掉这行代码并使用B方法抛出一个error时,运行结果如下 +// 四月 27, 2018 10:12:30 下午 org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines +// 信息: Discovered TestEngines with IDs: [junit-jupiter] +// java.lang.Error: B也犯了个错误 +// at com.javase.异常.异常链.B(异常链.java:33) +// at com.javase.异常.异常链.C(异常链.java:38) +// at com.javase.异常.异常链.test(异常链.java:13) +// at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) +// Caused by: java.lang.Error +// at com.javase.异常.异常链.B(异常链.java:29) + + }catch (ArithmeticException e) { + //这里通过throwable类的构造方法将最底层的异常重新包装并抛出,此时注入了A方法的信息。最后打印栈信息时可以看到caused by + A方法的异常。 + //如果直接抛出,栈信息打印结果只能看到上层方法的错误信息,不能看到其实是A发生了错误。 + //所以需要包装并抛出 + throw new Exception("A方法计算错误", e); } - public void B () throws Exception,Error { - try { - //接收到A的异常, - A(); - throw new Error(); - }catch (Exception e) { - throw e; - }catch (Error error) { - throw new Error("B也犯了个错误", error); - } + + } + public void B () throws Exception,Error { + try { + //接收到A的异常, + A(); + throw new Error(); + }catch (Exception e) { + throw e; + }catch (Error error) { + throw new Error("B也犯了个错误", error); } - public void C () { - try { - B(); - }catch (Exception | Error e) { - e.printStackTrace(); - } - + } + public void C () { + try { + B(); + }catch (Exception | Error e) { + e.printStackTrace(); } - - //最后结果 - // java.lang.Exception: A方法计算错误 - // at com.javase.异常.异常链.A(异常链.java:18) - // at com.javase.异常.异常链.B(异常链.java:24) - // at com.javase.异常.异常链.C(异常链.java:31) - // at com.javase.异常.异常链.test(异常链.java:11) - // 省略 - // Caused by: java.lang.ArithmeticException: / by zero - // at com.javase.异常.异常链.A(异常链.java:16) - // ... 31 more + } + //最后结果 +// java.lang.Exception: A方法计算错误 +// at com.javase.异常.异常链.A(异常链.java:18) +// at com.javase.异常.异常链.B(异常链.java:24) +// at com.javase.异常.异常链.C(异常链.java:31) +// at com.javase.异常.异常链.test(异常链.java:11) +// 省略 +// Caused by: java.lang.ArithmeticException: / by zero +// at com.javase.异常.异常链.A(异常链.java:16) +// ... 31 more +} +```` ## 自定义异常 如果要自定义异常类,则扩展Exception类即可,因此这样的自定义异常都属于检查异常(checked exception)。如果要自定义非检查异常,则扩展自RuntimeException。 @@ -482,7 +474,7 @@ throw 语句必须写在函数中,执行throw 语句的地方就是一个异 一个带有String参数和Throwable参数,并都传递给父类构造函数 一个带有Throwable 参数的构造函数,并传递给父类的构造函数。 下面是IOException类的完整源代码,可以借鉴。 - +```` public class IOException extends Exception { static final long serialVersionUID = 7818375828146090155L; @@ -507,7 +499,7 @@ throw 语句必须写在函数中,执行throw 语句的地方就是一个异 super(cause); } } - +```` ## 异常的注意事项 异常的注意事项 @@ -517,47 +509,47 @@ throw 语句必须写在函数中,执行throw 语句的地方就是一个异 > 例如,父类方法throws 的是2个异常,子类就不能throws 3个及以上的异常。父类throws IOException,子类就必须throws IOException或者IOException的子类。 至于为什么?我想,也许下面的例子可以说明。 - - class Father +```` +class Father +{ + public void start() throws IOException { - public void start() throws IOException - { - throw new IOException(); - } + throw new IOException(); } - - class Son extends Father +} + +class Son extends Father +{ + public void start() throws Exception { - public void start() throws Exception - { - throw new SQLException(); - } + throw new SQLException(); } +} /**********************假设上面的代码是允许的(实质是错误的)***********************/ - class Test +class Test +{ + public static void main(String[] args) { - public static void main(String[] args) + Father[] objs = new Father[2]; + objs[0] = new Father(); + objs[1] = new Son(); + + for(Father obj:objs) { - Father[] objs = new Father[2]; - objs[0] = new Father(); - objs[1] = new Son(); - - for(Father obj:objs) + //因为Son类抛出的实质是SQLException,而IOException无法处理它。 + //那么这里的try。。catch就不能处理Son中的异常。 + //多态就不能实现了。 + try { + obj.start(); + }catch(IOException) { - //因为Son类抛出的实质是SQLException,而IOException无法处理它。 - //那么这里的try。。catch就不能处理Son中的异常。 - //多态就不能实现了。 - try { - obj.start(); - }catch(IOException) - { - //处理IOException - } - } - } - } - + //处理IOException + } + } + } +} +```` ==Java的异常执行流程是线程独立的,线程之间没有影响== > Java程序可以是多线程的。每一个线程都是一个独立的执行流,独立的函数调用栈。如果程序只有一个线程,那么没有被任何代码处理的异常 会导致程序终止。如果是多线程的,那么没有被任何代码处理的异常仅仅会导致异常所在的线程结束。 @@ -565,44 +557,44 @@ throw 语句必须写在函数中,执行throw 语句的地方就是一个异 > 也就是说,Java中的异常是线程独立的,线程的问题应该由线程自己来解决,而不要委托到外部,也不会直接影响到其它线程的执行。 下面看一个例子 - - public class 多线程的异常 { - @Test - public void test() { - go(); - } - public void go () { - ExecutorService executorService = Executors.newFixedThreadPool(3); - for (int i = 0;i <= 2;i ++) { - int finalI = i; - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - executorService.execute(new Runnable() { - @Override - //每个线程抛出异常时并不会影响其他线程的继续执行 - public void run() { - try { - System.out.println("start thread" + finalI); - throw new Exception(); - }catch (Exception e) { - System.out.println("thread" + finalI + " go wrong"); - } - } - }); +```` +public class 多线程的异常 { + @Test + public void test() { + go(); + } + public void go () { + ExecutorService executorService = Executors.newFixedThreadPool(3); + for (int i = 0;i <= 2;i ++) { + int finalI = i; + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); } - // 结果: - // start thread0 - // thread0 go wrong - // start thread1 - // thread1 go wrong - // start thread2 - // thread2 go wrong - } + executorService.execute(new Runnable() { + @Override + //每个线程抛出异常时并不会影响其他线程的继续执行 + public void run() { + try { + System.out.println("start thread" + finalI); + throw new Exception(); + }catch (Exception e) { + System.out.println("thread" + finalI + " go wrong"); + } + } + }); + } +// 结果: +// start thread0 +// thread0 go wrong +// start thread1 +// thread1 go wrong +// start thread2 +// thread2 go wrong } - +} +```` ## 当finally遇上return @@ -610,163 +602,160 @@ throw 语句必须写在函数中,执行throw 语句的地方就是一个异 首先一个不容易理解的事实: 在 try块中即便有return,break,continue等改变执行流的语句,finally也会执行。 +```` +public static void main(String[] args) +{ + int re = bar(); + System.out.println(re); +} +private static int bar() +{ + try{ + return 5; + } finally{ + System.out.println("finally"); + } +} +/*输出: +finally +*/ +```` - public static void main(String[] args) +也就是说:try…catch…finally中的return 只要能执行,就都执行了,他们共同向同一个内存地址(假设地址是0×80)写入返回值,后执行的将覆盖先执行的数据,而真正被调用者取的返回值就是最后一次写入的。那么,按照这个思想,下面的这个例子也就不难理解了。 + +finally中的return 会覆盖 try 或者catch中的返回值。 +```` +public static void main(String[] args) { - int re = bar(); - System.out.println(re); + int result; + + result = foo(); + System.out.println(result); /////////2 + + result = bar(); + System.out.println(result); /////////2 } - private static int bar() + + @SuppressWarnings("finally") + public static int foo() { - try{ - return 5; + trz{ + int a = 5 / 0; + } catch (Exception e){ + return 1; } finally{ - System.out.println("finally"); + return 2; } + } - /*输出: - finally - */ -很多人面对这个问题时,总是在归纳执行的顺序和规律,不过我觉得还是很难理解。我自己总结了一个方法。用如下GIF图说明。 - - -[外链图片转存失败(img-SceF4t85-1569073569354)(http://incdn1.b0.upaiyun.com/2017/09/0471c2805ebd5a463211ced478eaf7f8.gif)] - -也就是说:try…catch…finally中的return 只要能执行,就都执行了,他们共同向同一个内存地址(假设地址是0×80)写入返回值,后执行的将覆盖先执行的数据,而真正被调用者取的返回值就是最后一次写入的。那么,按照这个思想,下面的这个例子也就不难理解了。 - - -finally中的return 会覆盖 try 或者catch中的返回值。 - - public static void main(String[] args) - { - int result; - - result = foo(); - System.out.println(result); /////////2 - - result = bar(); - System.out.println(result); /////////2 - } - - @SuppressWarnings("finally") - public static int foo() - { - trz{ - int a = 5 / 0; - } catch (Exception e){ - return 1; - } finally{ - return 2; - } - - } - - @SuppressWarnings("finally") - public static int bar() - { - try { - return 1; - }finally { - return 2; - } + + @SuppressWarnings("finally") + public static int bar() + { + try { + return 1; + }finally { + return 2; } - + } +```` finally中的return会抑制(消灭)前面try或者catch块中的异常 - - class TestException +```` +class TestException +{ + public static void main(String[] args) { - public static void main(String[] args) - { - int result; - try{ - result = foo(); - System.out.println(result); //输出100 - } catch (Exception e){ - System.out.println(e.getMessage()); //没有捕获到异常 - } - - try{ - result = bar(); - System.out.println(result); //输出100 - } catch (Exception e){ - System.out.println(e.getMessage()); //没有捕获到异常 - } + int result; + try{ + result = foo(); + System.out.println(result); //输出100 + } catch (Exception e){ + System.out.println(e.getMessage()); //没有捕获到异常 } - - //catch中的异常被抑制 - @SuppressWarnings("finally") - public static int foo() throws Exception - { - try { - int a = 5/0; - return 1; - }catch(ArithmeticException amExp) { - throw new Exception("我将被忽略,因为下面的finally中使用了return"); - }finally { - return 100; - } + + try{ + result = bar(); + System.out.println(result); //输出100 + } catch (Exception e){ + System.out.println(e.getMessage()); //没有捕获到异常 } - - //try中的异常被抑制 - @SuppressWarnings("finally") - public static int bar() throws Exception - { - try { - int a = 5/0; - return 1; - }finally { - return 100; - } + } + + //catch中的异常被抑制 + @SuppressWarnings("finally") + public static int foo() throws Exception + { + try { + int a = 5/0; + return 1; + }catch(ArithmeticException amExp) { + throw new Exception("我将被忽略,因为下面的finally中使用了return"); + }finally { + return 100; } } + + //try中的异常被抑制 + @SuppressWarnings("finally") + public static int bar() throws Exception + { + try { + int a = 5/0; + return 1; + }finally { + return 100; + } + } +} +```` finally中的异常会覆盖(消灭)前面try或者catch中的异常 - - class TestException +```` +class TestException +{ + public static void main(String[] args) { - public static void main(String[] args) - { - int result; - try{ - result = foo(); - } catch (Exception e){ - System.out.println(e.getMessage()); //输出:我是finaly中的Exception - } - - try{ - result = bar(); - } catch (Exception e){ - System.out.println(e.getMessage()); //输出:我是finaly中的Exception - } + int result; + try{ + result = foo(); + } catch (Exception e){ + System.out.println(e.getMessage()); //输出:我是finaly中的Exception } - - //catch中的异常被抑制 - @SuppressWarnings("finally") - public static int foo() throws Exception - { - try { - int a = 5/0; - return 1; - }catch(ArithmeticException amExp) { - throw new Exception("我将被忽略,因为下面的finally中抛出了新的异常"); - }finally { - throw new Exception("我是finaly中的Exception"); - } + + try{ + result = bar(); + } catch (Exception e){ + System.out.println(e.getMessage()); //输出:我是finaly中的Exception } - - //try中的异常被抑制 - @SuppressWarnings("finally") - public static int bar() throws Exception - { - try { - int a = 5/0; - return 1; - }finally { - throw new Exception("我是finaly中的Exception"); - } - + } + + //catch中的异常被抑制 + @SuppressWarnings("finally") + public static int foo() throws Exception + { + try { + int a = 5/0; + return 1; + }catch(ArithmeticException amExp) { + throw new Exception("我将被忽略,因为下面的finally中抛出了新的异常"); + }finally { + throw new Exception("我是finaly中的Exception"); } } - + + //try中的异常被抑制 + @SuppressWarnings("finally") + public static int bar() throws Exception + { + try { + int a = 5/0; + return 1; + }finally { + throw new Exception("我是finaly中的Exception"); + } + + } +} +```` 上面的3个例子都异于常人的编码思维,因此我建议: > 不要在fianlly中使用return。 @@ -782,22 +771,24 @@ finally中的异常会覆盖(消灭)前面try或者catch中的异常   下面是我个人总结的在Java和J2EE开发者在面试中经常被问到的有关Exception和Error的知识。在分享我的回答的时候,我也给这些问题作了快速修订,并且提供源码以便深入理解。我总结了各种难度的问题,适合新手码农和高级Java码农。如果你遇到了我列表中没有的问题,并且这个问题非常好,请在下面评论中分享出来。你也可以在评论中分享你面试时答错的情况。 **1) Java中什么是Exception?** -  这个问题经常在第一次问有关异常的时候或者是面试菜鸟的时候问。我从来没见过面高级或者资深工程师的时候有人问这玩意,但是对于菜鸟,是很愿意问这个的。简单来说,异常是Java传达给你的系统和程序错误的方式。在java中,异常功能是通过实现比如Throwable,Exception,RuntimeException之类的类,然后还有一些处理异常时候的关键字,比如throw,throws,try,catch,finally之类的。 所有的异常都是通过Throwable衍生出来的。Throwable把错误进一步划分为 java.lang.Exception -和 java.lang.Error.  java.lang.Error 用来处理系统错误,例如java.lang.StackOverFlowError 之类的。然后 Exception用来处理程序错误,请求的资源不可用等等。 +这个问题经常在第一次问有关异常的时候或者是面试菜鸟的时候问。我从来没见过面高级或者资深工程师的时候有人问这玩意,但是对于菜鸟,是很愿意问这个的。简单来说,异常是Java传达给你的系统和程序错误的方式。在java中,异常功能是通过实现比如Throwable,Exception,RuntimeException之类的类,然后还有一些处理异常时候的关键字,比如throw,throws,try,catch,finally之类的。所有的异常都是通过Throwable衍生出来的。Throwable把错误进一步划分为java.lang.Exception +和 java.lang.Error. + +java.lang.Error 用来处理系统错误,例如java.lang.StackOverFlowError 之类的。然后Exception用来处理程序错误,请求的资源不可用等等。 **2) Java中的检查型异常和非检查型异常有什么区别?** -  这又是一个非常流行的Java异常面试题,会出现在各种层次的Java面试中。检查型异常和非检查型异常的主要区别在于其处理方式。检查型异常需要使用try, catch和finally关键字在编译期进行处理,否则会出现编译器会报错。对于非检查型异常则不需要这样做。Java中所有继承自java.lang.Exception类的异常都是检查型异常,所有继承自RuntimeException的异常都被称为非检查型异常。 +这又是一个非常流行的Java异常面试题,会出现在各种层次的Java面试中。检查型异常和非检查型异常的主要区别在于其处理方式。检查型异常需要使用try, catch和finally关键字在编译期进行处理,否则会出现编译器会报错。对于非检查型异常则不需要这样做。Java中所有继承自java.lang.Exception类的异常都是检查型异常,所有继承自RuntimeException的异常都被称为非检查型异常。 **3) Java中的NullPointerException和ArrayIndexOutOfBoundException之间有什么相同之处?** -  在Java异常面试中这并不是一个很流行的问题,但会出现在不同层次的初学者面试中,用来测试应聘者对检查型异常和非检查型异常的概念是否熟悉。顺便说一下,该题的答案是,这两个异常都是非检查型异常,都继承自RuntimeException。该问题可能会引出另一个问题,即Java和C的数组有什么不同之处,因为C里面的数组是没有大小限制的,绝对不会抛出ArrayIndexOutOfBoundException。 +在Java异常面试中这并不是一个很流行的问题,但会出现在不同层次的初学者面试中,用来测试应聘者对检查型异常和非检查型异常的概念是否熟悉。顺便说一下,该题的答案是,这两个异常都是非检查型异常,都继承自RuntimeException。该问题可能会引出另一个问题,即Java和C的数组有什么不同之处,因为C里面的数组是没有大小限制的,绝对不会抛出ArrayIndexOutOfBoundException。 **4)在Java异常处理的过程中,你遵循的那些最好的实践是什么?** -  这个问题在面试技术经理是非常常见的一个问题。因为异常处理在项目设计中是非常关键的,所以精通异常处理是十分必要的。异常处理有很多最佳实践,下面列举集中,它们提高你代码的健壮性和灵活性: +这个问题在面试技术经理是非常常见的一个问题。因为异常处理在项目设计中是非常关键的,所以精通异常处理是十分必要的。异常处理有很多最佳实践,下面列举集中,它们提高你代码的健壮性和灵活性: -  1) 调用方法的时候返回布尔值来代替返回null,这样可以 NullPointerException。由于空指针是java异常里最恶心的异常 +  1) 调用方法的时候返回布尔值来代替返回null,这样可以NullPointerException。由于空指针是java异常里最恶心的异常   2) catch块里别不写代码。空catch块是异常处理里的错误事件,因为它只是捕获了异常,却没有任何处理或者提示。通常你起码要打印出异常信息,当然你最好根据需求对异常信息进行处理。 @@ -809,32 +800,20 @@ finally中的异常会覆盖(消灭)前面try或者catch中的异常 **5) 既然我们可以用RuntimeException来处理错误,那么你认为为什么Java中还存在检查型异常?** -  这是一个有争议的问题,在回答该问题时你应当小心。虽然他们肯定愿意听到你的观点,但其实他们最感兴趣的还是有说服力的理由。我认为其中一个理由是,存在检查型异常是一个设计上的决定,受到了诸如C++等比Java更早编程语言设计经验的影响。绝大多数检查型异常位于java.io包内,这是合乎情理的,因为在你请求了不存在的系统资源的时候,一段强壮的程序必须能够优雅的处理这种情况。通过把IOException声明为检查型异常,Java 确保了你能够优雅的对异常进行处理。另一个可能的理由是,可以使用catch或finally来确保数量受限的系统资源(比如文件描述符)在你使用后尽早得到释放。 Joshua -Bloch编写的 [Effective Java 一书](http://www.amazon.com/dp/0321356683/?tag=javamysqlanta-20) 中多处涉及到了该话题,值得一读。 - -**6)  throw 和 throws这两个关键字在java中有什么不同?** - -  一个java初学者应该掌握的面试问题。 throw 和 throws乍看起来是很相似的尤其是在你还是一个java初学者的时候。尽管他们看起来相似,都是在处理异常时候使用到的。但在代码里的使用方法和用到的地方是不同的。throws总是出现在一个函数头中,用来标明该成员函数可能抛出的各种异常, 你也可以申明未检查的异常,但这不是编译器强制的。如果方法抛出了异常那么调用这个方法的时候就需要将这个异常处理。另一个关键字  throw 是用来抛出任意异常的,按照语法你可以抛出任意 Throwable (i.e. Throwable -或任何Throwable的衍生类) , throw可以中断程序运行,因此可以用来代替return . 最常见的例子是用 throw 在一个空方法中需要return的地方抛出 UnSupportedOperationException 代码如下 : - - - - - -| 123 | `private``static` `void` `show() {``throw``new` `UnsupportedOperationException(``"Notyet implemented"``);``}` | -| --- | --- | - - - +这是一个有争议的问题,在回答该问题时你应当小心。虽然他们肯定愿意听到你的观点,但其实他们最感兴趣的还是有说服力的理由。我认为其中一个理由是,存在检查型异常是一个设计上的决定,受到了诸如C++等比Java更早编程语言设计经验的影响。绝大多数检查型异常位于java.io包内,这是合乎情理的,因为在你请求了不存在的系统资源的时候,一段强壮的程序必须能够优雅的处理这种情况。通过把IOException声明为检查型异常,Java 确保了你能够优雅的对异常进行处理。另一个可能的理由是,可以使用catch或finally来确保数量受限的系统资源(比如文件描述符)在你使用后尽早得到释放。Joshua +Bloch编写的[Effective Java 一书](http://www.amazon.com/dp/0321356683/?tag=javamysqlanta-20)中多处涉及到了该话题,值得一读。 +**6) throw 和 throws这两个关键字在java中有什么不同?** -  可以看下这篇 [文章](http://javarevisited.blogspot.com/2012/02/difference-between-throw-and-throws-in.html)查看这两个关键字在java中更多的差异 。 +  一个java初学者应该掌握的面试问题。throw 和 throws乍看起来是很相似的尤其是在你还是一个java初学者的时候。尽管他们看起来相似,都是在处理异常时候使用到的。但在代码里的使用方法和用到的地方是不同的。throws总是出现在一个函数头中,用来标明该成员函数可能抛出的各种异常, 你也可以申明未检查的异常,但这不是编译器强制的。如果方法抛出了异常那么调用这个方法的时候就需要将这个异常处理。另一个关键字 throw 是用来抛出任意异常的,按照语法你可以抛出任意 Throwable(i.e. Throwable +或任何Throwable的衍生类) , throw可以中断程序运行,因此可以用来代替return . 最常见的例子是用 throw 在一个空方法中需要return的地方抛出 UnSupportedOperationException. +可以看下这篇[文章](http://javarevisited.blogspot.com/2012/02/difference-between-throw-and-throws-in.html)查看这两个关键字在java中更多的差异 。 **7) 什么是“异常链”?**   “异常链”是Java中非常流行的异常处理概念,是指在进行一个异常处理时抛出了另外一个异常,由此产生了一个异常链条。该技术大多用于将“ 受检查异常” ( checked exception)封装成为“非受检查异常”(unchecked exception)或者RuntimeException。顺便说一下,如果因为因为异常你决定抛出一个新的异常,你一定要包含原有的异常,这样,处理程序才可以通过getCause()和initCause()方法来访问异常最终的根源。 -**) 你曾经自定义实现过异常吗?怎么写的?** +**8) 你曾经自定义实现过异常吗?怎么写的?**   很显然,我们绝大多数都写过自定义或者业务异常,像AccountNotFoundException。在面试过程中询问这个Java异常问题的主要原因是去发现你如何使用这个特性的。这可以更准确和精致的去处理异常,当然这也跟你选择checked 还是unchecked exception息息相关。通过为每一个特定的情况创建一个特定的异常,你就为调用者更好的处理异常提供了更好的选择。相比通用异常(general exception),我更倾向更为精确的异常。大量的创建自定义异常会增加项目class的个数,因此,在自定义异常和通用异常之间维持一个平衡是成功的关键。 diff --git "a/docs/java/basic/11\343\200\201\350\247\243\350\257\273Java\344\270\255\347\232\204\345\233\236\350\260\203.md" "b/docs/java/basic/11\343\200\201\350\247\243\350\257\273Java\344\270\255\347\232\204\345\233\236\350\260\203.md" index 1af4d91..02ace9b 100644 --- "a/docs/java/basic/11\343\200\201\350\247\243\350\257\273Java\344\270\255\347\232\204\345\233\236\350\260\203.md" +++ "b/docs/java/basic/11\343\200\201\350\247\243\350\257\273Java\344\270\255\347\232\204\345\233\236\350\260\203.md" @@ -11,18 +11,6 @@ * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) - - ---- -title: 夯实Java基础系列11:深入理解Java中的回调机制 -date: 2019-9-11 15:56:26 # 文章生成时间,一般不改 -categories: - - Java技术江湖 - - Java基础 -tags: - - 回调机制 ---- - 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -73,49 +61,49 @@ tags: Java多线程中可以通过callable和future或futuretask结合来获取线程执行后的返回值。实现方法是通过get方法来调用callable的call方法获取返回值。 其实这种方法本质上不是回调,回调要求的是任务完成以后被调用者主动回调调用者的接口。而这里是调用者主动使用get方法阻塞获取返回值。 - - public class 多线程中的回调 { - //这里简单地使用future和callable实现了线程执行完后 - public static void main(String[] args) throws ExecutionException, InterruptedException { - ExecutorService executor = Executors.newCachedThreadPool(); - Future future = executor.submit(new Callable() { - @Override - public String call() throws Exception { - System.out.println("call"); - TimeUnit.SECONDS.sleep(1); - return "str"; - } - }); - //手动阻塞调用get通过call方法获得返回值。 - System.out.println(future.get()); - //需要手动关闭,不然线程池的线程会继续执行。 - executor.shutdown(); - - //使用futuretask同时作为线程执行单元和数据请求单元。 - FutureTask futureTask = new FutureTask(new Callable() { +```` +public class 多线程中的回调 { + //这里简单地使用future和callable实现了线程执行完后 + public static void main(String[] args) throws ExecutionException, InterruptedException { + ExecutorService executor = Executors.newCachedThreadPool(); + Future future = executor.submit(new Callable() { @Override - public Integer call() throws Exception { - System.out.println("dasds"); - return new Random().nextInt(); + public String call() throws Exception { + System.out.println("call"); + TimeUnit.SECONDS.sleep(1); + return "str"; } }); - new Thread(futureTask).start(); - //阻塞获取返回值 - System.out.println(futureTask.get()); - } - @Test - public void test () { - Callable callable = new Callable() { - @Override - public Object call() throws Exception { - return null; - } - }; - FutureTask futureTask = new FutureTask(callable); + //手动阻塞调用get通过call方法获得返回值。 + System.out.println(future.get()); + //需要手动关闭,不然线程池的线程会继续执行。 + executor.shutdown(); - } - } + //使用futuretask同时作为线程执行单元和数据请求单元。 + FutureTask futureTask = new FutureTask(new Callable() { + @Override + public Integer call() throws Exception { + System.out.println("dasds"); + return new Random().nextInt(); + } + }); + new Thread(futureTask).start(); + //阻塞获取返回值 + System.out.println(futureTask.get()); +} +@Test +public void test () { + Callable callable = new Callable() { + @Override + public Object call() throws Exception { + return null; + } + }; + FutureTask futureTask = new FutureTask(callable); +} +} +```` ## Java回调机制实战 曾经自己偶尔听说过回调机制,隐隐约约能够懂一些意思,但是当让自己写一个简单的示例程序时,自己就傻眼了。随着工作经验的增加,自己经常听到这儿使用了回调,那儿使用了回调,自己是时候好好研究一下Java回调机制了。网上关于Java回调的文章一抓一大把,但是看完总是云里雾里,不知所云,特别是看到抓取别人的代码走两步时,总是现眼。于是自己决定写一篇关于Java机制的文章,以方便大家和自己更深入的学习Java回调机制。 @@ -138,22 +126,12 @@ Java多线程中可以通过callable和future或futuretask结合来获取线程 同步调用时序图: - - - - -![](https://upload-images.jianshu.io/upload_images/3796264-6a5b5b898aa3930e.png?imageMogr2/auto-orient/strip|imageView2/2/w/1031/format/webp) - - - -同步调用时序图 - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230403210703.png) **1.1.1 底层服务类:BottomService.java** -``` - +```` package synchronization.demo; /** @@ -182,11 +160,11 @@ return param +" BottomService.bottom() execute -->"; } -``` + **1.1.2 上层服务接口: UpperService.java** -``` + package synchronization.demo; /** @@ -203,11 +181,11 @@ public String callBottomService(final String param); } -``` + **1.1.3 上层服务接口实现类:UpperServiceImpl.java** -``` + package synchronization.demo; /** @@ -244,11 +222,11 @@ return bottomService.bottom(param + " callBottomService.bottom() execute --> "); } -``` + **1.1.4 Test测试类:Test.java** -``` + package synchronization.demo; import java.util.Date; @@ -281,7 +259,7 @@ System.out.println("=============== callBottomService end ====================:" } -``` +```` **1.1.5 输出结果:** @@ -306,37 +284,37 @@ callBottomService start --> callBottomService.bottom() execute --> BottomServi 解答:回调更像是一个约定,就是如果我调用了b()方法,那么就必须要回调,而不需要显示调用 一、Java的回调-浅 我们用例子来解释:小明和小李相约一起去吃早饭,但是小李起的有点晚要先洗漱,等小李洗漱完成后,通知小明再一起去吃饭。小明就是类A,小李就是类B。一起去吃饭这个事件就是方法a(),小李去洗漱就是方法b()。 - - public class XiaoMing { - //小明和小李一起吃饭 - public void eatFood() { - XiaoLi xl = new XiaoLi(); - //A调用B的方法 - xl.washFace(); - } - - public void eat() { - System.out.print("小明和小李一起去吃大龙虾"); - } - } - 那么怎么让小李洗漱完后在通知小明一起去吃饭呢 - - public class XiaoMing { - //小明和小李一起吃饭 - public void eatFood() { - XiaoLi xl = new XiaoLi(); - //A调用B的方法 - xl.washFace(); - eat(); - } - - public void eat() { - System.out.print("小明和小李一起去吃大龙虾"); - } - } - +```` +public class XiaoMing { + //小明和小李一起吃饭 + public void eatFood() { + XiaoLi xl = new XiaoLi(); + //A调用B的方法 + xl.washFace(); + } + + public void eat() { + System.out.print("小明和小李一起去吃大龙虾"); + } +} +那么怎么让小李洗漱完后在通知小明一起去吃饭呢 + +public class XiaoMing { + //小明和小李一起吃饭 + public void eatFood() { + XiaoLi xl = new XiaoLi(); + //A调用B的方法 + xl.washFace(); + eat(); + } + + public void eat() { + System.out.print("小明和小李一起去吃大龙虾"); + } +} +```` 不过上面已经说过了这个不是回调函数,所以不能这样子,正确的方式如下 - +```` public class XiaoLi{//小李 public void washFace() { System.out.print("小李要洗漱"); @@ -345,7 +323,7 @@ callBottomService start --> callBottomService.bottom() execute --> BottomServi xm.eat();//洗漱完后,一起去吃饭 } } - +```` 这样子就可以实现washFace()同时也能实现eat()。小李洗漱完后,再通知小明一起去吃饭,这就是回调。 二、Java的回调-中 @@ -354,50 +332,50 @@ callBottomService start --> callBottomService.bottom() execute --> BottomServi 小明和小李相约一起去吃早饭,但是小李起的有点晚要先洗漱,等小李洗漱完成后,通知小明再一起去吃饭。小明就是类A,小李就是类B。不同的是我们新建一个吃饭的接口EatRice,接口中有个抽象方法eat()。在小明中调用这个接口,并实现eat();小李声明这个接口对象,并且调用这个接口的抽象方法。这里可能有点绕口,不过没关系,看看例子就很清楚了。 EatRice接口: +```` +public interface EatRice { + public void eat(String food); +} +小明: - public interface EatRice { - public void eat(String food); - } - 小明: - - public class XiaoMing implements EatRice{//小明 - - //小明和小李一起吃饭 - public void eatFood() { - XiaoLi xl = new XiaoLi(); - //A调用B的方法 - xl.washFace("大龙虾", this);//this指的是小明这个类实现的EatRice接口 - } - - @Override - public void eat(String food) { - // TODO Auto-generated method stub - System.out.println("小明和小李一起去吃" + food); - } - } - 小李: - - public class XiaoLi{//小李 - public void washFace(String food,EatRice er) { - System.out.println("小李要洗漱"); - //B调用了A的方法 - er.eat(food); - } - } - 测试Demo: - - public class demo { - public static void main(String args[]) { - XiaoMing xm = new XiaoMing(); - xm.eatFood(); - } - } +public class XiaoMing implements EatRice{//小明 + //小明和小李一起吃饭 + public void eatFood() { + XiaoLi xl = new XiaoLi(); + //A调用B的方法 + xl.washFace("大龙虾", this);//this指的是小明这个类实现的EatRice接口 + } + + @Override + public void eat(String food) { + // TODO Auto-generated method stub + System.out.println("小明和小李一起去吃" + food); + } +} +小李: + +public class XiaoLi{//小李 + public void washFace(String food,EatRice er) { + System.out.println("小李要洗漱"); + //B调用了A的方法 + er.eat(food); + } +} +测试Demo: + +public class demo { + public static void main(String args[]) { + XiaoMing xm = new XiaoMing(); + xm.eatFood(); + } +} +```` 测试结果: 这样子就通过接口的形式实现了软编码。通过接口的形式我可以实现小李洗漱完后,和小王一起去上网。代码如下 - +```` public class XiaoWang implements EatRice{//小王 //小王和小李一起去上网 @@ -413,7 +391,7 @@ EatRice接口: System.out.println("小王和小李一起去" + bar); } } - +```` ## 实例三:Tom做题 数学老师让Tom做一道题,并且Tom做题期间数学老师不用盯着Tom,而是在玩手机,等Tom把题目做完后再把答案告诉老师。 @@ -425,64 +403,64 @@ EatRice接口: > 3 Tom需要数学老师的一个引用,以便Tom把答案给这位老师,而不是隔壁的体育老师。 回调接口,可以理解为老师接口 +```` +//回调指的是A调用B来做一件事,B做完以后将结果告诉给A,这期间A可以做别的事情。 +//这个接口中有一个方法,意为B做完题目后告诉A时使用的方法。 +//所以我们必须提供这个接口以便让B来回调。 +//回调接口, +public interface CallBack { + void tellAnswer(int res); +} +```` - //回调指的是A调用B来做一件事,B做完以后将结果告诉给A,这期间A可以做别的事情。 - //这个接口中有一个方法,意为B做完题目后告诉A时使用的方法。 - //所以我们必须提供这个接口以便让B来回调。 - //回调接口, - public interface CallBack { - void tellAnswer(int res); - } - - 数学老师类 - - //老师类实例化回调接口,即学生写完题目之后通过老师的提供的方法进行回调。 - //那么学生如何调用到老师的方法呢,只要在学生类的方法中传入老师的引用即可。 - //而老师需要指定学生答题,所以也要传入学生的实例。 - public class Teacher implements CallBack{ - private Student student; - - Teacher(Student student) { - this.student = student; - } - - void askProblem (Student student, Teacher teacher) { - //main方法是主线程运行,为了实现异步回调,这里开启一个线程来操作 - new Thread(new Runnable() { - @Override - public void run() { - student.resolveProblem(teacher); - } - }).start(); - //老师让学生做题以后,等待学生回答的这段时间,可以做别的事,比如玩手机.\ - //而不需要同步等待,这就是回调的好处。 - //当然你可以说开启一个线程让学生做题就行了,但是这样无法让学生通知老师。 - //需要另外的机制去实现通知过程。 - // 当然,多线程中的future和callable也可以实现数据获取的功能。 - for (int i = 1;i < 4;i ++) { - System.out.println("等学生回答问题的时候老师玩了 " + i + "秒的手机"); +```` + //老师类实例化回调接口,即学生写完题目之后通过老师的提供的方法进行回调。 + //那么学生如何调用到老师的方法呢,只要在学生类的方法中传入老师的引用即可。 + //而老师需要指定学生答题,所以也要传入学生的实例。 +public class Teacher implements CallBack{ + private Student student; + + Teacher(Student student) { + this.student = student; + } + + void askProblem (Student student, Teacher teacher) { + //main方法是主线程运行,为了实现异步回调,这里开启一个线程来操作 + new Thread(new Runnable() { + @Override + public void run() { + student.resolveProblem(teacher); } - } - - @Override - public void tellAnswer(int res) { - System.out.println("the answer is " + res); + }).start(); + //老师让学生做题以后,等待学生回答的这段时间,可以做别的事,比如玩手机.\ + //而不需要同步等待,这就是回调的好处。 + //当然你可以说开启一个线程让学生做题就行了,但是这样无法让学生通知老师。 + //需要另外的机制去实现通知过程。 + // 当然,多线程中的future和callable也可以实现数据获取的功能。 + for (int i = 1;i < 4;i ++) { + System.out.println("等学生回答问题的时候老师玩了 " + i + "秒的手机"); } } - -学生接口 + @Override + public void tellAnswer(int res) { + System.out.println("the answer is " + res); + } +} +```` +学生接口 +```` //学生的接口,解决问题的方法中要传入老师的引用,否则无法完成对具体实例的回调。 //写为接口的好处就是,很多个学生都可以实现这个接口,并且老师在提问题时可以通过 //传入List来聚合学生,十分方便。 public interface Student { void resolveProblem (Teacher teacher); } - +```` 学生Tom - +```` public class Tom implements Student{ @Override @@ -496,9 +474,9 @@ EatRice接口: e.printStackTrace(); } } - +```` 测试类 - +```` public class Test { public static void main(String[] args) { //测试 @@ -513,9 +491,7 @@ EatRice接口: // the answer is 111 } } - - - +```` ## 参考文章 https://blog.csdn.net/fengye454545/article/details/80198446 diff --git "a/docs/java/basic/12\343\200\201\345\217\215\345\260\204.md" "b/docs/java/basic/12\343\200\201\345\217\215\345\260\204.md" index 0513f67..4eb6669 100644 --- "a/docs/java/basic/12\343\200\201\345\217\215\345\260\204.md" +++ "b/docs/java/basic/12\343\200\201\345\217\215\345\260\204.md" @@ -25,16 +25,6 @@ * [微信公众号](#微信公众号) * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) ---- -title: 夯实Java基础系列12:深入理解Java中的反射机制 -date: 2019-9-12 15:56:26 # 文章生成时间,一般不改 -categories: - - Java技术江湖 - - Java基础 -tags: - - Java反射 ---- - 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -61,11 +51,11 @@ Oracle官方对反射的解释是 > The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control. > ->  简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。 +> 简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。 > > 程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。 > ->  反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。 +> 反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。 Java反射框架主要提供以下功能: @@ -82,27 +72,15 @@ Java反射框架主要提供以下功能: ## 反射的主要用途 ->  很多人都认为反射在实际的Java开发应用中并不广泛,其实不然。 - ->  当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。 - ->  反射最重要的用途就是开发各种通用框架。 +> 很多人都认为反射在实际的Java开发应用中并不广泛,其实不然。 ->  很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。 +> 当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。 ->  举一个例子,在运用Struts 2框架的开发中我们一般会在struts.xml里去配置Action,比如: +> 反射最重要的用途就是开发各种通用框架。 - - /shop/shop-index.jsp - login.jsp - -配置文件与Action建立了一种映射关系,当View层发出请求时,请求会被StrutsPrepareAndExecuteFilter拦截,然后StrutsPrepareAndExecuteFilter会去动态地创建Action实例。 +> 很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。 -——比如我们请求login.action,那么StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,检索action中name为login的Action,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。 - -> 对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。 +> 对于框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。 ## 反射的基础:关于Class类 @@ -117,25 +95,26 @@ Java反射框架主要提供以下功能: > 4、Class 对象只能由系统建立对象 > > 5、一个类在 JVM 中只会有一个Class实例 - - //总结一下就是,JDK有一个类叫做Class,这个类用来封装所有Java类型,包括这些类的所有信息,JVM中类信息是放在方法区的。 - - //所有类在加载后,JVM会为其在堆中创建一个Class<类名称>的对象,并且每个类只会有一个Class对象,这个类的所有对象都要通过Class<类名称>来进行实例化。 - - //上面说的是JVM进行实例化的原理,当然实际上在Java写代码时只需要用 类名称就可以进行实例化了。 - - public final class Class implements java.io.Serializable, - GenericDeclaration, - Type, - AnnotatedElement { - 虚拟机会保持唯一一 - //通过类名.class获得唯一的Class对象。 - Class cls = UserBean.class; - //通过integer.TYPEl来获取Class对象 - Class inti = Integer.TYPE; - //接口本质也是一个类,一样可以通过.class获取 - Class userClass = User.class; - +> +```` +//总结一下就是,JDK有一个类叫做Class,这个类用来封装所有Java类型,包括这些类的所有信息,JVM中类信息是放在方法区的。 + +//所有类在加载后,JVM会为其在堆中创建一个Class<类名称>的对象,并且每个类只会有一个Class对象,这个类的所有对象都要通过Class<类名称>来进行实例化。 + +//上面说的是JVM进行实例化的原理,当然实际上在Java写代码时只需要用 类名称就可以进行实例化了。 + +public final class Class implements java.io.Serializable, + GenericDeclaration, + Type, + AnnotatedElement { + //通过类名.class获得唯一的Class对象。 + Class cls = UserBean.class; + //通过integer.TYPEl来获取Class对象 + Class inti = Integer.TYPE; + //接口本质也是一个类,一样可以通过.class获取 + Class userClass = User.class; +} +```` JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象. @@ -146,8 +125,7 @@ JAVA反射机制是在运行状态中,对于任意一个类,都能够知道 如图是类的正常加载过程:反射的原理在与class对象。 熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。 -![](https://img-blog.csdn.net/20170513133210763) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230403211245.png) ## Java为什么需要反射?反射要解决什么问题? Java中编译类型有两种: @@ -176,29 +154,31 @@ Array类:提供了动态创建数组,以及访问数组的元素的静态方 1、获得Class对象方法有三种 (1)使用Class类的forName静态方法: - - public static Class forName(String className) - ``` - 在JDBC开发中常用此方法加载数据库驱动: - 要使用全类名来加载这个类,一般数据库驱动的配置信息会写在配置文件中。加载这个驱动前要先导入jar包 - ```java - Class.forName(driver); +```` +public static Class forName(String className) +```` +在JDBC开发中常用此方法加载数据库驱动: +要使用全类名来加载这个类,一般数据库驱动的配置信息会写在配置文件中。加载这个驱动前要先导入jar包 +```` +Class.forName(driver); +```` (2)直接获取某一个对象的class,比如: - - //Class是一个泛型表示,用于获取一个类的类型。 - Class klass = int.class; - Class classInt = Integer.TYPE; +```` +//Class是一个泛型表示,用于获取一个类的类型。 +Class klass = int.class; +Class classInt = Integer.TYPE; +```` (3)调用某个对象的getClass()方法,比如: - - StringBuilder str = new StringBuilder("123"); - Class klass = str.getClass(); - +```` +StringBuilder str = new StringBuilder("123"); +Class klass = str.getClass(); +```` ## 判断是否为某个类的实例 一般地,我们用instanceof关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断是否为某个类的实例,它是一个Native方法: - -==public native boolean isInstance(Object obj);== - +```` +public native boolean isInstance(Object obj); +```` ## 创建实例 通过反射来生成对象主要有两种方式。 @@ -206,20 +186,20 @@ Array类:提供了动态创建数组,以及访问数组的元素的静态方 (1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。 注意:利用newInstance创建对象:调用的类必须有无参的构造器 - - //Class代表任何类的一个类对象。 - //使用这个类对象可以为其他类进行实例化 - //因为jvm加载类以后自动在堆区生成一个对应的*.Class对象 - //该对象用于让JVM对进行所有*对象实例化。 - Class c = String.class; - - //Class 中的 ? 是通配符,其实就是表示任意符合泛类定义条件的类,和直接使用 Class - //效果基本一致,但是这样写更加规范,在某些类型转换时可以避免不必要的 unchecked 错误。 - - Object str = c.newInstance(); - +```` +//Class代表任何类的一个类对象。 +//使用这个类对象可以为其他类进行实例化 +//因为jvm加载类以后自动在堆区生成一个对应的*.Class对象 +//该对象用于让JVM对进行所有*对象实例化。 +Class c = String.class; + +//Class 中的 ? 是通配符,其实就是表示任意符合泛类定义条件的类,和直接使用 Class +//效果基本一致,但是这样写更加规范,在某些类型转换时可以避免不必要的 unchecked 错误。 + +Object str = c.newInstance(); +```` (2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。 - +```` //获取String所对应的Class对象 Class c = String.class; //获取String类带一个String参数的构造器 @@ -227,33 +207,35 @@ Array类:提供了动态创建数组,以及访问数组的元素的静态方 //根据构造器创建实例 Object obj = constructor.newInstance("23333"); System.out.println(obj); - +```` ## 获取方法 获取某个Class对象的方法集合,主要有以下几个方法: getDeclaredMethods()方法返回类或接口声明的所有方法,==包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法==。 - - public Method[] getDeclaredMethods() throws SecurityException - +```` +public Method[] getDeclaredMethods() throws SecurityException +```` getMethods()方法返回某个类的所有公用(public)方法,==包括其继承类的公用方法。== - - public Method[] getMethods() throws SecurityException +```` +public Method[] getMethods() throws SecurityException +```` getMethod方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象 - - - public Method getMethod(String name, Class... parameterTypes) +```` +public Method getMethod(String name, Class... parameterTypes) +```` 只是这样描述的话可能难以理解,我们用例子来理解这三个方法: 本文中的例子用到了以下这些类,用于反射的测试。 +```` //注解类,可可用于表示方法,可以通过反射获取注解的内容。 //Java注解的实现是很多注框架实现注解配置的基础 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Invoke { } - +```` userbean的父类personbean - +```` public class PersonBean { private String name; @@ -266,20 +248,17 @@ userbean的父类personbean public void setName(String name) { this.name = name; } - - -​ } - +```` 接口user - +```` public interface User { public void login (); } - +```` userBean实现user接口,继承personbean - +```` public class UserBean extends PersonBean implements User{ @Override public void login() { @@ -318,100 +297,100 @@ userBean实现user接口,继承personbean System.out.println("I'm a private method"); } } - +```` 1 getMethods和getDeclaredMethods的区别 - - public class 动态加载类的反射 { - public static void main(String[] args) { - try { - Class clazz = Class.forName("com.javase.反射.UserBean"); - for (Field field : clazz.getDeclaredFields()) { - // field.setAccessible(true); - System.out.println(field); - } - //getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。 - System.out.println("------共有方法------"); - // getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。 - // getMethod*()获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。 - for (Method method : clazz.getMethods()) { - String name = method.getName(); - System.out.println(name); - //打印出了UserBean.java的所有方法以及父类的方法 - } - System.out.println("------独占方法------"); - - for (Method method : clazz.getDeclaredMethods()) { - String name = method.getName(); - System.out.println(name); - } - } catch (ClassNotFoundException e) { - e.printStackTrace(); +```` +public class 动态加载类的反射 { + public static void main(String[] args) { + try { + Class clazz = Class.forName("com.javase.反射.UserBean"); + for (Field field : clazz.getDeclaredFields()) { +// field.setAccessible(true); + System.out.println(field); + } + //getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。 + System.out.println("------共有方法------"); +// getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。 +// getMethod*()获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。 + for (Method method : clazz.getMethods()) { + String name = method.getName(); + System.out.println(name); + //打印出了UserBean.java的所有方法以及父类的方法 + } + System.out.println("------独占方法------"); + + for (Method method : clazz.getDeclaredMethods()) { + String name = method.getName(); + System.out.println(name); } + } catch (ClassNotFoundException e) { + e.printStackTrace(); } } +} - +```` 2 打印一个类的所有方法及详细信息: - - public class 打印所有方法 { - - public static void main(String[] args) { - Class userBeanClass = UserBean.class; - Field[] fields = userBeanClass.getDeclaredFields(); - //注意,打印方法时无法得到局部变量的名称,因为jvm只知道它的类型 - Method[] methods = userBeanClass.getDeclaredMethods(); - for (Method method : methods) { - //依次获得方法的修饰符,返回类型和名称,外加方法中的参数 - String methodString = Modifier.toString(method.getModifiers()) + " " ; // private static - methodString += method.getReturnType().getSimpleName() + " "; // void - methodString += method.getName() + "("; // staticMethod - Class[] parameters = method.getParameterTypes(); - Parameter[] p = method.getParameters(); - - for (Class parameter : parameters) { - methodString += parameter.getSimpleName() + " " ; // String - } - methodString += ")"; - System.out.println(methodString); +```` +public class 打印所有方法 { + + public static void main(String[] args) { + Class userBeanClass = UserBean.class; + Field[] fields = userBeanClass.getDeclaredFields(); + //注意,打印方法时无法得到局部变量的名称,因为jvm只知道它的类型 + Method[] methods = userBeanClass.getDeclaredMethods(); + for (Method method : methods) { + //依次获得方法的修饰符,返回类型和名称,外加方法中的参数 + String methodString = Modifier.toString(method.getModifiers()) + " " ; // private static + methodString += method.getReturnType().getSimpleName() + " "; // void + methodString += method.getName() + "("; // staticMethod + Class[] parameters = method.getParameterTypes(); + Parameter[] p = method.getParameters(); + + for (Class parameter : parameters) { + methodString += parameter.getSimpleName() + " " ; // String } - //注意方法只能获取到其类型,拿不到变量名 - /* public String getName() - public long getId() - public static void staticMethod(String int ) - public void publicMethod() - private void privateMethod()*/ + methodString += ")"; + System.out.println(methodString); } + //注意方法只能获取到其类型,拿不到变量名 +/* public String getName() + public long getId() + public static void staticMethod(String int ) + public void publicMethod() + private void privateMethod()*/ } - +} +```` ## 获取构造器信息 获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例: - - public class 打印构造方法 { - public static void main(String[] args) { - // constructors - Class clazz = UserBean.class; - - Class userBeanClass = UserBean.class; - //获得所有的构造方法 - Constructor[] constructors = userBeanClass.getDeclaredConstructors(); - for (Constructor constructor : constructors) { - String s = Modifier.toString(constructor.getModifiers()) + " "; - s += constructor.getName() + "("; - //构造方法的参数类型 - Class[] parameters = constructor.getParameterTypes(); - for (Class parameter : parameters) { - s += parameter.getSimpleName() + ", "; - } - s += ")"; - System.out.println(s); - //打印结果//public com.javase.反射.UserBean(String, long, ) - +```` +public class 打印构造方法 { + public static void main(String[] args) { + // constructors + Class clazz = UserBean.class; + + Class userBeanClass = UserBean.class; + //获得所有的构造方法 + Constructor[] constructors = userBeanClass.getDeclaredConstructors(); + for (Constructor constructor : constructors) { + String s = Modifier.toString(constructor.getModifiers()) + " "; + s += constructor.getName() + "("; + //构造方法的参数类型 + Class[] parameters = constructor.getParameterTypes(); + for (Class parameter : parameters) { + s += parameter.getSimpleName() + ", "; } + s += ")"; + System.out.println(s); + //打印结果//public com.javase.反射.UserBean(String, long, ) + } } - +} +```` ## 获取类的成员变量(字段)信息 主要是这几个方法,在此不再赘述: @@ -419,140 +398,138 @@ getFiled: 访问公有的成员变量 getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量 getFileds和getDeclaredFields用法同上(参照Method) - public class 打印成员变量 { - public static void main(String[] args) { - Class userBeanClass = UserBean.class; - //获得该类的所有成员变量,包括static private - Field[] fields = userBeanClass.getDeclaredFields(); - - for(Field field : fields) { - //private属性即使不用下面这个语句也可以访问 - // field.setAccessible(true); - - //因为类的私有域在反射中默认可访问,所以flag默认为true。 - String fieldString = ""; - fieldString += Modifier.toString(field.getModifiers()) + " "; // `private` - fieldString += field.getType().getSimpleName() + " "; // `String` - fieldString += field.getName(); // `userName` - fieldString += ";"; - System.out.println(fieldString); - - //打印结果 - // public String userName; - // protected int i; - // static int j; - // private int l; - // private long userId; - } - +```` +public class 打印成员变量 { + public static void main(String[] args) { + Class userBeanClass = UserBean.class; + //获得该类的所有成员变量,包括static private + Field[] fields = userBeanClass.getDeclaredFields(); + + for(Field field : fields) { + //private属性即使不用下面这个语句也可以访问 +// field.setAccessible(true); + + //因为类的私有域在反射中默认可访问,所以flag默认为true。 + String fieldString = ""; + fieldString += Modifier.toString(field.getModifiers()) + " "; // `private` + fieldString += field.getType().getSimpleName() + " "; // `String` + fieldString += field.getName(); // `userName` + fieldString += ";"; + System.out.println(fieldString); + + //打印结果 +// public String userName; +// protected int i; +// static int j; +// private int l; +// private long userId; } - } + } +} +```` ## 调用方法 当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。invoke方法的原型为: - - public Object invoke(Object obj, Object... args) - throws IllegalAccessException, IllegalArgumentException, - InvocationTargetException - - public class 使用反射调用方法 { - public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException { - Class userBeanClass = UserBean.class; - //获取该类所有的方法,包括静态方法,实例方法。 - //此处也包括了私有方法,只不过私有方法在用invoke访问之前要设置访问权限 - //也就是使用setAccessible使方法可访问,否则会抛出异常 - // // IllegalAccessException的解释是 - // * An IllegalAccessException is thrown when an application tries - // * to reflectively create an instance (other than an array), - // * set or get a field, or invoke a method, but the currently - // * executing method does not have access to the definition of - // * the specified class, field, method or constructor. - - // getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。 - // getMethod*()获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。 - - //就是说,当这个类,域或者方法被设为私有访问,使用反射调用但是却没有权限时会抛出异常。 - Method[] methods = userBeanClass.getDeclaredMethods(); // 获取所有成员方法 - for (Method method : methods) { - //反射可以获取方法上的注解,通过注解来进行判断 - if (method.isAnnotationPresent(Invoke.class)) { // 判断是否被 @Invoke 修饰 - //判断方法的修饰符是是static - if (Modifier.isStatic(method.getModifiers())) { // 如果是 static 方法 - //反射调用该方法 - //类方法可以直接调用,不必先实例化 - method.invoke(null, "wingjay",2); // 直接调用,并传入需要的参数 devName - } else { - //如果不是类方法,需要先获得一个实例再调用方法 - //传入构造方法需要的变量类型 - Class[] params = {String.class, long.class}; - //获取该类指定类型的构造方法 - //如果没有这种类型的方法会报错 - Constructor constructor = userBeanClass.getDeclaredConstructor(params); // 获取参数格式为 String,long 的构造函数 - //通过构造方法的实例来进行实例化 - Object userBean = constructor.newInstance("wingjay", 11); // 利用构造函数进行实例化,得到 Object - if (Modifier.isPrivate(method.getModifiers())) { - method.setAccessible(true); // 如果是 private 的方法,需要获取其调用权限 - // Set the {@code accessible} flag for this object to - // * the indicated boolean value. A value of {@code true} indicates that - // * the reflected object should suppress Java language access - // * checking when it is used. A value of {@code false} indicates - // * that the reflected object should enforce Java language access checks. - //通过该方法可以设置其可见或者不可见,不仅可以用于方法 - //后面例子会介绍将其用于成员变量 - //打印结果 - // I'm a public method - // Hi wingjay, I'm a static methodI'm a private method - } - method.invoke(userBean); // 调用 method,无须参数 - - -​ - +```` +public Object invoke(Object obj, Object... args) + throws IllegalAccessException, IllegalArgumentException, + InvocationTargetException + +public class 使用反射调用方法 { + public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException { + Class userBeanClass = UserBean.class; + //获取该类所有的方法,包括静态方法,实例方法。 + //此处也包括了私有方法,只不过私有方法在用invoke访问之前要设置访问权限 + //也就是使用setAccessible使方法可访问,否则会抛出异常 +// // IllegalAccessException的解释是 +// * An IllegalAccessException is thrown when an application tries +// * to reflectively create an instance (other than an array), +// * set or get a field, or invoke a method, but the currently +// * executing method does not have access to the definition of +// * the specified class, field, method or constructor. + +// getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。 +// getMethod*()获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。 + + //就是说,当这个类,域或者方法被设为私有访问,使用反射调用但是却没有权限时会抛出异常。 + Method[] methods = userBeanClass.getDeclaredMethods(); // 获取所有成员方法 + for (Method method : methods) { + //反射可以获取方法上的注解,通过注解来进行判断 + if (method.isAnnotationPresent(Invoke.class)) { // 判断是否被 @Invoke 修饰 + //判断方法的修饰符是是static + if (Modifier.isStatic(method.getModifiers())) { // 如果是 static 方法 + //反射调用该方法 + //类方法可以直接调用,不必先实例化 + method.invoke(null, "wingjay",2); // 直接调用,并传入需要的参数 devName + } else { + //如果不是类方法,需要先获得一个实例再调用方法 + //传入构造方法需要的变量类型 + Class[] params = {String.class, long.class}; + //获取该类指定类型的构造方法 + //如果没有这种类型的方法会报错 + Constructor constructor = userBeanClass.getDeclaredConstructor(params); // 获取参数格式为 String,long 的构造函数 + //通过构造方法的实例来进行实例化 + Object userBean = constructor.newInstance("wingjay", 11); // 利用构造函数进行实例化,得到 Object + if (Modifier.isPrivate(method.getModifiers())) { + method.setAccessible(true); // 如果是 private 的方法,需要获取其调用权限 +// Set the {@code accessible} flag for this object to +// * the indicated boolean value. A value of {@code true} indicates that +// * the reflected object should suppress Java language access +// * checking when it is used. A value of {@code false} indicates +// * that the reflected object should enforce Java language access checks. + //通过该方法可以设置其可见或者不可见,不仅可以用于方法 + //后面例子会介绍将其用于成员变量 + //打印结果 +// I'm a public method +// Hi wingjay, I'm a static methodI'm a private method } + method.invoke(userBean); // 调用 method,无须参数 } } } } - +} +```` ## 利用反射创建数组 数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference。下面我们看一看利用反射创建数组的例子: - - public class 用反射创建数组 { - public static void main(String[] args) { - Class cls = null; - try { - cls = Class.forName("java.lang.String"); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - Object array = Array.newInstance(cls,25); - //往数组里添加内容 - Array.set(array,0,"hello"); - Array.set(array,1,"Java"); - Array.set(array,2,"fuck"); - Array.set(array,3,"Scala"); - Array.set(array,4,"Clojure"); - //获取某一项的内容 - System.out.println(Array.get(array,3)); - //Scala +```` +public class 用反射创建数组 { + public static void main(String[] args) { + Class cls = null; + try { + cls = Class.forName("java.lang.String"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); } - + Object array = Array.newInstance(cls,25); + //往数组里添加内容 + Array.set(array,0,"hello"); + Array.set(array,1,"Java"); + Array.set(array,2,"fuck"); + Array.set(array,3,"Scala"); + Array.set(array,4,"Clojure"); + //获取某一项的内容 + System.out.println(Array.get(array,3)); + //Scala } +} +```` 其中的Array类为java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象,它的原型是: - - public static Object newInstance(Class componentType, int length) - throws NegativeArraySizeException { - return newArray(componentType, length); +```` +public static Object newInstance(Class componentType, int length) + throws NegativeArraySizeException { + return newArray(componentType, length); } +```` 而newArray()方法是一个Native方法,它在Hotspot JVM里的具体实现我们后边再研究,这里先把源码贴出来 +```` +private static native Object newArray(Class componentType, int length) + throws NegativeArraySizeException; - private static native Object newArray(Class componentType, int length) - throws NegativeArraySizeException; - - -​ +```` + ## Java反射常见面试题 diff --git "a/docs/java/basic/13\343\200\201\346\263\233\345\236\213.md" "b/docs/java/basic/13\343\200\201\346\263\233\345\236\213.md" index 2251e75..d71286f 100644 --- "a/docs/java/basic/13\343\200\201\346\263\233\345\236\213.md" +++ "b/docs/java/basic/13\343\200\201\346\263\233\345\236\213.md" @@ -44,48 +44,45 @@ > > 泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。 - - ### 一个栗子 一个被举了无数次的例子: - - List arrayList = new ArrayList(); - arrayList.add("aaaa"); - arrayList.add(100); - - for(int i = 0; i< arrayList.size();i++){ - String item = (String)arrayList.get(i); - Log.d("泛型测试","item = " + item); - } - +```` +List arrayList = new ArrayList(); +arrayList.add("aaaa"); +arrayList.add(100); + +for(int i = 0; i< arrayList.size();i++){ + String item = (String)arrayList.get(i); + Log.d("泛型测试","item = " + item); +} +```` 毫无疑问,程序的运行结果会以崩溃结束: -java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String + java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。 我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。 - +```` List arrayList = new ArrayList(); ... //arrayList.add(100); 在编译阶段,编译器就会报错 - +```` ### 特性 泛型只在编译阶段有效。看下面的代码: +```` +List stringArrayList = new ArrayList(); +List integerArrayList = new ArrayList(); - List stringArrayList = new ArrayList(); - List integerArrayList = new ArrayList(); - - Class classStringArrayList = stringArrayList.getClass(); - Class classIntegerArrayList = integerArrayList.getClass(); - - if(classStringArrayList.equals(classIntegerArrayList)){ - Log.d("泛型测试","类型相同"); - } - +Class classStringArrayList = stringArrayList.getClass(); +Class classIntegerArrayList = integerArrayList.getClass(); +if(classStringArrayList.equals(classIntegerArrayList)){ + Log.d("泛型测试","类型相同"); +} +```` > 通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。 > 对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。 @@ -101,49 +98,49 @@ List arrayList = new ArrayList(); > 泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。 > > 泛型类的最基本写法(这么看可能会有点晕,会在下面的例子中详解): - +```` class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{ private 泛型标识 /*(成员变量类型)*/ var; ..... } - +```` 一个最普通的泛型类: //此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型 +```` +//在实例化泛型类时,必须指定T的具体类型 +public class Generic{ + //在类中声明的泛型整个类里面都可以用,除了静态部分,因为泛型是实例化时声明的。 + //静态区域的代码在编译时就已经确定,只与类相关 + class A { + T t; + } + //类里面的方法或类中再次声明同名泛型是允许的,并且该泛型会覆盖掉父类的同名泛型T + class B { + T t; + } + //静态内部类也可以使用泛型,实例化时赋予泛型实际类型 + static class C { + T t; + } + public static void main(String[] args) { + //报错,不能使用T泛型,因为泛型T属于实例不属于类 +// T t = null; + } - //在实例化泛型类时,必须指定T的具体类型 - public class Generic{ - //在类中声明的泛型整个类里面都可以用,除了静态部分,因为泛型是实例化时声明的。 - //静态区域的代码在编译时就已经确定,只与类相关 - class A { - T t; - } - //类里面的方法或类中再次声明同名泛型是允许的,并且该泛型会覆盖掉父类的同名泛型T - class B { - T t; - } - //静态内部类也可以使用泛型,实例化时赋予泛型实际类型 - static class C { - T t; - } - public static void main(String[] args) { - //报错,不能使用T泛型,因为泛型T属于实例不属于类 - // T t = null; - } - - //key这个成员变量的类型为T,T的类型由外部指定 - private T key; - - public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定 - this.key = key; - } - - public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定 - return key; - } + //key这个成员变量的类型为T,T的类型由外部指定 + private T key; + + public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定 + this.key = key; } + public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定 + return key; + } +} +```` > 12-27 09:20:04.432 13063-13063/? D/泛型测试: key is 123456 > 12-27 09:20:04.432 13063-13063/? D/泛型测试: key is key_vlaue @@ -151,22 +148,22 @@ List arrayList = new ArrayList(); > 定义的泛型类,就一定要传入泛型类型实参么?并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。 看一个例子: - - Generic generic = new Generic("111111"); - Generic generic1 = new Generic(4444); - Generic generic2 = new Generic(55.55); - Generic generic3 = new Generic(false); - - Log.d("泛型测试","key is " + generic.getKey()); - Log.d("泛型测试","key is " + generic1.getKey()); - Log.d("泛型测试","key is " + generic2.getKey()); - Log.d("泛型测试","key is " + generic3.getKey()); - - D/泛型测试: key is 111111 - D/泛型测试: key is 4444 - D/泛型测试: key is 55.55 - D/泛型测试: key is false - +```` +Generic generic = new Generic("111111"); +Generic generic1 = new Generic(4444); +Generic generic2 = new Generic(55.55); +Generic generic3 = new Generic(false); + +Log.d("泛型测试","key is " + generic.getKey()); +Log.d("泛型测试","key is " + generic1.getKey()); +Log.d("泛型测试","key is " + generic2.getKey()); +Log.d("泛型测试","key is " + generic3.getKey()); + +D/泛型测试: key is 111111 +D/泛型测试: key is 4444 +D/泛型测试: key is 55.55 +D/泛型测试: key is false +```` 注意: 泛型的类型参数只能是类类型,不能是简单类型。 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。 @@ -176,78 +173,78 @@ List arrayList = new ArrayList(); ### 泛型接口 泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子: - - //定义一个泛型接口 - public interface Generator { - public T next(); - } - +```` +//定义一个泛型接口 +public interface Generator { + public T next(); +} +```` 当实现泛型接口的类,未传入泛型实参时: - - /** - * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 - * 即:class FruitGenerator implements Generator{ - * 如果不声明泛型,如:class FruitGenerator implements Generator,编译器会报错:"Unknown class" - */ - class FruitGenerator implements Generator{ - @Override - public T next() { - return null; - } +```` +/** + * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 + * 即:class FruitGenerator implements Generator{ + * 如果不声明泛型,如:class FruitGenerator implements Generator,编译器会报错:"Unknown class" + */ +class FruitGenerator implements Generator{ + @Override + public T next() { + return null; } - +} +```` 当实现泛型接口的类,传入泛型实参时: - - /** - * 传入泛型实参时: - * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator - * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。 - * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型 - * 即:Generator,public T next();中的的T都要替换成传入的String类型。 - */ - public class FruitGenerator implements Generator { - - private String[] fruits = new String[]{"Apple", "Banana", "Pear"}; - - @Override - public String next() { - Random rand = new Random(); - return fruits[rand.nextInt(3)]; - } +```` +/** + * 传入泛型实参时: + * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator + * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。 + * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型 + * 即:Generator,public T next();中的的T都要替换成传入的String类型。 + */ +public class FruitGenerator implements Generator { + + private String[] fruits = new String[]{"Apple", "Banana", "Pear"}; + + @Override + public String next() { + Random rand = new Random(); + return fruits[rand.nextInt(3)]; } - +} +```` ### 泛型通配符 我们知道Ingeter是Number的一个子类,同时在特性章节中我们也验证过Generic与Generic实际上是相同的一种基本类型。那么问题来了,在使用Generic作为形参的方法中,能否使用Generic的实例传入呢?在逻辑上类似于Generic和Generic是否可以看成具有父子关系的泛型类型呢? 为了弄清楚这个问题,我们使用Generic这个泛型类继续看下面的例子: +```` +public void showKeyValue1(Generic obj){ + Log.d("泛型测试","key value is " + obj.getKey()); +} - public void showKeyValue1(Generic obj){ - Log.d("泛型测试","key value is " + obj.getKey()); - } - - Generic gInteger = new Generic(123); - Generic gNumber = new Generic(456); - - showKeyValue(gNumber); - - // showKeyValue这个方法编译器会为我们报错:Generic - // cannot be applied to Generic - // showKeyValue(gInteger); +Generic gInteger = new Generic(123); +Generic gNumber = new Generic(456); +showKeyValue(gNumber); + +// showKeyValue这个方法编译器会为我们报错:Generic +// cannot be applied to Generic +// showKeyValue(gInteger); +```` 通过提示信息我们可以看到Generic不能被看作为`Generic的子类。由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。 回到上面的例子,如何解决上面的问题?总不能为了定义一个新的方法来处理Generic类型的类,这显然与java中的多台理念相违背。因此我们需要一个在逻辑上可以表示同时是Generic和Generic父类的引用类型。由此类型通配符应运而生。 我们可以将上面的方法改一下: - - public void showKeyValue1(Generic obj){ - Log.d("泛型测试","key value is " + obj.getKey()); - +```` +public void showKeyValue1(Generic obj){ + Log.d("泛型测试","key value is " + obj.getKey()); +```` 类型通配符一般是使用?代替具体的类型实参,注意, 此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。 可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型 - +```` public void showKeyValue(Generic obj){ System.out.println(obj); } @@ -266,7 +263,7 @@ public void showKeyValue(Generic obj){ // showKeyValue这个方法编译器会为我们报错:Generic // cannot be applied to Generic // showKeyValue(gInteger); -。 +```` ### 泛型方法 @@ -274,167 +271,165 @@ public void showKeyValue(Generic obj){ 尤其是我们见到的大多数泛型类中的成员方法也都使用了泛型,有的甚至泛型类中也包含着泛型方法,这样在初学者中非常容易将泛型方法理解错了。 泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。 +```` +/** + * 泛型方法的基本介绍 + * @param tClass 传入的泛型实参 + * @return T 返回值为T类型 + * 说明: + * 1)public 与 返回值中间非常重要,可以理解为声明此方法为泛型方法。 + * 2)只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。 + * 3)表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。 + * 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。 + */ + public T genericMethod(Class tClass)throws InstantiationException , + IllegalAccessException{ + T instance = tClass.newInstance(); + return instance; + } - /** - * 泛型方法的基本介绍 - * @param tClass 传入的泛型实参 - * @return T 返回值为T类型 - * 说明: - * 1)public 与 返回值中间非常重要,可以理解为声明此方法为泛型方法。 - * 2)只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。 - * 3)表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。 - * 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。 - */ - public T genericMethod(Class tClass)throws InstantiationException , - IllegalAccessException{ - T instance = tClass.newInstance(); - return instance; - } - - Object obj = genericMethod(Class.forName("com.test.test")); - +Object obj = genericMethod(Class.forName("com.test.test")); +```` ### 泛型方法的基本用法 光看上面的例子有的同学可能依然会非常迷糊,我们再通过一个例子,把我泛型方法再总结一下。 - - /** - * 这才是一个真正的泛型方法。 - * 首先在public与返回值之间的必不可少,这表明这是一个泛型方法,并且声明了一个泛型T - * 这个T可以出现在这个泛型方法的任意位置. - * 泛型的数量也可以为任意多个 - * 如:public K showKeyName(Generic container){ - * ... - * } - */ - - public class 泛型方法 { - @Test - public void test() { - test1(); - test2(new Integer(2)); - test3(new int[3],new Object()); - - //打印结果 - // null - // 2 - // [I@3d8c7aca - // java.lang.Object@5ebec15 - } - //该方法使用泛型T - public void test1() { - T t = null; - System.out.println(t); - } - //该方法使用泛型T - //并且参数和返回值都是T类型 - public T test2(T t) { - System.out.println(t); - return t; - } - - //该方法使用泛型T,E - //参数包括T,E - public void test3(T t, E e) { - System.out.println(t); - System.out.println(e); - } +```` +/** + * 这才是一个真正的泛型方法。 + * 首先在public与返回值之间的必不可少,这表明这是一个泛型方法,并且声明了一个泛型T + * 这个T可以出现在这个泛型方法的任意位置. + * 泛型的数量也可以为任意多个 + * 如:public K showKeyName(Generic container){ + * ... + * } + */ + + public class 泛型方法 { + @Test + public void test() { + test1(); + test2(new Integer(2)); + test3(new int[3],new Object()); + + //打印结果 + //null + //2 + //[I@3d8c7aca + //java.lang.Object@5ebec15 + } + //该方法使用泛型T + public void test1() { + T t = null; + System.out.println(t); + } + //该方法使用泛型T + //并且参数和返回值都是T类型 + public T test2(T t) { + System.out.println(t); + return t; } - -​ + //该方法使用泛型T,E + //参数包括T,E + public void test3(T t, E e) { + System.out.println(t); + System.out.println(e); + } +} +```` ### 类中的泛型方法 当然这并不是泛型方法的全部,泛型方法可以出现杂任何地方和任何场景中使用。但是有一种情况是非常特殊的,当泛型方法出现在泛型类中时,我们再通过一个例子看一下 - - //注意泛型类先写类名再写泛型,泛型方法先写泛型再写方法名 - //类中声明的泛型在成员和方法中可用 - class A { - { - T t1 ; - } - A (T t){ - this.t = t; - } - T t; - - public void test1() { - System.out.println(this.t); - } - - public void test2(T t,E e) { - System.out.println(t); - System.out.println(e); - } +```` +//注意泛型类先写类名再写泛型,泛型方法先写泛型再写方法名 +//类中声明的泛型在成员和方法中可用 +class A { + { + T t1 ; } - @Test - public void run () { - A a = new A<>(1); - a.test1(); - a.test2(2,"ds"); - // 1 - // 2 - // ds + A (T t){ + this.t = t; } - - static class B { - T t; - public void go () { - System.out.println(t); - } + T t; + + public void test1() { + System.out.println(this.t); + } + + public void test2(T t,E e) { + System.out.println(t); + System.out.println(e); } +} +@Test +public void run () { + A a = new A<>(1); + a.test1(); + a.test2(2,"ds"); +// 1 +// 2 +// ds +} +static class B { + T t; + public void go () { + System.out.println(t); + } +} +```` ### 泛型方法与可变参数 再看一个泛型方法和可变参数的例子: - - public class 泛型和可变参数 { - @Test - public void test () { - printMsg("dasd",1,"dasd",2.0,false); - print("dasdas","dasdas", "aa"); - } - //普通可变参数只能适配一种类型 - public void print(String ... args) { - for(String t : args){ - System.out.println(t); - } +```` +public class 泛型和可变参数 { + @Test + public void test () { + printMsg("dasd",1,"dasd",2.0,false); + print("dasdas","dasdas", "aa"); + } + //普通可变参数只能适配一种类型 + public void print(String ... args) { + for(String t : args){ + System.out.println(t); } - //泛型的可变参数可以匹配所有类型的参数。。有点无敌 - public void printMsg( T... args){ - for(T t : args){ - System.out.println(t); - } + } + //泛型的可变参数可以匹配所有类型的参数。。有点无敌 + public void printMsg( T... args){ + for(T t : args){ + System.out.println(t); } - //打印结果: - //dasd - //1 - //dasd - //2.0 - //false - } + //打印结果: + //dasd + //1 + //dasd + //2.0 + //false +} +```` ### 静态方法与泛型 静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。 即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。 +```` +public class StaticGenerator { + .... + .... + /** + * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法) + * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。 + * 如:public static void show(T t){..},此时编译器会提示错误信息: + "StaticGenerator cannot be refrenced from static context" + */ + public static void show(T t){ - public class StaticGenerator { - .... - .... - /** - * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法) - * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。 - * 如:public static void show(T t){..},此时编译器会提示错误信息: - "StaticGenerator cannot be refrenced from static context" - */ - public static void show(T t){ - - } } - +} +```` ## 泛型方法总结 泛型方法能使方法独立于类而产生变化,以下是一个基本的指导原则: @@ -445,90 +440,93 @@ public void showKeyValue(Generic obj){ 在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。 为泛型添加上边界,即传入的类型实参必须是指定类型的子类型 +```` +public class 泛型通配符与边界 { + public void showKeyValue(Generic obj){ + System.out.println("key value is " + obj.getKey()); + } + @Test + public void main() { + Generic gInteger = new Generic(123); + Generic gNumber = new Generic(456); + showKeyValue(gNumber); + //泛型中的子类也无法作为父类引用传入 +// showKeyValue(gInteger); + } + //直接使用?通配符可以接受任何类型作为泛型传入 + public void showKeyValueYeah(Generic obj) { + System.out.println(obj); + } + //只能传入number的子类或者number + public void showKeyValue1(Generic obj){ + System.out.println(obj); + } - public class 泛型通配符与边界 { - public void showKeyValue(Generic obj){ - System.out.println("key value is " + obj.getKey()); - } - @Test - public void main() { - Generic gInteger = new Generic(123); - Generic gNumber = new Generic(456); - showKeyValue(gNumber); - //泛型中的子类也无法作为父类引用传入 - // showKeyValue(gInteger); - } - //直接使用?通配符可以接受任何类型作为泛型传入 - public void showKeyValueYeah(Generic obj) { - System.out.println(obj); - } - //只能传入number的子类或者number - public void showKeyValue1(Generic obj){ - System.out.println(obj); - } - - //只能传入Integer的父类或者Integer - public void showKeyValue2(Generic obj){ - System.out.println(obj); - } - - @Test - public void testup () { - //这一行代码编译器会提示错误,因为String类型并不是Number类型的子类 - //showKeyValue1(generic1); - Generic generic1 = new Generic("11111"); - Generic generic2 = new Generic(2222); - Generic generic3 = new Generic(2.4f); - Generic generic4 = new Generic(2.56); - - showKeyValue1(generic2); - showKeyValue1(generic3); - showKeyValue1(generic4); - } - - @Test - public void testdown () { - - Generic generic1 = new Generic("11111"); - Generic generic2 = new Generic(2222); - Generic generic3 = new Generic(2); - // showKeyValue2(generic1);本行报错,因为String并不是Integer的父类 - showKeyValue2(generic2); - showKeyValue2(generic3); - } + //只能传入Integer的父类或者Integer + public void showKeyValue2(Generic obj){ + System.out.println(obj); + } + + @Test + public void testup () { + //这一行代码编译器会提示错误,因为String类型并不是Number类型的子类 + //showKeyValue1(generic1); + Generic generic1 = new Generic("11111"); + Generic generic2 = new Generic(2222); + Generic generic3 = new Generic(2.4f); + Generic generic4 = new Generic(2.56); + + showKeyValue1(generic2); + showKeyValue1(generic3); + showKeyValue1(generic4); } + @Test + public void testdown () { + + Generic generic1 = new Generic("11111"); + Generic generic2 = new Generic(2222); + Generic generic3 = new Generic(2); +// showKeyValue2(generic1);本行报错,因为String并不是Integer的父类 + showKeyValue2(generic2); + showKeyValue2(generic3); + } +} +```` == 关于泛型数组要提一下 == 看到了很多文章中都会提起泛型数组,经过查看sun的说明文档,在java中是”不能创建一个确切的泛型类型的数组”的。 - 也就是说下面的这个例子是不可以的: - - List[] ls = new ArrayList[10]; - - 而使用通配符创建泛型数组是可以的,如下面这个例子: - - List[] ls = new ArrayList[10]; - - 这样也是可以的: - - List[] ls = new ArrayList[10]; +也就是说下面的这个例子是不可以的: +```` +List[] ls = new ArrayList[10]; + +而使用通配符创建泛型数组是可以的,如下面这个例子: + +List[] ls = new ArrayList[10]; + +这样也是可以的: +List[] ls = new ArrayList[10]; +```` 下面使用Sun的一篇文档的一个例子来说明这个问题: - List[] lsa = new List[10]; // Not really allowed. - Object o = lsa; - Object[] oa = (Object[]) o; - List li = new ArrayList(); - li.add(new Integer(3)); - oa[1] = li; // Unsound, but passes run time store check - String s = lsa[1].get(0); // Run-time error: ClassCastException. +```` +List[] lsa = new List[10]; // Not really allowed. +Object o = lsa; +Object[] oa = (Object[]) o; +List li = new ArrayList(); +li.add(new Integer(3)); +oa[1] = li; // Unsound, but passes run time store check +String s = lsa[1].get(0); // Run-time error: ClassCastException. +```` > 这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[1]赋上一个ArrayList而不会出现异常,但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明,上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。 > > 而对泛型数组的声明进行限制,对于这样的情况,可以在编译期提示代码有类型安全问题,比没有任何提示要强很多。 > 下面采用通配符的方式是被允许的:数组的类型不可以是类型变量,除非是采用通配符的方式,因为对于通配符的方式,最后取出数据是要做显式的类型转换的。 +```` List[] lsa = new List[10]; // OK, array of unbounded wildcard type. Object o = lsa; Object[] oa = (Object[]) o; @@ -536,6 +534,7 @@ public void showKeyValue(Generic obj){ li.add(new Integer(3)); oa[1] = li; // Correct. Integer i = (Integer) lsa[1].get(0); // OK +```` 最后 @@ -580,13 +579,13 @@ return cache.put(key, value); 8. 你可以把List传递给一个接受List参数的方法吗? 对任何一个不太熟悉泛型的人来说,这个Java泛型题目看起来令人疑惑,因为乍看起来String是一种Object,所以 List应当可以用在需要List的地方,但是事实并非如此。真这样做的话会导致编译错误。如 果你再深一步考虑,你会发现Java这样做是有意义的,因为List可以存储任何类型的对象包括String, Integer等等,而List却只能用来存储Strings。 - +```` List objectList; List stringList; objectList = stringList; //compilation error incompatible types - +```` 9. Array中可以用泛型吗? 这可能是Java泛型面试题中最简单的一个了,当然前提是你要知道Array事实上并不支持泛型,这也是为什么Joshua Bloch在Effective Java一书中建议使用List来代替Array,因为List可以提供编译期的类型安全保证,而Array却不能。 @@ -594,9 +593,9 @@ objectList = stringList; //compilation error incompatible types 10. 如何阻止Java中的类型未检查的警告? 如果你把泛型和原始类型混合起来使用,例如下列代码,Java 5的javac编译器会产生类型未检查的警告,例如 - +```` List rawList = new ArrayList() - +```` 注意: Hello.java使用了未检查或称为不安全的操作; 这种警告可以使用@SuppressWarnings(“unchecked”)注解来屏蔽。 @@ -609,9 +608,6 @@ https://www.cnblogs.com/dengchengchao/p/9717097.html https://www.cnblogs.com/cat520/p/9353291.html https://www.cnblogs.com/coprince/p/8603492.html - - - ## 微信公众号 ### Java技术江湖 diff --git "a/docs/java/basic/14\343\200\201\346\236\232\344\270\276\347\261\273.md" "b/docs/java/basic/14\343\200\201\346\236\232\344\270\276\347\261\273.md" index 2891f98..e5219d4 100644 --- "a/docs/java/basic/14\343\200\201\346\236\232\344\270\276\347\261\273.md" +++ "b/docs/java/basic/14\343\200\201\346\236\232\344\270\276\347\261\273.md" @@ -94,136 +94,136 @@ ### 枚举类的具体使用 这部分内容参考https://blog.csdn.net/qq_27093465/article/details/52180865 #### 常量 - - public class 常量 { - } - enum Color { - Red, Green, Blue, Yellow - } - +```` +public class 常量 { +} +enum Color { + Red, Green, Blue, Yellow +} +```` #### switch JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强。 - - public static void showColor(Color color) { - switch (color) { - case Red: - System.out.println(color); - break; - case Blue: - System.out.println(color); - break; - case Yellow: - System.out.println(color); - break; - case Green: - System.out.println(color); - break; - } - } - +```` +public static void showColor(Color color) { + switch (color) { + case Red: + System.out.println(color); + break; + case Blue: + System.out.println(color); + break; + case Yellow: + System.out.println(color); + break; + case Green: + System.out.println(color); + break; + } +} +```` #### 向枚举中添加新方法 如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum 实例。 - - enum Color { - //每个颜色都是枚举类的一个实例,并且构造方法要和枚举类的格式相符合。 - //如果实例后面有其他内容,实例序列结束时要加分号。 - Red("红色", 1), Green("绿色", 2), Blue("蓝色", 3), Yellow("黄色", 4); - String name; - int index; - Color(String name, int index) { - this.name = name; - this.index = index; - } - public void showAllColors() { - //values是Color实例的数组,在通过index和name可以获取对应的值。 - for (Color color : Color.values()) { - System.out.println(color.index + ":" + color.name); - } +```` +enum Color { + //每个颜色都是枚举类的一个实例,并且构造方法要和枚举类的格式相符合。 + //如果实例后面有其他内容,实例序列结束时要加分号。 + Red("红色", 1), Green("绿色", 2), Blue("蓝色", 3), Yellow("黄色", 4); + String name; + int index; + Color(String name, int index) { + this.name = name; + this.index = index; + } + public void showAllColors() { + //values是Color实例的数组,在通过index和name可以获取对应的值。 + for (Color color : Color.values()) { + System.out.println(color.index + ":" + color.name); } } - +} +```` #### 覆盖枚举的方法 所有枚举类都继承自Enum类,所以可以重写该类的方法 下面给出一个toString()方法覆盖的例子。 - - @Override - public String toString() { - return this.index + ":" + this.name; - } - +```` +@Override +public String toString() { + return this.index + ":" + this.name; +} +```` #### 实现接口 所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。 - - enum Color implements Print{ - @Override - public void print() { - System.out.println(this.name); - } +```` +enum Color implements Print{ + @Override + public void print() { + System.out.println(this.name); } - +} +```` #### 使用接口组织枚举 搞个实现接口,来组织枚举,简单讲,就是分类吧。如果大量使用枚举的话,这么干,在写代码的时候,就很方便调用啦。 - - public class 用接口组织枚举 { - public static void main(String[] args) { - Food cf = chineseFood.dumpling; - Food jf = Food.JapaneseFood.fishpiece; - for (Food food : chineseFood.values()) { - System.out.println(food); - } - for (Food food : Food.JapaneseFood.values()) { - System.out.println(food); - } +```` +public class 用接口组织枚举 { + public static void main(String[] args) { + Food cf = chineseFood.dumpling; + Food jf = Food.JapaneseFood.fishpiece; + for (Food food : chineseFood.values()) { + System.out.println(food); } - } - interface Food { - enum JapaneseFood implements Food { - suse, fishpiece + for (Food food : Food.JapaneseFood.values()) { + System.out.println(food); } } - enum chineseFood implements Food { - dumpling, tofu +} +interface Food { + enum JapaneseFood implements Food { + suse, fishpiece } - +} +enum chineseFood implements Food { + dumpling, tofu +} +```` #### 枚举类集合 java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的 key是enum类型,而value则可以是任意类型。 EnumSet在JDK中没有找到实现类,这里写一个EnumMap的例子 - - public class 枚举类集合 { - public static void main(String[] args) { - EnumMap map = new EnumMap(Color.class); - map.put(Color.Blue, "Blue"); - map.put(Color.Yellow, "Yellow"); - map.put(Color.Red, "Red"); - System.out.println(map.get(Color.Red)); - } +```` +public class 枚举类集合 { + public static void main(String[] args) { + EnumMap map = new EnumMap(Color.class); + map.put(Color.Blue, "Blue"); + map.put(Color.Yellow, "Yellow"); + map.put(Color.Red, "Red"); + System.out.println(map.get(Color.Red)); } - +} +```` ## 使用枚举类的注意事项 -![image](https://img-blog.csdn.net/20170112172420090) +![image](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230403212903.png) 枚举类型对象之间的值比较,是可以使用==,直接来比较值,是否相等的,不是必须使用equals方法的哟。 因为枚举类Enum已经重写了equals方法 - - /** - * Returns true if the specified object is equal to this - * enum constant. - * - * @param other the object to be compared for equality with this object. - * @return true if the specified object is equal to this - * enum constant. - */ - public final boolean equals(Object other) { - return this==other; - } - +```` +/** + * Returns true if the specified object is equal to this + * enum constant. + * + * @param other the object to be compared for equality with this object. + * @return true if the specified object is equal to this + * enum constant. + */ +public final boolean equals(Object other) { + return this==other; +} +```` ## 枚举类的实现原理 这部分参考https://blog.csdn.net/mhmyqn/article/details/48087247 @@ -235,46 +235,46 @@ EnumSet在JDK中没有找到实现类,这里写一个EnumMap的例子 > Java在1.5中添加了java.lang.Enum抽象类,它是所有枚举类型基类。提供了一些基础属性和基础方法。同时,对把枚举用作Set和Map也提供了支持,即java.util.EnumSet和java.util.EnumMap。 接下来定义一个简单的枚举类 - - public enum Day { - MONDAY { - @Override - void say() { - System.out.println("MONDAY"); - } +```` +public enum Day { + MONDAY { + @Override + void say() { + System.out.println("MONDAY"); } - , TUESDAY { - @Override - void say() { - System.out.println("TUESDAY"); - } - }, FRIDAY("work"){ - @Override - void say() { - System.out.println("FRIDAY"); - } - }, SUNDAY("free"){ - @Override - void say() { - System.out.println("SUNDAY"); - } - }; - String work; - //没有构造参数时,每个实例可以看做常量。 - //使用构造参数时,每个实例都会变得不一样,可以看做不同的类型,所以编译后会生成实例个数对应的class。 - private Day(String work) { - this.work = work; + } + , TUESDAY { + @Override + void say() { + System.out.println("TUESDAY"); } - private Day() { - + }, FRIDAY("work"){ + @Override + void say() { + System.out.println("FRIDAY"); } - //枚举实例必须实现枚举类中的抽象方法 - abstract void say (); - + }, SUNDAY("free"){ + @Override + void say() { + System.out.println("SUNDAY"); + } + }; + String work; + //没有构造参数时,每个实例可以看做常量。 + //使用构造参数时,每个实例都会变得不一样,可以看做不同的类型,所以编译后会生成实例个数对应的class。 + private Day(String work) { + this.work = work; } + private Day() { -反编译结果 + } + //枚举实例必须实现枚举类中的抽象方法 + abstract void say (); +} +```` +反编译结果 +```` D:\MyTech\out\production\MyTech\com\javase\枚举类>javap Day.class Compiled from "Day.java" @@ -291,43 +291,43 @@ EnumSet在JDK中没有找到实现类,这里写一个EnumMap的例子 com.javase.枚举类.Day(java.lang.String, int, java.lang.String, com.javase.枚举类.Day$1); static {}; } - +```` > 可以看到,一个枚举在经过编译器编译过后,变成了一个抽象类,它继承了java.lang.Enum;而枚举中定义的枚举常量,变成了相应的public static final属性,而且其类型就抽象类的类型,名字就是枚举常量的名字. > > 同时我们可以在Operator.class的相同路径下看到四个内部类的.class文件com/mikan/Day$1.class、com/mikan/Day$2.class、com/mikan/Day$3.class、com/mikan/Day$4.class,也就是说这四个命名字段分别使用了内部类来实现的;同时添加了两个方法values()和valueOf(String);我们定义的构造方法本来只有一个参数,但却变成了三个参数;同时还生成了一个静态代码块。这些具体的内容接下来仔细看看。 下面分析一下字节码中的各部分,其中: - - InnerClasses: - static #23; //class com/javase/枚举类/Day$4 - static #18; //class com/javase/枚举类/Day$3 - static #14; //class com/javase/枚举类/Day$2 - static #10; //class com/javase/枚举类/Day$1 - +```` +InnerClasses: + static #23; //class com/javase/枚举类/Day$4 + static #18; //class com/javase/枚举类/Day$3 + static #14; //class com/javase/枚举类/Day$2 + static #10; //class com/javase/枚举类/Day$1 +```` 从中可以看到它有4个内部类,这四个内部类的详细信息后面会分析。 - - static {}; - descriptor: ()V - flags: ACC_STATIC - Code: - stack=5, locals=0, args_size=0 - 0: new #10 // class com/javase/枚举类/Day$1 - 3: dup - 4: ldc #11 // String MONDAY - 6: iconst_0 - 7: invokespecial #12 // Method com/javase/枚举类/Day$1."":(Ljava/lang/String;I)V - 10: putstatic #13 // Field MONDAY:Lcom/javase/枚举类/Day; - 13: new #14 // class com/javase/枚举类/Day$2 - 16: dup - 17: ldc #15 // String TUESDAY - 19: iconst_1 - 20: invokespecial #16 // Method com/javase/枚举类/Day$2."":(Ljava/lang/String;I)V - //后面类似,这里省略 - } - +```` +static {}; + descriptor: ()V + flags: ACC_STATIC + Code: + stack=5, locals=0, args_size=0 + 0: new #10 // class com/javase/枚举类/Day$1 + 3: dup + 4: ldc #11 // String MONDAY + 6: iconst_0 + 7: invokespecial #12 // Method com/javase/枚举类/Day$1."":(Ljava/lang/String;I)V + 10: putstatic #13 // Field MONDAY:Lcom/javase/枚举类/Day; + 13: new #14 // class com/javase/枚举类/Day$2 + 16: dup + 17: ldc #15 // String TUESDAY + 19: iconst_1 + 20: invokespecial #16 // Method com/javase/枚举类/Day$2."":(Ljava/lang/String;I)V + //后面类似,这里省略 +} +```` 其实编译器生成的这个静态代码块做了如下工作:分别设置生成的四个公共静态常量字段的值,同时编译器还生成了一个静态字段$VALUES,保存的是枚举类型定义的所有枚举常量 编译器添加的values方法: - +```` public static com.javase.Day[] values(); flags: ACC_PUBLIC, ACC_STATIC Code: @@ -336,16 +336,17 @@ EnumSet在JDK中没有找到实现类,这里写一个EnumMap的例子 3: invokevirtual #3 // Method "[Lcom/mikan/Day;".clone:()Ljava/lang/Object; 6: checkcast #4 // class "[Lcom/javase/Day;" 9: areturn - 这个方法是一个公共的静态方法,所以我们可以直接调用该方法(Day.values()),返回这个枚举值的数组,另外,这个方法的实现是,克隆在静态代码块中初始化的$VALUES字段的值,并把类型强转成Day[]类型返回。 +```` +这个方法是一个公共的静态方法,所以我们可以直接调用该方法(Day.values()),返回这个枚举值的数组,另外,这个方法的实现是,克隆在静态代码块中初始化的$VALUES字段的值,并把类型强转成Day[]类型返回。 造方法为什么增加了两个参数? 有一个问题,构造方法我们明明只定义了一个参数,为什么生成的构造方法是三个参数呢? - 从Enum类中我们可以看到,为每个枚举都定义了两个属性,name和ordinal,name表示我们定义的枚举常量的名称,如FRIDAY、TUESDAY,而ordinal是一个顺序号,根据定义的顺序分别赋予一个整形值,从0开始。在枚举常量初始化时,会自动为初始化这两个字段,设置相应的值,所以才在构造方法中添加了两个参数。即: - - 另外三个枚举常量生成的内部类基本上差不多,这里就不重复说明了。 +从Enum类中我们可以看到,为每个枚举都定义了两个属性,name和ordinal,name表示我们定义的枚举常量的名称,如FRIDAY、TUESDAY,而ordinal是一个顺序号,根据定义的顺序分别赋予一个整形值,从0开始。在枚举常量初始化时,会自动为初始化这两个字段,设置相应的值,所以才在构造方法中添加了两个参数。即: + +另外三个枚举常量生成的内部类基本上差不多,这里就不重复说明了。 > 我们可以从Enum类的代码中看到,定义的name和ordinal属性都是final的,而且大部分方法也都是final的,特别是clone、readObject、writeObject这三个方法,这三个方法和枚举通过静态代码块来进行初始化一起。 diff --git "a/docs/java/basic/15\343\200\201Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" "b/docs/java/basic/15\343\200\201Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" index ab4a401..259a732 100644 --- "a/docs/java/basic/15\343\200\201Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" +++ "b/docs/java/basic/15\343\200\201Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" @@ -26,18 +26,6 @@ * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) - ---- -title: 夯实Java基础系列15:Java注解简介和最佳实践 -date: 2019-9-15 15:56:26 # 文章生成时间,一般不改 -categories: - - Java技术江湖 - - Java基础 -tags: - - annotation - - Java注解 ---- - 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -73,9 +61,7 @@ Annotation 中文译过来就是注解、标释的意思,在 Java 中注解是 并且,往抽象地说,标签并不一定是一张纸,它可以是对人和事物的属性评价。也就是说,标签具备对于抽象事物的解释。 -![](https://img-blog.csdn.net/20170627213419176?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYnJpYmx1ZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) - - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230403213103.png) 所以,基于如此,我完成了自我的知识认知升级,我决定用标签来解释注解。 @@ -166,8 +152,8 @@ java.lang.annotation提供了四种元注解,专门注解其他的注解(在 JDK 内置注解 先来看几个 Java 内置的注解,让大家热热身。 - @Override 演示 - +@Override 演示 +```` class Parent { public void run() { } @@ -182,7 +168,9 @@ JDK 内置注解 public void run() { } } +```` @Deprecated 演示 +```` class Parent { /** @@ -201,7 +189,9 @@ class Parent { parent.run(); // 在编译器中此方法会显示过时标志 } } +```` @SuppressWarnings 演示 +```` class Parent { // 因为定义的 name 没有使用,那么编译器就会有警告,这时候使用此注解可以屏蔽掉警告 @@ -209,8 +199,10 @@ class Parent { @SuppressWarnings("all") private String name; } - +```` @FunctionalInterface 演示 + +```` /** * 此注解是 Java8 提出的函数式接口,接口中只允许有一个抽象方法 * 加上这个注解之后,类中多一个抽象方法或者少一个抽象方法都会报错 @@ -219,7 +211,7 @@ class Parent { interface Func { void run(); } - +```` ## 注解处理器实战 @@ -230,27 +222,30 @@ interface Func { 我们先来了解下如何通过在运行时使用反射获取在程序中的使用的注解信息。如下类注解和方法注解。 类注解 - Class aClass = ApiController.class; - Annotation[] annotations = aClass.getAnnotations(); - - for(Annotation annotation : annotations) { - if(annotation instanceof ApiAuthAnnotation) { - ApiAuthAnnotation apiAuthAnnotation = (ApiAuthAnnotation) annotation; - System.out.println("name: " + apiAuthAnnotation.name()); - System.out.println("age: " + apiAuthAnnotation.age()); - } + +```` +Class aClass = ApiController.class; +Annotation[] annotations = aClass.getAnnotations(); + +for(Annotation annotation : annotations) { + if(annotation instanceof ApiAuthAnnotation) { + ApiAuthAnnotation apiAuthAnnotation = (ApiAuthAnnotation) annotation; + System.out.println("name: " + apiAuthAnnotation.name()); + System.out.println("age: " + apiAuthAnnotation.age()); } - 方法注解 - Method method = ... //通过反射获取方法对象 - Annotation[] annotations = method.getDeclaredAnnotations(); - - for(Annotation annotation : annotations) { - if(annotation instanceof ApiAuthAnnotation) { - ApiAuthAnnotation apiAuthAnnotation = (ApiAuthAnnotation) annotation; - System.out.println("name: " + apiAuthAnnotation.name()); - System.out.println("age: " + apiAuthAnnotation.age()); - } - } +} +方法注解 +Method method = ... //通过反射获取方法对象 +Annotation[] annotations = method.getDeclaredAnnotations(); + +for(Annotation annotation : annotations) { + if(annotation instanceof ApiAuthAnnotation) { + ApiAuthAnnotation apiAuthAnnotation = (ApiAuthAnnotation) annotation; + System.out.println("name: " + apiAuthAnnotation.name()); + System.out.println("age: " + apiAuthAnnotation.age()); + } +} +```` 此部分内容可参考: 通过反射获取注解信息 注解处理器实战 @@ -260,33 +255,33 @@ interface Func { 接下来要做的事情: 写一个切面,拦截浏览器访问带注解的接口,取出注解信息,判断年龄来确定是否可以继续访问。 在 dispatcher-servlet.xml 文件中定义 aop 切面 - - - - - - - - - +```` + + + + + + + + +```` 切面类处理逻辑即注解处理器代码如 - - @Component("apiAuthAspect") - public class ApiAuthAspect { - - public Object auth(ProceedingJoinPoint pjp) throws Throwable { - Method method = ((MethodSignature) pjp.getSignature()).getMethod(); - ApiAuthAnnotation apiAuthAnnotation = method.getAnnotation(ApiAuthAnnotation.class); - Integer age = apiAuthAnnotation.age(); - if (age > 18) { - return pjp.proceed(); - } else { - throw new RuntimeException("你未满18岁,禁止访问"); - } +```` +@Component("apiAuthAspect") +public class ApiAuthAspect { + + public Object auth(ProceedingJoinPoint pjp) throws Throwable { + Method method = ((MethodSignature) pjp.getSignature()).getMethod(); + ApiAuthAnnotation apiAuthAnnotation = method.getAnnotation(ApiAuthAnnotation.class); + Integer age = apiAuthAnnotation.age(); + if (age > 18) { + return pjp.proceed(); + } else { + throw new RuntimeException("你未满18岁,禁止访问"); } } - - +} +```` ## 不同类型的注解 @@ -295,7 +290,7 @@ interface Func { 你可以在运行期访问类,方法或者变量的注解信息,下是一个访问类注解的例子: ``` - Class aClass = TheClass.class; +Class aClass = TheClass.class; Annotation[] annotations = aClass.getAnnotations(); for(Annotation annotation : annotations){ diff --git "a/docs/java/basic/16\343\200\201JavaIO\346\265\201.md" "b/docs/java/basic/16\343\200\201JavaIO\346\265\201.md" index a405b7f..4403458 100644 --- "a/docs/java/basic/16\343\200\201JavaIO\346\265\201.md" +++ "b/docs/java/basic/16\343\200\201JavaIO\346\265\201.md" @@ -24,15 +24,6 @@ * [微信公众号](#微信公众号) * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) ---- -title: 夯实Java基础系列16:一文读懂Java IO流和常见面试题 -date: 2019-9-16 15:56:26 # 文章生成时间,一般不改 -categories: - - Java技术江湖 - - Java基础 -tags: - - Java IO流 ---- 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -60,7 +51,6 @@ tags: > 在这一小节,我会试着给出Java IO(java.io)包下所有类的概述。更具体地说,我会根据类的用途对类进行分组。这个分组将会使你在未来的工作中,进行类的用途判定时,或者是为某个特定用途选择类时变得更加容易。 -​ **输入和输出** 术语“输入”和“输出”有时候会有一点让人疑惑。一个应用程序的输入往往是另外一个应用程序的输出 @@ -174,7 +164,7 @@ java.io.InputStream类是所有Java IO输入流的基类。如果你正在开发 这使得RandomAccessFile可以覆盖一个文件的某些部分、或者追加内容到它的末尾、或者删除它的某些内容,当然它也可以从文件的任何位置开始读取文件。 下面是具体例子: - +```` @Test //文件流范例,打开一个文件的输入流,读取到字节数组,再写入另一个文件的输出流 public void test1() { @@ -193,6 +183,7 @@ java.io.InputStream类是所有Java IO输入流的基类。如果你正在开发 e.printStackTrace(); } } +```` ### 字符流和字节流 Java IO的Reader和Writer除了基于字符之外,其他方面都与InputStream和OutputStream非常类似。他们被用于读写文本。InputStream和OutputStream是基于字节的,还记得吗? @@ -204,46 +195,47 @@ Writer Writer类是Java IO中所有Writer的基类。子类包括BufferedWriter和PrintWriter等等。 这是一个简单的Java IO Reader的例子: +```` +Reader reader = new FileReader("c:\\data\\myfile.txt"); - Reader reader = new FileReader("c:\\data\\myfile.txt"); - - int data = reader.read(); - - while(data != -1){ - - char dataChar = (char) data; - - data = reader.read(); - - } +int data = reader.read(); + +while(data != -1){ + char dataChar = (char) data; + + data = reader.read(); + +} +```` 你通常会使用Reader的子类,而不会直接使用Reader。Reader的子类包括InputStreamReader,CharArrayReader,FileReader等等。可以查看Java IO概述浏览完整的Reader表格。 **整合Reader与InputStream** 一个Reader可以和一个InputStream相结合。如果你有一个InputStream输入流,并且想从其中读取字符,可以把这个InputStream包装到InputStreamReader中。把InputStream传递到InputStreamReader的构造函数中: - - Reader reader = new InputStreamReader(inputStream); +```` +Reader reader = new InputStreamReader(inputStream); +```` 在构造函数中可以指定解码方式。 **Writer** Writer类是Java IO中所有Writer的基类。子类包括BufferedWriter和PrintWriter等等。这是一个Java IO Writer的例子: +```` +Writer writer = new FileWriter("c:\\data\\file-output.txt"); - Writer writer = new FileWriter("c:\\data\\file-output.txt"); - - writer.write("Hello World Writer"); - - writer.close(); +writer.write("Hello World Writer"); +writer.close(); +```` 同样,你最好使用Writer的子类,不需要直接使用Writer,因为子类的实现更加明确,更能表现你的意图。常用子类包括OutputStreamWriter,CharArrayWriter,FileWriter等。Writer的write(int c)方法,会将传入参数的低16位写入到Writer中,忽略高16位的数据。 **整合Writer和OutputStream** 与Reader和InputStream类似,一个Writer可以和一个OutputStream相结合。把OutputStream包装到OutputStreamWriter中,所有写入到OutputStreamWriter的字符都将会传递给OutputStream。这是一个OutputStreamWriter的例子: - - Writer writer = new OutputStreamWriter(outputStream); - +```` +Writer writer = new OutputStreamWriter(outputStream); +```` ### IO管道 Java IO中的管道为运行在同一个JVM中的两个线程提供了通信的能力。所以管道也可以作为数据源以及目标媒介。 @@ -258,37 +250,38 @@ Java IO中的管道为运行在同一个JVM中的两个线程提供了通信的 Java IO管道示例 这是一个如何将PipedInputStream和PipedOutputStream关联起来的简单例子: - - //使用管道来完成两个线程间的数据点对点传递 - @Test - public void test2() throws IOException { - PipedInputStream pipedInputStream = new PipedInputStream(); - PipedOutputStream pipedOutputStream = new PipedOutputStream(pipedInputStream); - new Thread(new Runnable() { - @Override - public void run() { - try { - pipedOutputStream.write("hello input".getBytes()); - pipedOutputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } +```` +//使用管道来完成两个线程间的数据点对点传递 + @Test + public void test2() throws IOException { + PipedInputStream pipedInputStream = new PipedInputStream(); + PipedOutputStream pipedOutputStream = new PipedOutputStream(pipedInputStream); + new Thread(new Runnable() { + @Override + public void run() { + try { + pipedOutputStream.write("hello input".getBytes()); + pipedOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); } - }).start(); - new Thread(new Runnable() { - @Override - public void run() { - try { - byte []arr = new byte[128]; - while (pipedInputStream.read(arr) != -1) { - System.out.println(Arrays.toString(arr)); - } - pipedInputStream.close(); - } catch (IOException e) { - e.printStackTrace(); + } + }).start(); + new Thread(new Runnable() { + @Override + public void run() { + try { + byte []arr = new byte[128]; + while (pipedInputStream.read(arr) != -1) { + System.out.println(Arrays.toString(arr)); } + pipedInputStream.close(); + } catch (IOException e) { + e.printStackTrace(); } - }).start(); + } + }).start(); +```` 管道和线程 请记得,当使用两个相关联的管道流时,务必将它们分配给不同的线程。read()方法和write()方法调用时会导致流阻塞,这意味着如果你尝试在一个线程中同时进行读和写,可能会导致线程死锁。 @@ -304,7 +297,7 @@ Java中网络的内容或多或少的超出了Java IO的范畴。关于Java网 当两个进程之间建立了网络连接之后,他们通信的方式如同操作文件一样:利用InputStream读取数据,利用OutputStream写入数据。换句话来说,Java网络API用来在不同进程之间建立网络连接,而Java IO则用来在建立了连接之后的进程之间交换数据。 基本上意味着如果你有一份能够对文件进行写入某些数据的代码,那么这些数据也可以很容易地写入到网络连接中去。你所需要做的仅仅只是在代码中利用OutputStream替代FileOutputStream进行数据的写入。因为FileOutputStream是OuputStream的子类,所以这么做并没有什么问题。 - +```` //从网络中读取字节流也可以直接使用OutputStream public void test3() { //读取网络进程的输出流 @@ -318,7 +311,7 @@ Java中网络的内容或多或少的超出了Java IO的范畴。关于Java网 //处理网络信息 //do something with the OutputStream } - +```` ### 字节和字符数组 @@ -330,7 +323,7 @@ Java中网络的内容或多或少的超出了Java IO的范畴。关于Java网 前面的例子中,字符数组或字节数组是用来缓存数据的临时存储空间,不过它们同时也可以作为数据来源或者写入目的地。 举个例子: - +```` //字符数组和字节数组在io过程中的作用 public void test4() { //arr和brr分别作为数据源 @@ -339,7 +332,7 @@ Java中网络的内容或多或少的超出了Java IO的范畴。关于Java网 byte []brr = {1,2,3,4,5}; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(brr); } - +```` ### System.in, System.out, System.err System.in, System.out, System.err这3个流同样是常见的数据来源和数据流目的地。使用最多的可能是在控制台程序里利用System.out将输出打印到控制台上。 @@ -358,7 +351,7 @@ JVM启动的时候通过Java运行时初始化这3个流,所以你不需要初 System.out和System.err的简单例子: 这是一个System.out和System.err结合使用的简单示例: - +```` //测试System.in, System.out, System.err public static void main(String[] args) { int in = new Scanner(System.in).nextInt(); @@ -370,7 +363,7 @@ System.out和System.err的简单例子: // 10 // out } - +```` ### 字符流的Buffered和Filter @@ -437,8 +430,6 @@ Filter Stream是一种IO流主要作用是用来对存在的流增加一些额 在java.io包中主要由4个可用的filter Stream。两个字节filter stream,两个字符filter stream. 分别是FilterInputStream, FilterOutputStream, FilterReader and FilterWriter.这些类是抽象类,不能被实例化的。 - - ### 在文件拷贝的时候,那一种流可用提升更多的性能? 在字节流的时候,使用BufferedInputStream和BufferedOutputStream。 在字符流的时候,使用BufferedReader 和 BufferedWriter diff --git "a/docs/java/basic/17\343\200\201\345\244\232\347\272\277\347\250\213.md" "b/docs/java/basic/17\343\200\201\345\244\232\347\272\277\347\250\213.md" index e2cc758..3045d4e 100644 --- "a/docs/java/basic/17\343\200\201\345\244\232\347\272\277\347\250\213.md" +++ "b/docs/java/basic/17\343\200\201\345\244\232\347\272\277\347\250\213.md" @@ -19,15 +19,6 @@ * [微信公众号](#微信公众号) * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) ---- -title: 夯实Java基础系列17:一文搞懂Java多线程使用方式、实现原理以及常见面试题 -date: 2019-9-17 15:56:26 # 文章生成时间,一般不改 -categories: - - Java技术江湖 - - Java基础 -tags: - - 多线程 ---- 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial diff --git "a/docs/java/basic/18\343\200\201\346\267\261\345\205\245\347\220\206\350\247\243\345\206\205\351\203\250\347\261\273.md" "b/docs/java/basic/18\343\200\201\346\267\261\345\205\245\347\220\206\350\247\243\345\206\205\351\203\250\347\261\273.md" index 0ce799d..adf7861 100644 --- "a/docs/java/basic/18\343\200\201\346\267\261\345\205\245\347\220\206\350\247\243\345\206\205\351\203\250\347\261\273.md" +++ "b/docs/java/basic/18\343\200\201\346\267\261\345\205\245\347\220\206\350\247\243\345\206\205\351\203\250\347\261\273.md" @@ -12,21 +12,10 @@ * [内部类初始化](#内部类初始化) * [内部类的重载](#内部类的重载) * [内部类的继承](#内部类的继承) - * [Java内部类的实现原理](#java内部类的实现原理) * [参考文章](#参考文章) * [微信公众号](#微信公众号) * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) ---- -title: 夯实Java基础系列18:深入理解Java内部类及其实现原理 -date: 2019-9-18 15:56:26 # 文章生成时间,一般不改 -categories: - - Java技术江湖 - - Java基础 -tags: - - Java内部类 ---- - 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -58,8 +47,6 @@ tags: > > (4)外部类不能直接访问内部类的的成员,但可以通过内部类对象来访问 - -   内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的。   因为当某个外围类的对象创建内部类的对象时,此内部类会捕获一个隐式引用,它引用了实例化该内部对象的外围类对象。通过这个指针,可以访问外围类对象的全部状态。 @@ -121,125 +108,125 @@ tags: 3 外部类和它的内部类之间的关系 +```` +//本节讨论内部类以及不同访问权限的控制 +//内部类只有在使用时才会被加载。 +//外部类B +public class B{ + int i = 1; + int j = 1; + static int s = 1; + static int ss = 1; + A a; + AA aa; + AAA aaa; + //内部类A + + public class A { +// static void go () { +// +// } +// static { +// +// } +// static int b = 1;//非静态内部类不能有静态成员变量和静态代码块和静态方法, + // 因为内部类在外部类加载时并不会被加载和初始化。 + //所以不会进行静态代码的调用 + int i = 2;//外部类无法读取内部类的成员,而内部类可以直接访问外部类成员 - //本节讨论内部类以及不同访问权限的控制 - //内部类只有在使用时才会被加载。 - //外部类B - public class B{ - int i = 1; - int j = 1; - static int s = 1; - static int ss = 1; - A a; - AA aa; - AAA aaa; - //内部类A - - public class A { - // static void go () { - // - // } - // static { - // - // } - // static int b = 1;//非静态内部类不能有静态成员变量和静态代码块和静态方法, - // 因为内部类在外部类加载时并不会被加载和初始化。 - //所以不会进行静态代码的调用 - int i = 2;//外部类无法读取内部类的成员,而内部类可以直接访问外部类成员 - - public void test() { - System.out.println(j); - j = 2; - System.out.println(j); - System.out.println(s);//可以访问类的静态成员变量 - } - public void test2() { - AA aa = new AA(); - AAA aaa = new AAA(); - } - - } - //静态内部类S,可以被外部访问 - public static class S { - int i = 1;//访问不到非静态变量。 - static int s = 0;//可以有静态变量 - - public static void main(String[] args) { - System.out.println(s); - } - @Test - public void test () { - // System.out.println(j);//报错,静态内部类不能读取外部类的非静态变量 - System.out.println(s); - System.out.println(ss); - s = 2; - ss = 2; - System.out.println(s); - System.out.println(ss); - } + public void test() { + System.out.println(j); + j = 2; + System.out.println(j); + System.out.println(s);//可以访问类的静态成员变量 } - - //内部类AA,其实这里加protected相当于default - //因为外部类要调用内部类只能通过B。并且无法直接继承AA,所以必须在同包 - //的类中才能调用到(这里不考虑静态内部类),那么就和default一样了。 - protected class AA{ - int i = 2;//内部类之间不共享变量 - public void test (){ - A a = new A(); - AAA aaa = new AAA(); - //内部类之间可以互相访问。 - } + public void test2() { + AA aa = new AA(); + AAA aaa = new AAA(); } - //包外部依然无法访问,因为包没有继承关系,所以找不到这个类 - protected static class SS{ - int i = 2;//内部类之间不共享变量 - public void test (){ - - //内部类之间可以互相访问。 - } - } - //私有内部类A,对外不可见,但对内部类和父类可见 - private class AAA { - int i = 2;//内部类之间不共享变量 - - public void test() { - A a = new A(); - AA aa = new AA(); - //内部类之间可以互相访问。 - } + + } + //静态内部类S,可以被外部访问 + public static class S { + int i = 1;//访问不到非静态变量。 + static int s = 0;//可以有静态变量 + + public static void main(String[] args) { + System.out.println(s); } @Test - public void test(){ + public void test () { +// System.out.println(j);//报错,静态内部类不能读取外部类的非静态变量 + System.out.println(s); + System.out.println(ss); + s = 2; + ss = 2; + System.out.println(s); + System.out.println(ss); + } + } + + //内部类AA,其实这里加protected相当于default + //因为外部类要调用内部类只能通过B。并且无法直接继承AA,所以必须在同包 + //的类中才能调用到(这里不考虑静态内部类),那么就和default一样了。 + protected class AA{ + int i = 2;//内部类之间不共享变量 + public void test (){ A a = new A(); - a.test(); - //内部类可以修改外部类的成员变量 - //打印出 1 2 - B b = new B(); - + AAA aaa = new AAA(); + //内部类之间可以互相访问。 } } + //包外部依然无法访问,因为包没有继承关系,所以找不到这个类 + protected static class SS{ + int i = 2;//内部类之间不共享变量 + public void test (){ + //内部类之间可以互相访问。 + } + } + //私有内部类A,对外不可见,但对内部类和父类可见 + private class AAA { + int i = 2;//内部类之间不共享变量 -​ - //另一个外部类 - class C { - @Test public void test() { - //首先,其他类内部类只能通过外部类来获取其实例。 - B.S s = new B.S(); - //静态内部类可以直接通过B类直接获取,不需要B的实例,和静态成员变量类似。 - //B.A a = new B.A(); - //当A不是静态类时这行代码会报错。 - //需要使用B的实例来获取A的实例 - B b = new B(); - B.A a = b.new A(); - B.AA aa = b.new AA();//B和C同包,所以可以访问到AA - // B.AAA aaa = b.new AAA();AAA为私有内部类,外部类不可见 - //当A使用private修饰时,使用B的实例也无法获取A的实例,这一点和私有变量是一样的。 - //所有普通的内部类与类中的一个变量是类似的。静态内部类则与静态成员类似。 + A a = new A(); + AA aa = new AA(); + //内部类之间可以互相访问。 } } + @Test + public void test(){ + A a = new A(); + a.test(); + //内部类可以修改外部类的成员变量 + //打印出 1 2 + B b = new B(); + } +} +```` + +```` +//另一个外部类 +class C { + @Test + public void test() { + //首先,其他类内部类只能通过外部类来获取其实例。 + B.S s = new B.S(); + //静态内部类可以直接通过B类直接获取,不需要B的实例,和静态成员变量类似。 + //B.A a = new B.A(); + //当A不是静态类时这行代码会报错。 + //需要使用B的实例来获取A的实例 + B b = new B(); + B.A a = b.new A(); + B.AA aa = b.new AA();//B和C同包,所以可以访问到AA +// B.AAA aaa = b.new AAA();AAA为私有内部类,外部类不可见 + //当A使用private修饰时,使用B的实例也无法获取A的实例,这一点和私有变量是一样的。 + //所有普通的内部类与类中的一个变量是类似的。静态内部类则与静态成员类似。 + } +} +```` ## 内部类的加载 可能刚才的例子中没办法直观地看到内部类是如何加载的,接下来用例子展示一下内部类加载的过程。 @@ -277,43 +264,43 @@ tags: 需要注意的是: 局部内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。 +```` +public class 局部内部类 { + class A {//局部内部类就是写在方法里的类,只在方法执行时加载,一次性使用。 + public void test() { + class B { + public void test () { + class C { - public class 局部内部类 { - class A {//局部内部类就是写在方法里的类,只在方法执行时加载,一次性使用。 - public void test() { - class B { - public void test () { - class C { - - } } } } } - @Test - public void test () { - int i = 1; - final int j = 2; - class A { - @Test - public void test () { - System.out.println(i); - System.out.println(j); - } - } - A a = new A(); - System.out.println(a); - } - - static class B { - public static void test () { - //static class A报错,方法里不能定义静态内部类。 - //因为只有在方法调用时才能进行类加载和初始化。 - + } + @Test + public void test () { + int i = 1; + final int j = 2; + class A { + @Test + public void test () { + System.out.println(i); + System.out.println(j); } } + A a = new A(); + System.out.println(a); } + static class B { + public static void test () { + //static class A报错,方法里不能定义静态内部类。 + //因为只有在方法调用时才能进行类加载和初始化。 + + } + } +} +```` ### 匿名内部类 简单地说:匿名内部类就是没有名字的内部类,并且,匿名内部类是局部内部类的一种特殊形式。什么情况下需要使用匿名内部类?如果满足下面的一些条件,使用匿名内部类是比较合适的: @@ -336,61 +323,61 @@ tags: >6  因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。 一个匿名内部类的例子: +```` +public class 匿名内部类 { + +} +interface D{ + void run (); +} +abstract class E{ + E (){ - public class 匿名内部类 { - - } - interface D{ - void run (); - } - abstract class E{ - E (){ - - } - abstract void work(); } - class A { - - @Test - public void test (int k) { - //利用接口写出一个实现该接口的类的实例。 - //有且仅有一个实例,这个类无法重用。 - new Runnable() { - @Override - public void run() { - // k = 1;报错,当外部方法中的局部变量在内部类使用中必须改为final类型。 - //因为方外部法中即使改变了这个变量也不会反映到内部类中。 - //所以对于内部类来讲这只是一个常量。 - System.out.println(100); - System.out.println(k); - } - }; - new D(){ - //实现接口的匿名类 - int i =1; - @Override - public void run() { - System.out.println("run"); - System.out.println(i); - System.out.println(k); - } - }.run(); - new E(){ - //继承抽象类的匿名类 - int i = 1; - void run (int j) { - j = 1; - } - - @Override - void work() { - - } - }; + abstract void work(); +} +class A { + + @Test + public void test (int k) { + //利用接口写出一个实现该接口的类的实例。 + //有且仅有一个实例,这个类无法重用。 + new Runnable() { + @Override + public void run() { +// k = 1;报错,当外部方法中的局部变量在内部类使用中必须改为final类型。 + //因为方外部法中即使改变了这个变量也不会反映到内部类中。 + //所以对于内部类来讲这只是一个常量。 + System.out.println(100); + System.out.println(k); } - + }; + new D(){ + //实现接口的匿名类 + int i =1; + @Override + public void run() { + System.out.println("run"); + System.out.println(i); + System.out.println(k); + } + }.run(); + new E(){ + //继承抽象类的匿名类 + int i = 1; + void run (int j) { + j = 1; + } + + @Override + void work() { + + } + }; } +} +```` ### 匿名内部类里的final 使用的形参为何要为final @@ -401,37 +388,36 @@ tags: > 为什么必须要为final呢? > > 首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用: -> -> - public class OuterClass { - public void display(final String name,String age){ - class InnerClass{ - void display(){ - System.out.println(name); - } +```` +public class OuterClass { + public void display(final String name,String age){ + class InnerClass{ + void display(){ + System.out.println(name); } } } - +} +```` 从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下: - - public class OuterClass$InnerClass { - public InnerClass(String name,String age){ - this.InnerClass$name = name; - this.InnerClass$age = age; - } +```` +public class OuterClass$InnerClass { + public InnerClass(String name,String age){ + this.InnerClass$name = name; + this.InnerClass$age = age; + } -​ - public void display(){ - System.out.println(this.InnerClass$name + "----" + this.InnerClass$age ); - } + + public void display(){ + System.out.println(this.InnerClass$name + "----" + this.InnerClass$age ); } - +} +```` 所以从上面代码来看,内部类并不是直接调用方法传递的参数,而是利用自身的构造器对传入的参数进行备份,自己内部方法调用的实际上时自己的属性而不是外部方法传递进来的参数。 -> 直到这里还没有解释为什么是final +> 直到这里还没有解释为什么是final > 在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的。 > @@ -444,63 +430,62 @@ tags: ## 内部类初始化 我们一般都是利用构造器来完成某个实例的初始化工作的,但是匿名内部类是没有构造器的!那怎么来初始化匿名内部类呢?使用构造代码块!利用构造代码块能够达到为匿名内部类创建一个构造器的效果。 - - public class OutClass { - public InnerClass getInnerClass(final int age,final String name){ - return new InnerClass() { - int age_ ; - String name_; - //构造代码块完成初始化工作 - { - if(0 < age && age < 200){ - age_ = age; - name_ = name; - } - } - public String getName() { - return name_; +```` +public class OutClass { + public InnerClass getInnerClass(final int age,final String name){ + return new InnerClass() { + int age_ ; + String name_; + //构造代码块完成初始化工作 + { + if(0 < age && age < 200){ + age_ = age; + name_ = name; } - - public int getAge() { - return age_; - } - }; - } - - -​ + } + public String getName() { + return name_; + } + + public int getAge() { + return age_; + } + }; + } +```` + ## 内部类的重载 +如果你创建了一个内部类,然后继承其外围类并重新定义此内部类时,会发生什么呢?也就是说,内部类可以被重载吗?这看起来似乎是个很有用的点子,但是“重载”内部类就好像它是外围类的一个方法,其实并不起什么作用: +```` +class Egg { + private Yolk y; + + protected class Yolk { + public Yolk() { + System.out.println("Egg.Yolk()"); + } + } + + public Egg() { + System.out.println("New Egg()"); + y = new Yolk(); + } +} + +public class BigEgg extends Egg { + public class Yolk { + public Yolk() { + System.out.println("BigEgg.Yolk()"); + } + } + + public static void main(String[] args) { + new BigEgg(); + } +} -  如果你创建了一个内部类,然后继承其外围类并重新定义此内部类时,会发生什么呢?也就是说,内部类可以被重载吗?这看起来似乎是个很有用的点子,但是“重载”内部类就好像它是外围类的一个方法,其实并不起什么作用: - - - class Egg { - private Yolk y; - - protected class Yolk { - public Yolk() { - System.out.println("Egg.Yolk()"); - } - } - - public Egg() { - System.out.println("New Egg()"); - y = new Yolk(); - } - } - - public class BigEgg extends Egg { - public class Yolk { - public Yolk() { - System.out.println("BigEgg.Yolk()"); - } - } - - public static void main(String[] args) { - new BigEgg(); - } - } +```` 复制代码 输出结果为: New Egg() @@ -512,7 +497,7 @@ tags: ## 内部类的继承 因为内部类的构造器要用到其外围类对象的引用,所以在你继承一个内部类的时候,事情变得有点复杂。问题在于,那个“秘密的”外围类对象的引用必须被初始化,而在被继承的类中并不存在要联接的缺省对象。要解决这个问题,需使用专门的语法来明确说清它们之间的关联: - +```` class WithInner { class Inner { Inner(){ @@ -533,10 +518,11 @@ tags: InheritInner ii = new InheritInner(wi); } } -复制代码 -输出结果为: -this is a constructor in WithInner.Inner -this is a constructor in InheritInner +```` + 复制代码 + 输出结果为: + this is a constructor in WithInner.Inner + this is a constructor in InheritInner 可以看到,InheritInner 只继承自内部类,而不是外围类。但是当要生成一个构造器时,缺省的构造器并不算好,而且你不能只是传递一个指向外围类对象的引用。此外,你必须在构造器内使用如下语法: enclosingClassReference.super(); @@ -544,38 +530,6 @@ enclosingClassReference.super(); 有关匿名内部类实现回调,事件驱动,委托等机制的文章将在下一节讲述。 -## Java内部类的实现原理 - -内部类为什么能够访问外部类的成员? - -定义内部类如下: - -![Java 内部类](http://p3.pstatp.com/large/pgc-image/1539338093053104f5529ec) - -使用javap命令进行反编译。 - -编译后得到Main.class Main$Inner.class两个文件,反编译Main$Inner.class文件如下: - -![Java 内部类](http://p1.pstatp.com/large/pgc-image/1539338144888f57662c7bc) - -可以看到,内部类其实拥有外部类的一个引用,在构造函数中将外部类的引用传递进来。 - -匿名内部类为什么只能访问局部的final变量? - -其实可以这样想,当方法执行完毕后,局部变量的生命周期就结束了,而局部内部类对象的生命周期可能还没有结束,那么在局部内部类中访问局部变量就不可能了,所以将局部变量改为final,改变其生命周期。 - -编写代码如下: - -![Java 内部类](http://p1.pstatp.com/large/pgc-image/15393408957685bcddd1c64) - -这段代码编译为Main.class Main$1.class两个文件,反编译Main$1.class文件如下: - -![Java 内部类](http://p3.pstatp.com/large/pgc-image/1539341219661a629a53b8c) - -可以看到,java将编译时已经确定的值直接复制,进行替换,将无法确定的值放到了内部类的常量池中,并在构造函数中将其从常量池取出到字段中。 - -可以看出,java将局部变量m直接进行复制,所以其并不是原来的值,若在内部类中将m更改,局部变量的m值不会变,就会出现数据不一致,所以java就将其限制为final,使其不能进行更改,这样数据不一致的问题就解决了。 - ## 参考文章 https://www.cnblogs.com/hujingnb/p/10181621.html diff --git "a/docs/java/basic/19\343\200\201Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md" "b/docs/java/basic/19\343\200\201Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md" index 8ad5827..c5c96e2 100644 --- "a/docs/java/basic/19\343\200\201Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md" +++ "b/docs/java/basic/19\343\200\201Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md" @@ -61,8 +61,7 @@ tags: 在编写java程序中,我们最常用的除了八种基本数据类型,String对象外还有一个集合类,在我们的的程序中到处充斥着集合类的身影! -![image](https://img-blog.csdnimg.cn/20190222094403579.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p6dzE1MzE0MzkwOTA=,size_16,color_FFFFFF,t_70) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230403214433.png) java中集合大家族的成员实在是太丰富了,有常用的ArrayList、HashMap、HashSet,也有不常用的Stack、Queue,有线程安全的Vector、HashTable,也有线程不安全的LinkedList、TreeMap等等! ![](https://images0.cnblogs.com/blog/381060/201312/28124707-3a873160808e457686d67c118af6fa70.png) @@ -90,63 +89,63 @@ java中集合大家族的成员实在是太丰富了,有常用的ArrayList、H 在Java中所有实现了Collection接口的类都必须提供两套标准的构造函数,一个是无参,用于创建一个空的Collection,一个是带有Collection参数的有参构造函数,用于创建一个新的Collection,这个新的Collection与传入进来的Collection具备相同的元素。 //要求实现基本的增删改查方法,并且需要能够转换为数组类型 +```` +public class Collection接口 { + class collect implements Collection { - public class Collection接口 { - class collect implements Collection { - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object o) { - return false; - } - - @Override - public Iterator iterator() { - return null; - } - - @Override - public Object[] toArray() { - return new Object[0]; - } - - @Override - public boolean add(Object o) { - return false; - } - - @Override - public boolean remove(Object o) { - return false; - } - - @Override - public boolean addAll(Collection c) { - return false; - } - - @Override - public void clear() { - - } - //省略部分代码 - - @Override - public Object[] toArray(Object[] a) { - return new Object[0]; - } + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object o) { + return false; + } + + @Override + public Iterator iterator() { + return null; + } + + @Override + public Object[] toArray() { + return new Object[0]; + } + + @Override + public boolean add(Object o) { + return false; + } + + @Override + public boolean remove(Object o) { + return false; + } + + @Override + public boolean addAll(Collection c) { + return false; } - } + @Override + public void clear() { + + } +//省略部分代码 + + @Override + public Object[] toArray(Object[] a) { + return new Object[0]; + } + } +} +```` ## List接口 > List接口为Collection直接接口。List所代表的是有序的Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。 @@ -173,84 +172,84 @@ java中集合大家族的成员实在是太丰富了,有常用的ArrayList、H > > 2.4、Stack > Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。。 +```` +public class List接口 { + //下面是List的继承关系,由于List接口规定了包括诸如索引查询,迭代器的实现,所以实现List接口的类都会有这些方法。 + //所以不管是ArrayList和LinkedList底层都可以使用数组操作,但一般不提供这样外部调用方法。 + // public interface Iterable +// public interface Collection extends Iterable +// public interface List extends Collection + class MyList implements List { + + @Override + public int size() { + return 0; + } - public class List接口 { - //下面是List的继承关系,由于List接口规定了包括诸如索引查询,迭代器的实现,所以实现List接口的类都会有这些方法。 - //所以不管是ArrayList和LinkedList底层都可以使用数组操作,但一般不提供这样外部调用方法。 - // public interface Iterable - // public interface Collection extends Iterable - // public interface List extends Collection - class MyList implements List { - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object o) { - return false; - } - - @Override - public Iterator iterator() { - return null; - } - - @Override - public Object[] toArray() { - return new Object[0]; - } - - @Override - public boolean add(Object o) { - return false; - } - - @Override - public boolean remove(Object o) { - return false; - } - - @Override - public void clear() { - - } - - //省略部分代码 - - @Override - public Object get(int index) { - return null; - } - - @Override - public ListIterator listIterator() { - return null; - } - - @Override - public ListIterator listIterator(int index) { - return null; - } - - @Override - public List subList(int fromIndex, int toIndex) { - return null; - } - - @Override - public Object[] toArray(Object[] a) { - return new Object[0]; - } + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object o) { + return false; + } + + @Override + public Iterator iterator() { + return null; + } + + @Override + public Object[] toArray() { + return new Object[0]; + } + + @Override + public boolean add(Object o) { + return false; + } + + @Override + public boolean remove(Object o) { + return false; + } + + @Override + public void clear() { + + } + + //省略部分代码 + + @Override + public Object get(int index) { + return null; } - } + @Override + public ListIterator listIterator() { + return null; + } + + @Override + public ListIterator listIterator(int index) { + return null; + } + + @Override + public List subList(int fromIndex, int toIndex) { + return null; + } + + @Override + public Object[] toArray(Object[] a) { + return new Object[0]; + } + } +} +```` ## Set接口 > Set是一种不包括重复元素的Collection。它维持它自己的内部排序,所以随机访问没有任何意义。与List一样,它同样运行null的存在但是仅有一个。由于Set接口的特殊性,所有传入Set集合中的元素都必须不同,同时要注意任何可变对象,如果在对集合中元素进行操作时,导致e1.equals(e2)==true,则必定会产生某些问题。实现了Set接口的集合有:EnumSet、HashSet、TreeSet。 @@ -261,83 +260,84 @@ java中集合大家族的成员实在是太丰富了,有常用的ArrayList、H > 3.2、HashSet > HashSet堪称查询速度最快的集合,因为其内部是以HashCode来实现的。它内部元素的顺序是由哈希码来决定的,所以它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。 - public class Set接口 { - // Set接口规定将set看成一个集合,并且使用和数组类似的增删改查方式,同时提供iterator迭代器 - // public interface Set extends Collection - // public interface Collection extends Iterable - // public interface Iterable - class MySet implements Set { - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object o) { - return false; - } - - @Override - public Iterator iterator() { - return null; - } - - @Override - public Object[] toArray() { - return new Object[0]; - } - - @Override - public boolean add(Object o) { - return false; - } - - @Override - public boolean remove(Object o) { - return false; - } - - @Override - public boolean addAll(Collection c) { - return false; - } - - @Override - public void clear() { - - } - - @Override - public boolean removeAll(Collection c) { - return false; - } - - @Override - public boolean retainAll(Collection c) { - return false; - } - - @Override - public boolean containsAll(Collection c) { - return false; - } - - @Override - public Object[] toArray(Object[] a) { - return new Object[0]; - } +```` +public class Set接口 { + // Set接口规定将set看成一个集合,并且使用和数组类似的增删改查方式,同时提供iterator迭代器 + // public interface Set extends Collection + // public interface Collection extends Iterable + // public interface Iterable + class MySet implements Set { + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object o) { + return false; + } + + @Override + public Iterator iterator() { + return null; + } + + @Override + public Object[] toArray() { + return new Object[0]; + } + + @Override + public boolean add(Object o) { + return false; + } + + @Override + public boolean remove(Object o) { + return false; } - } + @Override + public boolean addAll(Collection c) { + return false; + } + + @Override + public void clear() { + + } + + @Override + public boolean removeAll(Collection c) { + return false; + } + + @Override + public boolean retainAll(Collection c) { + return false; + } + + @Override + public boolean containsAll(Collection c) { + return false; + } + + @Override + public Object[] toArray(Object[] a) { + return new Object[0]; + } + } +} +```` ## Map接口 -> Map与List、Set接口不同,它是由一系列键值对组成的集合,提供了key到Value的映射。同时它也没有继承Collection。在Map中它保证了key与value之间的一一对应关系。也就是说一个key对应一个value,所以它不能存在相同的key值,当然value值可以相同。实现map的有:HashMap、TreeMap、HashTable、Properties、EnumMap。 +> Map与List、Set接口不同,它是由一系列键值对组成的集合,提供了key到Value的映射。同时它也没有继承Collection。在Map中它保证了key与value之间的一一对应关系。也就是说一个key对应一个value,所以它不能存在相同的key值,当然value值可以相同。实现map的有:HashMap、TreeMap、HashTable、Properties、EnumMap。 > 4.1、HashMap > 以哈希表数据结构实现,查找对象时通过哈希函数计算其位置,它是为快速查询而设计的,其内部定义了一个hash表数组(Entry[] table),元素会通过哈希转换函数将元素的哈希地址转换成数组中存放的索引,如果有冲突,则使用散列链表的形式将所有相同哈希地址的元素串起来,可能通过查看HashMap.Entry的源码它是一个单链表结构。 @@ -347,152 +347,152 @@ java中集合大家族的成员实在是太丰富了,有常用的ArrayList、H > > 4.3、HashTable > 也是以哈希表数据结构实现的,解决冲突时与HashMap也一样也是采用了散列链表的形式,不过性能比HashMap要低 +```` +public class Map接口 { + //Map接口是最上层接口,Map接口实现类必须实现put和get等哈希操作。 + //并且要提供keyset和values,以及entryset等查询结构。 + //public interface Map + class MyMap implements Map { + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean containsKey(Object key) { + return false; + } + + @Override + public boolean containsValue(Object value) { + return false; + } + + @Override + public Object get(Object key) { + return null; + } + + @Override + public Object put(Object key, Object value) { + return null; + } + + @Override + public Object remove(Object key) { + return null; + } + + @Override + public void putAll(Map m) { + + } + + @Override + public void clear() { - public class Map接口 { - //Map接口是最上层接口,Map接口实现类必须实现put和get等哈希操作。 - //并且要提供keyset和values,以及entryset等查询结构。 - //public interface Map - class MyMap implements Map { - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean containsKey(Object key) { - return false; - } - - @Override - public boolean containsValue(Object value) { - return false; - } - - @Override - public Object get(Object key) { - return null; - } - - @Override - public Object put(Object key, Object value) { - return null; - } - - @Override - public Object remove(Object key) { - return null; - } - - @Override - public void putAll(Map m) { - - } - - @Override - public void clear() { - - } - - @Override - public Set keySet() { - return null; - } - - @Override - public Collection values() { - return null; - } - - @Override - public Set entrySet() { - return null; - } } - } + @Override + public Set keySet() { + return null; + } + + @Override + public Collection values() { + return null; + } + + @Override + public Set entrySet() { + return null; + } + } +} +```` ## Queue > 队列,它主要分为两大类,一类是阻塞式队列,队列满了以后再插入元素则会抛出异常,主要包括ArrayBlockQueue、PriorityBlockingQueue、LinkedBlockingQueue。另一种队列则是双端队列,支持在头、尾两端插入和移除元素,主要包括:ArrayDeque、LinkedBlockingDeque、LinkedList。 +```` +public class Queue接口 { + //queue接口是对队列的一个实现,需要提供队列的进队出队等方法。一般使用linkedlist作为实现类 + class MyQueue implements Queue { + + @Override + public int size() { + return 0; + } - public class Queue接口 { - //queue接口是对队列的一个实现,需要提供队列的进队出队等方法。一般使用linkedlist作为实现类 - class MyQueue implements Queue { - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object o) { - return false; - } - - @Override - public Iterator iterator() { - return null; - } - - @Override - public Object[] toArray() { - return new Object[0]; - } - - @Override - public Object[] toArray(Object[] a) { - return new Object[0]; - } - - @Override - public boolean add(Object o) { - return false; - } - - @Override - public boolean remove(Object o) { - return false; - } - - //省略部分代码 - @Override - public boolean offer(Object o) { - return false; - } - - @Override - public Object remove() { - return null; - } - - @Override - public Object poll() { - return null; - } - - @Override - public Object element() { - return null; - } - - @Override - public Object peek() { - return null; - } + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object o) { + return false; + } + + @Override + public Iterator iterator() { + return null; + } + + @Override + public Object[] toArray() { + return new Object[0]; + } + + @Override + public Object[] toArray(Object[] a) { + return new Object[0]; + } + + @Override + public boolean add(Object o) { + return false; + } + + @Override + public boolean remove(Object o) { + return false; + } + + //省略部分代码 + @Override + public boolean offer(Object o) { + return false; } - } + @Override + public Object remove() { + return null; + } + + @Override + public Object poll() { + return null; + } + + @Override + public Object element() { + return null; + } + + @Override + public Object peek() { + return null; + } + } +} +```` ## 关于Java集合的小抄 这部分内容转自我偶像 江南白衣 的博客:http://calvin1978.blogcn.com/articles/collection.html diff --git "a/docs/java/basic/20\343\200\201javac\345\222\214javap.md" "b/docs/java/basic/20\343\200\201javac\345\222\214javap.md" index dde513d..de74810 100644 --- "a/docs/java/basic/20\343\200\201javac\345\222\214javap.md" +++ "b/docs/java/basic/20\343\200\201javac\345\222\214javap.md" @@ -31,15 +31,6 @@ * [微信公众号](#微信公众号) * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) ---- -title: 夯实Java基础系列20:从IDE的实现原理聊起,谈谈那些年我们用过的Java命令 -date: 2019-9-20 15:56:26 # 文章生成时间,一般不改 -categories: - - Java技术江湖 - - Java基础 -tags: - - Java命令行 ---- 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -81,119 +72,119 @@ java提供了JavaCompiler,我们可以通过它来编译java源文件为class 通过上面一个查找class,得到Class对象后,可以通过newInstance()或构造器的newInstance()得到对象。然后得到Method,最后调用方法,传入相关参数即可。 示例代码: - - public class MyIDE { - - public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - // 定义java代码,并保存到文件(Test.java) - StringBuilder sb = new StringBuilder(); - sb.append("package com.tommy.core.test.reflect;\n"); - sb.append("public class Test {\n"); - sb.append(" private String name;\n"); - sb.append(" public Test(String name){\n"); - sb.append(" this.name = name;\n"); - sb.append(" System.out.println(\"hello,my name is \" + name);\n"); - sb.append(" }\n"); - sb.append(" public String sayHello(String name) {\n"); - sb.append(" return \"hello,\" + name;\n"); - sb.append(" }\n"); - sb.append("}\n"); - - System.out.println(sb.toString()); - - String baseOutputDir = "F:\\output\\classes\\"; - String baseDir = baseOutputDir + "com\\tommy\\core\\test\\reflect\\"; - String targetJavaOutputPath = baseDir + "Test.java"; - // 保存为java文件 - FileWriter fileWriter = new FileWriter(targetJavaOutputPath); - fileWriter.write(sb.toString()); - fileWriter.flush(); - fileWriter.close(); - - // 编译为class文件 - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - StandardJavaFileManager manager = compiler.getStandardFileManager(null,null,null); - List files = new ArrayList<>(); - files.add(new File(targetJavaOutputPath)); - Iterable compilationUnits = manager.getJavaFileObjectsFromFiles(files); - - // 编译 - // 设置编译选项,配置class文件输出路径 - Iterable options = Arrays.asList("-d",baseOutputDir); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, compilationUnits); - // 执行编译任务 - task.call(); - - -​ -​ // 通过反射得到对象 -​ // Class clazz = Class.forName("com.tommy.core.test.reflect.Test"); -​ // 使用自定义的类加载器加载class -​ Class clazz = new MyClassLoader(baseOutputDir).loadClass("com.tommy.core.test.reflect.Test"); -​ // 得到构造器 -​ Constructor constructor = clazz.getConstructor(String.class); -​ // 通过构造器new一个对象 -​ Object test = constructor.newInstance("jack.tsing"); -​ // 得到sayHello方法 -​ Method method = clazz.getMethod("sayHello", String.class); -​ // 调用sayHello方法 -​ String result = (String) method.invoke(test, "jack.ma"); -​ System.out.println(result); -​ } -​ } - +```` +public class MyIDE { + + public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + // 定义java代码,并保存到文件(Test.java) + StringBuilder sb = new StringBuilder(); + sb.append("package com.tommy.core.test.reflect;\n"); + sb.append("public class Test {\n"); + sb.append(" private String name;\n"); + sb.append(" public Test(String name){\n"); + sb.append(" this.name = name;\n"); + sb.append(" System.out.println(\"hello,my name is \" + name);\n"); + sb.append(" }\n"); + sb.append(" public String sayHello(String name) {\n"); + sb.append(" return \"hello,\" + name;\n"); + sb.append(" }\n"); + sb.append("}\n"); + + System.out.println(sb.toString()); + + String baseOutputDir = "F:\\output\\classes\\"; + String baseDir = baseOutputDir + "com\\tommy\\core\\test\\reflect\\"; + String targetJavaOutputPath = baseDir + "Test.java"; + // 保存为java文件 + FileWriter fileWriter = new FileWriter(targetJavaOutputPath); + fileWriter.write(sb.toString()); + fileWriter.flush(); + fileWriter.close(); + + // 编译为class文件 + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager manager = compiler.getStandardFileManager(null,null,null); + List files = new ArrayList<>(); + files.add(new File(targetJavaOutputPath)); + Iterable compilationUnits = manager.getJavaFileObjectsFromFiles(files); + + // 编译 + // 设置编译选项,配置class文件输出路径 + Iterable options = Arrays.asList("-d",baseOutputDir); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, compilationUnits); + // 执行编译任务 + task.call(); + + + + // 通过反射得到对象 +// Class clazz = Class.forName("com.tommy.core.test.reflect.Test"); + // 使用自定义的类加载器加载class + Class clazz = new MyClassLoader(baseOutputDir).loadClass("com.tommy.core.test.reflect.Test"); + // 得到构造器 + Constructor constructor = clazz.getConstructor(String.class); + // 通过构造器new一个对象 + Object test = constructor.newInstance("jack.tsing"); + // 得到sayHello方法 + Method method = clazz.getMethod("sayHello", String.class); + // 调用sayHello方法 + String result = (String) method.invoke(test, "jack.ma"); + System.out.println(result); + } +} +```` 自定义类加载器代码: +```` + +public class MyClassLoader extends ClassLoader { + private String baseDir; + public MyClassLoader(String baseDir) { + this.baseDir = baseDir; + } + @Override + protected Class findClass(String name) throws ClassNotFoundException { + String fullClassFilePath = this.baseDir + name.replace("\\.","/") + ".class"; + File classFilePath = new File(fullClassFilePath); + if (classFilePath.exists()) { + FileInputStream fileInputStream = null; + ByteArrayOutputStream byteArrayOutputStream = null; + try { + fileInputStream = new FileInputStream(classFilePath); + byte[] data = new byte[1024]; + int len = -1; + byteArrayOutputStream = new ByteArrayOutputStream(); + while ((len = fileInputStream.read(data)) != -1) { + byteArrayOutputStream.write(data,0,len); + } -​ -​ public class MyClassLoader extends ClassLoader { -​ private String baseDir; -​ public MyClassLoader(String baseDir) { -​ this.baseDir = baseDir; -​ } -​ @Override -​ protected Class findClass(String name) throws ClassNotFoundException { -​ String fullClassFilePath = this.baseDir + name.replace("\\.","/") + ".class"; -​ File classFilePath = new File(fullClassFilePath); -​ if (classFilePath.exists()) { -​ FileInputStream fileInputStream = null; -​ ByteArrayOutputStream byteArrayOutputStream = null; -​ try { -​ fileInputStream = new FileInputStream(classFilePath); -​ byte[] data = new byte[1024]; -​ int len = -1; -​ byteArrayOutputStream = new ByteArrayOutputStream(); -​ while ((len = fileInputStream.read(data)) != -1) { -​ byteArrayOutputStream.write(data,0,len); -​ } -​ - return defineClass(name,byteArrayOutputStream.toByteArray(),0,byteArrayOutputStream.size()); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (null != fileInputStream) { - try { - fileInputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } + return defineClass(name,byteArrayOutputStream.toByteArray(),0,byteArrayOutputStream.size()); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (null != fileInputStream) { + try { + fileInputStream.close(); + } catch (IOException e) { + e.printStackTrace(); } - - if (null != byteArrayOutputStream) { - try { - byteArrayOutputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } + } + + if (null != byteArrayOutputStream) { + try { + byteArrayOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); } } } - return super.findClass(name); } - } - + return super.findClass(name); + } +} +```` ## javac命令初窥 注:以下红色标记的参数在下文中有所讲解。 @@ -316,7 +307,7 @@ javac:如果当前你要编译的java文件中引用了其它的类(比如说 > 这里展示一个web项目的.classpath Xml代码 - +```` @@ -328,7 +319,7 @@ Xml代码 …… - +```` > XML文档包含一个根元素,就是classpath,类路径,那么这里面包含了什么信息呢?子元素是classpathentry,kind属性区别了种 类信息,src源码,con你看看后面的path就知道是JRE容器的信息。lib是项目依赖的第三方类库,output是src编译后的位置。 > 既然是web项目,那么就是WEB-INF/classes目录,可能用MyEclipse的同学会说他们那里是WebRoot或者是WebContext而不是webapp,有区别么?回答:完全没有! @@ -393,27 +384,27 @@ Xml代码 举个例子, - - public class A - { - public static void main(String[] args) { - B b = new B(); - b.print(); - } +```` +public class A +{ + public static void main(String[] args) { + B b = new B(); + b.print(); } +} -​ -​ -​ public class B -​ { -​ public void print() -​ { -​ System.out.println("old"); -​ } -​ } +public class B +{ + public void print() + { + System.out.println("old"); + } +} +```` + 目录结构如下: @@ -451,19 +442,19 @@ sourcepath //此处为当前目录 这里我用来实现一下这个功能,假设项目名称为project,此目录为当前目录,且在src/com目录中有一个Main.java文件。‘ -​ -​ package com; -​ public class Main -​ { -​ public static void main(String[] args) { -​ System.out.println("Hello"); -​ } -​ } +```` + package com; + public class Main + { + public static void main(String[] args) { + System.out.println("Hello"); + } + } -​ -​ -​ javac -d bin src/com/Main.java +```` + +javac -d bin src/com/Main.java 上面的语句将Main.class生成在bin/com目录下。 @@ -472,23 +463,23 @@ sourcepath //此处为当前目录 •如果有文件为A.java(其中有类A),且在类A中使用了类B,类B在B.java中,则编译A.java时,默认会自动编译B.java,且生成B.class。 •implicit:none:不自动生成隐式引用的类文件。 •implicit:class(默认):自动生成隐式引用的类文件。 - - public class A - { - public static void main(String[] args) { - B b = new B(); - } - } - - public class B - { +```` +public class A +{ + public static void main(String[] args) { + B b = new B(); } - - 如果使用: +} +public class B +{ +} +```` +如果使用: -​ -​ javac -implicit:none A.java + + +javac -implicit:none A.java 则不会生成 B.class。 @@ -578,18 +569,18 @@ src/com/yp/test/HelloWorld.java build/ -``` +```` ├─build └─src └─com └─yp └─test HelloWorld.java -``` +```` java文件非常简单 - +```` package com.yp.test; public class HelloWorld { @@ -597,12 +588,14 @@ java文件非常简单 System.out.println("helloWorld"); } } +```` + 编译: javac src/com/yp/test/HelloWorld.java -d build -d 表示编译到 build文件夹下 -``` +```` 查看build文件夹 ├─build │ └─com @@ -615,10 +608,11 @@ javac src/com/yp/test/HelloWorld.java -d build └─yp └─test HelloWorld.java -``` +```` 运行文件 + > E:\codeplace\n_learn\java\javacmd> java com/yp/test/HelloWorld.class > 错误: 找不到或无法加载主类 build.com.yp.test.HelloWorld.class > @@ -677,27 +671,28 @@ javac src/com/yp/test/HelloWorld.java -d build 先下一个jar包 这里直接下 log4j - * main函数改成 - - import com.yp.test.entity.Cat; - import org.apache.log4j.Logger; - - public class HelloWorld { - - static Logger log = Logger.getLogger(HelloWorld.class); - - public static void main(String[] args) { - Cat c = new Cat("keyboard"); - log.info("这是log4j"); - System.out.println("hello," + c.getName()); - } - +* main函数改成 + +```` +import com.yp.test.entity.Cat; +import org.apache.log4j.Logger; + +public class HelloWorld { + + static Logger log = Logger.getLogger(HelloWorld.class); + + public static void main(String[] args) { + Cat c = new Cat("keyboard"); + log.info("这是log4j"); + System.out.println("hello," + c.getName()); } +} +```` 现的文件是这样的 -``` +```` ├─build ├─lib │ log4j-1.2.17.jar @@ -710,8 +705,7 @@ javac src/com/yp/test/HelloWorld.java -d build │ └─entity Cat.java -``` - +```` 这个时候 javac命令要接上 -cp ./lib/*.jar E:\codeplace\n_learn\java\javacmd>javac -encoding "utf8" src/com/yp/test/HelloWorld.java -sourcepath src -d build -g -cp ./lib/*.jar @@ -729,21 +723,22 @@ javac src/com/yp/test/HelloWorld.java -d build 由于没有 log4j的配置文件,所以提示上面的问题,往 build 里面加上 log4j.xml - - - - - - - - - - - - - - - +```` + + + + + + + + + + + + + + +```` 再运行 E:\codeplace\n_learn\java\javacmd>java -cp lib/log4j-1.2.17.jar;build com.yp.tes t.HelloWorld @@ -757,7 +752,7 @@ ok 一个简单的java 工程就运行完了 但是 貌似有些繁琐, 需要手动键入 java文件 以及相应的jar包 很是麻烦, so 可以用 shell 来脚本来简化相关操作 shell 文件整理如下: - +```` #!/bin/bash echo "build start" @@ -788,6 +783,7 @@ shell 文件整理如下: #运行 通过-cp指定所有的引用jar包,指定入口函数运行 java -cp $BIN_PATH$jarfile com.zuiapps.danmaku.server.Main +```` > 有一点需要注意的是, javac -d $BIN_PATH/ -cp $jarfile @$SRC_FILE_LIST_PATH > 在要编译的文件很多时候,一个个敲命令会显得很长,也不方便修改, @@ -805,7 +801,7 @@ shell 文件整理如下: 1.需要吧 编译时设置的bin目录和 所有jar包加入到 classpath 中去 -​ + ## javap 的使用 > javap是jdk自带的一个工具,可以对代码反编译,也可以查看java编译器生成的字节码。 @@ -815,37 +811,37 @@ shell 文件整理如下: > > > javap命令分解一个class文件,它根据options来决定到底输出什么。如果没有使用options,那么javap将会输出包,类里的protected和public域以及类里的所有方法。javap将会把它们输出在标准输出上。来看这个例子,先编译(javac)下面这个类。 - - import java.awt.*; - import java.applet.*; - - public class DocFooter extends Applet { - String date; - String email; - - public void init() { - resize(500,100); - date = getParameter("LAST_UPDATED"); - email = getParameter("EMAIL"); - } - } - +```` +import java.awt.*; +import java.applet.*; + +public class DocFooter extends Applet { + String date; + String email; + + public void init() { + resize(500,100); + date = getParameter("LAST_UPDATED"); + email = getParameter("EMAIL"); + } +} +```` 在命令行上键入javap DocFooter后,输出结果如下 Compiled from "DocFooter.java" - - public class DocFooter extends java.applet.Applet { - java.lang.String date; - java.lang.String email; - public DocFooter(); - public void init(); - } - +```` +public class DocFooter extends java.applet.Applet { + java.lang.String date; + java.lang.String email; + public DocFooter(); + public void init(); +} +```` 如果加入了-c,即javap -c DocFooter,那么输出结果如下 Compiled from "DocFooter.java" - +```` public class DocFooter extends java.applet.Applet { java.lang.String date; @@ -876,6 +872,7 @@ Compiled from "DocFooter.java" 29: return } +```` 上面输出的内容就是字节码。 用法摘要 diff --git "a/docs/java/basic/21\343\200\201Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" "b/docs/java/basic/21\343\200\201Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" index 14b4137..157f61c 100644 --- "a/docs/java/basic/21\343\200\201Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" +++ "b/docs/java/basic/21\343\200\201Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" @@ -19,15 +19,6 @@ * [微信公众号](#微信公众号) * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) ---- -title: 夯实Java基础系列21:Java8新特性终极指南 -date: 2019-9-21 15:56:26 # 文章生成时间,一般不改 -categories: - - Java技术江湖 - - Java基础 -tags: - - Java8 ---- 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -48,8 +39,7 @@ tags: 这是一个Java8新增特性的总结图。接下来让我们一次实践一下这些新特性吧 -![image](https://img2018.cnblogs.com/blog/493447/201906/493447-20190604133048748-2090946599.png) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230403215737.png) ## Java语言新特性 ### Lambda表达式 @@ -100,13 +90,14 @@ java.lang.Runnable与java.util.concurrent.Callable是函数式接口最典型的 在实际使用过程中,函数式接口是容易出错的:如有某个人在接口定义中增加了另一个方法,这时,这个接口就不再是函数式的了,并且编译过程也会失败。 为了克服函数式接口的这种脆弱性并且能够明确声明接口作为函数式接口的意图,Java8增加了一种特殊的注解@FunctionalInterface(Java8中所有类库的已有接口都添加了@FunctionalInterface注解)。让我们看一下这种函数式接口的定义: - +```` @FunctionalInterface public interface Functional { void method(); } +```` 需要记住的一件事是:默认方法与静态方法并不影响函数式接口的契约,可以任意使用: - +```` @FunctionalInterface public interface FunctionalDefaultMethods { void method(); @@ -114,82 +105,83 @@ public interface FunctionalDefaultMethods { default void defaultMethod() { } } +```` Lambda是Java 8最大的卖点。它具有吸引越来越多程序员到Java平台上的潜力,并且能够在纯Java语言环境中提供一种优雅的方式来支持函数式编程。更多详情可以参考官方文档。 下面看一个例子: +```` +public class lambda和函数式编程 { + @Test + public void test1() { + List names = Arrays.asList("peter", "anna", "mike", "xenia"); - public class lambda和函数式编程 { - @Test - public void test1() { - List names = Arrays.asList("peter", "anna", "mike", "xenia"); - - Collections.sort(names, new Comparator() { - @Override - public int compare(String a, String b) { - return b.compareTo(a); - } - }); - System.out.println(Arrays.toString(names.toArray())); - } - - @Test - public void test2() { - List names = Arrays.asList("peter", "anna", "mike", "xenia"); - - Collections.sort(names, (String a, String b) -> { + Collections.sort(names, new Comparator() { + @Override + public int compare(String a, String b) { return b.compareTo(a); - }); - - Collections.sort(names, (String a, String b) -> b.compareTo(a)); - - Collections.sort(names, (a, b) -> b.compareTo(a)); - System.out.println(Arrays.toString(names.toArray())); - } - + } + }); + System.out.println(Arrays.toString(names.toArray())); } - - static void add(double a,String b) { - System.out.println(a + b); - } - @Test - public void test5() { - D d = (a,b) -> add(a,b); - // interface D { - // void get(int i,String j); - // } - //这里要求,add的两个参数和get的两个参数吻合并且返回类型也要相等,否则报错 - // static void add(double a,String b) { - // System.out.println(a + b); - // } - } - - @FunctionalInterface - interface D { - void get(int i,String j); - } -接下来看看Lambda和匿名内部类的区别 + @Test + public void test2() { + List names = Arrays.asList("peter", "anna", "mike", "xenia"); -匿名内部类仍然是一个类,只是不需要我们显式指定类名,编译器会自动为该类取名。比如有如下形式的代码: + Collections.sort(names, (String a, String b) -> { + return b.compareTo(a); + }); - public class LambdaTest { - public static void main(String[] args) { - new Thread(new Runnable() { - @Override - public void run() { - System.out.println("Hello World"); - } - }).start(); - } + Collections.sort(names, (String a, String b) -> b.compareTo(a)); + + Collections.sort(names, (a, b) -> b.compareTo(a)); + System.out.println(Arrays.toString(names.toArray())); } +} + + static void add(double a,String b) { + System.out.println(a + b); + } + @Test + public void test5() { + D d = (a,b) -> add(a,b); +// interface D { +// void get(int i,String j); +// } + //这里要求,add的两个参数和get的两个参数吻合并且返回类型也要相等,否则报错 +// static void add(double a,String b) { +// System.out.println(a + b); +// } + } + + @FunctionalInterface + interface D { + void get(int i,String j); + } +```` +接下来看看Lambda和匿名内部类的区别 + +匿名内部类仍然是一个类,只是不需要我们显式指定类名,编译器会自动为该类取名。比如有如下形式的代码: +```` +public class LambdaTest { + public static void main(String[] args) { + new Thread(new Runnable() { + @Override + public void run() { + System.out.println("Hello World"); + } + }).start(); + } +} +```` 编译之后将会产生两个 class 文件: LambdaTest.class LambdaTest$1.class 使用 javap -c LambdaTest.class 进一步分析 LambdaTest.class 的字节码,部分结果如下: - +```` public static void main(java.lang.String[]); Code: 0: new #2 // class java/lang/Thread @@ -200,28 +192,28 @@ Lambda是Java 8最大的卖点。它具有吸引越来越多程序员到Java平 11: invokespecial #5 // Method java/lang/Thread."":(Ljava/lang/Runnable;)V 14: invokevirtual #6 // Method java/lang/Thread.start:()V 17: return - +```` 可以发现在 4: new #3 这一行创建了匿名内部类的对象。 而对于 Lambda表达式的实现, 接下来我们将上面的示例代码使用 Lambda 表达式实现,代码如下: - - public class LambdaTest { - public static void main(String[] args) { - new Thread(() -> System.out.println("Hello World")).start(); - } +```` +public class LambdaTest { + public static void main(String[] args) { + new Thread(() -> System.out.println("Hello World")).start(); } - +} +```` 此时编译后只会产生一个文件 LambdaTest.class,再来看看通过 javap 对该文件反编译后的结果: - - public static void main(java.lang.String[]); - Code: - 0: new #2 // class java/lang/Thread - 3: dup - 4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; - 9: invokespecial #4 // Method java/lang/Thread."":(Ljava/lang/Runnable;)V - 12: invokevirtual #5 // Method java/lang/Thread.start:()V - 15: return - +```` +public static void main(java.lang.String[]); +Code: + 0: new #2 // class java/lang/Thread + 3: dup + 4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; + 9: invokespecial #4 // Method java/lang/Thread."":(Ljava/lang/Runnable;)V + 12: invokevirtual #5 // Method java/lang/Thread.start:()V + 15: return +```` 从上面的结果我们发现 Lambda 表达式被封装成了主类的一个私有方法,并通过 invokedynamic 指令进行调用。 因此,我们可以得出结论:Lambda 表达式是通过 invokedynamic 指令实现的,并且书写 Lambda 表达式不会产生新的类。 @@ -238,50 +230,50 @@ lambda表达式是如何符合 Java 类型系统的?每个lambda对应于一 我们可以使用任意的接口作为lambda表达式,只要这个接口只包含一个抽象方法。为了保证你的接口满足需求,你需要增加@FunctionalInterface注解。编译器知道这个注解,一旦你试图给这个接口增加第二个抽象方法声明时,它将抛出一个编译器错误。 下面举几个例子 - - public class 函数式接口使用 { - @FunctionalInterface - interface A { - void say(); - default void talk() { - - } - } - @Test - public void test1() { - A a = () -> System.out.println("hello"); - a.say(); - } - - @FunctionalInterface - interface B { - void say(String i); - } - public void test2() { - //下面两个是等价的,都是通过B接口来引用一个方法,而方法可以直接使用::来作为方法引用 - B b = System.out::println; - B b1 = a -> Integer.parseInt("s");//这里的a其实换成别的也行,只是将方法传给接口作为其方法实现 - B b2 = Integer::valueOf;//i与方法传入参数的变量类型一直时,可以直接替换 - B b3 = String::valueOf; - //B b4 = Integer::parseInt;类型不符,无法使用 - - } - @FunctionalInterface - interface C { - int say(String i); - } - public void test3() { - C c = Integer::parseInt;//方法参数和接口方法的参数一样,可以替换。 - int i = c.say("1"); - //当我把C接口的int替换为void时就会报错,因为返回类型不一致。 - System.out.println(i); - //综上所述,lambda表达式提供了一种简便的表达方式,可以将一个方法传到接口中。 - //函数式接口是只提供一个抽象方法的接口,其方法由lambda表达式注入,不需要写实现类, - //也不需要写匿名内部类,可以省去很多代码,比如实现runnable接口。 - //函数式编程就是指把方法当做一个参数或引用来进行操作。除了普通方法以外,静态方法,构造方法也是可以这样操作的。 +```` +public class 函数式接口使用 { + @FunctionalInterface + interface A { + void say(); + default void talk() { + } } + @Test + public void test1() { + A a = () -> System.out.println("hello"); + a.say(); + } + + @FunctionalInterface + interface B { + void say(String i); + } + public void test2() { + //下面两个是等价的,都是通过B接口来引用一个方法,而方法可以直接使用::来作为方法引用 + B b = System.out::println; + B b1 = a -> Integer.parseInt("s");//这里的a其实换成别的也行,只是将方法传给接口作为其方法实现 + B b2 = Integer::valueOf;//i与方法传入参数的变量类型一直时,可以直接替换 + B b3 = String::valueOf; + //B b4 = Integer::parseInt;类型不符,无法使用 + } + @FunctionalInterface + interface C { + int say(String i); + } + public void test3() { + C c = Integer::parseInt;//方法参数和接口方法的参数一样,可以替换。 + int i = c.say("1"); + //当我把C接口的int替换为void时就会报错,因为返回类型不一致。 + System.out.println(i); + //综上所述,lambda表达式提供了一种简便的表达方式,可以将一个方法传到接口中。 + //函数式接口是只提供一个抽象方法的接口,其方法由lambda表达式注入,不需要写实现类, + //也不需要写匿名内部类,可以省去很多代码,比如实现runnable接口。 + //函数式编程就是指把方法当做一个参数或引用来进行操作。除了普通方法以外,静态方法,构造方法也是可以这样操作的。 + } +} +```` 请记住如果@FunctionalInterface 这个注解被遗漏,此代码依然有效。 ### 方法引用 @@ -290,7 +282,7 @@ Lambda表达式和方法引用 有了函数式接口之后,就可以使用Lambda表达式和方法引用了。其实函数式接口的表中的函数描述符就是Lambda表达式,在函数式接口中Lambda表达式相当于匿名内部类的效果。 举个简单的例子: - +```` public class TestLambda { public static void execute(Runnable runnable) { @@ -310,7 +302,7 @@ public class TestLambda { execute(() -> System.out.println("run")); } } - +```` 可以看到,相比于使用匿名内部类的方式,Lambda表达式可以使用更少的代码但是有更清晰的表述。注意,Lambda表达式也不是完全等价于匿名内部类的, 两者的不同点在于this的指向和本地变量的屏蔽上。 方法引用可以看作Lambda表达式的更简洁的一种表达形式,使用::操作符,方法引用主要有三类: @@ -351,31 +343,31 @@ public class TestLambda { ### 接口的默认方法 Java 8 使我们能够使用default 关键字给接口增加非抽象的方法实现。这个特性也被叫做 扩展方法(Extension Methods)。如下例所示: - - public class 接口的默认方法 { - class B implements A { - // void a(){}实现类方法不能重名 - } - interface A { - //可以有多个默认方法 - public default void a(){ - System.out.println("a"); - } - public default void b(){ - System.out.println("b"); - } - //报错static和default不能同时使用 - // public static default void c(){ - // System.out.println("c"); - // } +```` +public class 接口的默认方法 { + class B implements A { +// void a(){}实现类方法不能重名 + } + interface A { + //可以有多个默认方法 + public default void a(){ + System.out.println("a"); } - public void test() { - B b = new B(); - b.a(); - + public default void b(){ + System.out.println("b"); } + //报错static和default不能同时使用 +// public static default void c(){ +// System.out.println("c"); +// } } + public void test() { + B b = new B(); + b.a(); + } +} +```` 默认方法出现的原因是为了对原有接口的扩展,有了默认方法之后就不怕因改动原有的接口而对已经使用这些接口的程序造成的代码不兼容的影响。 在Java8中也对一些接口增加了一些默认方法,比如Map接口等等。一般来说,使用默认方法的场景有两个:可选方法和行为的多继承。 默认方法的使用相对来说比较简单,唯一要注意的点是如何处理默认方法的冲突。关于如何处理默认方法的冲突可以参考以下三条规则: @@ -385,7 +377,7 @@ Java 8 使我们能够使用default 关键字给接口增加非抽象的方法 如果无法依据第一条规则进行判断,那么子接口的优先级更高:函数签名相同时,优先选择拥有最具体实现的默认方法的接口。即如果B继承了A,那么B就比A更具体。 最后,如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法,显式地选择使用哪一个默认方法的实现。那么如何显式地指定呢: - +```` public class C implements B, A { public void hello() { @@ -393,43 +385,46 @@ Java 8 使我们能够使用default 关键字给接口增加非抽象的方法 } } +```` 使用X.super.m(..)显式地调用希望调用的方法。 Java 8用默认方法与静态方法这两个新概念来扩展接口的声明。默认方法使接口有点像Traits(Scala中特征(trait)类似于Java中的Interface,但它可以包含实现代码,也就是目前Java8新增的功能),但与传统的接口又有些不一样,它允许在已有的接口中添加新方法,而同时又保持了与旧版本代码的兼容性。 默认方法与抽象方法不同之处在于抽象方法必须要求实现,但是默认方法则没有这个要求。相反,每个接口都必须提供一个所谓的默认实现,这样所有的接口实现者将会默认继承它(如果有必要的话,可以覆盖这个默认实现)。让我们看看下面的例子: - - private interface Defaulable { - // Interfaces now allow default methods, the implementer may or - // may not implement (override) them. - default String notRequired() { - return "Default implementation"; - } - } - - private static class DefaultableImpl implements Defaulable { - } +```` +private interface Defaulable { + // Interfaces now allow default methods, the implementer may or + // may not implement (override) them. + default String notRequired() { + return "Default implementation"; + } +} - private static class OverridableImpl implements Defaulable { - @Override - public String notRequired() { - return "Overridden implementation"; - } +private static class DefaultableImpl implements Defaulable { +} + +private static class OverridableImpl implements Defaulable { + @Override + public String notRequired() { + return "Overridden implementation"; } +} +```` Defaulable接口用关键字default声明了一个默认方法notRequired(),Defaulable接口的实现者之一DefaultableImpl实现了这个接口,并且让默认方法保持原样。Defaulable接口的另一个实现者OverridableImpl用自己的方法覆盖了默认方法。 Java 8带来的另一个有趣的特性是接口可以声明(并且可以提供实现)静态方法。例如: - +```` private interface DefaulableFactory { // Interfaces now allow static methods static Defaulable create( Supplier< Defaulable > supplier ) { return supplier.get(); } } +```` 下面的一小段代码片段把上面的默认方法与静态方法黏合到一起。 - +```` public static void main( String[] args ) { Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new ); System.out.println( defaulable.notRequired() ); @@ -437,6 +432,7 @@ Java 8带来的另一个有趣的特性是接口可以声明(并且可以提 defaulable = DefaulableFactory.create( OverridableImpl::new ); System.out.println( defaulable.notRequired() ); } +```` 这个程序的控制台输出如下: Default implementation @@ -450,58 +446,57 @@ Overridden implementation 自从Java 5引入了注解机制,这一特性就变得非常流行并且广为使用。然而,使用注解的一个限制是相同的注解在同一位置只能声明一次,不能声明多次。Java 8打破了这条规则,引入了重复注解机制,这样相同的注解可以在同一地方声明多次。 重复注解机制本身必须用@Repeatable注解。事实上,这并不是语言层面上的改变,更多的是编译器的技巧,底层的原理保持不变。让我们看一个快速入门的例子: - - package com.javacodegeeks.java8.repeatable.annotations; +```` +package com.javacodegeeks.java8.repeatable.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class RepeatingAnnotations { + @Target( ElementType.TYPE ) + @Retention( RetentionPolicy.RUNTIME ) + public @interface Filters { + Filter[] value(); + } - import java.lang.annotation.ElementType; - import java.lang.annotation.Repeatable; - import java.lang.annotation.Retention; - import java.lang.annotation.RetentionPolicy; - import java.lang.annotation.Target; + @Target( ElementType.TYPE ) + @Retention( RetentionPolicy.RUNTIME ) + @Repeatable( Filters.class ) + public @interface Filter { + String value(); + }; - public class RepeatingAnnotations { - @Target( ElementType.TYPE ) - @Retention( RetentionPolicy.RUNTIME ) - public @interface Filters { - Filter[] value(); - } - - @Target( ElementType.TYPE ) - @Retention( RetentionPolicy.RUNTIME ) - @Repeatable( Filters.class ) - public @interface Filter { - String value(); - }; - - @Filter( "filter1" ) - @Filter( "filter2" ) - public interface Filterable { - } - - public static void main(String[] args) { - for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) { - System.out.println( filter.value() ); - } + @Filter( "filter1" ) + @Filter( "filter2" ) + public interface Filterable { + } + + public static void main(String[] args) { + for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) { + System.out.println( filter.value() ); } } +} +```` 正如我们看到的,这里有个使用@Repeatable( Filters.class )注解的注解类Filter,Filters仅仅是Filter注解的数组,但Java编译器并不想让程序员意识到Filters的存在。这样,接口Filterable就拥有了两次Filter(并没有提到Filter)注解。 同时,反射相关的API提供了新的函数getAnnotationsByType()来返回重复注解的类型(请注意Filterable.class.getAnnotation( Filters.class )经编译器处理后将会返回Filters的实例)。 程序输出结果如下: -filter1 -filter2 + filter1 + filter2 更多详情请参考官方文档 - - ## Java编译器的新特性 ### 方法参数名字可以反射获取 很长一段时间里,Java程序员一直在发明不同的方式使得方法参数的名字能保留在Java字节码中,并且能够在运行时获取它们(比如,Paranamer类库)。最终,在Java 8中把这个强烈要求的功能添加到语言层面(通过反射API与Parameter.getName()方法)与字节码文件(通过新版的javac的–parameters选项)中。 - +```` package com.javacodegeeks.java8.parameter.names; import java.lang.reflect.Method; @@ -515,6 +510,7 @@ public class ParameterNames { } } } +```` 如果不使用–parameters参数来编译这个类,然后运行这个类,会得到下面的输出: Parameter: arg0 @@ -531,7 +527,7 @@ Java 8 通过增加大量新类,扩展已有类的功能的方式来改善对 Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。更多详情请参考官方文档。 我们下面用两个小例子来演示如何使用Optional类:一个允许为空值,一个不允许为空值。 - +```` public class 空指针Optional { public static void main(String[] args) { @@ -554,7 +550,7 @@ Optional实际上是个容器:它可以保存类型T的值,或者仅仅保 //输出Optional.empty。 } } - +```` 如果Optional类的实例为非空值的话,isPresent()返回true,否从返回false。为了防止Optional为空值,orElseGet()方法通过回调函数来产生一个默认值。map()函数对当前Optional的值进行转化,然后返回一个新的Optional实例。orElse()方法和orElseGet()方法类似,但是orElse接受一个默认值而不是一个回调函数。下面是这个程序的输出: Full Name is set? false @@ -581,14 +577,15 @@ Hey Tom! Stream API极大简化了集合框架的处理(但它的处理的范围不仅仅限于集合框架的处理,这点后面我们会看到)。让我们以一个简单的Task类为例进行介绍: Task类有一个分数的概念(或者说是伪复杂度),其次是还有一个值可以为OPEN或CLOSED的状态.让我们引入一个Task的小集合作为演示例子: - +```` final Collection< Task > tasks = Arrays.asList( new Task( Status.OPEN, 5 ), new Task( Status.OPEN, 13 ), new Task( Status.CLOSED, 8 ) ); +```` 我们下面要讨论的第一个问题是所有状态为OPEN的任务一共有多少分数?在Java 8以前,一般的解决方式用foreach循环,但是在Java 8里面我们可以使用stream:一串支持连续、并行聚集操作的元素。 - +```` // Calculate total points of all active tasks using sum() final long totalPointsOfOpenTasks = tasks .stream() @@ -597,9 +594,10 @@ Task类有一个分数的概念(或者说是伪复杂度),其次是还有 .sum(); System.out.println( "Total points: " + totalPointsOfOpenTasks ); +```` 程序在控制台上的输出如下: -Total points: 18 + Total points: 18 这里有几个注意事项。 @@ -617,7 +615,7 @@ Total points: 18 stream另一个有价值的地方是能够原生支持并行处理。让我们来看看这个算task分数和的例子。 stream另一个有价值的地方是能够原生支持并行处理。让我们来看看这个算task分数和的例子。 - +```` // Calculate total points of all tasks final double totalPoints = tasks .stream() @@ -626,23 +624,28 @@ stream另一个有价值的地方是能够原生支持并行处理。让我们 .reduce( 0, Integer::sum ); System.out.println( "Total points (all tasks): " + totalPoints ); +```` 这个例子和第一个例子很相似,但这个例子的不同之处在于这个程序是并行运行的,其次使用reduce方法来算最终的结果。 下面是这个例子在控制台的输出: Total points (all tasks): 26.0 经常会有这个一个需求:我们需要按照某种准则来对集合中的元素进行分组。Stream也可以处理这样的需求,下面是一个例子: - +```` // Group tasks by their status final Map< Status, List< Task > > map = tasks .stream() .collect( Collectors.groupingBy( Task::getStatus ) ); System.out.println( map ); +```` + 这个例子的控制台输出如下: -{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]} + {CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]} + 让我们来计算整个集合中每个task分数(或权重)的平均值来结束task的例子。 +```` // Calculate the weight of each tasks (as percent of total points) final Collection< String > result = tasks .stream() // Stream< String > @@ -655,15 +658,19 @@ Total points (all tasks): 26.0 .collect( Collectors.toList() ); // List< String > System.out.println( result ); +```` + 下面是这个例子的控制台输出: [19%, 50%, 30%] 最后,就像前面提到的,Stream API不仅仅处理Java集合框架。像从文本文件中逐行读取数据这样典型的I/O操作也很适合用Stream API来处理。下面用一个例子来应证这一点。 - +```` final Path path = new File( filename ).toPath(); try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) { lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println ); } +```` + 对一个stream对象调用onClose方法会返回一个在原有功能基础上新增了关闭功能的stream对象,当对stream对象调用close()方法时,与关闭相关的处理器就会执行。 Stream API、Lambda表达式与方法引用在接口默认方法与静态方法的配合下是Java 8对现代软件开发范式的回应。更多详情请参考官方文档。 @@ -674,19 +681,19 @@ Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时 这种情况直接导致了Joda-Time——一个可替换标准日期/时间处理且功能非常强大的Java API的诞生。Java 8新的Date-Time API (JSR 310)在很大程度上受到Joda-Time的影响,并且吸取了其精髓。新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。在设计新版API时,十分注重与旧版API的兼容性:不允许有任何的改变(从java.util.Calendar中得到的深刻教训)。如果需要修改,会返回这个类的一个新实例。 让我们用例子来看一下新版API主要类的使用方法。第一个是Clock类,它通过指定一个时区,然后就可以获取到当前的时刻,日期与时间。Clock可以替换System.currentTimeMillis()与TimeZone.getDefault()。 - +```` // Get the system clock as UTC offset final Clock clock = Clock.systemUTC(); System.out.println( clock.instant() ); System.out.println( clock.millis() ); - +```` 下面是程序在控制台上的输出: 2014-04-12T15:19:29.282Z 1397315969360 我们需要关注的其他类是LocaleDate与LocalTime。LocaleDate只持有ISO-8601格式且无时区信息的日期部分。相应的,LocaleTime只持有ISO-8601格式且无时区信息的时间部分。LocaleDate与LocalTime都可以从Clock中得到。 - +```` // Get the local date and local time final LocalDate date = LocalDate.now(); final LocalDate dateFromClock = LocalDate.now( clock ); @@ -700,7 +707,7 @@ Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时 System.out.println( time ); System.out.println( timeFromClock ); - +```` 下面是程序在控制台上的输出: 2014-04-12 @@ -715,7 +722,7 @@ Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时 2014-04-12T08:47:01.017-07:00[America/Los_Angeles] 最后,让我们看一下Duration类:在秒与纳秒级别上的一段时间。Duration使计算两个日期间的不同变的十分简单。下面让我们看一个这方面的例子。 - +```` // Get duration between two dates final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 ); final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 ); @@ -723,7 +730,7 @@ Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时 final Duration duration = Duration.between( from, to ); System.out.println( "Duration in days: " + duration.toDays() ); System.out.println( "Duration in hours: " + duration.toHours() ); - +```` 上面的例子计算了两个日期2014年4月16号与2014年4月16号之间的过程。下面是程序在控制台上的输出: Duration in days: 365 @@ -733,7 +740,7 @@ Duration in hours: 8783 ### 并行(parallel)数组 Java 8增加了大量的新方法来对数组进行并行处理。可以说,最重要的是parallelSort()方法,因为它可以在多核机器上极大提高数组排序的速度。下面的例子展示了新方法(parallelXxx)的使用。 - +```` package com.javacodegeeks.java8.parallel.arrays; import java.util.Arrays; @@ -755,7 +762,7 @@ Java 8增加了大量的新方法来对数组进行并行处理。可以说, System.out.println(); } } - +```` 上面的代码片段使用了parallelSetAll()方法来对一个有20000个元素的数组进行随机赋值。然后,调用parallelSort方法。这个程序首先打印出前10个元素的值,之后对整个数组排序。这个程序在控制台上的输出如下(请注意数组元素是随机生产的): Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378 @@ -766,7 +773,7 @@ Sorted: 39 220 263 268 325 607 655 678 723 793 在Java8之前,我们会使用JDK提供的Future接口来进行一些异步的操作,其实CompletableFuture也是实现了Future接口, 并且基于ForkJoinPool来执行任务,因此本质上来讲,CompletableFuture只是对原有API的封装, 而使用CompletableFuture与原来的Future的不同之处在于可以将两个Future组合起来,或者如果两个Future是有依赖关系的,可以等第一个执行完毕后再实行第二个等特性。 **先来看看基本的使用方式:** - +```` public Future getPriceAsync(final String product) { final CompletableFuture futurePrice = new CompletableFuture<>(); new Thread(() -> { @@ -775,13 +782,14 @@ Sorted: 39 220 263 268 325 607 655 678 723 793 }).start(); return futurePrice; } +```` 得到Future之后就可以使用get方法来获取结果,CompletableFuture提供了一些工厂方法来简化这些API,并且使用函数式编程的方式来使用这些API,例如: Fufure price = CompletableFuture.supplyAsync(() -> calculatePrice(product)); 代码是不是一下子简洁了许多呢。之前说了,CompletableFuture可以组合多个Future,不管是Future之间有依赖的,还是没有依赖的。 **如果第二个请求依赖于第一个请求的结果,那么可以使用thenCompose方法来组合两个Future** - +```` public List findPriceAsync(String product) { List> priceFutures = tasks.stream() .map(task -> CompletableFuture.supplyAsync(() -> task.getPrice(product),executor)) @@ -791,24 +799,26 @@ Fufure price = CompletableFuture.supplyAsync(() -> calculatePrice(produc return priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList()); } +```` 上面这段代码使用了thenCompose来组合两个CompletableFuture。supplyAsync方法第二个参数接受一个自定义的Executor。 首先使用CompletableFuture执行一个任务,调用getPrice方法,得到一个Future,之后使用thenApply方法,将Future的结果应用parse方法, 之后再使用执行完parse之后的结果作为参数再执行一个applyCount方法,然后收集成一个CompletableFuture的List, 最后再使用一个流,调用CompletableFuture的join方法,这是为了等待所有的异步任务执行完毕,获得最后的结果。 注意,这里必须使用两个流,如果在一个流里调用join方法,那么由于Stream的延迟特性,所有的操作还是会串行的执行,并不是异步的。 **再来看一个两个Future之间没有依赖关系的例子:** - +```` Future futurePriceInUsd = CompletableFuture.supplyAsync(() -> shop.getPrice(“price1”)) .thenCombine(CompletableFuture.supplyAsync(() -> shop.getPrice(“price2”)), (s1, s2) -> s1 + s2); +```` 这里有两个异步的任务,使用thenCombine方法来组合两个Future,thenCombine方法的第二个参数就是用来合并两个Future方法返回值的操作函数。 有时候,我们并不需要等待所有的异步任务结束,只需要其中的一个完成就可以了,CompletableFuture也提供了这样的方法: - +```` //假设getStream方法返回一个Stream> CompletableFuture[] futures = getStream(“listen”).map(f -> f.thenAccept(System.out::println)).toArray(CompletableFuture[]::new); //等待其中的一个执行完毕 CompletableFuture.anyOf(futures).join(); 使用anyOf方法来响应CompletableFuture的completion事件。 - +```` ## Java虚拟机(JVM)的新特性 PermGen空间被移除了,取而代之的是Metaspace(JEP 122)。JVM选项-XX:PermSize与-XX:MaxPermSize分别被-XX:MetaSpaceSize与-XX:MaxMetaspaceSize所代替。 diff --git "a/docs/java/basic/22\343\200\201\345\272\217\345\210\227\345\214\226\345\222\214\345\217\215\345\272\217\345\210\227\345\214\226.md" "b/docs/java/basic/22\343\200\201\345\272\217\345\210\227\345\214\226\345\222\214\345\217\215\345\272\217\345\210\227\345\214\226.md" index 6de5ffb..c668e69 100644 --- "a/docs/java/basic/22\343\200\201\345\272\217\345\210\227\345\214\226\345\222\214\345\217\215\345\272\217\345\210\227\345\214\226.md" +++ "b/docs/java/basic/22\343\200\201\345\272\217\345\210\227\345\214\226\345\222\214\345\217\215\345\272\217\345\210\227\345\214\226.md" @@ -12,15 +12,6 @@ * [微信公众号](#微信公众号) * [Java技术江湖](#java技术江湖) * [个人公众号:黄小斜](#个人公众号:黄小斜) ---- -title: 夯实Java基础系列22:一文读懂Java序列化和反序列化 -date: 2019-9-22 15:56:26 # 文章生成时间,一般不改 -categories: - - Java技术江湖 - - Java基础 -tags: - - Java序列化 ---- 本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -37,7 +28,6 @@ tags: - 本文参考 http://www.importnew.com/17964.html和 https://www.ibm.com/developerworks/cn/java/j-lo-serial/ @@ -86,96 +76,93 @@ Java为了方便开发人员将Java对象进行序列化及反序列化提供了 如果要序列化的类有父类,要想同时将在父类中定义过的变量持久化下来,那么父类也应该集成java.io.Serializable接口。 下面是一个实现了java.io.Serializable接口的类 +```` +public class 序列化和反序列化 { + public static void main(String[] args) { - public class 序列化和反序列化 { - - -​ -​ public static void main(String[] args) { -​ - } - //注意,内部类不能进行序列化,因为它依赖于外部类 - @Test - public void test() throws IOException { - A a = new A(); - a.i = 1; - a.s = "a"; - FileOutputStream fileOutputStream = null; - FileInputStream fileInputStream = null; - try { - //将obj写入文件 - fileOutputStream = new FileOutputStream("temp"); - ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); - objectOutputStream.writeObject(a); - fileOutputStream.close(); - //通过文件读取obj - fileInputStream = new FileInputStream("temp"); - ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); - A a2 = (A) objectInputStream.readObject(); - fileInputStream.close(); - System.out.println(a2.i); - System.out.println(a2.s); - //打印结果和序列化之前相同 - } catch (IOException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - } } - - class A implements Serializable { - - int i; - String s; + //注意,内部类不能进行序列化,因为它依赖于外部类 + @Test + public void test() throws IOException { + A a = new A(); + a.i = 1; + a.s = "a"; + FileOutputStream fileOutputStream = null; + FileInputStream fileInputStream = null; + try { + //将obj写入文件 + fileOutputStream = new FileOutputStream("temp"); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); + objectOutputStream.writeObject(a); + fileOutputStream.close(); + //通过文件读取obj + fileInputStream = new FileInputStream("temp"); + ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); + A a2 = (A) objectInputStream.readObject(); + fileInputStream.close(); + System.out.println(a2.i); + System.out.println(a2.s); + //打印结果和序列化之前相同 + } catch (IOException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } } +} + +class A implements Serializable { + int i; + String s; +} +```` **Externalizable接口** 除了Serializable 之外,java中还提供了另一个序列化接口Externalizable 为了了解Externalizable接口和Serializable接口的区别,先来看代码,我们把上面的代码改成使用Externalizable的形式。 +```` +class B implements Externalizable { + //必须要有公开无参构造函数。否则报错 + public B() { - class B implements Externalizable { - //必须要有公开无参构造函数。否则报错 - public B() { - - } - int i; - String s; - @Override - public void writeExternal(ObjectOutput out) throws IOException { - - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - - } } - - @Test - public void test2() throws IOException, ClassNotFoundException { - B b = new B(); - b.i = 1; - b.s = "a"; - //将obj写入文件 - FileOutputStream fileOutputStream = new FileOutputStream("temp"); - ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); - objectOutputStream.writeObject(b); - fileOutputStream.close(); - //通过文件读取obj - FileInputStream fileInputStream = new FileInputStream("temp"); - ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); - B b2 = (B) objectInputStream.readObject(); - fileInputStream.close(); - System.out.println(b2.i); - System.out.println(b2.s); - //打印结果为0和null,即初始值,没有被赋值 - //0 - //null - } + int i; + String s; + @Override + public void writeExternal(ObjectOutput out) throws IOException { + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + + } +} + +@Test + public void test2() throws IOException, ClassNotFoundException { + B b = new B(); + b.i = 1; + b.s = "a"; + //将obj写入文件 + FileOutputStream fileOutputStream = new FileOutputStream("temp"); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); + objectOutputStream.writeObject(b); + fileOutputStream.close(); + //通过文件读取obj + FileInputStream fileInputStream = new FileInputStream("temp"); + ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); + B b2 = (B) objectInputStream.readObject(); + fileInputStream.close(); + System.out.println(b2.i); + System.out.println(b2.s); + //打印结果为0和null,即初始值,没有被赋值 + //0 + //null + } +```` 通过上面的实例可以发现,对B类进行序列化及反序列化之后得到的对象的所有属性的值都变成了默认值。也就是说,之前的那个对象的状态并没有被持久化下来。这就是Externalizable接口和Serializable接口的区别: Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。 @@ -184,54 +171,56 @@ Externalizable继承了Serializable,该接口中定义了两个抽象方法: > 还有一点值得注意:在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器。 - class C implements Externalizable { - int i; - int j; - String s; - public C() { - - } - //实现下面两个方法可以选择序列化中需要被复制的成员。 - //并且,写入顺序和读取顺序要一致,否则报错。 - //可以写入多个同类型变量,顺序保持一致即可。 - @Override - public void writeExternal(ObjectOutput out) throws IOException { - out.writeInt(i); - out.writeInt(j); - out.writeObject(s); - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - i = in.readInt(); - j = in.readInt(); - s = (String) in.readObject(); - } +```` +class C implements Externalizable { + int i; + int j; + String s; + public C() { + } - - @Test - public void test3() throws IOException, ClassNotFoundException { - C c = new C(); - c.i = 1; - c.j = 2; - c.s = "a"; - //将obj写入文件 - FileOutputStream fileOutputStream = new FileOutputStream("temp"); - ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); - objectOutputStream.writeObject(c); - fileOutputStream.close(); - //通过文件读取obj - FileInputStream fileInputStream = new FileInputStream("temp"); - ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); - C c2 = (C) objectInputStream.readObject(); - fileInputStream.close(); - System.out.println(c2.i); - System.out.println(c2.j); - System.out.println(c2.s); - //打印结果为0和null,即初始值,没有被赋值 - //0 - //null - } + //实现下面两个方法可以选择序列化中需要被复制的成员。 + //并且,写入顺序和读取顺序要一致,否则报错。 + //可以写入多个同类型变量,顺序保持一致即可。 + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(i); + out.writeInt(j); + out.writeObject(s); + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + i = in.readInt(); + j = in.readInt(); + s = (String) in.readObject(); + } +} + +@Test +public void test3() throws IOException, ClassNotFoundException { + C c = new C(); + c.i = 1; + c.j = 2; + c.s = "a"; + //将obj写入文件 + FileOutputStream fileOutputStream = new FileOutputStream("temp"); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); + objectOutputStream.writeObject(c); + fileOutputStream.close(); + //通过文件读取obj + FileInputStream fileInputStream = new FileInputStream("temp"); + ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); + C c2 = (C) objectInputStream.readObject(); + fileInputStream.close(); + System.out.println(c2.i); + System.out.println(c2.j); + System.out.println(c2.s); + //打印结果为0和null,即初始值,没有被赋值 + //0 + //null +} +```` ## 序列化ID @@ -241,87 +230,89 @@ Externalizable继承了Serializable,该接口中定义了两个抽象方法: 问题:C 对象的全类路径假设为 com.inout.Test,在 A 和 B 端都有这么一个类文件,功能代码完全一致。也都实现了 Serializable 接口,但是反序列化时总是提示不成功。 解决:虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。清单 1 中,虽然两个类的功能代码完全一致,但是序列化 ID 不同,他们无法相互序列化和反序列化。 - - package com.inout; - - import java.io.Serializable; - - public class A implements Serializable { - - private static final long serialVersionUID = 1L; - - private String name; - - public String getName() - { - return name; - } - - public void setName(String name) - { - this.name = name; - } +```` +package com.inout; + +import java.io.Serializable; + +public class A implements Serializable { + + private static final long serialVersionUID = 1L; + + private String name; + + public String getName() + { + return name; } - - package com.inout; - - import java.io.Serializable; - - public class A implements Serializable { - - private static final long serialVersionUID = 2L; - - private String name; - - public String getName() - { - return name; - } - - public void setName(String name) - { - this.name = name; - } - } + + public void setName(String name) + { + this.name = name; + } +} + +package com.inout; + +import java.io.Serializable; + +public class A implements Serializable { + + private static final long serialVersionUID = 2L; + + private String name; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } +} +```` ### 静态变量不参与序列化 清单 2 中的 main 方法,将对象序列化后,修改静态变量的数值,再将序列化对象读取出来,然后通过读取出来的对象获得静态变量的数值并打印出来。依照清单 2,这个 System.out.println(t.staticVar) 语句输出的是 10 还是 5 呢? - - public class Test implements Serializable { - - private static final long serialVersionUID = 1L; - - public static int staticVar = 5; - - public static void main(String[] args) { - try { - //初始时staticVar为5 - ObjectOutputStream out = new ObjectOutputStream( - new FileOutputStream("result.obj")); - out.writeObject(new Test()); - out.close(); - - //序列化后修改为10 - Test.staticVar = 10; - - ObjectInputStream oin = new ObjectInputStream(new FileInputStream( - "result.obj")); - Test t = (Test) oin.readObject(); - oin.close(); - - //再读取,通过t.staticVar打印新的值 - System.out.println(t.staticVar); - - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } +```` +public class Test implements Serializable { + + private static final long serialVersionUID = 1L; + + public static int staticVar = 5; + + public static void main(String[] args) { + try { + //初始时staticVar为5 + ObjectOutputStream out = new ObjectOutputStream( + new FileOutputStream("result.obj")); + out.writeObject(new Test()); + out.close(); + + //序列化后修改为10 + Test.staticVar = 10; + + ObjectInputStream oin = new ObjectInputStream(new FileInputStream( + "result.obj")); + Test t = (Test) oin.readObject(); + oin.close(); + + //再读取,通过t.staticVar打印新的值 + System.out.println(t.staticVar); + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); } } +} +```` 最后的输出是 10,对于无法理解的读者认为,打印的 staticVar 是从读取的对象里获得的,应该是保存时的状态才对。之所以打印 10 的原因在于序列化时,并不保存静态变量,这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。 @@ -334,6 +325,7 @@ ArrayList的序列化 带着这个问题,我们来看java.util.ArrayList的源码 +```` public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable { @@ -341,26 +333,27 @@ ArrayList的序列化 transient Object[] elementData; // non-private to simplify nested class access private int size; } +```` 笔者省略了其他成员变量,从上面的代码中可以知道ArrayList实现了java.io.Serializable接口,那么我们就可以对它进行序列化及反序列化。 因为elementData是transient的(1.8好像改掉了这一点),所以我们认为这个成员变量不会被序列化而保留下来。我们写一个Demo,验证一下我们的想法: - - public class ArrayList的序列化 { - public static void main(String[] args) throws IOException, ClassNotFoundException { - ArrayList list = new ArrayList(); - list.add("a"); - list.add("b"); - ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("arr")); - objectOutputStream.writeObject(list); - objectOutputStream.close(); - ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("arr")); - ArrayList list1 = (ArrayList) objectInputStream.readObject(); - objectInputStream.close(); - System.out.println(Arrays.toString(list.toArray())); - //序列化成功,里面的元素保持不变。 - } - +```` +public class ArrayList的序列化 { + public static void main(String[] args) throws IOException, ClassNotFoundException { + ArrayList list = new ArrayList(); + list.add("a"); + list.add("b"); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("arr")); + objectOutputStream.writeObject(list); + objectOutputStream.close(); + ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("arr")); + ArrayList list1 = (ArrayList) objectInputStream.readObject(); + objectInputStream.close(); + System.out.println(Arrays.toString(list.toArray())); + //序列化成功,里面的元素保持不变。 + } +```` 了解ArrayList的人都知道,ArrayList底层是通过数组实现的。那么数组elementData其实就是用来保存列表中的元素的。通过该属性的声明方式我们知道,他是无法通过序列化持久化下来的。那么为什么code 4的结果却通过序列化和反序列化把List中的元素保留下来了呢? **writeObject和readObject方法** @@ -376,50 +369,48 @@ ArrayList的序列化 > 用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。 来看一下这两个方法的具体实现: +```` +private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + elementData = EMPTY_ELEMENTDATA; - private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { - elementData = EMPTY_ELEMENTDATA; - - // Read in size, and any hidden stuff - s.defaultReadObject(); - - // Read in capacity - s.readInt(); // ignored - - if (size > 0) { - // be like clone(), allocate array based upon size not capacity - ensureCapacityInternal(size); - - Object[] a = elementData; - // Read in all elements in the proper order. - for (int i=0; i