《我在北大青鸟的180天》/第三章/类和对象

写在前面的废话:
    本书正在创作中,发表这些节选的目的是想收集广大网友的宝贵意见(由于csdn不能贴图,不支持html,所以有些内容阅读不便,请见谅)。
    另外,如果有哪位达人可以帮助本书出版,也请与我联系
    email:[email protected]
    或登陆我的网站:www.designersky.com
----------------------------------------------------------------------------
第三章 类和对象
经过前两章的学习,学生们已经能用Java做些小玩意儿了,比如计算n个数字的和等,这些是学习任意一门语言首先要掌握的基?gt;>1菊氯允墙樯芑。热绱蔚男问剑哺呛秃卦兀乖旌龋挂樯芗坛械挠锓ǎ詈笃饰鯦ava提供的类“Object”,难度比上一章会有所提高,不过终于要开始接触面向对象啦,呵呵。
首先看看Java中是如何给函数传参的,Java中的情况比较复杂,既不能简单的说Java是按值传递也不能说是按址传递,要分成两种情况:
1.如果参数是基本数据类型(int, float, double等),则按值传递,比如下例:
public void test(int a) {
a = 10;
}
public static void main(String args[]) {
    int i = 20;
test(i);
System.out.println(i);
}
输出结果是什么?还是20。
2.如果参数不是基本数据类型(那肯定是对象了),则传入的是地址,但是这个地址又是按值传入的,这句话有点难以理解,我们先看下例:
class Person {
  public String name;
}
public class TestDemo {
  public static void clone(Person p1, Person p2) {
    p1.name = "被克隆的人";
    p2 = p1;
  }
  
  public static void main(String[] args) {
    Person tom = new Person();
    Person jerry = new Person();
    tom.name = "Tom";
    jerry.name = "Jerry";
    
    clone(tom, jerry);
    System.out.println(tom.name + “   ” + jerry.name);
  }
}
输出结果是什么呢?是的,tom对象的name属性被改成“被克隆的人”,但是jerry的name依然是Jerry。让我们来分析一下这个有趣的现象:首先,我们说对象作为参数时传递的是地址(默念:手无指针,心有指针),自然我们可以通过这个地址来改变对象的属性值,但是这个地址是按值传进来的,所以即使在函数中改变了参数的地址(如p2=p1就是让p2指向p1的地址),也不会对传入的对象造成影响,jerry还是指向原来的地址。
思考:能否写类似于swap的方法,功能是交换两个int型参数值?
回答:不能,但是本章的最后会给出一个变通的方法解决这个问题。
思考:下面这段代码的输出结果是什么?为什么?
public class Test {
  public static void test(String a) {
    a = "Jerry";
  }
  
