Java相同类型的两个对象比较属性值,得到不同属性的名称和对应的值,附代码实例

假设现在有需求如下:比较2个相同类型的不同对象,找到这2个对象的不同的地方,并展示记录一下。当然说的是相互对应的属性啦。

带着这个需求,看下面的例子。(我写代码的都不嫌弃长,你看代码的就也别嫌弃咯。)

package com.lxk.test;

import com.google.common.collect.Lists;
import com.lxk.model.Car;
import com.lxk.model.Dog;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

/**
 * Compare the difference between two objects
 * <p>
 * Created by lxk on 2017/3/12
 */
public class CompareObject {
    public static void main(String[] args) {
        Dog dog1 = new Dog("大师兄的dog", true, true);
        Dog dog2 = new Dog("大师兄的dog", false, false);
        List<Dog> dogs = Lists.newArrayList();
        List<Dog> myDogs = Lists.newArrayList();
        dogs.add(dog1);
        myDogs.add(dog2);
        List<String> boys = Lists.newArrayList("tom", "jerry", "jack");
        //List<String> myBoys = Lists.newArrayList("tom", "jerry", "jack");//这行注释打开,下行代码注释掉,则boys属性就相同了
        List<String> myBoys = Lists.newArrayList("tom hanks", "gery", "pul");
        Car car1 = new Car("q7", 182, dogs, boys);
        Car car2 = new Car("a6", 152, myDogs, myBoys);
        contrastObj(car1, car2);
    }

    private static void contrastObj(Object obj1, Object obj2) {
        if (obj1 instanceof Car && obj2 instanceof Car) {
            Car pojo1 = (Car) obj1;
            Car pojo2 = (Car) obj2;
            List<String> textList = Lists.newArrayList();
            try {
                Class clazz = pojo1.getClass();
                Field[] fields = pojo1.getClass().getDeclaredFields();
                for (Field field : fields) {
                    PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                    Method getMethod = pd.getReadMethod();
                    Object o1 = getMethod.invoke(pojo1);
                    Object o2 = getMethod.invoke(pojo2);
                    String s1 = o1 == null ? "" : o1.toString();//避免空指针异常
                    String s2 = o2 == null ? "" : o2.toString();//避免空指针异常
                    //思考下面注释的这一行:会bug的,虽然被try catch了,程序没报错,但是结果不是我们想要的
                    //if (!o1.toString().equals(o2.toString())) {
                    if (!s1.equals(s2)) {
                        textList.add("不一样的属性:" + field.getName() + " 属性值:[" + s1 + "," + s2 + "]");
                    }
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            for (Object object : textList) {
                System.out.println(object);
            }
        }
    }
}


代码里面使用的两个model,也是极为重要的。

Car的bean如下:

package com.lxk.model;

import java.util.List;

public class Car implements Comparable<Car> {
    private String sign;
    private int price;
    private List<Dog> myDog;
    private List<String> boys;

    public Car(String sign, int price) {
        this.sign = sign;
        this.price = price;
    }

    public Car(String sign, int price, List<Dog> myDog) {
        this.sign = sign;
        this.price = price;
        this.myDog = myDog;
    }

    public Car(String sign, int price, List<Dog> myDog, List<String> boys) {
        this.sign = sign;
        this.price = price;
        this.myDog = myDog;
        this.boys = boys;
    }

 //getter and setter

}

Dog的bean如下:

package com.lxk.model;

/**
 * 测试boolean属性的getter和setter
 * <p>
 * Created by lxk on 2016/12/23
 */
public class Dog {
    private String name;
    private boolean isLoyal;//是忠诚的
    private boolean alive;//活蹦乱跳的


    public Dog(boolean isLoyal, boolean alive) {
        this.isLoyal = isLoyal;
        this.alive = alive;
    }

    public Dog(String name, boolean isLoyal, boolean alive) {
        this.name = name;
        this.isLoyal = isLoyal;
        this.alive = alive;
    }

    //getter and setter

    //@Override
    //public boolean equals(Object o) {
    //    if (this == o) return true;
    //    if (!(o instanceof Dog)) return false;
    //    Dog dog = (Dog) o;
    //    return Objects.equal(getName(), dog.getName());
    //}
    //
    //@Override
    //public int hashCode() {
    //    return Objects.hashCode(getName());
    //}
}

先不要在意,我为什么把Dog bean的hashcode和equal方法给注释啦先。

如上代码的执行结果 图,如下:


从代码的执行情况看,可以得到,我们要的不同的属性的名称,当然你是可以把这些sign price myDog  boys这些属性,再次转化成你要的文字。

注意后面的对应的属性值。类型是String 或者int 或者List<String>这些类型,直接使用toString()方法就可以把他转化成字符串了,但是看到myDog属性,这个自定义对象的list的toString()的时候,却是两个地址,对,这个就是他们的内存堆地址吧。可以看到,2个集合所指向的引用是不一样的。


现在,看第二种情况,就是把Dog bean里面的注释,打开。其他地方的代码不动。

然后指向结果如下图:



可以看到,现在 myDog属性的值,竟然一样啦。但是,我们可以清楚的看到,我们的两个Car对象里面包含的Dog对象是不一样的。只是,这2个Car对象的两个Dog对象的name属性是一样的,都是:大师兄的dog。

因为,你在Dog bean里面已经重写了hashcode和equal两个方法,那么在new对象的时候,他就会只根据这个name属性去生成hashcode,他决定放在堆内存的什么位置。

那么就算,在我们看起来,那两个Car对象里面的两个Dog对象是不同的,但是计算机内存却认为是相同的。而且,toString()方法得到的结果也是跟他的内存地址有关系的。所以,比较结果,就是一样的。

这就是意外。

在实际开发中,我们也会有这种情况,可能因为某种需要,我们把某个bean的hashcode和equal方法给重写了。就像我这里 的这个Dog类,但是这个类又在其他地方被使用了。他的不同也决定另外一个对象是不是相同的,就比如我要判断两个Car对象是不是相同的。用如上方法的话,那么就判断失败啦。

怎么应对呢?一般,当你发现你判断失败了,多半是自己测试出来的,我表示我当时就是自己测试出来的,后来发现就是写入hashcode和equal方法的属性相同了,他才相同,就想到了这一点。那么就直接再重写下toString()方法,把你要比较的属性,都写在toString方法里面,这么一来。那个list类型的属性在toString之后就会变得不是内存地址啦

下面是我重写了toString()方法的代码,以及又一次的执行结果。

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", isLoyal=" + isLoyal +
                ", alive=" + alive +
                '}';
    }



这样,刚刚判断失败的地方,现在就完美的错过啦。


最后,总结一下。

要想搞明白这个,你得知道:

1,为什么要重写 hashcode和equal,以及这2个方法的作用到底 是什么。

2,正确重写hashcode和equal的姿势。上面的是可以预防空指针的哟。哦,虽然是还是这个guava的工具包里面的东西。

(这个guava包这个方法被jdk1.7借鉴了,在java.util包里面也有这个方法,可以试一下)

2,当然了,这个重点还是反射的简单应用,我上面的那个问题,只是自己试验出来的问题,算是扩展问题。

关于代码里面的Lists 就是一个guava,Google的一个工具包,感兴趣的,可以查查,也是很涨姿势的。哦,也行你已经知道啦。




评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值