Java封装、继承和多态

113 阅读6分钟

一、封装:

封装的介绍:

  • 定义:封装就是把抽象出的数据【属性】和对数据的操作【方法】封装在一起,数据被保护在内部,出现的其他部分只有通过被授权的操作【方法】,才能对数据进行操作。

  • 优点:

    • 可以隐藏实现细节:方法(连接数据库)<--调用(传入参数..);
    • 可以对数据进行验证,保证安全合理
  • 实现步骤:

    • 将属性进行私有 private【不能直接修改属性】;

    • 提供一个公共的set方法,用于对属性判断并赋值

      public void setXxx(类型 参数名){ 
          //加入数据验证的业务逻辑
           属性 = 参数名
       }
      
    • 提供一个公共的get方法,用于获取属性值;

      public XX getXxx(){
          // 权限判断
          return xxx;
      }
      
  • 举例:做一个小程序,不能随便查看别人的年龄,工资等隐私,并对设置年龄进行合理的验证,年龄合理就设置,否则就默认,年龄必须在1-120之间,工资不能直接查看,姓名的长度在2-6之间:

    class Person{
        public String name; // 名字 公开
        private int age;    // 年龄 私有化
        private double salary; // 工资 私有化
    ​
        // 设置 名字
        public void setName(String name) {
            if(name.length() >= 2 && name.length() <= 6) {
                this.name = name;
            }else{
                System.out.println("名字的长度不对,需要(2-6)个字符,默认名字");
                this.name = "无名人";
            }
        }
        // 获取 姓名
        public String getName() {
            return name;
        }
    ​
        // 年龄
        public int getAge() {
            return age;
        }
    ​
        public void setAge(int age) {
            if(age >=1 && age <= 120){
                this.age = age;
            } else {
                System.out.println("你设置的年龄不对,需要在(1-120)之间,给默认年龄为18");
                this.age = 18;
            }
        }
    ​
    ​
        // 工资
        public double getSalary() {
            // 可以增加对当前对象的权限判断
            return salary;
        }
    ​
        public void setSalary(double salary) {
            this.salary = salary;
        }
    ​
        public String info() {
            return "信息为:name = " + name + " age = " + age + " salary = " + salary;
        }
    }
    ​
    //然后我们在main主函数里面进行创建对象public class Encapsulation {
        public static void main(String[] args) {
            /*
             * 练习:请大家看一个小程序,不能随便查看别人的年龄,工资等隐私,并对设置的年龄进行
             * 合理的验证,年龄合理就设置,否则就默认,年龄必须在1-120之间工资不能直接查看,name
             * 的长度在2-6之间。
             * */
            Person person = new Person();
            person.setName("Jack");
            person.setAge(30);
            person.setSalary(30000);
            System.out.println("----Jack的信息-----------");
            System.out.println(person.info());
        }
    }
    

    使用构造器指定属性:

    class Person{
        public String name; // 名字 公开
        private int age;    // 年龄 私有化
        private double salary; // 工资 私有化
    ​
        // 快捷键 alt+insert
        public Person() {
        }
    ​
        // 三个属性的构造器
        public Person(String name, int age, double salary) {
            // this.name = name;
            // this.age = age;
            // this.salary = salary;
            // 在构造器中进行值的校验
            setName(name);
            setAge(age);
            setSalary(salary);
        }
    ​
        // 设置 名字
        public void setName(String name) {
            if(name.length() >= 2 && name.length() <= 6) {
                this.name = name;
            }else{
                System.out.println("名字的长度不对,需要(2-6)个字符,默认名字");
                this.name = "无名人";
            }
        }
        // 获取 姓名
        public String getName() {
            return name;
        }
    ​
        // 年龄
        public int getAge() {
            return age;
        }
    ​
        public void setAge(int age) {
            if(age >=1 && age <= 120){
                this.age = age;
            } else {
                System.out.println("你设置的年龄不对,需要在(1-120)之间,给默认年龄为18");
                this.age = 18;
            }
        }
    ​
    ​
        // 工资
        public double getSalary() {
            // 可以增加对当前对象的权限判断
            return salary;
        }
    ​
        public void setSalary(double salary) {
            this.salary = salary;
        }
    ​
        public String info() {
            return "信息为:name = " + name + " age = " + age + " salary = " + salary;
        }
    }
    ​
    // 此时main中就可以这么创建对象
    // 使用构造器指定属性
    Person smith = new Person("smith", 2000, 50000);
    System.out.println("----smith的信息-----------");
    System.out.println(smith.info());
    

