Java语言的特点 Java开发环境的搭建 青岛理工大学 Qingdao University of technoliy 第三章 面向对象(上) Java语言的特点 Java开发环境的搭建 环境变量的配置 Java的运行机制 主讲:李 兰
3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 面向对象的概念 类与对象 构造方法 this关键字 垃圾回收 static关键字 3.7 内部类 3.8 Java的帮助文档
3.1 面向对象的概念 面向对象是一种符合人类思维习惯的编程思想。现实生活中存在各种形态不同的事物,这些事物之间存在着各种各样的联系。在程序中使用对象来映射现实中的事物,使用对象的关系来描述事物之间的联系,这种思想就是面向对象。 面向过程就是分析解决问题所需要的步骤,面向对象则是把解决的问题按照一定规则划分为多个独立的对象,然后通过调用对象的方法来解决问题。 面向对象的特点主要可以概括为封装性、继承性和多态性。
3.2 类与对象 面向对象的编程思想力图在程序中对事物的描述与该事物在现实中的形态保持一致。为了做到这一点,面向对象的思想中提出两个概念,即类和对象。 类是对某一类事物的抽象描述,而对象用于表示现实中该类事物的个体。类和对象的关系如同玩具和玩具模型的关系。 可以将玩具模型看作是一个类,将一个个玩具看作对象,从玩具模型和玩具之间的关系便可以看出类与对象之间的关系。类用于描述多个对象的共同特征,它是对象的模板。对象用于描述现实中的个体,它是类的实例,并且一个类可以对应多个对象
3.2 类与对象 3.2.1 类的定义 在面向对象的思想中最核心就是对象,为了在程序中创建对象,首先需要定义一个类。类是对象的抽象,它用于描述一组对象的共同特征和行为 类中可以定义成员变量和成员方法,其中成员变量用于描述对象的特征,也被称作属性,成员方法用于描述对象的行为,可简称为方法
3.2 类与对象 3.2.1 类的定义 定义一个Person类 Person是类名,age是成员变量,speak()是成员方法。在成员方法speak()中可以直接访问成员变量age。
3.2 类与对象 3.2.2 对象的创建与使用 应用程序想要完成具体的功能,仅有类是远远不够的,还需要根据类创建实例对象。 在Java程序中可以使用new关键字来创建对象,具体格式如下: 例如,创建一个Person对象,具体示例如下:
3.2 类与对象 3.2.2 对象的创建与使用 “new Person()”用于创建Person类的一个实例对象,“Person p”则是声明了一个Person类型的变量p。中间的等号用于将Person对象在内存中的地址赋值给变量p,这样变量p便持有了对象的引用,变量p和对象之间的引用关系如下图所示。
3.2 类与对象 3.2.2 对象的创建与使用 在创建Person对象后,可以通过对象的引用来访问对象所有的成员,具体格式如下: 看一个例子,具体如下:
3.2 类与对象 3.2.2 对象的创建与使用 在例程3-2中,通过“p1.age=18”将p1对象的age属性赋值为18,没有对p2对象的age属性进行赋值,但p2对象的age属性也是有值的,其值为0。这是因为在针对不同类型的成员变量,Java虚拟机会赋予不同的初始值
3.2 类与对象 3.2.2 对象的创建与使用 当对象被实例化后,在程序中可以通过对象的引用变量来访问该对象的成员。当没有任何变量引用这个对象时,它将成为垃圾对象,不能再被使用。 情况1:变量超出作用域变为垃圾对象
3.2 类与对象 3.2.2 对象的创建与使用 情况2:变量的值变为null 运行结果 在例程3-3中,创建了一个Person类的实例对象,并两次调用了该对象的say()方法。第一次调用say()方法时可以正常打印,但在第10行代码中将变量p2的值置为null,当再次调用say()方法时抛出了空指针异常。在Java中,null是一种特殊的常量,当一个变量的值为null时,则表示该变量不指向任何一个对象。在例程中,当把变量p2置为null时,被p2所引用的Person对象就会失去引用,成为垃圾对象
3.2 类与对象 3.2.3 类的设计 假设要在程序中描述一个学校所有学生的信息,可以先设计一个学生类(Student),在这个类中定义两个属性name、age分别表示学生的姓名和年龄,定义一个方法introduce()表示学生做自我介绍。根据上面的描述设计出来的Student类如例程3-4所示
3.2 类与对象 3.2.4 类的封装 先来看一个例子,具体如下: 运行结果 在例程3-5的第5行代码中,将年龄赋值为一个负数-30,这在程序中不会有任何问题,但在现实生活中明显是不合理的。为了解决年龄不能为负数的问题,在设计一个类时,应该对成员变量的访问作出一些限定,不允许外界随意访问。这就需要实现类的封装。
3.2 类与对象 3.2.4 类的封装 所谓类的封装是指在定义一个类时,将类中的属性私有化,即使用private关键字来修饰,私有属性只能在它所在类中被访问,为了能让外界访问私有属性,需要提供一些使用public修饰的公有方法,其中包括用于获取属性值的getXxx方法和设置属性值的setXxx方法
3.2 类与对象 3.2.4 类的封装 使用private关键字将属性name和age声明为私有 getName()方法用于获取name属性的值,setName()方法用于设置name属性的值 getAge()和setAge()方法用于获取和设置age属性的值。在setAge()方法中对参数stuAge的值进行检查,
3.3 构造方法 3.3.1 构造方法的定义 在一个类中定义的方法如果同时满足以下三个条件,该方法称为构造方法,具体如下: 方法名和类名相同 方法名的前面没有返回值类型的声明 方法中不能使用return语句返回一个值
3.3 构造方法 3.3.1 构造方法的定义 在例程3-7的Person类中定义了一个无参的构造方法Person()。从运行结果可以看出,Person类中无参的构造方法被调用了。这是因为第9行代码在实例化Person对象时会自动调用类的构造方法,“new Person()”语句的作用除了会实例化Person对象,还会调用构造方法Person()
3.3 构造方法 3.3.1 构造方法的定义 在一个类中除了定义无参的构造方法,还可以定义有参的构造方法,通过有参的构造方法就可以实现对属性的赋值 例程3-8的Person类中定义了有参的构造方法Person(int a)。第13行代码中的“new Person(20)”会在实例化对象的同时调用有参的构造方法,并传入了参数20。在构造方法Person(int a)中将20赋值给对象的age属性。通过运行结果可以看出,Person对象在调用speak()方法时,其age属性已经被赋值为20。
3.3 构造方法 3.3.2 构造方法的重载 与普通方法一样,构造方法也可以重载,在一个类中可以定义多个构造方法,只要每个构造方法的参数类型或参数个数不同即可 例程3-9的Person类中定义了两个构造方法,它们构成了重载。在创建p1对象和p2对象时,根据传入参数的不同,分别调用不同的构造方法。从程序的运行结果可以看出,两个构造方法对属性赋值的情况是不一样的,其中一个参数的构造方法只针对name属性进行赋值,这时age属性的值为默认值0。
脚下留心 在Java中的每个类都至少有一个构造方法,如果在一个类中没有定义构造方法,系统会自动为这个类创建一个默认的构造方法,这个默认的构造方法没有参数,在其方法体中没有任何代码,即什么也不做。 下面程序中Person类的两种写法效果是完全一样的。
脚下留心 如果为该类定义了构造方法,系统就不再提供默认的构造方法了 运行结果 从图3-12可以看出程序在编译时报错,其原因是调用new Person()创建Person类的实例对象时,需要调用无参的构造方法,而我们并没有定义无参的构造方法,只是定义了一个有参的构造方法,系统将不再自动生成无参的构造方法
3.4 this关键字 1、通过this关键字可以明确地去访问一个类的成员变量,解决与局部变量名称冲突问题,具体示例如下: 在上面的代码中,构造方法的参数被定义为age,它是一个局部变量,在类中还定义了一个成员变量,名称也是age。在构造方法中如果使用“age”,则是访问局部变量,但如果使用“this.age”则是访问成员变量。
3.4 this关键字 2、通过this关键字调用成员方法,具体示例代码如下: 在上面的speak()方法中,使用this关键字调用openMouth()方法。注意,此处的this关键字可以省略不写,也就是说上面的第6行代码写成“this.openMouth()”和“openMouth()”,效果是完全一样的
3.4 this关键字 3、构造方法是在实例化对象时被Java虚拟机自动调用的,在程序中不能像调用其它方法一样去调用构造方法,但可以在一个构造方法中使用“this([参数1,参数2…])”的形式来调用其它的构造方法通过this关键字调用成员方法
3.4 this关键字 注意: 1、只能在构造方法中使用this调用其它的构造方法
3.5 垃圾回收 当一个对象成为垃圾后仍会占用内存空间,时间一长,就会导致内存空间的不足。针对这种情况,Java中引入了垃圾回收机制 除了等待Java虚拟机进行自动垃圾回收,也可以通过调用System.gc()方法来通知Java虚拟机立即进行垃圾回收。当一个对象在内存中被释放时,它的finalize()方法会被自动调用
3.5 垃圾回收 在例程3-13的Person类中定义了一个finalize()方法,该方法的返回值必须为void,并且要使用public来修饰。在main()方法中创建了两个对象p1和p2,然后将两个变量置为null,这意味着新创建的两个对象成为垃圾了,紧接着通过“System.gc()”语句通知虚拟机进行垃圾回收。从运行结果可以看出,虚拟机针对两个垃圾对象进行了回收,并在回收之前分别调用两个对象的finalize()方法。
3.6 static关键字 3.6.1 静态变量 在一个Java类中,可以使用static关键字来修饰成员变量,该变量被称作静态变量 静态变量被所有实例共享,可以使用“类名.变量名”的形式来访问 static关键字只能用于修饰成员变量,不能用于修饰局部变量,否则编译会报错,下面的代码是非法的。
3.6 static关键字 3.6.1 静态变量 例程3-14的Student类中定义了一个静态变量schoolName,用于表示学生所在的学校,它被所有的实例所共享。由于schoolName是静态变量,因此可以直接使用Student.schoolName的方式进行调用,也可以通过Student的实例对象进行调用,如stu2.schoolName。在第8行代码将变量schoolName赋值为“传智播客”,通过运行结果可以看出学生对象stu1和stu2的schoolName属性均为“传智播客”。
3.6 static关键字 3.6.2 静态方法 被static关键字修饰的方法称为静态方法 同静态变量一样,静态方法可以使用“类名.方法名”的方式来访问,也可以通过类的实例对象来访问。 在一个静态方法中只能访问用static修饰的成员,原因在于没有被static修饰的成员需要先创建对象才能访问,而静态方法在被调用时可以不创建任何对象。
3.6 static关键字 3.6.2 静态方法 例程3-15的Person类中定义了静态方法sayHello(),在第8行代码处通过“Person.sayHello()”的形式调用了静态方法,由此可见静态方法不需要创建对象就可以调用。
3.6 static关键字 3.6.3 静态代码块 在Java类中,使用一对大括号包围起来的若干行代码被称为一个代码块,用static关键字修饰的代码块称为静态代码块 当类被加载时,静态代码块会执行,由于类只加载一次,因此静态代码块只执行一次 在程序中,通常使用静态代码块来对类的成员变量进行初始化
3.6 static关键字 3.6.3 静态代码块 从图3-19所示的运行结果可以看出,程序中的两段静态代码块都执行了。在命令行窗口输入“java Example14”后,虚拟机首先会加载类Example14,在加载类的同时就会执行该类的静态代码块,紧接着会调用main()方法。在该方法中创建了两个Person对象,但在两次实例化对象的过程中,静态代码块只执行一次,这就说明类在第一次使用时才会被加载,并且只会加载一次
3.6 static关键字 3.6.4 单例模式 设计模式就是针对这些问题和需求,在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式 单例模式是Java中的一种设计模式,它是指在设计一个类时,需要保证在整个程序运行期间针对该类只存在一个实例对象 看一个实现了单例模式的类
3.6 static关键字 3.6.4 单例模式 类的构造方法使用private修饰,声明为私有,这样就不能在类的外部使用new关键字来创建实例对象了。 在类的内部创建一个该类的实例对象,并使用静态变量INSTANCE引用该对象,由于变量应该禁止外界直接访问,因此使用private修饰,声明为私有成员 为了让类的外部能够获得类的实例对象,需要定义一个静态方法getInstance(),用于返回该类实例INSTANCE。由于方法是静态的,外界可以通过“类名.方法名”的方式来访问
3.7 内部类 3.7.1 成员内部类 在一个类中除了可以定义成员变量、成员方法,还可以定义类,这样的类被称作成员内部类。 在成员内部类中可以访问外部类的所有成员 例程3-20中,Outer类是一个外部类,在该类中定义了一个内部类Inner和一个test()方法,其中,Inner类有一个show()方法,在show()方法中访问外部类的成员变量num,test()方法中创建了内部类Inner的实例对象,并通过该对象调用show()方法,将num值进行打印。从运行结果可以看出,内部类可以在外部类中被使用,并能访问外部类的成员
3.7 内部类 3.7.1 成员内部类 如果想通过外部类去访问内部类,则需要通过外部类对象去创建内部类对象,创建内部类对象的具体语法格式如下: 针对例程3-20中定义的Outer类写一个测试程序 运行结果同例程3-20一样,如图3-21所示。
3.7 内部类 3.7.2 静态内部类 可以使用static关键字来修饰一个成员内部类,该内部类被称作静态内部类,它可以在不创建外部类对象的情况下被实例化。创建静态内部类对象的具体语法格式如下:
3.7 内部类 3.7.3 方法内部类 方法内部类是指在成员方法中定义的类,它只能在当前方法中被使用。 例程3-23中,在Outer类的test()方法中定义了一个内部类Inner。由于Inner是方法内部类,因此程序只能在方法中创建该类的实例对象并调用show()方法。从运行结果可以看出,方法内部类也可以访问外部类的成员变量num。
3.8 Java的帮助文档 @author:用于对类的说明,表示这个程序的作者 @version:用于对类说明,表示这个程序的开发版本号 @param:用于对方法的说明,表示方法上定义的参数以及参数对应的说明 @return:用于对方法的说明,表示方法的返回值代表的意义 3.8.1 Java的文档注释 文档注释用于是嵌入到程序当中的帮助信息,用于说明如何使用当前程序,它以“/**”开头,以“*/”标志结束 Java中提供了javadoc命令,它可以将这些帮助信息提取出来,自动生成HTML格式的帮助文档,从而实现程序的文档化
3.8 Java的帮助文档 3.8.1 Java的文档注释 为程序添加文档注释后,便可以使用javadoc命令生成Person类的帮助文档。打开命令行窗口,进入程序所在的目录,输入生成文档的命令,具体如下所示: 其中: -d 用来指定输出文档存放的目录 . 表示当前的目录 -version 用来指定输出文档中需包含版本信息 -author 用来指定输出文档中需包含作者信息
3.8 Java的帮助文档 3.8.2 JDK帮助文档的使用 JDK帮助文档是Oracle公司针对JDK中所有的Java类提供的一整套帮助文档,它详细介绍了所有Java类的属性、方法、继承关系和示例用法等内容 JDK帮助文档通常有两种,一种是Oracle公司官方发布的HTML格式的JDK帮助文档,一种是由一些Java爱好者根据官方文档制作而成的CHM格式的JDK帮助文档。
本章小结 本章详细介绍了面向对象的基础知识,包括什么是面向对象、类和对象之间的关系、类的封装与使用、构造方法的定义与重载、this关键字、static关键字、内部类等 重点在于面向对象的编程思想、类和对象、类的封装、构造方法以及this关键字的使用,大家应该多思考,深刻领会面向对象的好处 熟练掌握好这些知识,有助于学习下一章的内容。深入理解面向对象的思想,对以后的实际开发也是大有裨益的