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

news/2024/7/4 8:03:08 标签: java, jvm, servlet

第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();
            }
        }
    }
}


http://www.niftyadmin.cn/n/349381.html

相关文章

chatgpt解析职场

在竞争激烈的职场上,各种职场难题时常出现,如何进行有效沟通、如何应对工作压力、如何提升职业能力等,这都是需要去克服的问题。一起来分享一下你的经验吧! 一、你遇到过哪些职场问题?分享一下你是怎么解决的呢?(人际沟通、工作压力、晋升机会、职业规划等类型问题) 作…

【git规范--husky的使用】

git提交规范 一、 前言 在团队开发中&#xff0c;或者自己的项目中&#xff0c;我们都会用到git来管理我们的代码&#xff0c;但是当我们commit(git commit)的时候&#xff0c;是没有规范的&#xff0c;有的时我们偷懒甚至git commit -m..&#xff0c;这种提交虽然当时爽&…

《程序员面试金典(第6版)》面试题 02.06. 回文链表(双指针(快慢指针),查找链表中间节点,反转链表)

题目描述 编写一个函数&#xff0c;检查输入的链表是否是回文的。 题目传送门~&#xff1a;面试题 02.06. 回文链表 示例 1&#xff1a; 输入&#xff1a; 1->2 输出&#xff1a; false 示例 2&#xff1a; 输入&#xff1a; 1->2->2->1 输出&#xff1a; true 进…

【MySQL】-【数据库的设计规范】

文章目录 为什么需要数据库设计范式范式简介范式都包括哪些键和相关属性的概念第一范式(1st NF)第二范式(2nd NF)第三范式(3rd NF) 反范式化概述应用举例反范式化的新问题反范式的适用场景 BCNF(巴斯范式)案例 为什么需要数据库设计 范式 范式简介 在关系型数据库中&#xff…

v-model使用及原理

关于v-model&#xff0c;vue2与vue3用法不一致&#xff0c;本文学习采用了vue3官网文档。与vue2区别写在本文末尾。一、为什么使用v-model&#xff1f; v-model指令可以在表单input、textarea以及select元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素…

可视化翻转教学python

目录 第1关 绘制折线图 第2关 绘制正弦曲线 第3关 绘制指定线型、颜色和标记的正弦曲线 第4关 定义绘制正余弦函数曲线的函数 第5关 绘制坐标轴并设置范围 第1关 绘制折线图 显示绘制结果 plt.show()&#xff1a;用于显示绘制的结果&#xff0c;无参数&#xff0c;执行此…

mysql子查询嵌套

目录 前言 一、实际需求解决 1.方式1&#xff1a;自连接 2.方式2&#xff1a;子查询 二、单行子查询 1.操作符子查询 三、相关子查询 四、自定义语句 五、子查询的问题 1.空值问题 2.非法使用子查询 六、多行子查询 七、聚合函数的嵌套使用 八、多行子查询空值问题…

LeetCode高频算法刷题记录8

文章目录 1. 零钱兑换【中等】1.1 题目描述1.2 解题思路1.3 代码实现 2. 最小栈【最小栈】2.1 题目描述2.2 解题思路2.3 代码实现 3. 最长有效括号【困难】3.1 题目描述3.2 解题思路3.3 代码实现 4. 从前序与中序遍历序列构造二叉树【中等】4.1 题目描述4.2 解题思路4.3 代码实…