试题一: 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
相关推荐
java面试的疑惑剪辑 博文链接:https://z-jq1015.iteye.com/blog/248599
在Java中,创建 `String` 对象时,主要涉及以下几种内存区域: 1. **常量池** (Constant Pool): 每个类或接口都有自己的常量池,它包含了编译期间确定的所有字面量和符号引用。 2. **堆** (Heap): 动态分配的内存...
通过编写和测试自定义的String类,初学者可以深入理解以下几个核心概念: 1. **对象和引用**:了解如何创建和使用对象,以及如何通过引用操作对象。 2. **封装**:理解数据成员的封装,以及为什么将字符串内容设为...
下面我们将详细讲解几个重要的`String`类的方法: 1. **构造方法**: - `String()`:创建一个空字符串。 - `String(char[] array)`:根据字符数组创建字符串。 - `String(String anotherString)`:复制已有字符...
为了更好地理解和使用String类,本文将从几个关键方面入手,探讨如何更加高效、合理地使用String对象。 #### 一、初始化String对象 **1.1 直接赋值** 最常见的初始化方式是通过直接赋值的方式创建String对象: ``...
如果比较的是两个对象变量,则是判断它们的引用是否指向同一个对象。 例如,Java 代码: String a = "ab"; String b = "ab"; System.out.println(a == b); // 输出:true 在这个例子中,a 和 b 指向同一个对象,...
对String创建几个对象代码详解,包括字符串相加,对象相加等等
标题中的“string类的构造方法”揭示了我们要探讨的核心内容——如何初始化和创建`std::string`对象。`std::string`类不仅在日常编程中频繁使用,而且在很多高级功能如正则表达式、文本处理等方面都有重要应用,因此...
为了创建自己的`string`类,我们需要考虑以下几个关键知识点: 1. **内存管理**:字符串是由字符数组组成的,因此需要处理动态内存分配和释放。这通常涉及到构造函数、析构函数以及复制构造函数。在C++中,遵循深...
在这个自定义的`string`类中,主要涉及以下几个知识点: 1. **字符指针**:字符指针是C/C++语言中处理字符串的基本工具。它是一个指向字符的指针,可以用来指向字符串的首字符。例如,`char *str`可以用来声明一个...
JAVA面试题String产生了几个对象 JAVA面试题String产生了几个对象?这个问题是很多Java开发者面试中常见的问题,本文将通过示例代码详细介绍String s = new String("xyz");产生了几个对象。 首先,需要了解Java中...
也就是说准确答案是产生了一个或两个对象,如果常量池中原来没有 ”宜春” ,就是两个。反之就是一个。 是的!如果面试官问到,回答一个或两个即可,但是…毕竟…毕竟 毕竟我和各位都是人才,java知识底蕴不能如此...
- `str3`和`str4`分别通过`new String()`创建,它们在堆中,因此`str3==str4`为`false`,创建了三个对象。 - `i`和`j`都是基本类型`int`,它们在栈中直接存储,所以`i==j`为`true`,只创建了一个对象。 - `l1`和`k1`...
2. **拷贝构造函数和赋值运算符**:这两个是C++中的深拷贝问题,确保在复制对象时,每个对象都有独立的内存空间,不会共享数据。 3. **迭代器**:虽然不是必须的,但实现迭代器可以提供类似标准库`std::string`的...
`string` 类提供了多种构造函数,以便用户能够灵活地创建 `string` 对象。 - **使用 C 风格字符串初始化**:`string(const char *s);` - 这种构造方式将 C 风格字符串 `s` 转换成 `string` 对象。例如: ```cpp ...
首先,一个自定义的`String`类通常会包含以下几个核心部分: 1. **数据成员**:存储字符串的实际字符序列,通常是一个`char`数组或`std::vector<char>`。初始化时可能需要指定默认容量以减少动态内存分配的频率。 ...
首先,`String`类通常会包含以下几个核心组成部分: 1. **数据成员**:用于存储字符串的字符数组。由于字符串长度是可变的,所以通常会使用动态内存分配来存储字符串,而不是固定大小的数组。这可能涉及到`char*`...
`string`类提供了几个方法来管理字符串的容量和大小: - `capacity()const;`:返回当前分配的存储空间的容量,可能大于实际的字符串长度。 - `max_size()const;`:返回`string`可以达到的最大尺寸。 - `size()const...