对象与内存控制

本文探讨了Java内存管理的基本概念,包括内存分配与回收,深入解析实例变量与类变量的区别及初始化时机,同时讨论了父类构造器的调用顺序、final修饰符的使用规范等关键知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java内存管理分为两个方面即内存分配和内存回收。内存分配特指创建Java对象时jvm为该对象在堆内存中所分配的内存空间;内存回收指的是当该Java对象失去引用变成垃圾时,jvm的垃圾回收机制自动清理该对象,并回收该对象所占用的内存。不能因为jvm内置了垃圾回收机制而认为java不存在内存泄漏,因此不能肆无忌惮的创建对象。

一、实例变量和类变量

1.java程序变量可分为成员变量和局部变量。

局部变量分为三类:形参、方法内的局部变量、代码块内的局部变量。局部变量的作用时间很短,它们被存储在方法的栈内存中。

成员变量即在类内定义的变量,如果定义该成员变量时没有使用static修饰符则该成员变量叫做非静态变量即实例变量,即该成员变量属于类的实例;若使用了static修饰符则该成员变量叫做静态变量即类变量,即该成员变量属于类本身。

“static”在Java中作用就是从实例成员变为类成员,它只能修饰类里的成员,不能修饰外部类,不能修饰局部变量、局部内部类。

类变量初始化时机总是早于实例变量,当一个类初始化完成时,类变量也初始化完成了。java要求定义变量时必须采用合法的前向引用,即变量必须先定义再使用。

比如下面就是错误的,因为num2还没定义就先被引用了:


但是,如果定义num2为类变量,就不会出错,因为类变量总是比市里变量先初始化完成,如下就不会报错:


2.实例变量和类变量的属性

实例变量属于类的实例,类变量属于类本身。在同一个jvm中,每个类对应一个class对象,但是每一个class可以创建很多歌java对象,即在同一个jvm中,一个类的类变量只需要一块内存空间,但是 对于实例变量而言,该类每创建一个实例对象就需要为实例变量分配一块内存空间,程序里有几个实例变量就需要几块内存空间。

有一个person类

class person{
	String name;
	int age;
	static int eyenum;
	public void info(){
		System.out.println("我的名字是:"+name+",我的年龄是:"+age);
	}
}
(1)person.eyenum=2:因为eyenum是类变量,因此类初始化完成后就可以使用该类变量,对该累变量赋值完成后,内存分配如下:


(2)person p=new person();p.name="ltt";p.age=18;p.eyenum:定义一个person对象p,并用p去访问类变量eyenum,内存分配如下:


可以看到虽然p调用了eyenum,但是系统不再为eyenum分配内存空间,这更说明当类初始化后类变量也初始化结束,无论类创建多少的实例对象,系统都不再为类变量分配空间,但是没创建一个实例对象就会为实例变量创建一块内存。

(3)person q=new person();q.name="pl";q.age=18;q.eyenum=6:再添加一个person对象q,并对类变量eyenum重新赋值,这样实际修改的是person类的eyenum,此时内存分配如下:


3.实例变量初始化时机

程序可以在三个地方对实例变量进行初始化:(1)定义实例变量时指定初始值(2)非静态初始化块中对实例变量指定初始值(3)构造器中对实例变量指定初始值

需要注意的是前两种方法总是在第三种方法之前执行,而前两种方法的执行顺序和它们在程序里面书写的顺序一致。而且值得一说的是,经过编译后前两种方法的赋值语句都被提到构造器中,且总是位于构造器中的所有语句之前。例子如下:

class cat{
	String name;
	int age;
	public cat(String name,int age){
		System.out.println("执行构造器!");
		this.name=name;
		this.age=age;
	}
	{
		System.out.println("执行非静态代码块!");
		weights=2.0;
	}
	double weights=2.3;//定义时指定初始值
	public String getString(){
		return "name="+name+",age="+age+",weights="+weights;
		
	}
}
public class tt{
public static void main(String[] args){
	cat cat=new cat("aa", 2);
	System.out.println(cat.getString());
}
}
结果:


最后执行构造器,按照程序里的顺序先执行静态代码块里的weigthts=2,再执行定义时指定的weights=2.3,所以最后的结果也是weights=2.3

4.类变量初始化时机

