Java 对象及其内存控制
作者:禅楼望月(http://www.cnblogs.com/yaoyinglong)
更新:其实这里有好多的变戏法,只要你理解了他们在JVM的中的实现机制,就豁然开朗了。有时间我会把这些变戏法的东西说明的。
Java 向程序员许下了美好的承诺:无需关心内存的回收,Java提供了优秀的垃圾回收机制来回收已经分配的内存。
所以初学者往往会肆无忌惮的挥霍Java内存,从而导致Java程序的运行效率下降,主要坏处为:
- 不断分配内存使得系统中可用内存减少,从而降低程序运行性能;
- (更重要的)大量已分配内存的回收使得垃圾回收的负担加重,降低程序的运行性能。
1 前向应用
这说明Java中定义实例成员变量时,必须采用合法的前向引用。同样两个类成员变量也必须采用合法的前向引用:
但是,如果一个是实例成员变量,一个是类成员变量,则实例成员变量总是可以引用类成员变量:
这是因为:类成员变量初始化时机总是在实例成员变量初始化时机之前,确切的说是在类加载时进行的。
2、静态成员可实例成员
使用static修饰的静态变量属于类本身,而实例变量数据类的实例。在同一JVM中,每个类只对应一个Class对象,因此同一JVM内的一个类的类变量只需一块内存空间,但每个类可以创建多个Java对象,因此JVM必须为每个Java对象的实例变量分配一块内存空间。
Java允许通过类来访问类成员变量,也允许类实例访问类成员变量,(Java这样设计是不合理的)
但是java设计者,却在类实例访问类成员变量时,底层依然转换为类来访问类成员变量。怎么证明呢?
通过反编译来看看:
JVM在底层使用someTh对象所对应的引用类型来调用静态成员,这就给程序员造成了一定的错觉,以为调用的是自己对象的东西,但是改变静态成员的值,在其他的对象的中会体现出来,这个很危险:
在一个类实例中修改了类成员变量的值,在另一个类实例中却体现出来了。
3 实例变量的初始化时机
从语法角度看,我们可以在如下3个地方对实例变量执行初始化:
①定义实例变量是指定初始值
②非静态初始化块中对实例变量指定初始值
③构造器中对实例变量指定初始值。
其中①和②比③更早执行,①和②那个更早执行,就看那个在代码中出现的更早。如:
由此可见类实例变量只能放在构造器中初始化,但是作为程序员编程时,可以放在定义处,也可以放在非静态块中,但是结果都是一样的,JVM会把它们抽取出来放在构造函数中。
4 类变量初始化时机
从语法角度看,可以在如下两个地方对类变量初始化:
①定义类变量时初始化;
②静态初始化块中对类变量指定初始值。
这两种方式的执行顺序和它们在源代码中出现的顺序相同:
由此可见,类变量只能在静态块中被初始化,但是作为程序员编程来说,可以放在定义处也可以放在静态快中,结果都是一样的,JVM会把它们收取出来都放进静态快中。
5 父类构造器
看如下代码:
这里便引发了一个疑问:在这里Sub类还没有被创建(因为调用display的时候父类的构造函数还没有走完,怎么会走子类的构造函数),怎么能调用它的方法呢?
诶!难道类实例不是由构造器创建的吗?
很多书籍中都是这样说的:类实例是由构造器创建。
其实,这句话是完全错误的。实际的情况是构造器只是负责对Java对象实例变量执行初始化(即赋初始值),在执行构造器代码之前,该对象所占的内存已经被分配下来了。这些内存里的值都是各个类型的默认值。
所以上面代码在执行new Sub();的时候系统已经为Sub对象分配了内存空间(两块内存空间,一块用于存放Sub的i另一块用于存放Base的i(这一块内存,子类和父类共用,改变任何一个另一个会跟着动),原因是子类不能完全覆盖父类的成员变量)
注意:
对象是由new关键字创建的,在执行new……的时候,一个Java对象已经建成了,只是它的变量还没有初始化,构造函数的功能就是对这些变量进行初始化。没有运行完构造函数Java对象的方法是可以被调用的,因为它和一般Java对象没有任何的区别。
再来看一段代码:
是否会感觉到this指代有点混乱呢?
但是从打印出来的结果来看,this确实指代的是Sub,但是我们也知道,当this在构造器中this指的是正在被初始化Java对象。怎么理解呢?从源代码看,此时this位于Base构造器中,但是这些代码实际放在Sub()构造器内执行,是Sub()构造器隐式调用了Base()构造器的代码。由此可见,this指的是Sub而不是Base。现在问题又来了,既然this指的是Sub,那么,为什么System.out.println("I come from "+this.getClass()+" -->"+this.i);执行结果却为2?这是因为,虽然,this实际指向的是Sub对象,但是当在Base构造器中时,它的编译类型为Base。所以会输出2.
因此我们可以得出如下结论:
当变量(a)编译时类型和运行是类型不同时,通过该变量(a)访问它引用的对象的实例变量时,该实例变量的值是由声明该变量(a)的类型决定。但当通过该变量调用它引用的对象的实例方法时,该方法行为将由该变量(a)实际所引用的对象来决定。
6 父子实例的内存控制
由上图可知:
1、变量d2b和d实际指向同一个对象,但是访问他们的实例变量时却输出不同的值,这表明d2b和d变量所指向的java对象中包含了两块内存,更别存放着值为2的count实例变量和值为20的count实例变量。
2、不管d、db、d2b,只要它们指向一个Sub对象,不管声明它们使用什么类型,当通过这些变量来调用时,方法的行为总是表现出它们实际类型的行为。但如果通过这些变量来访问它们所指对象的实例变量,这些实例变量的值总是表现出声明这些变量所用的类型的行为。由此可见Java继承在处理成员变量和方法时,是有区别的。
但是,还是可以通过super来调用父类中被覆盖的方法。
我们再来看一下这段代码:
//父类 public class Base { private int x=10; public int getX() { return x; } public void setX(int x) { this.x = x; } }
//子类 public class Sub extends Base { public Sub() { this.setX(20); } }
//测试 public static void main(String[] args) { Base b=new Base(); System.out.println("我是父类:"+b.getX()); //-->10 Base base=new Sub(); System.out.println("我是父类:"+base.getX()); //-->20 Sub s=new Sub(); System.out.println("我是子类:"+s.getX()); //-->20 System.out.println("我是父类:"+base.getX()); //-->20 }
用javap工具查看:
由此可见子类继承了父类的实例变量,内存中值为父类中的变量申请了空间,并没有为子类中该变量开辟内存空间。有人可能说你这里的实例变量x是private,其实即是public也是一样的,不信的话可以试试。
那么,我们在子类中调用setX方法其实,设置的是父类中的实例变量x。因为这个方法是从父类继承过来的。由此也可以得出父类中一般不要设置静态全局变量,这样会有线程安全的问题。
所以在子类中使用super的意思是,使用自己对象里面保存的从父类继承下来的那个方法。
由此可见,super本身并没有引用任何对象,它只能算作一个标记。它的作用仅限于在子类中(不是子类的对象)调用在父类中定义的,被隐藏了的实例变量,或者在子类中定义的,被覆盖的方法。
注意:虽然说这是父类中的方法和变量。其实和父类没有一点关系了。只是在调用上有点区别,其他的和类自己的方法没什么区别。
7 父子类的类变量
记住:Java允许通过实例对象来调用类的静态变量
其他的和实例变量一样。
8 final
final修饰的变量
final修饰的变量必须显示的指定初始值(普通变量系统会为其设置默认值),而且只能在以下3个地方制定初始值:
对于一个final变量而言,不管它是类变量、实例变量还是局部变量,只要该变量被final修饰,并且被赋予的初始值(必须的),那么该在类编译的时候就被确定了,那么,这个final变量就不再是变量了,而是相当于一个直接量。
内部类中的局部变量
如果程序需要在内部类中使用局部变量,那么这个局部变量必须由final修饰。
但是为什么内部类中要访问的局部变量都必须使用final修饰呢?
原因是:对于普通的局部变量,它的作用于就停留在该方法内,该方法结束后该局部变量就消失了;但是内部类则可能产生隐式的“闭包”,闭包将使得局部变量脱离了它所在的方法继续存在。
Java 对象及其内存控制的更多相关文章
- (二)Java对象与内存控制
一.java的类变量和实例变量: java中的变量可分成两种:成员变量和局部变量. 1.局部变量包括以下几类: 方法内的局部变量:作用域为方法体. 代码块内的局部变量:作用域为代码块 形参:方法内的形 ...
- JAVA对象与内存控制
1.1 实例变量和类变量 成员变量和局部变量: 局部变量分为三大类: 1)形参:在方法签名中定义的局部变量,由方法调用者为其赋值,随方法的结束而消亡. 2)方法内的局部变量:在方法内定义的局部变量,随 ...
- Java对象的内存模型(一)
前言 新人一枚,刚刚入门编程不久,各方面都在学习当中,博文有什么错误的地方,希望我们可以多多交流! 最近,在开发App后台过程中,需要将项目部署到云服务器上.而云服务器的内存大小却只有1G.要如何做到 ...
- Java对象的内存布局
对象的内存布局 平时用java编写程序,你了解java对象的内存布局么? 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域: 对象头 实例数据 对齐填充 对象头 对象头包括两部分信息: ...
- JVM总结-java对象的内存布局
在 Java 程序中,我们拥有多种新建对象的方式.除了最为常见的 new 语句之外,我们还可以通过反射机制.Object.clone 方法.反序列化以及 Unsafe.allocateInstance ...
- java对象在内存的大小
前言 一直以来,对java对象大小的概念停留在基础数据类型,比如byte占1字节,int占4字节,long占8字节等,但是一个对象包含的内存空间肯定不只有这些. 假设有类A和B,当new A()或者n ...
- Java对象的内存(一)
前言 新人一枚,刚刚入门编程不久,各方面都在学习当中,博文有什么错误的地方,希望我们可以多多交流! 最近,在开发App后台过程中,需要将项目部署到云服务器上.而云服务器的内存大小却只有1G.要如何做到 ...
- java对象在内存中的分配
java对象在内存中的分配 http://blog.csdn.net/qq_30753945/article/details/54974899
- 10 Java 对象的内存布局
Java 创建对象的方式 1:new 语句和反射机制创建.该方式会调用类的构造器,同时满足诸多约束.如果一个类没有构造器的话,Java 编译器会自动添加一个无参数的构造器.子类的构造器需要调用父类的构 ...
随机推荐
- mysql like 变量
Mysql: select * from 表名 where 字段 like concat('%',变量,'%');
- 线程池的类型以及执行线程submit()和execute()的区别
就跟题目说的一样,本篇博客,本宝宝主要介绍两个方面的内容,其一:线程池的类型及其应用场景:其二:submit和execute的区别.那么需要再次重申的是,对于概念性的东西,我一般都是从网上挑选截取,再 ...
- 浅谈Linux下mv和cp命令的区别
1.功能上的区别 mv:用户可以使用该命令为文件或目录重命名或将文件由一个目录移入另一个目录中. cp: 该命令的功能是将给出的文件或目录拷贝到另一文件或目录中. 2.从inode角度来区分 mv:会 ...
- web前端总结面试问题(理论)
一个页面从输入url到页面显示加载完成,这个过程发生了什么? 1.浏览器根据请求的URL交给DNS域名解析,找到真实的IP,向服务器发起请求. 2.服务器交给后台处理完成后返回数据,浏览器接收文件(h ...
- Lavavel5.5源代码 - 并发数控制
app('redis')->connection('default')->funnel('key000') // 每个资源最大锁定10秒自动过期,只有60个资源(并发),在3秒内获取不到锁 ...
- Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO
你有碰上过这样的提示吗? Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in t ...
- 第一个python代码
# -*- coding:utf-8 -*- user = raw_input("请输入用户名") passwd = raw_input("请输入密码") if ...
- HyperLedger Fabric 1.4 架构(6.2)
6.2.1 架构演进 Fabric架构经历了0.6版本到1.0版本的演进,架构上进行了重大改进,从0.6版本的结构简单演进到可扩展.多通道的设计,在架构上有了质的飞跃:从1.0版本以后,架 ...
- WPF把CheckBox的文字放到左边,开关在右边
原文:WPF把CheckBox的文字放到左边,开关在右边 效果 实现 这篇文章给了一个不错的参考方案. http://www.codeproject.com/Articles/19141/WPF-Ch ...
- C语言RL78 serial bootloader和C#语言bootloader PC端串口通信程序
了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 前段时间完成的hype ...