# 第四章 初始面向对象
# 面向对象编程基础
学习面向对象内容的三条主线
Java类及类的成员:(重点)属性、方法、构造器;(熟悉)代码块、内部类
面向对象的特征:封装、继承、多态、(抽象)
其他关键字的使用:this、super、package、import、static、final、interface、abstract等
# 1. 面向对象编程概述(了解)
# 1.1 程序设计的思路
面向对象,是软件开发中的一类编程风格、开发范式。除了面向对象
,还有面向过程
、指令式编程
和函数式编程
。在所有的编程范式中,我们接触最多的还是面向过程和面向对象两种。
类比:史书类型 - 纪传体:以人物传记为中心,“本纪”叙述帝王,“世家”记叙王侯封国和特殊人物,“列传”记叙民间人物。 - 编年体:按年、月、日顺序编写。 - 国别体:是一部分国记事的历史散文,分载多国历史。
1
2
3
4
早期先有面向过程思想,随着软件规模的扩大,问题复杂性的提高,面向过程的弊端
越来越明显,出现了面向对象思想并成为目前主流的方式。
1. 面向过程的程序设计思想(Process-Oriented Programming),简称POP
- 关注的焦点是
过程
:过程就是操作数据的步骤。如果某个过程的实现代码重复出现,那么就可以把这个过程抽取为一个函数
。这样就可以大大简化冗余代码,便于维护。 - 典型的语言:C语言
- 代码结构:以
函数
为组织单位。 - 是一种“
执行者思维
”,适合解决简单问题。扩展能力差、后期维护难度较大。
2. 面向对象的程序设计思想( Object Oriented Programming),简称OOP
- 关注的焦点是
类
:在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,用类来表示。 - 典型的语言:Java、C#、C++、Python、Ruby和PHP等
- 代码结构:以
类
为组织单位。每种事物都具备自己的属性
和行为/功能
。 - 是一种“
设计者思维
”,适合解决复杂问题。代码扩展性强、可维护性高。
# 1.2 由实际问题考虑如何设计程序
思考1:如何开车?
面向过程思想思考问题时,我们首先思考“怎么按步骤实现?
”并将步骤对应成方法,一步一步,最终完成。 这个适合简单任务
,不需要过多协作
的情况。针对如何开车,可以列出步骤:
面向过程适合简单、不需要协作的事务,重点关注如何执行。
思考2:如何造车?
造车太复杂,需要很多协作
才能完成。此时我们思考的是“车怎么设计?
”,而不是“怎么按特定步骤造车的问题”。这就是思维方式的转变,前者就是面向对象思想。所以,面向对象(Oriented-Object)思想更契合人的思维模式。
用面向对象思想思考“如何设计车”:
自然地,我们就会从“车由什么组成”开始思考。发现,车由如下结构组成:
我们找轮胎厂完成制造轮胎的步骤,发动机厂完成制造发动机的步骤,...;这样,大家可以同时进行车的制造,最终进行组装,大大提高了效率。但是,具体到轮胎厂的一个流水线操作,仍然是有步骤的,还是离不开面向过程思维!
因此,面向对象可以帮助我们从宏观上把握、从整体上分析整个系统。 但是,具体到实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去处理。
注意:
我们千万不要把面向过程和面向对象对立起来。他们是相辅相成的。面向对象离不开面向过程!
类比举例2:人把大象装进冰箱
面向过程
1.打开冰箱 2.把大象装进冰箱 3.把冰箱门关住
1
2
3
4
5
6面向对象
人{ 打开(冰箱){ 冰箱.开门(); } 操作(大象){ 大象.进入(冰箱); } 关闭(冰箱){ 冰箱.关门(); } } 冰箱{ 开门(){ } 关门(){ } } 大象{ 进入(冰箱){ } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
**练习:**抽象出下面系统中的“类”及其关系
# 1.3 如何掌握这种思想?
# 2. Java语言的基本元素:类和对象
# 2.1 引入
人认识世界,其实就是面向对象的。比如,我们认识一下美人鱼(都没见过)
经过“仔细学习”,发现美人鱼通常具备一些特征:
- 女孩
- 有鱼尾
- 美丽
这个总结的过程,其实是抽象化
的过程。抽象出来的美人鱼的特征,可以归纳为一个美人鱼类
。而图片中的都是这个类呈现出来的具体的对象
。
# 2.2 类和对象概述
类(Class)
和对象(Object)
是面向对象的核心概念。
1、什么是类
类:具有相同特征的事物的抽象描述,是抽象的
、概念上的定义。
2、什么是对象
对象:实际存在的该类事物的每个个体
,是具体的
,因而也称为实例(instance)
。
可以理解为:类 => 抽象概念的人
;对象 => 实实在在的某个人
# 2.3 类的成员概述
面向对象程序设计的重点是
类的设计
类的设计,其实就是
类的成员的设计
现实世界的生物体,大到鲸鱼,小到蚂蚁,都是由最基本的细胞
构成的。同理,Java代码世界是由诸多个不同功能的类
构成的。
现实生物世界中的细胞又是由什么构成的呢?细胞核、细胞质、…
Java中用类class来描述事物也是如此。类,是一组相关属性
和行为
的集合,这也是类最基本的两个成员。
- 属性:该类事物的状态信息。对应类中的
成员变量
- 成员变量 <=> 属性 <=> Field
- 行为:该类事物要做什么操作,或者基于事物的状态能做什么。对应类中的
成员方法
- (成员)方法 <=> 函数 <=> Method
举例:
# 2.4 面向对象完成功能的三步骤(重要)
# 步骤1:类的定义
类的定义使用关键字:class。格式如下:
[修饰符] class 类名{
属性声明;
方法声明;
}
2
3
4
举例1:
public class Person{
//声明属性age
int age ;
//声明方法showAge()
public void eat() {
System.out.println("人吃饭");
}
}
2
3
4
5
6
7
8
9
举例2:
public class Dog{
//声明属性
String type; //种类
String nickName; //昵称
String hostName; //主人名称
//声明方法
public void eat(){ //吃东西
System.out.println("狗狗进食");
}
}
2
3
4
5
6
7
8
9
10
11
public class Person{
String name;
char gender;
Dog dog;
//喂宠物
public void feed(){
dog.eat();
}
}
2
3
4
5
6
7
8
9
10
# 步骤2:对象的创建
创建对象,使用关键字:new
创建对象语法:
//方式1:给创建的对象命名
//把创建的对象用一个引用数据类型的变量保存起来,这样就可以反复使用这个对象了
类名 对象名 = new 类名();
//方式2:
new 类名()//也称为匿名对象
2
3
4
5
6
- 举例
class PersonTest{
public static void main(String[] args){
//创建Person类的对象
Person per = new Person();
//创建Dog类的对象
Dog dog = new Dog();
}
}
2
3
4
5
6
7
8
# 步骤3:对象调用属性或方法
- 对象是类的一个实例,必然具备该类事物的属性和行为(即方法)。
- 使用"
对象名.属性
" 或 "对象名.方法
"的方式访问对象成员(包括属性和方法)
举例1:
//声明Animal类
public class Animal { //动物类
public int legs;
public void eat() {
System.out.println("Eating.");
}
public void move() {
System.out.println("Move.");
}
}
2
3
4
5
6
7
8
9
10
11
12
//声明测试类
public class AnimalTest {
public static void main(String args[]) {
//创建对象
Animal xb = new Animal();
xb.legs = 4;//访问属性
System.out.println(xb.legs);
xb.eat();//访问方法
xb.move();//访问方法
}
}
2
3
4
5
6
7
8
9
10
11
举例2:针对前面步骤1的举例2:类的实例化(创建类的对象)
public class Game{
public static void main(String[] args){
Person p = new Person();
//通过Person对象调用属性
p.name = "康师傅";
p.gender = '男';
p.dog = new Dog(); //给Person对象的dog属性赋值
//给Person对象的dog属性的type、nickname属性赋值
p.dog.type = "柯基犬";
p.dog.nickName = "小白";
//通过Person对象调用方法
p.feed();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2.5 匿名对象 (anonymous object)
我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。
- 如:new Person().shout();
使用情况
- 如果一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
- 我们经常将匿名对象作为实参传递给一个方法调用。
# 3. 类的成员之一:成员变量(field)
# 3.1 如何声明成员变量
- 语法格式:
[修饰符1] class 类名{
[修饰符2] 数据类型 成员变量名 [= 初始化值];
}
2
3
说明:
- 位置要求:必须在类中,方法外
- 修饰符2(暂不考虑)
- 常用的权限修饰符有:private、缺省、protected、public
- 其他修饰符:static、final
- 数据类型
- 任何基本数据类型(如int、Boolean) 或 任何引用数据类型。
- 成员变量名
- 属于标识符,符合命名规则和规范即可。
- 初始化值
- 根据情况,可以显式赋值;也可以不赋值,使用默认值
实例:
public class Person{
private int age; //声明private变量 age
public String name; //声明public变量 name
}
2
3
4
# 3.2 成员变量 vs 局部变量
1、变量的分类:成员变量与局部变量
- 在方法体外,类体内声明的变量称为成员变量。
- 在方法体内部等位置声明的变量称为局部变量。
其中,static可以将成员变量分为两大类,静态变量和非静态变量。其中静态变量又称为类变量,非静态变量又称为实例变量或者属性。接下来先学习实例变量。
2、成员变量 与 局部变量 的对比
相同点
- 变量声明的格式相同: 数据类型 变量名 = 初始化值
变量必须先声明、后初始化、再使用。
- 变量都有其对应的作用域。只在其作用域内是有效的
不同点
1、声明位置和方式 (1)成员变量:在类中方法外 (2)局部变量:在方法体{}中或方法的形参列表、代码块中
2、作用域 (1)成员变量:通过对象就可以使用,本类中直接调用,其他类中“对象.实例变量” (2)局部变量:出了作用域就不能使用
3、对象属性的默认初始化赋值
当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。
案例1:
云课第四章4.6输出图书相关信息
案例2:
声明员工类Employee,包含属性:编号id、姓名name、年龄age、薪资salary
声明EmployeeTest测试类,并在main方法中,创建2个员工对象,并为属性赋值,打印两个员工的信息
案例3:
(1)声明一个MyDate类,有属性:年year,月month,日day
(2)声明一个Employee类,包含属性:编码、姓名、年龄、薪资、生日(数据类型是MyDate)
(3)在EmployeeTest类中,创建两个对象,并为他们的姓名和生日赋值,并显示
# 4. 类的成员之二:方法(method)
# 4.1方法的引入
《街霸》游戏中,每次人物出拳、出脚或跳跃等动作都需要编写50-80行的代码,在每次出拳、出脚或跳跃的地方都需要重复地编写这50-80行代码,这样程序会变得很臃肿
,可读性也非常差。为了解决代码重复编写的问题,可以将出拳、出脚或跳跃的代码提取出来放在一个{}中,并为这段代码起个名字,这样在每次的出拳、出脚或跳跃的地方通过这个名字来调用这个{}的代码就可以了。
上述过程中,所提取出来的代码可以被看作是程序中定义的一个方法,程序在需要出拳、出脚或跳跃时调用该方法即可。
# 4.2 方法(method、函数)的理解
方法
是类或对象行为特征的抽象,用来完成某个 功能操作。在某些语言中也称为函数
或过程
。- 方法用来封装某一个功能
- 将功能封装为方法的目的是,可以
实现代码重用,减少冗余,简化代码
- Java里的方法
不能独立存在
,所有的方法必须定义在类里。 - 举例1:
- Math.random()的random()方法
- Math.sqrt(x)的sqrt(x)方法
- System.out.println(x)的println(x)方法
- new Scanner(System.in).nextInt()的nextInt()方法
- Arrays类中的binarySearch()方法、sort()方法、equals()方法
- 举例2:
public class Person{
private int age;
public int getAge() { //声明方法getAge()
return age;
}
public void setAge(int i) { //声明方法setAge
age = i; //将参数i的值赋给类的成员变量age
}
}
2
3
4
5
6
7
8
9
# 4.3 如何声明方法
1、声明方法的语法格式
[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表]{
方法体的功能代码
}
2
3
(1)一个完整的方法 = 方法头 + 方法体。
- 方法头就是
[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表]
,也称为方法签名
。通常调用方法时只需要关注方法头就可以,从方法头可以看出这个方法的功能和调用格式。 - 方法体就是方法被调用后要执行的代码。对于调用者来说,不了解方法体如何实现的,并不影响方法的使用。
(2)方法头可能包含5个部分
修饰符:可选的。方法的修饰符也有很多,例如:public、protected、private、static、abstract、native、final、synchronized等,后面会一一学习。
- 其中,权限修饰符有public、protected、private。在讲封装性之前,我们先默认使用pulbic修饰方法。
- 其中,根据是否有static,可以将方法分为静态方法和非静态方法。其中静态方法又称为类方法,非静态方法又称为实例方法。咱们在讲static前先学习实例方法。
返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者。
- 无返回值,则声明:void
- 有返回值,则声明出返回值类型(可以是任意类型)。与方法体中“
return 返回值
”搭配使用
方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意”
形参列表:表示完成方法体功能时需要外部提供的数据列表。可以包含零个,一个或多个参数。
- 无论是否有参数,()不能省略
- 如果有参数,每一个参数都要指定数据类型和参数名,多个参数之间使用逗号分隔,例如:
- 一个参数: (数据类型 参数名)
- 二个参数: (数据类型1 参数1, 数据类型2 参数2)
- 参数的类型可以是基本数据类型、引用数据类型
throws 异常列表:之后章节再讲
(3)方法体:方法体必须有{}括起来,在{}中编写完成方法功能的代码
(4)关于方法体中return语句的说明:
return语句的作用是结束方法的执行,并将方法的结果返回去
如果返回值类型不是void,方法体中必须保证一定有 return 返回值; 语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。
如果返回值类型为void时,方法体中可以没有return语句,如果要用return语句提前结束方法的执行,那么return后面不能跟返回值,直接写return ; 就可以。
return语句后面就不能再写其他代码了,否则会报错:Unreachable code
补充:方法的分类:按照是否有形参及返回值
2、代码示例:
/**
* 方法定义案例演示
*/
public class MethodDefineDemo {
/**
* 无参无返回值方法的演示
*/
public void sayHello(){
System.out.println("hello");
}
/**
* 有参无返回值方法的演示
* @param length int 第一个参数,表示矩形的长
* @param width int 第二个参数,表示矩形的宽
* @param sign char 第三个参数,表示填充矩形图形的符号
*/
public void printRectangle(int length, int width, char sign){
for (int i = 1; i <= length ; i++) {
for(int j=1; j <= width; j++){
System.out.print(sign);
}
System.out.println();
}
}
/**
* 无参有返回值方法的演示
* @return
*/
public int getIntBetweenOneToHundred(){
return (int)(Math.random()*100+1);
}
/**
* 有参有返回值方法的演示
* @param a int 第一个参数,要比较大小的整数之一
* @param b int 第二个参数,要比较大小的整数之二
* @return int 比较大小的两个整数中较大者的值
*/
public int max(int a, int b){
return a > b ? a : b;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 4.4 如何调用实例方法
方法通过方法名被调用,且只有被调用才会执行。
1、方法调用语法格式
对象.方法名([实参列表])
2、示例
举例1:
/**
* 方法调用案例演示
*/
public class MethodInvokeDemo {
public static void main(String[] args) {
//创建对象
MethodDefineDemo md = new MethodDefineDemo();
System.out.println("-----------------------方法调用演示-------------------------");
//调用MethodDefineDemo类中无参无返回值的方法sayHello
md.sayHello();
md.sayHello();
md.sayHello();
//调用一次,执行一次,不调用不执行
System.out.println("------------------------------------------------");
//调用MethodDefineDemo类中有参无返回值的方法printRectangle
md.printRectangle(5,10,'@');
System.out.println("------------------------------------------------");
//调用MethodDefineDemo类中无参有返回值的方法getIntBetweenOneToHundred
md.getIntBetweenOneToHundred();//语法没问题,就是结果丢失
int num = md.getIntBetweenOneToHundred();
System.out.println("num = " + num);
System.out.println(md.getIntBetweenOneToHundred());
//上面的代码调用了getIntBetweenOneToHundred三次,这个方法执行了三次
System.out.println("------------------------------------------------");
//调用MethodDefineDemo类中有参有返回值的方法max
md.max(3,6);//语法没问题,就是结果丢失
int bigger = md.max(5,6);
System.out.println("bigger = " + bigger);
System.out.println("8,3中较大者是:" + md.max(8,9));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
举例2:
//1、创建Scanner的对象
Scanner input = new Scanner(System.in);//System.in默认代表键盘输入
//2、提示输入xx
System.out.print("请输入一个整数:"); //对象.非静态方法(实参列表)
//3、接收输入内容
int num = input.nextInt(); //对象.非静态方法()
2
3
4
5
6
7
8
# 4.5 使用的注意点
(1)必须先声明后使用,且方法必须定义在类的内部
(2)调用一次就执行一次,不调用不执行。
(3)方法中可以调用类中的方法或属性,不可以在方法内部定义方法。
正确示例:
类{
方法1(){
}
方法2(){
}
}
2
3
4
5
6
7
8
错误示例:
类{
方法1(){
方法2(){ //位置错误
}
}
}
2
3
4
5
6
7
# 4.6 关键字return的使用
- return在方法中的作用:
- 作用1:结束一个方法
- 作用2:结束一个方法的同时,可以返回数据给方法的调用者
- 注意点:在return关键字的直接后面不能声明执行语句
案例1:
(1)创建Person类的对象,设置该对象的name、age和gender属性
调用study方法,输出字符串"studying";
调用showAge()方法,返回age值
调用addAge(int addAge)方法给对象的age属性值增加addAge岁.比如:2岁
案例2:
云课4.4:简单的学生管理系统
案例3:
云课4.7:学生管理系统
# 方法的重载
练习:方法重载的练习
用已有的知识编写下面代码:
需求:
定义一个方法求两个整数的和
定义一个方法求三个整数的和
定义一个方法求四个整数的和
# 什么是方法重载
在同一个类中,定义了多个同名的方法,这些同名的方法具有多种功能
每个方法具有不同的参数类型或者参数个数,这些同名的方法,就构成了重载关系
简单一点来说
:方法名相同,参数列表不同。与返回值无关
参数不同:个数不同、类型不同、顺序不同
Java虚拟机会通过参数的不同来区分同名的方法
**案例1:**区分以下方法是否构成重载
案例2:根据类型比较
需求:使用方法重载的思想,设计比较两个整数是否相同的方法
要求:兼容全整数(byte short,int ,long)