java核心技术卷I 第1-3章

本书将详细介绍下列内容:

● 面向对象程序设计

● 反射与代理

● 接口与内部类

● 异常处理

● 泛型程序设计

● 集合框架

● 事件监听器模型

● 使用Swing UI工具箱进行图形用户界面设计

● 并行操作

Ⅱ(高级特性)涉及企业特性以及高级的用户界面程序设计,其中详细讨论下列内容:

● 流API

● 文件处理与正则表达式

● 数据库

● XML处理

● 注释

● 国际化

● 网络编程

● 高级GUI组件

● 高级图形

● 原生方法

第一章 java程序设计概述

1.2 Java“白皮书”的关键术语

1)简单性2)面向对象3)分布式4)健壮性5)安全性6)体系结构中立7)可移植性8)解释型9)高性能10)多线程11)动态性

1.2.1 简单性

  • 的确,Java语法是C++语法的一个“纯净”版本。这里没有头文件、指针运算(甚至指针语法)、结构、联合、操作符重载、虚基类等
  • 简单的另一个方面是小。Java的目标之一是支持开发能够在小型机器上独立运行的软件。基本的解释器以及类支持大约仅为40KB;再加上基础的标准类库和对线程的支持(基本上是一个自包含的微内核)大约需要增加175KB。

1.2.2 面向对象

  • 简单地讲,面向对象设计是一种程序设计技术。它将重点放在数据(即对象)和对象的接口上

1.2.3 分布式

  • Java有一个丰富的例程库,用于处理像HTTP和FTP之类的TCP/IP协议。Java应用程序能够通过URL打开和访问网络上的对象,其便捷程度就好像访问本地文件一样。

1.2.4 健壮性

  • Java的设计目标之一在于使得Java编写的程序具有多方面的可靠性。Java投入了大量的精力进行早期的问题检测、后期动态的(运行时)检测,并消除了容易出错的情况

  • Java编译器能够检测许多在其他语言中仅在运行时才能够检测出来的问题。

1.2.5 安全性

  • Java适用于网络/分布式环境。为了达到这个目标,在安全方面投入了很大精力。使用Java可以构建防病毒、防篡改的系统。

  • 从一开始,Java就设计成能够防范各种攻击,其中包括:

    • 运行时堆栈溢出。如蠕虫和病毒常用的攻击手段。
    • 破坏自己的进程空间之外的内存。
    • 未经授权读写文件。

1.2.6 体系结构中立

  • 编译器生成一个体系结构中立的目标文件格式,这是一种编译过的代码,只要有Java运行时系统,这些编译后的代码可以在许多处理器上运行。
  • Java编译器通过生成与特定的计算机体系结构无关的字节码指令来实现这一特性。精心设计的字节码不仅可以很容易地在任何机器上解释执行,而且还可以动态地翻译成本地机器代码。

1.2.7 可移植性

  • 与C和C++不同,Java规范中没有“依赖具体实现”的地方。基本数据类型的大小以及有关运算都做了明确的说明。
  • 例如,Java中的int永远为32位的整数,而在C/C++中,int可能是16位整数、32位整数
  • 唯一的限制只是int类型的大小不能低于short int,并且不能高于long int。在Java中,数据类型具有固定的大小,这消除了代码移植时令人头痛的主要问题。
  • 二进制数据以固定的格式进行存储和传输,消除了字节顺序的困扰。字符串是用标准的Unicode格式存储的。

1.2.8 解释型

  • Java解释器可以在任何移植了解释器的机器上执行Java字节码。
  • 由于链接是一个增量式且轻量级的过程,所以,开发过程也变得更加快捷,更加具有探索性。

1.2.9 高性能

  • 尽管对解释后的字节码性能已经比较满意,但在有些场合下还需要更加高效的性能。字节码可以(在运行时刻)动态地翻译成对应运行这个应用的特定CPU的机器码。

1.2.10 多线程

  • 多线程可以带来更好的交互响应和实时行为。
  • 我们不再追求更快的处理器,而是着眼于获得更多的处理器,而且要让它们一直保持工作。

1.2.11 动态性

  • 它能够适应不断发展的环境。库中可以自由地添加新方法和实例变量,而对客户端却没有任何影响。在Java中找出运行时类型信息十分简单。
  • 需要将某些代码添加到正在运行的程序中时,动态性将是一个非常重要的特性。一个很好的例子是:从Internet下载代码,然后在浏览器上运行。Java设计者很清楚动态语言可以很容易地实现运行程序的演进。最终,他们将这一特性引入这个主流程序设计语言中。

1.3 Java applet(程序)与Internet

  • 用户从Internet下载Java字节码,并在自己的机器上运行。在网页中运行的Java程序称为applet。要使用applet,需要启用Java的Web浏览器执行字节码。
  • 不需要安装任何软件。任何时候只要访问包含applet的网页都会得到程序的最新版本。最重要的是,要感谢虚拟机的安全性,它让我们不必再担心来自恶意代码的攻击。
  • 网页中插入一个applet就如同在网页中嵌入一幅图片。applet会成为页面的一部分。文本环绕着applet所占据的空间周围。关键的一点是这个图片是活动的。它可以对用户命令做出响应,改变外观,在运行它的计算机与提供它的计算机之间传递数据。

1.4 Java发展简史

1.5 关于Java的常见误解

在结束本章之前,我们列出了一些关于Java的常见误解,同时给出了解释。

  • Java是HTML的扩展

    • Java是一种程序设计语言;HTML是一种描述网页结构的方式除了用于在网页上放置Java applet的HTML扩展之外,两者没有任何共同之处。
  • 2.使用XML,所以不需要Java
    • Java是一种程序设计语言;XML是一种描述数据的方式。可以使用任何一种程序设计语言处理XML数据,而Java API对XML处理提供了很好的支持。此外,许多重要的第三方XML工具采用Java编写。有关这方面更加详细的信息请参看卷Ⅱ。
  • 所有的Java程序都是在网页中运行的
    • 所有的Java applet都是在网页浏览器中运行的。这也恰恰是applet的定义,即一种在浏览器中运行的Java程序
    • 然而,**大多数Java程序是运行在Web浏览器之外的独立应用程序。**实际上,很多Java程序都在Web服务器上运行并生成用于网页的代码。

第二章 Java程序设计环境

2.1.3 安装库源文件和文档

  • 库源文件在JDK中以一个压缩文件src.zip的形式发布必须将其解压缩后才能够访问源代码。建议按照下面所述的步骤进行操作。很简单:

    • 1)确保JDK已经安装,并且jdk/bin目录在执行路径中。
    • 2)在主目录中建立一个目录javasrc。
    • 3)在jdk目录下找到文件src.zip。
    • 4)将src.zip文件解压缩到javasrc目录。
  • src.zip文件中包含了所有公共类库的源代码。
  • 要想获得更多的源代码(例如:编译器、虚拟机、本地方法以及私有辅助类),请访问网站:http://jdk8.java.net。

2.5 构建并运行applet

  • 如何构建和运行一个applet,以及如何在Web浏览器中显示

  • 首先,打开终端窗口并转到CoreJava/v1ch02/RoadApplet,然后,输入下面的命令:

    • 第一条命令是大家已经非常熟悉的调用Java编译器的命令。它将RoadApplet.java源文件编译成字节码文件RoadApplet.class。
    • 不过这一次不要运行java程序。首先,使用jar工具将类文件打包到一个“JAR文件”。然后调用appletviewer程序,这是JDK自带的一个工具,可以用来快速测试applet。 需要为这个程序指定一个HTML文件名,而不是一个Java类文件名。
  • 显示了在applet查看器窗口中显示的内容。

  • RoadApplet/RoadApplet.html内容

  • <p><applet code="RoadApplet.class" archive="RoadApplet.jar" width="400" height="400" alt="Traffic jam visualization">
    </applet></p>
    
    • 这会告诉applet查看器加载applet,其代码存储在RoadApplet.jar中。applet会忽略除applet标签外的所有HTML标签。

第三章java的基本程序设计结构

