java学习笔记(八)-类和对象
文章目录
类
在名为“面向对象编程概念”的课程中,对面向对象概念的介绍以 自行车类为例,其中赛车,山地自行车和双人自行车为子类。这是一个Bicycle
类的可能实现的示例代码,以概述类的声明。
public class Bicycle {
// the Bicycle class has
// three fields
public int cadence;
public int gear;
public int speed;
// the Bicycle class has
// one constructor
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
// the Bicycle class has
// four methods
public void setCadence(int newValue) {
cadence = newValue;
}
public void setGear(int newValue) {
gear = newValue;
}
public void applyBrake(int decrement) {
speed -= decrement;
}
public void speedUp(int increment) {
speed += increment;
}
}
类声明的MountainBike
类,它是一个子类Bicycle
可能是这样的:
public class MountainBike extends Bicycle {
// the MountainBike subclass has
// one field
public int seatHeight;
// the MountainBike subclass has
// one constructor
public MountainBike(int startHeight, int startCadence,
int startSpeed, int startGear) {
super(startCadence, startSpeed, startGear);
seatHeight = startHeight;
}
// the MountainBike subclass has
// one method
public void setHeight(int newValue) {
seatHeight = newValue;
}
}
MountainBike
继承的所有字段和方法,Bicycle
并添加该字段seatHeight
及其设置方法(山地自行车的座椅可以根据地形要求上下移动)。
类的一些相关信息
class MyClass {
//字段,构造函数和
//方法声明
}
这是一个类声明。所述类体(括号之间的区域)中包含的所有提供了用于从类中创建的对象的生命周期的代码:构造函数来初始化新的对象,声明为提供所类及其对象的状态的领域,并实现类及其对象的行为的方法。
前面的类声明是最小的。它仅包含类声明中必需的那些组件。您可以在类声明的开头提供有关该类的更多信息,例如其超类的名称,它是否实现任何接口等等。
class MyClass extends MySuperClass implements YourInterface {
//表示它MyClass是的子类MySuperClass并实现YourInterface接口。
//字段,构造函数和
//方法声明
}
您还可以在开始时添加诸如public或private之类的修饰符,因此您可以看到类声明的开头可能会变得非常复杂。
通常,类声明可以按顺序包括以下组件:
- 修饰符,例如public,private和以后将遇到的许多其他修饰符。
- 类名,首字母大写为约定。
- 类的父级(超类)的名称(如果有的话),以关键字extends开头。一个类只能扩展(子类)一个父级。
- 由类实现的接口的列表,以逗号分隔(如果有的话),并以关键字Implements开头。一个类可以实现多个接口。
- 类主体,用大括号{}包围。
声明成员变量
变量
所有变量,无论它们是字段,局部变量还是参数,
有几种变量:
- 类中的成员变量,这些变量称为公共变量。
- 方法或代码块中的变量 -这些称为局部变量。
- 方法声明中的变量-称为私有变量。
public int cadence;
public int gear;
public int speed;
字段声明按顺序由三个部分组成:
-
零个或多个修饰符,例如
public
或private
。 -
字段的类型。
-
字段名称。
访问修饰符
使用第一个(最左侧)修饰符可以控制其他哪些类可以访问成员字段。
目前,仅考虑public
和private
。
public
修饰符-可以从所有类访问该字段。private
修饰符-该字段只能在其自己的类中访问。
本着封装的精神,通常将字段设为私有。这意味着只能从Bicycle类直接访问它们。但是,我们仍然需要访问这些值。这可以通过添加公共方法来间接完成,这些方法可以为我们获取字段值:
public class Bicycle {
private int cadence;
private int gear;
private int speed;
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
public int getCadence() {
return cadence;
}
public void setCadence(int newValue) {
cadence = newValue;
}
public int getGear() {
return gear;
}
public void setGear(int newValue) {
gear = newValue;
}
public int getSpeed() {
return speed;
}
public void applyBrake(int decrement) {
speed -= decrement;
}
public void speedUp(int increment) {
speed += increment;
}
}
类型
所有变量必须具有类型。您可以使用原始的类型,例如int
,float
,boolean
等你也可以使用引用类型,如字符串,数组或对象。
命名方式
方法和类名使用相同的命名规则和约定:
- 班级名称的首字母应大写,并且
- 方法名称中的第一个(或唯一)词应该是动词。
方法
方法定义
这是典型方法声明的示例:
public double calculateAnswer(double wingSpan, int numberOfEngines,
double length, double grossTons) {
//do the calculation here
}
方法声明中唯一需要的元素是方法的返回类型,名称,一对括号和()
,以及括号之间的主体{}
。
通常,方法声明按顺序包含六个部分:
- 改范围,如
public
,private
和其他人,你会了解以后。 - 返回类型-方法返回的值的数据类型,或者
void
该方法未返回值。 - 方法名称-字段名称的规则也适用于方法名称,但是约定有所不同。
- 括号中的参数列表-用逗号分隔的输入参数列表,其后为数据类型,并用括号括起来
()
。如果没有参数,则必须使用空括号。 - 例外列表-稍后再讨论。
- 括号之间的方法主体(此处是方法的代码,包括局部变量的声明)位于此处。
上面声明的方法的签名是:
computeAnswer(double,int,double,double)
命名方法
尽管方法名称可以是任何合法标识符,但是代码约定限制了方法名称。
方法名称应为小写的动词或以小写的动词开头的多词名称,后跟形容词,名词等。在多词名称中,第二个单词及其后每个单词的第一个字母应该大写。
run
runFast
getBackground
getFinalData
compareTo
setX
isEmpty
通常,方法在其类中具有唯一的名称。但是,由于方法重载,方法可能与其他方法同名。
重载方法
Java编程语言支持重载方法,而Java可以区分具有不同方法签名的方法。这意味着,如果类中的方法具有不同的参数列表,则它们可以具有相同的名称(对此有一些限定条件,将在标题为“接口与继承”的课程中进行讨论)。
public class DataArtist {
...
public void draw(String s){
...
}
public void draw(int i){
...
}
public void draw(double f){
...
}
public void draw(int i,int f){
...
}
}
重载的方法通过传递给方法的参数的数量和类型来区分。
您不能声明多个具有相同名称,相同数量和类型的参数的方法,因为编译器无法区分它们。
区分方法时,编译器不会考虑返回类型,因此即使两个方法具有不同的返回类型,也无法声明具有相同签名的两个方法。
注意: 应该谨慎使用重载的方法,因为它们会使代码的可读性大大降低。
构建函数
一个类包含构造函数,这些构造函数被调用以根据该类蓝图创建对象。构造函数声明类似于方法声明,只是它们使用类的名称且没有返回类型。
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
要创建一个Bicycle
名为的新对象myBike
,new
操作员将调用一个构造函数:
Bicycle myBike = new Bicycle(30, 0, 8);
尽管Bicycle
只有一个构造函数,但也可以有其他构造函数,包括无参数构造函数:
public Bicycle() {
gear = 1;
cadence = 10;
speed = 0;
}
可以声明两个构造函数,因为它们具有不同的参数列表。与方法一样,Java平台根据列表中参数的数量及其类型来区分构造函数。您不能为同一个类编写具有相同数量和类型的参数的两个构造函数,因为平台无法区分它们。这样做会导致编译时错误。
您不必为类提供任何构造函数,但是在执行此操作时必须小心。编译器会自动为任何没有构造函数的类提供无参数的默认构造函数。此默认构造函数将调用超类的无参数构造函数。在这种情况下,如果超类没有无参数构造函数,则编译器将抱怨,因此您必须验证它是否具有。如果您的类没有显式超类,则它具有的隐式超类Object
,它确实具有无参数构造函数。您可以自己使用超类构造函数。
您可以在构造函数的声明中使用访问修饰符,以控制其他哪些类可以调用构造函数。
注意: 如果另一个类不能调用MyClass
构造函数,则它不能直接创建MyClass
对象。
函数之间的参数传递
方法或构造函数的声明声明了该方法或构造函数的参数的数目和类型。
public double computePayment(
double loanAmt,
double rate,
double futureValue,
int numPeriods) {
double interest = rate / 100.0;
double partial1 = Math.pow((1 + interest),
- numPeriods);
double denominator = (1 - partial1) / interest;
double answer = (-loanAmt / denominator)
- ((futureValue * partial1) / denominator);
return answer;
}
注意: 参数是指方法声明中的变量列表。参数是调用该方法时传递的实际值。调用方法时,使用的参数必须在类型和顺序上与声明的参数匹配。
参数类型
您可以将任何数据类型用于方法或构造函数的参数。
public Polygon polygonFrom(Point[] corners) {
// method body goes here
}
注意: 如果要将方法传递给方法,请使用 lambda表达式或 方法引用。
任意数量的参数
可以使用名为varargs的构造将任意数量的值传递给方法。当您不知道将有多少特定类型的参数传递给方法时,可以使用varargs。这是手动创建数组的快捷方式(前面的方法可能使用varargs而不是数组)。
要使用varargs,请在最后一个参数的类型后面加上省略号(三个点,…)、空格和参数名。然后可以使用该参数的任意数量(包括无)调用该方法。
public Polygon polygonFrom(Point... corners) {
int numberOfSides = corners.length;
double squareOfSide1, lengthOfSide1;
squareOfSide1 = (corners[1].x - corners[0].x)
* (corners[1].x - corners[0].x)
+ (corners[1].y - corners[0].y)
* (corners[1].y - corners[0].y);
lengthOfSide1 = Math.sqrt(squareOfSide1);
//接下来是更多的方法主体代码,它们创建并返回
//连接点的多边形
}
您可以看到,在方法内部,它corners
被视为数组。可以使用数组或参数序列调用该方法。在任何一种情况下,方法主体中的代码均会将参数视为数组。
您通常会在打印方法中看到varargs。例如,此printf
方法:
public PrintStream printf(String format, Object... args)
允许您打印任意数量的对象。可以这样称呼:
System.out.printf("%s: %d, %s%n", name, idnum, address);
或者像这样
System.out.printf("%s: %d, %s, %s, %s%n", name, idnum, address, phone, email);
或使用其他数量的参数。
参数名称
在为方法或构造函数声明参数时,请为该参数提供名称。在方法主体中使用该名称来引用传入的参数。
参数名称在其范围内必须唯一。它不能与相同方法或构造函数的另一个参数的名称相同,也不能与方法或构造函数中的局部变量的名称相同。
参数可以与类的字段之一具有相同的名称。
public class Circle {
private int x, y, radius;
public void setOrigin(int x, int y) {
...
}
}
该Circle
班有三个字段:x
,y
,和radius
。该setOrigin
方法有两个参数,每个参数与一个字段具有相同的名称。每个方法参数遮盖共享其名称的字段。因此,使用简单名称x
或y
在方法主体内使用的是参数,而不是字段。要访问该字段,必须使用限定名称。
传递原始数据类型参数(实参)
原始参数(例如an int
或a)通过valuedouble
传递到方法中。这意味着对参数值的任何更改仅存在于方法范围内。方法返回时,参数消失,对它们的任何更改都将丢失。
public class PassPrimitiveByValue {
public static void main(String[] args) {
int x = 3;
// invoke passMethod() with
// x as argument
passMethod(x);
// print x to see if its
// value has changed
System.out.println("After invoking passMethod, x = " + x);
}
// change parameter in passMethod()
public static void passMethod(int p) {
p = 10;
}
}
运行此程序时,输出为:
After invoking passMethod, x = 3
传递参考数据类型参数(形参)
引用数据类型参数(例如对象)也通过value传递到方法中。这意味着当方法返回时,传入的引用仍然引用与以前相同的对象。但是,如果对象的字段的值具有适当的访问级别,则可以在方法中更改它们。
public void moveCircle(Circle circle, int deltaX, int deltaY) {
// code to move origin of circle to x+deltaX, y+deltaY
circle.setX(circle.getX() + deltaX);
circle.setY(circle.getY() + deltaY);
// code to assign a new reference to circle
circle = new Circle(0, 0);
}
使用以下参数调用该方法:
moveCircle(myCircle,23,56)