java面向对象三大特征

 

免责声明:java基础资料均来自于韩顺平老师的《循序渐进学Java零基础》教案,具体视频内容可以去B站观看,这些资料仅用于学习交流,不得转载用于商业活动

1.Java面向对象三大特征

Java面向对象编程有三大特征:封装、继承、多态

1.1 封装

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

封装的理解和好处

1)隐藏实现细节:方法(连接数据库)<---d调用(传入参数...)

2)可以对数据进行验证,保证安全合理

1.1.1 封装的实现步骤(三步)

1)将属性进行私有化private【不能直接修改属性】

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

    public void setXxx(类型 参数名){ //Xxx表示某个属性

    //加入数据验证的业务逻辑

    属性=参数名;

  }

3)提供一个公共的(public)get方法,用于获取属性的值

  public 数据类型 getXxx(){ //权限判断,Xxx某个属性

    return xx;

      }

 1 public class Encapsulation01 {
 2     public static void main(String[] args) {
 3         Person person=new Person();
 4         person.setName("张三");
 5         person.setAge(30);
 6         person.setSalary(3000);
 7         System.out.println(person.info());
 8         ;
 9         System.out.println(person.getSalary());
10 
11         System.out.println("=====p的信息=====");
12         //自己使用构造器指定属性
13         Person p=new Person("p",180,50000);
14 
15         System.out.println(p.info());
16     }
17 }
18 
19 class Person{
20     public String name; //名字公开
21     private int age; //age 私有化
22     private double salary;
23 
24     public void say(int n,String name){
25 
26     }
27 
28     //构造器
29     public Person(){
30 
31     }
32 
33     //三个属性构造器
34     public Person(String name,int age,double salary){
35        // this.name=name;
36        // this.age=age;
37        // this.salary=salary;
38 
39         //我们可以将set方法卸载构造器中,这样任然可以验证
40         setName(name);
41         setAge(age);
42         setSalary(salary);
43     }
44 
45     public void setName(String name){
46         //加入对数据的校验,相当于增加了业务逻辑
47         if(name.length()>=2 && name.length()<=20){
48             this.name=name;
49         }else {
50             System.out.println("名字的长度不对,需要(2-6)个字符,默认名字");
51             this.name="无名人";
52         }
53 
54     }
55 
56     public String getName(){
57         return name;
58     }
59 
60     public void setAge(int age){
61         //判断
62         if(age>=1 && age<=120){//如果是合理范围
63             this.age=age;
64         }else{
65             System.out.println("您设置的年龄不对,需要在(1-120),给默认年龄18");
66             this.age=18;
67         }
68     }
69 
70     public int getAge(){
71         return age;
72     }
73 
74     public void setSalary(double salary){
75         this.salary=salary;
76     }
77 
78     public double getSalary(){
79         return salary;
80     }
81 
82     //写一个方法,返回属性信息
83     public String info(){
84         return "信息为 name="+name+" age="+age+"  薪水="+salary;
85     }
86 }

1.2 继承

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

 1.2.1 继承的基本语法

class 子类 extends 父类{

}

1)子类就会自动拥有父类定义的属性和方法

2)父类又叫超类,基类

3)子类又叫派生类

//父类,是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);
    }
}


public class Graduate extends Student{//和Pupil不一样
    public void testing(){
        System.out.println("大学生"+name+"正在考大学数学...");
    }
}


public class Pupil extends Student{
    public void testing(){
        System.out.println("小学生"+name+" 正在考小学数学..");
    }
}


public class Extends01 {
    public static void main(String[] args) {
        Pupil pupil=new Pupil();
        pupil.name="王舞";
        pupil.age=10;
        pupil.testing();
        pupil.setScore(80);
        pupil.showInfo();

        System.out.println("========");
        Graduate graduate=new Graduate();
        graduate.name="李四";
        graduate.age=22;
        graduate.testing();;
        graduate.setScore(89.5);
        graduate.showInfo();
    }
}

 继承给变成带来的便利
1)代码的复用性提高了

2)代码的扩展性和维护性提高了

