原文出自 http://www.cnblogs.com/ggjucheng/archive/2012/11/27/2791380.html
类
类是对象的蓝图或者原型。这一部分会详细说明类是怎样模拟现实世界中对象的状态和行为的。它是基础,它会告诉你就是一个简单的类也能很好地模拟对象的状态和行为。
在现实世界中,你会发现很多独特的对象具有相同的种类。比如成千上万辆自行车,制作方法和模型都是相同的。每一个辆自行车都是从相同的设备和图纸中制造出来的,所以具有相同的组件。在面向对象的术语中,我们就说这辆自行车是自行车这个对象的中的一个实例。
下面给出自行车这个类的用法:
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类的声明看起来如下:
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; } }
山地继承自行车的所有字段和方法,并增加了seatHeight属性,还有一个方法来设置该属性(山地自行车有座位可移动,由于地形的要求)。
声明类
可以使用如下方式声明类:
class MyClass { // field, constructor, and // method declarations }
这是一个类声明,类结构体(大括号之间的区域)包含了基于类创建对象的整个生命周期的所有的代码:初始化新对象的构造方法,提供类和对象的状态的变量声明,实现类和对象的行为的方法声明。
上面的类声明是一个最小版本,它只包含类声明的必须元素。你可以为类声明提供更多的信息,例如父类的名字,实现任何接口。例如:
class MyClass extends MySuperClass implements YourInterface { // field, constructor, and // method declarations }
这表示MyClass是MySuperClass的子类,并且它实现了YourInterface接口。
你也可以类声明的一开始添加修饰符,例如public,private,这会决定哪些类可以访问MyClass。
一般来说,类声明按照顺序可以包含以下元素:
修饰符:public,private等等
类名:按照惯例,首字母大写。
父类的类名:如果有父类,前面要有关键字extends,一个类只能有一个父类。
使用逗号隔开的接口列表:如果要实现接口,前面要有关键字implements,一个类可以实现多个接口。
类体:使用大括号{}包围着。
声明成员变量
有几种类型的变量:
类的变量:成员变量
方法内或者代码块里的变量:局部变量
方法声明的变量:参数
Bicycle类使用以下代码声明它的成员变量
public int cadence; public int gear; public int speed;
成员变量声明,按照顺序由以下三个元素组成:
0或多个修饰符,例如public,private
成员变量的类型
成员变量的名字
Bicycle的成员变量cadence,gear,speed的数据类型都是int,public修饰符表示他们都是公有成员,可以被任何对象访问。
访问修饰符
第一个修饰符,用于控制其它类访问成员变量的访问权限,一般情况下,只有public和private,其他访问修饰符后面讨论。
public修饰的成员变量可以被其他类访问
private修饰的成员变量只能在自己的类里访问。
基于封装的精神,常见的方式是把成员变量设置为private.这表示这些变量只能被Bicycle类直接访问。但是我们依然需要访问这些值,不过我们可以通过添加public方法去获取成员变量的值:
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等。或者你可以使用引用类型,例如String,数组,类。
变量名
所有变量,无论是成员变量,局部变量,参数,都要遵从变量的命令规则。
对于方法和类名的命令,推荐如下:
类名的第一个字母应大写
一个方法的名称的第一个单词(或唯一)应该是一个动词。
声明成员方法
这里有一个典型的方法声明:
public double calculateAnswer(double wingSpan, int numberOfEngines, double length, double grossTons) { //do the calculation here }
方法声明的必须元素:方法的返回类型,方法的名字,一对圆括号(),和代码块之间的大括号{}
更加通用的是,方法声明按照顺序有六个元素:
修饰符:例如public,private,还有其他。
返回类型:方法的返回值的类型,或者是不需要返回值的void.
方法名:成员变量的命名规则也适用于方法命名,但是惯例有点不同
方法参数:在小括号()内,适用逗号隔开,前面有数据类型的输入参数列表。如果没有参数,就是适用空的小括号。
异常列表
方法体:方法的代码位于大括号内,包括局部变量的声明。
声明:一个方法声明的两个元素元素,就是方法签名----方法的名称和参数类型。
方法签名声明如下是:
calculateAnswer(double, int, double, double)
方法命名
尽管方法的名字可以是任何合法的标识符,代码约定限制的方法名。按照惯例,方法的名称应该是一个小写开头的动词,或小写开头的动词,紧跟形容词,动词等的多个单词。
如果是多个单词,第一个单词的首字母小写,后面的单词的首字母大写。下面是一些例子:
run
runFast
getBackground
getFinalData
compareTo
setX
isEmpty
典型的是,一个方法的名字在类里是唯一的,但是通过重载,一个方法的名字可以和其他方法的名字一样。
方法重载
java编程语言支持方法重载,Java可以区分不同的方法签名的方法。这意味着,在一个类中的方法可以有相同的名字,只要他们有不同的参数列表。
假设你有一个类,可以用书法绘制各种数据类型(字符串,整数等)包含绘制每个数据类型的方法。如果对于每种类型,就新建一个方法名,这是很麻烦的,例如,drawString,drawInteger,drawFloat等。在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; }
创建Bicycle对象myBike时,通过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(); 调用无参构造方法创建一个Bicycle对象yourBike
这两个构造方法可以都在Bicycle中声明,因为他们有不同的参数列表。和方法一样,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; }
这个方法有四个参数:贷款额loanAmt,利率rate,未来值futureValue,周期数numPeriods。前面三个是浮点类型,第四是整数。参数在方法体中使用,并在运行时将传入的参数的值
注意:参数是指在方法声明的变量列表。参数是在调用方法时传递的实际值。当你调用一个方法时,使用的参数必须匹配声明的参数的类型和顺序。
参数类型
您可以使用任何数据类型的方法或构造函数的参数。这包括基本数据类型,如你看到的computePayment方法的double,float和integers,还有引用类型,如对象和数组。
下面是一个例子,一个接受一个数组作为参数的方法。在这个例子中,该方法将创建一个新的多边形对象,并从一个Point对象数组(假设该点是一个类,它代表一个x,y坐标)初始化对象:
public Polygon polygonFrom(Point[] corners) { // method body goes here }
注意:java编程语言不能让你传到方法类型作为方法的参数,但是你可以使用参数作为方法的参数,再调用对象的方法。
可变数目的参数
你可以使用可变参数传入一组可变数目的变量给方法。当你不知道有多少个特定类型的参数会传给方法时,可以使用可变参数。它也是一种便利方式手动创建数组(先前的方法使用可变参数,比使用数组好)
使用可变参数,你可以在最后一个方法的参数后面跟一个省略号(三个点,...),接着一个空格,然后是参数名。方法调用时,可以传入任意参数,包括无参数。
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被当作一个数组。方法调用时,可以传入一个数组,或者是一个参数列表。而不管是什么情况,在方法里的代码会把参数当作数组。
你将会见到最常用的控制台输出方法的可变参数,例如
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有三个成员变量:x, y, radius。setOrigin方法有两个参数,它们都和成员变量有一样的名字。每个方法的参数都会隐藏名字和它一样的成员变量。所以在方法体里简单使用名字x或者y只能引用参数,无法引用成员变量。如果想引用成员变量,必须使用合法的名字,这个就是后面讨论的"使用关键字this"。
传递原生数据的类型参数
原生类型参数,如int,double,都是通过值传递。这意味着参数的值的任何改变都只是在方法体内。当方法返回后,参数会销毁,他们的任何改变会失效。这里是一个例子:
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对象的x,y坐标,值分别是23,56。这些改变在方法返回后是持久的。
接着circle重新赋值,指向了x=0和y=0的新对象Circle,但是重新赋值没有持久性,因为这个引用是通过值传递的,是不会改变的。
在方法内,指向circle的对象已经被修改了,当方法返回时,myCircle依然引用着调用方法前的同个Circle对象。