面向对象三大特性--封装、继承、多态

封装

什么是封装?

封装是面向对象的三大特性之一,它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对信息的操作和访问。

 封装:私有属性,公有方法

封装的好处

  • 隐藏类的实现细节;
  • 让使用者只能通过预先的方法来访问数据,从而可以在该方法中加入逻辑控制,限制对成员变量不合理的访问;
  • 可进行数据检查,从而有利保证对象的完整性。
  • 便与修改提高代码的可维护性;
  • 安全性较高

实际上封装的两个含义是:把该隐藏的隐藏起来,把该暴露的暴露出来。这两个方面都可以通过java提供访问控制符来实现。

访问控制符

java提供了三个访问控制符:private、protected、public,另外还有不加控制符的访问控制级别。
级别排序:private>default>protected>public级别由小到大


实例:隐藏属性---只能在类的内部使用,而对象不能直接使用。但是可以通过设定设置私有属性的方法和获取私有属性的方法来使用私有属性。

通过快捷键来定以方法set和get;点击右键:

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

这样可以快捷的定义方法。

关于访问控制符的使用,存在如下几条基本原则:

  • 类里面绝大部分成员变量都应该使用private修饰,只有一些static修饰的,类似于全局变量的成员变量,才可以考虑使用public修饰。除此之外,有些方法只用于辅助实现该类的其他方法,被称为工具方法,这种也可以用private修饰;
  • 如果某个类主要用做其他类的父类,该类里包含的大部分方法可能仅希望被其子类重写,而不想被外界调用,则应该适用protected修饰这些方法。
  • 大部分外部类都是用public修饰。
  • 安全性较高

代码块

什么是代码块?
在Java中,使用{}括起来的代码被称为代码块。

代码块分类
根据其位置和声明的不同可以分为:

  • 局部代码块:在方法中出现,限定变量生命周期,随着方法的调用而调用,销毁而销毁,及早释放,提高内存利用率。
public class CodeStock {
    public static void main(String[] args) {
        int num=100;
        {
            System.out.println(num);
            num=10;
        }
        System.out.println(num);
        } 
    }
  • 构造代码块:在类中方法外出现,多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行。
public class Student {
    String name;

    public Student(){
        System.out.println("我是构造方法");
    }

    {
        System.out.println("我是构造代码块");
    }
    
    public static void main(String[] args) {
        Student student = new Student();
    }
}

/*我是构造代码块
我是构造方法*/
  • 静态代码块:在类中方法外出现,加static修饰,用于给类进行初始化,在加载类的时候执行一次。
public class Student {
    String name;

    public Student(){
        System.out.println("我是构造方法");
    }

    {
        System.out.println("我是构造代码块");
    }

    static {
        System.out.println("我是静态代码块");
    }

    public static void main(String[] args) {
        Student student1 = new Student();
        Student student2 = new Student();
    }
}

/*我是静态代码块
我是构造代码块
我是构造方法
我是构造代码块
我是构造方法*/

继承

什么是继承?
继承是面向对象的三大特征之一,多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。也就是在一个类的基础上产生一个新类的过程叫继承。

如何实现继承?
通过extends关键字可以实现类的继承;

格式:class 子类名 extends 父类名{}

实现继承的类称为子类,被继承的类称为父类,有的也称为基类、超类。

对继承的理解:
父类和子类的关系一般与特殊,抽象与具体,广义和狭义的关系。比如父类为动物类,是一个抽象的概念,子类继承父类所以子类可以是猫类,狗类,老虎类等,是将抽象的动物类具体到哪类动物。
如果想实现多层继承,比如Tom这个猫还要继承猫类,则Tom可以称为动物类的子孙类,动物类称为Tom类的祖先类。
实例(以上面说的为例):

Animal.class

public class Animal {
    
    //给出默认值Tom
    String name=“Tom”;
    int age;
    
    public void eat(){
        System.out.println("动物喜欢吃饭");
    }
    
}

Cat.class

public class Cat extends Animal {

    @Override
    public void eat() {
        System.out.println("猫喜欢吃鱼");
    }
}

Tom.class

public class Tom extends Cat {

    @Override
    public void eat() {
        System.out.println(this.name+"喜欢吃带鱼");
    }
}

Test01.class测试类

public class Test01 {
    public static void main(String[] args) {

        Animal animal = new Animal();
        Cat cat = new Cat();
        Tom tom = new Tom();

        animal.eat();
        cat.eat();
        tom.eat();
    }
}
/*动物喜欢吃饭
猫喜欢吃鱼
Tom喜欢吃带鱼*/

可以从上面的代码中体会到多层继承将类的范围越来越具体到实例。并且在Tom类中用自身的对象可以访问到其祖先类的name属性,而在Tom中并没有定义name属性。

继承的优缺点

  • 优点:提高了代码的复用性;提高了代码的维护性;让类与类之间产生了关系,多态的前提;
  • 缺点:类的耦合性增强了;而开发的原则是高内聚,低耦合,大概意思是尽量降低类与类之间的依赖关系,增强类内部实现事物的能力。

继承的特点

  • java只支持单继承,不支持多继承,换句话说就是子类只能有一个父类而不能继承多个父类;
  • java实现多层继承:比如上述实例中有双层继承,即Cat继承Anaimal,Tom继承Cat,这种就叫做多层继承。

继承的注意事项

  • 子类继承父类的非私有的属性和方法

  • 子类可以扩展自己的属性和方法

  • 子类不能继承父类的构造器,只能通过super关键字调用

  • 可以声明父类,创建子类()

  1. 声明什么类型,就可以调用本类的属性和方法 
  2. 创建什么类型,就可以运行该类型的属性和方法
  3. 创建什么类型,就可以强转为什么类型
  • 不要为了部分功能而去继承;

方法重写
上面的三个继承类中都用到了方法重写,什么是方法重写呢?

子类中出现了和父类中一模一样的方法声明,也可称为方法覆盖,方法复写。

方法重写的应用

当子类需要父类的功能,而功能主体自雷有自己特有的内容时,可以重写父类中的方法,这样沿袭了父类的功能,又定义了子类特有的内容。

对方法重写的理解
如果父类时鸟类,鸟类都包含了飞翔的方法,鸵鸟也属于鸟类,可是将飞翔的方法放在他身上显然不合适,所以鸵鸟类就要重写飞翔这个方法。

方法重写的注意事项:

  • 父类中私有的方法不能被重写,因为父类私有方法子类根本就无法继承;
    如将上述实例中的成员方法eat设置为私有成员,在重写就会报错;

Animal.class

private void eat(){
        System.out.println("动物喜欢吃饭");
    }
  • 子类重写父类方法时,访问权限不能更低,也就是子类的权限不能比父类的权限级别低,最好权限一致。
    如Cat类中eat方法权限为public,现在将Tom类中的eat方法权限改为比public小的private;
public class Tom extends Cat {

    @Override
    private void eat() {
        System.out.println(this.name+"喜欢吃带鱼");
    }
}

可以看到权限太低不能覆盖父类的方法。

  • 可以看到权限太低不能覆盖父类的方法。

super关键字
当父类中的方法杯子类的方法覆盖,子类的对象将无法访问父类中被覆盖的方法,但是在子类方法中可以调用父类中被覆盖的方法。使用super关键字或者父类类名作为调用者来调用父类中被覆盖的方法。

