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;
    }
        
}

做为Bicycle的子类的MountainBike类的类声明可能以下所示:segmentfault

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和设置它的方法(山地自行车有座位,能够根据地形要求上下移动)。数组

声明类

你已经看到如下列方式定义的类:app

class MyClass {
    // field, constructor, and 
    // method declarations
}

这是一个类声明,类主体(大括号之间的区域)包含为从类建立的对象的生命周期提供的全部代码:用于初始化新对象的构造函数,提供类及其对象状态的字段的声明,以及实现类及其对象行为的方法。编程语言

前面的类声明是最小的,它仅包含类声明所需的那些组件,你能够在类声明的开头提供有关该类的更多信息,例如其超类的名称,是否实现任何接口等等,例如:ide

class MyClass extends MySuperClass implements YourInterface {
    // field, constructor, and
    // method declarations
}

表示MyClassMySuperClass的子类,它实现了YourInterface接口。函数

你也能够在最开始添加publicprivate等修饰符 — 这样你就能够看到类声明的开头行可能变得很是复杂,publicprivate修饰符决定了其余类能够访问MyClass的内容,本课程稍后将对此进行讨论。关于接口和继承的课程将解释如何以及为何在类声明中使用extendsimplements关键字,目前你不须要担忧这些额外的。this

一般,类声明能够按顺序包含这些组件:rest

  1. 修饰符,例如publicprivate以及稍后你将遇到的许多其余修饰符。
  2. 类名,首字母大写。
  3. 类的父级(超类)的名称(若是有)以关键字extends开头,一个类只能extend(子类)一个父类。
  4. 由类实现的以逗号分隔的接口列表(若是有),前面是关键字implements,一个类能够implement多个接口。
  5. 类体,被括号围绕,{}

声明成员变量

有几种变量:code

  • 类中的成员变量 — 这些变量称为字段。
  • 方法或代码块中的变量 — 这些变量称为局部变量。
  • 方法声明中的变量 — 这些变量称为参数。

Bicycle类使用如下代码行来定义其字段:

public int cadence;
public int gear;
public int speed;

字段声明按顺序由三个部分组成:

  1. 零个或多个修饰符,例如publicprivate
  2. 该字段的类型。
  3. 该字段的名称。

Bicycle的字段被命名为cadencegearspeed,而且都是整数数据类型(int),public关键字将这些字段标识为公共成员,可由任何能够访问该类的对象访问。

访问修饰符

使用的第一个(最左侧)修饰符容许你控制哪些其余类能够访问成员字段,目前,只考虑publicprivate,其余访问修饰符将在后面讨论。

  • public修饰符 — 能够从全部类访问该字段。
  • private修饰符 — 该字段只能在其本身的类中访问。

本着封装的精神,将字段设为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;
    }
}

类型

全部变量都必须具备类型,你可使用原始类型,如intfloatboolean等,或者你可使用引用类型,例如字符串、数组或对象。

变量名

全部变量(不管是字段、局部变量仍是参数)都遵循“语言基础”课程“变量命名”中介绍的相同命名规则和约定。

在本课程中,请注意相同的命名规则和约定用于方法和类名称,除了:

  • 类名的第一个字母应该大写。
  • 方法名称中的第一个(或惟一)单词应该是动词。

定义方法

如下是典型方法声明的示例:

public double calculateAnswer(double wingSpan, int numberOfEngines,
                              double length, double grossTons) {
    //do the calculation here
}

方法声明中惟一必需的元素是方法的返回类型、名称、一对圆括号()和大括号之间的主体{}

更通常地,方法声明有六个组件,顺序以下:

  1. 修饰符 — 例如publicprivate和其余你将在稍后了解的内容。
  2. 返回类型 — 方法返回的值的数据类型,若是方法未返回值,则返回void
  3. 方法名称 — 字段名称的规则也适用于方法名称,但约定略有不一样。
  4. 括号中的参数列表 — 以逗号分隔的输入参数列表,前面是数据类型,括在括号中(),若是没有参数,则必须使用空括号。
  5. 一个异常列表 — 稍后讨论。
  6. 括号之间的方法体 — 方法的代码,包括局部变量的声明,在这里。