1.2.2 继承的深入讨论

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
  2. 子类必须调用父类的构造器,完成父类的初始化
  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
  4. 如果希望指定去调用父类的某个构造器,则显式调用一下:super(参数列表)
  5. super在使用时,必须放在构造器第一行(super只能在构造器中使用)
  6. super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
  7. Java所有类都是Object类的子类,Object是所有类的基类
  8. 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
  9. 子类最多只能继承一个父类(指直接继承),即Java中是单继承机制
  10. 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
  1 public class TopBase {//父类是Object
  2 
  3     public TopBase(){
  4         //super(); Object的无参构造器
  5         System.out.println("构造器ToBase()被调用...");
  6     }
  7 }
  8 
  9 public class Base  extends TopBase{ //父类
 10     //4个属性
 11     public int n1=100;
 12     protected int n2=200;
 13     int n3=300;
 14     private  int n4=400;
 15 
 16     public Base(){//无参构造
 17         System.out.println("父类Base()构造器被调用...");
 18     }
 19 
 20     public Base(String name,int age){//有参构造器
 21         //默认super()
 22         System.out.println("父类Base(String name,int age)构造器被调用...");
 23     }
 24 
 25     public Base(String name){
 26         System.out.println("父类Base(String name)构造器被调用...");
 27     }
 28 
 29     //父类提供一个public的方法,返回了n4
 30     public int getN4(){
 31         return n4;
 32     }
 33 
 34     public void test100(){
 35         System.out.println("test100");
 36     }
 37 
 38     protected void test200(){
 39         System.out.println("test200");
 40     }
 41 
 42     void test300(){
 43         System.out.println("test300");
 44     }
 45 
 46     private void test400(){
 47         System.out.println("test400");
 48     }
 49 
 50     //call
 51     public void callTest400(){
 52         test400();
 53     }
 54 }
 55 
 56 public class Sub extends Base{ //子类
 57     public Sub(String name,int age){
 58         //调用父类的无参构造器,如下,或者不写 ,默认就是调用super()
 59         //super(); //父类的无参构造器
 60         //调用父类的Base(String name)构造器
 61         //super("zs");
 62         //调用父类的Base(String name,int age)构造器
 63         super("张三",20);
 64 
 65         //细节:super在使用时,必须放在构造器第一行
 66         //细节:super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器中
 67         //this() //不能再使用了
 68         System.out.println("子类Sub(String name,int age)构造器被调用...");
 69     }
 70 
 71     public Sub(){//无参构造器
 72         //super();//默认调用父类的无参构造器
 73         super("smith",10);
 74         System.out.println("子类Sub()构造器被调用");
 75     }
 76 
 77     //当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
 78     public Sub(String name){
 79         super("tom",30);
 80         System.out.println("子类Sub(String name)构造器被调用...");
 81     }
 82 
 83     public void sayOk(){//子类方法
 84         //非私有的属性和方法可以再子类直接访问
 85         //但是私有属性和方法不能在子类直接访问
 86         System.out.println(n1+" "+n2+" "+n3);
 87         test100();
 88         test200();
 89         test300();
 90         //test400(); //错误
 91         //要通过父类提供公共的方法去访问
 92         System.out.println("n4="+getN4());
 93         callTest400();
 94     }
 95 }
 96 
 97 
 98 public class ExtendsDetail {
 99     public static void main(String[] args) {
100         System.out.println("===第一个对象===");
101         Sub sub=new Sub();//创建了子类对象sub
102         System.out.println("===第二个对象===");
103         Sub sub2=new Sub("jack");//创建了子类对象sub2
104         System.out.println("===第三个对象===");
105         Sub sub3=new Sub("王五",10);//创建了子类对象sub3
106         sub3.sayOk();
107     }
108 }

1.2.3 继承的本质分析(重要)

使用一个案例来分析当子类继承父类,创建子类对象时,内存中到底发生了什么?

提示:当子类对象创建好后,建立查找的关系

 1 public class ExtendsTheory {
 2     public static void main(String[] args) {
 3         Son son=new Son();//内存的布局
 4         /**
 5          * ->这时请大家注意,要按照查找关系来返回信息
 6          * (1)首先看子类是否有该属性
 7          * (2)如果子类有这个属性,并且可以访问,则返回信息
 8          * (3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息...)
 9          *  (4) 如果父类没有就按照(3)的规则,继续找上级父类,直到Object...
10          */
11         System.out.println(son.name);//返回就是大头儿子
12         System.out.println(son.getAge());//返回的就是39
13         System.out.println(son.hobby);//返回的就是旅游
14     }
15 }
16 
17 class GrandPa{//爷爷类
18     String name="大头爷爷";
19     String hobby="旅游";
20 }
21 
22 class Father extends GrandPa{ //父类
23     String name="大头爸爸";
24     private int age=39;
25     
26     public int getAge(){
27         return age;
28     }
29     
30 }
31 
32 class Son extends Father{//子类
33     String name="大头儿子";
34 }

