Java核心技术 基础程序设计结构2

6.字符串

Java字符串就是Unicode字符序列,Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义类(String),每个用双引号括起来的字符串都是String类的一个实例。

子串

substring(a, b),可以从一个较大的字符串提取出一个子串,第二个参数时不想复制的第一个位置。

substring的优点是容易计算子串的长度,长度为b-a。

String greeting = "Hello";
String s = greeting.substring(0,3);
System.out.println(s);

 

拼接

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

当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串(任何一个Java对象都可以转化成字符串)。这种特性常用在输出语句。

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

如果需要把多个字符串放在一起,用一个定界符分隔,可以使用静态join方法:

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

 

不可变字符串

String类没有提供用于修改字符串的方法。将greeting修改为“Help!”时,不能直接将最后两个字符修改为p和!。

String greeting = "Hello";
greeting = greeting.substring(0, 3) + "p!";
System.out.println(greeting);

在Java文档中将String类对象成为不可变字符串,字符串“Hello”永远包含H、e、l、l、o的代码单元序列,而不能修改其中任何一个字符,可以修改字符串变量greeting,让它引用另外一个字符串。

不可变字符串的优点:编译器可以让字符串共享。公共存储池,如果复制一个字符串变量,原始字符串与复制字符串共享相同的字符。

Java的设计者认为共享带来的高效率远远胜于提取、拼接字符串所带来的低效率。程序中很少需要修改字符串,往往是对字符串进行比较。

 

检测字符串是否相等

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

equalsIgnoreCase检测两个字符串是否相等,而不区分大小写。

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

完全有可能将内容相同的多个字符串的拷贝放置在不同的位置上:

String s1 = new String("A");
String s2 = new String("A");
System.out.println(s1.equals(s2));
System.out.println(s1 == s2);
String s3 = "B";
String s4 = "B";
System.out.println(s3.equals(s4));
System.out.println(s3 == s4);
String greeting = "Hello";
System.out.println(greeting == "Hello");
System.out.println(greeting.substring(0, 3) == "Hel");
// true
// false
// true
// true
// true
// false

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

 

空串与Null串

空串""是一个Java对象,长度为0,内容(空)。

可以调用以下代码检查一个字符串是否为空:

if (str.length() == 0)
if(str.equals(""))

String变量还可以存放一个特殊值null,检查是否为null

if(str == null)

既不是null也不为空串:

if(str != null && string.length() != 0)

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

 

码点与代码单元

char数据类型是一个采用UTF-16编码表示Unicode码点的代码单元。

length方法返回采用UTF-16编码表示的给定字符串所需的代码单元数量,要想得到实际的长度,即码点数量,可以调用:

// 使用UTF-16编码表示字符𝕆(U+1D546)需要两个代码单元
String greeting = "𝕆 is the set of octonions";
int cpCount = greeting.codePointCount(0, greeting.length());
System.out.println(greeting.length());
System.out.println(cpCount);
// 26
// 25

表用s.charAt(n)将返回位置n的的代码单元,n介于0~s.length - 1之间:

char first = greeting.charAt(0);
char first = greeting.charAt(1);
char last = greeting.charAt(4);
// ?
// ?
// s

要想得到第i个码点:

int index = greeting.offsetByCodePoints(0, 2);
int cp = greeting.codePointAt(index);
System.out.println(index);
System.out.println(cp);
// 3
// 105

查看每个码点:

String greeting = "𝕆 is the set of octonions";

for (int i = 0; i < greeting.length();) {
    int cp = greeting.codePointAt(i);
    System.out.println(cp);
    if (Character.isSupplementaryCodePoint(cp)) {
        i += 2;
    } else {
        i++;
    }
}

更简单的方法:

String greeting = "𝕆 is the set of octonions";

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

反之,将码点数组转成一个字符串:

String greeting = "𝕆 is the set of octonions";

int[] codePoints = greeting.codePoints().toArray();

String str = new String(codePoints, 0, 1);
System.out.println(str);
// 𝕆

构建字符串

在JDK5.0中引入StringBuilder类,这个类的前身是StringBuffer,其效率稍有些低,但允许采用多线程的方法执行添加或删除字符的操作。如果所有字符串在一个单线程中编辑,则应该用StringBuilder替代它。

 

7.输入输出

构造一个Scanner对象,并与标准输入流System.in关联,就可以使用Scanner类的各种方法实现输入操作:

Scanner in = new Scanner(System.in);
String name = in.nextLine();

使用nextLine()方法是因为在输入中可能有空格

如果只读取一个单词,可以使用in.next()

想读取一个整数,就调用in.newInt()

浮点数就是in.nextDouble()

格式化输出

Java SE 5.0沿用了C语言库函数中的printf方法:

double x = 10000.0 / 3.0;
System.out.printf("%8.2f", x);

