09-对象和类
-
9.1 引言
- 面向对象编程使得大型软件及图形用户界面的开发变得更加高速。
-
9.2 为对象定义类
- 类为对象定义属性和行为。
- 对象代表现实世界中可以明确标识的一个实体。
- 一个对象的状态或属性是由数据域及其当前值来表示的。
- 一个对象的行为,也叫动作是由方法定义的。调用对象的一个方法就是要求对象完成一个动作。
- 类是一个模板、蓝本或者合约,用来定义对象的数据域以及方法。对象是类的实例,可以从一个类中创建多个实例,创建实例的过程称为实例化。
- Java类使用变量定义数据域,使用方法定义动作。类还提供了一种称为构造方法的特殊类型的方法,调用他可以创建一个新对象。
- 构造方法可以表示为:
ClassName(parameterName:parameterType)
- 方法可以表示为:
methodName(parameterName:parameterTypa):returnType
-
9.3 示例:定义类和创建对象
类定义了对象,对象是从类创建的。package chapter09; public class TestCircle { public static void main(String[] args) { Circle circle1 = new Circle(); System.out.println("The area of the circle of radius " + circle1.radius + " is " + circle1.getArea()); Circle circle2 = new Circle(25); System.out.println("The area of the circle of radius " + circle2.radius + " is " + circle2.getArea()); Circle circle3 = new Circle(125); System.out.println("The area of the circle of radius " + circle3.radius + " is " + circle3.getArea()); circle2.radius = 100; System.out.println("The area of the circle of radius " + circle2.radius + " is " + circle2.getArea()); } } class Circle{ double radius; Circle(){ radius = 1; } Circle(double newRadius){ radius = newRadius; } double getArea(){ return radius * radius * Math.PI; } double getPeremeter(){ return 2 * radius * Math.PI; } void setRadius(double newRadius){ radius = newRadius; } }
package chapter09; import com.sun.org.apache.bcel.internal.generic.ALOAD; public class AlternativeCircle { public static void main(String[] args) { AlternativeCircle circle1 = new AlternativeCircle(); System.out.println("The area of the circle of radius " + circle1.radius + " is " + circle1.getArea()); AlternativeCircle circle2 = new AlternativeCircle(25); System.out.println("The area of the circle of radius " + circle2.radius + " is " + circle2.getArea()); AlternativeCircle circle3 = new AlternativeCircle(125); System.out.println("The area of the circle of radius " + circle3.radius + " is " + circle3.getArea()); circle2.radius = 100; System.out.println("The area of the circle of radius " + circle2.radius + " is " + circle2.getArea()); } double radius; AlternativeCircle(){ radius = 1; } AlternativeCircle(double newRadius){ radius = newRadius; } double getArea(){ return radius * radius * Math.PI; } double getPerimeter(){ return 2 * radius * Math.PI; } void setRadius(double newRadius){ radius = newRadius; } }
- 绘制UML图的时候,:符号+表示publice修饰符,符号-表示private修饰符。
package chapter09; public class TV { int channel = 1; int volumeLevel = 1; boolean on = false; public TV(){ } public void turnOn(){ on = true; } public void turnOff(){ on = false; } public void setChannel(int newChannel){ if (on && newChannel >= 1 && newChannel <= 120) channel = newChannel; } public void setVolumeLevel(int newVolumnLevel){ if (on && newVolumnLevel >= 1 && newVolumnLevel <= 7) volumeLevel = newVolumnLevel; } public void channelUp(){ if (on && channel < 120) channel++; } public void channelDown(){ if (on && channel > 1) channel--; } public void volumnUp(){ if (on && volumeLevel < 7) volumeLevel++; } public void volumnDowm(){ if (on && volumeLevel > 1) volumeLevel--; } }
package chapter09; public class TestTV { public static void main(String[] args) { TV tv1 = new TV(); tv1.turnOn(); tv1.setChannel(30); tv1.setVolumeLevel(3); TV tv2 = new TV(); tv2.turnOn(); tv2.channelUp(); tv2.channelUp(); tv2.volumnUp(); System.out.println("tv1's channel is " + tv1.channel + " and volumn level is " + tv1.volumeLevel); System.out.println("tv2's channel is " + tv2.channel + " and volumn level is " + tv2.volumeLevel); } }
-
9.4 使用构造方法构造对象
- 使用new操作符调用构造方法创建对象。
- 构造方法是一种特殊的方法,有以下三个特殊之处:
- 构造方法必须和所在的类名字相同。
- 构造方法没有返回值类型,甚至连void也没有。
- 构造方法是子啊创建一个对象是由new操作符调用的。构造方法的作用是初始化对象。
- 构造方法和定义他的类的名字完全相同。和所有其他方法一样,构造方法也可以重载,这样更易于不同的初始值来构造对象。
- 通常,类会提供一个没有参数的构造方法。这样的构造方法称为无参构造方法。
- 在一个类中,用户可能没有定义构造方法。在这种情况下,类中会隐式定义一个方法体为空的无参构造方法。这个构造方法称为默认构造方法,当且仅当类中没有明确定义任何构造方法时才会自动提供。
-
9.5 通过引用变量访问对象
对象的数据和方法可以运用点操作符(.)通过对象的引用变量进行访问。- 9.5.1 引用变量和引用类型
- 对象是通过对象引用变量来访问的,该变量包含了对对象的引用,使用下面的语法声明这样的变量:
ClassName objectRefVar;
- 从表面上看,对象引用变量中似乎存放了一个对象,但事实上,他只是存放了对该对象的引用。
- 在Java中,数组呗看做对象。数组使用new操作符创建的。一个数组变量实际上是一个包含数组引用的变量。
- 对象是通过对象引用变量来访问的,该变量包含了对对象的引用,使用下面的语法声明这样的变量:
- 9.5.2 访问对象的数据和方法
- 在面向对象编程中,对象成员指该对象的数据域和方法。在创建一个对象后,他的数据域不和方法调用可以使用点操作符(.)来进行,哎操作符也称为对象成员访问操作符 :
- objectRefVar.datafield引用对象的数据域。
- objectRefVar.method(arguments)调用对象的方法。
- 数据域radius称为实例变量,因为他依赖某个具体的实例。同样的, getArea方法称为实例方法,因为只能在具体的实例上调用它。实例方法被调用的对象称为调用对象。
- Math类中的所有方法都是用关键字static定义的静态方法。但是getArea()是实例方法,因此它是非静态的。
- 通常,我们创建一个对象,然后将它赋值给一个变量,之后就可以使用这个变量来引用对象。有时候,对象在创建之后并不需要引用。在这种情况下,可以创建一个对象,而不将它明确的赋值给一个变量。如下所示:
new Circle(); //或者 System.out.println("Area is " + new Circle(5).getArea());
- 这种方式创建的对象成为匿名对象。
- 在面向对象编程中,对象成员指该对象的数据域和方法。在创建一个对象后,他的数据域不和方法调用可以使用点操作符(.)来进行,哎操作符也称为对象成员访问操作符 :
- 9.5.3 引用数据域和null值
- 如果一个引用类型的数据域没有引用任何对象,那么这个数据域就有一个特殊的Java值null。null同false和ture一样都是字面值。null是引用类型字面值。
- 引用类型字面值的默认值是null,数值类型字面值的默认值是0,boolean类型字面值的默认值是false,char类型字面值的默认值是‘\u0000’。但是,Java没有给方法中的局部变量赋默认值。
- 9.5.4 基本类型变量和引用类型变量的区别
- 基本类型变量在内存中存储的是一个基本类型值,而引用类型变量存储的是一个引用,他指向对象在内存中的位置。
- 如果你不需要某个对象,可以显式的给该对象的引用变量赋null值,如果该对象没有被任何引用变量所引用,Java虚拟机会自动回收他所占据的空间。
- 9.5.1 引用变量和引用类型
-
9.6 使用Java库中的类
- 9.6.1 Date类
- 9.6.2 Random类
- 如果这两个Random对象有相同的种子,那他们讲产生相同的数列。
- 可以使用java.security.SecureRandom类而不是Random类来产生随机数字。从Random类产生的随机数字是确定的,可能被黑客预测,而从SecureRandom类产生的随机数字是不确定的,因而是安全的。
- 9.6.3 Point2D类
package chapter09; import javafx.geometry.Point2D; import java.util.Scanner; public class TestPoint2D { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("Enter point1's x-,y-coordinates: "); double x1 = input.nextDouble(); double y1 = input.nextDouble(); System.out.print("Enter point2's x-,y-coordinates: "); double x2 = input.nextDouble(); double y2 = input.nextDouble(); Point2D p1 = new Point2D(x1,y1); Point2D p2 = new Point2D(x2,y2); System.out.println("pa is " + p1.toString()); System.out.println("p2 is " + p2.toString()); System.out.println("The distance between p1 and p2 is " + p1.distance(p2)); System.out.println("The midpoint between p1 and p2 is " + p1.midpoint(p2).toString()); } }
- 9.6.1 Date类
-
9.7 静态变量、常量和方法
- 静态变量被类中的所有对象所共享。静态方法不能访问类中的实例成员(也就是实例数据域和方法)
- 如果想要让一个类的所有实例共享数据,就要使用静态变量,也称为类变量。
- 要声明一个静态变量或定义一个静态方法,就要在这个变量或方法的声明中加上修饰符static。静态变量numberOfObjects和静态方法getNumberOfObjects可以声明如下:
static int numberOfObjects; static int getNumberOfObjects(){ return getNumberOfObjects; }
- 类中的变量是被该类的所有对象所共享的,因此,常量应该声明为final static。
package chapter09; public class CircleWithStaticMembers { double radius; static int numnerOfObjects = 0; CircleWithStaticMembers(){ radius = 1; numnerOfObjects++; } CircleWithStaticMembers(double newRadius){ radius = newRadius; numnerOfObjects++; } static int getNumnerOfObjects(){ return numnerOfObjects; } double getArea(){ return radius * radius * Math.PI; } }
package chapter09; public class TestCircleWithStaticMenbers { public static void main(String[] args) { System.out.println("Before creating objects: "); System.out.println("The number of Circle objects is " + CircleWithStaticMembers.numnerOfObjects); CircleWithStaticMembers c1 = new CircleWithStaticMembers(); System.out.println("\nAfter creating c1"); System.out.println("c1: radius (" + c1.radius + ") and number of Circle objects (" + c1.numnerOfObjects + ")"); CircleWithStaticMembers c2 = new CircleWithStaticMembers(5); c1.radius = 9; System.out.println("\nAfter creating c2 and modifying c1"); System.out.println("c1: radius (" + c1.radius + ") and number of Circle objects (" + c1.numnerOfObjects + ")"); System.out.println("c2: radius (" + c2.radius + ") and number of Circle objects (" + c2.numnerOfObjects + ")"); } }
- 使用“类名.方法名(参数)”的方式调用静态方法,使用“类名.静态变量”的方式访问静态变量
- 实例方法可以调用实例方法和静态方法,以及访问实例数据域或者静态数据域。静态方法可以调用静态方法以及访问静态数据域。然而,静态方法不能调用实例方法或访问树立数据域,因为静态方法和静态数据域不属于特定的对象。
- 如何判断一个变量或方法应该是实例的还是静态的?如果一个变量或方法依赖于类的某个具体实例,那就应该将它定义为实例方法或实例变量。如果一个变量或方法不依赖于类的某个具体实例,那就应该将它定义为静态方法或静态变量。例如,每个圆都有自己的半径,半径都依赖于某个具体的圆。因此,半径radius就是Circle类的一个实例变量。由于getArea方法依赖于某个具体的圆,所以,他也是一个实例方法。main方法是静态的。
-
9.8 可见性修饰符
- 可见性修饰符可以用于确定一个类以及他的成员的可见性。
- 可以在类、方法和数据前使用public可见性修饰符,表示他们可以被任何其他的类访问。如果没有使用可见性修饰符,那么默认类、和数据域是可以被同一个包中的恩和一个类访问的。称作私有包或者包访问。
- 包可以组织类。为了完成这个目标,需要在程序中出现下面这行语句,作为程序第一条非注释和非空白行的语句:
package packageName;
- 如果定义类时没有声明包,就表示他放在默认包中。
- private修饰符限定方法和数据域只能在他自己的类中访问。
- 修饰符private只能应用在类的成员上。修饰符public可以应用在类或类的成员上。局部变量上使用修饰符public和private都会导致编译错误。
- 大多数情况下,构造方法应该是公共的,你是如果项防止用户创建类的实例,就该使用私有构造方法。
-
9.9 数据域封装
- 将数据域设为私有可以保护数据,并且使得类易于维护。
- 为了避免对数据域的直接修改,应该使用private修饰符将数据域声明为私有的,这成为数据域封装。、私有数据域不能被对象从定义该死有余的类外访问。为了访问私有数据域,可以提供一个获取方法返回数据域的值。为了更新数据域,可以提供一个设置方法给数据域设置新值。获取方法也称为访问器,设置方法也称为修改器。获取方法有如下签名:
public returnType getPropetyName()
- 设置方法有如下签名:
public void setPropetyName(dataType propetyValue)
package chapter09; public class CircleWithPrivateDataFields { private double radius = 1; private static int numberOfObjects = 0; public CircleWithPrivateDataFields(){ numberOfObjects++; } public CircleWithPrivateDataFields(double newRadius){ numberOfObjects++; radius = newRadius; } public double getRadius() { return radius; } public void setRadius(double newRadius){ radius = (newRadius >= 0) ? newRadius : 0; } public static int getNumberOfObjects(){ return numberOfObjects; } public double getArea(){ return radius * radius * Math.PI; } }
package chapter09; public class TestCircleWithPrivateDataFields { public static void main(String[] args) { CircleWithPrivateDataFields myCircle = new CircleWithPrivateDataFields(5.0); System.out.println("The area of the circle of radius " + myCircle.getRadius() + " is " + myCircle.getArea()); System.out.println("The number of objects created is " + CircleWithPrivateDataFields.getNumberOfObjects()); } }
-
9.10 向方法传递对象参数
- 给方法传递一个对象,是昂独享的引用传递给方法。
- Java只有一种参数传递方式:值传递。
package chapter09; public class TestPassObject { public static void main(String[] args) { Circle myCircle = new Circle(1); int n = 5; printArea(myCircle,n); } public static void printArea(Circle c,int times){ System.out.println("Radius \t\tArea"); while (times >= 1){ System.out.println(c.getRadius() + "\t\t" + c.getArea()); c.setRadius(c.getRadius() + 1); times--; } } }
-
9.11 对象数组
- 数组既可以存储基本数据类型值,也可以存储对象。
- 对象的数组实际上是引用变量的数组。
- 当使用new操作符创建数组后,这个数组中的每个元素都是默认值为null的引用变量。
package chapter09; public class TotalArea { public static void main(String[] args) { Circle[] circleArray; circleArray = createCirclrArray(); printCircleArray(circleArray); } public static Circle[] createCirclrArray(){ Circle[] circleArray = new Circle[5]; for (int i = 0;i <circleArray.length;i++) circleArray[i] = new Circle(Math.random() * 100); return circleArray; } public static void printCircleArray(Circle[] circleArray){ System.out.printf("%-30s%-15s\n","Radius","Area"); for (int i = 0;i < circleArray.length;i++){ System.out.printf("-30f%-15f\n",circleArray[i].getRadius(),circleArray[i].getArea()); } System.out.println("- - - - - - - - - - - - - - - -"); System.out.printf("%-30s%-15s","The total area of circlrs is",sum(circleArray)); } public static double sum(Circle[] ciecleArray){ double sum = 0; for (int i = 0;i < ciecleArray.length;i++) sum += ciecleArray[i].getArea(); return sum; } }
-
9.12 不可变对象和类
- 可以定义不可变类来产生不可变对象。可变对象的内容不能呗改变。
- 有时候需要创建一个一旦创建其内容就不能再改变的对象。我们称这种对象为不可变对象,而它的类就称为不可变类。如:String就是不可变的。
- 一个类的所有数据都是私有的且没有修改器并不意味着他一定是不可变类。
- 要使一个类成为不可变类,必须满足婴喜爱的要求:
- 所有数据域都是私有的
- 没有修改器方法
- 没有返回一个指向可变数据域的引用的访问器方法
-
9.13 变量的作用域
- 实例变量和静态变量的作用域是整个类,无论变量是在哪里声明的。
- 一个类中的实例变量和静态变量称为类变量或数据域。在方法内部定义的变量称为局部变量。
- 如果一个局部变量和一个类变量具有相同的名字,那么局部变量优先,而同名的类变量将被隐藏。
-
9.14 this引用
关键字this引用对象自身,他也可以在构造方法内部用于调用同一个类的其他构造方法。- 9.14.1 使用this引用数据域
- 使用数据域作为设置方法或者构造方法的参数是一个好方法,这样可以使得代码易于阅读,并且可以避免创建不必要的名字。在这种情形下,在设置方法中需要使用this关键字来引用数据域。
- 9.14.2 使用this调用构造方法
- 关键字this可以用于调用同一类的另一个构造方法。
public class CirclrAnother{ private double radius; public CirclrAnother(double radius){ this.radius = radius;//this关键字用于引用正在被构建的对象的数据域radius } public CirclrAnother(){ this(1.0);//this关键字用于调用另外一个构造方法 } ... }
- Java要求,在构造方法中语句this(arg-list)应在任何其他可执行语句之前出现。
- 如果一个类有多个构造方法,最好尽可能使用this(参数列表)实现他们。通常,无参数或参数少的构造方法可以用this(arg-list)调用参数较多的构造方法。
- 关键字this可以用于调用同一类的另一个构造方法。
- 9.14.1 使用this引用数据域