例如在Tom类的eat方法中调用已经被覆盖的Cat的eat方法;

 @Override
    public void eat() {
        System.out.println(this.name+"喜欢吃带鱼");
        super.eat(); //调用父类被覆盖的方法

正如this不能在static修饰的方法中使用一样,super也不能在static修饰的方法中使用,在每个类里面的构造方法的第一行默认都有一行super();去调用父类的空参构成,先要完成父类数据的初始化,然后再初始化自己的数据。


继承中成员变量的关系:

  • 子类中的成员变量和父类中的成员变量名称不一样时,直接通过名称调用;
  • 当子类的成员变量与父类中的成员名一样时遵循就近原则,按下面的先后顺序,即在子类的方法局部范围找,在子类的成员范围找;在父类的成员范围找;找不到就报错。

Father.class

public class Father {
    int num=10;
    int num2=1;
}

Son.class

public class Son extends Father {
    int num=20;

    public void show(){
        System.out.println(num);
        System.out.println(num2);
    }
}

Test.class

public class Test02 {


    public static void main(String[] args) {
        Son son = new Son();
        int num=30;
        System.out.println(num);
        son.show();
    }
}
/*30
20
1*/

当程序创建一个子类对象时,系统不仅会为该类中定义的实例变量分配内存,也会为他从父类继承得到的所有实例变量分配内存,即使子类定义了与父类同名的实例变量。


调用父类构造器

子类不会获得父类的构造器,但子类构造器里可以调用父类构造器的初始化代码,类似于前面介绍的构造器调用另一个构造器。
在一个构造器中调用类内部的构造器使用this,调用父类构造器用super。

Base.class

public class Base {
    public double size;
    public String name;

    public Base(double size, String name) {
        this.size = size;
        this.name = name;
    }
}


Sub.class

public class Sub extends Base {
    public String color;

    public Sub(double size, String name, String color) {
        //通过super来调用父类构造器的初始化过程
        super(size, name);
        this.color = color;
    }
    public static void main(String[] args) {
        Sub s = new Sub(8,"测试对象","red");

        System.out.println(s.size+"-"+s.name+"-"+s.color);
    }
}


子类构造器调用父类构造器分以下几种情况:

1.子类构造器执行体的第一行使用super显式调用父类构造器,系统将根据super调用里传入的实参列表调用父类对应的构造器。
2.子类构造器执行体的第一行代码使用this显式调用本类中重载的构造器,系统将根据this调用里传入的实参列表调用本类的另一个构造器,执行本类的另一个构造器时即会调用父类构造器;
3.子类构造器执行体中既没有super调用,也没有this调用,系统将会在子类执行构造器之前,隐式调用父类无参数的构造器。
4.不管上面哪种情况,当调用子类构造器来初始化子类对象时,父类构造器总会在子类构造器之前执行,而且执行父类构造器时系统会再次上溯执行其父类构造器,以此类推直到顶层的Object类的构造器。

Father.class

public class Father {

    public Father() {
        System.out.println("我是父类构造器");
    }
}


Son.class

public class Son extends Father{
    public Son() {
        System.out.println("我是子类构造器");
    }

    public static void main(String[] args) {
        Son son = new Son();
    }
}
/*我是父类构造器
我是子类构造器*/


可以看出,创建任何对象总是从该类所在继承数最顶层类的构造器开始执行,然后依次向下执行,最后才执行本类的构造器。


多态

什么是多态?
Java引用变量有两个类型:

  • 编译时类型:由声明该变量时使用的类型决定;
  • 运行时类型:由实际赋给变量的对象决定;

如果编译时类型和运行时类型不一致,就可出现多态

1.两种体现:①方法的重载和重写。②对象的多态性

2.两种类型:①向上转型:子类对象→父类对象

                                         父类  父类对象 = 子类实例;

                    ②向下转型:父类对象→子类对象

                                        子类  子类对象 = (子类)父类实例。

3.向上转型过后所调用的方法一定是被子类重写过的方法。

4.向下转型要求:必须先进行向上转型,否则会出现对象转换异常。(买东西其他小孩要,你愿意给吗?)

Animal.class

public class Animal {
    int num=10;

    public void eat(){
        System.out.println("动物要吃饭");
    }

    public void sleep(){
        System.out.println("动物要睡觉");
    }

    public void show(){
        System.out.println("Animal中的show方法");
    }

    public static void method(){
        System.out.println("Animal中的静态方法");
    }
}

Cat.class

public class Cat extends Animal {
    int num=20;

    @Override
    public void eat() {
        System.out.println("猫爱吃鱼");
    }

    @Override
    public void sleep() {
        System.out.println("猫喜欢在白天睡觉");
    }

    @Override
    public void show() {
        System.out.println("Cat中的show方法");
    }
    public static void method(){
        System.out.println("Cat中的静态方法");
    }
}

Test.class

public class Test01 {
    public static void main(String[] args) {
        //左右类型一致,不发生多态
        Cat cat = new Cat();
        cat.eat();
        cat.sleep();
        cat.show();
        System.out.println("-------------------");

        //父类引用变量指向子类对象
        //左边为编译时类型,Animal
        //右边为运行时类型,Cat;类型不一致多态发生
        Animal an=new Cat();

        //通过多态访问成员方法
        an.eat();//猫爱吃鱼
        an.sleep();//猫喜欢在白天睡觉
        an.show();//Cat中的show方法
        System.out.println("----------------------");

        //通过太多访问成员变量:编译看左边,运行看右边
        System.out.println("num="+an.num);//num=10
        System.out.println("---------------------");

        //通过多态访问静态方法:编译看左边,运行看左边
        Animal.method();//Animal中的静态方法
        Cat.method();//Cat中的静态方法
        an.method();//Animal中的静态方法

    }
}

从实力可以看出:相同类型的变量调用同一方法时呈现出不同的行为特征,这就是多态

引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法。

例如将Cat中的show方法改名为show1,而Animal中没有这个方法。

运行结果:

Error:(20, 11) java: 找不到符号
  符号:   方法 show1()
  位置: 类型为org.westos.pactice.Animal的变量 an

实现多态的必要条件

  • 有继承关系
  • 有方法重写
  • 有父类引用指向子类对象

优点:

  • 提高了代码的维护性
  • 提高了代码的扩展性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值