搜索
您的当前位置:首页正文

Java编程指南-Chapter5 核心类

来源:知库网

Chapter5 核心类

1.java.lang.String

java.lang.String是最常用的类之一,也绝对是最重要的类之一。String对象表示一个字符串,即一段文本。也可以把String当成一个Unicode字符序列,String对象可以由任意数量的字符组成,没有字符的String称为空String。String对象是常量,一旦创建,值就不能改变。因此,String实例是不可以改变的。正因为如此,它们可以安全共享。

可以用关键字new来构造String对象,但这不是创建String常用的方法。最常用的方法是给String引用变量赋一个字符串字面量,下面是一个这样的例子:

    String s = "Java is cool";

这样就生成了一个包含“Javaiscool”的String对象,并将它的一个引用赋值给s。下面是同样的例子:

    String message = new String("Java is cool");

但是给引用变量赋给一个字符串字面量和使用关键字new还是截然不同的,JVM始终会创建一个String新实例。

使用字符串字面量时则会得到一个相同的String对象,但这个对象并非始终是新的,如果字符串“Javaiscool”之前已经创建,那么它可能来自于一个内存池。

因此,使用字符串字面量更好一些,因为这样JVM可以节省一些用于构建新实例的CPU周期。为此,在创建String对象时很少使用关键字new。如果有特殊需要,比如将字符数组转换为String,那么可以使用String类的构造器。

1.1 比较两个String

字符串比较是Java中最常用的运算之一。如下:

    String s1 = "Java";
    String s2 = "Java";
    if(s1 == s2) {
        //...
    }

在这里,(s1 == s2)的运算结果为true,因为s1和s2引用相同的实例。另一方面,在下面的代码中,(s1 == s2)的运算结果为false,因为s1和s2引用不同的实例:

    String s1 = new String("Java");
    String s2 = new String("Java");
    if(s1 == s2) {
        //...
    }

这体现了通过编写字符串字面量和通过使用关键字new创建String对象这两者之间的不同。

用==运算符比较两个String对象的方法几乎没有什么用处,因为比较的是两个变量引用的地址。大多数时候会比较两个String对象的值是否相同,这种情况下就要用到String类的equals方法。

    String s1 = "Java";
    if(s1.equals("Java")) {
        //...比较结果为true
    }

有时会看到这种风格:

    if("Java".equals(s1))

在(s1.equals("Java"))中,调用s1的equals方法。如果s1位null,那么表达式就会产生一个运行时错误。为了安全,必须确保s1不能为null,因此先要检查引用变量是否为null:

    if(s1 != null && s1.equals("Java"))

在("Java".equals(s1))中,JVM会创建或者从内存池中采用包含"Java"的String对象,并调用它的equals方法。这里无需检查是否为null,因为"Java"显然不是null。如果s1为null,表达式值返回false。因此,下面两行代码的结果一样:

    if(s1 != null && s1.equals("Java"))
    if("Java".equals(s1))

1.2 String类的方法

String类提供了操作String值的方法,但由于String对象是不可改变的,因此操作的结果始终是一个新的String对象。下面是一些比较有用的方法:

  • public char charAt(int index)
    // 返回指定索引处的字符。例如,下面的代码返回“J”:
      "Java is cool".charAt(0);
  • public String concat(String s)
    // 把指定字符串加到这个String的末尾并返回结果。例如,下面的代码返回“Javaiscool":
      "Java".concat("iscool");
  • public boolean equals(String anotherString)
    // 比较这个String和anotherString的值,如果匹配,则返回true
    String s1 = "Java";
    if(s1.equals("Java")) {
        //...比较结果为true
    }
  • public int indexOf(String substring)
    // 返回指定子字符串首次出现的索引,如果没有匹配上,则返回-1。例如,下面的代码返回8:
    "Java is cool".indexOf("cool");

类似的,如下:

    public int indexOf(String substring, int fromIndex)
    //  返回从指定索引开始,指定子字符串首次出现的索引。如果没有匹配上,则返回-1。
    
    public int lastIndexOf(String substring)
    //  返回指定字符串最后出现的索引。如果没有匹配上的,则返回-1。
    
    public int lastIndexOf(String substring, int fromIndex)
    //  返回从指定索引开始,指定子字符串最后出现的索引。如果没有匹配上的,则返回-1。
    
    public String substring(int beginIndex)
    //  返回当前字符串的从指定索引开始的一个子字符串。
    
    public String substring(int beginIndex, int endIndex)
    //  返回当前字符串的从beginIndex到endIndex之间的一个子字符串。
    
    public String replace(char oldChar, char newChar)
    //  将当前字符串出现的每个oldChar替换成newChar并返回新的String。只替换字符。
    
    public int length()
    //  返回这个String中的字符数量
    
    public boolean isEmpty()
    //  测试String是否为空,返回true则为空
    
    public char[] toCharArray()
    //  将这个字符串转换为字符数组
    
    public String trim()
    //  去掉前后的空格,然后返回新的字符串。

