字符串

子串

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

String greeting = "Hello"; 
​
String s = greeting.substring(0,3);

创建了一个由字符“ Hel” 组成的字符串。

拼接

String a="Hello ";
String b="World!";
String c=a+b;//c="Hello Wrold!"
int age=18;
String My_age="age= "+age//My_age="age= 18"
//多个字符串进行拼接时,使用静态方法String.join(str0,str1,str2,...);str0为定界符,如:
System。out.println(String.join(" / ", "S", "M", "L", "XL"));//输出结果为:S / M / L / XL

 

不可变字符串

String类没有提供用于修改字符串的方法6 如果希望将 greeting 的内容修改为“ Help!”, 不能直接地将 greeting的最后两个位置的字符修改为 ‘ p’ 和 ‘ !' 、这对于 C 程序员来说,将会感到无从下手 。如何修改这个字符串呢? 在 Java中实现这项操作非常容易。首先提取需 要的字符, 然后再拼接上替换的字符串:

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

上面这条语句将 greeting 当前值修改为“ Help !”。

 

检测字符串是否相等

可以使用 equals方法检测两个字符串是否相等。对于表达式:

s.equals(t)

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

空串与 Null 串

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

if (str.length()= 0)

if (str.equals(""))

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

if (str == null)

有时要检查一个字符串既不是 null 也不为空串,这种情况下就需要使用以下条件:

if (str != null && str.lengthO != 0)

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

 

码点和代码单元

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

调用s.charAt(n) 将返回位置 n 的代码单元,n 介于 0 ~ s.length()-l 之间。例如:

char first = greeting.charAtO); // first is 'H' 
char last = greeting.charAt(4); // last is ’o’ 

要想得到第 i 个码点,应该使用下列语句

int index = greeting.offsetByCodePoints(0,i); 
​
int cp = greeting.codePointAt(index); 
[注]  类似于 C 和 C++, Java 对字符串中的代码单元和码点从 0 开始计数。

使用 UTF-16 编码表示字符⑪(U+1D546) 需要两个代码单元。调用

char ch = sentence.charAt(i );

返回的不是一个空格,而是⑪的第二个代码单元。为了避免这个问题, 不要使用 char 类型。

这太底层了。 如果想要遍历一个字符串,并且依次査看每一个码点, 可以使用下列语句:

int i=0;
String sentence="Hello world!";
int cp = sentence.codePointAt(i); 
if (Character.isSupplementaryCodePoint(cp)) {
    i += 2; 
    }else{
    i++; 
    }

 

可以使用下列语句实现回退操作:

int i=0;
String sentence="Hello world!";
int cp = sentence.codePointAt(i); 
if (Character.isSupplementaryCodePoint(cp)) {
    i += 2; 
    }else{
    i++; 
    }

 

显然, 这很麻烦。更容易的办法是使用 codePoints 方法, 它会生成一个 int 值的“ 流”, 每个 int 值对应一个码点。可以将它转换为一个数组,再完成遍历。

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

 

反之,要把一个码点数组转换为一个字符串, 可以使用构造函数。

String str = new String(codePoints, 0, codePoints.length);

 

构建字符串

有些时候, 需要由较短的字符串构建字符串, 例如, 按键或来自文件中的单词。采用字 符串连接的方式达到此目的效率比较低。每次连接字符串, 都会构建一个新的 String 对象, 既耗时, 又浪费空间。使用 StringBuildei•类就可以避免这个问题的发生。

如果需要用许多小段的字符串构建一个字符串, 那么应该按照下列步骤进行。 首先, 构 建一个空的字符串构建器:

StringBuilder builder = new StringBuilder(); 

 

当每次需要添加一部分内容时, 就调用 append 方法。

 builder.append(ch); // appends a single character 
​
bui1der.append(str); // appends a string 

 

在需要构建字符串时就凋用 toString 方法, 将可以得到一个 String 对象, 其中包含了构建器 中的字符序列。

String completedString = builder.toString(); 

 

输入输出

读取输入