  public static void main(String args[]) {
    String a = "Tom";
    test(a);
    System.out.println(a);
  }
}
下面看看函数重载(也就是方法重载),在C++中已经讲过这个概念了,函数重载能够通过调用一个函数名称实现不同的功能,一组被重载的函数必须有不同的参数列表。需要注意的是,在C++我们说过函数的缺省参数完全可以由函数重载功能来代替,这一点在Java中就体现出来了——Java中不支持缺省参数,要模拟这一功能必须使用函数重载(参阅C++中“良好的函数重载”例子)。
Java中的构造函数和C++中差不多,主要讲讲什么时候会触发构造函数,与C++不同,假如定义了类Person,那么Person tom;并不会触发构造函数,这句话只是定义了一个Person型的对象tom,但是没有为tom分配内存,如果这时访问tom的属性或方法就会报NullPointException(空指针异常)。只有执行tom = new Person();才会去调用构造函数,并为对象tom分配内存。
讨论:应该在构造函数中干什么?
建议:构造函数中可以对类的数据成员赋值,或者执行一些必要前期操作(比如类的功能为访问数据库,可以考虑在构造函数中打开数据库)。
思考:如下例,在构造函数中已经为私有属性赋了初始值,还需要提供该私有属性的set方法吗?
public class Person {
private int age;
public Person(int age) {
    this.age = age;
}
public void setAge(int age) {
    this.age = age;
}
}
回答:有必要,虽然构造函数可以在对象实例化的时候改变age的值,但是当对象实例化后想再次改变age的值,就必须要用到setAge方法。
好,下面开始进入面向对象的部分,讲讲Java中的继承,有同学问了,怎么光讲了构造函数不讲析构函数呢?呵呵,Java和C++的体制不太一样,关于“析构函数”会在第五章进行介绍。
还是那句老话,继承最主要的一个目的就是实现高级的代码复用,而Java中的继承和C++中继承最大的区别就是Java不支持多重继承,好处是避免了多重继承中容易产生的二义性,缺陷是如果你真的、迫切的、急切的想要继承来自两个类的成员,那只能十分的失望。
不过好消息还是有的,Java的继承直截了当:extends后面加父类名,不象C++中细分了公有继承、保护继承和私有继承。再一个就是在子类的方法中如果想调用父类的方法,可以使用“super.方法名”,而不是C++中的“父类名::方法名”,觉得没什么区别吗?想想如果父类的名称改变了,会怎么样?
super这个关键字还有一个重要的用途:可以调用父类的构造函数。我们分析一下通常情况下实例化子类时构造函数的调用顺序,见下例:
class Father {
  public Father() {
    System.out.println("Father::Father()");
  }
  public Father(int age) {
    System.out.println("Father::Father(int)");
  }
}
class Son extends Father {
  public Son() {
    System.out.println("Son::Son()");
  }
  public Son(int age) {
    System.out.println("Son::Son(int)");
  }
}
public class TestConstruction {
  public static void main(String args[]) {
    Son tom = new Son();
    System.out.println("---------------------");
    Son jerry = new Son(20);
  }
}
----------------------------------------输出结果----------------------------------------
Father::Father()
Son::Son()
---------------------
Father::Father()
Son::Son(int)
输出结果是否如你所料呢?没错,和C++中的规律一样,子类会先调用父类中的不带参数的构造函数。能不能让子类指定要调用父类的那个构造函数呢?当然可以,代码如下:
//子类的构造函数  
public Son(int age) {
    super(age);
    System.out.println("Son::Son(int)");
}
使用super(参数列表)就可以调用父类中的构造函数了,注意两点,一,该语句只能用在子类的构造函数中,不能用在其它函数中;二,该语句只能放在子类构造函数的第一行。
常犯错误:父类中有带参数的构造函数,但是没有不带参数的构造函数,编译时就会出错。这是因为实例化子类时会自动调用父类中不带参数的构造函数,如果父类中存在其他构造函数,则编译器不会为父类生成缺省的不带参数的构造函数,就会报一个找不到父类构造函数的错误(绕口令?)。
解决方法:方法1:为父类添加不带参数的构造函数;方法2:在子类的每个构造函数中声明要调用父类中带参数的构造函数。
不知不觉一个小时已经过去了,因为有了C++的基础,学生们大部分都能理解,休息一下吧。

