Java与面向对象概述

来源:互联网 发布:java绘制动态图形 编辑:程序博客网 时间:2024/05/19 11:35
 

什么叫面向对象(Object Oriented,简称OO)?

你知道了JAVA虚拟机、编写了那么多的代码、也知道怎么编译、运行JAVA代码了,如果就到此为止,你就还没有窥探到JAVA的门道!换句话来讲,还没开始学习JAVA!

 

因为JAVA是面向对象的编程语言(OOP:Object-oriented Programming),我们到现在为止,还没有怎么接触面向对象的概念,所以,就等于还没开始学习JAVA!

 

顾名思义,所谓面向对象,就是一切以对象为核心!不管是面向过程,还是面向对象,都是为了解决问题而存在的一种思维方式!就是怎么样去理解和解决一个问题。世间万物皆对象。我们理解一个新的概念通常用类比的方法。现在我使用一个类比物:电脑。现在这里,每个人一台电脑,电脑就是一个对象。这台电脑是我用的,那台电脑是你用的,另外一台电脑是他用的……我的、你的、他的电脑看起来都一样,都有显示器、主机、键盘等等组成,我们都可以用它来工作、学习,这些同一种类型(type)的东西都叫电脑,这可以称为“电脑类”。类就是某种东西的类型。可以将类,看成是对象的蓝图。电脑类这个蓝图应该描述电脑的基本构成组件:显示器、键盘、鼠标、CPU、内存、主板、光驱等等,电脑类这个蓝图也应该描述它应该具备的基本功能:开机、关机、打字等等。换句话来说,类应该描述该类的对象所应包含的状态(state)信息和它们将能支持的行为(behavior)。

 

在面向对象的世界里,状态和行为结合成类的定义。这个定义用于作为创建对象的蓝图。电脑生产商根据电脑的规格描述生产电脑。也就是根据类创建对象。类的状态信息,也称为属性(attribute),比如:显示器、键盘、鼠标、CPU等等;类的行为称为操作(operation),比如:开机、关机、打字等等。

 

属性与操作,是描述一个类的最重要的内容在JAVA中,操作也称为方法(method)

 

那么我们需要定义一个什么样的类,或如何定义一个类呢?换句话来讲,就是我要定义的类,应该包括哪些属性和操作呢?这将涉及到一个概念:抽象

 

抽象

我们把抽象看成一个动词!所谓抽象,就是把现实世界中的事物用类来根据语境和需要进行描述。比如我的电脑CPU风扇运转不正常了,你的电脑光驱弹出有问题,这些内容,在电脑这个类中是否需要描述呢?对于生产电脑的系统来说,它可以不关心这些无需关心的细节;但是对于修理电脑的系统来说,这是必不可少的细节!这就是语境。

 

再比如说:我现在要开发一个学生信息管理系统,入学的时候,要把学生的信息录入,这时候,我关心学生的姓名、性别、专业、班级、民族、学生卡号等等,那么至于学生头发的颜色、穿什么衣服、有多少钱等等这些我们就不关心了。这就是关注该关注的,而忽略不重要的,无需关注的信息。

 

对现实进行抽象,这是我们在用面向对象的思维去开发软件的时候,要面对的一项真正的挑战!可以说,我们后面要开发的每一个项目,你都要进行相应的思维训练!训练你的抽象能力。

 

思维训练:

我手上拿着一根香烟:这根香烟就是“香烟”这个类的一个对象

一个班上有45个学生:那45个学生就是“学生”这个类的45个对象

大街上跑着3辆汽车:那3辆汽车就是“汽车”这个类的3个对象

 

继承

下面我们来学习面向对象三大特征(继承、封装、多态)之一的“继承”。世间万物皆对象,我们需要对这些对象进行归类,在归类过程中,我们发现有很多类型之间具有相同的特征。比如你家里养了一只猫,你家的那只猫可以称为“猫”这个类的一个对象;我家养了一条狗,我家的这条狗是“狗”这个类的一个对象;他家养了一只鸟,他家的这只鸟是“鸟”这个类的一个对象……那么,我们发现,猫、狗、鸟,都可以用同一个名称“动物”来称呼它!换句话来说,猫、狗、鸟都是一种动物!用面向对象的话来说,猫、狗、鸟这三个类“继承”了“动物”这个类。“动物”在这个关系中称为“父类/超类(super class)/基类”;猫、狗、鸟这三个类称为“子类(sub class)”。

 

在比如,上面说的电脑,台式机和笔记本都是电脑,笔记本如果按照厂商来分又可分IBM笔记本、HP笔记本、戴尔笔记本等等,当然,对于台式机也一样可以按照这种方式来划分,要不要这样划分,要看实际需要的情况。所以,IBM笔记本、戴尔笔记本是笔记本这个父类的子类,笔记本、台式机又是电脑这个父类的子类。

 

