------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
面向对象下:继承和多态的总结。
一、继承
1、继承的概述
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可
通过extends关键字可以实现类与类的继承,单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。有了继承
以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。
格式:
class 子类名 extends 父类名 {}
2、继承的好处和弊端
好处:
a: 提高了代码复用性
b: 提高了代码维护性
c: 让类于类之前产生了关系,是多态的前提
弊端:
提高了耦合
开发原则:高内聚 , 低耦合
内聚:某一个类完成某一件事的能力
耦合:类于类之间的关系
3、继承的特点
a:Java只支持单继承,不支持多继承。
一个类只能有一个父类,不可以有多个父类。
class SubDemo extends Demo{} //正确
class SubDemo extends Demo1,Demo2...//错误
b:Java支持多层继承(继承体系)
class A{}
class B extends A{}
class C extends B{}
4、注意事项
a:子类只能继承父类所有非私有的成员(成员方法和成员变量)
其实这也体现了继承的另一个弊端:打破了封装性
b:子类不能继承父类的构造方法,但是可以通过super(后面讲)关键字去访问父类构造方法。
c:不要为了部分功能而去继承
d:我们到底在什么时候使用继承呢?
继承中类之间体现的是:”is a”的关系
5、继承中成员方法的特点
(1)成员变量:
变量在访问的时候遵循一个原则"就近原则"
查询顺序:
a: 在本类中的局部位置进行查找
b: 在本类中的成员位置进行查找
c: 在父类的成员位置进行查找
e: 就报错了
(2)构造方法:
我们子类在进行初始化的时候,都需要调用父类的无参的构造方法对父类的数据进行初始化
我们如何一个构造方法的第一条语句默认都是super() ;
Object 类: 其实我们的任何一个类都是间接或者直接的继承自这个类
如果父类中没有无参的构造方法,子类如果进行初始化?
解决方案:
a: 在父类中添加一个构造方法
b: 子类可以使用super关键字去显式的访问父类有参的构造方法
c: 使用this关键字访问本类中其他的构造方法, 但是本类中的其他的构造方法需要使用super显式的访问父类有参的构造方法
6、super关键字
(1)super的用法和this很像
this代表本类对应的引用。
super代表父类存储空间的标识(可以理解为父类引用)
(2)用法(this和super均可如下使用)
访问成员变量:
this.成员变量 super.成员变量
访问构造方法:
this(…)super(…)
访问成员方法:
this.成员方法() super.成员方法()
注:this(...) 和 super(...) : 只能是构造方法第一行
7、成员方法的访问特点
查找顺序:
a: 在本类中查找
b: 在父类中查找
c: 就报错了
方法重写的注意事项:
a: 父类中私有的方法不能被子类重写
b: 子类在重写父类的方法的时候要求访问权限必须大于或者等于父类的权限
建议: 一致
c: 父类中的静态方法,子类在复写的时候必须使用静态
8、方法重写
(1)方法重写概述:
子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖,方法复写。
使用特点:
如果方法名不同,就调用对应的方法
如果方法名相同,最终使用的是子类自己的
(2)方法重写注意事项
a:父类中私有方法不能被重写
b:子类重写父类方法时,访问权限不能更低
c:父类静态方法,子类也必须通过静态方法进行重写
(3)方法重写和重载的区别
9、final关键字
final关键字是最终的意思,可以修饰类,成员变量,成员方法。
修饰类,类不能被继承
修饰变量,变量就变成了常量,只能被赋值一次
修饰方法,方法不能被重写
10、继承练习
例1-1:手机类
class PhoneTest{
public static void main(String[] args){
<span style="white-space:pre"> </span>
NewPhone np = new NewPhone();
np.call();
np.playMusic();
np.send();
}
}
class Phone{//定义手机类,父类
private void call(){
System.out.println("打电话...");//打电话功能
}
public void playMusic(){//听音乐功能
System.out.println("听音乐....");
System.out.println("定时关机");
}
public static void send(){//发短信功能
System.out.println("发短信");
}
}
class NewPhone extends Phone{
<span style="white-space:pre"> </span>//继承父类手机类,重写父类方法
public void call(){
System.out.println("打电话...---------------------");
}
public void playMusic(){
System.out.println("根据你的情绪,来推荐音乐...");
super.playMusic();
}
public static void send(){
System.out.println("发网络短息,走流量,不再是一毛一条");
}
}
例1-2:学生类
//定义Person类
class Person{
//成员变量
private String name;//姓名,年龄
private int age;
//构造
Person(){}
Person(String name,int age){
this.name = name;
this.age = age;
}
//成员方法
//get/set
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 void eat(){
System.out.println("吃饭,吃好喝好....");
}
public void sleep(){
System.out.println("睡好,别在睡觉的是爆炸就好...");
}
}
//定义学生类
class Student extends Person{
//成员变量 可以不写父类中已经有的
//构造
Student(){}
Student(String name ,int age){
super(name,age);//--注意通过super调用父类的构造
}
//特有方法 ,如果你想重写,就写.
public void coding(){
System.out.println("敲,狂敲,死里敲!!!键盘敲烂,薪资过万.....");
}
}
//定义学生类
class Teacher extends Person {
//成员变量 可以不写父类中已经有的
//构造
Teacher(){}
Teacher(String name ,int age){
super(name,age);//--注意通过super调用父类的构造
}
//特有方法 ,如果你想重写,就写.
public void coding(){
System.out.println("敲案例,多多给大家练习");
}
public void teaching(){
System.out.println("教,狂教,死里教!!!希望你们薪资过万.....");
}
}
二、多态
1、多态的概述
某一个事物,在不同时刻表现出来的不同状态。
举例:
猫可以是猫的类型。猫 m = new 猫();
同时猫也是动物的一种,也可以把猫称为动物。
动物 d = new 猫();
在举一个例子:水在不同时刻的状态
多态前提和体现
有继承关系
有方法重写
有父类引用指向子类对象
2、多态成员访问特点
成员访问特点:
a:成员变量
编译看左边,运行看左边
b:成员方法
编译看左边,运行看右边
c:静态方法
编译看左边,运行看左边
3、多态中的转型问题
(1)向上转型
从子到父
父类引用指向子类对象
(2)向下转型
从父到子
父类引用转为子类对象
a:多态的好处
提高了程序的维护性(由继承保证)
提高了程序的扩展性(由多态保证)
b:多态的弊端
不能访问子类特有功能
那么我们如何才能访问子类的特有功能呢?
多态中的转型
5、多态练习
例2-1:钢铁侠案例
class PersonTest{
public static void main(String[] args){
//多态的形式来创建对象
Person p = new IronMan();
System.out.println("我: 这谁啊?");
System.out.println("他: 我啊,"+ p.name +"!"); //成员变量,运行看左边
System.out.println("我:干啥呢?");
p.business(); //成员方法,运行看右边
System.out.println("救命~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//p.fly(); // 编译看左边,没有 fly save
//p.save();
IronMan im = (IronMan)p; // 变身了 ,向下转型
//IronMan im = new IronMan();
//byte b = (byte)300;
im.fly();
im.save();
}
}
class Person{
String name = "托尼.斯塔克";
public void business(){
System.out.println("开厂子,挣大钱....");
}
}
//钢铁侠是 Person 一种形态
class IronMan extends Person{
String name ="钢铁侠";
public void business(){
System.out.println("合影,救人,"
+"赚出场费 ,5块 !!!!");
}
//飞
public void fly(){
System.out.println("换装备就飞");
}
//救人
public void save(){
System.out.println("换装备就救人,当然也不是白救的~~~,5块就够");
}
}
例2-2:榨汁机案例
class ZhaZhiJiDemo{
public static void main(String[] args){
//创建榨汁机对象
ZhaZhiJi zzj = new ZhaZhiJi();
//创建苹果对象
Apple a = new Apple();
zzj.zhaZhi(a);
Watermelon w = new Watermelon();
zzj.zhaZhi(w);
//使用多态来创建对象
Fruit f = new Apple();
zzj.zhaZhi(f);
Fruit ff = new Watermelon();
zzj.zhaZhi(ff);
Fruit fff = new Banana();
zzj.zhaZhi(fff);
//创建树的对象
Tree t = new Tree(); //树
//Apple apple = t.makeApple(); // 从数上获取苹果
Fruit fruit = t.makeApple(); // 从数上获取苹果
zzj.zhaZhi(fruit); //把苹果用榨汁机变成苹果
}
}
//父类,水果类
class Fruit {
public void toJuice(){
System.out.println("水果变成水果汁,好喝");
}
}
class Apple extends Fruit{
public void toJuice(){
System.out.println("苹果变成苹果汁,更好喝");
}
}
class Watermelon extends Fruit{
public void toJuice(){
System.out.println("西瓜变成西瓜汁,更好喝,还解渴");
}
}
class Banana extends Fruit{
public void toJuice(){
System.out.println("香蕉变成香蕉汁,加点牛奶,更好喝~~~~");
}
}
class Tree{
//树生产苹果
/*
两个明确:
返回值类型: Apple
参数列表 : 无
*/
public Apple makeApple(){
System.out.println("树生产了苹果");
Apple a = new Apple();
return a;
}
}
//定义榨汁机
class ZhaZhiJi{
//使用多态来改进,参数变成父类
public void zhaZhi(Fruit f){
f.toJuice();
}
// public void zhaZhi(Apple a){
// a.toJuice();
// }
//
// public void zhaZhi(Watermelon w){
// w.toJuice();
// }
}
三、抽象类
1、抽象类概述
回想前面我们的猫狗案例,提取出了一个动物类。并且我们在前面也创建过了动物对象,其实这是不对的。为什么呢?因为,我说动
物,你知道我说的是什么动物吗?只有看到了具体的动物,你才知道,这是什么动物。 所以说,动物本身,不是一个具体的事物而
是一个抽象的事物。只有真正的猫,狗才是具体的动物。同理,我们也可以推想,不同的动物吃的东该是不一样的,所以,我们不
应该在动物类中给出具体体现,而是应该给出一个声明即可。在Java中,一个没有方法体的方法应 ,定义为抽象方法,而类中
如果有抽象方法,该类必须定义为抽象类。
2、抽象类的特点
(1)抽象类和抽象方法必须用abstract关键字修饰格式
abstract class 类名 {}
public abstract void eat();
(2)抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
(3)抽象类不能实例化
那么,抽象类如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。
(4)抽象类的子类
要么是抽象类
要么重写抽象类中的所有抽象方法
3、抽象类成员的特点
(1)成员变量可以是变量
也可以是常量
(2)构造方法
有构造方法,但是不能实例化
那么,构造方法的作用是什么呢?
用于子类访问父类数据的初始化
(3)成员方法
可以有抽象方法 限定子类必须完成某些动作
也可以有非抽象方法 提高代码服用性
4、抽象类案例
例3-1:动物案例
class AnimalTest{
public static void main(String[] args){
System.out.println();
//Animal a = new Animal();//错误: Animal是抽象的; 无法实例化
Cat c = new Cat();
c.eat();
c.sleep();
}
}
abstract class Animal {
// abstract int leg; // 错误: 此处不允许使用修饰符abstract
public void eat(){
System.out.println("嘴吃");
}
public abstract void sleep();
}
class Cat extends Animal {
final int tail;
public void eat(){
System.out.println("爱吃鱼");
}
public void sleep(){
System.out.println("白天想睡哪就睡了...");
}
}
四、接口
1、接口概述
继续回到我们的猫狗案例,我们想想狗一般就是看门,猫一般就是作为宠物了,对不。但是,现在有很多的驯养员或者是驯兽师
可以训练出:猫钻火圈,狗跳高,狗做计算等。而这些额外的动作,并不是所有猫或者狗一开始就具备的,这应该属于经过特殊的
培训训练出来的,对不。所以,这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中,因为只有部分猫狗具
备这些功能。所以,为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需
要被培训,只需要这部分猫狗把这些额外功能实现即可。
2、接口特点
a:接口用关键字interface表示
格式:interface 接口名 {}
b:类实现接口用implements表示
格式:class 类名 implements 接口名 { }
c:接口不能实例化
那么,接口如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,接口多态。
d:接口的子类
要么是抽象类
要么重写接口中的所有抽象方法
3、接口的成员特点
a:成员变量
只能是常量
默认修饰符 public static final
b:构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
c:成员方法
只能是抽象方法
默认修饰符 public abstract
4、类与类,类与接口以及接口与接口的关系
a:类与类
继承关系,只能单继承,但是可以多层继承
b:类与接口
实现关系,可以单实现,也可以多实现。还可以在继承一个类的同时实现多个接口
c:接口与接口
继承关系,可以单继承,也可以多继承
5、抽象类和接口的区别
a:成员区别
抽象类 变量,常量;有抽象方法;抽象方法,非抽象方法
接口 常量;抽象方法
b:关系区别
类与类 继承,单继承
类与接口 实现,单实现,多实现
接口与接口 继承,单继承,多继承
c:设计理念区别
抽象类 被继承体现的是:”is a”的关系。共性功能
接口 被实现体现的是:”like a”的关系。扩展功能
class InterFaceDemo{
public static void main(String[] args){
MiaoXingRen mxr = new MiaoXingRen();
mxr.jump();
mxr.count();
mxr.swim();
}
}
//定义训练接口
interface Training{
public abstract void jump();// 错误: 接口方法不能带有主体
public abstract void count();
public abstract void swim();
}
class Cat {// implements Training {
//InterFaceDemo.java:40: 错误: Cat不是抽象的, 并且未覆盖Training中的抽象方法jump()
public void jump(){
System.out.println("跳得高高的!!!" );
}
// public void count(){
//
// }
}/*
喵星人得extends 猫
喵星人 实现Training 接口
*/
class MiaoXingRen extends Cat implements Training {
public void count(){
System.out.println("用爪儿查数");
}
public void swim(){}
}
五、包和内部类
1、导包概述
不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了
导包的功能。
导包格式import 包名;
注意:
这种方式导入是到类的名称。
虽然可以最后写*,但是不建议
2、权限修饰符
public | protected | 默认 | private | |
同一类中 | √ | √ | √ | √ |
同一包子类,其他类 | √ | √ | √ | |
不同包子类 | √ | √ | ||
不同包其他类 | √ |
3、内部类概述
把类定义在其他类的内部,这个类就被称为内部类。
举例:在类A中定义了一个类B,类B就是内部类。
(1)内部类的访问特点:
内部类可以直接访问外部类的成员,包括私有。
外部类要访问内部类的成员,必须创建对象。
(2)内部类位置
按照内部类在类中定义的位置不同,可以分为如下两种格式:
a:成员位置(成员内部类)。
b:局部位置(局部内部类)。
成员内部类:
外界如何创建对象
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
(3)匿名内部类
就是内部类的简化写法。
前提:存在一个类或者接口
这里的类可以是具体类也可以是抽象类。
格式:
new 类名或者接口名() {重写方法;}
本质:
是一个继承了类或者实现了接口的子类匿名对象
4、内部类练习
例5-1;成员内部类
public class ChengyuanNeibulei {
int num=2;
//成员内部类
int num1=20;
private class Heart{
int num=1;
public void Open(){
System.out.println("紫霞仙子的"+num+"眼泪");
System.out.println("紫霞仙子的"+this.num+"眼泪");
System.out.println("紫霞仙子的"+ChengyuanNeibulei.this.num+"眼泪");
System.out.println("紫霞仙子的"+num1+"眼泪");
}
}
//定义成员方法
public void show(){
//创建内部类
Heart heart=new Heart();
heart.Open();
}
}
例5-2;局部内部类
lass OuterTest{
public static void main(String[] args){
System.out.println();
//创建外部类的对象
Outer o = new Outer();
o.method();
}
}
class Outer{
int num =10;
public void method(){
System.out.println("method 方法执行 ");
//num3 是局部变量,随着方法的调用而出现的,是在栈内存中的.
final int num3 = 30; //错误: 从内部类中访问本地变量num3; 需要被声明为最终类型
//在方法内部 定义类 , 类加载到方法区里面的 .
class Inner{
int num2 = 20;
public void show(){
System.out.println("num:" + num);
System.out.println("num2:" + num2);
System.out.println( num3);
}
}
//创建内部类对象
Inner i = new Inner();
i.show();
}
}
例5-2;匿名内部类
class InterDemoTest{
public static void main(String[] args){
System.out.println();
InterDemo id = new InterDemo();
id.method();
}
}
interface Inter{
public abstract void show();
public abstract void show2();
}
class InterDemo{
public void method(){
System.out.println("method方法");
/*
new Inter相当于new Inter的子类对象,而且是匿名的对象
在new出来对象之后,需要在调用show();
在调用show()前,必须把show重写.只能在创建对象时候给重写一下了.
*/
new Inter(){
public void show(){
System.out.println("匿名内部类的重写的--show");
}
public void show2(){
System.out.println("匿名内部类的重写的--show2222222");
}
}.show();
new Inter(){
public void show(){
System.out.println("匿名内部类的重写的--show");
}
public void show2(){
System.out.println("匿名内部类的重写的--show2222222");
}
}.show2();
System.out.println("----------------------------");
// 发现很麻烦
Inter inter = new Inter(){
public void show(){
System.out.println("匿名内部类的重写的--show");
}
public void show2(){
System.out.println("匿名内部类的重写的--show2222222");
}
}; //接口多态
inter.show();
inter.show2();
}
}