18.Java 封装详解/多态详解/类对象转型详解

封装概述

简述

封装是面向对象的三大特征之一。

封装优点

提高代码的安全性。
提高代码的复用性。
“高内聚”:封装细节,便于修改内部代码,提高可维护性。
“低耦合”:简化外部调用,便于调用者使用,便于扩展和写作。

封装的实现--使用访问控制符

  1. private 表示私有,只能自己类能访问
  2. default 表示没有修饰符修饰,只有同一个包的类能访问。
  3. protected 表示可以被同一个包的类以及其他包中的子类访问。
  4. public 表示谁都可以调用。

关于 protected 的两个细节

  1. 若父类和子类在同一个包中,子类可访问父类的 protected 成员,也可以访问父类对象的 protected 成员。
  2. 若子类和父类不在同一个包中,子类可访问父类的 protected 成员,不能访问父类对象的 protected 成员。

访问控制符示意图:

封装详解

开发中封装的简单规则:

  1. 属性一般使用 private 访问权限。
  2. 属性私有后,提供相应的 get/set 方法来访问相关属性,这些方法通常是 public 修饰,以提供对属性的赋值与读取操作(注意点:boolean 变量的 get 方法是 is 开头)
  3. boolean 变量的 get 方法是 is 开头的
  4. 一些只用于本类的辅助性方法可以用 private 修饰,希望其他类调用的方法就用 public 修饰。

代码示例:JavaBean 的封装演示

package cn.jungle.test.encapsulation.a;

public class Person {
    // 属性一般使用 private 修饰
    private String name;
    private int age;
    private boolean flag;
    // 为属性提供 public 修饰的 set/get 方法
    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;
    }
    // boolean 类型的属性 get 方法是 is 开头的
    public boolean isFlag(){
        return flag;
    }
    public void setFlag(boolean flag){
        this.flag = flag;
    }
}

代码示例:封装的使用

两个文件:Person.java 和 Test2.java
在 Person.java 中封装,然后 Test2.java 通过方法名去调用 Person.java 中的属性去赋值
(1) Person.java

package cn.jungle.test.encapsulation.a;

public class Person {
    // 属性一般使用 private 修饰
    private String name;
    private int age;
    private boolean flag;
    // 定义空构造方法
    public Person(){

    }
    public Person(String name,int age){
        this.name = name;
        // this.age = age; 这样子是不行的。构造方法中不能直接赋值,应该调用 setAge 方法
        setAge(age);
    }

    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        // 在赋值之前先判断年龄是否合法
        if (age > 130 || age < 0 ){
            this.age = 18; // 不合法赋一个默认值 18
        }else{
            this.age = age; // 合法的话就赋值给属性 age
        }
    }
    public int getAge(){
        return age;
    }
    // toString() 方法的重写
    @Override
    public String toString(){
        return "Person [name=" + name + ",age=" + age + "]";
    }
}

Test2.java

package cn.jungle.test.encapsulation.a;

public class Test2{
    public static void main(String[] args){
        Person p1 = new Person();
        // 下方会编译错误,对于封装以后的属性,不能直接用对象来调用属性进行赋值
//        p1.name = "阿jun";    // 会编译出错
//        p1.age = "200";       // 会编译出错
        // 调用封装方法对属性进行赋值
        p1.setName("阿jun");
        p1.setAge(200);
        // 没有输出打印对象的内存地址,因为当我们去打印某个对象的时候,实际上是去调用了这个对象的 toString() 方法,即打印了相关属性。
        System.out.println(p1);

        Person p2 = new Person("阿jun修炼手册",230);
        // 没有输出打印对象的内存地址,因为当我们去打印某个对象的时候,实际上是去调用了这个对象的 toString() 方法,即打印了相关属性。
        System.out.println(p2);
    }
}

多态

简述

多态指的是同一个方法调用,由于对象不同而产生的不同行为。
多种形态:同一个方法,具体实现会完全不同。
比如:同样是吃饭,东方人喜欢用筷子,西方人容易用叉子

多态的要点

多态的存在有三个必要条件:继承、方法重写、父类引用指向子类对象。
多态是方法的多态,不是属性的多态(多态与属性无关)。
父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。

多态产生的原因

同一个方法调用时,传入不同对象,可能会调用不同的对象方法。
这些对象对应的子类重写了父类的方法。如果子类没有重写父类的方法,就没有多态。

代码示例:多态和类型转换

同一个包的两个文件:Animal.java(放置父类和子类) 和 Test.java(调用测试)

(1)Animal.java

package cn.jungle.polymophism;

