面向对象详讲(中)

面向对象详讲(中)

**

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基本语法

  1. 访问父类的属性 , 不能访问父类的private属性 [案例]
    super.属性名;

  2. 访问父类的方法,不能访问父类的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给编程带来的便利/细节

  1. 调用父类的构造器 (分工明确, 父类属性由父类初始化,子类的属性由子类初始化)

  2. 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果! [举例]

  3. 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的动态绑定机制

  1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。
  2. 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用。
    在这里插入图片描述

多态的应用
在这里插入图片描述
代码

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;
	}
	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值