此外还有两个静态方法:valueOf和format。valueOf方法将基本类型、char数组或者Object的实例转换为字符串表示形式。例如,下面的代码会返回字符串“cool”:

    char[] c = {'c','o','o','l'};
    String.valueOf(c);

2.StringBuffer和StringBuilder

String对象是不可改变的,并不适于给它们添加或者插入字符,因为对String的字符操作总是会创建一个新的String对象。对于添加和插入,最好使用java.lang.StringBuffer或者StringBuilder对象转换成String。

一直到JDK1.4之前,StringBuffer类都是只用于可变字符串的。StringBuffer中的方法是同步的,这让StringBuffer适用于多线程的环境。但是同步要付出的代价是性能的降低。JDK5增加了StringBuilder类,它是StringBuffer的非同步版本。如果不需要同步,应该选择StringBuilder类而不是StringBuffer类。

1.3.1 StringBuilder类的构造器

StringBuilder类有4个构造器,可以传递java.lang.CharSequence、String或者int。

    public StringBUilder()
    public StringBuilder(CharSequence seq)
    public StringBuilder(int capacity)
    public StringBuilder(String string) 

如果创建了一个未指定容量的StringBuilder对象,那么这个对象将有16个字符的容量。如果它的内容超过了16个字符,它就会自动变大。如果事先知道字符串会超过16个字符,最好给它配足容量,因为增加StringBuilder的容量也是要花时间的。

1.3.2 StringBuilder类的方法

StringBuilder类有好多个方法,主要有capacity、length、append和insert。如下:

    public int capacity()
    //  返回StringBuilder对象的容量
    
    public int length()
    //  返回StringBuilder对象存储的字符串长度,它的值小于等于StringBuilder的容量。
    
    public StringBuilder append(String string)
    //  将指定字符串String添加到所包含字符串的末尾。

此外,append有各种重载方法允许你传递基本类型、char数组和java.lang.Object实例。如下:

    StringBuilder sb = new StringBuilder(100);
    sb.append("Matrix ");
    sb.append(2);

运行完最后一行代码后,sb的内容是“Matrix 2”。

需要注意的一个重点是append方法返回StringBuilder对象本身,即调用append对象。因此,可以将调用和append连接起来。如下:

    sb.append("Matrix ").append(2);
    //  sb的内容仍是“Matrix 2”

还有insert方法,在offset指定的位置插入指定字符串:

    public StringBuilder insert(int offset, String string)

此外,insert有各种重载方法允许传递基本类型和java.lang.Object实例。例如:

    StringBuilder sb2 = new StringBuilder(100);
    sb2.append("night");
    sb2.insert(0,'k');  //  value = "knight"

和append一样,insert也返回当前的StringBuilder对象,因此也可以连接insert。

还有一种方法:toString():

    public String toString()
    //  返回表示StringBuilder值的String对象

3.基本类型封装

出于性能考虑,并不是Java中的一切都是对象,也有基本类型,比如,int、long、float、double,等等。使用基本类型和对象时,经常需要将基本类型和对象进行转换。例如java.util.Collection对象可以用来存储对象,不能存储基本类型。如果要在Collection中存储基本类型,那就必须先将它们转换为对象。

java.lang包中有多个作用相当于基本类型封装的类。它们是Boolean、Character、Byte、Double、Float、Integer、Long和Short。

基本类型和对象间可以很容易地进行相互转换,这得归功于装箱和拆箱机制。

4.数组

在Java中,可以把同一类型的基本类型或者对象放到一起组成数组。属于一个数组的实体称为数组组件。每当创建一个数组时,编译器都会在后台创建一个对象,它允许你:

  • 通过length域得到数组中组件的数量,数组的长度就是它组件的数量;
  • 通过指定索引来访问每个组件。索引是以0位基数的,索引0是指第一个组件,以此类推;

数组的所有组件具有相同的类型,这个类型称为数组的组件类型。数组的大小是不可改变的,没有组件的数组称为空数组。

数组是一种Java对象,因此可以把引用数组的变量和其他引用变量同等对待。例如,可以把它和null进行比较:

    String[] names;  //  不可以直接这样写,必须要使用关键字new来创建数组
    if(names == null)  //

要使用数组,首先需要声明数组可以使用下面的语法声明数组:

    type[] arrayName;
    //  或者
    type arrayName[];

例如,下面的代码声明了一个名为numbers的long数组:

    long[] numbers;

声明数组不会创建数组或者给它的组件分配空间,编译器只是创建了一个对象引用。使用关键字new是创建数组的一个方法,必须指定创建数组的大小:

    new type[size]
    //  这只是创建方式,不能直接这样写

