面向对象详讲(中)
**
1.IDE-Eclipse的使用**
1)Eclipse 下载解压后,就可以直接运行,前提是已经安装好了JDK。
2)我们使用neon 这个版本
3)Neon依赖Java8,也就是说我们需要安装 jdk8 才能使用Neon这个版本
4)给同学们演示如何安装,解压即可使用
5)eclipse工作界面介绍
6)使用步骤:
1.选择工作空间
2.工作界面
3.创建项目,编程一个类,并运行,输出Hello,world
4.输入项目名,并确定
5.创建类
6.调整字体大小 , windows -> preference -> 下面的窗口.
7.编程一个类
8.右键->run->java application
Eclipse常用快捷键的使用 (核心,就是快捷键)
1)删除当前行 ctrl + d
2)复制当前行 ctrl + alt + 向下光标
3)补全代码 alt + /
4)添加注释和取消注释 ctrl + / 【第一次是添加注释,第二次是取消】
5)导入该行需要的包 ctrl + shift + o [导入需要的类]
6)快速格式化代码 ctrl + shift + f =》 【也可以自己修改 alt+f】
7)快速运行程序 ctrl + f11 => [自己修改alt + r]
8)生成构造方法 shift + alt + s [开发效率提高 alt+g]
9)查看一个类的层级关系 ctrl + t [学习继承后,非常有用]
10)将光标放在一个方法上,输入 f3 , 可以选择定位到哪个类(包括父类)的方法. [学继承后,非常有用]
11)自动的分配变量名[非常好] [ctrl+alt+a]
12)还有很多其它的快捷键…
2 包
2.1看一个应用场景
现在有两个程序员共同开发一个java项目,程序员xiaoming希望定义一个类取名 Dog ,程序员xiaoqiang也想定义一个类也叫 Dog。两个程序员为此还吵了起来,怎么办?
2.2包的三大作用
1)区分相同名字的类
2)当类很多时,可以很好的管理类[模块]
3)控制访问范围
2.3打包命令
打包基本语法
package com.atguigu;
说明: package 关键字,表示打包.
com.atguigu: 表示包名
打包的本质分析(原理)
实际上就是创建不同的文件夹来保存类文件,画出示意图
2.4快速入门
使用打包技术来解决上面的问题,不同包下Dog类
2.5包的命名
2.6常用的包
一个包下,包含很多的类,java中常用的包有:
java.lang.* //lang包是基本包,默认引入,不需要再引入.
java.util.* //util 包,系统提供的工具包, 工具类,使用 Scanner
java.net.* //网络包,网络开发=> 网络开发
java.awt.* //是做java的界面开发,GUI
2.7如何引入包
语法: import 包;
比如 import java.awt.; // * 表示将该包的所有类和接口引入.
我们引入一个包的主要目的是要使用该包下的类
比如 import java.util.Scanner; 就只是引入一个类Scanner。
import java.util.; // 表示将 java.util 包所有都引入
案例: // 包的作用, 完成排序 -> 自己写,使用系统提供.Arrays
2.8注意事项和使用细节
1)package 的作用是声明当前类所在的包,需要放在class的最上面,一个类中最多只有一句package
2)import指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。
3)代码
//package 的作用是声明当前类所在的包,需要放在class的最上面,
//一个类中最多只有一句package
package com.atguigu.pack;
//import指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
public class PackageDetail {
public static void main(String[] args) {
new Scanner(System.in);
ArrayList<String> al = new ArrayList<>();
}
}
class Tiger {
public void f1() {
HashMap hm = new HashMap();
}
}
3访问修饰符
3.1基本介绍
java提供四种访问控制修饰符号控制方法和属性(成员变量/字段)的访问权限(范围):
1)公开级别:用public 修饰,对外公开
2)受保护级别:用protected修饰,对子类和同一个包中的类公开
3)默认级别:没有修饰符号,向同一个包的类公开.
4)私有级别:用private修饰,只有类本身可以访问,不对外公开.
3.2种访问修饰符的访问范围
3.3使用的注意事项
1)修饰符可以用来修饰类中的属性,成员方法以及类。
2)只有默认的和public才能修饰类!,并且遵循上述访问权限的特点。
3)因为没有学习继承,因此关于在子类中的访问权限,我们讲完子类后,在回头讲解。
4)成员方法的访问规则和属性一样.。
3.4案例演示
package com.atguigu.visit;
public class A {
// 属性
public int n1 = 10;
protected int n2 = 20;
int n3 = 30;
private int n4 = 40;
public void m1() {
// 在同一类,可以访问 所有的修饰符的属性和方法
System.out.println(n1 + " " + n2 + " " + n3 + " " + n4);
t1();
t2();
t3();
t4();
}
public void t1() {
}
protected void t2() {
}
void t3() {
}
private void t4() {
}
}
package com.atguigu.visit;
//只有默认的和public才能修饰类!,并且遵循上述访问权限的特点
//如果没有 public ,B 类,不能跨包 使用, 只能在本包使用
public class B {
}
package com.atguigu.visit;
public class Test {
public static void main(String[] args) {
A a = new A();
//在同一个包中,可以访问 public ,默认,protected属性和方法
System.out.println(a.n1 + " " + a.n2 + " " + a.n3 + " " /*+ a.n4*/);
a.t1();
a.t2();
a.t3();
new B();
}
}
4.面向对象编程三大特征
4.1基本介绍
面向对象编程有三大特征:封装、继承和多态。
下面我们一一为同学们进行详细的讲解。
4.2封装介绍
**封装(encapsulation)**就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
4.3封装的理解和好处
1)隐藏实现细节
2)可以对数据进行验证,保证安全合理
Person {name, age} 1-120
4.4如何体现封装
1)对类中的属性进行封装
2)通过成员方法,包实现封装
4.5封装的实现步骤 //套路
1)将属性进行私有化 【不能直接修改属性】
2)提供一个公共的set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){
//加入数据验证的业务逻辑
属性 = 参数名;
}
3)提供一个公共的get方法,用于获取属性的值
public XX getXxx(){ //权限管理
return xx;
}
4.6快速入门案例
package com.atguigu.encap;
public class EncapTest {
public static void main(String[] args) {
Person person = new Person();
// person.name = "tom";
// person.age = 400000000;//没有验证.
person.setName("tom");
person.setAge(300);
person.showInfo();
}
}
class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = 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;
}
}
// 快捷键生成 setXxx 和 getXxx alt+shift+s
// public void setName(String name) {
// //逻辑
// this.name = name;
// }
//
// public void setAge(int age) {
// if( age >=1 && age <= 120) {
// this.age = age;
// } else{
// System.out.println("年龄1-120 默认值18");
// this.age = 18;
// }
// }
public void showInfo() {
System.out.println("信息 name=" + name + " age=" + age);
}
}
4.7将构造器和setXxx结合
看一个案例
package com.atguigu.encap;
public class EncapExercise {
public static void main(String[] args) {
Person2 person2 = new Person2("张飞", 88, 1000, "java架构师"); //10
System.out.println(person2); //
}
}
class Person2 {
private String name;
private int age;
private double sal;
private String job;
//构造器+setXxx
public Person2(String name, int age, double sal, String job) {
//this.name = name;
setName(name);
//this.age = age;
setAge(age);
this.sal = sal;
this.job = job;
}
public String getName() {
return name;
}
//name的长度在 2-6 之间
public void setName(String name) {
if( name.length() >= 2 && name.length() <= 6) {
this.name = name;
}else {
System.out.println("name 长度在 2-6 默认 无名");
this.name = "无名";
}
}
public int getAge() {
return age;
}
//年龄必须在 1-120
public void setAge(int age) {
if(age >= 1 && age <= 120) { //
this.age = age;
}else {
System.out.println("年龄不对应该在 1-120, 默认20");
this.age = 20;
}
}
public double getSal() {
//验证..
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
//输出信息toString
public String toString() {
return "Person2 [name=" + name + ", age=" + age + ", sal=" + sal + ", job=" + job + "]";
}
}
5.面向对象编程-继承
5.1为什么需要继承
一个小问题,还是看个程序Extends01.java,提出代码复用的问题。
我们编写了两个类,一个是Pupil 类(小学生),一个是Graduate(研究生).
问题 : 两个类的属性和方法有很多是相同的,怎么办 => 继承(代码复用性~)
5.2继承基本介绍和示意图
继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(比如刚才的Student),在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends语句来声明继承父类即可。
画出继承的示意图
5.3继承的基本语法
class 子类 extends 父类{
}
1)子类就会自动拥有父类定义的属性和方法。
2)父类又叫 超类,基类。
3)子类又叫派生类。
5.4快速入门案例
我们对Extends01.java 改进,使用继承的方法,请大家注意体会使用继承的好处:
package com.atguigu.extend.improve;
public class Student { //父类
String name;
double score;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public void showScore() {
System.out.println("学生名 " + name + " 成绩=" + score);
}
}
package com.atguigu.extend.improve;
public class Pupil extends Student { //小学生类, 子类
public void testing() {
System.out.println("小学生考语文...");
}
}
5.5继承给编程带来的便利
1)代码的复用性提高了
2)代码的扩展性和维护性提高了
5.6继承的深入讨论/细节问题
1)子类继承了所有的属性和方法,只是私有的属性不能直接访问,需要通过公共的方法去访问。
2)子类没有继承父类的构造器,但必须调用父类的构造器, 完成父类的初始化。
3)当创建子类时,不管你使用子类的哪个构造方法,默认情况下总会去调用父类的无参构造函数,如果父类没有提供无参构造函数,则必须在子类的构造函数中用 super 去指定使用父类的哪个构造函数完成对父类的初始化工作,否则,编译不会通过。
4)如果希望指定去调用父类的某个构造方法,则显示的调用一下。
5)super在使用时,需要放在方法体的第一句位置。
6)super() 和 this() 都只能放在构造方法句首,因此这两个方法不能共存在一个方法中。
7)java所有类都是Object类的子类。
8)父类构造器的调用不限于直接父类!将一直往上追溯直到Object类。
9)子类最多只能继承一个父类(指直接继承),即java中是单继承机制。
思考:如何让A类继承B类和C类? 【A 继承 B, B继承C】
10)不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系。
代码
package com.atguigu.extend;
import java.util.ArrayList;
public class ExtendsDetail {
public static void main(String[] args) {
BB bb = new BB();
bb.m1();
}
}
class DD {
public DD() {
System.out.println("DD() 被调用");
}
}
// 子类继承了所有的属性和方法,只是私有的属性不能直接访问,需要通过公共的方法去访问
class AA extends DD {
// 属性
public int n1 = 10;
protected int n2 = 20;
int n3 = 30;
private int n4 = 40;
public int getN4() {
return n4;
}
// public AA() {
// System.out.println("AA() 构造器..");
// }
/*
*/
public AA(String name) {
}
public AA() {
}
public AA(int num) {
//super();
}
}
//类的BB继承 ctrl+t
class BB extends AA { //子类BB 继承 AA
public void m1() {
System.out.println(n1 + " " + n2 + " " + n3 + " " /*+ n4 */);
System.out.println(getN4());
}
//子类没有继承父类的构造器,但必须调用父类的构造器, 完成父类的初始化.
//至于调用父类的哪个构造器,无所谓,但是一定要调用一个
public BB() {
//默认有一句话 super(), 父类的无参构造器
//如果希望指定去调用父类的某个构造方法,则显示的调用一下
//super在使用时,需要放在方法体的第一句位置
//super() 和 this() 都只能放在构造方法句首,因此这两个方法不能共存在一个方法中
super(10);
System.out.println("BB() 构造器..");
}
}
5.7继承的本质分析
案例
我们看一个案例来分析当子类继承父类,创建子类对象时,内存中到底发生了什么?
即当子类对象创建好后,建立查找的关系。
子类创建的内存图
代码案例
package com.atguigu.extend;
public class ExtendsTheory {
public static void main(String[] args) {
Son son = new Son();
System.out.println(son.name);
System.out.println(son.f1());
//son.say();
}
}
class GrandPa {
String name = "段大爷";
String hobby = "遛鸟";
// public void say() {
// System.out.println("GrandPa say..");
// }
}
class Father extends GrandPa {
String name = "段正纯";
int age = 30;
// public void say() {
// System.out.println("Father say..");
// }
}
class Son extends Father {
public String name = "段誉";
public String f1() {
return super.name;
}
// public void say() {
// System.out.println("son say..");
// }
}
导入项目
6super关键字
6.1基本介绍
super代表父类的引用,用于访问父类的属性、方法、构造器
6.2基本语法
-
访问父类的属性 , 不能访问父类的private属性 [案例]
super.属性名; -
访问父类的方法,不能访问父类的private方法
super.方法名(参数列表);
3)访问父类的构造器(这点前面用过):
super(参数列表);只能放在构造器的第一句,而且只能出现一句。
代码演示
package com.atguigu.super_;
public class SuperTest01 {
public static void main(String[] args) {
BB bb = new BB();
bb.m1();
}
}
class AA {
public int n1 = 10;
protected int n2 = 20;
int n3 = 30;
private int n4 = 40;
public void run() {
}
protected void eat() {
}
void sleep() {
}
private void cry() {
}
public AA() {
}
public AA(String name) {
}
}
class BB extends AA{
public void m1() {
//访问父类的属性 , 不能访问父类的private属性 [案例] super.属性名
//如果子类,和父类不在同一个包 ,默认的属性是否可以访问? 答不能
System.out.println(super.n1 + " " + super.n2 + " " + super.n3 /*+ super.n4*/);
}
//访问父类的方法,不能访问父类的private方法 super.方法名(参数列表);
//如果子类,和父类不在同一个包 ,默认的方法是否可以访问? 答不能
public void m2() {
super.eat();
super.run();
super.sleep();
//super.cry();
}
//访问父类的构造器(这点前面用过): super(参数列表);只能放在构造器的第一句,而且只能出现一句
//也只能访问 非私有的构造器,如果子类和父类不在同一个包,默认的构造器,也不能使用
public BB() {
// super();
super("hello");
}
}
6.3super给编程带来的便利/细节
-
调用父类的构造器 (分工明确, 父类属性由父类初始化,子类的属性由子类初始化)
-
当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果! [举例]
-
super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。A->B->C
代码
package com.atguigu.super_;
public class SuperDetail {
public static void main(String[] args) {
// TODO Auto-generated method stub
BBB bbb = new BBB();
bbb.m2();
}
}
class GrandPa {
public void hello() {
System.out.println("GrandPa hello()");
}
}
class AAA extends GrandPa{
public int n1 = 100;
public void say() {
System.out.println("AAA say...");
}
public void hi() {
System.out.println("AAA hi....");
}
public void hello() {
System.out.println("AAA hello()");
}
}
class BBB extends AAA {
public int n1 = 200;
public void say() {
System.out.println("BBB say...");
}
public void m1() {
//当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。
//如果没有重名,使用super、this、直接访问是一样的效果
//希望调用 父类的 say
super.say();
//希望调用 父类的 n1
System.out.println(super.n1);
//希望调用 父类 hi, super、this、直接访问是一样的效果
super.hi();//查找流程 (1) 直接到父类去查找 hi方法 (2) 如果父类没有就继续向上查找 (3) 如果都没有,则报错
this.hi();//查找流程(1) 先在本类去查找 hi方法 (2) 如果本类没有,则继续向父类及以上查找, 如果都没有,则报错
hi();//等价 this.hi()
//属性使用super, this , 默认不写的查找关系和 方法一样.
}
public void m2() {
//super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;
//如果多个基类中都有同名的成员,使用super访问遵循就近原则。A->B->C
super.hello();
}
}
6.4super和this的比较
7.方法重写(override)
7.1基本介绍
简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖/重写了父类的那个方法。
7.2快速入门
package com.atguigu.override;
//方法重写的基本案例
public class OverrideTest {
public static void main(String[] args) {
}
}
class A {
public int sum(int i , int j) {
return i + j;
}
}
class B extends A {
//这时,我们就说 子类B 的 sum方法,重写了 父类 A的sum方法
//@Override 注解: 作用是标记 方法重写了, 如果不满足重写规则,就会报错
@Override
public int sum(int n1, int n2) {
int res = n1 + n2;
return res;
}
}
7.3注意事项和使用细节
1)子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样。【演示】
2)子类方法的返回类型需要和父类方法返回类型一样,或者是父类返回类型的子类比如 父类 返回类型是 Object ,子类方法返回类型是String 【注:jdk5.0的特性】【演示】
3)子类方法不能缩小父类方法的访问权限 【演示】
4)代码演示
package com.atguigu.override;
public class OverrideDetail {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
class AA {
public void say(String name) {
System.out.println("AA say() " + name);
}
public Object hi() {
return new Object();
}
void run() {
System.out.println("AA run...");
}
}
class BB extends AA{
//子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样
public void say(String name) {
System.out.println("BB say() " + name);
}
//子类方法的返回类型需要和父类方法返回类型一样,或者是父类返回类型的子类
public String hi() {
return "";
}
//子类方法不能缩小父类方法的访问权限
//一代更比一代强
public void run() {
System.out.println("BB run...");
}
}
7.4重写和重载区别
8.面向对象编程-多态
8.1看一个问题,引出多态的必要性
使用传统的方法来解决(private属性)
package com.atguigu.poly;
public class PolyTest {
public static void main(String[] args) {
Master master = new Master("老韩");
Dog dog = new Dog("阿黄");
Bone bone = new Bone("大棒骨");
Cat cat = new Cat("布偶");
Fish fish = new Fish("黄花鱼");
master.feed(cat, fish); //
master.feed(dog, bone); //
}
}
//主人类
class Master {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Master(String name) {
super();
this.name = name;
}
//喂食物 -> 给猫猫喂鱼
public void feed(Cat cat , Fish fish) {
System.out.println("主人 " + name + " 给 " + cat.getName() + " 吃 " + fish.getName());
}
//方法重载
public void feed(Dog dog , Bone bone) {
System.out.println("主人 " + name + " 给 " + dog.getName() + " 吃 " + bone.getName());
}
//思考 如果喂食物的组合关系,那么就会出现很多的 feed方法,出现方法爆炸=>提出解决方案 多态
}
//使用程序 , 模拟 主人给动物喂食物
class Food { //食物
private String name;
public Food(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Fish extends Food { //鱼肉
public Fish(String name) {
super(name);
// TODO Auto-generated constructor stub
}
}
class Bone extends Food {//骨头
public Bone(String name) {
super(name);
// TODO Auto-generated constructor stub
}
}
class Animal { //动物类
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Animal(String name) {
super();
this.name = name;
}
}
class Cat extends Animal { //猫类
public Cat(String name) {
super(name);
// TODO Auto-generated constructor stub
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
// TODO Auto-generated constructor stub
}
}
传统的方法带来的问题?
引出我们要讲解的多态-> 代码的复用性不高,而且不利于代码维护
9.多[多种]态状态基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
9.1多态的具体体现
方法的多态
重写和重载
[案例说明:]
package com.atguigu.poly;
public class PolyOverLoad {
public static void main(String[] args) {
//方法重载体现多态
T t = new T();
t.say(100);
t.say("tom");
}
}
class T {
public void say(String name) {
System.out.println("hi " + name);
}
public void say(int num) {
System.out.println("hello" + num);
}
}
package com.atguigu.poly;
public class PolyOverride {
public static void main(String[] args) {
AA a = new AA();
a.hi("jack");
BB b = new BB();
b.hi("tom");
}
}
class AA {
public void hi(String name) {
System.out.println("AA " + name);
}
}
class BB extends AA {
@Override
public void hi(String name) { //子类hi 重写 父类的 hi
System.out.println("BB " + name);
}
}
对象的多态
一个对象的编译类型和运行类型不一致
Animal animal = new Dog(); 【Animal animal 就是编译类型,而 new Dog() 就是运行类型】[案例说明:]
package com.atguigu.poly;
public class PolyObject {
public static void main(String[] args) {
//对象多态的一个层面
//(1) 父类的引用 可以 指向 子类的对象
//(2) animal 就存在两种类型 1. 编译类型 2. 运行类型
//(3) 编译类型: 就是编译器识别的类型 animal 的编译类型就是 Animal
// 当程序员写代码时,只能调用编译类型可以 调用的方法和属性
//(4) 运行类型: 就是JVM 在运行的过程中识别的类型,这里 animal的运行类型就是 Cat
//对象多态的第二个层面
//(1) 一个对象引用,比如 animal 它的编译类型是不能改变的, 这里 就是Animal
//(2) 一个对象引用 ,它的运行类型是可以变化的. 比如变成 Dog
Animal animal = new Cat();
animal.cry();
animal = new Dog();
}
}
class Animal {
public void cry() {
System.out.println("Animal crying.. ");
}
}
class Cat extends Animal {
public void run() {
System.out.println("Cat is running");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("小狗 吃 狗粮...");
}
}
9.2多态快速入门案例
使用多态的机制来解决主人喂食物的问题,走代码。
public void feed(Animal animal, Food food) {
System.out.println("主人 " + name + " 给 " + animal.getName() + " 吃 " + food.getName());
}
9.3多态注意事项和细节讨论
多态的前提是:两个对象(类)存在继承关系
多态的向上转型
1)本质:父类的引用指向了子类的对象
2)语法:父类类型 引用名 = new 子类类型();
3)特点:编译看左边,运行看右边。可以调用父类类
型中的所有成员(需要遵守访问权限),
不能调用子类类型中特有成员;
最终运行效果看子类的具体实现!
4)代码
package com.atguigu.poly;
public class PolyUpTest {
public static void main(String[] args) {
AAA a = new AAA();
a.say();
a = new BBB();//向上转型 (父类引用指向了子类对象) => 动态绑定
a.say();
a.run(); //(1) 先找 BBB 的run ,没有找到 (2) 找 父类的 run
}
}
class AAA {
public void say() {
System.out.println("AAA say()");
}
public void run() {
System.out.println("AAA run()");
}
}
class BBB extends AAA {
public void say() {
System.out.println("BBB say()");
}
}
多态的向下转型
1)语法:子类类型 引用名 = (子类类型)父类引用;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)可以调用子类类型中所有的成员
5)代码
package com.atguigu.poly;
public class PolyDown {
public static void main(String[] args) {
Animal animal = new Cat();
//但是我希望调用 Cat的 catchMouse
//向下转型
//1. 语法:子类类型 引用名 = (子类类型)父类引用
//2. 只能强转父类的引用,不能强转父类的对象
//3. 父类的引用必须指向的是当前目标类型的对象, 即 animal 本身就是指向 一个 Cat 对象, 否则报错 ClassCastException
//4. 当执行完 Cat cat = (Cat)animal , animal 没有影响,只是返回了一个新的引用 cat 指向了 对象
Cat cat = (Cat)animal;
cat.catchMouse();
}
}
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("哈哈");
}
}
class Cat extends Animal { //子类
public void eat() {//重写
System.out.println("猫吃鱼");
}
public void catchMouse() {
System.out.println("猫抓老鼠");
}
}
class Dog extends Animal {
}
9.4多态注意事项和细节讨论
1)属性没有重写之说!属性的值看编译类型
代码
package com.atguigu.poly;
public class PolyProperties {
public static void main(String[] args) {
Base base = new Base();
System.out.println(base.n); // 200
Base base2 = new Sub();
System.out.println(base2.n); // 属性没有重写之说!属性的值看编译类型
}
}
class Base {
public int n = 200;
}
class Sub extends Base {
public int n = 300;
}
2)instanceOf 比较操作符,用于判断某个对象的运行类型是否为XX类型或XX类型的子类型 【举例说明】
package com.atguigu.poly;
public class InstanceOfTest {
public static void main(String[] args) {
AAAA bbbb = new BBBB();
//instanceOf 比较操作符,用于判断某个对象的运行类型是否为XX类型或XX类型的子类型
System.out.println(bbbb instanceof BBBB); // T
System.out.println(bbbb instanceof AAAA); // T
System.out.println(bbbb instanceof Object); // T
Object obj = new Object();
System.out.println(obj instanceof AAAA);// F
}
}
class AAAA {
}
class BBBB extends AAAA {
}
10.java的动态绑定机制
Java另一重要特性: 动态绑定机制
//提出java的动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。
- 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用。
多态的应用
代码
package com.atguigu.poly;
public class PolyArray {
public static void main(String[] args) {
//多态数组
Person[] persons = {new Person("jack", 10), new Student("tom",20, 78), new Student("king",21, 68)
, new Teacher("老王", 50, 10000), new Teacher("老李", 45, 20000)};
/*
* 应用实例升级:如何调用子类特有的方法,比如
* Teacher 有一个 teach , Student 有一个 study怎么
* 调用?[实现在多态数组调用各个对象的方法]遍历+instanceof + 向下转型
*/
PolyArray polyArray = new PolyArray();
polyArray.t1(persons);
}
//方法
public void t1(Person[] persons) {
for (int i = 0; i < persons.length; i++) {
//取出数组元素, 判断他的运行类型
if(persons[i] instanceof Teacher) {
//向下转型
((Teacher)persons[i]).teach();
} else if( persons[i] instanceof Student) {
((Student)persons[i]).study();
} else {
System.out.println(persons[i].say());
}
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
super();
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= " + name + " age= " + age;
}
}
class Student extends Person {
private double score;
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public void study() {
System.out.println("学生 " + getName() + " is studying java...");
}
}
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;
}
public void teach() {
System.out.println("老师 " + getName() + " is teaching java ");
}
}
代码
package com.atguigu.poly;
public class PolyArgs {
public static void main(String[] args) {
// TODO Auto-generated method stub
Employee manager = new Manager(10000, "小胡", 200000);
Employee worker = new Worker(2000, "老胡");
PolyArgs polyArgs = new PolyArgs();
polyArgs.showEmpAnnal(manager);
polyArgs.showEmpAnnal(worker);
polyArgs.testWork(manager);
polyArgs.testWork(worker);
}
/*
* 测试类中添加一个方法showEmpAnnal(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法 [e.getAnnual()]
*/
public void showEmpAnnal(Employee e) {
System.out.println("员工 " + e.getName() + " 年薪=" + e.getAnnual());
}
//测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
public void testWork(Employee e) {
//运行类型,instanceof + 向下转型 + 方法
if(e instanceof Worker) {
((Worker)e).work();
} else if(e instanceof Manager) {
((Manager)e).manage();
} else {
System.out.println("是雇员..");
}
}
}
//定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法
class Employee {
private double monSal;
private String name;
public Employee(double monSal, String name) {
super();
this.monSal = monSal;
this.name = name;
}
public double getMonSal() {
return monSal;
}
public void setMonSal(double monSal) {
this.monSal = monSal;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getAnnual () {
return 12 * monSal;
}
}
//普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,
//普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法
class Worker extends Employee {
public Worker(double monSal, String name) {
super(monSal, name);
// TODO Auto-generated constructor stub
}
public void work() {
System.out.println("普通员工 " + getName() + " is working");
}
@Override
public double getAnnual() {
// TODO Auto-generated method stub
return super.getAnnual();
}
}
//
class Manager extends Employee {
private double bonus;
public Manager(double monSal, String name, double bonus) {
super(monSal, name);
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() {
// TODO Auto-generated method stub
return super.getAnnual() + bonus;
}
}