java继承的内存分析_Java学习笔记-内存分析

本文详细介绍了Java内存管理,包括方法调用的内存图像、类创建时的内存结构、实例变量与静态变量的存储位置、垃圾回收机制。此外,还讨论了关键字`this`和`super`的内存表现,以及数组和String类的内存特点。通过对实例、继承和字符串常量池的分析,深入理解Java内存操作。
摘要由CSDN通过智能技术生成

一般方法调用的内存图像

例子

//只要是类名就一定是标识符

//方法调用时,参数传递是‘值传递’

public class Test{

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 a,int b){

int result = a + b;

int num = 3;

int retValue = divide(result, num);

return retValue

}

public static int divide(int x,int y){

int z = x / y;

return z;

}

}

f31baf5fcd7a70dd2ccb83022678ffe3.png

类创建时的内存图像

概述

1.类体 = 属性 + 方法

属性描述的是 : 状态

方法描述的是 : 行为动作

2.各种变量的含义

由于变量定义在类体当中,方法体之外,这种变量称为成员变量

类似于学生序号这样的成员变量,必须通过创建对象去访问,所以这种成员变量有称为实例变量

对象又称为实例,实例变量又被称为对象变量【对象级别的变量 】

实例变量存在在java对象的内部,创建一个对象就有一份,100个就有100份

想要访问实例变量,必须要有对象,不能通过"类名"的方式访问

3.java运算符 -new

实例化对象的语法:new 类名()

new运算符的作用是创建对象,在JVM堆内存中开辟新的内存空间

方法区内存:在类加载的时候,class字节码代码片段被加载到该内存空间中

栈内存:方法代码片段执行的时候,会给该方法分配内存空间,在栈内存中压栈

堆内存:new的对象在堆内存中存储

例子

public class Students{

int age;

boolean sex;

//姓名

//Sting是一种引用数据类型,代表字符串

//name是一个实例变量

//name是一个引用

String name;

//家庭住址

//Address是一种引用数据类型,代表家庭住址

//addr是一个实例变量

//addr是一个引用

Address addr;

}

public class Address{

String city;

Stirng Street;

}

public static void Test(Stirng[] atgs){

Students s1 = new Students("Zhang San",20,true);

}

da7b349c54300276594cf99074f76941.png

java语言中的三大变量

JVM主要包括三块内存空间,分别是:栈内存、堆内存、方法区内存

堆内存和方法区内存各有1个,而栈内存是一个线程对应一个栈内存

只有局部变量不存在线程安全问题,实例变量和静态变量都存在线程安全问题,原因如1.所述,只有局部变量不会被多个线程共享。

方法的代码片段以及整个类的代码片段都被存储到方法区内存中,在在类加载的是时候这些代码片段会载入

方法调用的时候,该方法所需要的内存空间在栈内存中分配。在该方法执行结束之后,该方法所属的内存空间释放

栈中主要存储的是方法体当中的局部变量

在程序执行过程中使用new运算符创建的java对象,存储在堆内存当中,对象内部有实例变量,所以实例变量存储在堆内存中

变量分类:

局部变量 【方法体中声明】

成员变量 【方法体外声明】

实例变量 【不加static】

静态变量 【加static】

静态变量存储在方法区内存中

三块内存中变化最频繁的是栈内存,最先有数据的是方法区内存,垃圾回收器主要针对的是堆内存

垃圾回收器【自动垃圾回收机制,GC机制】什么时候会考虑将某个java对象的内存回收呢?

当堆内存当中的java对象成为垃圾数据的时候,会被垃圾回收器回收

什么时候堆内存中的java对象会变成垃圾呢?

没有更多的引用指向它的时候

这个对象无法被访问,因为访问对象只能通过引用的方式访问

例子

public class Chinese{

String idCard;

String name;

Static String country = "中国";

public Chinese(String idCard, String name){

this.idCard = idCard;

this.name = name;

}

}

public static void main(String[] args){

Chinese c1 = new Chinese("1243434353","张三");

Chinese c2 = new Chinese("7423983949","李四");

}

ec7f7fced476d1dcece5f2ea187928ed.png

关键字 -this的内存图像

this是一个变量,是一个引用。this保存当前对象的内存地址,指向自身。

this存储在当前对象的堆内存中

this可以使用在实例方法中,也可以使用在构造方法中,不能使用在静态方法中

this()这种语法只能出现在构造方法的第一行,表示当前构造方法调用本类中其他的构造方法

目的:如果不存在这个语法,那么就需要对每一属性进行赋初值/默认值,而有了这个语法就可以实现代码复用

28123844003b7bc5289a9d44085cba4b.png

关键字 -super 子类继承时的内存图像

例子:

public class SuperTest03{

public static void main(String[] args){

CreditAccount ca1 = new CreditAccount();

CreditAccount ca2 = new CreditAccount("1111", 1000.0, 0.999);

}

}

