一切都是对象
Posted Another_me
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一切都是对象相关的知识,希望对你有一定的参考价值。
1.数据存储的位置
在程序中,数据可以存在于下面的位置:
- 寄存器 寄存器是最快的存储区,C和C++允许程序员建议寄存器分配方式,java没有这种行为。
- 堆栈。 堆栈是一个很容易误解的概念,很多书里都喜欢用这个名字。其实堆栈指的就是程序运行时栈内存,在java中就是jvm虚拟机栈和本地方法栈。有的虚拟机,如HotSpot就把二者合二为一了,只有一个虚拟机栈。在java中,方法体里的基本数据类型和对象的引用是存在栈中的,它们被存在栈中的一个“局部变量表中”。栈可以利用堆栈指针访问,通过这个指针可以从处理器那里获得直接的支持。通俗的理解就是,编译器需要了解栈中存储数据的存活时间,这对程序员是透明的。
- 堆。 这是java中对象的存储区域。可以理解为一种通用的内存池,堆与栈不同的地方在于编译器不需要知道堆中对象的存活时间,对象占用内存的回收交给jvm管理。当需要一个对象的时候只需要简单调用new 方法,虚拟机就会自动在堆中分配内存。
- 常量存储。 常量通常存储在程序代码内部,在java中常量一边存储于方法区中的运行时常量池中。
- 非RAM存储。 两个基本例子,流对象和持久化对象。对象可以被转化为字节流发送到网络,或被持久化存储在磁盘中。在后面可以看到这样的例子。
上图展示了运行时数据分区。程序计数器只占用少量的内存。堆内存区域和方法区是所有线程共享的,每个线程都有自己的程序计数器,虚拟机栈,本地方法栈,每个线程在执行的时候,每次进出一个方法,就对应这虚拟机栈或本地方法栈的一个栈帧的入栈和出栈。这里的程序计数器负责指示字节码的地址。
2.通过引用访问对象
public class Light { private int brightness; public Light(int brightness) { this.brightness = brightness; } public void on(){ System.out.println("the light is on" + " " + brightness); } public void off(){ System.out.println("the light is off" + " " + brightness); } public static void main(String[] args){ Light light = new Light(10); light.on(); light.off(); } }
看一段代码。从这段代码里分析一下一个简单的java程序在运行中具体发生了什么。编写完这个类后,将这个类编译为字节码,实际中在集成开发环境,eclipse或idea中运行这个类的时候已经帮我们做好了编译的工作。编译后,jvm的类加载器会将这个字节代码加载到内存中的方法区,现在可以将字节码理解为某些“描述性”的东西,jvm会自动寻找main函数,程序的入口就在这里。
在这个时候在栈内存中有一个栈针开始入栈,栈针存储的局部变量表会记录light这个对象的引用,在堆中开始创建对象,然后调用对象的方法,
由上表可以看出,基本数据类型存储在栈的本地变量表中,对象中的数据存储在堆中的对象内存区域,本例中就是brightness的值。类的方法存储在方法区中,调用一个对象的方法时,通过对象的引用找到对象的实例数据,和要调用的方法,完成调用。
4.默认值
为了保证安全,java会给没有显示初始化的对象数据成员一个默认值,无论它是对象的引用还是基本数据类型。
public class Light { private int brightness; private List<String> list; public void on(){ System.out.println(brightness); } public void off(){ System.out.println(list.toString()); } public static void main(String[] args){ Light light = new Light(); light.on(); light.off(); } }
0 Exception in thread "main" java.lang.NullPointerException at com.zhangjun.study.Light.off(Light.java:17) at com.zhangjun.study.Light.main(Light.java:23) Process finished with exit code 1
上面代码的执行展示了这一点。创建light对象的时候,虚拟机在堆中分配一块区域,同时“清除”这块区域,如果没有显式的构造函数或者初始化,那么对象的数据成员将会被分配给“默认值”,例如基本数据类型int默认值是0,对象类型默认为null,所以当试图调用对象的时候会产生空指针错误。当然其它数据类型也有其基本的默认值。
如果程序中对数据成员进行了初始化,或在构造函数中进行初始化,那创建的对象包含的数据成员的值就是初始化的值。默认的顺序是 分配默认值——>在定义处初始化——>构造函数初始化。 所以构造函数相当于创建一个对象的最后“把关者”,当然也可以在创建对象后再对数据成员赋值,如果你能保证你不会在调用数据成员之前忘掉的话。
5.传值调用还是传引用调用
在java中,调用一个方法传参过程中,基本数据类型是传值,对象是传引用。
public class Light { private int weight; public int getWeight() { return weight; } public Light(int weight) { this.weight = weight; } public void setWeight(int weight) { this.weight = weight; } public void change(int a, Light light){ a++; light.setWeight(1000); } public static void main(String[] args){ Light light = new Light(10); int a = 199; System.out.println("before change a"+ " " + a); System.out.println("before change light.weight"+ " " + light.getWeight()); light.change(a, light); System.out.println("after change a"+ " " + a); System.out.println("after change light.weight"+ " " + light.getWeight()); } }
before change a 199 before change light.weight 10 after change a 199 after change light.weight 1000 Process finished with exit code 0
调用change的时候,相当于把局部变量a的值复制了一份,传给了change方法里的a,而change方法的a是存储在change这个方法对应的栈帧中,与main方法中的a是两个变量,所以改变change方法里的a不会对main方法中的a造成影响。
而对应light对象而言,相当于把这个对象的引用,复制一份到change方法中的栈帧中,实际上,对于这个引用来讲,相当于传“值”,对整个对象来说,相当与传“引用”,通俗一点就是二者指向的是一个对象。(可以参照上图理解)
6.static关键字
static翻译过来是静态的,静态的就是不随着对象变化而变化,如果一个数据成员或方法被标记为static,那么这个成员和方法就是这个类所有对象所共享的。可以在不用创建对象的情况下直接调用静态成员或方法。
public class Light { public static int weight = 1; public Light(int weight) { this.weight = weight; } public static int getWeight() { return weight; } public static void main(String[] args){ System.out.println(weight); Light light = new Light(10); System.out.println(weight); } }
1 10 Process finished with exit code 0
可以看到,第一次调用weight是在没有创建对象的情况下产生的。main函数没什么特殊的,只是这个类中的一个静态方法,作为程序的入口。静态方法的内部不能引用非静态成员或方法,非静态的成员或方法只有在创建对象后可以被调用,这从getWeight中也可以看出。
可以看到,当创建一个light对象后,这个“破坏者”把大家的公共财产“weight”改成了它自己想要的结果,这样做的后果就是后面的所有light对象都要接受这个结果。
总结:static成员是类级别的,非static成员是对象级别的。
以上是关于一切都是对象的主要内容,如果未能解决你的问题,请参考以下文章
Android Studio - 在片段之间更改时底部导航崩溃