Java学习之String类与包装类

一、String 类

1、定义:

1、从概念上讲,java字符串就是Unicode字符序列。每个用双引号括起来的字符串都是String类的一个实例。

Java字符串由char序列组成,也就是说,String的最小单位是char类型的字符。字符数据类型是一个采用UTF-16的编码表示Unicode代码点的代码单元。(大多数的常用Unicode字符使用一个代码单元就可以表示,而付诸字符需要一对代码单元表示。具体内容,请自行查阅相关资料或者百度,本人不在此赘述了。)

2、格式:

String a = "abc";//存储在StringPool中  
String b = new String("abc")://存储在堆内存中  

3、StringPool(字符串池):

在java的内存分配中,存在着一个方法区,这里有一个常量池,是存放那些常量等不变的值,StringPool即字符串池就是存在于这里,它是一个存放字符串的公共存储池。字符变量指向存储池中的相应位置。如果是通过复制一个字符串变量,原始的字符串和复制的字符串将共享这个相同的字符串。

2、特点:

1、String是一种特殊的数据类型,可创建对象。
2、通过String创建的对象分别可存在于字符串池(StringPool)和堆内存中
3、String是一个final的类型,即不可被继承修改,一经初始化就终生不可改变。(要改变也是改变的引用变量)
4、StringPool中的数据可共享,即两个引用变量可指向同一个字符串池中的字符串
5、Java中的任何对象都可写成字符串的形式。

注:字符串不可变的原因:
a. 可以想得出,每种固定的东西是不会改变的,1就是1,也就是说你若想改变,只得改变这个引用变量所指向的地址。
b. 编译器可以让字符串共享数据。所以不必改变字符串,直接改变变量即可。
c. java的开发者认为共享数据的效率要远远胜于提取、拼接字符串的效率。程序中对字符串的修改操作很少,更多的是对字符串进行比较。
(例外:将源自于文件或键盘的单个字符或较短的字符串汇集成字符串。具体在下面会说到。)

3、String类相关方法:

String类适用于描述字符串事物,它提供了多个方法对字符串进行操作,下面给大家介绍几个比较常用的关于字符串的方法;如果还想了解更多关于String类的方法,可以查看String的相关API。

一)字符串的获取:

String str = "Hellow java"; 为例

1、int length()
length方法返回给定字符串的长度:
例如:

str.length();//值为11

2、char charAt(int index)
根据位置获取某个字符串
例如:

char c = str.charAt(4);//结果为 o

2、int indexOf(int ch)
根据字符串获取该字符在字符串中的位置。返回字符串中第一次出现的位置。
例如:

int x = str.indexOf('j');//结果为7

int indexOf(int ch,int fromIndex)
从fromIndex指定位置开始。获取在字符串中的位置。
例如:

int x = str.indexOf("w",3);//结果为5
int y = str.indexOf("f",3);//结果为-1,没有找到

注:
int indexOf(String str)int indexOf(String str,int fromIndex)同上,只是接收的是String类型的数据。(方法重载)

当访问到字符串中不存在的角标时,会发生StringIndexOutOfBoundsException的角标越界的错误。如果未找到则返回-1,下同。

3、int lastIndexOf(int ch)
反向索引一个字符出现的位置,此位置的角标仍为从左数的角标。
例如:

int m = str.lastIndexOf("v");//结果为9

二)字符串的判断:
以下面为例:

String str = "StringDemo.java";
String s = "StringDemo.java";

1、 boolean contains(str)
判断字符串中是否包含一个子串
例如:

boolean b1 = str.contains("Demo");//结果为true

2、boolean isEmpty()
判断字符串中是否有内容,原理就是判断字符串长度是否为0:
例如:

boolean b2 = str.isEmpty();//结果为true

3、 boolean startWith()
判断字符串是否以指定内容开头:
例如:

boolean b3 = str.startWith("String");//结果为true

4、 boolean endWith()
判断字符串是否以指定内容结尾:
例如:

boolean b4 = str.endWith(".java");//结果为true

5、 boolean equals()
判断两个字符串的内容是否相同:
例如:

boolean b5 = str.equals(s);//结果为true

注:
判断字符串内容是否相同,是复写了Object类中的equals的方法,是区分大小写的。

6、 boolean equalsIgnoreCase()
判断两个字符串的内容是否相同,忽略大小写:
例如:

boolean b5 = str.equalsIgnoreCase("STRINGdEmo.JAVA");//结果为true

三)字符串的转换:
例子:

char[] arr = {'a','b','c','d','e','f','g','h'};
String str = "Hello java";

1、将字符数组转化成字符串:
构造函数:String(char[])String(char[] ch,int offset,int count)
例如:

String s = new String(arr);//结果为:s = abcdefgh
String st = new String(arr,1,3);//结果为:st = bcd

2 、char[] toCharArray()
将字符串转化成数组:
例如:

char[] ch = str.toCharArray();//结果为:ch = {'H','e','l','l','o',' ','j','a','v','a'}

3、字符串和字节数组间的转换,同1和2,参数为byte[]型值。
String(byte[] b);String(byte[] b,int offset,int count)
byte[] getByte[]byte[] b = str.getByte();

4、static String valueOf(3)
专业写法:将基本数据类型转换成字符串:

String.valueOf(3);//结果为:"3"

非专业写法:

3+""

四)字符串的“更改”:
例子:

String s = "     Hello Java    ";

1、String replace(oldchar,newchar)
字符串的替换
例如:

str.replace("Java","World");//结果为:s1 = "     Hello World    "
str.replace('a','n');//结果为:s1 = "     Hello Jnvn    "

注:如果要替换的字符不存在,则返回原串。

2、String toUpperCase() 和 String toLowerCase()
将字符串转为大写或小写:
例如:

str.toUpperCase();//结果为:"     HELLO JAVA    "
str.toLowerCase();//结果为:"     hello java    "

3、String trim()
将字符串两端的多个空格去掉:
例如:

str.trim();//结果为:"Hello Java"

4、String[] split(regex)
字符串的切割:
例子:

String str = "zhangsan,wangwu,lisi,xiaohei";

例如:

String[] s = str.split(",");//结果为:s = {"zhangsan","wangwu","lisi","xiaohei"}

5、int compareTo(String str)
对两个字符进行自然顺序的比较:
例如:

String s1 = "abc";      String s2 = "aaa";
int n = s1.compareTo(s2);//结果为:n = 1

五)字符串的截取:
substring(int a,int b)
获得字符串的子串:
String类的方法substring可以从一个较大的字符串提取出一个子串。
方法的中a和b分别代表获取的字符串的起始位置(从哪个位置开始获取,包含此位置)和终止位置(不包括b),获得s.substring(a,b)的长度是b-a。
例如:

String s = "adflkwefnkl";

获取子串--从指定位置到结尾:

s.substring(2)//结果为:"flkwefnkl"

获取从指定位置到另一位置:

s.substring(2,4));//结果为:"fl"    --->包含头,不包含尾

六)返回字符串对象的规范化表示形式
intern()
该方法返回是始终都是StringPool中的对象。
该方法和String m = ”Hello”声明代码的效果实际上是一样的,使用intern返回的始终是StringPool中的对象。
比如你首先使用String s = new String("abc")声明一个变量,然后判断s == s.intern()的话,那么会得到一个false,原因就是因为s是分配在堆中的一个新对象。

4、字符串的比较:

在这里,我重点说一下关于字符串之间的比较,因为这个问题很常见,也很容易让人混淆:

