高级语言的运行机制
Java语言是一种特殊的高级语言,它既有***解释性语言***的特征,也具有***编译性语言***的特征。
Java程序要经过***先编译后解释***两个步骤
计算机高级语言按程序的执行方式可以分为
编译型
和解释型
两种编译型语言 (C,C++)
- 使用专门的编译器,针对特定平台(操作系统)将
某种高级语言源代码
一次性翻译为可被该平台硬件执行的机器码(包括机器指令和操作数)
,并包装成可被该平台所能识别的可执行要经过先编译后解要经过先编译后解性程序的格式。这个转换过程成为编译。- 编译生成的可执行程序可以脱离开发环境,在特定的平台上独立运行。但是无法移植到其他平台上运行,如果需要移植,必须将源代码复制到特定平台上,针对特定平台进行修改,至少要采用特定平台上的编译器重新编译。
- 有些程序编译结束后,还可能需要对其他编译好的目标代码进行链接,即组装两个以上的目标代码模块生成最终的可执行程序,通过这种方式实现低层次的代码复用。
解释型语言(Python,JavaScript)
- 使用专门的解释器对源程序逐行解释成特定平台上的机器码并立即执行的语言。
- 解释型语言通常不会进行整体性的编译和链接处理,解释型语言相当于把编译型语言中的编译和解释过程混合到一起同时完成。
- 不能脱离解释其独立运行,但可以跨平台,只需要提供特定平台上的解释器即可。
理解API
API(Application Programming Interface应用程序编程接口)是一些预先定义的函数,提供应用程序与开发人员基于某软件或硬件得以访问的能力,即无需访问源码或理解内部工作机制的细节。
例子
- 我们使用fopen()函数可以打来一个文件。文件保存在硬盘上,要经过复杂的处理才能显示,这些细节对我们来说是透明的,由操作系统来完成。即我们调用fopen函数来通知操作系统,让操作系统打开一个文件。
- 看似简单的操作到底层都非常复杂,打开文件首先要扫描硬盘,找到文件的位置,然后从文件中读取一部分数据,将数据放入I/O缓存区,放进内存;这些数据都是0,1序列,还要对照ASCII表或Unicode表“翻译”称字符,再在显示器上显示出来。很麻烦。于是:
- 操作系统想了一个好办法,它***预先将这些复杂的操作写入一个函数里面,编译成一个组件(一般是动态链接库),随操作系统一起发布,并配上说明文档。***程序员只需要简单地调用这些函数就可以完成复杂的工作。这些封装好的函数,就叫做API,即应用程序编程接口。
理解DOS
DOS是Disk Operation System的简写。“磁盘操作系统”。DOS主要是一种面向磁盘的的系统软件。DOS是人与机器的一座桥梁,是罩在机器硬件外面的一层外壳。
理解虚拟机
虚拟机就是允许你***在当前操作系统中运行其他操作系统***,虚拟操作系统会像你电脑上的另一个程序一样运行。
理解JVM
-
Java Virtual Machine(JVM) java虚拟机
-
他是一个抽象的计算机
- 指令集
- 寄存器
- 类文件的格式
- 栈
- 垃圾回收堆
- 存储区
-
抽象理解JVM
- 要将一个笔帽(Java字节码程序)套在两支不同的笔上,为这两支笔分别提供一个转接器(JVM),转接器向上的接口相同(连笔帽),向下的接口不同(适应两支不同的笔—不同的操作系统)。
- 所有平台上的JVM向上提供给Java字节码的接口完全相同,向下适应不同平台的接口则互不相同。
-
JVM为什么会让Java可以跨平台?
-
使用高级语言撰写程序(因为高级语言比较贴近人类阅读方法),但是机器***只***认识***0,1序列组成的机器指令***,所以必须有一个担任翻译员工作的***编译程序Compiler***.
-
每个平台认识的0,1序列并不一样,某个指令在Windows下也许是0101,但是在Linux下也许是1010.
-
而Java编译时,并不直接编译为相依与某个平台的01序列,而是翻译成中介格式的位码.
-
不同的平台必须安装专属该平台的JVM。
-
所以就达到了“编译一次,到处执行”的跨平台的目的
-
JVM就是Java程序的操作系统,JVM的可执行文件就是.class文档。
-
理解JRE和JDK
-
JRE:Java Runtime Environment Java运行环境。
-
JDK:Java Development Kit Java软件开发工具包
理解PATH和CLASSPATH和JAVA_HOME
实体操作系统下执行某个命令时,会根据PATH中的路径信息,试图找到可执行文件。
操作系统 | 搜索路径 |
---|---|
Linux | PATH |
JVM | CLASSPATH |
-
JVM是Java程序唯一识别的操作系统,想在JVM中执行某个可执行文件(.class),就要***告诉JVM这个虚拟操作系统到哪些路径下寻找文档***,方式是通过CLASSPATH指定其可执行文件(.class)的路径信息。
-
想在运行java程序时临时指定JRE搜索java类的路径,可以使用
-classpath
或者-cp
.JRE将严格按-cp所指定的路径来搜索java类,不会在当前路径下搜索Java类,CLASSPATH环境变量所指定的搜索路径也不再有效。java -cp dir1:dir2 Java类
-
如果想使CLASSPATH环境变量指定的搜索路径有效,而且还会在当前路径下搜索Java类
java -cp %CLASSPATH:.:dir1:dir2 Java类
名称 | 含义 | 使用方式 |
---|---|---|
JAVA_HOME | 保存你JDK的安装路径 | 告诉操作系统,javac和java的具体位置 |
PATH | 外部命令搜索路径(java,javac) | 是给操作系统用的 |
CLASSPATH | 类资源位置搜索路径(这是命令执行工具要执行的类文件的地址) | 是给javac和java用的,JVM操作系统用的。告诉他们你写的java类在什么地方 |
管理原始码与位码文档
原始码文档(以.java结尾的源代码)放到src(源码目录)
位码文档(以.class结尾的可执行文件)放到classes(位码目录)
eg:
javac -sourcepath src -d classes src/Main.java
: 在编译src/Main.java时,由于程序代码中要使用到Console类,你必须告诉编译程序Console类的原始码文档存放位置。使用
-sourcepath
指定从src文件夹中寻找原始码文档,而-d
制定了编译完成的位码存放的文件夹,编译程序会将使用到的相关类原始码也一并进行编译,编译完成之后会在classes文件夹中看到Console.class和Main.class
使用package管理类
一个程序会有很多个类彼此合作,如果A部门写了个Util类并编译成为Util.class,B部门写了个Util类并编译成为Util.class,当他们要将整个应用程序整合时就会发生文档覆盖的问题。如果现在要统一管理原始码,也许原始码也会发生彼此覆盖的问题。
包通常用组织或者单位的网址命名。有一个网址是openhome.cc,包就会反过来为cc.openhome。由于组织或单位的网址是独一无二的,所以这样的命名方式不会与其他组织或单位的包名称发生同名冲突。
使用package管理
Helloword这个类放在cc.openhome.util包中
当类原始码开始使用package进行分类时,会有四种管理上的意义:
原始码文档要放置在与package所定义名称层级相同的文件夹层级中。
- 原始码文档和包管理
- 计划将所有的原始码文档放在src文件夹中管理,由于Helloword类使用了package定义在cc.openhome.util包下,所以Helloword.java必须放在src文件夹中的cc/openhome/util文件夹中。手动建立文件夹。
- 目的:以后如果不同组织的原始码放置在一起管理就不容易发生原始码文档彼此覆盖的情况。
package所定义的名称与class所定义的名称,会结合而成类的完全吻合名称(Fully Qualified Name)
完全吻合名称
package cc.openhome.util; public class Console { public static void writeLine(String text) { System.out.println(text) } } package cc.openhome; public class Main { public static void main(String] args) { Console.writeLine("Hello,world"); } }
- Main类是位于cc.openhome包分类中,其完全吻合名称是cc.openhome.Main。Console类位于cc.openhome.util分类中,其完全吻合名称是cc.openhome.util.Console
- 在原始码中指定使用某个类时,如果是相同包中的类,只要使用class所定义的名称就可以。如果是不同包中的类,就必须使用cc.openhome.util.Console。所以现在Main.java要改
package cc.openhome; public class Main { public static void main(String] args) { cc.openhome.util.Console.writeLine("Hello,world"); } }
好处在于:如果另一个单位使用了class定义了Console,但他的包定义为com.abc,则其完全吻合名称为com.abc.Console,也就不会与cc.openhome.util.Console发生名称冲突问题。
位码文档要放置在与package所定义名称层级相同的文件夹层级中
- 位码文档与包管理
- 由于Console类使用package定义在cc.openhome.util包下,所以编译出来的Console.class必须放在classes文件夹的cc/openhome/util文件夹中。
- 不用手动建立对应报层级的文件夹,在编译的时候使用-d指定为麻纺织的位置,就会自动建立对应层级的文件夹,并将编译出来的位码文档防止到应有的位置。
- 注意:由于Main类位于cc.openhome包中,所以使用java执行程序时,必须指定完全吻合名称,也就是指定cc.openhome.Main这个名称
要在包间可以直接使用的类或方法必须声明为public
使用import偷懒
使用包管理,解决了实体文档和写程序时类名称冲突的问题,但如果每次写的时候都要输入完全吻合名称,也是件麻烦的事情。
![2019-01-28 12-06-17 的屏幕截图](/home/lala/图片/2019-01-28 12-06-17 的屏幕截图.png)
当程序剖析Main.java时看到import声明时,会先记得的import名称,后续剖析程序时,如果看到Console名称,原本会不知道Console是什么东西,但是编译程序记得你用import告诉过他,如果遇到不认识的名称,可以对比一下import过的名称,编译程序试着使用cc.openhome.util.Console。如果可以再指定的路径(cc/openhome/util文件夹)下找到Console.class,并进行编译。
所以import只是告诉编译程序,遇到不认识的类名称,可以尝试使用import过的名称,import可是让你少打一下一些字,让编译程序多为你做一些事情。
如果同一个包下使用到多个类,便会多次使用import:
import cc.openhome.Message; import cc.openhome.User; import cc.openhome.Address;
可以换成
import cc.openhome.*;
或者
import cc.openhome.util.*;
java程序的基本规则
java是现象对象的程序设计语言,java必须以
类
存在,所有程序必须定义在类中。Java解释器规定:必须有
public static void main(String[] args)
java程序从main方法开始执行
可以有很多类,但是只能有一个public类。只有一个类包含main方法,其他类都是用于被main方法直接或间接调用的。
垃圾回收
什么是内存泄露
一些分配出去的内存得不到及时回收,就会引起系统运行速度下降,甚至导致程序瘫痪。
java程序垃圾回收是什么?
- Java语言不需要程序员直接控制内存回收,他的内存分配和回收都是
由JRE在后台自动进行的
。JRE会负责回收那些不再使用的内存,这种机制被称为垃圾回收(GC)。- 通常JRE会提供一个后台线程来进行检测和控制,一般都是在CPU空闲或内存不足时自动进行垃圾回收。
- java的堆内存是一个运行时数据区,用以保存类的实例(对象),java虚拟机的堆内存中存储着正在运行的应用程序所建立的所有对象,这些对象不需要程序通过代码来显示的释放。
- 堆内存的回收由垃圾回收器负责,所有JVM的实现都有一个由垃圾回收器管理的堆内存。
- 垃圾回收是一种动态存储管理技术,它自动释放不再被程序引用的对象,按照特定的垃圾回收算法来实现内存资源的自动回收功能。
c/c++和java回收垃圾的区别
- 在c/c++中,对象所占用的内存不会被自动释放,如果程序没有显示释放对象所占用的内存,对象所占用的内存就不能分配给其他对象。该内存在程序结束运行之前都将一直被占用
- 在java中,当
没有引用变量指向原先分配给某个对象的内存时
,该内存便成为垃圾。JVM的一个超级线程会自动释放该内存区。还能干些什么?
- 释放没用的对象
- 清除内存记录碎片
- 创建对象和垃圾回收器释放丢弃对象所占的内存空间,内存会出现碎片。
- 碎片是分配给对象的内存块之间的空闲内存区,碎片整理将所占用的堆内存移到对的一端,JVM将整理出的内存分配给新的对象
- 自动释放内存空间
垃圾回收的特点
- 目标是回收无用对象的内存空间
- 这些内存空间都是JVM堆内存里的内存空间,垃圾回收只能回收内存资源,对其他物理资源,如数据库连接,磁盘I/O等资源则无能为力
- 更快让垃圾回收器回收不再使用的对象
- 可以将对象的引用变量设置为null
- 垃圾回收发生的不可预知性
- 不同的JVM采用了不同的垃圾回收机制和不同的垃圾回收算法。
- 他有可能是定时发生的,有可能是CPU空闲时候发生的,有可能和原始的垃圾回收一样,等到内存消耗出现极限时发生,这和垃圾回收实现机制的选择和具体的设置都有关系。
- 虽然程序员可以通过调用
Runtime
对象的gc()
和System.gc()
等方法来建议系统进行垃圾回收,但依然不能精确控制垃圾回收机制的执行。- 垃圾回收的精确性
- 明确标记活着的对象
- 完全回收所有废弃对象的前提
- 精确的定位对象之间的引用关系
- 实现归并和复制等算法的必要条件
- 通过这种引用关系可以保证所有对象都能被可靠地回收,所有对象都能被重新分配,从而有效的减少内存碎片的产生
- 回收机制的算法不同
- 当垃圾回收开始时停止应用程序的运行
- 当垃圾回收运行时允许应用程序的线程运行
- 在同一时间允许垃圾回收多线程运行