Java 核心技术 卷I 第3 章 Java 的基本程序设计结构

本文详细介绍了Java编程中的基本程序设计结构,包括类的定义、数据类型(如整型、浮点型、字符型和布尔型)、变量的声明与初始化、运算符的使用、输入输出操作(如Scanner和PrintWriter)、控制流程(如条件语句和循环结构)以及大数值处理。此外,还涵盖了数组的概念、初始化、操作以及多维数组的使用。
摘要由CSDN通过智能技术生成

第3 章 Java 的基本程序设计结构

3.1 一个简单的Java应用程序

Java区分大小写

关键字public 称为访问修饰符 (access modifier)

这些修饰符用于控制程序的其他部分对这段代码的访问级别。

关键字class表明Java程序中的全部内容都包含在类中。

类是构建所有Java应用程序和applet的构建块。

Java应用程序中的全部内容都必须放置在类中。

关键字class后面紧跟类名。Java中定义类名的规则很宽松。名字必须以字母开 头,后面可以跟字母和数字的任意组合。长度基本上没有限制。但是不能使用Java 保留字(例如,public或class)作为类名

标准的命名规范为(类名FirstSample就遵循了这个规范):类名是以大写字母开 头的名词。如果名字由多个单词组成,每个单词的第一个字母都应该大写(这种在 一个单词中间使用大写字母的方式称为骆驼命名法。以其自身为例,应该写成 CamelCase)。

源代码的文件名必须与公共类的名字相同,并用.java作为扩展名。因此,存储这段 源代码的文件名必须为FirstSample.java(再次提醒大家注意,大小写是非常重要 的,千万不能写成firstsample.java)。

根据Java语言规范,main方法必须声明为public

需要注意源代码中的括号{}。在Java中,像在C/C++中一样,用大括号划分程序的 各个部分(通常称为块)。Java中任何方法的代码都用“{”开始,用“}”结束。

现在需要记住:每个 Java应用程序都必须有一个main方法,其声明格式如下所示:

3.2 注释

与大多数程序设计语言一样,Java中的注释也不会出现在可执行程序中。因此,可 以在源程序中根据需要添加任意多的注释,而不必担心可执行代码会膨胀。在Java 中,有3种标记注释的方式。最常用的方式是使用//,其注释内容从//开始到本行结 尾。

当需要长篇的注释时,既可以在每行的注释前面标记//,也可以使用//将一段 比较长的注释括起来。

第3种注释可以用来自动地生成文档。这种注释以/*开始,以/结束。

public class FirstSample {
    public static void main(String[] args) {
        System.out.println("We will not use 'Hello, World!'");
    }
}

警告:在Java中,/**/注释不能嵌套。也就是说,不能简单地把代码用// 括起来作为注释,因为这段代码本身可能也包含一个*/。

3.3数据类型

Java是一种强类型语言。这就意味着必须为每一个变量声明一种类型。在Java中, 一共有8种基本类型(primitive type),其中有4种整型、2种浮点类型、1种用 于表示Unicode编码的字符单元的字符类型char(请参见论述char类型的章节)和1 种用于表示真值的boolean类型。

注释:Java有一个能够表示任意精度的算术包,通常称为“大数值”(big number)。虽然被称为大数值,但它并不是一种新的Java类型,而是一个Java对 象。

3.3.1 整型

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

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

长整型数值有一个后缀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编译器会去除这些下划线。

3.3.2 浮点类型

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

double表示这种类型的数值精度是float类型的两倍(有人称之为双精度数值)。

绝大部分应用程序都采用double类型。在很多情况下,float类型的精度很难满足 需求。实际上,只有很少的情况适合使用float类型,例如,需要单精度数据的库, 或者需要存储大量数据。

float类型的数值有一个后缀F或f(例如,3.14F)。没有后缀F的浮点数值(如 3.14)默认为double类型。当然,也可以在浮点数值后面添加后缀D或d(例如, 3.14D)。

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

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

3.3.3 char 类型

char类型原本用于表示单个字符。

,有些 Unicode字符可以用一个char值描述,另外一些Unicode字符则需要两个char值。

char类型的字面量值要用单引号括起来。

3.3.4 Unicode 和 char 类型

Unicode打破了传统字符编码机制的限制。

设计Unicode编码的目的就是要解决字符编码机制标准不同的问题。