// 定义一个父类--动物类
public class Animal {
    public void shout(){
        System.out.println("只叫了一声!");
    }
}
// 定义一个 Dog 子类
class Dog extends Animal{
    // 重写父类方法:shout()
    @Override
    public void shout(){
        System.out.println("汪汪汪!");
    }
}
// 定义一个 Bird 子类
class Bird extends Animal{
    // 重写父类方法:shout()
    @Override
    public void shout(){
        System.out.println("一只黄鹂鸣翠柳!");
    }
}
// 定义一个 Man 子类
class Man extends Animal{
    // 重写父类方法:shout()
    @Override
    public void shout(){
        System.out.println("嘤嘤嘤嘤嘤嘤嘤!");
    }
}
class Cat extends Animal{
    @Override
    public void shout() {
        super.shout();
    }
}

(2)Test.java

package cn.jungle.polymophism;

public class Test {
    // 定义一个形参为父类对象的静态方法来测试多态的对象调用
    static void animalCry(Animal a){
        a.shout();
    }
    public static void main(String[] args) {
        // 定义一个 Dog 类的对象
        Dog d = new Dog();
        animalCry(d);         // 输出结果:汪汪汪!
        // 父类引用指向子类对象
        // 定义一个 Man 类的对象并且将其传入 animalCry() 方法进行调用
        animalCry(new Man()); // 输出结果:嘤嘤嘤嘤嘤嘤嘤!
    }
}

对象转型

简述

对象的转型分为两种:向上转型 和 向下转型
父类引用指向子类对象,这个过程称之为向上转型,属于是自动类型转换。
向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。
此时,则需要进行类型的强制转换,称之为向下转型。

闲谈

类型可以随便转,随意可把猫变狗或狗变猫,但是仅仅只能编译通过。
编译通过只是语法没问题,不代表程序可以运行起来。
真正运行时,是什么类型就还是执行什么类型的方法。

代码示例:对象的转型

以 Object 类和其子类 String 进行对象转型的代码举例

package cn.jungle.polymophism;

//以 Object 类和其子类 String 进行对象转型的代码举例
public class TestCasting {
    public static void main(String[] args) {
        Object obj = new String("阿jun修炼手册");   // 类对象向上(子类向父类)可以自动转型,无需强转
        /**
         * obj.charAt(0) 无法调用。编辑器认为 obj 是 Object 类型而不是 String 类型
         * 编写程序时,如果想要调用运行时类型的方法,只能进行强制类型转换,否则无法通过编译器的检查
         */
        String str = (String) obj;         // 向下强制转型
        System.out.println(str.charAt(0)); // 获取字符串中索引位置为 0 的字符
        System.out.println(obj == str);    // true; 因为这俩运行的是同一个对象
    }
}

代码示例:向下类型转换异常

在向下转型过程中,必须将引用变量转成真实的子类类型(运行时的类型),否则会出现类型转换异常 ClassCastException 。

package cn.jungle.polymophism;

public class TestCasting2 {
    public static void main(String[] args) {
        Object obj = new String("阿jun修炼手册!");
        // 真实的子类类型是 String,但是此处向下转型是 StringBuffer,编译不会报错,运行时会报错
        StringBuffer str = (StringBuffer) obj;
        // 编译不会报错,运行时会报错
        System.out.println(str.charAt(0));
    }
}

代码示例:向下转型中使用 instanceof 运算符

使用 instanceof 运算符可以有效避免向下类型转换异常的报错

package cn.jungle.polymophism;

// 向下转型中使用 instanceof 运算符
public class TestCasting3 {
    public static void main(String[] args) {
        // 定义多态对象
        Object obj = new String("阿jun修炼手册!");
        // 判断 obj 对象是不是右边类或者其子类创建的对象
        if (obj instanceof String){  // 运算符结果为 true(没有强制转换之前)
            // 测试输出
            System.out.println("2");
            // obj 向下强制类型转换
            String str = (String)obj;
            System.out.println(str.charAt(0));
        }else if (obj instanceof StringBuffer){  // 结果为 false
            System.out.println("1");
            StringBuffer str = (StringBuffer)obj;
            System.out.println(str.charAt(0));
        }

        // 深入测试:需要依据前方多态知识点的 Animal.java ,这里不去重复复制了
        // Animal a 在没有声明类型之前,可以是 Animal 类的任意一个子类的对象
        Animal a;
        // 创建一个 a 对象的 Bird 子类实例,开辟了内存空间(new Bird()即是开辟)的叫实例,没开辟之前的叫声明。
        a = new Bird();
        a.shout();
        // 多态的应用:此处把对象 a 切换成了 Dog 类的实例
        a = new Dog();
        a.shout();
        // Man 类是 Animal 的子类,
        Animal b = new Man();
        b.shout();
    }
}
posted @ 2021-11-16 17:13  阿jun  阅读(105)  评论(0编辑  收藏  举报