Java基础学习:10、封装和继承和super、方法重载、多态、动态绑定
阅读原文时间:2023年07月09日阅读:3

封装:

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 +
'}';
}
}