我们强烈建议不要在程序中使用char类型,除非确实需要处理UTF-16代码单元。最 好将字符串作为抽象数据类型处理。

3.3.5 boolean类型

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

在Java中,每个变量都有一个类型(type)。在声明变量时,变量的类型位于变量名之前。

double salary;

int vacationDays;

long earthPopulation;

boolean done;

变量名必须是一个以字母开头并由字母或数字构成的序列。需要注意,与大多数程 序设计语言相比,Java中“字母”和“数字”的范围更大。字母包 括'A'~'Z'、'a'~'z'、'_'、'$'或在某种语言中表示字母的任何Unicode字符。

3.4 变量

3.4.1 变量初始化

声明一个变量之后,必须用赋值语句对变量进行显式初始化,千万不要使用未初始化的变量。

int vacationDays = 12;

在Java中,变量的声明尽可能地靠近变量第一次使用的地方,这是一种良好的程序 编写风格。

在Java中,不区分变量的声明与定义。

3.4.1 常量

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

public class Constants {
    public static void main(String[] args) {
        final double CM_PER_INCH = 2.54;
        double paperWidth = 8.5;
        double paperHeight = 11;
        System.out.println("Paper size in centimeters: "
                + paperWidth * CM_PER_INCH + " by " + paperHeight * CM_PER_INCH);
    }
}

关键字final表示这个变量只能被赋值一次。一旦被赋值之后,就不能够再更改了。 习惯上,常量名使用全大写。

在Java中,经常希望某个常量可以在一个类中的多个方法中使用,通常将这些常量称为类常量。可以使用关键字static final设置一个类常量。

public class Constant2 {
    public static final double CM_PER_INCH = 2.54;

    public static void main(String[] args) {
        double paperWidth = 8.5;
        double paperHeight = 11;
        System.out.println("Paper size in centimeters: "
        + paperWidth * CM_PER_INCH + " by " + paperHeight * CM_PER_INCH);
    }
}

类常量的定义位于main方法的外部。因此,在同一个类的其他方法中也 可以使用这个常量。而且,如果一个常量被声明为public,那么其他类的方法也可 以使用这个常量。

3.5 运算符

3.5.1 数学函数与常量

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

double x = 4;
double y = Math.sqrt(x);
System.out.println(y); // print 2.0

在Math类中,为了达到最快的性能,所有的方法都使用计算机浮点单元 中的例程。如果得到一个完全可预测的结果比运行速度更重要的话,那么就应该使 用StrictMath类。它使用“自由发布的Math库”(fdlibm)实现算法,以确保在 所有平台上得到相同的结果。

3.5.2 数值类型之间的转换

经常需要将一种数值类型转换为另一种数值类型。

3.5.3 强制类型转换

在必要的时候,int类型的值将会自动地转换为double类型。

有时也需要将double转换成int。在Java中,允许进行这种数值之间 的类型转换。当然,有可能会丢失一些信息。在这种情况下,需要通过强制类型转 换(cast)实现这个操作。强制类型转换的语法格式是在圆括号中给出想要转换的 目标类型,后面紧跟待转换的变量名。

public class TransformType {

    public static void main(String[] args) {

        // int tranform to float

        int n = 123456789;
        System.out.println(n);
        float f = n;
        System.out.println(f);

        System.out.println("-----------------");

        // 将 double 类型转换为 int   强制类型转换通过截断小数部分将浮点值转换为整型。

        double x = 9.997;
        System.out.println(x);

        int nx = (int) x;
        System.out.println(nx);

        System.out.println("-----------------");
        // 如果想对浮点数进行舍入运算,以便得到最近的整数,那就需要使用Math.round方法。

        double X = 9.997;
        System.out.println(X);
        int nX = (int) Math.round(x);
        System.out.println(nX);
    }
}

3.5.4 结合赋值和运算符

x += 4;

// 等价于

x = x + 4;

3.5.5 自增与自减运算符

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

3.5.6 关系和boolean运算符

Java包含丰富的关系运算符,要检测相等性,可以使用两个等号==

3.5.7 位运算符

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

3.5.8 括号与运算符级别

3.5.9 枚举类型

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

3.6 字符串

3.6.1 子串

String类的substring方法可以从一个较大的字符串提取出一个子串。

public class ByteString {
    public static void main(String[] args) {
        //String类的substring方法可以从一个较大的字符串提取出一个子串
        // 在substring中从0开始计数,直到3为止,但不包含3。
        String greeting = "Hello";
        String s = greeting.substring(0,3);

        System.out.println(s);

    }
}

3.6.2 拼接

Java语言允许使用+号连接(拼接)两个字符串。

import java.sql.SQLOutput;

public class ByteString {
    public static void main(String[] args) {
        //String类的substring方法可以从一个较大的字符串提取出一个子串
        // 在substring中从0开始计数,直到3为止,但不包含3。
        String greeting = "Hello";
        String s = greeting.substring(0,3);
        System.out.println("1.截取:");
        System.out.println(s);

        // 拼接

        String expletive = "Expletive";
        String PG13 = "deleted";
        String message = expletive + PG13;
        System.out.println("2.拼接:");
        System.out.println(message);

        System.out.println("---------------");
        int age = 13;
        String rating = "PG" + age;
        System.out.println(" This answer is " + rating);

        System.out.println("--------------------");
        //如果需要把多个字符串放在一起,用一个定界符分隔,可以使用静态join方法

        String all = String.join(" / ", "S","M","L","XL");
        System.out.println(all);

    }
}

3.6.3 不可变字符串

String类没有提供用于修改字符串的方法。

在Java中实现这项操作非常容易。首先提取需要的字符,然后再拼接上替换的 字符串:

greeting = greeting.substring(0,3) + "p!";

总而言之,Java的设计者认为共享带来的高效率远远胜过于提取、拼接字符串所带 来的低效率。查看一下程序会发现:很少需要修改字符串,而是往往需要对字符串 进行比较

3.6.4 检测字符串是否相等

可以使用equals方法检测两个字符串是否相等。

s.equals(t)

如果字符串s与字符串t相等,则返回true;否则,返回false。需要注意,s与t可以是字符串变量,也可以是字符串字面量。

"Hello".equals(greeting)

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

"Hello".equalsIgnoreCase("hello")

一定不要使用==运算符检测两个字符串是否相等!这个运算符只能够确定两个字符串是否放置在同一个位置上。

String greeting = "Hello"; // initialize greeting to a string
if (greeting == "Hello")...
// probably true
if (greeting.substring(0,3) == "Hel")...
// probably false

3.6.5 空串 与 Null 串

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

if(str.length() == 0)

or

if(str.equals(""))

空串是一个Java对象,有自己的串长度(0)和内容(空)。不过,String变量还 可以存放一个特殊的值,名为null,这表示目前没有任何对象与该变量关联

3.6.6 码点与代码单元

Java字符串由char值序列组成。

char数据类型是一个采用UTF-16编码表示Unicode码点的代码单元。大多数的常用Unicode字符 使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示。

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

String greeting = "Hello";
int n = greeting.length(); // is 5

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

int cpCount = greeting.codePointCount(0,greeting.length());
// length方法将返回采用UTF-16编码表示的给定字符串所需要的代码单元数量。

        String greeting1 = "Hello";
        int n = greeting1.length(); // is 5
        System.out.println("The length of greeting1 " + n);

        //  要想得到实际的长度,即码点数量,可以调用
        int cpCount = greeting1.codePointCount(0,greeting1.length());
        System.out.println("-----------------------------");
        System.out.println("码点数量 " + cpCount);

        // 调用s.charAt(n)将返回位置n的代码单元,n介于0~s.length()-1之间。
        System.out.println("-----------------------------");
        char first = greeting1.charAt(0); // first is "H"
        System.out.println(first);
        char last = greeting1.charAt(4);  // last is 'o'

        // 要想得到第i个码点,应该使用下列语句
        System.out.println("------------------------");
        int index = greeting1.offsetByCodePoints(0, 3);
        int cp = greeting1.codePointAt(index);

        System.out.println("第3个码点为 : " + cp);

3.6.7 String API

Java中的String类包含了50多个方法。令人惊讶的是绝大多数都很有用,可以设想 使用的频繁非常高。

API java.lang.string 1.0

返回给定位置的代码单元。除非对底层的代码单元感兴趣,否则不需要调用这个方 法。 返回从给定位置开始的码点。 返回从startIndex代码点开始,位移cpCount后的码点索引。 按照字典顺序,如果字符串位于other之前,返回一个负数;如果字符串位于other 之后,返回一个正数;如果两个字符串相等,返回0。 将这个字符串的码点作为一个流返回。调用toArray将它们放在一个数组中。 用数组中从offset开始的count个码点构造一个字符串。

