'); } '); } 对象的创建与使用-内存分析 | Journey to paradise

对象的创建与使用-内存分析


对象的创建与使用-内存分析

方法执行内存分析

注:栈原理图
栈原理图

部分源码示例

//方法调用的时候,在参数传递的时候,实际上传递的是变量中保存的那个"值"传过去了。
public class MethodTest01{
    public static void main (String [] args){
            int a=10;
            int b=20;
            int retvalue = sumInt (a,b);
            System.out.println ("retvalue= " + retvalue);
    }
    public static int sumInt (int i,int j){
            int result = i + j;
            int num = 3;
            int retvalue = divide (result, num) ;
            return retvalue;
    }
    public static int divide (int x , int y){
            int z = x / y;
            return z;
    }
}

内存分析图

方法执行内存图
处在栈顶的元素具有活跃状态。

递归方法执行内存分析

要点

1、什么是递归?
   方法自身调用自身。
2、递归是很耗费栈内存的,递归算法可以不用的时候尽量别用。I
3、程序运行的时候发生以下错误【不是异常,是错误Error】:java.lang.StackOverflowError
栈内存溢出错误,错误发生无法挽回,只有一个结果,就是JVM停止工作。
4、递归必须有结束条件,没有结束条件一定会发生栈内存溢出错误。
5、递归即使有了结束条件,即使结束条件是正确的,也可能会发生栈内存溢出错误,因为递归的太深了

示例

public class RecursionTest01{
    public static void main(String[] args){
        System.out.println( "main begin");
        dosome();
        System.out.println( "main over");
    }
    //以下的代码片段虽然只有一份,但是可以被重复的调用,并且只要调用doSome方法就会在栈内存中新分配一块所属的内存空间.
    public static void dosome{
        System.out.println("doSome begin");
        dosome();//这行代码不结束, 下一行程序不能执行System.out.println("doSome over");
    }

部分源码

public class RecursionTest03 {
    public static void main(String[] args) {
        //1~4的和
        int n = 4;
        int retvalue = sum(n);
        System.out.println(retvalue);
    }

    public static int sum(int n) {
        //4+3+2+1
        if (n == 1) {
            return 1;
        }
        return n + sum(n - 1);
    }
}

内存图分析

递归方法执行内存分析

递归原理图

递归原理图


对象的创建与使用-内存分析

要点

    1、变量必须先声明,再赋值才能访问。

    注意:对于成员变量来说,没有手动赋值时,系统默认赋值。

    类型				默认值
    -------------------------
    byte				0
    short			   0
    int				  0
    long				0L
    float			   0.0F
    double			   0.0
    boolean			  false
    char			   \u0000
    引用数据类型	    null

    null是一个java关键字,全部小写,表示空。是引用类型的默认值。

    2、通过一个类可以实例化N个对象

    实例化对象的语法:new 类名()
    new运算符的作用是创建对象,在JVM堆内存当中开辟新的内存空间

    方法区内存:在类加载的时侯,class字节码代码片段被加载到该内存空间当中。
    栈内存(局都变量):方法代码片段执行的时候,会给该方法分配内存空间,在栈内存中压栈。
    堆内存:new的对象在堆内存中存储

    3、对象与引用

    new运算符在堆内存中开辟的内存空间称为对象。
    引用是一个变量,只不过这个变量中保存了另一个java对象的内存地址。
    java语言当中,程序员不能直接操作堆内存,只能通过引用去访问堆内存当中对象内部的实例变量。
    java中没有指针,不像c语言。

    4、访问实例变量的语法格式
    读取数据:引用.变量名; 修改数据:引用.变量名=值

部分源码

public class Student{
    // 属性(描述状态),在java程序中以“成员变量”的形式存在。
    // 学号
    // 一个对象一份。
    int no; // 这种成员变量又被称为“实例变量”。

    // 姓名
    String name;

    // 年龄
    int age;

    // 性别
    boolean sex;

    // 住址
    String addr;
}

public class OOTest01 {
    public static void main(String[] args) {
        int i = 10;
        Student s = new Student();
    }
}

内存图

对象的创建内存图

部分源码

public class OOTest01 {
    public static void main(String[] args) {
        int i = 10;
        Student s = new Student();
        s.no = 10;
        s.name ="jack";
        s.age = 20;
        s.sex= true;
        s.addr = "北京";
        Student stu=new Student();
    }
}

内存图

对象的创建与使用内存图

部分源码

public class OOTest02
{
    public static void main (String[] args){

        //u保存内存地址指向堆内存的User对象User u=new User();
        u.name = "jack" ;
        //"jack"是一个java对象,属于string对象
        u.addr = new Address ();
}

}
public class User{
    int no;
    //String是一种引用数据类型:代表字符串
    //name是一个实例变量,是一个引用
    String name;

    //Address是一种引用数据类型:代表家庭住址
    // addr是一个实例变量,addr是一个引用
    Address addr ;
}

public class Address {
    //city是一个引用:保存内存地址的一个变量,该变量保存内存地址指向了堆内存当中的对象。
    String city;
    String street;
    Stringzipcode;
}

内存图



部分源码

public class OOTest03{
    public static void main (String[] args){
        User u=new User();
        u.addr = new Address();
    
        Address a = new Address();
        u.addr = a ;
        System.out.println (u.addr.city); //null
        a.city = "天津";
        System.out.println (u.addr.city);//天津
    }
}

内存图



总结


