Java学习笔记(9)Objects and Classes

9.1面向对象编程的基本概念

1、面向对象编程(Object-oriented programming, OOP) 是以对象为基本单位来搭建程序的编程方法。术语对象(object)指的是实体(entity),例如学生,桌子,圆,按钮。

2、对象具有唯一的标识,状态和行为。对象状态由一组数据域data fields(或属性properties)构成,对象的行为behavior则是由一组方法构成。

3、类创建对象,对象的数据域和方法由类定义


9.2类

1、Java用class来定义对象的概念,是同类型对象的总称。它本身不是实体,只是一个笼统的概念。例如学生这个概念,只有具体到某一个学生上,才能称为实体。

2、Java使用成员变量来定义对象的数据域,使用成员方法来定义对象的行为。

3、此外,Java的类还提供了一些特殊的方法,称为构造方法,用来创建具体的对象实体。


9.3圆对象的Java编程实现

 


9.4用Unified Modeling Language (UML)表示Circle类

UML是一种面向对象的建模语言,它运用统一的、标准化的标记和定义实现对软件系统进行面向对象的描述和建模。

 


9.5 Example: Defining Classes and Creating Objects

学习目标:如何创建一个对象,访问对象的数据,使用对象的方法。

LISTING 9.1 TestSimpleCircle.java

 public class TestSimpleCircle {
	 /** Main method */
	 public static void main(String[] args) {
		 // Create a circle with radius 1
		 SimpleCircle circle1 = new SimpleCircle();
		 System.out.println("The area of the circle of radius "
			 + circle1.radius + " is " + circle1.getArea());

		 // Create a circle with radius 25
		 SimpleCircle circle2 = new SimpleCircle(25);
		 System.out.println("The area of the circle of radius "
			 + circle2.radius + " is " + circle2.getArea());

		 // Create a circle with radius 125
		 SimpleCircle circle3 = new SimpleCircle(125);
		 System.out.println("The area of the circle of radius "
			 + circle3.radius + " is " + circle3.getArea());

		 // Modify circle radius
		 circle2.radius = 100; // or circle2.setRadius(100)
		 System.out.println("The area of the circle of radius "
			 + circle2.radius + " is " + circle2.getArea());
	 }
}

 // Define the circle class with two constructors
 class SimpleCircle {
	 double radius;

	 /** Construct a circle with radius 1 */
	 SimpleCircle() {
		 radius = 1;
	 }

	 /** Construct a circle with a specified radius */
	 SimpleCircle(double newRadius) {
		 radius = newRadius;
	 }

	 /** Return the area of this circle */
	 double getArea() {
		 return radius * radius * Math.PI;
	 }

	 /** Return the perimeter of this circle */
	 double getPerimeter() {
		 return 2 * radius * Math.PI;
	 }

	 /** Set a new radius for this circle */
	 void setRadius(double newRadius) {
		 radius = newRadius;
	 }
}

程序编译后的结果是:

 

程序的执行结果是:

 

几点说明:

1、Java可以在一个源文件中放置多个类,例如上述例子就有两个类。但是只能有一个类是public修饰的,这个类叫主类,main函数总是被放在这个类中。

2、TestSimpleCircle类是一个测试类,它的的功能是用于测试SimpleCircle类,所以它本身除了main函数之外,没有再定义自己的成员变量和方法。

3、其实一个类也可以自己带一个main函数来测试自己,例如改写上一个例子为:


LISTING 9.2 SimpleCircle.java

 public class SimpleCircle {
	 /** Main method */
	 public static void main(String[] args) {
		 // Create a circle with radius 1
		 SimpleCircle circle1 = new SimpleCircle();
		 System.out.println("The area of the circle of radius "
			 + circle1.radius + " is " + circle1.getArea());

		 // Create a circle with radius 25
		 SimpleCircle circle2 = new SimpleCircle(25);
		 System.out.println("The area of the circle of radius "
			 + circle2.radius + " is " + circle2.getArea());

		 // Create a circle with radius 125
		 SimpleCircle circle3 = new SimpleCircle(125);
		 System.out.println("The area of the circle of radius "
			 + circle3.radius + " is " + circle3.getArea());

		 // Modify circle radius
		 circle2.radius = 100;
		 System.out.println("The area of the circle of radius "
			 + circle2.radius + " is " + circle2.getArea());
	 }

	 double radius;

	 /** Construct a circle with radius 1 */
	 SimpleCircle() {
		 radius = 1;
	 }

	 /** Construct a circle with a specified radius */
	 SimpleCircle(double newRadius) {
		 radius = newRadius;
	 }

	 /** Return the area of this circle */
	 double getArea() {
		 return radius * radius * Math.PI;
	 }

	 /** Return the perimeter of this circle */
	 double getPerimeter() {
		 return 2 * radius * Math.PI;
	 }

	 /** Set a new radius for this circle */
	 void setRadius(double newRadius) {
		 radius = newRadius;
	 }
}


