Java语言概述
0. 前言(清华大学郑莉)
计算机程序设计——对问题进行抽象,用计算机语言表述,利用机器求解。
“C语言在很大程度上可以代替汇编语言”。
编译连接以后生成的可执行程序一定是与平台相关的,因为每个机器的系统不同,其指令集就不同。
编译执行的效率要高于解释——解释需要“翻译一行执行一行”,编译则已经完成翻译了,直接执行。
可移植不同与跨平台,可移值指不修改或修改极少的源代码,但仍需重新编译,跨平台则编译都不需要。
关于面向对象
面向对象的思想:将客观事物看作具有状态和行为的对象,通过抽象找出同一类对象的共同状态和行为,构成类。
一个完整的类型应该包含着它的数据、结构,以及这个数据能够参与的操作及操作规则。
面向对象给软件开发带来的益处:可重用性、可靠性。
如果一个语言只支持类而不支持继承和多态,那么不能说它是面向对象的语言,只能说它是基于对象的语言。
关于Java
Java语言的特点:面向对象、安全性。后者指Java不支持指针且内部有安全措施。
Java中没有全局变量,可以在类中定义公用、静态的数据成员实现相同功能。
Java不支持操作符重载。
Java语言中,能够独立运行的程序称为应用程序(Application),而Java还有另外一种程序,称为Java小程序(Applet) ,其是运行于各种网页中、用于增强网页的人机交互、动画显示、声音播放等功能的程序。
标准输入/输出:C/C++/Java都没有输入输出语句,而Fortran有(?)。C语言用函数库来实现输入输出,Java用类库……
1. 概念
Java语言是一门非常纯粹的面向对象编程语言,它吸收了C++语言的各种优点,又摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。
Java程序必须以类(class)的形式存在,类是Java程序的最小程序单位,Java不允许可执行性语句、方法等成分独立存在,所有的程序部分都必须放在类定义里。
Java是语言本身支持了多线程,而C语言是需要调用操作系统的接口来实现多线程。
从某种程度上来看,学精了Java语言的相关方面,相当于系统地学习了软件开发相关知识,而不是仅仅学完了一门编程语言。
Java的三个版本:
- J2SE: 整个Java技术的核心和基础,它是J2ME和J2EE编程的基础
- J2EE: Java技术中应用最广泛的部分,J2EE提供了企业应用开发相关的完整解决方案
- J2ME: 主要用于控制移动设备和信息家电等有限存储的设备
Java源程序命名:
扩展名必须是.java
通常情况下,源程序的主文件名可以是任意的;然而,如果Java程序源代码里定义了一个public类,该源文件名必须与该public类的类名相同。
由于Java程序源文件的文件名必须与public类的类名相同,因此,一个java源文件里最多只能定义一个public类。
2. Java的编译、执行
Java语言编写的程序需要经过编译步骤,但这个编译步骤并不会产生特定平台的机器码,而是生成一种与平台无关的字节码(也就是*.class
文件),这种字节码不是可执行的,必须使用Java虚拟机来解释执行。因此可以认为,Java语言既是编译型语言,又是解释型语言。

javac编译后生成的字节码文件有默认的文件名:文件名总是以源文件所定义类的类名作为主文件名,以.class
作为扩展名。这意味着如果一个源文件里定义了多个类,将编译生成多个字节码文件。
运行java程序的命令是:java Java类名
。
如果想在运行Java程序时临时指定JRE搜索Java类的路径,则可以使用-classpath
选项 (或用-cp
选项,-cp
是简写,作用完全相同),即格式为:
java -classpath dir1;dir2;dir3…;dirN Java类
其中;
是windows下对路径的分隔符,在linux下应该以:
分隔。
Java解释器规定:如需某个类被解释器直接解释执行,则这个类里必须包含main
方法,且其必须使用public static void
来修饰,同时其形参必须是字符串数组类型String[] args
。
这意味着
main
方法的写法几乎是固定的,Java虚拟机就从这个main
方法开始解释执行,因此,main
方法是程序的入口。若运行Java程序时在类名后紧跟一个或多个字符串(多个字符串之间以空格隔开),JVM就会把这些字符依次赋给
args
数组元素;若某参数本身包含了空格,则应该将该参数用双引号(""
)括起来,否则JVM会将这个空格当成参数分隔符,而非一个单独的参数。2
效率
当然,解释型虚拟机指令肯定会比全速运行机器指令慢很多。不过,Java虚拟机有一个选项,可以将执行最频繁的字节码序列转换成机器码,这一过程称为即时编译。
3. Java术语
术语名 | 缩写 | 解释 |
---|---|---|
Java Development Kit | JDK | Java开发工具包。如果需要开发Java程序,则应该选择安装JDK;当然,安装了JDK之后,就包含了JRE,也可以运行Java程序。 |
Java Runtime Environment | JRE | Java运行时环境。运行Java程序的必需条件。简单地说,JRE包含JVM,一般而言,如果只是运行java程序,可以只安装JRE,无须安装JDK。 |
Server JRE | / | 服务器JRE,在服务器上运行 Java 程序的软件 |
Standard Edition | SE | 标准版,用于桌面或简单服务器应用的Java平台 |
Enterprise Edition | EE | 企业版,用于复杂服务器应用的Java平台 |
Micro Edition | ME | 微型版,用于小型设备的Java平台 |
Java FX | / | 用于图形化用户界面的一个备选工具包,在Java11之前的某些JavaSE发布版本中提供 |
OpenJDK | / | Java SE的一个免费开源实现 |
Java 2 | J2 | 一个过时的术语,用于描述1998~2006年之间的Java版本 |
Software Development Kit | SDK | 一个过时的术语,用于描述1998~2006年之间的JDK |
Update | u | Oracle公司的术语,表示Java 8之前的bug修正版本 |
NetBeans | / | Oracle公司的集成开发环境 |
4. Java环境变量:JAVA_HOME、CLASSPATH
Java有两个重要的相关环境变量需要理解及正确配置。
安装好Java以后,各种IDE、Tomcat等软件是通过搜索操作系统的PATH
环境变量来找到你安装的Java软件的,因而需要在PATH
中配置Java的安装目录。通过像这样配置:在操作系统中新增JAVA_HOME
环境变量,令其为Java安装目录,然后在PATH
中添加一项来引用JAVA_HOME
,即
环境变量 | 内容 |
---|---|
JAVA_HOME | C:\Program Files\Java\jdk1.8.0_291 |
PATH | %JAVA_HOME%\bin |
对于CLASSPATH
环境变量,其含义是指定Java程序执行时JRE搜索Java类的路径,只有该.class
文件包含在CLASSPATH
的路径中,JRE才能找到该字节码文件并解释执行。
你可以选择在操作系统中添加一个CLASSPATH
属性,并指定相关文件夹路径以提供给JRE,如
环境变量 | 内容 |
---|---|
CLASSPATH | .;C:\Users\chuan\Desktop\temp |
就将当前目录即桌面的temp/
文件夹赋予了CLASSPATH
。不过,这样未免太麻烦,事实上在默认情况下(不在操作系统中设置CLASSPATH
环境变量),每次JRE执行时都会将当前目录作为搜索路径添加到CLASSPATH
中,因而它总能找到当前目录下的Java可执行文件;此外,如果你需要执行其他目录下的.class
文件,也通过在命令行下使用java命令的-classpath
属性指定搜索路径java -classpath dir1;dir2;dir3…;dirN JavaClassName
,或通过set
命令set classpath=dir1;dir2
设定搜索路径,达到同样的效果,不过要指出的是,这两种命令行的方法设置CLASSPATH
属性都具有临时性——只在当前命令行中有效。
需要进一步指出的是,一旦你像上述表格一样显示指定了CLASSPATH
环境变量,(且执行时没有通过命令行额外增加搜索路径,)那么JRE便只会在该环境变量中搜索Java类。这也就是说,如果你将CLASSPATH
显示设置为C:\Users\chuan\Desktop\temp
,也即没有增加当前路径,那么即便你在某个目录下想要执行当前目录的.class
文件,JRE也会报错。
5. Java垃圾回收
通常JRE会提供一个后台线程来进行检测和控制,一般都是在在CPU空闲或内存不足时自动进行垃圾回收,而程序员无法精确控制垃圾回收的时间和顺序等。
事实上,除释放没用的对象外,垃圾回收也可以清除内存记录碎片,碎片整理将所占用的堆内存移到堆的一端,JVM将整理出的内存分配给新的对象。
垃圾回收的一个潜在缺点是它的开销影响程序性能。因为JVM必须跟踪程序中有用的对象,才可以确定哪些对象是无用的对象,并最终释放这些无用的对象,这个过程需要花费处理器的时间。
为了更快地让垃圾回收器回收那些不再使用的对象,可以将该对象的引用变量设置为null
,以此暗示垃圾回收器可以回收该对象。
虽然程序员可以通过调用Runtime
对象的gc()
或System.gc()
等方法来建议系统进行垃圾回收,但这种调用仅仅是建议,依然不能精确控制垃圾回收机制的执行。
6. 关于Java9
Java9于2017年发布。
模块化系统是JDK 9的重大更新,为此JDK专门引入了一种新的JMOD格式。
在Java 9之前,有32位和64位两个版本的JDK,现在Oracle公司不再开发32位版本。
JDK 9工具的一大改进就是提供了jshell工具,它是一个交互式的命令行界面,可用于执行java语言的变量声明、语句和表达式,且可以立即看到执行结果。
除了/help,/exit
之外,jshell还有如下常用命令:
/help
:/exit
:/list
:列出用户输入的所有源代码/edit
:编辑用户输入的第几条源代码,jshell会启动一个文本编辑界面。/drop
:删除用户输入的第几条源代码/save
:保存用户输入的源代码/vars
:列出用户定义的所有变量/methods
:列出用户定义的全部方法/types
:列出用户定义的全部类型
7. var关键字
在Java10中,可以使用var
来声明局部变量,此时java将从变量的初始值来推导出它们的类型。
8. 与C++的异同
Java中所有的函数都是某个类的方法(标准术语将其称为方法,而不是成员函数)。
与C/C++一样,关键字void
表示这个方法没有返回值,所不同的是main
方法没有为操作系统返回“退出码”。如果main
方法正常退出,那么Java应用程序的退出码为0
,表示成功地运行了程序。如果希望在终止程序时返回其他的退出码,那就需要使用System.exit
方法。
在C++中,数值甚至可以代替boolean
值。值0
相当于布尔值false
,非0
值相当于布尔值true
。而在java中则不是这样,java程序员不会遇到下述麻烦:
if (x = 0) // cops... meant x == 0
Java的控制流程结构与C和C++的控制流程结构一样,只有很少的例外情况。Java中没有goto
语句,但break
语句可以带标签,可以利用它从内层循环跳出;此外,Java还有一种变形的for
循环,它有点类似于C++中基于范围的for
循环和C#中的foreach
循环。
在C++中,可以在嵌套的块中重定义一个变量。在内层定义的变量会覆盖在外层定义的变量,这就有可能带来编程错误,因此Java中不允许这样做。
Java数组与堆栈上的C++数组有很大不同,但基本上与在堆上分配的数组指标一样。
在C++中,通常在类的外面定义方法,如果在类的内部定义方法,这个方法将自动成为内联方法。而在Java中,所有的方法都必须在类的内部定义,但并不表示它们是内联方法,是否将某个方法设置为内联方法是JVM的任务,JIT会监视那些简短、经常调用而且没有被覆盖的方法调用,并进行优化。
void Employee::raiseSalary(double byPercent) {
// ...
}
class Employee {
// ...
int getName() {
return name; // inline in C++
}
}
在C++中,要使用::
操作符访问作用域之外的静态字段和静态方法,如Math::PI
。
在C++中,经常用下划线或某个固定的字母(一般选用m或x)作为实例字段的前缀。例如,salary字段可能被命名为_salary, mSalary或xSalary。Java程序员通常不这么做。
在Java中,this引用等价于C++中的this指针。但是,在C++中,一个构造器不能引用另一个构造器。在C++中,必须将抽取出的公共初始化代码编写成一个独立的方法。
C++程序员经常将import语句与#include弄混。实际上这两者之间没有共同之处。在C++中与包机制类似的是命名空间特性,可以认为Java中的package和import语句类似于C++中的namespace和using指令。
Java与C++定义继承的方式十分相似,Java用关键字extends
代替了C++中的冒号:
。在Java中,所有的继承都是公共继承,而没有C++中的私有继承和保护继承。
在C++中,有一种抽象方法称为纯虚函数,要在末尾用=0
标记。只要有一个纯函数,这个C++类就是抽象类,在C++中没有提供用于表示抽象类的特殊关键字。
Java中的protected
成分对所有子类及同一个包中的所有其他类都可见,这与C++中的保护机制稍有不同,Java的protected
概念要比C++中的安全性差。
在很多程序设计语言中,特别是在C/C++中,必须在编译时就确定整个数组的大小,很多程序员对此十分反感。
Class#newInstance
方法相当于C++中的虚拟构造器概念,Class
类类似于C++中的type_info
类,getClass
方法则等价于typeid
运算符。
有些程序设计语言(尤其是C++)允许一个类有多个超类,我们将这种特性称为多重继承。Java的设计者选择了不支持多重继承,其主要原因是多重继承会让语言变得非常复杂(如同C++),或者效率会降低(如同Eiffel)。
- C++的多重继承带来了一些复杂的特性,如虚基类、控制规则、横向指针类型转换,等等。很少有C++程序员使用多重继承。
- 实际上,Java的接口能够提供多重继承的大多数好处,同时还能避免多重继承的复杂性和低效性。
C++有嵌套类,被嵌套的类包含在外围类的作用域内。
Java中的异常处理与C++或Delphi中的异常处理十分类似。
C++中有两个基本的异常类,一个是runtime_error,另一个是logic_error。logic_error类相当于Java中的RuntimeException,也表示程序中的逻辑错误;runtime_error类是所有由于不可预测的原因所引发的异常的基类。
在Java中,只能抛出Throwable子类的对象,而在C++中,却可以抛出任何类型的值。
从表面上看,Java的泛型类似于C++的模板类,唯一明显的不同是Java没有特殊的template关键字。但实际上,这两种机制有着本质的区别。
面对一大堆相互冲突的设计策略,Java设计人员希望让类库规模很小而且要易于学习,不希望像C++的STL那样复杂,但却又希望能够得到STL率先提出的“泛型算法”所具有的优点。