Java基础——面向对象编程进阶(面向对象特征)

前言:

面向对象的第一阶段是类及类的成员,第二阶段就是面向对象特征:封装、继承、多态。但这三个特征并不是官方发布的,只是人们自己总结出来方便理解。

封装:

为什么需要封装

面向对象的开发原则是:高内聚、低耦合。高内聚:类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用,尽量方便外部调用。

举例来说:就跟咋们要用洗衣机,只需要按一下开关和洗涤模式就可以了。没有必要了解洗衣机内部的结构,也有必要碰电动机。为什么?就是因为封装。电动机这些部件都被封装成一个个体构成洗衣机。洗衣机向外留有按钮,只要按这个按钮。电动机这些内部器件就会自己运行。

什么是封装

所谓封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或者对象开放,向没必要开放的类或者对象隐藏信息。通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

对于上面的洗衣机来讲:电动机这些用户不了解也没必要了解那就隐藏起来,但我要用洗衣机,你给我留个按钮,我一按,洗衣机就能用。所以就需要将按钮暴露出来。

如何实现封装

  • 实现封装就是控制类或成员的可见性范围。这就需要依赖访问控制修饰符,也称为权限修饰符来控制。
  • 权限修饰符:public、protected、缺省、private。具体访问范围如下:

修饰符

本类内部

本包内

其他包的子类

其他包非子类

private   私有的

×

×

×

缺省      

×

×

protected     受保护的

×

public    公开的

具体修饰的结构:以上符号修饰权限由小到大

外部类:public、缺省

成员变量、成员方法、构造器、成员内部类:public、protected、缺省、private

实现封装也就是将该隐藏的隐藏,该暴露的暴露。有了权限修饰符,要隐藏还是要公开根据需要选择对应的权限修饰符。

封装的体现

成员变量/属性的私有化:私有化类的成员变量,提供公共的get和set方法,对外暴露获取和修改属性的功能。

//① 使用 private 修饰成员变量private 数据类型 变量名 ;

public class Person {
    private String name;
  	private int age;
    private boolean marry;
}
//② 提供 getXxx方法 / setXxx 方法,可以访问成员变量,代码如下:
public class Person {
    private String name;
  	private int age;
    private boolean marry;

	public void setName(String n) {
		name = n;
    }

    public String getName() {
        return name;
	}

    public void setAge(int a) {
        age = a;
    }

    public int getAge() {
        return age;
    }
    
    public void setMarry(boolean m){
        marry = m;
    }
    
    public boolean isMarry(){
        return marry;
    }
}
//③ 测试:
public class PersonTest {
    public static void main(String[] args) {
        Person p = new Person();

        //实例变量私有化,跨类是无法直接使用的
		/* p.name = "张三";
        p.age = 23;
        p.marry = true;*/

        p.setName("张三");
        System.out.println("p.name = " + p.getName());

        p.setAge(23);
        System.out.println("p.age = " + p.getAge());

        p.setMarry(true);
        System.out.println("p.marry = " + p.isMarry());
    }
}

成员变量封装的好处:

  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入控制逻辑,限制对成员变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。
  • 便于修改,提高代码的可维护性。主要说的是隐藏的部分,在内部修改了,如果其对外可以的访问方式不变的话,外部根本感觉不到它的修改。例如:Java8->Java9,String从char[]转为byte[]内部实现,而对外的方法不变,我们使用者根本感觉不到它内部的修改。

开发中,一般成员实例变量都习惯使用private修饰,再提供相应的public权限的get/set方法访问。对于final的实例变量,不提供set()方法。对于static final的成员变量,习惯上使用public修饰。 

继承:

举例:继承就是字面意思,有延续(下一代延续上一代的基因、财富)、扩展(下一代和上一代又有所不同)的意思。

为描述和处理个人信息,定义类Person:

为描述和处理学生信息,定义类Student:

通过继承,简化Student类的定义:

 说明:Student类继承了父类Person的所有属性和方法,并增加了一个属性school。Person中的属性和方法,Student都可以使用。

继承的好处:

  • 继承的出现减少了代码冗余,提高了代码的复用性。
  • 继承的出现,更有利于功能的扩展。
  • 继承的出现让类与类之间产生了is-a的关系,为多态的使用提供了前提。
    • 继承描述事物之间的所属关系,这种关系是:is-a 的关系。可见,父类更通用、更一般,子类更具体。不要仅为了获取其他类中某个功能而去继承!

继承语法:

通过 extends 关键字,可以声明一个类B继承另外一个类A,定义格式如下:

[修饰符] class 类A {
    ...
}

[修饰符] class 类B extends 类A {
    ...
}

类B,称为子类、派生类(derived class)、SubClass

类A,称为父类、超类、基类(base class)、SuperClass

标红:①子类会继承父类所有的实例变量和实例方法②子类不能直接访问父类中私有的(private)的成员变量和方法③在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”④Java支持多层继承(继承体系),即子继父类,父继爷类。子继爷类。⑤一个父类可以同时拥有多个子类⑥Java只支持单继承,不支持多重继承,即一个类只能有一个父类,不可以有多个直接父类。

方法重写、方法重载理解:


class Animal {
    void makeSound() {
        System.out.println("The animal makes a sound");
    }
}

class Dog extends Animal {
    // 方法重写
    @Override
    void makeSound() {
        System.out.println("The dog barks");
    }

