小白谈谈String那些事


 

Hello,大家好!非常感谢各位能够关注本人公众号:那些年我们撸过的代码听起来有似乎有一种沧桑感,或许本人就是年轮刻画比较老的样子吧,但是既然自己决定要做,我也遵循事务原子性,要么不做,要么做到底,做到极致。当然,我选择后者,这也是自己公众号的初衷!


 这篇文章是自己的第一篇文章(重要的时刻 记录一下 20190911),自己是职场小白(工作两年了),在学习以及工作中,多多少少会遇到自己的知识盲区。通过自己的解决方式,自己是当时懂了,但以后的某一天,又遇到相同的问题时候,好像发现自己真的又不懂了!究其因,还是自己理解的不到位。自己忠于自己,自己掌握知识的多少是骗不了自己的,喜欢早上起来,在一个没有人的地方,问问自己,自己的知识存储量到底有多少,自己的知识网络架构到底又编织了多少呢?(很少很少,有点尴尬


 So,自己将自己的理解的想法与各位大佬一起谈论哈,同时个人在这里声明:

  1. 此篇文章为自己原创,或许会存在自己书写文字以及思路上的问题       

  2. 此篇内容为自己原创,或许自己的见解以及理解会有偏差的地方

  3. 此篇内容为自己原创,抱着学习的心态与各位一起谈谈String那些事

      小白的世界也许会很狭隘,但是小白坚信:知来者之可追,自己的努力总会有回报的那一天,也许会迟到,但一定会到来。这句话,与各位共勉 !


       好了,话不多话,直接来干吧~

      俗话说的好,学习东西,师傅领进门,修行在个人。在Java世界亦是如此,甚至有句经典语句,入门开始,从HelloWorld开始吧!

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

       唠叨了这么久,终于上代码了。看到了"Hello World!"  终于谈到咱们这一次的正题了,没错 String字符串,其实,在这里我们现在就应该问问自己,到这里算是完事了吗,我敲出来了代码,我是不是入门了呀,也许你跟我一样是在小白的路上奋进着,你也许会说,对呀,我入门了,我敲这段代码,我已经基本掌握了Java语法,运用了jdk javac编译 输出来了结果。

      附:由于现在大都是jdk1.8 所以我本人自己也用的1.8 。接下来以下的内容,显示的编译结果也会是1.8编译的,为啥强调这个 ,因为 1.8 与 1.7甚至之前会有点区别(比如 方法区  元空间,这块的堆内存结构以及JMM模型,不是这次的重点,后期 自己也可以来写写)


手敲这代码,大家都会,但是大家的思维模式是否会是一样呢,前者,敲完了感觉自己做完了完事 了,后者会继续思考,这段代码,编译的时候,发生了什么呢,为啥会是这样输出结果呢?不管是前者 or 后者

  • 是否思考过 static 以及 main(Stirng[] args)

  • 是否思考过java文件编译成class文件 以及 class文件被JVM加载的过程


以上思考,好像跟这次的谈谈String 好像没有什么多大的联系,这涉及到的JVM类加载机制以及Javac编译的原理,既然写到这里了,自己也会稍微涉及到一点吧!


那在这里呢,自己就来稍微提一下这把,也算是为后面下文做一个抛砖引玉吧!

Hello World!
Process finished with exit code 0

相信大家对于 Hello World! 控制台输出觉得没啥问题,但是 

Process finished with exit code 0 这个东东是啥东西呢,为啥也会打印出来?好吧,为了满足有的人好奇心,自己在这里 javac源码走一波,简单介绍一下,为啥这个东东会出来


  • 当javac在编译的时候,会发生什么样的事情呢?

  • main函数是主入口,理解这真的很重要,(并发编程里面main函数 fork其他线程以及SpringBoot的启动加载等等)




看到这里 通过撸javac源码,是不是 明白了 Stirng[] args 这个参数了呢


看到这里 是否明白了 为啥有 0 输出了吧,底层的断点,这次就不带大家看了,明白为啥究竟是输出这样的就ok.其实javac 返回结果状态码0 是运用到了枚举 (这里也提示了 在我们写代码的时候,返回结果状态码,最好用枚举,后期代码好维护以及代码可读性


// 编译完成,没有错误.
 OK(0),
// 已完成但已报告的错误.
ERROR(1),
// 错误的命令行参数
CMDERR(2),
// 系统错误或资源耗尽.
SYSERR(3),
 // 编译程序异常终止
 ABNORMAL(4);

撸了撸 javac源代码,就完事了吗?好像还没入手我们的正题呢?

别急,再来研究一个东东,对后面的入戏有好处

上文也提到过了,java源文件通过javac编译为了.class文件,然后重点来了,.class文件是如何被JVM所识别,这就涉及到了JVM的知识了,(以后有空再来谈谈JVM,到时看看我这个小白到底怎么研究以及吃透JVM)

相信大家,看到这里.class文件,我也会啊,我也知道啊,比如会说生产环境功能要优化,项目部署的时候,找准对应的class文件路径替换就直接完事了?但是 ,是否想过,真的完事了吗?是否打开过 class文件看看究竟是啥东东呢?


看到这个图,相信大家都会有我这个疑问。全都是乱码

别急。来看我的 这个图,就是打开的 .class文件



      

没错,.class文件就是十六进制的形式,然后回到主题,怎么被JVM识别

其实我已经截图了  就是 因为有  CAFE BABE   以及  00 00 00 34 这些JVM的知识,以后后期再写写,这次就不细说JVM了。所以所要表达的意思就是,如果生产环境是jdk1.7,你编译的class文件是1.8编译的,直接替换,是不行的,原因就是 00 00 00 34 这个的作用,会检查jdk版本,以及 高版本 低版本的兼容问题,这在JVM类加载机制中,验证环节会出现的问题


     这个时候,或许会有人问了,我看不懂十六进制这个东东,我来反编译一下。好,那就来反编译一下,这也算是我们开始入戏了



      以上的图,其实就是 字节码被JVM识别解析的过程,在这里就不撸字节码指令了,还是留给以后,写写关于JVM的,其实,图上的三个黄框 才是重点,类名 HelloWorld    方法main()    以及字节码指令ldc 将String类型的 HelloWorld 字面常量值 压栈 到 3号常量池里面。最后调用invokevirtual指令 将HelloWorld!输出,这其实就是为啥是这样输出来的原因

(所以这也是顶级高手厉害的原因,可以手写字节码,打破JVM双亲委派机制,从而达到自己的目的)

      好了,有了以上铺垫,这下我们就来开始搞事情啦


 public static void main(String[] args) {
        String str ="y" ;
        str= str+"w"+"e";
        str = str +"w"+"e"+"i";
        System.out.println(str);
    }

       问题:请问 以上的图  执行 main 方法创建了几个对象呢

       哈哈 其实我真的想说,多少个真的不重要,重要的是要明白底层原理,毕竟万变不离其宗,我现在大声的吼出来答案,意义也不大,下次题目在稍微一变化,就又是一种答案。思维模式的转变,才会让自己立于不败之地,才是制胜之道,王者之道!

那么如何入手呢?

大家应该都知道,对象就是在 堆内存中,又涉及到JVM了,可以见JVM是如此重要,同时也要熟悉JMM模型,具体的我这里暂且不说,对 常量池,方法区,堆 ,都应该听说过,接下来,继续我这个小白的表演

那我们就继续来撸字节码

这就要求必须弄懂 常量池 方法区 以及 堆内存的具体意义了

题目千变万化,不变的是知识的考察点。

在这里,就不去一一撸字节码,只想说的是,要想大声的说出这个答案,真的是 常量池  方法区 堆内存 要理解透

JVM中的invokespecial指令是用来调用类的构造方法和私有方法

              invokevirtual调用实例方法

附:由于我这里是 jdk1.8编译的,用jdk1.7编译后,这道题的答案好像又变了,同时还有其他变化因素,比如,题目中 字符串常量的字面值,在之前出现过,常量池 已经存在 了,那么这道题的答案又变了

 public static void main(String[] args) {
        String str ="y" ;
        String s = "y"+"w"+"e";
        str= str+"w"+"e";
        System.out.println(s==str);
        str = str +"w"+"e"+"i";
    }

 问题:请问 以上的图  执行 main 方法创建了几个对象呢,以及输出是什么呢?

 我想 题目不管怎样变化,只要抓住 常量池 以及  字符串拼接过程中运用的StringBuilder创建对象,就可以大声的吼出答案来

来来来,说到这里,不得不说一个最经典的案例了

Stirng s = new String("yww");

问题:请问这个 创建了几个对象呢?

回答:1

回答:2

我想问问你们,回答 1  或者  2  你们确定吗

如果你大声的告诉我,确定

那么 我想说,老铁,你回答错误,这道题本身就是坑,这还是要看具体的情形,还是要真正理解 常量池  方法去  堆内存

话不多说,直接上图



字节码指令的过程,实质是可以帮助我们分析代码是如何进行的

说一种情况,如果字面量在之前出现过

String s = "yww";
String s1 = new String ("yww");


这个时候。又可以看到 执行的又不一样了...

进行到这里,最后,来下我们的经典


String s1 = "yww";
String s2 = "yww";
String s3 = new String ("yww");
String s4 = new String ("yww");
System.out.println(s1 == s2)
System.out.println(s1 == s3)
System.out.println(s3 == s4)
System.out.println(s1 == s3.intern())
System.out.println(s3.intern() == s4.intern())
会输出什么呢?
String s1 = "yww";
String s2 = "yw";
String s3 = "w";
System.out.println(s1 == "yw"+"w");
System.out.println(s1 == "yw"+s3);
会输出什么呢?

考查知识点字面量   内存地址  StringBuilder拼接  intern()方法 内存结构

不早了,现在都是凌晨三点钟了,代码写完了。第一次写这样的文章,感觉自己啰嗦了好多,

以上就是自己作为小白对于String这些问题的看法以及见解

希望自己的这次分享能带给思维的转变以及干货的摄取,后续有机会,继续呈上自己的小白大餐

最后,感谢大家的关注,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值