Java面向对象的内存分析

142 阅读6分钟

小伙伴们大家好,今天继续更新java系列,之前讲完了java的数组,那么今天就来看下java中的面向对象。

谈到面向对象就不得不想到封装、继承和多态的概念了,今天不讲这三个,先来简单介绍下面向对象和分析下对象的内存。

面向对象概述

面向对象是软件开发中的一类编程风格、开发范式。除了面向对象,还有面向过程指令式编程函数式编程。在所有的编程范式中,接触最多的还是面向过程和面向对象两种。

早期先有面向过程思想,随着软件规模的扩大问题复杂性的提高,面向过程的弊端越来越明显,出现了面向对象思想并成为目前主流的方式。

面向过程:

  • 关注的焦点是过程,就是操作数据的步骤,如果某个过程的实现代码重复出现,那么就可以把这个过程抽取为一个函数
  • 典型的语言为C语言,以函数为组织单位
  • 是一种执行者思维,适合解决简单问题,扩展能力差、后期维护难度较大

面向对象:

  • 关注的焦点是类,在计算机程序设计过程中,参照现实中的事物,将事物的属性特征、行为特征抽象出来用类表示
  • 典型的语言有java、c++、c#、python和php等
  • 是一种设计者思维,适合解决复杂问题,代码扩展性强、可维护性高

举例:人把苹果装进冰箱。

# 面向过程
打开冰箱
把苹果装进冰箱
把冰箱门关闭
# 面向对象
人{
	打开(冰箱){
		冰箱.开门();
	}
	操作(苹果){
		苹果.进入(冰箱);
	}
	关闭(冰箱){
		冰箱.关门();
	}
}

冰箱{
	开门(){}
	关门(){}
}

苹果{
	进入(冰箱){}
}

类和对象

类和对象都是面向对象的核心概念。

概述

类:具有相同特征的事物的抽象描述,是抽象的、概念上的定义。

对象:实际存在的该类事物的每个个体,是具体的,因而也称为实例。

可以理解为:类=>抽象概念的人,对象=>实实在在的某个人。

类的成员的概述

面向对象程序设计的重点是类的设计,类的设计其实就是成员的设计。

类是一组相关属性和行为的集合,这也是类最基本的两个成员。

属性:该类事物的状态信息,对应类中的成员变量。

行为:该类事物要做什么操作,或者基于事物的状态能做什么,对应类中的成员方法。

面向对象的步骤

要完成面向对象的功能,分为三个步骤,分别是类的定义、对象的创建、对象调用属性或方法。

类的定义: 类的定义使用关键字class,格式如下。

[修饰符] class 类名{
	属性声明;
	方法声明;
}

对象的创建: Java类的实例化即是创建类的对象,创建对象使用关键字new,语法如下:

//方式1:给创建的对象命名
类名 对象名 = new 类名();
//方式2:创建匿名对象
new 类名()

:如果一个对象只需要调用一次就可以使用匿名对象,通常将匿名对象作为实参传递给一个方法调用。

对象调用属性或方法: 对象是类的一个实例必然具备该类事物的属性和方法,使用对象名.属性或对象名.方法的方式访问对象成员,如下:

//声明Animal类
public class Animal {
    public int legs;
    public void eat() {
        System.out.println("Eating");
    }
    public void move() {
        System.out.println("Move");
    }
}
//声明测试类
public class AnimalTest {
    public static void main(String args[]) {
        //创建对象
        Animal dog = new Animal();
        dog.legs = 4; //访问属性
        System.out.println(xb.legs);
        dog.eat(); //访问方法
        dog.move(); //访问方法
    }
}

对象的内存解析

先来复习下之前说过的JVM的内存划分。

JVM内存结构划分

HotSpot Java虚拟机的架构图如下,其中主要关心的是运行时数据区部分(Runtime Data Area)。

下面介绍下堆、栈和方法区:

  • 堆(Heap):此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是,所有的对象实例以及数组都要在堆上分配。
  • 栈(Stack):是指虚拟机栈,虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址),方法执行完会自动释放。
  • 方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

对象的内存

在HotSpot虚拟机中,对象在堆内存中的存储布局可以划分为三个部分:对象头、实例数据和对齐填充,如下。

这里看下java对象和数组对象的内部结构:

其中对象头分为对象标记和类元信息,类元信息存储的是指向该对象类元数据的首地址。

下面写个示例代码看下内存解析:

class Person {
    String name;
    int age;
    boolean isMale;
}
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "张三";
        p1.age = 20;
        p1.isMale = true;

        Person p2 = new Person();
        p2.age = 10;

        Person p3 = p1;
        p3.name = "李四";
    }
}

内存解析图如下:

总之,凡是new出来的结构(对象、数组)都放在堆空间中,对象的属性也存放在堆空间中。创建一个类的多个对象(比如p1、p2),则每个对象都拥有当前类的一套"副本"(即属性),当通过一个对象修改其属性时不会影响其它对象此属性的值。当声明一个新的变量使用现有的对象进行赋值时(比如p3 = p1),此时并没有在堆空间中创建新的对象,而是两个变量共同指向了堆空间中同一个对象。当通过一个对象修改属性时,会影响另外一个对象对此属性的调用。

那么对象名中存储的是什么呢,先说结论:对象地址。下面举例:

public class StudentTest{
    public static void main(String[] args){
        System.out.println(new Student()); //Student@7856e922
        Student stu = new Student();
        System.out.println(stu); //Student@7e29154f
        int[] arr = new int[5];
		System.out.println(arr); //[I@893ea4e
    }
}

直接打印对象名和数组名都是显示“类型@对象的hashCode值",所以说类、数组都是引用数据类型,引用数据类型的变量中存储的是对象的地址,或者说指向堆中对象的首地址

今天的内容就到这里,关于类的成员下篇再做介绍,喜欢的话点个关注吧,下篇见!