前面已经看到,打印输出到“ 标准输出流”(即控制台窗口)是一件非常容易的事情,只要 调用 System.out.println 即可。然而,读取“ 标准输人流” System.in就没有那么简单了。要想通 过控制台进行输人,首先需要构造一个 Scanner 对象,并与“ 标准输人流” System.in 关联。

Scanner in = new Scanner(System.in);
System.out.print("What is your name? ");
String name = in.nextLine();//读取有空格的字符串
String firstName = in.next();//读取没有空格的字符串
System.out.print("How old are you? ");
int age = in.nextlnt();//读取整型数据 
int meny = in.nextlnt();//读取浮点型数据

 

格式化输出

int a=11;
char b='m';
String c="abc";
float d=12.3333f;
System.out.println(a);
System.out.print(a);
System.out.printf("a=%d,b=%c,c=%s,d=%.2f",a,b,c,d);

 

 

可以使用静态的 String.format 方法创建一个格式化的字符串, 而不打印输出:

 String message = String.format("Hello, %s. Next year, you'll be %d", name, age); 

 

打印当前的日期和时间:

System.out.printf("%tc", new java.util.Date());

 

System.out.printf("%1$s %2$tB %2$te, %2$tY", "Due date:", new java.util.Date());
//打印出 Due date: 八月 5, 2020

 

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

 

 

文件输入与输出

要想对文件进行读取,就需要一个用 File 对象构造一个 Scanner 对象,如下所示:

 Scanner in = new Scanner(Paths.get("D:\\myfile.txt"), "UTF-8");

 

如果文件名中包含反斜杠符号,就要记住在每个反斜杠之前再加一个额外的反斜杠:

"D:\\myfile.txt"

现在,就可以利用前面介绍的任何一个 Scanner方法对文件进行读取。

要想写入文件, 就需要构造一个 PrintWriter 对象。在构造器中,只需要提供文件名:

PrintWriter out = new PrintWriter("D:\myfile.txt", "UTF-8");

 

如果文件不存在,创建该文件。可以像输出到 System.out—样使用 print、 println 以及 printf 命令。

public static strictfp void main(String args[]) throws IOException{
PrintWriter out = new PrintWriter("D:\\myfile.txt", "UTF-8");
String string="I Love Learning!";
out.println(string);//将string字符串打印在myfile.txt文件中
out.close();
​
Scanner in = new Scanner(Paths.get("D:\\myfile.txt"), "UTF-8");
String s=in.nextLine();//从myfile.txt文件中读取包含空格的字符串,并将其赋值给变量s
System.out.println(s);
in.close();
}

 