9.6 几个要点复习

1创建一个对象,用new关键字,例如:

SimpleCircle circle1 = new SimpleCircle();

SimpleCircle circle2 = new SimpleCircle(25);

2创建对象之后,可以使用.访问对象成员和对象方法,例如:

circle1.radius

circle2.getArea()

3对象必须先创建后使用,没有经过new的对象是空对象null,无法使用。


9.7 再来个例子巩固一下

下面是一个电视类的UML图: +表示为public型

 


LISTING 9.3 TV.java

 public class TV {
	 int channel = 1; // Default channel is 1
	 int volumeLevel = 1; // Default volume level is 1
	 boolean on = false; // TV is off

	 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 setVolume(int newVolumeLevel) {
		 if (on && newVolumeLevel >= 1 && newVolumeLevel <= 7)
			 volumeLevel = newVolumeLevel;
	 }

	 public void channelUp() {
		 if (on && channel < 120)
			 channel++;
	 }

	 public void channelDown() {
		 if (on && channel > 1)
			 channel—–;
	 }

	 public void volumeUp() {
		 if (on && volumeLevel < 7)
			 volumeLevel++;
	 }

	 public void volumeDown() {
		 if (on && volumeLevel > 1)
			 volumeLevel—–;
	 }
}

 public class TestTV {
	 public static void main(String[] args) {
		 TV tv1 = new TV();
		 tv1.turnOn();
		 tv1.setChannel(30);
		 tv1.setVolume(3);

		 TV tv2 = new TV();
		 tv2.turnOn();
		 tv2.channelUp();
		 tv2.channelUp();
		 tv2.volumeUp();

		 System.out.println("tv1's channel is " + tv1.channel + " and volume level is " + tv1.volumeLevel);
		 System.out.println("tv2's channel is " + tv2.channel + " and volume level is " + tv2.volumeLevel);
	 }
}


9.8 构造方法

构造方法是一种特殊的方法,当对象被创建的时候,构造方法会被自动调用。

Circle() {

}

Circle(double newRadius) {  

  radius = newRadius;

}


9.8.1 构造方法的写法

1、构造方法的名字必须和类的名字完全相同,包括大小写。

2、构造方法不能有返回类型,甚至连void都不能写,所以不可能出现return语句。

3、构造方法会在new的时候被自动调用,所以它特别适合用来做对象初始化之类的工作。

4、构造方法经常被重载,所以new的时候可以利用传入的参数,调用不同的构造方法。

5、利用构造方法创建对象 new ClassName();

例如:

new Circle();

new Circle(5.0);


9.8.2 默认构造方法

如果一个类没有显式定义任何构造方法,Java会隐含定义一个没有形参,方法体为空的构造方法。这个方法称为默认构造方法。例如下面这个类,框出来的部分,如果你不写,Java也会默认偷偷补上(当然不会改动到源代码):

class Circle {

  Circle() {

  }

}


9.9 对象引用的变量声明

为了引用一个对象,必须将对象赋值给一个引用变量。声明一个对象的引用很简单,语法如下:

ClassName objectRefVar;

例如:Circle myCircle;

这个用法其实和以前的变量声明没有区别,例如以前是这样声明一个整型变量:int i;

只不过当变量类型变为对象之后,我们把这样的变量叫做引用变量,以和普通变量相区别。


9.10 声明并创建对象

ClassName objectRefVar = new ClassName();

 


9.10.1 访问对象

访问对象的数据:

        objectRefVar.data  例如:myCircle.radius

调用对象的方法:

       objectRefVar.methodName(arguments)  例如:myCircle.getArea()


9.10.2 注意

我们一直都是这样调用数学库的方法:

Math.methodName(arguments) (, Math.pow(3, 2.5))

那么,能否直接使用Circle1.getArea() 来调用 getArea() 方法?

答案是:不行。原因在于,在本章之前,我们定义的所有方法,前面都有一个修饰词static,这就是所谓的静态方法。显然这里的 getArea() 是一个非静态的方法,所以只能通过对象的引用来调用:
          objectRefVar.methodName(arguments) (, myCircle.getArea())


9.11 数据域

1、数据域也可以是引用类型,例如,下面这个Student 类就包含一个name 的成员,它是String 类型。

2、String其实是一个类,所以name在这里的确切表述,是一个引用变量,当然你简单把它看成变量也行。

