Java封装、继承、多态

149 阅读8分钟

封装、继承、多态

封装:解决数据安全问题

继承:解决代码冗余(重复)问题

多态:解决程序扩展问题(新增功能)

权限修饰符

  • private:表示私有的,表示类访问权限。只能在本类中访问,离开本类之后,就不能直接访问。
  • 不写(缺省):表示包私有,表示包访问权限。访问者的包必须和当前定义类的包相同才能访问。(两个类在同一个包下)
  • protected:表示子类访问权限,同包中的可以访问,即使不同包,但是有继承关系,也可以访问。
  • public:表示全局的,可以公共访问权限,如某个字段/方法,使用了public修饰,则可以在当前项目中任何地方访问。
修饰符类内部同一个包子类任何地方
private
protected
public

1.1 封装

  • 封装2个原则:
    • 成员变量使用private 修饰
    • 给成员变量提供公有的get/set方法
    • 虽然类中属性是私有,但可以设置公有方法来间接进行私有属性操作
public class fengzhuang {
    public static void main(String[] args) {
        Car c1 =new Car();
        Car c = new Car(12,"红色");
        System.out.println(c.getColor() + c.getPrice());
        c1.setColor("蓝色");
        System.out.println("颜色是" + c1.getColor());
    }
}

class Car {
    private double price;
    private String color;

    public Car(double price, String color) {
        this.price = price;
        this.color = color;
    }
    
    public Car() {
    }
    //设置公有方法
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        if (price <= 10) {

        } else {
            this.price = price;
        }
    }

    public void run() {
        System.out.println("跑程序ing");
    }
}
MARK
  • 类:只用使用public、缺省修饰
  • 类的内部成员、可以使用四种权限修饰进行修饰。

1.2 继承

  • 继承解决代码重复的问题

  • 子类(派生类) 继承 父类(基类),子类自动拥有父类的一切成员(成员变量和方法),但是只能访问可访问;

  • 格式:

        class A{
            //属性 方法
        }
        class B extends A{
        
        }
  • Object是根基类,所有的类都是Object的子类;如果没有显式的声明其父类时,则默认继承于 Java.lang.Oject

  • MARK:

    • Java只支持单继承(子类只能有一个父类)
    • 构造方法的执行顺序:子类构造方法执行之前 必须要先执行父类构造方法;子类构造方法没有显示添加有关父类构造方法的调用此时编译器默认添加一个父类无参的构造方法;一旦子类构造方法中添加了有关父类构造的调用,编译器不再默认添加

父类

//员工类 基类(父类)
public class Employee {
    private int id;
    private String name;
    private char gender;
    private double salary;

    public Employee() {
    }

    public Employee(int id, String name, char gender, double salary) {
        this.id = id;
        this.name = name;
        this.gender = gender;
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public void show() {
        System.out.println("id: " + id + " name: " + name + " gender: " + gender + " salary: " + salary);
    }
}

子类

public class ProgramManager extends Employee {
    private String exp;
    private double bonus;

    public ProgramManager() {
    }

    public ProgramManager(int id, String name, char gender, double salary, String exp, double bonus) {
        super(id, name, gender, salary);
        this.exp = exp;
        this.bonus = bonus;
    }

    public String getExp() {
        return exp;
    }

    public void setExp(String exp) {
        this.exp = exp;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }
}

1.2.1 方法重写

  • 子类继承父类的方法之后,对方法的实现不满意,就重写

  • 方法重写 发生在继承关系的前提下;

  • 方法重载和方法重写的区别:

    • 方法重载:同一个类体中,方法名相同 参数列表不同
    • 方法重写:发生在继承关系的前提下;子类重写了继承下来的方法;
MARK
  • 方法覆盖只是针对于方法,和属性无关。
  • 私有方法无法覆盖。
  • 构造方法不能被继承,所以构造方法也不能被覆盖。
  • 方法覆盖只是针对于“实例方法”,“静态方法覆盖”没有意义。

父类方法:

 public void show() {
        System.out.println("id: " + id + " name: " + name +
                " gender: " + gender + " salary: " + salary);
    }

子类重写父类方法

    public void show() {
        //逻辑
       
        @Override
        System.out.println("id: " + getId() + " name: " + getName() +
                " gender: " + getGender() + " salary: " + getSalary() + " hot: " + hot);
    }

1.3 package和import

  • package 包机制 解决类名冲突问题;一般是公司域名倒写
  • package 必须在首行;
  • import 导入类的全路径(包名+类名);使用的类型和当前类在不同的包下面的时候,必须 import
  • java.lang包下面的所有类不需要显示 import

1.4 多态

多态:用来解决程序扩展问题(新增功能)
设计原则:

开闭原则:对新增开放,对修改关闭 可扩展性比较好。使用父类做方法的形参,是多态使用最多的场合。即使增加了新的子类,方法也无需改变,提高了扩展性符个开闭原则 多态体现:继承,重写,父类引用指向子类对象;

多态:

运行过程中,根据实际对象去调用其对应的方法;

多态适用性:

适用于方法,不适用于属性。

多态的弊端:
 Person p2 = new Man();    
 问题:针对于创建的对象,在内存中是否加载了 Man 类中声明的特有的属性和方法?    加载了
 问题: 能不能直接调用 Man 中加的特有的属性和方法呢? 不能
在多态的场景下,调用方法时。
  • 编译时,认为方法是左边声明的父类的类型的方法(即被重写的方法)
  • 执行时,实际执行的是子类重写父类的方法。
特点
  • 多态情况下,子类和父类存在同名的成员变量时,访问的时父类的成员变量
  • 多态情况下,子父类存在同名的非静态成员方法时,访问的是子类中重写的方法
  • 多态情况下,子父类存在同名的静态成员变量成员方法时,访问的是父类的成员函数

多态的应用:马戏团案例

父类动物类:

class Animal {
    private String name;
    private int age;

