第一章:Java概述
1、高级语言的运行机制
计算机高级语言按程序的执行方式可以分为:编译型和解释型
编译型:使用专门的编译器、针对特定的平台(操作系统)将某种高级语言源代码一次性的翻译为可在该平台硬件执行的机器码(包括机器指令和操作数),并包装成该平台所能识别的并执行程序的格式。
编译生成的可执行的程序可以脱离开发环境,在特定的平台上独立运行。
有些程序编译完后,还需要对其编译好的目标代码进行链接,即组装两个以上的目标代码模块生成最终的可执行的程序,通过这种方式实现低层次的代码复用。
优点:一次性的编译为机器码,可以脱离开发环境独立运行,运行效率高。
缺点:该程序被编译为特定平台的机器码,因为编译成的可执行性程序通常无法移植到别的平台上。如果需要移植,则需要将源代码复制到特定平台,进行针对性的修改,再使用特定平台上的编译器进行重新编译。
C、C++、FORTRAN、Pascal都属于编译型的。
解释性:通常不会进行整体性的编译和链接,而是使用专门的解释器对源程序进行逐行的边解释成机器码边执行。
缺点:每次执行程序都需要都需要进行一次编译,运行效率低,而且不能脱离解释器独立运行。
优点:跨平台比较容易,只需要提供特定平台的解释器即可,每个特定平台的解释器,负责将源程序解释为特定的平台的机器指令,可以方便的进行移植,但这是以牺牲执行效率为代价的。
Ruby、Python都属于解释性的语言。
JAVA语言比较特殊,JAVA语言编写的程序需要经过编译步骤,但是他并不是编译为特定平台的机器码,而是编译为一种与平台无关的字节码(.class文件),这种字节码是不能执行的,必须使用JAVA解释器来解释执行。
JAVA程序的执行必须经过先编译,后解释两个步骤,既不是纯粹的编译型语言,也不是纯粹的解释型语言。
Java虚拟机JVM负责解释执行字节码文件,不同平台上的JVM是不同的,但他们都提供了相同的接口。
JVM是一个抽象的计算机,他和实际的计算机一样,它具有指令集并使用不同的存储区域,他负责执行指令,还要管理数据、内存和寄存器。
Sun公司制定的java虚拟机规范在技术上规定了JVM的统一标准,具体定义了JVM的如下细节:
* 指令集
* 寄存器
* 类文件的格式
* 栈
* 垃圾回收堆
* 存储区
Sun公司制定这些规范的目的是为了提供统一的标准,最终实现JAVA程序的平台无关性。
2、所谓JDK
JDK的全称为:Java SE Development Kit,即JAVA标准版开发包,它提供了编译、运行Java程序所需的各种工具和资源,包括Java编译器、Java运行时环境以及常用的Java类库等。
Java运行时环境,即JRE,是运行Java程序的必备条件,一般而言,如果我们只需要运行Java程序,我们可以只安装JRE,无需安装JDK。 JVM包含在JRE中。
安装完JDK后,可在JDK的路径下看到如下目录:
bin:该路径下存放了JDK的各种工具命令,如常用的java,javac。
db:安装Java DB的路径,没安装则没有
demo:该路径下存放了JDK提供的演示代码,初学者可以参考这些演示代码。
jre:运行Java所必须的JRE环境。
lib:该路径下存放的是JDK工具命令的实际执行程序,如我们使用WinRAR打开lib下的tools.jar文件,可以看到里面的文件结构。
sample:存放JDK提供的一些简单的示例代码,供初学者参考。
src.zip:该压缩文件里存放的就是Java所有核心类库的源代码。
其他的还有,readme和license等说明性文档。
环境变量的设置:
编译和运行Java程序必须经过两个步骤:
1、将源文件编译成字节码;
2、解释执行平台无关的字节码程序。
这两个步骤分别使用java和javac命令。
如果不配置环境变量,我们在命令提示符中输入java、javac命令,计算机不知道去哪里去找这个命令。
计算机是如何查找命令的呢?
windows根据Path环境变量来查找命令。Path环境变量的值是一系列路径,windows操作系统在其中查找命令,如果能找到这个命令,则该命令式可执行的。否则会出现“XX不是内部或外部命令,也不是可运行的程序或批处理文件”的提示。Linux操作系统则根据PATH环境变量来查找命令。windows操作系统不区分大小写,Path和PATH没有区别,但是Linux操作系统有大小写之分。但是不管Linux还是windows,都配置成PATH,就可以了。
3、我的第一个java程序--HelloWorld
public class HelloWorld{
//Java程序的入口方法,程序将从这里开始执行
public static void main(String [] args)
{
//向控制台打印一条语句
System.out.println("Hello Java!!");
}
}
注意:Java程序严格区分大小写!System.out而不能写成system.out
编写好Java源代码后,接下来应该编译该Java源文件来生成字节码文件了。
编译Java程序需要使用javac命令:
javac -d destdir srcFile
-d destdir :用以指定编译生成的字节码文件的存放路径,destdir只需是本地磁盘的一个有效路径即可
srcFile:java源文件所在的位置,这个位置可以是绝对路径,也可以是相对当前目录的相对路径。
通常生成的字节码文件存放在当前路径下,当前路径可用(.)来表示,在命令行进入HelloWorld.java文件所在的路径,然后输入如下命令:
javac -d . HelloWorld.java
javac编译文件只需要指定存放目标文件的位置即可,无需指定字节码文件的文件名。因为javac编译后生成的字节码文件有默认的文件名:
文件名总是以源文件所定义类的类名作为主文件名,以.class作为后缀名。这意味着,如果一个源文件里定义了多个类,将编译生成多个字节码文件。
编译完成后,运行Java程序使用java命令,启动命令行,进入相应目录,在命令行中输入 java java类名 (注意是java类名,不是字节码文件的文件名,也不是java的源文件名),例如此处输入:java HelloWorld,注意类名的大小写必须正确。
CLASSPATH环境变量
如果使用1.5以上版本的JDK,完全可以不用设置CLASSPATH
当我们使用java java类名 命令来运行Java程序时,JRE到哪里搜索java类呢?1.5以后的版本可以自动在当前目录下搜索,但是之前的版本不可以,需要在CLASSPAH路径下添加一个“.”,告诉JRE需要在当前目录下搜索类。
除此之外,1.4及其以前的版本,编译和运行Java程序还需要JDK的lib路径的dt.jar和tools.jar文件里Java类,因此还需要把这两个文件添加到CLASSPATH环境变量里。
所以,1.4及其以前的版本编译和运行Java程序,常常需要设置CLASSPATH为:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar
从1.5版本开始,Java改进了设计,可以自动搜索当前目录来查找类文件,而且使用Java的编译和运行工具时,系统可以自动加载dt.jar和tools.jar文件中的类,因此不再需要设置CLASSPATH环境变量。
4、Java程序的基本规则
Java程序是一种纯粹的面向对象的程序设计语言,因此Java程序必须以类(class)的形式存在,类是Java程序的最小程序单位。Java程序不允许可执行性语句、方法等成份独立存在,所有的程序部分必须放在类定义里。
上面的HelloWorld还不是最小的java程序,最简单的Java程序为:
class Test{
}
这个类定义了一个Test类,这个类中没有任何成份,是一个空类,这个程序时对的,可以被编译器编译,但是不能被java命令解释执行,因为Java解释器规定,如果类能被解释器直接解释执行,这个类中必须包含main()方法,而且main()方法必须使用public static void来修饰,且main()方法的形参必须是一个字符串数组(String[] args是字符串数组的形式),也就是说main()方法的写法几乎是固定的。
这个类没有main()方法,会报错:
Java解释器从这个main()方法开始解释执行,因此main()方法是Java程序的入口。
对于那些不包含main()方法的类,也是有用的类,对于一个大型的java程序而言,往往只需要一个入口,也就是只有一个类包含main()方法,而其他的类都是用于被main()方法直接或间接调用的。
java源文件的命名规范:
1、java程序的源文件的后缀必须是.java;
2、通常情况下,Java程序源文件的主文件名可以是任意的,但是有一种情况除外,如果java程序的源代码中定义了一个public类,则该源文件的文件名必须和该public类的类名相同。
一个java源文件可以包含多个类定义,但最多只能包含一个public类定义;
如果源文件里包含public类定义,则该源文件的文件名必须与这个public类的类名相同。
推荐如果源文件没有public类时,也让源文件的文件名与类名相同,为提供更好的可读性,建议:
1、一个Java源文件只定义一个类,不同的类使用不同的源文件定义;
2、将每个源文件中单独定义的类都定义为public;
3、保持java源文件的主文件名与该源文件中定义的public类名相同。
5、初学者容易犯的错误
1、CLASSPAH环境变量问题,使用1.5以上版本JDK时,不需要设置此变量,如果设置错误的话,还会出问题,比如忘记设置当前路径.,就会报错:
找不到类定义的错误。
2、大小写问题
java语言是严格区分大小写的语言,编译时,不要输错文件名,此外java程序中的关键字全部是小写的。
Linux平台也是区分大小写的。
3、路径里包含空格问题
Windows系统的很多路径都了空格,最典型的是Program Files文件夹,而且这个文件夹是JDK的默认安装路径。
但是如果CLASSPATH环境变量里的路径包含了空格,则可能引发错误,因此安装JDK和Java相关程序时不要安装在有空格的路径里,否则可能引发错误。
4、main()方法的问题
如果需要用解释器直接运行一个Java类,则这个Java类必须包含main()方法,而且这个main()方法有固定的格式。
定义main()方法时,不要写成Main(),如果不小心写成Main(),编译时不会有任何问题,但是运行该程序时将提示:
这个错误提示Java解释器找不到main()方法,因为Java解释器只会选择从main()方法开始执行,对于Main()方法,Java解释器只会把他当做一个普通的方法,而不是程序的入口。
在Java中执行输出有两种简单的方式:
System.out.print("Hello Java!"); //输出结束后不会换行
System.out.println("Hello Java!"); //输出结束后会换行
6、Java的垃圾回收机制
传统的C/C++程序,需要程序员负责回收已经分配的内存,显式进行垃圾回收是一件比较困难的事情,因为程序员比不知道什么时候适合回收内存。如果分配出去的内存,不能够及时的回收回来,就会引起系统性能的下降,甚至导致系统的瘫痪,这种现象被称为内存泄露。
显式回收 内存的缺点:
1、没能及时回收内存,导致内存泄露,降低系统的性能;
2、错误的回收了程序的核心类库的内存,导致系统的崩溃。
Java不需要程序员来显式回收内存,Java的内存分配和回收都是由JRE在后台自动进行的。JRE会负责回收那些不再使用的内存,这种机制被称为垃圾回收(Garbage Collection,简称GC)。通常JRE会提供一条超级线程来进行检测和控制,一般都是在CPU空闲或内存不足时自动进行,而程序员无法精确控制内存回收的时间和顺序等。
Java的堆内存是一个运行时数据区,用以保存类的实例(对象),Java虚拟机的堆内存中存储着正在运行的应用程序所建立的所有对象,这些对象不需要程序通过代码来显式的释放。
一般来说,堆内存是由垃圾回收来负责的,所有JVM在实现都有一个由垃圾回收机制管理的堆内存。垃圾回收一种动态存储管理技术,它自动的释放不再被程序引用的对象,按照特定的垃圾回收算法来实现内存资源的自动回收功能。
在C/C++中,对象所占用的内存在程序结束之前一直被占用,被明确释放之前不能分配给其他对象使用;而在Java程序中,当没有对象引用指向原先分配给某个对象的内存时,该内存就称为垃圾。JVM的一个超线程会自动释放该内存区。事实上,除了释放没用的对象,垃圾回收也可以清除内存记录碎片。由于创建对象和垃圾回收器释放丢弃对象所占用的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存区,碎片整理将所占用的堆内存移到堆的一端,JVM将整理出来的新内存分配给新的对象。
垃圾回收的两个显著性的优点:
1、垃圾回收能自动的释放内存,减轻编程负担,提高编程效率。在没有垃圾回收机制时,需要花费很多的时间来解决难懂的存储器问题;
2、垃圾回收能保护程序的完整性,垃圾回收是Java语言安全性策略的一个重要部分。
垃圾回收的潜在性缺点是:它的开销影响程序的性能。Java虚拟机必须跟踪程序中有用的对象,才可以确定哪些对象是无用的对象,并最终释放这些无用的对象。
通常垃圾回收机制有如下特点:
1、垃圾回收机制的目标是回收无用对象的内存空间,这些内存空间都是JVM堆内存里的内存空间,垃圾回收只能回收内存资源,对于其他的物理资源,如数据库连接、磁盘IO等资源则无能无力;
2、为了更快的让垃圾回收机制回收那些不再使用的对象,可以将该对象的引用变量设置为null,通过这种方式暗示垃圾回收机制可以回收这个对象。
3、垃圾回收发生的不可预知性:由于不同的JVM采用了不同的垃圾回收机制和不同的垃圾回收算法,因此它有可能是定时发生,有可能是当CPU空闲时发生,也有可能是和原始的垃圾回收一样,等到内存消耗到极限时发生。虽然程序员可以通过调用对象的finalize()方法或System.gc()等方法来建议系统进行垃圾回收,但是这种调用仅仅是建议,依然不能精确控制垃圾回收机制执行。
4、垃圾回收的精确性主要包括两个方面:一、垃圾回收机制能够精确的标记活着的对象;二、垃圾回收器能够精确的定位对象之间的引用关系。
前者是完全的回收所有废弃对象的前提,否则就有可能造成内存的泄露。
而后者则是实现归并和复制等算法的必要条件,通过这种引用关系,可以保证所有的对象都能被可靠的回收,所有对象都能够重新分配,从而有效的减少内存碎片的产生。
当我们编写Java程序时,一个基本的原则就是:对于不再需要的对象,不要再引用他们。如果我们保持了这些对象的引用,垃圾回收机制暂时不会回收该对象,则会导致系统可用内存越来越少。当系统可用内存越来越少时,垃圾回收执行的频率也越来越高,从而导致系统性能的下降。
第二章:理解面向对象
Java语言是纯粹的面向对象的程序设计语言,它完全以对象为中心,Java程序的最小程序单位是类,整个Java程序由一个一个的类组成。
面向对象设计允许从现实世界中客观存在的事物(即对象)出来来构造软件系统,在系统构造中尽可能运用人类的自然思维方式。
面向对象实际上是由:
OOA面向对象分析
OOD面向对象设计
OOP面向对象编程
三部分组成,其中OOA和OOD常用UML(统一建模语言)来描述并记录其结果。
UML一共分为13种类型的图形,使用这13种图形中的某些就可以很好的描述并记录软件分析、设计的结果。通常而言,我们没必要用13种图形,常用的UML图形有用例图、类图、组件图、部署图、顺序图、活动图和状态机图。
1、面向对象
早期的编程语言如C、Basic、Pascal等是结构化编程语言,由于面向对象可以提供更好的可重用性、可扩展性和可维护性,于是催生出了C++、Java、C#等。从世界观的角度可以认为:世界是由各种各样具有自己的运动规律和内部状态的对象所组成的;不同对象之间的相互作用和通信构成了完整的现实世界。面向对象编程它强调系统的结构应该直接与现实世界的结构相对应,应该围绕现实世界中的对象来构造系统,而不是结构化编程中围绕功能来构造系统。
系统中一切皆为对象,对象是属性及其操作的封装体,对象可抽象为类,对象是类的实例;
实例关系和继承关系是对象之间的静态关系,消息传递是对象之间动态联系的唯一形式,也是计算的唯一形式,方法是消息的序列。
面向对象方法具有三个基本特征:封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)
封装:是指将对象的实现细节隐藏起来,然后通过一些公用的方法来暴露该对象的功能;
继承:面向对象实现软件复用的重要手段,当子类继承父类之后,将直接获得父类的属性和方法;
多态:同一类型的对象在运行时可能表现出不同的行为特征。
类是具有共同属性、方法的对象的集合,类是对象的抽象,抽象对象的那些特征取决于程序的需要。
对象之间的相互合作需要一个机制来协助进行,这个机制就是“消息”,消息是一个实例和另一个实例之间传递的信息。
2、UML(统一建模语言)介绍
面向对象软件开发需要经过OOA、OOD和OOP三个阶段。UML就是用于描述OOA、OOD结果的符号化表示方法。
UML一共包括13种正式图形:
在这里就不再详细的介绍各种图了,需要时再补充
3、Java的面向对象特征
Java是纯粹的面向对象编程语言,Java程序的组成单位就是类,除了8大基本数据类型之外,一切都是对象。
对象实现了数据和操作的结合,使数据和操作封装于对象的统一体中。对象是Java的核心,Java里的对象具有唯一性,每个对象都有一个标识来引用他,如果某个对象失去了标识,这个对象将变成垃圾,只能等着垃圾系统来回收他,Java语言不允许直接访问对象,而是通过对象的引用来操作对象。
第三章、数据类型和运算符
Java语言是一门强类型的语言,所有的变量必须先声明后使用,从而保证程序更加健壮,可以在编译过程中发现源代码的错误。
1、注释
注释的作用非常大,为什么要添加注释?
(1)永远不要过分相信自己的理解力和记忆力,过一段时间再来看自己的代码,你还看得懂吗?
(2)团队开发,可读性第一,性能第二;
(3)代码即文档
记住:程序注释是源代码的一个重要部分,对于规范的程序源代码而言,注释应当占到代码的1/3以上。
Java的注释分为三种:单行、多行和文档。
单行注释://+注释内容
多行注释:/*注释内容*/
此外,添加注释也是调试程序的一个重要方法,如果我们觉得某段代码可能有问题,可以先把这段代码注释起来,让编译器忽略这段代码,再次编译、运行,如果编译、运行正常,则说明错误就是由这段代码引起的,这样就缩小问题的范围,有利于排错,如果依然出现相同的错误,则可以说明这个错误不是由这段代码引起的,同样也能缩小错误范围!
Java还提供了功能更强大的注释:文档注释。如果编写Java源代码时添加了合适的文档注释,然后通过JDK提供的javadoc工具可以直接将源代码里面的文档注释提取成一份系统的API文档。
API文档:在开发一个大型软件时,需要定义成千上万的类,而且需要很多人参与开发,每个人都会根据自己的需要开发许多的类,在类里面定义一些属性和方法供人使用,但是别人怎么知道如何使用这些类呢?这时就需要提供一份说明文档,用于说明类中的每个类和方法的用途。这样当其他人使用这个类的时候就无需知道他是怎么具体实现的,只要知道这个类的功能即可,然后使用这个类来完成相应的功能,这就是调用应用程序接口(API)来编程。API文档就是用来说明这些应用程序接口的文档。对于Java语言而言,API文档通常详细的说明了每个类或方法的功能,用法等。
Java语言提供了大量的基础类,因此Sun公司也为这些基础类提供了响应的API文档,用于告诉开发者如何使用这个类,以及这些类里面包含的方法。
由于只有以public和protected修饰的内容才是希望暴露给别人使用的内容,而API文档主要是向使用者提供信息,因此javadoc默认只处理public和protected修饰的内容,如果开发者希望javadc可以提取private修饰的内容,则可以在使用javadoc工具时增加-private选项。
文档注释以/**开始,以*/结尾,中间部分全部是文档注释,会被提取到API文档中。
上面Java程序中粗体字标识的部分就是文档注释,编写了Java程序后,就可以用javadoc工具来提取这两个程序中的文档注释来生成API文档,javadoc的命令基本用法如下:
javadoc 选项 Java源文件/包
javadoc命令可对源文件、包来生成API文档,在上面的语法格式中,Java源文件可以支持通配符,例如使用*.java来代表当前路径下所有的Java源文件。Javadoc的通常的选项有:
-d
-windowtitle
doctitle
-header
除此之外,如果我们希望javadoc能够生成更详细的文档信息,例如为方法参数、方法返回值等生成详细的说明信息,则可利用Javadoc标记,常用的有
2、标示符和关键字
所谓标示符就是用于给程序中变量、类、方法命名的符号。
Java语言的标示符必须以字母、下划线(_)、美元符($)开头,后面可以接任意数目的字母、数字、下划线和美元符。
不可以包含空格,不能整体使用Java的关键字和保留字,但可以包括。
Java的所有关键字都是小写的,TRUE、FALSE和NULL都不是Java的关键字。
Java一共包含48个关键字:
除以上关键字外,enum是从Java 5.0新增的关键字,用于定义一个枚举
此外,Java还包括goto和const两个保留字,保留字是Java现在还未使用这两个单词作为关键字,但可能在未来的版本中使用其作为关键字。
Java还提供了3个特殊的直接量,true、false和null。
Java语言的标识符也不能使用这两个保留字和3个特殊的直接量。
3、数据类型的分类
Java语言是一种强类型的语言,这意味着每个变量和表达式都有一个在编译时就确定了的类型,所以所有变量必须显式的声明类型,即所有变量必须先声明后使用,其类型限制了一个变量能有的值,限制了一个表达式可以产生的值,限制了在这些值上可以进行的操作,并确定了这些操作的含义。
强类型的语言可以在编译时进行严格的语法检查,从而减少编程的错误。
Java语言支持的类型分为两种:基本类型和引用类型。
基本类型包括:boolean类型和数值类型。数值类型有整数型和浮点型。
整数类型包括:byte、short、int、long、char。(char也称字符型,实际上字符型也是一种整数型。)
浮点类型包括:float和double。
引用类型包括类、接口和数组类型,还有一种特殊的null类型。所谓引用数据类型就是对一个对象的引用,对象包括实例和数组两种。实际上引用类型就是一个指针,只是Java语言中不再使用指针这个方法。
空类型就是null值的类型,这种类型没有名称。因为null类型没有名称,所以不可能声明一个null类型的变量或者转换到null类型。空引用(null)是null类型变量唯一的值。空引用可以转换为任何引用类型。
空引用(null)只能被转换为引用类型,不能转换为基本类型,因此不要把一个null值赋值给一个基本数据类型的变量。
4、基本数据类型
整型:
byte:在内存里占8位,表数范围是:-128(-2^7)至127(2^7-1)
short:在内存里占16位,表数范围是:-32768(-2^15)至32767(2^15-1)
int:在内存中占32位,表数范围是:-2147483648(-2^31)至2147483647(2^31-1)
long:在内存中占64位,表数范围是:-2^63至2^63-1
int是最常用的整数类型,通常情况下,一个Java整数常量默认就是int类型,此外有两种情况需要注意:
1》如果直接将一个较小的整数常量赋值给一个byte或short变量(在其表示范围内),系统会自动把这个整数常量当做byte或short类型来处理。
2》如果一个巨大的整数常量(超出了int类型的表数范围),Java不会自动把这个整数常量当做long类型来处理,如果希望系统把一个整数常量当成long类型来处理,应该在这个这个整数常量后增加l或者L作为后缀。为避免混淆,推荐用L。
可以把一个较小的整数常量(如在int类型的表数范围内)直接赋值给一个long类型的变量,这并不是因为Java会把这个较小的整数常量当成long类型来处理,Java依然会把这个整型的常量当做int类型来处理,只是这个int类型的值会自动类型转换到long类型。
Java中整数常量有3种表示方式:10进制、8进制和16进制,其中8进制的整数常量以0开头,16进制的整数以0x或0X开头。
字符型:
字符型通常用于表示单个的字符,字符常量必须使用单引号‘’括起来,Java语言使用16位的Unicode编码集作为编码方式,而Unicode被设计成支持世界上所有的书面语言的字符,包括中文字符,因此java程序支持各种语言的字符。
字符常量有三种表示形式:
1》直接通过单个字符来指定字符常量:例如'A'、'9'等。
2》通过转义字符表示特殊字符常量:例如'\n'、'\t'等。
3》直接使用Unicode值来表示字符常量,格式是:'\uXXXX',其中XXXX代表一个16进制的整数。
字符型常量也可以采用16进制编码方式来表示,范围是'\u0000'~'\uFFFF',一共可以表示65535个字符,其中前256个('\u0000'~'\u00FF')字符和ASCII码中的字符完全重合。
不仅如此,char类型的值也可直接作为整数型的值来使用,但它是一个16位的无符号整数,即全部是正数,表数范围是:0~65535。
如果你把一个在0~65535范围内的int整数赋给char类型的变量(char c=7;),系统会自动把这个int整数当成char类型去处理。
Java 没有提供表示字符串的基本数据类型,而是通过String 类来表示字符串,字符串由多个字符组成,字符串常量使用双引号括起来。
如:String s="曾经沧海难为水,除却巫山不是云";
要注意的是:Java语言的单引号、双引号和反斜线都是有特殊用途的,如果一个字符串中包含了这些特殊的字符,应该使用转义字符的表示形式。例如我们需要在Java程序中表示一个绝对路径:"c:\codes",应该写成:"c:\\codes",同时写两个反斜线,Java会把第一个当成反义字符,和后一个组成真正的斜线。
浮点型
Java的浮点数遵循IEEE754标准,采用二进制数据的科学计数法来表示浮点数,对于float(32位)型数值,第一位是符号位,接下来8位是指数,后面23位表示尾数。对于double(64位)类型数值第一位也是符号位,中间11位表示指数,后面52位表示尾数。
因为Java浮点数使用二进制数据的科学计数法来表示浮点数,因此不可能精确标识一个浮点数,例如我们把5.23456789f 赋值给一个float类型的变量,接着输出这个变量的值发现这个变量的值已经发生了改变。如果使用double类型的浮点数则比float类型的浮点数更精确,但是如果浮点数的精度足够高(小数点后面的数字有很多时),依然可能发生这种情况。但是开发者需要精确保存一个浮点数,可以考虑使用BigDecimal类。
Java的浮点型默认是double型,如果希望Java把一个浮点型当成float处理,应该在这个浮点型值后面紧跟f或F。例如5.12代表的是一个double类型的常量,它占64位的内存空间。5.12f或5.12F才表示一个float型的常量,占32位的空间。
float a=5.12;直接把5.12赋值给float将会出现错误,因为5.12默认是double类型,需要使用强制类型转换,如float a=(float)5.12;
此外,Java语言还提供了三个特殊的浮点数值:正无穷大、负无穷大和非数,用于表示溢出和出错。
例如: 使用一个正数除以0将得到正无穷大,正无穷大通过float或double的POSITIVE_INFINITY表示,注所有正无穷大都是相等的;
使用一个负数除以0将得到负无穷大,负无穷大通过float或double的NEGATIVE_INFINITY表示,注所有负无穷大都是相等的;
0.0除以0.0或对一个负数开方将得到一个非数,非数通过float或double的NaN表示,NaN不和任何值相等,也不和自身相等。
注意:只有浮点数除以0才可以得到正无穷大或负无穷大,因为Java语言会自动的把和浮点数运算的0(整数)转换为0.0(浮点数)处理。
如果一个整数值除以0,则会抛出一个异常,ArithmeticException:/by zero(除以0异常)。
布尔型
Java中boolean型的数值只能是true后者false,不能用0或非0来代表。其他基本数据类型的值也不能转换成boolean型。
boolean型的值主要用做标旗来进行流程控制:比如在if、while、do、for和三目运算中。
5、基本类型间的类型转换
Java语言提供七种数值类型间的转换,有两种基本的转换方式,自动类型转换和强制类型转换。
自动类型转换:
如果系统支持把某个基本类型的值赋给另一种基本类型的变量,则这种方式称之为自动类型转换。
当把一个表数范围很小的数值或者变量直接赋值给一个表数范围大的变量时,系统将可以自动进行类型转换。
注:当把任何一个基本数据类型和字符串进行连接运算时,基本类型的值将自动类型转换为字符串类型,虽然字符串类型不再是基本类型,而是引用类型。因此,如果希望把一个基本数据类型的值转换为对应的字符串,可以把基本类型的值和一个空字符串进行连接。
注:3+4+'hello"表达式的值和"hello"+3+4表达式的值不同!
强制类型转换:
如果希望把上图右边的类型转换为左边的类型,必须使用强制类型转换,其语法格式为(targetType)value,进行强制类型转换时,有可能会导致溢出,从而导致数据丢失,这种转换成为“缩小转换”。
例如把32位int 233强制转换为8为byte,结果为-23,这就是典型的溢出。
数据溢出,直接截取最后8位,最左边为1,即为负数,负数在计算机中是以补码的形式存在的,因此还需要换算为原码。
网页上的数字和字符验证码是如何生成的呢?
我们先随机生成一个在指定范围内的int数字(如果希望生成小写字母,也就是在97~122之间),然后将其强制转换为char型,再将多次生成的字符串连接起来即可。
public class yanzheng
{
public static void main(String[] args)
{
//定义一个空字符串
String result="";
//进行6次循环
for(int i=0;i<6;i++)
{
//生成一个97~122的int型的整数
int intValue=(int)(Math.random()*25+97);
//将int类型强制转换为char后连接到result后面
result=result+(char)intValue;
}
System.out.println(result);
}
}
通常情况下,字符串不能直接转换为基本类型,但是通过基本类型对应的包装类则可以实现把字符串转换成基本类型。例如我们把字符串转换成int类型,可通过如下的代码:
String a="45";
//使用Integer的方法将一个字符串转换成int类型
int iValue=Integer.parseInt(a);
Java为8种基本类型都提供了对应的包装类:
boolean-------Boolean
byte -------Byte
short -------Short
int -------Integer
long -------Long
char -------Character
float -------Float
double -------Double
8个包装类都提供了一个parseXxx(String str)的静态方法用于将字符串转换成基本类型。
表达式类型的自动提升
当一个算术表达式中包含多个基本类型的值时,整个算术表达式的数据类型将发生自动提升。Java定义的自动提升规则是:
1》所有byte、short和char类型将被提升至int类型。
2》整个算术表达式的数据类型自动提升至与表达式中最高等级操作数同样的类型。如上图箭头右侧的等级高于左侧的等级。
//定义一个short类型的变量
short sValue=5;
//表达式中的sValue将自动提升至int类型,则右边的表达式类型为int
//将一个int类型赋给short类型的变量将会出现错误
sValue=sValue-2;
sValue-2表达式的类型将被自动提升至int,把int类型赋给short变量,从而引起错误。
int val=3;
//右边表达式中2个操作数都是int,故右边的运算结果也是个int类型
//因此,虽然23/3不能除尽,依然得到的是一个int整数
int intResult=23/val;
//将输出7
System.out.println(intResult);
从上可知,如果不能整除,将把小数部分截断,得到取整后的部分。
如果表达式中包含了字符串,结果将大有不同:
//输出字符串 hello!a7
System.out.println("hello!"+'a'+7);
//输出字符串104hello!
System.out.println('a'+7+"hello!");
6、直接量
直接量指在程序中通过源代码直接指定的值,例如在int a=5; 我们为变量a所分配的初始值5就是个直接量。
直接量的类型:
并不是所有的数据类型都可以指定直接量,能指定直接量的通常只有三种类型:基本类型、字符串类型和null类型。具体而言,Java支持以下8种类型的直接量:
int 类型的直接量:在程序中直接给出的数值,可以是十进制、八进制和十六进制,八进制须以0开头,十六进制须以0x或0X开头。例如12、012、0x12,分别对应十进制12,10和18。
//输出字符串 hello!a7
System.out.println("hello!"+'a'+7);
//输出字符串104hello!
System.out.println('a'+7+"hello!");
6、直接量
直接量指在程序中通过源代码直接指定的值,例如在int a=5; 我们为变量a所分配的初始值5就是个直接量。
直接量的类型:
并不是所有的数据类型都可以指定直接量,能指定直接量的通常只有三种类型:基本类型、字符串类型和null类型。具体而言,Java支持以下8种类型的直接量:
int 类型的直接量:在程序中直接给出的数值,可以是十进制、八进制和十六进制,八进制须以0开头,十六进制须以0x或0X开头。例如12、012、0x12,分别对应十进制12,10和18。
long类型的直接量:在整数后面接l或者L就变成了long类型的直接量,例如3L。
float类型的直接量:在一个浮点数后面接f或者F就变成了float类型的直接量,这个浮点数可以是标准小数或者科学计数法的形式。例如5.34F、3.14E5f。
double类型的直接量:直接给出一个标准小数的形式或者科学计数法形式的浮点数就是double类型的直接量,如5.34、3.14E5。
boolean类型的直接量:这个类型的直接量只有true和false。boolean类型的直接量只能赋给boolean类型的直接量,不能赋给其他任何类型的变量。
char类型的直接量:char类型的直接量有3种形式,分别是单引号括起的字符、转义字符和Unicode值表示的字符。如'a'、'\n'和'\u0061'。
String类型的直接量:一个用双引号括起来的字符序列就是String类型的直接量。String类型的直接量不能赋给其他类型的变量。
null类型的直接量:这个类型的直接量只有一个值null,而且这个直接量可以赋给任何引用类型的变量(包括String类型),用以表示这个引用类型的变量中保存的地址为空,即还未指定任何有效地的对象。
7、运算符
算术运算符(+、-、*、/、%、++、--)
/除法运算符比较特殊,如果除法运算符的两个运算数都是整数类型,则计算结果也是整数,就是将自然除法的结果截取取整,比如19/4的结果是4,而不是5,而且如果两个都是整数,则除数不能为0,否则将引发除以0异常。但是,如果除法运算符的两个运算数有一个是浮点数,则计算结果也是浮点数,这个结果就是自然除法的结果。而且此时允许除数是0,或者0.0,得到结果是正无穷大或负无穷大。
%求余运算符,求余运算的结果不一定总是整数,他的计算结果是使用第一个运算数来除以第二个运算数,得到一个整除的结果后剩下的值就是余数,因为求余运算符也需要进行除法运算,因此如果求余运算的两个运算数都是整数,则求余运算的第二个运算数不能是0,否则将引发除0异常。如果求余运算的两个运算数中有1个或2个浮点数,则允许第二个操作数0或0.0,只是求余运算的结果是非数:NaN。0或0.0对零以外的任何数求余都将得到0或0.0。
++和--都只能用于操作变量,不能操作数值直接量或常量,例如5++和6--都是错误的。
Java没有提供其他更复杂的运算符,如需要乘方、开方等运算,可借助于java.lang.Math类的工具方法来完成复杂的运算。
public class yanzheng
{
public static void main(String[] args)
{
double a=2;
//求a的5次方
double b=Math.pow(a,5);
System.out.println(b);
//求a的平方根,并将结果赋值给c
double c=Math.sqrt(a);
System.out.println(c);
//计算随机数,返回一个0~1之间的伪随机数
double d=Math.random();
System.out.println(d);
//求1.57的sin函数值;1.57被当成弧度数
double e=Math.sin(1.57);
//输出接近1
System.out.println(e);
}
}
Java支持的位运算符有如下7个:&、|、~、^、<<、>>、>>>
第一个操作数 |
第二个操作数 |
按位与& |
按位或| |
按位异或^ |
0 |
0 |
1 |
1 |
0 |
0 |
1 |
0 |
1 |
1 |
1 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
0 |
5&9=1,5|9=13(默认int类型为32为,此处省略前面的24位)
00000101 00000101
&00001001 |00001001
=00000001 =00001101
按位非需要一个操作数,这个运算符将把操作数在计算机底层的2进制码按位取反,例如~-5如下:
左移运算符是将二进制码整体左移制定位数,左移后右边空出来的位以0填充。
以-5为例:
第一行是-5的补码,左移两位后的最高位仍为1,移动后为-20。
Java的右移运算符有两个,>>和>>>
>>运算符,把第一个操作数的二进制码右移到指定位数后,左边空出来的位以原来的符号位进行填充。即如果第一个操作数是原来为正数,则左边补0,如果第一个操作数为负数,则左边补1;
>>>无符号右移运算符,把第一个操作数的的二进制码右移后,左边空出来的位总是以0来填充。
扩展后的赋值运算符:
以上两种运算的底层机制是不同的,如果可以使用这种扩展后的运算符,总是推荐使用这种运算符。
比较运算符:
==等于:如果进行比较的两个操作数是两个数值类型,即使他们的数据类型不同,只要他们的值相同,都会返回true。例如97=='a'返回true,5.0==5也返回true。如果两个操作数都是引用类型,只有当两个引用变量引用相同类的实例时才可以比较,而且必须这两个引用指向同一个对象才会返回true。
!=不等于:如果进行比较的两个操作数都是个数值类型,不论类型是否相同,只要他们的值不等,都将返回true。如果两个操作数都是引用类型,只有当两个引用变量引用想同类的实例时才可以比较,而且只要两个引用指向不是同一个对象就返回true。
注意:Java为所有的基本数据类型都提供了对应的包装类,虽然基本类型的变量是引用数据类型,但数值型对应包装类的实例可以与数值型的值进行比较,这种比较是直接取出包装类实例所包装的数值来进行比较。
Integer a=new Integer(6);
//一下代码会输出true
System.out.println("6的包装类实例是否>5.0"+(a>5.0));
补充包装类: Date类和其他数据类型的相互转换
整型和Date类之间并不存在直接的对应关系,只是您能够使用int型为分别表示年,月,日,时,分,秒,这样就在两者之间建立了一个对应关系,在作这种转换时,您能够使用Date类构造函数的三种形式:
Date(int year, int month, int date):以int型表示年,月,日;
Date(int year, int month, int date, int hrs, int min):以int型表示年,月,日,时,分;
Date(int year, int month, int date, int hrs, int min, int sec):以int型表示年,月,日,时,分,秒;
在长整型和Date类之间有一个很有趣的对应关系,就是将一个时间表示为距离格林尼治标准时间1970年1月1日0时0分0秒的毫秒数.对于这种对应关系,Date类也有其相应的构造函数:Date(long date)
获取Date类中的年,月,日,时,分,秒连同星期您能够使用Date类的getYear(),getMonth(),getDate(),getHours(),getMinutes(),getSeconds(),getDay()方法,您也能够将其理解为将Date类转换成int.
而Date类的getTime()方法能够得到我们前面所说的一个时间对应的长整型数,和包装类相同,Date类也有一个toString()方法能够将其转换为String类.
但是两个包装类的实例进行比较的情况就比较复杂了,因为包装类的实例实际上是引用类型,只有两个包装类引用指向同一个对象时才会返回true。
//一下代码输出为false
System.out.println("比较两个包装类的实例是否相等:"+(new Integer(2)==new Integer(2)));
//通过自动装箱,允许把基本类型的值赋给包装类的实例
Integer inta=2;
Integer intb=2;
//下行代码输出true
System.out.println("两个2自动装箱后是否相等:"+(inta==intb));
Integer biga=128;
Integer bigb=128;
//下行代码输出false
System.out.println("两个128自动装箱后是否相等:"+(biga==bigb));
//定义一个长度为256的Integer数组
static final Integer[] cache=new Integer[-(-128)+127+1];
static {
//执行初始化,创建-128到127的Integer实例,并放入cache数组中
for(int i=0;i
从上面的代码中可以看出,系统-128~127之间的整数自动装箱为Integer实例,并放入名为cache的数组中缓存起来,如果以后要把-128~127之间的整数自动装箱成一个Integer实例时,实际上是自动指向对应的数组元素,因此-128~127之间的同一个整数自动装箱成Integer实例时,永远都是引用cache数组的同一个元素,所以他们全部相等。但是每次把一个不再-128~127之间的整数装箱成Integer实例时,系统总是重新创建一个Integer实例,所以出现程序中运行的结果。
于此类似的还有String类,String类也会对通过直接量赋值的String实例进行缓存。
//通过new调用构造器创建的两个String实例
String stra=new String("Hello");
String strb=new String("Hello");
//输出false
System.out.println("通过两个内容相同的字符串new出来的Sting实例是否相等"+(stra==strb));
//通过直接量赋值创建的两个String实例
String strc="Hello";
String strd="Hello";
//输出true
System.out.println("直接把两个内容相同的字符串赋给String变量是否相等:"+(strc==strd));
执行String strc="Hello";代码时,系统会自动创建一个内容为"Hello"的String实例,并将这个实例缓存起来,当执行到String strd="Hello";代码时,系统会先检查缓存中是否有一个String实例的内容与这个"Hello"直接量的字符序列相同,如果从缓存中找到了这样的一个String实例,系统会直接让引用指向这个String实例,这就是为什么strc和strd相等的原因。
逻辑运算符
逻辑运算符用来操作两个布尔类型的常量或变量,逻辑运算符主要有与或非异或,包括以下6种:&、&&、|、||、!、^。
下面来看看|和||的区别:
int a=5;
int b=10;
if(a>4|b++>10)
{
//输出a的值为5,b的值为11
System.out.println("a的值为:"+a+" b的值为:"+b);
}
int a=5;
int b=10;
if(a>4||b++>10)
{
//输出a的值为5,b的值为10
System.out.println("a的值为:"+a+" b的值为:"+b);
}
Java运算符的优先级:
未完待续,持续更新。。。