3、所以,可以看出类的成员是没什么限制的,可以是普通变量,也可以是某个类的引用,甚至是它自己的引用。

 


9.11.1 数据域的默认值

数据域都有默认值,引用类型的默认值是null,数值类型的默认值为0,布尔类型默认值为false,字符类型默认值为'\u0000'。

作为对比,Java对于局部变量或者临时变量都没有默认值,所以这些变量在被赋值之前都是随机值,关键是不能用。


9.12.1 基本数据类型的变量和引用类型的变量


9.12.2 基本数据类型的变量和引用类型的变量,执行=时的行为不同




9.13 垃圾回收

执行c1 = c2 之后, c1 和c2指向(或者说引用)了同一个对象。这个操作导致原先c1指向的那个对象没有被引用(即,再没有变量指向它),这时候这个对象已经无法使用,成为垃圾( garbage )。这些垃圾会占用掉一定的内存,不过它们会被JVM自动回收,所以不需要你做额外的处理。

小提示:如果你确定不再需要一个对象,并且想要清掉这个对象所占用的内存空间,你可以简单的用null给引用这个对象的变量赋值,这样JVM就知道这个对象需要被回收。



9.14 日期类Date

Java提供了一个处理日期和时间的日期类java.util.Date 。下面是这个类的UML图。


一个例子:

要打印当前时间,可以用下面的代码:

java.util.Date date = new java.util.Date();

System.out.println(date.toString());

输出的格式类似这样:
Mon May 27 17:55:46 CST 2016


9.15 随机数类Random

除了Math.random(),Java还提供了一个随机数生成类java.util.Random 。下面是这个类的UML图。

 


9.15.1 相同的种子点,将会生成相同的随机序列

Random random1 = new Random(3);

System.out.print("From random1: ");

for (int i = 0; i < 10; i++)

  System.out.print(random1.nextInt(1000) + " ");

Random random2 = new Random(3);

System.out.print("\nFrom random2: ");

for (int i = 0; i < 10; i++)

  System.out.print(random2.nextInt(1000) + " ");

 


9.16 2D坐标类Point2D

javafx.geometry.Point2D类,用来表示平面上的一个点(x, y) 。下面是这个类的UML图。

 


LISTING 9.5 TestPoint2D.java

 import java.util.Scanner;
 import javafx.geometry.Point2D;

 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("p1 is " + p1.toString());
		 System.out.println("p2 is " + p2.toString());
		 System.out.println("The distance between p1 and p2 is " +
		 p1.distance(p2));
	 }
}

9.17 实例变量和方法

1、实例变量属于某个特定的实例。不同实例的实例变量毫无关联。例如:

Circle c1 = new Circle(); Circle c2 = new Circle(5);

2、这两个圆的radius是实例变量,因此各自的半径是独立的。c1和c2的半径毫无关联。
3、实例方法需要借助某个类的实例才能被调用。

4、什么是实例变量和实例方法?答案很简单,类的成员中,凡是没有用static修饰的都算。


9.17.1 静态变量,常量和方法

1、静态变量和静态常量,是所有同一个类的实例共享的。
2、静态方法没有绑定在某个特定对象上。也就是说,它也是所有实例共享的。

3、如何区分成员是否是静态的?答案是,用static修饰的就是。

静态变量,常量和方法图示

UML图例说明:有下划线的成员表示静态成员。可以看出, numberOfObjects是多个实例共享的,radius是独立的。

 

9.17.2 实例变量的例子

public class CircleWithStaticMembers {
  double radius;
  static int numberOfObjects = 0;

  CircleWithStaticMembers() {
    radius = 1;
    numberOfObjects++;
  }
  CircleWithStaticMembers(double newRadius) {
    radius = newRadius;
    numberOfObjects++;
  }

  static int getNumberOfObjects() {
    return numberOfObjects;
  }
  double getArea() {
    return radius * radius * Math.PI;
  }
}
public class TestCircleWithStaticMembers {
	public static void main(String[] args) {
		System.out.println("Before creating objects");
		System.out.println("The number of Circle objects is " +CircleWithStaticMembers.numberOfObjects);
		ircleWithStaticMembers c1 = new CircleWithStaticMembers();
		System.out.println("\nAfter creating c1");
		System.out.println("c1: radius (" + c1.radius +") and number of Circle objects (" +c1.numberOfObjects+ ")");
		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.numberOfObjects + ")");
		System.out.println("c2: radius (" + c2.radius +") and number of Circle objects (" +c2.numberOfObjects+ ")");
	}
}

运行结果:

 

