Transcript
Page 1: Java 程序设计系列讲座- 2 面向对象程序设计

Java 程序设计系列讲座- 2面向对象程序设计

黄绍辉厦门大学计算机科学系

E-mail: [email protected]

Page 2: Java 程序设计系列讲座- 2 面向对象程序设计

面向对象程序设计( OOP )基本思想

• 对象的基本概念– 对象是系统中用来描述客观事物的一个实体,它是构

成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。

– 对象是问题域或实现域中某些事物的一个抽象,它反映该事物在系统中需要保存的信息和发挥的作用;它是一组属性和有权对这些属性进行操作的一组服务的封装体。

– 客观世界是由对象和对象之间的联系组成。

Page 3: Java 程序设计系列讲座- 2 面向对象程序设计

类的基本概念

• 类是具有相同属性和服务的一组对象的集合。分类的原则是抽象。

• 在面向对象的编程语言中,类是一个独立的程序单位,它应该有一个类名并包括属性说明和服务说明两个主要部分。

• 例如图上的三种车,可以做成一个类。具体到某一辆车子,就是一个具体对象。

Page 4: Java 程序设计系列讲座- 2 面向对象程序设计

面向对象的基本特征

• 封装性– 把对象的属性和服务结合成一个独立的相同单位,

并尽可能隐蔽对象的内部细节。• 继承性

– 从一个已有的类来构造新类,除了保留原有的类的特性,还增加了一些新特性。已有的类称为父类superclass ,新类称为子类 subclass 。

• 多态性– 在父类中定义的属性或服务被特殊类继承之后,可

以具有不同的数据类型或表现出不同的行为。

Page 5: Java 程序设计系列讲座- 2 面向对象程序设计

面向对象程序设计的框架

1. 用对象的思想来组织程序模块。每一个程序至少有一个对象 (class) ;

2. 对象与对象之间一般是并列关系,也可以有包含关系;

3. 对象可以拥有两种东西:属性和服务。属性是一堆的变量,服务是一堆的函数。它们都是对象的成员,所以又叫成员变量、成员函数;

4. 面向对象的程序设计分两步:一是先独立创建各种对象,好好设计每个对象的属性和服务;二是在 main 函数中使用各个对象,让对象按照设计的流程发挥各自的作用。

Page 6: Java 程序设计系列讲座- 2 面向对象程序设计

先来学习怎么使用类

数组专题

Page 7: Java 程序设计系列讲座- 2 面向对象程序设计

数组

• Java 的数组是用纯正 OOP 的方法设计的,所有专门有一个类,叫 Array ,用来实现数组的功能。

• 尽管我们刚刚知道,类和对象还是不同的,但是今后我们不太区分这两个词汇;如果要细究,可以自己结合上下文理解。

• 下面我们来学习如果使用人家做好的类,等有感觉了再来做自己的类。

Page 8: Java 程序设计系列讲座- 2 面向对象程序设计

数组入门- 1

• 1 声明一个数组– 数据类型 [] 数组名 ;– or– 数据类型 数组名 []; – 例如: double[] myList;

int a[];• 2 创建一个数组

– 数组名 = new 数据类型 [ 数组长度 ];– 如: myList = new double[10];

Page 9: Java 程序设计系列讲座- 2 面向对象程序设计

数组入门- 2

• 合并前两步,声明并创建一个数组– 数据类型 [] 数组名 = new 数据类型 [ 数组长度 ];– or– 数据类型 数组名 [] = new 数据类型 [ 数组长度 ];– 例如: double[] myList = new double[10];

int a[] = new int[10];

• 友情提醒:要使用一个对象,都需要先声明,再创建,然后才能使用,两步缺一不可;当然这两步可以合并,例如上面的例子。

• 声明的作用仅仅是弄出一个空对象( null)。

Page 10: Java 程序设计系列讲座- 2 面向对象程序设计

数组入门- 3

• 数组在创建( new)之后, Java 才会为这个数组自动分配空间。(注意声明数组的时候并没有分配数组元素的空间,因为那时候不知道数组多大,所以只是一个空数组,无法容纳任何元素)

Page 11: Java 程序设计系列讲座- 2 面向对象程序设计

数组入门- 4

•任何一个数组,都可以通过属性 length得到数组的长度。如: myList.length

• 数组创建的时候会自动赋零值,对于数值型的,就是 0 或者 0.0 或者 '\0' ,对于布尔型的,就是 false

•任何一个数组,其元素的最小下标是 0 ,最大下标是 length-1

• 数组下标越界会引发异常

Page 12: Java 程序设计系列讲座- 2 面向对象程序设计

数组入门- 5

• 数组元素的引用使用 [ ] ,以下两个方法等价,都可以打印出数组 myList 的每一个元素值:for (int i = 0; i < myList.length; i++) { System.out.print(" "+myList[i]);}或者( JDK1.5及以上版本才支持的)for (int val : myList) { System.out.print(" "+val);}

Page 13: Java 程序设计系列讲座- 2 面向对象程序设计

数组入门- 6

• 数组在创建的时候可以顺便初始化,如:– double[] myList = {1.9, 2.9, 3.4, 3.5};

• 它相当于:– double[] myList = new double[4];– myList[0] = 1.9;– myList[1] = 2.9;– myList[2] = 3.4;– myList[3] = 3.5;

Page 14: Java 程序设计系列讲座- 2 面向对象程序设计

数组例程- 1/3

• 用 100 以内随机数初始化数组for (int i = 0; i < myList.length; i++) { myList[i] = Math.random() * 100;}

Page 15: Java 程序设计系列讲座- 2 面向对象程序设计

数组例程- 2/3

• 数组求和double total = 0;for (int i = 0; i < myList.length; i++) { total += myList[i];}

Page 16: Java 程序设计系列讲座- 2 面向对象程序设计

数组例程- 3/3

•求数组中的最大值double max = myList[0];for (int i = 1; i < myList.length; i++) { if (myList[i] > max) max = myList[i];}

Page 17: Java 程序设计系列讲座- 2 面向对象程序设计

复制数组- 1

• 假设 list1 和 list2 都是数组,执行语句: list2 = list1

• 上述语句并不能将 list1全部复制给 list2 ,而是仅仅让 list2 变成 list1 的引用。换言之,此时list2 和 list1 表示同一个数组,原先 list2 数组的元素就全被抛弃在内存的某一处,找不回来了!

Page 18: Java 程序设计系列讲座- 2 面向对象程序设计

复制数组- 2

• 为了复制一个数组到另一个数组,可以自己写一个循环,一个一个复制。例如,有两个数组:

int[ ] sourceArray = {2, 3, 1, 5, 10};int[ ] targetArray = new int[sourceArray.lengt

h];• 一个一个搬的例子for (int i = 0; i < sourceArray.length; i++) { targetArray[i] = sourceArray[i];}

Page 19: Java 程序设计系列讲座- 2 面向对象程序设计

总结一下

• 假设 Student 是一个类,要使用这个类,必须这样:

1. Student a, b; // 先声明2. a = new Student(); b = new Student(); // 再

创建3. // 下面可以随意折腾a和 b了,它们是两个对象

• 对象之间不能通过 = 完成复制,例如在上面代码的基础上,使用 a=b ,不是把 b的内容给a,而是让 a和 b共享b的对象;原先 a的内容就被 a 抛弃了。

Page 20: Java 程序设计系列讲座- 2 面向对象程序设计

数组作为函数参数- 1

• 对象(包括数组)作为参数, Java采用的是引用传递方式,基本数据类型( int , char 等)作为参数,采用值传递方式。

• 简单一点说,引用传递方式,实际参数和形式参数,操作的其实是同一个对象;值传递方式,实际参数和形式参数,仅仅是值相等而已。

• Java 中,所有被用作参数的对象,都采用引用方式传递以提高效率(类似于C中的地址传递,因为 Java 的对象实在太大只了,所有成员都复制一遍不划算,复制对象起始地址就好)

Page 21: Java 程序设计系列讲座- 2 面向对象程序设计

数组作为函数参数- 2

