您好!欢迎来到爱源码

爱源码

热门搜索: 抖音快手短视频下载   

程序员进阶之路:Java字符串关键分析 {源码交易平台}

  • 时间:2022-07-08 02:11 编辑: 来源: 阅读:268
  • 扫一扫,手机访问
摘要:程序员进阶之路:Java字符串关键分析 {源码交易平台}
这段代码创建了多少个对象?S=="abc "这个判决的结果是什么?substring(0,2)。实习生()=="ab "这是什么结果?s.charAt(index)真的能代表所有对应的字符吗?“ABC”+“gbn”+s直接字符串拼接真的比使用StringBuilder性能低吗?Java序言中字符串对象的性质与c/c++语言有很大的不同,关键是它的不变性。 然后,为了设计服务字符串的不可变性,出现了许多相关的问题:为什么要保持它们不可变?如何在底部存储字符串?如何做字符串操作才能有更好的性能?等等 另外,字符编码的知识也很重要;毕竟现在用emoij再正常不过了。 本文的内容集中在不变性上:分析字符串对象的不变性;常数的存储原理,内积法原理,字符串拼接原理,优化后的代码单元和代码点的区别。总结不变性,可以简单看几行代码:String string = " abcdstring string 1 = string . replace(" a "," b ");system . out . println(string);system . out . println(string 1);output:abcdbcstring . replace(" A "," b ")该方法将" abcd "中的A替换为b。 通过输出可以发现原来的字符串没有改变,replace方法构造了一个新的字符串“bbcd”并赋给string1变量。 这就是字符串的不变性 再举个栗子:把“abcd”的最后一个字符D改成A,在c/c++语言中,直接修改最后一个字符就可以了;但是在java中,需要重新创建一个String对象:abca,因为“abcd”本身是不可变的,不能修改。 对象字符串的值是不可变的,所有操作都不会改变字符串的值,而是构造一个新的字符串来实现字符串操作。 经常很难理解为什么Java要这样设计,不会导致性能下降吗?回顾我们日常使用string的场景,更多的时候,我们不会直接修改一个String。相反,我们使用一次,它就被丢弃了。 但是下一次,很可能,同样的String对象会被再次使用。 比如日志打印:Log.d("MainActivity ",string);在“MainActivity”前面,我们不需要改变它,但是我们会频繁使用这个字符串。 Java String被设计成不可变的,只是为了保持数据的一致性,这样文字量相同的字符串引用的是同一个对象。 示例:String s1 = " helloString s2 = " helloS1和s2引用同一个字符串对象。 如果字符串是可变的,那么这个设计就无法实现。 因此,我们可以重用我们创建的字符串对象,而无需重新创建它。 基于复用字符串的情况比改变字符串的情况多的前提下,Java将字符串设计成不可变的,保持数据一致,使得同一个文字字符串可以引用同一个字符串对象,复用已有的字符串对象。 另一个观点是在《Java编程思想》一书中提到的。 我们先来看下面这段代码:public string all case(strings){ return string . toupper case();}allCase方法将所有传入的String对象变为大写,并返回修改后的字符串。 此时调用者的期望是传递的String对象只作为信息,不希望被修改,所以String的不可变属性非常符合这一点。 在使用String对象作为参数时,我们希望String对象本身不被改变,String的不变性符合这一点。 由于字符串对象的不可变属性,存储原理不同于存储中的普通对象。 我们都知道对象是在堆上创建的,但是字符串对象其实也是一样的。不同的是,它们也存储在常量池中。 堆区中的字符串对象很可能在GC中被回收。但是常量池中的String对象不会被轻易回收,所以常量池中的String对象是可以重用的。 也就是说,常量池是字符串对象重用的根本原因。 常量池不容易被垃圾回收,所以常量池中的String对象可以一直存在并被重用。 在通常的quantity池中创建string对象有两种方法:显式使用双引号构造String对象和使用String对象的intern()方法。 这两种方法都不能保证在常量池中创建一个对象。如果常量池中已经存在同一个对象,它会直接返回该对象的引用,重用字符串对象。 创建字符串对象的其他方法是在堆中创建字符串对象。 给我举个栗子。 当我们通过new String()的方法调用String对象的实例方法,比如String.substring()方法,就会在堆中创建一个String对象。 当我们使用双引号创建一个String对象,比如String s = "abc ",或者调用String对象的intern()方法时,就会在常量池中创建一个对象,如下图所示:还记得我们文章开头的问题吗?String =新字符串(“ABC”)。这段代码创建了多少个对象?“abc”在常量池中构造了一个对象,新的String()方法在堆区创建了另一个对象,所以总共有两个对象。 s = =“ABC”的结果是假的。 两个不同的对象,一个在堆中,一个在常量池中。 南子字符串(0,2)。INTERN () = = "ab" INTERN方法在常量池中构建一个值为" ab "的String对象。“ab”语句不会构建新的String对象,但会返回现有的String对象。 所以结果是真的。 只有两种方法,即显式使用双引号构造string对象和使用String对象的intern()方法,会在常量池中创建String对象,其他方法在堆区创建对象。 常量池每次创建一个字符串对象,都会检查是否存在相同的字符串对象。如果是,它将直接返回对象的引用,而不需要重新创建对象。 还有一个关于实习生方法的问题。不同jdk版本执行的具体逻辑是不一样的。 在jdk6之前,方法区存储在永恒代的内存区,与堆区是分开的,所以在通常的度量池中创建对象时,需要进行深度复制,即完全复制一个对象,创建一个新的对象,如下图所示:永恒代有一个严重的缺点:OOM容易发生。 永生是有内存上限的,而且很小。当一个程序大量调用intern方法时,很容易发生OOM。 在JDK7中,常量池从永恒代中迁移出来,取而代之的是在heap中实现。jdk8之后在局部空间实现。 jdk7之后,常量池的实现使得对常量池中创建的对象进行浅拷贝成为可能,即不需要拷贝整个对象,只需要拷贝对象的引用,避免重复创建对象,如下图所示:观察这段代码:string s = new string(new char[]{ ' a ' });s . intern();system . out . println(s = = " a ");jdk6之前创建了两个不同的对象,输出为假;;但是jdk7之后,常量池中不会再创建新的对象,而且引用的是同一个对象,所以输出为真。 在jdk6之前,intern用于创建对象的深层副本,而在jdk7之后,浅层副本用于重用堆中的字符串对象。 通过上面的分析,在直接用双引号创建字符串时,String确实重用了字符串。 虽然使用intern方法可以返回常量池中的字符串引用,但是它在堆中已经需要一个String对象。 因此,我们可以得出一个结论:尽量使用双引号显式构建字符串;如果一个字符串需要频繁重用,可以调用intern方法将其存储在常量池中。 字符串拼接是最常见的操作。由于字符串对象的不变性,如果每次拼接都需要创建新的字符串对象,会影响性能。 所以官方引入了两个类:StringBuffer和StringBuilder。 这两个类可以组装字符串和修改字符串,而无需创建新的字符串对象。 代码如下:StringBuilder StringBuilder = New StringBuilder(" ABC ");stringBuilder.append("p ")。append(new char[]{'q'})。deleteCharAt(2)。插入(2,“ABC”);string s = stringbuilder . tostring();拼接、插入、删除都可以快速完成。 所以用StringBuilder修改、拼接等操作来初始化字符串效率更高。 StringBuffer和StringBuilder的接口是一样的,但是StringBuffer在操作方法中加入了synchronize关键字,保证了线程安全,但也付出了相应的性能代价。 单线程环境下更推荐StringBuilder。 StringBuilder和StringBuffer可以通过拼接、修改等方式提高初始化字符串时的性能。StringBuilder更适合单线程环境。 一般来说,我们将使用+来连接字符串。 +已被java中的运算符重载,可用于拼接字符串。 编译器也为+做了一系列优化 观察下面的代码:String S1 = " ab "+" CD "+" fg ";string S2 = " hello "+S1;Object object =新对象();字符串S3 = S2+object;对于s1字符串,编译器会直接将“ab”+“CD”+“fg”优化为“abcdefg”,字符串S1 =“abcdefg”;是等价的。 这种优化也减少了拼接过程中的消耗。 甚至比使用StringBuilder更有效。 S2的拼接编译器会自动创建一个StringBuilder来构建字符串。 它相当于下面的代码:StringBuilder SB = New StringBuilder();sb追加(“你好”);sb . append(S1);string S2 = sb . tostring();那么这是不是意味着我们不需要显式使用StringBuilder,编译器还是会帮我们优化的呢?当然,看看下面的代码:String s = " afor(int I = 0;我& lt=100;i++){ s+= I;}如果这里有100个循环,就会创建100个StringBuilder对象,这显然是非常错误的做法。 这时我们需要显示创建的StringBuilder对象:StringBuilder SB = New StringBuilder(" A ");for(int I = 0;我& lt=100;i++){ sb . append(I);} String s = sb . tostring();只要你想构建一个StringBuilder对象,性能就大大提高了。 字符串S3 = S2+object;字符串拼接也支持直接拼接一个普通对象。此时会调用对象的toString方法返回一个字符串进行拼接。 ToString方法是Object类的方法。如果子类没有被覆盖,将调用Object class的toString方法,默认输出类名+引用地址。 这个好像没什么问题,但是有一个很大的漏洞:* *记住不要在toString方法中直接使用+splice本身* * 下面的代码@ override public string tostring(){ return this+" ABC ";}这里直接拼接这个会调用这个的toString方法,导致无限递归 Java+拼接字符串优化:可以直接拼接常用对象的文字量,直接拼接会合成一个文字量。普通的拼接会被StringBuilder优化,但同时需要注意的是,这些优化是有限的,需要我们在合适的场景下选择合适的拼接方式来提升性能。 用Java编码,一般来说,一个char对象可以存储一个字符,一个char的大小是16位。 但是随着计算机的发展,字符集也在不断发展,16位的存储大小是不够的。因此,使用两个char,即32位来存储少量特殊字符,如emoij。 一个16位称为一个码单元,一个字符称为一个码点,一个码点可能占用一个或两个码单元。 在一个字符串中,当我们调用String.length()方法时,返回代码单元的个数,String.charAt()返回带有相应下标的代码单元。 一般情况下,没有问题。 如果允许输入特殊字符,问题就大了。 要获得码位的实数,可以调用字符串。codePointCount方法;若要获取相应的代码点,请调用String.codePointAt方法。 以便与扩展字符集兼容。 一个字符是一个代码点,一个字符称为一个代码单元。 一个代码点可以占用一个或两个代码单元。 如果允许输入特殊字符,字符串必须由码位操作。 综上所述,已经分析了关于String的几个关键问题,文章开头的读者应该也知道答案。 这些都是常见的面试问题,也是String的重点。 除此之外,正则表达式,输入输出,常用API等。也是与弦乐相关的重要内容,有兴趣的读者可以自学。 希望文章对你有帮助。


  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【技术支持|常见问题】1556原创ng8文章搜索页面不齐(2024-05-01 14:43)
【技术支持|常见问题】1502企业站群-多域名跳转-多模板切换(2024-04-09 12:19)
【技术支持|常见问题】1126完美滑屏版视频只能显示10个(2024-03-29 13:37)
【技术支持|常见问题】响应式自适应代码(2024-03-24 14:23)
【技术支持|常见问题】1126完美滑屏版百度未授权使用地图api怎么办(2024-03-15 07:21)
【技术支持|常见问题】如何集成阿里通信短信接口(2024-02-19 21:48)
【技术支持|常见问题】算命网微信支付宝产品名称年份在哪修改?风水姻缘合婚配对_公司起名占卜八字算命算财运查吉凶源码(2024-01-07 12:27)
【域名/主机/服务器|】帝国CMS安装(2023-08-20 11:31)
【技术支持|常见问题】通过HTTPs测试Mozilla DNS {免费源码}(2022-11-04 10:37)
【技术支持|常见问题】别告诉我你没看过邰方这两则有思想的创意广告! (2022-11-04 10:37)

联系我们
Q Q:375457086
Q Q:526665408
电话:0755-84666665
微信:15999668636
联系客服
企业客服1 企业客服2 联系客服
86-755-84666665
手机版
手机版
扫一扫进手机版
返回顶部