构造代码块、equals

来源:互联网 发布:中国自然灾害数据库 编辑:程序博客网 时间:2024/06/06 11:38

构造代码块

在类中直接定义没有任何修饰符、前缀、后缀的代码块即为构造代码块。我们明白一个类必须至少有一个构造函数,构造函数在生成对象时被调用。构造代码块和构造函数一样同样是在生成一个对象时被调用,那么构造代码在什么时候被调用?如何调用的呢?看如下代码:

public class Test {    /**     * 构造代码     */    {        System.out.println("执行构造代码块...");    }    /**     * 无参构造函数     */    public Test(){        System.out.println("执行无参构造函数...");    }    /**     * 有参构造函数     * @param id  id     */    public Test(String id){        System.out.println("执行有参构造函数...");    }}

上面定义了一个非常简单的类,该类包含无参构造函数、有参构造函数以及构造代码块,同时在上面也提过代码块是没有独立运行的能力,他必须要有一个可以承载的载体,那么编译器会如何来处理构造代码块呢?编译器会将代码块按照他们的顺序(假如有多个代码块)插入到所有的构造函数的最前端,这样就能保证不管调用哪个构造函数都会执行所有的构造代码块。上面代码等同于如下形式:

public class Test {    /**     * 无参构造函数     */    public Test(){        System.out.println("执行构造代码块...");        System.out.println("执行无参构造函数...");    }    /**     * 有参构造函数     * @param id  id     */    public Test(String id){        System.out.println("执行构造代码块...");        System.out.println("执行有参构造函数...");    }}
public static void main(String[] args) {        new Test();        System.out.println("----------------");        new Test("1");    }------------Output:执行构造代码块...执行无参构造函数...----------------执行构造代码块...执行有参构造函数...

从上面的运行结果可以看出在new一个对象的时候总是先执行构造代码,再执行构造函数,但是有一点需要注意构造代码不是在构造函数之前运行的,它是依托构造函数执行的。正是由于构造代码块有这几个特性,所以它常用于如下场景:
1、 初始化实例变量

  如果一个类中存在若干个构造函数,这些构造函数都需要对实例变量进行初始化,如果我们直接在构造函数中实例化,必定会产生很多重复代码,繁琐和可读性差。这里我们可以充分利用构造代码块来实现。这是利用编译器会将构造代码块添加到每个构造函数中的特性。  2、 初始化实例环境  一个对象必须在适当的场景下才能存在,如果没有适当的场景,则就需要在创建对象时创建此场景。我们可以利用构造代码块来创建此场景,尤其是该场景的创建过程较为复杂。构造代码会在构造函数之前执行。  上面两个常用场景都充分利用构造代码块的特性,能够很好的解决在实例化对象时构造函数比较难解决的问题,利用构造代码不仅可以减少代码量,同时也是程序的可读性增强了。特别是当一个对象的创建过程比较复杂,需要实现一些复杂逻辑,这个时候如果在构造函数中实现逻辑,这是不推荐的,因为我们提倡构造函数要尽可能的简单易懂,所以我们可以使用构造代码封装这些逻辑实现部分。

java equals

在Java规范中,它对equals()方法的使用必须要遵循如下几个规则:
equals 方法在非空对象引用上实现相等关系:

 1、自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。 2、对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。  3、传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。  4、一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。 5、 对于任何非空引用值 x,x.equals(null) 都应返回 false。    对于上面几个规则,我们在使用的过程中最好遵守,否则会出现意想不到的错误。  在java中进行比较,我们需要根据比较的类型来选择合适的比较方式: 1) 对象域,使用equals方法 。    2) 类型安全的枚举,使用equals或== 。   3) 可能为null的对象域 : 使用 == 和 equals 。  4) 数组域 : 使用 Arrays.equals 。  5) 除float和double外的原始数据类型 : 使用 == 。  6) float类型: 使用Float.foatToIntBits转换成int类型,然后使用==。    7) double类型: 使用Double.doubleToLongBit转换成long类型,然后使用==。

至于6)、7)为什么需要进行转换,我们可以参考他们相应封装类的equals()方法,下面的是Float类的:

public boolean equals(Object obj) {    return (obj instanceof Float)           && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));    }原因嘛,里面提到了两点:However, there are two exceptions:If f1 and f2 both representFloat.NaN, then the equals method returnstrue, even though Float.NaN==Float.NaNhas the value false.If <code>f1 represents +0.0f whilef2 represents -0.0f, or viceversa, the equal test has the valuefalse, even though 0.0f==-0.0fhas the value true.

在equals()中使用getClass进行类型判断

  我们在覆写equals()方法时,一般都是推荐使用getClass来进行类型判断,不是使用instanceof。我们都清楚instanceof的作用是判断其左边对象是否为其右边类的实例,返回boolean类型的数据。可以用来判断继承中的子类的实例是否为父类的实现。注意后面这句话:可以用来判断继承中的子类的实例是否为父类的实现,正是这句话在作怪。我们先看如下实例(摘自《高质量代码 改善java程序的151个建议》)。
父类:Personpublic class Person {    protected String name;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Person(String name){        this.name = name;    }    public boolean equals(Object object){        if(object instanceof Person){            Person p = (Person) object;            if(p.getName() == null || name == null){                return false;            }            else{                return name.equalsIgnoreCase(p.getName());            }        }        return false;    }} 子类:Employeepublic class Employee extends Person{    private int id;    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public Employee(String name,int id){        super(name);        this.id = id;    }    /**     * 重写equals()方法     */    public boolean equals(Object object){        if(object instanceof Employee){            Employee e = (Employee) object;            return super.equals(object) && e.getId() == id;        }        return false;    }}

上面父类Person和子类Employee都重写了equals(),不过Employee比父类多了一个id属性。测试程序如下:

public class Test {    public static void main(String[] args) {        Employee e1 = new Employee("chenssy", 23);        Employee e2 = new Employee("chenssy", 24);        Person p1 = new Person("chenssy");        System.out.println(p1.equals(e1));        System.out.println(p1.equals(e2));        System.out.println(e1.equals(e2));    }}
  上面定义了两个员工和一个普通人,虽然他们同名,但是他们肯定不是同一人,所以按理来说输出结果应该全部都是false,但是事与愿违,结果是:true、true、false。  对于那e1!=e2我们非常容易理解,因为他们不仅需要比较name,还需要比较id。但是p1即等于e1也等于e2,这是非常奇怪的,因为e1、e2明明是两个不同的类,但为什么会出现这个情况?首先p1.equals(e1),是调用p1的equals方法,该方法使用instanceof关键字来检查e1是否为Person类,这里我们再看看instanceof:判断其左边对象是否为其右边类的实例,也可以用来判断继承中的子类的实例是否为父类的实现。他们两者存在继承关系,肯定会返回true了,而两者name又相同,所以结果肯定是true。  所以出现上面的情况就是使用了关键字instanceof,这是非常容易“专空子”的。故在覆写equals时推荐使用getClass进行类型判断。而不是使用instanceof。

在使用getClass是是需要进行null判断。 if(o == null || o.getClass() != getClass())

0 0
原创粉丝点击