继承这个概念的目的之一是为了减少类定义的重复。电脑有显示器、键盘、鼠标、CPU、硬盘、内存这些属性和开机、关机、打字这些操作;那么,如果笔记本和台式机是电脑的子类,我们在定义笔记本和台式机的时候,就无需重复定义这些属性和操作了。

 

头脑风暴-思维训练:

 

请举出更多现实生活中存在继承关系的例子!

 

封装

封装(encapsulation)也是面向对象三大特征之一。这是面向对象中最重要的内容。对于台式机来讲,用一个机箱将主板、CPU、硬盘、内存这些零件包装(封装)起来,然后将鼠标、键盘、显示器这些东西开放出来让外界的人去操作。这就是关于封装的概念。电脑把一些重要的组件封装起来,避免外界的人对这些组件误操作而导致电脑异常(比如不小心把硬盘弄掉了);同时,电脑也需要暴露一些接口给外界操作。通过封装/隐藏重要的细节,同时暴露给外界一些操作的接口,外界的事物便能够和谐地和这些对象共处!

 

那么,接下来的问题就是:我们如何决定一个类该隐藏哪些属性或操作,该暴露哪些属性和操作呢?这些问题,就是面向对象的分析和设计中该回答的问题!需要根据具体情况来决定。我们后面的很多项目,也是要训练大家的这种思维的!

 

可见性

还是拿电脑来举例。比如我现在要用电脑的光驱来读一些光盘。我们通过电脑暴露给我们的接口(一个按键),我们按一下光驱的弹出按键,电脑内部就进行了一系列的动作,驱动电机的运转,把光驱弹出。这个弹出的过程,我根本不关心它是怎么弹出的,也不想关心它。所以,电脑就不应该将这个弹出的细节暴露出来!如果暴露出来了,可能会导致更大的问题,比如手动转动一个电机,不小心用力过猛把它弄坏了!如果把细节暴露出来,也导致了我们使用电脑的复杂度!电机本身(电脑的属性)以及驱动电机运行(电脑的操作)这些东西对外界隐藏起来了,称为私有(private)属性和私有操作。电脑提供了一个我们可以执行的“弹出光驱”操作,这个操作称为公有(public)操作。当然,比如键盘、显示器这些电脑的部件也暴露给我们了,可以称它们为公有(public)属性。

 

private、public就是可见性。可见性还包括protected和package可见性。只有电脑自身可以对象硬盘、内存、光驱这些private部件进行操作,也就是对电脑自身可见。换句话来说,具有private可见性的属性和操作,只有在类内部才可以访问。而具有public可见性的属性和操作,在任何地方(也就是其它任何对象)都可以访问在类中声明为protected的元素,它的可见性程度介于private和public之间,可以被类内部的其它方法或类的所有子类(包括子类的子类)的方法访问。如果在一个类中声明一个元素为package可见性,那么它能够被类自身和这个类所在的包(package)中的其它类访问

 

现在我们来看看如何创建一个类,并定义它的属性(在类的内部,方法的外部定义的变量)和方法:

public class Computer {

   

    //私有属性

    private String harddisk; //硬盘

   

    //私有属性

    private String memory; //内存

   

    //公有属性

    public String display; //显示器

   

    //公有属性

    public String keyboard; //键盘

   

    //公有属性

    public String mouse; //鼠标

   

    //公有方法:弹出光驱

    public void popupCDRom(){

       //do something

    }

   

    //公有方法:弹入光驱

    public String injectCDRom(String cd){

      

       //在类的内部,才可以访问类内部的私有方法

       String content = readCDContent(cd);

       return content;

    }

   

    //私有方法:读取光盘的内容

    private String readCDContent(String cd){

       //do something else

       return "光盘"+cd+"上的内容:Hello World!";

    }

}

测试它的可见性:

public class ComputerTest {

   

    public static void main(String[] args){

       Computer computer1 = new Computer();

      

       //在ComputerTest这个类中,可以访问Computer中的public属性

       computer1.display = "液晶显示器";

      

       //在ComputerTest这个类中,可以访问Computer中的public方法

       computer1.popupCDRom();

       String content = computer1.injectCDRom("《2012》");

       System.out.println(content);

      

       //下面这行代码将导致编译错误

       computer1.memory = "521M内存";

      

       //下面这行代码将导致编译错误

       computer1.readCDContent("《2012》");

      

       //只有把ComputerTest这个类和Computer类放在一个package下面,才可以访问

       computer1.otherProperties = "其它属性";

    }

}

 

public class Person {

    private String name;

    protected int age;

    String address;

}

 

public class Teacher extends Person {

    public void myNameIs(){

       //编译错误

       System.out.println(name);

    }

    public void myAgeIs(){

       //age是protected,可以访问

       System.out.println(age);

    }

}

 

public class FemaleTeacher extends Teacher{

    public void myAge(){

      

       //age是protected,可以访问

       System.out.println(age);

    }

}

