Java基础学习:10、封装和继承和super、方法重载、多态、动态绑定

封装:

1、概念:

  将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。

2、意义:

  只能通过规定的方法访问数据。

       隐藏类的实例细节,方便修改和实现。 

3、实现步骤:

 

public class Encapsulation {
  public static void main(String[] args) {
    Person person = new Person("张三", 200, 30000);
    System.out.println(person.message());
  }
}

class Person{
  public String name;
  private int age;
  private double salary;

  public Person() {
  }

// 构造方法可以破解封装,那么如果既要实用构造器又要封装呢?
// 将set写在构造器里面 public Person(String name, int age, double salary) { // this.name = name; // this.age = age; // this.salary = salary; setName(name); setAge(age); setSalary(salary); } public String getName() { return name; } public void setName(String name) { if (name.length() >=2 && name.length() <=6) { this.name = name; } else { this.name = "张三"; } } public int getAge() { return age; } public void setAge(int age) { if (age >= 1 && age <= 150) { this.age = age; } else { System.out.println("输入的年龄不对,需要在1~150之间"); this.age = 16; // 给一个默认的年龄 } } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public String message() { return "姓名为:" + name + " " + "年龄为:" + age + " " + "工资为:" + salary; } }

  

继承:

1、定义:继承是类与类的一种关系,是一种“is a”的关系。比如“狗”继承“动物”,这里动物类是狗类的父类或者基类,狗类是动物类的子类或者派生类。

2、继承意义:

  子类拥有父类的所有属性和方法(除了private修饰的属性不能拥有)从而实现了实现代码的复用。

3、语法规则:只要在子类加上extends关键字继承相应的父类就可以了。

 

4、注意点:

  a、子类继承了父类所有的属性和方法,非私有的属性和方法可以在子类直接访问

       但是私有的属性和方法不能再子类直接访问,要通过父类提供的公共方法区访问

public class Student {
  public int score1 = 100;
  public int score2 = 80;
  private int score3 = 90;

  // 父亲提供一个public方法,返回score3
  public int getScore3() {
    return score3;
  }

  public void test1() {
    System.out.println("学生正在考试数学");
  }

  public void test2() {
    System.out.println("学生正在考试英语");
  }

  private void test3() {
    System.out.println("学生正在考试语文");
  }

  // 父亲提供一个public方法,调用test03
  public void callTest() {
    test3();
  }

}

  b、子类必须调用父类的构造器,完成父类的初始化。

  c、当创建子类对象时,不管使用子类哪个构造器,默认情况都会去调用父类的无参构造器。

       如果父类没有提供无参构造器,则必须在子类的构造器中用supre去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译 不会通过。

  d、如果希望指定去调用父类的某个构造器,则需要显示的调用一下:super(参数列表);

  e、super在使用时,必须放在构造器第一行(super只能在构造器中使用)。

  f、super()和this()都只能放在构造器第一行,因此两个方法不能共存在一个构造器中。

  g、Java中所有类都是object类的子类。

  h、父类构造器的调用不限于直接父类,将一直往上追溯到object类(顶级父类)。

  i、子类最多只能继承一个父类,即Java中的单继承机制。如果要让A继承B类和C类呢?【A继承B,B继承C】。

  j、不能滥用继承,子类和父类必须满足is-a的逻辑关系:如 猫是动物。

 

继承内存图:

 

继承中this和super:

 

public class ExtendsHomeWork {
  public static void main(String[] args) {
    C c = new C();
  }
}

 

 

super关键字:

1、定义:super代表父类的引用,用于访问父类的属性、方法、构造器。

2、基本用法:

  a、用于访问父类的属性,但不能访问父类的private属性:super.属性名;

  b、用于访问父类的方法,但不能访问父类private方法:super.方法名(参数列表);

  c、访问父类的构造器:super(参数列表); 只可以放在构造器第一句,只能出现一句。

3、意义及细节:

  a、super调用父类构造器的意义(分工明确,父类的属性由父类初始化,子类的属性由子类初始化)。

class NotePad extends Computer {
  private String color;

  public NotePad(String cpu, int memory, int disk, String color) {
    super(cpu, memory, disk);
    this.color = color;
  }
}

  b、当子类和父类中的成员(属性和方法)重名时,为了访问父类成员,必须通过super,如果没有重名,则super、this以及直接访问都是一样的。

  在子类中使用:直接访问、this、super三者的区别:

  直接访问和this:

  1、先找本类,本类有,则调用。

  2、本类没有,找父类(如果有且可以调用,则调用;如果有但是不能调用,则报错)。

  3、如果父类没有,则继续往上找,直到object类。一直没有找到,提示不存在。

  super:

  1、直接找父类,不找本类。其余规则如上。

class Computer {
  private String cpu;
  private int memory;
  private int disk;
  public int num = 12;

  public Computer(String cpu, int memory, int disk) {
    this.cpu = cpu;
    this.memory = memory;
    this.disk = disk;
  }
}

class NotePad extends Computer {
  private String color;
  public int num = 1;

  public void test() {
    System.out.println(num);        // 1
    System.out.println(this.num);   // 1
    System.out.println(super.num);  // 12
  }
}

  

  c、super访问不限于直接父类,如果爷爷类也有同名成员,也可以使用super去访问。如果多个上级类都有同名成员,遵循就近原则。

 

继承方法重写:

1、定义:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么就是子类方法覆盖了父类方法。

2、满足条件:

  a、子类的方法的形参列表、方法名称要和父类的参数方法名称完全一样。

  b、子类方法的返回类型要和父类一样活着是父类返回类型的子类:如父类返回类型是object,子类是String。

  c、子类方法不可以缩小父类方法的访问权限:public > protected > 默认 > private。

 

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

1、方法重载:Java中允许在同一个类中多个同名方法的存在,到要求形参列表不一致。

2、方法重写:子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么就是子类方法覆盖了父类方法。

 

多态:

一、多态概述:

 

1、多态是继封装、继承之后,面向对象的第三大特性。

2、多态现实意义理解:

 

  现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。

 

  Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。

 

3.多态体现为父类引用变量可以指向子类对象。

Animal animal = new Dog("米奇", 23);

 

 

4.前提条件:必须有子父类继承关系。

 

注意:在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法

 // 运行时,执行到该行的时候,animal运行类型是dog,所以是dog的eat();
 animal.eat();  // 小狗吃东西

  

 

5.多态的定义与使用格式

 

定义格式:父类类型 变量名=new 子类类型();

 

6.理解:

 

  多态是同一个行为具有多个不同表现形式或形态的能力。

 

  多态就是同一个接口,使用不同的实例而执行不同操作。

 

二、多态成员特点:

1、成员属性(变量):

  属性没有重写之说,属性的值看编译类型:直接调用的就是animal的属性的值:

 

Animal animal = new Dog("米奇", 23);   // 编译类型是animal
System.out.println(animal.num);   // 父类的num

 

2、成员方法:

  成员方法有重写,如果重写,调用的就是子类方法,无重写,调用父类:

 

animal.say();  // 子类重写父类say方法,调用的是子类

 

  

 

 

