Java 面向对象 day4
Java 面向对象 day4
面向对象特征之二: 继承
一、定义
将一系列类中共有的属性和方法提取出来形成一个新的类,其他类去继承该类.新的类称之为父类(基类,超类),其他类称之为子类(派生类),在Java中一个类只能继承 一个父类,不能继承多个父类,子类继承父类,子类拥有父类所有非私有的属性和方法。
为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
二、特征
1. 只支持单继承,即一个子类只允许有一个父类。2. 子类可以拥有父类的属性和方法
3. 子类可以拥有自己的属性和方法
4. 子类可以重写覆盖父类的方法
5. 可以声明父类,创建子类
6. 继承具有传递性
7. 父类的构造方法不能被继承
三、优点
1. 继承的出现提高了代码的复用性。2. 父类的属性方法可以用于子类。
3. 可以轻松的定义子类。
4. 继承的出现让类与类之间产生了关系,提供了多态的前提。
5. 不要仅为了获取其他类中某个功能而去继承。
四、基本使用
Person.java
/**
* 将子类中共有的属性和方法提取出来形成一个新的类
*/
public class Person {
private int id;
private String name;
private String sex;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat(String food) {
System.out.println(name + "吃了一顿" + food);
}
public void sleep(String address) {
}
}
Student .java
/**
* 子类继承父类:关键字使用extends
* 可以拥有父类中非私有的属性和方法
*/
public class Student extends Person {
private String grade;
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
//读书的方法
public void read() {
}
}
Teacher .java
public class Teacher extends Person {
private String level; //级别
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public void teach() {
}
}
TestExtends .java
public class TestExtends {
public static void main(String[] args) {
Student stu = new Student();
stu.setId(1001); //stu可以引用出父类中的方法
stu.setName("张三");
stu.setAge(20);
stu.setSex("男");
stu.setGrade("大二"); //子类中特有的属性
stu.eat("大闸蟹");
Teacher t = new Teacher();
t.setId(1002);
t.setName("李四");
t.setAge(19);
t.setSex("男");
t.setLevel("厨师");
t.eat("霸王餐");
//小的数据类型自动转换为大的数据类型
Person per = new Student(); //当父类引用指向子类对象时,因为类型是父类,所以只能使用父类与子类共有的属性和方法
per.setId(1005);
}
/*
* 运行结果:
* 张三吃了一顿大闸蟹
* 李四吃了一顿霸王餐
*/
}
super() 的使用
一、super()的使用实例 一一 子类重写父类的方法
在Java中super指代父类对象(直接父类),也就是说,super相当于是一个直接new出来的父类对象,所以可以通过它来调用父类的那些非private修饰的变量、方法(对于我们普通new出来的对象来说,也就只能访问那些非private的成员变量、方法了,这里的访问是指通过“对象名.变量名或方法名”的形式)。所以,super这个对象也就是一个普通对象,同样遵循访问控制修饰符的准则。
然而,对于子类来说,子类通过继承就直接拥有了父类的非private变量、方法,也就可以在子类中直接使用,再加一个super来修饰,岂不是显得有点多余了?正常情况下来说,是有点多余了(但是可以明确提示我们这是调用的父类变量或方法),但super关键字主要是用在以下两种情况中:
(1)发生了重写的情况
重写也分为两种情况,一个是重写了父类的方法;一个是重写了父类的成员变量;
重写父类方法的情况:
public class A {
String name = "lly";
protected void getName(){
System.out.println("父类getName->"+ name);
}
}
public class B extends A {
String nameB = "llyB";
@Override
protected void getName() {
System.out.println("子类getName->"+nameB);
super.getName();
}
public static void main(String[] args) {
B b = new B();
b.getName();
}
}
打印如下:
子类getName->llyB
父类getName->lly
在子类B中,我们重写了父类的getName方法,如果在重写的getName方法中我们去调用了父类的相同方法,必须要通过super关键字显示的指明出来。
如果不明确出来,按照子类优先的原则,相当于还是再调用重写的getName()方法,此时就形成了死循环,执行后会报java.lang.StackOverflowError异常。
二、super()的使用实例 一一 子类重写父类的变量
重写父类变量的情况:
我们将B类简单改造一下:
public class B extends A {
String name = "llyB";
@Override
protected void getName() {
name = super.name;
System.out.println("子类getName->"+name);
}
public static void main(String[] args) {
B b = new B();
b.getName();
}
}
打印如下:
子类getName->lly
此时子类B中有一个和父类一样的字段(也可以说成父类字段被隐藏了),为了获得父类的这个字段我们就必须加上super,如果没有加,直接写成name = name;不会报错,只是会警告,表示此条语句没有任何意义,因为此时都是访问的子类B里面的那么字段。
我们通过super是不能访问父类private修饰的变量和方法的,因为这个只属于父类的内部成员,一个对象是不能访问它的private成员的。
(2)在子类的构造方法中
编译器会自动在子类构造函数的第一句加上 super(); 来调用父类的无参构造器;此时可以省略不写。如果想写上的话必须在子类构造函数的第一句,可以通过super来调用父类其他重载的构造方法,只要相应的把参数传过去就好。
因此,super的作用主要在下面三种情况下:
1、调用父类被子类重写的方法;
2、调用父类被子类重定义的字段(被隐藏的成员变量);
3、调用父类的构造方法;
其他情况,由于子类自动继承了父类相应属性方法,关键字super可以不显示写出来。
方法的重写
一、定义
在子类中可以根据需要对从父类中继承来的方法进行改造,也称方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
1.重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型。
2.重写方法不能使用比被重写方法更严格的访问权限。
3.重写和被重写的方法须同时为static的,或同时为非static的
4.子类方法抛出的异常不能大于父类被重写方法的异常
二、应用 一一 实现宠物喂养
Animal .java
public class Animal {
private String name = "无名氏";
private int health = 80; //健康值
private int love = 20;
public Animal() {
this.health = 80;
}
public Animal(String name) {
this.name = name;
}
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.println("宠物的自白:\n我的名字叫:" + this.name + ",我的健康值为:" + this.health + " ,我和主人的亲密度是:" + this.love + "!");
}
/*
* 喂食的方法
* 根据吃的东西不同,增加的健康值和亲密度
*/
public void eat(String food) {
}
}
Dog .java
public class Dog extends Animal {
private String strain; //品种
public Dog() {
//super(); 调用父类构造方法,用于实例化
//在构造方法中使用super 必须放在第一行
super();
}
//子类构造方法 方法形参个数为 子类参数+父类参数
public Dog(String name, String strain) {
super(name); //也可以同时调用父类带参构造函数,初始化数据
this.strain = strain;
}
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public void show() {
System.out.println("调用父类属性和方法");
super.getHealth();
super.print();
}
@Override
public void print() {
super.print();
System.out.println("我是一只" + this.getStrain() + "犬..");
}
@Override
public void eat(String food) {
if (getHealth() >= 100) {
System.out.println("狗仔已经很健康了,不需要喂食...");
} else {
System.out.println("狗狗吃了一顿" + food + ",健康值增加:5,亲密度增加2");
setHealth(getHealth() + 5);
setLove(getLove() + 2);
}
}
}
Penguin .java
public class Penguin extends Animal {
private String sex;
public Penguin() {
}
public Penguin(String name, String sex) {
super(name);
this.sex = sex;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override //重写注解, 规定该方法只能是重写父类中的方法
public void print() {
super.print();
System.out.println("我的性别是:" + this.getSex() + ".");
}
@Override
public void eat(String food) {
if (getHealth() >= 100) {
System.out.println(getName() + "已经很健康了,不需要喂食...");
} else {
System.out.println(getName() + "吃了一顿" + food + ",健康值增加:3,亲密度增加1");
setHealth(getHealth() + 3);
setLove(getLove() + 1);
}
}
}
TestAnimal .java
public class TestAnimal {
public static void main(String[] args) {
Animal a = new Animal("保卫");
a.print();
Dog dog = new Dog("欧欧", "拉布拉多");
dog.print();
dog.eat("肉骨头");
Penguin pen = new Penguin("花花", "Q妹");
pen.print();
pen.eat("北极熊");
}
}
运行结果:
宠物的自白:
我的名字叫:保卫,我的健康值为:80 ,我和主人的亲密度是:20!
宠物的自白:
我的名字叫:欧欧,我的健康值为:80 ,我和主人的亲密度是:20!
我是一只拉布拉多犬…
狗狗吃了一顿肉骨头,健康值增加:5,亲密度增加2
宠物的自白:
我的名字叫:花花,我的健康值为:80 ,我和主人的亲密度是:20!
我的性别是:Q妹.
花花吃了一顿北极熊,健康值增加:3,亲密度增加1
抽象类和抽象方法
一、定义
抽象(abstract):在程序的提取过程中,父类的概念会越来越模糊化,一般通过为父类设置 一个关键字.使父类成为一个抽象类.这就是抽象。
抽象类:使用abstract关键字修饰的类叫做抽象类。
抽象方法:使用abstract关键字修饰的方法叫做抽象方法,且该方法没有方法体。
抽象类与抽象方法的关系:
抽象方法只能存在于抽象类,但是抽象类中不一定有抽象方法。
子类继承父类时,子类必须实现父类中所有的抽象方法,除非子类也是一个抽象类,但是最终的子类也必须实现(重写)所有抽象方法。
二、应用 一一 学生和教师
抽象父类
/**
* 使用abstract修饰的类叫做抽象类 一般都与父类设置抽象类
*/
public abstract class Person {
private int id;
private String name;
private String sex;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat(String food) {
System.out.println(name + "吃了一顿" + food);
}
//所有需要重写才使用的方法,一般都设置为抽象方法
//有抽象方法的类,一定是抽象类
public abstract void sleep(String address);
}
子类1-学生
/*
* 子类继承父类:关键字使用extends 可以拥有父类中非私有的属性和方法
*/
public class Student extends Person {
private String grade;
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
@Override
public void eat(String food) {
System.out.println(grade + "学生" + getName() + ",正在食堂吃" + food);
}
//读书的方法
public void read() {
System.out.println("学生" + getName() + "在" + grade + "年级上课....");
}
/*
* 子类继承抽象父类,必须实现父类中所有的抽象方法
*/
@Override
public void sleep(String address) {
System.out.println("学生" + getName() + ",回到" + address + "进行午休.....");
}
}
子类2-教师
public class Teacher extends Person {
private String level; //级别
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public void teach(String course) {
System.out.println(level + getName() + "正在上" + course + "课...");
}
@Override
public void sleep(String address) {
System.out.println(level + getName() + "正在" + address + "中睡午觉.....");
}
}
测试类
public class TestPerson {
public static void main(String[] args) {
/**
* 抽象类不可以被实例化,只能被继承
*/
// Person person = new Person();
Teacher t = new Teacher();
t.setId(6001);
t.setName("谭哥");
t.setLevel("高级讲师");
t.teach("java");
t.eat("澳洲龙虾");
t.sleep("办公室");
Student stu = new Student();
stu.setId(1001);
stu.setName("张三");
stu.setGrade("大四");
stu.read();
stu.eat("波士顿龙虾");
stu.sleep("宿舍");
}
}
运行结果:
高级讲师谭哥正在上java课…
谭哥吃了一顿澳洲龙虾
高级讲师谭哥正在办公室中睡午觉…
学生张三在大四年级上课…
大四学生张三,正在食堂吃波士顿龙虾
学生张三,回到宿舍进行午休…
面向对象特征之三: 多态
一、定义
同一指令作用于不同对象,产生不同反应或输出不同效果. 相同的指令被不同对象执行,产生不同的效果。
多态的好处:
1.提高系统的扩展性
2.降低系统的耦合性
多态的实现方式:
1.以方法的形式实现多态
2.以对象的形式实现多态
多态性,是面向对象中最重要的概念,在java中有两种体现:
1.方法的重载**(overload)和重写(overwrite)**。
2.对象的多态性 —— 可以直接应用在抽象类和接口上。
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
若编译时类型和运行时类型不一致,就出现多态(Polymorphism)
对象的多态 —在Java中,子类的对象可以替代父类的对象使用
- 一个变量只能有一种确定的数据类型
- 一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student();
m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”;//非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。
二、多态的应用
1.父类引用指向子类对象 一一 向上转型
向上转型:它表示我定义了一个Person类型的引用,指向新建的Student类型的对象。由于Student是继承自它的父类Person,所以Person类型的引用是可以指向Student类型的对象的。这就是“向上转型”。
宠物类
public class Pet {
private String name = "无名氏";
private int health = 80;
private int love = 20;
public Pet() {
this.health = 80;
}
public Pet(String name) {
this.name = name;
}
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 eat(String food) {
System.out.println("宠物吃了一顿"+food);
}
}
宠物主人类
import com.gec.polymorphism.Pet;
public class Master {
private String name;
private int money;
public Master() {
}
public Master(String name, int money) {
this.name = name;
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
/**
* //给宠物喂食
* 未使用多态
* public void feed(Dog dog) { dog.eat("狗肉"); }
* public void feed(Penguin pen) { pen.eat("里脊肉"); }
*/
//通过多态的形式,喂食
//子类对象转换为父类引用,这种方式是多态的向上转型
public void feed(Pet pet, String food) {
pet.eat(food);
}
}
狗类
import com.gec.polymorphism.Pet;
public class Dog extends Pet {
private String strain;
public Dog() {
super();
}
public Dog(String name, String strain) {
super(name);
this.strain = strain;
}
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
@Override
public void print() {
super.print();
System.out.println("我是一只"+this.getStrain()+"犬..");
}
@Override
public void eat(String food) {
if(getHealth()>=100) {
System.out.println("狗仔已经很健康了,不需要喂食...");
}else {
System.out.println("狗狗吃了一顿"+food+",健康值增加:5,亲密度增加2");
setHealth(getHealth()+5);
setLove(getLove()+2);
}
}
}
测试类
import com.gec.polymorphism.Master;
public class TestPet {
public static void main(String[] args) {
Master m = new Master("大王", 200000);
Dog dog = new Dog("欧欧", "金毛");
dog.setHealth(60);
m.feed(dog, "两斤大骨头");
}
}
运行结果:
狗狗吃了一顿两斤大骨头,健康值增加:5,亲密度增加2
2.子类引用指向父类对象 一一 向下转型
向下转型 :它表示在父类引用中无法调用子类中特有的属性和方法,必须将其还原为子类引用才可以使用子类中特有的属性和方法,这种转换就叫做向下转型。
宠物类
public class Pet {
private String name = "无名氏";
private int health = 80;
private int love = 20;
public Pet() {
this.health = 80;
}
public Pet(String name) {
this.name = name;
}
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 class Master {
private String name;
private int money;
public Master() {
}
public Master(String name, int money) {
this.name = name;
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public void play(Pet pet) {
//父类引用已经无法调用子类中特有的属性和方法,必须将对象还原为子类引用,这种转换方式为多态的向下转型
//父类引用中的对象只有1个不能同时转换为两种对象
//使用instanceof 判断具体对象是否属于某个类型 如果属于返回true
if (pet instanceof Dog) {
Dog dog = (Dog) pet;
dog.jieUFO();
} else if (pet instanceof Penguin) {
Penguin pen = (Penguin) pet;
pen.swimming();
}
}
}
狗类
public class Dog extends Pet {
private String strain;
public Dog() {
super();
}
public Dog(String name, String strain) {
super(name);
this.strain = strain;
}
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public void jieUFO() {
if(getHealth()>=20) {
System.out.println(getName()+"与主人一起玩飞碟,健康值减10,亲密度加15");
setHealth(getHealth()-10);
setLove(getLove()+15);
}else {
System.out.println(getName()+"已经饿得不行了,不能陪主人接飞碟了...");
}
}
}
企鹅类
public class Penguin extends Pet {
private String sex;
public Penguin() {
}
public Penguin(String name, String sex) {
super(name);
this.sex = sex;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void swimming() {
if (getHealth() >= 20) {
System.out.println(getName() + getSex() + "与主人一起游泳,健康值减5,亲密度增加10");
setHealth(getHealth() - 5);
setLove(getLove() + 10);
} else {
System.out.println(getName() + getSex() + "已经饿得不行了,不能陪主人游泳了....");
}
}
}
测试类类
public class TestPet {
public static void main(String[] args) {
Master m = new Master("大王", 200000);
Dog dog = new Dog("欧欧", "金毛");
m.play(dog);
Penguin penguin = new Penguin("花花","Q妹");
m.play(penguin);
}
}
运行结果:
欧欧的狗狗与主人一起玩飞碟,健康值减10,亲密度加15
花花Q妹与主人一起游泳,健康值减5,亲密度增加10