4.1 面向对象基本概念
4.1.1 对象
对象:相关数据和方法的集合
4.1.2 封装
封装的含义是把类设计成一个黑箱,使用者只能看见类中定义的公共方法,而看不到方法实现的细节,也不能直接对类的数据进行操作,迫使用户通过接口去访问数据。
封装是一种信息隐蔽技术,封装的定义为:
(1)一个清楚的边界;所有对象的内部软件的范围被限定在这个边界内;
(2)一个接口:这个接口描述该对象和其他对象之间的相互作用;
(3)受保护的内部实现:这个实现给出了由软件对象提供的功能的细节,实现细节不能在定义这个对象的类的外面访问。
4.1.3 消息
对象的行为由方法来实现,消息传递是对象之间进行交互的主要方式。
构成消息的3个要素是:接收消息的对象、接收消息后进行处理的方法和方法所需要的参数。
4.1.4 类
类是一种复杂的数据类型,是将不同类型的数据和这些数据相关的运算(即方法)封装在一起的集合体。它是对所要处理的问题的抽象描述。
4.1.5 继承
一个类的上一层称为父类,而下一层称为子类,一个类可以继承其父类的变量和方法,且这种继承具有传递性。
4.1.6 接口
接口可以看成是为两个不相关的提供交流途径的工具,是一个包含方法定义和常量值的集合。
Java不支持多继承,子类只能有一个父类,有时需要使用其他类中的方法,但又无法直接继承,这时可以使用接口技术。
接口不需要建立继承关系,就可以使两个不相关的类进行交互。
体现面向对象思想的程序:见P60
4.2 Java的类与对象
Java程序的所有数据类型都是用类来实现的,Java语言是建立在类这个逻辑结构之上的,所以Java是一种完全面对象的程序设计语言。类是Java的核心,Java程序都由类组成,一个程序至少包含一个类,也可以包含多个类。对象是类的实例,Java程序中可以使用标识符表示对象,并通过对象引用类中的变量和方法。
Java程序员的任务就是设计出类和对象来解决实际问题。创建类时既可以从父类继承,也可以自行定义。
4.2.1 类的创建
格式: [修饰符] <class> <类名> [extends 父类] [implements接口]
{ 类体(成员变量和成员方法) }
修饰符:指明类在使用时所受到的限制,包括类访问权限[public]和其他特性[abstract],[final]
例:public class C1 { …… }
public class C2 extends Applet implements ActionListener
{……}
1.class <类名>
告诉编译器这是一个类
2.Public(公共的)
在没有任何修饰符的默认情况下,类只能被同一个源程序文件或同一个包中的其他类使用,加上public后,该类可以被任何包中的类使用,称为公共类。
注意:在同一个源程序文件中不能出现两个及以上的public类,否则编译器会告诉你将第二个public类放在另一个文件中。
3.abstract(抽象的)
abstract说明的类称为抽象类,不能用它实例化一个对象,它只能被继承,abstract方法是不含代码的方法,需要以后的子类中重载实现,abstract类的子类必须实例化(实现)abstract方法,或将自己也声明为abstract。这对于定义概念是有用的。
4.final(最终的)
final说明的类称为最终类,最终类不能被继承
Java中的String和Array类就是一个final类
注意:final和abstract不能同时修饰一个类(出错),这样的类没有意义
无修饰符是默认方式,即不使用上述三种修饰符。因此它们的优势和限制都没有作用,无修饰符的类可以被其他类访问和继承,但只有在相同程序包中的那些对象才可能使用这样的类。
5.extends(继承)父类名
extends告诉编译器创建的类是从父类继承下来的子类,父类必须是Java系统类或已经定义的类。
从父类继承,可以提高代码重用性,不必从头开始设计程序。
6.implements(实现)接口名
implements告诉编译器类实现的接口,接口必须有定义,一般为系统类。
接口是消息传递的通道,通过接口,消息才能传递到处理方法中进行处理。implements说明你的类可以实现的一个或多个接口,如果有多个接口,要用逗号分隔。
例:import java.awt.*;
import java.applet.*;
public class ch3 extends Applet{
public void paint(Graphics g) {
g.drawString("Hello Java!I love you^_^",25,25); } }
<html>
<head>
<title>Hello Java Applet测试</title>
</head>
<APPLET code=ch3.class width=200 height=40>
抱歉,你的浏览器不支持JAVA APPLET┅
</APPLET>
</html>
4.2.2 对象的创建
类的对象的模板,Java运行应该是用类创建实例化对象。
一旦任务完成,对象就会被垃圾收集器收回,完成它从创建、使用到清除。
见P66 例4.3
1.创建对象与构造方法
创建对象格式: 类名 对象名=new 类名([参数]) ;
说明:(1)运算符new为对象分配内存空间;
(2)生成对象的最后一步是执行构造方法;
(3)创建对象相当于定义一个变量,即分两步进行。
创建对象分成两步: 类名 对象名 ;
对象名=new 类名([参数]) ;
2.对象初始化的说明
(1)系统如何对变量初始化
当用new创建了一个对象时,系统会为对象中的变量进行初始化。即不但为变量分配相应的存储单元,还设置相应初值。系统为byte , short , int , long类型设置初值为0;float类型变量初值为0.0f;double类型变量初值为0.0;char字符型变量为’u\0000’;boolean逻辑变量初值为false;引用类型初值勤为null。
(2)构造方法的作用与构成
new操作符为对象分配内存后将调用类的构造方法确定对象的初始状态,初始化所有变量。
构造方法功能:创建对象时,用给定的值,将对象初始化
构造方法特点:
(1)构造方法名与类名相同,且不指定类型说明;
(2)可以重载,即可以定义多个参数个数不同的函数,系统可以根据参数的不同,自动调用正确的构造方法;
(3)程序中不能直接调用构造函数,在创建对象时系统自动;
(4)可以不设计构造方法,若在初始化时还要执行一些其他命令,就必须设计构造方法,因为Java规定命令语句不能出现在类体中,只能放在方法中。
重载:参数不同可以是数量不同,类型不同,或两者都不同,但重载方法必须有相同的方法名和相同的返回类型。
例:class ABC{
public ABC( ) {……} // 无参数构造方法
public ABC(int a, int b ) {……} // 带两个参数构造方法
public ABC(String a ) {……} // 带一个个参数构造方法
public int ABC(int a) {……} // 错,构造方法不能有类型返回值
public void ABC( ) {……} // 错,构造方法不能有类型返回值
}
例:public class SC{
public static void main(String args[]){
sc1 a=new sc1( ) ;
sc1 b=new sc1("练习") ;
System.out.println("程序结束!");} }
class sc1{
public sc1( ) {System.out.print("开始");}
public sc1(String z) {System.out.println(z);} }
运行结果:开始练习
程序结束
3.对象的使用
格式:<对象名>.<变量名>
<对象名>.<方法名([参数])>
例:SC a =new SC( ) //SC是已定义的类,a则是新建的SC对象
a.bian=23 // 将对象a的变量bian赋值23
a.Fan( ) // 调用对象a的方法Fan
例:class parents{
private String name[]=new String[5];
parents(String s[])
{for(int i=0;i<s.length;i++)
name[i]=s[i]; }
public void showname()
{for(int i=0;i<s.length;i++)
System.out.print(name[i]+” ”);
}}
class SC
{ public static void main(String args[])
{ String name[]={"Zhang","Wang","Li"};
parents p=parents(name);
p.showname()
}}
结果: 输出 Zhang Wang Li
例:public class NewClass{
public static void main(String args[]){
G k=new G();
k.setK(8);
int y=k.getK();
System.out.println(“y=”+y); }
}}
class G{
private int k;
public void setK(int x){
k=x; }
public int getK(){ return k;}
}}
运行结果: y=8
4.清除对象
Java引入了新的内存管理机制,由Java虚拟机担当垃圾收集器的工作。你可以任意创建对象,而不用担心如何清除它们,垃圾收集器会自动清除它们。
如果要明确地清除一个对象,可以自行清除它。只需把一个空值赋给这个对象引用即可。如
SC a =new SC( )
……
a=null // 将对象a从内存中清除
5.析构方法(finalize)(补充)
析构方法(finalize)与构造方法相对应,当对象已经无用,需要清除时,编译器将自动调用对象的finalize方法。析构方法一般是做一些清除工作,如关闭打开的文件等,但不能执行用户输入操作或与其他对象进行交互。
如 class ABC{
……
protected|public void finalize( )
{ // do some cleanup code } }
4.3 成员变量与封装
成员变量描述了类和对象的状态,有时也称为属性、数据或域
4.3.1 成员变量的声明
成员变量的声明必须放在类体中,通常是在成员方法之前。在方法中声明的变量不是成员变量,而是方法的局部变量,二者是有区别的。
例:见P71
4.3.2 成员变量的修饰
声明变量格式:[public][private][protected][package] //访问控制修饰符
[static][final][transient][volatile]<数据类型><成员变量名称>
1.访问控制权限
表: 修饰符的作用范围
修饰符
类
子类
包
所有类和包
Public
√
√
√
√
Private
√
Protected
√
√
√
package(默认)
√
√
(1)public(公共)变量
由public修饰的变量称为公共变量,可被任何包中的任何类访问
(2)private(私有)变量
由private修饰的变量称为私有变量,只能被声明它的类所使用
(3)protected(受保护)变量
由protected修饰的变量称为受保护变量,可被声明它的类和派生的子类以及同一个包中的类访问
(4)package(包)变量
由package修饰的变量称为包变量,没有修饰符时,默认变量即是包变量,可被声明它的类和同一个包中的其他类(包括派生子类)访问
2.static(静态)变量
由static修饰的变量称为静态变量,静态变量是类固有的,可以直接引用(方法:类名.静态变量名),其他成员变量仅仅被声明,只有等到生成实例对象后才存在,才可以被引用,静态变量也称为类变量,非静态变量称为实例变量。相应地静态方法称为类方法,非静态方法称为实例方法。
静态变量可被此类创建的所有对象共享。
例:class Car {
String 车型;
static int 价格;
public Car(String 车型, int 价格)
{ this.车型=车型;
this.价格=价格; }
public void 介绍(String s)
{System.out.println(s+"\t"+车型+"\t"+"价格 "+价格);} }
public class SC{
public static void main(String args[]){
Car 奔驰=new Car("越野车",400000) ;
奔驰.介绍("奔驰");
Car 红旗=new Car("轿车",200000) ;
红旗.介绍("红旗");
奔驰.介绍("奔驰"); }}
运行结果:奔驰 越野车 价格 400000
红旗 轿车 价格 200000
奔驰 越野车 价格 200000
3.final(最终)变量
一旦成员变量被声明为final,在程序运行中将不能被改变,即是一个常量。
如: final int I=5 ;
final double PI=3.1415926 ;
4.transient(过渡)变量(不做要求)
Java目前对transient修饰符没有明确说明,它一般用在对象序列化(object serialization)上,说明成员变量不许被序列化。
5.volatile(易失)变量(不做要求)
volatile声明的成员变量为易失变量,用来防止编译器对该成员进行某种优化。这是Java语言的高级特性,仅被少数程序员使用。
4.4 成员方法
对象的行为由类的方法实现,实际上,方法就是完成某种功能的程序块。从功能上讲,方法和函数十分类似。
4.4.1 成员方法的设计
例:见P77
4.4.2 成员方法的声明与修饰
格式:[public][private][protected][package][static][final][abstract][native]
[synchronized] 返回值类型 方法名(参数表)[throws 异常类型]
说明:[public][private][protected][package][static] [final]与成员变量功能相同,都是定义方法访问权限。
1.final(最终)方法
方法被声明为最终方法后,将不能被子类覆盖,即最终方法,能被子类继承和使用但不能在子类中修改或重新定义它。这种修饰可以保护一些重要的方法不被修改。
在OOP中,子类可以把父类的方法重新定义,使之具有新功能但又和父类的方法同名、同参数、同返回值,这种情况称为方法覆盖(override)。
2.abstract(抽象)方法
抽象方法即无方法体的方法,抽象方法不能出现在非抽象类中。
为什么要使用抽象类和抽象方法呢?一个抽象类可以定义一个统一的编程接口,使其子类表现出共同的状态和行为,但各自细节是不同的。子类共有的行为由抽象类中的抽象方法来约束,而子类行为的具体细节则通过抽象方法的覆盖来实现。这种机制可增加编程的灵活性,也是OOP继承树的衍生基础。
3.native(本地)方法(不做要求)
用其他语言编写的方法在Java中称为本地方法。
SDK提供了Java本地接口JNI(Java Native Interface),使得Java虚拟机能运行嵌入在Java程序中的其他语言,这些语言包括C/C++ ,FORTRAN ,汇编语言等。
4.synchronized(同步)方法
同步方法用于多线程编程。多线程在运行时,要能会同时存取一个数据。为避免数据的不一致性,应将方法声明为同步方法,对数据进行加锁。
5.throws(异常)类型
程序在运行时可能发生异常现象。每一个异常对象都对应着一个异常类,如果暂时不希望方法处理某种异常,可将其抛出,使程序得以继续运行。
如:protected void SC( ) throws IOException ; //输入输出异常抛出
6.返回值类型
Java要求一个方法必须声明它的返回值类型,如果方法没有返回值就用关键字void作为返回值类型。否则应使用基本数据类型或对象类型说明返回值类型。
如: public void SC( ) ; // 没有返回值
int getx( ) ; // 返回值是int类型
private String abc( ) ; // 返回值是String类型
public static double du( ) ; // 返回值是double类型
protected Object oj( ) ; // 返回值是Object类型
7.方法名
方法名要以是任何有效的Java标识符,方法名可以和成员变量同名,也可以和成员方法同名。同一个类中的方法同名现象在OOP中称为方法重载(overload)。
如: void SC( ) {……}
void SC(int a ) {……}
void SC(int a, int b) {……}
void SC(double a, int b) {……}
8.参数表
方法的调用者正是通过参数表将外部消息传递给方法的。在参数表中要声明参数的类型,并用逗号分隔多个参数。
4.4.3 方法体
方法体包含在一对大括号中,即使方法体中没有语句,一对大括号也是必不可少的。
4.4.4 消息传递
一个对象和外部交换信息主要靠方法的参数来传递,如果允许的话,外部对象也可以直接存取一个对象的成员变量。
在Java中调用方法时,如果传递的参数是基本数据类型,在方法中将不能改变参数的值,你只能使用它们。如果传递的是对象引用,你也不能在方法中修改这个引用,但可以调用对象的方法以及修改允许存取的成员变量。所以想改变参数的值,可采用传递对象的方法,间接修改参数的值。
例1:class SC{
public static void main(String[] args){
int m=10, n=20 ;
Power p=new Power( ) ;
p.doPower(m , n);
System.out.println("x="+m+",y="+n);
System.out.println("x="+p.x+",y="+p.y) ; } }
class Power{
int x , y ;
void doPower(int a , int b)
{ x=a*a ;
y=b*b ; } }
运行结果:x=10,y=20
x=100,y=400
例2:class SC{
float ptValue ; //成员变量
public static void main(String[] args){
int val=11 ; // 局部变量
SC pt=new SC( ) ;
System.out.println("val1="+val) ;
pt.changeInt(val) ;
System.out.println("val2="+val) ;
pt.ptValue=101f ; //float类型,加f
System.out.println("valValue1="+pt.ptValue) ;
pt.ChangeObj(pt) ;
System.out.println("valValue2="+pt.ptValue) ; }
public void changeInt(int value) //参数是简单类型
{value=55 ; }
public void ChangeObj(SC ref)//参数是对象引用类型
{ref.ptValue=99f ; }
}
运行结果:val1=11
val2=11
valValue1=101.0
valValue2=99.0
作业:P87-16 class Rectangle {
double width,height;
double zc(double b1,double b2)
{ return 2*(b1+b2); }
double mj(double b1,double b2)
{ return b1*b2; }}
public class SC{
public static void main(String args[]){
Rectangle r=new Rectangle();
r.width=10 ; r.height=20;
System.out.println("周长="+r.zc(r.width,r.height)) ;
System.out.println("面积="+r.mj(r.width,r.height)) ; }}
作业:P87-17 public class SC{
public static void main(String args[]) {
int m[]={1,2,3,4,5};
Array A=new Array(m);
A.sum( );
System.out.println(A.getsum()); }}
class Array{
private int n[];
int sum=0;
Array(int i[]) { n=i; }
void sum( ){
for (int x=0;x<n.length;x++)
sum=sum+n[x] ; }
int getsum( ){
return sum; } }