[JAVA学习笔记]JAVA基本程序设计结构
一个简单的Java应用程序
public class FirstSample
{
public static void main(String[] args)
{
System.out.println("Just don't use 'Hello, World!'");
}
}
这个简单的Java应用程序,只发送一条消息到控制台窗口中,但所有的Java应用程序都具有这种结构。其中:
- 关键字public 称为访问修饰符,这些修饰符用于控制程序其他部分对这段代码的访问级别。
- 关键字class 表明Java 程序中的全部内容都包含在类中。这里的类相当于一个加载程序逻辑的容器,程序逻辑定义了程序的行为。
- class 后紧跟类名。类的命名规范应当符合骆驼命名法即:类似CamelCase。
- 源代码的文件名必须与公共类的名字相同,并以.java 作为扩展名。
- 从命令行运行上面这段代码时,分两步:
javac FirstSample.java
和java FirstSample
,值得注意的是第二步没有后缀.class 。
注释
三种:
// First
/*
Second
Second
/
/**
* Third, can automatically generate documentation.
*/
数据类型
Java 一共有8种基本类型,其中4种整型、2种浮点型、1种用于表示Unicode 编码的字符单元的字符类型char 和boolean 类型。Java 有一个能表示任意精度的算术包,称为大数值。但它其实是一个Java 对象,并不是一种Java 数据类型
整型
类型 | 存储需求 | 取值范围 |
---|---|---|
int | 4字节 | -2147483648 ~ 2147483647 |
short | 2字节 | -32768 ~ 32767 |
long | 8字节 | -9223372036854775808 ~ 9223372036854775807 |
byte | 1字节 | -128 ~ 127 |
长整型数值有一个后缀L或l(如4000000000L)。十六进制数值有一个前缀0x或0X(如0xCAF5)。八进制有一个前缀0,如010表示8,但容易混淆,故不推荐使用。加上前缀0b或0B表示二进制数(如0b1001表示9)。
浮点类型
类型 | 存储需求 | 取值范围 |
---|---|---|
float | 4字节 | 有效位数6 ~ 7位 |
double | 8字节 | 有效位数15位 |
一般使用double 类型,float 类型的数值有一个后缀F或f(如3.14f)。
会导致溢出和出错情况的三个特殊的浮点数值:
- 正无穷大
- 负无穷大
- NaN
常量Double.POSITIVE_INFINITY、Double.NEGATIVE_INFINITY 和 Double.NaN 分别表示这三个特殊值,要检测一个特定值是否等于Double.NaN:
if (x == Double.NaN) // 不可以,结果永远是false
if (Double.isNaN(x)) // 则可以
浮点值不适用于无法接受误差的金融计算中,例如System.out.println(2.0-1.1);
打印出的结果为:0.8999999999999999,而并非0.9.这种舍入误差的主要原因是浮点数值采用二进制系统表示,而在二进制系统种无法精确地表示分数1/10。如果在数值中不允许任何舍入误差,应该使用BigDecimal 类。
char类型(码点与代码单元)
要弄清char 类型,就必须了解Unicode 编码机制,一个char 类型的大小为16bit。Unicode 解决了不同编码方案下有可能产生不同的字符的问题。但是随着加入的字符越来越多,16位已经不能完全表示Unicode 中的字符。
码点是指一个编码表中的某个字符对应的代码值。在Unicode 标准中,码点采用十六进制书写,并加上前缀U+,例如U+0041就是拉丁字幕A的码点。Unicode 的码点可以分成17个代码级别。第一个代码级别称为基本的多语言级别,码点从U+0000到U+FFFF,其中包括经典的Unicode 代码;其余的16个级别码点从U+10000到U+10FFFF,其中包括一些辅助字符。
UTF-16编码采用不同长度的编码表示所有Unicode 码点。在基本的多语言级别中,每个字符用16位表示,通常被称为代码单元;而辅助字符采用一对连续的代码单元进行编码。这样构成的编码值落入基本的多语言级别中空闲的2048字节内,通常被称为替代区域[U+D800 ~ U+DBFF用于第一个代码单元,U+DC00 ~ U+DFFF用于第二个代码单元]。这样设计的巧妙之处在于我们可以从中迅速地知道一个代码单元是一个字符的编码,还是一个辅助字符的第一或第二部分。𝕆 是八元数集的一个数学符号,码点位U+1D546,编码位两个代码单元U+D835 和U+DD46。
不建议在程序中使用char 类型,除非确实需要处理UTF-16代码单元。最好将字符串作为抽象数据类型处理。
boolean 类型
两个false 和true ,用来判定逻辑条件。整型值和布尔值之间不能进行相互转换。
变量
在Java 中,每个变量都有一个类型(type)。在变量声明时,变量的类型位于变量名之前。
double salary;
int days;
long earthPopulation;
boolean done;
变量初始化
未对变量进行初始化直接使用会产生错误。值得注意的是,变量的声明尽可能地靠近变量第一次使用的地方,这是一种良好的程序编写风格。
常量
在Java 中,利用关键字final 指示常量。如:
final double CM_PER_INCH = 2.54;
如果需要一个常量在一个类的多个方法中使用,通常将这些常量称为类常量。可以使用关键字static final 设置一个类常量。例如:
public class FirstSample
{
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 ,那么其他类的方法也可以使用这个常量。本例中的FirstSample.CM_PER_INCH 就是这样一个常量。
运算符
数学函数与常量
方法 | 含义 |
---|---|
Math.sqrt(x) | 求平方根 |
Math.pow(x, a) | 求幂运算 |
Math.sin/cos/tan/atan/atan2 | 三角函数 |
Math.exp/log/log10 | 指数函数 |
Math.PI/E | Π 和 e常量的近似值 |
不必在数学方法名和常量名前添加前缀“Math”,只需在源文件的顶部加上import static java.lang.Math.*
即可。
如果要得到更精确的结果则需要使用StrictMath
类。
数值类型之间的转换
数值类型在进行转换时有可能导致精度损失,例如:
int n = 123456789;
float f = n; // f is 1.23456792E8
当两个不同类型的数值进行运算时,低精度的数值类型会自动转换为高精度的数值类型:
- 如果两个操作数中有一个是 double 类型, 另一个操作数就会转换为 double 类型。
- 否则, 如果其中一个操作数是 float 类型, 另一个操作数将会转换为 float 类型。
- 否则, 如果其中一个操作数是 long 类型, 另一个操作数将会转换为 long 类型 。
- 否则, 两个操作数都将被转换为 int 类型。
强制类型转换
把数值的类型从高精度转换为低精度的数值类型可以使用强制类型转换,方法是在圆括号中给出想要转换的目标类型,后面紧跟待转换的变量名。 例如:
double x = 9.998;
int nx = (int) x; // nx is 9
这样的方法是强制阶段小数部分,如果需要进行舍入运算,就需要使用Math.round 方法:
double x = 9.998;
int nx = (int) Math.round(x); // nx is 10
不要在boolean 类型与任何数值类型之间进行转换,只有极少数时需要的时候通过b ? 1:0
进行转换。
枚举类型
有时候变量的取值只在一个有限的集合内。例如衣服的尺寸为:S、M、L、X。为了保证相关的变量中不会储存错误的值,可以使用如下方法声明并初始化变量:
enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE};
Size s = Size.MEDIUM;
Size 类型的变量智能存储这个类型声明中的某个枚举值,或者null 值。
字符串
从概念上讲,Java 字符串就是Unicode 字符序列。例如,串“Java\u2122”由5个Unicode 字符J、a、v、a和™组成。Java 没有内置的字符串类型,而是在标准的Java 类库中提供了一个预定义类,很自然的叫做String。每个用双引号括起来的字符串都是String 类的一个实例:
public class FirstSample
{
public static void main(String[] args)
{
String java = "Java\u2122";
System.out.println(java.length());
}
}
运行上面的这段代码,输出结果为:5。
子串
String 类的substring 方法可以从一个较大的字符串提取出一个子串。例如:
String greeting = "Hello";
String s = greeting.substring(0, 3); // s is "Hel".
拼接
与绝大多数程序设计语言一样,Java 允许使用 + 号拼接两个字符串。
String greeting = "Hello" + ", how are you?";
值得注意的是,当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串。
如果需要把多个字符串用相同的分隔符拼接,可以使用静态join 方法:
String ae = String.join("-", "A", "B", "C", "D", "E"); // ae is "A-B-C-D-E"
不可变字符串
String 类没有提供用于修改字符串的方法 6。如果希望将 greeting 的内容修改为“ Help!”,
不能直接地将 greeting 的最后两个位置的字符修改为‘P’和‘!’ 这对于 C 程序员来说,
将会感到无从下手。 如何修改这个字符串呢? 在 Java中实现这项操作非常容易。首先提取需要的字符, 然后再拼接上替换的字符串:
greeting = greeting.substring(0, 3) + "p!";
检测字符串是否相等
使用String 类的equals 方法检测两个字符串是否相等,下面这两种用法都是OK 的:
s.equals(t);
"Hello".equals(t);
不能使用==运算符检测两个字符串是否相等,这个运算符智能确定两个字符串是否放置在同一位置上。
空串和Null 串
空串:str.length()==0
或str.equals("")
Null串:str == null
空串不等于Null 串。
码点和代码单元
大多数常用的Unicode 字符使用一个代码单元就可以表示,此时使用length()
方法的到的既是代码单元的数量,也是字符的数量。
String greeting = "Hello";
int n = greeting.length(); // n is 5
但是如果遇到需要一对代码单元表示的辅助字符时,length()
方法返回的代码单元的数量就不等于字符的数量了。此时如果想获得码点或者说字符的数量需要使用:
int cpCount = greeting.codePointCount(0, greeting.length());
调用s.charAt(n)
将返回位置n 的代码单元,n 介于0 ~ s.length()-1之间。例如:
char first = greeting.charAt(0); // first is "H"
char last = greeting.charAt(4); // last is "o"
要想得到第i个码点,应该使用下列语句:
int index = greeting.offsetByCodePoints(0, i);
int cp = greeting.codePointAt(index);
String API
Java 的String 类包含了50多个方法,而且绝大多数都很有用,下面的表格汇总了一些常用的方法:
方法 | 含义 |
---|---|
char charAt(int index) | 返回给定位置的代码单元 |
int codePointAt(int index) | 返回从给定位置开始的码点 |
int offsetByCodePoints(int startIndex, int cpCount) | 返回从startIndex 代码点开始,位移cpCount 后的码点索引 |
int compareTo(String other) | 按照字典顺序,如果字符串位于other 之前,返回一个负数;如果位于other之后,返回一个正数;如果相等,返回0 |
IntStream codePoints() | 将这个字符串的码点作为一个流返回。调用 toArray 将它们放在一个数组中 |
new String(int[] codePoints, int offset, int count) | 用数组中从 offset 开始的 count 个码点构造一个字符串 |
boolean equals(Object other) | 如果字符串与 other 相等, 返回 true |
boolean equalsIgnoreCase(String other) | 如果字符串与 other 相等 (忽略大小写,) 返回 tme |
boolean startsWith(String prefix) | 如果字符串以 suffix 开头, 则返回 true |
boolean endsWith(String suffix) | 如果字符串以 suffix 结尾, 则返回 true |
int index0f(String str) | 返回与字符串 str 或代码点 cp 匹配的第一个子串的开始位置。这个位置从索引 0 或fromlndex 开始计算。 如果在原始串中不存在 str, 返回 -1。 |
int index0f(String str, int fromIndex) | - |
int index0f(int cp) | - |
int index0f(int cp, int fromIndex) | - |
int lastIndex0f(String str) | 返回与字符串 str 或代码点 cp 匹配的最后一个子串的开始位置。 这个位置从原始串尾端或 fromIndex 开始计算 |
int lastIndex0f(String str, int fromIndex) | - |
int lastIndex0f(int cp) | - |
int lastIndex0f(int cp, int fromIndex) | - |
int length() | 返回字符串的长度 |
int codePointCount(int startIndex, int endIndex) | 返回 startlndex 和 endIndex - 1之间的代码点数量。没有配成对的代用字符将计入代码点 |
String replace(CharSequence oldString, charSequence newString) | 返回一个新字符串。这个字符串用 newString 代替原始字符串中所有的 oldString。可以用 String 或 StringBuilder 对象作为 CharSequence 参数 |
String substring(int beginIndex) | 返回一个新字符串。这个字符串包含原始字符串中从 beginlndex 到串尾或 endlndex - 1的所有代码单元 |
String substring(int beginIndex, int endIndex) | - |
String toLowerCase() | 返回一个新字符串。 这个字符串将原始字符串中的大写字母改为小写,或者将原始字符串中的所有小写字母改成了大写字母 |
String toUpperCase() | - |
String trim() | 返回一个新字符串。这个字符串将删除了原始字符串头部和尾部的空格 |
String join(CharSequence delimiter, CharSequence... elements) | 返回一个新字符串, 用给定的定界符连接所有元素 |
构建字符串
当需要用较短的字符串构建新字符串时,例如,按键或来自文件中的单词。采用字符串连接的方式达到此目的效率比较低。每次连接字符串,都会构建一个新的String 对象,既耗时又浪费空间。使用StringBuilder 类就可以避免这个问题的发生。
步骤如下,首先构建一个空的字符串构建器:
StringBuilder builder = new StringBuilder();
每次需要添加一部分内容时,就调用append 方法。
builder.append(ch); // appends a single character
builder.append(str); // appends a string
在需要构建字符串时,就调用toString 方法,将可以得到一个String 对象,其中包含了构建器中的字符序列。
String completedString = builder.toString();
StringBuilder 类中的常用方法:
方法 | 含义 |
---|---|
int length() | 返回构建器中的代码单元数量 |
StringBuilder append(String str) | 追加一个字符串并返回全部字符 |
StringBuilder append(char c) | 追加一个代码单元并返回全部字符 |
StringBuilder appendCodePoint(int cp) | 追加一个代码点,并将其转换为一个或两个代码单元并返回全部字符 |
void setCharAt(int i, char c) | 将第i 个代码单元设置为c |
StringBuilder insert(int offset, String str) | 在offset 位置插入一个字符串并返回全部字符 |
StringBuilder insert(int offset, char c) | 在offset 位置插入一个代码单元并返回全部字符 |
StringBuilder delete(int startIndex, int endIndex) | 删除偏移量从startIndex 到endIndex - 1 的代码单元并返回全部字符 |
String toString() | 返回一个与构建器或缓冲器内容相同的字符串 |
输入输出
读取输入
要想通过控制台进行输入,首先需要构造一个Scanner 对象,并与“标准输入流” System.in 关联:
Scanner in = new Scanner(System.in);
现在,就可以使用Scanner 类的各种方法来实现输入操作了。
String name = in.next();
方法 | 含义 |
---|---|
nextLine | 输入一行 |
next | 输入第一个空格之前的字符 |
nextInt | 输入的字符中的第一个整数 |
nextDouble | 输入的字符中的第一个浮点数 |
由于输入是可见的,所以Scanner 类不适用于从控制台读取密码。要想读取一个密码,可以使用Console 类:
Console cons = System.console();
String username = cons.readLine("User name: ");
char[] passwd = cons.readPassword("Password: ");
除了上面的方法,还有下表中常用的方法:
方法 | 含义 |
---|---|
boolean hasNext() | 检测输入中是否还有其他单词 |
boolean hasNextInt() | 检测是否还有表示整数或浮点数的下一个字符序列 |
boolean hasNextDouble() | - |
格式化输出
在使用System.out.print(x) 将数值x 输出时,将以x 对应的数据类型所允许的最大非0数字位数打印输出x。如:
double x = 10000.0 / 3;
System.out.print(x); // output 3333.3333333333335
Java SE 5.0 沿用了C 语言函数库中的printf 方法。如:
System.out.printf("%8.2f", x); // output 3333.33
%8.2f 中的8 表示宽度位8个字符,如果不够则左侧用空格补齐,2表示保留2位小数,如果多于2位小数会进行四舍五入,其中的f表示定点浮点数。其他类似的:
转换符 | 类型 | 栗子 |
---|---|---|
d | 十进制整数 | 123 |
x | 十六进制整数 | 9f |
o | 八进制整数 | 576 |
f | 定点浮点数 | 15.66 |
e | 指数浮点数 | 1.59e+01 |
a | 十六进制浮点数 | 0x1.fccdp3 |
s | 字符串 | Good |
c | 字符 | g |
b | 布尔 | True |
h | 散列码 | 42638b2 |
另外,还可以增加控制格式化输出的各种标志,例如,使用逗号增加分组的分隔符:
System.out.printf("%,.2f", x); // output 3,333.33
同时,还可以组合使用多个标志,例如,"%,(.2f"使用分组的分隔符并将负数括在括号内。
标志 | 目的 | 栗子 |
---|---|---|
+ | 打印正数和负数的符号 | +3333.33 |
空格 | 在正数之前添加空格 | | 3333.33| |
0 | 数字前面补0 | 003333.33 |
- | 左对齐 | |3333.33 | |
( | 将负数括在括号内 | (3333.33) |
, | 添加分组分隔符 | 3,333.33 |
#(对于f格式) | 包含小数点 | 3,333. |
#(对于x或0格式) | 添加前缀0x或0 | 0xcafe |
$ | 给定被格式化的参数索引。例如, %l$d, %l$x 将以十进制和十六进制格式打印第 1 个参数 | 159 9F |
< | 格式化前面说明的数值。 例如,%d%<x 以十进制和十六进制打印同一个数值 | 159 9F |
可以使用s 转换符格式化任意的对象至字符串类型。对于任意实现了Formattable 接口的对象都将调用formatTo 方法;否则将调用toString 方法,它可以将对象转换为字符串。
可以使用静态的String.format 方法创建一个格式化的字符串,而不打印输出:
String message = String.format("Hello, %s. Next year , you'll be %d", name, age");
格式说明符的语法图如下:
文件输入与输出
对文件进行读取,首先用File 对象构造一个Scanner 对象:
Scanner in = new Scanner(Path.get("myFile.txt"), "UTF-8");
要想写入文件,需要构造一个PrintWriter 对象。在构造器中,只需提供文件名:
PrintWriter out = new PrintWriter("myFIle.txt", "UTF-8");
使用集成开发环境IDE ,读写文件时的启动路径由IDE 控制。可以使用下面的代码查看启动路径的位置:
String dir = System.getProperty("user.dir");
System.out.println(dir);
或者,直接使用绝对路径即可。
方法 | 含义 |
---|---|
Scanner(File f) | 构造一个从给定文件读取数据的Scanner |
Scanner(String data) | 构造一个从给定字符串读取数据的Scanner |
PrintWriter(String fileName) | 构造一个将数据写入文件的PrintWriter。文件名由参数指定 |
static Path get(String pathname) | 根据给定的路径名构造一个Path |
控制流程
条件语句
if (yourSales >= target)
{
performance = "Satisfactory";
bonus = 100;
}
else
{
performance = "Unsatisfactory";
bonus = 0;
}
循环
while (balance < goal)
{
balance += payment;
double interest = balance * interestRate / 100;
balance ++ interest;
years++;
}
上面的代码,循环体中的语句有可能一次都不执行,如果希望循环体至少执行一次:
do
{
balance += payment;
double interest = balance * interestRate / 100;
balance ++ interest;
years++;
}while (input.equals("N"));
确定循环
for (int i = 1; i <= 10; i++)
{
...
}
多重选择:switch 语句
Scanner in = new Scanner(System.in);
System.out.print("Select an option (1, 2, 3, 4) ");
int choice = in.nextInt();
switch (choice) {
case 1:
...
break;
case 2:
...
break;
case 3:
...
break;
case 4:
...
break;
default:
// bad input
...
break;
如果在case 分支语句的末尾没有break 语句,那么就会接着执行下一个case 分支语句。为此,不建议在程序中使用switch 语句。如果非要使用switch 语句,编译代码时可以考虑加上-Xlint:fallthrough 选项:
javac -Xlint:fallthrough Test.java
这样,如果某个分支最后缺少一个break 语句,编译器就会给出一个警告消息。如果确实正式想使用这种“直通式”(fallthrough)行为,可以为其外围方法加一个标注@SuppressWarnings("fallthrough")。这样就不会对此方法生成警告了。
中断控制流程语句:break 和continue
break 语句在满足条件时跳出当前循环:
while (years <= 100) {
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
if (balance >= goal) break;
years++;
}
带标签的break 语句用于跳出多重嵌套的循环语句。标签必须放在希望跳出的最外层循环之前,并且必须紧跟一个冒号。
label:
{
...
if (condition) break label; // exits block
...
}
// jumps here when the break statement executes
continue 语句将控制转移到最内层循环的首部。
Scanner in = new Scanner(System.in);
while (sum < goal )
{
System.out.print("Enter a number: ");
n = in.nextlntO;
if (n < 0) continue;
sum += n; // not executed if n < 0
}
带标签的continue 语句与带标签的break 语句同理。
大数值
如果基本的整数和浮点数精度不能够满足需求,那么可以使用java.math 包中的两个很有用的类:BigInteger 和BigDecimal。这两个类可以处理包含任意长度数字序列的数值。BigInteger 类实现了任意精度的整数运算,BigDecimal 实现了任意精度的浮点数运算。
使用静态的valueOf 方法可以将普通的数值转换为大数值:
BigInteger a = BigInteger.valueOf(100);
遗憾的是,不能使用人们熟悉的算术运算符处理大数值。而需要使用大数值类中的add 和multiply 方法。
BigInteger c = a.add(b);
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2)));
下面的程序用来计算从n个球中抽取k个球时中奖的概率:
import java.math.BigInteger;
import java.util.*;
public class TempPractice {
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 odd are 1 in " + lotteryOdds + ". Good luck! ");
}
}
其中,for 循环中计算的是一共有多少种可能:
数组
创建数组并初始化:
int[] a = new int[100];
int[] b = new int[n];
创建一个数字数组时,所有元素都初始化为0。boolean 数组的元素会初始化为false。对象数组的元素则初始化为一个特殊值null,这表示这些元素还未存放任何对象。
如果想要创建一个元素均为空串的字符串数组,可以使用:
String[] names = new String[10];
for (int i = 0; i < 10; i++) names[i] = "";
如果想要获取数组中元素的个数,可以使用array.length。
System.out.println(names.length); // output 10
数组一旦被创建,便不能再改变数组的大小,如果经常需要再运行过程中扩展数组的大小,应该使用另一种数据结构——数组列表(array list)。
for each 循环
for each 循环用来依次处理数组中的每个元素而不必为指定下标值而担心。
for (String element : names)
System.out.println(element + ", ");
还有一个更加简单的方式打印数组中的所有值,利用Arrays 类的toString 方法。调用Arrays.toString(names),返回一个包含数组元素的字符串。
String[] names = new String[10];
for (int i = 0; i < 10; i++)
names[i] = String.valueOf(i + 1);
System.out.print(Arrays.toString(names)); // output [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
数组初始化以及匿名数组
Java 提供了一种创建数组对象并同时赋予初始值的简化书写形式:
int[] smallPrimes = {2, 3, 5, 7, 11, 13};
在使用这种语句时,不需要调用new。
初始化一个匿名数组:
new int[] = {17, 19, 23, 29, 31, 37};
使用这种语法形式可以在不创建新变量的情况下重新初始化一个数组。
smallPrimes = new int[]{17, 19};
还可以通过这种方式将一个数组的值赋给另一个数组:
int[] anonymous = {17, 19, 23, 29, 31, 37};
smallPrimes = anonymous;
数组拷贝
在Java 中,允许将一个数组变量拷贝给另一个数组变量。此时,两个变量将实际上引用同一个数组:
int[] luckyNumbers = smallPrimes;
luckyNumbers[5] = 12; // 现在smallPrimes[5] 的值也是12
如果希望将一个数组的所有值拷贝至另一个新的数组中去,就要使用Arrays 类的copyOf 方法:
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);
其中,第二个参数是新数组的长度。这个方法通常用来增加数组的长度:
luckyNumbers = Arrays.copyOf(luckyNumbers, 2 * luckyNumbers.length);
如果数组元素是数值型,那么新增加的元素将被赋值为0,如果是其他类型,则赋值为其他类型的默认值。相反,如果长度小于原始数组的长度,则只拷贝最前面的数据元素。
命令行参数
编写一个程序如下:
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,");
for (int i = 1; i < args.length; i++)
System.out.print(" " + args[i]);
System.out.print("!");
}
}
在终端输入以下命令:
java Message -h Dereen
输出如下:
Hello, Dereen!
数组排序
要对数值型数组进行排序,可以使用Arrays 类中的sort 方法:
int[] a = new int[10000];
...
Arrays.sort(a)
小项目:从49个球中随机取6个球中将,生成一种中将的可能结果:
import java.util.Arrays;
import java.util.Scanner;
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.print("What is the highest 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 sorted array
Arrays.sort(result);
System.out.println("Bet hte following combination. It'll make you rich!");
for (int r : result)
System.out.println(r);
}
}
运行上面的代码,结果如下:
How many numbers do you need to draw? 6
What is the highest number you can draw? 49
Bet hte following combination. It'll make you rich!
1
4
12
37
38
42
[47, 2, 3, 49, 5, 6, 7, 8, 9, 10, 11, 44, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 45, 46, 39, 40, 41, 48, 43, 44, 45, 46, 47, 48, 49]
Arrays 类常用方法:
方法 | 含义 |
---|---|
static String toString(type[] a) | 返回包含a中数据元素的字符串,这些数据元素被放在括号内,并用逗号分隔。 |
static type copyOf(type[] a, int length) | 返回与a类型相同的一个数组,其长度为length或者end - start,数组元素为a的值 |
static type copyOfRange(type[] a, int start, int end) | - |
static void sort(type[] a) | 采用优化的快速排序算法对数组进行排序 |
static int binarySearch(type[] a, type v) | 采用二分搜索算法查找值v。如果成功,返回相应下标值;否则,返回一个负数值r。-r-1 是为保持a有序v应该插入的位置 |
static int binarySearch(type[] a, int start, int end , type v) | - |
static void fill(type[] a, type v) | 将数组的所有数据元素值设置为v |
static boolean equals(type[] a, type[] b) | 如果两个数组大小相同,且下标相同的元素都对应相等,返回true |
多维数组
多维数组使用多个下标访问一个元素,适用于表格或更加复杂的排列形式。要想快速地打印一个二维数组的数据元素列表, 可以调用: Arrays.deepToString(a)
import java.util.Arrays;
public class LotteryDrawing {
public static void main(String[] args) {
int[][] square = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16},
{17, 18, 19, 20},
{21, 22, 23, 24}
};
System.out.print(Arrays.deepToString(square));
}
}
不规则数组
例子:构建一个“不规则”数组,即数组的每一行有不同长度。创建一个数组,第i行第j列将存放“从i个数值中抽取j个数值”可产生的可能数:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
程序如下:
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 triangular array
for (int[] row : odds) {
for (int odd : row)
System.out.printf("%4d", odd);
System.out.println();
}
}
}