第十三章 抽象类和接口
1.作业回顾
1,编写一个Person类,包括属性(name、age),有参构造器、方法say(返回自我介绍的字符串)。
2.编写一个Student类,继承Person类,增加sno、score属性,以及有参构造器,在子类中调用父类的构造器。
3.编写一个方法showInfo(返回自我介绍的字符串)。
1.编写一个Person类,包括属性(name、age),有参构造器、方法say(返回自我介绍的字符串)。
public class Person {
//属性(name、age)
private String name;
private int age;
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;
}
//有参构造器
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
//方法say(返回自我介绍的字符串)
public String say() {
return "我叫" + name + ",我今年:" + age + "岁了";
}
}
//2.编写一个Student类,继承Person类,增加sno、score属性,以及有参构造器,在子类中调用父类的构造器。
public class Student extends Person {
//增加sno、score属性
private int sno;
private int score;
public int getSno() {
return sno;
}
public void setSno(int sno) {
this.sno = sno;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
//有参构造器
public Student(String name, int age, int sno, int score) {
super(name, age);
this.sno = sno;
this.score = score;
}
//3.编写一个方法showInfo(返回自我介绍的字符串)。
public String showInfo() {
return super.say() + "我的学号是" + sno + "我的分数是" + score;
}
}
public class Day128 {
//1.编写一个Person类,包括属性(name、age),有参构造器、方法say(返回自我介绍的字符串)。
//2.编写一个Student类,继承Person类,增加sno、score属性,以及有参构造器,在子类中调用父类的构造器。
//3.编写一个方法showInfo(返回自我介绍的字符串)。
public static void main(String[] args) {
Student s = new Student("张三", 20, 20191101, 100);
System.out.println(s.showInfo());
}//我叫张三,我今年:20岁了我的学号是20191101我的分数是100
}
2.继承规则二
子类并不会继承超类的private成员,但是子类可以调用超类的public,protected,default方法来访问超类的私有字段。
public class Animal {
private String name;
public Animal(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Dog extends Animal{
private int age;
public Dog(String name, int age) {
super(name);
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void say() {
System.out.println("我叫" + getName() + "我今年" + age + "岁了");
}
}
public class Day1302 {
public static void main(String[] args) {
Dog d = new Dog("小狗1", 10);
d.say(); //我叫小狗1我今年10岁了
}
}
父类型的变量可以引用子类型的对象
import java.util.Date;
class Animal {
}
class Pet extends Animal {
}
class Dog extends Pet {
}
public class Day1303 {
public static void main(String[] args) {
//超类型的变量可以引用子类型的对象
Animal a1 = new Pet();
Animal a2 = new Dog();
Pet a3 = new Dog();
//由于Object类是所有类型的的超类,因此Object类的变量可以引用任何类的实例
Object o1 = new Animal();
Object o2 = new String();
Object o3 = new Date();
}
}
java中不支持多继承,即只能继承一个类。但是可以使用继承链。
class Dog extends Animal,Pet { //编译错误
}
3.方法重写
子类可以创建和超类拥有同样签名(方法名和参数列表)的方法,这叫做方法重写。
class Animal {
public void bark() {
System.out.println("不知道");
}
}
class Cat extends Animal {
public void bark() {
System.out.println("喵喵喵");
}
}
public class Day1304 {
public static void main(String[] args) {
Cat c = new Cat();
c.bark();
}
}
方法重写的规则
1.子类重写的方法不能比超类有更小的访问权限。
2.被标记为final的方法不能被重写。
3.在重写的方法中可以使用super关键字来调用其超类的方法。
4.重写方法的返回值类型不能比父类的大。
5.子类只能重写它可见的方法。 static成员不考虑继承问题,因此不能被重写。
4.多态
多态是指:执行同一个方法却产生不同的行为。 多态的前提是继承和方法重写。
多态:执行哪个方法看的是引用的对象的类型,而不是变量的类型。
class Animal {
public void bark() {
System.out.println("不知道");
}
}
class Dog extends Animal {
public void bark() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal {
public void bark() {
System.out.println("喵喵喵");
}
}
public class Day1305 {
public static void main(String[] args) {
//执行同一个方法却产生不同的行为
//a1引用的是Animal类型的对象,因此会调用Animal类的bark方法,运行时看对象的类型
Animal a1 = new Animal();
a1.bark(); //不知道
//a2引用的是Dog类型的对象,因此会调用Dog类的bark方法,运行时看对象的类型
Animal a2 = new Dog();
a2.bark(); //汪汪汪
//a3引用的是Cat类型的对象,因此会调用Cat类的bark方法,运行时看对象的类型
Animal a3 = new Cat();
a3.bark(); //喵喵喵
//执行同一个方法却产生不同的行为
Animal[] arr = { new Animal(), new Dog(), new Cat()};
//调用实际引用的对象的方法
arr[0].bark(); //不知道
arr[1].bark(); //汪汪汪
arr[2].bark(); //喵喵喵
}
}
class Animal {
public void bark() {
System.out.println("不知道");
}
}
class Dog extends Animal {
public void bark() {
System.out.println("汪汪汪");
}
public void run() {
System.out.println("run");
}
}
public class Day1306 {
public static void main(String[] args) {
// 变量的类型是Animal,对象的类型是Dog
Animal a1 = new Dog();
// 运行时看对象的类型
a1.bark(); // 汪汪汪
// 编译时看变量的类型,只能看到Animal类的方法,看不到实际引用对象的方法
// a1.run(); //编译错误
// 变量类型是Animal,它只能调用bark方法,但却不能调用run方法。
// 可以使用强制类型转换将变量a1的变量类型转换为Dog,这样就能调用run方法。
Dog a2 = (Dog) a1;
a2.run(); // 正确
Animal a3 = new Animal();
// 可以使用强制类型转换将a3的变量类型转换为Dog
// 但是a3,a4实际引用的是Animal类型的对象,而此对象没有run方法。
Dog a4 = (Dog) a3;
// a4.run(); //运行时错误,没有run方法。编译时通过
}
}
5.final修饰符
被final修饰的类不能被继承。
被final修饰的方法不能被重写。 被final修饰的变量不能被修改。
被final修饰的参数不能被修改。
6.抽象类
图形对象都具有某些属性(位置)和方法(moveTo,draw)。
对于所有的图形,这些属性和方法中的一些是相同(位置,moveTo)。draw有些特殊。每个子类的draw方法提供的功能不同。
可以在父类中提供一个draw方法,在 子类中使用方法重写来完成特定的draw方法。
class GraphicObject {
// x,y代表图形的位置
protected int x;
protected int y;
public GraphicObject(int x, int y) {
super();
this.x = x;
this.y = y;
}
// 移动到(x,y)点上
void moveTo(int x, int y) {
this.x = x;
this.y = y;
}
void shoeLocation() {
System.out.println("x = " + x + ", y = " + y);
}
void draw() {
// 不确定要画什么,无法实现
}
}
class Circle extends GraphicObject {
// 构造器
Circle(int x, int y) {
super(x, y);
}
public void draw() {
System.out.println("画一个圆[" + x + ", " + y + "]");
}
}
class Rectangle extends GraphicObject {
// 构造器
Rectangle(int x, int y) {
super(x, y);
}
public void draw() {
System.out.println("画一个矩形[" + x + ", " + y + "]");
}
}
public class Day1307 {
public static void main(String[] args) {
//GraphicObject应该是抽象的,不应该有具体的对象
GraphicObject g = new GraphicObject(0, 0);
Circle c = new Circle(0, 2);
c.shoeLocation(); //x = 0, y = 2
c.draw(); //画一个圆[0, 2]
Rectangle r = new Rectangle(5, 6);
r.shoeLocation(); //x = 5, y = 6
r.draw(); //画一个矩形[5, 6]
}
}
上面的例子中存在两个问题: GraphicObject类的draw方法内部是空的,此方法应该在,因为所有图形都应该有draw方法。但是对于基类GraphicObject而言它又不知道如何实现该方法。
GraphicObject(图形)应该是抽象的,不应该有具体的对象。 对于这样的情况可以使用抽象类来实现更加优雅。
//有抽象方法的类必须声明为抽象类,因此声明GraphicObject需要加关键字abstract
//抽象类不能被实例化
abstract class GraphicObject {
// x,y代表图形的位置
protected int x;
protected int y;
public GraphicObject(int x, int y) {
super();
this.x = x;
this.y = y;
}
// 移动到(x,y)点上
void moveTo(int x, int y) {
this.x = x;
this.y = y;
}
void shoeLocation() {
System.out.println("x = " + x + ", y = " + y);
}
//抽象方法,没有方法体
//声明为抽象方法,因为不知道如何实现它
//如果父类声明了抽象方法,那么子类就必须要实现它,因此这个方法对所有的子类进行了约束
abstract void draw();
}
class Circle extends GraphicObject {
// 构造器
Circle(int x, int y) {
super(x, y);
}
//子类必须实现实现父类的抽象方法,否则这个类必须声明为抽象类
public void draw() {
System.out.println("画一个圆[" + x + ", " + y + "]");
}
}
class Rectangle extends GraphicObject {
// 构造器
Rectangle(int x, int y) {
super(x, y);
}
//子类必须实现实现父类的抽象方法,否则这个类必须声明为抽象类
public void draw() {
System.out.println("画一个矩形[" + x + ", " + y + "]");
}
}
public class Day1308 {
public static void main(String[] args) {
//GraphicObject应该是抽象的,不应该有具体的对象
//抽象类不能创建对象
// GraphicObject g = new GraphicObject(0, 0);
Circle c = new Circle(0, 2);
c.shoeLocation(); //x = 0, y = 2
c.draw(); //画一个圆[0, 2]
Rectangle r = new Rectangle(5, 6);
r.shoeLocation(); //x = 5, y = 6
r.draw(); //画一个矩形[5, 6]
}
}
抽象类使用abstract来声明,它可以包含抽象方法,也可以不包含抽象方法。
有时候声明为抽象类,但又不包含抽象方法,仅仅是为了避免被实例化。
抽象类无法被实例化,但是可以被继承。
被abstract修饰的方法称为抽象方法,抽象方法只有声明,没有实现。 如果一个类包含了抽象方法,那它必须被声明为一个抽象类。
如果一个类继承了抽象类,它通常需要实现抽象类的抽象方法,如果没有实现,那这个子类也必须被声明为抽象的。
抽象类天生就是用来被继承的。抽象类实现了它所有子类必须使用的通用方法,这样子类就可以直接继承使用。抽象方法则留给子类根据自己的情况做不同的实现。
定义抽象方法的意义在于:给所有的子类制定一种规则,子类必须要实现抽象方法。
7.接口
接口定义了一个类所能对外提供的功能。接口不能被实例化,只能被实现。 接口可以理解为纯粹的抽象类,接口中的所有方法都是抽象的。 接口描述了一种规则。
//使用关键字interface声明一个接口。接口声明功能。
interface Flyable {
//public abstract不是必须的,接口的方法默认是public abstract。
//public abstract void fly();
void fly();
}
//使用关键字implements来实现一个接口。类实现接口中声明的功能。
class Aeroplane implements Flyable {
//实现一个接口,必须实现接口中的所有方法,否则此类必须声明为抽象类
@Override
public void fly() {
System.out.println("飞机能飞,飞机使用发动机飞");
}
}
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("鸟能飞,鸟使用翅膀飞");
}
}
public class Day1309 {
public static void main(String[] args) {
Bird bird = new Bird();
bird.fly(); //鸟能飞,鸟使用翅膀飞
Aeroplane aeroplane = new Aeroplane();
aeroplane.fly(); //飞机能飞,飞机使用发动机飞
//接口类型的变量可以引用实现了这个接口的类的实例
Flyable flyable1 = new Bird(); //Bird实现了Flyable接口,因此这个对象可以被flyable1引用
flyable1.fly(); //鸟能飞,鸟使用翅膀飞 多态,会调用其引用对象的fly方法
Flyable flyable2 = new Aeroplane();//Aeroplane实现了Flyable接口,因此这个对象可以被flyable2引用
flyable2.fly(); //飞机能飞,飞机使用发动机飞 多态,会调用其引用对象的fly方法
}
}
接口中的变量总是public static final的,接口中的变量不能声明为private。
interface ExampleInterface1{
//接口中的变量默认是public static final。静态常量
//value1,value2,value3,value4都是public static final
int value = 10;
public int value2 = 15;
public static int value3 = 20;
public static final int value4 = 25;
// private int value5 = 10; //编译错误
}
接口中的方法默认是public abstract的。
interface ExampleInterface1{
//默认是public abstract
void method1();
//接口中的方法不允许private
// private void method6(); //编译错误
}
接口继承
interface ExampleInterface1{
//默认是public abstract
void method1();
//接口中的方法不允许private
// private void method6(); //编译错误
}
//子接口
interface SubInterface1 extends ExampleInterface1 {
void method2();
}
//实现SubInterface
class C1 implements SubInterface1 {
@Override
public void method1() {
}
@Override
public void method2() {
}
}
interface ExampleInterface2 {
void method2();
}
//一个例可以实现多个接口,它必须要实现接口中的所有方法
//可以不实现接口中的所有方法,但是类必须声明为抽象类。
class SampleImpl implements ExampleInterface1, ExampleInterface2 {
@Override
public void method2() {
System.out.println("method2的实现");
}
@Override
public void method1() {
System.out.println("method1的实现");
}
}
public class Day1310 {
public static void main(String[] args) {
//接口类型的变量可以引用实现了这个接口的类的实例
ExampleInterface1 i1 = new SampleImpl();
i1.method1(); //调用SampleImpl对象的method1方法 //method1的实现
// i1.method2(); //编译错误,看不到method2方法
ExampleInterface2 i2 = new SampleImpl();
i2.method2(); //调用SampleImpl对象的method2方法 //method2的实现
// i1.method1(); //编译错误,看不到method1方法
ExampleInterface1 i3 = (ExampleInterface1)i2;
i3.method1(); //编译正确,能看到method1方法 //method1的实现
// i3.method2(); //编译错误,看不到method2方法
}
}
8.练习
1,将Frock类声明为抽象类,尺寸在Frock类中定义,在类中声明抽象方法calcArea方法,用来计算衣服的布料面积。编写Shirt类继承Frock类,实现 calcArea方法,用来计算衬衣所需的布料面积(尺寸 * 1.3)。编写Coat类继承Frock类,实现 calcArea方法,用来计算外套所需的布料面积(尺寸*1.5)。编写Test类,测试calcArea方法。