public class Test { public static void main(String[] args) { int x = 1; // x represents an int value int[] y = new int[10]; // y represents an array of int values

m(x, y); // Invoke m with arguments x and y System.out.println("x is " + x); // x 值不变,还是 1 System.out.println("y[0] is " + y[0]); // y[0] 值已改变,本来是 0

的 }

public static void m(int number, int[] numbers) { number = 1001; // Assign a new value to number numbers[0] = 5555; // Assign a new value to numbers[0] }}

Page 22: Java 程序设计系列讲座- 2 面向对象程序设计

数组作为函数参数- 3 1 public class TestPassArray { 2 /** Main method */ 3 public static void main(String[] args) { 4 int[] a = {1, 2}; 5 6 // Swap elements using the swap method 7 System.out.println("Before invoking swap"); 8 System.out.println("array is {" + a[0] + ", " + a[1] + "}"); 9 swap(a[0], a[1]); // 值传递,返回后不改变原值10 System.out.println("After invoking swap");11 System.out.println("array is {" + a[0] + ", " + a[1] + "}");1213 // Swap elements using the swapFirstTwoInArray method14 System.out.println("Before invoking swapFirstTwoInArra

y");15 System.out.println("array is {" + a[0] + ", " + a[1] + "}");16 swapFirstTwoInArray(a); // 引用传递,返回后原值可能改变17 System.out.println("After invoking swapFirstTwoInArray");18 System.out.println("array is {" + a[0] + ", " + a[1] + "}");19 }

Page 23: Java 程序设计系列讲座- 2 面向对象程序设计

数组作为函数参数- 420 // 值传递,返回后不改变原值21 /** Swap two variables */22 public static void swap(int n1, int n2) { 23 int temp = n1;24 n1 = n2;25 n2 = temp;26 }27 // 引用传递,返回后原值可能改变28 /** Swap the first two elements in the array */29 public static void swapFirstTwoInArray(int[] array)

{ 30 int temp = array[0];31 array[0] = array[1];32 array[1] = temp;33 }34 }

Page 24: Java 程序设计系列讲座- 2 面向对象程序设计

再来一个例子class ClassA { int a = 1; }public class Main { static void f1(int a) { a++; } static void f2(ClassA c) { c.a++; } public static void main(String[] args) { ClassA c = new ClassA(); System.out.println(“a=”+c.a); //输出 a=1 f1(c.a); // 基本数据类型做参数, c.a 不变 System.out.println("a="+c.a); //输出 a=1 f2(c); //引用数据类型做参数, c 改变 System.out.println("a="+c.a); //输出 a=2 }}

Page 25: Java 程序设计系列讲座- 2 面向对象程序设计

数组作为函数返回值

• 函数的返回值是通过 return带回来的,每次return 可以带回一个基本类型的值( char 、 int等),也可以带回一个对象(例如数组)

Page 26: Java 程序设计系列讲座- 2 面向对象程序设计

一个例子:计算字符出现的次数

• 解题思路:1. 随机生成 100 个小写字母,放在数组 chars

中;显然,这个数组的大小应该是 100 ;2. 利用 counts 数组存储字符出现的次数,下标

0,1,2,... 分别存储 'a','b','c',... 的次数;显然,这个数组的大小应该是 26 。

3. 生成随机小写字母可以借助于Math.random()

Page 27: Java 程序设计系列讲座- 2 面向对象程序设计

关键代码- 1/5

20 /** Create an array of characters */21 public static char[] createArray() {22 // Declare an array of characters and create it23 char[] chars = new char[100];2425 // Create lowercase letters randomly and assign26 // them to the array27 for (int i = 0; i < chars.length; i++) 28 chars[i] =

RandomCharacter.getRandomLowerCaseLetter();2930 // Return the array31 return chars;32 }

Page 28: Java 程序设计系列讲座- 2 面向对象程序设计

关键代码- 2/5

34 /** Display the array of characters */35 public static void displayArray(char[]

chars) {3637 for (int i = 0; i < chars.length; i++) {38 if ((i + 1) % 20 == 0)39 System.out.println(chars[i] + " ");40 else41 System.out.print(chars[i] + " ");42 }43 }

Page 29: Java 程序设计系列讲座- 2 面向对象程序设计

关键代码- 3/5

45 /** Count the occurrences of each letter */46 public static int[] countLetters(char[] chars)

{47 // Declare and create an array of 26 int48 int[] counts = new int[26];4950 // For each lowercase letter in the array,

count it51 for (int i = 0; i < chars.length; i++) 52 counts[chars[i] - 'a']++;5354 return counts;55 }

Page 30: Java 程序设计系列讲座- 2 面向对象程序设计

关键代码- 4/5

57 /** Display counts */58 public static void displayCounts(int[] counts) {59 for (int i = 0; i < counts.length; i++) {60 if ((i + 1) % 10 == 0)61 System.out.println(counts[i] + " " + (char)(i +

'a'));62 else63 System.out.print(counts[i] + " " + (char)(i + 'a') +

" ");64 }65 }

Page 31: Java 程序设计系列讲座- 2 面向对象程序设计

关键代码- 5/5

3 public static void main(String args[]) { 4 // Declare and create an array 5 char[] chars = createArray(); 6 7 // Display the array 8 System.out.println("The lowercase letters are:"); 9 displayArray(chars);1011 // Count the occurrences of each letter12 int[] counts = countLetters(chars);1314 // Display counts15 System.out.println();16 System.out.println("The occurrences of each letter

are:");17 displayCounts(counts);18 }

Page 32: Java 程序设计系列讲座- 2 面向对象程序设计

多维数组- 1

• 二维数组的定义– 数据类型 [][] 数组名 ;– or– 数据类型 数组名 [][];

• 例如:– int[][] matrix;– 或 int matrix[][];

• 注意,这里仅仅说明 matrix 是一个二维数组,但是这个数组目前是 null ,所以还不能存储元素。

Page 33: Java 程序设计系列讲座- 2 面向对象程序设计

多维数组- 2

• 二维数组有行和列的概念,如:

Page 34: Java 程序设计系列讲座- 2 面向对象程序设计

多维数组- 3

• 二维数组可以在声明的时候赋初值,也可以在new 之后再一个一个慢慢赋值。

• 其实二维数组行和行之间是相互独立的,每一行都相当于一个一维数组。

Page 35: Java 程序设计系列讲座- 2 面向对象程序设计

• 由于二维数组的每一行是一个一维数组,所以这些一维数组的大小不必相等,如:

• 此时:triangleArray.length = 5, //因为数组有 5 行triangleArray[0].length = 5, triangleArray[1].length = 4, triangleArray[2].length = 3, triangleArray[3].length = 2, triangleArray[4].length = 1.

多维数组- 4

Page 36: Java 程序设计系列讲座- 2 面向对象程序设计

二维数组例程- 1

•随机数填充for (int row = 0; row < matrix.length; row++) { for (int column = 0; column < matrix[row].length; column++)

{ matrix[row][column] = (int)(Math.random() * 100); }}

• 这里matrix 是二维数组。过一遍二维数组必须用两层的循环,一般用 i 循环行, j 循环列;如果英文好一点,可以用 row循环行, column 或者 col循环列。

Page 37: Java 程序设计系列讲座- 2 面向对象程序设计

二维数组例程- 2

•打印数组元素for (int row = 0; row < matrix.length; row++) { for (int column = 0; column < matrix[row].length; column+

+) { System.out.print(matrix[row][column] + " "); }

System.out.println();}

Page 38: Java 程序设计系列讲座- 2 面向对象程序设计

二维数组例程- 3

• 数组求和int total = 0;for (int row = 0; row < matrix.length; row++) { for (int column = 0; column < matrix[row].length; column++)

{ total += matrix[row][column]; }}

Page 39: Java 程序设计系列讲座- 2 面向对象程序设计

一个例子:试卷评分

• 假设让计算机改卷, 8 张试卷和标准答案如下:

Page 40: Java 程序设计系列讲座- 2 面向对象程序设计

关键代码

• 学生的试卷存储在 answers 二维数组中,答案在keys 一维数组中,则评分的代码是:

18 // Grade all answers19 for (int i = 0; i < answers.length ; i++) {20 // Grade one student21 int correctCount = 0;22 for (int j = 0; j < answers[i].length ; j++) {23 if (answers[i][j] == keys[j])24 correctCount++;25 }2627 System.out.println("Student " + i + "'s correct count is " +28 correctCount);29 }

Page 41: Java 程序设计系列讲座- 2 面向对象程序设计

再来学习怎么使用类

字符串专题

Page 42: Java 程序设计系列讲座- 2 面向对象程序设计

字符串类- 1

•字符串是 Java另一个常用类。• 创建一个字符串(三种方法均可)

1. String message = new String("Welcome to Java");

2. String message = "Welcome to Java";3. char[] charArray = {'G', 'o', 'o', 'd'}; String message = new

String(charArray);

Page 43: Java 程序设计系列讲座- 2 面向对象程序设计

字符串类- 2

• 字符串的内容是常量,一旦创建就无法修改。1. String s = "Java";2. s = “HTML”; // 友情提醒:这句是对的。但是这不

是串复制,只是一个 s 抛弃了“ Java”字符串的故事。

• 以上第 2 行代码仅仅修改了 s的指向,但是没有改动到 "Java" 这个字符串本身,如图:

Page 44: Java 程序设计系列讲座- 2 面向对象程序设计

字符串类- 3

• 正宗的字符串内容判等函数,是 equals ,不是==(这个符号相当于比较对象地址是否相等)– String s1 = new String("Welcome to Java");– String s2 = "Welcome to Java";– System.out.println("s1 == s2 is " + (s1 == s2));– System.out.println("s1.equals(s2) is " + (s1.equals(s2)));

• 字符串内容比较函数,用 compareTo– s1.compareTo(s2),其返回值相当于 s1-s2 ,相等为

0

Page 45: Java 程序设计系列讲座- 2 面向对象程序设计

字符串类- 4• 字符串长度函数 length()

– String message = new String("Welcome to Java");

– int len = message.length();

• 注意:数组也有长度的概念,但是数组的长度被设计为数组的属性而不是函数。– int[] arr = new int[5];– int len = arr.length;

• 取字符串的单个字符 charAt()– message.charAt(index) 下标范围是

0..s.length()–1

Page 46: Java 程序设计系列讲座- 2 面向对象程序设计

字符串类- 5

• 字符串拼接 concat() 或者+– String s3 = s1.concat(s2);– 等价于: String s3 = s1 + s2;

• 截取子串 substring– String message = "Welcome to

Java".substring(0, 11) + "HTML"; // 相当于从 0开始,到 11 为止的子串(不包括 11)

– 如果没有提供 endIndex ,相当于截取到串尾

Page 47: Java 程序设计系列讲座- 2 面向对象程序设计

字符串类- 6

•字符串转换– "Welcome". toLowerCase() //返回welcome– "Welcome". toUpperCase() //返回WELCOME– " Welcome". trim() //返回Welcome– "Welcome". replace('e', 'A') //返回WAlcomA– "Welcome". replaceFirst("e", "A") //返回WAlcome

– "Welcome". replaceAll("e", "A") //返回WAlcomA

Page 48: Java 程序设计系列讲座- 2 面向对象程序设计

字符串类- 7

•查找指定的字符或者子串– indexOf(char ch) //查找指定字符,返回下标– lastIndexOf(char ch) // 同上,但是逆向查找– indexOf(int ch, int fromIndex)

从下标 fromIndex开始查找指定字符– lastIndexOf(int ch, int endIndex)

同上,但是从下标 endIndex开始逆向查找• 以上函数都有字符串版本,把参数换成

String 就可以了。如果查找失败,返回值都是 -1 。因为这是个无效的下标。

Page 49: Java 程序设计系列讲座- 2 面向对象程序设计

字符串类- 8

1."Welcome to Java".indexOf('W') returns 0.2."Welcome to Java".indexOf('o') returns 4.3."Welcome to Java".indexOf('o', 5) returns 9.4."Welcome to Java".indexOf("come") returns 3.5."Welcome to Java".indexOf("Java", 5) returns 11.6."Welcome to Java".indexOf("java", 5) returns -1.

7."Welcome to Java".lastIndexOf('W') returns 0.8."Welcome to Java".lastIndexOf('o') returns 9.9."Welcome to Java".lastIndexOf('o', 5) returns 4.10."Welcome to Java".lastIndexOf("come") returns

3.11."Welcome to Java".lastIndexOf("Java", 5) returns

-1.12."Welcome to Java".lastIndexOf("java", 5) returns

-1.

Page 50: Java 程序设计系列讲座- 2 面向对象程序设计

字符串类- 9

• 字符串分拆到字符数组 toCharArray – char[] chars = "Java".toCharArray();

• 用字符串的子串去填充数组 getChars– char[] dst = {'J', 'A', 'V', 'A', '1', '3', '0', '1'};– "CS3720".getChars(2, 6, dst, 4);– 头两个参数取子串,后面是填充目标数组和起始位置

• 字符数组拼成字符串 String 或 valueOf– String str = new String(new char[]{'J', 'a', 'v', 'a'});– String str = String.valueOf(new char[]{'J', 'a', 'v',

'a'});

Page 51: Java 程序设计系列讲座- 2 面向对象程序设计

字符串类- 10

• 基本类型 (char, double, long, int, float)转换成字符串 valueOf– double d = 5.44;– String s = String.valueOf(d); //s="5.44"– 其实也可以这样 String s=""+d; //s="5.44"

•字符串转成数值型(不是 String 的函数)– Double.parseDouble(str) //"5.44"=>5.44– Integer.parseInt(str) //"123"=>123

Page 52: Java 程序设计系列讲座- 2 面向对象程序设计

例题:检查一个字符串是不是回文

• 关键代码: public static boolean isPalindrome(String s) {

int low = 0;int high = s.length() - 1;

while (low < high) { if (s.charAt(low) != s.charAt(high)) return false; low++; high--; } return true; }

Page 53: Java 程序设计系列讲座- 2 面向对象程序设计

跑步进入 OOP

Page 54: Java 程序设计系列讲座- 2 面向对象程序设计

Part I 封装性

Page 55: Java 程序设计系列讲座- 2 面向对象程序设计

从简单对象入手

• 对象不是很深奥的东西,一个对象在 Java里面就是一个 class而已。例如随便写一个对象:

class Point { int x,y;}• 上面这个 Point 对象用来表示平面上的一个点,

它和 C语言的结构体非常类似;当然,用法显然是不一样了。

Page 56: Java 程序设计系列讲座- 2 面向对象程序设计

对象的用法

• 要使用一个已有的对象不是太难,但是你要做足以下的两件事情:1.使用对象的名字声明一个变量2.创建对象的一个实例

• 用上面的 Point 做例子,就是这样:1.使用对象的名字声明一个变量 Point p;2.创建对象的一个实例 p = new Point();3.接下来这个 p就拥有了 Point 的全部资源,例如它有

两个成员变量,叫 x,y ,而且还是整型的。

Page 57: Java 程序设计系列讲座- 2 面向对象程序设计

对象的用法

• 对象的声明和创建可以合成一步:– Point p = new Point();

•当然也可以隔了很多步:– Point p;– /* 中间有很多很多很多行,但是和 p 无关 */– p = new Point();

•只有一个准则:对象在创建之前是不能使用的,所以 new很重要。

Page 58: Java 程序设计系列讲座- 2 面向对象程序设计

对象与实例

• 前面说到,对象必须创建才能使用,那么创建是不是只有一种方法呢?

•默认的显然是只有一种,所以你只能这样创建 p: Point p = new Point();

• 这里引入几个术语:– Point 是类名,也是对象名,或者干脆叫对象– p 是对象的实例,也就是 Point 的实例

•你可以理解对象是一个图章,它每盖章一次生成一个 p, p就是一个实例。

Page 59: Java 程序设计系列讲座- 2 面向对象程序设计

我们来用一下自己的对象1. class Point {2. int x,y;3. }4. class Test {5. public static void main(String args[]) {6. Point a = new Point();7. a.x = 10; a.y = 10;8. Point b = new Point();9. b.x = 100; b.y = 100;10. System.out.print("a("+a.x+", "+a.y+"),

b("+b.x+","+b.y+")");11. }12. }

Page 60: Java 程序设计系列讲座- 2 面向对象程序设计

什么是构造函数

• 可不可以换一种方式创建 Point?当然可以,不过你要自己加构造函数,例如:

class Point { int x,y; Point(int xx, int yy) { x=xx;y=yy;}}• 现在你可以这样创建一个 p了:

– Point p = new Point(1,2);

• 不过你现在反而不能这样创建一个 p了:– Point p = new Point(); why?

Page 61: Java 程序设计系列讲座- 2 面向对象程序设计

关于构造函数 I

• 构造函数和类的名字完全一样,并且没有返回值,它的作用是提供类的创建方式,所以,根据类必须先创建再使用的原则,构造函数是每一个类一定要具备的东西。

• 或许你注意到第一个版本的 Point没有构造函数,是的,不过 Java 是如此聪明的一个语言,所以它会帮你补充一个不带参数,没有函数体的构造函数,例如 Point 类,它偷偷加的代码是: class Point { int x,y; Point() { } // 这一行尽管你没看见,但是它确实

存在}

Page 62: Java 程序设计系列讲座- 2 面向对象程序设计

关于构造函数 II

• Java 自动提供构造函数仅仅当你自己没有提供构造函数的时候才这么干,如果你提供了哪怕只有一个的构造函数,它决不自作多情帮你加一个。所以第二个版本的 Point最好改成下面:

class Point { int x,y; Point(){ } // 由于 Java罢工,这个函数只好自

己加 Point(int xx, int yy) { x=xx;y=yy;}}

Page 63: Java 程序设计系列讲座- 2 面向对象程序设计

关于成员变量-对内篇

• 每一个类都可以有自己的成员变量,例如 Point类的 x 和 y ;对于 Point 类的内部而言, x,y 相当于全局变量;如果脱开 Point 类,那么就要看x,y 是怎么声明的了。

class Point { Point(){ x=10; y=10; } Point(int xx, int yy) { x=xx;y=yy;} int x,y; }

x,y的作用范围从这里开始

x,y的作用范围到这里结束

Page 64: Java 程序设计系列讲座- 2 面向对象程序设计

关于成员变量-对外篇• 成员变量有四种修饰,它们决定了外部访问成员变

量的权限:– private , default, protected、 public

• 对于 private ,实际上是不允许外部访问的,所以如果你的 Point 这样定义:

class Point { private int x,y;}• 然后有 Point p = new Point();•那么这两个语句是错的: p.x=10;

p.y=10;

Page 65: Java 程序设计系列讲座- 2 面向对象程序设计

关于成员变量-对外篇

• 如果这个时候你想从外面修改 x,y 的值怎么办?答案是:没法改!!!除非 Point 类另外提供函数给你用,例如:

class Point { private int x,y; public int getX() { return x;} // 这就是传说中

的 getter

public void setX(int xx) { x=xx;} // 传说中的 setter

}• 上帝保佑,现在你终于可以从外面改 x 的值了:

– Point p = new Point();– p.setX(100); System.out.print(p.getX());

Page 66: Java 程序设计系列讲座- 2 面向对象程序设计

关于成员函数 I

• 成员函数的访问权限和成员变量完全一样,所以这里不重复了。

• 成员函数的调用,也要通过对象的实例才能够调用(当然静态函数除外,后面再讲),不会忘记什么叫对象的实例了吧? Oh, my God!

• 还是加一个函数试试,例如我们给 Point 加一个成员函数:public double test(double s) {

return x*y*s; }

Page 67: Java 程序设计系列讲座- 2 面向对象程序设计

关于成员函数 II

• 现在我们来调这个函数(这里是在外部调,如果在类的内部调,那就直接调用就好了)– Point p = new Point(3,4);– System.out.print(p.test(5.0)); //通过p调用 test 函

数• 还是不太放心,演示一下对象内部怎么调 test :class Point { private int x,y; public double f() { return test(5.0);} public double test(double s) { return x*y*s; }}

Page 68: Java 程序设计系列讲座- 2 面向对象程序设计

关于成员函数 III

• 总结一下,一个类的成员函数:– 对于类的内部而言,是直接抓过来随便爱怎么调用

都行(多大公无私啊),不需要借助任何东西;– 对于类的外部而言,如果是 private 的成员函数,那显然是不能用的;如果遇到 public 的,那运气还不错,可以通过创建一个类的实例(也就是 new 一下),来间接调用这个成员函数。

• 成员变量的结论与此类似。• 顺便说一下,在一个类的外部,每次 new 一个实例的

时候,相当于调用一次构造函数;至于调用的是哪一个构造函数,那要看你给的参数了。

Page 69: Java 程序设计系列讲座- 2 面向对象程序设计

关于静态方法和静态成员 I

•如果一个 class有一个 static的成员,那使用的时候要小心一点。给一个例子:

class A { public int x; public static int y; public static void f() { System.out.print(x+y); }

}•还记得怎么调用f ,怎么使用 x,y么?很简单:• A a = new A(); //看到这句不要晕掉,好多a...

• a.x = 10; a.y = 10; a.f(); //调用函数f ,不要参数

Page 70: Java 程序设计系列讲座- 2 面向对象程序设计

关于静态方法和静态成员 II• 下面来一段复杂的测试代码,测试A 这个类:1. class Test {2. public static void main(String args[]) {3. A a = new A();4. a.x = 10; a.y = 10;5. A b = new A();6. b.x = 100; b.y = 100;7.

System.out.print("a.x="+a.x+"a.y="+a.y+"b.x="+b.x+"b.y="+b.y);

8. }9. }• 运行上面程序,你会发现一个奇怪的现象:

– a.x 不等于 b.x ,可是 a.y居然等于 b.y• 前一半比较好理解,因为 a和 b本来就是不同的实例,虽然

它们是一个模子造出来的;可是,那y怎么回事?

Page 71: Java 程序设计系列讲座- 2 面向对象程序设计

关于静态方法和静态成员 III 其实,静态成员是如此特殊的一个东西,因为它

是所有实例共享的!!!也就是无论你 new 多少个实例出来,静态成员自始至终就是一个而已。而且,就算是你没有 new 过实例,它就已经存在了,神奇吧?

所以, Java鼓励你这样直接使用静态成员: A.y = 100; A.f(); //注意这里没有实例, A 亲自上阵了

所以,静态成员变量在 java里一般被用作共享的常数,例如 Math.PI ,所以你可以常常看到 final和 static 一起混的情形;静态成员函数一般被用作全局函数,供任意类调用,例如 Math.sin()

Page 72: Java 程序设计系列讲座- 2 面向对象程序设计

再次啰唆一下类成员的访问控制

• 类成员总共有四种访问模式:private , protected , public 以及 default ,它们的访问控制如下:

• 确定访问控制权限的原则– 基于安全性的考虑,在能满足设计要求的前提下,访

问权限越严格越好

Page 73: Java 程序设计系列讲座- 2 面向对象程序设计

一个例子class Root {

private int a; protected float b; public double c; char d; void fun() { a++; b++; c++; d++; // 类的内部,成员变量相当于全局

// 变量,在任何函数中均可直接用 }}假设有一个类的实例 Root r = new Root();那么 r.a++ 就是错误的,因为这相当于在类的外部使用变量;但是 r.c++ 是正确的,因为 c是 public ,支持外部访问。

Page 74: Java 程序设计系列讲座- 2 面向对象程序设计

关于封装的进一步探讨

• Java 的封装属性有四个(按开放程度排序): private , default , protected 以及 public 。

• 一般来说,封装越严格越好,因此尽量不要将成员变量设置为 public 。

• 当把成员变量设置为 private时,为了能够提供外部类对这些成员变量的存取,一般要设置一堆的 public 的get 方法和 set 方法。例如对 hour 成员:– public int getHour() { return hour; }– public void setHour(int h) { hour = h; }

Page 75: Java 程序设计系列讲座- 2 面向对象程序设计

方法重载• 方法重载是指

多个方法享有相同的名字,但是这些方法的参数必须不同,或者是参数的个数不同,或者是参数类型不同。

• 返回类型不能用来区分重载的方法。

Page 76: Java 程序设计系列讲座- 2 面向对象程序设计

再次啰唆一下构造方法

◇ 构造方法是一个特殊的方法。 Java 中的每个类都有构造方法,用来初始化该类的一个对象。◇ 构造方法具有和类名相同的名称,而且不返回任何数据类型。◇ 重载经常用于构造方法。◇ 构造方法只能由 new运算符调用。

Page 77: Java 程序设计系列讲座- 2 面向对象程序设计

关于构造方法以及 new

• 要生成一个对象的实例,必须使用 new运算符。每次new 的时候,对象的构造方法会被自动调用。

• 构造的方法可能有很多个, new 的时候调用哪一个取决于 new 的时候带的参数。

• 例如:– MyTime t1=new MyTime(); //调用无参数的构造方法– MyTime t2=new MyTime(hour, second, minute);

//调用使用三个参数的构造方法,注意三个参数的类型// 要和构造方法声明的参数类型保持一致

Page 78: Java 程序设计系列讲座- 2 面向对象程序设计

一个练习

•写一个类 Person 表示一个人,用 name 和age 表示其姓名和年龄,并提供以下三个构造函数:– Person(); //默认名字为 noname ,年龄为 0– Person(String name); // 年龄默认为 0– Person(String name, int age);

• 在main 函数中使用以上三种构造方式创建3个 Person 对象,然后输出。

Page 79: Java 程序设计系列讲座- 2 面向对象程序设计
Page 80: Java 程序设计系列讲座- 2 面向对象程序设计

Part II 继承性

Page 81: Java 程序设计系列讲座- 2 面向对象程序设计

类的继承和实现

• 一个类可以被继承,继承有什么好处呢?– 当一个类被继承的时候,它所有说明为

protected 和 public 的成员(变量和函数)都可以传递到它的下一代,这就是好处。

– 例如图形界面的程序,一般都需要继承JFrame ,因为 JFrame 这个类帮你做了很多事情:绘制一个界面,做一个标题栏,可以缩放……要是你自己从头开始做一个类似功能的类,那难度可就太可怕了……

Page 82: Java 程序设计系列讲座- 2 面向对象程序设计

给个例子看看继承的好处

import javax.swing.*;class MyGUI extends JFrame { //注意这个 MyGUI 类,除了继承,啥都没干,可是人家就是有界面}public class Test {

public static void main(String args[]) { MyGUI frame = new MyGUI(); frame.setTitle("早上好! "); frame.setSize(500, 400); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}

}

Page 83: Java 程序设计系列讲座- 2 面向对象程序设计

继承性

• 创建子类格式:class SubClass extends SuperClass {…}

• 这里 SubClass 就可以继承所有 SuperClass 的public 和 protected 的成员,当然,如果SubClass 有同名的成员,那么 SuperClass 的将被屏蔽。例如:

class A { public int a; protected int b; }class B extends A { int a; // 此 a 将屏蔽父类的 a void f() { b++; } // 这里的 b实际上是从 A 继

承来的}

Page 84: Java 程序设计系列讲座- 2 面向对象程序设计

又一个继承的例子

class A{ protected int x; public void f() { x--; }}class B extends A{ B() { x = 100; // 这里直接使用的 x ,其实来自父类 A f(); //f 函数其实也是来自 A ,但现在可以直接使

用 }}

Page 85: Java 程序设计系列讲座- 2 面向对象程序设计

super && this

• Java 中通过 super 来实现对父类成员的访问,super 用来引用当前对象的父类。 super 的使用有三种情况: – 1 )访问父类被隐藏的成员变量,如:     super.variable;

– 2 )调用父类中被重写的方法,如:     super.Method([paramlist]);

– 3 )调用父类的构造函数,如:     super([paramlist]);

• this 用于调用同一个类的成员变量或者成员方法,用法和 super 类似,其格式为:– this.variable;– this.Method([paramlist]);

Page 86: Java 程序设计系列讲座- 2 面向对象程序设计

再来一个继承的例子

class A{ protected int x; public void f() { x--; }}class B extends A{ int x; void f() { this.x++; // 这里用的是自己的 x ,也可以直接写 x++ super.x = 100; // 这里用的是父类继承来的 x super.f(); //f 函数其实也是来自父类 }}

Page 87: Java 程序设计系列讲座- 2 面向对象程序设计

总结一下继承性

• public :包内及包外的任何类均可以继承或访问;

• protected :包内的任何类,及包外的那些继承了此类的子类才能访问;

• default :包内的任何类都可以访问它,而包外的任何类都不能访问它 (包括包外继承了此类的子类 ) ;

• private :不能被继承,只能在类内部使用。

Page 88: Java 程序设计系列讲座- 2 面向对象程序设计

Part III 多态性

Page 89: Java 程序设计系列讲座- 2 面向对象程序设计

多态性• 静态多态性

– 体现在方法的重载,参见方法重载– 在编译阶段,具体调用哪个被重载的方法,编译器会根据参数的不同来静态确定调用相应的方法。

• 动态多态性– 由于子类继承了父类所有的属性(私有的除外),

所以子类对象可以作为父类对象使用。程序中凡是使用父类对象的地方,都可以用子类对象来代替。一个对象可以通过引用子类的实例来调用子类的方法。

– 重写方法的调用原则(匹配原则):对子类的一个实例,如果子类重写了父类的方法,则运行时系统调用子类的方法;否则调用父类的方法。

Page 90: Java 程序设计系列讲座- 2 面向对象程序设计

Object 类- 1

• 在 Java 中,当你随手写一个类的时候,如果没有 extends 一个超类, Java 都会默认这个类extends Object(如图)。换言之,所有Java里面的类都不是孤立的,它们其实有一个共同的祖宗,那就是 Object 。

• Object 有三个很常见的成员函数,这些函数一般会在子类中被覆盖:– equals , hashCode , toString

Page 91: Java 程序设计系列讲座- 2 面向对象程序设计

Object 类- 2

• equals 函数– 此函数默认是比较两个变量是否引用到同一个

实例(相当于== ,也就是比较指向的内存地址是否相等),如果要实现别的判等规则(例如内容是否相等),子类就需要覆盖这个函数。

• hashCode 函数– 此函数用来指定一个整数,作为一个类的哈希码,以便能够在内存中对这个类进行快速定位。由于涉及到数据结构的知识,这里不做展开。

Page 92: Java 程序设计系列讲座- 2 面向对象程序设计

Object 类- 3

• toString 函数– 这个函数用来返回一个对该类进行描述的字符串,由于这个函数会被 System.out.print 和System.out.println 自动调用,所以经常被子类覆盖。建议每个类都应该实现这个函数。

– 例如假设 Loan 这个类实现了 toString 函数,那么可以直接使用 print 将 Loan 的实例输出:Loan loan = new Loan();System.out.print(loan);

Page 93: Java 程序设计系列讲座- 2 面向对象程序设计

多态性的例子 public class Test { public static void main(String[] args) { m(new GraduateStudent()); m(new Student()); m(new Person()); m(new Object()); } public static void m(Object x) { System.out.println(x.toString()); } } class GraduateStudent extends Student { } class Student extends Person { public String toString() { return

"Student"; } } class Person extends Object { public String toString() { return

"Person"; } }

这个程序的输出是:StudentStudentPersonjava.lang.Object@61de33

Page 94: Java 程序设计系列讲座- 2 面向对象程序设计

关于动态绑定的解释

• 假设 obj 是一个类 Obj 的实例,当程序运行过程中出现这个语句时: obj.f()1. Java总是优先调用类 Obj 自身的成员函数 f() ;2. 如果 Obj没有这个 f() ,那么就看看 Obj 的上一级超类有没有,有则调用之;

3. 如果 Obj 的上一级超类也没有 f() ,那就继续往上找;

4. 有没有可能上溯到老祖宗 Object 了还没有这个 f()?绝对不会,因为那样就是这句代码写错了,会编译出错的,不能运行的……

Page 95: Java 程序设计系列讲座- 2 面向对象程序设计

类型转换和 instanceof - 1

• 上一个例子有这样的代码:– m(new Student());

• 这个相当于:– Object o = new Student(); //隐式类型转换– m(o);

• 这个例子告诉我们:如果需要,子类可以自动被转成超类。但是反之是不行的,例如:– Student b = o; //错误– Student b = (Student)o; //显式类型转换

• 这个例子告诉我们:无论如何,超类不能被自动转成子类,除非强行转换。

Page 96: Java 程序设计系列讲座- 2 面向对象程序设计

类型转换和 instanceof - 2

• 下面语句都是合法的:– Object o = new GraduateStudent();– Object o = new Student();– Object o = new Person();

• 很明显的是,虽然 o可以指向 Student,Person 和GraduateStudent 的任何一种,但是在程序运行的时候,如何得知 o具体指向什么对象呢?

• instanceof 是一个运算符,用来确定一个引用的实际指向的类型。下面这个表达式取值为 boolean– o instanceof Student– 如果上式为 true ,则 o必定指向一个 Student 对象,于

是可以把 o强行转成 Student ,从而调用 Student 的函数

Page 97: Java 程序设计系列讲座- 2 面向对象程序设计

THE END