疯狂Java讲义第五版第5章 面向对象(上)

Java是面向对象的程序设计语言,Java语言提供了定义类、成员变量、方法等最基本的功能。类可被认为是一种自定义的数据类型,可以使用类来定义变量,所有使用类定义的变量都是引用变量它们将会引用到类的对象。类用于描述客观世界里某一类对象的共同特征,而对象则是类的具体存在,Java程序使用类的构造器来创建该类的对象

Java支持面向对象的三大特征:封装、继承和多态。

5.1 类和对象

Java是面向对象的程序设计语言,类是面向对象的重要内容,可以把类当成一种自定义类型,可以使用类来定义变量,这种类型的变量统称为引用变量。也就是说,所有类是引用类型。

5.1.1 定义类

Java语言里定义类的简单语法如下:
在这里插入图片描述
在上面的语法格式中,修饰符可以是public、final、abstract,或者完全省略这三个修饰符,类名只要是一个合法的标识符即可,但这仅仅满足的是Java的语法要求;如果从程序的可读性方面来看,Java类名必须是由一个或多个有意义的单词连缀而成的,每个单词首字母大写,其他字母全部小写,单词与单词之间不要使用任何分隔符。

对一个类定义而言,可以包含三种最常见的成员:构造器、成员变量和方法,三种成员都可以定义零个或多个,如果三种成员都只定义零个,就是定义了一个空类,这没有太大的实际意义。

static修饰的成员不能访问没有static修饰的成员

成员变量用于定义该类或该类的实例所包含的状态数据,方法则用于定义该类或该类的实例的行为特征或者功能实现。构造器用于构造该类的实例,Java语言通过new关键字来调用构造器,从而返回该类的实例。

定义成员变量的语法格式如下:
在这里插入图片描述

  • 修饰符:修饰符可以省略,也可以是public、protected、private、static、final,其中public、protected、private三个最多只能出现其中之一,可以与static、final组合起来修饰成员变量。
  • 类型:类型可以是Java语言允许的任何数据类型,包括基本类型和现在介绍的引用类型。
  • 成员变量名:成员变量名只要是一个合法的标识符即可,但这只是从语法角度来说的;如果从程序可读性角度来看,成员变量名应该由一个或多个有意义的单词连缀而成,第一个单词首字母小写,后面每个单词首字母大写,其他字母全部小写,单词与单词之间不要使用任何分隔符。成员变量用于描述类或对象包含的状态数据,因此成员变量名建议使用英文名词。
  • 默认值:定义成员变量还可以指定一个可选的默认值。

定义方法的语法格式如下:
在这里插入图片描述

  • 修饰符:修饰符可以省略,也可以是public、protected、private、static、final、abstract,其中public、protected、private三个最多只能出现其中之一;abstract和final最多只能出现其中之一,它们可以与static组合起来修饰方法。
  • 方法返回值类型:返回值类型可以是Java语言允许的任何数据类型,包括基本类型和引用类型;如果声明了方法返回值类型,则方法体内必须有一个有效的return语句,该语句返回一个变量或一个表达式,这个变量或者表达式的类型必须与此处声明的类型匹配。除此之外,如果一个方法没有返回值,则必须使用void来声明没有返回值
  • 方法名:方法名的命名规则与成员变量的命名规则基本相同,但由于方法用于描述该类或该类的实例的行为特征或功能实现,因此通常建议方法名以英文动词开头。
  • 形参列表:形参列表用于定义该方法可以接受的参数,形参列表由零组到多组“参数类型 形参名”组合而成,多组参数之间以英文逗号(,)隔开,形参类型和形参名之间以英文空格隔开。一旦在定义方法时指定了形参列表,则调用该方法时必须传入对应的参数值——谁调用方法,谁负责为形参赋值。

static是一个特殊的关键字,它可用于修饰方法、成员变量等成员static修饰的成员表明它属于这个类本身,而不属于该类的单个实例,因为通常把static修饰的成员变量和方法也称为类变量、类方法。不使用static修饰的普通方法、成员变量则属于该类的单个实例,而不属于该类。因为通常把不使用static修饰的成员变量和方法也称为实例变量、实例方法静态成员不能直接访问非静态成员

定义构造器的语法格式如下:
在这里插入图片描述

  • 修饰符:修饰符可以省略,也可以是public、protected、private其中之一。
  • 构造器名:构造器名必须和类名相同。
  • 形参列表:和定义方法形参列表的格式完全相同。

5.1.2 对象的产生和使用

