3.3类与对象的语法细则
用类定义对象——类的4大要素
– 方法名
– 形式参数列表
– 方法体
– 返回值类型
1 类的定义 Java语法:定义类 2 [public]class 类名 { 3 [访问权限]数据类型 字段名[=初始值]; 4 …… 5 [访问权限]返回值类型 方法名( 形式参数列表 ) { 6 方法体 7 } 8 …… 9 }
方法名
A) Java语言规定构造方法名与类名必须相同
B) Java语言规定构造方法没有返回值,但不用void声明
C) Java语言规定构造方法可以重载
B) Java语言规定构造方法没有返回值,但不用void声明
C) Java语言规定构造方法可以重载
D) Java语言规定构造方法只能通过new自动调用
this关键字
类的下属成员有两种,分别是字段(存储数据)和方法(处理数据的算法)。某些特殊的类可能只包含一种成员,比如只包含字段,或只包含方法。
类成员的访问权限有4种,分别是公有权限(public)、保护权限(protected)、私有权限(private)或默认权限(未指定访问权限)。
例3-8 一个钟表类Clock的Java示意代码(Clock.java)
1 import java.util.Scanner; // 导入外部程序Scanner 2 3 public class Clock { // 定义钟表类Clock 4 // 将字段设为private权限,即私有成员 5 private int hour; // 字段hour:保存小时数 6 private int minute; // 字段minute:保存分钟数 7 private int second; // 字段second:保存秒数 8 // 将方法设为public权限,即公有成员 9 10 public void set() { // 不带参数的方法set():从键盘输入时间 11 Scanner sc = new Scanner(System.in); // 创建键盘扫描器对象sc 12 hour = sc.nextInt(); 13 minute = sc.nextInt(); 14 second = sc.nextInt(); 15 } 16 17 public void set(int h, int m, int s) { // 带参数的方法set():用参数设定时间 18 hour = h; minute = m; second = s; // 将参数分别赋值给对应的数据成员 19 } 20 21 public void show( ) { // 方法show:显示时间 22 System.out.println( hour +”:” +minute +”:” +second ); // 显示格式:时:分:秒 23 } 24 }
类的定义:
字段成员的语法细则
• 字段初始化
private int hour = 0; // 将钟表的时间初始化为0:0:0
private int minute = 0;
private int second = 0;
• 空值
• 只读字段final
private final int second = 0; // 将字段second定义成只读字段
– 方法成员的语法细则
• 方法的4大要素
– 方法名--以小写字母开头
– 形式参数列表--接受输入参数所需的变量,这些变量称为形式变量(形参)
– 方法体--定义专供本方法使用的变量(局部变量;返回用return)
– 返回值类型--无返回类型要定义为void
• 方法签名:返回值类型 方法名(形式参数列表形参1,形参2,形参3)--定义方法的接口
• 重载方法:相同的方法名
public void set( ) { // 不带参数的方法set():从键盘输入时间
Scanner sc = new Scanner( System.in );
hour = sc.nextInt(); minute = sc.nextInt(); second = sc.nextInt();
}
public void set(int h, int m, int s) { // 带参数的方法set():用参数设定时间
hour = h; minute = m; second = s;
}
两个存在时,调用同名的重载方法调用根据调用语句实参的个数和类型,来自动调用形参匹配的那个方法
– 类成员访问权限的语法细则
• 公有权限public
• 私有权限private
• 保护权限protected
• 默认权限(未指定访问权限)
对象的定义与访问
– 定义对象
• 运算符new --来为对象分配内存
• 引用及引用变量 --指向对象所分配内存的首地址
Clock obj1; // 预先定义一个Clock类型的引用变量obj1// 此时obj1的引用值为null,即还未引用任何对象
obj1 = new Clock( );
上述两条语句可简写为一条语句:
Clock obj1 = new Clock( ); //obj1是对象名
• 请注意:“=”两边的类型应当一致
– 访问对象
• 成员运算符“.”
– 对象名.字段名
– 对象名.方法名(实参列表)
•访问钟表对象的公有成员
Clock obj1 = new Clock( ); // 创建一个钟表对象obj1
obj1.set( ); // 调用对象obj1的公有方法set(),输入时分秒数据
obj1.show( ); // 调用对象obj1的公有方法show(),显示其时间
•可以用钟表类Clock定义(生产)多个钟表对象
Clock obj2 = new Clock( ); // 创建第二个钟表对象obj2
obj2.set( 8, 30, 15 ); // 调用对象obj2的公有方法set(),设置时间8:30:15
obj2.show( ); // 调用对象obj2的公有方法show(),显示其时间
•一个对象可以被多次引用
Clock obj ; // 再定义一个Clock类的引用变量obj
obj = obj1; // 赋值后,obj与obj1引用同一个对象,该对象被引用了2次
obj.set( 12, 0, 0); // 通过引用变量obj操作钟表对象,将其时间设为12:0:0
obj.show( ); // 显示对象的时间,显示结果应为:12:0:0
obj1.show( ); // 显示obj1所引用对象的时间,显示结果也为:12:0:0
• 引用数据类型
– 基本数据类型(primitive data type)
byte、short、int、long、float、double、char、boolean
– Java语言中,使用基本数据类型定义变量,定义时直接为变量分配内存单元
– 后续程序将通过变量名访问变量的内存单元
• 引用数据类型(reference data type)
类类型、数组类型、接口类型、枚举类型等
– 先定义引用数据类型的引用变量
– 再用运算符new创建引用数据类型的对象,并
将所返回的对象引用保存到引用变量中
– 后续程序通过引用变量访问对象及其下属成员
– 变量及对象的内存分配
– 变量
int x;
double y;
Clock obj1;
以上都是局部变量:只能在栈内存分配内存
引用数据类型
– 变量及对象的内存分配
– 对象
obj1 = new Clock( ); --创建对象一当前被引用一次,计数1
Clock obj2 = new Clock( );--创建对象二
Clock obj;
obj = obj1;--obj引用obj1 对象一引用次数为2
obj = obj2;;--obj引用obj2 对象二引用次数为2
obj = null;--刚才引用对象二,现在不引用 对象二将为1
obj2 = null;--obj2引用对象不引用任何对象被 引用次数为0(– Java语言中的垃圾回收(garbage collection)机制)
三种不同的变量
– 字段:定义在类中的变量成员,用于保存属性数据。字段相当于是类中的全局变量,可以被类中的所有方法成员访问
– 局部变量:类中方法成员在方法体中定义的变量,仅能在所定义的方法体或复合语句中访问
– 形式参数(形参):类中方法成员在头部小括号里面定义的变量,用于接收原始数据。形参仅能在所定义的方法体中访问
例3-10 一个为钟表设置整点时间的Java示例代码(ClockTest.java)
1 import java.util.Scanner; // 导入外部程序Scanner 2 3 public class ClockTest { // 主类 4 public static void main(String[] args) { // 主方法 5 int hour; // 局部变量:普通变量,未初始化 6 Clock c1; // 局部变量:引用变量,未初始化 7 hour = 12; c1 = new Clock(); // 为局部变量赋值 8 c1.set(8, 30, 15); // 设置钟表对象c1的时间 9 c1.show(); // 显示结果:8:30:15 10 11 Clock c2; // 再定义一个局部引用变量c2 12 c2 = setHour( c1, hour ); // 调用setHour()将c1设为整点,并将返回值赋值给c2 13 c2.show( ); // 显示c2的时间,结果应为:12:0:0 14 c1.show( ); // 显示c1的时间,结果也为:12:0:0 15 } 16 17 private static Clock setHour(Clock rc, int h) { // 将钟表时间设为整点的方法 18 rc.set(h, 0, 0); // 设置rc的时间,小时数为接收的参数h,分钟和秒数设为0 19 return rc; 20 } 21 }
•传递基本数据类型数据时直接传递数值,即值传递。
•传递引用数据类型的对象时所传递的是对象引用(不是对象本身),这被称为是引用传递。--形参rc传递的是c1这个实参
类与对象的编译原理
– 类代码的编译
public void set( int h, int m, int s ) { hour = h; minute = m; second = s; } |
public void set( Clock this, int h, int m, int s ) { this.hour = h; this.minute = m; this.second = s; } |
1 public void show( ) { 2 System.out.println( 3 hour +”:” +minute +”:” +second ); 4 } |
1 public void show( Clock this ) 2 System.out.println( 3 this.hour +”:” +this.minute +”:” +this.second ); 4 } |
– 调用对象方法成员语句的编译
Clock obj1 = new Clock( ); // 创建钟表对象obj1
obj1.set( 8, 0, 0 ); // 将obj1的时间设为8:0:0
obj1.show( ); // 显示obj1的时间,显示结果:8:0:0
set( obj1, 8, 0, 0 );--调用set方法,按照顺序 实参obj1传值到set方法形参this
void set(Clock this, int h, int m, int s) { … } 形参this所引用的是当前调用方法的对象obj1.set( 8, 0, 0 )——this=obj1;h=8;m=0;s=0
this.hour = h; this.minute = m; this.second = s;
show( obj1 );
同类的多个对象在内存中共用一份方法代码
Clock obj1 = new Clock( ); // 创建钟表对象obj1
Clock obj2 = new Clock( ); // 创建钟表对象obj2
obj1.set( 8, 0, 0 );
obj2.set( 9, 30, 15 );
set( obj1, 8, 0, 0 );
set( obj2, 9, 30, 15 );
void set(Clock this, int h, int m, int s) { … }
形参this是隐含的,在方法体中访访问其他类成员也不需要添加this引用,这些都由编译器在编译时自动添加
– 程序员可以在访问类成员时显式添加“this.”,也可以通过this获取当前对象的引用,或把this作为实参将当前对象的引用继续传递给其他方法
– 程序员还可以利用this来区分与形参或局部变量重名的字段
public void set( int hour, int minute, int s ) {//传入形参hour、minute、s
this.hour = hour; // “this.hour”指代的是字段hour
this.minute = minute; // “this.minute”指代的是字段minute
second = s; // 形参s与字段second不重名,可以不用this
}
类的构造方法
– 构造方法(constructor)是类中一种特殊的方法
• 构造方法的名字必须与类名相同
• 构造方法通过形参传递初始值,实现对新建对象字段成员的初始化
• 构造方法可以重载,即定义多个同名的构造方法,这样可以提供多种形式的初始化方法
• 构造方法没有返回值,定义时不能写返回值类型,写void也不行
• 构造方法通常是类外调用,其访问权限不能设为private
如果没有定义构造方法,编译器会给定空的构造方法:默认构造方法,格式为 类名() { } // 默认构造方法
初始化对象
public Clock( int p1, int p2, int p3 ) { // 带形参的构造方法
hour = p1; minute = p2; second = p3;
}
public Clock( ) { // 不带形参的构造方法
this( 0, 0, 0 ); // 调用本类重载的带形参构造方法(须为第一条语句)
// hour = 0; minute = 0; second = 0; // 或直接赋值
}
Clock obj1 = new Clock( 8, 30, 15 ); // 给了实参,将调用带形参的构造方法
Clock obj2 = new Clock( ); // 未给实参,将调用不带形参的构造方法
Clock obj = obj1; // 未实际创建对象,不会调用构造方法
调用方法遵守三条原则①构造方法是在使用类创造对象时,由系统自动调用的,理解为由运算符new调用的。程序员不能直接调用构造方法
②使用类创建对象,每创建个对象 就会调用一次类的构造方法,创建多少个对象就会调用多少次构造方法
③定义引用对象时不会调用对象,因此也不会调用构造方法
– 拷贝构造方法
用一个已经存在的对象来初始化当前正在创建的新对象
public Clock( Clock oldObj ) {
hour = oldObj.hour;
minute = oldObj.minute;
second = oldObj.second;
}
用一个已经存在的对象来初始化当前正在创建的新对象
Clock obj1 = new Clock( 8, 30, 15 );
Clock obj2 = new Clock( obj1 ); obj2显示的也时8:30:15
显示对象的创建过程
public Clock( ) { // 不带形参的构造方法
hour = 0; minute = 0; second = 0;
System.out.println( “Clock( ) called.” ); // 添加显示信息的语句
}
public Clock( int p1, int p2, int p3 ) { // 带形参的构造方法
hour = p1; minute = p2; second = p3;
System.out.println( “Clock(int p1, int p2, int p3) called.” ); // 添加显示信息的语句
}
public Clock( Clock oldObj ) { // 拷贝构造方法
hour = oldObj.hour; minute = oldObj.minute; second = oldObj.second;
System.out.println( “Clock( Clock oldObj ) called.” ); // 添加显示信息的语句
}
Clock obj1 = new Clock( ); // 未给实参:Clock( ) called.
Clock obj2 = new Clock( 8, 30, 15 ); // 给3个int型实参:Clock(int p1, int p2, int p3) called.
Clock obj3 = new Clock( obj2 ); // 给1个Clock型实参:Clock( Clock oldObj ) called.