文章目录
- 三大特征
- 面向对象编程-封装
- 面向对象编程-继承
- 为什么需要继承
- 继承基本介绍和示意图
- 继承的基本语法
- 入门案例(修改上述代码)
- 继承带来的便利
- 继承的深入讨论/细节问题
- 1.子类继承了所有的属性和方法,但是私有属性和方法不能在子类直接访问,可以间接访问,但要通过公共的方法去访问
- 2.子类必须调用父类的构造器,完成父类的初始化
- 3.当创建子类对象是,不管使用子类的哪一个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。
- 4.如果希望子类指定去调用父类的某个构造器,则在子类构造器中显式的调用一下:
- 5.super在使用时,需要放在构造器第一行(super只能在构造器中使用)
- 6.super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- 7.java所有类都是Object类的子类
- 8.父类构造器的调用不限于直接父类 将一直往上追溯直到Object类(顶级父类)
- 9.子类最多只能继承一个父类(指直接继承),即java中是单继承机制
- 10.不能滥用继承,子类和父类之间必须满足is-a(是一个XXX)的逻辑关系
- 继承的本质分析(重要)
- super关键字
- 方法重写/覆盖(override)
- 面向对象编程-多态(非常重要)
三大特征
面向对象编程有三大特征:封装,继承和多态。
面向对象编程-封装
封装介绍
封装(encapsulation)就是把抽象出的数据【属性】和堆数据的操作【方法】封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作【方法】,才能对数据进行操作。
封装的理解和好处
1.隐藏实现细节
2.可以对数据进行验证,保证安全合理
封装的实现步骤(三步)
1.将属性进行私有化private【不能直接修改属性】
2.提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){//Xxx表示某个属性
//加入数据验证的业务逻辑
属性 = 参数名;
}
3.提供一个公共的get方法,用于获取属性的值
public XX getXxx(){//权限判断,Xxx表示某个属性
return xx;
}
封装的简单案例
写一个程序,不能随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认。年龄必须在1-120,年龄,工资不能直接查看,name的长度在2-6个字之间
import java.util.Scanner;
public class Encap {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Person person = new Person();
person.setName("jackfafasfasf");
person.setAge(20);
person.setSalary(1000);
// System.out.println(person.info());
System.out.println(person.getSalary());
System.out.println(person.getName());
}
}
class Person {
public String name;//名字公开
private int age; //年龄私有化
private double salary; //薪水私有化
//自己写set和get太慢了,可以使用快捷键(Alt + Insert),然后根据要求完善代码
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() >= 2 && name.length() <= 6) {
this.name = name;
}else{
System.out.println("名字长度不在范围内,按默认处理");
this.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() {
System.out.print("请输入密码:");
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
if (num == 123456) {
return salary;
} else {
System.out.println("密码错误");
return null;
}
}
public void setSalary(double salary) {
this.salary = salary;
}
//写一个方法,返回属性信息
public String info() {
return "信息为 name=" + name + " age=" + age + "salary=" + salary;
}
}
封装与构造器
import java.util.Scanner;
public class Encap {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Person person = new Person();
person.setName("jackfafasfasf");
person.setAge(20);
person.setSalary(1000);
// System.out.println(person.info());
System.out.println(person.getSalary());
System.out.println(person.getName());
Person person1 = new Person("小白",18,1000);
System.out.println(person1.info());
}
}
class Person {
public String name;//名字公开
private int age; //年龄私有化
private double salary; //薪水私有化
public Person() {
}
public Person(String name, int age, double salary) {
// this.name = name;
// this.age = age;
// this.salary = salary;
setAge(age);//等价于this.setAge(age);
setSalary(salary);
setName(name);
}
//自己写set和get太慢了,可以使用快捷键(Alt + Insert),然后根据要求完善代码
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() >= 2 && name.length() <= 6) {
this.name = name;
}else{
System.out.println("名字长度不在范围内,按默认处理");
this.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() {
System.out.print("请输入密码:");
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
if (num == 123456) {
return salary;
} else {
System.out.println("密码错误");
return null;
}
}
public void setSalary(double salary) {
this.salary = salary;
}
//写一个方法,返回属性信息
public String info() {
return "信息为 name=" + name + " age=" + age + "salary=" + salary;
}
}
封装的练习
创建程序,在其中定义两个类:Account和AccountTest类体会Java的封装性。
1.Account类要求具有属性:姓名(长度为2位3位或4位)、余额(必须>20)、密码(必须是6位),如果不满足,则给出提示信息,并给默认值。
2.通过setXxx的方法给Account的属性赋值。
3.在AccountText中测试
package com.fspdu.encap;
public class Account {
private String name;
private double banlance;
private String pwd;
public Account() {
}
public Account(String name, double banlance, String pwd) {
this.setName(name);
this.setBanlance(banlance);
this.setPwd(pwd);
}
public String getName() {
return name;
}
public String setName(String name) {
if(name.length() >= 2 && name.length() <= 6) {
this.name = name;
return name;
}else{
System.out.println("名字不规范,按默认处理");
this.name = "无名者";
return name;
}
}
public double getBanlance() {
return banlance;
}
public void setBanlance(double banlance) {
if(banlance > 20) {
this.banlance = banlance;
}else{
System.out.println("余额必须大于20,在此按默认0处理");
}
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
if(pwd.length() == 6) {
this.pwd = pwd;
}else{
System.out.println("密码必须是6位");
}
}
public void info(){
System.out.println("name:" + name + "banlance:" + banlance + "pwd:" + pwd);
}
}
package com.fspdu.encap;
public class AccounText {
public static void main(String[] args) {
Account account = new Account("小",1,"12345");
account.info();
}
}
面向对象编程-继承
为什么需要继承
看一个问题,我们编写了两个类,一个是Pupil类(小学生),一个是Graduate(大学毕业生)。
//小学生->模拟小学生考试情况
public class Pupic {
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);
}
}
//模拟大学生考试情况
public class Graduate {
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);
}
}
public class Extends01 {
public static void main(String[] args) {
Pupic pupic = new Pupic();
pupic.name = "小白";
pupic.age = 15;
pupic.testing();
pupic.setScore(100);
pupic.showInfo();
System.out.println("======================");
Graduate graduate = new Graduate();
graduate.name = "大白";
graduate.age = 18;
graduate.testing();
graduate.setScore(150);
graduate.showInfo();
}
}
测试结果:
小学生小白正在考小学数学
学生名字:小白 年龄:15 分数:100.0
======================
大学生大白正在考高等数学
学生名字:大白 年龄:18 分数:150.0
我们发现两个类的属性和方法有很多是相同的,怎么办?这就用到了继承(代码复用性)。
继承基本介绍和示意图
继承可以解决代码复用,让我们的编程更加靠近人类思维。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继续父类即可。
继承的基本语法
class 子类 extends 父类{
}
1.子类就会自动拥有父类定义的属性和方法
2.父类又叫超类,基类
3.子类又叫派生类
入门案例(修改上述代码)
父类
package com.fspdu.extend.improve;
//父类,是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);
}
}
两个子类
package com.fspdu.extend.improve;
public class Pupil extends Student{
public void testing()
{
System.out.println("小学生" + name + "正在考小学数学");
}
}
package com.fspdu.extend.improve;
public class Graduate extends Student{
public void testing(){
System.out.println("大学生" + name + "正在考高等数学");
}
}
测试
package com.fspdu.extend.improve;
public class Extends01 {
public static void main(String[] args) {
Pupil pupil = new Pupil();
pupil.name = "小白";
pupil.age = 10;
pupil.testing();
pupil.setScore(100);
pupil.showInfo();
Graduate graduate = new Graduate();
graduate.name = "小黑";
graduate.age = 18;
graduate.testing();
graduate.setScore(150);
graduate.showInfo();
}
}
测试结果:
小学生小白正在考小学数学
学生名字:小白 年龄:10 分数:100.0
大学生小黑正在考高等数学
学生名字:小黑 年龄:18 分数:150.0
继承带来的便利
1.代码复用性提高
2.代码的扩展性和维护性提高
继承的深入讨论/细节问题
1.子类继承了所有的属性和方法,但是私有属性和方法不能在子类直接访问,可以间接访问,但要通过公共的方法去访问
父类
package com.fspdu.extend;
public class Base {//父类
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public int getN4(){
return n4;
}
public Base(){
System.out.println("父类无参构造器运行");
}
public void test100(){
System.out.println("test100");
}
protected void test200(){
System.out.println("test200");
}
void test300(){
System.out.println("test300");
}
private void test400(){
System.out.println("test400");
}
public void test4(){
test400();
}
}
子类
package com.fspdu.extend;
public class Sub extends Base{//子类
public Sub(){
System.out.println("子类无参构造器运行");
}
public void sayOK(){
System.out.println(n1 + " " + n2 + " " + n3 + " " + getN4());
test100();
test200();
test300();
test4();
}
}
测试
package com.fspdu.extend;
public class ExtendsDetail {
public static void main(String[] args) {
Sub sub = new Sub();
sub.sayOK();
}
}
测试结果:
父类无参构造器运行
子类无参构造器运行
100 200 300 400
test100
test200
test300
test400
2.子类必须调用父类的构造器,完成父类的初始化
观察上述测试结果:父类无参构造器在子类无参构造器被调用之前先被调用,原因是Sub类里面隐藏了一句话(编译器默认),默认调用父类的无参构造器。
public Sub(){
super();//默认调用父类的无参构造器
System.out.println("子类无参构造器运行");
}
3.当创建子类对象是,不管使用子类的哪一个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。
父类
package com.fspdu.extend;
public class Base {//父类
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public int getN4(){
return n4;
}
public Base() {
System.out.println("父类无参构造器运行");
}
public void test100() {
System.out.println("test100");
}
protected void test200(){
System.out.println("test200");
}
void test300(){
System.out.println("test300");
}
private void test400(){
System.out.println("test400");
}
public void test4(){
test400();
}
}
子类
package com.fspdu.extend;
public class Sub extends Base{//子类
public Sub(){
super();//默认调用父类的无参构造器
System.out.println("子类无参构造器运行");
}
public Sub(String cchar){
System.out.println("子类有参构造器运行");
}
public void sayOK(){
System.out.println(n1 + " " + n2 + " " + n3 + " " + getN4());
test100();
test200();
test300();
test4();
}
public void exce(){
System.out.println("===========");
}
}
测试
package com.fspdu.extend;
public class ExtendsDetail {
public static void main(String[] args) {
Sub sub = new Sub();
sub.sayOK();
Sub sub1 = new Sub("aaa");
sub1.exce();
}
}
测试结果:
父类无参构造器运行
子类无参构造器运行
100 200 300 400
test100
test200
test300
test400
父类无参构造器运行
子类有参构造器运行
===========
父类
package com.fspdu.extend;
public class Base {//父类
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public int getN4(){
return n4;
}
// public Base() {
// System.out.println("父类无参构造器运行");
// }
public Base(String name){
System.out.println("父类有参构造器运行");
}
public void test100() {
System.out.println("test100");
}
protected void test200(){
System.out.println("test200");
}
void test300(){
System.out.println("test300");
}
private void test400(){
System.out.println("test400");
}
public void test4(){
test400();
}
}
子类
package com.fspdu.extend;
public class Sub extends Base{//子类
public Sub(){
super("fsp");//(父类没有无参构造器,用super指定父类构造器完成对父类的初始化)
System.out.println("子类无参构造器运行");
}
public Sub(String cchar){
super("fsp");
System.out.println("子类有参构造器运行");
}
public void sayOK(){
System.out.println(n1 + " " + n2 + " " + n3 + " " + getN4());
test100();
test200();
test300();
test4();
}
public void exce(){
System.out.println("===========");
}
}
测试
package com.fspdu.extend;
public class ExtendsDetail {
public static void main(String[] args) {
Sub sub = new Sub();
sub.sayOK();
Sub sub1 = new Sub("aaa");
sub1.exce();
}
}
测试结果:
父类有参构造器运行
子类无参构造器运行
100 200 300 400
test100
test200
test300
test400
父类有参构造器运行
子类有参构造器运行
===========
4.如果希望子类指定去调用父类的某个构造器,则在子类构造器中显式的调用一下:
public Xxx(....,....) {
super(....);
System.out.println(".....");
}
5.super在使用时,需要放在构造器第一行(super只能在构造器中使用)
6.super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
7.java所有类都是Object类的子类
8.父类构造器的调用不限于直接父类 将一直往上追溯直到Object类(顶级父类)
9.子类最多只能继承一个父类(指直接继承),即java中是单继承机制
思考:如何让A类继承B类和C类?【A继承B,然后B继承C】
10.不能滥用继承,子类和父类之间必须满足is-a(是一个XXX)的逻辑关系
Music extends Person //不合理
Cat extends Animal //合理
继承的本质分析(重要)
子类创建的内存布局
按照查找关系来返回信息
super关键字
基本介绍
super代表父类的引用,用于访问父类的属性、方法、构造器
基本语法
1.访问父类的属性,但不能访问父类的private属性:super.属性名;
2.访问父类的方法,不能访问父类的private方法:super.方法名(参数列表);
3.访问父类的构造器:super(参数列表);只能放在构造器的第一句,只能出现一句
父类
package com.fspdu.super_;
public class A {
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public void test100(){
}
protected void test200(){
}
void test300(){
}
private void test400(){
}
}
子类
package com.fspdu.super_;
public class B extends A{
public void hi(){
System.out.println(super.n1 + " " + super.n2 + " " + super.n3);
}
public void ok(){
super.test100();
super.test200();
super.test300();
}
public B(){
super();
}
}
super细节
1.调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
2.当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果
this等价于直接访问:先找本类,如果有,则调用;如果没有,则找父类(父类有就调用);如果父类没有,则找父类的父类,直到Object类(提示:如果查找方法或者属性的过程中,找到了但是不能访问,则报错;如果查找过程中没有找到,则提示方法不存在)
super:跳过本类(子类)直接查找父类,后续规则一样
3.super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。
super和this的比较
方法重写/覆盖(override)
基本介绍
简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名字、返回类型、参数一样,那么我们就说子类的这个方法覆盖(重写)了父类的那个方法
注意事项和使用细节
方法重写也叫方法覆盖,需要满足下面的条件
1.子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样
2.子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
(比如父类返回类型是Object,子类方法返回类型是String)
父类---public Object getInfo(){}
子类---public String getInfo(){}
3,子类方法不能缩小父类方法的访问权限,允许扩大**
public > protected > 默认 > private
父类---void sayOk(){}
子类---public void sayOk(){}
方法重装和方法重写比较
面向对象编程-多态(非常重要)
提高代码的复用性,有利于代码维护
多【多种】态【状态】基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的
多态的具体体现
1.方法的多态(重写和重载就体现出多态)
package com.fspdu.poly_;
public class PolyMethod {
public static void main(String[] args) {
//方法重载体现多态
A a = new A();
//我们通过不同的参数个数去调用sum方法,就会去调用不同的方法
//因此对sum方法来说,就是多种状态的体现
System.out.println(a.sum(11,22));
System.out.println(a.sum(11,22,33));
//方法重写体现多态
B b = new B();
a.say();
b.say();
}
}
class B{
public void say(){
System.out.println("B say()方法被调用");
}
}
class A extends B{
public int sum(int n1,int n2){
return n1 + n2;
}
public int sum(int n1,int n2,int n3){
return n1 + n2 + n3;
}
public void say(){
System.out.println("A say()方法被调用");
}
}
33
66
A say()方法被调用
B say()方法被调用
2.对象的多态(核心,困难,重点)
重要的几句话:
(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的
(4)编译类型看定义时 = 号的左边,运行类型看 = 号的右边
例:
Animal animal = new Dog();【animal编译类型是Animal,运行类型Dog】
animal = new Cat();【animal的运行类型变成了Cat,编译类型仍然是Animal】
父类
package com.fspdu.poly_.objpoly_;
public class Animal {
public void cry(){
System.out.println("动物在叫");
}
}
子类
package com.fspdu.poly_.objpoly_;
public class Dog extends Animal{
public void cry() {
System.out.println("dog.cry---小狗汪汪");
}
}
package com.fspdu.poly_.objpoly_;
public class Cat extends Animal{
public void cry(){
System.out.println("cat.cry---小猫喵喵");
}
}
测试
package com.fspdu.poly_.objpoly_;
public class PolyObject {
public static void main(String[] args) {
//对象多态的特点
//animal 编译类型是Animal
//运行类型 Dog
//结果以运行类型为主
Animal animal = new Dog();
animal.cry();
animal = new Cat();
animal.cry();
}
}
结果:
dog.cry—小狗汪汪
cat.cry—小猫喵喵
3.多态的使用案例
主人给动物喂食程序
父类:Animal
package com.fspdu.poly_;
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;
}
}
父类:Food
package com.fspdu.poly_;
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;
}
}
子类:Fish
package com.fspdu.poly_;
public class Fish extends Food{
public Fish(String name) {
super(name);
}
}
子类:Bone
package com.fspdu.poly_;
public class Bone extends Food{
public Bone(String name) {
super(name);
}
}
子类:Cat
package com.fspdu.poly_;
public class Cat extends Animal{
public Cat(String name) {
super(name);
}
}
子类:Dog
package com.fspdu.poly_;
public class Dog extends Animal{
public Dog(String name) {
super(name);
}
}
主人类:Master
package com.fspdu.poly_;
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;
}
public void feed(Animal animal,Food food){
System.out.println(name + "给" + animal.getName() + "吃" + food.getName());
}
}
main测试
package com.fspdu.poly_;
public class Poly01 {
public static void main(String[] args) {
Master tom = new Master("Tom");
Dog dog = new Dog("小黑");
Bone bone = new Bone("牛骨");
tom.feed(dog,bone);
Cat cat1 = new Cat("小白");
Bone fish = new Bone("鱼骨");
tom.feed(cat1,fish);
}
}
测试结果:
Tom给小黑吃牛骨
Tom给小白吃鱼骨
4.多态注意事项和细节讨论
多态的前提是:两个对象(类)存在继承关系
多态的向上转型
1)本质:父类的引用指向了子类的对象
2)语法:父类类型 引用名 = new 子类类型();
3)特点:
编译类型看左边,运行类型看右边。
可以调用父类中的所有成员(需遵守访问权限),
不能调用子类中特有成员;
最终运行效果看子类的具体实现。
4)案例
父类
package com.fspdu.poly_.detail_;
public class Animal {
String name = "动物";
int age = 10;
public void sleep(){
System.out.println("睡");
}
public void run(){
System.out.println("跑");
}
public void eat(){
System.out.println("吃");
}
public void show(){
System.out.println("hello");
}
}
子类
package com.fspdu.poly_.detail_;
public class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
测试
package com.fspdu.poly_.detail_;
public class PolyDetail {
public static void main(String[] args) {
//(向上转型):父类的引用指向了子类的对象
//语法:父类类型引用名 = new 子类类型();
Animal animal = new Cat();
Object obj = new Cat();//可以吗?可以,因为object也是Cat的父类
//向上转型调用方法的规则如下:
//(1)可以调用父类中的所有成员(需要遵守访问权限)
//(2)但是不能调用子类的特有的成员
//(3)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
//animal.catchMouse(); 错误
//(4)最终运行效果看子类的具体实现,即调用方法时,按照从子类开始查找方法,然后调用
//(5)规则和前面进的方法调用规则一致
animal.eat();//猫吃鱼
animal.run();//跑
animal.show();//hello
animal.sleep();//睡
}
}
多态的向下转型
1)语法: 子类类型 引用名 = (子类类型) 父类引用;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)可以调用子类类型种所有的成员
案例:
父类与子类和向上转型一样
package com.fspdu.poly_.detail_;
public class PolyDetail {
public static void main(String[] args) {
Animal animal = new Cat();
//向下转型
Cat cat = (Cat) animal;
cat.catchMouse();//猫抓老鼠
}
}
5.属性没有重写的说法,属性的值看编译类型
package com.fspdu.poly_.detail_;
public class PolyDetail02 {
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
}
}
class Base{
int count = 10;
}
class Sub extends Base{
int count = 20;
}
6.instanceOf比较操作符
用于判断对象的运行类型是否为XX类型或是XX类型的子类型
package com.fspdu.poly_.detail_;
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 = 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 Object);//true
}
}
class AA{}
class BB extends AA{}
多态的应用
多态数组
数组的定义类型Wie父类类型,里面保存的实际元素类型为子类类型
应用实例:现有一个继承结构如下:要求创建1个Person对象、2个Student对象和2个Teacher对象,统一放在数组中,并调用say方法。
public class PloyArray {
public static void main(String[] args) {
Person[] person = new Person[5];
person[0] = new Person("Jack",20);
person[1] = new Student("Jack",18,100);
person[2] = new Student("Toom",19,120);
person[3] = new Teacher("King",40,18000);
person[4] = new Teacher("Qing",50,20000);
for (int i = 0; i < person.length; i++) {
System.out.println(person[i].say());//动态绑定机制
}
}
}
class Person{ //父类
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say(){
return name + "\t" + age;
}
}
class Student extends Person{
private double score;
public Student(String name, int age,double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String say() {
return super.say() + " score=" + score;
}
}
class Teacher extends Person{
private double salary;
public Teacher(String name, int age, double salary){
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String say() {
return super.say() + " salary=" + salary;
}
}
Jack 20
Jack 18 score=100.0
Toom 19 score=120.0
King 40 salary=18000.0
Qing 50 salary=20000.0
多态数组(案例升级)
如何调用子类特有的方法,比如Teacher有一个teach,Student有一个study怎么调用?
public class PloyArray {
public static void main(String[] args) {
Person[] person = new Person[5];
person[0] = new Person("Jack",20);
person[1] = new Student("Jack",18,100);
person[2] = new Student("Toom",19,120);
person[3] = new Teacher("King",40,18000);
person[4] = new Teacher("Qing",50,20000);
for (int i = 0; i < person.length; i++) {
System.out.println(person[i].say());//动态绑定机制
if(person[i] instanceof Student){
// Student student = (Student)person[i];
// student.study();
((Student)person[i]).study();//向下转型简写
} else if (person[i] instanceof Teacher) {
((Teacher)person[i]).teach();//向下转型简写
}else if(person[i] instanceof Person){
}
}
}
}
class Person{ //父类
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say(){
return name + "\t" + age;
}
}
class Student extends Person{
private double score;
public Student(String name, int age,double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String say() {
return super.say() + " score=" + score;
}
public void study(){
System.out.println("学生 " + getName() + "正在学习");
}
}
class Teacher extends Person{
private double salary;
public Teacher(String name, int age, double salary){
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String say() {
return super.say() + " salary=" + salary;
}
//特有方法
public void teach(){
System.out.println("老师 " + getName() + "正在授课");
}
}
Jack 20
Jack 18 score=100.0
学生 Jack正在学习
Toom 19 score=120.0
学生 Toom正在学习
King 40 salary=18000.0
老师 King正在授课
Qing 50 salary=20000.0
老师 Qing正在授课
多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
应用实例1:前面的主人给动物喂食
应用实例2:
定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法
测试类中添加一个方法showEmpAnnal(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
public class PolyParameter {
public static void main(String[] args) {
Worker tom = new Worker("tom",2500);
Manager akl = new Manager("akl", 5000, 200000);
PolyParameter polyParameter = new PolyParameter();
polyParameter.showEmpAnnual(tom);
polyParameter.showEmpAnnual(akl);
polyParameter.testWork(tom);
polyParameter.testWork(akl);
}
public void showEmpAnnual(Employee e){
System.out.println(e.getAnnual());
}
public void testWork(Employee e){
if(e instanceof Worker){
((Worker) e).work();
}else if(e instanceof Manager){
((Manager) e).manage();
}else {
System.out.println("不做处理");
}
}
}
class Employee{ //员工类
private String name;
private double saraly;
public Employee(String name, double saraly) {
this.name = name;
this.saraly = saraly;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSaraly() {
return saraly;
}
public void setSaraly(double saraly) {
this.saraly = saraly;
}
//得到年工资的方法
public double getAnnual(){
return 12 * saraly;
}
}
class Worker extends Employee{ //普通员工子类
public Worker(String name, double saraly) {
super(name, saraly);
}
public void work(){
System.out.println("普通员工 " + getName() + "正在工作");
}
@Override
public double getAnnual() {
return super.getAnnual();
}
}
class Manager extends Employee{ //经理子类
private double bonus;
public Manager(String name, double saraly,double bonus) {
super(name, saraly);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public void manage(){
System.out.println("经理 " + getName() + "正在管理");
}
@Override
public double getAnnual() {
return super.getAnnual() + bonus;
}
}
30000.0
260000.0
普通员工 tom正在工作
经理 akl正在管理