Java语言程序设计— Java基础类库—基本类型的包装类、JDK5.0新特性、自动装箱和拆箱、Scanner类、String类、StringBuffer类和StringBuilder类
已经学习了异常( Java语言程序设计—异常—自定义异常、断言、异常的使用原则 Java语言程序设计—异常—异常的概念、异常的类型、异常的处理 ),Java以基础类库JFC(Java Foundation Class)的形式为程序员提供编程接口API,Java有丰富的基础类库,通过这些基础类库可以提高开发效率,降低开发难度,例如,要通过Java实现日历的功能,没有编过程序的人很难想象这样的功能如何实现,但是对于有经验的开发人员来说,就会知道Java基础类库中的Date类和Calendar类专门用来处理日期和时间,对于这些类库并不需要刻意去背,而是需要经过多次使用后熟练掌握,对于不熟悉的类库,可查阅Java API文档进行了解使用。本篇对Java基础类库中的常用类进行讲解。
Ø 基本类型的包装类
通过前面的学习大家了解到,Java语言是一种面向对象的语言,但是Java中的基本数据类型却是不面向对象的,这在实际使用时会存在很多的不便,很多方法都需要引用类型的对象,无法将一个基本类型的值传入,为了解决这个不足,JDK中提供了一系列的包装类可以将基本数据类型的值包装为引用数据类型的对象。Java中的8种基本数据类型都有与之对应的包装类,如表所示。
| 基本数据类型 | 包装类 |
| int | Integer |
| char | Character |
| float | Float |
| double | Double |
| byte | Byte |
| long | Long |
| short | Short |
| boolean | Boolean |
表中列举了8种基本数据类型对应的包装类,包装类和基本数据类型进行转换时要涉及两个概念——装箱和拆箱,装箱是指将基本数据类型的值转为引用数据类型的对象,拆箱是指将引用数据类型的对象转为基本数据类型,接下来以int类型的包装类Integer为例来学习装箱和拆箱。首先来了解一下Integer类的构造方法,如表所示。
表中列举出Integer类的构造方法,它还有一些常用方法,如表所示。
表中列举出Integer类的常用方法,接下来通过一个案例来演示包装类Integer的装箱过程,如例所示。
程序的运行结果如图所示。
了解装箱后,接下来通过一个案例学习一下拆箱的过程,如例所示。
程序的运行结果如图所示。
上面是用包装类Integer为例讲解的装箱和拆箱,其他几个包装类的操作类似,一些细小的不同可以查阅JDK使用文档。
脚下留心
在包装类使用过程中,有一些需要注意的问题,这里还是以Integer类为例,首先,它的构造方法中有一个public Integer(String s)方法,参数是String类型的,这里参数不可以为null,且字符串必须是可以解析为相应基本类型的数据,例如参数为“10a”,参数不可解析为相应的基本类型,运行时会报NumberFormatException异常。
另外,Integer还有两个静态方法,valueOf(String s)方法和parseInt(String s)方法,前者是返回Integer对象,后者是返回对应的基本数据类型,需要注意的问题和上面说的构造方法一样,如果参数不合法,一样会报出NumberFormatException异常。
Ø JDK5.0新特性——自动装箱和拆箱
JDK5.0提供了自动装箱和拆箱机制,是指基本类型值与包装类的对象相互自动转换,在变量赋值或方法调用等情况时,使用上更加简单直接,从而提高了开发效率。接下来通过一个案例演示自动装箱和拆箱的使用,如例所示。
程序的运行结果如图所示。
在例中,首先将10赋值给Integer类型的in,因为10是基本数据类型int的数据,所以在这里程序底层进行了自动装箱,将10转换为Integer类型的对象,调用Integer对象的toString()方法将in转换为字符串打印出来。接着将in赋值给int类型的i,因为in是Integer类型的对象,所以在这里程序底层进行了自动拆箱,将in对象转换为基本数据类型,最后将i加1并打印。
Ø Scanner类
之前写的程序,如果要设置一些参数,可以在编写代码的时候设置几个固定的参数,但是如果将需求变为参数不固定,在程序运行过程中输入参数,就无法满足。JDK5.0之后,Java基础类库提供了一个Scanner类,它位于java.util包,可以很方便地获取用户的键盘输入。
Scanner类是一个基于正则表达式的文本扫描器,它可以从文件、输入流、字符串中解析出基本类型值和字符串值,它有多个构造方法,不同的构造方法可以接收不同的数据来源,下面先来了解一下它的构造方法,如表所示。
| 构造方法声明 | 功能描述 |
| public Scanner(File source) | 构造一个新的 Scanner,它生成的值是从指定文件扫描的。 |
| public Scanner(File source, String charsetName) | 构造一个新的 Scanner,它生成的值是从指定文件扫描的。 |
| public Scanner(InputStream source) | 构造一个新的 Scanner,它生成的值是从指定的输入流扫描的。 |
| public Scanner(InputStream source, String charsetName) | 构造一个新的 Scanner,它生成的值是从指定的输入流扫描的。 |
| public Scanner(Readable source) | 构造一个新的 Scanner,它生成的值是从指定源扫描的。 |
| public Scanner(ReadableByteChannel source) | 构造一个新的 Scanner,它生成的值是从指定信道扫描的。 |
| public Scanner(ReadableByteChannel source, String charsetName) | 构造一个新的 Scanner,它生成的值是从指定信道扫描的。 |
| public Scanner(String source) | 构造一个新的 Scanner,它生成的值是从指定字符串扫描的。 |
表中列举了Scanner类的构造方法,构造Scanner类对象时指定不同的数据来源,它主要提供两个方法来扫描输入的信息,具体示例如下。
hasNextXxx()方法判断是否还有下一个输入项,其中,Xxx可以是Int、Long等代表基本数据类型的字符串,如果只判断是否包含下一个字符串,则直接使用hasNext()。nextXxx()方法可以获取下一个输入项,Xxx的含义与上一个方法中的Xxx相同。
讲解了Scanner类的构造方法和常用方法后,接下来通过一个案例来演示Scanner类的使用,如例所示。
程序的运行结果如图所示。
图中运行结果打印出键盘输入和程序读取到的内容,最后输入exit程序运行结束。例中,首先通过Scanner类的构造方法指定数据源为键盘输入,然后调用它的hasNext()方法,循环判断是否还有下一个输入项,如果有输入项,接收后判断是否为“exit”,若是则程序结束,若不是则打印键盘输入的内容,最后要记得释放资源。
String类、StringBuffer类 与StringBuilder类
在实际开发中经常会用到字符串,Java中定义了String、StringBuffer、StringBuilder三个类来封装字符串,并提供了一系列操作字符串的方法,下面将分别介绍它们的使用方法。
Ø String类的初始化
String类表示不可变的字符串,一旦String类被创建,该对象中的字符序列将不可改变,直到这个对象被销毁。
在Java中,字符串被大量使用,为了避免每次都创建相同的字符串对象及内存分配,JVM内部对字符串对象的创建做了一些优化,用一块内存区域专门来存储字符串常量,该区域被称为常量池。String类根据初始化方式的不同,对象创建的数量也有所不同,接下来分别演示String类两种初始化方式。
1.使用直接赋值初始化
使用直接赋值的方式将字符串常量赋值给String变量,JVM首先会在常量池中查找该字符串,如果找到,则立即返回引用;如果未找到,则在常量池中创建该字符串对象并返回引用。接下来演示直接赋值方法初始化字符串,示例代码参考。
程序的运行结果如图所示。
在例中,直接将字符串“1000phone”赋值给str1,初始化完成,接着初始化str2和str3,可以这样直接赋值初始化,是因为String类使用比较频繁,所以提供了这种简便操作。比较str1和str2结果是相等的,这就说明了字符串会放到常量池,如果使用相同的字符串,则引用指向同一个字符串常量,接下来讲解另一种初始化的方式。
2.使用构造方法初始化
String类可以直接调用构造方法进行初始化,常用的构造方法如表所示。
| 构造方法声明 | 功能描述 |
| public String() | 初始化一个空的 String 对象,使其表示一个空字符序列。 |
| public String(char[] value) | 分配一个新的 String,使其表示字符数组参数中当前包含的字符序列。 |
| public String(String original) | 初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列。 |
表中列出了String类的常用构造方法,接下来通过一个案例来演示String类使用构造方法初始化,如例所示。
程序的运行结果如图所示。
例中,创建了三个字符串,str1是用直接赋值的方式初始化,str2和str3是用构造方法初始化,比较字符串str1和str2,结果不相等,因为new关键字是在堆空间新开辟了内存,这块内存存放字符串常量的引用,所以二者地址值不相等,比较字符串str2和str3,结果不相等,原因也是str2和str3都是在堆空间中新开辟了内存,所以二者地址值不相等。
Ø String类的常见操作
前面讲解了String类的初始化,String类很常用,在实际开发中使用非常多,所以它的一些常见操作要熟练掌握,下面会详细讲解String类的常见操作,在讲解之前,先来了解一下String类的常用方法,表参考。
表中列举了String类的常用方法,接下来通过几个案例来具体学习这些方法的使用。
1.字符串与字符数组的转换
字符串可以使用toCharArray()方法转换为一个字符数组,如例所示。
程序的运行结果如图所示。
在例中,先定义一个字符串,然后调用toCharArray()方法将字符串转为字符数组,最后循环输入字符串。
2.字符串取指定位置的字符
字符串可以使用charAt()方法取出字符串指定位置的字符,如例所示。
程序的运行结果如图所示。
在例中,先定义一个字符串,然后调用charAt()方法取出字符串中第4个位置的字符并打印,这里索引位置也是从0开始计算的,第4个字符为p。
脚下留心
这里要注意,指定字符位置时,不能超出其字符串长度减1,例如字符串“abc”,最大索引为2,如果超出最大索引,会报StringIndexOutOfBoundsException异常。
3.字符串去空格
在实际开发中,用户输入的数据中可能有大量空格,使用trim()方法即可去掉字符串两端的空格,如例所示。
程序的运行结果如图所示。
在例中,先定义一个字符串,然后调用trim()方法去掉字符串两端的空格并打印。
4.字符串截取
在实际开发中,只截取字符串中的某一段也是很常用的,如例所示。
程序的运行结果如图所示。
String类中提供了两个substring()方法,一个是从指定位置截取到字符串结尾,另一个是截取字符串指定范围的内容。例中,先定义一个字符串,然后从索引为10的字符截取到字符串末尾,也就是从第11个字符开始截取,最后截取字符串索引为4~9的内容,也就是截取第5~9个字符的内容。
5.字符串拆分
字符串可以通过split()方法进行字符串的拆分操作,拆分的数据将以字符串数组的形式返回,如例所示。
程序的运行结果如图所示。
在例中,先定义一个字符串,然后调用split(String regex)方法按“.”进行字符串拆分,这里要写成“\\.”,因为split方法传入的是正则表达式,点是特殊符号,需要转义,在前面加“\”,而Java中反斜杠是特殊字符,需要用两个反斜杠表示一个普通斜杠,拆分成功后,循环打印这个字符串数组,关于正则表达式会在后面讲解。
6.字符串大小写转换
在实际开发中,接收用户输入的信息时,可能会需要统一接收大写或者小写的字母,字符串提供了toUpperCase()方法和toLowerCase()方法转换字符串大小写,如例所示。
程序的运行结果如图所示。
在例中,先定义一个字符串,打印出字符串,然后调用toUpperCase()方法将字符串转换为大写并打印,最后调用toLowerCase()方法将字符串转换为小写并打印。
以上是String类一些常用的操作,由于字符串使用频繁,所以要多加练习,熟练掌握,它还有更多的方法,读者可以查看JDK使用文档深入学习。
Ø StringBuffer类
StringBuffer类和String一样,也代表字符串,用于描述可变序列,因此StringBuffer在操作字符串时,不生成新的对象,在内存使用上要优于String类。在StringBuffer类中存在很多和String类一样的方法,这些方法在功能上和String类中的功能是完全一样的,接下来学习一下它不同于String类的一些常用方法,如表所示。
| 方法声明 | 功能描述 |
| StringBuffer append(String str) | 向StringBuffer追加内容str |
| StringBuffer append(StringBuffer sb) | 向StringBuffer追加内容sb |
| StringBuffer append(char c) | 向StringBuffer追加内容c |
| StringBuffer delete(int start, int end) | 删除指定范围的字符串 |
| StringBuffer insert(int offset, String str) | 在指定位置加上指定字符串 |
| StringBuffer reverse() | 将字符串内容反转 |
在表中列出了StringBuffer类的一些常用方法,接下来用一个案例来演示这些方法的使用,如例所示。
程序的运行结果如图所示。
在例中,先创建一个StringBuffer对象,向该StringBuffer对象中分别追加String类型、char类型和StringBuffer类型的数据,打印StringBuffer对象,调用delete(int start, int end)方法将指定范围的内容删除,在本例中指定索引为“5,6”,也就是将字符串中间的空格删除了,然后调用insert(int offset, String str)方法在刚删除的空格位置加上“——”,最后调用reverse()方法将内容反转。这是StringBuffer类的基本使用。
Ø StringBuilder类
JDK5.0提供了StringBuilder类,它和StringBuffer一样,都代表可变的字符序列。与之不同的是,StringBuilder是线程不安全的,而StringBuffer是线程安全的,因此StringBuilder的效率更高。接下来通过一个案例来分析String、StringBuffer和StringBuilder的运行效率,示例代码参考。
程序的运行结果如图所示。
在例中,分别修改String类、StringBuffer类和StringBuilder类两万次,计算执行时间,从而看出三者间的效率,从结果明显看出String类是三者中效率最差的,因为它是不可变的字符序列,StringBuffer类兼顾了效率和线程安全,StringBuilder类在三者中效率最高。
Ø String类对正则表达式的支持
正则表达式又称规则表达式,它可以方便地对字符串进行匹配、替换等操作,接下来先了解一下正则表达式的语法规则,示例代码参考。
表中列出了正则表达式的基本语法规则,在String类中提供了一些支持正则表达式的方法,如表所示。
在表中列出了String类中支持正则表达式的方法,接下来通过一个案例来演示这些方法的使用,如例所示。
程序的运行结果如图所示。
在例中,定义字符串和一个正则表达式匹配规则,该规则检测字符串是否为数字或字母,因为字符串中有“.”,不是数字或字母,所以检测后返回false,调用replaceAll(String regex, String replacement)方法,将字符串中的“w”都替换为“%”,接着调用replaceFirst(String regex, String replacement)方法将字符串中第一个“w”替换为“%”,定义另一个字符串,调用split(String regex)方法将字符串按“.”分割并遍历字符串数组,最后调用split(String regex, int limit)方法,将字符串按“.”分割,并指定limit参数分别为小于0、大于0和等于0,表中已经详细说明了limit参数的使用方法,这里就不再赘述。这就是String类对正则表达式的支持,可以方便地处理字符串,简化开发。
Ø String、StringBuffer、StringBuilder的区别
通过前面的学习,大家能够掌握String、StringBuffer、StringBuilder这三个类的用法,接下来将对它们做一个简单的总结,方便大家在后续的开发中灵活使用。
String是长度不可变的字符串,无法在末尾追加值。只能够改变String变量的引用地址,每次对String的操作都会有新的String对象生成,这样会浪费大量的内存资源,使其效率低下。使用String类时内存变化如图所示。
在中,可以看出创建str对象的过程中,会在虚拟机的堆内存中开辟一块区域存放字符串“1000phone”,然后,向这个字符串中添加新的“.com”字符串时,需要先开辟一块新的内存用来存放字符串“.com”,接着,再开辟一块内存存放新得到的字符串“1000phone.com”,最后,改变堆内存地址的指向,才能够完成对字符串的操作,这样的一个过程对资源造成了极大的浪费。
StringBuffer和StringBuilder都是可变长度的字符串,允许类的对象被多次修改而且不会产生新的对象,相对于StringBuilder而言,StringBuffer是线程安全的,线程安全速度就慢(多线程的知识在后续的章节中会做详细讲解,此处旨在了解),因此,StringBuilder的执行效率比StringBuffer的执行效率高。
在开发的过程中,如果操作数据不是很频繁,数据量小的情况下可以使用String类;如果在多线程下操作大量的数据可以使用StringBuffer类;如果仅仅是单线程环境下操作,不需要考虑线程安全性,而且想要提高效率,推荐使用StringBuilder类。