所看教程(视频):《浙江大学-翁恺-Java-面向对象程序设计》
作为我自己的复习笔记,也可以当做该视频的同步笔记
面向对象
什么是面向对象
面向对象是把一组数据结构和处理他们的方法组成对象。
把具有相同行为的对象归纳成类
通过封装隐藏类的内部细节
通过继承使类得到泛化
通过多态实现基于对象类型的动态分派
只能操作对象
一切事物都是调用加封装的结果
程序实现的一切功能都是调用加封装的结果
程序调用一个个对象
封装的也是一个个对象
基于对象编程
同一个事物,构成它的对象能有多种划分方式
机器语言、汇编语言将电路上的开关,由1和0组成的指令作为对象
c语音将对象界定为一个个数据和一个个算法
java将数据和算法的结合作为一个对象
数据和算法分开的编程:面向过程编程
数据和算法结合的编程:面向对象编程
面向过程与面向对象
面向过程关注如何实现,关注如何做,将一个要实现的、复杂的功能,用一个或多个大函数去实现,再抽丝剥茧,用更多的函数去实现这些函数。
面向对象关注数据,方法就在这,处理什么数据(对象)
面向对象中也有面向过程的代码,只是重点不在如何做,而是对对象的抽象与扩展
对于实现功能的核心算法,面向对象与面向过程并无区别,c语言也可通过结构体与函数指针实现面向对象
面向对象的封装、继承和多态,使得代码、功能的扩展、复用变得非常容易
这两种编程思想都是为了解决实际的问题
如何烧水
转自互联网
面向过程的烧水:
读取热水壶内水的水温,缓存
电热装置将发热量缓存
损耗算法读取发热量,并将将水的提升温度缓存
与水温相加
将这个数值重新赋给水温
直到温度达到沸点,完成烧水
面向对象的烧水:
定义热水壶类,继承自盛水容器,温度改变装置,温度计,水温控制接口
实例化一个热水壶类对象,命名为「我的热水壶」
为终止温度赋值:水.沸点
我的热水壶.温度处理(终止温度);
另:个人实现的烧水方法,仅图一乐:
为热水壶类实装烧水接口:实装水温监视事件
为热水壶类实装烧水接口:定义一个水温枚举器
为热水壶类实装烧水接口:实装温度处理方法
执行流程:
1.执行继承自盛水容器类的盛水方法
2.注册继承自温度改变装置类的温度监视事件
3.遍历水温枚举器:如果水温提升,则返回当前水温
4.如果返回值接近终止温度,跳出枚举过程,完成烧水。
5.否则,继续遍历枚举器。
你肯定会问这哪里优雅了,确实,看起来是复杂了不少,但再仔细想一想,这一整套流程只要稍加修改,稍加改变接口实现,你就可以直接用这个「热水壶类」实现一个热水器,甚至还可以是一个冰箱。
毕竟烧的又不一定是水,又不一定要烧水,又不一定要用壶烧水,又不一样要是个烧水壶
对象与类
对象是实体,需要被创建,可以为我们做事情
类是规范,根据类的定义来创建对象
一个类可以有多个对象
动物是一个类,每个对象,猫,狗,都是动物类的实体
我们用类制造出对象,再给对象所需要的数据,对象可以利用这些数据去做事情,我们大可无需知道对象是如何利用这些数据的,因为我们只要求,这个对象能实现一些功能
面向对象的思维
我们看到一个事物
它有什么东西?
能干什么?
第一个程序:自动售货机
售货机(VendingMachine)有什么?
商品的价格:price
显示的余额:balance
卖了多少钱:total
售货机能干什么?
输出一些提示:showPromot
取得一些钱:insertMomey
告诉用户余额:showBalance
给我们商品(食物):getFood
告诉商家总收入:showTotal
我们需要设计VendingMachine这个类,这个类有3个属性,有5个动作(方法)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42import java.util.Scanner;
//创建一个类
public class VendingMachine {
int price = 80;//商品价格(假设就只有一个商品,且价格固定)
int balance = 0;//当前余额
int total = 0;//总收入
int amount = 0;//钱
Scanner s=new Scanner(System.in);
void showPromot() {
//输出提示
System.out.println("欢迎!");
}
void insertMomey(){
// 投入钱,更新余额
System.out.print("请充值余额:");
amount = s.nextInt();
balance = balance + amount;
}
void showBalance(){
//输出余额
System.out.println("现在余额: "+ balance);
}
void getFood(){
//给食物
if (balance >= price) {
System.out.println("给你。");
balance = balance - price;
total = total + price;
}
else{
System.out.println("没有足够的余额!");
}
}
void showTotal(){
System.out.println("目前总收入:"+total);
}
}
有了这个类,就可以通过类去制造一个对象,并让对象去实现一些功能1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class Main {
public static void main(String[] args) {
//制作一个对象
VendingMachine vm = new VendingMachine();
boolean t = true;
vm.showPromot();
vm.showBalance();
while (t) {
vm.insertMomey();
vm.getFood();
vm.showBalance();
vm.showTotal();
}
}
}
运行结果:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15欢迎!
现在余额: 0
请充值余额:100
给你。
现在余额: 20
目前总收入:80
请充值余额:20
没有足够的余额!
现在余额: 40
目前总收入:80
请充值余额:40
给你。
现在余额: 0
目前总收入:160
请充值余额:
创建对象
使用new运算符,来创建这个类的一个对象
然后将这个对象交给这个类型的一个变量
VendingMachine vm = new VendingMachine();
对象变量是对象的管理者
让对象做事情
使用 . 运算符
vm.insertMomey();
vm.getFood();
通过.运算符调用某个对象的方法
成员变量、成员方法
类定义了对象中所具有的变量,这些变量称作成员变量
每个对象有自己的变量,和同一个类的其他对象是分开的
在方法中可以直接写成员变量(方法)的名字来访问成员变量(方法)(省去了this关键字)
java会给成员变量默认0值
成员变量(方法)分为实例变量(方法)和类变量(方法)
加了static的就是类变量(方法)
类变量
声明类变量: static <类型> <变量名>
访问类变量:
通过对象访问:<对象名>.<类变量名>
通过类访问:<类名>.<类变量名>
类变量不属于任何一个对象,属于这个类,但任何一个对象都拥有这个变量
修改类变量的值,所有对象中的该变量的值都会改变
类变量的初始化只会进行一次(在类的装载时)
类方法
声明类方法: static <返回类型> <方法名>() { }
static方法只能调用static方法,只能访问static变量
类方法可以通过类的名字去访问,也可以通过对象去访问
本地(局部)变量
定义在方法内部的变量是本地变量
本地变量的生存期和作用域都是方法内部
本地变量没被赋值,会被禁止使用
成员变量的生存期是对象的生存期,作用域是类内部的成员方法
var局部变量
使用var时必须指出初始值(不可以是null)
var <变量名> = <值>;
编译器可以推断出该变量的类型,且之后该变量的类型都是确定的,不可以给该变量赋其它类型的值
对象初始化
可以在定义成员变量的地方直接赋值
int price = 80;
在创建一个对象的过程中,会首先去做各种初始化的动作
构造方法
与类同名的函数,没有返回值
在创建一个对象时会自动调用的方法
应该是public1
2
3
4
5
6
7VendingMachine(){
total = 10;
}
VendingMachine(int price ){
this.price = price;
}
方法重载
一个类里可以有多个不同参数的构造方法
创建对象的时候给出不同的参数值,就会自动调用不同的构造方法
通过this()还可以在构造方法中调用其他构造方法,写在第一行,且只能使用一次
一个类里的同名但参数表不同的方法构成了重载关系
对象的识别
通过巧妙的思想,识别不同出对象的特点,让类更通用
例如,要实现一个时钟
可以设计一个类,通过这个类可以制造出时、分、秒三个对象
对象的交互
时、分、秒三个对象可以共同组成一个时钟对象
控制时、分、秒之间的交互在时钟对象的方法中完成1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class Display {
private int value = 0;
private int limit = 0;
public Display(int limit){
this.limit = limit;
}
public void increase(){
value++;
if(value == limit){
value = 0;
}
}
public int getValue(){
return value;
}
}
1 | public class Clock { |
1 | public class Main { |
访问属性
private:这个成员是私有的,只有在类的内部(成员方法和定义初始化 )才能访问
一般来说,成员变量都该是private
这个限制是对类的而不是对对象的:同一个类的不同对象可以互相访问对方的成员变量
public:任何人都可以访问
任何人指的是在任何类的方法或定义初始化中可以使用
使用指的是调用、访问或定义变量
很多的成员方法都是public
public的类,类名和文件名要一致,一个编译单元只能有一个public的类
protected:受保护的成员
friendly:默认属性,友好的成员
访问属性 | 本类 | 同包 | 子类 | 其它 |
---|---|---|---|---|
private | √ | |||
friendly | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
包package
包是java管理类的一个机制
源文件中同名类要在不同包内
声明该类的指定包名
package <包名>;
包名中的.代表文件夹的层次
没有package语句的源程序都将视为在同一个无名包内
import
使用import语句引入包中的类和接口
import test.Hallo
test包中的Hallo类
只要用到的类和本类不在同一个包内,就要import它
如果不使用import,当要用到类时要给出全名:<包名>.<类名>
引入一个包内的所有东西:import <包名>.*;(注意同名类的冲突)
NoteBook例子
记事本可以做什么?
1、能存储记录
2、不限制能存储的记录的数量
3、能知道已经存储的记录的数量
4、能查看存进去的每一条记录
5、能删除一条记录
6、能列出所有的记录
确定需求后,进行接口设计1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27public class NoteBook {
public void add(String s) {
//添加内容
}
public int getSize() {
//放了多少个
return 0;
}
public String getNote(int index) {
//得到指定位置的内容
return " ";
}
public boolean removeNote(int index) {
//删除
return true;
}
public String[] list() {
//返回全部内容
}
}
接口设计完,考虑实际功能的实现,首先是数据的存放
顺序容器
1 | private ArrayList<String> notes = new ArrayList<String>(); |
用来存放String的一个ArrayList
ArrayList内的东西是有顺序的,是加入数据的顺序,形成对应下标的索引(从0开始)
这种类型叫做范型类:泛型类封装不特定于特定数据类型的操作
这种范型类是一种容器
容器类有两个类型:容器的类型、元素的类型
利用容器类的方法可以实现需要的功能
notes.add(s); //向容器添加数据
notes.size(); //容器存了多少个东西
notes.get(1); //得到1位置处的数据
完成全部功能接口1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43import java.util.ArrayList;
public class NoteBook {
private ArrayList<String> notes = new ArrayList<String>();//容器类
public void add(String s) {
//添加内容
notes.add(s);
}
public void add(String s, int location) {
//加到指定位置前,后面的内容下标后推
notes.add(location, s);
}
public int getSize() {
//放了多少个
return notes.size();
}
public String getNote(int index) {
//得到指定位置的内容
return notes.get(index);
}
public void removeNote(int index) {
//删除,后面下标前移,因为remove方法自会抛异常,所以无需返回boolean
notes.remove(index);
}
public String[] list() {
//返回全部内容
String[] a = new String[notes.size()];
//for (int i=0; i< notes.size(); i++){
// a[i] = notes.get(i);
//}
notes.toArray(a);//会自己把数组按顺序填好
//要熟悉系统类库里有的方法,无需重复造轮子
return a;
}
}
写出上层程序1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class Main {
public static void main(String[] args) {
NoteBook nb = new NoteBook();
nb.add("first");
nb.add("second");
System.out.println(nb.getSize());
System.out.println(nb.getNote(1));
nb.add("third", 1);
System.out.println(nb.getNote(1));
System.out.println(nb.getNote(2));
System.out.println(nb.getSize());
nb.removeNote(1);
String[] b = nb.list();
for (String s : b) {
System.out.println(s);
}
}
}
输出:1
2
3
4
5
6
72
second
third
second
3
first
second
对象数组
1 | String[] a = new String[notes.size()]; |
对象数组中的每个元素都是对象的管理者而非对象本身
当创建了一个对象数组,只是管理者们被创建了,但对象还没有,得想办法把每个对象创建出来
for-each循环
对于普通数组:1
2
3
4
5
6
7
8int[] a = new int[10];
for (int i = 0; i < a.length; i++) {
a[i] = i;//赋值
}
for ( int k : a ) {
System.out.println(k);
k++;//每个k都是a中元素的复制品,不会起作用
}
对于对象数组:1
2
3
4
5
6
7
8
9Value[] a = new Value[10];
for (int i=0; i< 10; i++){
a[i] = new Value[];
a[i].set(i);
}
for ( Value v : a ){
System.out.println(v.get());
v.set(0);//起作用,因为对象数组存的是对象管理者,v=a[i],v也会成为对象管理者
}
集合容器
集合容器内所有元素都不相同
而且里面的元素不排序1
2
3
4
5HashSet<String> s = new HashSet<String>();
s.add("first");
s.add("second");
s.add("first");
System.out.println(s);//容器都可以这样输出
输出:1
[second, first]
public String toString
在java中只要类中实现了这样一个方法
就可以直接用对象名输出这个对象
容器当中都有这样一个方法1
2
3public String toString(){
return "";
}
Hash表
例子:数字与美元硬币名字对应,查找硬币名称
1=penny
5=nickel
10=dime
25=quarter
50=half-dollar
定义接口:1
2
3
4
5public class Coin {
public String getName(int amount){
return "";
}
}
为什么不用switch-case?
体现在代码中的硬编码越少越好
使用Hash表(一种数据结构)
在这个表中,所有东西是以一对值放入的,一个叫做key(键),一个叫做值
一个key对应一个值,可以用key取值
Hash表中的元素没有顺序1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32import java.util.HashMap;
public class Coin {
//不能使用int,容器当中所有的类型都得是对象,而不能是基本类型
//Integer是int的包裹类型
private HashMap<Integer, String> coinnames = new HashMap<Integer, String>();
public Coin(){
coinnames.put(1, "penny");//1对应penny
coinnames.put(10, "dime");
coinnames.put(25, "quarter");
coinnames.put(50, "half-dolar");
System.out.println(coinnames.keySet().size());//keySet(),把所有key做为一个HashSet的集合给你,在这个集合可以得到size
System.out.println(coinnames);//也可以直接输出
coinnames.put(50, "五十");//会替换掉前面的
System.out.println(coinnames);
for (Integer k : coinnames.keySet()){//遍历Hash表
String s = coinnames.get(k);
System.out.println(s);
}
}
public String getName(int amount){
if (coinnames.containsKey(amount))
return coinnames.get(amount);
else
return "NOT FOUND";//不判断的话,不存在会返回null
}
}1
2
3
4
5
6
7
8
9
10
11import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int amount = in.nextInt();
Coin coin = new Coin();
String name = coin.getName(amount);
System.out.println(name);
}
}
输出:1
2
3
4
5
6
7
8
910
4
{1=penny, 50=half-dolar, 25=quarter, 10=dime}
{1=penny, 50=五十, 25=quarter, 10=dime}
penny
五十
quarter
dime
dime
继承与子类
媒体资料库设计
和NoteBook一样,需要设计一个类,用类去表达一种媒体(CD,DVD)
然后用一个媒体类的容器去装媒体对象,一个资料库就完成了
CD有什么?
名称:title
艺术家:artist
多少首歌:numofTracks
持续时间:playingTime
是否被借出:gotIt
描述:comment
能做什么?
输出一些信息:print
1 | public class CD{ |
1 | import java.util.ArrayList; |
现在资料库中已经可以存各种CD媒体了
但我们还想在资料库中存DVD媒体或者其它媒体类型
当然,我们可以再创建一个类表示DVD1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class DVD{
private String title;
private String director;
private int playingTime;
private boolean gotIt = false;
private String comment;
public DVD(String title, String director, int playingTime, String comment) {
this.director = director;
this.title = title;
this.playingTime = playingTime;
this.comment = comment;
}
public void print() {
System.out.print("DVD:");
System.out.print(title+":");
System.out.println(director);
}
}
设计好类后在Database.java里创建一个放DVD的容器,以及配套的方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34import java.util.ArrayList;
public class Database {
private ArrayList<CD> listCD = new ArrayList<CD>();
//+
private ArrayList<DVD> listDVD = new ArrayList<DVD>();
public void add(CD cd){
listCD.add(cd);
}
//+
public void add(DVD dvd){
listCD.add(dvd);
}
public void list(){
for (CD cd : listCD){
cd.print();
}
//+
for(DVD dvd : listDVD){
dvd.print();
}
}
public static void main(String[] args) {
Database db = new Database();
db.add(new CD("abc","aaa",4,60,"bb"));
db.add(new CD("adc","dgh",5,40,"ak"));
//+
db.add(new DVD("add","eee",45,"qqq"));
db.list();
}
}
现在资料库中能存放两种媒体
运行一下:1
2
3CD:abc:aaa
CD:adc:dgh
DVD:add:eee
上面发生了什么?
我们创建了一个资料库类
资料库类里有两个容器,用来存放两种不同类型的媒体的对象管理者
这样的结构虽然能实现我们需要的功能,但DVD和CD类几乎一模一样
出现了很多代码复制,这是代码质量不良的表现
当我们需要修改print,add等方法,就得逐个去改
当我们需要新增一种媒体,就得为它做很多的工作
继承
CD和DVD类很相似,我们可以从中提取一些它们共有的东西封装成一个类Item
Item可以表达CD或者DVD
而Database只需管Item
1 | public class Item { |
1 | public class CD extends Item{ |
1 | public class DVD extends Item{ |
1 | import java.util.ArrayList; |
运行一下:1
2
3CD:abc:aaa
CD:adc:dgh
DVD:add:eee
上面发生了什么?
CD extends Item:CD扩展了Item
即CD变成了Item的子类
这就是继承
CD得到了Item里所有的东西
子类与父类
当父类里的东西是private时
private String title;
子类得到了这个东西,但不能用(可以通过父类的方法去用)
解决办法:将private改成protect
但这样不好,有很多时候父类和子类不在同一个包内
title本来就是父类的东西
可以让title在父类中初始化完,再让子类得到title1
2
3public Item(String title) {
this.title = title;
}
在子类构造器中使用super()来得到父类的title1
2
3 public CD(String title) {
super(title);
}
super()
当程序初始化对象时,会先运行super()
然后去运行父类的构造器,再回来继续运行自己的构造器
super():去父类调用一个没有参数的构造器
super(<参数>):去父类调用一个有对应参数的构造器
当子类没有super(),会默认去调用父类没有参数的构造器
通过super关键字来实现对父类成员的访问,用来引用当前对象的父类
super.<父类成员>
通过this来区分子类父类中的同名成员
this.aaa(); // this 调用自己的方法
super.aaa(); // super 调用父类方法
子类和子类型
类定义了类型
子类定义了子类型
子类的对象可以被当作父类的对象来使用
-赋值给父类的变量(父类的对象管理者可以管理子类的对象)
-传递给需要父类对象的方法
-放进存放父类对象的容器里
多态
多态变量
所有的对象变量都是多态的(它们能保存不止一种类型的对象,不同时刻可以放不同类型的对象(例如父类的对象变量放子类的对象))
它们可以保存的是声明类型的对象,或声明类型的子类的对象
当把子类的对象赋给父类的变量的时候,就发生了向上造型
每一个java的对象变量,都具有两个类型
一个是声明类型
一个是动态类型
有时候两者是一致的,有时候又不一样
这就是变量的多态(在运行过程中,它所管理的对象类型是会变化的)
造型
造型:把一个类型的对象,赋给另一个类型的变量
对象变量的赋值并不是把一个对象赋给另一个对象(注在c++中可以做两个对象之间的赋值)
而是让这两个对象的管理者去管理同一个对象1
2
3
4
5String s = "hello";
//原本这个String类型的对象变量s管理着一个对象
//这个对象里面有个"hello"
s = "bye";
//后来s去管理另一个对象,里面有"bye"
并不是将bye替换掉hello,java不能做这种事
java中”=“的赋值运算,实际上是在改变指向1
2
3
4String s = "hello";
String t = "bye";
s = t;
//原本s和t各管理一个对象,现在s和t管理同一个对象,里面有”hello“
当给一个对象变量管理着与它声明(静态)类型不符的对象时,就发生了造型1
2
3CD cd = new CD("abc","aaa",4,60,"bb");
Item item = cd;
//把子类的对象赋给父类的变量,让父类的对象变量去管理子类的对象
父类对象是不能直接赋给子类对象变量的
但可以强制把父类对象当成子类的对象,然后去造型1
2
3
4
5CD cd = new CD("abc","aaa",4,60,"bb");
Item item = cd;
CD cc = item;//不行。父类对象不能直接交给子类对象变量去管理
CD cc = (CD)item;//行,因为item已经管理着一个CD的对象了
//强制把item的类型当做CD
如果没有Item item = cd;1
2
3CD cd = new CD("abc","aaa",4,60,"bb");
//Item item = cd;
CD cc = (CD)item;//编译可以通过,但运行会出错
将一个变量强制造型成另一个类型,然后赋给另一个变量
CD cc = (CD)item;
只有当item这个变量实际管理着CD类型的对象才不会出错
在C语言中,有类似写法,但是是类型转换(对于基本类型int、double,java也能强制类型转换)
int i = (int)10.2;//强制类型转换
这与造型是不同的
类型转换是将10.2变成了10
但造型只是把item当做CD类型来看待
item本身还是Item类型
(类型名)对象名:将一个对象当做这个类型来看待
向上造型
向上造型是特殊的造型,无需写(父类类型)
拿一个子类的对象,当作父类的对象来用
向上造型总是安全的
方法调用的绑定
1 | public void list(){ |
item每次循环管理的对象不一样,甚至管理的对象的类型也不一样,可以是CD或是DVD
当item管理CD(DVD)类型的对象时,去调用print方法,调用的是CD(DVD)类型里的print
当通过对象变量调用方法的时候,调用哪个方法这件事情叫做绑定
-静态绑定:根据变量的声明类型来决定
-动态绑定:根据变量的动态类型来决定
在成员函数中调用其他成员函数也是通过this这个对象变量来调用的
java默认所有的绑定都是动态绑定
覆盖
子类和父类中存在名称和参数表完全相同的函数,这一对函数构成覆盖关系
通过父类的变量调用存在覆盖关系的函数时,调用变量当时所管理的对象所属的类的函数
这是一种动态绑定
多态总结
多态性是对象多种表现形式的体现
通过一个变量去调用一个函数,我们不去判断变量运行中实际类型是什么,我们只想它能print
多态是同一个行为具有多个不同表现形式或形态的能力
item是CD类型时它这样print,是DVD类型时那样print,但都是print行为
类型系统
Object类
java中所有类都是Object类型的子类
这是一种单根结构
发生继承时,父类所有public的东西子类都会得到
所以java中所有的类,都从Object类中得到了两个函数
-toString()
-equals()
toString()
toString()会返回一个字符串,用来表达对象
当一个类中没有toString()方法时,会调用继承自Object类的toString()1
2
3
4
5
6CD cd = new CD("abc","aaa",4,60,"bb");
System.out.println(cd.toString());
System.out.println(cd);//和上面的效果一个月,编译器会知道这个地方需要调用toString()
String s = "aa"+cd;//编译器知道这个地方需要调用toString()
System.out.println(s);1
2
3
4CD@3d075dc0
//类型名+一个类似地址、编号的东西
CD@3d075dc0
aaCD@3d075dc0
显然,默认的表达这个对象的toString(),是返回一个类型名+一个类似地址、编号的东西
我们可以在类中自定义一个toString()
即设计一个表达对象的toString()1
2
3
4
5
6
7
public String toString() {
return "CD{" +
"artist='" + artist + '\'' +
", numofTracks=" + numofTracks +
'}';
}1
CD{artist='aaa', numofTracks=4}
equals()
==无法比较两个对象的内容是否相同,只能比较这两个对象变量是否管理着同一个对象
我们需要使用equals()去比较内容
当类中没有equals(),会调用继承自Object类的equals()1
2
3CD cd1 = new CD("abc","aaa",4,60,"bb");
CD cd2 = new CD("abc","aaa",4,60,"bb");
System.out.println(cd1.equals(cd2));1
false
Object这个公共父类的equals()无法知道它的子类长什么样子,所以也无法比较这两个对象内容是否相等
Object的equals()实际上也是在比较两个对象变量是否管理着同一个对象
我们需要使用自定义的equals()去比较内容1
2
3
4
5
public boolean equals(Object o) {
CD cc = (CD) o;//将Object o看作是CD类型的
return numofTracks == cc.numofTracks && artist.equals(cc.artist);
}1
true
@Override
作用:告诉编译器,这个函数覆盖了父类的同属性、同名、同参方法
也可能会在代码界面报错,如果这个函数没有和父类的同名方法有相同属性、参数
不带@Override,如果自定义的equals()和父类的同属性、同名、同参
那么也会覆盖掉父类的,@Override只是起帮助检查作用
可扩展性
现在要往Database这个资料库里增加新的媒体类型,是一件非常容易的事情1
2
3
4
5
6
7
8
9
10
11
12
13
14public class VideoGame extends Item {
private int numberofPlayers;
public VideoGame(String title, int playingTime, boolean gotIt, String comment, int numberofPlayers) {
super(title, playingTime, gotIt, comment);
this.numberofPlayers = numberofPlayers;
}
public void print() {
System.out.print("VideoGame:");
super.print();
System.out.println(numberofPlayers);
}
}
只需要增加一个子类,然后构造一下,覆盖下方法,父类完全不需要去动
这种特性叫可扩展性:代码无需修改即可扩展去适应新的数据、新的内容
如果需要修改去适应新的数据、新的内容,则叫可维护性