面试中,关于字符串问题,出现几率比较高的两个问题
第一个问题:
public static void main(String[] args)
{
String s = new String("abc"); //执行到这一行时,创建了几个对象?
String s1 = "abc"; //执行到这一行时,创建了几个对象?
String s2 = new String("abc"); //执行到这一行时,创建了几个对象?
System.out.println(s == s1); //输出结果是什么?
System.out.println(s == s2); //输出结果是什么?
System.out.println(s2 == s1); //输出结果是什么?
}
解答:
(1) 执行第一行的时候创建两个对象。为什么是两个对象,我们先来分析下。应为String类型是一个特殊类型,因为是final 来存储字符串对象。所以一旦创建String类型的字符串,就不能更改。关于用常量来声明字符串对象就要不得不说String pool 的概念(关于String pool 的概念,我在下一章进行详细介绍,这里不多说了。)。在这里记住一点凡是""声明的字符串对象都存储在String pool池中。
那么在执行第一句中("abc")声明的对象就存储在String pool中,这是声明第一个对象。
当new Strring时,又产生一个对象,只不过这个对象是存放在内存heap(堆中)。还要记住一点只要有new 关键字就会产生一个新的对象,这是声明的第二个对象。
答案:执行第一行产生2个对象。
(2)当执行第二行的时候,我说过凡是用""声明的字符串对象就会存储到String pool池中。
但这里需要记住的是String pool池中,不会存储相同的字符串对象,每当用""声明字符串对象前,都会先检查String pool池中,是否存在相同的字符串对象。如果存在相同的字符串对象,则返回该对象。如果没有该字符串对象,则把该对象加入String pool池中。因为执行第一行时,已经把"abc"字符串放到String pool池中,所以当执行String s1="abc";已经发现String pool池中有abc字符串,则不会创建,而是直接返回该对象。所以这行并没有创建对象
答案:执行第二行产生0个对象。
(3)执行第三行时,我说过"abc"对象已经存在String pool池中,则不会创建。记住String pool池中不会存储相同字符串对象。只要有new 的地方就会在heap(堆)中产生一个新的对象。
答案:执行第三行产生1个对象。
(4)因为s引用的对象是存储在String pool池中,而s1引用的对象是存储在heap(堆)中,引用的不是同一个对象。所以执行System.out.println(s == s1);
答案:结果为false。
(5)因为s引用的对象是存储在String pool池中,而s2引用的对象是存储在heap(堆)中,引用的不是同一个对象。所以执行System.out.println(s == s2);
答案:结果为false。
(6)虽然s1和s2引用的对象都存储在heap(堆)中,但他们的内存地址不相同,所以不是同一个对象,这是因为使用new 关键字创建对象时,会新开辟一块内存空间来存放对象。所以用new 创建同一个对象,永远都不会相同。所以执行System.out.println(s2 == s1);
答案:结果为false。
-----------------------------------------------------------------------------
下面是一个关于字符串的机试问题:
编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 但是要保证汉字不被截半个,如“我ABC”4,应该截为“我AB”,输入“我ABC汉DEF”,6,应该输出为“我ABC”而不是“我ABC+汉的半个”。
样例:
/*
* 按照指定字节,截取子字符串函数
* @param str 要截取的字符串
* @param len 要截取的字节数
* @return String 截取后子字符串
*/
public static String splitString(String str,int len)
{
if(str==null||str.equals(""))
{
return "";
}
byte[] b=str.getBytes();
String subString=null;
if(b.length<len || len<0)
{
return str;
}
if(len>1)
{
if(b[len]<0)
{
subString=new String(b,0,--len);
}
else
{
subString=new String(b,0,len);
}
}
else
{
if(len==1)
{
if(b[len]<0)
{
subString=new String(b,0,++len);
}
else
{
subString=new String(b,0,len);
}
}
}
return subString;
}
下面提供两个按照指定编码截取字符串
第一种按照:Unicode 编码方式来产生字节数组
String str="我爱java";
String s=str.getBytes("Unicode");
这种方式产生的字节数组,字节数组的前两个字节为标志位,如byte[0]=-1,byte[1]=-2。从第三位开始才是真正的字符串。在Unicode 编码下,汉字、英文、数字都是两个字节。而英文和数字,第一个字节是相应的ASCII码,第二个字节是0.而汉字两个字节都大于0.
这种方法每个字符都是两个字节:
/*
* 按照双字节,截取指定函数
* @param str 要截取的字符串
* @param len 要截取的长度
* @return string 截取后的字符串
*/
public static String mySubString(String str,int len) throws UnsupportedEncodingException
{
/*首先把目标字符串分解成Unicode编码的字节数组,
* 这样的目的是每个字符都变成两个字节,在根据
* 英文和数字字符在Unicode编码下,第一个字节
* 是相应的ASCII码,第二个字节为0的特征,来进行
* 计算
*/
int pos=len+1;
byte[] b=str.getBytes("Unicode");
if(b[len]==0)
{
return new String(b,0,len+1,"Unicode");
}
if(b[len]!=0)
{
if(b[len+1]!=0)
{
if((len%2!=0))
{
return new String(b,0,len+1,"Unicode");
}
else
{
return new String(b,0,len+2,"Unicode");
}
}
else
{
return new String(b,0,len+2,"Unicode");
}
}
return null;
}
-------------------------------------------------------------------------------
第二种按照:GBK编码方式产生字节数组
String str="我爱java";
String s=str.getBytes("GBK");
这种方式产生的字节数组,汉字是两个字节,英文和数字是一个字节。而汉字两个字节都小于0.而英文和数字产生的字节都是相对应的ASCII码。如 a 对应 97。
样例:
/*
* 目标字符串中截取指定字节函数
* @param str 要截取的字符串
* @param len 要截取的长度
* @return string 截取后的字符串
*/
public static String mySubString(String str,int len,String symbol) throws UnsupportedEncodingException
{
if(str==null||str.equals(""))
{
return "";
}
int count=0;
byte[] b=str.getBytes("GBK");
if(b.length<len)
return str;
for(int i=0;i<b.length;i++)
{
if(b<0)
{
count++;
}
}
if(count%2==0)
{
return new String(b,0,len,"GBK")+symbol;
}
else
{
return new String(b,0,len-1,"GBK")+symbol;
}
}