成员应用
目录
在程序执行过程中使用new运算符的java对象,储存在堆内存中。对象内部有实例变量,所以实例变量储存在堆内存当中
变量分类:
- 局部变量,方法体中声明
- 成员变量,方法体外类内声明
- 实例变量,没有static修饰符,各个不同对象相互隔离
- 静态变量,有static修饰符,这个类的所有对象共享
- 静态变量储存在方法区内存中
- 三块主要内存中变化最频繁的是栈内存,最先有数据的是方法区内存,垃圾回收期主要针对的是堆内存
自动垃圾回收机制GC什么时候会将java对象的内存回收
- 当堆内存的对象成为垃圾数据的时候会被GC回收
- 什么时候堆内存对象会变成垃圾?
引用计数法:为每个对象创建一个引用计数器,有对象引用时计数器 +1 ,引用被释放时计数器 -1 ,当计数器为 0 时就可以被回收。它有一个缺点就是不能解决循环引用的问题可达性算法(引用链法):从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一 个对象到GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
总结
- 栈内存存储基本类型的变量和对象的引用变量
- 堆内存用于存放有new创建的对象和数组。每new一个对象就在堆内存中开辟一个新的储存空间存储此实例对象
- Person p=new Person()执行new命令时程序执行两步:a:在堆内存中开辟一段空间,存储new出来的新对象;b:在栈内存中添加一个变量p,p中存放的是该对象在堆内存中开始存放的物理地址
- p=null;执行此步骤的时候程序只是更改栈内存中的p变量保存的地址,把地址指向null,而并没有操作堆内存(把p所指向的对象实例清空回收)
- 无论是形参还是实参,执行xxx=null;操着时都是把xxx变量栈中存储的地址改为指向null的地址。不操作堆中的数据。
public void pp(int k){
System.out.println(k);
}
pp(1)
栈和堆的区别
- 管理方式:栈自动释放,堆需要GC
- 空间大小:栈比堆小
- 碎片相关:栈产生的碎片远小于堆
- 分配方式:栈支持静态和动态分配,而堆仅仅支持动态分配
- 效率:栈的效率比堆高
方法的问题
方法的分类:
- 无参无返 (没有参数列表,没有返回值)单纯的作为功能代码的聚合使用 便于功能复用
- 无参有返(没有参数列表,有返回值)
- 有参无返(有参数列表,没有返回值)适用于功能需要根据参数来进行计算的情况,但是计算的最终结果又无需返回处理
- 有参有返 (有参数列表,有返回值)适用于功能需要根据参数来进行计算的情况,而且最终的结果需要返回处理
方法的形参和实参
- 形参:是定义在方法声明上,用于指定该方法需要传递的参数类型的
- 实参:是调用方法时,实际传递的方法值
方法参数传递
- 基本数据类型作为参数传值:传值传的时值的内容,来到另一个方法空间之后,这个值和之前没有 任何关系。(如你们拷贝我分享的网盘内容不会改变我原有玩盘内容)
- 引用数据类型作为参数传值:传值传的时对象在堆的地址值,所以了两个内容指向了同一空间是相 互影响的。(如你登陆我的网盘拷贝内容改变的话会改变我的网盘内容)
基本数据类型的对象缓存
private static class IntegerCache {}这实际上就是Integer的cache
Integer num1 = 12;
Integer num2 = 12; 这块相等,<=127都是真的
System.out.println(num1 == num2);
- 定义Integer k1=12是先在缓存池中查找12这个对象,如果有则直接使用
- new Integer(12)一定会引发对象的创建,而不管缓存池中是否包含这个对象
Integer k1 = 12;
Integer k2 = 12;
System.out.println(k1 == k2);//true,k1和k2都是引用类型,所以==比较的是地址
Integer k3=new Integer(12);
System.out.println(k1==k3);//false
Integer k1 = 129;
Integer k2 = 129;
System.out.println(k1 == k2);//false,因为缓存只缓存-128到127之间的数据
字符串缓存池
String s1="abc";
String s2="abc";
String s3=new String("abc");//这里会创建2个对象,一个在缓存池中一个是new导致的新创建对象
System.out.println(s1==s2);//true
System.out.println(s1==s3);//false
注意:如果通过字串拼接所得内容和某个字串内容一致,但是地址不同
java.lang.Object
所有Java类都直接或者间接的继承自Object类,那么Object中的方法也是其他所有类都拥有的
Object类中的常用方法
getClass方法用于获得当前对象的类型
直接输出一个对象时,实际上默认输出的是该对象toString()方法的返回值。所有的Java类中都有这个方 法,因为Java类都直接或者间接的继承于Object类,而Object类中定义了toString方法。为了实现用户 自定义输出格式,允许在类中覆盖定义toString方法
比较两个字符串是否相等,能用“==”吗?
Java中的约定:重写equals()方法必须重写hasCode()方法
hashCode()方法返回一个整形数值,表示该对象的哈希码值。
hashCode()具有如下约定:
1).在Java应用程序程序执行期间,对于同一对象多次调用hashCode()方法时,其返回的哈希码是相同 的,前提是将对象进行equals比较时所用的标尺信息未做修改。在Java应用程序的一次执行到另外一次 执行,同一对象的hashCode()返回的哈希码无须保持一致;
2).如果两个对象相等(依据:调用equals()方法),那么这两个对象调用hashCode()返回的哈希码也必 须相等;
3).反之,两个对象调用hasCode()返回的哈希码相等,这两个对象不一定相等。
即严格的数学逻辑表示为: 两个对象相等 equals()相等 => hashCode()相等。因此,重写equlas() 方法必须重写hashCode()方法,以保证此逻辑严格成立,同时可以推理出:hasCode()不相等 => equals()不相等 两个对象不相等。
可能有人在此产生疑问:既然比较两个对象是否相等的唯一条件(也是冲要条件)是equals,那么为什 么还要弄出一个hashCode(),并且进行如此约定,弄得这么麻烦?
其实,这主要体现在hashCode()方法的作用上,其主要用于增强哈希表的性能。
以集合类中,以Set为例,当新加一个对象时,需要判断现有集合中是否已经存在与此对象相等的对象, 如果没有hashCode()方法,需要将Set进行一次遍历,并逐一用equals()方法判断两个对象是否相等,此 种算法时间复杂度为o(n)。通过借助于hasCode方法,先计算出即将新加入对象的哈希码,然后根据哈 希算法计算出此对象的位置,直接判断此位置上是否已有对象即可。(注:Set的底层用的是Map的原理 实现)
在此需要纠正一个理解上的误区:对象的hashCode()返回的不是对象所在的物理内存地址。甚至也不一 定是对象的逻辑地址,hashCode()相同的两个对象,不一定相等,换言之,不相等的两个对象, hashCode()返回的哈希码可能相同。
protected void finalize(); finalize方法主要与Java垃圾回收机制有关。首先我们看一下finalized方 法在Object中的具体定义: protected void finalize() throws Throwable { }
我们发现Object类中finalize方法被定义成一个空方法,为什么要如此定义呢?finalize方法的调用时机 是怎么样的呢?
首先,Object中定义finalize方法表明Java中每一个对象都将具有finalize这种行为,其具体调用时机在: JVM准备对此对形象所占用的内存空间进行垃圾回收前,将被调用。由此可以看出,此方法并不是由我 们主动去调用的(虽然可以主动去调用,此时与其他自定义方法无异)。
多态性
多态性是发送消息给某个对象,让该对象自行决定响应何种行为。通过将子类对象引用赋值给超类对象 引用变量来实现动态方法调用
public class Test1 {
public static void main(String[] args) {
Fa cc=new Fa();
//调用的是一个名称为pp的方法,但是参数不同执行的处理不同
cc.pp();//Fa...pp
cc.pp(12);//Fa.pp(int)
}
}
class Fa{
public void pp(){
System.out.println("Fa...pp");
}
public void pp(int k){
System.out.println("Fa.pp(int)");
}
}
多态性通过允许同一界面指定一类动作减少了程序的复杂度,编译器工作就是选择适用于各个情况的特 定动作,而程序员则无须手动进行选择,使用者仅仅是记得以及利用这个统一的界面
多态形成的三个必要条件:
有继承,父类定义方法,子类重写方法
父类的引用指向子类的对象
可以使用参数传递时多态,也可以直接创建对象时多态
多态可以用三个定义和两个方法来总结
三个定义分别是父类定义子类构建、接口定义实现类构建和抽 象类定义实体类构建,而两个方法分别是方法重载和方法重写。
多态分为两种:
编译时多态:方法的重载
运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态