[警告]  可以构造一个带有字符串参数的 Scanner, 但 这 个 Scanner 将字符串解释为数据, 而不是文件名。例如, 如果调用: Scanner in = new Scannerr(“myfile.txt"); // ERROR? 这个 scanner 会将参数作为包含 10 个字符的数据: ‘ m’, ‘ y’, ‘ f’ 等。在这个示 例中所显示的并不是人们所期望的效果。

省指定一个相对文件名时,例如, “ myfile.txt”, “ mydirectory/myfile.txt” 或“ ../myfile.txt”, 文件位于 Java 虚拟机启动路径的相对位置 , 如果在命令行方式下用下列命令启动程序:

java MyProg

启动路径就是命令解释器的当前路後。 然而,如果使用集成开发环境, 那么启动路 径将由 IDE 控制。 可以使用下面的调用方式找到路径的位置:

String dir = System.getProperty("user.dir");

如果觉得定位文件比较烦恼, 则可以考虑使用绝对路径, 例如: “ c:\mydirectory\ myfile.txt” 或者“ /home/me/mydirectory/myfile.txt” 。

 

当采用命令行方式启动一个程序时, 可以利用 Shell 的重定向语法将任意文件关联 到 System.in 和 System.out:

java MyProg <D:\myfile.txt > output.txt

这样,就不必担心处理 IOException 异常了。

控制流程

在深入学习控制结构之前,需要了解块(block) 的概念。

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

public static void main(String[] args) { 
    int n; 
    ...
    {
        int k;
        ...
    } // k is only defined up to here
} 

 

但是,不能在嵌套的两个块中声明同名的变量。例如,下面的代码就有错误,而无法通过编译:

public static void main(String[] args) { 
    int n; 
    ...
    {
        int  n;//Error can't redefine n in inner block
int k;
        ...
    }
} 

 

[注]  在 C++ 中, 可以在嵌套的块中重定义一个变量。在内层定义的变量会覆盖在 外层定义的变量。这样,有可能会导致程序设计错误, 因此在 Java 中不允许这样做。

条件语句

在 Java 中,条件语句的格式为

 if (condition) statement 

这里的条件必须用括号括起来。

[注]  使用块 (有时称为复合语句)可以在 Java 程序结构中原本只能放置一条(简单)语 句的地方放置多条语句。

if (yourSales >= target) { 
    performance = "Satisfactory"; 
    bonus = 100 + 0.01* (yourSales - target);
} else { 
    performance = "Unsatisfactory"; bonus = 0; 
}

 

其中 else 部分是可选的。else 子句与最邻近的 if 构成一组。

if (yourSales >= 2 * target) 
{ 
    performance = "Excellent"; 
    bonus = 1000; 
}
else if (yourSales >= 1.5 * target) 
{ 
    performance = "Fine"; 
    bonus = 500; 
} 
else if (yourSales >= target) 
{ 
    performance = "Satisfactory";
    bonus = 100; 
} 
else 
{ 
    System.out.println("You're fired"); 
}
​

 

 

循环

当条件为 true 时,while 循环执行一条语句(也可以是一个语句块) 。一般格式为

while (condition) statement 

如果开始循环条件的值就为 false, 则 while 循环体一次也不执行,如下图所示:

程序将计算需要多长时间才能够存储一定数量的退休金,假定每年存 人相同数量的金额,而且利率是固定的。

import java.util.*;
​
/**
 * This program demonstrates a <code>while</code> loop.
 * @version 1.20 2004-02-10
 * @author Cay Horstmann
 */
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.");
   }
}

 


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

do statement while (condition); 

这种循环语句先执行语句 (通常是一个语句块), 再检测循环条件;然后重复语句’ 冉检测循 环条件, 首先计算退休账户中的余额,然后再询问是否打算退休:

import java.util.*;
​
/**
 * This program demonstrates a <code>do/while</code> 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 isJava %,.2f%n", year, balance);
​
         // ask if ready to retire and get input
         System.out.print("Ready to retire? (Y/N) ");
         input = in.next();
      }
      while (input.equals("N"));
   }
}

 


确定循环

for 循环语句是支持迭代的一种通用结构, 利用每次迭代之后更新的计数器或类似的变量 来控制迭代次数。下面的程序将数字 1 ~ 10 输出到屏幕上。

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

 

[注]  在循环中,检测两个浮点数是否相等需要格外小心。下面的 for 循环 for (double x = 0; x != 10; x += 0.1) . . . 可能永远不会结束。 由于舍入的误差, 最终可能得不到精确值。 例如, 在上面的 循环中, 因为 0.1 无法精确地用二进制表示, 所以,x 将从 9.999 999 999 999 98 跳到 10.099 999 999 999 98。

下面程序是一个应用 for 循环的典型示例。这个程序用来计算抽奖中奖的概率。例 如, 如果必须从 1 ~ 50 之间的数字中取 6 个数字来抽奖,那么会有 (50x 49 x 48 x 47x 46 x 45)/ (1 x 2 x 3 x 4 x 5x 6) 种可能的结果,所以中奖的几率是 1/15890700。祝你好运!

import java.util.*;
​
/**
 * This program demonstrates a <code>for</code> 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!");
   }
}
​

 

[注]  “ 通用 for 循环”(又称为 for each 循环) , 这是 Java SE 5.0 新增 加的一种循环结构。

多重选择:switch 语句

switch语句将从与选项值相匹配的 case标签处开始执行直到遇到 break语句,或者执行到 switchi吾句的结束处为止。如果没有相匹配的 case 标签, 而有 default 子句, 就执行这个子句。

switch (key) {
        case value:
            
            break;
​
        default:
            break;
        }

 

[警告]  有可能触发多个 case 分支。如果在 case 分支语句的末尾没有 break 语句, 那么就 会接着执行下一个 case 分支语句。这种情况相当危险, 常常会引发错误。 为此,我们在 程序中从不使用 switch 语句 如果你比我们更喜欢 switch 语句, 编译代码时可以考虑加上-Xlint:fallthrough 选项, 如下所示: javac -Xlint:fallthrough Test.java 这样一来, 如果某个分支最后缺少一个 break 语句, 编译器就会给出一个警告消息。 如果你确实正是想使用这种“ 直通式”(fallthrough) 行为, 可以为其外围方法加一个 标注@SuppressWamings("fallthrough")。 这样就不会对这个方法生成警告了 。 (标注是为编译器或处理 Java 源文件或类文件的工具提供信息的一种机制。 )

case标签可以是:

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

•枚举常量。

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

 

中断控制流程语句

goto;//不建议使用
break;
continue;

大数值

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

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

Biglnteger a = Biglnteger.valueOf(100);

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

Biglnteger c = a.add(b); // c = a + b 
​
Biglnteger d = c.nultipiy(b.add(Biglnteger.valueOf(2))); // d = c * (b + 2)

 

中彩概率程序的改进, 使其可以采用大数值进行运算。 假设你被邀请参加抽奖活动, 并从 490 个可能的数值中抽取 60 个, 这个程序将会得到中彩 概率 1/71639584346199555741511622254009293341171761278926349349335101345948110466 8848。祝你好运!

import java.math.*;
import java.util.*;
​
/**
 * This program uses big numbers to compute the odds of winning the grand prize in a lottery.
 * @version 1.20 2004-02-10
 * @author Cay Horstmann
 */
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!");
   }
}

 

 

数 组

数组是一种数据结构, 用来存储同一类型值的集合。通过一个整型下标可以访问数组中 的每一个值。

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

 int[] a;

int a[];

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

 

for each 循环

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

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

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

for (int i = 0; i < a.length; i++) Java

System,out.println(a[i]);

但是,for each 循环语句显得更加简洁、更不易出错(不必为下标的起始值和终止值而操心)。

[注]  foreach 循环语句的循环变量将会遍历数组中的每个元素, 而不需要使用下标值。

数组初始化以及匿名数组

在 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 中, 允许数组长度为 0。在编写一个结果为数组的方法时, 如果碰巧结果 为空, 则这种语法形式就显得非常有用。此时可以创建一个长度为 0 的数组: new elementType[0] 注意, 数组长度为 0 与 null 不同。

数组拷贝

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

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

 

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

int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);

 

第 2 个参数是新数组的长度。这个方法通常用来增加数组的大小:

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

 

如果数组元素是数值型,那么多余的元素将被赋值为 0 ; 如果数组元素是布尔型,则将赋值 为 false。相反,如果长度小于原始数组的长度,则只拷贝最前面的数据元素。

Java 数组与 C++ 数组在堆栈上有很大不同, 但基本上与分配在堆(heap) 上 的数组指针一样。也就是说,

int[] a = new int[100]; // Java

不同于

int a[100]; // C++

而等同于

int* a = new int[100]; // C++

 

Java 中的 [ ]运算符被预定义为检查数组边界,而且没有指针运算, 即不能通过 a 加 1 得到数组的下一个元素。

命令行参数

每一个 Java应用程序都有一个带 String arg[]参数的 main方法。这个参数表明 main方法将接收一个字符串数组, 也就是命令行参数 3 例如, 看一看下面这个程序:

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].equa1s("-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("!"); 
     } 
}

 

如果使用下面这种形式运行这个程序:

java Message -g cruel world args 数组将包含下列内容:

args[0] :"-g"

args[1]:"cruel"

args[2]: "world"

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

Goodbye, cruel world!

posted on 2020-08-05 22:25  ♌南墙  阅读(256)  评论(0)    收藏  举报