二、继承:

  • 我们先来看一段代码:

    // 大学生-> 模拟大学生考试情况
    public class Graduate {
        public String name;
        public int age;
        private double score;
    ​
        public void setScore(double score) {
            this.score = score;
        }
    ​
        public void testing(){  // 和 Pupil 不一样
            System.out.println("大学生 " + name + "正在考高数..");
        }
    ​
        public void showInfo() {
            System.out.println("姓名:"+ name + " 年龄:" + age + " 分数:" + score);
        }
    }
    
    // 小学生-> 模拟小学生考试情况
    public class Pupil {
        public String name;
        public int age;
        private double score;
    ​
        public void setScore(double score) {
            this.score = score;
        }
    ​
        public void testing(){
            System.out.println("小学生 " + name + "正在考小学数学..");
        }
    ​
        public void showInfo() {
            System.out.println("姓名:"+ name + " 年龄:" + age + " 分数:" + score);
        }
    }
    
  • 上面我们编写了两个类,一个是Pupil类,一个是Graduate类。但是我们发现 两个类的属性(name, age, score)和方法(testing,showInfo) 有很多相同之处,怎么办呢? 这时候我们就可以用继承(代码的复用性)来很好的解决这个问题。

  • 继承的介绍:

    • 继承:继承可以解决代码服复用,让我们的编程更加靠近人类思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。

    • 继承语法:

      • class 子类 extends 父类 { }
      • 子类就会自动拥有父类的属性和方法;
      • 父类又叫超类、基类;
      • 子类又叫派生类;
    • 继承的细节:

      • 子类继承了所有的属性和方法,非私有属性的方法可以直接访问,但是私有属性或方法不能再子类中直接访问,要通过公共的方法去访问呢;
      • 子类必须调用父类的构造器,完成父类的初始化;
      • 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中 super() 去指定使用父类的那个构造器完成对父类的初始化工作,否则,编译不会通过;
      • super 在使用时,必须放在构造器的第一行(super只能在构造器中使用);
      • super() 和 this() 都只能在构造器的第一行,因此这两个方法不能共存一个构造器;
      • 子类最多只能继承一个父类(指直接继承),即java中是单继承继承。思考【如何让A类继承B类和C类?.... A 继承 > B 继承 > C 继承】;
      • 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系;

    案例:

    • 此时我们再来看之间大学生与小学生的问题:

      • 首先我们给创建一个 Student 的基类,将 Pulic 和Graduate 类中相同的属性和方法提取出来:

        // Student.class
        // 父类,是Pupil 和 Graduate 类的父类
        public class Student {
        ​
            public String name;
            public int age;
            private double score;
        ​
            public void setScore(double score) {
                this.score = score;
            }
        ​
            public void showInfo() {
                System.out.println("姓名:"+ name + " 年龄:" + age + " 分数:" + score);
            }
        }
        
      • 创建 Pulic 类继承 Student 类:

        public class Pupil extends Student {
            // 小学生
        ​
            public void testing(){  // 和 Pupil 不一样
                System.out.println("小学生" + name + "正在考数学..");
            }
        ​
        }
        
      • 创建 Graduate 类继承 Student 类:

        public class Graduate extends Student{
            // 大学生
        ​
            public void testing(){  // 和 Pupil 不一样
                System.out.println("小学生" + name + "正在考高数..");
            }
        ​
        }
        
      • main 主函数中创建这两个对象:

        public static void main(String[] args) {
               
        ​
                Pupil pupil = new Pupil();
                pupil.name = "小明~";
                pupil.age = 6;
                pupil.testing();
                pupil.setScore(66);
                pupil.showInfo();
        ​
                System.out.println("----------------");
                Graduate graduate = new Graduate();
                graduate.name = "大明~";
                graduate.age = 6;
                graduate.testing();
                graduate.setScore(99);
                graduate.showInfo();
        ​
                // 运行结果:
                // 小学生小明~正在考数学..
                // 姓名:小明~ 年龄:6 分数:66.0
                // ----------------
                // 小学生大明~正在考高数..
                // 姓名:大明~ 年龄:6 分数:99.0
        ​
            }
        

三、多态:

  • 首先看一个主人喂食的故事: image.png
问题:当主人有不同的食物,需要给不同的动物喂食,这就需要在父类中写许多重载的方法:cat -> fish、 dog -> bone、  pig - rice...
传统的方法带来的问题是:代码的复用性不高,而且不利于维护,因此我们需要 多态 来解决问题。
  • 多态的介绍:

    • 多态:方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承的基础之上的;

    • 多态的具体体现:

      • 方法的多态:重写和重载就体现多态;

      • 对象的多态(重点,难点):

        • 一个对象的编译类型和运行类型可以不一致;

        • 编译类型在定义对象时,就确定,不能改变;

        • 运行类型是可以变化的;

        • 编译类型看定义时 = 号的 左边,运行类型看 = 号的 右边;

        • 案列:

          Animal animal = new Dog(); //【animal 编译类型时Animal,运行类型Dog】
          animal = new Cat(); //【animal 的运行类型变成了cat,编译类型任然是 Animal】
          
  • 此时我们以 多态 来看之间主人喂食的这个程序:

    • 创建一个Master类:

      class Master {
          private String name;
      
          public Master(String name) {
              this.name = name;
          }
      
          //    public void feed(Dog dog, Bone bone) {
          //        System.out.println(name + "给" + dog.getName() + "吃" + bone.getName());
          //    }
          //
          //    public void feed(Cat cat, Fish fish) {
          //        System.out.println(name + "给" + cat.getName() + "吃" + fish.getName());
          //    }
      
      	//    上面代码 复用性差,不利于扩展和维护, 我们可以使用多态
          public void feed(Animal animal, Food food) {
              System.out.println(name + "给" + animal.getName() + "吃" + food.getName());
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      }
      
    • 分别创建Food,Fish,Bone,Animal,Dog,Cat类:

      class Food {
          private String name;
      
          public Food(String name) {
              this.name = name;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      }
      
      class Fish extends Food {
          public Fish(String name) {
              super(name);
          }
      }
      
      class Bone extends Food{
          public Bone(String name) {
              super(name);
          }
      }
      
      class Animal {
          private String name;
      
          public Animal(String name) {
              this.name = name;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      }
      
      class Dog extends Animal {
          public Dog(String name) {
              super(name);
          }
      }
      
      class Cat extends Animal {
          public Cat(String name) {
              super(name);
          }
      }
      
    • 上Master类中feed方法,我们使用多态将解决了之前的繁琐,不利于维护的问题。我们可以在main方法创建并调用这个方法,看一下运行结果:

      public static void main(String[] args) {
          
          Master tom = new Master("汤姆");
          Dog dog = new Dog("小白狗");
          Bone bone = new Bone("大棒骨");
          Cat cat = new Cat("小黄猫");
          Fish fish = new Fish("黄丽鱼");
      
          tom.feed(dog, bone);
          tom.feed(cat, fish);
      
      }
      

    image.png