java 实践中的一些有关问题(2)

java 实践中的一些问题(2)

问题四、java中对象相等的判断——equals

 

        在平常编写代码的过程中,我们经常会需要判断2个对象是否相等,下面就谈谈如何实现满足需求的对象相等方法。

 

       1>  一般自己来实现equals方法可以按照以下几个步骤去实现

 

1、如果某个class的2个对象占据不同的内存空间,也可以逻辑上认为相等,那就得为这个class定义equals();

2、检查“==”是否成立,这是最严格的相等判断;

3、比较class中的相关属性值是否相等,这里按照需要,逻辑上要求哪些字段相等;

4、如果该类的base class实现了equals方法,就应该调用super.equals();

5、如果只允许同一个class所产生的对象被视为相等,则通常使用getClass();

6、如果希望子类和父类的对象也能相等,则使用instanceof,注意这里可能会带来非对等性;

 

      2>  一般实现equals方法的同时要求同时实现hashcode方法

 

       因为java里面必须保证相等的对象必须要有相同的hashcode,但是不保证不同的对象就有不同的hashcode。所以假如在实现equals()方法时,判断2个对象的属性a和b都相等时,对象就相等,此时实现hashcode方法时,必须基于这2个字段去计算,从而保证equals的2个对象具有相同的hashcode。

下面是一个实现类的equals方法的典型例子:

 

class Person {

String name;

String age;

//set method

//get method;

  public boolean equals(Object obj){

      if(this == obj){

          return true;

      }

 

     if(obj!=null && obj.getClass() == obj.getClass){

           Person person = (Person)obj;

           if(this.name.equals(person.name)  && this.age.equals(person.age)){

                      return true;

           }

     }

        return false;

  }

       public int  hashCode(){

          return this.name.hashCode()*31+this.age.hashCode();

       }

}

 

 问题五、java多线程中经常遇到的问题

 

       大多数时候我们使用的是语言层次上的锁来解决多线程环境中对共享资源的访问问题,也就是Sychronized关键词来修饰。它修饰的是对象而不是方法或代码,在我的另一篇文章java中的进程与线程及java对象的内存结构中有提到java对象头里面对锁相关信息的存储。

 

Sychronized的用法:

同步实例方法,锁定这个方法对应的当前实例对象。

同步静态方法或class对象,锁定当前对象的Class对象。

同步对象或方法块,锁定这个对象。

 

需要通过wait方法来阻塞线程、notifynotifyAll方法来唤醒线程。

JDK中的suspend()和stop方法已经不建议再使用,前者可能会由于资源不释放造成死锁,后者可能会产生不可预知的错误。

编写同步代码的时候主要注意2个点:

1、对同步资源的可见性的保证

2、对同步资源操作的原子性保证

像JDK中应用层次上的同步框架AQS,就是通过volatile来保证可见性及CAS操作来保证原子性,从而实现了一系列的锁。

 

1>、 在进行同步控制时,使用private关键字修饰同步资源以及访问该资源的方法,这样才能保证能够真正的实现同步。否则,外部程序可以绕过同步控制而直接修改同步资源出现问题。

2>、避免无谓的同步控制,尽量使得同步控制的锁粒度最细,这样才能使性能影响尽可能的小。

3>、对于共享变量的访问,除了使用Sychronized(每次取得和释放锁才进行主存和私有内存的一致化),还可以使用volatile,这个关键词修饰的变量,在每次访问时就会进行主存和私有内存的一致化,从而保证可见性。

4>、当需要锁定多个对象进行一些复杂的操作时,以固定而全局的顺序去取得多个locks以避免死锁。

5>、wait和notify都是在获得锁的情况下才能调用的方法,在使用这些方法时要使用旋锁。因为当一个线程被唤醒,它必须重新检查它的等待条件,因为当初的等待条件有可能在此时又变化了。