1.2.4 子类创建的内存布局

 1.3 super关键字

基本介绍

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

基本语法

  • 访问父类的属性,但不能访问父类的private 属性。 super.属性名;
  • 访问父类的方法,但不能访问父类的private方法。 super.方法名(参数列表)
  • 访问父类的构造器 。 super(参数列表);只能放在构造器的第一句,只能出现一句

 

  1 public class Base {//父类是Object
  2     public int n1=999;
  3     public int age=111;
  4 
  5     public void cal(){
  6         System.out.println("Base类的cal()方法...");
  7     }
  8     public void eat(){
  9         System.out.println("Base类的eat()方法...");
 10     }
 11 }
 12 
 13 public class A extends Base{
 14     //4个属性
 15     public int n1=100;
 16     protected int n2=200;
 17     int n3=300;
 18     private int n4=400;
 19 
 20     public A(){
 21 
 22     }
 23     public A (String name){
 24 
 25     }
 26 
 27     public A(String name,int age){
 28 
 29     }
 30 
 31     public void cal(){
 32         System.out.println("A类的cal()方法...");
 33     }
 34 
 35     public void test100(){
 36         System.out.println("A类的test100()方法...");
 37     }
 38 
 39     protected void test200(){
 40         System.out.println("A类的test200()方法...");
 41     }
 42 
 43     void test300(){
 44         System.out.println("A类的test300()方法...");
 45     }
 46 
 47     private void test400(){
 48         System.out.println("A类的test400()方法...");
 49     }
 50 }
 51 
 52 public class B extends A{
 53     public int n1=888;
 54 
 55     //编写测试方法
 56     public void test(){
 57         //super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员
 58         //如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C
 59         System.out.println("super.n1="+super.n1);
 60         super.cal();
 61     }
 62 
 63     //访问父类的属性,但不能访问父类的private属性 super.属性名
 64     public void hi(){
 65         System.out.println(super.n1+" "+super.n2+" "+super.n3);
 66     }
 67 
 68     public void cal(){
 69         System.out.println("B类中的cal()方法...");
 70     }
 71 
 72     public void sum(){
 73         System.out.println("B类的sum()");
 74         //希望调用父类-A的cal方法
 75         //这时,因为子类B没有cal方法,因此我们可以使用下面三种方式
 76         //找到cal方法时(cal()和this.cal()),顺序是:
 77         //(1)先找本类,如果有,则调用
 78         //(2)如果没有,则找父类(如果有,并可以调用,则调用)
 79         //(3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到Object类
 80         //提示:如果查找方法的过程中,找到了,但是不能当问,则报错,cannout access
 81         //如果查找方法的过程中,没有找到,则提示方法不存在
 82         cal();
 83         this.cal();//等价cal()
 84 
 85         //找到cal方法(super.cal())的顺序是直接查找父类,其他的规则一样
 86         super.cal();
 87 
 88         //演示访问属性的规则
 89         /***
 90          * n1和this.n1查找的规则是
 91          * (1)先找本类,如果有,则调用
 92          * (2)如果没有,则找父类(如果有,并且可以调用,则调用)
 93          * (3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到Object类
 94          * 提示:如果查找属性的过程中,找到了,但是不能访问,则报错,cannot access
 95          *      如果查找属性的过程中,没有找到,则提示属性不存在
 96          */
 97         System.out.println(n1);
 98         System.out.println(this.n1);
 99         // 找n1(super.n1)的顺序是直接查找父类属性,其他的规则一样
100         System.out.println(super.n1);
101     }
102 
103     //访问父类的方法,不能访问父类的private方法super.方法名(参数列表);
104     public void ok(){
105         super.test100();
106         super.test200();
107         super.test300();
108         //super.test400();//不能访问父类的private方法
109     }
110     //访问父类的构造器super(参数列表);只能放在构造器的第一句,只能出现一句。
111     public B(){
112         //super();
113         //super("jack",10)
114         super("jack");
115     }
116 }
117 
118 public class Super01 {
119     public static void main(String[] args){
120         B b=new B();//子类对象
121         b.sum();
122         b.test();
123         b.ok();
124     }
125 }

