重读Core Java,记录点滴细节(1)

       糊里糊涂地用了Java快三年多了,一直没有很系统地读过一本Java的经典书籍。借到一本<<Core Java ,8th>>,记下来自己在里面领悟到的细节点滴。

 

1.  抽奖程序算法。

       很简单的一个应用,由于抽奖的结果不能出现重复。以前会每次来查看Math.Random()的结果是不是在已经抽出的列表中,如果有的话重新抽一次。看到书中巧妙的实现,很灵活。记录如下:

 

                //抽奖的样本总量数组
                int[] numers=new int[allSeek];
		for(int i=0;i<numers.length;i++){
			numers[i]=i+1;
		}
		//抽奖得到的结果存储数组
		int[] result=new int[pickerSeek];
		for(int i=0;i<result.length;i++){
                        //随机抽取
			int r=(int)(Math.random()*allSeek);
			//存储抽奖结果
			result[i]=numers[r];
			//由于已经抽出一个,下次抽奖的问题将减一,将当前被抽出的位置的样本用样本问题中的最后一个样本值替换
			numers[r]=numers[allSeek-1];
                        //抽奖样本问题减一
			allSeek--;
		}

 

 

2. 封装中访问器的实现

 

    特别注意不应该编写返回引用可变对象的访问器方法。这样的话会破坏类的封装性。如下代码

class Person{
     private Date birthday;

     public Date getBirthday(){
           return this.birthday;
     }
}

    初步来看的话,是没有问题的,但如果使用以下代码来访问:

 

Person p1=new Person();

Date birthday=p1.getBirthday();
birthday.setTime(birthday.getTime()-1);

    此时,在我们去访问p1.getBirthday()时就会发现当前的birthday发生了变化。这是因为birthday与p1.birthday引用的是同一个可变对象。所以,我们如果需要返回一个可变对象的引用 ,就应用首先对它进行克隆(clone),如下代码:

 

 

class Person{
     private Date birthday;

     public Date getBirthday(){
           return (Date)this.birthday.clone();
     }
}

 

 

 

3. 基于类的访问权限

 

      我们知道,方法可以访问所调用对象的私有数据。一个方法可以访问所属类的所有对象的私有数据,这点有点拗口,看下面的代码。

 

 

class Person{
     private String name;

     public boolean  equals(Person otherPerson){
           return this.name.equals(otherPerson.name);
     }
}

 

     代码没什么问题,我们在使用中的一种典型的使用形式如下:

 

if(zhangsan.equals(boss)){
       //zhangsan's name is the same as boss
}

 

    我们类方法中的代码在访问zhangsan的私有域name时没有问题。但是,它还访问了boss的私有域,有点奇怪。但这是合法的,它的原因是boss是Person类的对象,而Person类的方法可以访问Person类的任何一个对象 的私有域。

 

4.  Java程序设计语言总是采用值调用。

     这也就是说,方法得到的是所有参数值 的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。如下:

  • 一个方法不能修改一个基本数据类型的参数
  • 一个方法可以改变一个对象参数的状态
  • 一个方法不能实现 让对象参数引用 一个新的对象

      对于第三点,我们用下面的代码来说明:

 

public class Person{

    private String name;

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

    public String getName(){
        return this.name;
    }

     public static void  swap ( Person p1, Person p2){
        Person temp=p1;
        p1=p2;
        p2=temp;
    }

     public static void main(String[] args){
            Person p1=new Person("zhangsan");
            Person p2=new Person("lisi");
            Person.swap(p1,p2);
            System.out.println(p1.getName()+","+p2.getName());
    }

}

   上面代码的执行结果:

         zhangsan,lisi

   这是因为,在static方法中的参数p1与p2被初始化为两个对象引用的拷贝,这个方法交换的是两个拷贝。在方法执行完毕后,参数变量p1与p2都被垃圾回收器给回收了。原来的变量p1与p2仍然引用 的是方法调用之前引用的对象。

 

5. final的用法

    通常,我们将final关键字应用于局部变量,实例变量及静态变量。在所有这些情况下,它们的全部定义都是:在创建这个变量之后,只能够为其赋值一次。此后,再也不能修改它的值了。

    注意两点:

  •     局部类的方法只可以引用 定义为final的局部变量。这就是为什么我们在局部类中引用外部类的一些方法参数时,必须将相应的参数的修饰符改为final
  •     有时候 ,final限制显得并不太方便,如我们想更新在一个封闭作用域内的计数器。这里想要统计一下在排序过程中调用 compareTo方法的次数,如下代码所示:

 

int counter=0;
Date[] dates=new Date[100];
for(int i=0;i<dates.length;i++){
    dates[i]=new Date(){
        public int compareTo(Date other){
            counter++;  //Error
            return super.compareTo(other);
        }
    };
}

Array.sort(dates);
System.out.println(counter+" comparisons.");
 

 

    由于我们很清楚地知道counter需要更新,所以不能将counter声明为final。由于Integer对象是不可变的,所以也不能用Integer代替它。所以,我们应该使用一个长度为1的数组,代码如下:

 

   

final int[] counter=new int[1];

Date[] dates=new Date[100];
for(int i=0;i<dates.length;i++){
    dates[i]=new Date(){
        public int compareTo(Date other){
            counter[0]++;

            return super.compareTo(other);
        }
    };
}

Array.sort(dates);
System.out.println(counter[0]+" comparisons.");

 

    在此,数组变量仍然被声明为final,但是这仅仅表示 不可以让它引用 另外一个数组。数组中的数据元素可以自由地更改。

 

你可能感兴趣的:(java,算法)