    public Animal() {
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void get_animal(){
        System.out.println("动物:" + this.name + "年龄:"+this.age);
    }

    public void play() {
    }
}

子类 猴子类:子类对继承的方法进行了重写

class Monkey extends Animal {
    @Override
    public void play() {
        System.out.println("表演骑单车");
    }
}

子类 大象类

class Elephant extends Animal {
    @Override
    public void play() {
        System.out.println("表演喷水");
    }
}

马戏团类

public class Circus {
    /**
     * 形参类型是父类引用类型,可以传 子类对象进来
     * 这是因为 父类引用可以指向子类对象
     * 运行时 根据真真正正的实际对象去调用其重写的方法 就能体现出多态
     * 新增功能时不需要改动马戏团源码 ,直接新增类型就可以 符合开闭原则
     */
    public void performance(Animal animal) {
        animal.play();
    }
}

测试类

public static void main(String[] args) {
    Circus circus = new Circus();
    Animal animal = new Animal("猴子",3);
    animal.get_animal();
    circus.performance(new Monkey());
    // circus.performance(new Elephant());
}
多态练习题:车 宝马 奔驰

父类 车类型

public class Car {
    private String color;
    private int wheelNum;

    public Car() {
    }

    public Car(String color, int wheelNum) {
        this.color = color;
        this.wheelNum = wheelNum;
    }

    public void run() {
    }

   //getter setter 方法
}

子类 宝马类

public class Bmw extends Car {
    @Override
    public void run() {
        System.out.println("宝马车跑");
    }
}

司机类

public class Driver {
    public void drive(Car car) {
        car.run();
    }
}

测试类

private static void method1() {
        Driver driver = new Driver();
        Car bmw = new Bmw();
        driver.drive(bmw);
    }

多态的向上转型和向下转型

向上转型
  • 本质:父类的引用指向了子类的对象。
  • 语法: 父类类型 引用名 = new 子类类型();
  • 特点:可以调用父类的所有可访问成员(遵循相应的访问权限),但是不能调用子类的特有成员,而运行效果则需要看子类的具体实现。

例子:就是上述代码展现

向下转型
  • 语法: 子类类型 引用名 = (子类类型)父类引用;
  • 特点:
    1. 只能强制转换父类的引用,不能强制转换父类的对象;
    2. 要求父类的引用必须指向的是当前目标类型的对象;
    3. 可以调用子类类型中的所有成员。 例子:
/*
        Animal animal = new Monkey();
        animal.say();
        
        Animal animal_1 = new Monkey();
        Monkey monkey = (Monkey)animal_1;
*/

package com.package4;

public class duotai {
    public static void main(String[] args) {
        Circus circus = new Circus();
        Animal animal = new Animal("猴子",3);
        animal.get_animal();
        circus.performance(new Monkey());
        Animal animal1 = new Monkey();
        circus.performance1((Monkey)animal1);

    }
}

// 增加Monkey的特有方法

class Animal {
    private String name;
    private int age;

    public Animal() {
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void get_animal(){
        System.out.println("动物:" + this.name + "年龄:"+this.age);
    }

    public void play() {
    }
}

class Monkey extends Animal {
    @Override
    public void play() {
    System.out.println("表演骑单车");
  }
  public void play1(){
        System.out.println("猴子爱吃香蕉");
  }
}

class Elephant extends Animal {
    @Override
    public void play() {
        System.out.println("表演喷水");
    }
}
class Circus {
    /**
     * 形参类型是父类引用类型,可以传子类对象进来
     * 这是因为父类引用可以指向子类对象
     * 运行时根据真真正正的实际对象去调用其重写的方法 就能体现出多态
     * 新增功能时不需要改动 马戏团源码 ,直接新增类型就可以 符合开闭原则
     */
    public void performance(Animal animal) {
        animal.play();
    }
    public void performance1(Monkey m) {
        m.play1();
    }
}

Mark: instanceof

向下转型可能会出现:类型转换异常。建议在向下转型之前,使用 instanceof 进行判断,避免出现类型转换异常。
格式: a instanceof A:判断对象a是否是类A的实例。

  • 如果 a instanceof A 返回 true,则: a instanceof superA 返回也是true。 其中,A 是superA的子类。
    Animal animal_1 = new Monkey();
    if(animal_1 instanceof Monkey){
        Monkey monkey = (Monkey)animal_1;
    }
    
    
    ```
public static void main(String[] args) {
    Circus circus = new Circus();
    Animal animal = new Animal("猴子",3);
    animal.get_animal();
    circus.performance(new Monkey());
    Animal animal1 = new Monkey();
    //instanceof 判断
    if(animal1 instanceof Monkey) {
        circus.performance1((Monkey) animal1);
    }