1.3.1 super给编程带来的便利/细节

  1. 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类属性由子类初始化)
  2. 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果
  3. super的不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中有同名的成员,使用super访问遵循就近原则A->B->C,当然也需要遵守访问权限的相关规则

1.3.2 super和this的比较

1.4 方法重写/覆盖(override)

1.4.1 基本介绍

简单的说:方法重写(覆盖)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法重写(覆盖)了父类的方法

 1 public class Animal {
 2     public void cry() {
 3         System.out.println("动物叫唤");
 4     }
 5 
 6     public Object m1(){
 7         return null;
 8     }
 9 
10     public String m2(){
11         return null;
12     }
13 
14     public AAA m3()
15     {
16         return null;
17     }
18 
19     protected void eat()
20     {
21 
22     }
23 }
24 
25 class AAA
26 {
27 
28 
29 }
30 
31 class BBB extends AAA
32 {

Dog.java

public class Dog {
    //1.因为Dog是Animal子类
    //2.Dog的cry方法和Animal的cry定义形式一样(名称、返回类型、参数)
    //3.这时我们就说Dog的cry方法,重写了Animal的cry方法
    public void cry()
    {
        System.out.println("小狗汪汪叫..");
    }

    //细节:子类方法的返回类型和父类方法返回类型一样
    //或者是父类返回类型的子类
    //比如父类返回类型是Object
    //子类返回类型是String
    public String m1()
    {
        return null;
    }

    //这里Object不是String 因此编译错误
//    public Object m2()
//    {
//        return null;
//    }

    public BBB m3()
    {
        return null;
    }

    //细节:子类方法不能缩小父类方法的访问权限
    //public > protected > 默认 > private
    public void eat()
    {

    }
}

Override01.java

public class Override01 {
    public static void main(String[] args)
    {
        //演示方法重写的情况
        Dog dog=new Dog();
        dog.cry();
    }
}

1.4.2 注意事项和使用细节

方法重写也叫方法覆盖,需要满足下面的条件

  1. 子类的方法的形参列表、方法名称、要和父类方法的形参列表,方法名称完全一样
  2. 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
    1. 比如父类返回类型是Object,子类方法返回类型是String
  3. 子类方法不能缩小父类方法的访问权限

1.4.3 方法重写和方法重载的比较

1.5 多态

例子,请编写一个程序,Mster类中有一个 Feed(喂食)方法,可以完成主人给动物喂食的信息

 使用多态来解决这个问题

Animal.java

public 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;
    }
}

Dog&Cat

public class Dog extends Animal{
    public Dog(String name)
    {
        super(name);
    }
}

public class Cat extends Animal{
    public Cat(String name)
    {
        super(name);
    }
}

Food.java

public 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;
    }
}

Bone&Fish

public class Bone extends Food{
    public Bone(String name)
    {
        super(name);
    }
}


public class Fish extends Food{
    public Fish(String name)
    {
        super(name);
    }
}

Master.java

public class Master {
    private String name;

    public Master(String name) {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }

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

    //使用多态机制,可以统一的管理主人卫视的问题
    //animal编译类型是Animal,可以指向(接收)Animal子类的对象
    //food编译类型是Food,可以指向(接收)Food子类的对象
    public void feed(Animal animal,Food food)
    {
        System.out.println("主人"+name+"给"+animal.getName()+"吃"+food.getName());
    }

    //主人给小狗喂食骨头
//    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());
//    }

    //如果动物很多,食物很多
    //===>feed方法很多,不利于管理和维护
    //Pig->Rice
    //Tiger->meat
    //...
}

 1.5.1 多【多种】态【状态】基本介绍

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

 1.5.2 多态的具体体现

方法的多态 PloyMethod.java

public class PloyMethod {
    public static void main(String [] args){
        //方法重载体现多态
        A a=new A();
        //这里我们传入不同的参数,就会调用不同sum方法,就体现多态
        System.out.println(a.sum(10,20));
        System.out.println(a.sum(10,20,30));

        //方法重写体现多态
        B b=new B();
        a.say();
        b.say();
    }
}