每个jvm队一个Java类只初始化一次,因此系统只为类变量分配一次内存空间,执行一次初始化,程序可以在两个地方对类变量进行初始化(1)定义变量时指定初始值(2)静态初始化块中队类变量指定初始值。同样,这两种方法的执行顺序和它们在源程序中的排列顺序一致。

public class tt{
	static int count=2;
	static{
		System.out.println("静态初始化块!");
		name="java";
	}
	static String name="c++";
public static void main(String[] args){
	System.out.println(tt.count);
	System.out.print(tt.name);
}
}
结果如下:


对类初始化(1)系统为类变量分配内存空间,此时它们的初始值为0或者null(2)按照初始化代码块的排列顺序对类变量进行初始化。

二、父类构造器

1.程序总是会依次先调用每个父类的非静态初始化块、父类构造器,最后才调用本类的非静态初始化块、构造器。在调用父类的构造器时,既可以用super()显示调用,也可以隐式调用。super()调用用于显示的调用父类的构造器,this()调用用于显示调用本类中另一个重载的构造器,系统会根据super和this里面调用传入的实参列表确定调用哪一个构造器,super和this只能在构造器中使用,而且作为构造器的第一行。若没有super或this,则隐式的调用父类无参构造器。当this出现在构造器中,this代表正在初始化的java对象。

2.访问子类变量的实例变量

子类的方法可以访问父类的实例变量,但是父类方法不能访问子类的实例变量,但是在一些情况下,会出现父类访问子类的例子,如下:

class Base{
	private int i=2;
	public Base(){
		this.display();
	}
	public void display(){
		System.out.println(i);
	}
}
class sub extends Base{
	private int i=22;
	public sub(){
		i=222;
	}
	public void display(){
		System.out.println(i);
	}
}
public class tt{
public static void main(String[] args){
	new sub();
}
}
结果是0。

new sub()时会去执行sub的构造器,构造器会隐式的去执行父类的无参数构造器,里面只有this.display(),这个this指的是正在初始化的java对象即sub,则this调用的display方法即sub类的display,此时的i并没有 被初始化,因此为0,所以结果为0。

3.调用被子类重写的方法

在访问权限允许的情况下,子类可以调用父类的方法,但是父类不能调用子类的方法,但是当子类重写了父类的方法后,也会出现父类调用子类方法的例子。

class animal{
	private String desc;
	public animal(){
		this.desc=getDesc();
	}
	public String getDesc(){
		return "animal";
	}
	public String toString(){
		return desc;
	}
}
class wolf extends animal{
	private String name;
	private double weights;
	public wolf(String name,double weights){
		this.name=name;
		this.weights=weights;
	}
	public String getDesc(){
		return "name:"+name+" ,weights="+weights;
	}
}
public class tt{
public static void main(String[] args){
	System.out.println(new wolf("lkk", 28));
}
}
结果:name:null ,weights=0.0。

new wolf()会调用wolf的构造器,构造器隐式的调用父类无参构造器,父类里面的getDesc表面上是父类的,实际是子类的getDesc,此时name和wights还没有被赋值,因此为null和0.

因此为了避免这种结果,应该避免在父类构造器中调用被子类重写过的方法。

三、父子实例的内存控制

class Base{
	int count=2;
	public void display(){
		System.out.println(this.count);
	}
}
class sub extends Base{
	int count=20;
	public void display(){
		System.out.println(this.count);
	}
}
public class tt{
public static void main(String[] args){
	Base a=new Base();
	System.out.println("Base a=new Base():"+a.count);
	System.out.print("Base a=new Base():");
	a.display();
	sub b=new sub();
	System.out.println("sub b=new sub():"+b.count);
	System.out.print("sub b=new sub():");
	b.display();
    Base c=new sub();
    System.out.println("Base c=new sub():"+c.count);
    System.out.print("Base c=new sub():");
	c.display();
	Base d=b;
	System.out.println("Base d=b:"+d.count);
	System.out.print("Base d=b:");
	d.display();
}
}
结果如下:


对于上面的结果,需要注意的如下:

(1)首先,子类父类中定义的同名的实例变量,子类中会保留父类的实例变量,即在子类对象中,不仅保存了子类的实例变量而且保存了父类的实例变量,但是如果子类重写了父类方法,就相当于子类完全覆盖了父类里的同名方法,即系统不能把父类的方法转移到子类中去。上面子类sub的内存如下:


可以看到sub中不仅保存了自己的count,也保存了父类Base的count.

(2)当变量的编译类型和运行类型不一致时,通过该变量访问它所引用的对象的实例变量时,实例变量的值由声明该变量的类型决定,但是通过该变量调用它引用的对象的实例方法时,由它实际所引用的类型对象来决定。

所以上面的例子Base c=new sub():声明了一个Base变量c,但是把sub对象赋值给c,调用c.count就和声明类型Base有关,因此输出的是Base里面的count2,而c.dispaly()和实际类型sub有关,因此调用的是sub里面的display(),,即输出20。

四、final修饰符

1.final修饰过的变量被赋值后,不能再重新赋值;被final修饰过的方法不能被重写;被final修饰过的类不能派生子类。

2.final修饰过的变量在编译时就确定了值,则这个final变量不再是变量而是一个确定的,因此系统会把它当做宏变量来处理。

对于final修饰的实例变量来说只有在定义该变量时指定初始值才会有宏变量的效果,在非静态初始化块和构造器中不会有宏变量的效果,类变量也只有在定义时指定初始值才会有宏变量的效果。

3.如果程序需要再内部类中使用局部变量,则这个局部变量必须是final修饰的。

内容概要:《2024年中国城市低空经济发展指数报告》由36氪研究院发布,指出低空经济作为新质生产力的代表,已成为中国经济新的增长点。报告从发展环境、资金投入、创新能力、基础支撑和发展成效五个维度构建了综合指数评价体系,评估了全国重点城市的低空经济发展状况。北京和深圳在总指数中名列前茅,分别以91.26和84.53的得分领先,展现出强大的资金投入、创新能力和基础支撑。低空经济主要涉及无人机、eVTOL(电动垂直起降飞行器)和直升机等产品,广泛应用于农业、物流、交通、应急救援等领域。政策支持、市场需求和技术进步共同推动了低空经济的快速发展,预计到2026年市场规模将突破万亿元。 适用人群:对低空经济发展感兴趣的政策制定者、投资者、企业和研究人员。 使用场景及目标:①了解低空经济的定义、分类和发展驱动力;②掌握低空经济的主要应用场景和市场规模预测;③评估各城市在低空经济发展中的表现和潜力;④为政策制定、投资决策和企业发展提供参考依据。 其他说明:报告强调了政策监管、产业生态建设和区域融合错位的重要性,提出了加强法律法规建设、人才储备和基础设施建设等建议。低空经济正加速向网络化、智能化、规模化和集聚化方向发展,各地应找准自身比较优势,实现差异化发展。
数据集一个高质量的医学图像数据集,专门用于脑肿瘤的检测和分类研究以下是关于这个数据集的详细介绍:该数据集包含5249张脑部MRI图像,分为训练集和验证集。每张图像都标注了边界框(Bounding Boxes),并按照脑肿瘤的类型分为四个类别:胶质瘤(Glioma)、脑膜瘤(Meningioma)、无肿瘤(No Tumor)和垂体瘤(Pituitary)。这些图像涵盖了不同的MRI扫描角度,包括矢状面、轴面和冠状面,能够全面覆盖脑部解剖结构,为模型训练提供了丰富多样的数据基础。高质量标注:边界框是通过LabelImg工具手动标注的,标注过程严谨,确保了标注的准确性和可靠性。多角度覆盖:图像从不同的MRI扫描角度拍摄,包括矢状面、轴面和冠状面,能够全面覆盖脑部解剖结构。数据清洗筛选:数据集在创建过程中经过了彻底的清洗,去除了噪声、错误标注和质量不佳的图像,保证了数据的高质量。该数据集非常适合用于训练和验证深度学习模型,以实现脑肿瘤的检测和分类。它为开发医学图像处理中的计算机视觉应用提供了坚实的基础,能够帮助研究人员和开发人员构建更准确、更可靠的脑肿瘤诊断系统。这个数据集为脑肿瘤检测和分类的研究提供了宝贵的资源,能够帮助研究人员开发出更准确、更高效的诊断工具,从而为脑肿瘤患者的早期诊断和治疗规划提供支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值