类与对象.docx
- 文档编号:12360994
- 上传时间:2023-04-18
- 格式:DOCX
- 页数:26
- 大小:31.21KB
类与对象.docx
《类与对象.docx》由会员分享,可在线阅读,更多相关《类与对象.docx(26页珍藏版)》请在冰豆网上搜索。
类与对象
类与对象
本章内容
⏹理解面向对象
⏹类的组成
⏹对象的生命周期
⏹修饰符
⏹初始化块
⏹包
一 理解面向对象
1.1 回顾对象
类,我们并不陌生。
在前几章的学习中,我们已经学习了一些类,例如String、BigInteger等。
我们知道如果获取一个字符串的长度;使用equals方法比较”a”与”b”的结果是false。
对象与基本类型不同,我们可以向其发送消息或请求。
可以看出,引用类型比基本类型的功能强大的多。
Java允许自定义类型(类),并创建及使用自定义类的对象。
这也是本章的主要内容。
学习JavaAPI类库中的类,与如何自定义类是本期课程的重中之重。
1.2 对象与类
⏹对象,万物皆为对象,对象是人们要进行研究的任何事物。
每个对象都有自己的行为与待征。
理论上讲,你可以抽取待求解问题的任何概念化构件(狗、建筑物、服务等),将其表示为程序中的对象。
⏹类,类型的同意词。
对具体对象进行概括,抽出它们的公共属性与行为并加以描述的结果。
类与类之间的最重要区别就是“可以发送什么样的消息给它”。
1.3 OOP
OOP(ObjectOrientedProgramming)即面向对象程序设计。
在OOP的世界里,程序是对象集合,它们通过发送消息来告知彼此所要做的。
OOP允许把待求解问题抽象为Java的对象。
1.3.1 OOP中的对象
要想使用OOP,一定要清楚对象的三个主要特性:
⏹对象的行为――可以对对象施加哪些操作,或可以对对象施加哪些方法。
⏹对象的状态――当施加那些方法时,对象如何响应。
⏹对象的标识――如何区分具有相同行为与状态的不同对象。
1.3.2 OOP有如下一些特性
⏹抽象――将实际待解问题的模型(位于“问题空间”内,这是问题存在的地方,例如一项业务)变为机器模型(位于“解空间”内,这是你对问题建模的地方)的过程。
在Java中抽象的结果就是类。
可以把抽象分为行为抽象与特征(数据)抽象。
⏹封装――将数据(特征)和行为结合在一个包中,并对对象的使用者隐藏了数据的实现方式。
对象中的数据被称为实例域,操纵数据的过程被称为方法。
对于每个特定的类对象实例都有一组特定的实例域值。
这些值的集合就是这个对象的当前状态。
⏹继承――特殊类的对象拥有其一般类的全部属性与行为,称作特殊类对一般类的继承。
其中一般类称为父类(基类、超类),特殊类称为子类(派生类、导出类)。
继承将在下一章中讲解。
⏹多态--一个对象的多种形态。
一个人类的对象,也是一个动物类的对象。
这就是一种多态性。
多态有编译期多态与运行期多态两种。
1.3.3 OOP的顶部
传统的过程化程序设计,必须从顶部的main函数开始编写程序。
当设计一个面向对象的系统时,没有所谓的“顶部”。
对于学习OOP的初学者来说常常会感觉无从下手。
答案是首先从设计类开始,然后再向每个类中添加方法。
1.3.4 使用OOP
在传统的结构化程序设计中,重点在于设计一系列的函数(或者说是算法)来解决某一问题。
在确定了函数以后,下一步是寻找合适的方法来存储数据。
算法总是排在前面,然后才是数据结构。
OOP颠倒了这种次序,把数据(类型)放在首位,然后才考虑在数据上操作的算法(类的方法)。
在订单处理系统中,有这样一些名词:
项目、订单、送货地址、付款、账户。
这些问题空间中的名词很可能会成为解空间中的类。
接下来,查看动词。
项目被添加到订单中。
订单被发送或取消。
订单货款被支付。
对于每个动词如:
“添加”、“发送”、“取消”以及“支付”,都要标识出主要负责完成相应动作的对象。
例如,当一个新的条目添加到订单中时,那个订单对象就是被指定的对象,因为它知道如何存储条目以及如何对条目进行排序。
也就是说,add应该是Order类的一个方法,而Item对象是一个参数。
当然,所谓“名词与动词”原则只是一种粗略的方法,在建立类的时候,哪些名词和动词是重要的完全取决于个人开发经验。
1.4 对象的接口
到目前为止,我们已经知道如果与对象沟通。
每个对象有一个接口(一组方法),通过这个接口,才能向对象施加操作。
同一类型的对象具有相同的接口。
你可以调用String对象的length方法,但你不能向BigInteger类的对象施加同样的方法。
这也就OOP封装性的体现,你无法操作对象的实例域(属性),而是通过接口(所有公共方法)向对象施加方法。
1.5 类之间的关系
在类之间,最常见的关系有以下三种:
⏹依赖(user-a)。
订单的支付方法需要使用账户类的对象,那么我们说订单类依赖账户类。
体现在类的方法于其参数类型或者方法内部的局部变量类型。
⏹关联(has-a)。
每个用户都有自己的帐户,那么我们说用户关联一个账户。
体现在类与其属性。
⏹继承(is-a)。
加急订单是一种特殊的订单,那么我们说加急订单继承于订单。
体现在子类与父类。
二 类的组成
编写一个自定义类型的目的是在你的项目中使用该类型的对象。
如果订单处理系统中的类(项目、订单、送货地址、付款、账户)象String和BigInteger一样存在于Java类库中,那么我们就无需去自行编写,而是拿来即用。
至于如何在main方法中组织这些对象,应该不是什么难题。
当然,在大多数项目中,还是需要自己编写类,所以我们需要了解一下类的组成。
类的主要组成部分是:
构造器(又叫构造方法或构造函数)、属性(又叫实例域、成员变量或成员数据)、方法(又叫实例方法或成员函数)。
2.1 构造器
2.1.1 创建对象
创建一个对象与创建一个基本类型的变量有所不同,BigIntegerbi;这条语句真的创建了一个对象了吗?
这只是创建了一个对象的引用。
创建一个对象需要使用new关键字与构造器。
你无法向一个对象施加操作(例如调用它的方法),而是通过对象的引用来操作对象。
每一个对象引用都应该有一个对应的对象实体。
BigIntegerbi=newBigInteger(“1”);
使用new的意思是“给我一个新对象”。
上面的语句会在堆内存中创建一个对象,并且返回一个对象引用,赋给bi。
2.1.2 定义构造器
构造器是特殊的方法,不过它与方法有几点不同:
⏹构造器的名字必须与类名字相同。
例如String类的构造器的名字必须是String。
⏹构造器没有返回值,就算是void也不可以。
⏹使用构造器时必须同new关键字一起使用,而不能通过成员运算符(.)来使用构造器。
classMyClass{
MyClass(){
......
}
}
根据需要也可以重载构造器。
classMyClass{
MyClass(){
......
}
MyClass(inti){
......
}
MyClass(inti,intj){
......
}
MyClass(Strings){
......
}
}
2.1.3 构造器的作用
可以这样理解,new是通知JVM,根据构造器说明的类图创建一个空白对象。
空白对象的属性被赋予默认值。
然后执行构造器内容。
构造器的作用是初始化对象,初始化对象就是给对象的属性初始化。
因为构造器总是要和new一起使用,所以可以理解为构造器会隐式的为所有属性赋予默认值。
2.1.4 默认构造器
当一个类没有提供任何构造器时,编译器会为其提供一个默认构造器。
默认构造器没有参数,并且方法体内没有任何语句。
2.2 属性
属性是类存储数据的载体,它可以表示一个对象的状态。
同一个类的对象,其属性的类型及个数是相同的,但它们都有自己的一份拷贝。
属性的类型可以基本类型,也可以是引用类型。
你可以定义一个Computer类,使其拥有一个CPU类的属性。
2.2.1 声明属性
声明一个属性与声明一个局部变量没有什么区别,可以直接为属性赋值,也可以不直接赋值。
但属性不是局部变量,属性必须是类直接包含的,而不是构造器或方法内部的局部变量。
classMyClass{
inti;
booleanb=false;
}
属性声明的位置不受限制,无论属性声明在哪个位置,该类的所有构造器与方法都可以访问它。
classMyClass{
MyClass(){
i=10;
b=true;
}
inti;
booleanb;
}
2.2.2 属性默认值
构造器会为属性赋予默认值:
⏹数值类型(byte、short、int、long、float、double)的默认值为0。
⏹字符类型(char)的默认值为’\0’。
⏹逻辑类型(boolean)的默认值为false。
⏹引用类型(非基本类型)的默认值为null。
2.2.3 访问属性
访问属性需要对象+成员运算符+属性。
一般类不支持直接访问属性值,而是通过对外接口(一组访问)来访问属性,因为这样会破坏类的封装性。
例4-1给出了一个访问对象属性的示例。
例4-1 FieldTest.java
publicclassFieldTest{
publicstaticvoidmain(String[]args){
MyClassmc=newMyClass();
System.out.println(mc.i+","+mc.b);
mc.i=100;
mc.b=false;
System.out.println(mc.i+","+mc.b);
MyClassmc1=newMyClass();
System.out.println(mc1.i+","+mc1.b);
}
}
classMyClass{
inti;
booleanb;
MyClass(){
i=10;
b=true;
}
}
2.3 方法
在第三章已经介绍过static方法了,本节要介绍的是实例方法。
实例方法就是没有使用static修饰符修饰的方法。
2.3.1 实例方法的声明与访问
实例方法的声明与static方法没有什么不同,只是实例方法不使用static来修饰。
访问一个实例方法与访问一个实例属性是相同的,需要使用对象+成员运算符+方法调用来访问。
例4-2给出了一个实例方法的示例。
例4-1 MethodTest.java
publicclassMethodTest{
publicstaticvoidmain(String[]args){
MyClassmc=newMyClass();
MyClassmc1=newMyClass();
mc1.i=100;
mc1.b=false;
mc.display();
mc1.display();
}
}
classMyClass{
inti;
booleanb;
MyClass(){
i=10;
b=true;
}
voiddisplay(){
System.out.println("i="+i+",b="+b);
}
}
2.3.2 实例方法的作用
通常实例属性是不可以直接访问的,而是通过实例方法来访问实例属性。
因为直接访问实例属性会破坏类的封装性。
实例方法总是需要访问实例属性,如果实例方法不需要访问实例属性,那么它就可以使用static修饰了。
2.3.3 this关键字
现在只看MyClass类,如果我问你display()方法被调用时,打印的结果是什么,你能回答么?
或者我问你调用一个字符串的length()方法的返回结果是多少,你能回答么?
你可能会反问我,你调用的是哪个字符串的length()方法?
如果是”abc”的length()方法,你会很快回答出是3。
原因是实例方法施加在不同对象上结果是不同的,对象本身就是一个实际参数,它被传递给方法的形式参数,而这个方法的形式参数的名字就是this。
voiddisplay(){
System.out.println("i="+this.i+",b="+this.b);
}
this关键字代表本类的当前对象,当你用a对象调用方法时,当前对象就是a。
对象a本身就是一个隐示参数。
this有三种用法:
⏹this可以引用实例属性,它表示当前对象的属性。
如果不使用this引用属性,默认与使用this相同。
如果在方法或构造器内有局部变量与属性同名时,引用属性必须使用this。
在不使用this时,表示引用的是局部变量。
⏹this可以引用方法,它表示当前对象的方法。
例如:
在MyClass类的构造器中调用display()方法。
如果不使用this引用方法,默认与使用this相同。
⏹this可以引用构造器,它只能在构造器中的第一句出现。
它表示委托另一个构造器来完成对象的初始化工作。
例4-2 MyClass.java
classMyClass{
inti;
booleanb;
MyClass(){
this(0,false);
}
MyClass(inti,booleanb){
this.i=i;
this.b=b;
}
voiddisplay(){
System.out.println("i="+this.i+",b="+this.b);
}
}
在例4-2中,有参构造器的形参与属性同名,所以引用属性时必须使用this。
在无参构造器中调用了有参构造器,它把初始化当前对象的工作委托给有参构造器。
编译器通过参数列表来查找委托的构造器。
三 对象的生命周期
对象(不是对象引用)被创建后,最终需要销毁它,不然所有对象都常驻内存会出现内存泄漏。
C++提供了析构器,当程序员不再使用某个对象时,可以调用析构器来回收对象。
但是这出现了一个问题,程序员有时并不知道对象在什么时候不再被使用。
Java使用自动垃圾回收机制,它使用垃圾回收器来监视用new创建的所有对象,并辨别那些不会再被引用的对象。
随后,释放这些对象的内存空间,以便供其他新的对象使用。
也就是说,你根本不必担心内存回收的问题。
你只需要创建对象,一旦不再需要,它们就会自行消失。
垃圾回收器并不是在出现“垃圾”时立即工作,如果Java虚拟机(JVM)并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存的。
原因是垃圾回收本身也会消耗很大的资源。
所以你并不能确定垃圾回收器在什么时候工作,甚至你的程序到结束都没有占用大量内存,而使垃圾回收器从未执行过。
垃圾回收器只回收Java自己创建的对象所占用的内存空间,而不能回收其它语言所占用的内存空间。
垃圾回收器也不能回收内存以外的资源,例如数据库连接、IO流等。
这些资源你需要手动关闭。
如果有需要,你可以调用System.gc()方法请求垃圾回收器马上工作。
过于频繁的调用该方法会使你的程序性能降低。
每个类都可以声明一个名字为finalize()的方法,垃圾回收器会在回收对象之前调用该对象的finalize()方法。
但你不能依赖finalize()方法为你做什么,因为你无法确定垃圾回收器什么时候工作,所以你也无法确定finalize()方法方法什么时候会被调用。
基本上你不需要finalize()方法做什么,只有一些特殊情况下才会用上finalize()方法。
例如调用本机方法释放其它语言使用的内存。
四 修饰符
4.1 访问修饰符
通常情况下,类的属性不希望被直接访问的,而是通过类的接口来访问。
这就需要使用访问修饰符。
下面我们通过一个Time类来了解访问修饰符。
例4-3 Time.java
publicclassTime{
inthour;
intminute;
intsecond;
Time(){
this(0,0,0);
}
Time(inthour,intminute,intsecond){
this.setHour(hour);
this.setMinute(minute);
this.setSecond(second);
}
intgetHour(){
returnhour;
}
voidsetHour(inthour){
if(hour<0||hour>23){
hour=0;
}
this.hour=hour;
}
intgetMinute(){
returnminute;
}
voidsetMinute(intminute){
if(minute<0||minute>59){
minute=0;
}
this.minute=minute;
}
intgetSecond(){
returnsecond;
}
voidsetSecond(intsecond){
if(second<0||second>59){
second=0;
}
this.second=second;
}
voiddisplay(){
StringhourStr=hour<10?
"0"+hour:
""+hour;
StringminuteStr=minute<10?
"0"+minute:
""+minute;
StringsecondStr=second<10?
"0"+second:
""+second;
System.out.println(hourStr+":
"+minuteStr+":
"+secondStr);
}
}
例4-3看似没有问题,但可以通过对象直接访问属性,这就破坏了封装性。
例如下面的语句把hour属性设置为非法值了。
classTimeTest{
publicstaticvoidmain(String[]){
Timet=newTime();
t.hour=100;
t.display();
}
}
访问修饰符private修饰的属性,只能在类内访问,类外无法访问。
当使用private修饰属性之后,main方法中试图访问类的私有属性将无法编译通过。
Java提供了四种访问修饰符,分别是:
public、protected、默认、private。
访问修饰符可以修饰类、构造器、属性及方法。
public
⏹修饰类--可供任何类使用。
一个java文件中只能有一个public类。
⏹修饰属性--可在其它类中通过对象直接访问的属性。
⏹修饰方法--可在其它类中通过对象访问的方法,
⏹修饰构造器--可在其它类中调用构造器来实例化对象。
protected
⏹修饰属性--可以在同一包下的其它类或子类中访问。
⏹修饰方法--可以在同一包下的其它类或子类中访问。
⏹修饰构造器--可以在同一包下的其它类或了类中访问。
默认
⏹修饰类--可以在同一包下的其它类中访问。
⏹修饰属性--可以在同一包下的其它类中访问。
⏹修饰方法--可以在同一包下的其它类中访问。
⏹修饰构造器--可以在同一包下的其它类中访问。
private
⏹修饰属性--可以在本类内部访问。
⏹修饰方法--可以在本类内部访问。
⏹修饰构造器--可以在本类内部访问。
包与继承的概念还没有学习,所以对于默认与protected访问修饰符的进入了解还需要等到学习了包与继承之后。
在本期课程中,通常用private修饰属性,public修饰方法。
4.2 static关键字
Static关键字可以用来修饰属性、方法。
所有用static修饰的属性或方法都与this(当前实例对象)无关。
⏹修饰属性--静态属性(类属性),所有对象共享同一个类属性,实例属性是每个对象都有自己的一份拷贝。
可以通过对象或类名访问,强烈建议学员使用类名访问类属性。
⏹修饰方法--静态方法(类方法),不访问实例属性的方法。
在方法中不能使用this关键字。
可以用来初始化类属性,或者不访问实例属性的算法。
可以通过对象或类名访问,强烈建议学员使用类名访问类方法。
类方法是类的方法,而不是某个对象的方法,使用类名来调用。
所以无法传递当前对象给this,就算是使用对象调用也不能传递当前对象给this。
如果需要在类方法中使用this(访问实例属性或方法),那么应该把它修改为实例方法。
类的静态方法如果不访问本类的静态属性,那么可以把它放到其它类中,也没有什么区别。
原因是它不会访问本类的任何数据(与类或对象的状态无关)。
不建议使用static属性,原因是所有对象共享同一个属性。
因为可能是造成数据的争抢。
而static方法比较常用。
我们最为熟悉的main方法就是一个static方法。
JVM调用main时无需使用任何对象,因为main方法是静态的。
事实上,在启动程序的时候还没有任何一个对象,静态的main方法将执行并创建程序所需要的对象。
4.3 final关键字
final关键字可以用来修饰类、属性、方法、局部变量。
final是本章中唯一可以修饰局部变量的关键字。
⏹修饰类--终极类,不能被其它类继承。
⏹修饰属性--属性只能被赋值一次,并且必须在构造器结束之前赋值。
⏹修饰方法--终极方法,不能被子类覆盖。
⏹修饰局部变量--常量,只能被赋值一次。
需要注意的是,final修饰的属性或局部变量可以是基本类型,也可以是引用类型。
其中对象的引用是可以使用的,例如施加方法等。
但不能再次为该引用赋值。
finalTimet=newTime();
t.setHour=10;
t=newTime(1,2,3);//error!
可以使用staticfinal来修饰属性。
这种属性为类常量,该属性只有一份,并且必须即刻赋值,而且不能再次赋值。
五 初始化块
前面介绍了类的基本组成部分有属性、构造器及方法。
其实类中还可以包含初始化块及内部类。
本小节介绍初始化块。
5.1 静态初始化块
静态初始化块也是类的组成部分之一,它由类直接包含。
其语法为static关键字后跟随代码块。
static{
......
}
类的加载
类的加载分为显示加载与隐示加载。
隐示加载发生在类的代码的初始使用,类的隐式加载生成在类第一次第使用的时候,例如创建第一个对象,或者第一次访问类的static属性或方法时。
显示加载需要使用Class类的静态方法forName()。
无论是显示还是隐示加载,JVM都会通过classpath找到要加载的.class文件,并把.class文件的内容加载到内存。
一旦一个类被加载之后,就无需再次加载。
假如我们要运行MainApp类的main方法,JVM首先需要加载MainApp类,然后才会调用main方法。
因为main方法是静态的,所以无需创建MainApp类的实例。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 对象