重写和重载就体现多态

public class B {
    public void say(){
        System.out.println("B say()方法被调用...");
    }
}

public class A extends B{
    public int sum(int n1,int n2){//和下面sum构成重载
        return n1 + n2;
    }
    public int sum(int n1,int n2,int n3){
        return n1 + n2 + n3;
    }

    public void say(){
        System.out.println("A say()方法被调用");
    }
}

对象的多态(核心,困难,重点)

  • 一个对象的编译类型和运行类型可以不一致
  • 编译类型在定义对象时,就确定了,不能改变
  • 运行类型是可以变化的
  • 编译类型看定义时=号的左边,运行类型看=的右边

父类子类

public class Animal {
    public void cry(){
        System.out.println("Animal cry() 动物在叫");
    }
}

public class Cat extends Animal{
    public void cry(){
        System.out.println("Cat cry()小猫喵喵叫...");
    }
}

public class Dog extends Animal{
    public void cry(){
        System.out.println("Dog cry() 小狗汪汪叫...");
    }
}

PloyObject.java

public class PloyObject {
    public static void main(String[] args){
        //体验对象多态特点
        //animal编译类型就是Animal,运行类型Dog
        Animal animal=new Dog();
        //因为运行时,执行到该行时,animal运行类型是Dog所以cry就是Dog的cry
        animal.cry();//小狗汪汪叫

        //animal编译类型Animal,运行类型就是Cat
        animal=new Cat();
        animal.cry();//小猫喵喵叫
    }
}

 

Animal animal=new Dog();

animal编译类型是Animal,运行类型Dog

animal =new Cat();

animal的运行类型变成了Cat,编译类型仍然是Animal

1.5.3 多态注意事项和细节

多态的前提是:两个对象(类)存在继承关系

多态的向上转型

1)本质:父类的引用指向了子类的对象

2)语法:父类类型 引用名称= new 子类类型();

3)特点:编译类型看左边,运行类型看右边

  • 可以调用父类中的所有成员(需要遵守访问权限)
  • 不能调用子类中特有成员;
  • 最终运行效果看子类的具体实现

多态向下转型

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

  • 只能强转父类的引用,不能强转父类的对象
  • 要求父类的引用必须指向当前目标类型的对象
  • 当向下转型后,可以调用子类类型中所有的成员

 父类子类

 1 public class Animal {
 2     String name="动物";
 3     int age=10;
 4 
 5     public void sleep() {
 6         System.out.println("睡");
 7     }
 8 
 9     public void run()
10     {
11         System.out.println("跑");
12     }
13 
14     public void eat(){
15         System.out.println("吃");
16     }
17 
18     public void show(){
19         System.out.println("hello,你好");
20     }
21 }
22 
23 
24 public class Cat extends Animal{
25     public void eat(){//方法重写
26         System.out.println("猫吃鱼");
27     }
28 
29     public void catchMouse(){//Cat特有方法
30         System.out.println("猫抓老鼠");
31     }
32 }
33 
34 
35 public class Dog extends Animal{//Dog是Animal的子类
36     public void guard(){
37         System.out.println("狗看家守院");
38     }
39 }

 PolyDetail.java

 1 public class PolyDetail {
 2     public static void main(String[] args) {
 3         //向上转型:父类的引用指向了子类的对象
 4         //语法:父类类型 引用名 = new 子类类型();
 5         Animal animal = new Cat();
 6         Object obj = new Cat();//可以吗?可以Object也是Cat的父类
 7 
 8         //向上转型调用方法的规则如下
 9         //(1)可以调用父类中的所有成员(需要遵守访问权限)
10         //(2)但是不能调用子类特有的成员
11         //(#)因为在编译阶段,能调用哪些成员,由编译类型来决定
12         //animal.catchMouse();//错误
13         //(4)最终运行效果看子类(运行类型)的具体实现,即调用方法时,
14         // 按照从子类(运行类型)开始查找方法,然后调用
15         animal.eat();//猫吃鱼...
16         animal.run();//
17         animal.show();//hello,你好
18         animal.sleep();//19 
20         //如果希望可以调用Cat的catchMouse方法
21         //多态的向下转型
22         //(1)语法:子类类型 引用名 = (子类类型)父类引用;
23         //cat的编译类型是Cat,运行类型是Cat
24         Cat cat = (Cat) animal;
25         cat.catchMouse();//猫抓老鼠
26         //(2)要求父类的引用必须指向的是当前目标类型的对象
27         //Dog dog = (Dog) animal;//不行 会报错
28         //dog.guard();
29     }
30 }

