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 继承的深入讨论
- 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
- 子类必须调用父类的构造器,完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
- 如果希望指定去调用父类的某个构造器,则显式调用一下:super(参数列表)
- super在使用时,必须放在构造器第一行(super只能在构造器中使用)
- super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- Java所有类都是Object类的子类,Object是所有类的基类
- 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
- 子类最多只能继承一个父类(指直接继承),即Java中是单继承机制
- 不能滥用继承,子类和父类之间必须满足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给编程带来的便利/细节
- 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类属性由子类初始化)
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果
- 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 注意事项和使用细节
方法重写也叫方法覆盖,需要满足下面的条件
- 子类的方法的形参列表、方法名称、要和父类方法的形参列表,方法名称完全一样
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
- 比如父类返回类型是Object,子类方法返回类型是String
- 子类方法不能缩小父类方法的访问权限
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 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
2023-06-21 jmeter组件使用详解(二)
2023-06-21 jmeter组件使用详解(一)
2023-06-21 jmeter入门简介