可以用8个字符的宽度和小数点后两个字符的精度打印x,也就是说,打印输出一个空格和7个字符。

每一个以%字符开始的格式说明符都用相应的参数替代。格式说明符尾部的转换符将指示被格式化的数值类型:

转换符类型举例
d十进制整数159
x十六进制整数9f
o八进制整数237
f定点浮点数15.9
e指数浮点数1.59e+01
g通用浮点数
a十六进制浮点数 
s字符串Hello
c字符H
b布尔True
h散列码42628b2
tx或Tx日期时间已过时
%百分号%
n行分隔符 

还可以给出控制格式化输出的各种标志:

int i = 1000000;
System.out.printf("%,d", i);
// 1,000,000

用于printf的标志:

标志目的举例
+打印整数和负数符号+3333.33
空格在整数之前加空格| 3333.33|
0数字前面补0003333.33
-左对齐|3333.33 |
(将负数括在括号内(3333.33)
,添加分组分隔符3,333.33
#(对于f格式)包含小数点3,333.

#(对于x或0格式)

添加前缀0x或00xcafe
$给定格式化的参数索引,例如,%1$d, %1$x将以十进制和十六进制打印第一个参数 
<格式化前面说明的数值,%d%<x以十进制和十六进制打印同一个数值 

格式化说明符的语法图:

文件输入与输出

用File对象构造一个Scanner对象:

Scanner in = new Scanner(Paths.get("d:\\test.txt"), "UTF-8");
while (in.hasNextLine()) {
    System.out.println(in.nextLine());
}

Scanner in = new Scanner("myfile.txt"); // ERROR,scanner会将字符串解释为数据而不是文件名,这个scanner会将参数做为包含10个字符的数据。

要想写入文件,就需要构造一个PrintWriter,如果文件不存在,创建该文件:

PrintWriter out = new PrintWriter("myfile.text", "UTF-8");

 

8.控制流程

块(block),即复合语句,是指由一队大括号括起来的若干条简单的Java语句。块确定了变量的作用域,一个块可以嵌套到另一个块中。但是,不能在嵌套的两个块中声明同名的变量。

条件语句

if

if…else

if…else if

if…else if…else if…else

循环语句

1.for(表达式1;表达式2;表达式3)

首先计算表达式1,接着执行表达式2,若表达式2的值=true,则执行语句,接着计算表达式3,在判断表达式2的值;以此重复下去,直到表达式2的值=false

2.while(逻辑表达式){语句}

先判断逻辑表达式的值。若为true,则执行后面的语句,然后再次判断条件并反复执行,直到条件不成立为止。

3.do{语句}while(逻辑表达式);

先执行语句,再判断逻辑表达式的值,若为true,在执行语句,否则结束循环

public static void main (String[] array){
  int i = 0;
  while(i < 10){
   System.out.print(i+" ");
   i++;
  }
  i = 0;
  do{
   System.out.print(i+" ");
   i++;
  }while(i < 10);
 }

结果为:

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 

 

4.break和continue

break语句用于终止某个语句块的执行。用在循环语句体中,可以强行退出循环;

    public static void main (String[] array){
        int stop = 4;
        for(int i = 1; i <=10;i++){
            if(i == stop)break;                         //当i=stop时,退出循环
            System.out.println(" i= " +i);
        }
    }

结果为:

i=1;

i=2;

i=3;

continue语句用在循环体中,用于终止某次循环过程,跳过循环体中continue语句下面未执行的循环,开始下一次循环过程;

    public static void main (String[] array){
        int skip = 4;
        for(int i = 1;i <=5;i++){
            if(i == skip)continue;                   //当i等于skip时,跳过当次循环
            System.out.println("i="+i);
        }
    }

结果是:

i=1
i=2
i=3
i=5

循环语句举例,求101到200之间的质数

    public static void main (String[] array){
        for(int i=101;i<200;i+=2){
            boolean f =true;
            for(int j =2;j < i;j++){
                if(i%j== 0){
                    f= false;
                    break;
                }
            }
            if(!f){
                continue;}
            System.out.print(" "+ i);
        }
    }

结果是: 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173179 181 191 193 197 199

 

5.switch语句

switch(){

case xx:

……

case xx:

……

default:

……

}

(1).小心case穿透,后面跟上break;

(2).多个case可以合并到一起;

例:

int i = 8;
switch(i){
    case 8:
    case 3:
    case 2:
    System.out.printIn("A");
    break;
    ……
}

结果是:A

(3).default可以省略,但不推荐省略

(4).java中switch语句case标签可以是:

类型为char、byte、short、或int的常量表达式

枚举常量

从Java SE 7开始,case标签还可以是字符串字面量

当switch语句中使用枚举常量时,不必在每个标签中指明枚举名:

Size sz = Size.SMALL;
switch (sz) {
    case SMALL:  // 不必写Size.SMALL
        ...
        break;
    case BIG:
        ...
        break;
}

9.大数值

BigInteger和BigDecimal,这两个类可以处理包含任意长度数字序列的数值。

 

10.数组

数组是一种数据结构,用来存储同一类型值的集合。

在声明数组变量时,需要指出数组类型和数组变量名字:

int[] a;
int a[]; // java程序员更喜欢1的风格,因为它将类型int[]和变量名分开了

不过这条语句只声明了变量a,并没有将a初始化为一个真正的数组,应该使用new运算符创建数组:

int[] a = new int[100];

创建了一个可以存储100个整数的数组,长度不要求是常量。

创建一个数字数组时,所有元素都初始化为0,boolean数组元素都初始化为false,对象元素都初始化为null,这表示这些元素未存放任何对象。

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

一旦创建了数组,就不能再改变它的大小,如果在运行过程中扩展数组的大小,就应该使用另一种数据结构——数组列表(array list)。

 

for each循环

增强for循环:

for (variable : collection) statement

collection这一集合表达式必须是一个数组或一个实现了Iterable接口的类对象。

普通的for循环也可以实现同样的效果,但是for each循环语句更加简洁、更不易出错。

Arrays类的toString方法,返回一个包含数组元素的字符串,这些元素被放置在括号内,并用逗号分开,要想打印数组可以调用。

 

数组初始化以及匿名数组

在Java中提供了一种创建数组并赋值初始化的简化书写方式:

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

在使用这种语句时,不需要调用new,甚至可以创建一个匿名的数组:

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

使用这种语法可以不在创建新变量的情况下重新初始化一个数组。

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

Java中允许数组长度wei0,长度为0与null不同。

 

数组拷贝

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

int[] smallPrimes = {2, 3, 5, 7, 11, 13};
int[] luckNumber = smallPrimes;
luckNumber[5] = 12;
System.out.println(Arrays.toString(smallPrimes));
// [2, 3, 5, 7, 11, 13]

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

int[] smallPrimes = {2, 3, 5, 7, 11, 13};
int[] luckNumber = Arrays.copyOf(smallPrimes, smallPrimes.length);
luckNumber[5] = 12;
System.out.println(Arrays.toString(smallPrimes));
// [2, 3, 5, 7, 11, 13]

第2个参数是新数组的长度,这个方法可以用来增加数组大小:

luckNumber = Arrays.copyOf(luckNumber, luckNumber.length * 2);

如果是数值型,多余的元素都被赋值为0,如果长度小于原始数组的长度,则只拷贝最前面的数据元素。

 

命令行参数

main方法接收一个字符串数组,也就是命令行参数。

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.println("!");
    }
}
// Goodbye , cruel world!