1、概述:
在Object这个根类中,存在一个equals(Ojectobj)的方法,指示其他某个对象是否与此对象“相等”。但是在String的方法中也有一个equals的方法,你查看API文档就会发现:根类Object中的equals方法被String中的equals方法覆盖了,它是将此字符串与指定的对象比较。当且仅当该参数不为null,并且是与此对象表示相同字符序列的String对象时,结果才为true。
表达式为:x.equals(y)

如果字符串x和字符串y相等,则返回true,否则返回false。需要注意的是,x和y可以使字符串变量,也可以是字符串常量,如这样的写法也是合法的:”Hello”.equals(str)

2、检测两字符串是否相等:

  1. equalsIgnoreCase方法:
    要想检测两个字符串是否相等,而且不区分大小写,则可以使用此方法。

使用==运算符,检测的是指向字符串池中同一个字符串的两个引用变量是否相等。可以思考一下,只有字符串常量是真正实现共享的,如果用+或者substring这样的拼接等操作产生的结果并不是共享的。而是在StringPool又创建了一个新的字符串对象。

2)比较:
第一、String s ="abc”;
在执行这句代码的时候,JVM会首先查询StringPool,如果在该StringPool中已经存在相应的String实例了,那么将不会在堆中分配相应的内存来生成一个String对象,所以在代码中多次使用上面的声明来声明的变量实际指向的是同一个对象(那么当然就是相同的数据了)。
现在来说一下JVM查找的时候没有找到相应的String对象的情况,这时JVM会在StringPool中生成相应的对象,但是并不会在堆中生成相应的对象,所以只要使用上面的代码声明,堆中将始终不会生成新的String对象。

第二、String str= new String(”abc”)
要是比较这样定义格式的字符串,就需要使用equals方法了,上面也说到了,String类对Object的equals进行了覆盖了,所以String类是有自己比较字符串的方法的。

只有使用new创建一个对象,那么就会在堆内存中开辟一块空间存放这个数据。看下面的代码:

String s1 = "abc";    
String s2 = new String("abc");   
String s3 = new String("abc");   
String s4= s2;  
s1==s2;//false,不是同一个对象  
//下面都是比较的字符串这个值,即数据  
s1.equals(s2);// true  
s2.equals(s3);//true  
s2.equals(s4);//true  

第三、需要补充一点:

String s1 = "abc";    
String s2 = new String("abc");   

对于上面的代码,有一点要说的:就是无论String s2 = new String("abc")在s1前面写还是后面写,s2都会检查StringPool中是否有"abc",若有,就直接在堆内存中创建一个对象;若没有,则要先在StringPool中创建"abc"后,再在堆内存中创建一个对象。
注意:

选择用==运算符比较两个字符串总不是太好的方法,否则String的API文档也不会覆盖Object中的equals方法了。所以最好不要使用==运算符检测两个字符串是否相等,因为这个运算符只能确定两个字符串是否放在同一个位置(内存地址是否相同)上。对于两个对象,==运算符检测的结果当然为false了。也就是说一般检测下面所定义格式的字符串:

String greet = "Hello";  
System.out.println(greet == "Hello");//true  
System.out.println(greet.substring(0,3) == "Hel");//false  

5、构造字符串

在这里以StringBuilder为例,在穿插着说一下StringBuffer。

1、概述:
上面也提到了:按键或者来自文件中的单词。采用字符串连接的方式达到此目的的效率是十分低的,每次连接字符串,都会构建一个新的String对象,既降低了效率,又占用大量的空间。通过使用StringBuilder类局可以避免这个问题。另外,在构建字符串时就调用toString方法,可以得到一个String对象。

StringBuilder作为一个容器,是被final修饰的,它是字符串缓冲区,可以对这个“容器”进行“CURD”操作,即存储(creat),修改(update),获取(read),删除(delete)。

这里需要说明的一点是:StringBuilder是在JDK1.5之后才出来的,相对于StringBuffer的效率要高。因为可以不用每次判断锁。虽然不安全,但是效率更高,建议开发使用StringBuilder。