上述程序的结果说明,静态成员是共享的,其余成员则是独立的。


9.18 包(package)

1、包的作用类似于C的函数库,但是C的函数库很容易出现重名的问题,包在一定程度上解决了这个问题。

2、一个包通常包含很多个功能相近的类。


9.18.1创建自己的包

package 包名;//此句必须是源文件的第一条语句

例如:

package com.example.graphics;

package oop;

一个Java源文件必定属于某个包。如果你没有使用package语句,这个源文件的所有类,会被自动归入一个匿名包。


9.18.2包的目录映射

1、包名和目录名有一个映射规则,所以源代码必须存放在指定位置才行。假设Rectangle.java代码第一行是:

package com.example.graphics;

2、那么Rectangle.java的存放目录必须是:

....\com\example\graphics\Rectangle.java

3、前面….可以是任意目录,因为package只规定了相对目录,因此只要保持\com\example\graphics\结构就行。


9.18.3包的编译、运行

1、假设源代码Test.java的第一行是:package oop; 此时,Test.java必须存放在oop目录下。

2、编译这个文件,可以在oop的同一级目录运行:

javac oop/Test.java

3、顺利的话,oop目录下会有Test.class生成。当然,也可以在oop目录下运行javac Test.java

运行这个程序,一定要在oop的同一级目录运行(否则会出现错误: 找不到或无法加载主类):

java oop.Test

4、友情提醒:使用IDE开发,不需要考虑上述问题


9.18.4包的命名

1、Java建议包的名字取成域名的逆序,例如cn.edu.xmu,这是为了避免命名重复。

2、包之间没有嵌套关系,例如java.awt和java.awt.geom是两个完全独立的包。

3、其实上面已经提到,包名中的.最后会被映射成文件目录。因此,包java.awt是由java/awt下的java文件编译而来,包java.awt.geom则是由java/awt/geom下的文件编译而来,这是两个不同的文件夹,所以源文件之间不存在包含关系。


9.18.5包的使用

1、导入包有两种格式

import 包名.类名; //导入这个包的特定类

import 包名.*;       //导入这个包的所有类

2、假设你想使用java.awt.event包中的ActionEvent类,可以选择以下两种方式导入:

import java.awt.event.ActionEvent;

import java.awt.event.*;

3、导入后,可以这样使用ActionEvent类:

ActionEvent myEvent = new ActionEvent();

4、其实不导入一个类也是可以用的,就是写起来麻烦:

     java.awt.event.ActionEvent myEvent = new java.awt.event.ActionEvent();


9.19可见性修饰符

默认情况下,类的变量和方法,可以被在同一个包(package)中的任意类访问。

public

类的成员能被任意包的任意类访问。

private 

类的成员仅能被类自身内部的方法所访问。如果一个属性被private修饰,外部的类想要访问这个属性,就只能通过get和set方法(如果这个类有提供的话)


private修饰符将访问范围限制到类自身内部;默认的(没有加任何修饰符的)情况下,访问范围是同一个包;如果是public,则访问范围不再局限于包(相当于没有任何限制)。

为什么需要将数据域设置为 private?

1、可以保护数据。

2、可以让类容易维护。

这就是面向对象的重要特性:封装性


9.20传递对象到方法中

1、对基本数据类型而言,传递的是值(执行形参=实参的操作)

2、对引用类型而言,传递的是对象的引用 (执行形参=实参的操作)

也就是说,其实Java参数传递只有一种,就是值传递(形参=实参)。只不过在上面两种情况下,对=的处理有所不同。

