Hello,大家好!非常感谢各位能够关注本人公众号:那些年我们撸过的代码。听起来有似乎有一种沧桑感,或许本人就是年轮刻画比较老的样子吧,但是既然自己决定要做,我也遵循事务原子性,要么不做,要么做到底,做到极致。当然,我选择后者,这也是自己公众号的初衷!
这篇文章是自己的第一篇文章(重要的时刻 记录一下 20190911),自己是职场小白(工作两年了),在学习以及工作中,多多少少会遇到自己的知识盲区。通过自己的解决方式,自己是当时懂了,但以后的某一天,又遇到相同的问题时候,好像发现自己真的又不懂了!究其因,还是自己理解的不到位。自己忠于自己,自己掌握知识的多少是骗不了自己的,喜欢早上起来,在一个没有人的地方,问问自己,自己的知识存储量到底有多少,自己的知识网络架构到底又编织了多少呢?(很少很少,有点尴尬)
So,自己将自己的理解的想法与各位大佬一起谈论哈,同时个人在这里声明:
此篇文章为自己原创,或许会存在自己书写文字以及思路上的问题
此篇内容为自己原创,或许自己的见解以及理解会有偏差的地方
此篇内容为自己原创,抱着学习的心态与各位一起谈谈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这些问题的看法以及见解
希望自己的这次分享能带给思维的转变以及干货的摄取,后续有机会,继续呈上自己的小白大餐
最后,感谢大家的关注,谢谢!