如果字符串与other相等,返回true。 如果字符串与other相等(忽略大小写),返回true。 如果字符串以suffix开头或结尾,则返回true。 返回与字符串str或代码点cp匹配的第一个子串的开始位置。这个位置从索引0或 fromIndex开始计算。

返回与字符串str或代码点cp匹配的最后一个子串的开始位置。这个位置从原始串尾 端或fromIndex开始计算。 返回字符串的长度。 返回startIndex和endIndex-1之间的代码点数量。没有配成对的代用字符将计入 代码点。 返回一个新字符串。这个字符串用newString代替原始字符串中所有的 oldString。可以用String或StringBuilder对象作为CharSequence参数。 返回一个新字符串。这个字符串包含原始字符串中从beginIndex到串尾或 endIndex–1的所有代码单元。 返回一个新字符串。这个字符串将原始字符串中的大写字母改为小写,或者将原始 字符串中的所有小写字母改成了大写字母。 返回一个新字符串。这个字符串将删除了原始字符串头部和尾部的空格。 返回一个新字符串,用给定的定界符连接所有元素。

3.6.8 阅读联机API文档

Overview (Java Platform SE 8 ) (oracle.com)

屏幕被分成三个窗框。在左上方的小窗框中显示了可使用的所有包。在它下面稍大的窗框中列出了所有的类。点击任何一个类名之后,这个类的API文档就会显示在右侧的大窗框中

3.6.9 构建字符串

每次连接字符串,都会构建一个新的String对象,既耗时,又浪费空间。使用StringBuilder类就可以避免这个问题的发生。

// first of all, 构建一个空的字符串构建器
StringBuilder builder = new StringBuilder();

// 当每次需要添加一部分内容时,就调用append方法。
builder.append(ch); // append a single character
builder.append(str); // append a string

// 在需要构建字符串时就调用toString方法,将可以得到一个String对象,其中包含了构建器中的字符序列。
String completedString = builder.toString();

API java.lang.StringBuilder 5.0

构造一个空的字符串构建器。 返回构建器或缓冲器中的代码单元数量。 追加一个字符串并返回this。 追加一个代码单元并返回this。 追加一个代码点,并将其转换为一个或两个代码单元并返回this.

将第i个代码单元设置为c。 在offset位置插入一个字符串并返回this。 在offset位置插入一个代码单元并返回this。 删除偏移量从startIndex到-endIndex-1的代码单元并返回this。 返回一个与构建器或缓冲器内容相同的字符串。

3.7 输入输出

3.7.1 读取输入

打印输出到“标准输出流”(即控制台窗口)是一件非常容易的事情,只要调用System.out.println即可。

要想通过控制台进行输入,首先需要构造一个Scanner对象,并与“标准输入流”System.in关联。

// 现在,就可以使用Scanner类的各种方法实现输入操作了。例如,nextLine方法将输入一行。
Scanner in = new Scanner(System.in);

// 使用nextLine方法是因为在输入行中有可能包含空格。要想读取一个单词(以空白符作为分隔符)
System.out.print("What is your name? ")
String name = in.nextLine();

// 要想读取一个整数,就调用nextInt方法。
System.out.print("How old are you? ");
int age = in.nextInt();

//要想读取下一个浮点数,就调用nextDouble方法。

程序清单3-2 InputTest/InputTest.java

public class InputTest {

    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);

        // get first input
        System.out.println("What is your name? ");
        String name = in.nextLine();

        // get second input
        System.out.println("How old are you? ");
        int age = in.nextInt();

        // display output on console
        System.out.println("Hello, " + name + ". Next year, you'll be " + (age + 1));
    }
}

API java.util.Scanner 5.0

用给定的输入流创建一个Scanner对象。 读取输入的下一行内容。 读取输入的下一个单词(以空格作为分隔符)。 读取并转换下一个表示整数或浮点数的字符序列。 检测输入中是否还有其他单词。 检测是否还有表示整数或浮点数的下一个字符序列。

API java.lang.System 1.0

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

API java.io.Console 6

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

3.7.2 格式化输出

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