LISTING 9.10 TestPassObject.java

 public class TestPassObject {
	 /** Main method */
	 public static void main(String[] args) {
		 // Create a Circle object with radius 1
		 CircleWithPrivateDataFields myCircle =
		 new CircleWithPrivateDataFields(1);

		 // Print areas for radius 1, 2, 3, 4, and 5.
		 int n = 5;
		 printAreas(myCircle, n);

		 // See myCircle.radius and times
		 System.out.println("\n" + "Radius is " + myCircle.getRadius());
		 System.out.println("n is " + n);
	 }

	 /** Print a table of areas for radius */
	 public static void printAreas(
		 CircleWithPrivateDataFields 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.21对象数组

 Circle[] circleArray = new Circle[10];

 对象数组的实质,是一堆引用变量构成的。例如,调用circleArray[1].getArea(),实际上通过引用操作了两次对象, 一次是下标,表示取到数组对象的第二个元素,该元素是个Circle的引用,然后通过这个引用,再调用getArea方法。


9.21.1对象数组图示

   Circle[] circleArray = new Circle[10];

  注意这行代码执行后, circleArray[0]… circleArray[9]这10个元素都是null,因为它们还没有指向某个对象。


LISTING 9.11 TotalArea.java

 public class TotalArea {
	 /** Main method */
	 public static void main(String[] args) {
		 // Declare circleArray
		 CircleWithPrivateDataFields[] circleArray;

		 // Create circleArray
		 circleArray = createCircleArray();

		 // Print circleArray and total areas of the circles
		 printCircleArray(circleArray);
	 }

	 /** Create an array of Circle objects */
	 public static CircleWithPrivateDataFields[] createCircleArray() {
		 CircleWithPrivateDataFields[] circleArray =
			 new CircleWithPrivateDataFields[5];

	 for (int i = 0; i < circleArray.length; i++) {
		 circleArray[i] =
		 new CircleWithPrivateDataFields(Math.random() * 100);
	 }

	 // Return Circle array
	 return circleArray;
	}

	 /** Print an array of circles and their total area */
	 public static void printCircleArray(
		 CircleWithPrivateDataFields[] 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("—————————————————————————————————————————-");

		 // Compute and display the result
		 System.out.printf("%-30s%-15f\n", "The total area of circles is",
			 sum(circleArray) );
	 }

	 /** Add circle areas */
	 public static double sum(CircleWithPrivateDataFields[] circleArray) {
		 // Initialize sum
		 double sum = 0;

		 // Add areas to sum
	 for (int i = 0; i < circleArray.length; i++)
 			sum += circleArray[i].getArea();

		 return sum;
	 }
}



9.22不可修改的对象和类

如果希望对象的所有成员都是不可修改的,例如:

 

然而按照这个写法,dateCreated有可能被外部类黑掉。

例如:下面这段代码可以把dateCreated成员黑掉。

 

所以,对需要保护的成员,忠告是:

1、所有成员都需要设置为private;

2、不对外提供能够修改成员的方法;

3、所有对外的方法,都不能返回成员的引用。


9.23变量作用域

实例变量和静态变量的作用范围都是整个类,无论它们在类内部的何处声明。当然为了可读性,一般统一放在类的最前或者最后;

局部变量的作用范围,是从声明的地方开始,到包含它的最近的右括号}为止。局部变量在使用前,必须显式初始化,否则会导致编译错误。


9.24 this关键字

1、this关键字表示一个引用,这个引用指向任何对象自己。this最常用的地方在于通过它访问一个类的隐藏数据域(hidden data fields)。

2、this的另外一个常用之处,是在一个类的某个构造方法中,通过它来调用同一个类的另外一个构造方法。


9.24.1访问隐藏数据域


9.24.2调用重载的另一个构造方法


CHAPTER 9 SUMMARY

1. Aclass is a template forobjects. It defines theproperties of objects and provides

constructors for creating objects and methods for manipulating them.

2. A class is also a data type. You can use it to declare objectreference variables. An object reference variable that appears to hold an object actually contains a reference to that object. Strictly speaking, an object reference variable and an object are different, but most of the time the distinction can be ignored.

3. An object is aninstance of a class. You use thenew operator to create an object, and the dot operator(.) to access members of that object through its reference variable.

4. Aninstance variable ormethod belongs to an instance of a class. Its use is associated with individual instances. Astatic variable is a variable shared by all instances of the same class. Astatic method is a method that can be invoked without using instances.

5. Every instance of a class can access the class’s static variables and methods. For clarity,

however, it is better to invoke static variables and methods usingClassName.variable and ClassName.method.

6. Visibility modifiers specify how the class, method, and data are accessed. Apublic class, method, or data is accessible to all clients. Aprivate method or data is accessible only inside the class.

7. You can provide a getter (accessor) method or a setter (mutator) method to enable clients to see or modify the data.

8. A getter method has the signaturepublic returnType getPropertyName(). If thereturnType isboolean, theget method should be defined aspublic boolean isPropertyName(). A setter method has the signaturepublic void setPropertyName(dataType propertyValue).

9. All parameters are passed to methods using pass-by-value. For a parameter of a primitive

type, the actual value is passed; for a parameter of areference type, the reference for the object is passed.

10. A Java array is an object that can contain primitive type values or object type values. When an array of objects is created, its elements are assigned the default value of null.

11. Once it is created, animmutable object cannot be modified. To prevent users from modifying an object, you can defineimmutable classes.

12. The scope of instance and static variables is the entire class, regardless of where the variables are declared. Instance and static variables can be declared anywhere in the class. For consistency, they are declared at the beginning of the class in this book.

13. The keywordthis can be used to refer to the calling object. It can also be used inside a constructor to invoke another constructor of the same class.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值