`

有关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

 

分享到:
评论

相关推荐

    台州市乡镇边界,shp格式

    shp格式,可直接导入arcgis使用

    perl516-perl-Time-Piece-1.20.1-19.el6.centos.alt.x86_64.rpm

    perl516-perl-Time-Piece-1.20.1-19.el6.centos.alt.x86_64.rpm

    基于蒙特卡洛模拟的电动汽车接入对配电网影响研究:灵活容量预测、潮流计算与电压稳定性优化分析,基于蒙特卡洛法的电动汽车无序接入对配电网影响的分析 采用蒙特卡洛法对电动汽车的接入容量进行预测 再将预测

    基于蒙特卡洛模拟的电动汽车接入对配电网影响研究:灵活容量预测、潮流计算与电压稳定性优化分析,基于蒙特卡洛法的电动汽车无序接入对配电网影响的分析 采用蒙特卡洛法对电动汽车的接入容量进行预测 再将预测的结果接入IEEE33节点配电网 通过对配电网的潮流计算 得到接入前后对电网电压和网损的影响 这个接入的数目也是可以灵活改变的 这段程序主要是对一个电力系统进行潮流计算和优化。下面我将对程序的功能、应用领域、工作内容、主要思路进行详细解释。 1. 功能和应用领域: 这段程序的功能是对一个电力系统进行潮流计算和优化。潮流计算是电力系统中的一种重要分析方法,用于计算系统中各节点的电压幅值和相位角,以及各支路的功率损耗。优化是指对系统进行调整,以减小功率损耗、提高电压稳定性等方面的指标。这种潮流计算和优化在电力系统规划、运行和管理中具有重要的应用价值。 2. 工作内容: 这段程序包含了多个函数和主程序,下面将逐个进行解释。 - 主程序: - 清除工作区和命令窗口中的变量和数据。 - 从文件中加载负荷数据,并进行单位转。 - 调用函数`car

    蓄电池与超级电容混合储能系统仿真模型:功率分配与波动抑制研究,基于Matlab Simulink平台实现,结合低通滤波器与智能SOC管理策略,蓄电池与超级电容混合储能并网matlab simulink

    蓄电池与超级电容混合储能系统仿真模型:功率分配与波动抑制研究,基于Matlab Simulink平台实现,结合低通滤波器与智能SOC管理策略,蓄电池与超级电容混合储能并网matlab simulink仿真模型,混合储能采用低通滤波器进行功率分配,可有效抑制功率波动,并对超级电容的soc进行能量管理,soc较高时多放电,较低时少放电,soc较低时状态与其相反。 ,核心关键词:混合储能; MATLAB Simulink仿真模型; 功率分配; 低通滤波器; 功率波动抑制; 能量管理; 超级电容SOC; 蓄电池。,"混合储能系统:Matlab Simulink仿真模型中低通滤波器与SOC能量管理优化"

    医疗废物信息化管理系统建设方案

    该文档是医疗废物信息化管理系统建设方案,围绕医疗废物管理展开。先介绍政策历程,因医疗废物危害大,国家出台多项政策推动信息化管理。接着阐述建设背景,指出当前医废管理存在纸质单据缺陷、流程不规范等问题。随后详细说明解决方案,涵盖系统应用、业务流程等。功能设计部分分别介绍卫健委局端、医院端、处置公司的功能,如数据展示、医废采集入库出库、人员管理等,旨在实现医废精准管理、全程追溯和闭环监管。

    syslinux-perl-4.05-15.el7.x64-86.rpm.tar.gz

    1、文件内容:syslinux-perl-4.05-15.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/syslinux-perl-4.05-15.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    基于Matlab Simulink的单向Boost PFC与全桥LLC串联谐振开关电源仿真模型:2kw功率输出48V,谐振频率100kHz及输出电压闭环PFM控制,单向Boost PFC+全桥LLC

    基于Matlab Simulink的单向Boost PFC与全桥LLC串联谐振开关电源仿真模型:2kw功率输出48V,谐振频率100kHz及输出电压闭环PFM控制,单向Boost PFC+全桥LLC串联谐振开关电源Matlab simulink仿真模型 2kw功率,输出48V,谐振频率100k LLC使用输出电压闭环 PFM控制 ,核心关键词:单向Boost;PFC+全桥LLC;串联谐振开关电源;Matlab simulink仿真模型;2kw功率;输出48V;谐振频率100k;LLC输出电压闭环;PFM控制;,"Matlab Simulink仿真模型:2kW全桥LLC谐振电源的Boost-PFC研究"

    perl516-perl-File-Slurp-9999.19-10.el6.centos.alt.noarch.rpm

    perl516-perl-File-Slurp-9999.19-10.el6.centos.alt.noarch.rpm

    python项目实战之基于深度学习的电影评论情感分析源码+报告PDF

    python项目实战之基于深度学习的电影评论情感分析源码+报告PDF,个人大三设计项目、经导师指导并认可通过的高分设计项目,评审分99分,代码完整确保可以运行,小白也可以亲自搞定,主要针对计算机相关专业的正在做课程设计大作业的学生和需要项目实战练习的学习者,可作为课程设计、期末大作业。 python项目实战之基于深度学习的电影评论情感分析源码+报告PDFpython项目实战之基于深度学习的电影评论情感分析源码+报告PDFpython项目实战之基于深度学习的电影评论情感分析源码+报告PDFpython项目实战之基于深度学习的电影评论情感分析源码+报告PDFpython项目实战之基于深度学习的电影评论情感分析源码+报告PDFpython项目实战之基于深度学习的电影评论情感分析源码+报告PDFpython项目实战之基于深度学习的电影评论情感分析源码+报告PDFpython项目实战之基于深度学习的电影评论情感分析源码+报告PDFpython项目实战之基于深度学习的电影评论情感分析源码+报告PDFpython项目实战之基于深度学习的电影评论情感分析源码+报告PDFpython项目实战之基于深度

    weixin079-springboot智能校园点餐管理系统设计(论文+PPT).zip

    标题SpringBoot智能校园点餐管理系统设计AI更换标题第1章引言介绍智能校园点餐管理系统的背景、意义以及本文的设计目标和主要内容。1.1研究背景与意义分析校园点餐的现状及问题,阐述智能点餐系统的必要性和优势。1.2设计目标与内容明确系统的设计目标,概述论文的主要研究内容和结构安排。第2章系统需求分析对智能校园点餐管理系统的功能需求和非功能需求进行详细分析。2.1功能需求分析列举系统应具备的各项功能,如菜品浏览、点餐、支付等。2.2非功能需求分析分析系统的性能、安全性、可靠性等非功能需求。第3章系统设计根据需求分析,设计系统的整体架构、功能模块和数据库。3.1系统架构设计设计系统的整体架构,包括前后端分离、微服务架构等。3.2功能模块设计详细设计系统的各个功能模块,如用户管理、菜品管理、订单管理等。3.3数据库设计设计系统的数据库结构,包括表结构、数据字典等。第4章系统实现阐述系统的具体实现过程,包括开发环境、技术选型、关键代码实现等。4.1开发环境与技术选型介绍系统的开发环境,选择合适的技术栈和工具。4.2关键代码实现展示系统关键功能的代码实现,如用户登录、菜品展示、订单生成等。

    基于改进Fi-GNN模型的CTR预估方法.pdf

    基于改进Fi-GNN模型的CTR预估方法.pdf

    MAKINO系列机床操作与维修设定指南:PRO3操作、V55维护、报警表及作业规范手册,MAKINO 牧野 PRO3 维修设定操作 A55 PRO3操作说明书 日文.pdf A55卧加工作台旋转后加

    MAKINO系列机床操作与维修设定指南:PRO3操作、V55维护、报警表及作业规范手册,MAKINO 牧野 PRO3 维修设定操作 A55 PRO3操作说明书 日文.pdf A55卧加工作台旋转后加工原点计算.xlsx A61_SPECS.pdf MAKINO PRO3 V55-Operation-Guide 英文.pdf MAKINO S 系列PRO5 使用说明书PIC-Makino-S33-S56-0209.pdf MAKINO 培训课程Schulung_英文.pdf MAKINO-F3F5安装手册MANUAL 英文.pdf Makino-GF8主轴头取汲说明书.pdf MAKINO-PRO3-ProgManua英文l.pdf PIC-Makino-a61-0209.pdf V33 V55 -Series-Operation-485a-9911e英文.pdf V55-Maintenance-Guide-4v2b1563英文.pdf 牧野J5机床说明书J5_OPERATION_中文.pdf 牧野Professional5使用说明书摘要(a1系列 a51 a61 a71 a81 a8

    sgml-common-0.6.3-39.el7.x64-86.rpm.tar.gz

    1、文件内容:sgml-common-0.6.3-39.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/sgml-common-0.6.3-39.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    基于ST-GCN的轻量级行为识别方法.pdf

    基于ST-GCN的轻量级行为识别方法.pdf

    开发板FPGA电机控制源码:底层Verilog与Nios II软件架构的协同工作,集成编码器模块算法与矢量调制等核心技术,开发板FPGA电机控制源码(verilog+nios2架构)FPGA电机控制源

    开发板FPGA电机控制源码:底层Verilog与Nios II软件架构的协同工作,集成编码器模块算法与矢量调制等核心技术,开发板FPGA电机控制源码(verilog+nios2架构)FPGA电机控制源码, 方案为单FPGA方案才用底层verilog + 应用层nios2的软件架构,很具有学习价值。 包括编码器模块算法, 坐标变算法, 矢量调制算法等等。 注:此代码不适合新手小白。 ,核心关键词:FPGA电机控制源码; 开发板; Verilog; Nios2架构; 编码器模块算法; 坐标变换算法; 矢量调制算法; 学习价值; 单FPGA方案; 底层Verilog + 应用层Nios2软件架构。,基于Verilog+Nios2架构的FPGA电机控制源码:单FPGA方案学习宝典

    产品经理-yyb.apk

    产品经理-yyb.apk

    基于PSO优化的SVM时间序列预测分析:详细代码注释,数据替换即用,PSO优化SVM做时间序列预测分析,代码内注释详细,直接替数据就可以使用 ,核心关键词:PSO优化; SVM; 时间序列预测分析;

    基于PSO优化的SVM时间序列预测分析:详细代码注释,数据替换即用,PSO优化SVM做时间序列预测分析,代码内注释详细,直接替数据就可以使用 ,核心关键词:PSO优化; SVM; 时间序列预测分析; 代码内注释详细; 直接替换数据即可使用。,PSO算法优化SVM时间序列预测分析工具——注释详尽的代码直接替换数据即可使用

    telepathy-glib-0.24.1-1.el7.x64-86.rpm.tar.gz

    1、文件内容:telepathy-glib-0.24.1-1.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/telepathy-glib-0.24.1-1.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    perl516-perl-DB_File-1.826-19.el6.centos.alt.x86_64.rpm

    perl516-perl-DB_File-1.826-19.el6.centos.alt.x86_64.rpm

    OMRON CP1HPLC电子手轮控制伺服:详细教程-接线、设定与编程指南,OMRON CP1HPLC 电子手轮控制伺服,如何接线,设定,编写程序 PDF文档,我自己总结编写的教程,实际项目应用

    OMRON CP1HPLC电子手轮控制伺服:详细教程——接线、设定与编程指南,OMRON CP1HPLC 电子手轮控制伺服,如何接线,设定,编写程序。 PDF文档,我自己总结编写的教程,实际项目应用,私家珍藏。 ,核心关键词:OMRON CP1HPLC; 电子手轮控制伺服; 接线方法; 设定步骤; 编写程序; 教程; PDF文档; 实际项目应用; 私家珍藏。,OMRON CP1HPLC伺服控制教程:接线、设定与编程指南

Global site tag (gtag.js) - Google Analytics