《Head First Java》笔记(2)

2.1 面向过程与面向对象之间的战争

面向过程与面向对象之间的战争是由下面一则小故事引起的。

从前,有一个软件小铺,该小铺的老板娘(同时也是项目经历)手下有两个小兵:阿朱和阿娇。有一天,老板娘故意刁难两个兵,并且让她两进行比赛,看谁的程序写的好。要求:在图形接口画出正方形、圆形与三角形。当用户点选某个图形时,图形要顺时针旋转360度,并且根据形状的不同而播放不同的AIF音效的音乐文件。 最终赢的人会坐上象征身份地位的宝座!

当接到这个任务的时候,阿朱首先想到的是:这个程序要执行什么动作?会需要什么样的程序?一会她就想到了,需要旋转(rotate)和播放音乐(playSound)这两个动作。于是,阿朱就迫不及待的写下了下面的程序:
这里写图片描述

当接到这个任务的时候,阿娇首先想到的是:这个程序有什么样的事物?有什么关键角色?她首先想到的时候形状体(shape)。当然,她还想到了用户、声响等对象与点击等事件。但是这些对象早就已经建立好了,而她只需要专注于创建形状体就ok了。于是,她就写下了如下程序:
这里写图片描述

此时,当阿朱看到阿娇的代码这么长的时候,心里已经乐开花了,心想,这次赢定了,那个宝座是自己的了!!!

然而,万恶的老板娘此时却又说规格需要有点改动。在原有的基础上,加上阿米巴原虫形状(不规则形状体),用户点选时也是旋转并播放.hif声音的文件。

听到这个改动后,阿朱心想rotate这个程序还可以接着用,但是playSound这个就得需要修改了,于是,她奋笔写下了如下程序:
这里写图片描述

听到这个改动后,阿娇苦笑一下,在原有的基础上又增加了一个类,如下:
这里写图片描述

当她们两把各自的程序拿给老板娘看时,老板娘失望的说:“唉呀,不对呀,阿米巴原虫不是这样旋转的…..”。
原来两个人都把旋转部分写成了这样:
1) 找出指定形状的外接四边形
2) 计算出外接四边形的中心点,以此点为中心进行旋转。
但是老板娘认为阿米巴原虫是这样旋转的:绕着一端进行旋转。
听到这里,阿朱一脸的懵逼,最后奋力的在rotate函数中加上“if/else”,于是写下了下面的程序:
这里写图片描述

这样做之后,就会造成其有一堆程序需要修改,从而本来已经测试号的东西全部又要重来一遍。

而阿娇仅仅在Amoeda这类上作了相应的修改,其他已经测试通过的部分完全不需要修改。其实现如下:
这里写图片描述

此时,阿朱看到阿娇的改动非常小,心有不甘。于是,就质问道:“阿娇,你写的代码中,冲的部分太多,而且设计的时候需要同时维护4个不同的rotate方法,效率有点低”。面对阿朱的质疑。阿娇不紧不慢的回答道:“阿朱,让我告诉你什么叫做面向对象的继承(inheritance)”。

第一步:找出4个类中的共同部分
这里写图片描述

第二步:它们都是形状(Shape),而且都有rotate和playSound这两个方法,因此可以提取出新的类
这里写图片描述

第三步:将上面的4个不同的形状体以继承的关系连接到Shape这个新类中,如下:
这里写图片描述

我们可以称“Square继承自Shape”,“Circle继承自Shape”,“Triangle继承自Shape”,“Amoeda继承自Shape”,而rotate和playSound这两个方法已经移出了Square、Circle、Triangle和Amoeda这四个类了。这里,我们称Shape这个类为父类(基类),称Square、Circle、Triangle和Amoeda这四个类为子类(派生类)。

到这里,阿朱还不死心,继续提出了质疑:“你这样设计,岂不是阿米巴原虫也是继承自Shape,那旋转的功能不就是都变成一样的了吗?”
阿娇说:“问的号。Amoeda这个类可以覆盖(override)Shape的方法。Java虚拟机会知道在遇到Amoeda时会使用不同的rotate方法。”

第四步:让Amoeda这个类去覆盖Shape这个父类中的rotate()和playSound()的两个方法。
这里写图片描述

覆盖的意思就是由子类重新定义继承下来的方法,以改变或延伸此方法的行为。

到此,你是不是就觉得这场比赛,阿娇大获全胜呢??非也,其实获胜的是阿花(老板娘的侄女)。看到这里,各位看官想必都明白:出来混社会,关系一定要硬啊!!!!

2.2 类与对象

首先要明白:类不是对象,类是用来创建对象的模型

创建对象需要两个类:一个是要被操作于对象的类,另一个是用来测试该类的类。

测试用的类带有main()方法,并且会在其中建立与存取被测的对象。
习惯用法:测试用的类一般命名为“受测类名称”+TestDrive。

下面就来创建第一个对象Dog
第1步:编写被测对象Dog类

class Dog
{
    //定义实例变量
    int size;
    String breed;
    String name;

    //定义方法
    void bark()
    {
        System.out.println("Ruff ! Ruff !");
    }   
}

第2步:编写测试用的类DogTestDrive

class DogTestDrive
{
    public static void main(String[] args)
    {
        Dog d = new Dog(); //创建一个Dog对象,对象名为d
        d.size = 40; //采用圆点运算符存取对象的变量
        d.bark();  //采用圆点运算符调用对象的方法
    }
}

命令行中执行命令:(必须在绝对路径下去执行)

>javac DogTestDirve.java
>java DogTestDrive

运行结果:
这里写图片描述

2.3 快离开main

只要还待在main中,你就是游离在对象村之外!因为,对于面向对象应用程序来说,我们需要用对象来与对象交互。

2.3.1 main()的两种用途:

  1. 测试真正的类
  2. 启动Java应用程序

真正的Java程序只会让对象与对象交互。这里所说的交互是指相互调用方法。

2.3.2 猜数字游戏

游戏规则:
游戏开始先产生一个0~9之间的随机数作为谜底,然后由三个玩家进行猜数字,只要有一个玩家猜的数字与谜底数字相同,游戏就结束,否则一直进行下去。

对象设计:
这里涉及到两个对象:Player(玩家)和Game(游戏),针对游戏规则,我们设计三个类:Player、GuessGame、GuessLauncher,如下:
这里写图片描述

程序源码:

class GuessGame
{
    public void startGame()
    {
        //先创建三个玩家
        Player p1 = new Player();
        Player p2 = new Player();
        Player p3 = new Player();

        //定义三个变量,分别存放玩家所猜的数字
        int guessP1 = 0;
        int guessP2 = 0;
        int guessP3 = 0;

        //声明三个变量,保存玩家是否猜中
        boolean p1isRight = false;
        boolean p2isRight = false;
        boolean p3isRight = false;

        //随机产生一个0~9之间的数作为谜底数字
        int targetNumber = (int)(Math.random() * 10);
        System.out.println("Target Number is " + targetNumber);

        while(true)
        {
            //获取每个玩家所猜的数字
            p1.guess();
            guessP1 = p1.number;
            System.out.println("Player one guessed is " + guessP1);

            p2.guess();
            guessP2 = p2.number;
            System.out.println("Player two guessed is " + guessP2);

            p3.guess();
            guessP3 = p3.number;
            System.out.println("Player three guessed is " + guessP3);

            if(guessP1 == targetNumber)
            {
                p1isRight = true;
            }

            if(guessP2 == targetNumber)
            {
                p2isRight = true;
            }

            if(guessP3 == targetNumber)
            {
                p3isRight = true;
            }

            if(p1isRight || p2isRight || p3isRight)//只要有一人猜中,游戏就结束
            {
                System.out.println("We have a winner !");
                System.out.println("Player one  guessed right ? " + p1isRight);
                System.out.println("Player two  guessed right ? " + p2isRight);
                System.out.println("Player three  guessed right ? " + p3isRight);
                System.out.println("Game Over");

                break; //游戏结束,跳出循环
            }
            else
            {
                System.out.println("No one guess right, and game must be continue");
            }       
        }       
    }
}

class Player
{
    int number = 0; //保存玩家猜的数字

    public void guess()
    {
        number = (int) (Math.random() * 10);        
    }
}

public class GameLauncher
{
    public static void main(String[] args)
    {
        GuessGame game = new GuessGame();
        game.startGame();
    }
}

编译遇到的问题:
1> 命令行编译java源文件提示“编码GBK的不可映射字符”
解决方法:
在输入 javac 命令时,额外输入 -encoding utf-8, 例如:如果编译的源文件名为 Test.java ,那么编译时原先只需输入javac Test.java,而现在需要输入 javac -encoding utf-8 Test.java

2> 显示错误
1. 类GuessGame是公共的,应在名为GuessGame.java文件中声明。
2. 类Player是公共的,应在名为Player.java文件中声明
解决方法:
产生上面的错误原因是,将三个类都写在了同一个.java文件中了,并且类的前面都用了public修饰符进行了修饰,而一个.java文件里面只能有一个public class XXX{} 。
所以将GuessGame和Player这两个类前面的public修饰符去掉,或者将这两个类写在不同的.java文件中即可。
运行结果:
这里写图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值