参考教材:《Java核心技术 卷一》
前言
第一篇教程里,我们已经学习了java的基本程序结构和基础语法,下面我们就来学习java面向对象(OOP)的语法。当然最重要的理解面对对象的编思思想,以便读者将来学习其他面对对象的语言时能触类旁通。
面向对象的具体概念及和面向过程的区别网上有很多,在这里就不赘述了(毕竟笔者也总结不好)。另外笔者觉得理解一个由几个深奥名词拼接起来的概念对于初学者来说可能会难以理解,甚至可能起到适得其反的效果。所以笔者建议:在学习java的过程中,自己感受和对比下面向对象和面向过程编程的不同,然后试着自己去总结,最后再比对网上的总结查漏补缺。
类和对象
一句话,类(class)是构造对象的模板和蓝图。举个例子,我们可以把类想象“切割动物小饼干的切割机”,将对象想象成“动物小饼干”。我们把“用切割机制造出小饼干的过程”也就是“由类构造对象的过程”称为创建类的实例。
类和对象教程:类和对象
一定要清楚对象的三个主要特征:(以一只宠物狗为例)
对象的行为(behavior):可以对对象施加哪些操作(方法)? (查看狗的种类 ,改变狗名字)
对象的状态(state):当施加某个方法时,对象如何响应?(如调用dog.getName时所返回的信息,就是这个对象的一种状 态);总结起来就是:对象的状态就是每个对象所保存着描述该对象当前特征的信息。
对象的标识(identity):如何辨别具有相同行为与状态的不同对象?(两只种类,毛发,习性等一模一样的宠物狗怎么区分它们的不同)。
补充:关于构造方法的一些理解:
1. 构造方法也叫构造器(constructor),作用是用来构造并初始化对象的状态。
2.构造方法是一种特殊的方法,但没有返回值(不能用void修饰该方法),它与类名必须保持相同,如Date类的构造方法名为Date。
3.如果没有在类中显式的构造方法,类在编译的过程中,编译器会自动在类中添加默认的一个无参的构造方法,用于对象的初始化。你也可以在类中添加一个或多个构造函数,用于不同情况下对象的初始化。但多个构造函数的签名不能一致(参数的类型和个数不能完全一样),此时,编译器将不会自动添加构造方法。
public class Pet {
String name;
String color;
int age;
// 四个不同的构造函数
public Pet(String aName, String aColor, int aAge) {
name = aName;
color = aColor;
age = aAge;
}
public Pet(String aName, int aAge) {
name = aName;
color = "white";
age = aAge;
}
public Pet(String aName, String aColor) {
name = aName;
color = aColor;
age = 2;
}
public Pet() {
name = "Amy";
color = "white";
age = 1;
}
//重写toString方法以便格式化输出
public String toString() {
return "name=" + name + " color=" + color + " age=" + age;
}
public static void main(String[] args) {
// 使用不同的构造方法初始化对象,程序会根据参数的不同选择正确的构造方法
Pet pet1 = new Pet("Jack", "black", 2);
Pet pet2 = new Pet("Dam", "purple");
Pet pet3 = new Pet("John", 3);
Pet pet4 = new Pet();
// 打印查看結果,注意:如果沒有重写toString方法,直接打印对象不能打印出对象的状态。
System.out.println(pet1);
System.out.println(pet2);
System.out.println(pet3);
System.out.println(pet4);
}
}
运行结果如下:
name=Jack color=black age=2
name=Dam color=purple age=2
name=John color=white age=3
name=Amy color=white age=1
方法的重载:
在函数里添加多个构造函数实际上是对构造方法的重载.
教程:方法重载
注意:
1. java允许重载任何方法,而不只是构造方法。
2. 方法的签名:方法名及参数类型。 值得注意的是方法的返回类型不是方法签名的的一部分,即,不能有两个名字形同、参数类型也相同,返回类型却不同的方法。
怎样识别类
在写面向过程的程序时,如C语言,会从顶部的main函数开始写。但在面向对象的程序设计里,是没有“顶部”这个说法的,那么从哪里开始写一个java程序?答案是:从设计类开始。那么怎么识别类就很重要了。
比较简单好用的的方法是:名词作为类,动词作为类的方法。
先来思考一个问题,假如要写一个订单处理系统,运用面向对象的方法要怎么写?
我们先来找找订单处理系统中可能涉及的名词,看哪些可以作为类:
商品
订单
用户
送货地址
.....
这些名词很可以设计成单独的一个类,如Item类(商品类),Order类(订单类)、User类(用户类)等。
然后我们再来找找看可能涉及的动词:
添加商品到订单中,发送订单或取消订单。
上面提到的动词,如“添加”、“取消”、“发送”等,在设计过程中,应该要明确这些动作是由什么对象完成的。
例如:“添加商品到订单”,那么在Order类(订单类)中,应该有一个add方法,方法参数为Item对象,从而达到添加商品到订单的目的。
public class Order{
...
public void add(Item item){ //方法参数为Item对象
...
// 该方法实现将商品添加到订单里
}
...
}
类似的,我们根据动词可以往相应的类里添加相应的方法了。当然,这种所谓的“找名词找动词”只是一种经验,在创建类的过程中,哪些名词和动词重要主要还是靠个人的开发经验。
另外,从这个例子里,我们也可以感受到面向对象设计和面向过程设计的差异,读者可以慢慢比对两者的不同点。
包以及类的导入
教程:包以及import关键字
如果没有在源文件的开头没有package语句(即没有注明该源文件所属的包),那么这个源文件中的类就被放置在一个默认包(default package)中,默认包是一个没有名字的包。
简单介绍在设计类的几点小技巧:
1.一定要保证数据私有
即,在我们设计类的时候,声明的变量要要用private修饰,保证数据的私有性。这样可以保证类的封装性不被破坏。
2.一定要对数据初始化
Java不对局部变量进行初始化,但会对对象的实例域进行初始化,最好不要依赖于系统的默认值,而是应该显式的初始化所有的数据,具体的初始化方式可以使提供默认值,也可以是在所有构造器中设置默认值。
3.不要在类中使用过多的基本类型
就是说,用其他的类代替多个相关的的基本类型的使用,这样使得类更加容易理解和修改。
例如,有一个person类有以下表示地址的实例域
private String city;
private String street;
private String state;
private int zip;
比较好的处理方式是,新建一个包含以上实例域的Address的类,那么person类实例域在声明的时候只要一行
public class person{
Address address;
...
}
就可以达到同样的效果,且当需要修改地址的显示方法的时候,只需要修改Address类就可以了,而无需修改person类
修饰符
教程:java 修饰符
这这篇教程里,还涉及到父类和子类的概念,那么我们现在就来讲java的三大特性。
java 封装 继承 多态
上面的java修饰符,我们学到了private修饰符,java三大特性中的封装,和这个private修饰符息息相关。
封装教程: 封装
以后,在我们设计类的过程中,要尽量降低变量的访问权限,尽量不要将变量设置为public,这样会破坏类的封装性,且如果这个变量的值域被破坏(或者不正常),破坏的原因可以出现在任何地方,因为public让变量随意访问。
继承教程:继承
这篇教程很详细的讲述了继承的概念和使用方法。那我们如何判断两个类是否应该设计为继承关系呢?有一个简单的规则:“is-a”规则.它表明子类的每个对象也是父类的对象。如student is a person。每个学生都是人,所以person应该设计为student的父类。
这里,笔者补充关于方法重写的概念。
方法重写:
子类在继承父类的过程中,会继承父类的方法,但是父类的方法不一定适合子类,这时候我们就要重写一个专属于子类的方法,来覆盖父类的方法,这个过程就叫方法的重写。(注意和之前讲的方法的重载区分)。
关于方法重写的特性和注意点百度百科总结得很好,引用如下:
还有一点需要注意的,在子类中可以增加域,增加方法,重写父类的方法,但是,绝对不能删除继承自父类的任何域和方法。
多态教程:多态
我们可以用一种简单的思路来理解多态:
上面,我们讲了一种“is-a”规则。“Student is a Person”。所以,可以将一个子类Student的对象赋给父类变量(这就是多态),这是很容易理解的:如果是学生,那么肯定是人。所以Person类变量引用student对象是完全没有问题的。但反过来就不行了,因为不是每一个人都是学生。所以子类变量不能引用父类对象(即使通过强制类型转换可以转换通过,但是使用过程中还是可能会出错,如调用子类添加的方法时)。
抽象类和抽象方法:
抽象类教程:抽象类