一切都是对象

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成员是对象级别的。

 

以上是关于一切都是对象的主要内容,如果未能解决你的问题,请参考以下文章

片段中ListView的android自定义适配器

Android Studio - 在片段之间更改时底部导航崩溃

读TIJ -2 一切都是对象

水合没有查询的中继片段

python中一切皆是对象,对象都是在堆上存放的,一切都是指针

.NET 中的一切都是对象吗?