属性没有重写之说,属性的值看编译类型

public class Base {
    int count = 10;//属性
}

class Sub extends Base{
    int count=20;
}

public class PloyDetail02 {
    public static void main(String[] args){
        //属性没有重写之说!属性的值看编译类型
        Base base=new Sub();//向上转型
        System.out.println(base.count);//看编译类型10
        Sub sub=new Sub();
        System.out.println(sub.count);//20
    }
}

instanceOf比较操作符

instanceOf用于判断对象的运行类型是否为XX类型或XX类型的子类

public class PolyDetail03 {
    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,运行类型BB
        //BB是AA子类
        AA aa = new BB();
        System.out.println(aa instanceof AA);//true
        System.out.println(aa instanceof BB);//true
        
        Object obj=new Object();
        System.out.println(obj instanceof AA);//false;
        String str = "hello";
        //System.out.println(str instanceof AA);
        System.out.println(str instanceof Object); //true
    }
}

class AA{}

class BB extends AA{}

1.5.4 java的动态绑定机制(非常非常重要)

Java重要特性:动态绑定机制

 1 public class DynamicBinding {
 2     public static void main(String[] args) {
 3         //a的编译类型是A,运行类型是B
 4         A a = new B();//向上转型
 5         System.out.println(a.sum());//40->30
 6         System.out.println(a.sum1());//30->20
 7 
 8     }
 9 }
10 
11 class A {
12     public int i = 10;
13     //动态绑定机制
14 
15     public int sum(){
16         return getI() + 10;//20+10
17     }
18 
19     public int sum1(){
20         return i + 10;//10+10;
21     }
22 
23     public int getI(){
24         return i;
25     }
26 }
27 
28 class B extends A{
29     public int i = 20;
30 
31 //    public int sum(){
32 //        return i + 20;
33 //    }
34 
35     public int getI(){
36         return i;
37     }
38 
39 //    public int sum1(){
40 //        return i + 10;
41 //    }
42 }

Java的动态绑定机制

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

当调用对象属性时,没有动态绑定机制,哪里声明哪里使用

1.5.5 多态的应用

多态数组

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

 1 public class Person {
 2     private String name;
 3     private int age;
 4 
 5     public Person(String name,int age){
 6         this.name = name;
 7         this.age = age;
 8     }
 9 
10     public String getName(){
11         return name;
12     }
13 
14     public void setName(String name){
15         this.name = name;
16     }
17 
18     public int getAge(){
19         return age;
20     }
21 
22     public void setAge(int age){
23         this.age = age;
24     }
25 
26     public String say(){
27         return name + "\t" + age;
28     }
29 }

Student.java

 1 public class Student extends Person{
 2     private double score;
 3 
 4     public Student (String name,int age,double score){
 5         super(name,age);
 6         this.score = score;
 7     }
 8 
 9     public double getScore(){
10         return score;
11     }
12 
13     public void setScore(double score){
14         this.score = score;
15     }
16 
17     //重写父类say
18     @Override
19     public String say(){
20         return "学生 "  + super.say() + "score=" + score;
21     }
22 
23     //特有方法
24     public void study(){
25         System.out.println("学生 " + getName() + " 正在学java...");
26     }
27 }

Teacher.java

 1 public class Teacher extends Person{
 2     private double salary;
 3 
 4     public Teacher(String name,int age,double salary){
 5         super(name,age);
 6         this.salary = salary;
 7     }
 8 
 9     public double getSalary(){
10         return salary;
11     }
12 
13     public void setSalary(double salary){
14         this.salary = salary;
15     }
16 
17     //重写父类的say方法
18     @Override
19     public String say(){
20         return "老师 " + super.say() + " salary=" + salary;
21     }
22 
23     //特有方法
24     public void teach(){
25         System.out.println("老师 " + getName() + "正在讲java课程...");
26     }
27 }

