对象和类-02
面向对象就是使用对象进行程序设计(是一种通过对象的方式,把现实世界映射到计算机模型的一种编程方法)。对象代表现实世界中可以明确标识的一个实体。例如:一个学生,一张桌子,一个圆,一个按钮甚至一笔贷款都可以看着一个对象。每个对象都有自己独特的标识、状态和行为。
- 一个对象的状态(state,也称未特征(property)或属性(attribute))是由具有当前值的数据域来表示的。例如:圆对象具有一个数据域radius,它是标识圆的属性。一个矩形对象具有数据域width和height,它们都是矩形的属性。java使用变量定义数据域。
- 一个对象的行为(behavior,也称为动作(action))是由方法定义的。调用对象的一个方法就是要求对象完成一个动作。例如:可以为圆设置半径,可以计算圆的面积,还可以获取它的周长等。java使用方法定义动作。
现实世界中,我们定义了“学生”这种抽象概念,而具体的学生则是“peppa”、“suzy”、“emily”等一个个具体的学生。所以,“学生”可以定义为一个类(class),而具体的学生则是类的一个实例(instance)
所以,只要理解了class和instance的概念,基本上就明白了什么是面向对象编程。
使用一个通用类来定义同一类型的对象。类是一个模板,蓝本或者说是合约,用来定义对象的数据域是什么以及方法是做什么的。一个对象是类的一个实例。可以从一个类中创建多个实例。创建实例的过程称为实例化。对象(object)和实例(instance)经常是可以互换的。类和对象之间的关系类似于⬇
可以用一种配方做出任意多的冰淇淋处来。下图展示名为Circle的类和它的三个对象。
class是一种对象模版,它定义了如何创建实例,因此,class本身就是一种数据类型:
package edu.uestc.avatar; /** * 每个对象都具有自己独特的标识、状态和行为 * 状态也称为属性或者特征。用数据域(成员变量)描述 * 行为也称为动作。通过成员方法描述 * 使用类描述同一类型的对象 * 类是对象模板、蓝本或者说是合约。用来定义对象的数据域是什么以及方法是做什么的 */ public class Circle { //通过数据域描述对象的属性(状态) public double radius; /** * 构造方法在使用new关键字创建对象时被调用 * 构造方法作用:一般用作对象的初始化 * 构造方法是一种特殊的方法: * 1、构造必须具有和所在类形同的名字(必须和类名相同) * 2、构造方法没有返回值类型,连void都没有 * 3、使用new创建对象时会调用构造方法,作用为初始化对象 * * 构造方法也可以进行重载 * * 如果类中没有定义构造方法,编译器会自动添加一个公共的无参的构造方法(默认),如果已经定义了构造方法,编译器不再添加 * 一般构造定义在数据域下 */ public Circle() { System.out.println("调用无参构造方法"); radius = 1; } public Circle(double newRadius) { System.out.println("初始化圆的半径为:" + newRadius); radius = newRadius; } /** * 成员方法描述对象的行为:动作(计算圆的面积) * @return 圆的面积 */ public double getArea() { return 3.14 * radius * radius; } /** * 对象的行为/动作 * 调用一个对象的方法就是要求对象完成一个动作 * @return 该圆的周长 */ public double getPerimeter() { return 3.14 * 2 * radius; } }
而instance是对象实例,instance是根据class创建的实例,可以创建多个instance,每个instance类型相同,但各自属性可能不相同:
package edu.uestc.avatar; public class CircleTest { public static void main(String[] args) { Circle circle1 = new Circle();//调用的对应的构造方法 double perimeter1 = circle1.getPerimeter(); System.out.println(circle1 + "==>radius:" + circle1.radius + "==>area:" + circle1.getArea() + "==>perimeter:" + perimeter1); Circle circle2 = new Circle(25); System.out.println(circle2 + "==>radius:" + circle2.radius + "==>area:" + circle2.getArea() + "==>perimeter:" + circle2.getPerimeter()); Circle circle3 = new Circle(125);//调用的对应的构造方法 System.out.println(circle3 + "==>radius:" + circle3.radius + "==>area:" + circle3.getArea() + "==>perimeter:" + circle3.getPerimeter()); } }
要想使用对象,必须先构造对象并指定其初始状态。和创建数组一样,通过new操作符从构造方法创建一个对象。new Circle()创建一个半径为1的对象,new Circle(25)创建一个半径为25的对象,new Circle(125)创建一个半径为125的对象.
这三个对象(通过circle1,circle2,circle3来引用)有不同的数据,但是有相同的方法。因此可以使用getArea()方法计算它们各自的面积。这三个对象是相互独立的。可以使用点操作符(.)通过对象引用访问数据域及调用方法。
数据域radius称为实例变量,因为它依赖于某个具体的实例。基于同样的原因,getArea()及getPerimeter()方法称为实例方法,因为只能在具体的实例上调用它。调用对象上的实例方法的过程称为调用对象。
使用构造方法构造对象
构造方法在使用new操作符创建对象的时候被调用。构造方法时一种特殊的方法:
- 构造方法必须具备和所在类相同的名字
- 构造方法没有返回值类型
- 构造方法是在创建一个对象使用new操作符时调用的。构造方法的作用是初始化对象。
构造方法与它所在的类具有完全相同的名字。和其它方法一样,构造方法也可以重载(也就是说,可以有多个同名构造方法,但它们有不同的参数列表),这样更易于用不同的初始数据值来构造对象。一个类可以不定义构造方法。在这种情况下,编译器回自动添加一个方法体为空的公共的无参构造方法。这个构造方法称为默认构造方法。当且仅当类中没有明确定义任何构造方法时才会自动提供它。
通过引用变量访问对象
新创建的对象在内存中被分配空间。它们可以通过引用变量来访问。对象的数据和方法可以通过点操作符(.)通过对象的引用变量进行访问。
//声明对象引用变量、创建对象以及将对象的引用赋值给这个变量 Circle circle1 = new Circle();
变量circle1存放的是对Circle对象的一个引用。在创建一个对象后,它的数据域和方法可以使用点操作符(.)来调用和访问。该操作符也称为对象成员访问操作符。
例如:circle1.radius引用circle1的半径,而circle1.getArea()调用circle1的getArea方法。方法作为对象上的操作被调用。
数据域radius称作实例变量,因为它依赖于某个具体的实例。基于同样的原因,getArea()方法称为实例方法,因为只能在具体的实例上调用它。调用对象上的实例方法的过程称为调用对象。
引用数据域和null值
数据域也可能是引用类型的。例如:
class Student{ String name; //引用类型,默认值为null int age; //数值型默认值为0 boolean isScienceMajor; //boolean类型默认值为false char gender; //字符型默认值为'\u0000' }
如果一个引用类型没有引用任何对象,那么这个数据域就有一个特殊的java值null。null同true和false一样都是一个直接量。true和false是boolean类型的直接量,null是引用类型直接量。
引用类型数据域的默认值为null,数值型数据域默认值为0,boolean类型数据域默认值为false,字符型数据域默认值为'\u0000'。java没有给方法中的局部变量赋默认值,必须对其进行初始化才能使用。
public static void main(String[] args) { Student student = new Student(); System.out.println("name:" + name); System.out.println("age:" + age); System.out.println("isScienceMajor:" + isScienceMajor); System.out.println("gender:" + gender); //局部变量未初始化使用 int x; String y; System.out.println("x:" + x);//编译不通过 System.out.println("y:" + y); }
注意:NullPointerException是一种常见的运行时异常,当调用值为null的引用变量上的方法时会发生此类异常。在通过引用变量调用一个方法前。确保先将对象引用赋值给这个变量。
基本类型变量和引用类型变量的区别
每个变量都代表一个存储值的内存位置。声明一个变量时,就是告诉编译器这个变量可以存放什么类型的值。对基本类型变量来说,对应内存所存储的值是基本类型。对引用类型变量来说,对应内存所存储的值是一个引用,是对象的存储地址。例如:Date对象birthday的值存的是一个引用。它指明这个Date对象存储在内存中的什么位置。
将一个变量赋值给另一个变量时,另一个变量就被赋予同样的值。对基本类型变量而言,就是将一个变量的实际值赋给另一个变量。对引用变量而言,就是将一个变量的引用赋给另一个变量。如下图所示,赋值语句i = j将基本类型变量j的值复制给基本类型变量i;对引用变量来讲,赋值语句deadline = birthday是将birthday的引用赋给deadline。赋值之后,变量birthday和deadline指向同一个对象。
注:所有的Java对象都存储在堆中。当一个对象包含另一个对象变量时,它只是包含着另一个堆对象的指针。在C++中,指针十分令人头疼,因为它们很容易出错。稍不小心就会创建一个错误的指针,或者内存管理出问题。在Java语言中,这些问题都不复存在。如果使用一个没有初始化的指针,运行时系统将会产生一个运行时错误,而不是生成一个随机的结果。同时,不必担心内存管理问题,垃圾回收器会处理相关的事宜。
使用Java中预定义的类
在使用java开发应用程序,我们将频繁的使用到Java类库里的类来开发程序。下面介绍一个典型的类——Date和LocalDate
可以使用Date类中的无参构造方法为当前的日期和时间创建一个实例。它的getTime()方法返回从GMT时间1970年1月1日至今流逝的时间。它的toString()方法返回日期和时间的字符串。Date类还有另外一个构造方法Date(long elapseTime),可以用它创建一个Date对象。
public class DateDemo { public static void main(String[] args) throws ParseException { System.out.println(System.currentTimeMillis()); //为当前时间创建Date对象 Date date = new Date(); System.out.println(date);//调用的date.toString(); //date.setTime(0);//为date设置一个新的流逝时间 /* * 对Date进行格式化(字符串) * pattern: * y:year * M:month * d:date * h:12小时的小时 * H:24小时的小时 * m:minutes * s:seconds */ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //将date对象转为指定格式的字符串 System.out.println(sdf.format(date)); //将指定格式的字符串转为Date对象 String pattern = "2012-03-30 14:21:38"; Date date1 = sdf.parse(pattern); System.out.println(date1); } }
Date类的实例有一个状态,即特定的时间点。尽管在使用Date类时不必知道这一点,但时间是用距离一个固定时间点的毫秒数(可正可负)表示的,这个时间点就是所谓的纪元( epoch),它是UTC时间1970年1月1日00:00:00。UTC就是Coordinated Universal Time(国际协调时间),与大家熟悉的GMT(即Greenwich Mean Time,格林尼治时间)一样,是一种实用的科学标准时间。
但是,Date类对于处理人类记录日期的日历信息并不是很有用,如“December 31,1999”。这种特定的日期描述遵循了世界上大多数地区使用的Gregorian 阳历。但是,同样的这个时间点采用中国或希伯来的阴历来描述就很不一样了,倘若我们有来自火星的顾客,基于他们使用的火星历来描述这个时间点就更不一样了。
类库设计者决定将保存时间与给时间点命名分开。所以标准Java类库分别包含了两个类:一个是用来表示时间点的Date类;另一个是用大家熟悉的日历表示法表示日期的LocalDate类。Java 8引入了另外一些类来处理日期和时间的不同方面——有关内容参见后续博客。
将时间度量与日历分开是一种很好的面向对象设计。通常,最好使用不同的类表示不同的概念。
//不要使用构造器来构造LocalDate类的对象。实际上,应当使用静态工厂方法( factorymethod),它会代表你调用构造器。 LocalDate localDate = LocalDate.now(); //可以提供年、月和日来构造对应一个特定日期的对象: LocalDate newDate = LocalDate.of(2008,10,20); //一旦有了一个LocalDate对象,可以用方法 getYear、getMontValue和 getDayOfMonth得到年、月和日: int year = newDate.getYear(); // 2008 int month = newDate.getMonthvalue(); // 10 int day = newDate.getDayOfMonth(); // 20
看起来这似乎没有多大的意义,因为这正是构造对象时使用的那些值。不过,有时可能有一个计算得到的日期,然后你希望调用这些方法来了解它的更多信息。例如,plusDays方法会得到一个新的LocalDate,如果把应用这个方法的对象称为当前对象,这个新日期对象则是距当前对象指定天数的一个新日期:
LocalDate aThousandDaysLater = newDate.plusDays ( 1000) ; year = aThousandDaysLater.getYear(); month = aThousandDaysLater.getMonthvalue(); aThousandDaysLater.getDayOfMonth();
LocalDate类封装了实例字段来维护所设置的日期。如果不查看源代码,就不可能知道类内部的日期表示。当然,封装的意义就在于内部表示并不重要,重要的是类对外提供的方法。
Random类
前面我们经常使用Math.random()来获取一个0.0到1.0(不包括1.0)之间的随机double型值。另一种产生随机数的方法是使用java.uitl.Random类,它可以产生一个int、long、double、float和boolean型值。创建一个Random对象时,必须指定一个种子或者使用默认的种子。种子是一个用于初始化一个随机生成器的数字。无参构造方法使用当前已经流逝的时间作为种子,创建一个Random对象。如果两个Random对象有相同的种子,那么它们将产生相同的数列(软件测试中非常有用)。
public class RandomDemo { /** * 可以使用Math.random()获取一个0.0到1.0(不含)之间的随机double型值 * 使用Random产生随机的int、long、float、double、boolean值 * * Random():以当前时间作为种子创建Random对象 * Random(seed:long):以特定值seed作为种子创建随机对象 * * nextInt():int 返回随机的int值 * nextInt(n:int):int 返回一个0到n(不含)之间的随机整数 * nextLong():long * nextFloat():float * nextDouble():double * nextBoolean():boolean */ public static void main(String[] args) { Random random = new Random(); System.out.println("随机的int值:" + random.nextInt()); System.out.println("随机的int值[0--100):" + random.nextInt(100)); System.out.println("随机的long值:" + random.nextLong()); System.out.println("随机的float值:" + random.nextFloat()); System.out.println("随机的double值:" + random.nextDouble()); System.out.println("随机的boolean值:" + random.nextBoolean()); /* * 如果种子数相同,产生的随机序列也是相同 */ Random random1 = new Random(10); for(int i = 0; i < 10; i++) System.out.print(random1.nextInt(100) + " "); System.out.println(); Random random2 = new Random(10); for(int i = 0; i < 10; i++) System.out.print(random2.nextInt(100) + " "); } }
static
静态变量
public class Student{ int id; static int nextId; String name; }
静态变量被类中的所有对象所共享。Student的实例域id和name称为实例变量。实例变量是绑定到某个特定实例的。它是不能被同一个类的不同对象所共享的。例如,假设创建了如下两个对象:
Student peppa = new Student(); Student suzy = new Student();
peppa中的name和suzy中的name是不相关的,它们存储在不同的内存位置。peppa中name的变化不会影响suzy中的name,反之亦然。
静态变量也称为类变量,它被该类的所有实例所共享。静态变量将变量值存储在一个公共的内存地址。因为它是公共地址,所以如果某一个对象修改了静态变量的值,那么同一个类的所有对象都会受到影响。java支持静态方法和静态变量,无须创建类的实例就就可以调用静态方法。
静态常量
类中的常量是被该类的所有对象所共享的。因此,常量应该声明为final static,例如,Math类中的常量PI是如下定义的:
pulbic final static double PI = 3.1415926538979323846;
静态方法
静态方法是不在对象上执行的方法,比如,Math类的pow方法就是一个静态方法。表达式:Math.pow(a,b);会计算幂ab。在完成运算时,它并不使用任何Math对象。
Student类的静态方法不能访问id及name实例字段,因为它不能在对象上执行操作。但是,静态方法可以访问静态字段。下面是这样一个静态方法的示例:
public static int getNextId() { return nextId; }
可以通过类名来调用这个方法:Student.getNextId();
这个方法可以省略关键字static吗?答案是肯定的。但是,这样一来,你就需要通过Student类对象的引用来调用这个方法。
注:可以使用对象调用静态方法,这是合法的。例如,如果peppa是一个 Student对象,可以用peppa.getNextId()代替Student.getNextId()。不过,这种写法很容易造成混绢,其原因是getNextId方法计算的结果与 peppa毫无关系。我们建议使用类名而不是对象来调用静态方法。
在下面两种情况下可以使用静态方法:
- 方法不需要访问对象状态,因为它需要的所有参数都通过显式参数提供(例如:Math.pow)。
- 方法只需要访问类的静态字段(例如:Student.getNextId)。
初始化块
前面已经讲过两种初始化数据字段的方法:
- 在构造器中设置值
- 在声明中赋值。
实际上,Java还有第三种机制,称为初始化块( initialization block)。在一个类的声明中,可以包含任意多个代码块。只要构造这个类的对象,这些块就会被执行。例如,
public class Student { /** * 学生id,用于唯一标识该学生 */ int id; /** * 下一个id号 */ static int nextId; /** * 学生姓名 */ String name; { id = nextId++; } public Student() { } public Student(String name) { this.name = name; } }
在这个示例中,无论使用哪个构造器构造对象,id字段都会在对象初始化块中初始化。首先运行初始化块,然后才运行构造器的主体部分。
这种机制不是必需的,也不常见。通常会直接将初始化代码放在构造器中。
静态代码块
如果类的静态字段需要很复杂的初始化代码,那么可以使用静态的初始化块。
将代码放在一个块中,并标记关键字static。下面是一个示例。其功能是将学生ID的起始值赋子一个小于10000的随机整数。
import java.util.Random; public class Student { /** * 学生id,用于唯一标识该学生 */ int id; /** * 下一个id号 */ static int nextId; /** * 学生姓名 */ String name; static{ var generator = new Random(); nextId = generator.nextInt(10000); } { id = nextId++; } public Student() { } public Student(String name) { this.name = name; } /** * 实例域id的访问器 * @return id实例域值 */ public int getId() { return id; } /** * 实例域id修改器 * @param id id值 */ public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public static int getNextId() { return nextId; } }
在类第一次加载的时候,将会进行静态字段的初始化。与实例字段一样,除非将静态字段显式地设置成其他值,否则默认的初始值是0、false或null。所有的静态字段初始化方法以及静态初始化块都将依照类声明中出现的顺序执行:静态代码块>main>构造代码块>构造器>普通代码块。
可见性修饰符
可见性修饰符可以用于确定一个类以及他的成员的可见性。可以在类、方法、数据域前使用public修饰符,表示他们可以被任何其他类访问。如果没有使用可见性修饰符,那么默认类、方法和数据域可以被同一个包中的任何一个类访问。这称为包私有(package-private)或包内访问(package-access)。
protected允许子类访问父类中的数据域或方法,但不允许非子类访问这些数据域和方法。
private修饰符限定方法和数据域只能在自己的类中访问。注意修饰符private只能应用在类成员上,修饰符public可以应用在类或类成员上。在局部变量上使用public和private都会导致编译错误。在大多数情况下构造方法都是公共的,但如果想防止用户创建类的实例,就使用私有构造方法,实例化时会提示构造方法不可视错误。例如Math类所有方法都是静态方法,所以为防止用户创建Math对象,其构造方法定义如下
private Math(){ }
数据域封装
在前面的程序中,Student类的数据域可以直接修改。这不是一个好的做法,因为:
首先,数据可能被篡改。例如:nextId是用来计算下一个id值的,但是它可能会被错误地设置为一个任意值(例如:Student.nextId = -10).
其次,它使类变得难以维护,同时容易出现错误。假如在其它程序已经使用Student类之后想修改id以确保id是一个非负数。因为使用该类的客户可以直接修改id(例如:peppa.id= -5),所以,不仅要修改Student,而且还要修改使用Student类的这些程序。
为了避免对数据域的直接修改,应该使用private修饰符将数据域声明为私有的,这称为数据域封装。在定义私有数据域的类外的对象是不能访问这个数据域的。但是经常会有客户端需要获取、修改数据域的情况。为了能够访问私有数据域,可以提供一个访问器返回数据域的值(get方法)。为了更新一个数据域,可以提供一个修改器给数据域设置新值(set方法)。
public class Student { /** * 学生id,用于唯一标识该学生 */ private int id; /** * 下一个id号 */ private static int nextId; /** * 学生姓名 */ private String name; { id = nextId++; } public Student() { } public Student(String name) { this.name = name; } /** * 实例域id的访问器 * @return id实例域值 */ public int getId() { return id; } /** * 实例域id修改器 * @param id id值 */ public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public static int getNextId() { return nextId; } }
向方法传递对象参数
给方法传递一个对象,是将对象的引用传递给方法。java只有一种参数传递方式:值传递(pass-by-value)
package edu.uestc.avatar; public class CircleTest { public static void printArea(Circle circle, int times) { System.out.println("Radis\tArea"); for(int i = 1; i <= times; i++) { circle.setRadius(i); System.out.println(circle.getRadius() + "\t" + String.format("%.5f", circle.getArea())); } } public static void main(String[] args) { Circle circle = new Circle(); int times = 5; printArea(circle, times); } }
针对基本数据类型,传递的是基本数据类型值
针对引用数据类型,传递的是引用
对象数组
数组既可以存储基本数据类型,也可以存储对象。对象的数组实际上是引用变量的数组。
不可变对象和类
通常,创建一个对象后,它的内容是允许之后改变的。有时需要创建一个一旦创建其内容就不能再改变的对象。称这种对象为一个不可变对象,而它的类称为不可变类。例如:String类就是不可变的。要使一个类称为不可变的,它必须满足:
- 所有的数据域都是私有的
- 没有修改器方法
- 没有一个返回指向可变数据域的引用的访问器方法
import java.util.Date; public class Employee { private static int nextId; private int id; private String name; private Date hireDate; public Employee(String name) { this.id = nextId; nextId++; this.name = name; this.hireDate = new Date(); } public int getId() { return id; } public String getName() { return name; } public Date getHireDate() { //返回的是Date对象的一个引用。通过这个引用可以改变hireDate的值 //return hireDate; //可以修改为: return (Date)hireDate.clone(); } }
成员变量VS局部变量
对象的属性就是成员变量。局部变量的声明和使用都在一个方法的内部。
共同点:
1.都是变量,他们的定义形式相同:类型 变量名 = 初始化值;
2.都有作用域:作用域是在一对大括号内
不同点:1.内存中存放的位置不同:
成员变量存放在堆空间内
局部变量存放在栈空间内
2.声明的位置不同(作用域不同):
成员变量声明在类的内部,方法的外部,作用整个类;
局部变量声明在方法的内部,从它声明的地方开始到包含它最近的块结束。
3.初始化值不同
成员变量可以不赋初值,其默认值按照其数据类型来定
局部变量必须显式地赋初值
4.权限修饰符不同
成员变量的权限修饰符有四个:public (default) protected private
局部变量没有权限修饰符,其访问权限依据其所在的方法而定(与方法的访问权限相同)
如果一个局部变量和一个成员变量具有相同的名字,那么局部变量优先,而同名的类变量将被隐藏。例如:
public class Demo{ private int x = 0; private int y = 0; public void test(){ int x = 100; System.out.println("x = " + x); System.out.println("y = " + y); } }
用var声明局部变量
在Java 10中,如果可以从变量的初始值推导出它们的类型,那么可以用var关键字声明局部变量,而无须指定类型。例如,可以不这样声明:
Circle circle = new Circle(10);
只需要写以下代码:
var circle = new Circle(10);
这一点很好,因为这样可以避免重复写类型名Circle。
从现在开始,倘若无须了解任何Java API就能从等号右边明显看出类型,在这种情况下我们都将使用var表示法。对Java APl有了更多使用经验后,你可能会希望更多地使用var关键字。
注意var关键字只能用于方法中的局部变量。参数和数据域的类型必须声明。
this引用
关键字this引用对象自身(自身对象的引用)。this指向调用对象本身的引用名。可以使用this关键字引用对象的实例成员。
public class Circle { private double radius;/** * 行为:计算该圆的面积 * @return 圆面积 */ public double getArea() { //省略了this,return 3.14 * this.radius * this.radius; return 3.14 * radius * radius; } }
this关键字可以用于引用类的隐藏数据域。例如,再数据域的set方法中,经常将数据域名用作参数名。在这种情况下,这个数据域在set方法中被隐藏。为了给它设置新值,需要在方法中引用隐藏的数据名。隐藏的静态变量可以简单通过"类名.静态变量"的方式引用。隐藏的实例变量就需要使用this来引用。
public class Circle { private double radius; private static int numberOfObjects; /** * 关键字this是指调用实例方法setRadius的对象 * @param radius 半径 */ public void setRadius(double radius) { this.radius = radius; } public static void setNumberOfObjects(int numberOfObjects){ Circle.numberOfObjects = numberOfObjects; } }
关键字this可以用于调用同一个类的另一个构造方法。例如
public class Circle { private double radius; /** * java规定:在构造方法中使用this(参数列表)调用构造方法应在任何其它可执行语句之前出现(出现在第一句) * @param newRadius 半径 */ public Circle() { this(1.0); } public Circle(double radius) { this.radius = radius; } }
如果一个类有多个构造方法,最好尽可能使用this(参数列表)实现它们。这样做通常可以简化代码,使类易于阅读和维护。