public class FormatOutput {
    public static void main(String[] args) {
        //使用System.out.print(x)将数值x输出到控制台上。

        double x = 10000.0 / 3.0;
        System.out.print(x);

        System.out.println();

        System.out.println("-----------------------");
        //如果希望显示美元、美分等符号,则有可能会出现问题。

        System.out.printf("%8.2f",x);

        System.out.println();

        System.out.println("-------------------");
        // ,还可以给出控制格式化输出的各种标志。逗号标志增加了分组的分隔符。

        System.out.printf("%,.2f", 10000.0/ 3.0);

    }
}

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

表3-5 用于printf的转换符

表3-6 用于printf的标志

import java.util.Date;

public class FormatOutput {
    public static void main(String[] args) {
        //使用System.out.print(x)将数值x输出到控制台上。

        double x = 10000.0 / 3.0;
        System.out.print(x);

        System.out.println();

        System.out.println("-----------------------");
        //如果希望显示美元、美分等符号,则有可能会出现问题。

        System.out.printf("%8.2f",x);

        System.out.println();

        System.out.println("-------------------");
        // ,还可以给出控制格式化输出的各种标志。逗号标志增加了分组的分隔符。

        System.out.printf("%,.2f", 10000.0/ 3.0);

        System.out.println();
        System.out.println("-----------------------");

        //可以使用静态的String.format方法创建一个格式化的字符串,而不打印输出:
        String name = "Maxwell";
        int age = 30;
        String message = String.format("Hello, %s. Next year, you'll be %d", name, age);
        System.out.println(message);

        System.out.println("-----------------------------");
        // c 转换符 完整的日期和时间
        System.out.printf("%tc", new Date());

        System.out.println();
        System.out.println("-----------------------");
        // 如果需要多次对日期操作才能实现对每一部分进行格式化的目的 ,可以采用一个格式化的字符串指出要被格式化的参数索引。索引必须紧跟在%后面,并以$终止。

        System.out.printf("%1$s %2$tB %2$te, %2$tY","Due date", new Date());

        System.out.println();
        System.out.println("----------------------");

        //还可以选择使用<标志。它指示前面格式说明中的参数将被再次使用。也就是说,下列语句将产生与前面语句同样的输出结果:

        System.out.printf("%s %tB %<te, %<tY", "Due date", new Date());
    }
}

提示:参数索引值从1开始,而不是从0开始,%1$...对第1个参数格式化。这就避免了与0标志混淆。

3.7.3 文件输入与输出

java.util.Scanner 5.0 构造一个从给定文件读取数据的Scanner。 构造一个从给定字符串读取数据的Scanner。

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

java.nio.file.Paths 7 根据给定的路径名构造一个Path。

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Paths;
import java.util.Scanner;

public class FileInputOutput {

    public static void main(String[] args) throws IOException {
        // 要想对文件进行读取, 就需要一个用File对象构造一个Scanner对象,
        Scanner in = new Scanner(Paths.get("E:\\\\2023\\\\SourceCode\\\\JavaSourceCode\\\\JavaCore\\\\Chapter3ObjectsAndClass\\\\FileInputOutput\\\\myfile.txt"),"UTF-8");
        String myFileText = in.nextLine();
        System.out.println(myFileText);
        // 如果文件名中包含反斜杠符号,就要记住在每个反斜杠之前再加一个额外的反斜杠:“c:\\\\mydirectory\\\\myfile.txt”。

        System.out.println();
        System.out.println("-------------------------");
        // 想要写入文件,就需要构造一个PrintWriter对象。在构造器中,只需要提供文件名

        PrintWriter out = new PrintWriter("E:\\\\2023\\\\SourceCode\\\\JavaSourceCode\\\\JavaCore\\\\Chapter3ObjectsAndClass\\\\FileInputOutput\\\\myfile.txt","UTF-8");
        // 如果文件不存在,创建该文件。可以像输出到System.out一样使用print、println以及printf命令。

    }
}

3.8 控制流程

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

3.8.1 块作用域

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

3.8.2 条件语句

// 条件语句的格式
if(condition) statement

if(condition) statement1 else statement2

if else if 

3.8.3 循环

while(condition) statement

import java.util.Scanner;

/**
 * This program demonstrates a while loop
 * @version 1.20 2004-02-10
 * @author Maxwell Pan
 */

public class Retirement {
    public static void main(String[] args) {

        // read inputs
        Scanner in = new Scanner(System.in);

        System.out.print("How much money do you need to retire? ");
        double goal = in.nextDouble();

        System.out.print("How much money will you contribute every year? ");
        double payment = in.nextDouble();

        System.out.print("Interest rate in %: ");
        double interestRate = in.nextDouble();

        double balance = 0;
        int years = 0;

        // update account balance while goal isn't reached
        while (balance < goal){
            // add this year's payment and interest
            balance += payment;
            double interest = balance * interestRate / 100;
            balance += interest;
            years++;
        }

        System.out.println("You can retire in " + years + " years.");

    }
}

循环体中的代码有可能不被执行。如果希 望循环体至少执行一次,则应该将检测条件放在最后。使用do/while循环语句可以 实现这种操作方式。它的语法格式为:

do statement while (condition)

这种循环语句先执行语句(通常是一个语句块),再检测循环条件;然后重复语 句,再检测循环条件,以此类推。

import java.util.Scanner;

/**
 * This program demonstrates a do/while loop.
 * @version 1.20 2004-02-10
 * @author Cay Horstmann
 */

public class Retirement2 {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        System.out.print("How much money will you contribute every year? ");
        double payment = in.nextDouble();

        System.out.print("Interest rate in %: ");
        double interestRate = in.nextDouble();

        double balance = 0;
        int year = 0;

        String input;

        // update account balance while user isn't ready to retire
        do {
            // add this year's payment and interest
            balance += payment;
            double interest = balance * interestRate / 100;
            balance += interest;

            year++;

            //print current balance
            System.out.printf("After year %d, your balance is %,.2f%n", year,balance);

            // ask if ready to retire and get input
            System.out.print("Reay to retire?(Y/N) ");
            input = in.next();
        }
        while (input.equals("N"));
    }
}

3.8.4 确定循环

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

for(int i = 1; i <= 10; i++)
   System.out.println(i);

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

import java.util.Scanner;

/**
 * This program demonstrates a for loop.
 * @version 1.20 2004-02-10
 * @author Cay Horstmann
 */

public class LotteryOdds {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.print("How many numbers do you need to draw? ");
        int k = in.nextInt();

        System.out.print("What is the highest number you can draw? ");
        int n = in.nextInt();

        /**
         *  compute binomial coefficient n*(n-1)*(n-2)*...*(n-k+1)/(1*2*3*...*k)
         */
        int lotteryOdds = 1;
        for (int i = 1; i <= k; i++) {
            lotteryOdds = lotteryOdds * (n - i + 1) / i;
        }
        System.out.println("Your odds are 1 in " + lotteryOdds + ". Good luck!");
    }
}

3.8.5 多重选择: switch语句

在处理多个选项时,使用if/else结构显得有些笨拙。Java有一个与C/C++完全一样 的switch语句。

警告:有可能触发多个case分支。如果在case分支语句的末尾没有break语 句,那么就会接着执行下一个case分支语句。这种情况相当危险,常常会引发错 误。为此,我们在程序中从不使用switch语句。

3.8.6 中断控制流程语句

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

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

请注意,标签必须放在希望跳出的最外层循环之前,并且必须紧跟一个冒号。

3.9 大数值

如果基本的整数和浮点数精度不能够满足需求,那么可以使用java.math包中的两 个很有用的类:BigInteger和BigDecimal。这两个类可以处理包含任意长度数字 序列的数值。BigInteger类实现了任意精度的整数运算,BigDecimal实现了任意 精度的浮点数运算。

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

BigInteger a = BigInteger.valueOf(10);

遗憾的是,不能使用人们熟悉的算术运算符(如:+和*)处理大数值。而需要使用大数值类中的add和multiply方法。

BigInteger c = a.add(b); // c = a + b
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // d = c * (b + 2)

程序清单3-6 BigIntegerTest/BigIntegerTest.java

import java.math.BigInteger;
import java.util.Scanner;

/**
 * This program uses big numbers to compute the odds of winning the grand prize in a lottery.
 * @version 1.21 2023-05-11
 * @author Maxwell Pan
 */

public class BigIntegerTest {

    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);
        System.out.print("How many numbers do you need to draw? ");
        int k = in.nextInt();

        System.out.print("What is the highest number you can draw? ");
        int n = in.nextInt();

        /**
         *  compute binomial coefficient n*(n-1)*(n-2)*...*(n-k+1)/(1*2*3*...*k)
         */

        BigInteger lotteryOdds = BigInteger.valueOf(1);
        for (int i = 1; i <= k; i++) {
            lotteryOdds = lotteryOdds.multiply(BigInteger.valueOf(n - i + 1)).divide(BigInteger.valueOf(i));
        }
        System.out.println("Your odds are 1 in " + lotteryOdds + ". Good luck!");

    }
}

