diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index e85bf95..0000000 --- a/.gitattributes +++ /dev/null @@ -1,6 +0,0 @@ -* text=auto -*.js linguist-language=java -*.css linguist-language=java -*.html linguist-language=java - - diff --git a/.gitignore b/.gitignore index 517ec8a..e233607 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ out gen /target/ + + diff --git a/ReadMe.md b/ReadMe.md index b9348a8..84572da 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,379 +1,492 @@ -本仓库为�Java工程师技术指�】力求打造最完整最实用的Java工程师学习指�� +
+ + + +
+ +

+

+ + + + + + +
+

+ +力求打造最完整最实用的Java工程师学习指�� 这些文章和总结都是我近几年学习Java总结和整�出�的,�常实用,对于学习Java�端的朋��说应该是最全�最完整的技术仓库。 我��这些内容进行�习,拿到了BAT等大厂的offer,这个仓库也已�帮助了很多的Java学习者,如果对你有用,希望能给个star支�我,谢谢� 为了更好地讲清楚�个知识模�,我们也�考了很多网上的优质�文,力求��掉�一个知识点,所有�考�文都将声明转载��,如有侵�,请�系我。 -点击关注[微信公众�](#微信公众�)�时获�笔主最新更新文章,并��费领�Java工程师必备学习资�,以�我原创的电�书《�鸟程�员�长之路:从技术�白到阿里巴巴Java工程师》 +点击关注[微信公众�](#微信公众�)�时获�笔主最新更新文章,并��费领�Java工程师必备学习资�

- +

-

- 阅读 - 微信公众� -

- -推�使用 https://how2playlife.com/ 在线阅读,在线阅读内容本仓库�步一致。这�方�阅读的优势在于:左侧边�有目录,阅读体验更好。 - - -## 目录 - -- [Java基础](#Java基础) - - [基础知识](#基础知识) - - [容器](#容器) - - [设计模�](#设计模�) -- [JavaWeb](#JavaWeb) - - [Srping](#Spring) - - [SpringMVC](#SpringMVC) - - [SpringBoot](#SpringBoot) -- [Java进阶](#Java进阶) - - [并�](#并�) - - [JVM](#JVM) - - [Java网络编程](#Java网络编程) -- [计算机基础](#计算机基础) - - [计算机网络](#计算机网络) - - [�作系统](#�作系统) - - [Linux相关](#linux相关) - - [数�结构与算法](#数�结构与算法) - - [数�结构](#数�结构) - - [算法](#算法) -- [数�库](#数�库) - - [MySQL](#MySQL) -- [缓存](#缓存) - - [Redis](#Redis) -- [消�队列](#消�队列) - - [Kafka](#Kafka) -- [大�端](#大�端) -- [分布�](#分布�) - - [�论](#�论) - - [实战](#实战) -- [�试指�](#�试指�) - - [校招指�](#校招指�) - - [��](#��) -- [工具](#工具) - - [Git](#git) -- [资料](#资料) - - [书�](#书�) -- [待办](#待办) -- [说明](#说明) -- [微信公众�](#微信公众�) - -## Java基础 - -### 基础知识 - -* [��对象基础](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/19�Java集�框架梳�.md) -* [Java8新特性终�指�](docs/java/basic/20�javac和javap.md) -* [Java类和包](docs/java/basic/21�Java8新特性终�指�.md) -* [�列化和��列化](docs/java/basic/22��列化和��列化.md) -* [继承��装�多�的实现原�](docs/java/basic/23�继承��装�多�的实现原�.md) - -### 容器 - -* [Java集�类总结](docs/java/collection/Java集�类总结.md) -* [Java集�详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原�](docs/java/collection/Java集�详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原�.md) -* [Java集�详解2:Queue和LinkedList](docs/java/collection/Java集�详解2:Queue和LinkedList.md) -* [Java集�详解3:Iterator,fail-fast机制与比较器](docs/java/collection/Java集�详解3:Iterator,fail-fast机制与比较器.md) -* [Java集�详解4:HashMap和HashTable](docs/java/collection/Java集�详解4:HashMap和HashTable.md) -* [Java集�详解5:深入�解LinkedHashMap和LRU缓存](docs/java/collection/Java集�详解5:深入�解LinkedHashMap和LRU缓存.md) -* [Java集�详解6:TreeMap和红黑树](docs/java/collection/Java集�详解6:TreeMap和红黑树.md) -* [Java集�详解7:HashSet,TreeSet与LinkedHashSet](docs/java/collection/Java集�详解7:HashSet,TreeSet与LinkedHashSet.md) -* [Java集�详解8:Java集�类细节精讲](docs/java/collection/Java集�详解8:Java集�类细节精讲.md) - -### 设计模� - -* [设计模�学习总结](docs/java/design-parttern/设计模�学习总结.md) -* [�探Java设计模�1:创建型模�(工厂,�例等).md](docs/java/design-parttern/�探Java设计模�1:创建型模�(工厂,�例等).md) -* [�探Java设计模�2:结构型模�(代�模�,适�器模�等).md](docs/java/design-parttern/�探Java设计模�2:结构型模�(代�模�,适�器模�等).md) -* [�探Java设计模�3:行为型模�(策略,观察者等).md](docs/java/design-parttern/�探Java设计模�3:行为型模�(策略,观察者等).md) -* [�探Java设计模�4:JDK中的设计模�.md](docs/java/design-parttern/�探Java设计模�4:JDK中的设计模�.md) -* [�探Java设计模�5:Spring涉�到的9�设计模�.md](docs/java/design-parttern/�探Java设计模�5:Spring涉�到的9�设计模�.md) - -## JavaWeb - -* [走进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%29工作原�详解.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) -* [走进JavaWeb技术世界8:浅�Tomcat9请求处��程与�动部署过程](docs/java-web/走进JavaWeb技术世界8:浅�Tomcat9请求处��程与�动部署过程.md) -* [走进JavaWeb技术世界9:Java日志系统的诞生与�展](docs/java-web/走进JavaWeb技术世界9:Java日志系统的诞生与�展.md) -* [走进JavaWeb技术世界10:从JavaBean讲到Spring](docs/java-web/走进JavaWeb技术世界10:从JavaBean讲到Spring.md) -* [走进JavaWeb技术世界11:�元测试框架Junit](docs/java-web/走进JavaWeb技术世界11:�元测试框架Junit.md) -* [走进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技术世界16:�简�置的SpringBoot](docs/java-web/走进JavaWeb技术世界16:�简�置的SpringBoot.md) - -### Spring - -* [Spring��剖�1:Spring概述](docs/java-web/Spring/Spring��剖�1:Spring概述.md) -* [Spring��剖�2:�探Spring IOC核心�程](docs/java-web/Spring/Spring��剖�2:�探Spring%20IOC核心�程.md) -* [Spring��剖�3:Spring IOC容器的加载过程 ](docs/java-web/Spring/Spring��剖�3:Spring%20IOC容器的加载过程.md) -* [Spring��剖�4:懒加载的�例Bean获�过程分�](docs/java-web/Spring/Spring��剖�4:懒加载的�例Bean获�过程分�.md) -* [Spring��剖�5:JDK和cglib动�代�原�详解 ](docs/java-web/Spring/Spring��剖�5:JDK和cglib动�代�原�详解.md) -* [Spring��剖�6:Spring AOP概述](docs/java-web/Spring/Spring��剖�6:Spring%20AOP概述.md) -* [Spring��剖�7:AOP实现原�详解 ](docs/java-web/Spring/Spring��剖�7:AOP实现原�详解.md) -* [Spring��剖�8:Spring事务概述](docs/java-web/Spring/Spring��剖�8:Spring事务概述.md) -* [Spring��剖�9:Spring事务��剖�](docs/java-web/Spring/Spring��剖�9:Spring事务��剖�.md) - -### 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) - -### SpringBoot - -todo - -### SpringCloud - -todo - -## Java进阶 - -### 并� - -* [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) - -### JVM - -* [JVM总结](docs/java/jvm/JVM总结.md) -* [深入�解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虚拟机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虚拟机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) -* [深入�解JVM虚拟机12:JVM性能管�神器VisualVM介�与实战](docs/java/jvm/深入�解JVM虚拟机12:JVM性能管�神器VisualVM介�与实战.md) -* [深入�解JVM虚拟机13:�谈四�引用�GC实践](docs/java/jvm/深入�解JVM虚拟机13:�谈四�引用�GC实践.md) -* [深入�解JVM虚拟机14:GC调优�路与常用工具](docs/java/jvm/深入�解JVM虚拟机:GC调优�路与常用工具.md) - -### 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基础 + +## 基础知识 +* [��对象基础](docs/Java/basic/��对象基础.md) +* [Java基本数�类型](docs/Java/basic/Java基本数�类型.md) +* [string和包装类](docs/Java/basic/string和包装类.md) +* [final关键字特性](docs/Java/basic/final关键字特性.md) +* [Java类和包](docs/Java/basic/Java类和包.md) +* [抽象类和接�](docs/Java/basic/抽象类和接�.md) +* [代��和代�执行顺�](docs/Java/basic/代��和代�执行顺�.md) +* [Java自动拆箱装箱里��的秘密](docs/Java/basic/Java自动拆箱装箱里��的秘密.md) +* [Java中的Class类和Object类](docs/Java/basic/Java中的Class类和Object类.md) +* [Java异常](docs/Java/basic/Java异常.md) +* [解读Java中的回调](docs/Java/basic/解读Java中的回调.md) +* [�射](docs/Java/basic/�射.md) +* [泛型](docs/Java/basic/泛型.md) +* [枚举类](docs/Java/basic/枚举类.md) +* [Java注解和最佳实践](docs/Java/basic/Java注解和最佳实践.md) +* [JavaIO�](docs/Java/basic/JavaIO�.md) +* [多线程](docs/Java/basic/多线程.md) +* [深入�解内部类](docs/Java/basic/深入�解内部类.md) +* [javac和javap](docs/Java/basic/javac和javap.md) +* [Java8新特性终�指�](docs/Java/basic/Java8新特性终�指�.md) +* [�列化和��列化](docs/Java/basic/�列化和��列化.md) +* [继承�装多�的实现原�](docs/Java/basic/继承�装多�的实现原�.md) + +## 集�类 + +* [Java集�类总结](docs/Java/collection/Java集�类总结.md) +* [Java集�详解:一文读懂ArrayList,Vector与Stack使用方法和实现原�](docs/Java/collection/Java集�详解:一文读懂ArrayList,Vector与Stack使用方法和实现原�.md) +* [Java集�详解:Queue和LinkedList](docs/Java/collection/Java集�详解:Queue和LinkedList.md) +* [Java集�详解:Iterator,fail-fast机制与比较器](docs/Java/collection/Java集�详解:Iterator,fail-fast机制与比较器.md) +* [Java集�详解:HashMap和HashTable](docs/Java/collection/Java集�详解:HashMap和HashTable.md) +* [Java集�详解:深入�解LinkedHashMap和LRU缓存](docs/Java/collection/Java集�详解:深入�解LinkedHashMap和LRU缓存.md) +* [Java集�详解:TreeMap和红黑树](docs/Java/collection/Java集�详解:TreeMap和红黑树.md) +* [Java集�详解:HashSet,TreeSet与LinkedHashSet](docs/Java/collection/Java集�详解:HashSet,TreeSet与LinkedHashSet.md) +* [Java集�详解:Java集�类细节精讲](docs/Java/collection/Java集�详解:Java集�类细节精讲.md) + +# JavaWeb + +* [走进JavaWeb技术世界:JavaWeb的由�和基础知识](docs/JavaWeb/走进JavaWeb技术世界:JavaWeb的由�和基础知识.md) +* [走进JavaWeb技术世界:JSP与Servlet的曾�与现在](docs/JavaWeb/走进JavaWeb技术世界:JSP与Servlet的曾�与现在.md) +* [走进JavaWeb技术世界:JDBC的进化与连接池技术](docs/JavaWeb/走进JavaWeb技术世界:JDBC的进化与连接池技术.md) +* [走进JavaWeb技术世界:Servlet工作原�详解](docs/JavaWeb/走进JavaWeb技术世界:Servlet工作原�详解.md) +* [走进JavaWeb技术世界:�探Tomcat的HTTP请求过程](docs/JavaWeb/走进JavaWeb技术世界:�探Tomcat的HTTP请求过程.md) +* [走进JavaWeb技术世界:Tomcat5总体架构剖�](docs/JavaWeb/走进JavaWeb技术世界:Tomcat5总体架构剖�.md) +* [走进JavaWeb技术世界:Tomcat和其他WEB容器的区别](docs/JavaWeb/走进JavaWeb技术世界:Tomcat和其他WEB容器的区别.md) +* [走进JavaWeb技术世界:浅�Tomcat9请求处��程与�动部署过程](docs/JavaWeb/走进JavaWeb技术世界:浅�Tomcat9请求处��程与�动部署过程.md) +* [走进JavaWeb技术世界:Java日志系统的诞生与�展](docs/JavaWeb/走进JavaWeb技术世界:Java日志系统的诞生与�展.md) +* [走进JavaWeb技术世界:从JavaBean讲到Spring](docs/JavaWeb/走进JavaWeb技术世界:从JavaBean讲到Spring.md) +* [走进JavaWeb技术世界:�元测试框架Junit](docs/JavaWeb/走进JavaWeb技术世界:�元测试框架Junit.md) +* [走进JavaWeb技术世界:从手动编译打包到项目构建工具Maven](docs/JavaWeb/走进JavaWeb技术世界:从手动编译打包到项目构建工具Maven.md) +* [走进JavaWeb技术世界:Hibernate入门�典与注解�开�](docs/JavaWeb/走进JavaWeb技术世界:Hibernate入门�典与注解�开�.md) +* [走进JavaWeb技术世界:Mybatis入门](docs/JavaWeb/走进JavaWeb技术世界:Mybatis入门.md) +* [走进JavaWeb技术世界:深入浅出Mybatis基本原�](docs/JavaWeb/走进JavaWeb技术世界:深入浅出Mybatis基本原�.md) +* [走进JavaWeb技术世界:�简�置的SpringBoot](docs/JavaWeb/走进JavaWeb技术世界:�简�置的SpringBoot.md) + +# Java进阶 + +## 并�编程 + +* [Java并�指�:并�基础与Java多线程](docs/Java/concurrency/Java并�指�:并�基础与Java多线程.md) +* [Java并�指�:深入�解Java内存模型JMM](docs/Java/concurrency/Java并�指�:深入�解Java内存模型JMM.md) +* [Java并�指�:并�三大问题与volatile关键字,CAS�作](docs/Java/concurrency/Java并�指�:并�三大问题与volatile关键字,CAS�作.md) +* [Java并�指�:Java中的�Lock和synchronized](docs/Java/concurrency/Java并�指�:Java中的�Lock和synchronized.md) +* [Java并�指�:JMM中的final关键字解�](docs/Java/concurrency/Java并�指�:JMM中的final关键字解�.md) +* [Java并�指�:Java内存模型JMM总结](docs/Java/concurrency/Java并�指�:Java内存模型JMM总结.md) +* [Java并�指�:JUC的核心类AQS详解](docs/Java/concurrency/Java并�指�:JUC的核心类AQS详解.md) +* [Java并�指�:AQS中的公平�与�公平�,Condtion](docs/Java/concurrency/Java并�指�:AQS中的公平�与�公平�,Condtion.md) +* [Java并�指�:AQS共享模�与并�工具类的实现](docs/Java/concurrency/Java并�指�:AQS共享模�与并�工具类的实现.md) +* [Java并�指�:Java读写�ReentrantReadWriteLock��分�](docs/Java/concurrency/Java并�指�:Java读写�ReentrantReadWriteLock��分�.md) +* [Java并�指�:解读Java阻塞队列BlockingQueue](docs/Java/concurrency/Java并�指�:解读Java阻塞队列BlockingQueue.md) +* [Java并�指�:深度解读java线程池设计�想���实现](docs/Java/concurrency/Java并�指�:深度解读Java线程池设计�想���实现.md) +* [Java并�指�:Java中的HashMap和ConcurrentHashMap全解�](docs/Java/concurrency/Java并�指�:Java中的HashMap和ConcurrentHashMap全解�.md) +* [Java并�指�:JUC中常用的Unsafe和Locksupport](docs/Java/concurrency/Java并�指�:JUC中常用的Unsafe和Locksupport.md) +* [Java并�指�:ForkJoin并�框架与工作窃�算法剖�](docs/Java/concurrency/Java并�指�:ForkJoin并�框架与工作窃�算法剖�.md) +* [Java并�编程学习总结](docs/Java/concurrency/Java并�编程学习总结.md) + +## JVM + +* [JVM总结](docs/Java/JVM/JVM总结.md) +* [深入�解JVM虚拟机:JVM内存的结构与消失的永久代](docs/Java/JVM/深入�解JVM虚拟机:JVM内存的结构与消失的永久代.md) +* [深入�解JVM虚拟机:JVM垃圾回收基本原�和算法](docs/Java/JVM/深入�解JVM虚拟机:JVM垃圾回收基本原�和算法.md) +* [深入�解JVM虚拟机:垃圾回收器详解](docs/Java/JVM/深入�解JVM虚拟机:垃圾回收器详解.md) +* [深入�解JVM虚拟机:Javaclass介�与解�实践](docs/Java/JVM/深入�解JVM虚拟机:Java字节�介�与解�实践.md) +* [深入�解JVM虚拟机:虚拟机字节�执行引擎](docs/Java/JVM/深入�解JVM虚拟机:虚拟机字节�执行引擎.md) +* [深入�解JVM虚拟机:深入�解JVM类加载机制](docs/Java/JVM/深入�解JVM虚拟机:深入�解JVM类加载机制.md) +* [深入�解JVM虚拟机:JNDI,OSGI,Tomcat类加载器实现](docs/Java/JVM/深入�解JVM虚拟机:JNDI,OSGI,Tomcat类加载器实现.md) +* [深入了解JVM虚拟机:Java的编译期优化与�行期优化](docs/Java/JVM/深入�解JVM虚拟机:Java的编译期优化与�行期优化.md) +* [深入�解JVM虚拟机:JVM监控工具与诊断实践](docs/Java/JVM/深入�解JVM虚拟机:JVM监控工具与诊断实践.md) +* [深入�解JVM虚拟机:JVM常用�数以�调优实践](docs/Java/JVM/深入�解JVM虚拟机:JVM常用�数以�调优实践.md) +* [深入�解JVM虚拟机:Java内存异常原�与实践](docs/Java/JVM/深入�解JVM虚拟机:Java内存异常原�与实践.md) +* [深入�解JVM虚拟机:JVM性能管�神器VisualVM介�与实战](docs/Java/JVM/深入�解JVM虚拟机:JVM性能管�神器VisualVM介�与实战.md) +* [深入�解JVM虚拟机:�谈四�引用�GC实践](docs/Java/JVM/深入�解JVM虚拟机:�谈四�引用�GC实践.md) +* [深入�解JVM虚拟机:GC调优�路与常用工具](docs/Java/JVM/深入�解JVM虚拟机:GC调优�路与常用工具.md) + +## Java网络编程 + +* [Java网络编程和NIO详解:JAVA 中原生的 socket 通信机制](docs/Java/network/Java网络编程与NIO详解:JAVA中原生的socket通信机制.md) +* [Java网络编程与NIO详解:JAVA NIO 一步步构建IO多路�用的请求模型](docs/Java/network/Java网络编程与NIO详解:JavaNIO一步步构建IO多路�用的请求模型.md) +* [Java网络编程和NIO详解:IO模型与Java网络编程模型](docs/Java/network/Java网络编程与NIO详解:IO模型与Java网络编程模型.md) +* [Java网络编程与NIO详解:浅�NIO包中的BufferChannel和Selector](docs/Java/network/Java网络编程与NIO详解:浅�NIO包中的BufferChannel和Selector.md) +* [Java网络编程和NIO详解:Java�阻塞IO和异步IO](docs/Java/network/Java网络编程与NIO详解:Java�阻塞IO和异步IO.md) +* [Java网络编程与NIO详解:LinuxEpoll实现原�详解](docs/Java/network/Java网络编程与NIO详解:LinuxEpoll实现原�详解.md.md) +* [Java网络编程与NIO详解:浅谈Linux中Selector的实现原�](docs/Java/network/Java网络编程与NIO详解:浅谈Linux中Selector的实现原�.md) +* [Java网络编程与NIO详解:浅�mmap和DirectBuffer](docs/Java/network/Java网络编程与NIO详解:浅�mmap和DirectBuffer.md) +* [Java网络编程与NIO详解:基于NIO的网络编程框架Netty](docs/Java/network/Java网络编程与NIO详解:基于NIO的网络编程框架Netty.md) +* [Java网络编程与NIO详解:Java网络编程与NIO详解](docs/Java/network/Java网络编程与NIO详解:深度解读Tomcat中的NIO模型.md) +* [Java网络编程与NIO详解:Tomcat中的Connector��分�(NIO)](docs/Java/network/Java网络编程与NIO详解:Tomcat中的Connector��分�(NIO).md) + +# Spring全家桶 + +## Spring + +* [SpringAOP的概念与作用](docs/Spring全家桶/Spring/Spring常�注解.md) +* [SpringBean的定义与管�(核心)](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中对于数�库的访问](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中对于校验功能的支�](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中的Environment环境��](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中的事件处�机制](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中的资�管�](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring中的�置元数�(管��置的基本数�)](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring事务基本用法](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring�集](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring容器与IOC](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring常�注解](docs/Spring全家桶/Spring/Spring常�注解.md) +* [Spring概述](docs/Spring全家桶/Spring/Spring常�注解.md) +* [第一个Spring应用](docs/Spring全家桶/Spring/Spring常�注解.md) + +## Spring��分� + +### 综� +* [Spring��剖�:�探SpringIOC核心�程](docs/Spring全家桶/Spring��分�/Spring��剖�:�探SpringIOC核心�程.md) +* [Spring��剖�:SpringIOC容器的加载过程 ](docs/Spring全家桶/Spring��分�/Spring��剖�:SpringIOC容器的加载过程.md) +* [Spring��剖�:懒加载的�例Bean获�过程分�](docs/Spring全家桶/Spring��分�/Spring��剖�:懒加载的�例Bean获�过程分�.md) +* [Spring��剖�:JDK和cglib动�代�原�详解 ](docs/Spring全家桶/Spring��分�/Spring��剖�:JDK和cglib动�代�原�详解.md) +* [Spring��剖�:SpringAOP概述](docs/Spring全家桶/Spring��分�/Spring��剖�:SpringAOP概述.md) +* [Spring��剖�:AOP实现原�详解 ](docs/Spring全家桶/Spring��分�/Spring��剖�:AOP实现原�详解.md) +* [Spring��剖�:Spring事务概述](docs/Spring全家桶/Spring��分�/Spring��剖�:Spring事务概述.md) +* [Spring��剖�:Spring事务��剖�](docs/Spring全家桶/Spring��分�/Spring��剖�:Spring事务��剖�.md) + +### AOP +* [AnnotationAwareAspectJAutoProxyCreator 分�(上)](docs/Spring全家桶/Spring��分�/SpringAOP/AnnotationAwareAspectJAutoProxyCreator分�(上).md) +* [AnnotationAwareAspectJAutoProxyCreator 分�(下)](docs/Spring全家桶/Spring��分�/SpringAOP/AnnotationAwareAspectJAutoProxyCreator分�(下).md) +* [AOP示例demo�@EnableAspectJAutoProxy](docs/Spring全家桶/Spring��分�/SpringAOP/AOP示例demo�@EnableAspectJAutoProxy.md) +* [SpringAop(四):jdk 动�代�](docs/Spring全家桶/Spring��分�/SpringAOP/SpringAop(四):jdk动�代�.md) +* [SpringAop(五):cglib 代�](docs/Spring全家桶/Spring��分�/SpringAOP/SpringAop(五):cglib代�.md) +* [SpringAop(六):aop 总结](docs/Spring全家桶/Spring��分�/SpringAOP/SpringAop(六):aop总结.md) + +### 事务 +* [spring 事务(一):认识事务组件](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(一):认识事务组件.md) +* [spring 事务(二):事务的执行�程](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(二):事务的执行�程.md) +* [spring 事务(三):事务的隔离级别与传播方�的处�](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(三):事务的隔离级别与传播方�的处�01.md) +* [spring 事务(四):事务的隔离级别与传播方�的处�](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(四):事务的隔离级别与传播方�的处�02.md) +* [spring 事务(五):事务的隔离级别与传播方�的处�](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(五):事务的隔离级别与传播方�的处�03.md) +* [spring 事务(六):事务的隔离级别与传播方�的处�](docs/Spring全家桶/Spring��分�/Spring事务/Spring事务(六):事务的隔离级别与传播方�的处�04.md) + +### �动�程 +* [spring�动�程(一):�动�程概览](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(一):�动�程概览.md) +* [spring�动�程(二):ApplicationContext 的创建](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(二):ApplicationContext的创建.md) +* [spring�动�程(三):包的扫��程](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(三):包的扫��程.md) +* [spring�动�程(四):�动�的准备工作](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(四):�动�的准备工作.md) +* [spring�动�程(五):执行 BeanFactoryPostProcessor](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(五):执行BeanFactoryPostProcessor.md) +* [spring�动�程(六):注册 BeanPostProcessor](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(六):注册BeanPostProcessor.md) +* [spring�动�程(七):国际化与事件处�](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(七):国际化与事件处�.md) +* [spring�动�程(八):完� BeanFactory 的�始化](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(八):完�BeanFactory的�始化.md) +* [spring�动�程(�):�例 bean 的创建](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(�):�例bean的创建.md) +* [spring�动�程(�):�动完�的处�](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(�):�动完�的处�.md) +* [spring�动�程(�一):�动�程总结](docs/Spring全家桶/Spring��分�/Spring�动�程/Spring�动�程(�一):�动�程总结.md) + +### 组件分� +* [spring 组件之 ApplicationContext](docs/Spring全家桶/Spring��分�/Spring组件分�/Spring组件之ApplicationContext.md) +* [spring 组件之 BeanDefinition](docs/Spring全家桶/Spring��分�/Spring组件分�/Spring组件之BeanDefinition.md) +* [Spring 组件之 BeanFactory](docs/Spring全家桶/Spring��分�/Spring组件分�/Spring组件之BeanFactory.md) +* [spring 组件之 BeanFactoryPostProcessor](docs/Spring全家桶/Spring��分�/Spring组件分�/Spring组件之BeanFactoryPostProcessor.md) +* [spring 组件之 BeanPostProcessor](docs/Spring全家桶/Spring��分�/Spring组件分�/Spring组件之BeanPostProcessor.md) + +### ��机制探秘 + +* [ConfigurationClassPostProcessor(一):处� @ComponentScan 注解](docs/Spring全家桶/Spring��分�/Spring��机制探秘/ConfigurationClassPostProcessor(一):处�@ComponentScan注解.md) +* [ConfigurationClassPostProcessor(三):处� @Import 注解](docs/Spring全家桶/Spring��分�/Spring��机制探秘/ConfigurationClassPostProcessor(三):处�@Import注解.md) +* [ConfigurationClassPostProcessor(二):处� @Bean 注解](docs/Spring全家桶/Spring��分�/Spring��机制探秘/ConfigurationClassPostProcessor(二):处�@Bean注解.md) +* [ConfigurationClassPostProcessor(四):处� @Conditional 注解](docs/Spring全家桶/Spring��分�/Spring��机制探秘/ConfigurationClassPostProcessor(四):处�@Conditional注解.md) +* [Spring 探秘之 AOP 的执行顺�](docs/Spring全家桶/Spring��分�/Spring��机制探秘/Spring探秘之AOP的执行顺�.md) +* [Spring 探秘之 Spring 事件机制](docs/Spring全家桶/Spring��分�/Spring��机制探秘/Spring探秘之Spring事件机制.md) +* [spring 探秘之循环�赖的解决(一):�论基石](docs/Spring全家桶/Spring��分�/Spring��机制探秘/Spring探秘之循环�赖的解决(一):�论基石.md) +* [spring 探秘之循环�赖的解决(二):��分�](docs/Spring全家桶/Spring��分�/Spring��机制探秘/Spring探秘之循环�赖的解决(二):��分�.md) +* [spring 探秘之监�器注解 @EventListener](docs/Spring全家桶/Spring��分�/Spring��机制探秘/spring探秘之监�器注解@EventListener.md) +* [spring 探秘之组�注解的处�](docs/Spring全家桶/Spring��分�/Spring��机制探秘/Spring探秘之组�注解的处�.md) + +## SpringMVC + +* [SpringMVC中的国际化功能](docs/Spring全家桶/SpringMVC/SpringMVC中的国际化功能.md) +* [SpringMVC中的异常处�器](docs/Spring全家桶/SpringMVC/SpringMVC中的异常处�器.md) +* [SpringMVC中的拦截器](docs/Spring全家桶/SpringMVC/SpringMVC中的拦截器.md) +* [SpringMVC中的视图解�器](docs/Spring全家桶/SpringMVC/SpringMVC中的视图解�器.md) +* [SpringMVC中的过滤器Filter](docs/Spring全家桶/SpringMVC/SpringMVC中的过滤器Filter.md) +* [SpringMVC基本介�与快速入门](docs/Spring全家桶/SpringMVC/SpringMVC基本介�与快速入门.md) +* [SpringMVC如何实现文件上传](docs/Spring全家桶/SpringMVC/SpringMVC如何实现文件上传.md) +* [SpringMVC中的常用功能](docs/Spring全家桶/SpringMVC/SpringMVC中的常用功能.md) + +## SpringMVC��分� + +* [SpringMVC��分�:SpringMVC概述](docs/Spring全家桶/SpringMVC��分�/SpringMVC��分�:SpringMVC概述.md) +* [SpringMVC��分�:SpringMVC设计�念与DispatcherServlet](docs/Spring全家桶/SpringMVC��分�/SpringMVC��分�:SpringMVC设计�念与DispatcherServlet.md) +* [SpringMVC��分�:DispatcherServlet的�始化与请求转� ](docs/Spring全家桶/SpringMVC��分�/SpringMVC��分�:DispatcherServlet的�始化与请求转�.md) +* [SpringMVC��分�:DispatcherServlet如何找到正确的Controller ](docs/Spring全家桶/SpringMVC��分�/SpringMVC��分�:DispatcherServlet如何找到正确的Controller.md) +* [SpringMVC��剖�:消�转�器HttpMessageConverter与@ResponseBody注解](docs/Spring全家桶/SpringMVC/SpringMVC��剖�:消�转�器HttpMessageConverter与@ResponseBody注解.md) +* [DispatcherServlet �始化�程 ](docs/Spring全家桶/SpringMVC��分�/DispatcherServlet�始化�程.md) +* [RequestMapping �始化�程 ](docs/Spring全家桶/SpringMVC��分�/RequestMapping�始化�程.md) +* [Spring 容器�动 Tomcat ](docs/Spring全家桶/SpringMVC��分�/Spring容器�动Tomcat.md) +* [SpringMVC demo 与@EnableWebMvc 注解 ](docs/Spring全家桶/SpringMVC��分�/SpringMVC的Demo与@EnableWebMvc注解.md) +* [SpringMVC 整体��结构总结 ](docs/Spring全家桶/SpringMVC��分�/SpringMVC整体��结构总结.md) +* [请求执行�程(一)之获� Handler ](docs/Spring全家桶/SpringMVC��分�/请求执行�程(一)之获�Handler.md) +* [请求执行�程(二)之执行 Handler 方法 ](docs/Spring全家桶/SpringMVC��分�/请求执行�程(二)之执行Handler方法.md) + +## SpringBoot + +* [SpringBoot系列:SpringBoot的�世今生](docs/Spring全家桶/SpringBoot/SpringBoot的�世今生.md) +* [给你一份SpringBoot知识清�.md](docs/Spring全家桶/SpringBoot/给你一份SpringBoot知识清�.md) +* [Spring常�注解使用指�(包�Spring+SpringMVC+SpringBoot)](docs/Spring全家桶/SpringBoot/Spring常�注解使用指�(包�Spring+SpringMVC+SpringBoot).md) +* [SpringBoot中的日志管�](docs/Spring全家桶/SpringBoot/SpringBoot中的日志管�.md) +* [SpringBoot常�注解](docs/Spring全家桶/SpringBoot/SpringBoot常�注解.md) +* [SpringBoot应用也�以部署到外部Tomcat](docs/Spring全家桶/SpringBoot/SpringBoot应用也�以部署到外部Tomcat.md) +* [SpringBoot生产环境工具Actuator](docs/Spring全家桶/SpringBoot/SpringBoot生产环境工具Actuator.md) +* [SpringBoot的Starter机制](docs/Spring全家桶/SpringBoot/SpringBoot的Starter机制.md) +* [SpringBoot的�世今生](docs/Spring全家桶/SpringBoot/SpringBoot的�世今生.md) +* [SpringBoot的基本使用](docs/Spring全家桶/SpringBoot/SpringBoot的基本使用.md) +* [SpringBoot的�置文件管�](docs/Spring全家桶/SpringBoot/SpringBoot的�置文件管�.md) +* [SpringBoot自带的热部署工具](docs/Spring全家桶/SpringBoot/SpringBoot自带的热部署工具.md) +* [SpringBoot中的任务调度与@Async](docs/Spring全家桶/SpringBoot/SpringBoot中的任务调度与@Async.md) +* [基于SpringBoot中的开�监控工具SpringBootAdmin](docs/Spring全家桶/SpringBoot/基于SpringBoot中的开�监控工具SpringBootAdmin.md) + +## SpringBoot��分� +* [@SpringBootApplication 注解](docs/Spring全家桶/SpringBoot��解�/@SpringBootApplication注解.md) +* [springboot web应用(一):servlet 组件的注册�程](docs/Spring全家桶/SpringBoot��解�/SpringBootWeb应用(一):servlet组件的注册�程.md) +* [springboot web应用(二):WebMvc 装�过程](docs/Spring全家桶/SpringBoot��解�/SpringBootWeb应用(二):WebMvc装�过程.md) + +* [SpringBoot �动�程(一):准备 SpringApplication](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(一):准备SpringApplication.md) +* [SpringBoot �动�程(二):准备�行环境](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(二):准备�行环境.md) +* [SpringBoot �动�程(三):准备IOC容器](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(三):准备IOC容器.md) +* [springboot �动�程(四):�动IOC容器](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(四):�动IOC容器.md) +* [springboot �动�程(五):完��动](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(五):完��动.md) +* [springboot �动�程(六):�动�程总结](docs/Spring全家桶/SpringBoot��解�/SpringBoot�动�程(六):�动�程总结.md) + +* [springboot 自动装�(一):加载自动装�类](docs/Spring全家桶/SpringBoot��解�/SpringBoot自动装�(一):加载自动装�类.md) +* [springboot 自动装�(二):�件注解](docs/Spring全家桶/SpringBoot��解�/SpringBoot自动装�(二):�件注解.md) +* [springboot 自动装�(三):自动装�顺�](docs/Spring全家桶/SpringBoot��解�/SpringBoot自动装�(三):自动装�顺�.md) + +## SpringCloud +* [SpringCloud概述](docs/Spring全家桶/SpringCloud/SpringCloud概述.md) +* [Spring Cloud Config](docs/Spring全家桶/SpringCloud/SpringCloudConfig.md) +* [Spring Cloud Consul](docs/Spring全家桶/SpringCloud/SpringCloudConsul.md) +* [Spring Cloud Eureka](docs/Spring全家桶/SpringCloud/SpringCloudEureka.md) +* [Spring Cloud Gateway](docs/Spring全家桶/SpringCloud/SpringCloudGateway.md) +* [Spring Cloud Hystrix](docs/Spring全家桶/SpringCloud/SpringCloudHystrix.md) +* [Spring Cloud LoadBalancer](docs/Spring全家桶/SpringCloud/SpringCloudLoadBalancer.md) +* [Spring Cloud OpenFeign](docs/Spring全家桶/SpringCloud/SpringCloudOpenFeign.md) +* [Spring Cloud Ribbon](docs/Spring全家桶/SpringCloud/SpringCloudRibbon.md) +* [Spring Cloud Sleuth](docs/Spring全家桶/SpringCloud/SpringCloudSleuth.md) +* [Spring Cloud Zuul](docs/Spring全家桶/SpringCloud/SpringCloudZuul.md) + +## SpringCloud ��分� +* [Spring Cloud Config��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudConfig��分�.md) +* [Spring Cloud Eureka��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudEureka��分�.md) +* [Spring Cloud Gateway��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudGateway��分�.md) +* [Spring Cloud Hystrix��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudHystrix��分�.md) +* [Spring Cloud LoadBalancer��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudLoadBalancer��分�.md) +* [Spring Cloud OpenFeign��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudOpenFeign��分�.md) +* [Spring Cloud Ribbon��分�](docs/Spring全家桶/SpringCloud��分�/SpringCloudRibbon��分�.md) + +## SpringCloud Alibaba +* [SpringCloud Alibaba概览](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibaba概览.md) +* [SpringCloud Alibaba nacos](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaNacos.md) +* [SpringCloud Alibaba RocketMQ](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaRocketMQ.md) +* [SpringCloud Alibaba sentinel](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaSentinel.md) +* [SpringCloud Alibaba skywalking](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaSkywalking.md) +* [SpringCloud Alibaba seata](docs/Spring全家桶/SpringCloudAlibaba/SpringCloudAlibabaSeata.md) + +## SpringCloud Alibaba��分� +* [Spring Cloud Seata��分�](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudSeata��分�.md) +* [Spring Cloud Sentinel��分�](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudSentinel��分�.md) +* [SpringCloudAlibaba nacos��分�:概览](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudAlibabaNacos��分�:概览.md) +* [SpringCloudAlibaba nacos��分�:�务�现](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudAlibabaNacos��分�:�务�现.md) +* [SpringCloudAlibaba nacos��分�:�务注册](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudAlibabaNacos��分�:�务注册.md) +* [SpringCloudAlibaba nacos��分�:�置中心](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudAlibabaNacos��分�:�置中心.md) +* [Spring Cloud RocketMQ��分�](docs/Spring全家桶/SpringCloudAlibaba��分�/SpringCloudRocketMQ��分�.md) + +# 设计模� + +* [设计模�学习总结](docs/Java/design-parttern/设计模�学习总结.md) +* [�探Java设计模�:创建型模�(工厂,�例等).md](docs/Java/design-parttern/�探Java设计模�:创建型模�(工厂,�例等).md) +* [�探Java设计模�:结构型模�(代�模�,适�器模�等).md](docs/Java/design-parttern/�探Java设计模�:结构型模�(代�模�,适�器模�等).md) +* [�探Java设计模�:行为型模�(策略,观察者等).md](docs/Java/design-parttern/�探Java设计模�:行为型模�(策略,观察者等).md) +* [�探Java设计模�:JDK中的设计模�.md](docs/Java/design-parttern/�探Java设计模�:JDK中的设计模�.md) +* [�探Java设计模�:Spring涉�到的�设计模�.md](docs/Java/design-parttern/�探Java设计模�:Spring涉�到的�设计模�.md) + + +# 计算机基础 + +## 计算机网络 todo -### �作系统 +## �作系统 todo -#### Linux相关 +## Linux相关 todo -### 数�结构与算法 +## 数�结构与算法 todo -#### 数�结构 +## 数�结构 todo -#### 算法 +## 算法 todo -## 数�库 +# 数�库 todo -### MySQL +## 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数�库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) -* [�新学习MySQL数�库6:浅谈MySQL的中事务与�](docs/database/�新学习MySQL数�库6:浅谈MySQL的中事务与�.md) -* [�新学习Mysql数�库7:详解MyIsam与InnoDB引擎的�实现](docs/database/�新学习Mysql数�库7:详解MyIsam与InnoDB引擎的�实现.md) -* [�新学习Mysql数�库8:MySQL的事务隔离级别实战](docs/database/�新学习Mysql数�库8:MySQL的事务隔离级别实战.md) -* [�新学习MySQL数�库9:Innodb中的事务隔离级别和�的关系](docs/database/�新学习MySQL数�库9:Innodb中的事务隔离级别和�的关系.md) -* [�新学习MySQL数�库10:MySQL里的那些日志们](docs/database/�新学习MySQL数�库10:MySQL里的那些日志们.md) -* [�新学习MySQL数�库11:以Java的视角���SQL注入](docs/database/�新学习MySQL数�库11:以Java的视角���SQL注入.md) -* [�新学习MySQL数�库12:从实践sql语�优化开始](docs/database/�新学习MySQL数�库12:从实践sql语�优化开始.md) -* [�新学习Mysql数�库13:Mysql主从�制,读写分离,分表分库策略与实践](docs/database/�新学习Mysql数�13:Mysql主从�制,读写分离,分表分库策略与实践.md) - - -## 缓存 - -### Redis +* [�新学习Mysql数�库:无废�MySQL入门](docs/database/�新学习MySQL数�库:无废�MySQL入门.md) +* [�新学习Mysql数�库:『浅入浅出�MySQL和InnoDB](docs/database/�新学习MySQL数�库:『浅入浅出�MySQL和InnoDB.md) +* [�新学习Mysql数�库:Mysql存储引擎与数�存储原�](docs/database/�新学习MySQL数�库:Mysql存储引擎与数�存储原�.md) +* [�新学习Mysql数�库:Mysql索引实现原�和相关数�结构算法](docs/database/�新学习MySQL数�库:Mysql索引实现原�和相关数�结构算法.md) +* [�新学习Mysql数�库:根�MySQL索引原�进行分�与优化](docs/database/�新学习MySQL数�库:根�MySQL索引原�进行分�与优化.md) +* [�新学习MySQL数�库:浅谈MySQL的中事务与�](docs/database/�新学习MySQL数�库:浅谈MySQL的中事务与�.md) +* [�新学习Mysql数�库:详解MyIsam与InnoDB引擎的�实现](docs/database/�新学习MySQL数�库:详解MyIsam与InnoDB引擎的�实现.md) +* [�新学习Mysql数�库:MySQL的事务隔离级别实战](docs/database/�新学习MySQL数�库:MySQL的事务隔离级别实战.md) +* [�新学习MySQL数�库:Innodb中的事务隔离级别和�的关系](docs/database/�新学习MySQL数�库:Innodb中的事务隔离级别和�的关系.md) +* [�新学习MySQL数�库:MySQL里的那些日志们](docs/database/�新学习MySQL数�库:MySQL里的那些日志们.md) +* [�新学习MySQL数�库:以Java的视角���SQL注入](docs/database/�新学习MySQL数�库:以Java的视角���SQL注入.md) +* [�新学习MySQL数�库:从实践sql语�优化开始](docs/database/�新学习MySQL数�库:从实践sql语�优化开始.md) +* [�新学习Mysql数�库:Mysql主从�制,读写分离,分表分库策略与实践](docs/database/�新学习MySQL数�库:Mysql主从�制,读写分离,分表分库策略与实践.md) + + +# 缓存 + +## Redis * [Redis原�与实践总结](docs/cache/Redis原�与实践总结.md) * [探索Redis设计与实现开篇:什么是Redis](docs/cache/探索Redis设计与实现开篇:什么是Redis.md) -* [探索Redis设计与实现1:Redis 的基础数�结构概览](docs/cache/探索Redis设计与实现1:Redis%20的基础数�结构概览.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) -* [探索Redis设计与实现5:Redis内部数�结构详解——quicklist](docs/cache/探索Redis设计与实现5:Redis内部数�结构详解——quicklist.md) -* [探索Redis设计与实现6:Redis内部数�结构详解——skiplist](docs/cache/探索Redis设计与实现6:Redis内部数�结构详解——skiplist.md) -* [探索Redis设计与实现7:Redis内部数�结构详解——intset](docs/cache/探索Redis设计与实现7:Redis内部数�结构详解——intset.md) -* [探索Redis设计与实现8:连接底层与表�的数�结构robj](docs/cache/探索Redis设计与实现8:连接底层与表�的数�结构robj.md) -* [探索Redis设计与实现9:数�库redisDb与键过期删除策略](docs/cache/探索Redis设计与实现9:数�库redisDb与键过期删除策略.md) -* [探索Redis设计与实现10:Redis的事件驱动模型与命令执行过程](docs/cache/探索Redis设计与实现10:Redis的事件驱动模型与命令执行过程.md) -* [探索Redis设计与实现11:使用快照和AOF将Redis数��久化到硬盘中](docs/cache/探索Redis设计与实现11:使用快照和AOF将Redis数��久化到硬盘中.md) -* [探索Redis设计与实现12:浅�Redis主从�制](docs/cache/探索Redis设计与实现12:浅�Redis主从�制.md) -* [探索Redis设计与实现13:Redis集群机制�一个Redis架构演进实例](docs/cache/探索Redis设计与实现13:Redis集群机制�一个Redis架构演进实例.md) -* [探索Redis设计与实现14:Redis事务浅�与ACID特性介�](docs/cache/探索Redis设计与实现14:Redis事务浅�与ACID特性介�.md) -* [探索Redis设计与实现15:Redis分布��进化� ](docs/cache/探索Redis设计与实现15:Redis分布��进化�.md ) - -## 消�队列 - -### Kafka - -## 大�端 -* [�端技术�谈开篇:云计算,大数�与AI的故事](docs/big-backEnd/�端技术�谈开篇:云计算,大数�与AI的故事.md) -* [�端技术�谈1:�索引擎基础倒排索引](docs/big-backEnd/�端技术�谈1:�索引擎基础倒排索引.md) -* [�端技术�谈2:�索引擎工作原�](docs/big-backEnd/�端技术�谈2:�索引擎工作原�.md) -* [�端技术�谈3:Lucene基础原�与实践](docs/big-backEnd/�端技术�谈3:Lucene基础原�与实践.md) -* [�端技术�谈4:Elasticsearch与solr入门实践](docs/big-backEnd/�端技术�谈4:Elasticsearch与solr入门实践.md) -* [�端技术�谈5:云计算的�世今生](docs/big-backEnd/�端技术�谈5:云计算的�世今生.md) -* [�端技术�谈6:白�虚拟化技术](docs/big-backEnd/�端技术�谈6:白�虚拟化技术.md ) -* [�端技术�谈7:OpenStack的基石KVM](docs/big-backEnd/�端技术�谈7:OpenStack的基石KVM.md) -* [�端技术�谈8:OpenStack架构设计](docs/big-backEnd/�端技术�谈8:OpenStack架构设计.md) -* [�端技术�谈9:先�懂Docker核心概念�](docs/big-backEnd/�端技术�谈9:先�懂Docker核心概念�.md) -* [�端技术�谈10:Docker 核心技术与实现原�](docs/big-backEnd/�端技术�谈10:Docker%20核心技术与实现原�.md) -* [�端技术�谈11:�分钟�解Kubernetes核心概念](docs/big-backEnd/�端技术�谈11:�分钟�解Kubernetes核心概念.md) -* [�端技术�谈12:�一�大数�研�的基本概念](docs/big-backEnd/�端技术�谈12:�一�大数�研�的基本概念.md) - -## 分布� -### �论 -* [分布�系统�论基础开篇:从放弃到入门](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:分布�系统的一些基本概念 ](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 ) - - -## �试指� +* [探索Redis设计与实现:Redis的基础数�结构概览](docs/cache/探索Redis设计与实现:Redis的基础数�结构概览.md) +* [探索Redis设计与实现:Redis内部数�结构详解——dict](docs/cache/探索Redis设计与实现:Redis内部数�结构详解——dict.md) +* [探索Redis设计与实现:Redis内部数�结构详解——sds](docs/cache/探索Redis设计与实现:Redis内部数�结构详解——sds.md) +* [探索Redis设计与实现:Redis内部数�结构详解——ziplist](docs/cache/探索Redis设计与实现:Redis内部数�结构详解——ziplist.md) +* [探索Redis设计与实现:Redis内部数�结构详解——quicklist](docs/cache/探索Redis设计与实现:Redis内部数�结构详解——quicklist.md) +* [探索Redis设计与实现:Redis内部数�结构详解——skiplist](docs/cache/探索Redis设计与实现:Redis内部数�结构详解——skiplist.md) +* [探索Redis设计与实现:Redis内部数�结构详解——intset](docs/cache/探索Redis设计与实现:Redis内部数�结构详解——intset.md) +* [探索Redis设计与实现:连接底层与表�的数�结构robj](docs/cache/探索Redis设计与实现:连接底层与表�的数�结构robj.md) +* [探索Redis设计与实现:数�库redisDb与键过期删除策略](docs/cache/探索Redis设计与实现:数�库redisDb与键过期删除策略.md) +* [探索Redis设计与实现:Redis的事件驱动模型与命令执行过程](docs/cache/探索Redis设计与实现:Redis的事件驱动模型与命令执行过程.md) +* [探索Redis设计与实现:使用快照和AOF将Redis数��久化到硬盘中](docs/cache/探索Redis设计与实现:使用快照和AOF将Redis数��久化到硬盘中.md) +* [探索Redis设计与实现:浅�Redis主从�制](docs/cache/探索Redis设计与实现:浅�Redis主从�制.md) +* [探索Redis设计与实现:Redis集群机制�一个Redis架构演进实例](docs/cache/探索Redis设计与实现:Redis集群机制�一个Redis架构演进实例.md) +* [探索Redis设计与实现:Redis事务浅�与ACID特性介�](docs/cache/探索Redis设计与实现:Redis事务浅�与ACID特性介�.md) +* [探索Redis设计与实现:Redis分布��进化� ](docs/cache/探索Redis设计与实现:Redis分布��进化�.md ) + +# 消�队列 + +## Kafka +* [消�队列kafka详解:Kafka快速上手(Java版)](docs/mq/kafka/消�队列kafka详解:Kafka快速上手(Java版).md) +* [消�队列kafka详解:Kafka一�消�存到broker的过程](docs/mq/kafka/消�队列kafka详解:Kafka一�消�存到broker的过程.md) +* [消�队列kafka详解:消�队列kafka详解:Kafka介�](docs/mq/kafka/消�队列kafka详解:Kafka介�.md) +* [消�队列kafka详解:Kafka原�分�总结篇](docs/mq/kafka/消�队列kafka详解:Kafka原�分�总结篇.md) +* [消�队列kafka详解:Kafka常�命令��置总结](docs/mq/kafka/消�队列kafka详解:Kafka常�命令��置总结.md) +* [消�队列kafka详解:Kafka架构介�](docs/mq/kafka/消�队列kafka详解:Kafka架构介�.md) +* [消�队列kafka详解:Kafka的集群工作原�](docs/mq/kafka/消�队列kafka详解:Kafka的集群工作原�.md) +* [消�队列kafka详解:Kafka��知识点+�试题大全](docs/mq/kafka/消�队列kafka详解:Kafka��知识点+�试题大全.md) +* [消�队列kafka详解:如何实现延迟队列](docs/mq/kafka/消�队列kafka详解:如何实现延迟队列.md) +* [消�队列kafka详解:如何实现死信队列](docs/mq/kafka/消�队列kafka详解:如何实现死信队列.md) + +## RocketMQ +* [RocketMQ系列:事务消�(最终一致性)](docs/mq/RocketMQ/RocketMQ系列:事务消�(最终一致性).md) +* [RocketMQ系列:基本概念](docs/mq/RocketMQ/RocketMQ系列:基本概念.md) +* [RocketMQ系列:广播与延迟消�](docs/mq/RocketMQ/RocketMQ系列:广播与延迟消�.md) +* [RocketMQ系列:批���与过滤](docs/mq/RocketMQ/RocketMQ系列:批���与过滤.md) +* [RocketMQ系列:消�的生产与消费](docs/mq/RocketMQ/RocketMQ系列:消�的生产与消费.md) +* [RocketMQ系列:环境�建](docs/mq/RocketMQ/RocketMQ系列:环境�建.md) +* [RocketMQ系列:顺�消费](docs/mq/RocketMQ/RocketMQ系列:顺�消费.md) + +# 大�端 +* [�端技术�谈开篇:云计算,大数�与AI的故事](docs/backend/�端技术�谈开篇:云计算,大数�与AI的故事.md) +* [�端技术�谈:�索引擎基础倒排索引](docs/backend/�端技术�谈:�索引擎基础倒排索引.md) +* [�端技术�谈:�索引擎工作原�](docs/backend/�端技术�谈:�索引擎工作原�.md) +* [�端技术�谈:Lucene基础原�与实践](docs/backend/�端技术�谈:Lucene基础原�与实践.md) +* [�端技术�谈:Elasticsearch与solr入门实践](docs/backend/�端技术�谈:Elasticsearch与solr入门实践.md) +* [�端技术�谈:云计算的�世今生](docs/backend/�端技术�谈:云计算的�世今生.md) +* [�端技术�谈:白�虚拟化技术](docs/backend/�端技术�谈:白�虚拟化技术.md ) +* [�端技术�谈:OpenStack的基石KVM](docs/backend/�端技术�谈:OpenStack的基石KVM.md) +* [�端技术�谈:OpenStack架构设计](docs/backend/�端技术�谈:OpenStack架构设计.md) +* [�端技术�谈:先�懂Docker核心概念�](docs/backend/�端技术�谈:先�懂Docker核心概念�.md) +* [�端技术�谈:Docker 核心技术与实现原�](docs/backend/�端技术�谈:Docker%核心技术与实现原�.md) +* [�端技术�谈:�分钟�解Kubernetes核心概念](docs/backend/�端技术�谈:�分钟�解Kubernetes核心概念.md) +* [�端技术�谈:�一�大数�研�的基本概念](docs/backend/�端技术�谈:�一�大数�研�的基本概念.md) + +# 分布� +## 分布��论 +* [分布�系统�论基础:一致性PC和PC ](docs/distributed/basic/分布�系统�论基础:一致性PC和PC.md) +* [分布�系统�论基础:CAP ](docs/distributed/basic/分布�系统�论基础:CAP.md) +* [分布�系统�论基础:时间时钟和事件顺�](docs/distributed/basic/分布�系统�论基础:时间时钟和事件顺�.md) +* [分布�系统�论基础:Paxos](docs/distributed/basic/分布�系统�论基础:Paxos.md) +* [分布�系统�论基础:选举多数派和租约](docs/distributed/basic/分布�系统�论基础:选举多数派和租约.md) +* [分布�系统�论基础:RaftZab ](docs/distributed/basic/分布�系统�论基础:RaftZab.md) +* [分布�系统�论进阶:Paxos��和优化 ](docs/distributed/basic/分布�系统�论进阶:Paxos��和优化.md) +* [分布�系统�论基础:zookeeper分布��调�务 ](docs/distributed/basic/分布�系统�论基础:zookeeper分布��调�务.md) +* [分布��论总结](docs/distributed/分布�技术实践总结.md) + +## 分布�技术 +* [�懂分布�技术:分布�系统的一些基本概念](docs/distributed/practice/�懂分布�技术:分布�系统的一些基本概念.md ) +* [�懂分布�技术:分布�一致性�议与Paxos,Raft算法](docs/distributed/practice/�懂分布�技术:分布�一致性�议与Paxos,Raft算法.md) +* [�懂分布�技术:�探分布��调�务zookeeper](docs/distributed/practice/�懂分布�技术:�探分布��调�务zookeeper.md ) +* [�懂分布�技术:ZAB�议概述与选主�程详解](docs/distributed/practice/�懂分布�技术:ZAB�议概述与选主�程详解.md ) +* [�懂分布�技术:Zookeeper的�置与集群管�实战](docs/distributed/practice/�懂分布�技术:Zookeeper的�置与集群管�实战.md) +* [�懂分布�技术:Zookeeper典型应用场景�实践](docs/distributed/practice/�懂分布�技术:Zookeeper典型应用场景�实践.md ) +* [�懂分布�技术:LVS实现负载�衡的原�与实践 ](docs/distributed/practice/�懂分布�技术:LVS实现负载�衡的原�与实践.md ) +* [�懂分布�技术:分布�session解决方案与一致性hash](docs/distributed/practice/�懂分布�技术:分布�session解决方案与一致性hash.md) +* [�懂分布�技术:分布�ID生�方案 ](docs/distributed/practice/�懂分布�技术:分布�ID生�方案.md ) +* [�懂分布�技术:缓存的那些事](docs/distributed/practice/�懂分布�技术:缓存的那些事.md) +* [�懂分布�技术:SpringBoot使用注解集�Redis缓存](docs/distributed/practice/�懂分布�技术:SpringBoot使用注解集�Redis缓存.md) +* [�懂分布�技术:缓存更新的套路 ](docs/distributed/practice/�懂分布�技术:缓存更新的套路.md ) +* [�懂分布�技术:浅谈分布��的几�方案 ](docs/distributed/practice/�懂分布�技术:浅谈分布��的几�方案.md ) +* [�懂分布�技术:浅�分布�事务](docs/distributed/practice/�懂分布�技术:浅�分布�事务.md ) +* [�懂分布�技术:分布�事务常用解决方案 ](docs/distributed/practice/�懂分布�技术:分布�事务常用解决方案.md ) +* [�懂分布�技术:使用RocketMQ事务消�解决分布�事务 ](docs/distributed/practice/�懂分布�技术:使用RocketMQ事务消�解决分布�事务.md ) +* [�懂分布�技术:消�队列因何而生](docs/distributed/practice/�懂分布�技术:消�队列因何而生.md) +* [�懂分布�技术:浅谈分布�消�技术Kafka](docs/distributed/practice/�懂分布�技术:浅谈分布�消�技术Kafka.md ) +* [分布�技术实践总结](docs/distributed/分布��论总结.md) + +# �试指� todo -### 校招指� +## 校招指� todo -### �� +## �� todo -## 工具 +# 工具 todo -## 资料 +# 资料 todo -### 书� +## 书� todo -## 待办 +# 待办 springboot和springcloud -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师技术学习资料:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -**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) +## Java技术江湖 +如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】 +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/Javatutorial.jpeg) diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 34ac42a..0000000 --- a/_config.yml +++ /dev/null @@ -1,2 +0,0 @@ -theme: jekyll-theme-cayman - diff --git a/backup.md b/backup.md deleted file mode 100644 index f2fb131..0000000 --- a/backup.md +++ /dev/null @@ -1,81 +0,0 @@ -## 声明 - -**关于仓库** - -本仓库是笔者在准备 2018 年秋招�习过程中的学习总结,内容以Java�端的知识总结为主,其中大部分都是笔者根�自己的�解加上个人�客总结而�的。 - -其中有少数内容�能会包�瞎XX说,语��通顺,内容�全�等�方�问题,还请�谅。 - -�篇文章都会有笔者更加详细的一系列�客�供�考,这些文章也被我�表在CSDN技术�客上,整���客专�,欢迎查看�(*`∀´*)ノ亻! - -详细内容请�我的CSDN技术�客:https://blog.csdn.net/a724888 - -**更多校招干货和技术文章请关注我的公众�:黄�斜** - -| Ⅰ | Ⅱ | Ⅲ | Ⅳ | Ⅴ | Ⅵ | Ⅶ | Ⅷ | Ⅸ | -| :----------------------------: | :--------------------------------------: | :------------------------: | :----------------------------------------: | :--------------------------: | :---------------------------------: | :-----------------------------------------: | :----------------------------------: | :--------------------------------------------: | -| 算法[:pencil2:](#算法-pencil2) | �作系统[:computer:](#�作系统-computer) | 网络[:cloud:](#网络-cloud) | 数�库[:floppy_disk:](#数�库-floppy_disk) | Java[:couple:](#Java-couple) | JavaWeb [:coffee:](#JavaWeb-coffee) | 分布� [:sweat_drops:](#分布�-sweat_drops) | 设计模�[:hammer:](#设计模�-hammer) | Hadoop[:speak_no_evil:](#Hadoop-speak_no_evil) | - -## 算法 :pencil2: - -> [剑指offer算法总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/%E5%89%91%E6%8C%87offer.md) - -> [LeetCode刷题指�](https://github.com/h2pl/Java-Tutorial/blob/master/md/LeetCode%E5%88%B7%E9%A2%98%E6%8C%87%E5%8D%97.md) - -## �作系统 :computer: - -> [�作系统学习总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93.md) - -> [Linux内核与基础命令学习总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/Linux%E5%86%85%E6%A0%B8%E4%B8%8E%E5%9F%BA%E7%A1%80%E5%91%BD%E4%BB%A4%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93.md) - -## 网络 :cloud: - -> [计算机网络学习总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93.md) - -## 数�库 :floppy_disk: - -> [Mysql原�与实践总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/Mysql%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E8%B7%B5%E6%80%BB%E7%BB%93.md) - -> [Redis原�与实践总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/Redis%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E8%B7%B5%E6%80%BB%E7%BB%93.md) - -## Java :couple: - -> [Java核心技术总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/Java%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%E6%80%BB%E7%BB%93.md) - -> [Java集�类总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/Java%E9%9B%86%E5%90%88%E7%B1%BB%E6%80%BB%E7%BB%93.md) - -> [Java并�技术总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/Java%E5%B9%B6%E5%8F%91%E6%80%BB%E7%BB%93.md) - -> [JVM原�学习总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/JVM%E6%80%BB%E7%BB%93.md) - -> [Java网络与NIO总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/Java%E7%BD%91%E7%BB%9C%E4%B8%8ENIO%E6%80%BB%E7%BB%93.md) - -## JavaWeb :coffee: - -> [JavaWeb技术学习总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/JavaWeb%E6%8A%80%E6%9C%AF%E6%80%BB%E7%BB%93.md) - -> [Spring与SpringMVC��解�](https://github.com/h2pl/Java-Tutorial/blob/master/md/Spring%E4%B8%8ESpringMVC%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E6%80%BB%E7%BB%93.md) - -## 分布� :sweat_drops: - -> [分布��论学习总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA%E6%80%BB%E7%BB%93.md) - -> [分布�技术学习总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/%E5%88%86%E5%B8%83%E5%BC%8F%E6%8A%80%E6%9C%AF%E5%AE%9E%E8%B7%B5%E6%80%BB%E7%BB%93.md) - -## 设计模� :hammer: -> [设计模�学习总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93.md) - -## Hadoop :speak_no_evil: - -> [Hadoop生�学习总结](https://github.com/h2pl/Java-Tutorial/blob/master/md/Hadoop%E7%94%9F%E6%80%81%E6%80%BB%E7%BB%93.md) - -
- -**关于转载** - -转载的时候请注明一下出处就行啦。 - -�外我这个仓库的格�模仿的是@CyC2018 大佬的仓库 - -并且其中一篇LeetCode刷题指�也是fork这�大佬而�的。我�是自己刷了一�然��微加了一些解�,站在大佬肩膀上。 - diff --git "a/docs/java/jvm/JVM\346\200\273\347\273\223.md" "b/docs/Java/JVM/JVM\346\200\273\347\273\223.md" similarity index 86% rename from "docs/java/jvm/JVM\346\200\273\347\273\223.md" rename to "docs/Java/JVM/JVM\346\200\273\347\273\223.md" index 2762768..60b89e6 100644 --- "a/docs/java/jvm/JVM\346\200\273\347\273\223.md" +++ "b/docs/Java/JVM/JVM\346\200\273\347\273\223.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [JVM介�和��](#jvm介�和��) * [JVM内存模型](#jvm内存模型) @@ -11,9 +11,7 @@ * [编译期优化和�行期优化](#编译期优化和�行期优化) * [JVM的垃圾回收](#jvm的垃圾回收) * [JVM的�优化](#jvm的�优化) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + --- @@ -153,20 +151,4 @@ g1收集器和cms的收集方�类似,但是g1将堆内存划分�了大� 在Java并�中讲述了synchronized��级�以��优化的方法,包括轻�级�,���,自旋�等。详细内容�以�考我的专�:Java并�技术指� -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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\272\357\274\232GC\350\260\203\344\274\230\346\200\235\350\267\257\344\270\216\345\270\270\347\224\250\345\267\245\345\205\267.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\272\357\274\232GC\350\260\203\344\274\230\346\200\235\350\267\257\344\270\216\345\270\270\347\224\250\345\267\245\345\205\267.md" new file mode 100644 index 0000000..ceda305 --- /dev/null +++ "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232GC\350\260\203\344\274\230\346\200\235\350\267\257\344\270\216\345\270\270\347\224\250\345\267\245\345\205\267.md" @@ -0,0 +1,838 @@ +# 目录 + +* [核心概念(Core Concepts)](#核心概念core-concepts) + * [Latency(延迟)](#latency延迟) + * [Throughput(���)](#throughput���) + * [Capacity(系统容�)](#capacity系统容�) +* [相关示例](#相关示例) + * [Tuning for Latency(调优延迟指标)](#tuning-for-latency调优延迟指标) + * [Tuning for Throughput(���调优)](#tuning-for-throughput���调优) + * [Tuning for Capacity(调优系统容�)](#tuning-for-capacity调优系统容�) + * [6\. GC 调优(工具篇) - GC�考手册](#6-gc-调优工具篇---gc�考手册) + * [JMX API](#jmx-api) + * [JVisualVM](#jvisualvm) + * [jstat](#jstat) + * [GC日志(GC logs)](#gc日志gc-logs) + * [GCViewer](#gcviewer) + * [分�器(Profilers)](#分�器profilers) + * [hprof](#hprof) + * [Java VisualVM](#java-visualvm) + * [AProf](#aprof) + * [�考文章](#�考文章) + + + +本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 +> https://github.com/h2pl/Java-Tutorial + +喜欢的�麻烦点下Star哈 + +文章首�于我的个人�客: +> www.how2playlife.com + +本文是微信公众��Java技术江湖】的《深入�解JVM虚拟机》其中一篇,本文部分内容��于网络,为了把本文主题讲得清晰�彻,也整�了很多我认为�错的技术�客内容,引用其中了一些比较好的�客文章,如有侵�,请�系作者。 +该系列�文会告诉你如何从入门到进阶,一步步地学习JVM基础知识,并上手进行JVM调优实战,JVM是�一个Java工程师必须�学习和�解的知识点,你必须�掌�其实现原�,�能更完整地了解整个Java技术体系,形�自己的知识框架。为了更好地总结和检验你的学习�果,本系列文章也会�供�个知识点对应的�试题以��考答案。 + +如果对本系列文章有什么建议,或者是有什么疑问的�,也�以关注公众��Java技术江湖】�系作者,欢迎你�与本系列�文的创作和修订。 + + + +> **说明**: +> +> **Capacity**: 性能,能力,系统容�; 文中翻译为�**系统容�**“; �为硬件�置。 + +您应该已�阅读了��的章节: + +1. 垃圾收集简介 - GC�考手册 +2. Java中的垃圾收集 - GC�考手册 +3. GC 算法(基础篇) - GC�考手册 +4. GC 算法(实现篇) - GC�考手册 + +GC调优(Tuning Garbage Collection)和其他性能调优是�样的原�。�学者�能会被 200 多个 GC�数弄得一头雾水, 然��便调整几个�试试结果,�或者修改几行代��测试。其实���照下�的步骤,就能��你的调优方�正确: + +1. 列出性能调优指标(State your performance goals) +2. 执行测试(Run tests) +3. 检查结果(Measure the results) +4. 与目标进行对比(Compare the results with the goals) +5. 如果达�到指标, 修改�置�数, 然�继续测试(go back to running tests) + +第一步, 我们需��的事情就是: 制定明确的GC性能指标。对所有性能监控和管��说, 有三个维度是通用的: + +* Latency(延迟) +* Throughput(���) +* Capacity(系统容�) + +我们先讲解基本概念,然��演示如何使用这些指标。如果您对 延迟����和系统容�等概念很熟悉, �以跳过这一�节。 + +### 核心概念(Core Concepts) + +我们先�看一家工厂的装��水线。工人在�水线将现�的组件按顺�拼接,组装�自行车。通过实地观测, 我们�现从组件进入生产线,到�一端组装�自行车需�4�时。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224347.png) +继续观察,我们还�现,此��分钟就有1辆自行车完�组装, �天24�时,一直如此。将这个模型简化, 并忽略维护窗�期�得出结论:**这��水线��时�以组装60辆自行车**。 + +> **说明**: 时间窗�/窗�期,请类比车站�票的窗�,是一段规定/�定��件事的时间段。 + +通过这两�测�方法, 就知�了生产线的相关性能信�:**延迟**与**���**: + +* 生产线的延迟:**4�时** +* 生产线的���:**60辆/�时** + +请注�, 衡�延迟的时间��根�具体需�而确定 —— 从纳秒(nanosecond)到几�年(millennia)都有�能。系统的���是�个��时间内完�的�作。�作(Operations)一般是特定系统相关的东西。在本例中,选择的时间��是�时, �作就是对自行车的组装。 + +掌�了延迟和���两个概念之�, 让我们对这个工厂�进行实际的调优。自行车的需求在一段时间内都很稳定, 生产线组装自行车有四个�时延迟, 而���在几个月以�都很稳定: 60辆/�时。�设�个销售团队�然业绩暴涨, 对自行车的需求增加了1�。客户�天需�的自行车��是 60 * 24 = 1440辆, 而是 2*1440 = 2880辆/天。��对工厂的产能�满�,想��些调整以��产能。 + +看起�总��很容易得出正确的判断, 系统的延迟没法�进行处� —— 他关注的是�天的自行车生产总�。得出这个结论以�, �若工厂资金充足, 那么应该立�采�措施, 改善���以增加产能。 + +我们很快会看到, 这家工厂有两�相�的生产线。��生产线一分钟�以组装一辆��自行车。 �以想象,�天生产的自行车数�会增加一�。达到 2880辆/天。�注�的是, �需��少自行车的装�时间 —— 从开始到结��然需� 4 �时。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224359.png) + +巧�的是,这样进行的性能优化,�时增加了���和产能。一般�说,我们会先测�当�的系统性能, �设定新目标, �优化系统的�个方��满足性能指标。 + +在这里�了一个很��的决定 —— �增加���,而�是��延迟。在增加���的�时, 也需�增加系统容�。比起原�的情况, 现在需�两��水线�生产出所需的自行车。在这�情况下, 增加系统的���并�是�费的, 需�水平扩展, 以满足增加的���需求。 + +在处�性能问题时, 应该考虑到还有�一�看似�相关的解决办法。�如生产线的延迟从1分钟�低为30秒,那么����样�以增长 1 �。 + +或者是�低延迟, 或者是客户�常有钱。软件工程里有一�相似的说法 —— �个性能问题背�,总有两���的解决办法。 �以用更多的机器, 或者是花精力�改善性能低下的代�。 + +#### Latency(延迟) + +GC的延迟指标由一般的延迟需求决定。延迟指标通常如下所述: + +* 所有交易必须在10秒内得到�应 +* 90%的订�付款�作必须在3秒以内处�完� +* 推�商�必须在 100 ms 内展示到用户�� + +�对这类性能指标时, 需�确�在交易过程中, GC暂��能�用太多时间,�则就满足�了指标。“�能�用太多� 的��需�视具体情况而定, 还�考虑到其他因素, 比如外部数��的交互时间(round-trips), �竞争(lock contention), 以�其他的安全点等等。 + +�设性能需求为:`90%`的交易�在`1000ms`以内完�, �次交易最长�能超过`10秒`。 根��验, �设GC暂�时间比例�能超过10%。 也就是说, 90%的GC暂�必须在`100ms`内结�, 也�能有超过`1000ms`的GC暂�。为简�起�, 我们忽略在�一次交易过程中�生多次GC�顿的�能性。 + +有了正�的需求,下一步就是检查暂�时间。有许多工具�以使用, 在接下�的6\. GC 调优(工具篇) + + + +``` +2015-06-04T13:34:16.974-0200: 2.578: [Full GC (Ergonomics) + [PSYoungGen: 93677K->70109K(254976K)] + [ParOldGen: 499597K->511230K(761856K)] + 593275K->581339K(1016832K), + [Metaspace: 2936K->2936K(1056768K)] + , 0.0713174 secs] + [Times: user=0.21 sys=0.02, real=0.07 secs +``` + + + +这表示一次GC暂�, 在`2015-06-04T13:34:16`这个时刻触�. 对应于JVM�动之�的`2,578 ms`。 + +此事件将应用线程暂�了`0.0713174`秒。虽然花费的总时间为 210 ms, 但因为是多核CPU机器, 所以最��的数字是应用线程被暂�的总时间, 这里使用的是并行GC, 所以暂�时间大约为`70ms`。 这次GC的暂�时间�于`100ms`的阈值,满足需求。 + +继续分�, 从所有GC日志中��出暂�相关的数�, 汇总之�就�以得知是�满足需求。 + +#### Throughput(���) + +���和延迟指标有很大区别。当然两者都是根�一般���需求而得出的。一般���需求(Generic requirements for throughput) 类似这样: + +* 解决方案�天必须处� 100万个订� +* 解决方案必须支�1000个登录用户,�时在5-10秒内执行�个�作: A�B或C +* �周对所有客户进行统计, 时间�能超过6�时,时间窗�为�周日晚12点到次日6点之间。 + +�以看出,���需求�是针对�个�作的, 而是在给定的时间内, 系统必须完�多少个�作。和延迟需求类似, GC调优也需�确定GC行为所消耗的总时间。�个系统能接�的时间��, 一般�说, GC�用的总时间比�能超过`10%`。 + +现在�设需求为: �分钟处� 1000 笔交易。�时, �分钟GC暂�的总时间�能超过6秒(�10%)。 + +有了正�的需求, 下一步就是获�相关的信�。�然是从GC日志中��数�, �以看到类似这样的信�: + + + +``` +2015-06-04T13:34:16.974-0200: 2.578: [Full GC (Ergonomics) + [PSYoungGen: 93677K->70109K(254976K)] + [ParOldGen: 499597K->511230K(761856K)] + 593275K->581339K(1016832K), + [Metaspace: 2936K->2936K(1056768K)], + 0.0713174 secs] + [Times: user=0.21 sys=0.02, real=0.07 secs +``` + + + +此时我们对 用户耗时(user)和系统耗时(sys)感兴趣, 而�关心实际耗时(real)。在这里, 我们关心的时间为`0.23s`(user + sys = 0.21 + 0.02 s), 这段时间内, GC暂��用了 cpu 资�。 ��的是, 系统�行在多核机器上, 转�为实际的�顿时间(stop-the-world)为`0.0713174秒`, 下�的计算会用到这个数字。 + +��出有用的信��, 剩下��的就是统计�分钟内GC暂�的总时间。看看是�满足需求: �分钟内总的暂�时间�得超过6000毫秒(6秒)。 + +#### Capacity(系统容�) + +系统容�(Capacity)需求,是在达����和延迟指标的情况下,对硬件环境的�外约�。这类需求大多是��于计算资�或者预算方�的原因。例如: + +* 系统必须能部署到�于512 MB内存的Android设备上 +* 系统必须部署在Amazon**EC2**实例上, �置�得超过**c3.xlarge(4核8GB)**。 +* �月的 Amazon EC2 账��得超过`$12,000` + +因此, 在满足延迟和���需求的基础上必须考虑系统容�。�以说, �若有无�的计算资��供挥�, 那么任何 延迟和���指标 都��问题, 但现实情况是, 预算(budget)和其他约��制了�用的资�。 + +### 相关示例 + +介�完性能调优的三个维度�, 我们�进行实际的�作以达�GC性能指标。 + +请看下�的代�: + + + +``` +//imports skipped for brevity +public class Producer implements Runnable { + + private static ScheduledExecutorService executorService + = Executors.newScheduledThreadPool(2); + + private Deque deque; + private int objectSize; + private int queueSize; + + public Producer(int objectSize, int ttl) { + this.deque = new ArrayDeque(); + this.objectSize = objectSize; + this.queueSize = ttl * 1000; + } + + @Override + public void run() { + for (int i = 0; i < 100; i++) { + deque.add(new byte[objectSize]); + if (deque.size() > queueSize) { + deque.poll(); + } + } + } + + public static void main(String[] args) + throws InterruptedException { + executorService.scheduleAtFixedRate( + new Producer(200 * 1024 * 1024 / 1000, 5), + 0, 100, TimeUnit.MILLISECONDS + ); + executorService.scheduleAtFixedRate( + new Producer(50 * 1024 * 1024 / 1000, 120), + 0, 100, TimeUnit.MILLISECONDS); + TimeUnit.MINUTES.sleep(10); + executorService.shutdownNow(); + } +} +``` + + + +这段程�代�, � 100毫秒 �交两个作业(job)�。�个作业都模拟特定的生命周期: 创建对象, 然�在预定的时间释放, 接�就�管了, 由GC�自动回收�用的内存。 + +在�行这个示例程�时,通过以下JVM�数打开GC日志记录: + + + +``` +-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps +``` + + + +还应该加上JVM�数`-Xloggc`以指定GC日志的存储�置,类似这样: + + + +``` +-Xloggc:C:\\Producer_gc.log +``` + + + +* 1 +* 2 + +在日志文件中�以看到GC的行为, 类似下�这样: + + + +``` +2015-06-04T13:34:16.119-0200: 1.723: [GC (Allocation Failure) + [PSYoungGen: 114016K->73191K(234496K)] + 421540K->421269K(745984K), + 0.0858176 secs] + [Times: user=0.04 sys=0.06, real=0.09 secs] + +2015-06-04T13:34:16.738-0200: 2.342: [GC (Allocation Failure) + [PSYoungGen: 234462K->93677K(254976K)] + 582540K->593275K(766464K), + 0.2357086 secs] + [Times: user=0.11 sys=0.14, real=0.24 secs] + +2015-06-04T13:34:16.974-0200: 2.578: [Full GC (Ergonomics) + [PSYoungGen: 93677K->70109K(254976K)] + [ParOldGen: 499597K->511230K(761856K)] + 593275K->581339K(1016832K), + [Metaspace: 2936K->2936K(1056768K)], + 0.0713174 secs] + [Times: user=0.21 sys=0.02, real=0.07 secs] +``` + + + +基于日志中的信�, �以通过三个优化目标���性能: + +1. 确�最�情况下,GC暂�时间�超过预定阀值 +2. 确�线程暂�的总时间�超过预定阀值 +3. 在确�达到延迟和���指标的情况下, �低硬件�置以��本。 + +为此, 用三���的�置, 将代��行10分钟, 得到了三���的结果, 汇总如下: + + +| **堆内存大�(Heap)** | **GC算法(GC Algorithm)** | **有效时间比(Useful work)** | **最长�顿时间(Longest pause)** | +| -Xmx12g | -XX:+UseConcMarkSweepGC | 89.8% | **560 ms** | +| -Xmx12g | -XX:+UseParallelGC | 91.5% | 1,104 ms | +| -Xmx8g | -XX:+UseConcMarkSweepGC | 66.3% | 1,610 ms | + +使用��的GC算法,和��的内存�置,�行相�的代�, 以测�GC暂�时间与 延迟����的关系。实验的细节和结果在��章节详细介�。 + +注�, 为了尽�简�, 示例中�改�了很少的输入�数, 此实验也没有在��CPU数�或者��的堆布局下进行测试。 + +#### Tuning for Latency(调优延迟指标) + +�设有一个需求,**�次作业必须在 1000ms 内处�完�**。我们知�, 实际的作业处��需�100 ms,简化�, 两者相�就�以算出对 GC暂�的延迟�求。现在需求��:**GC暂��能超过900ms**。这个问题很容易找到答案, �需�解�GC日志文件, 并找出GC暂�中最大的那个暂�时间��。 + +��看测试所用的三个�置: + + +| **堆内存大�(Heap)** | **GC算法(GC Algorithm)** | **有效时间比(Useful work)** | **最长�顿时间(Longest pause)** | +| -Xmx12g | -XX:+UseConcMarkSweepGC | 89.8% | **560 ms** | +| -Xmx12g | -XX:+UseParallelGC | 91.5% | 1,104 ms | +| -Xmx8g | -XX:+UseConcMarkSweepGC | 66.3% | 1,610 ms | + +�以看到,其中有一个�置达到了�求。�行的�数为: + + + +``` +java -Xmx12g -XX:+UseConcMarkSweepGC Producer +``` + + + +对应的GC日志中,暂�时间最大为`560 ms`, 这达到了延迟指标`900 ms`的�求。如果还满足���和系统容�需求的�,就�以说�功达�了GC调优目标, 调优结�。 + +#### Tuning for Throughput(���调优) + +�定���指标为:**��时完� 1300万次�作处�**。�样是上�的�置, 其中有一��置满足了需求: + + +| **堆内存大�(Heap)** | **GC算法(GC Algorithm)** | **有效时间比(Useful work)** | **最长�顿时间(Longest pause)** | +| -Xmx12g | -XX:+UseConcMarkSweepGC | 89.8% | 560 ms | +| -Xmx12g | -XX:+UseParallelGC | **91.5%** | 1,104 ms | +| -Xmx8g | -XX:+UseConcMarkSweepGC | 66.3% | 1,610 ms | + +此�置对应的命令行�数为: + + + +``` +java -Xmx12g -XX:+UseParallelGC Producer +``` + + + +* �以看到,GC�用了 8.5%的CPU时间,剩下的`91.5%`是有效的计算时间。为简�起�, 忽略示例中的其他安全点。现在需�考虑: + +1. �个CPU核心处�一次作业需�耗时`100ms` +2. 因此, 一分钟内�个核心�以执行 60,000 次�作(**�个job完�100次�作**) +3. 一�时内, 一个核心�以执行 360万次�作 +4. 有四个CPU内核, 则��时�以执行: 4 x 3.6M = 1440万次�作 + +�论上,通过简�的计算就�以得出结论, ��时�以执行的�作数为:`14.4 M * 91.5% = 13,176,000`次, 满足需求。 + +值得一�的是, �若还�满足延迟指标, 那就有问题了, 最�情况下, GC暂�时间为`1,104 ms`, 最大延迟时间是�一��置的两�。 + +#### Tuning for Capacity(调优系统容�) + +�设需�将软件部署到�务器上(commodity-class hardware), �置为`4核10G`。这样的�, 系统容�的�求就��: 最大的堆内存空间�能超过`8GB`。有了这个需求, 我们需�调整为第三套�置进行测试: + + +| **堆内存大�(Heap)** | **GC算法(GC Algorithm)** | **有效时间比(Useful work)** | **最长�顿时间(Longest pause)** | +| -Xmx12g | -XX:+UseConcMarkSweepGC | 89.8% | 560 ms | +| -Xmx12g | -XX:+UseParallelGC | 91.5% | 1,104 ms | +| **-Xmx8g** | -XX:+UseConcMarkSweepGC | 66.3% | 1,610 ms | + +程��以通过如下�数执行: + + + +``` +java -Xmx8g -XX:+UseConcMarkSweepGC Producer +``` + + + +* 测试结果是延迟大幅增长, ����样大幅�低: +* 现在,GC�用了更多的CPU资�, 这个�置�有`66.3%`的有效CPU时间。因此,这个�置让���从最好的情况**13,176,000 �作/�时**下�到**�足 9,547,200次�作/�时**. +* 最�情况下的延迟��了**1,610 ms**, 而��是**560ms**。 + +通过对这三个维度的介�, 你应该了解, �是简�的进行“性能(performance)�优化, 而是需�从三���的维度�进行考虑, 测�, 并调优延迟和���, 此外还需�考虑系统容�的约�。 + +请继续阅读下一章:6\. GC 调优(工具篇) - GC�考手册 + +原文链接:[GC Tuning: Basics](https://plumbr.eu/handbook/gc-tuning) + +翻译时间: 2016年02月06日 + +## 6\. GC 调优(工具篇) - GC�考手册 + +2017年02月23日 18:56:02 + +阅读数:6469 + +进行GC性能调优时, 需�明确了解, 当�的GC行为对系统和用户有多大的影�。有多�监控GC的工具和方法, 本章将�一介�常用的工具。 + +您应该已�阅读了��的章节: + +1. 垃圾收集简介 - GC�考手册 +2. Java中的垃圾收集 - GC�考手册 +3. GC 算法(基础篇) - GC�考手册 +4. GC 算法(实现篇) - GC�考手册 +5. GC 调优(基础篇) - GC�考手册 + +JVM 在程�执行的过程中, �供了GC行为的原生数�。那么, 我们就�以利用这些原生数��生���报告。原生数�(_raw data_) 包括: + +* �个内存池的当�使用情况, +* �个内存池的总容�, +* �次GC暂�的�续时间, +* GC暂�在�个阶段的�续时间。 + +�以通过这些数�算出��指标, 例如: 程�的内存分�率, ��率等等。本章主�介�如何获�原生数�。 �续的章节将对��的派生指标(derived metrics)展开讨论, 并引入GC性能相关的�题。 + +## JMX API + +从 JVM �行时获�GC行为数�, 最简�的办法是使用标准[JMX API 接�](https://docs.oracle.com/javase/tutorial/jmx/index.html). JMX是获� JVM内部�行时状�信� 的标准API. �以编写程�代�, 通过 JMX API �访问本程�所在的JVM,也�以通过JMX客户端执行(远程)访问。 + +最常�的 JMX客户端是[JConsole](http://docs.oracle.com/javase/7/docs/technotes/guides/management/jconsole.html)和[JVisualVM](http://docs.oracle.com/javase/7/docs/technotes/tools/share/jvisualvm.html)(�以安装���件,�分强大)。两个工具都是标准JDK的一部分, 而且很容易使用. 如果使用的是 JDK 7u40 �更高版本, 还�以使用�一个工具:[Java Mission Control](http://www.oracle.com/technetwork/java/javaseproducts/mission-control/java-mission-control-1998576.html)( 大致翻译为 Java控制中心,`jmc.exe`)。 + +> JVisualVM安装MBeans�件的步骤: 通过 工具(T) – �件(G) – �用�件 – 勾选VisualVM-MBeans – 安装 – 下一步 – 等待安装完�…… 其他�件的安装过程基本一致。 + +所有 JMX客户端都是独立的程�,�以连接到目标JVM上。目标JVM�以在本机, 也�能是远端JVM. 如果�连接远端JVM, 则目标JVM�动时必须指定特定的环境��,以开�远程JMX连接/以�端��。 示例如下: + + + +``` +java -Dcom.sun.management.jmxremote.port=5432 com.yourcompany.YourApp +``` + + + +在此处, JVM 打开端�`5432`以支�JMX连接。 + +通过 JVisualVM 连接到�个JVM以�, 切�到 MBeans 标签, 展开 “java.lang/GarbageCollector� . 就�以看到GC行为信�, 下图是 JVisualVM 中的截图: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224430.png) + +下图是Java Mission Control 中的截图: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224439.png) + +从以上截图中�以看到两款垃圾收集器。其中一款负责清�年轻代(**PS Scavenge**),�一款负责清��年代(**PS MarkSweep**); 列表中显示的就是垃圾收集器的�称。�以看到 , jmc 的功能和展示数�的方�更强大。 + +对所有的垃圾收集器, 通过 JMX API 获�的信�包括: + +* **CollectionCount**: 垃圾收集器执行的GC总次数, +* **CollectionTime**: 收集器�行时间的累计。这个值等于所有GC事件�续时间的总和, +* **LastGcInfo**: 最近一次GC事件的详细信�。包括 GC事件的�续时间(duration), 开始时间(startTime) 和 结�时间(endTime), 以��个内存池在最近一次GC之�和之�的使用情况, +* **MemoryPoolNames**: �个内存池的�称, +* **Name**: 垃圾收集器的�称 +* **ObjectName**: 由JMX规范定义的 MBean的�字,, +* **Valid**: 此收集器是�有效。本人��过 “`true`“的情况 (^_^) + +根��验, 这些信�对GC的性能�说,�能得出什么结论. �有编写程�, 获�GC相关的 JMX 信��进行统计和分�。 在下文�以看到, 一般也�怎么关注 MBean , 但 MBean 对于�解GC的原�倒是挺有用的。 + +## JVisualVM + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224451.png) + +Visual GC �件常用�监控本机�行的Java程�, 比如开�者和性能调优专家�常会使用此�件, 以快速获�程��行时的GC信�。 + +![06_03_jvmsualvm-garbage-collection-monitoring.png](https://s4.51cto.com/images/blog/202106/25/eabd68ba262d004c4919475f00d8ec9c.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk= "06_03_jvmsualvm-garbage-collection-monitoring.png") + +左侧的图表展示了�个内存池的使用情况: Metaspace/永久代, �年代, Eden区以�两个存活区。 + +在�边, 顶部的两个图表与 GC无关, 显示的是 JIT编译时间 和 类加载时间。下�的6个图显示的是内存池的历�记录, �个内存池的GC次数,GC总时间, 以�最大值,峰值, 当�使用情况。 + +�下�是 HistoGram, 显示了年轻代对象的年龄分布。至于对象的年龄监控(objects tenuring monitoring), 本章�进行讲解。 + +与纯粹的JMX工具相比, VisualGC �件�供了更�好的界�, 如果没有其他�手的工具, 请选择VisualGC. 本章接下�会介�其他工具, 这些工具�以�供更多的信�, 以�更好的视角. 当然, 在“Profilers(分�器)�一节中,也会介� JVisualVM 的适用场景 —— 如: 分�分�(allocation profiling), 所以我们��会贬低哪一款工具, 关键还得看实际情况。 + +## jstat + +[jstat](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html)也是标准JDK�供的一款监控工具(Java Virtual Machine statistics monitoring tool),�以统计��指标。既�以连接到本地JVM,也�以连到远程JVM. 查看支�的指标和对应选项�以执行 “`jstat -options`� 。例如: + + + +``` ++-----------------+---------------------------------------------------------------+ +| Option | Displays... | ++-----------------+---------------------------------------------------------------+ +|class | Statistics on the behavior of the class loader | +|compiler | Statistics on the behavior of the HotSpot Just-In-Time com- | +| | piler | +|gc | Statistics on the behavior of the garbage collected heap | +|gccapacity | Statistics of the capacities of the generations and their | +| | corresponding spaces. | +|gccause | Summary of garbage collection statistics (same as -gcutil), | +| | with the cause of the last and current (if applicable) | +| | garbage collection events. | +|gcnew | Statistics of the behavior of the new generation. | +|gcnewcapacity | Statistics of the sizes of the new generations and its corre- | +| | sponding spaces. | +|gcold | Statistics of the behavior of the old and permanent genera- | +| | tions. | +|gcoldcapacity | Statistics of the sizes of the old generation. | +|gcpermcapacity | Statistics of the sizes of the permanent generation. | +|gcutil | Summary of garbage collection statistics. | +|printcompilation | Summary of garbage collection statistics. | ++-----------------+---------------------------------------------------------------+ +``` + + + +* jstat 对于快速确定GC行为是��康�常有用。�动方�为: “`jstat -gc -t PID 1s`� , 其中,PID 就是�监视的Java进程ID。�以通过`jps`命令查看正在�行的Java进程列表。 + + + +``` +jps + +jstat -gc -t 2428 1s +``` + + + +以上命令的结果, 是 jstat �秒�标准输出输出一行新内容, 比如: + + + +``` +Timestamp S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT +200.0 8448.0 8448.0 8448.0 0.0 67712.0 67712.0 169344.0 169344.0 21248.0 20534.3 3072.0 2807.7 34 0.720 658 133.684 134.404 +201.0 8448.0 8448.0 8448.0 0.0 67712.0 67712.0 169344.0 169343.2 21248.0 20534.3 3072.0 2807.7 34 0.720 662 134.712 135.432 +202.0 8448.0 8448.0 8102.5 0.0 67712.0 67598.5 169344.0 169343.6 21248.0 20534.3 3072.0 2807.7 34 0.720 667 135.840 136.559 +203.0 8448.0 8448.0 8126.3 0.0 67712.0 67702.2 169344.0 169343.6 21248.0 20547.2 3072.0 2807.7 34 0.720 669 136.178 136.898 +204.0 8448.0 8448.0 8126.3 0.0 67712.0 67702.2 169344.0 169343.6 21248.0 20547.2 3072.0 2807.7 34 0.720 669 136.178 136.898 +205.0 8448.0 8448.0 8134.6 0.0 67712.0 67712.0 169344.0 169343.5 21248.0 20547.2 3072.0 2807.7 34 0.720 671 136.234 136.954 +206.0 8448.0 8448.0 8134.6 0.0 67712.0 67712.0 169344.0 169343.5 21248.0 20547.2 3072.0 2807.7 34 0.720 671 136.234 136.954 +207.0 8448.0 8448.0 8154.8 0.0 67712.0 67712.0 169344.0 169343.5 21248.0 20547.2 3072.0 2807.7 34 0.720 673 136.289 137.009 +208.0 8448.0 8448.0 8154.8 0.0 67712.0 67712.0 169344.0 169343.5 21248.0 20547.2 3072.0 2807.7 34 0.720 673 136.289 137.009 +``` + + + +�微解释一下上�的内容。�考[jstat manpage](http://www.manpagez.com/man/1/jstat/), 我们�以知�: + +* jstat 连接到 JVM 的时间, 是JVM�动�的 200秒。此信�从第一行的 “**Timestamp**� 列得知。继续看下一行, jstat �秒钟从JVM 接收一次信�, 也就是命令行�数中 “`1s`� 的�义。 +* 从第一行的 “**YGC**� 列得知年轻代共执行了34次GC, 由 “**FGC**� 列得知整个堆内存已�执行了 658次 full GC。 +* 年轻代的GC耗时总共为`0.720 秒`, 显示在“**YGCT**� 这一列。 +* Full GC 的总计耗时为`133.684 秒`, 由“**FGCT**�列得知。 这立马就�引了我们的目光, 总的JVM �行时间�有 200 秒,**但其中有 66% 的部分被 Full GC 消耗了**。 + +�看下一行, 问题就更明显了。 + +* 在接下�的一秒内共执行了 4 次 Full GC。�� “**FGC**� 列. +* 这4次 Full GC 暂��用了差�多 1秒的时间(根�**FGCT**列的差得知)。与第一行相比, Full GC 耗费了`928 毫秒`, �`92.8%`的时间。 +* 根� “**OC**和 “**OU**� 列得知,**整个�年代的空间**为`169,344.0 KB`(“OC“), 在 4 次 Full GC ��然�用了`169,344.2 KB`(“OU“)。用了`928ms`的时间��释放了 800 字节的内存, 怎么看都觉得很�正常。 + +�看这两行的内容, 就知�程�出了很严�的问题。继续分�下一行, �以确定问题�然存在,而且�得更糟。 + +JVM几乎完全��了(stalled), 因为GC�用了90%以上的计算资�。GC之�, 所有的�代空间�然还在�用。事实上, 程�在一分钟以�就挂了, 抛出了 “[java.lang.OutOfMemoryError: GC overhead limit exceeded](https://plumbr.eu/outofmemoryerror/gc-overhead-limit-exceeded)� 错误。 + +�以看到, 通过 jstat 能很快�现对JVM�康�为�利的GC行为。一般�说, �看 jstat 的输出就能快速�现以下问题: + +* 最�一列 “**GCT**�, 与JVM的总�行时间 “**Timestamp**� 的比值, 就是GC 的开销。如果�一秒内, “**GCT**� 的值都会明显增大, 与总�行时间相比, 就暴露出GC开销过大的问题. ��系统对GC开销有��的容�度, 由性能需求决定, 一般�讲, 超过`10%`的GC开销都是有问题的。 +* “**YGC**� 和 “**FGC**� 列的快速�化往往也是有问题的�兆。频�的GC暂�会累积,并导致更多的线程�顿(stop-the-world pauses), 进而影����。 +* 如果看到 “**OU**� 列中,�年代的使用�约等于�年代的最大容�(**OC**), 并且��低的�, 就表示虽然执行了�年代GC, 但基本上属于无效GC。 + +## GC日志(GC logs) + +通过日志内容也�以得到GC相关的信�。因为GC日志模�内置于JVM中, 所以日志中包�了对GC活动最全�的�述。 这就是事实上的标准, �作为GC性能评估和优化的最真实数���。 + +GC日志一般输出到文件之中, 是纯 text 格�的, 当然也�以打�到控制�。有多个�以控制GC日志的JVM�数。例如,�以打��次GC的�续时间, 以�程�暂�时间(`-XX:+PrintGCApplicationStoppedTime`), 还有GC清�了多少引用类型(`-XX:+PrintReferenceGC`)。 + +�打�GC日志, 需�在�动脚本中指定以下�数: + + + +``` +-XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc: +``` + + + +以上�数指示JVM: 将所有GC事件打�到日志文件中, 输出�次GC的日期和时间戳。��GC算法输出的内容略有��. ParallelGC 输出的日志类似这样: + + + +``` +199.879: [Full GC (Ergonomics) [PSYoungGen: 64000K->63998K(74240K)] [ParOldGen: 169318K->169318K(169472K)] 233318K->233317K(243712K), [Metaspace: 20427K->20427K(1067008K)], 0.1473386 secs] [Times: user=0.43 sys=0.01, real=0.15 secs] +200.027: [Full GC (Ergonomics) [PSYoungGen: 64000K->63998K(74240K)] [ParOldGen: 169318K->169318K(169472K)] 233318K->233317K(243712K), [Metaspace: 20427K->20427K(1067008K)], 0.1567794 secs] [Times: user=0.41 sys=0.00, real=0.16 secs] +200.184: [Full GC (Ergonomics) [PSYoungGen: 64000K->63998K(74240K)] [ParOldGen: 169318K->169318K(169472K)] 233318K->233317K(243712K), [Metaspace: 20427K->20427K(1067008K)], 0.1621946 secs] [Times: user=0.43 sys=0.00, real=0.16 secs] +200.346: [Full GC (Ergonomics) [PSYoungGen: 64000K->63998K(74240K)] [ParOldGen: 169318K->169318K(169472K)] 233318K->233317K(243712K), [Metaspace: 20427K->20427K(1067008K)], 0.1547695 secs] [Times: user=0.41 sys=0.00, real=0.15 secs] +200.502: [Full GC (Ergonomics) [PSYoungGen: 64000K->63999K(74240K)] [ParOldGen: 169318K->169318K(169472K)] 233318K->233317K(243712K), [Metaspace: 20427K->20427K(1067008K)], 0.1563071 secs] [Times: user=0.42 sys=0.01, real=0.16 secs] +200.659: [Full GC (Ergonomics) [PSYoungGen: 64000K->63999K(74240K)] [ParOldGen: 169318K->169318K(169472K)] 233318K->233317K(243712K), [Metaspace: 20427K->20427K(1067008K)], 0.1538778 secs] [Times: user=0.42 sys=0.00, real=0.16 secs] +``` + + + +在 “04\. GC算法:实现篇� 中详细介�了这些格�, 如果对此�了解, �以先阅读该章节。 + +分�以上日志内容, �以得知: + +* 这部分日志截�自JVM�动�200秒左�。 +* 日志片段中显示, 在`780毫秒`以内, 因为垃圾回收 导致了5次 Full GC 暂�(去掉第六次暂�,这样更精确一些)。 +* 这些暂�事件的总�续时间是`777毫秒`, �总�行时间的**99.6%**。 +* 在GC完�之�, 几乎所有的�年代空间(`169,472 KB`)�然被�用(`169,318 KB`)。 + +通过日志信��以确定, 该应用的GC情况�常糟糕。JVM几乎完全�滞, 因为GC�用了超过`99%`的CPU时间。 而GC的结果是, �年代空间�然被�满, 这进一步肯定了我们的结论。 示例程�和jstat �节中的是�一个, 几分钟之�系统就挂了, 抛出 “[java.lang.OutOfMemoryError: GC overhead limit exceeded](https://plumbr.eu/outofmemoryerror/gc-overhead-limit-exceeded)� 错误, �用说, 问题是很严�的. + +从此示例�以看出, GC日志对监控GC行为和JVM是�处于�康状��常有用。一般情况下, 查看 GC 日志就�以快速确定以下症状: + +* GC开销太大。如果GC暂�的总时间很长, 就会�害系统的���。��的系统�许��比例的GC开销, 但一般认为, 正常范围在`10%`以内。 +* �个别的GC事件暂�时间过长。当�次GC暂�时间太长, 就会影�系统的延迟指标. 如果延迟指标规定交易必须在`1,000 ms`内完�, 那就�能容�任何超过`1000毫秒`的GC暂�。 +* �年代的使用�超过�制。如果�年代空间在 Full GC 之��然接近全满, 那么GC就�为了性能瓶颈, �能是内存太�, 也�能是存在内存泄�。这�症状会让GC的开销暴增。 + +�以看到,GC日志中的信��常详细。但除了这些简�的�程�, 生产系统一般都会生�大�的GC日志, 纯�人工是很难阅读和进行解�的。 + +## GCViewer + +我们�以自己编写解�器, �将庞大的GC日志解�为直观易读的图形信�。 但很多时候自己写程�也�是个好办法, 因为��GC算法的��性, 导致日志信�格�互相之间�太兼容。那么神器�了:[GCViewer](https://github.com/chewiebug/GCViewer)。 + +[GCViewer](https://github.com/chewiebug/GCViewer)是一款开�的GC日志分�工具。项目的 GitHub 主页对�项指标进行了完整的�述. 下�我们介�最常用的一些指标。 + +第一步是获�GC日志文件。这些日志文件�能够�映系统在性能调优时的具体场景. �若��部门(operational department)�馈: �周五下�,系统就�行缓慢, �管GC是�是主�原因, 分�周一早晨的日志是没有多少�义的。 + +获�到日志文件之�, 就�以用 GCViewer 进行分�, 大致会看到类似下�的图形界�: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224526.png) + +使用的命令行大致如下: + + + +``` +java -jar gcviewer_1.3.4.jar gc.log +``` + + + +当然, 如果�想打开程�界�,也�以在��加上其他�数,直接将分�结果输出到文件。 + +命令大致如下: + + + +``` +java -jar gcviewer_1.3.4.jar gc.log summary.csv chart.png +``` + + + +以上命令将信�汇总到当�目录下的 Excel 文件`summary.csv`之中, 将图形信��存为`chart.png`文件。 + +点击下载:gcviewer的jar包�使用示例 + +上图中, Chart 区域是对GC事件的图形化展示。包括�个内存池的大�和GC事件。上图中, �有两个�视化指标: �色线�表示堆内存的使用情况, 黑色的Bar则表示�次GC暂�时间的长短。 + +从图中�以看到, 内存使用�增长很快。一分钟左�就达到了堆内存的最大值. 堆内存几乎全部被消耗, �能顺利分�新对象, 并引�频�的 Full GC 事件. 这说明程��能存在内存泄露, 或者�动时指定的内存空间�足。 + +从图中还�以看到 GC暂�的频率和�续时间。`30秒`之�, GC几乎�间断地�行,最长的暂�时间超过`1.4秒`。 + +在�边有三个选项�。“`**Summary**`(摘�)� 中比较有用的是 “`Throughput`�(���百分比) 和 “`Number of GC pauses`�(GC暂�的次数), 以�“`Number of full GC pauses`�(Full GC 暂�的次数). ���显示了有效工作的时间比例, 剩下的部分就是GC的消耗。 + +以上示例中的���为`**6.28%**`。这�味�有`**93.72%**` + +下一个有��的地方是“**Pause**�(暂�)选项�: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224540.png) + +“`Pause`� 展示了GC暂�的总时间,平�值,最�值和最大值, 并且将 total 与minor/major 暂�分开统计。如果�优化程�的延迟指标, 这些统计�以很快判断出暂�时间是�过长。�外, 我们�以得出明确的信�: 累计暂�时间为`634.59 秒`, GC暂�的总次数为`3,938 次`, 这在`11分钟/660秒`的总�行时间里那�是一般的高。 + +更详细的GC暂�汇总信�, 请查看主界�中的 “**Event details**� 标签: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224553.png) + +从“**Event details**� 标签中, �以看到日志中所有��的GC事件汇总:`普通GC�顿`和`Full GC �顿次数`, 以�`并�执行数`,`� stop-the-world 事件`等。此示例中, �以看到一个明显的地方, Full GC 暂�严�影�了���和延迟, ��是:`3,928 次 Full GC`, 暂�了`634秒`。 + +�以看到, GCViewer 能用图形界�快速展现异常的GC行为。一般�说, 图�化信�能迅速�示以下症状: + +* 低���。当应用的���下�到�能容�的地步时, 有用工作的总时间就大��少. 具体有多大的 “容�度�(tolerable) �决于具体场景。按照�验, 低于 90% 的有效时间就值得警惕了, �能需�好好优化下GC。 +* �次GC的暂�时间过长。��有一次GC�顿时间过长,就会影�程�的延迟指标. 例如, 延迟需求规定必须在 1000 ms以内完�交易, 那就�能容�任何一次GC暂�超过1000毫秒。 +* 堆内存使用率过高。如果�年代空间在 Full GC 之��然接近全满, 程�性能就会大幅�低, �能是资��足或者内存泄�。这�症状会对���产生严�影�。 + +业界良心 —— 图形化展示的GC日志信��对是我们�磅推�的。�用去阅读冗长而���的GC日志,通过容易�解的图形, 也�以得到�样的信�。 + +## 分�器(Profilers) + +下�介�分�器([profilers](http://zeroturnaround.com/rebellabs/developer-productivity-report-2015-java-performance-survey-results/3/), Oracle官方翻译是:`抽样器`)。相对于��的工具, 分�器�关心GC中的一部分领域. 本节我们也�关注分�器相关的GC功能。 + +首先警告 —— ��认为分�器适用于所有的场景。分�器有时确实作用很大, 比如检测代�中的CPU热点时。但�些情况使用分�器�一定是个好方案。 + +对GC调优�说也是一样的。�检测是�因为GC而引起延迟或���问题时, �需�使用分�器. ���到的工具(`jstat`或 原生/�视化GC日志)就能更好更快地检测出是�存在GC问题. 特别是从生产环境中收集性能数�时, 最好��使用分�器, 因为性能开销�常大。 + +如果确实需�对GC进行优化, 那么分�器就�以派上用场了, �以对 Object 的创建信�一目了然. �个角度看, 如果GC暂�的原因�在�个内存池中, 那就�会是因为创建对象太多了。 所有分�器都能够跟踪对象分�(via allocation profiling), 根�内存分�的轨迹, 让你知�**实际驻留在内存中的是哪些对象**。 + +分�分�能定�到在哪个地方创建了大�的对象. 使用分�器辅助进行GC调优的好处是, 能确定哪�类型的对象最�用内存, 以�哪些线程创建了最多的对象。 + +下�我们通过实例介�3�分�分�器:`**hprof**`,`**JVisualV**`**M**和`**AProf**`。实际上还有很多分�器�供选择, 有商业产�,也有�费工具, 但其功能和应用基本上都是类似的。 + +### hprof + +[hprof 分�器](http://docs.oracle.com/javase/8/docs/technotes/samples/hprof.html)内置于JDK之中。 在��环境下都�以使用, 一般优先使用这款工具。 + +�让`hprof`和程�一起�行, 需�修改�动脚本, 类似这样: + + + +``` +java -agentlib:hprof=heap=sites com.yourcompany.YourApplication +``` + + + +在程�退出时,会将分�信�dump(转储)到工作目录下的`java.hprof.txt`文件中。使用文本编辑器打开, 并�索 “**SITES BEGIN**� 关键字, �以看到: + + + +``` +SITES BEGIN (ordered by live bytes) Tue Dec 8 11:16:15 2015 + percent live alloc'ed stack class + rank self accum bytes objs bytes objs trace name + 1 64.43% 4.43% 8370336 20121 27513408 66138 302116 int[] + 2 3.26% 88.49% 482976 20124 1587696 66154 302104 java.util.ArrayList + 3 1.76% 88.74% 241704 20121 1587312 66138 302115 eu.plumbr.demo.largeheap.ClonableClass0006 + ... 部分�略 ... + +SITES END +``` + + + +从以上片段�以看到, allocations 是根��次创建的对象数��排�的。第一行显示所有对象中有`**64.43%**`的对象是整型数组(`int[]`), 在标识为`302116`的�置创建。�索 “**TRACE 302116**� �以看到: + + + +``` +TRACE 302116: + eu.plumbr.demo.largeheap.ClonableClass0006.(GeneratorClass.java:11) + sun.reflect.GeneratedConstructorAccessor7.newInstance(:Unknown line) + sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) + java.lang.reflect.Constructor.newInstance(Constructor.java:422) +``` + + + +现在, 知�有`64.43%`的对象是整数数组, 在`ClonableClass0006`类的构造函数中, 第11行的�置, 接下�就�以优化代�, 以�少GC的压力。 + +### Java VisualVM + +本章��的第一部分, 在监控 JVM 的GC行为工具时介�了 JVisualVM , 本节介�其在分�分�上的应用。 + +JVisualVM 通过GUI的方�连接到正在�行的JVM。 连接上目标JVM之� : + +1. 打开 “工具� –> “选项� ��, 点击**性能分�(Profiler)**标签, 新增�置, 选择 Profiler 内存, 确�勾选了 “Record allocations stack traces�(记录分�栈跟踪)。 +2. 勾选 “Settings�(设置) �选框, 在内存设置标签下,修改预设�置。 +3. 点击 “Memory�(内存) 按钮开始进行内存分�。 +4. 让程��行一段时间,以收集关于对象分�的足够信�。 +5. �击下方的 “Snapshot�(快照) 按钮。�以获�收集到的快照信�。 + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224729.png) + +完�上�的步骤�, �以得到类似这样的信�: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224743.png) + +上图按照�个类被创建的对象数�多少�排�。看第一行�以知�, 创建的最多的对象是`int[]`数组. 鼠标�键�击这行, 就�以看到这些对象都在哪些地方创建的: + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404224753.png) + +与`hprof`相比, JVisualVM 更加容易使用 —— 比如上�的截图中, 在一个地方就�以看到所有`int[]`的分�信�, 所以多次在�一处代�进行分�的情况就很容易�现。 + +### AProf + +最��的一款分�器,是由 Devexperts 开�的[**AProf**](https://code.devexperts.com/display/AProf/About+Aprof)。 内存分�分�器 AProf 也被打包为 Java agent 的形�。 + +用 AProf 分�应用程�, 需�修改 JVM �动脚本,类似这样: + + + +``` +java -javaagent:/path-to/aprof.jar com.yourcompany.YourApplication +``` + + + +��应用之�, 工作目录下会生�一个`aprof.txt`文件。此文件�分钟更新一次, 包�这样的信�: + + + +``` +======================================================================================================================== +TOTAL allocation dump for 91,289 ms (0h01m31s) +Allocated 1,769,670,584 bytes in 24,868,088 objects of 425 classes in 2,127 locations +======================================================================================================================== + +Top allocation-inducing locations with the data types allocated from them +------------------------------------------------------------------------------------------------------------------------ +eu.plumbr.demo.largeheap.ManyTargetsGarbageProducer.newRandomClassObject: 1,423,675,776 (80.44%) bytes in 17,113,721 (68.81%) objects (avg size 83 bytes) + int[]: 711,322,976 (40.19%) bytes in 1,709,911 (6.87%) objects (avg size 416 bytes) + char[]: 369,550,816 (20.88%) bytes in 5,132,759 (20.63%) objects (avg size 72 bytes) + java.lang.reflect.Constructor: 136,800,000 (7.73%) bytes in 1,710,000 (6.87%) objects (avg size 80 bytes) + java.lang.Object[]: 41,079,872 (2.32%) bytes in 1,710,712 (6.87%) objects (avg size 24 bytes) + java.lang.String: 41,063,496 (2.32%) bytes in 1,710,979 (6.88%) objects (avg size 24 bytes) + java.util.ArrayList: 41,050,680 (2.31%) bytes in 1,710,445 (6.87%) objects (avg size 24 bytes) + ... cut for brevity ... +``` + + + +上�的输出是按照`size`进行排�的。�以看出,`80.44%`的 bytes 和`68.81%`的 objects 是在`ManyTargetsGarbageProducer.newRandomClassObject()`方法中分�的。 其中,**int[]**数组�用了`40.19%`的内存, 是最大的一个。 + +继续往下看, 会�现`allocation traces`(分�痕迹)相关的内容, 也是以 allocation size 排�的: + + + +``` +Top allocated data types with reverse location traces +------------------------------------------------------------------------------------------------------------------------ +int[]: 725,306,304 (40.98%) bytes in 1,954,234 (7.85%) objects (avg size 371 bytes) + eu.plumbr.demo.largeheap.ClonableClass0006.: 38,357,696 (2.16%) bytes in 92,206 (0.37%) objects (avg size 416 bytes) + java.lang.reflect.Constructor.newInstance: 38,357,696 (2.16%) bytes in 92,206 (0.37%) objects (avg size 416 bytes) + eu.plumbr.demo.largeheap.ManyTargetsGarbageProducer.newRandomClassObject: 38,357,280 (2.16%) bytes in 92,205 (0.37%) objects (avg size 416 bytes) + java.lang.reflect.Constructor.newInstance: 416 (0.00%) bytes in 1 (0.00%) objects (avg size 416 bytes) +... cut for brevity ... +``` + + + +�以看到,`int[]`数组的分�, 在`ClonableClass0006`构造函数中继续增大。 + +和其他工具一样,`AProf`�露了 分�的大�以��置信�(`allocation size and locations`), 从而能够快速找到最耗内存的部分。在我们看�,**AProf**是最有用的分�分�器, 因为它�专注于内存分�, 所以�得最好。 当然, 这款工具是开��费的, 资�开销也最�。 + +请继续阅读下一章:7\. GC 调优(实战篇) - GC�考手册 + +原文链接:[GC Tuning: Tooling](https://plumbr.eu/handbook/gc-tuning-measuring) + +翻译时间: 2016年02月06日 + +## �考文章 + + + + + + + + + +https://blog.csdn.net/android_hl/article/details/53228348 + + 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\272\357\274\232JNDI\357\274\214OSGI\357\274\214Tomcat\347\261\273\345\212\240\350\275\275\345\231\250\345\256\236\347\216\260.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\272\357\274\232JNDI\357\274\214OSGI\357\274\214Tomcat\347\261\273\345\212\240\350\275\275\345\231\250\345\256\236\347\216\260.md" new file mode 100644 index 0000000..6c1b066 --- /dev/null +++ "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JNDI\357\274\214OSGI\357\274\214Tomcat\347\261\273\345\212\240\350\275\275\345\231\250\345\256\236\347\216\260.md" @@ -0,0 +1,372 @@ +# 目录 + * [打破�亲委派模型](#打破�亲委派模型) + * [JNDI](#jndi) + * [JNDI 的�解](#[jndi-的�解]) + * [OSGI](#osgi) + * [1.如何正确的�解和认识OSGI技术?](#1如何正确的�解和认识osgi技术?) + * [Tomcat类加载器以�应用间class隔离与共享](#tomcat类加载器以�应用间class隔离与共享) + * [类加载器](#类加载器) + * [�考文章](#�考文章) + + + +本文转自互�网,侵删 + +本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 +> https://github.com/h2pl/Java-Tutorial + +喜欢的�麻烦点下Star哈 + +文章将�步到我的个人�客: +> www.how2playlife.com + +本文是微信公众��Java技术江湖】的《深入�解JVM虚拟机》其中一篇,本文部分内容��于网络,为了把本文主题讲得清晰�彻,也整�了很多我认为�错的技术�客内容,引用其中了一些比较好的�客文章,如有侵�,请�系作者。 + +该系列�文会告诉你如何从入门到进阶,一步步地学习JVM基础知识,并上手进行JVM调优实战,JVM是�一个Java工程师必须�学习和�解的知识点,你必须�掌�其实现原�,�能更完整地了解整个Java技术体系,形�自己的知识框架。 + +为了更好地总结和检验你的学习�果,本系列文章也会�供�个知识点对应的�试题以��考答案。 + +如果对本系列文章有什么建议,或者是有什么疑问的�,也�以关注公众��Java技术江湖】�系作者,欢迎你�与本系列�文的创作和修订。 + + + +## 打破�亲委派模型 + +### JNDI + +### JNDI 的�解 + + +JNDI是 Java 命�与文件夹接�(Java Naming and Directory Interface),在J2EE规范中是��的规范之中的一个,�少专家觉得,没有�彻�解JNDI的�义和作用,就没有真正掌�J2EE特别是EJB的知识。 + +那么,JNDI究竟起什么作用?//带�问题看文章是最有效的 + +�了解JNDI的作用,我们能够从“�设�用JNDI我们如何�?用了JNDI�我们�将如何�?�这个问题�探讨。 + +没有JNDI的�法: + +程�猿开�时,知��开�訪问MySQL数�库的应用,于是将一个对 MySQL JDBC 驱动程�类的引用进行了编�,并通过使用适当的 JDBC URL 连接到数�库。 +就�以下代�这样: + +```` + 1. Connectionconn=null; + 2. try{ + 3. Class.forName("com.mysql.jdbc.Driver", + 4. true,Thread.currentThread().getContextClassLoader()); + 5. conn=DriverManager. + 6. getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue"); + 7. ...... + 8. conn.close(); + 9. }catch(Exceptione){ + 10. e.printStackTrace(); + 11. }finally{ + 12. if(conn!=null){ + 13. try{ + 14. conn.close(); + 15. }catch(SQLExceptione){} + 16. } + 17. } +```` + + +这是传统的�法,也是曾��Java程�猿(如Delphi�VB等)常�的�法。 + +这��法一般在�规模的开�过程中�会产生问题,仅仅�程�猿熟悉Java语言�了解JDBC技术和MySQL,能够�常快开�出对应的应用程�。 + +没有JNDI的�法存在的问题: + + 1�数�库server�称MyDBServer �username和�令都�能须�改�,由此引�JDBC URL须�改动; + 2�数�库�能改用别的产�,如改用DB2或者Oracle,引�JDBC驱动程�包和类�须�改动; + 3���实际使用终端的添加,原�置的连接池�数�能须�调整; + 4�...... + +解决的方法: + +程�猿应该�须�关心“详细的数�库��是什么?JDBC驱动程�是什么?JDBC URL格�是什么?訪问数�库的username和�令是什么?�等等这些问题。 + +程�猿编写的程�应该没有对 JDBC驱动程�的引用,没有server�称,没实username称或�令 —— 甚至没有数�库池或连接管�。 + +而是把这些问题交给J2EE容器(比方weblogic)��置和管�,程�猿仅仅须�对这些�置和管�进行引用就�以。 + +由此,就有了JNDI。 + +//看的出�。是为了一个最最核心的问题:是为了解耦,是为了开�出更加�维护��扩展//的系统 + +用了JNDI之�的�法: +首先。在在J2EE容器中�置JNDI�数,定义一个数��。也就是JDBC引用�数,给这个数��设置一个�称;然�,在程�中,通过数���称引用数��从而訪问��数�库。 + +//红色的字能够看出。JNDI是由j2ee容器�供的功能 + +详细�作例如以下(以JBoss为例): +1��置数�� +在JBoss 的 D:\jboss420GA\docs\examples\jca 文件夹以下。有�常多��数�库引用的数��定义模�。 + +将当中的 mysql-ds.xml 文件Copy到你使用的server下,如 D:\jboss420GA\server\default\deploy。 +改动 mysql-ds.xml 文件的内容,使之能通过JDBC正确訪问你的MySQL数�库。例如以下: + + + +```` + + + +MySqlDS +jdbc:mysql://localhost:3306/lw +com.mysql.jdbc.Driver +root +rootpassword + +org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter + + +mySQL + + + +```` + + +这里,定义了一个�为MySqlDS的数��。其�数包�JDBC的URL。驱动类�,username�密�等。 + +2�在程�中引用数��: + +```` +1. Connectionconn=null; +2. try{ +3. Contextctx=newInitialContext(); +4. ObjectdatasourceRef=ctx.lookup("java:MySqlDS");//引用数�� +5. DataSourceds=(Datasource)datasourceRef; +6. conn=ds.getConnection(); +7. ...... +8. c.close(); +9. }catch(Exceptione){ +10. e.printStackTrace(); +11. }finally{ +12. if(conn!=null){ +13. try{ +14. conn.close(); +15. }catch(SQLExceptione){} +16. } +17. } +```` + + +直接使用JDBC或者通过JNDI引用数��的编程代��相差无几,�是如今的程�能够�用关心详细JDBC�数了。 + +//解藕了。�扩展了 +在系统部署�。�设数�库的相关�数�更。仅仅须��一次�置 mysql-ds.xml 改动当中的JDBC�数,仅仅���数��的�称��,那么程���就无需改动。 + +由此��。JNDI��了程�与数�库之间的紧耦�,使应用更加易于�置�易于部署。 + +JNDI的扩展: +JNDI在满足了数���置的�求的基础上。还进一步扩充了作用:全部与系统外部的资�的引用,都能够通过JNDI定义和引用。 + +//注�什么�资� + +所以,在J2EE规范中,J2EE 中的资�并�局�于 JDBC 数��。 + +引用的类型有�常多,当中包�资�引用(已�讨论过)�环境实体和 EJB 引用。 + +特别是 EJB 引用,它暴露了 JNDI 在 J2EE 中的�外一项关键角色:查找其它应用程�组件。 + +EJB 的 JNDI 引用�常相似于 JDBC 资�的引用。在�务趋于转�的环境中,这是一��常有效的方法。能够对应用程�架构中所得到的全部组件进行这类�置管�,从 EJB 组件到 JMS 队列和主题。�到简��置字符串或其它对象。这能够�低�时间的推移�务�更所产生的维护�本,�一时候还能够简化部署,�低集�工作。外部资��。 + +总结: + +J2EE 规范�求全部 J2EE 容器都��供 JNDI 规范的实现。//sun 果然喜欢制定规范JNDI 在 J2EE 中的角色就是“交�机� —— J2EE 组件在执行时间接地查找其它组件�资�或�务的通用机制。在多数情况下,�供 JNDI 供应者的容器能够充当有�的数�存储。这样管�员就能够设置应用程�的执行属性,并让其它应用程�引用这些属性(Java 管�扩展(Java Management Extensions,JMX)也能够用作这个目的)。JNDI 在 J2EE 应用程�中的主�角色就是�供间接层,这样组件就能够�现所须�的资�,而�用了解这些间接性。 + +在 J2EE 中,JNDI 是把 J2EE 应用程��在一起的粘�剂。JNDI �供的间接寻���跨�业交付�伸缩的�功能强大且�常�活的应用程�。 + +这是 J2EE 的承诺,并且�过一些计划和预先考虑。这个承诺是全然能够实现的。 + + 从上�的文章中能够看出: +1�JNDI �出的目的是为了解藕,是为了开�更加easy维护,easy扩展。easy部署的应用。 +2�JNDI 是一个sun�出的一个规范(相似于jdbc),详细的实现是�个j2ee容器�供商。sun 仅仅是�求,j2ee容器必须有JNDI这�功能。 + +3�JNDI 在j2ee系统中的角色是“交�机�,是J2EE组件在执行时间接地查找其它组件�资�或�务的通用机制。 +4�JNDI 是通过资�的�字�查找的,资�的�字在整个j2ee应用中(j2ee容器中)是唯一的。 + + + 上文�到过�亲委派模型并�是一个强制性的约�模型,而是Java设计者推�给开�者的类加载器实现方�。在Java的世界中大部分的类加载器都�循这个模型,但也有例外。 + + �亲委派模型的一次“被破��是由这个模型自身的缺陷所导致的,�亲委派很好地解决了�个类加载器的基础类的统一问题(越基础的类由越上层的加载器进行加载),基础类之所以称为“基础�,是因为它们总是作为被用户代�调用的API,但世事往往没有�对的完美,如果基础类��调用回用户的代�,那该怎么办? + +这并�是��能的事情,一个典型的例�便是JNDI�务,JNDI现在已�是Java的标准�务,它的代�由�动类加载器去加载(在JDK 1.3时放进去的rt.jar),但JNDI的目的就是对资�进行集中管�和查找,它需�调用由独立厂商实现并部署在应用程�的ClassPath下的JNDI接��供者(SPI,Service Provider Interface)的代�,但�动类加载器��能“认识�这些代�,因为�动类加载器的�索范围中找�到用户应用程�类,那该怎么办? + + +为了解决这个问题,Java设计团队�好引入了一个�太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器�以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程�的全局范围内都没有设置过的�,那这个类加载器默认就是应用程�类加载器(Application ClassLoader)。 + + 有了线程上下文类加载器,就�以�一些“舞弊�的事情了,JNDI�务使用这个线程上下文类加载器去加载所需�的SPI代�,也就是父类加载器请求�类加载器去完�类加载的动作,这�行为实际上就是打通了�亲委派模型的层次结构�逆�使用类加载器,实际上已��背了�亲委派模型的一般性原则,但这也是无�奈何的事情。Java中所有涉�SPI的加载动作基本上都采用这�方�,例如JNDI�JDBC�JCE�JAXB和JBI等。 + + + +## OSGI + + + +目�,业内关于OSGI技术的学习资�或者技术文档还是很少的。我在��网�索了一下“OSGI�的书�,结果倒是有,但是�类少的�怜,而且几乎没有人购买。 +因为工作的原因我需�学习OSGI,所以我�得�想尽办法�主动学习OSGI。我将用文字记录学习OSGI的整个过程,通过整�书�和视频教程,�让我更加了解这门技术,�时也让需�学习这门技术的�志们有一个清晰的学习路线。 + +我们需�解决一下几问题: + +### 1.如何正确的�解和认识OSGI技术? + +我们从外文资料上或者从翻译过�的资料上看到OSGi解释和定义,都是直译过�的,但是OSGI的真实�义未必是中文直译过�的��。OSGI的解释就是Open Service Gateway Initiative,直译过�就是“开放的�务入�(网关)的�始化�,�起��常费解,什么是�务入��始化? + +所以我们�去直译这个OSGI,我们�一�说法��述OSGI技术。 + +我们�回到我们以�的�些开�场景中去,�设我们使用SSH(struts+spring+hibernate)框架�开�我们的Web项目,我们�产�设计和开�的时候都是分模�的,我们分模�的目的就是实现模�之间的“解耦�,更进一步的目的是方便对一个项目的控制和管�。 +我们对一个项目进行模�化分解之�,我们就�以把��模�交给��的开�人员�完�开�,然�项目��把大家完�的模�集中在一起,然�拼装�一个最终的产�。一般我们开�都是这样的基本情况。 + +那么我们开�的时候预计的是系统的功能,根�系统的功能�进行模�的划分,也就是说,这个产�的功能或客户的需求是划分的����。 + +但是我们在开�过程中,我们模�之间还�彼此���系,比如A模��从B模�拿到一些数�,而B模��能�调用C模�中的一些方法(除了公共底层的工具类之外)。所以这些模��是一�逻辑�义上的划分。 + +最��的一点是,我们把最终的项目�去部署到tomcat或者jBoss的�务器中去部署。那么我们�动�务器的时候,能�能关闭项目的�个模�或功能呢?很明显是��到的,一旦�务器�动,所有模�就�一起�动,都��用�务器资�,所以关闭�了模�,�设能强制拿掉,就会影�其它的功能。 + +以上就是我们传统模��开�的一些局�性。 + +我们�软件开�一直在追求一个境界,就是模�之间的真正“解耦��“分离�,这样我们在软件的管�和开�上�就会更加的�活,甚至包括给客户部署项目的时候都�以�到更加的�活�控。但是我们以�使用SSH框架等架构模�进行产�开�的时候我们是达�到这��求的。 + +所以我们“架构师�或顶尖的技术高手都在为模�化开�努力的摸索和�试,然�我们的OSGI的技术规范就应�而生。 + +现在我们的OSGI技术就�以满足我们之�所说的境界:在��的模�中�到彻底的分离,而�是逻辑�义上的分离,是物�上的分离,也就是说在�行部署之�都�以在��止�务器的时候直接把�些模�拿下�,其他模�的功能也��影�。 + +由此,OSGI技术将�会�得�常的��,因为它在实现模�化解耦的路上,走得比现在大家�常所用的SSH框架走的更远。这个技术在未�大规模�高访问�高并�的Java模�化开�领域,或者是项目规范化管�中,会大大超过SSH等框架的地�。 + +现在主�的一些应用�务器,Oracle的weblogic�务器,IBM的WebSphere,JBoss,还有Sun公�的glassfish�务器,都对OSGI�供了强大的支�,都是在OSGI的技术基础上实现的。有那么多的大型厂商支�OSGI这门技术,我们既�以看到OSGI技术的��性。所以将�OSGI是将��常��的技术。 + +但是OSGI�然脱离�了框架的支�,因为OSGI本身也使用了很多spring等框架的基本控件(因为�实现AOP�赖注入等功能),但是哪个项目��去�赖第三方jar呢? + + + �亲委派模型的�一次“被破��是由于用户对程�动�性的追求而导致的,这里所说的“动�性�指的是当�一些�常“热门�的��:代�热替�(HotSwap)�模�热部署(HotDeployment)等,说白了就是希望应用程�能�我们的计算机外设那样,接上鼠标�U盘,�用��机器就能立�使用,鼠标有问题或��级就�个鼠标,�用�机也�用��。 + + 对于个人计算机�说,��一次其实没有什么大�了的,但对于一些生产系统�说,关机��一次�能就�被列为生产事故,这�情况下热部署就对软件开�者,尤其是�业级软件开�者具有很大的�引力。Sun公�所�出的JSR-294�JSR-277规范在与JCP组织的模�化规范之争中�败给JSR-291(�OSGi R4.2),虽然Sun�甘失去Java模�化的主导�,独立在�展Jigsaw项目,但目�OSGi已��为了业界“事实上�的Java模�化标准,而OSGi实现模�化热部署的关键则是它自定义的类加载器机制的实现。 + + �一个程�模�(OSGi中称为Bundle)都有一个自己的类加载器,当需�更�一个Bundle时,就把Bundle连�类加载器一起�掉以实现代�的热替�。 + + 在OSGi环境下,类加载器��是�亲委派模型中的树状结构,而是进一步�展为更加��的网状结构,当收到类加载请求时,OSGi将按照下�的顺�进行类�索: + + 1)将以java.*开头的类委派给父类加载器加载。 + + 2)�则,将委派列表��内的类委派给父类加载器加载。 + + 3)�则,将Import列表中的类委派给Export这个类的Bundle的类加载器加载。 + + 4)�则,查找当�Bundle的ClassPath,使用自己的类加载器加载。 + + 5)�则,查找类是�在自己的Fragment Bundle中,如果在,则委派给Fragment Bundle的类加载器加载。 + + 6)�则,查找Dynamic Import列表的Bundle,委派给对应Bundle的类加载器加载。 + + 7)�则,类查找失败。 + + 上�的查找顺�中�有开头两点�然符��亲委派规则,其余的类查找都是在平级的类加载器中进行的。 + + ��有足够�义和�由,�破已有的原则就�认为是一�创新。正如OSGi中的类加载器并�符�传统的�亲委派的类加载器,并且业界对其为了实现热部署而带�的�外的高��度还存在�少争议,但在Java程�员中基本有一个共识:OSGi中对类加载器的使用是很值得学习的,弄懂了OSGi的实现,就�以算是掌�了类加载器的精髓。 + + +## Tomcat类加载器以�应用间class隔离与共享 + + + +Tomcat的用户一定都使用过其应用部署功能,无论是直接拷�文件到webapps目录,还是修改server.xml以目录的形�部署,或者是增加虚拟主机,指定新的appBase等等。 + +但部署应用时,�知�你是�曾注�过这几点: + +1. 如果在一个Tomcat内部署多个应用,甚至多个应用内使用了�个类似的几个��版本,但它们之间�互�影�。这是如何�到的。 + +2. 如果多个应用都用到了�类似的相�版本,是��以统一�供,�在�个应用内分别�供,�用内存呢。 + +3. 还有时候,在开�Web应用时,在pom.xml中添加了servlet-api的�赖,那实际应用的class加载时,会加载你的servlet-api 这个jar� + +以上�到的这几点,在Tomcat以��类的应用�务器中,都是通过类加载器(ClasssLoader)�实现的。通过本文,你�以了解到Tomcat内部�供的��类加载器,Web应用的class和资�等加载的方�,以�其内部的实现原�。在�到类似问题时,更胸有�竹。 + +### 类加载器 + +Java语言本身,以�现在其它的一些基于JVM之上的语言(Groovy,Jython, Scala...),都是在将代�编译生�class文件,以实现跨多平�,write once, run anywhere。最终的这些class文件,在应用中,�被加载到JVM虚拟机中,开始工作。而把class文件加载到JVM的组件,就是我们所说的类加载器。而对于类加载器的抽象,能�对更多的class数��供形�,例如网络�文件系统等。 + +Java中常�的那个ClassNotFoundException和NoClassDefFoundError就是类加载器告诉我们的。 + +Servlet规范指出,容器用于加载Web应用内Servlet的class loader, �许加载�于Web应用内的资�。但��许�写java.*, javax.*以�容器实现的类。�时 + +�个应用内使用Thread.currentThread.getContextClassLoader()获得的类加载器,都是该应用区别于其它应用的类加载器等等。 + +根�Servlet规范,�个应用�务器厂商自行实现。所以�其他的一些应用�务器一样, Tomcat也�供了多�的类加载器,以便应用�务器内的class以�部署的Web应用类文件�行在容器中时,�以使用��的class repositories。 + +在Java中,类加载器是以一�父�关系树�组织的。除Bootstrap外,都会包�一个parent 类加载器。(这里写parent 类加载器,而�是父类加载器,�是为了装X,是为了��和Java里的父类混淆)一般以类加载器需�加载一个class或者资�文件的时候,他会先委托给他的parent类加载器,让parent类加载器先�加载,如果没有,��在自己的路径上加载。这就是人们常说的�亲委托,�把类加载的请求委托给parent。 + +但是...,这里需�注�一下 + +> 对于Web应用的类加载,和上�的�亲委托是有区别的。 + + 主�的Java Web�务器(也就是Web容器),如Tomcat�Jetty�WebLogic�WebSphere或其他笔者没有列举的�务器,都实现了自己定义的类加载器(一般都�止一个)。因为一个功能�全的Web容器,�解决如下几个问题: + + 1)部署在�一个Web容器上的两个Web应用程�所使用的Java类库�以实现相互隔离。这是最基本的需求,两个��的应用程��能会�赖�一个第三方类库的��版本,�能�求一个类库在一个�务器中�有一份,�务器应当��两个应用程�的类库�以互相独立使用。 + + 2)部署在�一个Web容器上的两个Web应用程�所使用的Java类库�以互相共享。这个需求也很常�,例如,用户�能有10个使用[spring](https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Flink.juejin.im%2F%3Ftarget%3Dhttp%253A%252F%252Flib.csdn.net%252Fbase%252Fjavaee "Java EE知识库")组织的应用程�部署在�一��务器上,如果把10份Spring分别存放在�个应用程�的隔离目录中,将会是很大的资�浪费——这主�倒�是浪费�盘空间的问题,而是指类库在使用时都�被加载到Web容器的内存,如果类库�能共享,虚拟机的方法区就会很容易出现过度膨胀的风险。 + + 3)Web容器需�尽�能地��自身的安全��部署的Web应用程�影�。目�,有许多主�的Java Web容器自身也是使用Java语言�实现的。因此,Web容器本身也有类库�赖的问题,一般�说,基于安全考虑,容器所使用的类库应该与应用程�的类库互相独立。 + + 4)支�JSP应用的Web容器,大多数都需�支�HotSwap功能。我们知�,JSP文件最终�编译�Java Class�能由虚拟机执行,但JSP文件由于其纯文本存储的特性,�行时修改的概率远远大于第三方类库或程�自身的Class文件。而且ASP�[PHP](https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Flink.juejin.im%2F%3Ftarget%3Dhttp%253A%252F%252Flib.csdn.net%252Fbase%252Fphp "PHP知识库")和JSP这些网页应用也把修改�无须��作为一个很大的“优势��看待,因此“主��的Web容器都会支�JSP生�类的热替�,当然也有“�主��的,如�行在生产模�(Production Mode)下的WebLogic�务器默认就�会处�JSP文件的�化。 + + 由于存在上述问题,在部署Web应用时,�独的一个Class Path就无法满足需求了,所以��Web容都“�约而��地�供了好几个Class Path路径供用户存放第三方类库,这些路径一般都以“lib�或“classes�命�。被放置到��路径中的类库,具备��的访问范围和�务对象,通常,�一个目录都会有一个相应的自定义类加载器去加载放置在里�的Java类库。现在,就以Tomcat容器为例,看一看Tomcat具体是如何规划用户类库结构和类加载器的。 + + 在Tomcat目录结构中,有3组目录(“/common/*��“/server/*�和“/shared/*�)�以存放Java类库,�外还�以加上Web应用程�自身的目录“/WEB-INF/*�,一共4组,把Java类库放置在这些目录中的�义分别如下: + + ①放置在/common目录中:类库�被Tomcat和所有的Web应用程�共�使用。 + + ②放置在/server目录中:类库�被Tomcat使用,对所有的Web应用程�都���。 + + ③放置在/shared目录中:类库�被所有的Web应用程�共�使用,但对Tomcat自己���。 + + ④放置在/WebApp/WEB-INF目录中:类库仅仅�以被此Web应用程�使用,对Tomcat和其他Web应用程�都���。 + + 为了支�这套目录结构,并对目录里�的类库进行加载和隔离,Tomcat自定义了多个类加载器,这些类加载器按照�典的�亲委派模型�实现,其关系如下图所示。 + + + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404222020.png) + + + + + + + 上图中�色背景的3个类加载器是JDK默认�供的类加载器,这3个加载器的作用已�介�过了。而CommonClassLoader�CatalinaClassLoader�SharedClassLoader和WebappClassLoader则是Tomcat自己定义的类加载器,它们分别加载/common/*�/server/*�/shared/*和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和Jsp类加载器通常会存在多个实例,�一个Web应用程�对应一个WebApp类加载器,�一个JSP文件对应一个Jsp类加载器。 + + 从图中的委派关系中�以看出,CommonClassLoader能加载的类都�以被CatalinaClassLoader和SharedClassLoader使用,而CatalinaClassLoader和SharedClassLoader自己能加载的类则与对方相互隔离。WebAppClassLoader�以使用SharedClassLoader加载到的类,但�个WebAppClassLoader实例之间相互隔离。 + + 而JasperLoader的加载范围仅仅是这个JSP文件所编译出�的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替�掉目�的JasperLoader的实例,并通过�建立一个新的Jsp类加载器�实现JSP文件的HotSwap功能。 + +对于Tomcat的6.x版本,�有指定了tomcat/conf/catalina.properties�置文件的server.loader和share.loader项��会真正建立CatalinaClassLoader和Shared ClassLoader的实例,�则在用到这两个类加载器的地方都会用Common ClassLoader的实例代替,而默认的�置文件中没有设置这两个loader项,所以Tomcat 6.x顺��章地把/common�/server和/shared三个目录默认�并到一起��一个/lib目录,这个目录里的类库相当于以�/common目录中类库的作用。 + +这是Tomcat设计团队为了简化大多数的部署场景所�的一项改进,如果默认设置�能满足需�,用户�以通过修改�置文件指定server.loader和share.loader的方��新�用Tomcat 5.x的加载器[架构](https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Flink.juejin.im%2F%3Ftarget%3Dhttp%253A%252F%252Flib.csdn.net%252Fbase%252Farchitecture "大型网站架构知识库")。 + + Tomcat加载器的实现清晰易懂,并且采用了官方推�的“正统�的使用类加载器的方�。如果读者阅读完上�的案例�,能完全�解Tomcat设计团队这样布置加载器架构的用�,那说明已�大致掌�了类加载器“主��的使用方�,那么笔者�妨��一个问题让读者�考一下:��曾��到过一个场景,如果有10个Web应用程�都是用Spring�进行组织和管�的�,�以把Spring放到Common或Shared目录下让这些程�共享。 + + Spring�对用户程�的类进行管�,自然�能访问到用户程�的类,而用户的程�显然是放在/WebApp/WEB-INF目录中的,那么被CommonClassLoader或SharedClassLoader加载的Spring如何访问并�在其加载范围内的用户程�呢?如果研究过虚拟机类加载器机制中的�亲委派模型,相信读者�以很容易地回答这个问题。 + +分�:如果按主�的�亲委派机制,显然无法�到让父类加载器加载的类去访问�类加载器加载的类,上�在类加载器一节中�到过通过线程上下文方�传播类加载器。 + + 答案是使用线程上下文类加载器�实现的,使用线程上下文加载器,�以让父类加载器请求�类加载器去完�类加载的动作。 + + 看spring���现,spring加载类所用的Classloader是通过Thread.currentThread().getContextClassLoader()�获�的,而当线程创建时会默认setContextClassLoader(AppClassLoader),�线程上下文类加载器被设置为AppClassLoader,spring中始终�以获�到这个AppClassLoader(在Tomcat里就是WebAppClassLoader)�类加载器�加载bean,以�任何一个线程都�以通过getContextClassLoader()获�到WebAppClassLoader�getbean了。 + + + +本篇�文内容��自《深入�解Java虚拟机:JVM高级特性与最佳实践》 + +## �考文章 + + + + + + + + + +https://blog.csdn.net/android_hl/article/details/53228348 + + 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\2721\357\274\232JVM\345\206\205\345\255\230\347\232\204\347\273\223\346\236\204\344\270\216\346\266\210\345\244\261\347\232\204\346\260\270\344\271\205\344\273\243.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\272\357\274\232JVM\345\206\205\345\255\230\347\232\204\347\273\223\346\236\204\344\270\216\346\266\210\345\244\261\347\232\204\346\260\270\344\271\205\344\273\243.md" similarity index 75% 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\2721\357\274\232JVM\345\206\205\345\255\230\347\232\204\347\273\223\346\236\204\344\270\216\346\266\210\345\244\261\347\232\204\346\260\270\344\271\205\344\273\243.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\272\357\274\232JVM\345\206\205\345\255\230\347\232\204\347\273\223\346\236\204\344\270\216\346\266\210\345\244\261\347\232\204\346\260\270\344\271\205\344\273\243.md" index d040fa5..17431e9 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2721\357\274\232JVM\345\206\205\345\255\230\347\232\204\347\273\223\346\236\204\344\270\216\346\266\210\345\244\261\347\232\204\346\260\270\344\271\205\344\273\243.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\272\357\274\232JVM\345\206\205\345\255\230\347\232\204\347\273\223\346\236\204\344\270\216\346\266\210\345\244\261\347\232\204\346\260\270\344\271\205\344\273\243.md" @@ -1,5 +1,4 @@ -# Table of Contents - +# 目录 * [�言](#�言) * [Java堆(Heap)](#java堆(heap)) * [方法区(Method Area)](#方法区(method-area)) @@ -7,25 +6,22 @@ * [JVM栈(JVM Stacks)](#jvm栈(jvm-stacks)) * [本地方法栈(Native Method Stacks)](#本地方法栈(native-method-stacks)) * [哪儿的OutOfMemoryError](#哪儿的outofmemoryerror) -* [[JDK8-废弃永久代(PermGen)迎�元空间(Metaspace)](https://www.cnblogs.com/yulei126/p/6777323.html)](#[jdk8-废弃永久代(permgen)迎�元空间(metaspace)]httpswwwcnblogscomyulei126p6777323html) * [一�背景](#一�背景) * [1.1 永久代(PermGen)在哪里?](#11-永久代(permgen)在哪里?) * [1.2 JDK8永久代的废弃](#12-jdk8永久代的废弃) - * [ 二�为什么废弃永久代(PermGen)](# 二�为什么废弃永久代(permgen)) - * [ 2.1 官方说明](# 21-官方说明) + * [二�为什么废弃永久代(PermGen)](#二�为什么废弃永久代(permgen)) + * [2.1 官方说明](#21-官方说明) * [Motivation](#motivation) - * [ 2.2 现实使用中易出问题](# 22-现实使用中易出问题) + * [2.2 现实使用中易出问题](#22-现实使用中易出问题) * [三�深入�解元空间(Metaspace)](#三�深入�解元空间(metaspace)) * [3.1元空间的内存大�](#31元空间的内存大�) * [3.2常用�置�数](#32常用�置�数) * [3.3测试并追踪元空间大�](#33测试并追踪元空间大�) - * [ 3.3.1.测试字符串常�](# 331测试字符串常�) + * [3.3.1.测试字符串常�](#331测试字符串常�) * [3.3.2.测试元空间溢出](#332测试元空间溢出) - * [ 四�总结](# 四�总结) + * [四�总结](#四�总结) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + 本文转自互�网,侵删 @@ -54,11 +50,9 @@ 其实如果你�常解决�务器性能问题,那么这些问题就会�的�常常�,了解JVM内存也是为了�务器出现性能问题的时候�以快速的了解那�的内存区域出现问题,以便于快速的解决生产故障。 - - 先看一张图,这张图能很清晰的说明JVM内存结构布局。 -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036730192392.png) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404214718.png) JVM内存结构主�有三大�:堆内存�方法区和栈。堆内存是JVM中最大的一�由年轻代和�年代组�,而年轻代内存�被分�三部分,Eden空间�From Survivor空间�To Survivor空间,默认情况下年轻代按照8:1:1的比例�分�; @@ -66,8 +60,7 @@ JVM内存结构主�有三大�:堆内存�方法区和栈。堆内存是JV 在通过一张图�了解如何通过�数�控制�区域的内存大� -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036730949557.png) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404214735.png) 控制�数 * -Xms设置堆的最�空间大�。 @@ -84,7 +77,7 @@ JVM内存结构主�有三大�:堆内存�方法区和栈。堆内存是JV 从更高的一个维度�次�看JVM和系统调用之间的关系 -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036732641186.png) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404214754.png) 方法区和对是所有线程共享的内存区域;而java栈�本地方法栈和程�员计数器是�行是线程�有的内存区域。 @@ -112,7 +105,7 @@ Java虚拟机规范对这个区域的�制�常宽�,除了和Java堆一样 方法区有时被称为�久代(PermGen)。 -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036749972827.png) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404214839.png) 所有的对象在实例化�的整个�行周期内,都被存放在堆内存中。堆内存�被划分���的部分:伊甸区(Eden),幸存者区域(Survivor Sapce),�年代(Old Generation Space)。 @@ -121,27 +114,26 @@ Java虚拟机规范对这个区域的�制�常宽�,除了和Java堆一样 - -

import java.text.SimpleDateFormat;import java.util.Date;import org.apache.log4j.Logger;
+````
+import java.text.SimpleDateFormat;import java.util.Date;import org.apache.log4j.Logger;
  public class HelloWorld {
-    private static Logger LOGGER = Logger.getLogger(HelloWorld.class.getName());
-    public void sayHello(String message) {
-        SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.YYYY");
-        String today = formatter.format(new Date());
-        LOGGER.info(today + ": " + message);
-    }}
- + private static Logger LOGGER = Logger.getLogger(HelloWorld.class.getName()); + public void sayHello(String message) { + SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.YYYY"); + String today = formatter.format(new Date()); + LOGGER.info(today + ": " + message); + }} +```` 这段程�的数�在内存中的存放如下: -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036766406898.png) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404214906.png) 通过JConsole工具�以查看�行中的Java程�(比如Eclipse)的一些信�:堆内存的分�,线程的数�以�加载的类的个数; -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036768475136.png) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404214922.png) ## 程�计数器(Program Counter Register) @@ -172,69 +164,38 @@ Java虚拟机规范对这个区域的�制�常宽�,除了和Java堆一样 对内存结构清晰的认识�样�以帮助�解��OutOfMemoryErrors: - - - -
Exception in thread “main�: java.lang.OutOfMemoryError: Java heap space
- - - +Exception in thread “main�: java.lang.OutOfMemoryError: Java heap space 原因:对象�能被分�到堆内存中 - - - -
Exception in thread “main�: java.lang.OutOfMemoryError: PermGen space
- - - +Exception in thread “main�: java.lang.OutOfMemoryError: PermGen space 原因:类或者方法�能被加载到�久代。它�能出现在一个程�加载很多类的时候,比如引用了很多第三方的库; - - - - -
Exception in thread “main�: java.lang.OutOfMemoryError: Requested array size exceeds VM limit
- - - +Exception in thread “main�: java.lang.OutOfMemoryError: Requested array size exceeds VM limit 原因:创建的数组大于堆内存的空间 - - - -
Exception in thread “main�: java.lang.OutOfMemoryError: request  bytes for . Out of swap space?
- - - +Exception in thread “main�: java.lang.OutOfMemoryError: request bytes for . Out of swap space? 原因:分�本地分�失败。JNI�本地库或者Java虚拟机都会从本地堆中分�内存空间。 - - - -
Exception in thread “main�: java.lang.OutOfMemoryError:  (Native method)
- - - +Exception in thread “main�: java.lang.OutOfMemoryError: (Native method) 原因:�样是本地方法内存分�失败,��过是JNI或者本地方法或者Java虚拟机�现 -# [JDK8-废弃永久代(PermGen)迎�元空间(Metaspace)](https://www.cnblogs.com/yulei126/p/6777323.html) - - +关于永久代的废弃�以�考这篇文章 +JDK8-废弃永久代(PermGen)迎�元空间(Metaspace) +(https://www.cnblogs.com/yulei126/p/6777323.html) 1.背景 @@ -253,8 +214,7 @@ Java虚拟机规范对这个区域的�制�常宽�,除了和Java堆一样 根�,hotspot jvm结构如下(虚拟机栈和本地方法栈�一起了): -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036768860207.png) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404215109.png) 上图引自网络,但有个问题:方法区和heap堆都是线程共享的内存区域。 关于方法区和永久代: @@ -265,17 +225,16 @@ Java虚拟机规范对这个区域的�制�常宽�,除了和Java堆一样 JDK8 永久代�化如下图: -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036768834803.jpg) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404215123.png) 1.新生代:Eden+From Survivor+To Survivor 2.�年代:OldGen 3.永久代(方法区的实现) : PermGen----->替�为Metaspace(本地内存中) -##  二�为什么废弃永久代(PermGen) +## 二�为什么废弃永久代(PermGen) -###  2.1 官方说明 +### 2.1 官方说明 �照JEP122:http://openjdk.java.net/jeps/122,原文截�: @@ -283,9 +242,9 @@ JDK8 永久代�化如下图: This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation. - �:移除永久代是为��HotSpot JVM与 JRockit VM而�出的努力,因为JRockit没有永久代,�需��置永久代。 +�:移除永久代是为��HotSpot JVM与 JRockit VM而�出的努力,因为JRockit没有永久代,�需��置永久代。 -###  2.2 现实使用中易出问题 +### 2.2 现实使用中易出问题 由于永久代内存�常�够用或�生内存泄露,爆出异常java.lang.OutOfMemoryError: PermGen @@ -303,7 +262,7 @@ This is part of the JRockit and Hotspot convergence effort. JRockit customers do 1.MetaspaceSize -�始化的Metaspace大�,控制元空间�生GC的阈值。GC�,动�增加或�低MetaspaceSize。在默认情况下,这个值大�根���的平�在12M到20M浮动。使用[Java](http://lib.csdn.net/base/javase "Java SE知识库") -XX:+PrintFlagsInitial命令查看本机的�始化�数 +�始化的Metaspace大�,控制元空间�生GC的阈值。GC�,动�增加或�低MetaspaceSize。在默认情况下,这个值大�根���的平�在12M到20M浮动。使用[Java](http://lib.csdn.net/base/javase "Java SE知识库")-XX:+PrintFlagsInitial命令查看本机的�始化�数 2.MaxMetaspaceSize @@ -327,40 +286,32 @@ Metaspace增长时的最�幅度。在本机上该�数的默认值为340784B ### 3.3测试并追踪元空间大� -####  3.3.1.测试字符串常� - - - - - - -
 1 public class StringOomMock {
- 2     static String  base = "string";
- 3     
- 4     public static void main(String[] args) {
- 5         List list = new ArrayList();
- 6         for (int i=0;i< Integer.MAX_VALUE;i++){
- 7             String str = base + base;
- 8             base = str;
- 9             list.add(str.intern());
-10         }
-11     }
-12 }
- - - - - +#### 3.3.1.测试字符串常� + +```` +public class StringOomMock { + static String base = "string"; + + public static void main(String[] args) { + List list = new ArrayList(); + for (int i=0;i< Integer.MAX_VALUE;i++){ + String str = base + base; + base = str; + list.add(str.intern()); + } + } +} +```` 在eclipse中选中类--》run configuration-->java application--》new �数如下: -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036770114377.png) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404215213.png) - 由于设定了最大内存20M,很快就溢出,如下图: +由于设定了最大内存20M,很快就溢出,如下图: -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036770308300.png) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404215254.png) - ��在jdk8中: +��在jdk8中: 1.字符串常�由永久代转移到堆中。 @@ -369,86 +320,71 @@ Metaspace增长时的最�幅度。在本机上该�数的默认值为340784B #### 3.3.2.测试元空间溢出 根�定义,我们以加载类�测试元空间溢出,代�如下: - - - - - - -
 1 package jdk8;
- 2 
- 3 import java.io.File;
- 4 import java.lang.management.ClassLoadingMXBean;
- 5 import java.lang.management.ManagementFactory;
- 6 import java.net.URL;
- 7 import java.net.URLClassLoader;
- 8 import java.util.ArrayList;
- 9 import java.util.List;
-10 
-11 /**
-12  * 
-13  * @ClassName:OOMTest
-14  * @Description:模拟类加载溢出(元空间oom)
-15  * @author diandian.zhang
-16  * @date 2017年4月27日上�9:45:40
-17  */
-18 public class OOMTest {  
-19     public static void main(String[] args) {  
-20         try {  
-21             //准备url  
-22             URL url = new File("D:/58workplace/11study/src/main/java/jdk8").toURI().toURL();  
-23             URL[] urls = {url};  
-24             //获�有关类型加载的JMX接�  
-25             ClassLoadingMXBean loadingBean = ManagementFactory.getClassLoadingMXBean();  
-26             //用于缓存类加载器  
-27             List classLoaders = new ArrayList();  
-28             while (true) {  
-29                 //加载类型并缓存类加载器实例  
-30                 ClassLoader classLoader = new URLClassLoader(urls);  
-31                 classLoaders.add(classLoader);  
-32                 classLoader.loadClass("ClassA");  
-33                 //显示数�信�(共加载过的类型数目,当�还有效的类型数目,已�被�载的类型数目)  
-34                 System.out.println("total: " + loadingBean.getTotalLoadedClassCount());  
-35                 System.out.println("active: " + loadingBean.getLoadedClassCount());  
-36                 System.out.println("unloaded: " + loadingBean.getUnloadedClassCount());  
-37             }  
-38         } catch (Exception e) {  
-39             e.printStackTrace();  
-40         }  
-41     }  
-42 }  
- - - - - +```` +package jdk8; + +import java.io.File; +import java.lang.management.ClassLoadingMXBean; +import java.lang.management.ManagementFactory; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @ClassName:OOMTest + * @Description:模拟类加载溢出(元空间oom) + * @author diandian.zhang + * @date 2017年4月27日上�9:45:40 + */ +public class OOMTest { + public static void main(String[] args) { + try { + //准备url + URL url = new File("D:/58workplace/11study/src/main/java/jdk8").toURI().toURL(); + URL[] urls = {url}; + //获�有关类型加载的JMX接� + ClassLoadingMXBean loadingBean = ManagementFactory.getClassLoadingMXBean(); + //用于缓存类加载器 + List classLoaders = new ArrayList(); + while (true) { + //加载类型并缓存类加载器实例 + ClassLoader classLoader = new URLClassLoader(urls); + classLoaders.add(classLoader); + classLoader.loadClass("ClassA"); + //显示数�信�(共加载过的类型数目,当�还有效的类型数目,已�被�载的类型数目) + System.out.println("total: " + loadingBean.getTotalLoadedClassCount()); + System.out.println("active: " + loadingBean.getLoadedClassCount()); + System.out.println("unloaded: " + loadingBean.getUnloadedClassCount()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} +```` 为了快速溢出,设置�数:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=80m,�行结果如下: -![](http://blog.itpub.net/ueditor/php/upload/image/20190817/1566036772261654.png) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404215337.png) - 上图�实了,我们的JDK8中类加载(方法区的功能)已��在永久代PerGem中了,而是Metaspace中。�以��JVisualVM�看,更直观一些。 +上图�实了,我们的JDK8中类加载(方法区的功能)已��在永久代PerGem中了,而是Metaspace中。�以��JVisualVM�看,更直观一些。 -##  四�总结 +## 四�总结 本文讲解了元空间(Metaspace)的由�和本质,常用�置,以�监控测试。元空间的大�是动��更的,但�是无�大的,最好也时常关注一下大�,以�影��务器内存。 - - - - - - ## �考文章 - +https://segmentfault.com/a/1190000009707894 - +https://www.cnblogs.com/hysum/p/7100874.html - +http://c.biancheng.net/view/939.html - +https://www.runoob.com https://blog.csdn.net/android_hl/article/details/53228348 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\2722\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.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\272\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.md" similarity index 80% 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\2722\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.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\272\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.md" index e8a7e29..8048fb5 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2722\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.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\272\357\274\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\237\272\346\234\254\345\216\237\347\220\206\345\222\214\347\256\227\346\263\225.md" @@ -1,5 +1,4 @@ -# Table of Contents - +# 目录 * [JVM GC基本原�与GC算法](#jvm-gc基本原�与gc算法) * [Java关键术语](#java关键术语) * [Java HotSpot 虚拟机](#java-hotspot-虚拟机) @@ -15,7 +14,7 @@ * [垃圾回收中实例的终结](#垃圾回收中实例的终结) * [对象什么时候符�垃圾回收的�件?](#对象什么时候符�垃圾回收的�件?) * [GC Scope 示例程�](#gc-scope-示例程�) - * [[JVM GC算法](https://www.cnblogs.com/wupeixuan/p/8670341.html)](#[jvm-gc算法]httpswwwcnblogscomwupeixuanp8670341html) + * [JVM GC算法](#JVM GC算法) * [JVM垃圾判定算法](#jvm垃圾判定算法) * [引用计数算法(Reference Counting)](#引用计数算法reference-counting) * [�达性分�算法(根�索算法)](#�达性分�算法(根�索算法)) @@ -26,9 +25,7 @@ * [标记—整�算法(Mark-Compact)](#标记整�算法(mark-compact)) * [分代收集算法(Generational Collection)](#分代收集算法generational-collection) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + 本文转自互�网,侵删 @@ -53,8 +50,6 @@ ## JVM GC基本原�与GC算法 - - Java的内存分�与回收全部由JVM垃圾回收进程自动完�。与C语言��,Java开�者�需�自己编写代�实现垃圾回收。这是Java深�大家欢迎的众多特性之一,能够帮助程�员更好地编写Java程�。 下�四篇教程是了解Java 垃圾回收(GC)的基础: @@ -100,7 +95,7 @@ Java 垃圾回收是一项自动化的过程,用�管�程�所使用的� 虽然这个请求机制�供给程�员一个�动 GC 过程的机会,但是�动由 JVM负责。JVM�以拒�这个请求,所以并���这些调用都将执行垃圾回收。�动时机的选择由JVM决定,并且�决于堆内存中Eden区是��用。JVM将这个选择留给了Java规范的实现,��实现具体使用的算法�尽相�。 -毋庸置疑,我们知�垃圾回收过程是�能被强制执行的。我刚刚�现了一个调用`System.gc()`有�义的场景。通过这篇文章了解一下[适�调用System.gc() ](http://javapapers.com/core-java/system-gc-invocation-a-suitable-scenario/)这��端情况。 +毋庸置疑,我们知�垃圾回收过程是�能被强制执行的。我刚刚�现了一个调用`System.gc()`有�义的场景。通过这篇文章了解一下[适�调用System.gc()](http://javapapers.com/core-java/system-gc-invocation-a-suitable-scenario/)这��端情况。 ## ��GC的触�时机(When) @@ -125,11 +120,11 @@ Java 垃圾回收是一项自动化的过程,用�管�程�所使用的� 除直接调用System.gc外,触�Full GC执行的情况有如下四�。 -1. 旧生代空间�足 +1.旧生代空间�足 旧生代空间�有在新生代对象转入�创建为大对象�大数组时�会出现�足的现象,当执行Full GC�空间�然�足,则抛出如下错误: -java.lang.OutOfMemoryError: Java heap space  +java.lang.OutOfMemoryError:Javaheapspace 为��以上两�状况引起的Full GC,调优时应尽��到让对象在Minor GC阶段被回收�让对象在新生代多存活一段时间���创建过大的对象�数组。 @@ -137,7 +132,7 @@ java.lang.OutOfMemoryError: Java heap space  Permanet Generation中存放的为一些class的信�等,当系统中�加载的类��射的类和调用的方法较多时,Permanet Generation�能会被�满,在未�置为采用CMS GC的情况下会执行Full GC。如果�过Full GC�然回收�了,那么JVM会抛出如下错误信�: -java.lang.OutOfMemoryError: PermGen space  +java.lang.OutOfMemoryError:PermGenspace 为��Perm Gen�满造�Full GC现象,�采用的方法为增大Perm Gen空间或转为使用CMS GC。 @@ -149,7 +144,7 @@ promotion failed是在进行Minor GC时,survivor space放�下�对象�能 应对措施为:增大survivor space�旧生代空间或调低触�并�GC的比率,但在JDK 5.0+�6.0+的版本中有�能会由于JDK的bug29导致CMS在remark完毕�很久�触�sweeping动作。对于这�状况,�通过设置-XX: CMSMaxAbortablePrecleanTime=5(��为ms)���。 -4. 统计得到的Minor GC晋�到旧生代的平�大�大于旧生代的剩余空间 +4.统计得到的Minor GC晋�到旧生代的平�大�大于旧生代的剩余空间 这是一个较为��的触�情况,Hotspot为了��由于新生代对象晋�到旧生代导致旧生代空间�足的现象,在进行Minor GC时,�了一个判断,如果之�统计所得到的Minor GC晋�到旧生代的平�大�大于旧生代的剩余空间,那么就直接触�Full GC。 @@ -161,7 +156,7 @@ promotion failed是在进行Minor GC时,survivor space放�下�对象�能 ### 总结 -** Minor GC ,Full GC 触��件** +**Minor GC ,Full GC 触��件** Minor GC触��件:当Eden区满时,触�Minor GC。 @@ -201,7 +196,7 @@ JVM里有一�特殊的线程��VM Threads,专门用�执行一些特殊 Eden 区:当一个实例被创建了,首先会被存储在堆内存年轻代的 Eden 区中。 -注�:如果你�能�解这些�汇,我建议你阅读这篇 [垃圾回收介�](http://javapapers.com/java/java-garbage-collection-introduction/) ,这篇教程详细地介�了内存模型�JVM 架构以�这些术语。 +注�:如果你�能�解这些�汇,我建议你阅读这篇[垃圾回收介�](http://javapapers.com/java/java-garbage-collection-introduction/),这篇教程详细地介�了内存模型�JVM 架构以�这些术语。 Survivor 区(S0 和 S1):作为年轻代 GC(Minor GC)周期的一部分,存活的对象(�然被引用的)从 Eden 区被移动到 Survivor 区的 S0 中。类似的,垃圾回收器会扫� S0 然�将存活的实例移动到 S1 中。 @@ -209,7 +204,7 @@ Survivor 区(S0 和 S1):作为年轻代 GC(Minor GC)周期的一部分 死亡的实例(��被引用)被标记为垃圾回收。根�垃圾回收器(有四�常用的垃圾回收器,将在下一教程中介�它们)选择的��,�么被标记的实例都会��地从内存中移除,�么回收过程会在一个�独的进程中完�。 -�年代: �年代(Old or tenured generation)是堆内存中的第二�逻辑区。当垃圾回收器执行 Minor GC 周期时,在 S1 Survivor 区中的存活实例将会被晋�到�年代,而未被引用的对象被标记为回收。 +�年代:�年代(Old or tenured generation)是堆内存中的第二�逻辑区。当垃圾回收器执行 Minor GC 周期时,在 S1 Survivor 区中的存活实例将会被晋�到�年代,而未被引用的对象被标记为回收。 �年代 GC(Major GC):相对于 Java 垃圾回收过程,�年代是实例生命周期的最�阶段。Major GC 扫��年代的垃圾回收过程。如果实例��被引用,那么它们会被标记为回收,�则它们会继续留在�年代中。 @@ -217,7 +212,7 @@ Survivor 区(S0 和 S1):作为年轻代 GC(Minor GC)周期的一部分 ## 垃圾回收中实例的终结 -在释放一个实例和回收内存空间之�,Java 垃圾回收器会调用实例�自的 `finalize()` 方法,从而该实例有机会释放所�有的资�。虽然�以�� `finalize()` 会在回收内存空间之�被调用,但是没有指定的顺�和时间。多个实例间的顺�是无法被预知,甚至�能会并行�生。程��应该预先调整实例之间的顺�并使用 `finalize()` 方法回收资�。 +在释放一个实例和回收内存空间之�,Java 垃圾回收器会调用实例�自的`finalize()`方法,从而该实例有机会释放所�有的资�。虽然�以��`finalize()`会在回收内存空间之�被调用,但是没有指定的顺�和时间。多个实例间的顺�是无法被预知,甚至�能会并行�生。程��应该预先调整实例之间的顺�并使用`finalize()`方法回收资�。 * 任何在 finalize过程中未被�获的异常会自动被忽略,然�该实例的 finalize 过程被�消。 * JVM 规范中并没有讨论关于弱引用的垃圾回收机制,也没有很明确的�求。具体的实现都由实现方决定。 @@ -237,8 +232,8 @@ Survivor 区(S0 和 S1):作为年轻代 GC(Minor GC)周期的一部分 | 弱引用(Weak Reference) | 符�垃圾收集 | | 虚引用(Phantom Reference) | 符�垃圾收集 | -在编译过程中作为一�优化技术,Java 编译器能选择给实例赋 `null` 值,从而标记实例为�回收。 - +在编译过程中作为一�优化技术,Java 编译器能选择给实例赋`null`值,从而标记实例为�回收。 +```` class Animal { public static void main(String[] args) { @@ -258,140 +253,143 @@ Survivor 区(S0 和 S1):作为年轻代 GC(Minor GC)周期的一部分 } } - -在上�的类中,`lion` 对象在实例化行�从未被使用过。因此 Java 编译器作为一�优化措施�以直接在实例化行�赋值`lion = null`。因此,�使在 SOP 输出之�, finalize 函数也能够打�出 `'Rest in Peace!'`。我们�能�明这确定会�生,因为它�赖JVM的实现方�和�行时使用的内存。然而,我们还能学习到一点:如果编译器看到该实例在未��也�会被引用,能够选择并�早释放实例空间。 +```` +在上�的类中,`lion`对象在实例化行�从未被使用过。因此 Java 编译器作为一�优化措施�以直接在实例化行�赋值`lion = null`。因此,�使在 SOP 输出之�, finalize 函数也能够打�出`'Rest in Peace!'`。我们�能�明这确定会�生,因为它�赖JVM的实现方�和�行时使用的内存。然而,我们还能学习到一点:如果编译器看到该实例在未��也�会被引用,能够选择并�早释放实例空间。 * 关于对象什么时候符�垃圾回收有一个更好的例�。实例的所有属性能被存储在寄存器中,��寄存器将被访问并读�内容。无一例外,这些值将被写回到实例中。虽然这些值在将�能被使用,这个实例�然能被标记为符�垃圾回收。这是一个很�典的例�,�是�? * 当被赋值为null时,这是很简�的一个符�垃圾回收的示例。当然,��的情况�以�上�的几点。这是由 JVM 实现者所�的选择。目的是留下尽�能�的内存�用,加快�应速度,�高���。为了实现这一目标, JVM 的实现者�以选择一个更好的方案或算法在垃圾回收过程中回收内存空间。 -* 当 `finalize()` 方法被调用时,JVM 会释放该线程上的所有�步�。 +* 当`finalize()`方法被调用时,JVM 会释放该线程上的所有�步�。 ### GC Scope 示例程� - Class GCScope { - - GCScope t; - - static int i = 1; - - - - public static void main(String args[]) { - - GCScope t1 = new GCScope(); - - GCScope t2 = new GCScope(); - - GCScope t3 = new GCScope(); - - - - // No Object Is Eligible for GC - - - - t1.t = t2; // No Object Is Eligible for GC - - t2.t = t3; // No Object Is Eligible for GC - - t3.t = t1; // No Object Is Eligible for GC - - - - t1 = null; - - // No Object Is Eligible for GC (t3.t still has a reference to t1) - - - - t2 = null; - - // No Object Is Eligible for GC (t3.t.t still has a reference to t2) - - - - t3 = null; - - // All the 3 Object Is Eligible for GC (None of them have a reference. - - // only the variable t of the objects are referring each other in a - - // rounded fashion forming the Island of objects with out any external - - // reference) - - } - - - - protected void finalize() { - - System.out.println("Garbage collected from object" + i); - - i++; - - } - - - - class GCScope { - - GCScope t; - - static int i = 1; - - - - public static void main(String args[]) { - - GCScope t1 = new GCScope(); - - GCScope t2 = new GCScope(); - - GCScope t3 = new GCScope(); - - - - // 没有对象符�GC - - t1.t = t2; // 没有对象符�GC - - t2.t = t3; // 没有对象符�GC - - t3.t = t1; // 没有对象符�GC - - - - t1 = null; - - // 没有对象符�GC (t3.t �然有一个到 t1 的引用) - - - - t2 = null; - - // 没有对象符�GC (t3.t.t �然有一个到 t2 的引用) - - - - t3 = null; - - // 所有三个对象都符�GC (它们中没有一个拥有引用。 - - // �有�对象的�� t 还指�了彼此, - - // 形�了一个由对象组�的环形的岛,而没有任何外部的引用。) - - } - - - - protected void finalize() { - - System.out.println("Garbage collected from object" + i); - - i++; - - } -## [JVM GC算法](https://www.cnblogs.com/wupeixuan/p/8670341.html) +```` +Class GCScope { + + GCScope t; + + static int i = 1; + + + + public static void main(String args[]) { + + GCScope t1 = new GCScope(); + + GCScope t2 = new GCScope(); + + GCScope t3 = new GCScope(); + + + + // No Object Is Eligible for GC + + + + t1.t = t2; // No Object Is Eligible for GC + + t2.t = t3; // No Object Is Eligible for GC + + t3.t = t1; // No Object Is Eligible for GC + + + + t1 = null; + + // No Object Is Eligible for GC (t3.t still has a reference to t1) + + + + t2 = null; + + // No Object Is Eligible for GC (t3.t.t still has a reference to t2) + + + + t3 = null; + + // All the 3 Object Is Eligible for GC (None of them have a reference. + + // only the variable t of the objects are referring each other in a + + // rounded fashion forming the Island of objects with out any external + + // reference) + + } + + + + protected void finalize() { + + System.out.println("Garbage collected from object" + i); + + i++; + + } + + + +class GCScope { + + GCScope t; + + static int i = 1; + + + + public static void main(String args[]) { + + GCScope t1 = new GCScope(); + + GCScope t2 = new GCScope(); + + GCScope t3 = new GCScope(); + + + + // 没有对象符�GC + + t1.t = t2; // 没有对象符�GC + + t2.t = t3; // 没有对象符�GC + + t3.t = t1; // 没有对象符�GC + + + + t1 = null; + + // 没有对象符�GC (t3.t �然有一个到 t1 的引用) + + + + t2 = null; + + // 没有对象符�GC (t3.t.t �然有一个到 t2 的引用) + + + + t3 = null; + + // 所有三个对象都符�GC (它们中没有一个拥有引用。 + + // �有�对象的�� t 还指�了彼此, + + // 形�了一个由对象组�的环形的岛,而没有任何外部的引用。) + + } + + + + protected void finalize() { + + System.out.println("Garbage collected from object" + i); + + i++; + + } +} +```` +## JVM GC算法 在判断哪些内存需�回收和什么时候回收用到GC 算法,本文主�对GC 算法进行讲解。 @@ -433,7 +431,6 @@ public class ReferenceCountingGC { �行结果 ``` - [GC (System.gc()) [PSYoungGen: 3329K->744K(38400K)] 3329K->752K(125952K), 0.0341414 secs] [Times: user=0.00 sys=0.00, real=0.06 secs] [Full GC (System.gc()) [PSYoungGen: 744K->0K(38400K)] [ParOldGen: 8K->628K(87552K)] 752K->628K(125952K), [Metaspace: 3450K->3450K(1056768K)], 0.0060728 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] Heap @@ -457,7 +454,7 @@ Process finished with exit code 0 从GC Roots(��具体实现对GC Roots有��的定义)作为起点,�下�索它们引用的对象,�以生�一棵引用树,树的节点视为�达对象,�之视为��达。 -![�达性分�算法](https://images.cnblogs.com/cnblogs_com/wupeixuan/1186116/o_4240985.jpg) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404215732.png) 在Java语言中,�以作为GC Roots的对象包括下�几�: @@ -524,8 +521,7 @@ Stop-the-world�味� JVM由于�执行GC而�止了应用程�的执行, * 标记和清除过程效率都�高 * 会产生大�碎片,内存碎片过多�能导致无法给大对象分�内存。 -![标记-清除](https://images.cnblogs.com/cnblogs_com/wupeixuan/1186116/o_a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404215805.png) ## �制算法(Copying) 将内存划分为大�相等的两�,�次�使用其中一�,当这一�内存用完了就将还存活的对象�制到�一�上�,然��把使用过的内存空间进行一次清�。 @@ -537,7 +533,7 @@ Stop-the-world�味� JVM由于�执行GC而�止了应用程�的执行, * 将内存缩�为原�的一�,浪费了一�的内存空间,代价太高;如果�想浪费一�的空间,就需�有�外的空间进行分�担�,以应对被使用的内存中所有对象都100%存活的�端情况,所以在�年代一般�能直接选用这�算法。 * �制收集算法在对象存活率较高时就�进行较多的�制�作,效率将会�低。 -![�制](https://images.cnblogs.com/cnblogs_com/wupeixuan/1186116/o_e6b733ad-606d-4028-b3e8-83c3a73a3797.jpg) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404215817.png) ## 标记—整�算法(Mark-Compact) @@ -547,7 +543,7 @@ Stop-the-world�味� JVM由于�执行GC而�止了应用程�的执行, 效率�高,�仅�标记存活对象,还�整�所有存活对象的引用地�,在效率上�如�制算法。 -![标记—整�](https://images.cnblogs.com/cnblogs_com/wupeixuan/1186116/o_902b83ab-8054-4bd2-898f-9a4a0fe52830.jpg) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404215845.png) ## 分代收集算法(Generational Collection) @@ -569,20 +565,4 @@ Stop-the-world�味� JVM由于�执行GC而�止了应用程�的执行, https://blog.csdn.net/android_hl/article/details/53228348 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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\272\357\274\232JVM\345\270\270\347\224\250\345\217\202\346\225\260\344\273\245\345\217\212\350\260\203\344\274\230\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\272\357\274\232JVM\345\270\270\347\224\250\345\217\202\346\225\260\344\273\245\345\217\212\350\260\203\344\274\230\345\256\236\350\267\265.md" new file mode 100644 index 0000000..caac027 --- /dev/null +++ "b/docs/Java/JVM/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\272\357\274\232JVM\345\270\270\347\224\250\345\217\202\346\225\260\344\273\245\345\217\212\350\260\203\344\274\230\345\256\236\350\267\265.md" @@ -0,0 +1,659 @@ +# 目录 + + * [JVM优化的必�性](#jvm优化的必�性) + * [JVM调优原则](#jvm调优原则) + * [JVM�行�数设置](#jvm�行�数设置) + * [JVM性能调优工具](#jvm性能调优工具) + * [常用调优策略](#常用调优策略) + * [六�JVM调优实例](#六�jvm调优实例) + * [七�一个具体的实战案例分�](#七�一个具体的实战案例分�) + * [�考资料](#�考资料) + * [�考文章](#�考文章) + + + + +![](https://pic.rmb.bdstatic.com/bjh/down/97522789c423e19931adafa65bd5424d.png) + +## JVM优化的必�性 + +**1.1: 项目上线�,什么原因使得我们需�进行jvm调优** + +1)�垃圾太多(java线程,对象�满内存),内存�满了,程�跑�动了�� +2)�垃圾回收线程太多,频�地回收垃圾(垃圾回收线程本身也会�用资�: �用内存,cpu资�),导致程�性能下� +3)�回收垃圾频�导致STW + +因此基于以上的原因,程�上线�,必须进行调优,�则程�性能就无法��;也就是程�上线�,必须设置��的垃圾回收策略; + +**1.2: jvm调优的本质是什么??** + +答案: 回收垃圾,�时回收没有用垃圾对象,�时释放掉内存空间 + +**1.3: 基于�务器环境,jvm堆内存到底应用设置多少内存?** + +1�32�的�作系统 --- 寻�能力 2^32 = 4GB ,最大的能支�4gb; jvm�以分� 2g+ + +2�64�的�作系统 --- 寻�能力 2^64 = 16384PB , 高性能计算机(IBM Z unix 128G 200+) + +![](https://pic.rmb.bdstatic.com/bjh/down/aae367a929e2d1361975942091ffadfe.png) + +Jvm堆内存�能设置太大,�则会导致寻�垃圾的时间过长,也就是导致整个程�STW, 也�能设置太�,�则会导致回收垃圾过于频�; + +**1.4:总结** + +如果你�到以下情况,就需�考虑进行JVM调优了: + +* Heap内存(�年代)�续上涨达到设置的最大内存值; + +* Full GC 次数频�; + +* GC �顿时间过长(超过1秒); + +* 应用出现OutOfMemory 等内存异常; + +* 应用中有使用本地缓存且�用大�内存空间; + +* 系统���与�应性能�高或下�。 + +## JVM调优原则 + +![](https://pic.rmb.bdstatic.com/bjh/down/7cd4f5c19ab4f5f5d3087422097ee931.png) + +JVM调优是一个手段,但并�一定所有问题都�以通过JVM进行调优解决,因此,在进行JVM调优时,我们��循一些原则: + +* 大多数的Java应用�需�进行JVM优化; + +* 大多数导致GC问题的原因是代�层�的问题导致的(代�层�); + +* 上线之�,应先考虑将机器的JVM�数设置到最优; + +* �少创建对象的数�(代�层�); + +* �少使用全局��和大对象(代�层�); + +* 优先架构调优和代�调优,JVM优化是�得已的手段(代��架构层�); + +* 分�GC情况优化代�比优化JVM�数更好(代�层�); + +通过以上原则,我们�现,其实最有效的优化手段是架构和代�层�的优化,而JVM优化则是最��得已的手段,也�以说是对�务器�置的最�一次“压榨�。 + +**2.1 JVM调优目标** + +调优的最终目的都是为了令应用程�使用最�的硬件消耗�承载更大的��。jvm调优主�是针对垃圾收集器的收集性能优化,令�行在虚拟机上的应用能够使用更少的内存以�延迟获�更大的���。 + +* 延迟:GC低�顿和GC低频率; + +* 低内存�用; + +* 高���; + +其中,任何一个属性性能的�高,几乎都是以牺牲其他属性性能的�为代价的,��兼得。具体根�在业务中的��性确定。 + +**2.2 JVM调优�化目标** + +下�展示了一些JVM调优的�化目标�考实例: + +* Heap 内存使用率 <= 70%; + +* Old generation内存使用率<= 70%; + +* avgpause <= 1秒; + +* Full gc 次数0 或 avg pause interval >= 24�时 ; + +注�:��应用的JVM调优�化目标是�一样的。 + +**2.3 JVM调优的步骤** + +一般情况下,JVM调优�通过以下步骤进行: + +* 分�GC日志�dump文件,判断是�需�优化,确定瓶颈问题点; + +* 确定JVM调优�化目标; + +* 确定JVM调优�数(根�历�JVM�数�调整); + +* �次调优内存�延迟����等指标; + +* 对比观察调优��的差异; + +* �断的分�和调整,直到找到�适的JVM�数�置; + +* 找到最�适的�数,将这些�数应用到所有�务器,并进行�续跟踪。 + +以上�作步骤中,�些步骤是需�多次�断迭代完�的。一般是从满足程�的内存使用需求开始的,之�是时间延迟的�求,最��是���的�求,�基于这个步骤��断优化,�一个步骤都是进行下一步的基础,��逆行之。 + +![](https://pic.rmb.bdstatic.com/bjh/down/c271890a3714d538ed3362073c66893d.png) + +**调优原则总结** + +JVM的自动内存管�本�就是为了将开�人员从内存管�的泥潭里拉出�。JVM调优�是常规手段,性能问题一般第一选择是优化程�,最�的选择�是进行JVM调优。 + +�使�得�进行JVM调优,也�对�能�脑门就去调整�数,一定�全�监控,详细分�性能数�。 + +**附录:系统性能优化指导** + +![](https://pic.rmb.bdstatic.com/bjh/down/00d90cef8369568f8581a9850dcd42e6.png) + +### JVM�行�数设置 + +**3.1�堆�数设置** + +**-XX:+PrintGC**使用这个�数,虚拟机�动�,���到GC就会打�日志 + +**-XX:+UseSerialGC**�置串行回收器 + +**-XX:+PrintGCDetails**�以查看详细信�,包括�个区的情况 + +**-Xms**设置Java程��动时�始化堆大� + +**-Xmx**设置Java程�能获得最大的堆大� + +**-Xmx20m -Xms5m -XX:+PrintCommandLineFlags**�以将��或者显示传给虚拟机的�数输出 + +**3.2�新生代�数�置** + +**-Xmn**�以设置新生代的大�,设置一个比较大的新生代会�少�年代的大�,这个�数对系统性能以�GC行为有很大的影�,新生代大�一般会设置整个堆空间的1/3到1/4左� + +**-XX:SurvivorRatio**用�设置新生代中eden空间和from/to空间的比例。�义:-XX:SurvivorRatio=eden/from**/**eden/to + +��的堆分布情况,对系统执行会产生一定的影�,在实际工作中,应该根�系统的特点�出��的�置,基本策略:尽�能将对象预留在新生代,�少�年代的GC次数 + +除了�以设置新生代的�对大�(-Xmn),还�以使用(-XX:NewRatio)设置新生代和�年代的比例:-XX:NewRatio=�年代/新生代 + +**�置�行时�数:** + +-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC + +**3.3�堆溢出�数�置** + +在Java程�在�行过程中,如果对空间�足,则会抛出内存溢出的错误(Out Of Memory)OOM,一旦这类问题�生在生产环境,则�能引起严�的业务中断,Java虚拟机�供了**-XX:+ +HeapDumpOnOutOfMemoryError**,使用该�数�以在内存溢出时导出整个堆信�,与之��使用的还有�数**-XX:HeapDumpPath**,�以设置导出堆的存放路径 + +内存分�工具:Memory Analyzer + +**�置�行时�数**-Xms1m -Xmx1m -XX:+ +HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/Demo3.dump + +**3.4�栈�数�置** + +Java虚拟机�供了�数**-Xss**�指定线程的最大栈空间,整个�数也直接决定了函数�调用的最大深度。 + +**�置�行时�数:**-Xss1m + +3.5�方法区�数�置 + +和Java堆一样,方法区是一�所有线程共享的内存区域,它用于�存系统的类信�,方法区(永久区)�以�存多少信��以对其进行�置,在默认情况下,**-XX:MaxPermSize**为64M,如果系统�行时生产大�的类,就需�设置一个相对�适的方法区,以�出现永久区内存溢出的问题 + +-XX:PermSize=64M -XX:MaxPermSize=64M + +**3.6�直接内存�数�置** + +直接内存也是Java程�中�常��的组�部分,特别是广泛用在NIO中,直接内存跳过了Java堆,使用Java程��以直接访问原生堆空间,因此在一定程度上加快了内存空间的访问速度 + +但是说直接内存一定就�以�高内存访问速度也��得,具体情况具体分� + +**相关�置�数:-XX:MaxDirectMemorySize**,如果�设置,默认值为最大堆空间,�-Xmx。直接内存使用达到上�时,就会触�垃圾回收,如果�能有效的释放空间,就会引起系统的OOM + +**3.7�对象进入�年代的�数�置** + +一般而言,对象首次创建会被放置在新生代的eden区,如果没有GC介入,则对象�会离开eden区,那么eden区的对象如何进入�年代呢? + +通常情况下,��对象的年龄达到一定的大�,就会自动离开年轻代进入�年代,对象年龄是由对象�历数次GC决定的,在新生代�次GC之�如果对象没有被回收,则年龄加1 + +虚拟机�供了一个�数�控制新生代对象的最大年龄,当超过这个年龄范围就会晋��年代 + +**-XX:MaxTenuringThreshold**,默认情况下为15 + +**�置�行时�数:**-Xmx64M -Xms64M -XX:+PrintGCDetails + +**结论**:对象首次创建会被放置在新生代的eden区,因此输出结果中from和to区都为0% + +根�设置MaxTenuringThreshold�数,�以指定新生代对象�过多少次回收�进入�年代。�外,大对象新生代eden区无法装入时,也会直接进入�年代。 + +JVM里有个�数�以设置对象的大�超过在指定的大�之�,直接晋��年代 **-XX:PretenureSizeThreshold=15** + +�数:-Xmx1024M -Xms1024M -XX:+UseSerialGC -XX:MaxTenuringThreshold=15 -XX:+PrintGCDetails + +使用PretenureSizeThreshold�以进行指定进入�年代的对象大�,但是�注�TLAB区域优先分�空间。虚拟机对于体积�大的对象 会优先把数�分�到TLAB区域中,因此就失去了在�年代分�的机会 + +�数:-Xmx30M -Xms30M -XX:+UseSerialGC -XX:+PrintGCDetails +-XX:PretenureSizeThreshold=1000 -XX:-UseTLAB + +**3.8�TLAB�数�置** + +TLAB全称是Thread Local Allocation Buffer,�线程本地分�缓存,从�字上看是一个线程专用的内存分�区域,是为了加速对象分�对象而生的。�一个线程都会产生一个TLAB,该线程独享的工作区域,Java虚拟机使用这�TLAB区���多线程冲�问题,�高了对象分�的效率 + +TLAB空间一般�会太大,当大对象无法在TLAB分�时,则会直接分�到堆上 + +**-XX:+UseTLAB**使用TLAB + +**-XX:+TLABSize**设置TLAB大� + +**-XX:TLABRefillWasteFraction**设置维护进入TLAB空间的�个对象大�,它是一个比例值,默认为64,�如果对象大于整个空间的1/64,则在堆创建对象 + +**-XX:+PrintTLAB**查看TLAB信� + +**-XX:ResizeTLAB**自调整TLABRefillWasteFraction阈值 + +�数:-XX:+UseTLAB -XX:+PrintTLAB -XX:+PrintGC -XX:TLABSize=102400 -XX:-ResizeTLAB +-XX:TLABRefillWasteFraction=100 -XX:-DoEscapeAnalysis -server + +内存�数 + + + + + +![](https://pics7.baidu.com/feed/bba1cd11728b47102f9e1e7af46fe7f6fd03237b.png@f_auto?token=ca9d5541411861cfb09e792849a82a06) + + + + + +## JVM性能调优工具 + +这个篇幅在这里就�过多介�了,�以�照: + +深入�解JVM虚拟机——Java虚拟机的监控�诊断工具大全 + +## 常用调优策略 + +这里还是��一下,�时确定�进行JVM调优,也��陷入“知�障�,进行分�之�,�现�以通过优化程���性能,�然首选优化程�。 + +**5.1�选择�适的垃圾回收器** + +CPU�核,那么毫无疑问Serial 垃圾收集器是你唯一的选择。 + +CPU多核,关注��� ,那么选择PS+PO组�。 + +CPU多核,关注用户�顿时间,JDK版本1.6或者1.7,那么选择CMS。 + +CPU多核,关注用户�顿时间,JDK1.8�以上,JVM�用内存6G以上,那么选择G1。 + +�数�置: + +> //设置Serial垃圾收集器(新生代) +> +> 开�:-XX:+UseSerialGC +> +> //设置PS+PO,新生代使用功能Parallel Scavenge �年代将会使用Parallel Old收集器 +> +> 开� -XX:+UseParallelOldGC +> +> //CMS垃圾收集器(�年代) +> +> 开� -XX:+UseConcMarkSweepGC +> +> //设置G1垃圾收集器 +> +> 开� -XX:+UseG1GC + +**5.2�调整内存大�** + +现象:垃圾收集频率�常频�。 + +原因:如果内存太�,就会导致频�的需�进行垃圾收集�能释放出足够的空间�创建新的对象,所以增加堆内存大�的效果是�常显而易�的。 + +注�:如果垃圾收集次数�常频�,但是�次能回收的对象�常少,那么这个时候并�内存太�,而�能是内存泄露导致对象无法回收,从而造�频�GC。 + +�数�置: + +> //设置堆�始值 +> +> 指令1:-Xms2g +> +> 指令2:-XX:InitialHeapSize=2048m +> +> //设置堆区最大值 +> +> 指令1:`-Xmx2g` +> +> 指令2: -XX:MaxHeapSize=2048m +> +> //新生代内存�置 +> +> 指令1:-Xmn512m +> +> 指令2:-XX:MaxNewSize=512m + +**5.3�设置符�预期的�顿时间** + +**现象**:程�间接性的�顿 + +**原因**:如果没有确切的�顿时间设定,垃圾收集器以���为主,那么垃圾收集时间就会�稳定。 + +**注�**:��设置�切实际的�顿时间,�次时间越短也�味�需�更多的GC次数�能回收完原有数�的垃圾. + +**�数�置**: + +> //GC�顿时间,垃圾收集器会�试用��手段达到这个时间 +> +> -XX:MaxGCPauseMillis + +**5.4�调整内存区域大�比率** + +现象:�一个区域的GC频�,其他都正常。 + +原因:如果对应区域空间�足,导致需�频�GC�释放空间,在JVM堆内存无法增加的情况下,�以调整对应区域的大�比率。 + +注�:也许并�空间�足,而是因为内存泄造�内存无法回收。从而导致GC频�。 + +�数�置: + +> //survivor区和Eden区大�比率 +> +> 指令:-XX:SurvivorRatio=6 //S区和Eden区�新生代比率为1:6,两个S区2:6 +> +> //新生代和�年代的�比 +> +> -XX:NewRatio=4 //表示新生代:�年代 = 1:4 ��年代�整个堆的4/5;默认值=2 + +**5.5�调整对象��年代的年龄** + +**现象**:�年代频�GC,�次回收的对象很多。 + +**原因**:如果�代年龄�,新生代的对象很快就进入�年代了,导致�年代对象�多,而这些对象其实在��的很短时间内就�以回收,这时候�以调整对象的�级代年龄,让对象�那么容易进入�年代解决�年代空间�足频�GC问题。 + +**注�**:增加了年龄之�,这些对象在新生代的时间会�长�能导致新生代的GC频率增加,并且频��制这些对象新生的GC时间也�能�长。 + +�置�数: + +> //进入�年代最�的GC年龄,年轻代对象转�为�年代对象最�年龄值,默认值7 +> +> -XX:InitialTenuringThreshol=7 + +**5.6�调整大对象的标准** + +**现象**:�年代频�GC,�次回收的对象很多,而且�个对象的体积都比较大。 + +**原因**:如果大�的大对象直接分�到�年代,导致�年代容易被填满而造�频�GC,�设置对象直接进入�年代的标准。 + +**注�**:这些大对象进入新生代��能会使新生代的GC频率和时间增加。 + +�置�数: + +> //新生代�容纳的最大对象,大于则直接会分�到�年代,0代表没有�制。 +> +> -XX:PretenureSizeThreshold=1000000 + +**5.7�调整GC的触�时机** + +**现象**:CMS,G1 �常 Full GC,程��顿严�。 + +**原因**:G1和CMS 部分GC阶段是并�进行的,业务线程和垃圾收集线程一起工作,也就说明垃圾收集的过程中业务线程会生�新的对象,所以在GC的时候需�预留一部分内存空间�容纳新产生的对象,如果这个时候内存空间�足以容纳新产生的对象,那么JVM就会�止并�收集暂�所有业务线程(STW)���垃圾收集的正常�行。这个时候�以调整GC触�的时机(比如在�年代�用60%就触�GC),这样就�以预留足够的空间�让业务线程创建的对象有足够的空间分�。 + +**注�**:�早触�GC会增加�年代GC的频率。 + +�置�数: + +> //使用多少比例的�年代�开始CMS收集,默认是68%,如果频��生SerialOld�顿,应该调� +> +> -XX:CMSInitiatingOccupancyFraction +> +> //G1混�垃圾回收周期中�包括的旧区域设置�用率阈值。默认�用率为 65% +> +> -XX:G1MixedGCLiveThresholdPercent=65 + +5.8�调整 JVM本地内存大� + +**现象**:GC的次数�时间和回收的对象都正常,堆内存空间充足,但是报OOM + +**原因**: JVM除了堆内存之外还有一�堆外内存,这片内存也�本地内存,�是这�内存区域�足了并�会主动触�GC,�有在堆内存区域触�的时候顺带会把本地内存回收了,而一旦本地内存分��足就会直接报OOM异常。 + +**注�**: 本地内存异常的时候除了上�的现象之外,异常信��能是OutOfMemoryError:Direct buffer memory。 解决方�除了调整本地内存大�之外,也�以在出现此异常时进行�获,手动触�GC(System.gc())。 + +�置�数: + +> XX:MaxDirectMemorySize + +## 六�JVM调优实例 + +整�的一些JVM调优实例: + +**6.1�网站���览�暴增�,网站�应页��很慢** + +> 1�问题推测:在测试环境测速度比较快,但是一到生产就�慢,所以推测�能是因为垃圾收集导致的业务线程�顿。 +> +> 2�定�:为了确认推测的正确性,在线上通过jstat -gc 指令 看到JVM进行GC 次数频率�常高,GC所�用的时间�常长,所以基本推断就是因为GC频率�常高,所以导致业务线程�常�顿,从而造�网页�应很慢。 +> +> 3�解决方案:因为网页访问�很高,所以对象创建速度�常快,导致堆内存容易填满从而频�GC,所以这里问题在于新生代内存太�,所以这里�以增加JVM内存就行了,所以�步从原�的2G内存增加到16G内存。 +> +> 4�第二个问题:增加内存�的确平常的请求比较快了,但是�出现了�外一个问题,就是�定期的会间断性的�顿,而且�次�顿的时间�比之��长很多。 +> +> 5�问题推测:练习到是之�的优化加大了内存,所以推测�能是因为内存加大了,从而导致�次GC的时间�长从而导致间接性的�顿。 +> +> 6�定�:还是通过jstat -gc 指令 查看到 的确FGC次数并�是很高,但是花费在FGC上的时间是�常高的,根�GC日志 查看到�次FGC的时间有达到几�秒的。 +> +> 7�解决方案: 因为JVM默认使用的是PS+PO的组�,PS+PO垃圾标记和收集阶段都是STW,所以内存加大了之�,需�进行垃圾回收的时间就�长了,所以这里�想���次GC时间过长,所以需�更�并�类的收集器,因为当�的JDK版本为1.7,所以最�选择CMS垃圾收集器,根�之�垃圾收集情况设置了一个预期的�顿的时间,上线�网站�也没有了�顿问题。 + +**6.2���导出数�引�的OOM** + +**问题�述:**公�的��系统,��性的引�OOM异常,堆内存溢出。 + +> 1�因为是��性的,所以第一次简�的认为就是堆内存�足导致,所以�方�的加大了堆内存从4G调整到8G。 +> +> 2�但是问题�然没有解决,�能从堆内存信�下手,通过开�了-XX:+ +> HeapDumpOnOutOfMemoryError�数 获得堆内存的dump文件。 +> +> 3�VisualVM 对 堆dump文件进行分�,通过VisualVM查看到�用内存最大的对象是String对象,本�想跟踪�String对象找到其引用的地方,但dump文件太大,跟踪进去的时候总是�死,而String对象�用比较多也比较正常,最开始也没有认定就是这里的问题,于是就从线程信�里�找�破点。 +> +> 4�通过线程进行分�,先找到了几个正在�行的业务线程,然��一跟进业务线程看了下代�,�现有个引起我注�的方法,导出订�信�。 +> +> 5�因为订�信�导出这个方法�能会有几万的数��,首先�从数�库里�查询出�订�信�,然�把订�信�生�excel,这个过程会产生大�的String对象。 +> +> 6�为了验�自己的猜想,于是准备登录��去测试下,结果在测试的过程中�现到处订�的按钮�端居然没有�点击�按钮置�交互事件,结果按钮�以一直点,因为导出订�数�本�就�常慢,使用的人员�能�现点击�很久�页�都没�应,结果就一直点,结果就大�的请求进入到��,堆内存产生了大�的订�对象和EXCEL对象,而且方法执行�常慢,导致这一段时间内这些对象都无法被回收,所以最终导致内存溢出。 +> +> 7�知�了问题就容易解决了,最终没有调整任何JVM�数,�是在�端的导出订�按钮上加上了置�状�,等�端�应之�按钮��以进行点击,然��少了查询订�信�的�必�字段��少生�对象的体积,然�问题就解决了。 + +**6.3��个缓存数�过大导致的系统CPU飚高** + +> 1�系统�布��现CPU一直飚高到600%,�现这个问题�首先��的是定�到是哪个应用�用CPU高,通过top 找到了对应的一个java应用�用CPU资�600%。 +> +> 2�如果是应用的CPU飚高,那么基本上�以定��能是�资�竞争,或者是频�GC造�的。 +> +> 3�所以准备首先从GC的情况排查,如果GC正常的��从线程的角度排查,首先使用jstat -gc PID 指令打�出GC的信�,结果得到得到的GC 统计信�有明显的异常,应用在�行了�几分钟的情况下GC的时间就�用了482秒,那么问这很明显就是频�GC导致的CPU飚高。 +> +> 4�定�到了是GC的问题,那么下一步就是找到频�GC的原因了,所以�以从两方�定�了,�能是哪个地方频�创建对象,或者就是有内存泄露导致内存回收�掉。 +> +> 5�根�这个�路决定把堆内存信�dump下�看一下,使用jmap -dump 指令把堆内存信�dump下�(堆内存空间大的慎用这个指令�则容易导致会影�应用,因为我们的堆内存空间�2G所以也就没考虑这个问题了)。 +> +> 6�把堆内存信�dump下��,就使用visualVM进行离线分�了,首先从�用内存最多的对象中查找,结果排�第三看到一个业务VO�用堆内存约10%的空间,很明显这个对象是有问题的。 +> +> 7�通过业务对象找到了对应的业务代�,通过代�的分�找到了一个�疑之处,这个业务对象是查看新闻资讯信�生�的对象,由于想��查询的效率,所以把新闻资讯�存到了redis缓存里�,�次调用资讯接�都是从缓存里�获�。 +> +> 8�把新闻�存到redis缓存里�这个方�是没有问题的,有问题的是新闻的50000多�数�都是�存在一个key里�,这样就导致�次调用查询新闻接�都会从redis里�把50000多�数�都拿出�,��筛选分页拿出10�返回给�端。50000多�数�也就�味�会产生50000多个对象,�个对象280个字节左�,50000个对象就有13.3M,这就�味���查看一次新闻信�就会产生至少13.3M的对象,那么并�请求���到10,那么�秒钟都会产生133M的对象,而这�大对象会被直接分�到�年代,这样的�一个2G大�的�年代内存,�需�几秒就会塞满,从而触�GC。 +> +> 9�知�了问题所在�那么就容易解决了,问题是因为�个缓存过大造�的,那么�需�把缓存��就行了,这里�需�把缓存以页的粒度进行缓存就行了,�个key缓存10�作为返回给�端1页的数�,这样的��次查询新闻信��会从缓存拿出10�数�,就��了此问题的 产生。 + +**6.4�CPU�常100% 问题定�** + +问题分�:CPU高一定是�个程�长期�用了CPU资�。 + +1�所以先需�找出那个进行�用CPU高。 + +> top 列出系统�个进程的资��用情况。 + +2�然�根�找到对应进行里哪个线程�用CPU高。 + +> top -Hp 进程ID 列出对应进程里�的线程�用资�情况 + +3�找到对应线程ID�,�打�出对应线程的堆栈信� + +> printf "%x\n" PID 把线程ID转�为16进制。 +> +> jstack PID 打�出进程的所有线程信�,从打�出�的线程信�中找到上一步转�为16进制的线程ID对应的线程信�。 + +4�最�根�线程的堆栈信�定�到具体业务方法,从代�逻辑中找到问题所在。 + +> 查看是�有线程长时间的watting 或blocked +> +> 如果线程长期处于watting状�下, 关注watting on xxxxxx,说明线程在等待这把�,然�根��的地�找到�有�的线程。 + +**6.5�内存飚高问题定�** + +分�: 内存飚高如果是�生在java进程上,一般是因为创建了大�对象所导致,�续飚高说明垃圾回收跟�上对象创建的速度,或者内存泄露导致对象无法回收。 + +1�先观察垃圾回收的情况 + +> jstat -gcPID 1000查看GC次数,时间等信�,�隔一秒打�一次。 +> +> jmap -histo PID | head -20 查看堆内存�用空间最大的�20个对象类型,��步查看是哪个对象�用了内存。 + +如果�次GC次数频�,而且�次回收的内存空间也正常,那说明是因为对象创建速度快导致内存一直�用很高;如果�次回收的内存�常少,那么很�能是因为内存泄露导致内存一直无法被回收。 + +2�导出堆内存文件快照 + +> jmap -dump:live,format=b,file=/home/myheapdump.hprof PID dump堆内存信�到文件。 + +3�使用visualVM对dump文件进行离线分�,找到�用内存高的对象,�找到创建该对象的业务代��置,从代�和业务场景中定�具体问题。 + +**6.6�数�分�平�系统频� Full GC** + +平�主�对用户在 App 中行为进行定时分�统计,并支�报表导出,使用 CMS GC 算法。 + +数�分�师在使用中�现系统页�打开�常�顿,通过 jstat 命令�现系统�次 Young GC �大约有 10% 的存活对象进入�年代。 + +原�是因为 Survivor 区空间设置过�,�次 Young GC �存活对象在 Survivor 区域放�下,��进入�年代。 + +通过调大 Survivor 区,使得 Survivor 区�以容纳 Young GC �存活对象,对象在 Survivor 区�历多次 Young GC 达到年龄阈值�进入�年代。 + +调整之��次 Young GC �进入�年代的存活对象稳定�行时仅几百 Kb,Full GC 频率大大�低。 + +**6.7�业务对接网关 OOM** + +网关主�消费 Kafka 数�,进行数�处�计算然�转�到�外的 Kafka 队列,系统�行几个�时候出现 OOM,��系统几个�时之�� OOM。 + +通过 jmap 导出堆内存,在 eclipse MAT 工具分��找出原因:代�中将�个业务 Kafka 的 topic 数�进行日志异步打�,该业务数��较大,大�对象堆积在内存中等待被打�,导致 OOM。 + +**6.8�鉴�系统频�长时间 Full GC** + +系统对外�供��账�鉴��务,使用时�现系统�常�务��用,通过 Zabbix 的监控平�监控�现系统频��生长时间 Full GC,且触�时�年代的堆内存通常并没有�满,�现原�是业务代�中调用了 + +## 七�一个具体的实战案例分� + +**7.1 典型调优�数设置** + +> �务器�置: 4cpu,8GB内存 ---- jvm调优实际上是设置一个��大�的jvm堆内存(既�能太大,也�能太�) + +> -Xmx3550m 设置jvm堆内存最大值 (�验值设置: 根�压力测试,根�线上程��行效果情况) +> +> -Xms3550m 设置jvm堆内存�始化大�,一般情况下必须设置此值和最大的最大的堆内存空间��一致,防止内存抖动,消耗性能 +> +> -Xmn2g 设置年轻代�用的空间大� +> +> -Xss256k 设置线程堆栈的大�;jdk5.0以�默认线程堆栈大�为1MB; 在相�的内存情况下,��堆栈大�,�以使得�作系统创建更多的业务线程; + +jvm堆内存设置: + +> nohup java -Xmx3550m -Xms3550m -Xmn2g -Xss256k -jar jshop-web-1.0-SNAPSHOT.jar --spring.addition-location=application.yaml > jshop.log 2>&1 >< + +TPS性能曲线: + +![](https://pic.rmb.bdstatic.com/bjh/down/ee3949fc51c360330ece8f6729cd0560.png) + +**7.2 分�gc日志** + +如果需�分�gc日志,就必须使得�务gc输入gc详情到log日志文件中,然�使用相应gc日志分�工具�对日志进行分���; + +把gc详情输出到一个gc.log日志文件中,便于gc分� + +> -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log + +Throughput: 业务线程执行时间 / (gc时间+业务线程时间) + +![](https://pic.rmb.bdstatic.com/bjh/down/25529b786b50b8b30067e6f721101369.png) + +分�gc日志,�现,一开始就�生了3次fullgc,很明显jvm优化�数的设置是有问题的; + +![](https://pic.rmb.bdstatic.com/bjh/down/e70f3ffedc03bc0b387ff860cb8c948b.png) + +查看fullgc�生问题原因: jstat -gcutil pid + +![](https://pic.rmb.bdstatic.com/bjh/down/0e135e59c1e140aaded9223edd2f0177.png) + +Metaspace�久代: �始化分�大�20m , 当metaspace被�满�,必须对�久代进行扩容,如果metaspace�进行一次扩容,fullgc就需�执行一次;(fullgc回收整个堆空间,�常�用时间) + +调整gc�置: 修改永久代空间�始化大�: + +> nohup java -Xmx3550m -Xms3550m -Xmn2g -Xss256k -XX:MetaspaceSize=256m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop-web-1.0-SNAPSHOT.jar --spring.addition-location=application.yaml > jshop.log 2>&1 >< + +�过调优�,fullgc现象已�消失了: + +![](https://pic.rmb.bdstatic.com/bjh/down/0f8e8b90a8f9d810759ae43b3b0ed1b9.png) + +**7.3 Young&Old比例** + +年轻代和�年代比例:1:2 �数:-XX:NewRetio = 4 , 表示年轻代(eden,s0,s1)和�年代所�比值为1:4 + +1) -XX:NewRetio = 4 + +![](https://pic.rmb.bdstatic.com/bjh/down/f8353c852a2f65626b5b3a23f9616208.png) + +年轻代分�的内存大���了,这样YGC次数�多了,虽然fullgc��生了,但是YGC花费的时间更多了� + +2) -XX:NewRetio = 2 YGC�生的次数必然会�少;因为eden区域的大��大了,因此YGC就会�少; + +![](https://pic.rmb.bdstatic.com/bjh/down/11ca145ee5a93f511feda69e749582cb.png) + +7.4 Eden&S0S1 + +为了进一步�少YGC, �以设置 enden ,s 区域的比值大�; 设置方�: -XX:SurvivorRatio=8 + +1) 设置比值:8:1:1 + +![](https://pic.rmb.bdstatic.com/bjh/down/6330cbdd9acd6a5c18e91c5bea13cca2.png) + +2) Xmn2g 8:1:1 + +nohup java -Xmx3550m -Xms3550m -Xmn2g -XX:SurvivorRatio=8 -Xss256k -XX:MetaspaceSize=256m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop-web-1.0-SNAPSHOT.jar --spring.addition-location=application.yaml > jshop.log 2>&1 >< + +根�gc调优,垃圾回收次数,时间,���都是一个比较优的一个�置; + +![](https://pic.rmb.bdstatic.com/bjh/down/68edd8bfceb7d5e85d18a53e150cad2a.png) + +**7.5 ���优先** + +使用并行的垃圾回收器,�以充分利用多核心cpu�帮助进行垃圾回收;这样的gc方�,就�����优先的调优方� + +垃圾回收器组�: ps(parallel scavenge) + po (parallel old) 此垃圾回收器是Jdk1.8 默认的垃圾回收器组�; + +> nohup java -Xmx3550m -Xms3550m -Xmn2g -XX:SurvivorRatio=8 -Xss256k -XX:+UseParallelGC -XX:UseParallelOldGC -XX:MetaspaceSize=256m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop-web-1.0-SNAPSHOT.jar --spring.addition-location=application.yaml > jshop.log 2>&1 >< + +**7.6 �应时间优先** + +使用cms垃圾回收器,就是一个�应时间优先的组�; cms垃圾回收器(垃圾回收和业务线程交�执行,�会让业务线程进行�顿stw)尽�能的�少stw的时间,因此使用cms垃圾回收器组�,是�应时间优先组� + +> nohup java -Xmx3550m -Xms3550m -Xmn2g -XX:SurvivorRatio=8 -Xss256k -XX:+UseParNewGC -XX:UseConcMarkSweepGC -XX:MetaspaceSize=256m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop-web-1.0-SNAPSHOT.jar --spring.addition-location=application.yaml > jshop.log 2>&1 >< + +�以�现,cms垃圾回收器时间�长; + +**7.7 g1** + +�置方�如下所示: + +> nohup java -Xmx3550m -Xms3550m -Xmn2g -XX:SurvivorRatio=8 -Xss256k -XX:+UseG1GC -XX:MetaspaceSize=256m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop-web-1.0-SNAPSHOT.jar --spring.addition-location=application.yaml > jshop.log 2>&1 >< + +![](https://pic.rmb.bdstatic.com/bjh/down/4146281952d45f65334e7ba97c6ba873.png) + + +## �考资料 + +* [Java HotSpot™ Virtual Machine Performance Enhancements](http://docs.oracle.com/javase/8/docs/technotes/guides/vm/performance-enhancements-7.html) +* [Java HotSpot Virtual Machine Garbage Collection Tuning Guide](http://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/index.html) +* [[HotSpot VM] JVM调优的�标准�数�的��陷阱](http://hllvm.group.iteye.com/group/topic/27945) + + +## �考文章 + + + + + + + + + +https://blog.csdn.net/android_hl/article/details/53228348 + + 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\27212\357\274\232JVM\346\200\247\350\203\275\347\256\241\347\220\206\347\245\236\345\231\250VisualVM\344\273\213\347\273\215\344\270\216\345\256\236\346\210\230.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\272\357\274\232JVM\346\200\247\350\203\275\347\256\241\347\220\206\347\245\236\345\231\250VisualVM\344\273\213\347\273\215\344\270\216\345\256\236\346\210\230.md" similarity index 90% 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\27212\357\274\232JVM\346\200\247\350\203\275\347\256\241\347\220\206\347\245\236\345\231\250VisualVM\344\273\213\347\273\215\344\270\216\345\256\236\346\210\230.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\272\357\274\232JVM\346\200\247\350\203\275\347\256\241\347\220\206\347\245\236\345\231\250VisualVM\344\273\213\347\273\215\344\270\216\345\256\236\346\210\230.md" index 9df9174..e2547b3 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\27212\357\274\232JVM\346\200\247\350\203\275\347\256\241\347\220\206\347\245\236\345\231\250VisualVM\344\273\213\347\273\215\344\270\216\345\256\236\346\210\230.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\272\357\274\232JVM\346\200\247\350\203\275\347\256\241\347\220\206\347\245\236\345\231\250VisualVM\344\273\213\347\273\215\344\270\216\345\256\236\346\210\230.md" @@ -1,5 +1,4 @@ -# Table of Contents - +# 目录 * [一�VisualVM是什么?](#一�visualvm是什么?) * [二�如何获�VisualVM?](#二�如何获�visualvm?) * [三�获�那个版本?](#三�获�那个版本?) @@ -9,9 +8,6 @@ * [查找JAVA应用程�耗时的方法函数](#查找java应用程�耗时的方法函数) * [排查JAVA应用程�线程�](#排查java应用程�线程�) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) 本文转自互�网,侵删 @@ -36,9 +32,6 @@ ## 一�VisualVM是什么? VisualVM是一款�费的JAVA虚拟机图形化监控分�工具。 - - - 1. 拥有图形化的监控界�。 2. �供本地�远程的JVM监控分�功能。 3. 是一款�费的JAVA工具。 @@ -49,13 +42,7 @@ VisualVM�版本下载页�: http://visualvm.java.net/releases.html 下载VisualVM时也应该注�,��的JDK版本对应��版本的VisualVM,具体根�安装的JDK版本�下载第一的VisualVM。 - - - - - ## 三�获�那个版本? - 下载版本�考:Java虚拟机性能管�神器 - VisualVM(4) - JDK版本与VisualVM版本对应关系 @@ -257,20 +244,4 @@ https://blog.csdn.net/android_hl/article/details/53228348 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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\2729\357\274\232JVM\347\233\221\346\216\247\345\267\245\345\205\267\344\270\216\350\257\212\346\226\255\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\272\357\274\232JVM\347\233\221\346\216\247\345\267\245\345\205\267\344\270\216\350\257\212\346\226\255\345\256\236\350\267\265.md" similarity index 89% 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\2729\357\274\232JVM\347\233\221\346\216\247\345\267\245\345\205\267\344\270\216\350\257\212\346\226\255\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\272\357\274\232JVM\347\233\221\346\216\247\345\267\245\345\205\267\344\270\216\350\257\212\346\226\255\345\256\236\350\267\265.md" index ab03095..1d57b95 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2729\357\274\232JVM\347\233\221\346\216\247\345\267\245\345\205\267\344\270\216\350\257\212\346\226\255\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\272\357\274\232JVM\347\233\221\346\216\247\345\267\245\345\205\267\344\270\216\350\257\212\346\226\255\345\256\236\350\267\265.md" @@ -1,5 +1,4 @@ -# Table of Contents - +# 目录 * [一�jvm常�监控工具&指令](#一�jvm常�监控工具指令) * [1� jps:jvm进程状况工具](#1�-jpsjvm进程状况工具) * [2�jstat: jvm统计信�监控工具](#2�jstat-jvm统计信�监控工具) @@ -14,9 +13,7 @@ * [2.查看java进程的线程快照信�](#2查看java进程的线程快照信�) * [3�OOM内存泄露](#3�oom内存泄露) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + 本文转自:https://juejin.im/post/59e6c1f26fb9a0451c397a8c @@ -57,10 +54,10 @@ ``` -jps [options] [hostid]�制代� +jps [options] [hostid] ``` - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404222505.png) 如果�指定hostid就默认为当�主机或�务器。 @@ -95,6 +92,7 @@ generalOption - �个的常用的命令行选项,如-help, -options, 或 -ver outputOptions -一个或多个输出选项,由�个的statOption选项组�,�以和-t, -h, and -J等选项��使用。�制代� ``` +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404222543.png) �数选项: @@ -117,7 +115,6 @@ outputOptions -一个或多个输出选项,由�个的statOption选项组� 查看gc 情况执行:jstat-gcutil 27777 -![](data:image/svg+xml;utf8,) ### 3�jinfo: java�置信� @@ -129,11 +126,10 @@ outputOptions -一个或多个输出选项,由�个的statOption选项组� jinfo[option] pid�制代� ``` - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404222602.png) 比如:获�一些当�进程的jvm�行和�动信�。 -![](data:image/svg+xml;utf8,) ### 4�jmap: java 内存映射工具 @@ -150,7 +146,7 @@ jmap [ option ] executable core jmap [ option ] [server-id@]remote-hostname-or-IP�制代� ``` - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404222621.png) �数选项: @@ -175,10 +171,12 @@ jmap [ option ] [server-id@]remote-hostname-or-IP�制代� -J 传递�数给jmap�动的jvm. �制代� ``` +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404222638.png) ### 5�jhat:jvm堆快照分�工具 jhat 命令与jamp��使用,用�分�map生产的堆快存储快照。jhat内置了一个微型http/Html�务器,�以在�览器找那个查看。�过建议尽��用,既然有dumpt文件,�以从生产环境拉�下�,然�通过本地�视化工具�分�,这样既�轻了线上�务器压力,有�以分�的足够详尽(比如 MAT/jprofile/visualVm)等。 + ### 6�jstack:java堆栈跟踪工具 jstack用于生�java虚拟机当�时刻的线程快照。线程快照是当�java虚拟机内�一�线程正在执行的方法堆栈的集�,生�线程快照的主�目的是定�线程出现长时间�顿的原因,如线程间死��死循环�请求外部资�导致的长时间等待等。 @@ -391,20 +389,4 @@ jmap -dump:format=b,file=/tmp/dump.dat 11869 https://blog.csdn.net/android_hl/article/details/53228348 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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\27211\357\274\232Java\345\206\205\345\255\230\345\274\202\345\270\270\345\216\237\347\220\206\344\270\216\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\272\357\274\232Java\345\206\205\345\255\230\345\274\202\345\270\270\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" similarity index 87% 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\27211\357\274\232Java\345\206\205\345\255\230\345\274\202\345\270\270\345\216\237\347\220\206\344\270\216\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\272\357\274\232Java\345\206\205\345\255\230\345\274\202\345\270\270\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" index 0805113..a999de2 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\27211\357\274\232Java\345\206\205\345\255\230\345\274\202\345\270\270\345\216\237\347\220\206\344\270\216\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\272\357\274\232Java\345\206\205\345\255\230\345\274\202\345\270\270\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" @@ -1,5 +1,4 @@ -# Table of Contents - +# 目录 * [实战内存溢出异常](#实战内存溢出异常) * [1 . 对象的创建过程](#1--对象的创建过程) * [2 . 对象的内存布局](#2--对象的内存布局) @@ -22,9 +21,7 @@ * [6��例模�](#6��例模�) * [如何检测内存泄�](#如何检测内存泄�) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + 本文转自互�网,侵删 @@ -77,16 +74,16 @@ Student stu =new Student("张三","18"); * 实例数� * 对�填充 -**对象头:** 对象头中包�了对象�行时一些必�的信�,如GC分代信�,�信�,哈希�,指�Class类元信�的指针等,其中对Javaer比较有用的是**�信�与指�Class对象的指针**,关于�信�,�期有机会讲解并�编程JUC时�扩展,关于指�Class对象的指针其实很好�解。比如上�那个Student的例�,当我们拿到stu对象时,调用Class stuClass=stu.getClass();的时候,其实就是根�这个指针去拿到了stu对象所属的Student类在方法区存放的Class类对象。虽然说的有点拗�,但这��我���磨了好几�,应该是说清楚了。 +**对象头:**对象头中包�了对象�行时一些必�的信�,如GC分代信�,�信�,哈希�,指�Class类元信�的指针等,其中对Javaer比较有用的是**�信�与指�Class对象的指针**,关于�信�,�期有机会讲解并�编程JUC时�扩展,关于指�Class对象的指针其实很好�解。比如上�那个Student的例�,当我们拿到stu对象时,调用Class stuClass=stu.getClass();的时候,其实就是根�这个指针去拿到了stu对象所属的Student类在方法区存放的Class类对象。虽然说的有点拗�,但这��我���磨了好几�,应该是说清楚了。 -**实例数�:** 实例数�部分是对象真正存储的有效信�,就是程�代�中所定义的��类型的字段内容。 +**实例数�:**实例数�部分是对象真正存储的有效信�,就是程�代�中所定义的��类型的字段内容。 -**对�填充:** 虚拟机规范�求对象大�必须是8字节的整数�。对�填充其实就是�补全对象大�的。 +**对�填充:**虚拟机规范�求对象大�必须是8字节的整数�。对�填充其实就是�补全对象大�的。 ## 3 . 对象的访问定� 谈到对象的访问,还拿上�学生的例��说,当我们拿到stu对象时,直接调用stu.getName();时,其实就完�了对对象的访问。但这里�累赘说一下的是,stu虽然通常被认为是一个对象,其实准确�说是�准确的,stu�是一个��,��里存储的是指�对象的指针,(如果干过C或者C++的�伙伴应该比较清楚指针这个概念),当我们调用stu.getName()时,虚拟机会根�指针找到堆里�的对象然�拿到实例数�name.需�注�的是,当我们调用stu.getClass()时,虚拟机会首先根�stu指针定�到堆里�的对象,然�根�对象头里�存储的指�Class类元信�的指针�次到方法区拿到Class对象,进行了两次指针寻找。具体讲解图如下: -![在这里�入图片�述](https://img-blog.csdnimg.cn/201901092235030.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzE5MjczMg==,size_16,color_FFFFFF,t_70) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404223333.png) ## 4 .实战内存异常 @@ -140,8 +137,10 @@ Dumping heap to /Users/zdy/Desktop/dump/java_pid1099.hprof … �以看到生�了dump文件到指定目录。并且爆出了OutOfMemoryError,还告诉了你是哪一片区域出的问题:heap space 打开VisualVM工具导入对应的heapDump文件(如何使用请读者自行查阅相关资料),相应的说明�图: -![在这里�入图片�述](https://img-blog.csdnimg.cn/20190109224456933.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzE5MjczMg==,size_16,color_FFFFFF,t_70) -![在这里�入图片�述](https://img-blog.csdnimg.cn/20190109224531128.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzE5MjczMg==,size_16,color_FFFFFF,t_70) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404223348.png) + +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404223403.png) + 分�dump文件�,我们�以知�,OOMObject这个类创建了810326个实例。所以它能�溢出�?接下�就在代�里找这个类在哪new的。排查问题。(我们的样例代�就�用排查了,While循环太凶猛了)分�dump文件�,我们�以知�,OOMObject这个类创建了810326个实例。所以它能�溢出�?接下�就在代�里找这个类在哪new的。排查问题。(我们的样例代�就�用排查了,While循环太凶猛了) ### Java栈内存异常 @@ -182,7 +181,7 @@ public class JavaVMStackSOF { �以看到,递归调用了751次,栈容��够用了。 默认的栈容�在正常的方法调用时,栈深度�以达到1000-2000深度,所以,一般的递归是�以承�的�的。如果你的代�出现了StackOverflowError,首先检查代�,而�是改�数。 -这里顺带�一下,很多人在�多线程开�时,当创建很多线程时,**容易出现OOM(OutOfMemoryError),** 这时�以通过具体情况,�少最大堆容�,或者栈容��解决问题,这是为什么呢。请看下�的公�: +这里顺带�一下,很多人在�多线程开�时,当创建很多线程时,**容易出现OOM(OutOfMemoryError),**这时�以通过具体情况,�少最大堆容�,或者栈容��解决问题,这是为什么呢。请看下�的公�: 线程数*(最大栈容�)+最大堆值+其他内存(忽略�计或者一般�改动)=机器最大内存 @@ -190,10 +189,10 @@ public class JavaVMStackSOF { ### 方法区内存异常 -写到这里时,作者本�想写一个无�创建动�代�对象的例��演示方法区溢出,�开谈论JDK7与JDK8的内存区域�更的过渡,但细想一想,还是把这一�从始致终的说清楚。在上一篇文章中JVM系列之Java内存结构详解讲到方法区时�到,JDK7环境下方法区包括了(�行时常�池),其实这么说是�准确的。因为从JDK7开始,HotSpot团队就想到开始去"永久代",大家首先明确一个概念,**方法区和"永久代"(PermGen space)是两个概念,方法区是JVM虚拟机规范,任何虚拟机实现(J9等)都�能少这个区间,而"永久代"�是HotSpot对方法区的一个实现。** 为了把知识点列清楚,我还是�用列表的形�: +写到这里时,作者本�想写一个无�创建动�代�对象的例��演示方法区溢出,�开谈论JDK7与JDK8的内存区域�更的过渡,但细想一想,还是把这一�从始致终的说清楚。在上一篇文章中JVM系列之Java内存结构详解讲到方法区时�到,JDK7环境下方法区包括了(�行时常�池),其实这么说是�准确的。因为从JDK7开始,HotSpot团队就想到开始去"永久代",大家首先明确一个概念,**方法区和"永久代"(PermGen space)是两个概念,方法区是JVM虚拟机规范,任何虚拟机实现(J9等)都�能少这个区间,而"永久代"�是HotSpot对方法区的一个实现。**为了把知识点列清楚,我还是�用列表的形�: -* [ ]  JDK7之�(包括JDK7)拥有"永久代"(PermGen space),用�实现方法区。但在JDK7中已���在实现中把永久代中把很多东西移了出�,比如:符�引用(Symbols)转移到了native heap,�行时常�池(interned strings)转移到了java heap;类的����(class statics)转移到了java heap. -* [ ]  所以这就是为什么我说上一篇文章中说方法区中包��行时常�池是�正确的,因为已�移动到了java heap; +* [ ] JDK7之�(包括JDK7)拥有"永久代"(PermGen space),用�实现方法区。但在JDK7中已���在实现中把永久代中把很多东西移了出�,比如:符�引用(Symbols)转移到了native heap,�行时常�池(interned strings)转移到了java heap;类的����(class statics)转移到了java heap. +* [ ] 所以这就是为什么我说上一篇文章中说方法区中包��行时常�池是�正确的,因为已�移动到了java heap; **在JDK7之�(包括7)�以通过-XX:PermSize -XX:MaxPermSize�控制永久代的大�. JDK8正�去除"永久代",��Metaspace(元空间)作为JVM虚拟机规范中方法区的实现。** 元空间与永久代之间最大的区别在于:元空间并�在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大�仅�本地内存�制,但��以通过�数控制:-XX:MetaspaceSize与-XX:MaxMetaspaceSize�控制大�。 @@ -316,7 +315,8 @@ Java的一个��优点就是通过垃圾收集器(Garbage Collection,GC)自 为了更好�解GC的工作原�,我们�以将对象考虑为有�图的顶点,将引用关系考虑为图的有�边,有�边从引用者指�被引对象。�外,�个线程对象�以作为一个图的起始顶点,例如大多程�从main进程开始执行,那么该图就是以main进程顶点开始的一棵根树。在这个有�图中,根顶点�达的对象都是有效对象,GC将�回收这些对象。如果�个对象 (连通�图)与这个根顶点��达(注�,该图为有�图),那么我们认为这个(这些)对象��被引用,�以被GC回收。 以下,我们举一个例�说明如何用有�图表示内存管�。对于程�的�一个时刻,我们都有一个有�图表示JVM的内存分�情况。以下�图,就是左边程��行到第6行的示�图。 -![在这里�入图片�述](https://img-blog.csdnimg.cn/20190110151433131.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzE5MjczMg==,size_16,color_FFFFFF,t_70) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404223428.png) + Java使用有�图的方�进行内存管�,�以消除引用循环的问题,例如有三个对象,相互引用,��它们和根进程��达的,那么GC也是�以回收它们的。这�方�的优点是管�内存的精度很高,但是效率较低。�外一�常用的内存管�技术是使用计数器,例如COM模型采用计数器方�管�构件,它与有�图相比,精度行低(很难处�循环引用的问题),但执行效率很高。 ## 什么是Java中的内存泄露? @@ -326,7 +326,8 @@ Java使用有�图的方�进行内存管�,�以消除引用循环的问 在C++中,内存泄�的范围更大一些。有些对象被分�了内存空间,然����达,由于C++中没有GC,这些内存将永远收�回�。在Java中,这些��达的对象都由GC负责回收,因此程�员�需�考虑这部分的内存泄露。 通过分�,我们得知,对于C++,程�员需�自己管�边和顶点,而对于Java程�员�需�管�边就�以了(�需�管�顶点的释放)。通过这�方�,Java�高了编程的效率。 -![在这里�入图片�述](https://img-blog.csdnimg.cn/20190110152226472.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzE5MjczMg==,size_16,color_FFFFFF,t_70) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404223441.png) + 因此,通过以上分�,我们知�在Java中也有内存泄�,但范围比C++��一些。因为Java从语言上��,任何对象都是�达的,所有的��达对象都由GC管�。 对于程�员�说,GC基本是�明的,���的。虽然,我们�有几个函数�以访问GC,例如�行GC的函数System.gc(),但是根�Java语言规范定义, 该函数���JVM的垃圾收集器一定会执行。因为,��的JVM实现者�能使用��的算法管�GC。通常,GC的线程的优先级别较低。JVM调用GC的策略也有很多�,有的是内存使用到达一定程度时,GC�开始工作,也有定时执行的,有的是平缓执行GC,有的是中断�执行GC。但通常�说,我们�需�关心这些。除�在一些特定的场�,GC的执行影�应用程�的性能,例如对于基于Web的实时系统,如网络游�等,用户�希望GC�然中断应用程�执行而进行垃圾回收,那么我们需�调整GC的�数,让GC能够通过平缓的方�释放内存,例如将垃圾回收分解为一系列的�步骤执行,Sun�供的HotSpot JVM就支�这一特性。 @@ -443,12 +444,6 @@ Optimizeit Profiler版本4.11支�Application,Applet,Servlet和Romote Appli 当设置好所有的�数了,我们就�以在OptimizeIt环境下�行被测程�,在程��行过程中,Optimizeit�以监视内存的使用曲线(如下图),包括JVM申请的堆(heap)的大�,和实际使用的内存大�。�外,在�行过程中,我们�以�时暂�程�的�行,甚至强行调用GC,让GC进行内存回收。通过内存使用曲线,我们�以整体了解程�使用内存的情况。这�监测对于长期�行的应用程��常有必�,也很容易�现内存泄露。 - - - - - - ## �考文章 @@ -461,20 +456,4 @@ Optimizeit Profiler版本4.11支�Application,Applet,Servlet和Romote Appli https://blog.csdn.net/android_hl/article/details/53228348 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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\272\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 97% 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\272\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" index 2371f8f..cf18680 100644 --- "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\272\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" @@ -1,5 +1,4 @@ -# Table of Contents - +# 目录 * [�言](#�言) * [Class文件](#class文件) * [什么是Class文件?](#什么是class文件?) @@ -50,8 +49,7 @@ java之所以能够实现跨平�,便在于其编译阶段�是将代�直 �便找到一个class文件,用Sublime Text打开是这样的: -![](https://user-gold-cdn.xitu.io/2017/2/6/0ac6ea6e3c01482f77234bef7aa236f0?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404220426.png) 是�是一脸懵逼,�过java虚拟机规范中给出了class文件的基本格�,��按照这个格�去解�就�以了: @@ -337,11 +335,7 @@ for (int j = 0; j < methodInfo.attributesCount; j++) { 整个项目终于写完了,接下�就�看看效果如何,�便找一个class文件解��行: -![](https://user-gold-cdn.xitu.io/2017/2/6/fa350cdc04576bab58ade0955b9f0388?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) - -
- - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404220510.png) 哈哈,是�是很赞� 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\272\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 85% 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\272\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" index 2db99f7..58b8166 100644 --- "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\272\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" @@ -1,6 +1,5 @@ -# Table of Contents - - * [[java编译期优化](https://www.cnblogs.com/LeonNew/p/6187411.html)](#[java编译期优化]httpswwwcnblogscomleonnewp6187411html) +# 目录 + * [java编译期优化](#[java编译期优化]) * [早期(编译期)优化](#早期(编译期)优化) * [泛型与类型擦除](#泛型与类型擦除) * [自动装箱�拆箱与�历循环](#自动装箱�拆箱与�历循环) @@ -12,9 +11,7 @@ * [编译优化技术](#编译优化技术) * [java与C/C++编译器对比](#java与cc编译器对比) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + 本文转自互�网,侵删 @@ -37,24 +34,14 @@ - - - - - - -## [java编译期优化](https://www.cnblogs.com/LeonNew/p/6187411.html) - - - - +## java编译期优化 java语言的编译期其实是一段�确定的�作过程,因为它�以分为三类编译过程: 1.�端编译:把_.java文件转�为_.class文件 2.�端编译:把字节�转�为机器� 3.����编译:直接把*.java文件编译�本地机器代� 从JDK1.3开始,虚拟机设计团队就把对性能的优化集中到了�端的�时编译中,这样�以让那些�是由Javac产生的Class文件(如JRuby�Groovy等语言的Class文件)也能享�到编译期优化所带�的好处 -**Java中�时编译在�行期的优化过程对于程��行�说更��,而�端编译期在编译期的优化过程对于程�编��说关系更加密切    ** +**Java中�时编译在�行期的优化过程对于程��行�说更��,而�端编译期在编译期的优化过程对于程�编��说关系更加密切** ### 早期(编译期)优化 @@ -115,11 +102,10 @@ public static void main(String[] args) { ##### 解释器与编译器 -Java程�最�是通过解释器进行解释执行的,当程�需�迅速�动和执行时,解释器�以首先�挥作用,�去编译时间,立�执行;当程��行�,��时间的推移,编译期���挥作用,把越�越多的代�编译�本地代�,获得更高的执行效率。**解释执行节约内存,编译执行��效率。** �时,解释器�以作为编译器激进优化时的一个“逃生门�,让编译器根�概率选择一些大多数时候都能���行速度的优化手段,当激进优化的�设��立,则通过逆优化退回到解释状�继续执行。 +Java程�最�是通过解释器进行解释执行的,当程�需�迅速�动和执行时,解释器�以首先�挥作用,�去编译时间,立�执行;当程��行�,��时间的推移,编译期���挥作用,把越�越多的代�编译�本地代�,获得更高的执行效率。**解释执行节约内存,编译执行��效率。**�时,解释器�以作为编译器激进优化时的一个“逃生门�,让编译器根�概率选择一些大多数时候都能���行速度的优化手段,当激进优化的�设��立,则通过逆优化退回到解释状�继续执行。 HotSpot虚拟机中内置了两个�时编译器,分别称为Client Compiler(C1编译器)和Server Compiler(C2编译器),默认采用解释器与其中一个编译器直接��的方�工作,使用哪个编译器�决于虚拟机�行的模�,也�以自己去指定。若强制虚拟机�行与“解释模��,编译器完全�介入工作,若强制虚拟机�行于“编译模��,则优先采用编译方�执行程�,解释器�然�在编译无法进行的情况下介入执行过程。 -![](https://images2015.cnblogs.com/blog/907283/201612/907283-20161216162941651-154732490.png) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404222130.png) ##### 分层编译策略 ``` @@ -143,13 +129,11 @@ HotSpot虚拟机中内置了两个�时编译器,分别称为Client Compiler( ``` HotSpot虚拟机使用的是第二�,它为�个方法准备了两类计数器:方法调用计数器和回边计数器,下图表示方法调用计数器触��时编译: -![](https://images2015.cnblogs.com/blog/907283/201612/907283-20161216162952058-1104250840.png) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404222225.png) 如果��任何设置,执行引擎会继续进入解释器按照解释方�执行字节�,直到�交的请求被编译器编译完�,下次调用�会使用已编译的版本。�外,方法调用计数器的值也�是一个�对次数,而是一段时间之内被调用的次数,超过这个时间,次数就��,这称为计数器热度的衰�。 下图表示回边计数器触��时编译: -![](https://images2015.cnblogs.com/blog/907283/201612/907283-20161216163000854-2133382071.png) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404222324.png) 回边计数器没有计数器热度衰�的过程,因此统计的就是�对次数,并且当计数器溢出时,它还会把方法计数器的值也调整到溢出状�,这样下次进入该方法的时候就会执行标准编译过程。 ##### 编译优化技术 @@ -235,20 +219,4 @@ Java虚拟机的�时编译器与C/C++的��编译器相比,�能会由 https://blog.csdn.net/android_hl/article/details/53228348 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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\27213\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\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\272\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\345\256\236\350\267\265.md" similarity index 81% 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\27213\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\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\272\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\345\256\236\350\267\265.md" index 3167cfa..cca62d4 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\27213\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\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\272\357\274\232\345\206\215\350\260\210\345\233\233\347\247\215\345\274\225\347\224\250\345\217\212GC\345\256\236\350\267\265.md" @@ -1,5 +1,4 @@ -# Table of Contents - +# 目录 * [一�背景](#一�背景) * [二�简介](#二�简介) * [1.强引用 StrongReference](#1强引用-strongreference) @@ -8,9 +7,7 @@ * [4.虚引用 PhantomReference](#4虚引用-phantomreference) * [三��结](#三��结) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + 本文转自互�网,侵删 @@ -35,15 +32,15 @@ ## 一�背景 -Java的内存回收�需�程�员负责,JVM会在必�时�动Java GC完�垃圾回收。Java以便我们控制对象的生存周期,�供给了我们四�引用方�,引用强度从强到弱分别为:强引用�软引用�弱引用�虚引用。  +Java的内存回收�需�程�员负责,JVM会在必�时�动Java GC完�垃圾回收。Java以便我们控制对象的生存周期,�供给了我们四�引用方�,引用强度从强到弱分别为:强引用�软引用�弱引用�虚引用。 ## 二�简介 ### 1.强引用 StrongReference StrongReference是Java的默认引用形�,使用时�需�显示定义。任何通过强引用所使用的对象�管系统资�有多紧张,Java GC都�会主动回收具有强引用的对象。 - -
public class StrongReferenceTest {
+````
+public class StrongReferenceTest {
 
 	public static int M = 1024*1024;
 
@@ -76,17 +73,16 @@ StrongReference是Java的默认引用形�,使用时�需�显示定义。
 		System.out.println("strongReference : "+strongReference);
 		}
 }
-
+```` �行结果: -![](https://user-gold-cdn.xitu.io/2018/1/7/160cd0dc536b2384?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404223852.png) ### 2.弱引用 WeakReference 如果一个对象�具有弱引用,无论内存充足与�,Java GC�对象如果�有弱引用将会被自动回收。 - -
public class WeakReferenceTest {
+````
+public class WeakReferenceTest {
 
 	public static int M = 1024*1024;
 
@@ -110,17 +106,16 @@ StrongReference是Java的默认引用形�,使用时�需�显示定义。
 		System.out.println("weakRerference.get() : "+weakRerference.get());
 	}   
 }
-
+```` �行结果: -![](https://user-gold-cdn.xitu.io/2018/1/7/160cd0f1ead8184e?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404223905.png) ### 3.软引用 SoftReference 软引用和弱引用的特性基本一致, 主�的区别在于软引用在内存�足时�会被回收。如果一个对象�具有软引用,Java GC在内存充足的时候�会回收它,内存�足时�会被回收。 - -
public class SoftReferenceTest {
+````
+public class SoftReferenceTest {
 
 	public static int M = 1024*1024;
 
@@ -151,17 +146,16 @@ StrongReference是Java的默认引用形�,使用时�需�显示定义。
 		System.out.println("softRerference2.get() : "+softRerference2.get());  
 	 } 
 }
-
+```` �行结果: -![](https://user-gold-cdn.xitu.io/2018/1/7/160cd1023a8f3de2?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404223922.png) ### 4.虚引用 PhantomReference 从PhantomReference类的�代��以知�,它的get()方法无论何时返回的都�会是null。所以�独使用虚引用时,没有什么�义,需�和引用队列ReferenceQueue类��使用。当执行Java GC时如果一个对象�有虚引用,就会把这个对象加入到与之关�的ReferenceQueue中。 - -
public class PhantomReferenceTest {
+````
+public class PhantomReferenceTest {
 
 	public static int M = 1024*1024;
 
@@ -205,12 +199,11 @@ StrongReference是Java的默认引用形�,使用时�需�显示定义。
 	    System.out.println("referenceQueue.poll() : "+referenceQueue.poll());	    	
 	}
 }
-
+```` �行结果: -![](https://user-gold-cdn.xitu.io/2018/1/7/160cd110cba23d84?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404223938.png) ## 三��结 强引用是 Java 的默认引用形�,使用时�需�显示定义,是我们平时最常使用到的引用方�。�管系统资�有多紧张,Java GC都�会主动回收具有强引用的对象。 弱引用和软引用一般在引用对象为�必需对象的时候使用。它们的区别是被弱引用关�的对象在垃圾回收时总是会被回收,被软引用关�的对象�有在内存�足时�会被回收。 虚引用的get()方法获�的永远是null,无法获�对象实例。Java GC会把虚引用的对象放到引用队列里�。�用�在对象被回收时��外的一些资�清�或事物回滚等处�。 由于无法从虚引获�到引用对象的实例。它的使用情况比较特别,所以这里�把虚引用放入表格进行对比。这里对强引用�弱引用�软引用进行对比: @@ -229,20 +222,4 @@ StrongReference是Java的默认引用形�,使用时�需�显示定义。 https://blog.csdn.net/android_hl/article/details/53228348 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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\2723\357\274\232\345\236\203\345\234\276\345\233\236\346\224\266\345\231\250\350\257\246\350\247\243.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\272\357\274\232\345\236\203\345\234\276\345\233\236\346\224\266\345\231\250\350\257\246\350\247\243.md" similarity index 77% 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\2723\357\274\232\345\236\203\345\234\276\345\233\236\346\224\266\345\231\250\350\257\246\350\247\243.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\272\357\274\232\345\236\203\345\234\276\345\233\236\346\224\266\345\231\250\350\257\246\350\247\243.md" index 741c437..7e89241 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2723\357\274\232\345\236\203\345\234\276\345\233\236\346\224\266\345\231\250\350\257\246\350\247\243.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\272\357\274\232\345\236\203\345\234\276\345\233\236\346\224\266\345\231\250\350\257\246\350\247\243.md" @@ -1,5 +1,4 @@ -# Table of Contents - +# 目录 * [1 概述](#1-概述) * [2 对象已�死亡?](#2-对象已�死亡?) * [2.1引用计数法](#21引用计数法) @@ -85,8 +84,7 @@ Minor Gc和Full GC 有什么��呢? ### 2.2�达性分�算法 -这个算法的基本�想就是通过一系列的称为 **“GC Roots�** 的对象作为起点,从这些节点开始�下�索,节点所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的�,则�明此对象是��用的。 -![�达性分�算法](https://user-gold-cdn.xitu.io/2018/4/28/1630be33dd8c8e32?w=807&h=485&f=png&s=223006) +这个算法的基本�想就是通过一系列的称为**“GC Roots�**的对象作为起点,从这些节点开始�下�索,节点所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的�,则�明此对象是��用的。 ### 2.3 �谈引用 @@ -114,7 +112,7 @@ JDK1.2以�,Java对引用的感念进行了扩充,将引用分为强引用 **虚引用主�用�跟踪对象被垃圾回收的活动**。 -**虚引用与软引用和弱引用的一个区别在于:** 虚引用必须和引用队列(ReferenceQueue)��使用。当垃 圾回收器准备回收一个对象时,如果�现它还有虚引用,就会在回收对象的内存之�,把这个虚引用加入到与之关�的引用队列中。程��以通过判断引用队列中是 �已�加入了虚引用,�了解被引用的对象是�将�被垃圾回收。程�如果�现�个虚引用已�被加入到引用队列,那么就�以在所引用的对象的内存被回收之�采�必�的行动。 +**虚引用与软引用和弱引用的一个区别在于:**虚引用必须和引用队列(ReferenceQueue)��使用。当垃 圾回收器准备回收一个对象时,如果�现它还有虚引用,就会在回收对象的内存之�,把这个虚引用加入到与之关�的引用队列中。程��以通过判断引用队列中是 �已�加入了虚引用,�了解被引用的对象是�将�被垃圾回收。程�如果�现�个虚引用已�被加入到引用队列,那么就�以在所引用的对象的内存被回收之�采�必�的行动。 特别注�,在程�设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为**软引用�以加速JVM对垃圾内存的回收速度,�以维护系统的�行安全,防止内存溢出(OutOfMemory)等问题的产生**。 @@ -126,7 +124,7 @@ JDK1.2以�,Java对引用的感念进行了扩充,将引用分为强引用 方法区(或Hotspot虚拟中的永久代)的垃圾收集主�回收两部分内容:**废弃常�和无用的类。** -判定一个常�是�是“废弃常��比较简�,而�判定一个类是�是“无用的类�的�件则相对苛刻许多。类需��时满足下�3个�件�能算是 **“无用的类�** : +判定一个常�是�是“废弃常��比较简�,而�判定一个类是�是“无用的类�的�件则相对苛刻许多。类需��时满足下�3个�件�能算是**“无用的类�**: - 该类所有的实例都已�被回收,也就是Java堆中�存在该类的任何实例。 - 加载该类的ClassLoader已�被回收。 - 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过�射访问该类的方法。 @@ -136,19 +134,16 @@ JDK1.2以�,Java对引用的感念进行了扩充,将引用分为强引用 ### 3.1 标记-清除算法 算法分为“标记�和“清除�阶段:首先标记出所有需�回收的对象,在标记完��统一回收所有被标记的对象。它是最基础的收集算法,会带�两个明显的问题;1:效率问题和2:空间问题(标记清除�会产生大��连续的碎片) -![标记-清除算法](https://user-gold-cdn.xitu.io/2018/4/28/1630bedcd37144b0?w=809&h=438&f=png&s=143686) ### 3.2 �制算法 为了解决效率问题,“�制�收集算法出现了。它�以将内存分为大�相�的两�,�次使用其中的一�。当这一�的内存使用完�,就将还存活的对象�制到�一�去,然��把使用的空间一次清�掉。这样就使�次的内存回收都是对内存区间的一�进行回收。 -![�制算法](https://user-gold-cdn.xitu.io/2018/4/28/1630bee78552641e?w=829&h=420&f=png&s=188338) ### 3.3 标记-整�算法 根��年代的特点特出的一�标记算法,标记过程�然与“标记-清除�算法一样,但�续步骤�是直接对�回收对象回收,而是让所有存活的对象�一段移动,然�直接清�掉端边界以外的内存。 -![标记-整�算法](https://user-gold-cdn.xitu.io/2018/4/28/1630bef38f362a98?w=816&h=459&f=png&s=147162) ### 3.4分代收集算法 @@ -156,7 +151,7 @@ JDK1.2以�,Java对引用的感念进行了扩充,将引用分为强引用 比如在新生代中,�次收集都会有大�对象死去,所以�以选择�制算法,�需�付出少�对象的�制�本就�以完��次垃圾收集。而�年代的对象存活几率是比较高的所以我们�以选择“标记-清��或“标记-整��算法进行垃圾收集。 -**延伸�试问题:** HotSpot为什么�分为新生代和�年代? +**延伸�试问题:**HotSpot为什么�分为新生代和�年代? 根�上�的对分代收集算法的介�回答。 @@ -167,8 +162,7 @@ JDK1.2以�,Java对引用的感念进行了扩充,将引用分为强引用 ### 4.1 Serial收集器 -Serial(串行)收集器收集器是最基本�历�最悠久的垃圾收集器了。大家看�字就知�这个收集器是一个�线程收集器了。它的 **“�线程�** 的�义�仅仅�味�它�会使用一�垃圾收集线程去完�垃圾收集工作,更��的是它在进行垃圾收集工作的时候必须暂�其他所有的工作线程( **“Stop The World�** 了解一下),直到它收集结�。 -![Serial收集器](https://user-gold-cdn.xitu.io/2018/4/28/1630c032627e6728?w=767&h=177&f=png&s=80000) +Serial(串行)收集器收集器是最基本�历�最悠久的垃圾收集器了。大家看�字就知�这个收集器是一个�线程收集器了。它的**“�线程�**的�义�仅仅�味�它�会使用一�垃圾收集线程去完�垃圾收集工作,更��的是它在进行垃圾收集工作的时候必须暂�其他所有的工作线程(**“Stop The World�**了解一下),直到它收集结�。 虚拟机的设计者们当然知�Stop The World带�的�良用户体验,所以在�续的垃圾收集器设计中�顿时间在�断缩短(�然还有�顿,寻找最优秀的垃圾收集器的过程�然在继续)。 @@ -178,12 +172,11 @@ Serial(串行)收集器收集器是最基本�历�最悠久的垃圾收 **ParNew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制�数�收集算法�回收策略等等)和Serial收集器完全一样。** -![ParNew收集器](https://user-gold-cdn.xitu.io/2018/4/28/1630c07412c5e73a?w=785&h=187&f=png&s=87246) 它是许多�行在Server模�下的虚拟机的首�选择,除了Serial收集器外,�有它能与CMS收集器(真正�义上的并�收集器,��会介�到)��工作。 **并行和并�概念补充:** -* **并行(Parallel)** :指多�垃圾收集线程并行工作,但此时用户线程�然处于等待状�。 +* **并行(Parallel)**:指多�垃圾收集线程并行工作,但此时用户线程�然处于等待状�。 * **并�(Concurrent)**:指用户线程与垃圾收集线程�时执行(但�一定是并行,�能会交替执行),用户程�在继续�行,而垃圾收集器�行在�一个CPU上。 @@ -191,7 +184,7 @@ Serial(串行)收集器收集器是最基本�历�最悠久的垃圾收 Parallel Scavenge收集器是一个新生代收集器,它也是使用�制算法的收集器,�是并行的的多线程收集器。。。那么它有什么特别之处呢? -**Parallel Scavenge收集器关注点是���(高效率的利用CPU)。CMS等垃圾收集器的关注点更多的是用户线程的�顿时间(�高用户体验)。所谓���就是CPU中用于�行用户代�的时间与CPU总消耗时间的比值。** Parallel Scavenge收集器�供了很多�数供用户找到最�适的�顿时间或最大���,如果对于收集器�作�太了解的�,手工优化存在的��以选择把内存管�优化交给虚拟机去完�也是一个�错的选择。 +**Parallel Scavenge收集器关注点是���(高效率的利用CPU)。CMS等垃圾收集器的关注点更多的是用户线程的�顿时间(�高用户体验)。所谓���就是CPU中用于�行用户代�的时间与CPU总消耗时间的比值。**Parallel Scavenge收集器�供了很多�数供用户找到最�适的�顿时间或最大���,如果对于收集器�作�太了解的�,手工优化存在的��以选择把内存管�优化交给虚拟机去完�也是一个�错的选择。 ### 4.4.Serial Old收集器 @@ -205,45 +198,41 @@ Parallel Scavenge收集器是一个新生代收集器,它也是使用�制算 **CMS(Concurrent Mark Sweep)收集器是一�以获�最短回收�顿时间为目标的收集器。它而�常符�在注�用户体验的应用上使用。** -从�字中的**Mark Sweep**这两个��以看出,CMS收集器是一� **“标记-清除�算法**实现的,它的�作过程相比于��几�垃圾收集器�说更加��一些。整个过程分为四个步骤: - -* **�始标记:** 暂�所有的其他线程,并记录下直接与root相连的对象,速度很快 ; -* **并�标记:** �时开�GC和用户线程,用一个闭包结构去记录�达对象。但在这个阶段结�,这个闭包结构并�能��包�当�所有的�达对象。因为用户线程�能会�断的更新引用域,所以GC线程无法���达性分�的实时性。所以这个算法里会跟踪记录这些�生引用更新的地方。 -* **�新标记:** �新标记阶段就是为了修正并�标记期间因为用户程�继续�行而导致标记产生�动的那一部分对象的标记记录,这个阶段的�顿时间一般会比�始标记阶段的时间�长,远远比并�标记阶段时间短 -* **并�清除:** 开�用户线程,�时GC线程开始对为标记的区域�清扫。 +从�字中的**Mark Sweep**这两个��以看出,CMS收集器是一�**“标记-清除�算法**实现的,它的�作过程相比于��几�垃圾收集器�说更加��一些。整个过程分为四个步骤: -![CMS收集器](https://user-gold-cdn.xitu.io/2018/4/28/1630c17fcd5f9497?w=816&h=210&f=png&s=91490) +* **�始标记:**暂�所有的其他线程,并记录下直接与root相连的对象,速度很快 ; +* **并�标记:**�时开�GC和用户线程,用一个闭包结构去记录�达对象。但在这个阶段结�,这个闭包结构并�能��包�当�所有的�达对象。因为用户线程�能会�断的更新引用域,所以GC线程无法���达性分�的实时性。所以这个算法里会跟踪记录这些�生引用更新的地方。 +* **�新标记:**�新标记阶段就是为了修正并�标记期间因为用户程�继续�行而导致标记产生�动的那一部分对象的标记记录,这个阶段的�顿时间一般会比�始标记阶段的时间�长,远远比并�标记阶段时间短 +* **并�清除:**开�用户线程,�时GC线程开始对为标记的区域�清扫。 从它的�字就�以看出它是一款优秀的垃圾收集器,主�优点:**并�收集�低�顿**。但是它有下�三个明显的缺点: -- **对CPU资��感;** -- **无法处�浮动垃圾;** -- **它使用的回收算法-“标记-清除�算法会导致收集结�时会有大�空间碎片产生。** +-**对CPU资��感;** +-**无法处�浮动垃圾;** +-**它使用的回收算法-“标记-清除�算法会导致收集结�时会有大�空间碎片产生。** ### 4.7 G1收集器 上一代的垃圾收集器(串行serial, 并行parallel, 以�CMS)都把堆内存划分为固定大�的三个部分: 年轻代(young generation), 年�代(old generation), 以��久代(permanent generation). -![年轻代(young generation), 年�代(old generation), 以��久代(permanent generation)](https://user-gold-cdn.xitu.io/2018/4/28/1630c1b1b3b95713?w=960&h=513&f=png&s=24990) **G1 (Garbage-First)是一款���务器的垃圾收集器,主�针对�备多颗处�器�大容�内存的机器. 以�高概率满足GC�顿时间�求的�时,还具备高���性能特�.** 被视为JDK1.7中HotSpot虚拟机的一个��进化特�。它具备一下特点: -- **并行与并�**:G1能充分利用CPU�多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)�缩短stop-The-World�顿时间。部分其他收集器原本需��顿Java线程执行的GC动作,G1收集器�然�以通过并�的方�让java程�继续执行。 -- **分代收集**:虽然G1�以�需�其他收集器��就能独立管�整个GC堆,但是还是�留了分代的概念。 -- **空间整�**:与CMS的“标记–清��算法��,G1从整体�看是基于“标记整��算法实现的收集器;从局部上�看是基于“�制�算法实现的。 -- **�预测的�顿**:这是G1相对于CMS的�一个大优势,�低�顿时间是G1和CMS共�的关注点,但G1除了追求低�顿外,还能建立�预测的�顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内。 +-**并行与并�**:G1能充分利用CPU�多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)�缩短stop-The-World�顿时间。部分其他收集器原本需��顿Java线程执行的GC动作,G1收集器�然�以通过并�的方�让java程�继续执行。 +-**分代收集**:虽然G1�以�需�其他收集器��就能独立管�整个GC堆,但是还是�留了分代的概念。 +-**空间整�**:与CMS的“标记–清��算法��,G1从整体�看是基于“标记整��算法实现的收集器;从局部上�看是基于“�制�算法实现的。 +-**�预测的�顿**:这是G1相对于CMS的�一个大优势,�低�顿时间是G1和CMS共�的关注点,但G1除了追求低�顿外,还能建立�预测的�顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内。 **G1收集器在��维护了一个优先列表,�次根��许的收集时间,优先选择回收价值最大的Region(这也就是它的�字Garbage-First的由�)**。这�使用Region划分内存空间以�有优先级的区域回收方�,��了GF收集器在有�时间内�以尽�能高的收集效率(把内存化整为零)。 G1收集器的�作大致分为以下几个步骤: -- **�始标记** -- **并�标记** -- **最终标记** -- **筛选回收** +-**�始标记** +-**并�标记** +-**最终标记** +-**筛选回收** 上�几个步骤的�作过程和CMS有很多相似之处。�始标记阶段仅仅�是标记一下GC Roots能直接关�到的对象,并且修改TAMS的值,让下一个阶段用户程�并��行时,能在正确�用的Region中创建新对象,这一阶段需��顿线程,但是耗时很短,并�标记阶段是从GC Root开始对堆中对象进行�达性分�,找出存活的对象,这阶段时耗时较长,但�与用户程�并�执行。而最终标记阶段则是为了修正在并�标记期间因用户程�继续�作而导致标记产生�动的那一部分标记记录,虚拟机将这段时间对象�化记录在线程Remenbered Set Logs里�,最终标记阶段需�把Remembered Set Logs的数��并到Remembered Set Logs里�,最终标记阶段需�把Remembered Set Logs的数��并到Remembered Set中,这一阶段需��顿线程,但是�并行执行。最�在筛选回收阶段首先对�个Region的回收价值和�本进行排�,根�用户所期望的GC�顿时间�制定回收计划。 -![G1收集器](https://user-gold-cdn.xitu.io/2018/4/28/1630c1e0bb4557d8?w=814&h=206&f=png&s=98459) ## 5 内存分�与回收策略 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\2726\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.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\272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" similarity index 82% 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\2726\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.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\272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" index 60ad7b7..198ce72 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2726\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.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\272\357\274\232\346\267\261\345\205\245\347\220\206\350\247\243JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" @@ -1,5 +1,4 @@ -# Table of Contents - +# 目录 * [一.目标:](#一目标:) * [二.原� (类的加载过程�其最终产�):](#二原�-(类的加载过程�其最终产�)) * [三.过程(类的生命周期):](#三过程(类的生命周期):) @@ -11,9 +10,6 @@ * [四.类加载器:](#四类加载器:) * [五.�亲委派机制:](#五�亲委派机制:) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) 本文转自互�网,侵删 @@ -36,7 +32,6 @@ - ## 一.目标: 1.什么是类的加载? @@ -56,14 +51,7 @@ JVM将class文件字节�文件加载到内存中, 并将这些��数� JVM类加载机制分为五个部分:加载,验�,准备,解�,�始化,下�我们就分别�看一下这五个过程。其中加载�检验�准备��始化和�载这个五个阶段的顺�是固定的,而解�则未必。为了支�动�绑定,解�这个过程�以�生在�始化阶段之�。 - - - -![](https://upload-images.jianshu.io/upload_images/13202633-3cb11d1712a9efc9.png?imageMogr2/auto-orient/strip|imageView2/2/w/739/format/webp) - - - - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404221551.png) ### 加载: @@ -139,13 +127,7 @@ java中,对于�始化阶段,有且�有以下五�情况�会对�求 ## 五.�亲委派机制: - - - -![](https://upload-images.jianshu.io/upload_images/13202633-4c819649aebff4df.png?imageMogr2/auto-orient/strip|imageView2/2/w/590/format/webp) - - - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404221618.png) �亲委派机制工作过程: @@ -169,20 +151,4 @@ java中,对于�始化阶段,有且�有以下五�情况�会对�求 https://blog.csdn.net/android_hl/article/details/53228348 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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\2725\357\274\232\350\231\232\346\213\237\346\234\272\345\255\227\350\212\202\347\240\201\346\211\247\350\241\214\345\274\225\346\223\216.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\272\357\274\232\350\231\232\346\213\237\346\234\272\345\255\227\350\212\202\347\240\201\346\211\247\350\241\214\345\274\225\346\223\216.md" similarity index 85% 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\2725\357\274\232\350\231\232\346\213\237\346\234\272\345\255\227\350\212\202\347\240\201\346\211\247\350\241\214\345\274\225\346\223\216.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\272\357\274\232\350\231\232\346\213\237\346\234\272\345\255\227\350\212\202\347\240\201\346\211\247\350\241\214\345\274\225\346\223\216.md" index faebc2d..aabb34c 100644 --- "a/docs/java/jvm/\346\267\261\345\205\245\347\220\206\350\247\243JVM\350\231\232\346\213\237\346\234\2725\357\274\232\350\231\232\346\213\237\346\234\272\345\255\227\350\212\202\347\240\201\346\211\247\350\241\214\345\274\225\346\223\216.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\272\357\274\232\350\231\232\346\213\237\346\234\272\345\255\227\350\212\202\347\240\201\346\211\247\350\241\214\345\274\225\346\223\216.md" @@ -1,5 +1,4 @@ -# Table of Contents - +# 目录 * [1 概述](#1-概述) * [2 �行时栈帧结构](#2-�行时栈帧结构) * [2.1 局部��表](#21-局部��表) @@ -45,14 +44,13 @@ ## 2 �行时栈帧结构 -**栈帧(Stack Frame)** 是用于支�虚拟机方法调用和方法执行的数�结构,它是虚拟机�行时数�区中**虚拟机栈(Virtual Machine Stack)的栈元素**。 +**栈帧(Stack Frame)**是用于支�虚拟机方法调用和方法执行的数�结构,它是虚拟机�行时数�区中**虚拟机栈(Virtual Machine Stack)的栈元素**。 栈帧存储了方法的局部��表��作数栈�动�连接和方法返回地�等信�。�一个方法从调用开始至执行完�的过程,都对应�一个栈帧在虚拟机栈里�从入栈到出栈的过程。 **栈帧概念结构如下图所示:** -![栈帧概念结构](https://user-gold-cdn.xitu.io/2018/5/4/1632919c2fc34c02?w=640&h=569&f=png&s=116882) - +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404221305.png) ### 2.1 局部��表 **局部��表是一组��值存储空间,用于存放方法�数和方法内定义的局部��。 @@ -66,17 +64,16 @@ ### 2.2 �作数栈 -**�作数栈(Operand Stack)** 也常称为�作栈,它是一个**�入先出栈**。当一个方法执行开始时,这个方法的�作数栈是空的,在方法执行过程中,会有��字节�指令往�作数栈中写入和��内容,也就是 **出栈/入栈**�作。 - -![�作数栈](https://user-gold-cdn.xitu.io/2018/5/4/16329250e46c341a?w=470&h=411&f=png&s=63864) +**�作数栈(Operand Stack)**也常称为�作栈,它是一个**�入先出栈**。当一个方法执行开始时,这个方法的�作数栈是空的,在方法执行过程中,会有��字节�指令往�作数栈中写入和��内容,也就是**出栈/入栈**�作。 +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404221316.png) 在概念模型中,一个活动线程中两个栈帧是相互独立的。但大多数虚拟机实现都会�一些优化处�:让下一个栈帧的部分�作数栈与上一个栈帧的部分局部��表��在一起,这样的好处是方法调用时�以共享一部分数�,而无须进行�外的�数�制传递。 ### 2.3 动�连接 �个栈帧都包�一个指��行时常�池中该栈帧所属方法的引用,�有这个引用是为了支�方法调用过程中的**动�连接**; -字节�中方法调用指令是以常�池中的指�方法的符�引用为�数的,有一部分符�引用会在类加载阶段或第一次使用的时候转化为直接引用,这�转化称为 **��解�**,�外一部分在�次的�行期间转化为直接引用,这部分称为**动�连接**。 +字节�中方法调用指令是以常�池中的指�方法的符�引用为�数的,有一部分符�引用会在类加载阶段或第一次使用的时候转化为直接引用,这�转化称为**��解�**,�外一部分在�次的�行期间转化为直接引用,这部分称为**动�连接**。 ### 2.4 方法返回地� @@ -106,11 +103,11 @@ “编译期�知,�行期����的方法(��方法和�有方法),在类加载的解�阶段,会将其符�引用转化为直接引用(入�地�)。这类方法的调用称为“**解�(Resolution)**�。 在Java虚拟机中�供了5�方法调用字节�指令: -- **invokestatic** : 调用��方法 -- **invokespecial**:调用实例构造器方法��有方法�父类方法 -- **invokevirtual**:调用所有的虚方法 -- **invokeinterface**:调用接�方法,会在�行时在确定一个实现此接�的对象 -- **invokedynamic**:先在�行时动�解�出点�定符所引用的方法,然��执行该方法,在此之�的4�调用命令的分派逻辑是固化在Java虚拟机内部的,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。 +-**invokestatic**: 调用��方法 +-**invokespecial**:调用实例构造器方法��有方法�父类方法 +-**invokevirtual**:调用所有的虚方法 +-**invokeinterface**:调用接�方法,会在�行时在确定一个实现此接�的对象 +-**invokedynamic**:先在�行时动�解�出点�定符所引用的方法,然��执行该方法,在此之�的4�调用命令的分派逻辑是固化在Java虚拟机内部的,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。 ### 3.2 分派 @@ -252,14 +249,14 @@ JDK新增加了invokedynamic指令�是实现“动�类型语言�。 **��语言和动�语言的区别:** * **��语言(强类型语言)**: - ��语言是在编译时��的数�类型��确定的语言,多数��类型语言�求在使用��之�必须声明数�类型。  + ��语言是在编译时��的数�类型��确定的语言,多数��类型语言�求在使用��之�必须声明数�类型。 例如:C++�Java�Delphi�C#等。 -* **动�语言(弱类型语言)** : - 动�语言是在�行时确定数�类型的语言。��使用之��需�类型声明,通常��的类型是被赋值的那个值的类型。  +* **动�语言(弱类型语言)**: + 动�语言是在�行时确定数�类型的语言。��使用之��需�类型声明,通常��的类型是被赋值的那个值的类型。 例如PHP/ASP/Ruby/Python/Perl/ABAP/SQL/JavaScript/Unix Shell等等。 -* **强类型定义语言** : +* **强类型定义语言**: 强制数�类型定义的语言。也就是说,一旦一个��被指定了�个数�类型,如果��过强制转�,那么它就永远是这个数�类型了。举个例�:如果你定义了一个整型��a,那么程�根本��能将a当作字符串类型处�。强类型定义语言是类型安全的语言。 -* **弱类型定义语言** : +* **弱类型定义语言**: 数�类型�以被忽略的语言。它与强类型定义语言相�, 一个���以赋��数�类型的值。强类型定义语言在速度上�能略逊色于弱类型定义语言,但是强类型定义语言带�的严谨性能够有效的��许多错误。 ## 4 基于栈的字节�解释执行引擎 @@ -268,15 +265,13 @@ JDK新增加了invokedynamic指令�是实现“动�类型语言�。 ### 4.1 解释执行 -Java语言�常被人们定�为 **“解释执行�语言**,在Java�生的JDK1.0时代,这�定义还比较准确的,但当主�的虚拟机中都包�了�时编译�,Class文件中的代�到底会被解释执行还是编译执行,就�了�有虚拟机自己�能准确判断的事情。���,Java也�展出�了直接生�本地代�的编译器[如何GCJ(GNU Compiler for the Java)],而C/C++也出现了通过解释器执行的版本(如CINT),这时候�笼统的说“解释执行�,对于整个Java语言�说就�了几乎没有任何�义的概念,**�有确定了谈论对象是��具体的Java实现版本和执行引擎�行模�时,谈解释执行还是编译执行�会比较确切**。 - -![ 解释执行](https://user-gold-cdn.xitu.io/2018/5/4/16329338654c4e30?w=612&h=274&f=png&s=105184) +Java语言�常被人们定�为**“解释执行�语言**,在Java�生的JDK1.0时代,这�定义还比较准确的,但当主�的虚拟机中都包�了�时编译�,Class文件中的代�到底会被解释执行还是编译执行,就�了�有虚拟机自己�能准确判断的事情。���,Java也�展出�了直接生�本地代�的编译器[如何GCJ(GNU Compiler for the Java)],而C/C++也出现了通过解释器执行的版本(如CINT),这时候�笼统的说“解释执行�,对于整个Java语言�说就�了几乎没有任何�义的概念,**�有确定了谈论对象是��具体的Java实现版本和执行引擎�行模�时,谈解释执行还是编译执行�会比较确切**。 Java语言中,javac编译器完�了程�代��过�法分��语法分�到抽象语法树,��历语法树生�线性的字节�指令�的过程,因为这一部分动作是在Java虚拟机之外进行的,而解释器在虚拟机内部,所以Java程�的编译就是�独立实现的, ### 4.2 基于栈的指令集和基于寄存器的指令集 -Java编译器输出的指令�,基本上是一�**基于栈的指令集架构(Instruction Set Architecture,ISA)**,**�赖�作数栈进行工作**。与之相对应的�一套常用的指令集架构是**基于寄存器的指令集**, **�赖寄存器进行工作**。 +Java编译器输出的指令�,基本上是一�**基于栈的指令集架构(Instruction Set Architecture,ISA)**,**�赖�作数栈进行工作**。与之相对应的�一套常用的指令集架构是**基于寄存器的指令集**,**�赖寄存器进行工作**。 那么,**基于栈的指令集和基于寄存器的指令集这两者有什么��呢?** 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/Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" similarity index 81% rename from "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" rename to "docs/Java/basic/Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" index 14b4137..fa70648 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/Java8\346\226\260\347\211\271\346\200\247\347\273\210\346\236\201\346\214\207\345\215\227.md" @@ -16,18 +16,7 @@ * [Java虚拟机(JVM)的新特性](#java虚拟机(jvm)的新特性) * [总结](#总结) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [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 +37,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 +88,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 +103,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 +190,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 +228,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 +280,7 @@ Lambda表达�和方法引用 有了函数�接�之�,就�以使用Lambda表达�和方法引用了。其实函数�接�的表中的函数�述符就是Lambda表达�,在函数�接�中Lambda表达�相当于匿�内部类的效果。 举个简�的例�: - +```` public class TestLambda { public static void execute(Runnable runnable) { @@ -310,7 +300,7 @@ public class TestLambda { execute(() -> System.out.println("run")); } } - +```` �以看到,相比于使用匿�内部类的方�,Lambda表达��以使用更少的代�但是有更清晰的表述。注�,Lambda表达�也�是完全等价于匿�内部类的, 两者的��点在于this的指�和本地��的�蔽上。 方法引用�以看作Lambda表达�的更简�的一�表达形�,使用::�作符,方法引用主�有三类: @@ -351,31 +341,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 +375,7 @@ Java 8 使我们能够使用default 关键字给接�增加�抽象的方法 如果无法��第一�规则进行判断,那么�接�的优先级更高:函数签�相�时,优先选择拥有最具体实现的默认方法的接�。�如果B继承了A,那么B就比A更具体。 最�,如果还是无法判断,继承了多个接�的类必须通过显�覆盖和调用期望的方法,显�地选择使用哪一个默认方法的实现。那么如何显�地指定呢: - +```` public class C implements B, A { public void hello() { @@ -393,43 +383,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 +430,7 @@ Java 8带�的�一个有趣的特性是接��以声明(并且�以� defaulable = DefaulableFactory.create( OverridableImpl::new ); System.out.println( defaulable.notRequired() ); } +```` 这个程�的控制�输出如下: Default implementation @@ -450,58 +444,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 +508,7 @@ public class ParameterNames { } } } +```` 如果�使用–parameters�数�编译这个类,然��行这个类,会得到下�的输出: Parameter: arg0 @@ -531,7 +525,7 @@ Java 8 通过增加大�新类,扩展已有类的功能的方��改善对 Optional实际上是个容器:它�以�存类型T的值,或者仅仅�存null。Optional�供很多有用的方法,这样我们就�用显�进行空值检测。更多详情请�考官方文档。 我们下�用两个�例��演示如何使用Optional类:一个�许为空值,一个��许为空值。 - +```` public class 空指针Optional { public static void main(String[] args) { @@ -554,7 +548,7 @@ Optional实际上是个容器:它�以�存类型T的值,或者仅仅� //输出Optional.empty。 } } - +```` 如果Optional类的实例为�空值的�,isPresent()返回true,�从返回false。为了防止Optional为空值,orElseGet()方法通过回调函数�产生一个默认值。map()函数对当�Optional的值进行转化,然�返回一个新的Optional实例。orElse()方法和orElseGet()方法类似,但是orElse接�一个默认值而�是一个回调函数。下�是这个程�的输出: Full Name is set? false @@ -581,14 +575,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 +592,10 @@ Task类有一个分数的概念(或者说是伪��度),其次是还有 .sum(); System.out.println( "Total points: " + totalPointsOfOpenTasks ); +```` 程�在控制�上的输出如下: -Total points: 18 + Total points: 18 这里有几个注�事项。 @@ -617,7 +613,7 @@ Total points: 18 stream�一个有价值的地方是能够原生支�并行处�。让我们�看看这个算task分数和的例�。 stream�一个有价值的地方是能够原生支�并行处�。让我们�看看这个算task分数和的例�。 - +```` // Calculate total points of all tasks final double totalPoints = tasks .stream() @@ -626,23 +622,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 +656,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 +679,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 +705,7 @@ Java 8通过�布新的Date-Time API (JSR 310)�进一步加强对日期与时 System.out.println( time ); System.out.println( timeFromClock ); - +```` 下�是程�在控制�上的输出: 2014-04-12 @@ -715,7 +720,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 +728,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 +738,7 @@ Duration in hours: 8783 ### 并行(parallel)数组 Java 8增加了大�的新方法�对数组进行并行处�。�以说,最��的是parallelSort()方法,因为它�以在多核机器上�大�高数组排�的速度。下�的例�展示了新方法(parallelXxx)的使用。 - +```` package com.javacodegeeks.java8.parallel.arrays; import java.util.Arrays; @@ -755,7 +760,7 @@ Java 8增加了大�的新方法�对数组进行并行处�。�以说, System.out.println(); } } - +```` 上�的代�片段使用了parallelSetAll()方法�对一个有20000个元素的数组进行�机赋值。然�,调用parallelSort方法。这个程�首先打�出�10个元素的值,之�对整个数组排�。这个程�在控制�上的输出如下(请注�数组元素是�机生产的): Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378 @@ -766,7 +771,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 +780,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 +797,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所代替。 @@ -824,20 +832,3 @@ https://www.jianshu.com/p/4df02599aeb2 https://www.cnblogs.com/yangzhilong/p/10973006.html https://www.cnblogs.com/JackpotHan/p/9701147.html -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) diff --git "a/docs/java/basic/16\343\200\201JavaIO\346\265\201.md" "b/docs/Java/basic/JavaIO\346\265\201.md" similarity index 88% rename from "docs/java/basic/16\343\200\201JavaIO\346\265\201.md" rename to "docs/Java/basic/JavaIO\346\265\201.md" index a405b7f..9d1ecd1 100644 --- "a/docs/java/basic/16\343\200\201JavaIO\346\265\201.md" +++ "b/docs/Java/basic/JavaIO\346\265\201.md" @@ -21,18 +21,7 @@ * [说说File类](#说说file类) * [说说RandomAccessFile?](#说说randomaccessfile) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [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 +49,6 @@ tags: > 在这一�节,我会试�给出Java IO(java.io)包下所有类的概述。更具体地说,我会根�类的用途对类进行分组。这个分组将会使你在未�的工作中,进行类的用途判定时,或者是为�个特定用途选择类时�得更加容易。 -​ **输入和输出** 术语“输入�和“输出�有时候会有一点让人疑惑。一个应用程�的输入往往是�外一个应用程�的输出 @@ -174,7 +162,7 @@ java.io.InputStream类是所有Java IO输入�的基类。如果你正在开� 这使得RandomAccessFile�以覆盖一个文件的�些部分�或者追加内容到它的末尾�或者删除它的�些内容,当然它也�以从文件的任何�置开始读�文件。 下�是具体例�: - +```` @Test //文件�范例,打开一个文件的输入�,读�到字节数组,�写入�一个文件的输出� public void test1() { @@ -193,6 +181,7 @@ java.io.InputStream类是所有Java IO输入�的基类。如果你正在开� e.printStackTrace(); } } +```` ### 字符�和字节� Java IO的Reader和Writer除了基于字符之外,其他方�都与InputStream和OutputStream�常类似。他们被用于读写文本。InputStream和OutputStream是基于字节的,还记得�? @@ -204,46 +193,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 +248,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 +295,7 @@ Java中网络的内容或多或少的超出了Java IO的范畴。关于Java网 当两个进程之间建立了网络连接之�,他们通信的方�如��作文件一样:利用InputStream读�数�,利用OutputStream写入数�。����说,Java网络API用�在��进程之间建立网络连接,而Java IO则用�在建立了连接之�的进程之间交�数�。 基本上�味�如果你有一份能够对文件进行写入�些数�的代�,那么这些数�也�以很容易地写入到网络连接中去。你所需��的仅仅�是在代�中利用OutputStream替代FileOutputStream进行数�的写入。因为FileOutputStream是OuputStream的�类,所以这么�并没有什么问题。 - +```` //从网络中读�字节�也�以直接使用OutputStream public void test3() { //读�网络进程的输出� @@ -318,7 +309,7 @@ Java中网络的内容或多或少的超出了Java IO的范畴。关于Java网 //处�网络信� //do something with the OutputStream } - +```` ### 字节和字符数组 @@ -330,7 +321,7 @@ Java中网络的内容或多或少的超出了Java IO的范畴。关于Java网 ��的例�中,字符数组或字节数组是用�缓存数�的临时存储空间,�过它们�时也�以作为数���或者写入目的地。 举个例�: - +```` //字符数组和字节数组在io过程中的作用 public void test4() { //arr和brr分别作为数�� @@ -339,7 +330,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 +349,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 +361,7 @@ System.out和System.err的简�例�: // 10 // out } - +```` ### 字符�的Buffered和Filter @@ -437,8 +428,6 @@ Filter Stream是一�IO�主�作用是用�对存在的�增加一些� 在java.io包中主�由4个�用的filter Stream。两个字节filter stream,两个字符filter stream. 分别是FilterInputStream, FilterOutputStream, FilterReader and FilterWriter.这些类是抽象类,�能被实例化的。 - - ### 在文件拷�的时候,那一���用��更多的性能? 在字节�的时候,使用BufferedInputStream和BufferedOutputStream。 在字符�的时候,使用BufferedReader 和 BufferedWriter @@ -459,20 +448,3 @@ https://www.cnblogs.com/UncleWang001/articles/10454685.html https://www.cnblogs.com/Jixiangwei/p/Java.html https://blog.csdn.net/baidu_37107022/article/details/76890019 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) diff --git "a/docs/java/basic/9\343\200\201Java\344\270\255\347\232\204Class\347\261\273\345\222\214Object\347\261\273.md" "b/docs/Java/basic/Java\344\270\255\347\232\204Class\347\261\273\345\222\214Object\347\261\273.md" similarity index 91% rename from "docs/java/basic/9\343\200\201Java\344\270\255\347\232\204Class\347\261\273\345\222\214Object\347\261\273.md" rename to "docs/Java/basic/Java\344\270\255\347\232\204Class\347\261\273\345\222\214Object\347\261\273.md" index 4caf35a..7ff3baa 100644 --- "a/docs/java/basic/9\343\200\201Java\344\270\255\347\232\204Class\347\261\273\345\222\214Object\347\261\273.md" +++ "b/docs/Java/basic/Java\344\270\255\347\232\204Class\347\261\273\345\222\214Object\347\261\273.md" @@ -1,4 +1,4 @@ -# Table of Contents +# 目录 * [Java中Class类�用法](#java中class类�用法) * [Class类原�](#class类原�) @@ -16,9 +16,7 @@ * [finalize()方法](#finalize方法) * [CLass类和Object类的关系](#class类和object类的关系) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -48,25 +46,25 @@ Java程�在�行时,Java�行时系统一直对所有的对象进行所谓 > Class类的对象内容是你创建的类的类型信�,比如你创建一个shapes类,那么,Java会生�一个内容是shapes的Class类的对象 > Class类的对象�能�普通类一样,以 new shapes() 的方�创建,它的对象�能由JVM创建,因为这个类没有public构造函数 - - /* - * Private constructor. Only the Java Virtual Machine creates Class objects. - * This constructor is not used and prevents the default constructor being - * generated. - */ - //�有构造方法,�能由jvm进行实例化 - private Class(ClassLoader loader) { - // Initialize final field for classLoader. The initialization value of non-null - // prevents future JIT optimizations from assuming this final field is null. - classLoader = loader; - } - +```` +/* + * Private constructor. Only the Java Virtual Machine creates Class objects. + * This constructor is not used and prevents the default constructor being + * generated. + */ + //�有构造方法,�能由jvm进行实例化 +private Class(ClassLoader loader) { + // Initialize final field for classLoader. The initialization value of non-null + // prevents future JIT optimizations from assuming this final field is null. + classLoader = loader; +} +```` > Class类的作用是�行时�供或获得�个对象的类型信�,和C++中的typeid()函数类似。这些信�也�用于�射。 ### Class类原� 看一下Class类的部分�� - +```` //Class类中�装了类型的��信�。在jvm中就是通过Class类的实例�获��个Java类的所有信�的。 public class Class类 { @@ -161,6 +159,7 @@ Java程�在�行时,Java�行时系统一直对所有的对象进行所谓 // } // } } +```` > 我们都知�所有的java类都是继承了object这个类,在object这个类中有一个方法:getclass().这个方法是用��得该类已�被实例化了的对象的该类的引用,这个引用指�的是Class类的对象。 > @@ -172,11 +171,7 @@ Java程�在�行时,Java�行时系统一直对所有的对象进行所谓 { return defineClass(name, b, off, len, null); } -> -> 我们生�的对象都会有个字段记录该对象所属类在CLass类的对象的所在�置。如下图所示: - -[外链图片转存失败(img-ZfMJTzO4-1569074134147)(http://dl.iteye.com/upload/picture/pic/101542/0047a6e9-6608-3c3c-a67c-d8ee95e7fcb8.jpg)] - +> ### 如何获得一个Class类对象 @@ -204,7 +199,7 @@ Java程�在�行时,Java�行时系统一直对所有的对象进行所谓 > 生��精确的object实例 > -==获�一个Class类的对象�,�以用 newInstance() 函数�生�目标类的一个实例。然而,该函数并�能直接生�目标类的实例,�能生�object类的实例== +获�一个Class类的对象�,�以用 newInstance() 函数�生�目标类的一个实例。然而,该函数并�能直接生�目标类的实例,�能生�object类的实例 > Class obj=Class.forName("shapes"); > Object ShapesInstance=obj.newInstance(); @@ -214,7 +209,7 @@ Java程�在�行时,Java�行时系统一直对所有的对象进行所谓 > Class obj=shapes.class; > shapes newShape=obj.newInstance(); > 因为有了类型�制,所以使用泛化Class语法的对象引用�能指�别的类。 - + Class obj1=int.class; Class obj2=int.class; obj1=double.class; @@ -236,11 +231,12 @@ Java程�在�行时,Java�行时系统一直对所有的对象进行所谓 Class rclass=round.class; Class sclass= rclass.getSuperClass(); //Class sclass=rclass.getSuperClass(); + 我们明知�,round的基类就是shapes,但是��能直接声明 Class < shapes >,必须使用特殊语法 Class < ? super round > - -这个记�就�以啦。 + + 这个记�就�以啦。 ## Object类 @@ -288,7 +284,9 @@ Object类�于java.lang包中,java.lang包包��Java最基础和核心的 ### registerNatives()方法; +```` private static native void registerNatives(); +```` > registerNatives函数��有native关键字修饰,Java中,用native关键字修饰的函数表明该方法的实现并�是在Java中去完�,而是由C/C++去完�,并被编译�了.dll,由Java去调用。 > @@ -298,19 +296,16 @@ private static native void registerNatives(); > > 既然如此,�能有人会问,registerNatives()修饰符为private,且并没有执行,作用何以达到?其实,在Java��中,此方法的声明�有紧接�一段��代��: - - private static native void registerNatives(); - static { - registerNatives(); - } - +```` +private static native void registerNatives(); +static { + registerNatives(); +} +```` ### Clone()方法实现浅拷� - - protected native Object clone() throwsCloneNotSupportedException; - - - - +```` +protected native Object clone() throwsCloneNotSupportedException; +```` > 看,clode()方法�是一个被声明为native的方法,因此,我们知�了clone()方法并�是Java的原生方法,具体的实现是有C/C++完�的。clone英文翻译为"克隆",其目的是创建并返回此对象的一个副本。 > 形象点�解,这有一辆科�兹,你看��错,想�个一模一样的。你调用此方法����魔术一样�出一辆一模一样的科�兹出�。�置一样,长相一样。但从此刻起,原�的那辆科�兹如果进行了新的装饰,与你克隆出�的这辆科�兹没有任何关系了。 @@ -321,7 +316,7 @@ private static native void registerNatives(); 首先看一下下�的这个例�: - +```` package com.corn.objectsummary; import com.corn.Person; @@ -336,9 +331,7 @@ private static native void registerNatives(); } } - - - +```` > 例�很简�,在main()方法中,new一个Oject对象�,想直接调用此对象的clone方法克隆一个对象,但是出现错误�示:"The method clone() from the type Object is not visible" > @@ -350,25 +343,23 @@ private static native void registerNatives(); 于是,上例改�如下形�,我们�现,�以正常编译: - - public class clone方法 { - public static void main(String[] args) { +```` +public class clone方法 { + public static void main(String[] args) { - } - public void test1() { + } + public void test1() { - User user = new User(); - // User copy = user.clone(); - } - public void test2() { - User user = new User(); - // User copy = (User)user.clone(); - } + User user = new User(); + //User copy = user.clone(); } + public void test2() { + User user = new User(); + //User copy = (User)user.clone(); + } +} - - - +```` 是的,因为此时的主调已�是�类的引用了。 > 上述代�在�行过程中会抛出"java.lang.CloneNotSupportedException",表明clone()方法并未正确执行完毕,问题的原因在与Java中的语法规定: @@ -378,9 +369,8 @@ private static native void registerNatives(); > Cloneable接�仅是一个表示接�,接�本身�包�任何方法,用�指示Object.clone()�以�法的被�类引用所调用。 > > 于是,上述代�改�如下形�,��正确指定clone()方法以实现克隆。 - - - public class User implements Cloneable{ +```` +public class User implements Cloneable{ public int id; public String name; public UserInfo userInfo; @@ -407,11 +397,12 @@ private static native void registerNatives(); // com.javase.Class和Object.Object方法.用到的类.User@6ff3c5b5 // com.javase.Class和Object.Object方法.用到的类.UserInfo@d716361 } +```` 总结: clone方法实现的是浅拷�,�拷�当�对象,并且在堆中分�新的空间,放这个�制的对象。但是对象如果里�有其他类的�对象,那么就�会拷�到新的对象中。 -==深拷�和浅拷�的区别== +深拷�和浅拷�的区别 > 浅拷� > 浅拷�是按�拷�对象,它会创建一个新对象,这个对象有�原始对象属性值的一份精确拷�。如果属性是基本类型,拷�的就是基本类型的值;如果属性是内存地�(引用类型),拷�的就是内存地� ,因此如果其中一个对象改�了这个地�,就会影�到�一个对象。 @@ -430,12 +421,13 @@ clone方法实现的是浅拷�,�拷�当�对象,并且在堆中分� > > 填充完�之�,clone方法返回,一个新的相�的对象被创建,�样�以把这个新对象的引用�布到外部。 -==也就是说,一个对象在浅拷�以�,�是把对象�制了一份放在堆空间的�一个地方,但是�员��如果有引用指�其他对象,这个引用指�的对象和被拷�的对象中引用指�的对象是一样的。当然,基本数�类型还是会�新拷�一份的。== - +也就是说,一个对象在浅拷�以�,�是把对象�制了一份放在堆空间的�一个地方,但是�员��如果有引用指�其他对象,这个引用指�的对象和被拷�的对象中引用指�的对象是一样的。当然,基本数�类型还是会�新拷�一份的。 ### getClass()方法 +```` +public final native Class getClass(); -4.public final native Class getClass(); +```` > getClass()也是一个native方法,返回的是此Object对象的类对象/�行时类对象Class。效果与Object.class相�。 > @@ -443,8 +435,8 @@ clone方法实现的是浅拷�,�拷�当�对象,并且在堆中分� > > 作为概念层次的类,其本身也具有�些共�的特性,如都具有类�称�由类加载器去加载,都具有包,具有父类,属性和方法等。 > -> 于是,Java中有专门定义了一个类,Class,去�述其他类所具有的这些特性,因此,从此角度去看,类本身也都是属于Class类的对象。为与�常�义上的对象相区分,在此称之为"类对象"。 - +> 于是,Java中定义了一个类,Class,去�述其他类所具有的这些特性,因此,从此角度去看,类本身也都是属于Class类的对象。为与�常�义上的对象相区分,在此称之为"类对象"。 +```` public class getClass方法 { public static void main(String[] args) { User user = new User(); @@ -470,6 +462,7 @@ clone方法实现的是浅拷�,�拷�当�对象,并且在堆中分� } } } +```` 此处主�大�涉�到Java中的�射知识 @@ -490,9 +483,6 @@ clone方法实现的是浅拷�,�拷�当�对象,并且在堆中分� return (this == obj); } - - - > 由此��,Object原生的equals()方法内部调用的正是==,与==具有相�的�义。既然如此,为什么还�定义此equals()方法? > > equals()方法的正确�解应该是:判断两个对象是�相等。那么判断对象相等的标尺�是什么? @@ -506,10 +496,10 @@ ObjectTest中打�出true,因为User类定义中�写了equals()方法,这 > 如上�写equals方法表�上看上去是�以了,实则�然。因为它破�了Java中的约定:�写equals()方法必须�写hasCode()方法。 - ### hashCode()方法; - -6. public native int hashCode() +```` +public native int hashCode() +```` hashCode()方法返回一个整形数值,表示该对象的哈希�值。 @@ -549,16 +539,16 @@ hashCode()具有如下约定: ### toString()方法 -7.public String toString(); +```` +public String toString(); toString()方法返回该对象的字符串表示。先看一下Object中的具体方法体: public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } - - - + +```` > toString()方法相信大家都�常用到,�使没有显�调用,但当我们使用System.out.println(obj)时,其内部也是通过toString()�实现的。 > @@ -570,7 +560,7 @@ hashCode()具有如下约定: ### wait() notify() notifAll() -8/9/10/11/12. wait(...) / notify() / notifyAll() + > > 一说到wait(...) / notify() | notifyAll()几个方法,首先想到的是线程。确实,这几个方法主�用于java多线程之间的�作。先具体看下这几个方法的主��义: > @@ -583,8 +573,7 @@ hashCode()具有如下约定: > wait(...) / notify() | notifyAll()一般情况下都是�套使用。下��看一个简�的例�: 这是一个生产者消费者的模型,��过这里�用flag�标识哪个线程需�工作 - - +```` public class wait和notify { //volatile��线程��性 volatile static int flag = 1; @@ -652,6 +641,7 @@ hashCode()具有如下约定: // notify t2 //�断循环 } +```` > 从上述例�的输出结果中�以得出如下结论: > @@ -661,13 +651,11 @@ hashCode()具有如下约定: 在Java��中,�以看到wait()具体定义如下: - - public final void wait() throws InterruptedException { - wait(0); - } - - - +```` +public final void wait() throws InterruptedException { + wait(0); +} +```` > 且wait(long timeout, int nanos)方法定义内部实质上也是通过调用wait(long timeout)完�。而wait(long timeout)是一个native方法。因此,wait(...)方法本质上都是native方�实现。 @@ -676,15 +664,11 @@ notify()/notifyAll()方法也都是native方法。 Java中线程具有较多的知识点,是一�比较大且��的知识点。�期会有�文专门针对Java多线程作出详细总结。此处��细述。 ### finalize()方法 -13. protected void finalize(); - finalize方法主�与Java垃圾回收机制有关。首先我们看一下finalized方法在Object中的具体定义: - - protected void finalize() throws Throwable { } - - - +```` +protected void finalize() throws Throwable { } +```` > 我们�现Object类中finalize方法被定义�一个空方法,为什么�如此定义呢?finalize方法的调用时机是怎么样的呢? > @@ -727,21 +711,11 @@ https://blog.csdn.net/dufufd/article/details/80537638 https://blog.csdn.net/farsight1/article/details/80664104 https://blog.csdn.net/xiaomingdetianxia/article/details/77429180 -## 微信公众� - ### Java技术江湖 如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� **Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) +![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/1.png) 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/Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" similarity index 83% rename from "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" rename to "docs/Java/basic/Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" index d601d6e..b871b14 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/Java\345\237\272\346\234\254\346\225\260\346\215\256\347\261\273\345\236\213.md" @@ -15,9 +15,7 @@ * [存在栈中](#存在栈中) * [存在堆里](#存在堆里) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + @@ -34,9 +32,6 @@ 如果对本系列文章有什么建议,或者是有什么疑问的�,也�以关注公众��Java技术江湖】�系作者,欢迎你�与本系列�文的创作和修订。 - - - # Java 基本数�类型 @@ -53,7 +48,6 @@ - 内置数�类型 - 引用数�类型 -- - * ### 内置数�类型 @@ -128,50 +122,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 +152,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 +181,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 +226,7 @@ Java 5增加了自动装箱与自动拆箱机制,方便基本类型与包装 Integer�� -``` +```` public final class Integer extends Number implements Comparable { private final int value; @@ -343,7 +293,7 @@ public final class Integer extends Number implements Comparable { private IntegerCache() {} } -``` +```` 以上是Oracle(Sun)公�JDK 1.7中Integer��的一部分,通过分�上�的代�,得到: @@ -363,21 +313,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 +345,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 +361,6 @@ public static void main(String[] args) { System.out.println(d1 == d2); System.out.println(d3 == d4); - `结果` `false` @@ -423,8 +372,6 @@ public static void main(String[] args) { `false` `true` - - Integer i = 10; Byte b = 10; //比较Byte和Integer.两个对象无法直接比较,报错 @@ -460,17 +407,15 @@ public static void main(String[] args) { 需�注�的是:“==�在没�到算术�算时,�会自动拆箱;基本类型�会自动装箱为对应的包装类型,代�中最�一�说明的内容。 - - 在JDK 1.5中�供了自动装箱与自动拆箱,这其实是Java 编译器的语法糖,编译器通过调用包装类型的valueOf()方法实现自动装箱,调用xxxValue()方法自动拆箱。自动装箱和拆箱会有一些陷阱,那就是包装类型�用了�些对象。 (1)Integer默认�用了[-128,127]这些对象,其中高�置�以修改; (2)Byte�用了全部256个对象[-128,127]; -(3)Short�用了[-128,127]这些对象; +(3)Short�用了[-128,127]这些对象; -(4)Long�用了[-128,127]; +(4)Long�用了[-128,127]; (5)Character�用了[0,127],Charater�能表示负数; @@ -574,10 +519,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方法还是会先去查询常�池中是�有已�存在,如果存在,则返回常�池中的引用,这一点与之�没有区别,区别在于,如果在常�池找�到对应的字符串,则�会�将字符串拷�到常�池,而�是在常�池中生�一个对原字符串的引用。 @@ -602,20 +543,4 @@ https://blog.csdn.net/a724888/article/details/80033043 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) diff --git "a/docs/java/basic/10\343\200\201Java\345\274\202\345\270\270.md" "b/docs/Java/basic/Java\345\274\202\345\270\270.md" similarity index 64% rename from "docs/java/basic/10\343\200\201Java\345\274\202\345\270\270.md" rename to "docs/Java/basic/Java\345\274\202\345\270\270.md" index c937417..4f77437 100644 --- "a/docs/java/basic/10\343\200\201Java\345\274\202\345\270\270.md" +++ "b/docs/Java/basic/Java\345\274\202\345\270\270.md" @@ -15,14 +15,7 @@ * [当finally�上return](#当finally�上return) * [JAVA异常常��试题](#java异常常��试题) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) ---- - - - Java异常 ---- 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -51,7 +44,7 @@ > 3� 由调用函数�分�异常,这�求程�员对库函数有很深的了解。 > - 在OO中�供的异常处�机制是�供代��壮的强有力的方�。使用异常机制它能够�低错误处�代�的��度,如果�使用异常,那么就必须检查特定的错误,并在程�中的许多地方去处�它。 +在OO中�供的异常处�机制是�供代��壮的强有力的方�。使用异常机制它能够�低错误处�代�的��度,如果�使用异常,那么就必须检查特定的错误,并在程�中的许多地方去处�它。 而如果使用异常,那就�必在方法调用处进行检查,因为异常机制将��能够�获这个错误,并且,�需在一个地方处�错误,�所谓的异常处�程�中。 @@ -61,21 +54,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 +95,7 @@ Throwable�派生出Error类和Exception类。 异常是在执行�个函数时引�的,而函数�是层级调用,形�调用栈的,因为,��一个函数�生了异常,那么他的所有的caller都会被异常影�。当这些被影�的函数以异常信�输出时,就形�的了异常追踪栈。 异常最先�生的地方,��异常抛出点。 - +```` public class 异常 { public static void main (String [] args ) { @@ -130,9 +122,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 +133,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 +146,7 @@ Throwable�派生出Error类和Exception类。 ## 异常和错误 下�看一个例� - +```` //错误�error一般指jvm无法处�的错误 //异常是Java定义的用于简化错误处��程和定�错误的一�工具。 public class 错误和错误 { @@ -198,7 +187,7 @@ Throwable�派生出Error类和Exception类。 // at com.javase.异常.错误.main(错误.java:11) } - +```` ## 异常的处�方� 在编写代�处�异常时,对于检查异常,有2���的处�方�: @@ -213,7 +202,7 @@ Throwable�派生出Error类和Exception类。 上�的例�是�行时异常,�需�显示�获。 下�这个例�是�检查异常需,�显示�获或者抛出。 - +```` @Test public void testException() throws IOException { @@ -309,6 +298,7 @@ Throwable�派生出Error类和Exception类。 // at com.javase.异常.异常处�方�.test(异常处�方�.java:62) throw new StringIndexOutOfBoundsException(); } +```` 其实有的语言在�到异常��然�以继续�行 @@ -321,11 +311,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 +332,7 @@ finally��管异常是��生,��对应的try执行了,则它一定 3�在�一try…catch…finally�中 ,try�生异常,且匹�的catch�中处�异常时也抛出异常,那么��的finally也会执行:首先执行finally�,然�去外围调用者中寻找�适的catch�。 +```` public class finally使用 { public static void main(String[] args) { try { @@ -357,6 +349,7 @@ finally��管异常是��生,��对应的try执行了,则它一定 } } } +```` ## throw : JRE也使用的关键字 @@ -365,15 +358,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 +381,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 +472,7 @@ throw 语�必须写在函数中,执行throw 语�的地方就是一个异 一个带有String�数和Throwable�数,并都传递给父类构造函数 一个带有Throwable �数的构造函数,并传递给父类的构造函数。 下�是IOException类的完整�代�,�以借鉴。 - +```` public class IOException extends Exception { static final long serialVersionUID = 7818375828146090155L; @@ -507,7 +497,7 @@ throw 语�必须写在函数中,执行throw 语�的地方就是一个异 super(cause); } } - +```` ## 异常的注�事项 异常的注�事项 @@ -517,47 +507,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 +555,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 +600,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 +769,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 +798,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的个数,因此,在自定义异常和通用异常之间维�一个平衡是�功的关键。 @@ -862,21 +839,4 @@ http://c.biancheng.net/view/1038.html https://blog.csdn.net/Lisiluan/article/details/88745820 https://blog.csdn.net/michaelgo/article/details/82790253 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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/Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" similarity index 85% rename from "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" rename to "docs/Java/basic/Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" index ab4a401..435e587 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/Java\346\263\250\350\247\243\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" @@ -22,21 +22,7 @@ * [什么是元注解?](#什么是元注解?) * [下�的代�会编译�?](#下�的代�会编译�?) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [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 +59,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 +150,8 @@ java.lang.annotation�供了四�元注解,专门注解其他的注解(在 JDK 内置注解 先�看几个 Java 内置的注解,让大家热热身。 - @Override 演示 - +@Override 演示 +```` class Parent { public void run() { } @@ -182,7 +166,9 @@ JDK 内置注解 public void run() { } } +```` @Deprecated 演示 +```` class Parent { /** @@ -201,7 +187,9 @@ class Parent { parent.run(); // 在编译器中此方法会显示过时标志 } } +```` @SuppressWarnings 演示 +```` class Parent { // 因为定义的 name 没有使用,那么编译器就会有警告,这时候使用此注解�以�蔽掉警告 @@ -209,8 +197,10 @@ class Parent { @SuppressWarnings("all") private String name; } - +```` @FunctionalInterface 演示 + +```` /** * 此注解是 Java8 �出的函数�接�,接�中��许有一个抽象方法 * 加上这个注解之�,类中多一个抽象方法或者少一个抽象方法都会报错 @@ -219,7 +209,7 @@ class Parent { interface Func { void run(); } - +```` ## 注解处�器实战 @@ -230,27 +220,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 +253,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 +288,7 @@ interface Func { 你�以在�行期访问类,方法或者��的注解信�,下是一个访问类注解的例�: ``` - Class aClass = TheClass.class; +Class aClass = TheClass.class; Annotation[] annotations = aClass.getAnnotations(); for(Annotation annotation : annotations){ @@ -410,8 +403,8 @@ public class TheClass { 你�以�这样�访问��的注解: ``` -Field field = ... //获�方法对象 -
Annotation[] annotations = field.getDeclaredAnnotations();
+Field field = ... //获�方法对象目录
+Annotation[] annotations = field.getDeclaredAnnotations();
 
 for(Annotation annotation : annotations){
  if(annotation instanceof MyAnnotation){
@@ -425,8 +418,8 @@ for(Annotation annotation : annotations){
 你�以�这样访问指定的��注解:
 
 ```
-Field field = ...//获�方法对象
-
+Field field = ...//获�方法对象目录
+
 Annotation annotation = field.getAnnotation(MyAnnotation.class);
 
 if(annotation instanceof MyAnnotation){
@@ -668,20 +661,5 @@ https://blog.51cto.com/4247649/2109129
 https://www.jianshu.com/p/2f2460e6f8e7
 https://blog.csdn.net/yuzongtao/article/details/83306182
 
-## 微信公众�
-
-### Java技术江湖
-
-如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开��
-
-**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。
-
-![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg)
-
-### 个人公众�:黄�斜
-
-作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力��
 
-**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 
 
-![](https://img-blog.csdnimg.cn/20190829222750556.jpg)
diff --git "a/docs/java/basic/5\343\200\201Java\347\261\273\345\222\214\345\214\205.md" "b/docs/Java/basic/Java\347\261\273\345\222\214\345\214\205.md"
similarity index 67%
rename from "docs/java/basic/5\343\200\201Java\347\261\273\345\222\214\345\214\205.md"
rename to "docs/Java/basic/Java\347\261\273\345\222\214\345\214\205.md"
index 4084792..43d7244 100644
--- "a/docs/java/basic/5\343\200\201Java\347\261\273\345\222\214\345\214\205.md"
+++ "b/docs/Java/basic/Java\347\261\273\345\222\214\345\214\205.md"
@@ -1,27 +1,21 @@
 # 目录
 
-  * [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�试指�》仓库,更多精彩内容请到我的仓库里查看
@@ -74,11 +68,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 +81,6 @@ Java 使用包(package)这�机制是为了防止命�冲�,访问控
 
 由于包创建了新的命�空间(namespace),所以�会跟其他包中的任何�字产生命�冲�。使用包这�机制,更容易实现访问控制,并且让定�相关类更加简�。
 
-
-
 ### package 的目录结构
 
 类放在包中会有两�主�的结果:
@@ -104,28 +92,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 +112,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\Runoob.class + .\com\runoob\test\Google.class +```` -你�以�下�这样�导入所有** \com\runoob\test\ **中定义的类�接�等: - -
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 +198,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 +266,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) - - rt.jar 默认就在Root Classloader的加载路径里�的,而在Claspath�置该��是�需�的;�时jre/lib目录下的其他jar:jce.jar�jsse.jar�charsets.jar�resources.jar都在Root Classloader中。 +![image](https://www.pianshen.com/images/75/856cbbdf52da90fa4f9bbb7b0597ce63.png) -## *.java文件的奥秘 +rt.jar 默认就在Root Classloader的加载路径里�的,而在Claspath�置该��是�需�的;�时jre/lib目录下的其他jar:jce.jar�jsse.jar�charsets.jar�resources.jar都在Root Classloader中。 -### *.Java文件简介 +## java文件的奥秘 +### Java文件简介 .java文件你�以认为�是一个文本文件, 这个文件�是用java语言写�的程�,或者说任务的代��。 @@ -335,43 +304,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 +367,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包的命�规则 @@ -444,20 +409,3 @@ https://www.runoob.com/java/java-package.html https://www.breakyizhan.com/java/4260.html https://blog.csdn.net/qq_36626914/article/details/80627454 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员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/Java\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" similarity index 63% rename from "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" rename to "docs/Java/basic/Java\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..4d2dfac 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/Java\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" @@ -14,14 +14,8 @@ * [存在栈中:](#存在栈中:) * [存在堆里](#存在堆里) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) - - Java基本数�类型 ---- - 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -148,14 +142,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 +162,9 @@ boolean bf = Boolean.FALSE; `1.4E-45` `1.7976931348623157E308` `4.9E-324` -`￿` - `true` `false` +``` ### 引用类型 @@ -203,23 +196,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 +224,7 @@ Integer value = Integer.valueOf(10); 实例方法xxxValue():将具体的包装类型对象转��基本类型; 下�我们以int和Integer为例,说明Java中自动装箱与自动拆箱的实现机制。看如下代�: +```` class Auto //code1 { public static void main(String[] args) @@ -246,7 +240,10 @@ class Auto //code1 } } +```` + 上�的代�先将int型转为Integer对象,�讲Integer对象转�为int型,毫无疑问,这是�以正确�行的。�是,这�转�是怎么进行的呢?使用�编译工具,将生�的Class文件在�编译为Java文件,让我们看看�生了什么: +```` class Auto//code2 { public static void main(String[] paramArrayOfString) @@ -254,17 +251,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 +265,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 +404,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 +436,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 +463,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 +482,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 +537,6 @@ JDK1.7�,常�池被放入到堆空间中,这导致intern()函数的功能 ``` -[java] view plain copy String s = new String("1"); s.intern(); String s2 = "1"; @@ -554,9 +553,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方法还是会先去查询常�池中是�有已�存在,如果存在,则返回常�池中的引用,这一点与之�没有区别,区别在于,如果在常�池找�到对应的字符串,则�会�将字符串拷�到常�池,而�是在常�池中生�一个对原字符串的引用。 @@ -581,23 +577,6 @@ https://blog.csdn.net/a724888/article/details/80033043 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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/Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md" similarity index 67% rename from "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" rename to "docs/Java/basic/Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md" index 8ad5827..16fe721 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/Java\351\233\206\345\220\210\346\241\206\346\236\266\346\242\263\347\220\206.md" @@ -26,9 +26,7 @@ * [线程安全的阻塞队列](#线程安全的阻塞队列) * [�步队列](#�步队列) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + --- title: 夯实Java基础系列19:一文�懂Java集�类框架,以�常��试题 date: 2019-9-19 15:56:26 # 文章生�时间,一般�改 @@ -61,8 +59,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 +87,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 +170,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 +258,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 +345,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; + } - 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 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; + } + } +} +```` ## 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 @@ -712,20 +710,3 @@ https://www.cnblogs.com/uodut/p/7067162.html https://www.jb51.net/article/135672.htm https://www.cnblogs.com/suiyue-/p/6052456.html -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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/final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md" similarity index 90% rename from "docs/java/basic/4\343\200\201final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md" rename to "docs/Java/basic/final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md" index f9c0f39..6b28474 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/final\345\205\263\351\224\256\345\255\227\347\211\271\346\200\247.md" @@ -18,9 +18,7 @@ * [读 final 域的�排�规则](#读-final-域的�排�规则) * [如果 final 域是引用类型](#如果-final-域是引用类型) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -184,7 +182,7 @@ final方法的好处: 2. final��在多线程中并�安全,无需�外的�步开销 3. final方法是��编译的,�高了调用速度 4. **final类创建的对象是��读的,在多线程�以安全共享** -5. + ## final关键字的最佳实践 ### final的用法 @@ -198,7 +196,7 @@ final修饰的��有三�:�����实例��和局部��,分  �外,final��定义的时候,�以先声明,而�给�值,这中��也称为final空白,无论什么情况,编译器都确�空白final在使用之�必须被�始化。   但是,final空白在final关键字final的使用上�供了更大的�活性,为此,一个类中的final数��员就�以实现�对象而有所��,�有��其�定��的特�。 - +```` public class FinalTest { final int p; final int q=3; @@ -210,19 +208,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 +229,7 @@ final修饰的函数会被编译器优化,优化的结果是�少了函数调 for(int j=0;j<1000;j++) {System.out.println("g");} }} - +```` 看出�区别了�?编译器直接将func的函数体内嵌到了调用函数的地方,这样的结果是节�了1000次函数调用,当然编译器处��字节�,�是我们�以想象�这样,看个明白。 �过,当函数体太长的�,用final�能适得其�,因为�过编译器内嵌之�代�长度大大增加,于是就增加了jvm解释字节�的时间。 @@ -243,7 +242,7 @@ final修饰的函数会被编译器优化,优化的结果是�少了函数调 �下�的测试代�,我会执行五次: - +```` public class Test { public static void getJava() @@ -283,6 +282,7 @@ final修饰的函数会被编译器优化,优化的结果是�少了函数调 } } +```` 结果为: 第一次: @@ -308,13 +308,13 @@ final修饰的函数会被编译器优化,优化的结果是�少了函数调 由以上�行结果�难看出,执行最快的是“正常的执行��代�直接编写,而使用final修饰的方法,��有些书上或者文章上所说的那样,速度与效率与“正常的执行�无异,而是�于第二�,最差的是调用�加final修饰的方法。 -观点:加了比�加好一点。 + 观点:加了比�加好一点。 ### 使用final修饰��会让��的值�能被改��; �代�: - +```` public class Final { public static void main(String[] args) @@ -335,18 +335,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 +384,7 @@ final修饰的函数会被编译器优化,优化的结果是�少了函数调 } - +```` 这样就OK了,既��了代�安全,�能让数组中的元素被访问了。 @@ -397,7 +397,7 @@ final修饰的函数会被编译器优化,优化的结果是�少了函数调 规则3:父类中private final方法,�类�以�新定义,这�情况�是�写。 代�示例 - +```` 规则1代� public class FinalMethodTest @@ -429,7 +429,7 @@ final修饰的函数会被编译器优化,优化的结果是�少了函数调 // 下�方法定义将�会出现问题 public void test(){} } - +```` ## final 和 jvm的关系 @@ -439,8 +439,8 @@ final修饰的函数会被编译器优化,优化的结果是�少了函数调 2. �次读一个包� final 域的对象的引用,与���次读这个 final 域,这两个�作之间�能�排�。 下�,我们通过一些示例性的代��分别说明这两个规则: - -
public class FinalExample {
+````
+public class FinalExample {
     int i;                            // 普通�� 
     final int j;                      //final �� 
     static FinalExample obj;
@@ -460,7 +460,7 @@ final修饰的函数会被编译器优化,优化的结果是�少了函数调
         int b = object.j;                // 读 final 域 
     }
 }
-
+```` 这里�设一个线程 A 执行 writer () 方法,���一个线程 B 执行 reader () 方法。下�我们通过这两个线程的交互�说明这两个规则。 @@ -478,11 +478,11 @@ 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 ���始化之�的值。 -写 final 域的�排�规则�以确�:在对象引用为任�线程��之�,对象的 final 域已�被正确�始化过了,而普通域�具有这个�障。以上图为例,在读线程 B“看到�对象引用 obj 时,很�能 obj 对象还没有构造完�(对普通域 i 的写�作被�排�到构造函数外,此时�始值 2 还没有写入普通域 i)。 +写 final 域的�排�规则�以确�:在对象引用为任�线程��之�,对象的 final 域已�被正确�始化过了,而普通域�具有这个�障。以上图为例,在读线程 B“看到�对象引用 obj 时,很�能 obj 对象还没有构造完�(对普通域 i 的写�作被�排�到构造函数外,此时�始值 1 还没有写入普通域 i)。 ### 读 final 域的�排�规则 @@ -495,8 +495,8 @@ final修饰的函数会被编译器优化,优化的结果是�少了函数调 reader() 方法包�三个�作: 1. �次读引用�� obj; -2. �次读引用�� obj 指�对象的普通域 j。 -3. �次读引用�� obj 指�对象的 final 域 i。 +2. �次读引用�� obj 指�对象的普通域 i。 +3. �次读引用�� obj 指�对象的 final 域 j。 现在我们�设写线程 A 没有�生任何�排�,�时程�在��守间接�赖的处�器上执行,下�是一��能的执行时�: @@ -511,8 +511,8 @@ reader() 方法包�三个�作: 上�我们看到的 final 域是基础数�类型,下�让我们看看如果 final 域是引用类型,将会有什么效果? 请看下列示例代�: - -
public class FinalReferenceExample {
+````
+public class FinalReferenceExample {
 final int[] intArray;                     //final 是引用类型 
 static FinalReferenceExample obj;
 
@@ -535,7 +535,7 @@ public static void reader () {              // 读线程 C 执行
     }
 }
 }
-
+```` 这里 final 域为一个引用类型,它引用一个 int 型的数组对象。对于引用类型,写 final 域的�排�规则对编译器和处�器增加了如下约�: @@ -543,14 +543,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 @@ -561,20 +561,4 @@ https://www.iteye.com/blog/cakin24-2334965 https://blog.csdn.net/chengqiuming/article/details/70139503 https://blog.csdn.net/hupuxiang/article/details/7362267 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) diff --git "a/docs/java/basic/20\343\200\201javac\345\222\214javap.md" "b/docs/Java/basic/javac\345\222\214javap.md" similarity index 74% rename from "docs/java/basic/20\343\200\201javac\345\222\214javap.md" rename to "docs/Java/basic/javac\345\222\214javap.md" index dde513d..f797af7 100644 --- "a/docs/java/basic/20\343\200\201javac\345\222\214javap.md" +++ "b/docs/Java/basic/javac\345\222\214javap.md" @@ -18,28 +18,10 @@ * [-encoding](#-encoding) * [-verbose](#-verbose) * [其他命令](#其他命令) - * [使用javac构建项目](#使用javac构建项目) - * [](#) -* [java文件列表目录](#java文件列表目录) - * [放入列表文件中](#放入列表文件中) - * [生�bin目录](#生�bin目录) - * [列表](#列表) - * [通过-cp指定所有的引用jar包,将src下的所有java文件进行编译](#通过-cp指定所有的引用jar包,将src下的所有java文件进行编译) - * [通过-cp指定所有的引用jar包,指定入�函数�行](#通过-cp指定所有的引用jar包,指定入�函数�行) + * [使用javac构建项目](#使用javac构建项目) * [javap 的使用](#javap-的使用) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [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 +63,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 +298,7 @@ javac:如果当�你�编译的java文件中引用了其它的类(比如说 > 这里展示一个web项目的.classpath Xml代� - +```` @@ -328,7 +310,7 @@ Xml代� …… - +```` > XML文档包�一个根元素,就是classpath,类路径,那么这里�包�了什么信�呢?�元素是classpathentry,kind属性区别了� 类信�,src��,con你看看��的path就知�是JRE容器的信�。lib是项目�赖的第三方类库,output是src编译�的�置。 > 既然是web项目,那么就是WEB-INF/classes目录,�能用MyEclipse的�学会说他们那里是WebRoot或者是WebContext而�是webapp,有区别么?回答:完全没有� @@ -393,27 +375,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 +433,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 +454,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 A +{ + public static void main(String[] args) { + B b = new B(); } - - public class B - { - } - - 如果使用: +} + +public class B +{ +} +```` +如果使用: -​ -​ javac -implicit:none A.java + +javac -implicit:none A.java 则�会生� B.class。 @@ -578,18 +560,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 +579,14 @@ java文件�常简� System.out.println("helloWorld"); } } +```` + 编译: javac src/com/yp/test/HelloWorld.java -d build -d 表示编译到 build文件夹下 -``` +```` 查看build文件夹 ├─build │ └─com @@ -615,10 +599,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 +662,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 +696,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 +714,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 +743,7 @@ ok 一个简�的java 工程就�行完了 但是 貌似有些��, 需�手动键入 java文件 以�相应的jar包 很是麻烦, so �以用 shell �脚本�简化相关�作 shell 文件整�如下: - +```` #!/bin/bash echo "build start" @@ -788,6 +774,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 +792,7 @@ shell 文件整�如下: 1.需�� 编译时设置的bin目录和 所有jar包加入到 classpath 中去 -​ + ## javap 的使用 > javap是jdk自带的一个工具,�以对代��编译,也�以查看java编译器生�的字节�。 @@ -815,37 +802,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 +863,7 @@ Compiled from "DocFooter.java" 29: return } +```` 上�输出的内容就是字节�。 用法摘� @@ -903,20 +891,3 @@ https://www.jianshu.com/p/6a8997560b05 https://blog.csdn.net/w372426096/article/details/81664431 https://blog.csdn.net/qincidong/article/details/82492140 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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/string\345\222\214\345\214\205\350\243\205\347\261\273.md" similarity index 50% rename from "docs/java/basic/3\343\200\201string\345\222\214\345\214\205\350\243\205\347\261\273.md" rename to "docs/Java/basic/string\345\222\214\345\214\205\350\243\205\347\261\273.md" index 13dfe0a..9adf53e 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/string\345\222\214\345\214\205\350\243\205\347\261\273.md" @@ -27,9 +27,7 @@ * [���有什么好处?](#���有什么好处?) * [String常用工具类](#string常用工具类) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + @@ -46,11 +44,8 @@ 如果对本系列文章有什么建议,或者是有什么疑问的�,也�以关注公众��Java技术江湖】�系作者,欢迎你�与本系列�文的创作和修订。 - - - ## string基础 ### Java String 类 @@ -70,9 +65,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 +87,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 +125,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 +167,9 @@ runoob
         System.out.println(str1.equals(str3));   //true  内容相�
         System.out.println(str1==str2);   //true   地�相�
         System.out.println(str1==str3);   //false  地���
- }
-}
- + } +} +```` ### 字符串的���性 @@ -170,7 +177,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 +225,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 +320,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 +342,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 +391,26 @@ ensureCapacityInternal(count + len); ## String和JVM的关系 - - - 下�我们了解下Java栈�Java堆�方法区和常�池: - - Java栈(线程�有数�区): ``` �个Java虚拟机线程都有自己的Java虚拟机栈,Java虚拟机栈用�存放栈帧,�个方法被执行的时候都会�时创建一个栈帧(Stack Frame)用于存储局部��表��作栈�动�链接�方法出�等信�。�一个方法被调用直至执行完�的过程,就对应�一个栈帧在虚拟机栈中从入栈到出栈的过程。 ``` - - Java堆(线程共享数�区): ``` 在虚拟机�动时创建,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分�。 ``` - - 方法区(线程共享数�区): ``` 方法区在虚拟机�动的时候被创建,它存储了�一个类的结构信�,例如�行时常�池�字段和方法数��构造函数和普通方法的字节�内容�还包括在类�实例�接��始化时用到的特殊方法。在JDK8之�永久代是方法区的一�实现,而JDK8元空间替代了永久代,永久代被移除,也�以�解为元空间是方法区的一�实现。 ``` - - 常�池(线程共享数�区): ``` @@ -437,23 +431,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 +452,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 +474,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 +504,7 @@ System.out.println(f); System.out.println(ff); } -``` +```` 这里的对f.a的修改�以�解为char[0] = 'a'这样的�作。�改�数�值,�改�内存值。 @@ -523,301 +512,288 @@ 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(); - } - - 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(); - } + s = s.toLowerCase(locale); + StringBuilder sb = new StringBuilder(); + for (char c : s.toCharArray()) { + sb.append(c == split ? Character.toUpperCase(c) : c); } -## �考文章 -https://blog.csdn.net/qq_34490018/article/details/82110578 -https://www.runoob.com/java/java-string.html -https://www.cnblogs.com/zhangyinhua/p/7689974.html -https://blog.csdn.net/sinat_21925975/article/details/86493248 -https://www.cnblogs.com/niew/p/9597379.html + return sb.toString(); +} -## 微信公众� +public static String toCamelCase(String s) { + return toCamelCase(s, Locale.getDefault(), SEPARATOR); +} -### Java技术江湖 +public static String toCamelCase(String s, Locale locale) { + return toCamelCase(s, locale, SEPARATOR); +} -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� +public static String toCamelCase(String s, char split) { + return toCamelCase(s, Locale.getDefault(), split); +} -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 +public static String toUnderScoreCase(String s, char split) { + if (isBlank(s)) { + return ""; + } -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) + 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(); +} -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� +public static String toUnderScoreCase(String s) { + return toUnderScoreCase(s, SEPARATOR); +} -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 +/** + * 把字符串转�为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 +https://www.runoob.com/java/java-string.html +https://www.cnblogs.com/zhangyinhua/p/7689974.html +https://blog.csdn.net/sinat_21925975/article/details/86493248 +https://www.cnblogs.com/niew/p/9597379.html -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) diff --git "a/docs/java/basic/7\343\200\201\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md" "b/docs/Java/basic/\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md" similarity index 89% rename from "docs/java/basic/7\343\200\201\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md" rename to "docs/Java/basic/\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md" index 09dc5b5..7ebb784 100644 --- "a/docs/java/basic/7\343\200\201\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md" +++ "b/docs/Java/basic/\344\273\243\347\240\201\345\235\227\345\222\214\344\273\243\347\240\201\346\211\247\350\241\214\351\241\272\345\272\217.md" @@ -17,9 +17,7 @@ * [��代��](#��代��) * [Java代���构造方法(包�继承关系)的执行顺�](#java代���构造方法(包�继承关系)的执行顺�) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) + 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 > https://github.com/h2pl/Java-Tutorial @@ -40,20 +38,20 @@ ## Java中的构造方法 ### 构造方法简介 -构造方法是类的一�特殊方法,用��始化类的一个新的对象。[Java](http://c.biancheng.net/java/) 中的�个类都有一个默认的构造方法,它必须具有和类�相�的�称,而且没有返回类型。构造方法的默认返回类型就是对象类型本身,并且构造方法�能被 static�final�synchronized�abstract 和 native 修饰。 +构造方法是类的一�特殊方法,用��始化类的一个新的对象。[Java](http://c.biancheng.net/java/)中的�个类都有一个默认的构造方法,它必须具有和类�相�的�称,而且没有返回类型。构造方法的默认返回类型就是对象类型本身,并且构造方法�能被 static�final�synchronized�abstract 和 native 修饰。 �示:构造方法用于�始化一个新对象,所以用 static 修饰没有�义;构造方法�能被�类继承,所以用 final 和 abstract 修饰没有�义;多个线程�会�时创建内存地�相�的�一个对象,所以用 synchronized 修饰没有必�。 构造方法的语法格�如下: - -
class class_name
+````
+class class_name
 {
     public class_name(){}    //默认无�构造方法
     public ciass_name([paramList]){}    //定义构造方法
     …
     //类主体
-}
- +} +```` 在一个类中,与类�相�的方法就是构造方法。�个类�以具有多个构造方法,但�求它们�自包���的方法�数。 ### 构造方法实例 @@ -61,8 +59,8 @@ #### 例 1 构造方法主�有无�构造方法和有�构造方法两�,示例如下: - -
public class MyClass
+````
+public class MyClass
 {
     private int m;    //定义�有��
     MyClass()
@@ -75,8 +73,8 @@
         //定义有�的构造方法
         this.m=m;
     }
-}
- +} +```` 该示例定义了两个构造方法,分别是无�构造方法和有�构造方法。在一个类中定义多个具有���数的��方法,这就是方法的�载。这两个构造方法的�称都与类�相�,�为 MyClass。在实例化该类时�以调用��的构造方法进行�始化。 注�:类的构造方法�是�求必须定义的。如果在类中没有定义任何一个构造方法,则 Java 会自动为该类生�一个默认的构造方法。默认的构造方法�包�任何�数,并且方法体为空。如果类中显�地定义了一个或多个构造方法,则 Java ���供默认构造方法。 @@ -86,8 +84,8 @@ �在��的�件下使用��的�始化行为创建类的对象,这时候就需�在一个类中创建多个构造方法。下�通过一个示例�演示构造方法的使用。 (1) 首先在员工类 Worker 中定义两个构造方法,代�如下: - -
public class Worker
+````
+public class Worker
 {
     public String name;    //姓�
     private int age;    //年龄
@@ -106,15 +104,15 @@
     {
         return"大家好�我是新�的员工,我�"+name+",今年"+age+"�。";
     }
-}
- +} +```` 在 Worker 类中定义了两个属性,其中 name 属性��改�。分别定义了带有一个�数和带有两个�数的构造方法,并对其属性进行�始化。最�定义了该类的 toString() 方法,返回一�新进员工的介�语�。 �示:Object 类具有一个 toString() 方法,该方法是个特殊的方法,创建的�个类都会继承该方法,它返回一个 String 类型的字符串。如果一个类中定义了该方法,则在调用该类对象时,将会自动调用该类对象的 toString() 方法返回一个字符串,然�使用“System.out.println(对象�)�就�以将返回的字符串内容打�出�。 (2) 在 TestWorker 类中创建 main() 方法作为程�的入�处,在 main() 方法中调用��的构造方法实例化 Worker 对象,并对该对象中的属性进行�始化,代�如下: - -
public class TestWorker
+````
+public class TestWorker
 {
     public static void main(String[] args)
     {
@@ -127,16 +125,16 @@
         Worker worker2=new Worker("�丽",25);
         System.out.println(worker2);
     }
-}
- +} +```` 在上述代�中,创建了两个��的 Worker 对象:一个是姓�为张强的员工对象,一个是姓�为�丽�年龄为 25 的员工对象。对于第一个 Worker 对象 Worker1,并未指定 age 属性值,因此程�会将其值采用默认值 0。对于第二个 Worker 对象 Worker2,分别对其指定了 name 属性值和 age 属性值,因此程�会将传递的�数值�新赋值给 Worker 类中的属性值。 �行 TestWorker 类,输出的结果如下: -
-----------带有一个�数的构造方法-----------
-大家好�我是新�的员工,我�张强,今年0�。
------------带有两个�数的构造方法------------
-大家好�我是新�的员工,我��丽,今年25�。
+ -----------带有一个�数的构造方法----------- + 大家好�我是新�的员工,我�张强,今年0�。 + -----------带有两个�数的构造方法------------ + 大家好�我是新�的员工,我��丽,今年25�。 通过调用带�数的构造方法,在创建对象时,一并完�了对象�员的�始化工作,简化了对象�始化的代�。 @@ -152,7 +150,7 @@ �能被static�final�abstract修饰(有final和static修饰的是�能被�类继承的,abstract修饰的是抽象类,抽象类是�能实例化的,也就是�能new) �以被private修饰,�以在本类里�实例化,但是外部�能实例化对象(注����) - +```` public class A{ int i=0; public A(){ @@ -162,25 +160,26 @@ this.i=i; } } - +```` ### 默认构造方法 如果没有任何的构造方法,编译时系统会自动添加一个默认无�构造方法 ��的默认构造方法 - +```` public A(){} - +```` 显示的默认构造方法 - +```` public A(){ System.out.print("显示的默认构造方法") } +```` ### �载构造方法 比如原本的类里的构造方法是一个�数的,现在新建的对象是有三个�数,此时就��载构造方法 当一个类中有多个构造方法,有�能会出现��性�作,这时�以用this语�调用其他的构造方法。 - +```` public class A{ private int age; private String name; @@ -209,6 +208,7 @@ System.out.println(name); System.out.println(name1); System.out.println(age); +```` ### java�类构造方法调用父类构造方法 @@ -265,9 +265,9 @@ > 调用:调用其所在的方法时执行 -​ - - public class 局部代�� { + +```` +public class 局部代�� { @Test public void test (){ B b = new B(); @@ -294,7 +294,7 @@ System.out.println("hello"); } } - +```` ### 构造代�� >�置:类�员的�置,就是类中方法之外的�置 @@ -302,7 +302,7 @@ >作用:把多个构造方法共�的部分��出�,共用构造代�� >调用:�次调用构造方法时,都会优先于构造方法执行,也就是�次new一个对象时自动调用,对 对象的�始化 - +```` class A{ int i = 1; int initValue;//�员��的�始化交给代���完� @@ -340,7 +340,7 @@ A a = new A(); } } - +```` ### ��代�� �置:类�员�置,用static修饰的代�� @@ -348,7 +348,8 @@ 作用:对类进行一些�始化 �加载一次,当new多个对象时,�有第一次会调用��代��,因为,��代�� 是属于类的,所有对象共享一份 调用: new 一个对象时自动调用 - + + ```` public class ��代�� { @Test @@ -376,7 +377,7 @@ System.out.println("��代��调用"); } } - +```` ## Java代���构造方法(包�继承关系)的执行顺� 这是一�常�的�试题,�回答这个问题,先看看这个实例�。 @@ -385,7 +386,7 @@ 其中A是B的父类,C无继承仅作为输出 A类: - +```` public class A { static { @@ -408,8 +409,9 @@ A类: } } +```` B类: - +```` public class B extends A { private static C c1 = new C("B���员"); @@ -434,14 +436,16 @@ B类: } } +```` C类: - +```` public class C { public C(String str) { Log.i("HIDETAG", str + "构造方法"); } } +```` 执行语�:new B(); 输出结果如下: @@ -473,7 +477,7 @@ C类: **看完上�这个demo,��看看下�这�题,看看你�得定�?** 看下�一段代�,求执行顺�: - +```` class A { public A() { System.out.println("1A类的构造方法"); @@ -485,7 +489,7 @@ C类: System.out.println("3A类的���"); } } - + public class B extends A { public B() { System.out.println("4B类的构造方法"); @@ -503,6 +507,7 @@ C类: System.out.println("8"); } } +```` 执行顺�结果为:367215421548 @@ -535,20 +540,3 @@ http://c.biancheng.net/view/976.html https://blog.csdn.net/evilcry2012/article/details/79499786 https://www.jb51.net/article/129990.htm -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) diff --git "a/docs/java/basic/12\343\200\201\345\217\215\345\260\204.md" "b/docs/Java/basic/\345\217\215\345\260\204.md" similarity index 51% rename from "docs/java/basic/12\343\200\201\345\217\215\345\260\204.md" rename to "docs/Java/basic/\345\217\215\345\260\204.md" index 0513f67..14629eb 100644 --- "a/docs/java/basic/12\343\200\201\345\217\215\345\260\204.md" +++ "b/docs/Java/basic/\345\217\215\345\260\204.md" @@ -22,18 +22,6 @@ * [Java�射机制的作用](#java�射机制的作用) * [如何使用Java的�射?](#如何使用java的�射) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [Java技术江湖](#java技术江湖) - * [个人公众�:黄�斜](#个人公众�:黄�斜) ---- -title: 夯实Java基础系列12:深入�解Java中的�射机制 -date: 2019-9-12 15:56:26 # 文章生�时间,一般�改 -categories: - - Java技术江湖 - - Java基础 -tags: - - Java�射 ---- 本系列文章将整�到我在GitHub上的《Java�试指�》仓库,更多精彩内容请到我的仓库里查看 @@ -61,11 +49,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 +70,15 @@ Java�射框架主��供以下功能: ## �射的主�用途 ->  很多人都认为�射在实际的Java开�应用中并�广泛,其实�然。 +> 很多人都认为�射在实际的Java开�应用中并�广泛,其实�然。 ->  当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点�,编译器就会自动列出它的属性或方法,这里就会用到�射。 +> 当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点�,编译器就会自动列出它的属性或方法,这里就会用到�射。 ->  �射最��的用途就是开���通用框架。 +> �射最��的用途就是开���通用框架。 ->  很多框架(比如Spring)都是�置化的(比如通过XML文件�置JavaBean,Action之类的),为了��框架的通用性,它们�能需�根��置文件加载��的对象或类,调用��的方法,这个时候就必须用到�射——�行时动�加载需�加载的对象。 +> 很多框架(比如Spring)都是�置化的(比如通过XML文件�置JavaBean,Action之类的),为了��框架的通用性,它们�能需�根��置文件加载��的对象或类,调用��的方法,这个时候就必须用到�射——�行时动�加载需�加载的对象。 ->  举一个例�,在�用Struts 2框架的开�中我们一般会在struts.xml里去�置Action,比如: - - - /shop/shop-index.jsp - login.jsp - -�置文件与Action建立了一�映射关系,当View层�出请求时,请求会被StrutsPrepareAndExecuteFilter拦截,然�StrutsPrepareAndExecuteFilter会去动�地创建Action实例。 - -——比如我们请求login.action,那么StrutsPrepareAndExecuteFilter就会去解�struts.xml文件,检索action中name为login的Action,并根�class属性创建SimpleLoginAction实例,并用invoke方法�调用execute方法,这个过程离�开�射。 - -> 对与框架开�人员�说,�射虽�但作用�常大,它是��容器实现的核心。而对于一般的开�者�说,�深入框架开�则用�射用的就会少一点,�过了解一下框架的底层机制有助于丰富自己的编程�想,也是很有益的。 +> 对于框架开�人员�说,�射虽�但作用�常大,它是��容器实现的核心。而对于一般的开�者�说,�深入框架开�则用�射用的就会少一点,�过了解一下框架的底层机制有助于丰富自己的编程�想,也是很有益的。 ## �射的基础:关于Class类 @@ -117,25 +93,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 +123,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 +152,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 +184,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 +205,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 +246,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 +295,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 +396,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�射常��试题 @@ -637,20 +612,3 @@ https://blog.csdn.net/grandgrandpa/article/details/84832343 http://blog.csdn.net/lylwo317/article/details/52163304 https://blog.csdn.net/qq_37875585/article/details/89340495 -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) diff --git "a/docs/java/basic/17\343\200\201\345\244\232\347\272\277\347\250\213.md" "b/docs/Java/basic/\345\244\232\347\272\277\347\250\213.md" similarity index 94% rename from "docs/java/basic/17\343\200\201\345\244\232\347\272\277\347\250\213.md" rename to "docs/Java/basic/\345\244\232\347\272\277\347\250\213.md" index e2cc758..35e3ada 100644 --- "a/docs/java/basic/17\343\200\201\345\244\232\347\272\277\347\250\213.md" +++ "b/docs/Java/basic/\345\244\232\347\272\277\347\250\213.md" @@ -16,18 +16,7 @@ * [Java多线程优先级](#java多线程优先级) * [Java多线程�试题](#java多线程�试题) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [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 @@ -91,7 +80,7 @@ Java 给多线程编程�供了内置的支�。 一�线程指的是进程 * **新建状�:** - 使用 **new** 关键字和 **Thread** 类或其�类建立一个线程对象�,该线程对象就处于新建状�。它��这个状�直到程� **start()** 这个线程。 + 使用**new**关键字和**Thread**类或其�类建立一个线程对象�,该线程对象就处于新建状�。它��这个状�直到程�**start()**这个线程。 * **就绪状�:** @@ -99,7 +88,7 @@ Java 给多线程编程�供了内置的支�。 一�线程指的是进程 * **�行状�:** - 如果就绪状�的线程获� CPU 资�,就�以执行 **run()**,此时线程便处于�行状�。处于�行状�的线程最为��,它�以�为阻塞状��就绪状�和死亡状�。 + 如果就绪状�的线程获� CPU 资�,就�以执行**run()**,此时线程便处于�行状�。处于�行状�的线程最为��,它�以�为阻塞状��就绪状�和死亡状�。 * **阻塞状�:** @@ -642,21 +631,4 @@ https://blog.csdn.net/qq_38038480/article/details/80584715 https://blog.csdn.net/tongxuexie/article/details/80145663 https://www.cnblogs.com/snow-flower/p/6114765.html -## 微信公众� - -### Java技术江湖 - -如果大家想�实时关注我更新的文章以�分享的干货的�,�以关注我的公众��Java技术江湖】一�阿里 Java 工程师的技术�站,作者黄�斜,专注 Java 相关技术:SSM�SpringBoot�MySQL�分布��中间件�集群�Linux�网络�多线程,�尔讲点Docker�ELK,�时也分享技术干货和学习�验,致力于Java全栈开�� - -**Java工程师必备学习资�:** 一些Java工程师常用学习资�,关注公众��,��回�关键字 **“Java�** ���费无套路获�。 - -![我的公众�](https://img-blog.csdnimg.cn/20190805090108984.jpg) - -### 个人公众�:黄�斜 - -作者是 985 硕士,蚂�金� JAVA 工程师,专注于 JAVA �端技术栈:SpringBoot�MySQL�分布��中间件�微�务,�时也懂点投资�财,�尔讲点算法和计算机�论基础,��学习和写作,相信终身学习的力�� - -**程�员3T技术学习资�:** 一些程�员学习技术的资�大礼包,关注公众��,��回�关键字 **“资料�** ���费无套路获�。 - -![](https://img-blog.csdnimg.cn/20190829222750556.jpg) 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/\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" similarity index 57% rename from "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" rename to "docs/Java/basic/\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..c6d33a7 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/\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" @@ -9,18 +9,7 @@ * [为什么�实现Serializable](#为什么�实现serializable) * [�列化知识点总结](#�列化知识点总结) * [�考文章](#�考文章) - * [微信公众�](#微信公众�) - * [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 +26,6 @@ tags: - 本文�考 http://www.importnew.com/17964.html和 https://www.ibm.com/developerworks/cn/java/j-lo-serial/ @@ -86,96 +74,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 +169,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() { + + } + //实现下�两个方法�以选择�列化中需�被�制的�员。 + //并且,写入顺�和读�顺��一致,�则报错。 + //�以写入多个�类型��,顺���一致��。 + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(i); + out.writeInt(j); + out.writeObject(s); } - - @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 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 +228,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 +323,7 @@ ArrayList的�列化 带�这个问题,我们�看java.util.ArrayList的�� +```` public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable { @@ -341,26 +331,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 +367,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