上面的name/address/age,是定义在类的内部,方法体的外部的属性,称为成员变量,也称为实例变量(instance variable)或对象的状态(state)或字段(field)。

 

如何调用一个方法?参数必须匹配

 

思考:如果在一个类中声明一个元素为package可见性,那么它是否能够被这个类的子类访问?

 

其它术语

l           一个类的对象一般也称为类的实例(instance)

l           类的属性(attribute)和操作(方法,method)也称为类的成员

l           总结起来,定义在类内部,方法外部的属性,可称为instance variable(实例变量)或attribute(属性或特性)或member variable(成员变量)或field(字段),实例变量和成员变量这两个术语比较常用。

l           当说到属性这个词的时候,要特别注意:我们通常混淆了attribute和property之间的区别,如果属性指的是property,那么它的意思是由getters/setters方法所确定的property。在类内部直接定义实例变量/成员变量/字段称为attribute(请参考下面说的普遍被接受的基本原则)

l           为什么使用get/set?主要的目的还是为了封装(隐藏具体的实现细节),举例:

public class MyDateNoWrap {

    public int year;

    public int month;

    public int day;

}

public class MyDateNoWrapClient {

 

    /**

     * @param args

     */

    public static void main(String[] args) {

       MyDateNoWrap mdnw = new MyDateNoWrap();

      

       //把内部结构暴露给了外界,在赋值时,数据无法得到有效控制

        mdnw.year = 2009;

       mdnw.month = 15; //?? 导致类内部的数据不受控制!

       mdnw.day = 1;

      

       //把内部结构暴露给了外界,在取值时,给客户端制造麻烦

       //客户端必需知道这个结构,才能得到正确的值

       String theday = mdnw.year + "-" + mdnw.month + "-" + mdnw.day;

       System.out.println(theday);

    }

 

}

 

package cn.com.leadfar;

 

public class MyDate {

    private int year;

    private int month;

    private int day;

   

    public String getTheDay(){

       return year + "-" + month + "-" + day;

    }

   

    public int getYear() {

       return year;

    }

    public void setYear(int year) {

       this.year = year;

    }

    public int getMonth() {

       return month;

    }

    public void setMonth(int month) {

      

       if(month < 1 || month > 12){

           throw new RuntimeException("您设置的月份数据有误!");

       }

      

       this.month = month;

    }

    public int getDay() {

       return day;

    }

    public void setDay(int day) {

      

       if(month <1 || month > 31){

           throw new RuntimeException("您设置的日期数据有误!");

       }

      

       this.day = day;

    }

}

 

package cn.com.leadfar;

 

public class MyDateClient {

    public static void main(String[] args) {

       MyDate md = new MyDate();

       md.setYear(2009);

       md.setMonth(12);

       md.setDay(20);

      

       System.out.println(md.getTheDay());

    }

}

 

 

 

 

普遍被接受的基本原则

1、  属性应该被定义为private,除非在类继承结构中你需要把一个属性开放给你的子类使用(这时使用protected)。极少极少的情况,才需要定义为public。

2、  如果要将一个类内部的私有属性暴露给外界使用,通常不是将其定义为public,而是在类中针对这个属性创建get和set方法。get和set方法后面紧跟属性的名称,且第一个字母大写。

public class Person {

    private String name;

    private int age;

    private String address;

   

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

    public int getAge() {

       return age;

    }

    public void setAge(int age) {

       this.age = age;

    }

    public String getAddress() {

       return address;

    }

    public void setAddress(String address) {

       this.address = address;

    }

}

上述类定义,定义了name/age/address这三个实例变量,同时,这个类,也定义了name/age/address这三个property

3、如果在一个类中,定义了get或set方法,那么通常称它为属性(property),这区别于类中的成员变量的名称。

public class Person {

    private String _name;

    private int _age;

    private String _address;

   

    public String getName() {

       return _name;

    }

    public void setName(String name) {

       this._name = name;

    }

    public int getAge() {

       return _age;

    }

    public void setAge(int age) {

       this._age = age;

    }

    public String getAddress() {

       return _address;

    }

    public void setAddress(String address) {

       this._address = address;

    }

}

比如上面这段代码,_name、_age、_address称为Person这个类的成员变量(或实例变量或字段或attribute),而name、age、address称为Person这个类的property。大家看:

getName()/setName这两个方法,以get/set开头,紧跟Name这个名称,N大写,这是习惯写法,而且大家也应该这样写(N这个字母不要写小写!),这种写法,就确定了名为name(注意是n而不是N)的property。

这些get/set方法,称为getters/setters。

 

如果一个property有get方法,称这个property是readable(可读的)的

如果一个property有set方法,称这个property是writable或modifiable(可写的)的

如果一个property只有get方法,称这个propety是readOnly(只读)

如果一个property只有set方法,称这个property是writeOnly(只写)