修饰符、返回类型和参数将在本课程的后面部分讨论,异常将在后面的课程中讨论。

定义:方法声明的两个组件包括方法签名 — 方法的名称和参数类型。

上面声明的方法的签名是:

calculateAnswer(double, int, double, double)

命名方法

虽然方法名称能够是任何合法标识符,但代码约定限制方法名称,按照惯例,方法名称应该是小写的动词或以小写的动词开头的多单词名称,后跟形容词、名词等。在多单词名称中,第二个和后面每一个单词的第一个字母应该大写,这里有些例子:

run
runFast
getBackground
getFinalData
compareTo
setX
isEmpty

一般,方法在其类中具备惟一名称,可是,因为方法重载,方法可能与其余方法具备相同的名称。

重载方法

Java编程语言支持重载方法,Java能够区分具备不一样方法签名的方法,这意味着若是类中的方法具备不一样的参数列表,则它们能够具备相同的名称(有一些条件,将在标题为“接口和继承”的课程中讨论)。

假设你有一个类可使用书法来绘制各类类型的数据(字符串、整数等),而且包含绘制每种数据类型的方法,为每一个方法使用新名称很麻烦 — 例如,drawStringdrawIntegerdrawFloat等等。在Java编程语言中,你能够对全部绘图方法使用相同的名称,可是为每一个方法传递不一样的参数列表,所以,数据绘图类可能会声明四个名为draw的方法,每一个方法都有一个不一样的参数列表。

public class DataArtist {
    ...
    public void draw(String s) {
        ...
    }
    public void draw(int i) {
        ...
    }
    public void draw(double f) {
        ...
    }
    public void draw(int i, double f) {
        ...
    }
}

重载方法由传递给方法的参数的数量和类型区分,在代码示例中,draw(String s)draw(int i)是不一样且惟一的方法,由于它们须要不一样的参数类型。

你不能声明具备相同名称和相同数量和类型的参数的多个方法,由于编译器没法区分它们。

在区分方法时编译器不考虑返回类型,所以即便它们具备不一样的返回类型,也不能声明具备相同签名的两个方法。

注意:应谨慎使用重载方法,由于它们会使代码的可读性下降。

为你的类提供构造函数

类包含被调用以从类蓝图建立对象的构造函数,构造函数声明看起来像方法声明 — 除了它们使用类的名称而且没有返回类型,例如,Bicycle有一个构造函数:

public Bicycle(int startCadence, int startSpeed, int startGear) {
    gear = startGear;
    cadence = startCadence;
    speed = startSpeed;
}

要建立一个名为myBike的新Bicycle对象,new运算符将调用构造函数:

Bicycle myBike = new Bicycle(30, 0, 8);

new Bicycle(30, 0, 8)为对象建立内存空间并初始化其字段。

虽然Bicycle只有一个构造函数,但它可能有其余构造函数,包括一个无参构造函数:

public Bicycle() {
    gear = 1;
    cadence = 10;
    speed = 0;
}

Bicycle yourBike = new Bicycle();调用无参构造函数来建立一个名为yourBike的新Bicycle对象。

两个构造函数均可以在Bicycle中声明,由于它们具备不一样的参数列表,与方法同样,Java平台根据列表中的参数数量及其类型来区分构造函数。你不能为相同类编写两个具备相同参数数量和类型的构造函数,由于平台没法区分它们,这样作会致使编译时错误。

你没必要为你的类提供任何构造函数,但在执行此操做时必须当心,编译器自动为没有构造函数的任何类提供无参数的默认构造函数,此默认构造函数将调用超类的无参数构造函数,在这种状况下,若是超类没有无参数构造函数,编译器将会报错,所以你必须验证它是否有,若是你的类没有显式的超类,那么它有一个隐式的超类Object,它有一个无参数的构造函数。

