一.数字
在用到数字时,大多数情况下我们都会使用基本数据类型。例如:
int i = 500;
float gpa = 3.65f;
byte mask = 0xff;
然而,有时候我们既需要用到数字又需要用到对象。Java为每个基本数据类型都提供了包装类。这些类将基本数据类型包装在对象中。通常,这个包装动作是由编译器完成的。当你在需要使用包装类的时候使用基本数据类型,编译器将会把这个基本数据类型包装到包装类中去,这种行为称为装箱;当你在需要使用基本数据类型的时候使用包装类,编译器会把基本数据类型从包装类的对象中取出来,这种行为称为拆箱。在本文的最后一小节将会对自动装箱和拆箱进行介绍。
所有的数字包装类都是抽象类Number的子类:
注: 还有4个Number类的子类本文不会讨论。BigDecimal和BigInteger用于高精度计算,AtomicInteger和AtomicLong则用于多线程程序中。
在以下情形中,你应该使用Number类而不是基本数据类型:
- 方法的参数是一个对象(通常是在操作数字集合时);
- 要使用类定义的常量,例如MIN_VALUE和MAX_VALUE,它们提供了数据类型的上下限;
- 要使用基本数据类型之间互相转换的方法,基本数据类型与字符串相互转换的方法,以及进制间互相转换的方法。
下面的表格列出了所有Number类的子类都实现的方法:
每个Number类的子类都包含了很多与字符串和其他进制间转换的比较实用的方法。下面的表格列出了Integer类中的这些方法,其他子类的这些方法较为类似,不再一一列出。
Java支持基本数学运算以及它们的运算符:+,-,*,/和%。此外,位于java.lang包中的Math类还提供了执行高级运算的方法和常量。Math类中的方法都是静态的,你可以通过类名去直接调用它们:
Math.cos(angle);
Math类包含两个常量:
- Math.E,自然常数的近似值
- Math.PI,圆周率的近似值
Math类中包含了超过40个静态方法,下面列出了一下比较基础的方法:
下表列出了Math类中的指数和对数方法:
下表中总结了Math类中提供的一系列三角函数。每个方法的参数都以弧度表示,可以使用toRadians方法将角度转换为弧度。
Math类中的random()方法还可以用来生成随机数。该方法返回一个介于0.0到1.0之间(包括0.0但不包括1.0)的伪随机数。要获取不同范围内的数字,可以对方法返回的结果进行一些运算,例如,要生成0到9之间的整数,可以这样写:
int number = (int)(Math.random() * 10);
当需要生成单个随机数时,使用这个方法的效果很好。如果需要生成一系列随机数,则应该使用java.util包中的Random类中的方法。
二.字符
大部分情况下,如果要使用单个字符,我们都会用char类型。例如:
char ch = 'a';
// Unicode for uppercase Greek omega character
char uniChar = 'u03A9';
// an array of chars
char[] charArray = { 'a', 'b', 'c', 'd', 'e' };
不过,有时候我们仍然需要使用对象来表示字符。Java提供了一个包装类Character,可以将字符包装到这个对象中。此外,这个类还提供了很多操作字符的方法。
可以使用Character类的构造方法来创建一个Character对象:
Character ch = new Character('a');
在某些情况下,Java编译器会自动为你创建一个Character对象。例如,如果你将char类型传递给一个需要对象作为参数的方法,则编译器会自动将char类型转换为Character对象。反之也成立,在需要char类型的时候使用Character对象,Java编译器也会自动将Character对象转换为char类型。这个特性自动装箱(或拆箱),我们马上会在下一小节中介绍有关装箱和拆箱的内容。
下边列出了Character类中比较实用的一些方法:
某些字符前面加上反斜杠()表示转义字符,对于编译器来说有特殊的含义。下表列出了Java中的转义字符:
当字符串中出现转义字符时,编译器会首先对它进行转义。例如,你要输出下面的语句:
She said "Hello!" to me.
那么你可以这样写:
System.out.println("She said "Hello!" to me.");
三.自动装箱和拆箱
自动装箱是指Java编译器自动将基本数据类型转换为对应的包装类的行为,例如将int转为Integer,double转为Double。同理,自动拆箱是指将包装类转为对应的基本数据类型的行为。
下面是一个自动装箱的例子:
Character ch = 'a';
当出现以下情况时,编译器将会启用自动装箱:
- 将基本数据类型传递给需要包装类作为参数的方法
- 将基本数据类型赋值给包装类的对象
例如:
List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2)
li.add(i);
li是Integer的集合,li.add()需要Integer作为参数。但是这里我们传递的变量i是int类型,代码仍然可以通过编译。这是因为编译器在编译时自动调用了Integer.valueOf()方法将i转换为了Integer对象:
List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2)
li.add(Integer.valueOf(i));
当出现以下情况时,编译器将会启用自动拆箱:
- 将包装类传递给需要基本数据类型作为参数的方法
- 将包装类赋值给基本类型的变量
考虑下面的程序:
public static int sumEven(List<Integer> li) {
int sum = 0;
for (Integer i: li)
if (i % 2 == 0)
sum += i;
return sum;
}
由于%和+运算符不能应用于Integer对象,因此编译器在编译时自动调用了intValue方法将Integer转为int:
public static int sumEven(List<Integer> li) {
int sum = 0;
for (Integer i : li)
if (i.intValue() % 2 == 0)
sum += i.intValue();
return sum;
}
自动装箱和拆箱使开发人员可以编写更清晰、更简洁的代码,使其易于阅读。下表列出了基本数据类型及其对应的包装类:
四.字符串
字符串在Java中被广泛使用,它的本质是一连串字符。Java提供了String类来创建和操作字符串。
1.创建字符串
创建字符串最直接的方式就像下面这样:
String greeting = "Hello world!";
在这个例子中,"Hello world!"是一个字符串常量。字符串常量是指用双引号括起来的一连串字符。每当编译器在代码中遇到字符串时,就会创建一个String对象。因此,也可以在字符串常量上调用String类的方法,因为它们就是字符串对象。
与其他对象一样,也可以使用构造器来创建String对象。String类有13个构造器,具体可以参考API文档。下面的例子使用一个字符数组作为参数来创建String对象:
char[] helloArray = { 'h', 'e', 'l', 'l', 'o', '.' };
String helloString = new String(helloArray);
System.out.println(helloString);
String类是不可变的。因此一旦创建一个String对象之后就不能对它进行修改。String类中虽然提供了一些操作字符串的方法,但由于Java中字符串的不可变性,这些方法真正做的是创建并返回包含操作结果的新字符串。
2.字符串的长度
通过String类的length方法,可以获取字符串对象中包含的字符数。下面的程序将会输出17:
String palindrome ="Dot saw I was Tod";
System.out.println(palindrome.length());
回文是说一个词语或句子是对称的,即从前往后和从后往前的字符顺序是相同的(忽略大小写和标点符号)。下面的程序将一个会问字符串反转,它用到了charAt(i)方法,这个方法会返回字符串的第i个字符:
public class StringDemo {
public static void main(String[] args) {
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
char[] tempCharArray = new char[len];
char[] charArray = new char[len];
// put original string in an array of chars
for (int i = 0; i < len; i++) {
tempCharArray[i] = palindrome.charAt(i);
}
// reverse array of chars
for (int j = 0; j < len; j++) {
charArray[j] = tempCharArray[len - 1 - j];
}
String reversePalindrome = new String(charArray);
System.out.println(reversePalindrome);
}
}
运行该程序会产生以下输出:
doT saw I was toD
在这个例子中,程序先将字符串转换为一个字符数组,然后将这个数组反转为第二个数组,最后使用这个数组构造了一个字符串。String类中的getChars方法可以将字符串或字符串的一部分转换为字符数组,所以上面的第一个循环可以使用以下语句代替:
palindrome.getChars(0, len, tempCharArray, 0);
上面的语句代表将palindrome字符串中从索引为0到len(不包括len)的字符复制到tempCharArray数组中从索引为0开始的位置。
3.拼接字符串
String类中包含一个用来拼接两个字符串的方法:
string1.concat(string2);
这将会返回一个将string2添加到string1之后得到的新的字符串。
在拼接字符串时,更一般的做法是使用+运算符:
"Hello," + " world" + "!"
上面的运算结果是:
"Hello, world!"
+运算符被广泛地应用在print语句中,例如:
String string1 = "saw I was ";
System.out.println("Dot " + string1 + "Tod");
将会输出:
Dot saw I was Tod
4.将数字转换为字符串
在第一小节中,我们已经知道可以使用包装类中的方法将字符串转换为数字,例如Integer类的parseInt和valueOf可以将字符串分别转换为int类型和Integer对象。那么如何将数字转换为字符串呢?有几种简单的方法可以将数字转换为字符串。
将数字与空字符串相加:
int i = 1;
String s1 = "" + i;
使用String类的valueOf也可以将数字转换为字符串:
int i = 2;
String s2 = String.valueOf(i);
每个包装类都包含一个带参数的toString方法,它可以将给定的参数转换为字符串:
int i = 3;
String s3 = Integer.toString(i);
5.获取子串
如果要从一个字符串中获取子串,可以使用substring方法。substring方法有以下两个版本:
例如,如果要从下面的字符串中获取“roar”这个单词,可以这样写:
String anotherPalindrome = "Niagara. O roar again!";
String roar = anotherPalindrome.substring(11, 15);
6.在字符串中查找字符或子串
String提供了在字符串中查询指定字符或字符串的位置的方法:indexOf和lastIndexOf。indexOf方法从前向后查找,而lastIndexOf方法则是从后向前查找。如果找不到,它们将会返回-1。
String类还提供了一个contains方法,它可以用来判断字符串中是否包含给定的字符序列。当只需要判断是否包含某字符序列而不关心其位置时,可以使用这个方法。
注:CharSequence是String类实现的一个接口。因此,你可以使用字符串作为contains方法的参数。
7.替换字符串中的字符或子串
String类中有四个方法可以用来替换字符或子串,它们分别是:
上面的第三个和第四个方法用到了正则表达式,有关这一部分的内容会在以后的教程中进行介绍。
下面的例子分别使用了上面的四个方法:
String s = "Hello World!";
String s1 = s.replace('o', 'b');
String s2 = s.replace("ll", "bb");
String s3 = s.replaceAll("[aeiou]", "b");
String s4 = s.replaceFirst("[aeiou]", "b");
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
正则表达式[aeiou]用于匹配a、e、i、o、u中的任意一个字符。上面的例子输出如下:
Hellb Wbrld!
Hebbo World!
Hbllb Wbrld!
Hbllo World!
8.字符串的比较
String类中提供了很多用于比较整个字符串或字符串的某一部分的方法:
9.其他比较常用的字符串方法
除了上面的这些方法,String类的方法还有很多,不再一一列举。最后再介绍几个比较常用的方法:
五.StringBuilder类
StringBuilder类也可以用来存储字符串,但与String不同,它可以被修改。它可以被看做是包含字符序列的变长数组,因此,可以通过方法调用来改变字符系列的长度和内容。
除非是为了编写更简洁的代码或更好的性能,否则应该始终使用String。例如,如果你需要拼接非常多的字符串,那么使用StringBuilder的append方法会更加高效。
与String类似,StringBuilder也有length()方法,可以用来获取StringBuilder对象中的字符序列的长度。此外,StringBuilder类中还有一个capacity()方法,可以用来获取当前StringBuilder对象的容量。当字符序列的长度达到所能容纳的最大字符数时,StringBuilder将会自动进行扩容。
StringBuilder一共有4个构造方法:
下面是StringBuilder中常用的方法: