Java三大特性----封装继承多态

目录

封装

什么是封装

封装的实现

继承

什么是继承

 继承的语法

成员变量访问

成员方法访问 

super关键字

super关键字访问父类成员

super调用父类构造方法

辨析:super和this的异同 

多态

什么是多态

多态的实现

重写

向上转型

向下转型

动态绑定

多态的优点缺点 

避免在构造方法方法当中调用重写方法


封装

继承

什么是继承

 继承的语法

成员变量访问

成员方法访问 

super关键字

super关键字访问父类成员

super调用父类构造方法

辨析:super和this的异同 


封装

什么是封装

封装:隐藏对象的属性和实现细节,仅仅对外提供公开接口来和对象进行交互。

在类和对象一节,我们当时谈到了面向对象和面向过程的区别,还举了一个洗衣服的例子。那现在我们回到这个例子

例子:洗衣服

面向过程:

  1. 收拾脏衣服
  2. 将脏衣服放到盆子里
  3. 放水
  4. 放洗衣液
  5. 搓衣服
  6. 洗去泡沫
  7. 换水
  8. 搓衣服
  9. 拧干
  10. 晾晒

面向对象:

在洗衣服事件中涉及到的对象有:

  • 洗衣机 
  • 洗衣服的人 
  • 洗衣粉 
  • 衣服

在面向对象时,我们将洗衣服的具体实现(3456789)隐藏了起来,让洗衣机对象去实现这些步骤,我们不用关心。洗衣机只需要对外提供按钮来和洗衣服的人进行交互,那么我们就可以将这个理解为封装。

封装的实现

Java提供了访问限定符来对类的成员变量和方法的访问权限进行控制,同时也引入了包机制对类的访问进行控制。

继承

什么是继承

“世界上没有两片完全相同的树叶”,同时,世界上也没有两片完全不同的树叶(沃兹基硕德)。任何两片树叶,他们之间一定存在着相同的地方,比如:都是树叶。同时,也存在着不同的地方,比如形状颜色等。

那么让我们把格局打开,跳出树叶这个例子,就会发现世界上的事物其实都存在着关联,他们会有相同的地方,也会有不同的地方。那么,我们在用编程语言去描绘这个世界的实体的时候,就需要将这些关联考虑进去,因此,在面向对象的语言中,存在着继承的特性,我们用继承来描述事物与事物之间的共性和特性。

例子:在一个游戏开发项目中,我们定义了这样的三个类,用户类,成年人类,未成年人类。这三个用户的关系是这样的,在游戏用户之中,有成年人,未成年人,成年人和未成年人之间存在着共性(比如都有自己的id),也有着自己的特性(登录游戏的时间),因此,我们可以将共性的部分抽取出来,放在我们在父类之中,我们的子类在继承父类的基础上(共性),扩展自己的功能和属性(特性)。

 继承:用来抽取共性,实现代码复用。

 父类:又称超类,基类。用来保存子类的共性的类,可以直接被继承。

 子类:又称派生类,子类可以继承父类的所有成员,同时也可以定义属于自己的成员。

 继承的语法

修饰符 class 子类名 extends 父类 {

        成员属性

        成员方法

}

//用户类
class User {
    public String name;
    public String ID;
    public int loginTime;

    //登录功能
    public void login() {
        System.out.println("登录功能");
    }
}

//成年用户类
class AdultUser extends User {
    //充值功能
    public void recharge() {
        System.out.println("氪金功能");
    }
}

//未成年用户类
class MinorUser extends User {
    //强制下线功能
    public void downLine() {
        System.out.println("强制下线");
    }
}

public class Test {
    public static void main(String[] args) {
        MinorUser minorUser = new MinorUser();
        minorUser.name = "那小子真帅";           //继承父类得来的
        minorUser.ID = "16888";                //继承父类得来的
        minorUser.login();                     //继承父类得来的
        minorUser.downLine();                  //子类自己定义的
        AdultUser adultUser = new AdultUser();
        adultUser.name = "一剑霜寒十四州";         //继承父类得来的
        adultUser.ID = "52588";                 //继承父类得来的
        adultUser.login();                      //继承父类得来的
        adultUser.recharge();                   //子类自己定义的
        
        //在成年用户和未成年用户之中,我们并没有定义名字和id属性以及登录功能,
        //所以这两个属性和登录功能一定是从父类用户类继承来的
        //除了可以访问从父类继承来的功能和属性,还可以访问子类自己定义的属性和方法。
        
    }
}

