QX-AI
GPT-4
QX-AI初始化中...
暂无预设简介,请点击下方生成AI简介按钮。
介绍自己
生成预设简介
推荐相关文章
生成AI简介
所看教程(视频):《浙江大学-翁恺-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 42
| import 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 15
| public 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局部变量}
使用var时必须指出初始值(不可以是null)
var <变量名> = <值>;
编译器可以推断出该变量的类型,且之后该变量的类型都是确定的,不可以给该变量赋其它类型的值
对象初始化 {#对象初始化}
可以在定义成员变量的地方直接赋值
int price = 80;
在创建一个对象的过程中,会首先去做各种初始化的动作
构造方法 {#构造方法}
与类同名的函数,没有返回值
在创建一个对象时会自动调用的方法
应该是public
|-----------------------|-------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7
| VendingMachine(){ total = 10; } VendingMachine(int price ){ this.price = price; }
|
方法重载 {#方法重载}
一个类里可以有多个不同参数 的构造方法
创建对象的时候给出不同的参数值,就会自动调用不同的构造方法
通过this()还可以在构造方法中调用其他构造方法,写在第一行,且只能使用一次
一个类里的同名但参数表不同的方法构成了重载关系
对象的识别 {#对象的识别}
通过巧妙的思想,识别不同出对象的特点,让类更通用
例如,要实现一个时钟
可以设计一个类,通过这个类可以制造出时、分、秒三个对象
对象的交互 {#对象的交互}
时、分、秒三个对象可以共同组成一个时钟对象
控制时、分、秒之间的交互在时钟对象的方法中完成
Display.java
|------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public 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; } }
|
Clock.java
|------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Clock { Display hour = new Display(24); Display minute = new Display(60); Display second = new Display(60); public void start() { while (true) { second.increase(); if (second.getValue() == 0) { minute.increase(); if (minute.getValue() == 0) { hour.increase(); } } System.out.printf("%02d:%02d:%02d\n", hour.getValue(), minute.getValue(), second.getValue()); } } }
|
Main.java
|---------------------|--------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6
| public class Main { public static void main(String[] args) { Clock clock = new Clock(); clock.start(); } }
|
访问属性 {#访问属性}
private :这个成员是私有的,只有在类的内部(成员方法和定义初始化 )才能访问
一般来说,成员变量都该是private
这个限制是对类的而不是对对象的:同一个类的不同对象可以互相访问对方的成员变量
public :任何人都可以访问
任何人指的是在任何类的方法或定义初始化中可以使用
使用指的是调用、访问或定义变量
很多的成员方法都是public
public的类,类名和文件名要一致,一个编译单元只能有一个public的类
protected:受保护的成员
friendly:默认属性,友好的成员
| 访问属性 | 本类 | 同包 | 子类 | 其它 | |-----------|----|----|----|----| | private | √ | | | | | friendly | √ | √ | | | | protected | √ | √ | √ | | | public | √ | √ | √ | √ |
包package {#包package}
包是java管理类的一个机制
源文件中同名类要在不同包内
声明该类的指定包名
package <包名>;
包名中的.代表文件夹的层次
没有package语句的源程序都将视为在同一个无名包内
import {#import}
使用import语句引入包中的类和接口
import test.Hallo
test包中的Hallo类
只要用到的类和本类不在同一个包内,就要import它
如果不使用import,当要用到类时要给出全名:<包名>.<类名>
引入一个包内的所有东西:import <包名>.*;(注意同名类的冲突)
NoteBook例子 {#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 27
| public 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位置处的数据
完成全部功能接口
NoteBook.java
|---------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 43
| import 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; } }
|
写出上层程序
Main.java
|---------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public 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 7
| 2 second third second 3 first second
|
对象数组 {#对象数组}
|-----------|------------------------------------------------|
| 1
| String[] a = new String[notes.size()];
|
对象数组中的每个元素都是对象的管理者而非对象本身
当创建了一个对象数组,只是管理者们被创建了,但对象还没有,得想办法把每个对象创建出来
for-each循环 {#for-each循环}
对于普通数组:
|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8
| int[] 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 9
| Value[] 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 5
| HashSet<String> s = new HashSet<String>(); s.add("first"); s.add("second"); s.add("first"); System.out.println(s);//容器都可以这样输出
|
输出:
|-----------|-------------------------|
| 1
| [second, first]
|
public String toString {#public-String-toString}
在java中只要类中实现了这样一个方法
就可以直接用对象名输出这个对象
容器当中都有这样一个方法
|---------------|------------------------------------------------|
| 1 2 3
| public String toString(){ return ""; }
|
Hash表 {#Hash表}
例子:数字与美元硬币名字对应,查找硬币名称
1=penny
5=nickel
10=dime
25=quarter
50=half-dollar
定义接口:
|-------------------|--------------------------------------------------------------------------------|
| 1 2 3 4 5
| public class Coin { public String getName(int amount){ return ""; } }
|
为什么不用switch-case?
体现在代码中的硬编码越少越好
使用Hash表(一种数据结构)
在这个表中,所有东西是以一对值放入的,一个叫做key(键),一个叫做值
一个key对应一个值,可以用key取值
Hash表中的元素没有顺序
Coin.java
|------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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
| import 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 } }
|
Main.java
|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11
| import 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 9
| 10 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
CD.java
|------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class CD{ private String title; private String artist; private int numofTracks; private int playingTime; private boolean gotIt = false; private String comment; public CD(String title, String artist, int numofTracks, int playingTime, String comment) { this.title = title; this.artist = artist; this.numofTracks = numofTracks; this.playingTime = playingTime; this.comment = comment; } public void print() { System.out.print("CD:"); System.out.print(title+":"); System.out.println(artist); } }
|
Database.java
|------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import java.util.ArrayList; public class Database { private ArrayList<CD> listCD = new ArrayList<CD>(); public void add(CD cd){ listCD.add(cd); } public void list(){ for (CD cd : listCD){ cd.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.list(); } }
|
现在资料库中已经可以存各种CD媒体了
但我们还想在资料库中存DVD媒体或者其它媒体类型
当然,我们可以再创建一个类表示DVD
DVD.java
|------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public 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的容器,以及配套的方法
Database.java
|------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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
| import 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 3
| CD:abc:aaa CD:adc:dgh DVD:add:eee
|
上面发生了什么?
我们创建了一个资料库类
资料库类里有两个容器,用来存放两种不同类型的媒体的对象管理者
这样的结构虽然能实现我们需要的功能,但DVD和CD类几乎一模一样
出现了很多代码复制,这是代码质量不良的表现
当我们需要修改print,add等方法,就得逐个去改
当我们需要新增一种媒体,就得为它做很多的工作
继承 {#继承}
CD和DVD类很相似,我们可以从中提取一些它们共有的东西封装成一个类Item
Item可以表达CD或者DVD
而Database只需管Item
Item.java
|------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Item { private String title; private int playingTime; private boolean gotIt = false; private String comment; public Item(String title, int playingTime, boolean gotIt, String comment) { this.title = title; this.playingTime = playingTime; this.gotIt = gotIt; this.comment = comment; } public Item(){ } public void print() { System.out.print(title+":"); } }
|
CD.java
|---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class CD extends Item{ // private String title; private String artist; private int numofTracks; // private int playingTime; // private boolean gotIt = false; // private String comment; public CD(String title, String artist, int numofTracks, int playingTime, String comment) { super(title, playingTime, false, comment); // this.title = title; this.artist = artist; this.numofTracks = numofTracks; // this.playingTime = playingTime; // this.comment = comment; } public void print() { System.out.print("CD:"); super.print(); System.out.println(artist); } }
|
DVD.java
|---------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class DVD extends Item{ // 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) { super(title, playingTime, false, comment); this.director = director; // this.title = title; // this.playingTime = playingTime; // this.comment = comment; } public void print() { System.out.print("DVD:"); super.print(); System.out.println(director); } }
|
Database.java
|------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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
| import java.util.ArrayList; public class Database { // private ArrayList<CD> listCD = new ArrayList<CD>(); // private ArrayList<DVD> listDVD = new ArrayList<DVD>(); private ArrayList<Item> listItem = new ArrayList<Item>(); // public void add(CD cd){ // listCD.add(cd); // } // // public void add(DVD dvd){ // listCD.add(dvd); // } public void add(Item item){ listItem.add(item); } public void list(){ // for (CD cd : listCD){ // cd.print(); // } // for(DVD dvd : listDVD){ // dvd.print(); // } for (Item item : listItem){ item.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 3
| CD: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在父类中初始化完,再让子类得到title
|---------------|-----------------------------------------------------------|
| 1 2 3
| public Item(String title) { this.title = title; }
|
在子类构造器中使用super()来得到父类的title
|---------------|---------------------------------------------------|
| 1 2 3
| public CD(String title) { super(title); }
|
super() {#super}
当程序初始化对象时,会先运行super()
然后去运行父类的构造器,再回来继续运行自己的构造器
super():去父类调用一个没有参数的构造器
super(<参数>):去父类调用一个有对应参数的构造器
当子类没有super(),会默认去调用父类没有参数的构造器
通过super关键字来实现对父类成员的访问,用来引用当前对象的父类
super.<父类成员>
通过this来区分子类父类中的同名成员
this.aaa(); // this 调用自己的方法
super.aaa(); // super 调用父类方法
子类和子类型 {#子类和子类型}
类定义了类型
子类定义了子类型
子类的对象可以被当作父类的对象来使用
-赋值给父类的变量(父类的对象管理者可以管理子类的对象)
-传递给需要父类对象的方法
-放进存放父类对象的容器里
多态 {#多态}
多态变量 {#多态变量}
所有的对象变量 都是多态 的(它们能保存不止一种类型的对象,不同时刻可以放不同类型的对象(例如父类的对象变量放子类的对象))
它们可以保存的是声明类型的对象,或声明类型的子类的对象
当把子类的对象 赋给父类的变量 的时候,就发生了向上造型
每一个java的对象变量,都具有两个类型
一个是声明类型
一个是动态类型
有时候两者是一致的,有时候又不一样
这就是变量的多态(在运行过程中,它所管理的对象类型是会变化的)
造型 {#造型}
造型:把一个类型的对象 ,赋给另一个类型的变量
对象变量的赋值并不是把一个对象赋给另一个对象(注在c++中可以做两个对象之间的赋值)
而是让这两个对象的管理者去管理同一个对象
|-------------------|-------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5
| String s = "hello"; //原本这个String类型的对象变量s管理着一个对象 //这个对象里面有个"hello" s = "bye"; //后来s去管理另一个对象,里面有"bye"
|
并不是将bye替换掉hello,java不能做这种事
java中"="的赋值运算,实际上是在改变指向
|-----------------|---------------------------------------------------------------------------------------------|
| 1 2 3 4
| String s = "hello"; String t = "bye"; s = t; //原本s和t各管理一个对象,现在s和t管理同一个对象,里面有"hello"
|
当给一个对象变量 管理着与它声明(静态)类型不符 的对象时,就发生了造型
CD是Item的子类
|---------------|-------------------------------------------------------------------------------------------------|
| 1 2 3
| CD cd = new CD("abc","aaa",4,60,"bb"); Item item = cd; //把子类的对象赋给父类的变量,让父类的对象变量去管理子类的对象
|
父类对象是不能直接赋给子类对象变量的
但可以强制把父类对象当成子类的对象,然后去造型
CD是Item的子类
|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5
| CD 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;
CD是Item的子类
|---------------|---------------------------------------------------------------------------------------------------|
| 1 2 3
| CD 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 2 3 4 5
| public void list(){ for (Item item : listItem){ item.print(); } }
|
item每次循环管理的对象不一样,甚至管理的对象的类型也不一样,可以是CD或是DVD
当item管理CD(DVD)类型的对象时,去调用print方法,调用的是CD(DVD)类型里的print
当通过对象变量调用方法的时候,调用哪个方法这件事情叫做绑定
-静态绑定:根据变量的声明类型来决定
-动态绑定:根据变量的动态类型来决定
在成员函数中调用其他成员函数也是通过this这个对象变量来调用的
java默认所有的绑定都是动态绑定
覆盖 {#覆盖}
子类和父类中存在名称和参数表 完全相同的函数,这一对函数构成覆盖 关系
通过父类的变量调用存在覆盖关系的函数时,调用变量当时所管理的对象 所属的类的函数
这是一种动态绑定
多态总结 {#多态总结}
多态性是对象多种表现形式的体现
通过一个变量去调用一个函数,我们不去判断变量运行中实际类型是什么,我们只想它能print
多态是同一个行为 具有多个不同表现形式或形态 的能力
item是CD类型时它这样print,是DVD类型时那样print,但都是print行为
类型系统 {#类型系统}
Object类 {#Object类}
java中所有类都是Object类型的子类
这是一种单根结构
发生继承时,父类所有public的东西子类都会得到
所以java中所有的类,都从Object类中得到了两个函数
-toString()
-equals()
toString() {#toString-NaN}
toString()会返回一个字符串,用来表达对象
当一个类中没有toString()方法时,会调用继承自Object类的toString()
|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6
| CD 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 4
| CD@3d075dc0 //类型名+一个类似地址、编号的东西 CD@3d075dc0 aaCD@3d075dc0
|
显然,默认的表达这个对象的toString(),是返回一个类型名+一个类似地址、编号的东西
我们可以在类中自定义一个toString()
即设计一个表达对象的toString()
|-----------------------|----------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7
| @Override public String toString() { return "CD{" + "artist='" + artist + '\'' + ", numofTracks=" + numofTracks + '}'; }
|
输出
|-----------|-----------------------------------------|
| 1
| CD{artist='aaa', numofTracks=4}
|
equals() {#equals}
==无法 比较两个对象的内容是否相同,只能比较这两个对象变量是否管理着同一个对象
我们需要使用equals()去比较内容
当类中没有equals(),会调用继承自Object类的equals()
|---------------|------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3
| CD 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
| @Override public boolean equals(Object o) { CD cc = (CD) o;//将Object o看作是CD类型的 return numofTracks == cc.numofTracks && artist.equals(cc.artist); }
|
输出
|-----------|--------------|
| 1
| true
|
@Override {#Override}
作用:告诉编译器,这个函数覆盖了父类的同属性、同名、同参方法
也可能会在代码界面报错,如果这个函数没有和父类的同名方法有相同属性、参数
不带@Override,如果自定义的equals()和父类的同属性、同名、同参
那么也会覆盖掉父类的,@Override只是起帮助检查作用
可扩展性 {#可扩展性}
现在要往Database这个资料库里增加新的媒体类型,是一件非常容易的事情
VideoGame.java
|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public 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); } }
|
只需要增加一个子类,然后构造一下,覆盖下方法,父类完全不需要去动
这种特性叫可扩展性:代码无需修改即可扩展去适应新的数据、新的内容
如果需要修改去适应新的数据、新的内容,则叫可维护性