Hello,大家好。
有段时间不见了,最近这阵子有点忙。
我java已经进入复习阶段,所以最近更新频率会高一些。
然后抽出其余的时间去学Android开发和高并发技术。
我想先说几句题外话,java是解释型语言,我们平常编写的java代码编译后会编译成class文件,然后class文件会被JVM虚拟机解释成机器可以识别的二进制数据然后运行。
所以,不管你的java代码写的到底多么的妖魔鬼怪,JVM只认class文件,所以,最终到底程序是怎么运行的,或者研究一些原理,得看class,得学JVM。
其实这就是分层思想,把代码分层,层层封装。
下面是我的研究代码:
class A{
private String msg="尘封已久的恨意";//直接给成员变量赋值
// public A(String msg){//构造方法赋值
//
// this.msg=msg;
// }
{
//构造块赋值
msg="打扫房间哦i啊但是金佛IP圣诞节发票收据阿萨PDF觉得撒泼覅静安寺粕发酵埃斯珀附件";
}
public String getMsg(){
return this.msg;
}
}
public class Main {
public static void main(String[] args) {
A a=new A();
System.out.println(a.getMsg());
}
}
好的这里,我也不卖关子,直接说结论。
不管你是在构造块里对成员变量赋值还是直接给成员变量赋值,程序运行后,赋值的语句都被还原到了构造方法里。
也就是说,你给成员变量赋值的办法只有构造方法一种。
以下是证据:
<init>()V
L0
LINENUMBER 4 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
L1
LINENUMBER 6 L1
ALOAD 0
LDC "\u5c18\u5c01\u5df2\u4e45\u7684\u6068\u610f"
PUTFIELD com/myjava/A.msg : Ljava/lang/String;
L2
LINENUMBER 16 L2
ALOAD 0
LDC "\u6253\u626b\u623f\u95f4\u54e6i\u554a\u4f46\u662f\u91d1\u4f5bIP\u5723\u8bde\u8282\u53d1\u7968\u6536\u636e\u963f\u8428PDF\u89c9\u5f97\u6492\u6cfc\u8985\u9759\u5b89\u5bfa\u7c95\u53d1\u9175\u57c3\u65af\u73c0\u9644\u4ef6"
PUTFIELD com/myjava/A.msg : Ljava/lang/String;
L3
LINENUMBER 18 L3
RETURN
L4
LOCALVARIABLE this Lcom/myjava/A; L0 L4 0
MAXSTACK = 2
MAXLOCALS = 1
这个是字节码中的构造方法。
你可以把里面的Unicode编码转成字符串看看.
你赋值的语句最终都被还原到构造方法里了。
但是这里有一个先后顺序
{
msg="打扫房间哦i啊但是金佛IP圣诞节发票收据阿萨PDF觉得撒泼覅静安寺粕发酵埃斯珀附件";
}
private String msg="尘封已久的恨意";
z这一次构造块赋值在前,直接赋值在后。
把它俩颠倒一下赋值的顺序,对应着在字节码中的构造方法里,顺序也被颠倒了。
字节码(构造方法):
<init>()V
L0
LINENUMBER 4 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
L1
LINENUMBER 8 L1
ALOAD 0
LDC "\u6253\u626b\u623f\u95f4\u54e6i\u554a\u4f46\u662f\u91d1\u4f5bIP\u5723\u8bde\u8282\u53d1\u7968\u6536\u636e\u963f\u8428PDF\u89c9\u5f97\u6492\u6cfc\u8985\u9759\u5b89\u5bfa\u7c95\u53d1\u9175\u57c3\u65af\u73c0\u9644\u4ef6"
PUTFIELD com/myjava/A.msg : Ljava/lang/String;
L2
LINENUMBER 11 L2
ALOAD 0
LDC "\u5c18\u5c01\u5df2\u4e45\u7684\u6068\u610f"
PUTFIELD com/myjava/A.msg : Ljava/lang/String;
RETURN
L3
LOCALVARIABLE this Lcom/myjava/A; L0 L3 0
MAXSTACK = 2
MAXLOCALS = 1
我写这种帖子其实对我帮助不是很大,主要就是复习的时候用。
我希望可以帮助那些新手,如果能帮到你真的太好了。
嗯?你以为就这样就完了吗???
哪有这么简单。
让我们来看一下最后的一种情况。
前面两种情况,我只是想告诉大家,不管你在哪里赋值,赋值的语句都会被还原到构造方法里面,然后它们的在构造方法里的赋值顺序,和你写的java代码里的顺序是对应的。
这一次,我要研究的是, 1.直接给成员变量赋值,2.通过构造块给对象赋值,3.通过构造方法赋值这三种赋值的顺序。
class A{
{
//构造块赋值
msg="打扫房间哦i啊但是金佛IP圣诞节发票收据阿萨PDF觉得撒泼覅静安寺粕发酵埃斯珀附件";
}
private String msg="尘封已久的恨意";//直接给成员变量赋值
public A(){
//通过构造方法赋值,为了看字节码的时候效果更加直观,所以直接我把要赋值的内容直接写死在构造方法里了。
this.msg="oiasdjfoisajisdaofjosdapjfosipajfpsoaijfjpsoiafjdsopafjspaoifjsdopafjdsapoifjsdapoifjsdpoaijfopdspaofifjsdapofjdsopajfposaijfdposajfposajf";
}
public String getMsg(){
return this.msg;
}
}
以上就是这三种赋值的状态,
OK,我先说结论,不管你怎么折腾着三种赋值语句的顺序,在构造方法里赋值的语句,顺序永远是垫底的。
就算你把构造方法写在java代码最前面,运行后一看字节码,构造方法赋值的语句一样排在最后。
直接给对象赋值和构造块赋值,他们俩都在构造方法赋值语句的前面,且它俩的顺序会在java代码里的顺序对应
证据:(字节码文件)
public <init>()V
L0
LINENUMBER 13 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
L1
LINENUMBER 8 L1
ALOAD 0
LDC "\u6253\u626b\u623f\u95f4\u54e6i\u554a\u4f46\u662f\u91d1\u4f5bIP\u5723\u8bde\u8282\u53d1\u7968\u6536\u636e\u963f\u8428PDF\u89c9\u5f97\u6492\u6cfc\u8985\u9759\u5b89\u5bfa\u7c95\u53d1\u9175\u57c3\u65af\u73c0\u9644\u4ef6"
PUTFIELD com/myjava/A.msg : Ljava/lang/String;
L2
LINENUMBER 11 L2
ALOAD 0
LDC "\u5c18\u5c01\u5df2\u4e45\u7684\u6068\u610f"
PUTFIELD com/myjava/A.msg : Ljava/lang/String;
L3
LINENUMBER 15 L3
ALOAD 0
LDC "oiasdjfoisajisdaofjosdapjfosipajfpsoaijfjpsoiafjdsopafjspaoifjsdopafjdsapoifjsdapoifjsdpoaijfopdspaofifjsdapofjdsopajfposaijfdposajfposajf"
PUTFIELD com/myjava/A.msg : Ljava/lang/String;
L4
LINENUMBER 16 L4
RETURN
L5
LOCALVARIABLE this Lcom/myjava/A; L0 L5 0
MAXSTACK = 2
MAXLOCALS = 1
可以看到,在构造方法里赋值的语句,排在构造方法的最后,而那俩赋值都在它前面。
所以直接赋值和构造块赋值就是一种假象,真赋值还是得靠构造方法。
好了,本文结束。
我是尘封已久得恨意,原创码字不易,希望支持一下老铁。
我一直相信费曼学习法。大家一起共勉吧。
有什么不懂得,希望大家多交流吧。