    1. JVM ( Java虚拟机)主要包括三块内存空间,分别是:栈内存、堆内存、方法区内存。
    2、堆内存和方法区内存各有1个。一个线程一个栈内存。
    3、方法调用的时候,该方法所需要的内存空间在栈内存中分配,称为压栈。方法执行结束之后,该方法所属的内存空间释放,称为弹栈。
    4、栈中主要存储的是方法体当中的局部变量。
    5、方法的代码片段以及整个类的代码片段都被存储到方法区内存当中,在类加载的时候这些代码片段会载入。
    6、在程序执行过程中使用new运算符创建的java对象,存储在堆内存当中。对象内部有实例变量,所以实例变量存储在堆内存当中。
    7、变量分类:
        局部变量【方法体中声明】 
        成员变量【方法体外声明】
        实例变量【前边修饰符没有static]
        静交变量【前边修饰符中有static]
    8、静态变量存储在方法区内存当中。
    9、三块内存当中变化最频繁的是栈内存,最先有数据的是方法区内存,垃圾回收器主要针对的是堆内存。
    10、垃圾回收器【自动垃圾回收机制、GC机制】
        当堆内存当中的java对象成为垃圾数据,即没有更多的引用指向它的时候,会被垃圾回收器回收。
        回收对象无法被访问,因为访问对象只能通过引用的方式访问。

构造方法

要点

关于java类中的构造方法:

    1、构造方法又被称为构造的数/构造器/constructor
    2、构造方法语法结构:
    [修饰符列表]构造方法名(形式参数列表){
        构造方法体;
    }
    3、回顾普通方法的语法结构:
    [修饰符列表]返回值类型方法名(形式参数列表){
        方法体;
    }
    4、对于构造方法来说,没有返回值,"返回值类型"不需要指定,并且也不能写void,只要写上void,那么这个方法就成为普通方法了。
    5、构造方法的方法名必须和类名保持一致。
    6、构造方法的作用?
        构造方法存在的意义是,通过构造方法的调用,可以创建对象。
    7、构造方法应该怎么调用?
        ① 普通方法调用:方法修饰符中有static的时候:类名.方法名(实参列表)、方法修饰符列表中没有static的时候:引用.方法名(实参列表)
        ② new 构造方法名(实参列表)
    8、构造方法调用执行之后,有返回值吗?
        每一个构造方法实际上执行结束之后都有返回值,但是这个"return值;"这样的语句不需要写。
        构造方法结束的时候java程序自动返回值。并且返回值类型是构造方法所在类的类型。
        由于构造方法的返回值类型就是类本身,所以返回值类型不需要编写。
    9、注释和取消注释:ctrI +/ ,多行注释:ctrl +shift +/
    10、当一个类中没有定义任何构造方法的话,系统默认给该类提供一个无参数的构造方法,这个构造方法被称为缺省构造器
    11、当一个类显示的将构造方法定义出来了,那么系较则不再默认为这个类提供缺省构造器。
        建议开发中手动的为当前类提供无参数构造方法。因为无参数构造方法太常用了。
    12、构造方法支持重载机制。在一个类当中编写多个构造方法,这多个构造方法显然已经构成方法重载机制。

构造方法的作用:
    1、创建对象
    2、给实例变量赋值

    成员变量之实例变量,属于对象级别的变量,这种变量必须先有对象才能有实例变量。
    实例变量没有手动赋值的时候,系统默认赋值,那么这个系统默认赋值是在什么时候完成的呢? 是在类加载的时候吗?
    不是,因为类加载的时候只加载了代码片段,还没来得及创建对象。所以此时实例变量并没有初始化。
    实际上,实例变量的内存空间是在构造方法执行过程当中完或开辟的。完成初始化的。
    系统在默认赋值的时候,也是在构造方法执行过程当中完成的赋值。

实例变量默认值:
    byte , short,int, 1ong: 0
    float, double: 0.0
    bolean: false
    引用数据类型: null

get set函数和构造方法编译器都可以自动生成    

代码示例

pub1ic class ConstructorTest01{
    public static void main String[] args) {
    //创建User对象
    //调用User类的构造方法来完成对象的创建
    //以下程序创建了4个对象,只要构造函数调用就会创建对象,并且一定是在"堆内存"中开辟内存空间。User u1 = new User();
    User u2 = new User(10);
    User u3 = new User("zhangsan");
    User u4 = new User(10, " zhangsan");
}
public class User {
    //无参数构造方法
    public User(){
        System.out.println("User's Default Constructor Invoke!");
    }
    //有参数的构造方法
    public User(int i){
        System.out.println("带有int类型参数的构造器");
    }
    //有参数的构造方法
    public User(String nane){
        System.out.println("带有string类型的构造器");
    }
    //有参数的构造方法
    public User(int i ,String name){
        System.out.println("带有int,string类型的构造器");
    }
}

**********本笔记整理于动力节点JavaSE基础课程 **************


文章作者: 涂爽
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 涂爽 !
评论
  目录
Copyright © 2020 涂爽
  站点总字数: 145.4k 字 本站总访问量 人次, 访客数 人.
载入运行时间... 鄂ICP备20005056