Java中的各种引用(强引用、软引用、弱引用、虚引用)(一)

前言

以下是自己学习的记录和一些尝试,如果存在不对的地方,请指出。多谢!

强引用

正常引用即为强引用。eg:

Object o = new Object();
软引用

使用SoftReference获取的对象即为软引用对象。eg:

SoftReference<Object> sr = new SoftReference<>(new Object());

通过sr实现对对象new Object()的软引用。

弱引用

使用WeakReference获取的对象即为弱引用对象。eg:

WeakReference<Object> wr = new WeakReference<>(new Object());

通过wr实现对对象new Object()的弱引用。

虚引用

使用PhantomReference获取的对象即为虚引用对象。eg:

PhantomReference<Object> pr = new PhantomReference<>(new Object(), new ReferenceQueue<>());

通过pr实现对对象new Object()的虚引用。
PS:1. 该引用对象不可访问;2. 需要指明new ReferenceQueue<>()来作为回收使用的队列容器。

主要区别

除了申明以外,主要区别在于对象的引用方式、内存定位和回收机制。

引用

首先,说一下引用,先创建一个简单的类Person,有两个属性name, age。

package com.test.customize;

public class Person {
    private String name;

    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

然后开始调用创建对象(代码是另一台电脑写的,没有配置中文,见谅!)

package com.test.application;

import com.test.customize.Person;

public class References {

    public static void main(String[] args) {
        //Create a new object and add three references
        Person p1 = new Person("a", 1);
        Person p2 = p1;
        Person p3 = p2;
        System.out.println("p1 = " + p1 + "; " + "p2 = " + p2 + "; " + "p3 = " + p3 + "; " + "(p1 == p3) = " + (p1 == p3) + "; " + "(p1.equals(p3) = " + (p1.equals(p3)));
        //Set the middle reference to be null
        p2 = null;
        System.out.println("p1 = " + p1 + "; " + "p2 = " + p2 + "; " + "p3 = " + p3 + "; " + "(p1 == p3) = " + (p1 == p3) + "; " + "(p1.equals(p3) = " + (p1.equals(p3)));
        //Use the last reference to set a new value for the age in the object
        p3.setAge(5);
        System.out.println("p1 = " + p1 + "; " + "p2 = " + p2 + "; " + "p3 = " + p3 + "; " + "(p1 == p3) = " + (p1 == p3) + "; " + "(p1.equals(p3) = " + (p1.equals(p3)));
        //Set the first reference to be null
        p1 = null;
        System.out.println("p1 = " + p1 + "; " + "p2 = " + p2 + "; " + "p3 = " + p3);
        //Create another object with the same name and age as the created object
        Person p4 = new Person("a", 5);
        System.out.println("p4 = " + p4 + "; " + "(p3 == p4) = " + (p3 == p4) + "; " + "(p3.equals(p4)) = " + (p3.equals(p4)));
        //Get the hashcode of two object
        System.out.println("p3 hashcode = " + p3.hashCode());
        System.out.println("p4 hashcode = " + p4.hashCode());
    }
}

运行结果:

p1 = Person{name='a', age=1}; p2 = Person{name='a', age=1}; p3 = Person{name='a', age=1}; (p1 == p3) = true; (p1.equals(p3) = true
p1 = Person{name='a', age=1}; p2 = null; p3 = Person{name='a', age=1}; (p1 == p3) = true; (p1.equals(p3) = true
p1 = Person{name='a', age=5}; p2 = null; p3 = Person{name='a', age=5}; (p1 == p3) = true; (p1.equals(p3) = true
p1 = null; p2 = null; p3 = Person{name='a', age=5}
p4 = Person{name='a', age=5}; (p3 == p4) = false; (p3.equals(p4)) = false
p3 hashcode = 25418303
p4 hashcode = 26202540

分析一下对象的创建和引用

  1. 第一次创建对象
    第一次创建对象的时候,创建了一个对象new Person(“a”, 1),并且建立了3个变量实现对它的引用。此时,内存中只有这一个对象。
    这三个变量是完全相等的,并且也是完全一致的,比较的结果都是true。
  2. 变量和对象修改
    1.修改3个变量中的p2,将其置为null,此时,对象 new Person(“a”, 1) 不再获得p2的引用,但是对已经存在的引用p1和p3不产生任何影响。此时,内存中还是只有这一个对象。
    2.使用p3对对象 new Person(“a”, 1) 重新设置属性,则对该对象存在引用的p1打印结果同步变化。
    3.p1置为null,断开对对象 new Person(“a”, 1) 的引用,对已经建立引用关系的p3不产生影响。
  3. 第二次创建对象
    创建一个新的对象 new Person(“a”, 5),并使用变量p4来引用该对象。虽然该对象的属性值和第一个对象完全保持一直,但是该对象是一个全新的对象,与之前的对象共存于内存中。
    使用这个语句 Person p4 = new Person(p3.getName(), p3.getAge()); 创建的也一样。
  4. 关于比较对象
    正常的比较对象,实际上比较的是内存地址。每一个对象的内存地址都是唯一的,只要内存地址一致,则是同一个对象。在没有重写Object的equals()方法的前提下,“==”和“equals”是完全一直的,比较的都是内存地址。因此,最开始的p1\p2\p3是完全相等的,但是p4是单独的,与前面三个不相等。
    Object的equals方法如下:
    public boolean equals(Object obj) {
        return (this == obj);
    }
  1. 关于重写equals()方法
    先说结论:不建议重写,尤其是对已经存在的对象进行二次编辑的时候
    对于String类,是已经重写了Object的equals()方法的,源码如下:
    private final char value[];
    ···
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

可以看到,String也是优先采取内存地址比较的方法来判断的。当内存地址不一致时,才将对象拆分成char数组来比较每一位上的char值是否一致(此时,已经忽略掉了对象的内存地址和hashcode)。
接下来说下为什么不建议重写equals方法(String是final类,不能再次重写了,而且比较的时候使用的是char,不受内存地址和hashcode影响。)
对于已经存在的系统,如果系统中已经存在了使用类似于(p3.equals(p4))的语句,在没有重写的前提下,它的效用和(p3 == p4)是完全一样的。如果此时,重写了equals方法,比如:

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        Person person = (Person) o;

        if (age != person.age)
            return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }

此时,(p3.equals(p4))的效应已经跟(p3 == p4)不一样了,很有可能会导致之前的判断已经失效了。而对于一个新的系统而言,除非明确规定了或者说所有人的编程习惯都一样,否则的话,不能绝对排除有人会使用(p3.equals(p4))来代替(p3 == p4)作为判断的条件。
如果需要进行非内存地址的判断,建议自己重新定义一个比较方法,例如:

    public boolean isEqual(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        Person person = (Person) o;

        if (age != person.age)
            return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值