  public static void main(String[] args) {
    // animal 编译类型是 Animal,运行类型是 dog
    Animal animal = new Dog("米奇", 23);
    // 运行时,执行到该行的时候,animal运行类型是dog,所以是dog的eat();

  // 如果子类没有重写父类的方法,那么调用的就是父类的方法 animal.AnimalEat(); // 父类的方法:动物吃东西

  // 如果子类重写了父类的方法,那么调用的就是子类的方法。
   animal.say(); // 子类的方法:小狗唱歌
System.out.println(animal.num); // 父类的值:10 }

  

三、多态的转型 :向上转型和向下转型


1、向上转型

  父类的引用指向了子类的对象(多态本身就是向上转型过的过程)
  使用格式:父类类型 变量名=new 子类类型();

Animal animal = new Dog("米奇", 23);

  特点:

    a、可以调用父类的所有成员(变量和方法),但是需要遵循访问权限。

    b、不能调用子类的特有成员,可以调用子类的重写方法:因为在编译阶段,能调用哪些成员,有编译类型决定(编译看左边)。

     为什么不能调用子类成员?

      因为编译的时候就不通过,编译就会报错。

  

总结向上转型    父类类型 变量名=new 子类类型();

  a、编译的时候只能调用父类方法。

Animal animal = new Dog("米奇", 23);
animal.AnimalEat();   // 父类方法
animal.DogEat(); // 子类方法报错

  b、运行的时候还是先从子类找该方法,如果有就调用,没有往上找。

     (就是如果子类重写了父类方法,运行的时候就会调用子类,没有重写就不会调用子类方法,调用父类方法)

// 子类重写了父类say方法:编译时调用的是父类方法,但是实际运行时调用的是子类重写的say方法
animal.say(); // 小狗唱歌

  

 

2、向下转型

1、前提:如果要向下转型必须先向上转型:

Animal animal = new Dog;

2、语法:子类类型 引用名  = (子类类型) 父类引用

Dog dog = (Dog) animal;

3、注意点:

  a、只能强转父类引用,不能强转父类对象。

Dog dog = (Dog) Animal;   // 错误

  b、向下转型后,可以调用子类中所有成员。

dog.DogEat();

  

instanceof:比较操作符

1、意义:

  1、用于判断对象的运行类型是否为某个类型 或者 为某类型的子类型。

  2、它的作用是测试它左边的对象是否是它右边的类的实例

public class instanceOf {
  public static void main(String[] args) {
    BB bb = new BB();
    System.out.println(bb instanceof BB);  // true
    System.out.println(bb instanceof AA);  // true

    // aa的编译类型是AA,aa的运行类型是BB
    AA aa = new BB();
    // 下面这句话意思:aa的运行类型BB 是 AA 的类或者子类吗?
    System.out.println(aa instanceof AA);  // true
    // 下面这句话意思:aa的运行类型BB 是 BB 的类或者子类吗?
    System.out.println(aa instanceof BB);  // true
  }
}

class AA {}
class BB extends AA{}

2、实际用法:

  在写程序的时候,如果要进行类型转换,我们最好使用instanceof运算符来判断

       它左边的对象是否是它右边的类的实例,再进行强制转换。

 

动态绑定:

1、当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。

 

public class DynamicBinding {
  public static void main(String[] args) {
    // father的编译类型:Father;father的运行类型:Son。
    Father father = new Son();
    // 调用方法时,会和运行类型绑定,cal方法会调用子类的sum方法。
    System.out.println(father.cal());  // 40
  }
}

class Father{
  int num = 10;

  public int cal() {
    return sum() + 20;
  }

  public int sum() {
    return num + 10;
  }
}
class Son extends Father{ int num = 20; public int sum() { return num; } public int cal() { return num + 20; } }

 

2、当调用对象属性的时候,没有动态绑定,哪里声明,哪里使用。

 

多态参数:

  方法的形参类型为父类类型,实参为子类类型:

 

public class Main {
  public static void main(String[] args) {
    Ordinary tom = new Ordinary("tom", 2500);
    Manager milan = new Manager("milan", 5000, 200000);
    Main main = new Main();
    main.showEmpAnnual(tom);
    main.showEmpAnnual(milan);

    main.testWork(tom);
    main.testWork(milan);

  }

  // 方法:输出普通员工和经理的工资
  public void showEmpAnnual(Employee e) {
    System.out.println(e.getAnnual());
  }

  // 添加一个方法:如果是普通员工,则调用work方法,如果是经理,则调用manage方法
  // 使用 类型判断 + 向下转型   这里 e 就是 Employee的引用类型 Employee e =
  public void testWork(Employee e) {
    if (e instanceof Ordinary) {
      /*
      下面这句相当于:
      Ordinary ordinary = (Ordinary)e;
      ordinary.work;
      的简写
       * */
      ((Ordinary) e).work();  // 向下转型

    } else if (e instanceof Manager) {
      ((Manager) e).manage();
    } else {
      System.out.println("不处理");
    }
  }
}

 

toString方法:

1、他是object类下面的一个方法,因此所有的类都可以使用。

2、默认返回:全类名(包名 + 类名)+ @ + 十六进制哈希值。

3、当直接输出一个对象时,默认会调用tostring方法。

public class ToString {
  public static void main(String[] args) {
    AA aa = new AA();
    System.out.println(aa.toString());  // com.homework.AA@776ec8df

    System.out.println(aa);  // com.homework.AA@776ec8df

  }
}

class AA {}

4、子类往往会重写tostring方法,用于返回对象的属性信息

public class ToString {
  public static void main(String[] args) {
    AA aa = new AA("mary", 23);
    System.out.println(aa.toString());  // AA{name='mary', age=23}

    System.out.println(aa);  // AA{name='mary', age=23}

  }
}

class AA {
  String name;
  int age;

  public AA(String name, int age) {
    this.name = name;
    this.age = age;
  }

  @Override
  public String toString() {
    return "AA{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
  }
}

  

 

 

 

 

 

 

 

 

  

 

posted @ 2021-07-23 14:47  Y字仇杀队  阅读(73)  评论(0编辑  收藏  举报