程序名并没有存在args数组中,例如java Message -h world中args[0]是“-h”,而不是Message或java。

 

数组排序

Arrays.sort()使用了快速排序算法,对于大多数数据集合来说都是效率比较高的。

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    System.out.print("你需要抓取多少个数字?");
    int k = in.nextInt();
    System.out.print("你需要抓取的最大数字?");
    int n = in.nextInt();
    int[] numbers = new int[n];
    for (int i = 0; i < numbers.length; i++) {
        numbers[i] = i + 1;
    }
    int[] result = new int[k];
    for (int i = 0; i < result.length; i++) {
        int r = (int)(Math.random() * n); // 从0到 n-1 之间的一个随机数
        result[i] = numbers[r];
        numbers[r] = numbers[n - 1];
        n--;
    }
    Arrays.sort(result);
    System.out.println("结果:");
    for (int r : result) {
        System.out.println(r);
    }
}

 

多维数组

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

想快速打印一个二维数组元素列表,可以调用:

Arrays.deepToString();

 

不规则数组

Java实际没有多维数组,只有一维数组,多维数组被解释为“数组的数组”,这样方便构造一个不规则数组,即数组的每一行有不同的长度。

public class LotterArray {
    public static void main(String[] args) {
        final int NMAX = 10;

        // 指定三角形阵列
        int[][] odds = new int[NMAX + 1][];
        for (int n = 0; n <= NMAX; n++) {
            odds[n] = new int[n + 1];
        }

        // 填充三角形阵列
        for (int n = 0; n < odds.length; n++) {
            for (int k = 0; k < odds[n].length; k++) {
                int lotteryOdds = 1;
                for (int i = 1; i <= k; i++) {
                    lotteryOdds = lotteryOdds * (n -i + 1) / i;
                }
                odds[n][k] = lotteryOdds;
            }
        }
        for (int[] row : odds) {
            for (int odd : row) {
                System.out.printf("%4d", odd);
            }
            System.out.println();
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值