前言(转载请说明作者!)3.18~3.25编写
类和对象是Java语言中极为重要的一节,是Java编程中的基础。
即Java是个面向对象的一门计算机高级语言。
面向对象的程序设计正是对现实世界的模拟,以人类考虑问题的方式出发,把人类解决问题的思维方式转化为程序能够理解的过程,面向对象程序设计主要任务:
建立模拟问题领域的对象模型
观看前自问
- 面向对象思想是什么?
- 类和对象有什么关系?
- 类的结构、成员变量、成员方法、方法重载是什么?
- 怎样构造方法?
- 对象怎样创建和使用?
- 如何理解对象内存模型?
- 类如何封装?
- static/this关键字有什么作用?
- 包如何使用?
- 如何使用UML类图?
面向对象实现过程
- 面向对象分析 (Object-Oriented Analysis/OOA)
- 面向对象设计 (Object-Oriented Design/OOD)
- 面向对象编程 (Object-Oriented Programming/OOP)
面向对象三大特性
封装
封装即把内容和对内容的操作直接封装在一起,封装将数据隐藏在内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。可以一定程度上保证数据的隐私性。
对外只提供访问的方式,而不能直接访问数据和实现细节。
继承
继承是指子类可以继承父类的属性和行为,同时又可以新增自己属性和行为。
继承可以很大程度上解决代码重复的问题,代码可以复用。
多态
多态是具有表现多种形态的能力的特征。
同一实现接口使用不同的实例,而执行不同的操作。
如何理解类与对象(实例)的关系?
现实理解
把具有共同属性和行为对象称之为一类。简言之,类描述了一类对象所具有的一些共同的属性和行为。
e.g. 学生类
属性有:姓名、学号……
行为有:上课、考试……
类是对一类对象共同特征的描述,是抽象的、概念上的定义。
对象是实际存在的该类对象的每个个体,因而也成为 实例
类像是模具,对象像是用模具生产出的产品。
类为它的全部对象给出了统一的定义,描述了所有对象具有的属性和行为,相当于一个模具,而它的对象就是根据模具生产出的一个个产品。
如何理解对象?
现实理解
在现实生活中,一个人、一只动物、一栋楼……都可以看作是一个个的对象,即 世界是由对象组成的。
如何区分一个个的对象呢?
即每个对象特征不一样,这里的特征可以看成:事物的属性(静态特性)和行为(动态特征),也可说叫属性和功能。
e.g.1 一个人
属性 | 行为 |
---|---|
姓名:篮球王子 | 吃饭 |
性别:男 | 睡觉 |
年龄:23岁 | 打球 |
身高:195cm | 上厕所 |
e.g.2 一部手机
属性 | 功能 |
---|---|
品牌:小米 | 打电话 |
型号:小米10 | 发短信 |
价格:3999RMB | 玩游戏 |
SOC:晓龙865 | 拍照 |
e.g.3 下述电子宠物Alpha中的每一只电子宠物就是一个对象。
任务导引:电子宠物Alpha
内容大致实现
在本博客中,任务导引要做的内容是实现一款类似于电子宠物的一款程序。
宠物是一个个的类,可以通过创建类来对电子宠物的创建,实现其领养功能。
该程序可以给电子宠物们:
- 起名字
- 领养不同类型与品种的电子宠物(猫、猪等)
- 查看电子宠物信息,包括体力值、心情值
- 给电子宠物喂食
- 陪电子宠物玩耍
(宠物)特征
(宠物)属性
猫:昵称、品种、体力值、心情值……
猪:昵称、性别、体力值、心情值……
(宠物)行为
吃食、玩耍、自我介绍……
类
类的定义
public class 类名//类声明
{
//属性描述——使用成员变量描述
变量类型 变量1;
变量类型 变量2;
……
//行为描述——使用成员方法实现
方法1定义;
方法2定义;
……
}//类体
类名要求
首字母要大写,每个单词首写字母都要大写,见名知意。
e.g. HelloJava
案例
创建一个学生类(Student),学生类具有特征:
- 属性:姓名(name)、年龄(age)
- 行为:自我介绍(introduce())、上某课(haveClass(String course))
public class Student {
//属性
//name和age即成员变量
String name;
int age;
//行为/方法/行为
public void introduce() {
System.out.println("姓名:"+name+"\n年龄:"+age);
}
public void haveClass(String course) {
System.out.println("我正在上"+course+"课");
}
}
类体中描述属性的变量为成员变量,它是出现在类体内部,方法的外部。格式如下:
[修饰符] 数据类型 变量名 [=值];
- 修饰符是表示该变量的访问权限,当然它也不是必须说明的。
- 成员变量的数据类型可以是Java中任何一种数据类型,包括基本数据类型、引用数据类型。
成员变量的作用域在整个类体里都有效,相当于一种全局变量。 - 成员变量的名字需是合法的,首字母需要小写,如果单个单词组成,每个单词的首个字母都要大写,e.g. name、numberOfStudent,见名知意。
- 成员变量可以不显式的初始化,系统会赋给默认值,不同的数据类型,默认值也不同。
成员变量类型 | 初始值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0D |
char | 空字符 / ‘\u0000’ |
boolean | false |
引用数据类型 | null |
类的行为:方法
PS:和C语言中的函数非常类似。
类体中定义的方法被称为成员方法。
方法的定义包括:方法声明和方法体。
[修饰符] 返回类型 方法名([参数列表]){
//方法体:合法的Java语句
}
- 修饰符:public代表该方法的访问权限是公共的。
- 返回类型:方法返回类型可以是Java中任何一种数据类型,当声明方法有返回值时,必须有return语句,返回值必须与声明的类型保持一致。方法不需要返回值时,返回值必须为void。
- 方法名:方法名字命名跟变量名字一样,首字母小写,后来的单词首字母都大写。
- 参数列表:参数列表可以是零到多个,多个用逗号分开。
- 方法体:它里面可以包括局部变量定义和任何合法的Java语句,在方法体内可以对成员变量和方法体中声明的局部变量进行操作。方法体中声明的变量和方法的参数被称为局部变量。
//测试变量(计算器)
public class VariableTestCalculator {
int num1 = 10;//成员变量
int num2;//默认为0
public void add() {
int sum;
sum = num1 + num2;
System.out.println(num1 + "+" + num2 + "=" + sum);
}
public static void main(String[] args) {
VariableTestCalculator method = new VariableTestCalculator();
method.add();
}
}
输出为:
10+0=10
方法重载
Java中允许有多种同名的方法,为了区分同名方法,参数列表必须不同(个数或者是参数类型),方法的返回类型不参与比较。这叫做重载。
即 同名不同参
方法重载与多态
方法重载是多态特性的一种体现。可以通过传递不同的参数,使同名的方法产生不同的行为,即表现出多态特征。
//方法重载测试程序
public class OverloadingTest {
public void print() {
System.out.println("正在调用无参数的方法");
}
public void print(int in) {
System.out.println("正在调用有整形参数的方法");
}
public static void main(String[] args) {
OverloadingTest method = new OverloadingTest();
method.print();
method.print(1);
}
}
输出:
正在调用无参数的方法
正在调用有整形参数的方法
构造方法
构造方法的名字必须跟类名相同,没有返回类型。
[修饰符] 类名(参数列表){
//方法体
}
- 当一个类中没有编写构造方法时,系统默认提供一个无参构造方法,方法中没有语句。
- 构造方法也可重载。
- 如果在类体中定义了参数非空的构造方法,Java就不再提供默认的无参构造方法。要想使用默认的无参构造方法,必须显式的写出来。
类的小结
PS:在类体中定义了变量又单独给变量赋值,或者直接在类体中写赋值语句、输出语句、if语句、循环语句等,都是错误的。
因为这些语句既不是成员方法,也不是构造方法,更不是变量的定义,所以这些语句不能直接出现在类体中。但这些可以放入方法体内
对象
对象的创建(类的实例化)
前面提到,类和对象的关系是 模具和产品的关系,定义类的目的是用来创建具有相应属性和行为的对象。
由类生成对象的过程,也称类的实例化过程。
类是一种数据类型,只是与前面所学的基本数据类型不同,这叫做 引用数据类型。
一个类可以生成多个对象。所以 对象的创建等价于变量定义过程。
声明对象
语法格式:类名 对象名; 等价于声明变量。
e.g. Student stuOne,stuTwo;
创建对象
语法格式:对象名 = new 构造方法; 等价于变量赋值。
构造方法专门用于构造对象,只有创建对象的时候才会被调用。
e.g. stuOne = new Student();(调用无参构造方法)
e.g.2 stuTwo = new Student(“小刚”,18,“数学系”);(调用三个参数的构造方法)
上述两个例子利用了方法重载。
声明对象和创建对象可以在一条语句中完成。
e.g. Student stuOne = new Student();
对象的使用
等价于变量的使用。
对象一旦创建,对象就有了自己的属性和行为,所以对象不仅可以操作自己成员变量改变状态,而且能调用类中的方法来产生一定的行为。
对象操作自己的成员变量
体现对象的属性。
对象创建之后,对象通过点运算符"."访问操作自己的成员变量。
访问格式:对象名.成员变量; 为对象的属性赋值,可以这样完成:对象名.成员变量 = 值;
对象调用类中的成员方法
体现对象的行为。
对象创建后,对象可以通过点运算符"."调用类中的方法,从而产生一定的行为(功能)。
调用格式:对象名.成员方法; 成员方法只有被调用的时候才会被执行。
public class Student {
// 属性定义
String name;
int age;
String department;
// 无参构造方法
public Student() {
}
// 有参构造方法
public Student(String name1, int age1, String department1) {
name = name1;
age = age1;
department = department1;
}
// 自我介绍方法
public void introduce() {
System.out.println("我叫" + name + ",今年" + age + "岁,我来自" + department);
}
// 上课方法
public void haveClass(String course) {
System.out.println("我正在上" + course + "课。");
}
}
public class StudentClassTesting {
public static void main(String[] args) {
// 1.声明对象
Student stuOne;
// 2.对象初始化
stuOne = new Student();
// 3.使用对象
stuOne.name = "张三";
stuOne.age = 20;
stuOne.department = "信工学院";
stuOne.introduce();
stuOne.haveClass("程序设计");
// 声明同时初始化
Student stuTwo = new Student("李四", 21, "外语学院");
stuTwo.introduce();
stuTwo.haveClass("语文");
}
}
对象的内存模型
声明对象时的内存模型
当Student类声明stuOne对象时,系统会为对象开辟两部分内存:栈内存和堆内存。
e.g.
Student stuOne;
栈内存(引用) | 堆内存(实体) |
---|---|
stuOne | - |
- | - |
- | - |
此时的对象为一个空对象,因为对象中没有实体,引用为空。
对象使用new运算符创建对象后的内存模型
当使用new运算符调用无参的构造方法为stuOne赋值时,对象的成员变量会被分配内存,同时把内存占用地址赋给对象的引用,变量值都为默认。
stuOne.name = “小明”;stuOne.age=20;
创建多个不同的对象时的内存模型
Student stuTwo = new Student(“小刚”,18);
在声明stuTwo对象的同时为其变量开辟内存空间并赋值。stuOne和stuTwo对应的实体不同,所以为属性赋值时,不会混淆,这就是为什么使用对象属性必须用对象名做前缀。
内存模型小结
一个完整的对象包括引用与实体,实体中存放对象对应属性的值,引用中存放的是实体的地址,每个对象可以通过引用找到属于自己的实体,这就是为什么类称作引用数据类型的原因。
类的封装
Java语言中,提供了private、默认的、protected、public四个访问限制修饰符来限制用户的访问权限。
所谓的封装,就是指使用private修饰成员变量,private指私有,它修饰的成员变量只能在类体内被引用。如果外界想要访问私有属性,需要提供一些使用public修饰的共有方法,其中包括用于获取属性值的getXxx方法和设置属性值的setXxx方法。
简言之:属性私有化,方法公有化。
package com.s6enc;
public class Student {
// 属性定义
// 私有数据
private String name;
private int age;
private String department;
// setter方法
public void setName(String name1) {
name = name1;
}
public void setAge(int age1) {
if (age1 > 0 && age1 <= 100) {
age = age1;
} else {
System.out.println("年龄非法!");
}
}
public void setDepartment(String department1) {
department = department1;
}
// 取值:getter方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getDepartment() {
return department;
}
// 无参构造方法
public Student() {
}
// 有参构造方法
public Student(String name1, int age1, String department1) {
name = name1;
age = age1;
department = department1;
}
// 自我介绍方法
public void introduce() {
System.out.println("我叫" + name + ",今年" + age + "岁,我来自" + department);
}
// 上课方法
public void haveClass(String course) {
System.out.println("我正在上" + course + "课。");
}
}
//测试类
public class StudentMain {
public static void main(String[] args) {
// 1.声明对象
Student stuOne;
// 2.对象初始化
stuOne = new Student();
// 3.使用对象
// 因私有化下列赋值无效
/*
* stuOne.name = "张三";
* stuOne.age = 20;
* stuOne.department = "信工学院";
*/
stuOne.setName("张三");
stuOne.setAge(101);
stuOne.setAge(20);
stuOne.setDepartment("信工学院");
stuOne.introduce();
stuOne.haveClass("程序设计");
// 声明同时初始化
Student stuTwo = new Student("李四", 21, "外语学院");
stuTwo.introduce();
stuTwo.haveClass("语文");
}
}
static(静态)关键字
定义一个类时,是在描述某类事物的特征和行为,并没有产生具体的数据。只有通过new关键字创建该类的实例对象后,系统才会为每个对象分配空间,存储各自的数据。有时候,我们希望某些特定的数据在内存中只有一份,且能够被一个类的所有实例对象所共享。
e.g. 某个学校的所有学生共享同一个学校名,所以完全没有必要为每个学生都创建一个变量来表示学校名,可以在对象以外的内存空间中都定义一个变量来表示学校名,而可在对象以外的空间定义一个表示学校名的变量,让所有对象共享。
在一个Java类中,可以使用static关键字来修饰成员变量,该变量被称为静态变量,也叫做类变量。静态变量是一块公共内存,与类绑定,被所有实例共享。
可以使用"类名.变量名"的形式来访问。
PS:static关键字只能修饰成员变量,不能用于修饰局部变量!
public class Student {
static String schoolName;//定义静态变量
// 属性定义
// 私有数据
private String name;
private int age;
private String department;
// setter方法
public void setName(String name1) {
name = name1;
}
public void setAge(int age1) {
if (age1 > 0 && age1 <= 100) {
age = age1;
} else {
System.out.println("年龄非法!");
}
}
public void setDepartment(String department1) {
department = department1;
}
// 取值:getter方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getDepartment() {
return department;
}
// 无参构造方法
public Student() {
}
// 自我介绍方法
public void introduce() {
System.out.println("我叫" + name + ",今年" + age + "岁,我来自" + schoolName + department);
}
}
public class StudentMain {
public static void main(String[] args) {
// 1.声明对象
Student stuOne;
// 2.对象初始化
stuOne = new Student();
// 3.使用对象
Student.schoolName = "BZU";//stuOne.schoolName = "BZU";等价
stuOne.setName("张三");
stuOne.setAge(20);
stuOne.setDepartment("信工学院");
stuOne.introduce();
}
}
静态变量与实例变量的区别
- 占用内存空间不同
系统会为每个对象分配对应实例变量内存空间,比如每个学生对象都会有对应的name变量和age变量。而所有对象会共享一个静态变量。 - 分配内存时间不同
静态变量在类一加载到内存时就分配了相应的内存空间。而实例变量只有在创建了对象后才为每个对象的实例变量分配相应的内存空间。
简而言之:
没有static修饰的成员方法为实例方法,静态方法与类绑定,实例方法跟对象绑定。
this关键字
this修饰的变量表示当前或者指代对象里面的成员变量。
e.g.
class Student{
private String name;
private int age;
public Student (String name,int age) {
this.name = name;//前面的this.name表示构建出来的对象的变量
this.age = age;//后面的age表示构造方法传入的局部变量
}
}
在方法里面调用对象的成员变量时,如果*参数没有和成员变量重名*,this关键词可不写。
包的创建与引用
包的创建
通过关键字package声明包语句。package语句作为Java源文件的第一条语句,指明该源文件定义的类所在的包。
package声明语句语法格式为:
//package 包名;
e.g. package cn.edu.bzu;
public class Student{
}
包的声明与创建的文件夹有关:
e.g. cn.edu.bzu
则对应的文件夹路径:cn\edu\bzu
包的命名规范
包名由小写字母组成,不能以圆点开头或结尾。自己设定的包名之前最好加.上唯一的前缀 ,通常使用组织倒置的网络域名。
e.g. cn.edu.bzu
包的使用
为了使用不在同一包中的类, 需要在Java程序中使用import关键字导入这个类,导入格式有以下两种:
- import 包名. * ; //该包下所有的类都可以被引用
- import 包名. 类名; //只能引用类名指定的类
前面的学习中使用的import java.util.Scanner就是用的第二种方式。
在import不仅可以导入Java开发人员提供的类,也可以导入自己开发的类。
e.g. 使用上面的Student类,则要使用import语句导入
- import cn.edu.bzu.Student;
- import cn.edu.bzu.*;
同一包中的类不需要导入,直接可以使用。
被忽略的必备包:Java.lang
java.lang包是Java语言的核心类库,它包含了运行Java程序必不可少的系统类,系统自动为程序引入java.lang包中的类( System、String等 ) , 不需要使用import引入就可以使用。
UML类图
UML类图又称统一建模语言,是一种为面向对象软件设计提供统一的、标准的、可视化的**建模语言**。
它提供了一系列**框图表示对象模型**,为开发人员阅读和交流系统架构和规划提供便利。
在UML中,类图使用包含类名、属性和操作且带有分隔线的长方形来表示。
e.g.
Student |
---|
-name:String -age:int -department:String |
+introduce():void +haveClass(course:String):void +Student(name:String , age:int , department:String) !!构造方法无返回类型!! |
“可见性”的表示
“可见性”表示该变量对于类外的元素而言是否可见。
公有(public)、私有(private)和受保护(protected)三种
“可见性” | 符号表示 |
---|---|
public | + |
private | - |
protected | # |
“缺省值”是一个 可选项 ,即变量的初始值。
类与类的关系
类不是孤立存在的,类与类之间存在各种各样关系。
在UML类图中,类与类之间有以下几种关系:
- 泛化(Generalization)
- 实现(Realization)
- 关联(Association)
- 聚合(Aggregation)
- 组合(Composition)
- 依赖(Dependency)
泛化
含义:泛化关系是指类之间的继承关系,表示一般与特殊的关系。
PS:关于继承的内容以后的博客会讲解涉及。
e.g.(不做过多解释)
教师(Teacher) 和 学生 Student) 都是人类(Person)。
具体表现:
class子类 extends父类{ }
图示:空心箭头+实线,箭头指向父类
Person | Student | Teacher |
---|---|---|
+name:String +age:int | +department:String | +major:String |
+introduce():void | +haveClass():void | +giveLesson():void |
任务实现:电子宠物Alpha
主函数实现:
PS:包名:com.epet1_0
package com.epet1_0;
import java.util.Scanner;
public class AdoptTest {
public static void main(String[] args) {
System.out.println("欢迎光临电子宠物Alpha版!");
System.out.println("---------------------------");
Scanner input = new Scanner(System.in);
// name
System.out.println("请输入你要领养宠物的昵称:");
String name = input.next();
// 领养类型
System.out.println("请选择要领养的宠物的类型:");
System.out.println("1.猫咪 2.猪猪");
switch (input.nextInt()) {
case 1: // 猫咪类型
System.out.println("请选择猫咪的品种:");
System.out.println("1.波斯猫 2.挪威的森林");
String strain = null;
if (input.nextInt() == 1) {
strain = "波斯猫";
} else {
strain = "挪威的森林";
}
String answer = null;
do {
Cat cat = new Cat(name, strain);
System.out.println("请选择您的操作:\n1.查看宠物信息 2.给宠物喂食 3.陪宠物玩耍");
int operation = input.nextInt();
if (operation == 1) {
cat.introduce();
} else if (operation == 2) {
cat.eat();
} else {
cat.play();
}
System.out.println("是否退出电子宠物Alpha?(yes/no)");
answer = input.next();
} while (answer.equalsIgnoreCase("no"));
break;
case 2:// 猪猪类型
System.out.println("请选择猪猪的性别:");
System.out.println("1.GG 2.MM");
String sex = null;
if (input.nextInt() == 1) {
sex = "GG";
} else {
sex = "MM";
}
Pig pig = new Pig();
do {
pig.setSex(sex);
pig.setName(name);
System.out.println("请选择您的操作:\n1.查看宠物信息 2.给宠物喂食 3.陪宠物玩耍");
int operation = input.nextInt();
if (operation == 1) {
pig.introduce();
} else if (operation == 2) {
pig.eat();
} else {
pig.play();
}
System.out.println("是否退出电子宠物Alpha?(yes/no)");
answer = input.next();
} while (answer.equalsIgnoreCase("no"));
break;
}
System.out.println("感谢光临电子宠物Alpha!");
input.close();
}
}
Pig类
package com.epet1_0;
public class Pig {
// 属性定义:昵称、*性别*、体力值、心情值
private String name, sex;
private int strength = 100, mood = 20;
// 猪自我介绍方法
public void introduce() {
System.out.println("亲爱的主人,我的名字叫" + name + ",我是一只" + sex + "猪猪。我目前的体力值是"+ strength + ",心情值是" + mood + "。");
}
// 猪吃食物方法
public void eat() {
if (strength > 120) {
System.out.println("猪猪"+name + "需要多运动!");
} else {
strength += 10;
System.out.println("猪猪"+name + "吃饱了!体力值+10,目前体力值是" + strength + "。");
}
}
// 猪玩耍方法
public void play() {
if (strength < 60) {
System.out.println("猪猪"+name + "生病了,需要多休息!");
} else {
strength -= 10;
mood += 5;
System.out.println("猪猪"+name + "正在和主人玩耍!体力值-10,心情值+5,目前体力值是" + strength+ ",心情值是" + mood + "。");
}
}
// getter&setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getStrength() {
return strength;
}
public void setStrength(int strength) {
this.strength = strength;
}
public int getMood() {
return mood;
}
public void setMood(int mood) {
this.mood = mood;
}
}
Cat类
package com.epet1_0;
public class Cat {
// 属性定义:昵称、品种、体力值、心情值
private String name, strain;
private int strength = 100, mood = 20;
// 构造方法
public Cat(String name, String strain) {
this.name = name;
this.strain = strain;
}
// 猫自我介绍方法
public void introduce() {
System.out.println("亲爱的主人,我的名字叫" + name + ",我是一只纯种的" + strain
+ "猫咪。我目前的体力值是" + strength + ",心情值是" + mood + "。");
}
// 猫吃食物方法
public void eat() {
if (strength > 120) {
System.out.println("猫咪"+name + "需要多运动!");
} else {
strength += 10;
System.out.println("猫咪"+name + "吃饱了!体力值+10,目前体力值是" + strength + "。");
}
}
// 猫玩耍方法
public void play() {
if (strength < 60) {
System.out.println("猫咪"+name + "生病了,需要多休息!");
} else {
strength -= 10;
mood += 5;
System.out.println("猫咪"+name + "正在和主人玩耍!体力值-10,心情值+5,目前体力值是" + strength+ ",心情值是" + mood + "。");
}
}
// getter&setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public int getStrength() {
return strength;
}
public void setStrength(int strength) {
this.strength = strength;
}
public int getMood() {
return mood;
}
public void setMood(int mood) {
this.mood = mood;
}
}