9多态
9.1Object 类是所有类的父类
Object类经常被重写的方法
方法 | 说明 |
equals() | 比较两个对象是否是同一个对象,是则返回true |
hasCode() | 返回该对象的哈希代码值 |
getClass() | 获取当前对象所属的类信息,返回Class对象 |
toString() | 返回当前对象本身的有关信息,按字符串对象返回 |
举例:Student类重写toString方法
com.nj.polyphism.Student@150bd4d 包名+类名+”@“ +该对象的hashcode的对应的16进制字符串。如果重写了,按你重写之后的内容显示
@Override
public String toString() {
return "学号:"+this.sid+",姓名:"+this.name+",年龄:"+this.age;
}
Object类的equals()方法
比较两个对象是否是同一个对象,是则返回true
操作符==
简单数据类型,直接比较值。如1==2
引用类型,比较两者是否为同一对象
(1)Object类的equals()方法与==没区别
(2)当有特殊需求,如认为属性相同即为同一对象时,需要重写equals()
(3)Java.lang.String 重写了equals()方法,把equals()方法的判断变为了判断其值
举例:Student类 名字,学号相同,则认为是同一个人。重写equals()方法
/**
* toS + alt+ /
* eq + alt+ /
* 对象A instanceof 类型B 判断对象A是否是类型B这种类型的,如果是,返回true,否则返回false dog instanceOf Dog dog对象是否是Dog这种类型的
* @author Administrator
*/
public class Student {
private int sid; //学号
private String name; //名字
private int age; //年龄
public Student() {
}
public Student(int sid, String name, int age) {
super();
this.sid = sid;
this.name = name;
this.age = age;
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* 方式1:
* 学号,姓名相等,认为是同一个对象
*/
/*@Override
public boolean equals(Object obj) {
if(this==obj){ //自身和自身比
return true;
}
//判断obj是不是Student类型 ,如果不是返回false
if(!(obj instanceof Student)){
return false;
}
//如果代码能够通过if判断,执行到此次,那么传入的obj肯定是Student类型的
Student s = (Student)obj;
if(this.sid==s.sid && this.name.equals(s.name)){
return true;
}
return false;
}*/
/**
* 方式2:
*/
public boolean equals(Object obj){
if(!(obj instanceof Student)){
return false;
}
return (this.sid == ((Student)obj).sid) && ((Student)obj).name.equals(this.name);
}
}
package com.njwb.polyphism2;
/**
* == 对于基本数据类型而言,比较的是基本数据类型的值
* == 对于引用数据类型而言,比较的是内存地址
* equals()是Object类的方法,作用和“==”是一样的,都是用来比较内存地址的,但是用户可以根据自己的需求改写,比方说
* java.lang.String类重写了 equals()方法,将其变成比较字符串的内容是否相等
* 需求:如果姓名,学号相等,就认为是同一个人,也就是说 equals()比较结果,要返回true
* @author Administrator
*/
public class TestStudent01 {
public static void main(String[] args) {
Student stu1 = new Student(101,"张三",18);
Student stu2 = new Student(101,"张三",18);
// 比较2个对象的内存地址
System.out.println("比较内存地址方式1:"+(stu1==stu2));
//没有重写equals()方法之前,equals()作用和“==”是一样,都是用来比较内存地址的
//System.out.println("重写前比较内存地址方式2:"+stu1.equals(stu2));
//重写equals()方法,认为名字,学号相等,就是同一个人
//System.out.println("重写后比较的是内容是自己设定的:"+stu1.equals(stu2));
Dog dog = new Dog();
System.out.println("两个对象的之间的比较:"+stu1.equals(dog));
Student stu3 = new Student(102,"李四",19);
System.out.println("两个对象之间的比较:"+stu1.equals(stu3));
}
}
Object类的hashCode()方法
public int hashCode()
返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
对于包含容器类型的程序设计语言来说,基本上都会涉及到hashCode。在java中也一样,hashCode方法的主要作用也是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet,HashMap,HashTable.
为什么这么说呢?考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?(注意:集合中不允许重复的元素存在),也许大多数人都会想到调用equals()方法去逐一比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashCode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashCode值,如果table中没有该hashCode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址, 对象的字段等)映射成一个数值,这个数值称作散列值。从HashMap的put方法的具体实现可以看出,hashCode的存在是为了减少equals方法的调用次数,从而提高程序效率。
问题:可以直接根据hashCode值判断两个对象是否相等吗?肯定不可以。因为不同的对象可能会生成相同的hashcode值。虽然不能根据hashCode值判断两个对象是否相等,但是可以直接根据hashcode值判断两个对象不等,如果两个对象的hashcode值不等,则必定是两个不同的对象。
如果要判断两个对象是否真正相等,必须通过equals方法。
l 对于两个对象,如果调用equals方法得到的结果为true,则两个对象的hashcode值必定相等;
l 如果equals方法得到的结果为false,则两个对象的hashcode值不一定不同;
l 如果两个对象的hashcode值不等,则equals方法得到的结果必定为false;
l 如果两个对象的hashcode值相等,则equals方法得到的结果未知。
举例:在有些情况下,程序设计者在设计一个类的时候为需要重写equals方法,比如String类,但是千万要注意,根据equals重写的要求,重写equals的同时,最好hashcode也相同。
public class Student {
private int sid; //学号
private String name; //名字
private int age; //年龄
public Student() {
}
public Student(int sid, String name, int age) {
super();
this.sid = sid;
this.name = name;
this.age = age;
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* 方式2:
*/
public boolean equals(Object obj){
if(!(obj instanceof Student)){
return false;
}
return (this.sid == ((Student)obj).sid) && ((Student)obj).name.equals(this.name);
}
@Override
public int hashCode() {
return name.hashCode()*57+sid+10;
}
}
public class TestStudent01 {
public static void main(String[] args) {
Student stu1 = new Student(101,"张三",18);
System.out.println("****stu1的内存地址:"+stu1.toString());
//键-Student类型 ,值-Integer
Map<Student,Integer> map = new HashMap<Student,Integer>();
//添加元素
map.put(stu1, Integer.valueOf(1));
//根据键获取值
System.out.println("stu1这个键对应的值是:"+map.get(stu1));
//根据键获取值 ,换一种方式 stu1和下面这句中new Student(101,"张三”,18) 不是同一个对象,不在同一块内存
//获取的是null,但是如果重写hashcode()方法,让stu1和下句中new Student()在逻辑上保持一致性,就可以获取到值1
System.out.println("根据键获取值:"+map.get(new Student(101,"张三",18)));
Student stu2 = new Student(101,"张三",18);
System.out.println("根据键获取值:"+map.get(stu2));
System.out.println("****stu2的内存地址:"+stu2.toString());
}
}
public class TestStudent02 {
public static void main(String[] args) {
Student stu1 = new Student(101,"张三",18);
System.out.println("创建后的****stu1的内存地址:"+stu1.toString());
//键-Student类型 ,值-Integer
Map<Student,Integer> map = new HashMap<Student,Integer>();
//添加元素
map.put(stu1, Integer.valueOf(1));
//根据键获取值
System.out.println("stu1这个键对应的值是:"+map.get(stu1));
//更改stu1中的属性的值 ,hashcode()方法,equals()方法依赖此属性值,所以下方获取的是null
stu1.setName("李四");
//更改stu1中的属性值 age,hashcode()方法,equals()方法不依赖此属性,所以下方不受影响,仍旧能获取到1
//stu1.setAge(39);
System.out.println("属性更改后的****stu1的内存地址:"+stu1.toString());
System.out.println("根据键获取值:"+map.get(new Student(101,"张三",18)));
Student stu2 = new Student(101,"张三",18);
System.out.println("根据键获取值:"+map.get(stu2));
System.out.println("****stu2的内存地址:"+stu2.toString());
}
}
面试题: “==”和equals的区别?
对于基本数据类型而言 ,“==”比较的是基本数据类型的值 ,对于引用数据类型而言,“==”比较的是内存地址,equals()方法是Object类提供的,作用和“==”一样,都是比较的内存地址,但是用户可以根据自己的需求来改写,比方说java.lang.String 类,就将equals()方法改写成了比较字符串内容是否相等。根据equals重写的要求,重写equals的时候,最好hashcode()也相同。
9.2 为什么使用多态?
package com.njwb.polyphism4.d0428;
/**
* 主人类 ,1个方法,宠物生病了,主人带宠物区看病,如果这个时候养了新的宠物,主人需要带猫看病
* 实现思路: 1.创建类Class cat extends Pet 2.修改主人类 添加 猫看病的方法 public void cure(Cat cat){....具体猫看病的方法} 3.测试
* 这样,会造成一个问题,频繁修改主人类,动物写不尽的,主人类非常的庞大
* 这个时候,需要用多态来解决
* 1.创建类Class cat extends Pet 2.主人类 只提供1个方法 ,父类做形参 public void cure(Pet pet){//宠物去病 } 3.修改父类,父类 提供1个空方法
* public void toHospital(){ } 4.每个子类去重写toHospital()方法,每个子类看病,看病的方式放在自己的类去实现.
* 5.测试的时候,一定 父类引用,指向子类对象 Pet p = new Dog(); 父类类型 变量名= new 子类(); 一定调用的是子类重写之后的方法,而不会调用父类的方法
* @author Administrator
*
*/
public class Host {
/**
* 狗狗看病
* @param dog
*/
public void cure(Dog dog){
if(dog.getHealth()<60){
System.out.println("狗狗看病:打针,吃药");
dog.setHealth(60);
}
}
/**
* 企鹅看病
* @param pgn
*/
public void cure(Penguin pgn){
if(pgn.getHealth()<60){
System.out.println("企鹅看病:吃药,疗养");
pgn.setHealth(60);
}
}
}
如果这个时候,再添加个XXX类,需要给XXX看病呢?频繁的修改主人类,代码可扩展性,可维护性差。使用多态优化。
9.3什么是多态?
同一个引用类型,使用不同的实例,而执行不同的操作。
多态的使用一:演示多态示例。宠物看病。
package com.njwb.polyphism4.d0428;
/**
* 主人类
* @author Administrator
*
*/
public class Master {
/**
* Pet父类做形参
* @param pet
*/
public void cure(Pet pet){
//宠物去看病
pet.toHospital();
}
}
package com.njwb.polyphism4.d0428;
public class Pet {
private String name; //名字
private int health; //健康值
private int love; //亲密度
public Pet() {
}
public Pet(String name, int health, int love) {
super();
this.name = name;
this.health = health;
this.love = love;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public int getLove() {
return love;
}
public void setLove(int love) {
this.love = love;
}
public void print(){
System.out.print("宠物的自白:\n 我叫"+this.getName()+",我的健康值是:"+this.getHealth()
+",和主人的亲密度为:"+this.getLove());
}
public void toHospital(){
}
}
package com.njwb.polyphism4.d0428;
public class Dog extends Pet {
private String strain; //品种
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public Dog() {
}
public Dog(String name, int health, int love,String strain) {
super(name,health,love);
this.strain = strain;
}
public void print(){
super.print();
System.out.println(",我是一只:"+this.getStrain());
}
/**
* 子类看病的方式放在子类里去实现,跟父类,根主人类无关
*/
public void toHospital(){
if(this.getHealth()<60){
System.out.println("狗狗看病:打针,吃药");
this.setHealth(60);
}
}
}
package com.njwb.polyphism4.d0428;
public class TestPet02 {
public static void main(String[] args) {
Pet p = new Dog("旺财",19,90,"吉娃娃");
p.print();
//实例化主人类
Master m = new Master();
m.cure(p);
System.out.println("-------------看病后------------------");
p.print();
System.out.println("===============以下是企鹅看病===================");
p = new Penguin("小花", 30, 33,"Q妹");
p.print();
m.cure(p);
System.out.println("-------------看病后------------------");
p.print();
System.out.println("===============以下是猫看病===================");
p = new Cat("小猫咪",18,39);
p.print();
m.cure(p);
System.out.println("-------------看病后------------------");
p.print();
}
}
9.4抽象类和抽象方法的使用
什么是抽象方法?
抽象方法是一种特殊的方法:它只有声明,而没有具体的实现。
抽象方法必须用abstract关键字进行修饰。
声明格式:
abstract void fun();
注意:直接在普通类中写抽象方法会报错。
什么是抽象类?
包含抽象方法的类。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。
特殊情况:如果一个类用abstract修饰,里面却没有抽象方法,也是抽象类,只不过没有意义。
为什么要用抽象类?
因为抽象类中方法不包含具体的业务代码,仅仅是方法的声明,无法实现功能,所以不能用抽象类创建对象(不能实例化抽象类)。
抽象类存在的意义?
抽象类就是为了继承而存在的,如果定义了一个抽象类,却不去继承它,那么等于白白创建了这个抽象类,因为不能用它来做任何事情。
什么时候用抽象类?
对于一个父类,如果它的某个方法在父类中实现出来没有任何意义,必须根据子类的实际需求来进行不同的实现,那么就可以将这个方法声明为abstract方法,此时这个类也就成为abstract类了。
抽象类包含什么?
抽象类不仅包含抽象方法,而且抽象类和普通类一样,可以拥有成员变量和普通的成员方法。
抽象类和普通类的区别:
语法:
普通方法
[访问修饰符] 返回值类型 方法名(参数列表){
方法体
}
抽象方法
[访问修饰符] abstract 返回值类型 方法名(参数列表);
l 抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),如果是default(缺省的默认修饰符,那么子类必须在本包内);抽象方法没有方法体;
l 抽象类不能用来创建对象;不能被实例化。
l 抽象方法必须在抽象类里(反过来抽象类里不一定有抽象方法)
l 如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为abstract类(换种说法抽象方法必须在子类中被实现,除非子类是抽象类);
l abstract修饰符不能和final修饰符一起使用
l 可以拥有普通类所拥有的一切成员(属性,方法,代码块,静态代码块等还可以拥有抽象方法)。
示例:
abstract class AbsDemo{
public abstract int calNum(int a,int b);
public abstract void showNum();
//public final abstract void showName(); // final abstract冲突 ,不能在一起使用
}
/**
* 普通的子类必须实现父类的所有抽象方法
* @author Administrator
*
*/
class SubDemo extends AbsDemo{
@Override
public int calNum(int a, int b) {
return 0;
}
@Override
public void showNum() {
}
}
/**
* 抽象的子类可以不用实现父类的所有抽象方法
* @author Administrator
*
*/
abstract class SubSecDemo extends AbsDemo{
@Override
public int calNum(int a, int b) {
return 0;
}
}
public class TestAbstractDemo {
public static void main(String[] args) {
//AbsDemo ad = new AbsDemo();
}
}
Pet类:
public abstract class Pet {
public abstract void toHospital();
}
课堂练习:
需求说明宠物饿了,主人需要为宠物喂食,使用多态实现该过程, 不同宠物吃的东西不一样,不同宠物吃完东西后恢复健康值不一样
健康值达到100时,不需要继续喂食
狗狗:健康值增加3 企鹅:健康值增加5
Master 类 public void feed(Pet pet){ //宠物吃饭 pet.toEat()} ; Pet父类: public abstract void toEat(); 具体的喂食的方法放在具体的子类里去实现 。
Dog类 public void toEat(){ this.getHealth>=100 //打印狗狗吃饱了,不需要再喂了。如果健康康<100, 狗狗吃饱了,健康值增3 this.setHealth(this.getHealth+3);}
测试类 Pet p = new Dog(“旺财”,10,29,”吉娃娃”);
Dog类中:
@Override
public void toEat() {
if(this.getHealth()>=100){
System.out.println("狗狗吃饱了,不需要再喂了");
}else{
//判断目前的健康值是否大于97 大于97,健康值不能加3
int liang = this.getHealth()>97?(100-this.getHealth()):3;
System.out.println("狗狗吃饱了,健康值加"+liang);
this.setHealth(this.getHealth()+liang);
}
}
测试类中:
public static void main(String[] args) {
//父类引用指向子类对象
Pet p = new Dog("旺财",87,80,"吉娃娃");
p.print();
//实例化主人类
Master m = new Master();
m.feed(p);
System.out.println("-------------喂食后-----------------");
p.print();
}
9.5 向上转型
向上转型:父类引用指向子类对象,自动进行类型转换,
Pet p = new Dog("旺财",87,80,"吉娃娃");
注意:
<父类型> <引用变量名> = new <子类型>();
n 此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法(调用是子类重写之后的方法),不是父类的方法,。
n 此时通过父类引用变量无法调用子类特有的方法 ,该如何解决? 只能向下转型
动态绑定(dynamic binding)又名动态链接:父类引用指向子类对象,调用方法时会调用子类的实现,而不是父类的实现。
9.6 向下转型
向下转型:将一个指向子类对象的父类引用赋给一个子类的引用,即:父类类型转换为子类类型。需强制类型转换。
Pet p = new Dog(“旺财”,87,80,”吉娃娃”);
Dog dog = (Dog) p;
dog.catchFlyingDisc();
注意:
<子类型> <引用变量名> = (<子类型> )<父类型的引用变量>;
在向下转型的过程中,如果没有转换为真实子类类型,会出现类型转换异常
9.7 instanceof
如何减少在向下转型的过程中,没有转换为真实子类类型的类型转换异常?
//这个时候,需要先判断p实际是什么类型,就转化成对应的子类类型
if(p instanceof Dog){
Dog d = (Dog)p;
d.catchFlyingDisc();
}else if(p instanceof Penguin){
Penguin pgn = (Penguin)p;
pgn.swim();
}
使用instanceof时,对象的类型必须和instanceof后面的参数所指定的类在继承上有上下级关系
对象A(引用数据类型) instanceof 类型(可以是类,接口,可以是对应的包装类)
Object num = Integer.valueOf(12);
if(num instanceof Integer){
System.out.println("num是Integer这种类型的");
}else if(num instanceof Double){
System.out.println("num是Double这种类型的");
}else if(num instanceof Short){
System.out.println("num是Short这种类型的");
}
9.8 多态的应用
9.8.1 使用父类作为方法的形参
public void cure(Pet pet){
//宠物去看病
pet.toHospital();
}
public void feed(Pet pet){
//宠物吃饭
pet.toEat();
}
9.8.2 使用父类作为方法的返回值
/**
* 主人类
* @author Administrator
*
*/
public class Host {
/**
* 捐赠动物 ,父类做返回值
* @param type 动物的类型
* @return
*/
public Animal donate(String type){
Animal an = null;
if(type.equals("鸭子")){
an = new Duck();
}else if(type.equals("狗狗")){
an = new Dog();
}else if(type.equals("小猫")){
an = new Cat();
}
return an;
}
}
/**
* 抽象父类
* @author Administrator
*
*/
abstract class Animal{
public abstract void cry();
}
class Cat extends Animal{
@Override
public void cry() {
System.out.println("小猫叫:喵喵喵");
}
}
class Dog extends Animal{
@Override
public void cry() {
System.out.println("小狗叫:汪汪汪");
}
}
class Duck extends Animal{
@Override
public void cry() {
System.out.println("小鸭子叫:嘎嘎嘎");
}
}
public class TestAnimal {
public static void main(String[] args) {
//实例化主人类
Host host = new Host();
//想要鸭子
Animal an = host.donate("鸭子");
//获取鸭子后,控制鸭子叫
an.cry();
}
}
课堂练习:
自定义类和方法,使用父类作为返回值实现打印不同类型商品价格功能
父类:Goods(商品类)
子类:TVs (电视类),Foods(食品类)
分析: 抽象父类 abstract class Goods{
Public abstract void printPrice(); //打印价格
}
子类 class TVs extends Goods{
Public void printPrice(){
//打印电视机价格
}
}
商品哪里来的?工厂生产出来的。
Class Factory{
//根据传入的参数,确定具体的商品对象
Public Goods generateGoods(String type){
Goods goods = null;
//多重if
Return goods;
}
}
测试类:
abstract class Goods{
public abstract void printPrice();
}
class TVs extends Goods{
@Override
public void printPrice() {
System.out.println("打印电视机价格");
}
}
class Foods extends Goods{
@Override
public void printPrice() {
System.out.println("打印食品价格");
}
}
public class TestGoods {
public static void main(String[] args) {
//实例工厂类
Factory fac = new Factory();
//想打印电视机价格
Goods goods = fac.generateGoods("TV");
//调用方法打印
goods.printPrice();
//打印食品价格
goods = fac.generateGoods("食品");
goods.printPrice();
}
}
public class Factory {
/**
* 父类做返回值
* @param type
* @return
*/
public Goods generateGoods(String type){
Goods goods = null;
if(type.equals("TV")){
goods = new TVs();
}else if(type.equals("食品")){
goods = new Foods();
}
return goods;
}
}
9.9面向对象的三大特性总结
l 封装隐藏了类的内部实现机制,可以在不影响使用者的前提下修改类的内部结构,同时保护了数据;
l 继承是为了重用父类代码,子类继承父类就拥有了父类的成员。
l 多态允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。
9.10多态存在的三个必要条件
l 要有继承;
l 要有重写;
l 父类引用指向子类对象。
9.11多态的好处
l 可替换性。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
l 可扩充性。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
l 接口性。多态是父类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
l 灵活性。它在应用中体现了灵活多样的操作,提高了使用效率。
l 简化性。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
9.12多态总结
l 使用父类类型的引用指向子类的对象;
l 该引用只能调用父类中定义的方法和变量;
l 如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;(动态连接、动态调用)
l 变量不能被重写(覆盖),“重写”的概念只针对方法,如果在子类中“重写”了父类中的变量,那么在编译时会报错。
9.13 动态绑定 静态绑定
/**
* 动态绑定 :多态 父类引用指向子类对象,调用的一定是子类重写之后的方法(普通方法)
* 静态绑定: 普通变量 ,静态变量,静态方法 调用的方法由变量p前面的父类类型决定,调用的是父类中的方法,访问的变量的值也是有变量p前面的类型决定的
* ,访问父类中的变量的值。
* @author Administrator
*
*/
class Person{
String name= "person";
int num = 10;
static int num2 = 20;
public void shout(){
System.out.println(name);
}
public static void say(){
System.out.println("我是一个人类 ");
}
}
class Teacher extends Person{
String name = "teacher"; //定义了一个和父类中同名的属性 ,现在Teacher类而言,2个name属性,一个是从父类继承的,一个是自身的
int num = 15;
static int num2 = 25;
public void ceshi(){
System.out.println("本类中的name="+this.name+",从父类继承的name="+super.name);
}
public void shout(){
System.out.println(name);
}
public static void say(){
System.out.println("我是一个教师");
}
}
public class TestPerson {
public static void main(String[] args) {
Person p = new Teacher();
p.say();
System.out.println("父类引用指向子类对象 普通变量的值:"+p.num+",父类引用指向子类对象 静态变量的值:"+p.num2);
//p.shout();
/*Teacher t = new Teacher();
t.ceshi();*/
}
}
9.14作业
1. 抽取用户管理系统和图书管理系统中公共方法到抽象类,并继承。
见《继承和多态》上机实践2
见 《多态》
商品管理系统
//需要哪些类
Goods 商品类
goodsPrice价格
kucun 库存
type 类别
remainingDays 保质期
抽象类 定义几个抽象方法 Manager
Public abstract void showAllGoods(); //显示所有商品 ,支持分类显示
类别编号 类别 名称
1 奶制品
2 零食
3 肉制品
4 水果类
5 所有类别
请选择要显示的类别编号: 1
“奶制品”类别下的所有商品如下:
商品编号 商品名称 单价 库存
1 旺仔牛奶 3.3 200
2 蒙牛牛奶 5.6 300
3 伊利牛奶 2.3 200
Public abstract void addGoods(); //添加商品 需要重名验证 ,类别应该选(显示类别列表,让用户选择类别编号,不要让用户输入类别)
Public abstract void updateGoods(); //根据商品名称 修改商品的信息(价格,库存)
Public abstact void delGoods(); //删除商品 //根据商品名称 提示是否删除成功
Public abstract void buyGoods(); // 先显示商品列表,然后用户输入商品编号,进行购买 买东西的时候,记得减少库存 ,验证 超出库存的,要提示用户无法购买
商品编号 商品名称 单价 库存 类别
1 康师傅 2.8 100 零食
2 瓜子 6.7 1003 零食
3 旺仔牛奶 3.4 200 奶制品
请输入购买编号: (编号只能在1-3范围内,如果输入错误,提示用户输入范围不正确,请重输)
Public abstract void updateReamingDays(String typeName) ;// 针对某一个类别 ,调用一次,该类别下的所有商品保质期都要减少1天
Public abstract void checkRemaingDays() ;//模拟的假方法,在该方法里调用10次减少保质期的方法,看是否有商品过期,如果有的话,删除该商品
抽象类的实现类 GoodsManager (业务类)
无参构造里 初始化数据 至少初始化5条商品信息
程序的入口 GoodsManagerSystem
工具类 NumsUtil 定义几个方法,用于接收正整数 ,正的浮点数
GoodsManagerSystem包括main方法,用来调用
GoodsManager商品管理类,实现增删改查,继承Manager抽象类
Goods商品类,private通过get和set访问
需求:
(1) 能够显示出所有的商品。
(2) 能够添加商品
(3) 能够根据商品的名称,编辑商品的价格、库存
(4) 能够删除商品
(5) 购买商品,先输出名称,让用户挑选。
(6) 买完商品,能够自动减少库存,如果库存为0,用户购买时看不到商品。
需求part2:
(1)系统启动时,自带了5个商品。
(2)添加商品时,如果商品名称重复,提示用户商品名称重复,要求用户重新输入商品名称。
(3)编辑商品时,如果用户输入的价格或库存不正确,如价格是汉字或负值,要提示用户输入错误重新输入。
(4)删除商品时,要提示删除成功。如果用户要删除的商品不存在数组中,提示删除失败。
(5)购买商品时,输出的商品列表,每个商品左侧要有数字表示商品的编号,用户可以输入一个数字来选择商品,而不用输入商品的名称。如果输入的数字不在范围内,提示输入不正确重新输入。
(6)购买商品时,用户输入的购买数量如果大于库存,要给出提示。
需求part3:
(1) 添加商品时,增加一个类型属性,用于标记该商品的分类,如“生活用品”、“食物”等。
(2) 编辑时,允许编辑类型属性。
(3) 除了支持直接输出所有商品的列表,还支持根据类型输出商品,进入该功能时,输出系统中所有的商品类型,让用户输入商品类型,根据商品类型输出相应的商品。
(4) 新增保质期属性RemainingDays(剩余天数),该属性保存商品保质期剩余天数,如:6。
(5) 新编写一个方法,叫做updateRemainingDays。该方法每执行一次,所有商品的保质期减1天。如果执行后有商品到期,在控制台输出到期的商品列表,并从数组删除到期的商品。