PloyArray.java

 1 public class PloyArray {
 2     public static void main(String[] args) {
 3         //应用示例:现有一个继承结构如下:要求创建1个Person对象
 4         //2个Student对象和2个Teacher对象,统一放在数组中,并调用每个对象say方法
 5         Person [] persons = new Person[5];
 6         persons[0] = new Person("jack",20);
 7         persons[1] = new Student("mary",18,100);
 8         persons[2] = new Student("smith",19,87.5);
 9         persons[3] = new Teacher("scott",30,20000);
10         persons[4] = new Teacher("king",50,25000);
11 
12         //循环遍历多态数组,调用say
13         for(int i = 0;i < persons.length;i++){
14             //person[i]编译类型是Person,运行类型是根据实际情况由JVM来判断
15             System.out.println(persons[i].say());//动态绑定机制
16             //使用类型判断 + 向下转型
17             if(persons[i] instanceof Student){//判断person[1]的运行类型是不是Student
18                 Student student = (Student) persons[i];//向下转型
19                 student.study();
20                 //也可以使用一条语句((Student)persons[i].study())
21             }else if(persons[i] instanceof Teacher){
22                 Teacher teacher=(Teacher) persons[i];
23                 teacher.teach();
24             }else if(persons[i] instanceof Person){
25                 System.out.println(persons[i].say());
26             }else {
27                 System.out.println("你的类型有误,请检查...");
28             }
29         }
30     }

多态参数

 方法定义的形参类型为父类类型,实参类型允许为子类类型

Employee.java

 1 public class Employee {
 2     private String name;
 3     private double salary;
 4 
 5     public Employee(String name,double salary){
 6         this.name = name;
 7         this.salary = salary;
 8     }
 9 
10     //得到年工资的方法
11     public double getAnnual(){
12         return  12 * salary;
13     }
14 
15     public String getName(){
16         return name;
17     }
18 
19     public void setName(String name){
20         this.name = name;
21     }
22 
23     public double getSalary(){
24         return salary;
25     }
26 
27     public void setSalary(double salary){
28         this.salary = salary;
29     }
30 }

Manage.java

 1 public class Manager extends Employee{
 2     private double bonus;
 3 
 4     public Manager(String name,double salary,double bonus){
 5         super(name,salary);
 6         this.bonus = bonus;
 7     }
 8 
 9     public double getBonus(){
10         return bonus;
11     }
12 
13     public void setBonus(double bonus){
14         this.bonus = bonus;
15     }
16 
17     public void manage(){
18         System.out.println("经理 " + getName() + " is managing");
19     }
20 
21     //重写获取年薪方法
22     @Override
23     public double getAnnual(){
24         return super.getAnnual() + bonus;
25     }
26 }

Worker.java

public class Worker extends Employee{
    public Worker(String name,double salary){
        super(name,salary);
    }

    public void work(){
        System.out.println("普通员工 " + getName() +"is working");
    }

    @Override
    public double getAnnual(){//因为普通员工没有其他输入,则直接调用父类方法
        return super.getAnnual();
    }
}

PloyParameter.java

 1 public class PloyParameter {
 2     public static void main(String[] args){
 3         Worker tom = new Worker("tom",3500);
 4         Manager milan=new Manager("milan",5000,200000);
 5         PloyParameter ployParameter=new PloyParameter();
 6         ployParameter.showEmpAnnual(tom);
 7         ployParameter.showEmpAnnual(milan);
 8 
 9         ployParameter.testWork(tom);
10         ployParameter.testWork(milan);
11     }
12 
13 
14     //showEmpAnnual(Employee e)
15     //实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
16     public void showEmpAnnual(Employee e){
17         System.out.println(e.getAnnual());//动态绑定机制
18     }
19 
20     //添加一个方法,testWork,如果是普通员工,则调用work,如果是经理,则调用manage方法
21     public void testWork(Employee e){
22         if (e instanceof Worker){
23             ((Worker)e).work();//有向下转型操作
24         }else if(e instanceof Manager){
25             ((Manager) e).manage();
26         }else {
27             System.out.println("不做处理");
28         }
29     }
30 }

 

posted @ 2024-06-21 17:48  万溪汇海  阅读(15)  评论(0编辑  收藏  举报