API java.math.BigInteger 1.1 返回这个大整数和另一个大整数other的和、差、积、商以及余数。 如果这个大整数与另一个大整数other相等,返回0;如果这个大整数小于另一个大 整数other,返回负数;否则,返回正数。 返回值等于x的大整数。

java.math.BigInteger 1.1 返回这个大实数与另一个大实数other的和、差、积、商。要想计算商,必须给出舍 入方式(rounding mode)。RoundingMode.HALF_UP是在学校中学习的四舍五入 方式(即,数值0到4舍去,数值5到9进位)。它适用于常规的计算。有关其他的舍 入方式请参看API文档。 如果这个大实数与另一个大实数相等,返回0;如果这个大实数小于另一个大实数, 返回负数;否则,返回正数。 返回值为x或x/10scale的一个大实数。

3.10 数组

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

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

int[] a;

这条语句创建了一个可以存储100个整数的数组。数组长度不要求是常量:new int[n]会创建一个长度为n的数组。

int[] a = new int[100];

注释:可以使用下面两种形式声明数组

int[] a;

// or

int a[];

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

这个数组的下标从0~99(不是1~100)。一旦创建了数组,就可以给数组元素赋 值。例如,使用一个循环:

				int[] a = new int[100];
        for (int i = 0; i < 100; i++) {
            a[i] = i; // fills the array with numbers 0 to 99
        }

创建一个数字数组时,所有元素都初始化为0。boolean数组的元素会初始化为 false。对象数组的元素则初始化为一个特殊值null,这表示这些元素(还)未存 放任何对象。初学者对此可能有些不解。例如,

String[] names = new String[10];

会创建一个包含10个字符串的数组,所有字符串都为null。如果希望这个数组包含 空串,可以为元素指定空串:

for(int i = 0; i < 10; i++) names[i] = "";

警告:如果创建了一个100个元素的数组,并且试图访问元素a[100](或任何 在0~99之外的下标),程序就会引发“array index out of bounds”异常而终 止执行。

要想获得数组中的元素个数,可以使用array.length。例如,

for(int i = 0; i < a.length; i++){
	System.out.println(a[i]);
}

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

3.10.1 for each 循环

Java有一种功能很强的循环结构,可以用来依次处理数组中的每个元素(其他类型的元素集合亦可)而不必为指定下标值而分心。

这种增强的for循环的语句格式为:

for(variable: collection) statement

定义一个变量用于暂存集合中的每一个元素,并执行相应的语句(当然,也可以是 语句块)。collection这一集合表达式必须是一个数组或者是一个实现了 Iterable接口的类对象(例如ArrayList)。

for(int element : a)
  System.out.println(element);

打印数组a的每一个元素,一个元素占一行。

循环a中的每一个元素”(for each element in a)

使用传统的for循环也可以获得同样的效果:

for(int i = 0; i < a.length; i++)
  System.out.println(a[i]);

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

在Java中,提供了一种创建数组对象并同时赋予初始值的简化书写形式。

int[] smallPrimes = {2, 3, 5, 7, 11, 13}

请注意,在使用这种语句时,不需要调用new。

甚至还可以初始化一个匿名的数组:

new int[] {17, 19, 23, 29, 31, 37}

这种表示法将创建一个新数组并利用括号中提供的值进行初始化,数组的大小就是 初始值的个数。使用这种语法形式可以在不创建新变量的情况下重新初始化一个数 组。

注释:在Java中,允许数组长度为0。在编写一个结果为数组的方法时,如果碰 巧结果为空,则这种语法形式就显得非常有用。此时可以创建一个长度为0的数组:

new elementType[0]

注意,数组长度为0与null不同。

3.10.3 数组拷贝

在Java中,允许将一个数组变量拷贝给另一个数组变量。这时,两个变量将引用同 一个数组:

int[] luckyNumbers = smallPrimes;
luckyNumbers[s] = 12; //now smallPrimes[5] is also 12

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

3.10.4 命令行参数

public class Message {
    public static void main(String[] args) {
        if (args.length == 0 || args[0].equals("-h")){
            System.out.print("Hello.");
        } else if (args[0].equals("-g")) {
            System.out.print("Goodbye,");
            // print the other command-line arguments
            for (int i = 1; i < args.length; i++) {
                System.out.print(" " + args[i]);
            }
            System.out.println("!");
        }
    }
}

这个程序将显示下列信息:

E:\\2023\\SourceCode\\JavaSourceCode\\JavaCore\\Chapter3ObjectsAndClass\\Message\\src>java Message.java -g cruel world
Goodbye, cruel world!

E:\\2023\\SourceCode\\JavaSourceCode\\JavaCore\\Chapter3ObjectsAndClass\\Message\\src>

3.10.5 数组排序

import java.util.Arrays;
import java.util.Scanner;

/**
 * This program demonstrate array manipulation
 * @version 1.20 2023-05-11
 * @author Maxwell Pan
 */

public class LotteryDrawing {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        System.out.print("How many numbers do you need to draw? ");
        int k = in.nextInt();

        System.out.println("What is the hightest number you can draw? ");
        int n = in.nextInt();

        // fill an array with numbers 1 2 3 ... n
        int[] numbers = new int[n];
        for (int i = 0; i < numbers.length; i++) {
            numbers[i] = i + 1;
        }

        // draw k numbers and put them into a second array
        int[] result = new int[k];
        for (int i = 0; i < result.length; i++) {
            // make a random index between 0 and n-1
            int r = (int)(Math.random() * n);

            // pick the element at the random location
            result[i] = numbers[r];

            // move the last element into the random location
            numbers[r] = numbers[n - 1];
            n--;
        }

        // print the sort array
        Arrays.sort(result);
        System.out.println("Bet the following combination. It'll make you rich!");
        for (int r: result) {
            System.out.println(r);
        }
    }

}

3.10.6 多维数组

多维数组将使用多个下标访问数组元素,它适用于表示表格或更加复杂的排列形 式。

可以使用一个二维数组(也称为矩阵)存储这些信息。这个数组被命名为 balances。

double[][] balances;
public class CompoundInterest {
    public static void main(String[] args) {
        final double STARTRATE = 10;
        final int NRATES = 6;
        final int NYEARS = 10;

        // set interest rates to 10 ... 15%
        double[] interestRate = new double[NRATES];
        for (int j = 0; j < interestRate.length; j++){
            interestRate[j] = (STARTRATE + j) / 100.0;
        }

        double[][] balances = new double[NYEARS][NRATES];

        // set initial balances to 10000
        for (int j = 0; j < balances[0].length; j++) {
            balances[0][j] = 10000;
        }

        // compute interest for future years
        for (int i = 1; i < balances.length; i++) {
            for (int j = 0; j < balances[i].length; j++) {

                // get last year's balance from previous row
                double oldBalance = balances[i-1][j];

                // compute interest
                double interest = oldBalance * interestRate[j];

                // compute this year's balance
                balances[i][j] = oldBalance + interest;

            }
        }

        // print one row of interest rates
        for (int j = 0; j < interestRate.length; j++) {
            System.out.printf("%9.0f%%", 100 * interestRate[j]);
        }
        System.out.println();
        // print balance table
        for (double[] row: balances) {
            // print table row
            for (double b: row) {
                System.out.printf("%10.2f", b);
            }
            System.out.println();
        }

    }
}

3.10.7 不规则数组

/**
 * p3.9
 * This is program demonstrate a triangular array
 * @version 1.21 2023-05-12
 * @author Maxwell Pan
 */

public class LotteryArray {
    public static void main(String[] args) {

        final int NMAX = 10;

        // allocate triangular array
        int[][] odds = new int[NMAX+1][];
        for (int n = 0; n <= NMAX; n++) {
            odds[n] = new int[n + 1];
        }

        // fill triangular array
        for (int n = 0; n < odds.length; n++) {
            for (int k = 0; k < odds[n].length; k++) {
                /**
                 * compute binomial coefficient n*(n-1)*(n-2)*...*(n-k+1)/(1*2*3*...*k)
                 */
                int lotteryOdds = 1;
                for (int i = 1; i <= k; i++) {
                    lotteryOdds = lotteryOdds * (n - i + 1) / i;
                }
                odds[n][k] = lotteryOdds;
            }
            // print trangular array
            for (int[] row : odds) {
                for (int odd: row) {
                    System.out.printf("%4d",odd);
                }
                System.out.println();
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值