话说回来了,理解是能理解,但一定要自己亲自做出来才是自己的。我对这个班的学生最大的意见就是动手能力不强,上机不积极。学生们总认为理论课是重要的,要好好听,上机课一般都不重视,这实在是一个很大的误区阿。
剩下的时间来研究一下Object这个类,它处于java.lang包中,是所有类的父类,也就是说,你的类或直接或间接的继承了Object,呵呵,Object的地位很高吧?好!想一想Java为什么要这样设计?有两个好处:1,所有的类都可以使用Object中提供的公有方法;2,这种结构(学名叫单根结构)可以保证所有的类都有一些共同点。下面我们来详细了解一下。
Object类中有十几个方法,这一章我们先介绍两个比较简单的。
第一个方法:boolean equals(Object obj),来看看它的实现:
public boolean equals(Object obj) {
return (this == obj);
}
看明白这个方法是干什么用的了吗?它就是用来比较当前对象和传入的obj对象地址是否相同的。我们说所有的类都可以使用Object中提供的公有方法,那么我们自己定义了一个类Person,用它实例化两个对象tom和jerry,可以使用tom.equals(jerry)来判断这两个对象的地址是否相同。
思考I:如果存在类Cat和Mouse,以及它们的实例tom和jerry,那么语句tom.equals(jerry)是否正确?
思考II:如果存在类Cat以及它的实例tom,那么语句tom.equals(12)是否正确?
回答:第一题是正确的,因为equals方法要求传入的参数类型是Object,C++中我们知道,子类对象是可以转换成父类对象的,Cat和Mouse都是Object的子类,所以传入jerry也没有错误。第二题编译就无法通过,因为12属于int,是基本型不是类,自然也不是Object的子类。
equals方法就是这个规则:可以比较两个对象的地址,但是不能比较基本型。等等,还记得吗?我们在讲字符串比较的时候说过,String的equals方法是用来比较两个字符串的值阿,怎么前后矛盾呢?
并不矛盾,看看String类的源代码,是不是也找到了一个叫equals的方法,跟刚才我们看到的equals的声明还一模一样?子类中存在和父类声明相同的函数,我们管它叫什么?是的,函数覆盖!函数覆盖覆盖的特性就是当子类中存在于父类声明相同的函数,会优先调用子类的函数(当然前提是你通过子类对象访问该方法了)。也就是说当我们使用equals方法比较字符串的时候,实际上执行的是String类中equals方法。好,现在再看看String中的equals方法是怎么实现的,代码很长,不过能看出来是将两个字符串转换成char数组然后一位一为比较的。
思考:覆盖equals方法的好处是什么,举一个例子。
回答:好处就是我们可以自己实现两个对象比较的规则,看这个例子:设计一个读者类,里面包含姓名、身份证等属性,可能还需要提供一个读者注册方法,注册的时候要求同一个读者只能注册一次,如果保证这一点呢?可以用身份证号区分读者是否相同。类代码如下:
public class Reader {
public String name;
public String id;
public boolean equals(Object obj) {
    //如果传入的对象和本对象地址相同,直接返回true
    if (this==obj) return true;
    //只有当传入的对象是Reader的实例时,才比较其身份证,否则返回false
if (obj instanceof Reader) {
    //将obj强制转换成子类
Reader objReader = (Reader)obj;
//因为id为子类特有的属性,所以不能直接通过obj对象访问
        if (id.equals(objReader.id)) return true;
    }
    return false;
}
}
第二个方法:toString(),还是先看代码:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
很难看懂,对吗?不用着急,刚才举例的那个类Reader还用的着,我们实例化Reader对象tom,然后运行System.out.println(tom),看看输出什么。嗯,你认为对象无法直接打印?那是C++的老观念喽!输出的结果是类似于“Reader@1a5ab41”的字符串,这个”@”符号是否似曾相识?是的,就出现在toString方法中!难道……
如你猜想的那样,对象在输出时会自动调用toString方法,那么toString中到底输出的是什么呢?观察输出结果可知,getClass().getName()得到的是类名,而Integer.toHexString(hashCode())得到了一个十六进制数,我们先不去探究这个数字的含义,至少我们知道,Java中对象也可以直接打印了,虽然输出的内容很别扭。
其实我们可以让Reader对象的输出结果更合理,怎么做的?有些学生已经说出来了:“函数覆盖”。我们可以在Reader类中再增加一个toString方法,改变输出的内容。代码如下:
public String toString() {
return “读者信息 :[姓名]” + name + “ [身份证]” + id;
}
再运行一下,这下输出结果看上去更好吧?
还有什么时候会调用toString方法呢?比如String a = “Hello” + tom(tom为Reader对象)也会调用。好了,自己总结一下toString的用途吧。
下课前提一个老问题:Overload(函数重载)与Override(函数覆盖)有什么区别?(答案请参考C++问题集)

考!

沙发!

终于顶到了你的帖子!激动呀!心情演艺鱼鳔!
呵呵!写的十分透彻,看的出你对多太这些jb概念十分熟悉!
这是写好程序的基础!

我我我

啊!以前不是只能回复3次的骂!

第6次测试!

看来是改版了,以前只能发3次的!一个帖子的最大回复也有限制的!










































好文章啊,第四章什么时候贴上来!!!期待中……

老长了,该结贴了,就结了吧~~~~~~~~~~~~~~

pqds(一个女农民) 你变态呀!

