#### **前言** 本系列总结主要来自平时积累的知识点,以及传智毕向东老师的视频以及笔记、以及[阳哥的Java基础教程的笔记](http://bbs.itheima.com/thread-200600-1-1.html)以及一些[大神终点的csdn博客](http://blog.csdn.net/zhangerqing/article/list/1),文章中难免有些地方不足,还请大家共同发现学习交流。 [菜鸟教程之Java教程](https://www.runoob.com/java/java-tutorial.html) [JDK源码在线查看-JDK6](http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/8deef18bb749/src/share/classes) [JDK源码在线查看-JDK7U](http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/bcba89ce0a8c/src/share/classes/) [JDK源码在线查看-JDK8](http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes) [JDK源码在线查看-JDK10](http://hg.openjdk.java.net/jdk10/jdk10/jdk/file/777356696811/src) [JDK 11 在线中文手册](https://www.runoob.com/manual/jdk11api/index.html) [JDK 1.6 在线中文手册(旧版)](https://www.runoob.com/manual/jdk1.6/) [W3Cschool——Java教程](https://www.w3cschool.cn/java/) [Java教程官方网站](https://www.oracle.com/java/technologies/) [深入理解Java虚拟机](https://www.w3cschool.cn/javavm/) * [ ] Java发展历史 ![](https://img.kancloud.cn/ef/88/ef888da7c2cc1eba7b23e359754f4ba2_2214x778.png) #### **1. Java三大特性:面向对象、跨平台(与平台无关)、垃圾回收机制** **Java主要特性**——大图[请点击这里](https://box.kancloud.cn/7551dcc8d4f6e57a8abaa013536bc284_1517x1008.png) ![Java主要特性](https://box.kancloud.cn/7551dcc8d4f6e57a8abaa013536bc284_1517x1008.png =1517x1008) #### **2. 面向对象三大特征:封装、继承、多态** #### 3. Android 系统中提供了一套开发工具,就不需要JAVAME #### **4. 跨平台性**:通过Java语言编写的应用程序,一次编译后,在不同的系统平台上都可运行。 原理是只要在需要运行java应用程序的操作系统上,先安装一个Java虚拟机(JVM Java Virtual Machine)即可。**JVM不具有跨平台性**,不同的操作系统安装不同版本的JVM,由JVM来负责Java程序在该系统中的运行。也称Java有良好的可移植性。(**注意:不是所有的平台都可运行,关键是该平台是否能安装相应的虚拟机JVM**)。 ![不同版本的JVM](https://box.kancloud.cn/e39917ff121474b7e12dfaeea252ae6d_660x277.bmp =660x277) #### **5. 名词解释**(下面的图示详解了JDK、JRE、JVM、Java开发工具以及Java类库之间的关系) - **JDK**(Java Development Kit)Java开发工具包 : JRE+Java开发工具 - **JRE**(Java Runtime Environment)Java运行环境:JVM+类库(Java library) - **Java开发工具**:编译工具(javac.exe)+打包工具(jar.exe)等等 - **API** Application Programming Interface(应用编程接口), 语言、框架以及类库对外提供的编码的接口。 - 使用JDK开发完成的java程序,交给JRE去运行。 **为什么JDK中包含一个JRE(使用ANSI C 实现的)呢?** 其一,开发完的程序,需要运行一下看看效果,就像exe文件需要在windows环境下运行一样。 其二,也是最重要的,**JDK中的开发工具(如javac.exe、java.exe等)其实都是Java语言编写的应用程序**,为了方便使用才打包成exe文件,如果没有JRE,那么这些工具是运行不了的。 - JDK环境变量配置可以参考[这里](http://www.cnblogs.com/smyhvae/p/3788534.html),配置完成打开运行输入cmd,打开DOS命令行,输入java、javac、java -version检验是否配置成功 ![Java语言](https://box.kancloud.cn/c6a4df4d76aeba5cc1046e5f45a36a35_900x380.png =900x380) - java是分两部分的:一个是编译,一个是运行。**Java编译器是Java实现的,Java运行环境是用ANSI C实现的**。 - javac:负责的是编译的部分,当执行javac时,会启动java的编译器程序。对指定扩展名的.java文件进行编译。 生成了jvm可以识别的字节码文件(中立的体系结构)。也就是class文件,也就是java的运行程序。**不是只有Java语言才可以生成class文件,也就是说不只是有Java语言才可以运行在JVM上(只要能够生成字节码文件的语言都可以运行在JVM上)**。关于class文件的详细介绍,可参考——[Class文件详解](https://www.kancloud.cn/alex_wsc/android_plugin/471061) - java:负责运行的部分,会启动jvm,加载运行时所需的类库,并对class文件进行执行。 运行时,Java平台中的解释器对这些字节码进行解释执行,执行过程中需要的类在联接阶段被载入到运行环境中。 一个文件要被执行,必须要有一个执行的起始点,这个起始点就是main函数。如下图所示 ![Java程序运行机制](https://box.kancloud.cn/1113635236eee2e3c485d925e935a2ec_1019x167.png =1019x167) **备注:javap命令详解** 通过在命令行中输入javap -help,可以查看到javap的命令选项帮助。 ![](https://box.kancloud.cn/53dc0afcbf17b12dc4d31fe502402fc5_549x335.jpg) **javap**:方便的反编译工具,不能将字节码还原为java文件,只能编译出数据区、方法、类引用、vm指令等,不过在线上环境还是能起到快速定位问题的作用 **作用**:反编译Java代码,查看Java字节码。 **用法**:`javap <options> <classes>` **选项**: * -version:版本信息 * -v:输出附加信息 * -l:输出行号和本地变量表 * -public:仅显示公共类和成员 * -protected:显示受保护的/公共类和成员 * -package:显示程序包/受保护的/公共类和成员 (默认) * -p或-private:显示所有类和成员 * -c:对代码进行反汇编 * -s:输出内部类型签名 * -sysinfo:显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列) * -constants:显示最终常量 * -`classpath <path>`:指定查找用户类文件的位置 * -`cp <path>`:指定查找用户类文件的位置 * -`bootclasspath <path>`:覆盖引导类文件的位置 >[info] 注意: > no options(后面无任何操作):列出-package对应的成员变量、方法,不带vm指令,例如:`javap Hello.class` > -c 分解方法代码,即显示每个方法具体的字节码,例如:`javap -c Hello.class` #### **6. 包** 包:定义包用 package关键字 1:对类文件进行分管理。 2:给类文件提供多层名称空间。 包是一种封装形式,用于封装类,想要被包以外的程序访问,该类必须是public权限。 类名的全称的是:包名.类名。 包与包之间访问可以使用的权限有两种: 1:public 2:protected :只能是不同包中的子类可以使用的权限。 **总结Java中的四种权限** ![](https://box.kancloud.cn/33633a69a1fdce8f437b3f4c6e8e4410_558x295.jpg) - jdk中常用的包 1. java.lang----包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。 2. java.awt----包含了构成抽象窗口工具集(abstract window toolkits抽象窗口工具包)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。 3. java.applet----包含applet运行所需的一些类。 4. java.net----包含执行与网络相关的操作的类。网络应用编程接口 5. java.io----包含能提供多种输入/输出功能的类。 6. java.util----包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。 7. javax.swing: 提供所有的windows桌面应用程序包括的控件,比如:Frame , Dialog, Table, List 等等,就是java的图形界面库。 #### **7. 垃圾回收器** - Java 的一个重要特点就是具有一个**垃圾回收器**,并**且能够自动回收垃圾**,这也是Java相对于其他语言有优势的地方。 - Java 类的**实例对象和数组**所需的存储空间是**在堆上分配**的,解释器具体承担了为该类的实例对象分配空间的工作。解释器在为一个实例对象分配完存储空间后,便开始记录对该实例对象所占用的内存区域的使用。一旦对象使用完毕,便将其回收到垃圾箱中。 - **在Java 语言中,除了new 语句外没有任何其他方法会为一个对象申请和释放内存**。 - Java 的自动垃圾回收功能解决了两个最常见的应用程序错误:**内存泄露和无效内存的引用。** - **Java 垃圾回收器只能管理程序中的类的实例对象,没法去管理系统产生的资源,所以程序需要调用close 方法(是接口Closeable中的方法),去通知系统释放其自身产生的资源**。 - **finalize 方法** - finalize()方法是Object 类的一个方法,任何一个类都从Object 那继承了这个方法。 - 当垃圾回收器(GC)确定不存在对该对象的更多引用时,由对象的GC调用此方法,子类重写finalize()方法,以配置系统资源或者执行其他清除,**对于任何给定对象,JVM最多调用一次finalize()方法,如果一个对象的finalize方法被调用,就表示这个对象马上要被从内存中清除。** - finalize()方法是在对象被当成垃圾从内存中释放前调用,而不是在对象变成垃圾前调用,**垃圾回收器的启用不由程序员控制,也无规律可循,并不会一产生了垃圾,它就被唤起,甚至有可能到程序终止,它都没有启动的机会**。因此这并不是一个很可靠的机制,所以,我们**无法保证每个对象的finalize()方法最终都会被调用。我们只要了解一下finalize()方法的作用就行了,不要期望finalize()方法去帮我们做“需要可靠完成”的工作。** - **System.gc的作用** - Java 的垃圾回收器被执行的偶然性有时候也会给程序运行带来麻烦,比如说在一个对象成为垃圾时需要马上被释放,或者程序在某段时间内产生大量垃圾时,释放垃圾占据的内存空间似乎成了一件棘手的事情,如果垃圾回收器不被启动,finalize()方法也不会被调用。为此,**Java 里提供了一个System.gc()方法,使用这个方法可以强制启动垃圾回收器来会收垃圾** - 手动调用这个方法,可以**强制回收垃圾** ![](https://box.kancloud.cn/c37097a1ef880fe7b58cf6297fa9e922_1187x1012.png) #### **8. Java的内存(JVM内存)** - Java程序在运行时,需要在内存中分配空间。为了提高运算效率,又对空间进行了不同区域的划分,因为**每一片区域都有特定的处理数据方式和内存管理方式。** 下图就是Java虚拟机运行时数据区 ![Java虚拟机运行时数据区](https://box.kancloud.cn/f18b3567804272a50bdad1b16aec8e91_1005x590.png) - java分了5片内存 - 1:寄存器,2:本地方法栈(Native Method Stack)3:方法区,4:栈(Stack)即JVM虚拟机栈,5:堆(heap)。 - 寄存器(程序计数器)——线程私有 - 每个线程都必须有一个独立的程序计数器,这类计数器为线程私有的内存。此类内存是线程私有的内存 - 如果一个线程正在执行一个Java方法,则计数器记录的是虚拟机字节码指令的地址;如果执行的一个Native方法,则计数器的记录为空,**此内存区是唯一一个在Java规范中没有任何OutOfMemoryError情况的区域。** - 本地方法栈——线程私有 - 本地方法栈为虚拟机使用到的Native方法服务 - 本地方法栈(Native Method Stack)与虚拟机栈(即**平常我们所说的栈**)所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。**与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常**。 - 方法区——线程公有 - 用于存储已被虚拟机加载的**类信息、常量、静态变量、即时编译器编译后的代码等数据** - **方法区分为静态区域和非静态区域** - 静态区域存放静态成员 - 方法区的变量也有默认值(string为null,int为0) - 方法区又称为数据区、共享区、共享数据区等(不同的版本翻译也不一样) - **当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常**。 - Java虚拟机规范对方法区的限制非常宽松,除了和Java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。**这区域的内存回收目标主要是针对常量池的回收和对类型的卸载**,一般来说,这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收确实是必要的 - 运行时常量池(线程公有) - 运行时常量池(Runtime Constant Pool)是方法区的一部分。主要用于存放在编译过程中产生的字面量(字面量简单理解就是**常量**)和**引用**。一般情况,常量的内存分配在编译期间就能确定,但不一定全是,有一些可能就是运行时也可将常量放入常量池中。 - 如上面所讲:运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。 - 栈(JVM虚拟机栈)——线程私有 - 栈:**存储的都是局部变量** ( 函数中定义的变量,函数上的参数,语句中的变量 );**只要数据运算完成所在的区域结束,该数据就会被释放**。在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配 - **局部变量都是在栈内存中**,**主函数在栈里面**; - 在方法中定义的变量(局部变量),包括基本数据类型和引用数据类型变量,都将在栈内存中分配空间,当超过变量作用范围后自动回收。 - 函数执行要进栈,函数执行完毕就要出栈,释放资源 - 基本类型的变量和对象的引用变量都在栈里面;这个引用变量的取值和对象在栈中的内存首地址是一一对应的。 - 线程私有,生命周期和线程相同。 - 局部变量表中存放的是各种基本数据类型,如boolean、byte、char、等8种,及引用类型(存放的是指向各个对象的内存地址),因此,它有一个特点:内存空间可以在编译期间就确定,运行期不在改变。 - 虚拟机描述的Java方法执行的内存模型:每个方法被执行的时候会产生一个栈帧,用于存储局部变量表、动态链接、操作数、方法出口等信息。**方法的执行过程就是栈帧在JVM中出栈和入栈的过程**。 - 这个内存区域会有两种可能的Java异常: - StackOverFlowError:线程请求的栈的深度大于虚拟机所允许的深度 - OutOfMemoryError:扩展时无法申请到足够的内存 - 堆——线程公有 - 堆内存用来存放由new 创建的对象(包含对象的属性和行为)和数组(**存放实体**),在堆中分配的内存,由**Java 虚拟机的自动垃圾回收器来管理**。 - 用于存储数组和对象,也就是**实体**。啥是实体啊?就是用于封装多个数据的。 1. 每一个实体都有内存首地址值。 2. 堆内存中的变量都有默认初始化值。因为数据类型不同,值也不一样。 3. 垃圾回收机制。 - 关于堆的描述,深入理解Java虚拟机这样描述 - Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。**此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。** - Java虚拟机规范中描述:**所有的对象实例以及数组都要在堆上分配,但是随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了** - Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆” - 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常 总结:堆内存用来释放由new创建的对象和数组,在堆中分配的内存,由Java虚拟机自动垃圾回收器来管理;**栈内存中定义的变量,分配的内存在程序运行到起作用域之外就会被释放,而堆内存不会被释放,由GC决定。** > 在堆中产生了一个数组或对象后,我们还可以在**栈中定义一个特殊的变量**,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,**栈中的这个变量就成了数组或对象的引用变量**,我们以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象,引用变量就相当于是我们为数组或对象起的一个名称(叫代号也行)。**引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放**。而**数组和对象本身在堆中分配,即使程序运行到使用new 产生数组和对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它时,才会变为垃圾,不能再被使用,但仍然占据内存空间不放,在随后一个不确定的时间被垃圾回收器收走(释放掉)。这也是Java 比较吃内存的原因。** ![引用变量栈内存和堆内存](https://box.kancloud.cn/aa35661d8ecd5316045210b1900196df_423x176.bmp =423x176) 在[Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收](https://blog.csdn.net/zhangerqing/article/details/8214365)一文中介绍了JVM的内存结构 ![JVM内存结构](https://img-my.csdn.net/uploads/201211/23/1353648016_8668.jpg) 关于JVM内存的详细介绍,可参考周志明老师的深入理解Java虚拟机:JVM高级特性与最佳实践一书 **PS:直接内存(Direct Memory)** - 直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现 - 在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。 - 本机直接内存的分配不会受到Java堆大小的限制,但是,既然是内存,肯定还是会受到本机总内存(包括RAM以及SWAP区或者分页文件)大小以及处理器寻址空间的限制。服务器管理员在配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。 **参考文章**:[Jvm系列5----Jvm内存模型](https://www.kancloud.cn/alex_wsc/androidsystem/483875) #### **9. JVM语言原理** ![](https://img.kancloud.cn/94/5e/945e838a753eb986e411af69fc55d840_662x1072.png) >[info]JVM规范与Java规范是相互独立的,只要生成的编译文件匹配JVM字节码规范,任何语言都可以由JVM编译运行。 Kotlin也是一种JVM语言,完全兼容java,可以与java相互调用;Kotlin语言的设计受到Java、C#、JavaScript、Scala、Groovy等语言的启发。 #### **10. 学习 java注意的地方** - Java语言拼写上严格区分大小写 ; - 写代码,阅读性第一,功能性第二,一定要注意写代码的格式! - 一个 Java源文件里可以定义多个 Java 类, 但其中最多只能有一个类被定义成public类; - 若源文件中包括了public类,源文件必须和该 public类同名; - 一个源文件中包含N个Java类时,编译后会生成N份字节码文件,即每个类都会生成一份单独的class文件,且字节码文件名和其对应的类名相同; - 将每个源文件中单独定义的类都定义成 public的; - 保持Java源文件的主文件名与源文件中的类名一致 - main方法的作用 - 程序的入口 - 保证程序的独立运行 - 被JVM调用