class Account{

private String actno;

private double balance;

public Account(){

//super();

//this actno = null;

//this.balance = 0.0;

}

public Account(String actno, double balance){

this.actno = actno;

this.balance = balance;

}

}

class CreditAccount extends Account{

private double credit;

public CreditAccount(){}

public CreditAccount(String actno, double balance, double credit){

//super()存在的意义在构造方法中:不是代码复用,而是可以间接的在子类中方法父类的私有属性

//但是父类的私有属性又被子类继承,是存在于子类对象中的,详见内存图描述

super(actno, balance);

this.credit = credit;

}

}

b5a3b1ff103f3c6cec40782c6d28f7bc.png

补充:子类对父类的继承

子类是可以继承父类的所有方法和属性的,包括私有方法和私有属性。

当一个子类对象被创建的时候,首先会在内存中创建一个父类对象,然后在父类对象的外部放上子类独有的属性,这个两者结合起来形成了一个子类的对象。

子类是拥有父类的私有属性和方法,但无法直接使用。

1.两种父类的私有属性的访问方法:

在子类构造方法可以通过super关键字访问;super(),括号中调用父类的相同参数的构造函数

父类中有get和set方法,子类可以通过调用this.方法名访问父类的私有属性。

2.间接访问父类的私有方法:

当子类继承父类时,子类会继承父类所有的方法(不管这个方法的访问权限是private、protected、public的);

只不过在子类自己定义的方法中无法直接去访问继承而来的private方法罢了,但是可以通过继承而来的public及protected方法来间接访问继承而来的private方法。

即可以简单的将子类的方法分为两个版本,一是继承自父类的方法(简称继承版),二是自己定义的方法(简称自定版义版);

但是不管是继承版的还是自定义版的方法,它们都是属于这个子类的。

所以当子类实例化一个对象去调用这些方法时,这些方法中的this变量肯定指向这个对象本身(只不过访问继承版的private方法时,需要绕一点弯路)

例子:

class Father {

public Father() {

System.out.println("Father");

}

private void hello(Son son) {

System.out.println("hello");

System.out.println(this==son);//输出true,说明this和son引用的是同一个对象

}

public void sayHello(Son son) {

System.out.println(this instanceof Son);

this.hello(son);

}

}

public class Son extends Father {

public static void main(String[] args) {

Son s = new Son();

s.sayHello(s);

}

}

f309512d0abc4df68720a247eb9d9d62.png

数组的内存图像

1.Java语言中的数组是一种引用数据类型

2.数组当中可以存储基本数据类型的数据,也可以存储引用数据类型的数据

3.数组因为是引用数据类型,所以数组对象是在堆内存中的

4c36fc568c4c2f0091bf4538e22e33f3.png

String类相关的内存图像

1.String表示字符串类型,属于引用数据类型,不属于基本数据类型

2.java中规定,双引号括起来的字符串,是不可变的

3.在java中随便使用双引号括起来的都是String对象。例如:"abc", "def"

4.在JDK当中双引号括起来的字符串,都是直接存储在方法区的"字符串常量池"当中的

因为实际使用中,字符串使用的太频繁。为了执行效率,所以放到字符串常量池

5.在学习完String类后,发现之前的String类内存图像的画法是有问题的

示例1 - 静态和动态创建字符串的内存图像

1.静态创建:

双引号括起来的都在字符串常量池中有一份

2.动态创建:

不仅在字符串常量池有一份,且在new对象的时候一定在堆内存当中开辟空间

public class StringTest01 {

public static void main(String[] args) {

//这两行代码表示底层创建了3个字符串对象,都在字符串常量池中

String s1 = "abcdef";

String s2 = "abcdef" + "xy";

String s3 = new String("xy");

}

}

在下图可以看出静态和动态创建的区别:

c5720d943564338a8d0ea4e4ac65d49e.png

示例2 -类的属性是String类

public class StringTest02 {

public static void main(String[] args) {

User u = new User("张三",110);

}

}

class User{

String name;

int id;

public User() {

}

public User(String name, int id) {

this.name = name;

this.id = id;

}

}

9d18ceabc31dec9f7111eda105386417.png

为什么判断字符串相等需要使用equals方法?

通过String类内存图的学习,可以得出答案,如下示例程序:

public class StringTest03 {

public static void main(String[] args) {

//"hello"存储在字符串常量池中,所以不会再新创建一份了

String s1 = "hello";

String s2 = "hello";

//内存地址相同,True

System.out.println(s1 == s2);

String s3 = new String("xy");

String s4 = new String("xy");

//堆内存中对象的内存地址不同,false

System.out.println(s3 == s4);

//通过以上例子,说明字符串的比较使用equals方法才能万无一失

//System.out.println(s1.equals("testString"));

//这种写法不保险,可能出现空指针异常,推荐以下写法:

System.out.println("testString".equals(s1));

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值