面向对象编程 (抽象类 最终类 权限修饰 对象) 第一天 第二天
抽象与封装是面向对象程序设计的两个重要概念。类将数据以及对数据的操作封装在一个抽象数据类型中,为属于该类的所有对象提供了同意的抽象描述。
理解抽象与封装的概念,掌握JAVA中的类与对象的应用,能够运用面向对象思想进行类的设计,正确设计类中的成员方法和成员变量,是我们学习的目标。
面向对象是一种新型的程序设计方法,或者说是一种心的程序设计规范,其基本思想是实用对象、类、继承、封装、消息等基本概念来进行程序设计。从显示世界中客观存在的事务(即对象)出发来构造软件系统,并且在系统构建中尽可能运行人类的自然思维方式。
面向对象的设计方法用对象描述事物,而每个具体的对象又可以用两个特征来描述:描述事物静态属性所需的数据结构以及对这些数据进行的有限操作。也就是说,把数据结构和对数据的操作放在一起构成一个整体,才能完整地反应实际问题。数据结构和对数据的操作实际上是相互以来不可分割的整体。
面向对象程序设计具有抽象、封装、集成和多态4个特点。抽象去掉了被研究对象中与主旨无关的次要部分,而仅仅抽取出与研究工作有关的实质性的内容加以考虑。抽象有两类:一类是过程抽象,另一类是数据抽象。
面向对象程序设计强调数据抽象,数据抽象把系统总需要处理的数据和这些数据上的操作结合在一起,根据功能、性质、作用等因素抽象成不同的抽象数据类型。每个抽象数据类型既包含数据,又包含针对这些数据的操作,是相对于过程抽象更为严格的抽象方法。
封装
封装就是利用抽象数据类型把数据和基于数据的操作封装在一起,数据被保护在抽象数据类型的内部,系统的其他部分只有通过数据的操作,才能够于这个抽象数据类型进行交互,封装包含两层含义。
第一,把对象的全部属性及其行为结合在一起,形成一个不可分割的独立单位(即对象)。
第二,信息隐蔽,即尽可能隐蔽对象的内部细节,对外形成一个边界(或者说形成一道屏障),只保留有限的对外接口,使之与外部发生联系。
封装的原则在软件上的反应是:对象以外的部分不能随意存取对象的内部数据(属性),从而有效地避免了外部错误对他的“交叉感染”,使软件错误能够局部化,大大减少查错和排错难度。
在面向对象的程序设计中,抽象数据类型是用“类”这种面向对象工具表示的,每个类里都防撞了相关的数据和操作。封装性降低了程序开发过程的复杂性,提高了效率和质量,保证了数据的完整性和安全性。同时,封装性提高了抽象数据类型的可重用性,使抽象数据类型称为一个结构完整、能够自行管理的有机整体。
类
把众多的事物归纳、划分成一些类,是人类在认识客观世界时经常采用的思维方法。分类的原则是抽象。类是一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和服务两个主要部分。在面向对象的编程语言中,类是一个独立的程序单位行为。类的实例化结果就是对象,而对一类对象的抽象就是类。
类的定义
类是JAVA的核心和本质,想要在JAVA程序中实现的每个概念,都必须封装自爱类中。类是具有共同属性和行为的对象的抽象,属性描述了对象的状态,以数据的形式存在,在JAVA面向对象程序设计中又贝称为变量。行为描述了对数据进行的操作,可引起对象状态的改变。在JAVA面向对象程序设计中贝称为方法。
JAVA中类的实现包括两个部分:类头和类体。类头定义的格式如下:
[类定义修饰符] class <类名> [extends <父类名>] [implements <接口列表>]
类的定义主要包括关键词 class 和类名。类名是JAVA语言合法的标识符,类名一般具有一定的含义。其中,CLASS为定义类的关键字,extends是用于继承的关键字,implements是用于实现接口的关键字,有关类的修饰符后面将详细阐述。
类体是类的主体部分,它包含了所有实现类功能的JAVA语言程序代码,包括成员变量和成员方法。JAVA类除了继承它的父类中的变量和方法外,一般仍会在类体中明确定义自己的变量和方法。程序中,变量说明应放在定义之前。类体要用{}括起来。类体的格式如下:
class <类名>{
[变量修饰符] <变量内省> <变量名>
[方法修饰符] <方法返回值类型> <方法名>([<参数列表>]){
方法体
}
}
其中,变量名是JAVA语言中的合法标识符,变量的类型可以是JAVA给定的任意数据类型或者用户自定义类。用于说明变量和方法的访问权限等属性。
一个简单的学生类:
//**********Student.java*********
//学生类的定义
import java.io.*; //加载系统输入输出类
public class Strdent{
/*类成员的定义*/
private String name; //成员变量
private String sex; //成员变量
rivate int number; //成员变量
void setValue(String a, String b, int c)//成员方法
{
name = a;
sex = b;
number = c;
}
public int getNumber() //成员方法
{
return number;
}
public static void main(String args[]) //成员方法
{
//打印输出一行字符
System.out.println("这是一个学生类的定义!");
}
}// Student 类结束
//代表JAV语言中的单行注释。
/* */ 之间的部分是多行注释。
类的修饰符
类的修饰符说明类的性质和访问权限,分为访问控制符和非访问控制符,包括
public 、protected 、 private 、abstract 和 final 。
1. public , protected 和 private
public , protected 和 private 说明类的访问权限,属于访问控制符。
用public修饰的类,不仅能贝同一包中其他类访问,还能贝包之外的类和对象使用。
protected 修饰的类为保护类,可贝同包类和异包子类访问。
private 修饰符的类为似有类,外界无法访问。
共有类的定义。
//*******TestPublic.java*******
//共有类的定义
import java.io.*; //加载系统输入输出类
public class TestPublic
{
/*每个类的main()方法是该程序的入口,而包含main方法的类是程序的主类。
main()方法的形式是 public static void main(String args[]) */
public static void main(String args[])
{
System.out.println("这是一个共有类");
}
}
下面定义一个名为TestProtectedAndPrivate的类来测试保护类和似有类。
保护类和似有类的定义:
//*************TestProtectedAndPrivate.java**********
//保护类和似有类的定义
import java.io.*; //加载系统输入输出类
public class TestProtectedAndPrivate{
//主方法
public static void main(String args[]){
//分别创建保护类和似有类的对象t1和t2
TestProtected t1 = new TestProtected();
TestPrivate t2 = new TestPrivate();
//打印输出两行字符
System.out.println(t1.toString());
System.out.println(t2.toString());
}
protected static class TestProtected //保护类
{
public String toString(){
return "保护类的定义:作为潜逃类的内部类";
}
}
private static class TestPrivate //似有类
{
public string toString(){
return "私有类的定义:作为嵌套类的内部类";
}
}
} // TestProtectedAndPrivate 类结束
aabstract
abstract 说明一个类为抽象类,所谓抽象类是指不能直接实例化对象的类。如果一个抽象类贝说明了,则这个类中将包括一个或几个抽象方法。所谓抽象方法是指该方法只有方法说明却没有方法体,即没有具体实现的代码。抽象类本身不具备实际功能,它只用于衍生子类。定义抽象类和抽象方法的目的是建立抽象模型。抽象类中不一定包含抽象方法,但一旦某个类中说明了抽象方法,该类必须说明为抽象类。
不包含抽象方法的抽象类的定义。
//*************TestAbstract.java*************
//不包含抽象方法的抽象类
import java.io.*; //加载系统输入输出类
public abstract class TestAbstract{
//该类不包含抽象方法
//主方法
public static void main(String args]){
//打印输出两行字符
System.out.println("这是一个不包含抽象方法的抽象类!");
System.out.println("抽象类不能创建对象!");
}
} // TestAbstract 类结束
包含抽象方法的抽象类的定义。
//********** Mankind.java **********
//包含抽象方法的抽象类
import java.io.*; //加载系统输入输出类
public abstract class Mankind{
//定义4个抽象方法
public abstract void closing();
public abstract void food();
public abstract void shelter();
public abstract void transportation();
//主方法
public static void main(String args[]){
//打印输出两行字符
System.out.println("这是一个包含抽象方法的抽象类");
System.out.println("抽象方法没有方法体");
}
}// Mankind 类结束
final
final 修饰的类又称为最终类,是指该类不能被继承,不能再有子类。它的目的是为了避免盲目继承。一个final类无法贝继承,意味着此类在一个继承树中是一个叶子类,并且此类的设计已贝认为很具体而不需要进行修改或扩展。对于final类中的成员变量,可以定义其为final,也可以不是final。而对于方法,由于所属类为final的关系,自然也就成了final类型。也可以明确地给final类中的方法加上一个final关键字。下面定义一个名为TestFinal的最终类。
//************* TestFinal.java ***************
//最终类的定义
import java.io.*; //加载系统输入输出类
public final class TestFinal
{
//主方法
public static void main(String args[]){
//打印输出一行字符
System.out.println("这是一个最终类,不能被继承");
}
}//TestFinal类结束
因为抽象类的目的就是为了被继承,而最终类的目的是不让其贝继承,所以一个类是不能用 abstract 和 final 同时修饰的。
对象
对象是具有某些特俗属性(数据)和行为方式(操作)的实体。对象可以是有生命的个体,比如一个人或一只老虎;还可以是无生命的个体,比如一辆汽车或一台计算机;也可以是一个抽象的概念,如天气的变化或鼠标产生的事件。对象有两个特征:属性(property)和行为(Behavior)。例如,一个人的属性有姓名、性别、年龄、身高、体重等;行为有唱歌、打球、骑车和学习等。
类是一个抽象的概念,而对象是一个具体的概念,是类实例化的结果。对象通过消息传递来进行交互。消息传递即激活指定的某个对象的方法以改变其状态或让它产生一定的行为。
对象的生命与引用
同基本内置类型一样,为了声明对象,首先必须写出类型名,然后写出该类型的所有变量的名字,中间用逗号隔开。下面举例说明怎样生命一个类的对象:
Student s1, s2, s3;
上面的生命表示s1\s2\s3是 Student 类型的引用变量,可以用来引用Student型的对象。引用变量的值将对应一个内存地址,这个地址标识的空间用来存放一个Student对象。
声明对象的引用变量,并不等于创建对象,当然更没有为对象分配存储空间。这些需要通过 new 关键词和对引用变量的赋值才能实现。
创建对象与定义构造方法
在JAVA语言中,一个JAVA对象是类的一个实力,创建对象的过程是对类的实例化过程,实例化对象就是创建一个对象。实例化对象意味着给对象分配必要的存储空间,用来保存对象的变量和方法大妈。new 运算符用于创建一个类的实例并返回对象的引用,即用来实现对象的实例化。一般格式为:
<类名> <对象名> = new <类名>(<参数列表>);
创建对象与生命基本数据类型的变量相似,首先说明新建对象所属的类名,然后说明该对象名,赋值号左边的NEW 关键字是用于为新建对象开辟内存空间的运算符。与变量相比,对象占用的内存空间要大得多,因为对象既包含变量又包含方法,多数情况下,变量与方法还不止一个。例如前面定义的学生类 Strdent,假设有一个对象s1,则它就有3个属性和2个方法: s1.name ,s1.sex, s1.number, s1.setValue(), s1.getNumber() 。这些变量和方法保存在一块内存中,这块内存就是对象s1 所占用的内存。如果在新建另一个 Strdent类的对象 s2,则s2对象将在内存中拥有自己的与s1对象不同的位置相同大小的内存空间。
创建对象的同时要调用这个对象的构造方法完成对象的初始化工作,JAVA语言中每一个类都有构造方法,该方法是一种特殊的方法,其特殊性主要体现在以下几个方面。
(1) 构造方法的名称与类的名称相同。
(2) 构造方法不返回任何数据类型,也就没有返回值。
(3) 构造方法的修饰符只能是访问修饰符,即 public ,private,protected 中的任一个。
(4) 构造方法只能由 new 运算符调用,一般不能由编程人员显式地直接调用。
(5) 构造方法不能从父类中继承。而在构造方法中可以调用但前父类和其父类的另一个构造方法,调用语句必须是构造方法的第一条语句。使用当前类的构造方法用this来引用,使用其父类的构造方法用 super 来引用,在构造方法的实现中,可以进行方法的重载。
下面定义一个 Student 类,该类具有两个构造方法,方法的名称都是 Student,与类名相同。
public class Student{
//成员变量
private String name;
private String sex;
private int number;
//成员方法
Student() //构造方法
{
name = "NO name";
sex = "unknown";
number = 0;
}
Student(String a, String b, int c)//构造方法
{
name = a;
sex = b;
number = c;
}
}// Student 类结束
构造方法的重载。
//************* TestConstructor.java ***************
//构造方法的重载
import java.io.*; //加载系统输入输出类
class Constructor{
//成员变量
private int x;
private double y;
//没有参数的构造方法
Constructor()
{
x = 0;
y = 0.0;
}
//一个参数的构造方法
Constructor(int x){
this.x = x; //使用 this 关键字标识成员变量,以区别于同名参数
}
//一个参数的构造方法,参数类型于前一个构造方法不同
Constructor(double y){
this.y = y;
}
//两个参数的构造方法
Constructor(int x, double y){
this.x = x;
this.y = y;
}
// print() 方法显示成员变量
void print()
{
System.out.println("x=" +x+" y="+y);
}
} // Constructor 类结束
// ConstructorTest 类
public class TestConstructor
{
//主方法
public static void main(String args[]){
//创建 Constructor 类的对象 c1 c2 c3 c4 分别使用了不同的构造方法
Constructor c1 = new Constructor();
Constructor c2 = new Constructor(53);
Constructor c3 = new Constructor(6.98);
Constructor c4 = new Constructor(5,7.8);
//在屏幕上输出
c1.print();
c2.print();
c3.print();
c4.print();
}
} // TestConstructor 类结束
对象的使用
通过运算符“.”可以实现对对象中成员变量的访问和成员方法的调用。可以通过设定成员变量和成员方法访问权限来限制对它的访问。
调用对象的成员变量的一般格式为:
<对象名>.<成员变量名>
其中,对象名是一个已生成的对象,也可以是能生成对象的表达式。
例如:
s1.name = "李明";
int num = new Student().number;
调用对象的成员方法的一般格式为:
<对象名>.<成员方法名>([<参数列表>])
其中,参数列表是可选项。在进行对象的方法引用时,方法中参数的个数、参数的数据类型与原方法中定义的要一致,否则编译器会出错。
对象的使用示例。
//************** Student2.java ***************
//对象的使用
import java.ioo.*;
public class Student2{
private String name; //姓名
private String sex; //性别
private int number; //学号
Student2() //无参构造方法
{
name = "No name";
sex = "unknown";
number = 0;
}
Student2(String a, String b, int c){
name = a;
sex = b;
number = c;
}
public void setName(String a){
name = a;
}
public void setSex(String b){
sex = b;
}
public void setNumber(int c){
number = c;
}
public String getName(){
return name;
}
public String getSex(){
return sex;
}
public int getNumber(){
return number;
}
public static void main(String args[]){
Student2 s1 = new Student2();
Student2 s2 = new Student2("李明","男",102305);
System.out.println("对象的使用");
System.out.println("-------------------------");
System.out.println("第一个学习的姓名:"+s1.getName());
System.out.println("第一个学生的性别:"+s1.getSex());
System.out.println("第一个学生的学号:"+s1.getNumber());
System.out.println("-------------------------");
System.out.pringln("第二个学生的姓名:"+s2.getName());
System.out.println("第二个学生的性别:"+s2.getSex());
System.out.println("第二个学生的学号:"+s2.getNumber());
}
} // Student2 类结束
在定义一个类时,类中的成员变量也可以是一个类,例2-9中包含2个类:Data类和Person类,Person类的成员变量birth为Data类型。
//************* Person.java **************
//对象作为类的成员变量
import java.io.*;
public class Date
{
int year;
int month;
int day;
Date(int year, int month, int day){
this.year = year; //用关键字this标识类成员变量,一面与同名参数混淆
this.month = month;
this.day = day;
}
void showDate(){
System.out.println(year+","+month+","+day);
}
}//Date类结束
public class Person
{
private String name;
private String sex;
private Date birth; //对象作为类的成员变量,默认初始值为null
void setValue(String s1, String s2, int y, int m, int d){
name = s1;
sex = s2;
birth = new Date(y,m,d);
}
void showValue(){
System.out.println("姓名:"+name);
System.out.println("性别:"+sex);
System.out.print("出生年月日:");
birth.showDate();
}
public static void main(String args[]){
Person per = new Person();
per.setValue("李明","男",1975,7,8);
per.showValue();
}
}//Person 类结束
finalize() 是销毁一个对象用的
成员变量
成员变量是类和对象的数据属性,其本身也有一些修饰符:访问控制符 public private protected 以及非访问控制符 static final transient volatile.这里主要探讨常用的static 和final这两个非访问控制符。
静态变量
使用关键字static 生命的成员变量称为静态变量。静态变量也称类变量(class variable),它与类关联,由类钟的所有对象共享。无论生成多少个这种类的对象,静态变量都只有一份,并且即使没有生成任何对象,这种变量也存在。静态变量之所以称为类变量,是因为这种变量是属于相应类的,而不是属于特定对象的,他们在该类的全部对象中共享。静态变量可以由任何该类的对象和类引用,当某个对象修改了静态变量的值后,其他对象在访问静态变量时,取得的将是改变后的新值。
相对地,没有用static修饰的普通成员变量称为实例变量(instance variable)。在类中,实例变量与每个对象都唯一关联,每个类的对象将拥有自己的一份实例变量的复制,并且赋予他自己的值。实例变量使一个对象与其他对象相互区别,使对象具有自己的个性。
下面定义一个学生 Student 类,其中包含2个静态变量和3个实例变量。
public class Student
{
static String school = "黑龙江科技学院";
static String classNumber;
//实例变量
String name;
string sex;
int number;
Student(){
name = "No name";
sex = "unknown";
number = 0;
}
Strdent (String a,String b, int c) //构造方法
{
name = a;
sex = b;
number = c;
}
}//Student类结束
最终变量
使用关键字final声明的成员变量称为最终变量,更确切地说是常量。只不过为他们取了一个类似变量的标识符名字。用final修饰符声明常量时,需要说明常量的数据类型并要初始化具体的数值。另外,因为所有类对象的常量数据成员数值都一样,为了节省内存控件,常量通常声明为static。 例如下面球体类中的常量PI:
public class Sphere
{
//常量
static final double PI = 3.14;
//变量
double xCenter;
double yCenter;
double zCenter;
double radius;
}// Sphere 类结束
成员方法
成员方法是类的行为,标志着类所具有的功能,是对类中成员变量的操作。JAVA中方法的一般格式如下:
[方法修饰符] [返回值类型] <方法名> ([<形式参数列表>]) [throws <异常列表>]
{
<方法体>
}
成员方法是具有相对独立性的功能模块,是一个类与外界进行通信的接口。成员方法也有一些修饰符:访问修饰符public private procected 和非访问控制符 static abstract final native synchronized 下面分别探讨这几个非访问控制符
静态方法
使用那个关键字static声明的成员方法称为静态方法。静态方法也称为类方法(class method),它与类关联,是属于整个类的,即使类中一个对象也不存在,也可以执行该类的类方法。正式因为在没有对象存在的情况下也可以执行类方法,因此它只能调用类变量,即静态方法只能处理静态变量,而不能处理类中的实例变量。很明显,实例变量是属于某个特定对象的,如果对象不存在,实例变量也就不存在,这时用类方法去操纵和处理一个可能不存在的变量肯定会出问题。实际上,JAVA编译器根本就不允许做这样的尝试。如果在一个类方法中引用了一个实例变量,系统将拒绝编译,从而出现出错信息。
相对地,未用static 修饰的普通成员方法称为实例方法(instance method)。实例方法只能针对特定的对象执行,因此如果任何对象都不存在,也就没有可以执行的实例方法。
静态方法实例:
//************** StaticFunction.java ***************
//静态方法
import java.io.*; //加载系统输入输出类
public class StaticFunction
{
static int classVariable; //静态变量,即类变量
int instanceVariable; //非静态变量,即实例变量
// setClassVariable()方法为静态方法,即类方法
static void setClassVariable(int i){
//this.classvar = i; 不能使用this
classvariable = i;
//instanceVar=i; 不能访问实例变量
}
void setInstanceVar(int i){
instanceVariable = i;
classvariable = i;
}
//MAIN方法也为静态方法
public static void main(String a[]){
StaticFunction sf = new StaticFunction();
/*
下面的语句不合法,main()方法是类方法,不能在类方法中直接调用实例方法
setInseranceVariable(58);
*/
sf.setInstanceVariable(58);//在类方法中只能通过实例对象访问实例方法
sf.setInstanceVariable(58);
setInstanceVariable(58);
StaticFunction.setClassVariable(55);
System.out.println("静态方法中不能直接调用实例方法,只能通过对象访问实例方法");
}
}
抽象方法
使用关键字abstract声明的成员方法称为抽象方法。抽象方法只有方法头,没有方法体。抽象方法的声明以一个分号结束。
抽象方法示例。
//*************** AbstractFunction.java**************
//抽象方法
import java.io.*;
abstract class Mankind
{
protected String type; //保护数据成员用于继承
public abstract void closing(); //抽象方法
public abstract void food(); //抽象方法
public abstract void shelter(); //抽象方法
public abstract void transportation(); //抽象方法
public String toString(){
return "这是一个"+type;
}
}//Mankind类结束
abstract class people extends Mankind
{
//重写Mankind类的抽象方法colsing()
public void closing(){
System.out.println("学会穿衣了!");
}
//重写Mankind类的抽象方法food()
public void food(){
System.out.println("学会吃饭了");
}
}//People 类结束
class Person extends People{
//重写People类的抽象方法shelter()
public void shelter(){
System.out.println("盖房子用来居住");
}
//重写people类的抽象方法transportation()
public void transportation(){
System.out.println("使用交通工具出行");
}
//构造方法
public Person(String aType){
type = new String(aType);
}
}// Person类结束
public class AbstractFunction{
//AbstractFunction类的主方法
public static void main(String args[]){
System.out.println("不能创建抽象类Mankind和People的对象!");
System.out.println("");
Person man = new Person("男人");
Person woman = new Person("女人");
System.out.println(man);
man.closing();
man.food();
man.shelter();
man.transportation();
System.out.println(" ");
System.out.println(woman);
woman.closing();
woman.food();
women.transportation();
}
}//AbstractFunction 类结束
最终方法
使用关键字final声明的成员方法称为最终方法。最终方法的功能和内部语句不能在被更改,也就是说该方法不能贝当前类的子类重新定义。最终方法使得子类不能重新定义与父类同名的方法,而只能使用从负累继承来的方法,从而防止了子类对父类一些方法的错误定义,保证了程序的安全性和正确性。
本地方法
。。。。。。。。。。。。。。