你能够本身使用超类构造函数,本课开头的MountainBike类就是这样作的,稍后将在有关接口和继承的课程中对此进行讨论。

你能够在构造函数的声明中使用访问修饰符来控制哪些其余类能够调用构造函数。

注意:若是另外一个类不能调用 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;
}

此方法有四个参数:贷款金额、利率、将来价值和期数,前三个是双精度浮点数,第四个是整数,参数在方法体中使用,而且在运行时将采用传入的参数的值。

注意:参数是指方法声明中的变量列表,参数是调用方法时传递的实际值,调用方法时,使用的参数必须与声明参数的类型和顺序匹配。

参数类型

你能够将任何数据类型用于方法或构造函数的参数,这包括原始数据类型,如在computePayment方法中看到的双精度数、浮点数和整数,以及引用数据类型,如对象和数组。

这是一个接受数组做为参数的方法示例,在此示例中,该方法建立一个新的Polygon对象,并从Point对象数组中初始化它(假设Point是一个表示x,y坐标的类):

public Polygon polygonFrom(Point[] corners) {
    // method body goes here
}
注意:若是要将方法传递给方法,请使用 lambda表达式或方法引用。

任意数量的参数

你可使用名为可变参数的构造将任意数量的值传递给方法,当你不知道将多少特定类型的参数传递给该方法时,你可使用可变参数,这是手动建立数组的快捷方式(前一种方法可使用可变参数而不是数组)。

要使用可变参数,你经过省略号跟随最后一个参数的类型(三个点,...),而后是空格和参数名称,而后可使用任何数量的参数调用该方法,包括无参数。

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);

    // more method body code follows that creates and returns a 
    // polygon connecting the Points
}

你能够看到,在方法内部,corners被视为数组,可使用数组或参数序列调用该方法,在任何一种状况下,方法体中的代码都会将参数视为数组。

你最多见的是使用打印方法的可变参数,例如,这个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);

或者还有不一样数量的参数。

参数名

向方法或构造函数声明参数时,为该参数提供名称,此名称在方法体内用于引用传入的参数。

参数的名称在其范围内必须是惟一的,它不能与同一方法或构造函数的另外一个参数的名称相同,也不能是方法或构造函数中的局部变量的名称。

参数能够与类的某个字段具备相同的名称,若是是这种状况,则称该参数遮蔽该字段,遮蔽字段可能使你的代码难以阅读,而且一般仅在设置特定字段的构造函数和方法中使用,例如,考虑如下Circle类及其setOrigin方法:

public class Circle {
    private int x, y, radius;
    public void setOrigin(int x, int y) {
        ...
    }
}

Circle类有三个字段:xyradiussetOrigin方法有两个参数,每一个参数与其中一个字段具备相同的名称,每一个方法参数都会影响共享其名称的字段,所以,在方法体内使用简单名称x或y是指参数,而不是字段。要访问该字段,你必须使用限定名称,这将在本课程后面的“使用this关键字”一节中讨论。

传递原始数据类型参数

原始参数(如intdouble)按值传递给方法,这意味着对参数值的任何更改都仅存在于方法的范围内,方法返回时,参数消失,对它们的任何更改都将丢失,这是一个例子:

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

传递引用数据类型参数

引用数据类型参数(如对象)也按值传递给方法,这意味着当方法返回时,传入的引用仍然引用与之前相同的对象,可是,若是对象的字段的值具备适当的访问级别,则能够在该方法中更改它们的值。

例如,考虑任意类中移动Circle对象的方法:

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)

在方法内部,circle最初引用的是myCircle,该方法将circle引用的对象(即myCircle)的x和y坐标分别改变23和56,方法返回时,这些更改将保持不变。而后circle被赋予新的Circle对象的引用,其中x = y = 0,可是,这种从新分配没有永久性,由于引用是按值传递的,不能更改,在该方法中,circle指向的对象已更改,可是,当方法返回时,myCircle仍然引用与调用方法以前相同的Circle对象。


上一篇:控制流语句

下一篇:对象

相关文章
相关标签/搜索