Java学习笔记二:内存分析、this、static和参数传值机

2020-01-14  本文已影响0人  开发者连小超

一、面向对象的内存分析

1.内存分配
  1. 栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)。
  2. JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)。
  3. 栈属于线程私有,不能实现线程间的共享。
  4. 栈的存储特性是“先进后出”,类似弹夹。
  5. 栈是由系统自动分配,速度快,栈是一个连续的内存空间。
  1. 堆用于存储创建好的对象和数组(数组也是对象)。
  2. JVM只有一个堆,被所有线程共享。
  3. 堆是一个不连续的内存空间,分配灵活,速度慢。
  1. JVM只有一个方法区,被所有线程共享。
  2. 方法区实际也是堆,只是用于存储类、常量相关的信息。
  3. 用来存放程序中永远是不变或唯一的内容。(类信息【Class对象】、静态变量、字符串常量等)
内存分配.png

以下代码为例,绘制内存执行图

public class SxtStu {
    int  id;
    String  sname;
    int  age;
    Computer  comp;

    void  study(){
        System.out.println("我在认真学习!!,使用电脑:"+comp.brand);
    }
    void  play(){
        System.out.println("我在玩游戏!王者农药!");
    }

    public static void main(String[] args) {
        SxtStu  stu = new SxtStu();
        stu.id=1001;
        stu.sname= "高淇";
        stu.age = 18;

        Computer  c1 = new Computer();
        c1.brand = "联想";
        stu.comp = c1;

        stu.play();
        stu.study();
    }
}

class  Computer {
    String  brand;
}
图片.png

二、构造方法

1.构造方法解释

构造器也叫构造方法(constructor),用于对象的初始化。构造器是一个创建对象时被自动调用的特殊方法,目的是对象的初始化。构造器的名称应与类的名称一致。Java通过new关键字来调用构造器,从而返回该类的实例,是一种特殊的方法。

2.构造方法重载
public class User {
    int id; // id
    String name; // 账户名
    String pwd; // 密码
    public User() {
 
    }
    public User(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }
    public static void main(String[] args) {
        User u1 = new User();
        User u2 = new User(101, "高小七");
        User u3 = new User(100, "高淇", "123456");     
    }
}

三、this关键字

this的本质就是“创建好的对象的地址”,由于在构造方法调用前,对象已经创建。因此,在构造方法中也可以使用this代表“当前对象” 。

this最常的用法:

  1. 在程序中产生二义性之处,应使用this来指明当前对象;普通方法中,this总是指向调用该方法的对象。构造方法中,this总是指向正要初始化的对象。
  2. 使用this关键字调用重载的构造方法,避免相同的初始化代码。但只能在构造方法中用,并且必须位于构造方法的第一句。
  3. this不能用于static方法中。

四、static 关键字

在类中,用static声明的成员变量为静态成员变量,也称为类变量。 类变量的生命周期和类相同,在整个应用程序执行期间都有效。它有如下特点:

  1. 为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化。
  2. 对于该类的所有对象来说,static成员变量只有一份,被该类的所有对象共享。
  3. 一般用“类名.类属性/方法”来调用。(也可以通过对象引用或类名(不需要实例化)访问静态成员。)
  4. 在static方法中不可直接访问非static的成员。

核心要点:

代码示例:

/**
 * 测试static关键字的用法
 */
public class User2 {
    int id; // id
    String name; // 账户名
    String pwd; // 密码
     
    static String company = "北京尚学堂"; // 公司名称
     
    public User2(int id, String name) {
        this.id = id;
        this.name = name;
    }
     
    public void login() {
        printCompany();
        System.out.println(company); 
        System.out.println("登录:" + name);
    }
     
    public static void printCompany() {
        //login();//调用非静态成员,编译就会报错
        //System.out.println(this.id); //调用非静态成员变量,编译就会报错
        System.out.println(company);
    }
     
    public static void main(String[] args) {
        User2 u = new User2(101, "高小七");
        User2.printCompany();
        User2.company = "北京阿里爷爷";
        User2.printCompany();
    }
}
代码示例解析.png

如上图所示,printCompany()方法调用login()方法就会报错,因为方法区里找不到login()方法;而login()可调用printCompany()方法。同理printCompany()方法里也无法调用id,name成员变量。

四、静态初始化块

构造方法用于对象的初始化,静态初始化块用于类的初始化操作。在静态初始化块中不能直接访问非static成员。

静态初始化块执行顺序:

  1. 上溯到Object类,先执行Object的静态初始化块,再向下执行子类的静态初始化块,直到我们的类的静态初始化块为止。而且由于类先加载,再执行构造方法初始化对象,所以静态初始化先于构造方法。
  2. 构造方法上溯执行顺序和上面顺序一样。
public class User {
    int id;        //id
    String name;   //账户名
    String pwd;   //密码
    static String company; //公司名称
    static {
        System.out.println("执行类的初始化工作");
        company = "北京尚学堂";
        printCompany();
    }
    public static void printCompany(){
        System.out.println(company);
    }
    public static void main(String[] args) {
        User  u3 = new User();
    }
}

五、参数传值机制

Java中,方法中所有参数都是“值传递”,也就是“传递的是值的副本”。 也就是说,我们得到的是“原参数的复印件,而不是原件”。因此,复印件改变不会影响原件。

上一篇下一篇

猜你喜欢

热点阅读