3.1 一个简单的Java应用程序

  • 首先,Java区分大小写。如果出现了大小写拼写错误,程序将无法运行
  • 关键字public称为访问修饰符(accessmodifier),这些修饰符用于控制程序的其他部分对这段代码的访问级别
  • 关键字class表明Java程序中的全部内容都包含在类中。这里,只需要将类作为一个加载程序逻辑的容器程序逻辑定义了应用程序的行为。
  • Java中定义类名的规则很宽松。名字必须以字母开头,后面可以跟字母和数字的任意组合。长度基本上没有限制。但是不能使用Java保留字(例如,public或class)作为类名
  • 标准的命名规范为(类名FirstSample就遵循了这个规范):类名是以大写字母开头的名词。如果名字由多个单词组成,每个单词的第一个字母都应该大写(这种在一个单词中间使用大写字母的方式称为骆驼命名法
  • 源代码的文件名必须与公共类的名字相同,并用.java作为扩展名。因此,存储这段源代码的文件名必须为FirstSample.java
  • 如果已经正确地命名了这个文件,并且源代码中没有任何录入错误,在编译这段源代码之后就会得到一个包含这个类字节码的文件。Java编译器将字节码文件自动地命名为FirstSample. class,并与源文件存储在同一个目录下。
  • 使用下面这行命令运行这个程序:(请记住,不要添加.class扩展名。)
    • java FirstSample
  • 当使用 java ClassName运行已编译的程序时,Java虚拟机将从指定类中的main方法开始执行,因此为了代码能够执行,在类的源文件中必须包含一个main方法。

接下来,研究一下这段代码:

  • 上面这个main方法体中只包含了一条语句,其功能是:将一个文本行输出到控制台上。
  • 在这里,使用了System.out对象并调用了它的println方法。注意,点号(·)用于调用方法。Java使用的通用语法是object.method(parameters)
  • 在这个示例中,调用了println方法并传递给它一个字符串参数。这个方法将传递给它的字符串参数显示在控制台上。然后,终止这个输出行,使得每次调用println都会在新的一行上显示输出。
  • 对于一个方法,即使没有参数也需要使用空括号。例如,**不带参数的println方法只打印一个空行。**下面的语句来调用:System.out.println();
  • System.out还有一个print方法,它在输出之后不换行。

3.2 注释

    1. 在每行的注释前面标记//
    2. 使用/*和 */将一段比较长的注释括起来。
    3. 可以用来自动地生成文档。这种注释以/ * *开始,以 * /结束
  • 在Java中,/ * * /注释不能嵌套。也就是说,不能简单地把代码用/ 和 * /括起来作为注释,因为这段代码本身可能也包含一个/。

3.3 数据类型

Java是一种强类型语言。这就意味着必须为每一个变量声明一种类型。

  • 在Java中,一共有8种基本类型(primitive type)

    • 有4种整型、2种浮点类型、1种用于表示Unicode编码的字符单元的字符类型char1种用于表示真值的boolean类型。
    • Java有一个能够表示任意精度的算术包,通常称为**“大数值”(bignumber)。虽然被称为大数值**,但它并不是一种新的Java类型,而是一个Java对象。

3.3.1 整型

整型用于表示没有小数部分的数值,它允许是负数。Java提供了4种整型

在通常情况下,int类型最常用。但如果表示星球上的居住人数,就需要使用long类型了。byte和short类型主要用于特定的应用场合,例如,底层的文件处理或者需要控制占用存储空间量的大数组

  • 在Java中,整型的范围与运行Java代码的机器无关。这就解决了软件从一个平台移植到另一个平台,或者在同一个平台中的不同操作系统之间进行移植给程序员带来的诸多问题。

  • 由于Java程序必须保证在所有机器上都能够得到相同的运行结果,所以各种数据类型的取值范围必须固定。

  • 长整型(long)数值有一个后缀L或l(如4000000000L)。

  • 十六进制数值有一个前缀0x或0X(如0xCAFE)。八进制有一个前缀0,例如,010对应八进制中的8。很显然,八进制表示法比较容易混淆,所以建议最好不要使用八进制常数。

  • **从Java 7开始,加上前缀0b或0B就可以写二进制数。**例如,0b1001就是9。

  • 同样是从Java 7开始,还可以为数字字面量加下划线,如用1_000_000(或0b1111_0100_0010_0100_0000)表示一百万。这些下划线只是为了让人更易读。Java编译器会去除这些下划线。

  • 注意,Java没有任何无符号(unsigned)形式的int、long、short或byte类型。

  • Java语言的整型常数默认为int型,声明long型常量可以后加‘ l ’或‘ L ’ 。

    • long a = 55555555; //编译成功,在int表示的范围内(21亿内)。整型自动转换 小=》大

      long b = 55555555555;//不加L编译错误,已经超过int表示的范围。

3.3.2 浮点类型

浮点类型用于表示有小数部分的数值。在Java中有两种浮点类型,具体内容如表3-2所示。

  • double表示这种类型的数值精度是float类型的两倍(有人称之为双精度数值)。
  • 绝大部分应用程序都采用double类型。在很多情况下,float类型的精度很难满足需求。
  • 实际上,只有很少的情况适合使用float类型,例如,需要单精度数据的库,或者需要存储大量数据。
  • float类型的数值有一个后缀F或f(例如,3.14F)。没有后缀F的浮点数值(如3.14)默认为double类型。当然,也可以在浮点数值后面添加后缀D或d(例如,3.14D)。
  • 可以使用十六进制表示浮点数值。例如,0.125=2-3可以表示成0x1.0p-3。在十六进制表示法中,使用p表示指数,而不是e。注意,尾数采用十六进制,指数采用十进制。指数的基数是2,而不是10。

所有的浮点数值计算都遵循IEEE 754规范。具体来说,下面是用于表示溢出和出错情况的三个特殊的浮点数值:

● 正无穷大 ● 负无穷大 ● NaN(不是一个数字

例如,一个正整数除以0的结果为正无穷大。计算0/0或者负数的平方根结果为NaN。

  • 常量**Double.POSITIVE_INFINITY、Double.NEGATIVE_INFINITY和Double.NaN(以及相应的Float类型的常量)分别表示这三个特殊的值,**但在实际应用中很少遇到。

  • 不能这样检测一个特定值是否等于Double.NaN:

    • if(x == Double.NaN) //never true
      
  • 所有“非数值”的值都认为是不相同的。然而,可以使用Double.isNaN方法:

    • if(Double.isNaN(x))//check whether x is "not a number"
      

警告 :

  • 浮点数值不适用于无法接受舍入误差的金融计算中。例如,命令System.out.println(2.0-1.1)将打印出0.8999999999999999,而不是人们想象的0.9。
  • 这种舍入误差的主要原因是浮点数值采用二进制系统表示,而在二进制系统中无法精确地表示分数1/10。这就好像十进制无法精确地表示分数1/3一样。//TODO 为什么无法表示
  • 如果在数值计算中不允许有任何舍入误差,就应该使用BigDecimal类

3.3.3 char类型

**char类型原本用于表示单个字符。**不过,现在情况已经有所变化。如今,有些Unicode字符可以用一个char值描述,另外一些Unicode字符则需要两个char值。

Unicode:一种字符编码标准。简而言之,这是一个文本字符(数字,字母,标点符号)与二进制代码对应的表格

  • char类型的字面量值要用单引号括起来。例如:'A’是编码值为65所对应的字符常量。它与"A"不同,"A"是包含一个字符A的字符串。

  • char类型的值可以表示为十六进制值其范围从\u0000到\uffff

  • 转义序列\u:用来转义16进制数值在Unicode对应的字符

    • char x = 0x005B;
      char a = '\u005B';//结果都为[
      
  • 一些用于表示特殊字符的转义序列,请参看表3-3。所有这些转义序列都可以出现在加引号的字符字面量或字符串中。例如,’\u2122’或"Hello\n"。

  • 转义序列\u还可以出现在加引号的字符常量或字符串之外(而其他所有转义序列不可以)。完全符合语法规则,\u005B和\u005D是[和]的编码

  • 例如

警告:

  • Unicode转义序列会在解析代码之前得到处理。

  • 例如,"\u0022+\u0022"并不是一个由引号(U+0022)包围加号构成的字符串。实际上,\u0022会在解析之前转换为"这会得到""+"",也就是一个空串。

  • 更隐秘地,一定要当心注释中的\u

    • 注释 //\u000a is a newline 会产生一个语法错误(不能通过编译,编译期出现,运行出错),因为读程序时\u000a会替换为一个换行符
    • 报错:java: 不是语句
  • 类似地,下面这个注释

    • //Look inside C:\users 也会产生一个语法错误,因为**\u后面并未跟着4个十六进制数。** 路径要\ \
    • 报错:java: 非法的 Unicode 转义

3.3.4 Unicode和char类型

要想弄清char类型,就必须了解Unicode编码机制,Unicode打破了传统字符编码机制的限制。

  • 在Unicode出现之前,已经有许多种不同的标准,这样就产生了下面两个问题:

    • 一个是对于任意给定的代码值,在不同的编码方案下有可能对应不同的字母
    • 二是采用大字符集的语言其编码长度有可能不同,例如,有些常用的字符采用单字节编码,而另一些字符则需要两个或更多个字节。
  • 设计Unicode编码的目的就是要解决这些问题,当时人们认为两个字节的代码宽度足以对世界上各种语言的所有字符进行编码,现在,Unicode字符超过了65536个,16位的char类型已经不能满足描述所有Unicode字符的需要了。

Java语言解决这个问题的基本方法

  • 码点(code point)是指与一个编码表中的某个字符对应的代码值

    • 在Unicode标准中,码点采用十六进制书写,并加上前缀U+,例如U+0041就是拉丁字母A的码点。
  • Unicode的码点可以分成17个代码级别(code plane)

    • 第一个代码级别称为基本的多语言级别(basic multilingual plane),码点从U+0000到U+FFFF,其中包括经典的Unicode代码;
    • 其余的16个级别码点从U+10000到U+10FFFF,其中包括一些辅助字符(supplementary character)。
  • UTF-16编码采用不同长度的编码表示所有Unicode码点。

    • 在基本的多语言级别中,每个字符(码点)用16位表示,通常被称为代码单元(code unit);而辅助字符采用一对连续的代码单元进行编码。
    • 这样构成的编码值落入基本的多语言级别中空闲的2048个位置(16^4)上,通常被称为替代区域(surrogate area)[U+D800~U+DBFF用于第一个代码单元,U+DC00~U+DFFF用于第二个代码单元]。
    • 我们可以从中迅速地知道一个代码单元是一个字符的编码,还是一个辅助字符的第一或第二部分。
    • 例如,是八元数集的一个数学符号,码点为U+1D546,编码为两个代码单元U+D835和U+DD46。
    • 在Java中,char类型描述了UTF-16编码中的一个代码单元。
    • 我们强烈建议不要在程序中使用char类型除非确实需要处理UTF-16代码单元。最好将字符串作为抽象数据类型处理
  • UTF-16编码方式

    • 知道Unicode的范围为0x00x10FFFF,那0xFFFF0x10FFFF这段码点怎么表示?
    • 0x10FFFF-0xFFFF=0x100000,那么这个十六进制表示的十进制也就是:1048576个码位 那肯定就是32位存咯
    • 分开存储,也就是将32位分开前16位和后16位每个16位各存一半
    • 每一半存的就是1024**,32位二进制数字中,前后16位中各存10位就够用了**
    • 剩余的6位用来让识别字符串变得容易(从文本的任意位置开始,均能区分一个字符的起始)
    • 是UTF-16中,表示数据有单16位和双16位(32位)两种,那么我们设计成单16位和32位中的前16位和后16位这三个16位完全不会重复,那么我们就能随时读到一组16位,就能知道他是单16还是前16还是后16
    • 通过前6位来区分数据,那么前6位就是2^6=64
    • 官方设定如下:
      54开头的为32位的前16位
      55开头的为32位的后16位
      其他开头的为单16位
    • 这样我们就能区分开这三个16位了,在读取文档中的任意位置,都能随意区分出间隔咯
      那么54开头的数据区间是多少呢,就是1101 10xx xxxx xxxx,区间就是D800~DBFF
      那么55开头的数据区间是多少呢,就是1101 11xx xxxx xxxx,区间就是DC00~DFFF

3.3.5 boolean类型

boolean(布尔)类型有两个值:false和true,用来判定逻辑条件**。整型值和布尔值之间不能进行相互转换。**

3.4 变量

在Java中,每个变量都有一个类型(type)。在声明变量时,变量的类型位于变量名之前。这里列举一些声明变量的示例:

由于声明是一条完整的Java语句,所以必须以分号结束。

  • 变量名必须是一个以字母开头并由字母或数字构成的序列
  • Java中“字母”和“数字”的范围更大
  • 字母包括**’A’~’Z’、‘a’~’z’、’_’、’$'或在某种语言中表示字母的任何Unicode字符**
  • 同样,数字包括**’0’~’9’和在某种语言中表示数字的任何Unicode字符。**
  • 变量名中所有的字符都是有意义的,并且大小写敏感。变量名的长度基本上没有限制。
  • 可以在一行中声明多个变量 int i,j;不过,不提倡使用这种风格。逐一声明每一个变量可以提高程序的可读性。

提示:

  • 如果想要知道哪些Unicode字符属于Java中的“字母”,可以使用Character类的isJavaIdentifierStart和isJavaIdentifierPart方法来检查。
  • 尽管$是一个合法的Java字符,但不要在你自己的代码中使用这个字符。它只用在Java编译器或其他工具生成的名字中。
  • 不能使用Java关键字作为变量名
  • 在对两个不同的变量进行命名时,最好不要只存在大小写上的差异。
  • 有些时候,确实很难给变量取一个好的名字。于是,许多程序员将变量名命名为类型名 如 Box box;

3.4.1 变量初始化

  • **声明一个变量之后,必须用赋值语句对变量进行显式初始化,千万不要使用未初始化的变量。**例如,Java编译器认为下面的语句序列是错误的

  • 在Java中,变量的声明尽可能地靠近变量第一次使用的地方,这是一种良好的程序编写风格。
  • 要想对一个已经声明过的变量进行赋值,就需要将变量名放在等号(=)左侧,相应取值的Java表达式放在等号的右侧。
  • 可以将变量的声明和初始化放在同一行

3.4.2 常量

在Java中,利用关键字final指示常量

  • 关键字final表示这个变量只能被赋值一次。一旦被赋值之后,就不能够再更改了。
  • 习惯上,常量名使用全大写。
  • 在Java中,经常希望某个常量可以在一个类中的多个方法中使用,通常将这些常量称为类常量。可以使用关键字static final设置一个类常量
  • 类变量(static)只能定义在类结构中,不能定义在方法中

3.5 运算符

  • 当参与 / 运算的两个操作数都是整数时,表示整数除法否则,表示浮点除法。整数的求余操作(有时称为取模)用 % 表示
  • 需要注意,整数被0除将会产生一个异常,而浮点数被0除将会得到无穷大或NaN结果。
  • 必须使用严格的浮点的方法可添加strictfp关键字 如: public static strictfp void main(String[] args)

3.5.1 数学函数与常量

在Math类中,包含了各种各样的数学函数。在编写不同类别的程序时,可能需要的函数也不同。

  • 要想计算一个数值的平方根,可以使用sqrt方法:Math.sqrt(Object o)
  • 在Java中,没有幂运算,因此需要借助于Math类的pow方法。语句:double y = Math.pow(x,a),将y的值设置为x的a次幂(x^a)。pow方法有两个double类型的参数,其返回结果也为double类型。
  • floorMod方法:floorMod(position + adjustment, 12)总会得到一个0~11之间的数(包括position + adjustment为负数的情况)。
  • Math类提供了一些常用的三角函数:
  • 指数函数以及它的反函数——自然对数以及以10为底的对数:
  • Math.random方法将返回一个0到1之间 [0,1)的随机浮点数。用n乘以这个浮点数,就可以得到从0到n-1之间的一个随机数。如下可得到0到n-1间随机整数
  • Java还提供了两个用于表示π和e常量的近似值
    • Math.PI
    • Math.E

提示:

  • 不必在数学方法名和常量名前添加前缀“Math”,只要在源文件的顶部加上下面这行代码就可以了。(静态导入起作用)

    • import static java.lang.Math.*;
      

3.5.2 数值类型之间的转换

在图3-1中有6个实心箭头,表示无信息丢失的转换;有3个虚箭头,表示可能有精度损失的转换。例如,123 456 789是一个大整数,它所包含的位数比float类型所能够表达的位数多。当将这个整型数值转换为float类型时,将会得到同样大小的结果,但却失去了一定的精度。

int n = 123456789;

float f = n; // f is 1.23456792E8

当使用上面两个数值进行二元操作时(例如n + f, n是整数,f是浮点数),先要将两个操作数转换为同一种类型,然后再进行计算

  • 如果两个操作数中有一个是double类型,另一个操作数就会转换为double类型。
  • 否则,如果其中一个操作数是float类型,另一个操作数将会转换为float类型。
  • 否则,如果其中一个操作数是long类型,另一个操作数将会转换为long类型。
  • 否则,两个操作数都将被转换为int类型。
  • double => float => long => int //默认找精度高的

3.5.3 强制类型转换 //version 1.0 2021/4/22

有时也需要将double转换成int。在Java中,允许进行这种数值之间的类型转换。当然,有可能会丢失一些信息。在这种情况下,需要通过强制类型转换(cast)实现这个操作。

  • 强制类型转换的语法格式是在圆括号中给出想要转换的目标类型,后面紧跟待转换的变量名。例如:

  • 想对浮点数进行舍入运算,以便得到最接近的整数(在很多情况下,这种操作更有用),那就需要使用Math.round方法:

  • 当调用round的时候,仍然需要使用强制类型转换(int)。**其原因是round方法返回的结果为long类型,由于存在信息丢失的可能性,所以只有使用显式的强制类型转换才能够将long类型转换成int类型。**现在,变量nx的值为10。

警告:

  • 如果试图将一个数值从一种类型强制转换为另一种类型,而又超出了目标类型的表示范围,结果就会截断成一个完全不同的值。例如,(byte)300的实际值为44。
  • 不要在boolean类型与任何数值类型之间进行强制类型转换,这样可以防止发生错误。只有极少数的情况才需要将布尔类型转换为数值类型,这时可以使用条件表达式b==1?1:0。

3.5.4 结合赋值和运算符

可以在赋值中使用二元运算符(一般地,要把运算符放在=号左边,如*=或%=,%+=)

如果运算符得到一个值,其类型与左侧操作数的类型不同,就会发生强制类型转换。 x+=3.5; =》 把x设置为(int)(x+3.5)。

3.5.5 自增与自减运算符

前缀形式会先完成加1;而后缀形式会使用变量原来的值。

建议不要在表达式中使用++,因为这样的代码很容易让人困惑,而且会带来烦人的bug。

3.5.6 关系和boolean运算符

  • 检验相等性 使用两个等号==
  • 使用 != 检测不相等
  • 最后,还有经常使用的<(小于)、>(大于)、<=(小于等于)和>=(大于等于)运算符。
  • 使用&&表示逻辑“与”运算符,使用||表示逻辑“或”运算符,感叹号!就是逻辑非运算符
  • &&和||运算符是按照**“短路”**方式来求值的:**如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。**可以利用这一点来避免错误

3.5.7 位运算符

处理整型类型时,可以直接对组成整型数值的各个位完成操作。这意味着可以使用掩码技术得到整数中的各个位。位运算符包括:

这些运算符按位模式处理。例如,如果n是一个整数变量,而且用二进制表示的n从右边数第4位为1,则

会返回1,否则返回0。利用&并结合使用适当的2的幂,可以把其他位掩掉,而只保留其中的某一位。

  • 应用在布尔值上时,**&和|运算符也会得到一个布尔值。不过&和|运算符不采用“短路”**方式来求值

  • 和<<运算符将位模式左移或右移。需要建立位模式来完成位掩码时,这两个运算符会很方便:

  • 运算符会用0填充高位,这与 >>不同,它会用符号位填充高位不存在<<<运算符。

警告:

  • 移位运算符的右操作数要完成模32的运算(除非左操作数是long类型,在这种情况下需要对右操作数模64
  • 例如,1 << 35的值等同于1 << 3
  • 负数一定要换成补码再移位,因为负数在计算机中是以补码存储.

3.5.8 括号与运算符级别

表3-4给出了运算符的优先级。如果不使用圆括号,就按照给出的运算符优先级次序进行计算。同一个级别的运算符按照从左到右的次序进行计算除了表中给出的右结合运算符外。

例如,由于&&的优先级比||的优先级高,所以表达式等价于

又因为**+=是右结合运算符**,所以表达式

等价于

也就是将b += c的结果(加上c之后的b)加到a上。

3.5.9 枚举类型

  • 变量的取值只在一个有限的集合内

  • 枚举类型包括有限个命名的值

  • 现在,可以声明这种类型的变量:

  • **Size类型的变量只能存储这个类型声明中给定的某个枚举值,或者null值,**null表示这个变量没有设置任何值。

3.6 字符串

  • 从概念上讲,Java字符串就是Unicode字符序列字符串长度即为字符串代码单元的长度)。例如,串“Java\u2122”由5个Unicode字符J、a、v、a和 TM
  • Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义类,很自然地叫做String。每个用双引号括起来的字符串都是String类的一个实例:

3.6.1 子串

  • String类的substring方法可以从一个较大的字符串提取出一个子串
  • 创建了一个由字符“Hel”组成的字符串。
  • substring方法的第二个参数是不想复制的第一个位置。这里要复制位置为0、1和2(从0到2,包括0和2)的字符。在substring中从0开始计数,直到3为止,但不包含3。
  • substring的工作方式有一个优点:**容易计算子串的长度。字符串s.substring(a, b)的长度为b-a。**例如,子串“Hel”的长度为3-0=3。

3.6.2 拼接

  • Java语言允许使用**+号连接(拼接)两个字符串。**
  • 当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串
    • 这种特性通常用在输出语句中。例如:
  • 如果需要把多个字符串放在一起,用一个定界符分隔,可以使用静态join方法:

3.6.3 不可变字符串

  • String类没有提供用于修改字符串的方法
  • 由于不能修改Java字符串中的字符,所以在Java文档中将String类对象称为不可变字符串,如同数字3永远是数字3一样
  • 可以修改字符串变量greeting,让它引用另外一个字符串,这就如同可以将存放3的数值变量改成存放4一样。
  • 通过拼接“Hel”和“p! ”来创建一个新字符串的效率确实不高。但是,不可变字符串却有一个优点:编译器可以让字符串共享。
  • 各种字符串存放在公共的存储池,**字符串变量指向存储池中相应的位置。**如果复制一个字符串变量,原始字符串与复制的字符串共享相同的字符。

3.6.4 检测字符串是否相等

  • equals方法检测两个字符串是否相等

  • s与t可以是字符串变量,也可以是字符串字面量。例如,下列表达式是合法的:

  • 要想检测两个字符串是否相等,而不区分大小写,可以使用equalsIgnoreCase方法。

  • 一定不要使用==运算符检测两个字符串是否相等!这个运算符只能够确定两个字符串是否放置在同一个位置上。完全有可能将内容相同的多个字符串的拷贝放置在不同的位置上。

  • 如果虚拟机始终将相同的字符串共享,就可以使用==运算符检测是否相等。但实际上只有字符串常量是共享的,而**+或substring等操作产生的结果并不是共享的。**

3.6.5 空串与Null串

  • 空串""是长度为0的字符串。可以调用以下代码检查一个字符串是否为空:

  • 空串是一个Java对象,有自己的串长度(0)和内容(空)。

  • String变量还可以存放一个特殊的值,名为null,这表示目前没有任何对象与该变量关联

  • 要检查一个字符串是否为null,要使用以下条件:

  • 有时要检查一个字符串既不是null也不为空串,这种情况下就需要使用以下条件:

  • 首先要检查str不为null,如果在一个null值上调用方法,会出现错误。

3.6.6 码点与代码单元

  • Java字符串由char值序列组成。char数据类型是一个采用UTF-16编码表示Unicode码点

  • 大多数的常用Unicode字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示。

  • length方法将返回采用UTF-16编码表示的给定字符串所需要的代码单元数量。

  • 要想得到实际的长度,即码点数量,可用

    • codePointCount(int beginIndex, int endIndex)两个参数分别表示char值(代码单元)的索引
  • 调用s.charAt(n)将返回位置n(代码单元索引)的代码单元,n介于0~s.length()-1之间。

  • 要想得到第i个码点,应该使用下列语句

    • //index表示要偏移的码点索引(0)向正方向偏移i位的码点位置
      //即第i个码点的位置
      int index = greeting.offsetByCodePoints(0,i);
      //cp 表示代码单元索引为index处的码点
      //TODO 源码分析
      int cp = greeting.codePointAt(index);
      
  • 如果想要遍历一个字符串,并且依次查看每一个码点,可以使用下列语句:

    int cp =sentence.codePointAt(i);
    //判断该码点是否占了两个代码单元
    if(Character.isSupplementaryCodePoint(cp)) i+=2;
    else i++;
    
  • 可以使用下列语句实现回退操作:

  • 更容易的办法是**使用codePoints方法,它会生成一个int值的“流”,每个int值对应一个码点。**可以将它转换为一个数组,再完成遍历。

    • int[] ints = sentence.codePoints().toArray();
      
  • 要把一个码点数组转换为一个字符串,可以使用构造函数

    • public String(int[] codePoints, int offset, int count)//codePoints码点数组 offset码点数组偏移量 代表从哪开始 count从码点数组中截取的长度
      
    String str =new String(ints,0,ints.length);
    

3.6.7 String API

Java中的String类包含了50多个方法。令人惊讶的是绝大多数都很有用,可以设想使用的频繁非常高。下面的API注释汇总了一部分最常用的方法。

● char charAt (int index)

返回给定位置的代码单元。除非对底层的代码单元感兴趣,否则不需要调用这个方法。

● int codePointAt(int index) 5.0

返回从给定位置开始的码点

● int offsetByCodePoints(int startIndex, int cpCount) 5.0

返回从startIndex代码点开始,位移cpCount(可负数)后的码点索引。

● int compareTo(String other)

按照字典顺序,如果字符串位于other之前,返回一个负数;如果字符串位于other之后,返回一个正数;如果两个字符串相等,返回0。

● IntStream codePoints() 8

这个字符串的码点作为一个流返回。调用toArray将它们放在一个数组中

● new String(int[ ] codePoints, int offset, int count) 5.0

用数组中从offset开始的count个码点构造一个字符串

● boolean equals(Object other)

如果字符串与other相等,返回true。

● boolean equalsIgnoreCase(String other)

如果字符串与other相等**(忽略大小写)**,返回true。

● boolean startsWith(String prefix)

● boolean endsWith(String suffix)

如果字符串以suffix开头或结尾,则返回true。

● int index0f(String str)

● int index0f(String str, int fromIndex)

● int index0f(int cp)

● int index0f(int cp, int fromIndex)

返回与字符串str或代码点cp匹配的第一个子串的开始位置。这个位置从索引0或fromIndex开始计算。如果在原始串中不存在str,返回-1。

● int lastIndex0f(String str)

● int lastIndex0f(String str, int fromIndex)

● int lastindex0f(int cp)

● int lastindex0f(int cp, int fromIndex)

返回与字符串str或代码点cp匹配的最后一个子串的开始位置。这个位置从原始串尾端或fromIndex开始计算。

● int length( )返回字符串的长度。

● int codePointCount(int startIndex, int endIndex) 5.0

返回startIndex和endIndex-1之间的代码点数量。没有配成对的代用字符(index处为高位代码单元时)将计入代码点。

● String replace(CharSequence oldString, CharSequence newString)

返回一个新字符串。这个字符串用newString代替原始字符串中所有的oldString。可以用String或StringBuilder对象作为CharSequence参数。

● String substring(int beginIndex)

● String substring(int beginIndex, int endIndex)

返回一个新字符串。这个字符串包含原始字符串中从beginIndex到串尾或endIndex-1的所有代码单元。

● String toLowerCase( )

● String toUpperCase( )

返回一个新字符串。这个字符串将原始字符串中的大写字母改为小写,或者将原始字符串中的所有小写字母改成了大写字母。

● String trim( )

返回一个新字符串。这个字符串将删除了原始字符串头部和尾部的空格

● String join(CharSequence delimiter, CharSequence… elements) 8

返回一个新字符串,用给定的定界符连接所有元素。

3.6.8 阅读联机API文档

String类包含许多方法。而且,在标准库中有几千个类,方法数量更加惊人。要想记住所有的类和方法是一件不太不可能的事情。因此,学会使用在线API文档十分重要,从中可以查阅到标准类库中的所有类和方法。

3.6.9 构建字符串

  • 有些时候,需要由较短的字符串构建字符串,例如,按键或来自文件中的单词。**采用字符串连接的方式达到此目的效率比较低。**每次连接字符串,都会构建一个新的String对象,既耗时,又浪费空间。

  • 使用StringBuilder类就可以避免这个问题的发生。

  • 如果需要用许多小段的字符串构建一个字符串,那么应该按照下列步骤进行。

    • 首先,构建一个空的字符串构建器:

    • 当每次需要添加一部分内容时,就调用append方法。

    • 需要构建字符串时就调用toString方法,将可以得到一个String对象,其中包含了构建器中的字符序列

    •     StringBuilder builder = new StringBuilder();    builder.append('a');//append a single character    builder.append("abc");//append a string    String toString = builder.toString();
      
  • StringBuilder类。这个类的前身是StringBuffer,其效率稍有些低,但允许采用多线程的方式执行添加或删除字符的操作

  • 如果所有字符串在一个单线程中编辑(通常都是这样),则应该用StringBuilder替代它。这两个类的API是相同的。

下面的API注释包含了StringBuilder类中的重要方法。

● StringBuilder()

构造一个空的字符串构建器。

● int length()

返回构建器或缓冲器中的代码单元数量。

● StringBuilder append(String str)

追加一个字符串并返回this。

● StringBuilder append(char c)

追加一个代码单元并返回this。

● StringBuilder appendCodePoint(int cp)

追加一个代码点,并将其转换为一个或两个代码单元并返回this。

● void setCharAt(int i, char c)

将第i个代码单元设置为c。

● StringBuilder insert(int offset, String str)

在offset位置插入一个字符串并返回this。

● StringBuilder insert(int offset, Char c)

在offset位置插入一个代码单元并返回this。

● StringBuilder delete(int startIndex, int endIndex)

删除偏移量从startIndex到endIndex-1的代码单元并返回this。

● String toString( )

返回一个与构建器或缓冲器内容相同的字符串。

3.7 输入输出

程序能够接收输入,并以适当的格式输出。

3.7.1 读取输入

  • 打印输出到“标准输出流”(即控制台窗口)是一件非常容易的事情,只要调用System.out.println即可
  • 读取“标准输入流”System.in就没有那么简单了。要想通过控制台进行输入,首先需要构造一个Scanner对象,并与**“标准输入流”System.in关联。**

  • 现在,就可以使用Scanner类的各种方法实现输入操作了。

    • 例如,nextLine方法将输入一行

              System.out.println("what is your name?");String name = in .nextLine();
      
    • 在这里,使用nextLine方法是因为在输入行中有可能包含空格。要想读取一个单词以空白符作为分隔符,遇到第一个空格就停止),就调用

      String firstname = in.next();
      
    • 读取一个整数,就调用nextInt方法

    • 与此类似,要想读取下一个浮点数,就调用nextDouble方法

  • Scanner类定义在java.util包中。当使用的类不是定义在基本java.lang包中时,一定要使用import指示字将相应的包加载进来。

  • 因为输入是可见的,所以Scanner类不适用于从控制台读取密码

    • JavaSE 6特别引入了Console类实现这个目的。要想读取一个密码,可以采用下列代码:

              Console console = System.console();String username = console.readLine("User name: ");char[] passwd = console.readPassword("Password: ");
      
    • **为了安全起见,返回的密码存放在一维字符数组中,而不是字符串中。**在对密码进行处理之后,应该马上用一个填充值覆盖数组元素

    • 采用Console对象处理输入不如采用Scanner方便。每次只能读取一行输入,而没有能够读取一个单词或一个数值的方法。

    • Java.io.Console 只能用在标准输入、输出流未被重定向的原始控制台中使用,在 Eclipse 或者其他 IDE 的控制台是用不了的。因为IDE将应用程序作为后台进程运行,而不是作为带有系统控制台的顶级进程运行。

    • 虚拟机是否具有控制台取决于底层平台以及虚拟机的调用方式。如果JVM是在交互式命令行(比如Windows的cmd)中启动的,并且输入输出没有重定向到另外的地方,那么就可以得到一个可用的Console实例。 如果虚拟机是自动启动的,例如由后台作业调度程序启动,则通常不具有控制台。

java.util.Scanner 5.0 API

● Scanner (InputStream in)

用给定的输入流创建一个Scanner对象。

● String nextLine( )

读取输入的下一行内容。

● String next( )

读取输入的下一个单词(以空格作为分隔符)。

● int nextInt( )

● double nextDouble( )

读取并转换下一个表示整数或浮点数的字符序列。

● boolean hasNext( )

检测输入中是否还有其他单词。

● boolean hasNextInt( )

● boolean hasNextDouble( )

检测是否还有表示整数或浮点数的下一个字符序列。

java.lang.System 1.0 API

● static Console console( ) 6

如果**有可能进行交互操作,就通过控制台窗口为交互的用户返回一个Console对象,否则返回null。对于任何一个通过控制台窗口启动的程序,都可使用Console对象。**否则,其可用性将与所使用的系统有关。

java.io.Console 6 API

● static char[ ] readPassword(String prompt, Object…args)

● static String readLine(String prompt, Object…args)

显示字符串prompt并且读取用户输入,直到输入行结束。args参数可以用来提供输入格式。有关这部分内容将在下一节中介绍。

3.7.2 格式化输出(printf)

  • System.out.print(x)将数值x输出到控制台上。这条命令将以x对应的数据类型所允许的最大非0数字位数打印输出x。例如:打印

  • Java SE 5.0沿用了C语言库函数中的printf方法。例如,调用System.out.printf("%8.2f",x); 可以用8个字符的宽度和小数点后两个字符的精度打印x(.数字表示精度)。也就是说,打印输**出一个空格(头部出现)**和7个字符,如下所示:

  • printf中,可以使用多个参数,例如

    每一个以%字符开始的格式说明符都用相应的参数替换。格式说明符尾部的转换符将指示被格式化的数值类型:f表示浮点数,s表示字符串,d表示十进制整数。表3-5列出了所有转换符。

  • 可以使用s转换符格式化任意的对象。对于任意实现了Formattable接口的对象都将调用formatTo方法否则将调用toString方法,它可以将对象转换为字符串。

  • 可以使用静态的String.format方法创建一个格式化的字符串,而不打印输出:String message =String.format(“Hello,%s”,x);

  • 还可以给出控制格式化输出的各种标志。表3-6列出了所有的标志。例如,逗号标志增加了分组的分隔符。即

    打印可以使用多个标志,例如,“%, ( .2f”使用分组的分隔符并将负数括在括号内。

  • 简略地介绍printf方法中日期与时间的格式化选项Date类和相关的格式化选项。格式包括两个字母,以t开始,以表3-7中的任意字母结束。例如,

    这条语句将用下面的格式打印当前的日期和时间

  • 采用一个格式化的字符串指出要被格式化的参数索引索引(从1开始)必须紧跟在%后面,并以$终止

    • 例如,System.out.printf("%1ss %2stc %2$tB",“12354”,new Date());
    • 打印 12354 星期五 四月 09 16:56:40 CST 2021 四月
    • 还可以选择使用**<标志**。它指示前面格式说明中的参数将被再次使用。也就是说,下列语句将产生与前面语句同样的输出结果:System.out.printf("%s %tc %<tB",“12354”,new Date());
  • 提示:

    • 参数索引值从1开始,而不是从0开始,%1$….对第1个参数格式化。这就避免了与0标志混淆
    • 用了参数索引后,其他格式说明符(%xx)也需要使用参数索引
    • 许多格式化规则是本地环境特有的。例如,在德国,组分隔符是句号而不是逗号,Monday被格式化为Montag。在卷Ⅱ第5章中将介绍如何控制应用的国际化行为。

现在,已经了解了printf方法的所有特性。图3-6给出了格式说明符的语法图。

3.7.3 文件输入与输出

  • 要想对文件进行读取,就需要一个用File对象构造一个Scanner对象如下所示:

    如果文件名中包含反斜杠符号,就要记住在每个反斜杠之前再加一个额外的反斜杠:“c:\mydirectory\myfile.txt”。

    如果省略字符编码,则会使用运行这个Java程序的机器的“默认编码”。这不是一个好主意,如果在不同的机器上运行这个程序,可能会有不同的表现。

    现在,就可以利用前面介绍的任何一个Scanner方法对文件进行读取。

    //判断文件输入中是否有单词while(in.hasNext()){    //输出 文件输入中的下一行    System.out.println(in.nextLine());}
    
  • 要想写入文件,就需要构造一个PrintWriter对象。在构造器中,只需要提供文件名:PrintWriter out = new PrintWriter(“myfile.txt”,“UTF-8”);

    相对路径 相对当前项目路径

    如果文件不存在,创建该文件。可以像输出到System.out一样使用print、println以及printf命令。

  • 可以构造一个带有字符串参数的Scanner,但这个Scanner将字符串解释为数据,而不是文件名。例如,如果调用:

    这个scanner会将参数作为包含10个字符的数据:‘m’, ‘y’, ‘f’等。在这个示例中所显示的并不是人们所期望的效果。

  • 指定一个相对文件名时,例如,“myfile.txt”,“mydirectory/myfile.txt”或“…/myfile.txt”,文件位于Java虚拟机启动路径的相对位置。

  • 如果在命令行方式下用下列命令启动程序:启动路径就是命令解释器的当前路径。然而,如果使用集成开发环境,那么启动路径将由IDE控制。

  • 可以使用下面的调用方式找到路径的位置:String dir = System.getProperty(“user.dir”);

  • 如果觉得定位文件比较烦恼,则可以考虑使用绝对路径

  • 访问文件与使用System.in和System.out一样容易。要记住一点:如果用一个不存在的文件构造一个Scanner,或者用一个不能被创建的文件名构造一个PrintWriter,那么就会发生异常。

  • 当采用命令行方式启动一个程序时,可以利用Shell的重定向语法将任意文件关联到System.in和System.out:这样,就不必担心处理IOException异常了

java.util.Scanner 5.0 API

● Scanner(File f)

构造一个从给定文件读取数据的Scanner。

● Scanner(String data)

构造一个从给定字符串读取数据的Scanner。

java.io.PrintWriter 1.1 API

● PrintWriter(String fileName)

构造一个将数据写入文件的PrintWriter。文件名由参数指定

java.nio.file.Paths 7 API

● static Path get(String pathname)

根据给定的路径名构造一个Path。

3.8 控制流程

Java使用条件语句和循环结构确定控制流程。

3.8.1 块作用域

在深入学习控制结构之前,需要了解**块(block)**的概念。

  • 块(即复合语句)是指由一对大括号括起来的若干条简单的Java语句。块确定了变量的作用域。一个块可以嵌套在另一个块中。下面就是在main方法块中嵌套另一个语句块的示例。

  • 不能在嵌套的两个块中声明同名的变量。例如,下面的代码就有错误,而无法通过编译:

3.8.2 条件语句

  • else子句与最邻近的if构成一组
  • 使用块(有时称为复合语句)可以在Java程序结构中原本只能放置一条(简单)语句的地方放置多条语句。示例:

3.8.3 循环

  • 当条件为true时,while循环执行一条语句(也可以是一个语句块)。
  • while循环语句首先检测循环条件。因此,循环体中的代码有可能不被执行。**如果希望循环体至少执行一次,则应该将检测条件放在最后。**使用do/while循环语句可以实现这种操作方式。

3.8.4 确定循环

  • for循环语句是支持迭代的一种通用结构,利用每次迭代之后更新的计数器或类似的变量来控制迭代次数。

  • for语句的第1部分通常用于对计数器初始化第2部分给出每次新一轮循环执行前要检测的循环条件;第3部分指示如何更新计数器

  • 尽管Java允许在for循环的各个部分放置任何表达式,但有一条不成文的规则:for语句的**3个部分应该对同一个计数器变量进行初始化、检测和更新。**若不遵守这一规则,编写的循环常常晦涩难懂。

  • **警告:**在循环中,检测两个浮点数是否相等需要格外小心。下面的for循环

    可能永远不会结束由于舍入的误差,最终可能得不到精确值。例如,在上面的循环中,**因为0.1无法精确地用二进制表示,所以,x将从9.999999 99999998跳到10.099999 999999 98。**TODO 双精度浮点数表示法

  • 当在for语句的第1部分中声明了一个变量之后,这个变量的作用域就为for循环的整个循环体。

  • 如果在for语句内部定义一个变量,这个变量就不能在循环体之外使用。因此,如果希望在for循环体之外使用循环计数器的最终值,就要确保这个变量在循环语句的前面且在外部声明!

  • 另一方面,可以在各自独立的不同for循环中定义同名的变量:

3.8.5 多重选择:switch语句

在处理多个选项时,使用if/else结构显得有些笨拙

  • switch语句将从与选项值相匹配的case标签处开始执行直到遇到break语句,或者执行到switch语句的结束处为止。
  • 如果没有相匹配的case标签,而有default子句,就执行这个子句。
  • 警告: 有可能触发多个case分支。如果在case分支语句的末尾没有break语句,那么就会接着执行下一个case分支语句。这种情况相当危险,常常会引发错误。为此,我们在程序中从不使用switch语句。
  • case标签可以是:
    • ● 类型为char、byte、short或int的常量表达式
    • 枚举常量
    • ● 从Java SE 7开始,case标签还可以是字符串字面量
    • 如:
  • 当在switch语句中使用枚举常量时,不必在每个标签中指明枚举名,可以由switch的表达式值确定。例如:

3.8.6 中断控制流程语句

  • 尽管Java的设计者将goto作为保留字,但实际上并没有打算在语言中使用它。通常,使用goto语句被认为是一种拙劣的程序设计风格。

  • 但在有些情况下,偶尔使用goto跳出循环还是有益处的。Java设计者同意这种看法,甚至在Java语言中增加了一条带标签的break,以此来支持这种程序设计风格。

  • 不带标签的break语句。与用于退出switch语句的break语句一样,它也可以用于退出循环语句。例如,

  • Java还提供了一种带标签的break语句,用于跳出多重嵌套的循环语句。有时候,在嵌套很深的循环语句中会发生一些不可预料的事情。此时可能更加希望跳到嵌套的所有循环语句之外。通过添加一些额外的条件判断实现各层循环的检测很不方便。

  • 这里有一个示例说明了break语句的工作状态。请注意,标签必须放在希望跳出 的最外层循环之前**,并且必须紧跟一个冒号

    •     read_data:    for (int i = 0; i < 40; i++) {        for (int i1 = 0; i1 < 50; i1++) {            System.out.println(i);            if(i==10) break read_data;        }    }//break跳出外层的for循环
      
    • 如果输入有误,通过执行带标签的break跳转到带标签的语句块末尾。对于任何使用break语句的代码都需要检测循环是正常结束,还是由break跳出。

  • 事实上,可以将标签应用到任何语句中,甚至可以应用到if语句或者块语句中,如下所示:

  • 因此,**如果希望使用一条goto语句,并将一个标签放在想要跳到的语句块之前,就可以使用break语句!**当然,并不提倡使用这种方式。另外需要注意,只能跳出语句块,而不能跳入语句块。

  • continue语句。与break语句一样,它将中断正常的控制流程。continue语句越过了当前循环体的剩余部分,立刻跳到循环首部。。

    • 还有一种带标签的continue语句,将跳到与标签匹配的循环首部。

3.9 大数值

  • 如果基本的整数和浮点数精度不能够满足需求,那么可以使用java.math包中的两个很有用的类:BigInteger和BigDecimal

  • 这两个类可以处理包含任意长度数字序列的数值。BigInteger类实现了任意精度的整数运算,BigDecimal实现了任意精度的浮点数运算。

  • 使用静态的valueOf方法可以将普通的数值转换为大数值:

    • public static BigInteger valueOf(long val)//long类型 需在整型常数后加l
      
  • 遗憾的是,不能使用人们熟悉的算术运算符(如:+和)处理大数值*。而需要使用大数值类中的add和multiply方法。

    • BigInteger c = a.add(b);BigInteger d =c.multiply(c);
      
  • Java没有提供运算符重载功能。程序员无法重定义+和*运算符,使其应用于BigInteger类的add和multiply运算。Java语言的设计者确实为字符串的连接重载了+运算符,但没有重载其他的运算符,也没有给Java程序员在自己的类中重载运算符的机会。

API java.math.BigInteger 1.1

● BigInteger add(BigInteger other)

● BigInteger subtract(BigInteger other)

● BigInteger multiply(BigInteger other)

● BigInteger divide(BigInteger other)

● BigInteger mod(BigInteger other)

返回这个大整数和另一个大整数other的和、差、积、商以及余数。

● int compareTo(BigInteger other)

**如果这个大整数与另一个大整数other相等,返回0;**如果这个大整数小于另一个大整数other,返回负数;否则,返回正数。

static BigInteger valueOf(long x)返回值等于x的大整数。

java.math.BigInteger 1.1 API

● BigDecimal add(BigDecimal other)

● BigDecimal subtract(BigDecimal other)

● BigDecimal multiply(BigDecimal other)

● BigDecimal divide(BigDecimal other RoundingMode mode) 5.0

返回这个大实数与另一个大实数other的和、差、积、商。要想计算商,必须给出舍入方式(rounding mode)。RoundingMode.HALF_UP是在学校中学习的四舍五入方式(即,数值0到4舍去,数值5到9进位)。它适用于常规的计算。有关其他的舍入方式请参看API文档。

● int compareTo(BigDecimal other)

如果这个大实数与另一个大实数相等,返回0;如果这个大实数小于另一个大实数,返回负数;否则,返回正数。

● static BigDecimal valueOf(long x)

● static BigDecimal valueOf(long x, int scale)

返回值为x或的一个大实数。

3.10 数组

  • 数组是一种数据结构,用来存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个值。例如,如果a是一个整型数组,a[i]就是数组中下标为i的整数。

  • 声明数组变量时,需要指出数组类型(数据元素类型紧跟[])和数组变量的名字。下面声明了整型数组a:

  • 不过,这条语句只声明了变量a并没有将a初始化为一个真正的数组。应该使用new运算符创建数组。此时变量a指向100个int类型的单元 变量a的指向可以改变

  • 数组长度不要求是常量:newint[n]会创建一个长度为n的数组 n为变量。

  • 两种形式声明数组 int[] a; 或 int a[]; 大多数Java应用程序员喜欢使用第一种风格,因为它将类型int[](整型数组)与变量名分开了。

  • 创建一个整数数组时,所有元素都初始化为0boolean数组的元素会初始化为false对象数组的元素则初始化为一个特殊值null,这表示这些元素(还)未存放任何对象。

  • 数组越界访问错误:

    int[] a=new int[10];//初始化为0
    System.out.println(a[10]);//ArrayIndexOutOfBoundsException
    
  • 数组中的元素个数,可以使用array.length

  • 一旦创建了数组,就不能再改变它的大小(尽管可以改变每一个数组元素)。如果**经常需要在运行过程中扩展数组的大小,就应该使用另一种数据结构——数组列表(array list)**有关数组列表的详细内容请参看第5章。

3.10.1 for each循环

  • Java有一种功能很强的循环结构,可以用来依次处理数组中的每个元素其他类型的元素集合亦可)而不必为指定下标值而分心。即增强的for循环
  • 格式
  • 定义一个变量用于暂存集合中的每一个元素,并执行相应的语句(当然,也可以是语句块)。collection这一集合表达式必须是一个数组或者是一个实现了Iterable接口的类对象(例如ArrayList)。
  • 这个循环应该读作“循环a中的每一个元素”(for each element in a)。
  • 如果需要处理一个集合中的所有元素,for each循环语句对传统循环语句所进行的改进更是叫人称赞不已。然而,在很多场合下,还是需要使用传统的for循环。例如,如果不希望遍历集合中的每个元素,或者在循环内部需要使用下标值等。
  • 提示:有个更加简单的方式打印数组中的所有值,即利用Arrays类的toString方法。调用Arrays.toString(a),返回一个包含数组元素的字符串,这些元素被放置在括号内,并用逗号分隔,例如,“[2,3,5,7,11,13]”。

3.10.2 数组初始化以及匿名数组

  • 在Java中,提供了一种创建数组对象并同时赋予初始值的简化书写形式。下面是一个例子:int[] x = {1,2,3,4,5};
  • 请注意,在使用这种语句时,不需要调用new
  • 甚至还可以初始化一个匿名的数组
  • 创建一个新数组并利用括号中提供的值进行初始化数组的大小就是初始值的个数。
  • 使用这种语法形式可以在不创建新变量的情况下重新初始化一个数组。例如:
    • 这是下列语句的简写形式:
  • 在Java中,允许数组长度为0。在编写一个返回类型为数组的方法时,如果碰巧结果数组为空,则这种语法形式就显得非常有用 此时无须对null引用检查。此时可以创建一个长度为0的数组:
  • 注意,数组长度为0与null不同。 长度为0的数组为一个非空引用对象,便于进行判断
  • 对象数组并不会实例化对象,而只是声明了若干个引用变量的空间,且对象数组元素默认初始化为null

3.10.3 数组拷贝

在Java中,允许将一个数组变量拷贝给另一个数组变量(不会开辟新空间)。这时,两个变量将引用同一个数组:

  • 如果希望将一个数组的所有值拷贝到一个新的数组(开辟新空间)中去,就要使用Arrays类的copyOf方法:

  • 第2个参数是新数组的长度。这个方法通常用来增加数组的大小:

    • 如果数组元素是数值型,那么多余的元素将被赋值为0;如果数组元素是布尔型,则将赋值为false。相反,如果长度小于原始数组的长度,则只拷贝最前面的数据元素。
  • Java中的**[ ]运算符被预定义为检查数组边界**,而且没有指针运算,即不能通过a加1得到数组的下一个元素。

3.10.4 命令行参数

  • 每一个Java应用程序都有一个带Stringarg[]参数的main方法。这个参数表明main方法将接收一个字符串数组,也就是命令行参数

  • 如果使用下面这种形式运行这个程序:java ClassName -g cruel world

    args数组将包含下列内容:

3.10.5 数组排序

要想对数值型数组进行排序,可以使用Arrays类中的sort方法:

  • 这个方法使用了优化的快速排序算法。快速排序算法对于大多数数据集合来说都是效率比较高的。Arrays类还提供了几个使用很便捷的方法,在稍后的API注释中将介绍它们。

java.util.Arrays 1.2 API

● static String toString(type[] a) 5.0

返回包含a中数据元素的字符串,这些数据元素被放在括号内,并用逗号分隔。参数:a 类型为int、long、short、char、byte、boolean、float或double的数组。

● static type copyOf(type[] a, int length) 6

● static type copyOfRange(type[] a, int start, int end) 6

返回与a类型相同的一个数组,其长度为length或者end-start,数组元素为a的值。

参数:a 类型为int、long、short、char、byte、boolean、float或double的数组。

start 起始下标(包含这个值)。

end 终止下标(不包含这个值)。**这个值可能大于a.length。**在这种情况下,结果为0或false。

length 拷贝的数据元素长度。如果length值大于a.length,结果为0或false;否则,数组中只有前面length个数据元素的拷贝值。

● static void sort(type[] a)

采用优化的快速排序算法对数组进行排序。参数:a 类型为int、long、short、char、byte、boolean、float或double的数组。

● static int binarySearch(type[] a, type v)

● static int binarySearch(type[] a, int start, int end, type v) 6

采用二分搜索算法查找值v。如果查找成功,则返回相应的下标值;否则,返回一个负数值r。**-r-1是为保持a有序v应插入的位置。**TODO原理 和数组的底层

参数:a 类型为int、long、short、char、byte、boolean、float或double的有序数组。

start 起始下标(包含这个值)。

end 终止下标(不包含这个值)。

v 同a的数据元素类型相同的值。

● static void fill(type[] a, type v)

将数组的所有数据元素值设置为v。

参数:a 类型为int、long、short、char、byte、boolean、float或double的数组。v 与a数据元素类型相同的一个值。

● static boolean equals(type[] a, type[] b)

如果两个数组大小相同,并且下标相同的元素都对应相等,返回true。

参数:a、b 类型为int、long、short、char、byte、boolean、float或double的两个数组。

3.10.6 多维数组

  • 多维数组将使用多个下标访问数组元素,它适用于表示表格或更加复杂的排列形式。这一节的内容可以先跳过,等到需要使用这种存储机制时再返回来学习。

  • 在Java中,声明一个二维数组相当简单。例如:

  • 与一维数组一样,在调用new对多维数组进行初始化之前不能使用它。在这里可以这样初始化:

  • 另外,如果知道数组元素,就可以不调用new,而直接使用简化的书写形式对多维数组进行初始化。例如:

  • for each循环语句不能自动处理二维数组的每一个元素。它是按照行,也就是一维数组处理的。要想访问二维数组a的所有元素,需要使用两个嵌套的循环,如下所示:

  • 快速地打印一个二维数组的数据元素列表,可以调用:Arrays.deepToString(a) 输出格式为:

3.10.7 不规则数组

  • Java实际上没有多维数组,只有一维数组。多维数组被解释为**“数组的数组**。”

  • 例如,在前面的示例中,balances数组实际上是一个包含10个元素的数组,而每个元素又是一个由6个浮点数组成的数组(请参看图3-15)

  • 表达式balances[i]引用第i个子数组,也就是二维表的第i行。它本身也是一个数组,balances[ i ] [ j ]引用这个数组的第j项。

  • 由于可以单独地存取数组的某一行,所以可以让两行交换

  • 方便地构造一个**“不规则”数组**,即数组的每一行有不同的长度

    • 要想创建一个不规则的数组,首先需要分配一个具有所含行数的数组
    • 接下来,分配这些行。
    • 在分配了数组之后,假定没有超出边界,就可以采用通常的方式访问其中的元素了
  • Java声明

    等于分配了一个包含10个指针的数组

    然后,指针数组的每一个元素填充了一个包含6个数字的数组

    当创建new double[10] [6]时,这个循环将自动地执行。当需要不规则的数组时,只能单独地创建行数组。

java核心技术卷I 第1-3章 笔记相关推荐

  1. Java 核心技术卷 II(第 8 版) – 读书笔记 – 第 1 章(下)

    22.一旦获得了一个 Charset,就可以在 Java 的 Unicode 和指定的编码格式之间进行转化,下面以 GBK 和 Unicode 之间做为例子. 从 Unicode 到 GBK: imp ...

  2. Java核心技术卷阅读随笔--第3章【Java 的基本程序设计结构】

    3.1 一个简单的Java应用程序 下面看一个最简单的 Java 应用程序,它只发送一条消息到控制台窗口中: 复制代码 public class FirstSample { public static ...

  3. Java核心技术卷2 高级特性 学习笔记(5)

    参考:Java核心技术卷2 高级特性  第九章 Java技术提供了三种确保安全的机制: 语言设计特性(对数组的边界进行检查,无不受检查的类型转换,无指针算法等). 访问控制机制,用于控制代码能够执行的 ...

  4. java12章_【有书共读】java核心技术卷1--第12章

    ==============java核心技术卷1第12章----Swing用户界面组件===========主要内容:1 swing和模型-视图-控制器设计模式2布局管理 3文本输入4选择组件 5菜单 ...

  5. 《Java 核心技术 卷1》 笔记 第11章 异常、日志、断言和调试

    出现不可预计的问题时,需要进行如下处理: 报告错误 保存操作结果 允许用户退出 本章解决的问题: 验证程序正确性 记录程序错误 调试技巧 11.1 处理异常 程序出现错误时应该: 返回安全状态,能让用 ...

  6. 《Java 核心技术 卷1》 笔记 第五章 继承(3)

    5.1.6 抽象类 有时候我们无法说出具体是什么,只能用于标识一个类型,比如图形,就可作为抽象类.虽然无法具体描述图形,但是图形通常都有面积.周长.这种时候就可用抽象类标识. 抽象类使用abstrac ...

  7. java实现图形界面输入半径求圆面积_【读】Java核心技术卷1

    阅读原文:[读]Java核心技术卷1 看到这本书时,我的内心是崩溃的,卷1就700多页,但是这本书是很多前辈所推荐的,想必其中必有精华所在,硬着头皮上吧. 如何阅读本书 拿到书的第一眼肯定去看目录,大 ...

  8. JAVA基础----终弄清java核心技术卷1中的int fourthBitFromRight = (n 0b1000 ) / 0b1000;是怎么回事了。。。

    一个关于位与运算的问题 对于<JAVA核心技术 卷1>(第11版) page43 第三章 3.5.8一节中有个描述如下: 如果n是一个整数变量,而二进制表示的n从右边数第四位1,则 int ...

  9. 《Java核心技术 卷Ⅰ》读书笔记一

    Java核心技术·卷 I(原书第10版) 作者: [美] 凯.S.霍斯特曼(Cay S. Horstmann) 出版社: 机械工业出版社 原作名: Core Java Volume I - Funda ...

最新文章

  1. 在AI人工智能时代,大学或需要重新定位
  2. Ubuntu12.04配置LAMP 环境
  3. Rhel6.0升级内核到3.0.4
  4. MATLAB移植C的小结
  5. 百度ERNIE新突破,登顶中文医疗信息处理权威榜单CBLUE冠军
  6. Server.UrlEncode、HttpUtility.UrlDecode不同编码
  7. 乘积最大子数组—leedcode152
  8. Cream Finance关于提高三个v2 yVaults的质押系数的提案已经完成
  9. 关于matlab中 CC = bwconncomp(A,4)命令的问题
  10. ASP.NET 3.5核心编程学习笔记(44):HTTP处理程序的编写之从数据库加载图像
  11. DOSBOX怎么使用 从编译到连接到执行操作全过程 + debug
  12. Mac下载安装vscode
  13. 谷歌工具栏不再支持火狐浏览器
  14. 大学四年总结——找工作是真的难
  15. 【Python】第2次作业:同符号数学运算
  16. 省-市-区三级联动选择地址 + 地图定位(高德api定位获取位置信息),互相联动显示
  17. 如何获取微信小程序页面路径
  18. linux下并行运行脚本与让程序可靠运行
  19. science 计算机论文,计算机科学毕业论文
  20. 【小程序开发之准备工作】如何开通云开发和CMS内容管理平台

热门文章

  1. 《圣斗士星矢》的武士道精神
  2. Fish-Lottie:纯Dart如何实现一个高性能动画框架?
  3. 树莓派安装smbus_如何配置树莓派并安装有用的库
  4. Received HTTP code 503 from proxy after CONNECT
  5. Python 的列表推导式
  6. vSphere ESXI 7.0镜像 Rufus U盘安装盘制作(Windows)
  7. 为什么电子通信,嵌入式工程师会有中年危机(修订版)
  8. php字幕格式,如何给视频添加字幕 快速添加视频字幕|可设置文字样式、位置等...
  9. 2021年秋季PAT乙级题解(C语言)
  10. C++设计:关于CMatrix类的相关操作