创建对象的根本途径是构造器,通过new关键字来调用某个类的构造器即可创建这个类的实例。

Java的对象大致有如下作用:

  1. 访问对象的实例变量。
  2. 调用对象的方法。

如果访问权限允许,类里定义的方法和成员变量都可以通过类或实例来调用。类或实例访问方法或成员变量的语法是:类.类变量|方法,或者实例.实例变量|方法,在这种方式中,类或实例是主调者,用于访问该类或该实例的成员变量或方法。

5.1.3 对象、引用和指针

5.1.4 对象的this引用

Java提供了一个this关键字,this关键字总是指向调用该方法的对象。根据this出现位置的不同,this作为对象的默认引用有两种情形。

  1. 构造器中引用该构造器正在初始化的对象。
  2. 在方法中引用调用该方法的对象。

this关键字最大的作用就是让类中一个方法,访问该类里的另一个方法或实例变量。

对于static修饰的方法而言,则可以使用类来直接调用该方法,如果在static修饰的方法中使用this关键字,则这个关键字就无法指向合适的对象。所以,static修饰的方法中不能使用this引用。由于static修饰的方法不能使用this引用,所以static修饰的方法不能访问不使用static修饰的普通成员,因此Java语法规定:静态成员不能直接访问非静态成员

5.2 方法详解

方法是类或对象的行为特征的抽象,方法是类或对象最重要的组成部分。

5.2.1 方法的所属性

在Java语言里,方法不能独立存在,方法必须属于类或对象。一旦将一个方法定义在某个类的类体内,如果这个方法使用了static修饰,则这个方法属于这个类,否则这个方法属于这个类的实例。

执行方法时必须使用类或对象来作为调用者,即所有方法都必须使用“类.方法”或“对象.方法”的形式来调用。同一个类的一个方法调用另外一个方法时,如果被调方法是普通方法,则默认使用this作为调用者;如果被调方法是静态方法,则默认使用类作为调用者

Java语言里方法的所属性主要体现在如下几个方面。

  1. 方法不能独立定义,方法只能在类体里定义。
  2. 从逻辑意义上来看,方法要么属于该类本身,要么属于该类的一个对象。
  3. 永远不能独立执行方法,执行方法必须使用类或对象作为调用者。

5.2.2 方法的参数传递机制

前面已经介绍了Java里的方法是不能独立存在的,调用方法也必须使用类或对象作为主调者。如果声明方法时包含了形参声明,则调用方法时必须给这些形参指定参数值,调用方法时实际传给形参的参数值也被称为实参。

Java方法的参数传递机制来控制的,Java里方法的参数传递方式只有一种:值传递。所谓值传递,就是将实际参数值的副本(复制品)传入方法内,而参数本身不会受到任何影响

5.2.3 形参个数可变的方法

从JDK 1.5之后,Java允许定义形参个数可变的参数,从而允许为方法指定数量不确定的形参。如果在定义方法时,在最后一个形参的类型后增加三点(…),则表明该形参可以接受多个参数值,多个参数值被当成数组传入

个数可变的形参只能处于形参列表的最后。一个方法中最多只能包含一个个数可变的形参。个数可变的形参本质就是一个数组类型的形参,因此调用包含个数可变形参的方法时,该个数可变的形参既可以传入多个参数,也可以传入一个数组。

5.2.4 递归方法

一个方法体内调用它自身,被称为方法递归。方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。

当一个方法不断地调用它本身时,必须在某个时刻方法的返回值是确定的,即不再调用它本身,否则这种递归就变成了无穷递归,类似于死循环。因此定义递归方法时有一条最重要的规定:递归一定要向已知方向递归。

5.2.5 方法重载

Java允许同一个类里定义多个同名方法,只要形参列表不同就行。如果同一个类中包含了两个或两个以上方法的方法名相同,但形参列表不同,则被称为方法重载。

方法重载的要求就是两同一不同同一个类中方法名相同参数列表不同。至于方法的其他部分,如方法返回值类型、修饰符等,与方法重载没有任何关系。
Java里不能使用方法返回值类型作为区分方法重载的依据

如果被重载的方法里包含了个数可变的形参,则需要注意。
在这里插入图片描述
编译、运行上面程序,将看到olv.test();和olv.test(“aa”,“bb”);两次调用的是test(String… books)方法,而olv.test(“aa”);则调用的是test(String msg)方法。通过这个程序可以看出,如果同一个类中定义了test(String… books)方法,同时还定义了一个test(String)方法,则test(String… books)方法的books 不可能通过直接传入一个字符串参数来调用,如果只传入一个参数,系统会执行重载的test(String)方法。如果需要调用test(String… books)方法,又只想传入一个字符串参数,则可采用传入字符串数组的形式,如下代码所示。
在这里插入图片描述
大部分时候并不推荐重载形参个数可变的方法,因为这样做确实没有太大的意义,而且容易降低程序的可读性。

5.3 成员变量和局部变量

在Java语言中,根据定义变量位置的不同,可以将变量分成两大类:成员变量和局部变量。

5.3.1 成员变量和局部变量

成员变量指的是在类里定义的变量,局部变量指的是在方法里定义的变量。
在这里插入图片描述
成员变量被分为类变量和实例变量两种,定义成员变量时没有static修饰的就是实例变量有static修饰的就是类变量。其中类变量从该类的准备阶段起开始存在,直到系统完全销毁这个类,类变量的作用域与这个类的生存范围相同;而实例变量则从该类的实例被创建起开始存在,直到系统完全销毁这个实例,实例变量的作用域与对应实例的生存范围相同。

Java允许局部变量和成员变量同名,如果方法里的局部变量和成员变量同名,局部变量会覆盖成员变量,如果需要在这个方法里引用被覆盖的成员变量则可使用this(对于实例变量)或类名(对于类变量)作为调用者来限定访问成员变量

5.3.2 成员变量的初始化和内存中的运行机制

当系统加载类或创建该类的实例时,系统自动为成员变量分配内存空间,并在分配内存空间后,自动为成员变量指定初始值。

当程序需要访问类变量时,尽量使用类作为主调,而不要使用对象作为主调,这样可以避免程序产生歧义,提高程序的可读性。

5.3.3 局部变量的初始化和内存中的运行机制

局部变量定义后,必须经过显式初始化后才能使用系统不会为局部变量执行初始化。这意味着定义局部变量后,系统并未为这个变量分配内存空间,直到等到程序为这个变量赋初始值时,系统才会为局部变量分配内存,并将初始值保存到这块内存中

与成员变量不同,局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存中。如果局部变量是基本类型的变量,则直接把这个变量的值保存在该变量对应的内存中;如果局部变量是一个引用类型的变量,则这个变量里存放的是地址,通过该地址引用到该变量实际引用的对象或数组

栈内存中的变量无须系统垃圾回收,往往随方法或代码块的运行结束而结束

局部变量的作用域是从初始化该变量开始直到该方法或该代码块运行完成而结束。因为局部变量只保存基本类型的值或者对象的引用,因此局部变量所占的内存区通常比较小。

5.3.4 变量的使用规则

如果有如下几种情形,则应该考虑使用成员变量。

  1. 如果需要定义的变量是用于描述某个类或某个对象的固有信息的,例如人的身高、体重等信息,它们是人对象的固有信息,每个人对象都具有这些信息。这种变量应该定义为成员变量。如果这种信息对这个类的所有实例完全相同,或者说它是类相关的,例如人类的眼睛数量,目前所有人的眼睛数量都是2,如果人类进化了,变成了3个眼睛,则所有人的眼睛数量都是3,这种类相关的信息应该定义成类变量;如果这种信息是实例相关的,例如人的身高、体重等,每个人实例的身高、体重可能互不相同,这种信息是实例相关的,因此应该定义成实例变量
  2. 如果在某个类中需要以一个变量来保存该类或者实例运行时的状态信息,例如上面五子棋程序中的棋盘数组,它用以保存五子棋实例运行时的状态信息。这种用于保存某个类或某个实例状态信息的变量通常应该使用成员变量
  3. 如果某个信息需要在某个类的多个方法之间进行共享,则这个信息应该使用成员变量来保存。例如,在把浮点数转换为人民币读法字符串的程序中,数字的大写字符和单位字符等是多个方法的共享信息,因此应设置为成员变量。

5.4 隐藏和封装

Java程序推荐将类和对象的成员变量进行封装。

5.4.1 理解封装

封装(Encapsulation)是面向对象的三大特征之一(另外两个是继承和多态),它指的是将对象的状态信息隐藏在对象内部不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问

为了实现良好的封装,需要从两个方面考虑。

  1. 将对象的成员变量和实现细节隐藏起来,不允许外部直接访问。
  2. 把方法暴露出来,让方法来控制对这些成员变量进行安全的访问和操作。

5.4.2 使用访问控制符

Java提供了3个访问控制符:private、protected和public,分别代表了3个访问控制级别,另外还有一个不加任何访问控制符的访问控制级别,提供了4个访问控制级别。Java的访问控制级别由小到大如图所示。
在这里插入图片描述
private(当前类访问权限):如果类里的一个成员(包括成员变量、方法和构造器等)使用private访问控制符来修饰,则这个成员只能在当前类的内部被访问。很显然,这个访问控制符用于修饰成员变量最合适,使用它来修饰成员变量就可以把成员变量隐藏在该类的内部。

default(包访问权限):如果类里的一个成员(包括成员变量、方法和构造器等)或者一个外部类不使用任何访问控制符修饰,就称它是包访问权限的,default访问控制的成员或外部类可以被相同包下的其他类访问

protected(子类访问权限):如果一个成员(包括成员变量、方法和构造器等)使用protected访问控制符修饰,那么这个成员**既可以被同一个包中的其他类访问,也可以被不同包中的子类访问。**在通常情况下,如果使用protected来修饰一个方法,通常是希望其子类来重写这个方法。关于父类、子类的介绍请参考5.6节的内容。

public(公共访问权限):这是一个最宽松的访问控制级别,如果一个成员(包括成员变量、方法和构造器等)或者一个外部类使用public访问控制符修饰,那么这个成员或外部类就可以被所有类访问,不管访问类和被访问类是否处于同一个包中,是否具有父子继承关系。
在这里插入图片描述
关于访问控制符的使用,存在如下几条基本原则。

  1. 类里的绝大部分成员变量都应该使用private修饰,只有一些static修饰的、类似全局变量的成员变量,才可能考虑使用public修饰。除此之外,有些方法只用于辅助实现该类的其他方法,这些方法被称为工具方法,工具方法也应该使用private修饰。
  2. 如果某个类主要用做其他类的父类该类里包含的大部分方法可能仅希望被其子类重写,而不想被外界直接调用,则应该使用protected修饰这些方法。
  3. 希望暴露出来给其他类自由调用的方法应该使用public修饰。因此,类的构造器通过使用public修饰,从而允许在其他地方创建该类的实例。因为外部类通常都希望被其他类自由使用,所以大部分外部类都使用public修饰。

5.4.3 package、import和import static

Java引入了包(package)机制,提供了类的多层命名空间,用于解决类的命名冲突、类文件管理等问题。位于包中的类,在文件系统中也必须有与包名层次相同的目录结构。


为了简化编程,Java引入了import关键字,import可以向某个Java文件中导入指定包层次下某个类或全部类,import语句应该出现在package语句(如果有的话)之后、类定义之前。一个Java源文件只能包含一个package语句,但可以包含多个import语句,多个import语句用于导入多个包层次下的类。

使用import语句导入指定包下全部类的用法如下:

import packbag.subpackpage...*;

上面import语句中的星号*只能代表类,不能代表包。因此使用import lee.*;语句时,它表明导入lee包下的所有类,即Hello类和HelloTest类,而lee包下sub子包内的类则不会被导入。如需导入lee.sub.Apple类,则可以使用import lee.sub.*;语句来导入lee.sub包下的所有类。

Java默认为所有源文件导入java.lang包下的所有类,因此前面在Java程序中使用String、System类时都无须使用import语句来导入这些类。但对于前面介绍数组时提到的Arrays类,其位于java.util包下,则必须使用import语句来导入该类。


JDK 1.5以后更是增加了一种静态导入的语法,它用于导入指定类的某个静态成员变量、方法或全部的静态成员变量、方法。

静态导入使用import static语句,静态导入也有两种语法,分别用于导入指定类的单个静态成员变量、方法和全部静态成员变量、方法,其中导入指定类的单个静态成员变量、方法的语法格式如下:
在这里插入图片描述
导入指定类的全部静态成员变量、方法的语法格式如下:
在这里插入图片描述

用一句话来归纳import和import static的作用:使用import可以省略写包名;而使用import static则可以连类名都省略。

5.4.4 Java的常用包

5.5 深入构造器

构造器是一个特殊的方法,这个特殊方法用于创建实例时执行初始化。构造器是创建对象的重要途径(即使使用工厂模式、反射等方式创建对象,其实质依然是依赖于构造器),因此,Java类必须包含一个或一个以上的构造器。

5.5.1 使用构造器执行初始化

5.5.2 构造器重载

5.6 类的继承

继承是面向对象的三大特征之一,也是实现软件复用的重要手段。Java的继承具有单继承的特点,每个子类只有一个直接父类。

5.6.1 继承的特点

5.6.2 重写父类的方法

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值