第3章 面向对象(上)
类与对象是 Java 的核心思维:把现实事物抽象成类,用对象承载数据与行为。
本章学习目标
- 能定义类、创建对象、调用方法
- 理解封装并用 private + getter/setter 实现
- 会写构造方法与 this 关键字
- 理解 static 成员与普通成员的区别
本章导览
核心内容
面向对象概述
什么是面向对象?
面向对象是一种符合人类思维习惯的编程思想。现实生活中存在各种形态不同的事物,这些事物之间存在着各种各样的联系。在程序中使用对象来映射现实中的事物,使用对象的关系来描述事物之间的联系,这种思想就是面向对象。
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一一实现,使用的时候依次调用就可以了。
面向过程
面向对象则是把构成问题的事务按照一定规则划分为多个独立的对象,然后通过调用对象的方法来解决问题。
面向对象
VS
封装
继承
多态
封装是面向对象的核心思想,将对象的属性和行为封装起来,不需要让外界知道具体实现细节。
继承主要描述的就是类与类之间的关系,通过继承,可以在无需重新编写原有类的情况下,对原有类的功能进行扩展。
多态指的是在一个类中定义的属性和功能被其他类继承后,当把子类对象直接赋值给父类引用变量时,相同引用类型的变量调用同一个方法所呈现出的多种不同行为特性。
📄 展开课件原文(第 11–13 页,共 3 页)
Java中的类与对象
说明:面向对象的编程思想,力图让程序中对事物的描述与该事物在现实中的形态保持一致。为了做到这一点,面向对象的思想中提出了两个概念——类和对象。
抽象概念:类是对某一类事物的抽象描述,而对象用于表示现实中该类事物的个体。
分析:可以将上图人看作是一个类,将每个具体的人(如小韩、小石等)看作对象,从人与具体个人之间的关系便可以看出类与对象之间的关系。
说明:类用于描述多个对象的共同特征,它是对象的模板,而对象用于描述现实中的个体,它是类的实例。对象是类的具体化,并且一个类可以对应多个对象。
在面向对象的思想中,最核心的就是对象。
为了在程序中创建对象,首先需要定义一个类。
类是对象的抽象,它用于描述一组对象的共同特征和行为,例如人都有姓名、年龄、性别等特征,还有学习、工作、购物等行为。
以面向对象的编程思想,就可以将某一类中共同的特征和行为封装起来,把共同特征作为类的属性(也叫成员变量),把共同行为作为类的方法(也叫成员方法)。
[修饰符] class 类名 [extends 父类名] [implements 接口名]{
// 类体,包括类的成员变量和成员方法}
修饰符可以是public,也可以不写(默认)
class关键字用于定以类
定义类的名称,首字母大写且符合标识符
用于类的继承
用于类实现接口
需要在类中编写的内容,它主要包括类的成员变量和成员方法
类的定义格式
[修饰符] 数据类型 变量名 [ = 值];
修饰符可以是public、private等
用于声明变量存储的数据类型
变量的名称,必须符合标识符的命名规则
对变量进行赋值,也可以后续再赋值
声明(定义)成员变量
private String name; // 声明一个String类型的name;
private int age = 20; // 定义一个int类型的age,并赋值为20;
[修饰符] [返回值类型] 方法名([参数类型 参数名1,参数类型 参数名2,...]){
//方法体...
return 返回值; //当方法的返回值类型为void时,return及其返回值可以省略对访问权限进行限定的(如public、protected、private);
也有静态修饰符static,还有最终修饰符final等
用于限定方法返回值的数据类型,如果不需要返回值,可以使用void关键字
用于结束方法以及返回方法指定类型的值,当方法的返回值类型为void时,return及其返回值可以省略
用于限定调用方法时传入参数的数据类型
是一个变量,用于接收调用方法时传入的数据
被return语句返回的值,该值会返回给调用者
声明(定义)成员方法
在Java中,定义在类中的变量被称为成员变量,定义在方法中的变量被称为局部变量。如果在某一个方法中定义的局部变量与成员变量同名,这种情况是允许的,此时方法中通过变量名访问到的是局部变量,而并非成员变量。
public class Person {
int age = 10; // 类中定义的变量被称作成员变量
void speak() {
int age = 30; // 方法内部定义的变量被称作局部变量
System.out.println("我今年" + age + "岁了!");分析:上面的代码中,speak()方法中所访问的变量age,就是局部变量,也就是说,当有另外一个程序来调用speak()方法时,输出的值为30,而不是10。
脚
下
留
心
对象创建的语法格式:
类名 对象名称 = new 类名();对象创建的使用示例:
Person p = new Person();在创建对象时,程序会占用两块内存区域,分别是栈内存和堆内存。其中Person类型的变量p被存放在栈内存中,它是一个引用,会指向真正的对象;通过new Person()创建的对象则放在堆内存中,这才是真正的对象。
小提示
Java将内存分为两种,即栈内存和堆内存。其中栈内存用于存放基本类型的变量和对象的引用变量(如Person p),堆内存用于存放由new创建的对象和数组。
对象创建的内存分配:
对比说明:第2种方式,创建实例对象的同时就访问了对象成员,并且在创建后只能访问其中某一个成员,而不能像第1种方式那样可以访问多个对象成员。同时,由于没有对象引用的存在,在完成某一个对象成员的访问后,该对象就会变成垃圾对象。所以,在实际开发中,创建实例对象时多数会使用对象引用。
对象成员的调用:
通过对象的引用来访问对象所有的成员。
对象引用.对象成员
直接使用创建的对象本身来引用对象成员。
new 类名().对象成员| 成员变量类型 | 初始值 |
|---|---|
| byte、short、int、long | 0 |
| float、double | 0.0 |
| char | 一个空字符,即’\u0000’ |
| boolean | false |
| 引用数据类型 | null,表示变量不引用任何对象 |
形成:当没有任何变量引用对象时,该对象将成为垃圾对象,不能再被使用。
说明:变量p1引用了一个Person类型的对象,当这段代码运行完毕时,变量p1就会超出其作用域而被销毁,这时Person类型的对象将因为没有被任何变量所引用而变成垃圾。
{
Person p1 = new Person();......
对象的引用超出作用域。
说明:使用变量p2引用了一个Person类型的对象,接着将变量p2的值置为null,被p2所引用的Person对象就会失去引用,成为垃圾对象。
对象的引用重新指向空地址(null)。
Person p2 = new Person();
p2 = null;在Java中,针对类、成员方法和属性提供了四种访问级别,分别是private、default、protected和public。
private(当前类访问级别):如果类的成员被private访问控制符来修饰,则这个成员只能被该类的其他成员访问,其他类无法直接访问。类的良好封装就是通过private关键字来实现的。
default(包访问级别):如果一个类或者类的成员不使用任何访问控制符修饰,则称它为默认访问控制级别,这个类或者类的成员只能被本包中的其他类访问。
四种访问控制级别说明:
protected(子类访问级别):如果一个类的成员被protected访问控制符修饰,那么这个成员既能被同一包下的其他类访问,也能被不同包下该类的子类访问。
public(公共访问级别):这是一个最宽松的访问控制级别,如果一个类或者类的成员被public访问控制符修饰,那么这个类或者类的成员能被所有的类访问,不管访问类与被访问类是否在同一个包中。
通过一个表将这四种访问级别更加直观的表示出来。
| 访问范围 | private | default | protected | public |
|---|---|---|---|---|
| 同一类中 | ||||
| 同一包中 | ||||
| 子类中 | ||||
| 全局范围 |



📄 展开课件原文(第 14–30 页,共 17 页)
类的封装
示例分析:上述示例将年龄赋值为一个负数-18,在语法上不会有任何问题,因此程序可以正常运行,但在现实生活中明显是不合理的。
解决方案:为了避免出现上述不合理的问题,在设计一个Java类时,应该对成员变量的访问作出一些限定,不允许外界随意访问,这就需要实现类的封装。
public static void main(String[] args) {
Person p = new Person();p.name = "张三";
p.age = -18;
p.speak();}
定义:类的封装,是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,而是通过该类所提供的方法来实现对内部信息的操作访问。
如何实现封装:在定义一个类时,将类中的属性私有化,即使用private关键字来修饰,私有属性只能在它所在类中被访问,如果外界想要访问私有属性,需要提供一些使用public修饰的公有方法,其中包括用于获取属性值的getXxx()方法和设置属性值的setXxx()方法。
案例演示(参考教材文件3-4)
📄 展开课件原文(第 31–32 页,共 2 页)
方法的重载和递归
案例演示(参考教材文件3-5&3-6)
假设如下场景:要在程序中实现一个对数字求和的方法。
可能的办法:由于参与求和数字的个数和类型都不确定,因此要针对不同的情况去设计不同的方法(这样的话非常麻烦,也不便于命名和区分)。
解决办法:Java允许在一个程序中定义多个名称相同,但是参数的类型或个数不同的方法,这就是方法的重载。
案例演示(参考教材文件3-7)
定义:方法的递归是指在一个方法的内部调用自身的过程。递归必须要有结束条件,不然就会陷入无限递归的状态,永远无法结束调用。
📄 展开课件原文(第 33–34 页,共 2 页)
构造方法
实例化一个类的对象后,如果要为这个对象中的属性赋值,则必须通过直接访问对象的属性或调用setXxx()方法的方式才可以。如果需要在实例化对象的同时就为这个对象的属性进行赋值,可以通过构造方法来实现。
定义:构造方法(也被称为构造器)是类的一个特殊成员,它会在类实例化对象时被自动调用。
定义构造方法时需同时满足以下三个条件:
方法名与类名相同。
在方法名的前面没有返回值类型的声明。
在方法中不能使用return语句返回一个值,但是可以单独写return语句来作为方法的结束。
案例演示(参考教材文件3-8&3-9)
定义构造方法的语法格式:
[修饰符] 方法名 ([参数列表]){
// 方法体}
案例演示(参考教材文件3-10)
定义:与普通方法一样,构造方法也可以重载,在一个类中可以定义多个构造方法,只要每个构造方法的参数类型或参数个数不同即可。
说明:在创建对象时,可以通过调用不同的构造方法来为不同的属性进行赋值。
在Java中的每个类都至少有一个构造方法,如果在一个类中没有显示地定义构造方法,系统会自动为这个类创建一个默认的无参构造方法。
说明:第一种写法,类中没有显示地声明构造方法,但仍然可以用new Person()语句来创建Person类的实例对象。由于系统提供的无参构造方法往往不能满足需求,因此,可以自己在类中定义构造方法,一旦为该类定义了构造方法,系统将不再提供默认的无参构造方法。
class Person {
public Person() { }脚
下
留
心
思考一下,声明构造方法时,可以使用private访问修饰符吗?下面就来测试一下,看看会出现什么结果。
结论:被private访问控制符修饰的构造方法Person(int)只能在当前Person类中被访问无法在类的外部被访问,无法通过该私有构造方法来创建对象。因此,为了方便实例化对象,构造方法通常会使用public来修饰。
案例演示(参考教材3.5.2小节脚下留心)

📄 展开课件原文(第 35–39 页,共 5 页)
this关键字
为什么要使用this关键字?
在文件3-9中使用变量表示年龄时,构造方法中使用的参数是a,成员变量使用的是age,虽然在语法上没有任何问题,但这样的程序可读性很差。这时可以将Person类中表示年龄的变量进行统一命名,例如都声明为age,但是这样做又会导致成员变量和局部变量的名称冲突,在方法中将无法访问成员变量age。
解决方案:为了解决这个问题,Java中提供了一个关键字this来指代当前对象,用于在方法中访问对象的其他成员。
说明:在上面的代码中,构造方法的参数被定义为age,它是一个局部变量,在类中还定义了一个成员变量,名称也是age。在构造方法中如果使用“age”,则是访问局部变量,但如果使用“this.age”则是访问成员变量。
通过this关键字调用成员变量,解决与局部变量名称冲突问题。
class Person {
int age; // 成员变量age
public Person(int age) { // 局部变量age
this.age = age; // 将局部变量age的值赋给成员变量age}
说明:在上面的代码中,使用this关键字调用了openMouth()方法。需要注意的是,此处的this关键字可以省略不写,也就是说上面的代码中,写成“this.openMouth()”和“openMouth()”效果是完全一样的。
通过this关键字调用成员方法。
public void openMouth() {...
public void speak() {
this.openMouth();通过this关键字调用构造方法。
案例演示(参考教材文件3-11)
说明:构造方法是在实例化对象时被Java虚拟机自动调用的,在程序中不能像调用其他方法一样去调用构造方法,但可以在一个构造方法中使用“this([参数1,参数2…])”的形式来调用其他的构造方法。
public Person() {
public Person(int age) {
this(); // 调用无参的构造方法在使用this调用类的构造方法时,应注意以下几点:
只能在构造方法中使用this调用其他的构造方法,不能在成员方法中使用。
在构造方法中,使用this调用构造方法的语句必须是该方法的第一条执行语句,且只能出现一次。
不能在一个类的两个构造方法中使用this互相调用。
📄 展开课件原文(第 40–44 页,共 5 页)
static关键字
为什么要使用static关键字?
Java中的static关键字,用于修饰类的成员,如成员变量、成员方法以及代码块等,被static修饰的成员具备一些特殊性。比如被static关键字修饰的成员变量、方法可以被类直接访问,而不需要预先构造类的实例化对象。
在一个Java类中,可以使用static关键字来修饰成员变量,该变量被称作静态变量。
静态变量被所有实例共享,可以使用“类名.变量名”的形式来访问。
static关键字只能用于修饰成员变量,不能用于修饰局部变量,否则编译会报错。
从图可以看出,所有学生对象共享一个名称为schoolName的变量。在一个Java类中,要实现这种功能可以使用static关键字来修饰成员变量,该变量被称作静态变量,它可以被所有实例所共享。
案例演示(参考教材文件3-12)
public class Student {
public void study() {
static int num = 10; // 这行代码是非法的,编译会报错}
static关键性不能修饰局部变量
脚
下
留
心
被static关键字修饰的方法称为静态方法。
同静态变量一样,静态方法可以使用“类名.方法名”的方式来访问,也可以通过类的实例对象来访问。
在一个静态方法中只能访问用static修饰的成员,原因是没有被static修饰的成员需要先创建对象才能访问,而静态方法在被调用时可以不创建任何对象。
案例演示(参考教材文件3-13)
在Java中,使用一对大括号包围起来的若干行代码被称为一个代码块。
使用static关键字修饰的代码块称为静态代码块。
当类被加载时,静态代码块会执行,并且只会执行一次。
在程序中,经常使用静态代码块来对类的成员变量进行初始化。
案例演示(参考教材文件3-14)

📄 展开课件原文(第 45–50 页,共 6 页)
本章小结
本章详细讲解了面向对象的基础知识。首先讲解了面向对象的思想,然后讲解了类与对象之间的关系,接着讲解了方法的重载与递归、构造方法和this关键字的使用,最后讲解了static关键字的使用。
📄 展开课件原文(第 51–52 页,共 2 页)
课件里的提醒 课件原文中的“注意 / 说明 / 定义”
- 说明:面向对象的编程思想,力图让程序中对事物的描述与该事物在现实中的形态保持一致。为了做到这一点,面向对象的思想中提出了两个概念——类和对象。
- 说明:类用于描述多个对象的共同特征,它是对象的模板,而对象用于描述现实中的个体,它是类的实例。对象是类的具体化,并且一个类可以对应多个对象。
- 定义类的名称,首字母大写且符合标识符
- 说明:变量p1引用了一个Person类型的对象,当这段代码运行完毕时,变量p1就会超出其作用域而被销毁,这时Person类型的对象将因为没有被任何变量所引用而变成垃圾。
- 说明:使用变量p2引用了一个Person类型的对象,接着将变量p2的值置为null,被p2所引用的Person对象就会失去引用,成为垃圾对象。
- 定义:类的封装,是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,而是通过该类所提供的方法来实现对内部信息的操作访问。
- 定义:方法的递归是指在一个方法的内部调用自身的过程。递归必须要有结束条件,不然就会陷入无限递归的状态,永远无法结束调用。
- 定义:构造方法(也被称为构造器)是类的一个特殊成员,它会在类实例化对象时被自动调用。
- 定义构造方法时需同时满足以下三个条件:
- 定义构造方法的语法格式:
- 定义:与普通方法一样,构造方法也可以重载,在一个类中可以定义多个构造方法,只要每个构造方法的参数类型或参数个数不同即可。
- 说明:在创建对象时,可以通过调用不同的构造方法来为不同的属性进行赋值。
本章代码速览 把课件里的示例代码集中起来,可直接复制运行
[修饰符] class 类名 [extends 父类名] [implements 接口名]{// 类体,包括类的成员变量和成员方法private String name; // 声明一个String类型的name;private int age = 20; // 定义一个int类型的age,并赋值为20;[修饰符] [返回值类型] 方法名([参数类型 参数名1,参数类型 参数名2,...]){//方法体return 返回值; //当方法的返回值类型为void时,return及其返回值可以省略public class Person {int age = 10; // 类中定义的变量被称作成员变量void speak() {int age = 30; // 方法内部定义的变量被称作局部变量System.out.println("我今年" + age + "岁了!");