不知道关于java中对象的讲解是不是都用到“地址”这个名字,因为看了很多英文书对于java中对象传递,都是叫reference,引用吧。没有提到所谓地址的,不知道是中文翻译过来的事,还是因为大家都熟悉C++。
我觉得对象的讲解最好不提C++,一题反而有点乱。C++里的指针,传值,传地址等很麻烦。
java中还是比较简单和纯粹的。
楼主总体说的不错的。不过总和C++做对比,如果学生都有c++基础那还可以。我没有c++这方面基础,所以看起来乱一点。

讲的比较通俗易懂。我会每章都收藏的
呵呵

收藏~~~
一个女农民真是变态

先回一个再看~

收藏

顶一下!
非常感谢楼主!
支持

看了第三章,懂了字符串池是怎么回事了~

扎实基础,真是好文章!

等第四章了~HOHO~~~~~~~~

不错不错!!!

bt de ren
up~~~~

不cuo

这种东西不要用真实身份发布,更谈不上出版书了。小心吃BDQL的官司!陪的你裤子都没穿的。

HOHO,~~快出?gt;>茸耪?br>
不错,我这一两天对这个有疑惑,谢谢楼主啊,先收藏,一定得好好看看,基础的东西太重要了。
支持继续发呀

希望不要说太多与c++的比较和联系,那样会学得更彻底一些的!

学习

这个……因为我带的班是先讲C++然后讲Java的,不对比不行啊,诸位对C++不感兴趣的看官见谅了。

写得很不错!!这些基础的东西一段时间不看,就忘得差不多了。又重新复习了一遍!多谢!

你是不是 北大青鸟  的帮凶啊```
 这么简单的东西`你都贴上来``
 你不害羞吗``
 这就是北大青鸟的水平吗```
  ```
 告诉你``北大青鸟是一条贼船````上的了船就不知道下不下的来了 ``
 
个人意见```
` ```

不错不错!

你知道 北大青鸟 是啥吗`````
  学费2W多 `
` 拿几个公司不要的证````学生自己买个电脑还要自己交电费```夏天还说不开风扇 ````
 ````
  没别的`` 北大青鸟`就认识钱``一句话``只阌星甡`有个脑袋`有两只手```你就可以来``管你有没有脚```
````
```湖南湘潭北大青鸟的第一班``应该是2004过年就出去就业的``现在是2005年4月8日```全班十多位学生还只出去3个``一个学生明明只有900RMB一个月``还要在广告上写着"初始工资2500RMB" ```  
``一句忠告```:``
  北大青鸟就是一条贼船```。。。``

本文其实和北大青鸟无关,为了避免争议,我决定更改书名。至于您说的“这么简单的东西`你都贴上来`` 你不害羞吗``”,是的,我不害羞。我没有说这些是高深的,甚至没有说它是有用的,我只是想写一本计算机编程的入门而已。

经过紧张的测试,我将一部分习题代码放到我的主页上,可以供在线浏览了,地址是:http://www.designersky.com 可以在主页上找到链接。
这个程序还不完善,请大家多提意见

在哪提问呀?

论坛!

真是农民,呵呵

埃!还有人骂我!也罢!
想必你的水平也不如我!

不错

iloverain1314(狼)
你很牛吗? 怎么听你说话都要冲上天了似的
北大青鸟你很了解吗? 你受过它的坑害?
我不是北大青鸟的学员,我只知道基础的东西很重要,楼主写的东西不错
走都还没稳,想跑......

路脚踏实地的走,基础扎实了到最后也见效!!


  楼主辛苦了

谢谢

谢谢,学习

楼主在干什么?

我没在干什么啊??

谢谢楼主~~

to: pqds(一个女农民) ( ) 
"埃!还有人骂我!也罢!
想必你的水平也不如我!"
对我觉得别人傻的水平肯定没有你!!!我B4你,一颗星了不起啊,看看你的信誉度吧!!

呵呵。辛苦辛苦。顶~!

文章没有仔细看,不过不妨碍我B4北大青鸟儿,呵呵

讲的很好啊,对刚入门的挺有价值的!
希望某些人不要装牛,记住人外有人。

你可能感兴趣的:(《我在北大青鸟的180天》/第三章/类和对象)