总结:子类会继承父类的成员方法和属性,在访问范围允许的情况下,子类可以直接使用父类的成员和属性。

成员变量访问

子类和父类成员不同名情况:在访问成员变量时,优先在子类当中查找,如果子类当中查无这个成员,就去父类当中找。

子类和父类成员同名情况:和不同名情况类似,优先在子类当中查找,如果子类有该成员,则优先访问子类自己的成员。

public class Tset {

    public static void main(String[] args) {
        Base base = new Base();             //创建一个父类对象
        Derived derived = new Derived();    //创建一个子类对象
        System.out.println(derived.d);      //访问子类成员 d -->打印666
        System.out.println(derived.c);      //访问子类成员 c -->打印999
        //父类和子类的同名变量,优先访问子类的
    }
}

class Base {

    public int a;
    public int b;
    public int c = 99;

}
class Derived extends Base {

    public int c = 999;
    public int d = 666;

}

注意:子类和父类成员名相同,但是类型不同时,依旧是优先访问子类的成员。

成员方法访问 

成员方法名不同:和成员变量的访问相同,成员方法名不同时,依旧是优先在子类当中查找,子类有就访问子类的成员方法,子类没有就到父类当中查找,父类有就访问父类的成员方法,父类也没有就报错。

成员方法名相同:方法名相同时存在两种情况。

  • 构成重载:参数列表不同时,根据调用方法时传递的参数选择合适的方法。
  • 不构成重载:优先访问子类的成员方法。

   

总结:在存在继承关系的子类中,在进行访问成员变量和成员方法时,始终坚持“就近原则”。即优先访问自己的成员。

super关键字

在成员访问时,Java语法坚持了就近原则,优先访问子类成员,那么,为了访问到父类成员,java引入了super关键字。值得说清楚的是,和this是个引用变量不相同,super只是一个关键字,仅仅说明此刻我们访问的是父类成员而已,并非是引用变量。在使用super关键字时,并没有创建一个父类对象,super也没有指向一个父类对象。

super关键字的作用:在子类方法当中访问父类成员。

super关键字访问父类成员

public class Tset {
    public static void main(String[] args) {
        Base base = new Base();
        Derived derived = new Derived();
        derived.func2();
    }
}

class Base {

    int a = 99;
    
    public void func() {
        System.out.println("base");
    }
}

class Derived extends Base{

    int a = 88;

    public void func() {
        System.out.println("derived");
    }

    public void func2() {
        System.out.println(a);          //访问子类的成员变量a
        System.out.println(super.a);    //访问父类的成员变量a
        func();                         //访问子类的func()方法
        super.func();                   //访问父类的func()方法
    }
}

super调用父类构造方法

在实例化一个子类对象时,需要调用构造方法对子类成员进行初始化。但是,我们要怎么初始化父类的成员呢?这里就涉及到了super调用父类构造方法了。

在子类对象构造时,需要先调用父类的构造方法,对父类成员进行初始化,再调用子类的构造方法,对子类成员进行初始化。可以简单认为,将父类的构造方法放到了子类构造方法的最前面,只有将父类的构造方法的代码执行完毕,才能执行子类的构造方法代码。

class Base {

    int a = 99;
    
    public Base () {
        
    }
}

class Derived extends Base{

    int b = 88;
    
    public Derived() {
        super();  //如果用户自己不调用,编译器会自动帮助我们隐式调用
    }
}

子类构造方法的第一行代码一定是调用父类的构造方法,如果用户不调用,编译器自动帮助我们调用父类没有参数的构造方法。

 总结

  • 可以通过super在子类方法当中访问父类成员和方法。格式:super.data
  • 在子类构造方法第一行,通过super调用父类构造方法。在构造方法当中,super构造和this构造不能同时出现。(因为这两个都要求出现在第一行,存在冲突)
  • 如果显式定义父类的有参构造方法,则需要用户显示定义子类构造方法,并且在构造方法第一行通过super调用父类的有参构造。
  • super关键字必须在非静态方法当中使用。

辨析:super和this的异同 

多态

什么是多态

多态,可以简单理解为对同一件事物,不同对象会做出不同的响应。比如动物都会叫,但是鸡是咯咯叫,狗是汪汪叫,猫是喵喵叫。

多态的实现

在了解多态的实现之前,还需要先了解几个知识点。

重写

重写:重写又称覆盖,是指子类对继承来的父类方法的方法体部分做出需要的修改,其他部分保持不变的操作。重写的优点是可以在保留父类原有功能的基础上,对父类方法进行扩展或者是做出需要的修改。

重写的条件 

  • 存在于继承体系当中,子类继承父类的方法
  • 方法的参数名,参数列表,返回值不变
  • 静态方法,构造方法,private修饰的方法,final修饰的方法不能重写 
  • 子类重写方法的修饰权限必须不小于父类被重写方法的修饰权限。如父类被protected修饰,子类可以为public或者protected,但是不能为默认权限或者private。

重写方法实例 

注意:

  • 如果重写方法的返回值是一个类,则子类和父类的返回值可以不相同,但是要求父类和子类的返回值存在继承关系。
  • @override 是编译器提供的注解符,可以加也可以不加,如果加上了,编译器会自动帮助用户检测重写的方法是否合法,如果不合法则会报错, 如果不加上,就算方法不合法,编译器也不会检测到错误,所以,推荐最好加上。

重写和重载的区别

 

向上转型

向上转型:父类引用接收子类对象。将一个子类对象当成父类对象来使用。

向上转型的应用场景:

  • 直接赋值

  • 方法返回
  • 方法参数

 向上转型的优缺点

优点:可以用一个父类引用接收不同子类的对象,让代码实现更灵活,不需要重复定义。

缺点:父类引用不能调用子类的特有方法。

向下转型

向下转型子类引用接收父类引用,需要强制转换。向下转型需要将子类对象先向上转型给父类引用,再将父类引用强制类型转换为子类对象。要求父类引用实际上指向的是子类,否则运行时会报错。

 向下转型的两种常见错误:

向下转型的缺点:不安全,容易出现以上两个错误。

instanceof关键字

为了保证向下转型的安全型,java语法引入了instanceof关键字,该关键字的作用是判断父类引用是否真的指向需要转型的子类对象。

动态绑定

动态绑定:接收子类对象的父类引用调用子类父类重写的重名的方法。在程序运行时,编译器调用子类重写的方法。动态绑定运行的结果就是多态思想的提现,同时,动态绑定是多态的前提。

动态绑定的条件

  • 父类引用接收子类对象
  • 子类重写父类方法
  • 父类引用调用子类父类重写的同名方法

  • 到这里,我们就可以得出多态的实现条件就是存在动态绑定。多态的体现则是调用不同类对象时,会得到不一样的响应,这个响应不同是由于调用了不同类的重写方法。
  • 编译器在编译时,并不清楚应该调用哪个子类对象的方法,只有等到程序开始运行,父类引用的指向的对象确定后,才能知道调用哪个子类的方法

 

多态的优点缺点 

优点:利用多态思想,可以让代码的实现更加灵活,只需要一个接口就可以接收不同类型的对象。同时,代码的可扩展能力增强,在新增一个子类对象时,同样可以用该接口去接收,如果没有多态则需要对代码做出较大的变动。

缺点:无法调用子类的专属方法。

避免在构造方法方法当中调用重写方法

 构造 Derived 对象的时, 会先会调用 父类Base 的构造方法,Base 的构造方法中调用了 func 方法, 此时会触发动态绑定, 调用到 Derived 中的 func。此时 Derived的构造方法还没被调用,所以,此时num还没有初始化,所以打印的结果0。(此处涉及到代码块的执行顺序问题,有兴趣的小伙伴可以自己调试一下)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值