4.1 引言
- 本章重点介绍数学函数、字符和字符串对象,并使用它们来开发程序
- 前面章节介绍了基础编程技术,以及如何使用选择语句编写简单的程序来解决一些基本问题。本章介绍实现常用数学操作的方法。你将在第6章学到如何创建自定义方法
- 假设你需要估算被四座城市包围的区域的面积,给定这些城市的GPS位置(经纬度),如下图所示。如何编写程序来求解这个问题?在学完本章后,你将可以编写这样的程序。
- 因为字符串在编程中经常要用到,所以尽早介绍字符串从而开始使用它们来编写使用的程序是有裨益的。本章还给出了字符串对象的简要介绍,你将在第9章和第10章进一步学习对象和字符串的相关知识。
4.2 常用数学函数
- Java在Math类中提供了许多实用的方法,来计算常用的数学函数。
- 方法是一组语句,用于执行一个特定的任务。在2.9.4节中我们已经使用过方法pow(a,b)来计算ab ,在3.7节中也使用过random()方法来产生一个随机数。本节介绍Math类中其他的有用方法。这些方法分为三类:
- 三角函数方法(trigonometric method)
- 指数函数方法(exponent method)
- 服务方法(service method)
- 除了这些方法之外,Math类还提供了两个很有用的double型常量,PI( π )和E(自然对数的底)。可以在任意程序中用Math.PI和Math.E来使用这两个常量
4.2.1 三角函数方法
方法 |
描述 |
sin(radians) |
返回以弧度为单位的角度的三角正弦函数值 |
cos(radians) |
返回以弧度为单位的角度的三角余弦函数值 |
tan(radians) |
返回以弧度为单位的角度的三角正切函数值 |
toRadians(degree) |
将以度为单位的角度值转换为以弧度表示 |
toDegrees(radians) |
将以弧度为单位的角度值转换为以度表示 |
asin(a) |
返回以弧度为单位的角度的反三角正弦函数值 |
acos(a) |
返回以弧度为单位的角度的反三角余弦函数值 |
atan(a) |
返回以弧度为单位的角度的反三角正切函数值 |
- sin、cos和tan的参数都是以弧度为单位的角度。asin和atan的返回值是 -π/2 ~ π/2的一个弧度制,acos的返回值在0到π之间。1°相当于π/180弧度,90°相当于π/2弧度,而30°相当于π/6弧度
4.2.2 指数函数方法
- Math类中有5个与指数函数有关的方法,如表4-2所示
方法 |
描述 |
exp(x) |
返回e的x次方(ex) |
log(x) |
返回x的自然对数(lnx = loge x) |
log10(x) |
返回x的以10为底的对数(log10 x) |
pow(a,b) |
返回a的b次方(ab) |
sqrt(x) |
对于x ≥ 0的数字,返回x的平方根() |
4.2.3 取整方法
方法 |
描述 |
ceil(x) |
x向上取整为它最接近的整数。该整数作为一个双精度值返回 |
floor(x) |
x向下取整为它最接近的整数。该整数作为一个双精度值返回 |
rint(x) |
x取整为它最接近的整数。如果x与两个整数的距离相等,偶数的整数作为一个双精度值返回 |
round(x) |
如果x是单精度数,返回(int)Math.floor(x+0.5);如果x是双精度数,返回(long)Math.floor(x+0.5) |
4.2.4 min、max和abs方法
4.2.5 random方法
4.2.6 示例学习:计算三角形的角度
A = acos((a * a - b * b - c * c) / (-2 * b * c))
B = acos((b * b - a * a - c * c) / (-2 * a * c))
C = acos((c * c - b * b - a * a) / (-2 * a * b))
程序清单4-1是一个示例程序,提示用户输入三角形三个顶点的x和y坐标值,然后三个角。
程序清单 4-1 ComputeAngles.java
import java.util.Scanner;
public class ComputeAngles {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
//Prompt the user to enter three points
System.out.print("Enter three points: ");
double x1 = input.nextDouble();double y1 = input.nextDouble();
double x2 = input.nextDouble();double y2 = input.nextDouble();
double x3 = input.nextDouble();double y3 = input.nextDouble();
//Compute three sides
double a = Math.sqrt(Math.pow(x2 - x3, 2) + Math.pow(y2 - y3, 2));
double b = Math.sqrt(Math.pow(x3 - x1, 2) + Math.pow(y3 - y1, 2));
double c = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
//Compute three angle
double A = Math.toDegrees(Math.acos((a * a - b * b - c * c) / (-2 * b * c)));
double B = Math.toDegrees(Math.acos((b * b - a * a - c * c) / (-2 * a * c)));
double C = Math.toDegrees(Math.acos((c * c - b * b - a * a) / (-2 * a * b)));
//Display results
System.out.println("The three angles are: " +
Math.round(A * 100) / 100.0 + " " +
Math.round(B * 100) / 100.0 + " " +
Math.round(C * 100) / 100.0);
}
}
- 程序提示用户输入三个顶点(第8行)。这个提示消息并不是很清晰,应该给予用户非常清晰的提示,如下所示:
System.out.print("Enter the coordinates of three points separated" + "by spaces like x1 y1 x2 y2 x3 y3: ");
- 注意,两个点(x1,y1),(x2,y2)之间的距离可以通过公式%5E2%20%2B%20(y_2%20-%20y_1)%5E2)#card=math&code=%5Csqrt%28x_2%20-%20x_1%29%5E2%20%2B%20%28y_2%20-%20y_1%29%5E2%29&id=liBNM)计算。程序计算两个点之间的距离,并且应用该公式来计算角度。的角度值保留小数点后两位数字。
- Math类在程序中使用,但是并没有导入,因为它在java.lang包中。在一个Java程序中,java.lang包中的所有类是
隐式
导入的。
4.3 字符数据类型和操作
- 字符数据类型用于表示单个字符
除了处理数值之外,Java还可以处理字符。字符数据类型char用于表示单个字符。字符型字面值用单引号括住。考虑以下代码:
char letter = 'A';
char numChar = '4';
第一条语句将字符A赋值给char型变量letter。第二条语句将数字字符4赋值给char型变量numChar
字符串字面值必须括在双引号中。而字符字面值是括在单引号中的单个字符。因此,”A”是一个字符串,而’A’是一个字符。
4.3.1 Unicode 和 ASCII码
- 计算机内部使用二进制数。一个字符在计算机中是以0和1构成的序列的形式来存储的。将字符映射到它的二进制形式的过程称为
编码
(encoding)。字符有多种不同的编程方式,编码表
(encoding scheme)定义该如何编码每个字符。 - Java支持Unicode码,Unicode码是由Unicode协会建立的一种编码方案,它支持使用世界各种语言所书写的文本的交换、处理和显示。Unicode一开始被设计为16位的字符编码。基本数据类型char试图通过提供一种能够存放任意字符的简单数据类型来利用这个设计。但是。一个16位的编码所能产生的字符只有65536个,它是不足以表示全世界所有字符的。因此Unicode标准被扩展位1 112 064个字符。这些字符都远远超过了原来16位的限制,它们称为补充字符(supplementary character)。Java支持这些补充字符。对补充字符的处理和表示介绍超过了本书的范围。为了简化问题,本书只考虑原来的16位Unicode字符。这些字符都可以存储在一个char型变量中。
- 一个16位Unicode占两个字节,用以
\u
开头的4位十六进制数表示,范围从'\u0000'
到'\uFFFF'
。关于十六进制数字的更多内容请参见附录F。例如:welcome被翻译成中文需要两个字符”欢迎“。这两个字符的Unicode为\u6B22\u8FCE
。希腊字母 α β γ 的Unicode是\u03b1\u03b2\u03b3
。 - 大多数计算机采用ASCII(美国标准信息交换码: American Standard Code for Information Interchange ),它是表示所有大小写字母、数字、标点符号和控制字符的8位编码表。Unicode码包括ASCII码,从
'\u0000'
到'\u007F'
对应128个ASCII字符。表4-4给出了一些常用字符的ASCII码。附录B给出了ASCII字符的完整清单,以及它们的十进制和十六进制编码。
字符 |
十进制编码值 |
Unicode值 |
‘0’ ~ ‘9’ |
48 ~ 57 |
\u0030 ~ \u0039 |
‘A’ ~ ‘Z’ |
65 ~ 90 |
\u0041 ~ \u005A |
‘a’ ~ ‘z’ |
97 ~ 122 |
\u0061 ~ \u007A |
4.3.2 特殊字符的转义序列
- 它的输出是:
He said "Java is fun"
注意,符号\和””一起代表一个字符
转义序列 |
名称 |
Unicode码 |
十进制值 |
\b |
退格键 |
\u0008 |
8 |
\t |
Tab键 |
\u0009 |
9 |
\n |
换行符 |
\u000A |
10 |
\f |
换页符 |
\u000C |
12 |
\r |
回车符 |
\u000D |
13 |
\ |
反斜杠 |
\u005C |
92 |
\“ |
双引号 |
\u0022 |
34 |
4.3.3 字符型数据与数值型数据之间的转换
- char型数据可以转换成任意一种数值类型,反之亦然。将整数转换成char型数据时,只用到该数据的低十六位,其余部分都被忽略。例如:
//Note a hex integer is written using prefix OX
char ch = (char)0XAB0041; //The lower 16 bits hex code 0041 is assigned to ch
System.out.println(ch); //ch is character A
- 要将一个浮点值转换成char型时,首先将浮点值转换成int型,然后将这个整型值转换位char型。
char ch = (char)65.25; //Decimal 65 is assigned to ch
System.out.println(ch); //ch is character A
- 当一个char型数据转换成数值型时,这个字符的Unicode就被转换成某个指定的数值类型。
int i = (int)'A'; //The Unicode of character A is assigned to i
System.out.println(i); //i is 65
- 但是,因为Unicode码\uFFF4超过了一个字节的范围,所以下面的转换就是不正确的:
byte b = '\uFFF4';
- 为了强制赋值,就必须使用显式转换,如下所示
byte b = (byte)'\uFFF4';
4.3.4 字符的比较和测试
方法 |
描述 |
isDigit(ch) |
如果指定的字符是一个数字,返回true |
isLetter(ch) |
如果指定的字符是一个字母,返回true |
isLetterOrDigit(ch) |
如果指定的字符是一个字母或者数字,返回true |
isLowerCase(ch) |
如果指定的字符是一个小写字母,返回true |
isUpperCase(ch) |
如果指定的字符是一个大写字母,返回true |
toLowerCase(ch) |
返回指定的字符的小写形式 |
toUpperCase(ch) |
返回指定的字符的大写形式 |
4.4 String类型
方法 |
描述 |
length() |
返回字符串中的字符数 |
charAt(index) |
返回字符串中指定位置的字符 |
concat(s1) |
将本字符串和字符串s1连接,返回一个新字符串 |
toUpperCase() |
返回一个新字符串,其中所有的字母大写 |
toLowerCase() |
返回一个新字符串,其中所有的字母小写 |
trim() |
返回一个新字符串,去掉了两边的空白字符 |
- String是Java中的对象。表4-7中的方法只能从一个特定的字符串实例来调用。由于这个原因,这些方法称为实例方法。非实例方法称为
静态方法
。静态方法可以不使用对象来调用。定义在Math类中的所有方法都是静态方法。它们没有绑定到一个特定的对象实例上。调用一个实例方法的语法是reference - Variable.methodName(arguments)
。一个方法可以由多个参数,或者没有参数。例如,charAt(index)方法具有一个参数,但是length()方法则没有参数。回顾曾经介绍过的,调用静态方法的语法是ClassName.methodName(arguments)。例如,Math类中的pow方法可以使用Math.pow(2,2.5)来调用
4.4.1 获取字符串长度
- 使用一个字符串时,往往是知道它的字面值的。为方便起见,Java允许在不创建新变量的情况下,使用字符串字面值直接引用字符串。这样,”Welcome to Java”.length()是正确的,它返回15。注意,””表示空字符串,并且””.length()为0.
4.4.2 从字符串中获取字符
- 在字符串s中越界访问字符是一种常见的程序设计错误。为了避免此类错误,要确保使用的下标不会超过s.length() -1 。例如,s.charAt(s.length())会造成一个StringIndexOutOfBoundsException异常。
4.4.3 连接字符串
4.4.4 字符串转换
4.4.5 从控制台读取字符串
- 为了方便,将使用方法next()、nextByte()、nextShort()、nextInt()、nextLong()、nextFloat()和nextDouble()的输入称为基于标记的输入,因为它们读取采用空白字符分隔的单个元素,而不是读取整行。nextLine()方法称为基于行的输入。
- 为了避免输入错误,程序中不要在基于标记的输入之后使用基于行的输入,原因将在12.11.4节中解释
4.4.6 从控制台读取字符
4.4.7 字符串比较
- String类提供了如表4-8所示的方法,用于比较两个字符串。
方法 |
描述 |
equals(s1) |
如果该字符串等于字符串s1,返回true |
equalsIgnoreCase(s1) |
如果该字符串等于字符串s1,返回true,不区分大小写 |
compareTo(s1) |
返回一个大于0、等于0、小于0的整数,表明该字符串是否大于、等于或者小于s1 |
compareToIgnoreCase(s1) |
和compareTo一样,除了比较是不区分大小写的外 |
startsWith(suffix) |
如果字符串以特定的前缀开始,返回true |
endWith(suffix) |
如果字符串以特定的后缀结束,返回true |
contains(s1) |
如果s1是该字符串的子字符串,返回true |
- 如果使用像>、≥、<或≤这样的比较操作符比较两个字符串,就会发生语法错误。替代的方法就是使用s1.compareTo(s2)来进行比较
- 如果两个字符串相等,equals方法返回true;如果它们不等,方法返回false。compareTo方法会根据一个字符串是否等于、大于或小于另一个字符串,分别返回0、正整数或负整数。
程序清单 4-2 OrdeTwoCities.java
import java.util.Scanner;
public class OrdeTwoCities {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
//Prompt the user to enter two cities
System.out.print("Enter the first city: ");
String city1 = input.nextLine();
System.out.print("Enter the second city");
String city2 = input.nextLine();
if (city1.compareTo(city2) < 0) {
System.out.println("The cities in alphabetical order are " +
city1 + " " + city2);
} else {
System.out.println("The cities in alphabetical order are " +
city2 + " " + city1);
}
}
}
4.4.8 获得子字符串
方法 |
描述 |
substring(beginIndex) |
返回该字符串的子串,从指定位置beginIndex的字符开始到字符串的结尾,如图4-2所示 |
substring(beginIndex, endIndex) |
返回该字符串的子串,从指定位置beginIndex的字符开始到下标为endIndex -1 的字符,如图4-2所示。注意,位于endIndex位置的字符不属于该子字符串的一部分 |
- 如果beginIndex为endIndex,substring(beginIndex, endIndex)返回一个长度为0的空字符串。如果beginIndex > endIndex,将发生运行时错误。
4.4.9 获取字符串中的字符或者子串
- String类提供了几个版本的indexOf和lastIndexOf方法,它们可以在字符串中找出一个字符或一个子串,如表4-10所示
方法 |
描述 |
indexOf(ch) |
返回字符串中出现的第一个ch的下标。如果没有匹配的,返回-1 |
indexOf(ch,fromIndex) |
返回字符串中fromIndex之后出现的第一个ch的下标。如果没有匹配的,返回-1 |
indexOf(s) |
返回字符串中出现的第一个字符串s的下标。如果没有匹配的,返回-1 |
indexOf(s, fromIndex) |
返回字符串中fromIndex之后出现的第一个字符串s的下标。如果没有匹配的,返回-1 |
lastIndexOf(ch) |
返回字符串中出现的最后一个ch的下标。如果没有匹配的,返回-1 |
lastIndexOf(ch,fromIndex) |
返回字符串中fromIndex之前出现的最后一个ch的下标。如果没有匹配的,返回-1 |
lastIndexOf(s) |
返回字符串中出现的最后一个字符串s的下标。如果没有匹配的,返回-1 |
lastIndexOf(s,fromIndex) |
返回字符串中fromIndex之前出现的最后一个字符串s的下标。如果没有匹配的,返回-1 |
4.4.10 字符串和数字间的转换
4.5 示例学习
4.5.1 猜测生日
程序清单 4-3 GuessBirthday.java
import java.util.Scanner;
public class GuessBirthday {
public static void main(String[] args) {
String set1 = " 1 3 5 7\n" +
" 9 11 13 15\n" +
"17 19 21 23\n" +
"25 27 29 31";
String set2 = " 2 3 6 7\n" +
"10 11 14 15\n" +
"18 19 22 23\n" +
"26 27 30 31";
String set3 = " 4 5 6 7\n" +
"12 13 14 15\n" +
"20 21 22 23\n" +
"28 29 30 31";
String set4 = " 8 9 10 11\n" +
"12 13 14 15\n" +
"24 25 26 27\n" +
"28 29 30 31";
String set5 = "16 17 18 19\n" +
"20 21 22 23\n" +
"24 25 26 27\n" +
"28 29 30 31";
int day = 0;
//Create a Scanner
Scanner input = new Scanner(System.in);
//Prompt the user to answer questions
System.out.print("\nIs your birthday in Set1?\n");
System.out.print(set1);
System.out.print("\nEnter 0 for No and 1 for Yes: ");
int answer = input.nextInt();
if (answer == 1){
day += 1;
}
//Prompt the user to answer questions
System.out.print("\nIs your birthday in Set2?\n");
System.out.print(set2);
System.out.print("\nEnter 0 for No and 1 for Yes: ");
answer = input.nextInt();
if (answer == 1) {
day += 2;
}
//Prompt the user to answer questions
System.out.print("\nIs your birthday in Set3?\n");
System.out.print(set3);
System.out.print("\nEnter 0 for No and 1 for Yes: ");
answer = input.nextInt();
if (answer == 1) {
day += 4;
}
//Prompt the user to answer questions
System.out.print("\nIs your birthday in Set4?\n");
System.out.print(set4);
System.out.print("\nEnter 0 for No and 1 for Yes: ");
answer = input.nextInt();
if (answer == 1) {
day += 8;
}
//Prompt the user to answer questions
System.out.print("\nIs your birthday in Set5?\n");
System.out.print(set5);
System.out.print("\nEnter 0 for No and 1 for Yes: ");
answer = input.nextInt();
if (answer == 1) {
day += 16;
}
System.out.println("\nYour birthday is " + day + "!");
}
}
4.5.2 将十六进制数转换为十进制数
程序清单 4-4 HexDigit2Dec.java
import java.util.Scanner;
public class HexDigit2Dec {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("Enter a hex digit: ");
String hexString = input.nextLine();
//Check if the hex string has exactly one character
if (hexString.length() != 1) {
System.out.println("You must enter exactly one character");
System.exit(1);
}
//Display decimal value for the hex digit
char ch = Character.toUpperCase(hexString.charAt(0));
if ('A' <= ch && ch <= 'F') {
int value = ch - 'A' + 10;
System.out.println("The decimal value for hex digit " +
ch + " is " + value);
} else if (Character.isDigit(ch)) {
System.out.println("The decimal value for hex digit " +
ch + " is " + ch);
} else {
System.out.println(ch + " is an invalid input");
}
}
}
4.5.3 使用字符串修改彩票程序
程序清单 4-5 LotteryUsingStrings.java
import java.util.Scanner;
public class LotteryUsingStrings {
public static void main(String[] args) {
//Generate a lottery as a two-digit string
String lottery = "" + (int) (Math.random() * 10)
+ (int) (Math.random() * 10);
//Prompt the user to enter a guess
Scanner input = new Scanner(System.in);
System.out.print("Enter your lottery pick(two digits");
String guess = input.nextLine();
//Get digits from lottery
char lotteryDigit1 = lottery.charAt(0);
char lotteryDigit2 = lottery.charAt(0);
//Get digits from guess
char guessDigit1 = guess.charAt(0);
char guessDigit2 = guess.charAt(1);
System.out.println("The lottery number is " + lottery);
//Check the guess
if (guess.equals(lottery)) {
System.out.println("Exact match: you win $10,000");
} else if (guessDigit2 == lotteryDigit1 && guessDigit1 == lotteryDigit2) {
System.out.println("Match all digits: you win $3,000");
} else if (guessDigit1 == lotteryDigit1 || guessDigit1 == lotteryDigit2
|| guessDigit2 == lotteryDigit1 || guessDigit2 == lotteryDigit2) {
System.out.println("Match one digit: you win $1,000");
} else {
System.out.println("Sorry, no match");
}
}
}
4.6 格式化控制台输出
- 可以使用System.out.printf方法在控制台上显示格式化输出。
- 格式限定符指定每项应该如何显示。这里的项可以是数值、字符、布尔值或字符串。简单的格式标识符是以百分号(%)开头的转换码。表4-11列出了一些常用的简单格式限定符
限定符 |
输出 |
举例 |
限定符 |
输出 |
举例 |
%b |
布尔值 |
true或false |
%f |
浮点数 |
45.460000 |
%c |
字符 |
‘a’ |
%e |
标准科学记数法形式的数 |
4.556 000e+01 |
%d |
十进制整数 |
200 |
%s |
字符串 |
“Java is cool” |
举例 |
输出 |
%5c |
输出字符并在这个字符条目前面加4个空格 |
%6b |
输出布尔值,在false值前加一个空格,在true值前加两个空格 |
%5d |
输出整数项,宽度至少为5。如果该条目的数字位数小于5,就在数字前面加空格。如果该条目的位数大于5,则自动增加宽度 |
%10.2f |
输出的浮点数项的宽度至少为10,包括小数点和小数点后两位数字。这样,给小数点前分配了7位数字。如果该条目小数点前的位数小于7,就在数字前面加空格。如果该条目小数点前的位数大于7,则自动增加宽度 |
%10.2e |
输出的浮点数项的宽度至少为10,包括小数点、小数点后两位数字和指数部分。如果按科学记数法显示的数字位数小于10,就在数前加空格 |
%12s |
输出的字符串宽度至少为12个字符。如果该字符串条目小于12个字符。就在该字符串前加空格。如果该字符串条目多于12个字符,则自动增加宽度 |
- 程序清单4-6 给出一个使用printf来显示一个表格的程序。
程序清单 4-6 FormatDemo.java
public class FormatDemo {
public static void main(String[] args) {
//Display the header of the table
System.out.printf("%-10s%-10s%-10s%-10s%-10s\n", "Degrees", "Radians", "Sine", "Cosine", "Tangent");
//Display values for 30 degrees
int degrees = 30;
double radians = Math.toRadians(degrees);
System.out.printf("%-10d%-10.4f%-10.4f%-10.4f%-10.4f\n",
degrees, radians, Math.sin(radians), Math.cos(radians), Math.tan(radians));
}
}