通常是在同一行声明并创建数组:

    int[] number = new int[6];

要想引用数组的组件,那么就在变量名后加一个索引。例如,下面的代码段创建了一个有4个String对象的数组,并初始化了它的第一个成员:

    String[] names = new String[4];
    names[0] = "Lynn";

引用超出范围的成员将会产生运行时错误,确切的说,将会抛出java.lang.ArrayIndexOutOfBoundException。

不使用关键字new也可以创建并初始化数组。Java允许通过将值放到一对大括号中来创建数组。例如,下面的代码创建了一个有3个String对象的数组:

    String[] names = {"John", "Mary", "Paul"};

将数组传递给方法时要注意,下面的代码就是非法的,即使其中的average方法接收int数组:

    int avg = average({1, 2, 3, 10});
    //  非法

你必须要单独实例化数组:

    int[] numbers = {1, 2, 3, 10};
    int avg = average(numbers);

或者这样:

    int avg = average(new int[] {1, 2, 3, 10});

4.1 迭代数组

在Java5之前,迭代数组成员唯一的方法就是使用for循环和数组索引。例如,下面的代码时迭代变量names引用的String数组:

    String[] names = {"John", "Mary", "Paul"};
    for(int i = 0; i < names.length; i++) {
        System.out.println("\t- " + names[i]);
    }
    //  输出:
    //  - John
    //  - Mary
    //  - Paul

Java5增强了for语句,因此现在不用索引就可以使用它迭代数组或集合。可以使用下面的语法迭代数组:

    for(componentType variable : arrayName)

这里的arrayName是数组的引用,componentType是数组的组件类型,variable是引用数组中每个组件的变量。例如,下面的代码迭代String数组:

    String[] names = {"John", "Mary", "Paul"};
    for(String name : names) {
                System.out.println("\t- " + name);
    }
    //  输出:
    //  - John
    //  - Mary
    //  - Paul

4.2 修改数组大小

一旦创建了数组,它的大小就不能改变了。如果想改变它的大小,那必须要创建一个新的数组并用原数组的值填充它。例如,下面的代码增大了numbers的大小,将3个int的数组增大为4个:

    int[] numbers = {1, 2, 3};
    int[] temp = new int[4];
    int length = numbers.length;
    for(int j = 0; j < length; j++) {
        temp[j] = numbers[j];
    }
    numbers = temp;
    System.out.println(numbers.length); //  输出 4

一个更为简便的方法是使用java.util.Arrays类的copyOf方法。例如,下面的代码创建了一个有4个元素的数组,并将numbers的内容复制到它的前三个元素:

int[] newArray = Arrays.copyOf(numbers, 4);
for(int num : numbers) {
    System.out.println(num);
}
System.out.println(numbers.length);
for(int num : newArray) {
    System.out.println(num);
}
System.out.println(newArray.length);

这个方法并没有增大numbers数组的大小,只是新创建了一个规定长度的新数组,并且把要复制的数组的元素复制给新数组。

当然也可以将新数组赋值给原始变量:

    numbers = Arrays.copyOf(numbers, 4);

此时原数组的长度改变,与新数组一样了。现在输出numbers数组的值为:1,2,3,0。

copyOf方法有10种重载方法,其中8个用于各个Java基本类型,2个用于对象。下面是它们的签名:

    public static boolean[] copyOf(boolean[] original, int newLength)
    public static byte[] copyOf(byte[] original, int newLength)
    public static char[] copyOf(char[] original, int newLength)
    public static double[] copyOf(double[] original, int newLength)
    public static float[] copyOf(float[] original, int newLength)
    public static int[] copyOf(int[] original, int newLength)
    public static long[] copyOf(long[] original, int newLength)
    public static short[] copyOf(short[] original, int newLength)
    public static <T> T[] copyOf(T[] original, int newLength)
    public static <T, U> T[] copyOf(U[] original, int newLength, java.lang.Class<? extends T[]> newType)

(看的我眼睛都花了……)如果original为null,则每种重载方法都可以抛出一个java.lang.NullPointerException,如果newLength为负值,则抛出一个java.lang.NegativeArraySizeException

newLength参数可以小于、等于或者大于原数组的长度。如果是小于,只有第一个newLength元素会复制到副本中;如果是大于,那么最后几个元素就会是缺省值,即如果是整数数组,默认为0,如果是对象数组,默认为null。

与copyOf方法类似的另一个方法是copyOfRange。它是将一系列元素复制到一个新的数组中。与copyOf一样,copyOfRange也给每种Java数据类型提供了重载方法,签名就不写了,好累。有兴趣再去查API手册。

5.其他

5.1 java.lang.Class

5.2 java.lang.System

这两个没啥精力码字了,以后有空再补上。

Top