    // 方法重载
    void makeSound(int times) {
        for (int i = 0; i < times; i++) {
            System.out.println("The dog barks");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        myDog.makeSound(); // 输出 "The dog barks",因为这是重写后的方法
        myDog.makeSound(3); // 输出 "The dog barks" 三次,因为这是重载后的方法
    }
}

Dog 类继承了 Animal 类,并重写了 makeSound 方法。同时,Dog 类还提供了一个重载版本的 makeSound 方法,该方法接受一个整数参数 times,表示狗叫的次数。

多态:

对于多态的理解:

广义上的理解:子类对象的多态性、方法的重写;方法的重载

狭义上的理解:子类对象的多态性。

对于同一个行为,不同的子类对象具有不同的表现形式。多态存在的3个条件:①继承 ②重写 ③父类引用指向子类对象。

Java引用变量有两个类型:编译时类型运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。

若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)

多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法)  “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)

多态的体现:

在Java中的体现:对象的多态性:父类的引用指向子类的对象

格式:(父类类型:指子类继承的父类类型,或者实现的接口类型)

父类类型 变量名 = 子类对象;Object obj = new String("hello"); 父类的引用指向子类的对象。

Person p = new Student();

Object o = new Person();//Object类型的变量o,指向Person类型的对象

o = new Student(); //Object类型的变量o,指向Student类型的对象

对象的多态:在Java中,子类的对象可以替代父类的对象使用。所以,一个引用类型变量可能指向(引用)多种不同类型的对象

方法内局部变量的赋值体现多态


public class TestPet {
    public static void main(String[] args) {
        //多态引用
        Pet pet = new Dog();
        pet.setNickname("小白");

        //多态的表现形式
        /*
        编译时看父类:只能调用父类声明的方法,不能调用子类扩展的方法;
        运行时,看“子类”,如果子类重写了方法,一定是执行子类重写的方法体;
         */
        pet.eat();//运行时执行子类Dog重写的方法
//      pet.watchHouse();//不能调用Dog子类扩展的方法

        pet = new Cat();
        pet.setNickname("雪球");
        pet.eat();//运行时执行子类Cat重写的方法
    }
}
方法的形参声明体现多态 
public class Person{
    private Pet pet;
    public void adopt(Pet pet) {//形参是父类类型,实参是子类对象
        this.pet = pet;
    }
    public void feed(){
        pet.eat();//pet实际引用的对象类型不同,执行的eat方法也不同
    }
}
package com.atguigu.polymorphism.grammar;

public class TestPerson {
    public static void main(String[] args) {
        Person person = new Person();

        Dog dog = new Dog();
        dog.setNickname("小白");
        person.adopt(dog);//实参是dog子类对象,形参是父类Pet类型
        person.feed();

        Cat cat = new Cat();
        cat.setNickname("雪球");
        person.adopt(cat);//实参是cat子类对象,形参是父类Pet类型
        person.feed();
    }
}
方法返回值类型体现多态
public class PetShop {
    //返回值类型是父类类型,实际返回的是子类对象
    public Pet sale(String type){
        switch (type){
            case "Dog":
                return new Dog();
            case "Cat":
                return new Cat();
        }
        return null;
    }
}


public class TestPetShop {
    public static void main(String[] args) {
        PetShop shop = new PetShop();

        Pet dog = shop.sale("Dog");
        dog.setNickname("小白");
        dog.eat();

        Pet cat = shop.sale("Cat");
        cat.setNickname("雪球");
        cat.eat();
    }
}

多态的好处和弊端

好处:变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编写更灵活、功能更强大,可维护性和扩展性更好了。

弊端:一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法。

开发中:使用父类做方法的形参,是多态使用最多的场合。即使增加了新的子类,方法也无需改变,提高了扩展性,符合开闭原则。【开闭原则OCP】对扩展开放,对修改关闭通俗解释:软件系统中的各种组件,如模块(Modules)、类(Classes)以及功能(Functions)等,应该在不修改现有代码的基础上,引入新功能

多态的使用:

①虚拟方法调用:

在Java中虚方法是指在编译阶段不能确定方法的调用入口地址,在运行阶段才能确定的方法,即可能被重写的方法。

Person e = new Student();
e.getInfo();  //调用Student类的getInfo()方法

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

②“编译看左边,运行看右边”。

③属性,不存在多态性。

多态的逆过程:向下转型,使用强转符()。

因为多态,就一定会有把子类对象赋值给父类变量的时候,这个时候,在编译期间,就会出现类型转换的现象。

但是,使用父类变量接收了子类对象之后,我们就不能调用子类拥有,而父类没有的方法了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做类型转换,使得编译通过

向上转型当左边的变量的类型(父类) > 右边对象/变量的类型(子类),我们就称为向上转型

    • 此时,编译时按照左边变量的类型处理,就只能调用父类中有的变量和方法,不能调用子类特有的变量和方法了
    • 但是,运行时,仍然是对象本身的类型,所以执行的方法是子类重写的方法体。
    • 此时,一定是安全的,而且也是自动完成的

向下转型当左边的变量的类型(子类)<右边对象/变量的编译时类型(父类),我们就称为向下转型

    • 此时,编译时按照左边变量的类型处理,就可以调用子类特有的变量和方法了
    • 但是,运行时,仍然是对象本身的类型
    • 不是所有通过编译的向下转型都是正确的,可能会发生ClassCastException,为了安全,可以通过isInstanceof关键字进行判断。
  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值