
在之前的部分,我们学习了实例字段和实例方法。每个类的实例都有自己的字段集合,这些字段被称为实例字段。你可以创建类的几个实例,并在每个实例的字段中存储不同的值。
但是,有时候我们需要创建不属于任何类实例的字段或方法。这样的成员被称为静态字段和静态方法。当值存储在静态字段中时,它不是存储在类的实例中。事实上,即使类的实例不存在,也可以将值存储在类的静态字段中。
同样,静态方法不操作属于任何类实例的字段。相反,它们只能操作静态字段。你可以将静态字段和静态方法视为属于类而不是类的实例。
当一个字段使用static关键字声明时,这个字段属于整个类本身,而不是某个具体的对象实例。无论你创建了多少个该类的对象,内存中都只会有这一个静态字段的副本。
所有该类的实例都共享同一个静态字段,这意味着如果一个实例修改了静态字段的值,其他所有实例访问这个字段时看到的也是被修改后的新值。静态字段通常用于存储与类相关的全局信息,而不是与某个特定对象相关的数据。
让我们看一个例子:
public class Countable {
private static int instanceCount = 0;
/**
* 构造函数增加静态字段instanceCount。
* 这跟踪创建的此类实例的数量。
*/
public Countable() {
instanceCount++;
}
/**
* getInstanceCount方法返回已创建的此类实例的数量。
* @return instanceCount字段中的值。
*/
public int getInstanceCount() {
return instanceCount;
}
}首先,注意第2行静态字段instanceCount的声明:
private static int instanceCount = 0;静态字段是通过在访问说明符之后、字段数据类型之前放置static关键字创建的。注意我们明确地将instanceCount字段初始化为值0。这种初始化只发生一次,无论创建了多少个类实例。
接下来,看第7到10行的构造函数。构造函数使用++操作符来增加instanceCount字段。每次创建Countable类的实例时,都会调用构造函数,instanceCount字段会增加。因此,instanceCount字段将包含已创建的Countable类实例的数量。
让我们看一个演示这个类的程序:
public class StaticDemo {
public static void main(String[] args) {
int objectCount;
// 创建Countable类的三个实例
Countable object1 = new Countable();
Countable object2 = new Countable();
Countable object3 = new Countable();
// 从类的静态字段获取实例数量
objectCount = object1.getInstanceCount
运行这个程序会输出:
3个类实例被创建。程序创建了Countable类的三个实例,由变量object1、object2和object3引用。虽然有三个类实例,但只有一个静态字段副本。这说明了所有类实例共享静态字段。
当类包含静态方法时,不需要创建类的实例就可以执行该方法。让我们看一个包含静态方法的类的例子:
public class Metric {
/**
* milesToKilometers方法将英里距离转换为公里。
* @param m 英里距离
* @return 公里距离
*/
public static double milesToKilometers(double m) {
return m * 1.609;
}
/**
* kilometersToMiles方法将公里距离转换为英里。
* @param k 公里距离
* @return
静态方法是通过在方法头中的访问说明符之后放置static关键字创建的。Metric类有两个静态方法:milesToKilometers和kilometersToMiles。因为它们被声明为静态的,所以它们属于类,可以在没有任何类实例存在的情况下调用。
你只需在方法调用中的点操作符之前写入类的名称。这里是一个例子:
kilometers = Metric.milesToKilometers(10.0);这个语句调用milesToKilometers方法,传递值10.0作为参数。注意,该方法不是从类的实例调用的,而是直接从Metric类调用的。
让我们看一个使用Metric类的程序:
import javax.swing.JOptionPane;
public class MetricDemo {
public static void main(String[] args) {
String input; // 保存输入
double miles; // 英里距离
double kilos; // 公里距离
// 获取英里距离
input = JOptionPane.showInputDialog("输入英里距离。");
miles = Double.parseDouble(input);
运行这个程序会显示对话框,让用户输入距离并进行转换。
静态方法有一个重要的限制:它们不能直接访问类的非静态成员变量或调用非静态方法。原因在于,静态方法属于类本身,而非静态成员属于类的具体实例。当没有创建任何对象实例时,静态方法依然可以被调用,此时并不存在任何实例成员可供访问。 具体来说:
System.out.println(this.someField);,因为this关键字在静态方法中是不可用的。static。我们之前讨论了如何将基本值以及String对象的引用作为参数传递给方法。你也可以将其他类型对象的引用作为参数传递给方法。
当你将对象作为参数传递时,你传递的是引用变量中的值。当方法接收到对象引用作为参数时,它可以修改该变量引用的对象的内容。
让我们看一个例子:
public class PassObject {
public static void main(String[] args) {
// 创建一个Rectangle对象
Rectangle box = new Rectangle(12.0, 5.0);
// 将对对象的引用传递给displayRectangle方法
displayRectangle(box);
}
/**
* displayRectangle方法显示矩形的长度和宽度
* @param r 对Rectangle对象的引用
*/
运行这个程序会输出:
长度:12.0 宽度:5.0在这个程序的main方法中,box变量是一个Rectangle引用变量。在第8行,它的值作为参数传递给displayRectangle方法。displayRectangle方法有一个参数变量r,它也是一个Rectangle引用变量,接收参数。
当方法接收到对象引用作为参数时,方法可以修改该变量引用的对象的内容。这在下例中得到了演示:
public class PassObject2 {
public static void main(String[] args) {
// 创建一个Rectangle对象
Rectangle box = new Rectangle(12.0, 5.0);
// 显示对象的内容
System.out.println("box对象的内容:");
System.out.println("长度:" + box.getLength() + " 宽度:"
运行这个程序会输出:
box对象的内容:
长度:12.0 宽度:5.0
现在box对象的内容是:
长度:0.0 宽度:0.0在Java中,方法不仅可以返回基本数据类型的值(如int、double、float等),还可以返回对象的引用。也就是说,方法的返回类型可以是一个类名,这样的方法在执行完毕后会返回对某个对象的引用。通过这种方式,方法能够将新创建的对象、已经存在的对象,或者根据某些逻辑处理后得到的对象引用返回给调用者。这使得我们可以在方法之间灵活地传递和操作对象,极大地增强了程序的结构和功能。

让我们看一个例子,其中方法返回对BankAccount对象的引用:
import javax.swing.JOptionPane;
public class ReturnObject {
public static void main(String[] args) {
BankAccount account;
// 获取对BankAccount对象的引用
account = getAccount();
// 显示账户余额
JOptionPane.showMessageDialog(null,
"账户余额为$" + account.getBalance());
System.
注意getAccount方法有一个BankAccount的返回数据类型。这表示方法返回对BankAccount对象的引用。
我们经常需要显示表示对象状态的消息。对象的状态简单地说就是存储在对象字段中的数据。例如,BankAccount类有一个字段:balance。在任何给定时刻,BankAccount对象的balance字段将保存某个值。balance字段的值表示该时刻对象的状态。
创建表示对象状态的字符串是如此常见的任务,以至于许多程序员为他们的类配备了一个返回这种字符串的方法。在Java中,将此方法命名为toString是标准做法。
让我们看一个具有toString方法的类的例子。Stock类保存有关公司股票的数据:
public class Stock {
private String symbol; // 股票交易符号
private double sharePrice; // 每股当前价格
/**
* 构造函数
* @param sym 股票交易符号
* @param price 股票每股价格
*/
public Stock(String sym, double price) {
symbol = sym;
sharePrice = price;
}
当你为类编写了toString方法后,Java会在需要将对象转换为字符串时自动调用该方法。最常见的场景包括:
Stock xyzCompany = new Stock("XYZ", 9.62);
System.out.println(xyzCompany);Java还会在你将类的对象与字符串连接时隐式调用对象的toString方法。例如,以下代码会隐式调用xyzCompany对象的toString方法:
Stock xyzCompany = new Stock("XYZ", 9.62);
System.out.println("股票数据是:\n" + xyzCompany);在Java中,不能仅仅通过使用==操作符来判断两个对象是否拥有相同的数据。==操作符在比较对象时,实际上比较的是它们在内存中的地址(即引用),而不是对象内部存储的数据内容。因此,即使两个对象的属性值完全相同,只要它们是不同的实例,==操作符的结果也会是false。
如果我们希望判断两个对象的内容是否相同,就需要在类中专门编写一个方法来实现内容的比较。Java为此提供了equals方法。通过重写equals方法,我们可以自定义对象内容的比较逻辑,使得可以根据实际需求判断两个对象的数据是否一致。例如,在Stock类中,可以通过重写equals方法来比较两个Stock对象的symbol和sharePrice字段,从而判断它们是否代表相同的股票信息。
你不能使用==操作符来比较两个对象的内容。例如,以下代码可能看起来比较两个Stock对象的内容,但实际上没有:
// 创建具有相同值的两个Stock对象
Stock company1 = new Stock("XYZ", 9.62);
Stock company2 = new Stock("XYZ", 9.62);
// 使用==操作符比较对象(这是错误的)
if (company1 == company2) {
System.out.println("两个对象相同。");
} else {
System.out.println("对象不同。");
}当你使用==操作符与引用变量时,操作符比较变量包含的内存地址,而不是变量引用的对象的内容。因为这段代码中的两个数组变量引用内存中的不同对象,它们将包含不同的地址。因此,布尔表达式company1 == company2的结果为false,代码报告对象不同。
让我们为Stock类编写一个equals方法:
public boolean equals(Stock object2) {
boolean status;
// 确定此对象的symbol和sharePrice字段是否等于object2的symbol和sharePrice字段
if (symbol.equals(object2.symbol) && sharePrice == object2.sharePrice) {
status = true; // 是的,对象相等
} else {
status = false; // 不,对象不相等
}
// 返回status中的值
return status;
}equals方法接受一个Stock对象作为其参数。参数变量object2将引用作为参数传递的对象。if语句执行以下比较:如果调用对象的symbol字段等于object2的symbol字段,并且调用对象的sharePrice字段等于object2的sharePrice字段,那么两个对象包含相同的值。
让我们看一个演示equals方法的程序:
public class StockCompare {
public static void main(String[] args) {
// 创建具有相同值的两个Stock对象
Stock company1 = new Stock("XYZ", 9.62);
Stock company2 = new Stock("XYZ", 9.62);
// 使用equals方法比较对象
if (company1.equals(company2)) {
System.out.
运行这个程序会输出:
两个对象相同。在 Java 中,不能像复制基本数据类型变量那样,直接通过赋值语句来复制一个对象。对于基本类型(如 int、double 等),使用赋值语句会将变量的值完整地复制到另一个变量中。但对于对象,赋值语句只是复制对象的引用(即内存地址),而不会创建一个全新的对象副本。来看下面的代码示例:
Stock company1 = new Stock("XYZ", 9.62);
Stock company2 = company1;第一个语句创建一个Stock对象并将其地址赋给company1变量。第二个语句将company1赋给company2。这不会复制company1引用的对象。相反,它复制存储在company1中的地址并将该地址存储在company2中。执行此语句后,company1和company2变量都将引用同一个对象。
这种类型的赋值操作称为引用复制,因为只复制对象的地址,而不是实际对象本身。要复制对象本身,你必须创建一个新对象,然后将新对象的字段设置为与被复制对象的字段相同的值。
让我们为Stock类添加一个copy方法:
public Stock copy() {
// 创建一个新的Stock对象并用调用对象持有的相同数据初始化它
Stock copyObject = new Stock(symbol, sharePrice);
// 返回对新对象的引用
return copyObject;
}copy方法创建一个新的Stock对象,并将调用对象的symbol和sharePrice字段作为参数传递给构造函数。这使新对象成为调用对象的副本。
另一种创建对象副本的方法是使用复制构造函数。复制构造函数是接受同一类对象作为参数的构造函数。它使正在创建的对象成为作为参数传递的对象的副本。
让我们为Stock类添加一个复制构造函数:
public Stock(Stock object2) {
symbol = object2.symbol;
sharePrice = object2.sharePrice;
}注意构造函数接受一个Stock对象作为参数。参数变量object2将引用作为参数传递的对象。构造函数将object2的symbol和sharePrice字段中的值复制到正在创建的对象的symbol和sharePrice字段中。
聚合是指一个类的实例是另一个类中的字段。在现实生活中,对象经常由其他对象组成。例如,房子由门对象、窗对象、墙对象等组成。正是所有这些对象的组合构成了房子对象。
在设计软件时,有时从其他对象创建对象是有意义的。例如,假设你需要一个对象来表示你在大学里正在学习的课程。你决定创建一个Course类,它将保存以下信息:
除了课程名称外,该类还将保存与教师和教科书相关的项目。你可以将每个项目的字段放在Course类中。但是,一个好的设计原则是将相关项目分离到它们自己的类中。
让我们看看如何做到这一点。Instructor类可以创建来保存教师相关的数据,TextBook类可以创建来保存教科书相关的数据。这些类的实例然后可以用作Course类中的字段。
首先,Instructor类的定义如下:
public class Instructor {
private String lastName; // 姓氏
private String firstName; // 名字
private String officeNumber; // 办公室号码
/**
* 此构造函数初始化姓氏、名字和办公室号码
* @param lname 教师的姓氏
* @param fname 教师的名字
* @param office 办公室号码
*/
public Instructor(String lname, String fname, String office
接下来,我们定义TextBook类,定义如下:
public class TextBook {
private String title; // 书籍标题
private String author; // 作者姓氏
private String publisher; // 出版商名称
/**
* 此构造函数初始化标题、作者和出版商字段
* @param textTitle 书籍标题
* @param auth 作者姓名
* @param pub 出版商名称
*/
public TextBook(String textTitle, String auth, String pub
最后,我们定义Course类,定义如下:
public class Course {
private String courseName; // 课程名称
private Instructor instructor; // 教师
private TextBook textBook; // 教科书
/**
* 此构造函数初始化courseName、instructor和text字段
* @param name 课程名称
* @param instructor Instructor对象
* @param text TextBook对象
*/
public Course(String name, Instructor instr, TextBook text
让我们看一个演示Course类的程序:
public class CourseDemo {
public static void main(String[] args) {
// 创建一个Instructor对象
Instructor myInstructor = new Instructor("张", "三", "RH3010");
// 创建一个TextBook对象
TextBook myTextBook = new TextBook("Java入门", "李明", "教育出版社");
运行这个程序会输出:
课程名称:Java编程入门
教师信息:
姓氏:张
名字:三
办公室号码:RH3010
教科书信息:
标题:Java入门
作者:李明
出版商:教育出版社关键字this是对象可以用来引用自己的引用变量的名称。例如,回忆我们前面介绍的Stock类。该类有一个equals方法,它将调用的Stock对象与作为参数传递的另一个Stock对象进行比较:
public boolean equals(Stock object2) {
boolean status;
// 确定此对象的symbol和sharePrice字段是否等于object2的symbol和sharePrice字段
if (symbol.equals(object2.symbol) && sharePrice == object2.sharePrice) {
status = true; // 是的,对象相等
} else {
status = false; // 不,对象不相等
}
// 返回status中的值
return status;
}当此方法执行时,this变量包含调用对象的地址。我们可以重写if语句如下,它将执行相同的操作:
if (this.symbol.equals(object2.symbol) && this.sharePrice == object2.sharePrice)this关键字的一个常见用途是克服参数名称对字段名称的阴影。如果方法的参数与同一类中的字段具有相同的名称,则参数名称会遮蔽字段名称。
例如,看Stock类中的构造函数:
public Stock(String sym, double price) {
symbol = sym;
sharePrice = price;
}这个方法使用参数sym来接受分配给symbol字段的参数,使用参数price来接受分配给sharePrice字段的参数。有时很难(甚至耗时)想出一个与字段名称不同的好参数名称。为了避免这个问题,许多程序员给参数与它们对应的字段相同的名称,然后使用this关键字来引用字段名称。
例如,Stock类的构造函数可以写成如下:
public Stock(String symbol, double sharePrice) {
this.symbol = symbol;
this.sharePrice = sharePrice;
}虽然参数名称symbol和sharePrice遮蔽了字段名称symbol和sharePrice,但this关键字克服了遮蔽。因为this是对调用对象的引用,表达式this.symbol引用调用对象的symbol字段,表达式this.sharePrice引用调用对象的sharePrice字段。
你已经知道当创建对象时会自动调用构造函数。你也知道不能像调用其他方法那样显式调用构造函数。但是,有一个例外:你可以使用this关键字从同一类中的另一个构造函数调用一个构造函数。
为了说明这一点,请看下面的例子,它有以下构造函数:
public Stock(String sym, double price) {
symbol = sym;
sharePrice = price;
}这个构造函数接受分配给symbol和sharePrice字段的参数。假设我们还想要一个只接受symbol字段参数的构造函数,并将0.0分配给sharePrice字段。以下是编写构造函数的一种方法:
public Stock(String sym) {
this(sym, 0.0);
}这个构造函数简单地使用this变量调用第一个构造函数。它将sym中的值作为第一个参数传递,将0.0作为第二个参数传递。结果是symbol字段被分配sym中的值,sharePrice字段被分配0.0。
记住关于使用this调用构造函数的以下规则:
枚举数据类型由一组预定义值组成。你可以使用数据类型创建只能保存属于枚举数据类型的值的变量。
你已经学习了数据类型的概念以及它们如何与基本变量一起使用。例如,int数据类型的变量可以在特定范围内保存整数值。你不能将浮点值分配给int变量,因为只有int值可以分配给int变量。数据类型定义了该数据类型任何变量的合法值。
有时创建具有特定合法值集的自己的数据类型是有帮助的。例如,假设你想创建一个名为Day的数据类型,该数据类型中的合法值是星期几的名称(星期日、星期一等)。 当你创建Day数据类型的变量时,你只能在该变量中存储星期几的名称。任何其他值都是非法的。在Java中,这种类型被称为枚举数据类型。
你使用enum关键字创建自己的数据类型并指定属于该类型的值。以下是枚举数据类型声明的例子:
enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }枚举数据类型声明以enum关键字开始,后跟类型名称,后跟大括号内的标识符列表。示例声明创建一个名为Day的枚举数据类型。 大括号内列出的标识符SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY和SATURDAY被称为枚举常量。它们表示属于Day数据类型的值。
一旦你在程序中创建了枚举数据类型,你就可以声明该类型的变量。例如,以下语句将workDay声明为Day类型的变量:
Day workDay;因为workDay是Day变量,我们可以合法地分配给它的唯一值是枚举常量Day.SUNDAY、Day.MONDAY、Day.TUESDAY、Day.WEDNESDAY、Day.THURSDAY、Day.FRIDAY和Day.SATURDAY。如果我们尝试分配Day类型的枚举常量以外的任何值,将导致编译器错误。
当你编写枚举类型声明时,你实际上是在创建一种特殊的类。此外,你在大括号内列出的枚举常量实际上是类的对象。 在前面的例子中,Day是一个类,枚举常量Day.SUNDAY、Day.MONDAY、Day.TUESDAY、Day.WEDNESDAY、Day.THURSDAY、Day.FRIDAY和Day.SATURDAY都是Day类的实例。
枚举常量实际上是对象,它们自动配备了几个方法。其中之一是toString方法。toString方法简单地返回调用枚举常量的名称作为字符串。
枚举常量还有一个名为ordinal的方法。ordinal方法返回表示常量序数值的整数值。常量的序数值是它在枚举声明中的位置,第一个常量位于位置0。
让我们看一个完整的例子:
public class EnumDemo {
// 声明Day枚举类型
enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }
public static void main(String[] args) {
// 声明一个Day变量并为其赋值
Day workDay = Day.WEDNESDAY;
// 以下语句显示WEDNESDAY
System.out.
运行这个程序会输出:
WEDNESDAY
Day.SUNDAY的序数值是0
Day.SATURDAY的序数值是6
FRIDAY大于MONDAY在Java中,switch语句不仅可以用于基本数据类型(如int、char等),还可以直接用于枚举类型。这意味着你可以在switch语句中将枚举常量作为条件进行判断,从而根据不同的枚举值执行不同的代码分支。下面是一个具体的示例,演示了如何在switch语句中使用枚举类型:
public class SportsCarDemo2 {
public static void main(String[] args) {
// 创建一个SportsCar对象
SportsCar yourNewCar = new SportsCar(CarType.PORSCHE, CarColor.RED, 100000);
// 获取汽车品牌并对其进行switch
switch (yourNewCar.getMake()) {
case PORSCHE:
System.out.println("你的汽车是在德国制造的。");
break;
运行这个程序会输出:
你的汽车是在德国制造的。在Java中,内存管理的一项重要机制是垃圾回收(Garbage Collection,简称GC)。Java虚拟机(JVM)会定期启动一个称为垃圾收集器(Garbage Collector)的后台进程,自动检测并清理程序中不再被任何变量引用的对象。这些“无主”的对象占用的内存会被回收,从而为新的对象分配空间,避免内存泄漏。
具体来说,当你在程序中通过new关键字创建对象时,这些对象会被分配在堆内存中。只要有变量(引用)指向这些对象,它们就会一直存在于内存中。当某个对象不再被任何变量引用(即没有任何方式可以再访问到它),这个对象就变成了“不可达对象”。JVM的垃圾收集器会定期扫描内存,发现这些不可达对象后,将其占用的内存空间释放出来。
与C++等需要手动释放内存的语言不同,Java程序员无需显式地销毁对象或释放内存。垃圾回收器会自动完成这些工作。这大大降低了内存泄漏和悬挂指针等问题的风险,提高了程序的健壮性和安全性。
需要注意的是,垃圾回收的具体时机和频率由JVM自行决定,程序员无法精确控制。虽然可以通过调用System.gc()方法建议JVM进行垃圾回收,但这只是一个建议,JVM是否立即执行垃圾回收并不保证。因此,良好的编程习惯是让对象在不再需要时尽早失去引用(如将引用变量赋值为null),其余的内存管理工作交给JVM自动处理。
例如,看以下代码:
// 声明两个BankAccount引用变量
BankAccount account1, account2;
// 创建一个对象并用account1引用它
account1 = new BankAccount(500.0);
// 用account2引用同一个对象
account2 = account1;
// 在account1中存储null,使其不再引用对象
account1 = null;
// 对象仍然被account2引用
// 在account2中存储null,使其不再引用对象
account2 = null;
// 现在对象不再被引用,所以可以被垃圾收集器删除这段代码使用两个引用变量account1和account2。创建一个BankAccount对象并由account1引用。然后,account1被赋给account2,这导致account2引用与account1相同的对象。
接下来,null值被赋给account1。这从account1变量中移除对象的地址,使其不再引用对象。对象仍然可以访问,因为它被account2变量引用。下一个语句将null赋给account2。这从account2中移除对象的地址,使其不再引用对象。因为对象不再可访问,它将在下次垃圾收集器进程运行时从内存中删除。
如果类有一个名为finalize的方法,它会在类的实例被垃圾收集器销毁之前自动调用。如果你希望在对象被销毁之前执行代码,可以在类中创建finalize方法并将代码放在那里。finalize方法不接受参数,具有void返回类型。
8. 静态成员练习
创建一个类,使用静态字段来跟踪创建的实例数量,并提供一个静态方法来获取实例数量。
public class Student {
private String name;
private static int count = 0; // 静态字段,跟踪实例数量
// 构造函数
public Student(String name) {
this.name = name;
count++; // 每次创建对象时,计数加1
}
// 静态方法,获取实例数量
public static int getCount() {
9. toString方法练习
为Student类编写一个toString方法,显示学生的姓名和年龄。
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 重写toString方法
@Override
public String toString() {
return "Student{姓名='"
10. this关键字练习
在类的构造函数中使用this关键字来避免参数名称遮蔽字段名称。
public class Rectangle {
private double width;
private double height;
// 使用this关键字区分参数和字段
public Rectangle(double width, double height) {
this.width = width; // this.width是字段,width是参数
this.height = height; // this.height是字段,height是参数
}
// 也可以使用不同的参数名,避免使用this
public void setWidth
11. equals方法练习
为Student类编写一个equals方法,比较学生的姓名和学号是否相等。
public class Student {
private String name;
private String studentId;
public Student(String name, String studentId) {
this.name = name;
this.studentId = studentId;
}
// 重写equals方法
@Override
public boolean equals(Object obj) {
// 如果是同一个对象,返回true
输出结果:
初始实例数量: 0
创建1个对象后: 1
创建2个对象后: 2
创建3个对象后: 3说明:
count是静态字段,属于类本身,所有实例共享count++getCount()是静态方法,可以通过类名直接调用输出结果:
Student{姓名='张三', 年龄=20}
Student{姓名='张三', 年龄=20}说明:
@Override注解表示重写父类方法输出结果:
宽度: 10.0
高度: 5.0
面积: 50.0说明:
this关键字引用当前对象this.字段名访问字段this可以明确区分参数和字段this输出结果:
student1.equals(student2): true
student1.equals(student3): false
student1 == student2: false说明: