`

有关String类创建几个对象的问题

 
阅读更多
试题一:
	String s = new String("abc");创建了几个String对象? 
	涉及内容:	 
	引用变量与对象的区别;
	字符串文字"abc"是一个String对象; 
	文字池(pool of literal strings)和堆(heap)中的字符串对象。

	1、引用变量与对象:
		A aa;
		这个语句声明一个类A的引用变量aa[我们常常称之为句柄],而对象一般通过new创建。所以题目中s仅仅是一个引用变量,它不是对象。

	2、Java中所有的字符串文字[字符串常量]都是一个String的对象。有人[特别是C程序员]在一些场合喜欢把字符串"当作/看成"字符数组,这也没有办法,因为字符串与字符数组存在一些内在的联系。事实上,它与字符数组是两种完全不同的对象。

		System.out.println("Hello".length());
		char[] cc={'H','i'};
		System.out.println(cc.length);

	3、字符串对象的创建:
		由于字符串对象的大量使用(它是一个对象,一般而言对象总是在heap分配内存),Java中为了节省内存空间和运行时间(如比较字符串时,==比equals()快),
		在编译阶段就把所有的字符串文字放到一个文字池(pool of literal strings)中,而运行时文字池成为常量池的一部分。
		文字池的好处,就是该池中所有相同的字符串常量被合并,只占用一个空间。
			
		我们知道,对两个引用变量,使用==判断它们的值(引用)是否相等,即指向同一个对象:

		String s1 = "abc" ;
		String s2 = "abc" ;
		if( s1 == s2 ) System.out.println("s1,s2 refer to the same object");
		else System.out.println("trouble");
		
		
		这里的输出显示s1,s2 refer to the same object,两个字符串文字保存为一个对象。就是说,上面的代码只在pool中创建了一个String对象。
			即s1,s2指向了同一个对象。
			s1-------
							|
							|-------"abc"
							|
			s2-------
		
		现在看String s = new String("abc");语句:
		这里"abc"本身就是pool中的一个对象,而在执行new String()时,将pool中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s持有。ok,这条语句就创建了2个String对象。
			即引用s指向了两个对象。
							|-------"abc"
							|
			s-------|
							|
							|-------new String()								
		
		String s1 = new String("abc") ;
		String s2 = new String("abc") ;
		if( s1 == s2 ){ //不会执行的语句}
		这时用==判断就可知,虽然两个对象的"内容"相同(equals()判断),但两个引用变量所持有的引用不同,
		上面的代码创建了几个String Object? (三个,pool中一个,heap中2个。) 
							|-------new String()
							|
			s1------|
							|
							|-------"abc"
							  |
			s2--------|
							  |
							  |-----new String()
							 
		String s1 = "abc";
		String s2 = new String("abc");
		上面代码创建了几个对象?
		两个。pool中一个,heap中一个。


试题二:
	先看看下面的代码
    public String makinStrings()
     {
         String s = "Fred";
         s = s + "47";
         s = s.substring(2, 5);
         s = s.toUpperCase();
        return s.toString();
     }
		问:调用makinStrings方法会创建几个String对象呢。 
		答案:3个
    上面的方法有五条语句:现在让我们来一条一条分析一下。
		1、String s = "Fred";   结论:创建了一个String对象
					这条语句相当于String s = new String("Fred");
					因此,毫无疑问,第一条语句创建了一个String对象,我想没有有疑问吧?

		2、s = s + "47";   结论:未创建String对象
					这条语句也许很多人认为是创建了String对象,我一开始也是这么认为的。但是为了验证我的想法。决定
					用点法术恢复这条语句的本来面目。(有很多时候,编译器总是在里面搞一些小动作,javac.exe也不例外)
		
					现在找到这个程序所生成的.class文件(假设是Test.class),找一个反编译工具,我推荐JAD,可以http://www.softpedia.com/progDownload/JAD-Download-85911.html下载
					下载后,有一个jad.exe,将其路径放到环境变量path中(只限windows)。并在.class文件的当前路径执行如下的命令:jad Test
					然后大喊一声“还我本来面目”
					会在当前目录下生成一个Test.jad文件,打开它,文件内容如下:
							public String makinStrings()
							     {
							         String s = "Fred";
							         s = (new StringBuilder(String.valueOf(s))).append("47").toString();
							         s = s.substring(2, 5);
							         s = s.toUpperCase();
							        return s.toString();
							     }
					    哈哈,其他的语句都没变,只有第二条变长了,虽然多了个new,但是建立的是StringBuilder对象。原来这是java编译器的优化处理。原则是能不建String对象就不建String对象。而是用StringBuilder对象加这些字符串连接起来,相当于一个字符串队列。这种方式尤其被使用在循环中,大家可以看看下面的代码:
					        String s = "";
					        for(int i=0; i < 10000000; i++)
					            s += "aa";
					    没有哪位老大认为这是建立了10000000个String对象吧。但不幸的是,上面的代码虽然没有建立10000000个String对象
					但却建立了10000000个StringBuilder对象,那是为什么呢,自已用jad工具分析一下吧。
					正确的写法应该是:
					
					        StringBuilder sb = new StringBuilder("");
					        for(int i=0; i < 10000000; i++)
					            sb.append(String.valueOf(i));

		3、s = s.substring(2, 5);     结论:创建了一个String对象
					也许有很多人一开始就认为这条语句是创建了一个String对象,那么恭喜你,这条语句确实创建了一个String对象
					实际上就是substring方法创建了一个String对象。这也没什么复杂的,自已下一个JDK源代码,看看substring是如何实现的就可以知道了。我先说一下吧。先不用管substring是如何实现的,反正在substring方法返回时使用了一个new显式地建立了一个String对象不信自己看看源码。原码如下:
							public String substring(int beginIndex, int endIndex) {
							return ((beginIndex == 0) && (endIndex == count)) ? this :
								    new String(offset + beginIndex, endIndex - beginIndex, value);
							}
		4、s = s.toUpperCase();   
							结论:创建了一个String对象
							toUpperCase()、toLowCase()和substring()方法类似,在返回时也是使用了new建立了一个String对象。
									public String toUpperCase(Locale locale) {
									return new String(0, result.length, result);
									}
		5、return s.toString();   结论:未创建String对象
							toString方法返回的就是this,因此,它的返回值就是s。
							  public String toString() {
								return this;
							    }
			这道题还算比较简单,再给大家出一个更复杂一点的,也是关于String对象的创建的(只是改了一个原题)。
			

试题三:
    public String makinStrings()
     {
         String s = "Fred";
         s = s + "Iloveyou.".substring(1).toLowerCase();
         s = s.substring(0);
         s = s.substring(0,1).toUpperCase();
        return s.toString();
     }
			先公布答案吧,上述代码也创建了3个String对象,哈哈!
			为什么呢?
			要想知道为什么,先得弄清楚substring、toLowerCase和toUpperCase什么时候创建String对象,什么时候不创建对象。
				substring方法在截取的子字符串长度等于原字符串时,直接返回原字符串,并不创建新的String对象。
				即substring(0),substring(o,字符串长度)将不会创建对象,直接返回自身。
						public String substring(int beginIndex) {
							return substring(beginIndex, count);
						} 
						public String substring(int beginIndex, int endIndex) {
							return ((beginIndex == 0) && (endIndex == count)) ? this :
							    new String(offset + beginIndex, endIndex - beginIndex, value);
						    }
						    示例:
						    "unhappy".substring(2) returns "happy"
								"unhappy".substring(0) returns "unhappy"
								"smiles".substring(1, 5) returns "mile"     substring从下标0开始,第0个为 s,第一个为m,返回的个数=5-1即返回4个字符。
						
						
				toLowerCase方法在字符串中根本没有需要转换的大写字母时直接返回原字符串,如"abcd".toLowerCase()直接返回abcd,并不创建新的String对象看原代码:
						  public String toLowerCase() {
						        return toLowerCase(Locale.getDefault());
						    }
						    
							public String toLowerCase(Locale locale) {
								if (locale == null) {
								    throw new NullPointerException();
							        }
							        int     firstUpper;
								/* Now check if there are any characters that need to be changed. */
								scan: {
							            int c;
								    for (firstUpper = 0 ;firstUpper < count ;firstUpper += Character.charCount(c)) {
													c = codePointAt(firstUpper);
												if (c != Character.toLowerCase(c)) {
							                break scan;
							             }
								    }
								    return this;
								}
							
							        char[]  result = new char[count];
											int  resultOffset = 0;  
										  System.arraycopy(value, offset, result, 0, firstUpper);
											String lang = locale.getLanguage().intern();
											boolean localeDependent = (lang == "tr" || lang == "az" || lang == "lt");
							        char[] lowerCharArray;
							        int lowerChar;
							        int srcChar;
							        int srcCount;
							        for (int i = firstUpper; i < count; i += srcCount) {
								    			srcChar = codePointAt(i);
							            srcCount = Character.charCount(srcChar);
							            if (localeDependent || srcChar == '\u03A3') { // GREEK CAPITAL LETTER SIGMA
							                lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale);
							            } else {
							                lowerChar = Character.toLowerCase(srcChar);
							            }
							            if ((lowerChar == Character.ERROR) ||
							                    Character.isSupplementaryCodePoint(lowerChar)) {
							                if (lowerChar == Character.ERROR) {
							                    lowerCharArray =
							                        ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale);
							                } else {
							                    lowerCharArray = Character.toChars(lowerChar);
							                }
							                /* Grow/Shrink result. */
							                int mapLen = lowerCharArray.length;
							                char[] result2 = new char[result.length + mapLen - srcCount];
							                System.arraycopy(result, 0, result2, 0,
							                    i + resultOffset);
							                for (int x=0; x<mapLen; ++x) {
							                    result2[i+resultOffset+x] = lowerCharArray[x];
							                }
							                resultOffset += (mapLen - srcCount);
							                result = result2;
							            } else {
							                result[i+resultOffset] = (char)lowerChar;
							            }
							        }
							        return new String(0, result.length, result);
							    }

			toUpperCase方法和toLowerCase类似。"ABCD".toUpperCase()直接返回ABCD。
			知道了这个,上面的代码就非常清楚了。
					    public String makinStrings()
					     {
					         String s = "Fred";     // 创建一个String对象
					         s = s + "Iloveyou.".substring(1).toLowerCase();  // substring(1)创建一个String对象,由于toLowerCase()转换的字符串是"loveyou.",没有大写字母,因此,它不创建新的String对象
					         s = s.substring(0);   // 由于substring(0)截获的是s本身,因此,这条语句不创建新的String对象
					         s = s.substring(0,1).toUpperCase();  // substring(0,1)创建了一个String对象,但由于substring(0,1)的结果是"F",为一个大写字母,因此,toUpperCase直接返回"F"本身。
					        return s.toString();
					     }

试题四、
		public class TestStringBuffer {

				public static void main(String[] args) {
					StringBuffer a = new StringBuffer("A");
					StringBuffer b = new StringBuffer("B");
					operate(a,b);
					System.out.println(a+","+b);
					
					String a1 = new String("A");
					String b1 = new String("B");
					operateStr(a1,b1);
					System.out.println(a1+","+b);
				}
				public static void operate(StringBuffer x,StringBuffer y){
					x.append(y);
					y=x;
				}
				
				public static void operateStr(String x,String y){
					x+=y;
					y=x;
				}
			}
			输出结果为:
			AB,B
			A,B
			
			分析:
			StringBuffer只是存储字符串的缓冲区,通过x.append(y)即得到AB
			但	operate(a,b);传递参数的时候只是让x也指向缓冲区,y指向另一个缓冲区,使用y=x并不能改变a,b所指向的缓冲区,所以y=x不能改变b指向的内容
			所以前面输出AB,B
			
			对String类,x+y只是创建了一个StringBuffer对象,在Buffer中加入了y的值,这样a1所指向的仍然是A,y=x一样不能改变什么,所以最后输出
			结果为A,B

 

分享到:
评论

相关推荐

    new String(\"abc\")创建几个对象的解释

    java面试的疑惑剪辑 博文链接:https://z-jq1015.iteye.com/blog/248599

    JAVA面试题String产生了几个对象

    主要介绍了JAVA面试题 String s = new String("xyz");产生了几个对象?,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    String对象详解(附代码)

    对String创建几个对象代码详解,包括字符串相加,对象相加等等

    String s = new String(” a “) 到底产生几个对象?

    也就是说准确答案是产生了一个或两个对象,如果常量池中原来没有 ”宜春” ,就是两个。反之就是一个。 是的!如果面试官问到,回答一个或两个即可,但是…毕竟…毕竟 毕竟我和各位都是人才,java知识底蕴不能如此...

    JAVA面试题解惑系列(二)——到底创建了几个String对象-JAVA程序员JAVA工程师面试必看.pdf,这是一份不错的文件

    JAVA面试题解惑系列(二)——到底创建了几个String对象-JAVA程序员JAVA工程师面试必看.pdf,这是一份不错的文件

    java面试题目.docx

    答:String创建对象有两种情况: 第一种:如果String常量池中已经创建了“xyz”,则不会继续创建,此时只创建了一个对象new String(“xyz”) 第二种:如果String常量池中没有创建“xyz”,则会创建两个对象,一个...

    Java问题宝典2012版

    创建了几个String Object? 二者之间有什么区别? 25 34、String 和StringBuffer的区别 25 35、如何把一段逗号分割的字符串转换成一个数组? 26 36、数组有没有length()这个方法? String有没有length()这个方法? 26 ...

    java中创建对象的步骤有哪些.txt

    在Java中创建对象的步骤通常包括以下几个步骤: 定义类(Class):类是一个定义了对象的特性的蓝图,包括数据和方法。例如,我们可以定义一个名为“Person”的类,其中包含姓名和年龄等数据以及一些方法,如“set...

    别再问我 new 字符串创建了几个对象了!我来证明给你看!

    有人说创建了 1 个对象,也有人说创建了 2 个对象,还有人说可能创建了 1 个或 2 个对象,但谁都没有拿出干掉对方的证据,这就让我们这帮吃瓜群众们陷入了两难之中,不知道到底该信谁得。 但是今天就斗胆和大家聊聊...

    java 面对对象编程.pdf.zip

    创建一个对象用什么运算符?对象实体与对象引用有何不同? 对象的相等和引用相等的区别 类的构造方法的作用是什么? 如果一个类没有声明构造方法,该程序能正确执行吗? 构造方法有哪些特点?是否可被 override? 面向...

    jsp九大内置对象

     使用Request对象的getCookie()方法,执行时将所有客户端传来的Cookie对象以数组的形式排列,如果要取出符合需要的Cookie对象,就需要循环比较数组内每个对象的关键字。  例如:  Cookie[] c = request.get...

    关于Java的几个经典问题

    到底创建了几个String对象(三)——变量(属性)的覆盖 (四)——final、finally和finalize的区别 (五)——传了值还是传了引用(六)——字符串(String)杂谈 (七)——日期和时间的处理 (八)——聊聊基本...

    【05-面向对象(下)】

    •如果一个类始终只能创建一个对象,称为单例类。须符合以下几个条件:  –1.我们把该类的构造器使用Private修饰,从而把该 类的所有构造器隐藏起来。  –2.则需要提供一个public方法作为该类的访问点,用于创建...

    JavaScript中创建类/对象的几种方法总结

    在JS中,创建对象(Create Object)并不完全是我们时常说的创建类对象,JS中的对象强调的是一种复合类型,JS中创建对象及对对象的访问是极其灵活的。 JS对象是一种复合类型,它允许你通过变量名存储和访问,换一种...

    C#写的基本文件操作类,创建删除读写

    VS任意版本均可使用 交大老师验证与修改的 C#写的基本文件操作类 包括 创建 删除 写入 读取,满足一般用法,注意用的时候要改命名空间。 操作时两种模式,1....就是基本的几个功能,学习和使用都不错

    JavaScript创建类/对象的几种方式概述及实例

    在JS中,创建对象(Create Object)并不完全是我们时常说的创建类对象,JS中的对象强调的是一种复合类型,JS中创建对象及对对象的访问是极其灵活的。 JS对象是一种复合类型,它允许你通过变量名存储和访问,换一种...

    JAVA面试题解

    java 面试 题解 String创建了几个对象

    超级有影响力霸气的Java面试题大全文档

    创建了几个String Object? 两个 31、EJB包括(SessionBean,EntityBean)说出他们的生命周期,及如何管理事务的?  SessionBean: Stateless Session Bean 的生命周期是由容器决定的,当客户机发出请求要建立一个...

    C++中stringstream的用法和实例

    介绍:C++引入了ostringstream、istringstream、stringstream这三个类,要使用他们创建对象就必须包含sstream.h头文件。 istringstream类用于执行C++风格的串流的输入操作。 ostringstream类用于执行C风格的串流的...

Global site tag (gtag.js) - Google Analytics