第一版
import java.util.ArrayList;
public class Datebase {
private ArrayList<CD> listCD = new ArrayList<CD>();//用提示直接创建CD类
public void add(CD cd) {
listCD.add(cd); // 这个add是ArrayList的类库方法
}
public void list() {
for (CD cd : listCD)
cd.print();
}
public static void main(String[] args) {
Datebase db = new Datebase();
db.add(new CD("abc", "abc", 4, "...")); // add是调用public void add(CD cd)方法
db.add(new CD("def", "def", 4, "..."));
db.list();
}
}
public class CD {
private String title;
private String artist;
private int numOfTracks;
private boolean gotIt; // 默认值为false
private String comment;
public CD(String title, String artist, int numOfTracks, String comment) {
//顶部工具栏>Sourse>Generate Constructor Using Files..构造函数
//这里不需要gotIt所以不需要勾选gotIt
// super();//此时可有可无,这是继承的时候用到的
this.title = title;
this.artist = artist;
this.numOfTracks = numOfTracks;
this.comment = comment;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
public void print() {
System.out.println(title + ":" + artist);
}
}
输出结果
abc:abc
def:def
第二版(加入DVD)
import java.util.ArrayList;
public class Datebase {
private ArrayList<CD> listCD = new ArrayList<CD>();
private ArrayList<DVD> listDVD = new ArrayList<DVD>(); // 设置一个DVD
public void add(CD cd) {
listCD.add(cd); // 这个add是ArrayList的类库方法
}
public void add(DVD dvd) { // 和cd一样的功能,方法重载就好
listDVD.add(dvd);
}
public void list() {
for (CD cd : listCD) {
cd.print();
}
for (DVD dvd : listDVD) {
dvd.print();
}
}
public static void main(String[] args) {
Datebase db = new Datebase();
db.add(new CD("abc", "abc", 4, "...", 60)); // add是调用public void add(CD cd)方法
db.add(new CD("def", "def", 4, "...", 60));
db.add(new DVD("xxx", "aaa", 60, "...", 60));
db.list();
}
}
public class CD {
private String title;
private String artist;
private int numOfTracks;
private boolean gotIt; // 默认值为false
private String comment;
private int palyingTime;
public CD(String title, String artist, int numOfTracks, String comment, int palyingTime) {
// super();
this.title = title;
this.artist = artist;
this.numOfTracks = numOfTracks;
this.comment = comment;
this.palyingTime = palyingTime;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
public void print() {
System.out.println("CD : " + title + ":" + artist);
}
}
public class DVD {
private String title;
private String director;
private int numOfTracks;
private boolean gotIt; // 默认值为false
private String comment;
private int playingTime;
public DVD(String title, String director, int numOfTracks, String comment, int playingTime) {
super();
this.title = title;
this.director = director;
this.numOfTracks = numOfTracks;
this.comment = comment;
this.playingTime = playingTime;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
public void print() {
System.out.println("DVD : " + title + " :" + director);
}
}
结果
CD : abc:abc
CD : def:def
DVD : xxx :aaa
Datebase 和 CD 和 DVD 的结构关系
大致是DateBase管理CD和DVD,然后CD和DVD又各自管理着自己的东西
第二版发现CD和DVD两个类非常的相似,DateBase也有很多相同的调用方式,不同的是一个是CD一个是DVD。这样的代码不具扩展性,难维护,你要一个一个去改,各个地方都要改。
第三版(改进版)
思路:从两个类中提取共同的东西,可以管CD 和DVD,然后DateBase管公共那个就好了
import java.util.ArrayList;
public class Datebase { //注释掉的是原来的,表示新改进的不再需要了
// private ArrayList<CD> listCD = new ArrayList<CD>();
// private ArrayList<DVD> listDVD = new ArrayList<DVD>(); // 设置一个DVD
private ArrayList<Item> listItem = new ArrayList<Item>();
// public void add(CD cd) {
// listCD.add(cd); // 这个add是ArrayList的类库方法
// }
//
// public void add(DVD dvd) { // 和cd一样的功能,方法重载就好
// listDVD.add(dvd);
// }
public void add(Item item) { // 不再对DVD或者CD做操作,现在只对Item做操作
listItem.add(item);
}
// public void list() {
// for (CD cd : listCD) {
// cd.print();
// }
// for (DVD dvd : listDVD) {
// dvd.print();
// }
// }
public void list() {
for (Item item : listItem) {
item.print(); //同样使用警告提示帮我们在Item做一个print();
}
}
public static void main(String[] args) {
Datebase db = new Datebase();
db.add(new CD("abc", "abc", 4, "...", 60)); // add是调用public void add(CD cd)方法
db.add(new CD("def", "def", 4, "...", 60));
db.add(new DVD("xxx", "aaa", 60, "...", 60));
db.list();
}
}
public class CD extends Item { //ectends表示继承,继承父类Item,剩下代码没有改变
private String title;
private String artist;
private int numOfTracks;
private boolean gotIt; // 默认值为false
private String comment;
private int palyingTime;
public CD(String title, String artist, int numOfTracks, String comment, int palyingTime) {
super();
this.title = title;
this.artist = artist;
this.numOfTracks = numOfTracks;
this.comment = comment;
this.palyingTime = palyingTime;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
public void print() {
System.out.println("CD : " + title + ":" + artist);
}
}
public class DVD extends Item { //同样是继承父类Item,剩下代码没有改变
private String title;
private String director;
private int numOfTracks;
private boolean gotIt; // 默认值为false
private String comment;
private int playingTime;
public DVD(String title, String director, int numOfTracks, String comment, int playingTime) {
super();
this.title = title;
this.director = director;
this.numOfTracks = numOfTracks;
this.comment = comment;
this.playingTime = playingTime;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
public void print() {
System.out.println("DVD : " + title + " :" + director);
}
}
public class Item {
public void print() {
}
}//Item类先什么也不做,为什么还能有结果呢?
输出结果
CD : abc:abc
CD : def:def
DVD : xxx :aaa
以前DateBase认识CD和DVD,管理CD和DVD,现在只认识Item,只管理Item。
public void add(Item item) {
listItem.add(item);
}
db.add(new CD("abc", "abc", 4, "...", 60));
为什么add里面new的CD可以被Item接收?
因为CD是Item的子类,对Datebase来说,他对Item的全部的认识是说,Item应该有一个print()函数,DateBase要的是你有print()函数就好了,具体是哪一个item,他不关心。但是CD里头也有print()函数。
把CD中的print()删掉并不会引起错误,因为他继承了Item的print()函数;
子类得到父类所有可访问的东西。看下面的例子
public class Item {
public void print() {
System.out.println("Item"); //为了看出变化,在Item设置输出一些东西
}
}
public class CD extends Item {
public static void main(String[] args) {
CD cd = new CD("a", "A", 2, "...", 20);
cd.print(); //CD中并没有print()函数,但是却可以运行,来看看结果
}
//注释掉原来CD的print()函数
// public void print() {
// System.out.println("CD : " + title + ":" + artist);
//
// }
输出结果
Item
我们看到输出的是父类的print()函数,说明CD继承了Item的方法。
public static void main(String[] args) {
CD cd = new CD("a", "A", 2, "...", 20);
cd.print();
}
//把注释掉的CD类的print()函数弄回来,看看现在的print()是谁的
public void print() {
System.out.println("CD : " + title + ":" + artist);
}
输出结果
CD : a:A
说明如果子类有和父类相同的方法时,会使用自己类里面的方法
同样子类当中有和父类一模一样的成员变量,那么父类的就会被隐藏了
来看看CD类中的title去向
public class Item {
private String title;
public void print() {
System.out.println("Item");
}
public void setTitle(String title) {
this.title = title;
}
}
public class CD extends Item {
private String title;
private String artist;
private int numOfTracks;
private boolean gotIt; // 默认值为false
private String comment;
private int palyingTime;
public CD(String title, String artist, int numOfTracks, String comment, int palyingTime) {
super();
this.title = title; //自己类得到中的title得到title
setTitle(title); //setTitle时父类的方法,title传递到父类中,由父类自己处理
this.artist = artist;
this.numOfTracks = numOfTracks;
this.comment = comment;
this.palyingTime = palyingTime;
}
结论:父类的title有private,子类是无法访问的,但是可以通过父类本身的方法去访问,在谁的函数里指的成员变量就是谁的,如果子类和父类出现同名的成员变量,子类自己函数里面指的就是他自己的,那么在父类函数里指的就是父类的,他们之间没有任何联系。
尽量不要把成员变量变成protected,除非迫不得已
来看看怎么调用父类的函数
public class DVD extends Item {
private String director;
public DVD(String title, String director, int numOfTracks, String comment, int playingTime) {
super(title, playingTime, false, comment);
this.director = director;
}
public static void main(String[] args) {
DVD dvd = new DVD("a", "A", 2, ">>>", 20);
dvd.print(); // 子类和父类同名的方法,在子类里面就调用子类自己的
}
public void print() {
System.out.print("DVD ");
//title是父类私有的,我们想输出title,用父类的函数来输出title好了
//不建议用protected来修饰title,能不用就不用
super.print(); // 想调用父类的print()要用super
System.out.println(director);
}
}
public class Item {
private String title;
private int numOfTracks;
private boolean gotIt; // 默认值为false
private String comment;
private int palyingTime;
public Item(String title, int palyingTime, boolean gotIt, String comment) {
super();
this.title = title;
this.gotIt = gotIt;
this.comment = comment;
this.palyingTime = palyingTime;
}
public void print() {
System.out.println(title);
}
public void setTitle(String title) {
this.title = title;
}
}
输出结果
DVD a
A
- 子类的对象可以赋给父类的变量
好像父类“车”,子类“自行车”和“汽车”,"车"的变量可以接收“自行车”和“汽车”的对象
Vehicle v1 = new Vehicle();
Vehicle v2 = new car();
Vehicle v3 = new Bicyle(); - 子类的对象可以传递给需要父类的函数
- 子类的对象可以放在父类对象的容器里,比如上面的ArrayList<>。
多态变量
- Java的对象变量是多态的,他们能保存不止一种类型的对象,像ArrayList<>
- 他们可以保存的是声明类型的对象(item),或声明类型的子类的对象(cd和dvd)
public void list() {
for (Item item : listItem) {
item.print();
//声明类型就是item,传递过来得可能是dvd可能是cd,所以看的是实际管理的是谁,就执行谁的print();
//实际管理的是cd,就做cd的print(),实际管理得是dvd,就做dvd的print(),而不一定是Item类里面的print
}
}
- 当把子类的对象赋给父类的时候,就发生了向上造型。
什么是造型?
把一个类型的对象,赋给另外一个类型的变量,这个过程叫做造型。
造型cast
- 子类的对象可以赋值给父类的变量
- 注意!Java中不存在对象给对象赋值!!
- 父类的对象不能赋值给子类的变量!
Vechicle v;
Car c = new Car(); //创建一个Car的对象
v = c; //子类对象赋值给父类变量,可以!
c = v; //编译错误
- 可以用造型
c = (Car)v; //前提是v这个变量实际管理的是Car才行
再比如
Item item = new Item("a", 2, false, "...");
CD cd = new CD("A", "AA", 2, ">>>", 3);
cd = item; //错误,编译不通过
item = cd; //正确
Item item = new Item("a", 2, false, "...");
CD cd = new CD("A", "AA", 2, ">>>", 3);
item = cd;
CD cc = new CD("W", "WW", 2, "<<", 4);
cc = cd; //同类型,可以
cc = item; //父类给子类,编译不通过
cc = (CD)item; //强制转化让item管理CD,编译无奈通过,但是item事实上并不管理CD类,运行失败,类造型异常
造型
- 用括号围起类型放在值得前面
- 对象本身并没有发生任何变化
- 运行时有机制来检查这样的转化是否合理
- ClassCastException(类造型异常)
向上造型
- 拿一个子类的对象,当作父类来用
- 向上造型是默认的,不需要运算符
- 向上造型总是安全的
多态
比如声明类型就是item,传递过来得可能是dvd可能是cd,所以看的是实际管理的是谁,就执行谁的print();
实际管理的是cd,就做cd的print(),实际管理得是dvd,就做dvd的print(),而不一定是Item类里面的print()。
这样的过程叫多态。
函数调用的绑定
- 当通过对象变量调用函数的时候,调用那个函数这件事情叫做绑定。
- 静态绑定:根据变量的声明类型来决定
- 动态绑定:根据变量的动态类型来决定
- 在成员函数中调用其他成员函数也是通过this这个对象变量来调用的。
public void list() {
for (Item item : listItem) {
item.print(); //编译的时候并不知道这个item管的是什么,
//只有运行的时候才知道,运行的时候才知道的就是动态的。
//对Java所有的成员函数的调用,都应该被看做是动态绑定
}
}
覆盖override
- 子类和父类中存在名称和参数表完全相同的函数,这一对函数构成覆盖关系
- 通过父类的变量调用存在的覆盖关系的函数时,会调用所管理的对象所属的函数