StringBuilder和StringBuffer的对比:
----StringBuilder:是线程不同步的,相对效率更高,但是不安全
----StringBuffer: 是线程同步的,相对效率较低,但安全。

2、构建格式:

StringBuilder bul = new StringBuilder();

3、特点:
第一、StringBuilder的长度是可变的,也可以通过length()的方法求当前对象的长度。
第二、可以直接操作多个数据类型。可将如int、double、char、boolean等类型的数据作为字符串相连接存入容器中。
第三、最终会通过toString方法变为字符串。

4、StringBuilder的操作方法:
4.1 append():存储
StringBuilder append();
如果要在字符串中“添加”一些内容,则可以使用append方法。可以将制定数据作为参数添加到已有数据的结尾处。

例子:

StringBuilder result = new StringBuilder();  
for (int i = 0; i <  5; i++)  
{  
    result.append("a");  
    result.append(" ");  
}  
System.out.println(result.toString());//结果为:"a a a a a " 

注:方法调用连 -----> 一连调用方法,返回的还是对象。
如:

str.append("abc").append(true).append(123).append(20.6);//值为:“abctrue12320.6”

StringBuilder insert(index,数据)
将数据插入到指定index位置
例如:

StringBuilder bul = new StringBuilder("adflkwefnkl");
bul.insert(1,"qq");//结果为:"aqqdflkwefnkl"

3.2 delete()
删除
*StringBuilder delete(start,end) *:删除缓冲区中的数据,包含start,不包含end
例如:

StringBuilder bul = new StringBuilder("adflkwefnkl");
bul.delete(1,3);//结果为:"alkwefnkl"
bul.delete(0,bul.length());//清空缓存区,bul为""
bul.deleteCharAt(2);//结果为:"adlkwefnkl"

3.3 获取:
char charAt(int index)---> 获取指定位置index上的字符
int indexOf(String str)---> 获取指定字符串的位置
int lastIndexOf(String str)---> 反向获取指定字符串的位置
int length()---> 获取字符串的长度
*String substring(int start,int end):在Stringbuilder中比较特殊,返回的是String类型。
例如:

StringBuilder bul = new StringBuilder("adflkwefnkl");
bul.charAt(1);//结果为:"d"
bul.indexOf("a");//结果为:0
bul.lastIndexOf("n");//结果为:8
bul.length();//结果为:10       
bul.substring(1,3)//结果为:"df"

3.4修改:
StringBuilder replace(start,end,string)---> 将start到end(不包含end)位置上的字符串更改为指定字符串string
void setCharAt(int index,char ch)---> 替换指定位置上的字符为指定字符
例如

StringBuilder bul = new StringBuilder("adflkwefnkl");
bul.replace(1,3,"JAVA");//结果为:"aJAVAlkwefnkl"
bul.replace(2,'M');//结果为:"adMlkwefnkl"

3.5 反转
StringBuilder setreverse() ---> 替换指定位置上的字符为指定字符
例如:

StringBuilder bul = new StringBuilder("ABCD");
bul.reverse();//结果为:"DCBA"

关于StringBuilder的内容,我找了一些资料,如果想了解更多,可以参阅:
Java中的StringBuilder类功能详解

总结:
对于String这个比较特殊的类,使用是十分频繁的,对于她在内存中的分配,字符串间的操作,如比较,以及如何优化String中的数据等等,这些问题还需要在以后慢慢积累。
在此,我也将一篇别人的文章分享给大家,是关于一个经常苦恼人的问题的(String s = "a" + "b" + "c" + "d"创建了几个对象的问题)。希望对大家有所帮助
文章地址:
String s = "a" + "b" + "c" + "d"创建了几个对象的问题

二、包装类

1、概述:

1、有时候,需要将int这样的基本类型转换为对象,以便可以使用一些方法。所有的基本类型都有一个相对应的类。这些类就称之为包装类。

2、基本数据类型对应的包装类:

基本数据类型 引用数据类型(包装类)
byte Byte
short Short
int Integeter
long Long
float Float
double Double
boolean Boolean
char Character

2、常见操作:

用于基本数据类型和字符串类型间的转换

1、转换:
1)基本数据类型转换为字符串
基本数据类型 + "" ---> 直接转换成字符串
基本数据类型.toString(基本数据类型值) ---> 如:Integer.toString(34);//将整数34变为"34"
2)字符串转换为基本数据类型:
基本数据类型名 变量名 = 包装类名.pase包装类名(String)
如:int a = Integer.paseInt("123");//结果为123

2、转二进制:
Integer.toBinaryString(int a);
十进制 -----> 其他进制

1)转为二进制:toBinaryString();
2)转为八进制:toOctalString();
3)转为十六进制:toHexString();
其他进制 -----> 十进制
parseInt(String,radix);

class IntegerDemo  
{  
    public static void sop(Object obj)  
    {  
        System.out.println(obj);  
    }  
    public static void main(String[] args)   
    {  
        //整数类型的最大值  
        sop("int max --Integer.MAX_VALUE:" + Integer.MAX_VALUE);  
        sop("------------------");  
  
        //将一个字符串转换成整数  
        //静态  
        int x = Integer.parseInt("123");  
        long y = Long.parseLong("123");  
        sop("字符串转换成整数Integer.parseInt(”123“):x = " + (x + 4));  
        //非静态  
        Integer i = new Integer("123");  
        int num = i.intValue();  
        sop("非静态:" + num);  
        sop("------------------");  
  
        sop("十进制转为二进制Integer.toBinaryString(6):" + Integer.toBinaryString(6));  
        sop("十进制转为十六进制Integer.toHexString(60):" + Integer.toHexString(60));  
        sop("------------------");  
  
        int x1 = Integer.parseInt("110",10);  
        int y1 = Integer.parseInt("3c",16);  
        sop("其他进制转为十进制 Integer.parseInt(”进制值”,被转的进制):" + x + "--" + y1);  
    }  
}

3、比较

先看段小程序:

class IntegerDemo  
{  
    public static void main(String[] args)   
    {  
        Integer m = 128;  
        Integer n = 128;  
        System.out.println("128--m==n :" + (m==n));  
        Integer a = 127;  
        Integer b = 127;  
        System.out.println("127--a==b :" + (a==b));   
    }  
}

其实说起来,这个Integer和int的关系有点像String和StringBuilder的关系。
java为了优化内存,提高性能,就单开了一片内存池(pool),也就是说,在这里共享了那些固定不变的数据(我个人理解),如数字和字符串等等,这也是一种对象。

重点说Integer和int,在内存池中定义他们的范围是 -128 ~ 127,这里的数是共享的,其实共享的是地址,就是变量指向的地址。(题外话:变量其实都是指向的地址,地址才是代表一块内存的空间的。)java为了提高效率,初始化了-128--127之间的整数对象,所以,如果写Integer a =100的话,是在内存池中创建了一个变量为a的对象,再写b=100,就共享了100这个数据,其实是指向了相同地址。但是如果超过了这个范围的话,这数据就不是共享的了,指向的不是相同地址。所以就不相等了。

具体参阅:Java Integer值范围问题

4、打包和拆包:

class IntegerDemo  
    {  
        public static void main(String [] args)  
        {  
            Integer x = 4;  
            x = x + 2;  
            System.out.println("x" + x);  
        }  
    }

以上面的小程序为例:
自动封箱:其中Integer x = 4;就是自动封箱的过程,即Integer x = new Integer(4);

自动拆箱:其中x+2就是进行了自动拆箱的过程,将x变为int类型,再和2进行加法运算,在拆箱的时候,要先进行 x.intValue()的判断,是否x为null,不可为null,否则编译失败

自动封箱:再将求的和进行封装,赋值给x

推荐阅读更多精彩内容