------ android培训、java培训、期待与您交流! ----------
多态其实就是多种状态的含义,如我们方法重载,相同的方法名称可以完成不同的功能,这就是多态的一种表现,此时成为静态多态。另外就是我们将学生保存到数据的示例,当我们调用Istudent 接口中的方法,那么java 会自动调用实现类的方法,如果是Oracle 实现就调用Oracle 的方法,如果是MySql 实现就调用MySql 中的方法,这是在运行期决定的。多态的条件是:有继承或实现,有方法覆盖或实现,父类对象(接口)指向子类对象。
一、多态知识总结
1、多态的前提
①有继承或实现;
②有方法覆盖或实现;
③父类对象(接口)指向子类对象
2、多态的好处与弊端
好处:提高代码的维护性(继承保证);提高代码的扩展性(多态保证)。
<span style="font-size:18px;color:#666666;">class Animal{
public void eat(){
System.out.println("Hello 吃!");
}
public void sleep(){
System.out.println("Hello 睡!");
}
public void play(){
System.out.println("Hello 玩!");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼!");
}
public void sleep(){
System.out.println("猫蜷着睡!");
}
public void play(){
System.out.println("玩毛线!");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头!");
}
public void sleep(){
System.out.println("狗趴着睡");
}
public void play(){
System.out.println("狗玩猫");
}
}
class DuoTaiDemo2{
public static void main(String[] args) {
//我想养猫
Cat c = new Cat();
//c.eat();
//c.sleep();
//c.play();
//show(c);
AnimalTools.show(c);
//觉得一只猫太孤单,又来了一个伴
Cat c2 = new Cat();
//c2.eat();
//c2.sleep();
//c2.play();
//show(c2);
AnimalTools.show(c2);
//它们产了一只小猫
Cat c3 = new Cat();
//c3.eat();
//c3.sleep();
//c3.play();
//show(c3);
AnimalTools.show(c3);
//这时 发现代码重复的太多了 ?
System.out.println("---又想养狗了---------------------");
Dog d = new Dog();
//show(d);
AnimalTools.show(d);
Dog d2 = new Dog();
//show(d2);
AnimalTools.show(d2);
Dog d3 = new Dog();
//show(d3);
AnimalTools.show(d3);
//DuoTaiDemo2 这是一个测试类,测试类一般写main方法,所以 我不喜欢功能的代码写在测试类里
//怎么办 这是不是都是对动物进行的操作 是不是可以写一个工具类 里面放这些动物的show()方法
//我还想养老虎 怎么办 老虎继承动物类 ,老虎在重写那些功能,在工具类里写一个老虎的show() 创建对象,调用show();
//工具类来回做操作 好吗?
//怎么办 想一次写好
}
/*
//写个show方法,看一下一只猫都能干什么
public static void show(Cat c){
c.eat();
c.sleep();
c.play();
}
public static void show(Dog d){
d.eat();
d.sleep();
d.play();
}
*/
}
//动物操作工具类
class AnimalTools{
public static void show(Animal a){
// 传递过来的是c =new Cat(); c是猫的对象 猫是动物的子类
// Animal a = c; Animal a = new Cat();多态的体现
a.eat();
a.sleep();
a.play();
}
/*
public static void show(Cat c){
c.eat();
c.sleep();
c.play();
}
public static void show(Dog d){
d.eat();
d.sleep();
d.play();
}
*/
}</span>
上图中的代码在没有使用多态前异常复杂,引入多态之后代码变得相当简化,又扩展了一些新功能,体现了多态对代码的维护性和扩展性。
弊端:父类不能访问子类的特有功能。
3、多态的表现形式
①向上转型: 父类引用指向子类对象 Fu f = new Zi(); 相当于基本数据类型的自动转换
②向下转型: 将一个父类引用转换成子类引用 Zi z = (Zi)f;前提:该父类引用必须是指向该子类对象的。
<span style="font-size:18px;color:#666666;">class Fu{
int age = 50;
String name = "王健林";
public void house(){
System.out.println("一座101层大楼!");
}
}
class Zi extends Fu{
String name = "王思聪";
int age = 25;
public void house(){
System.out.println("给51层!");
}
public void netManager(){
System.out.println("网管!");
}
}
class NvEr extends Fu{
String name = "王冬梅";
int age = 21;
public void house(){
System.out.println("给50层!");
}
public void show(){
System.out.println("参加车展!");
}
}
class DuoTaiDemo3{
public static void main(String[] args) {
//健林病了,但是有合约要签
Fu f = new Zi();//王思聪代替他爹去签合约 向上转型
System.out.println(f.name);//王健林的名字
f.house();
//f.netManager();//能去当网管吗?爹是不能当网管的
Zi z = (Zi)f;//换回自己的身份 向下转型
z.netManager();
/*
Exception in thread "main" java.lang.ClassCastException: Zi cannot be cast to NvEr
at DuoTaiDemo3.main(DuoTaiDemo3.java:52)
*/
// NvEr nv = (NvEr)f;//向下转型
}
}</span>
4、多态中成员访问特点
①成员变量 : 编译看左边,(=的左边),运行看左边 成员变量的特点:加载到堆内存中了
②成员方法 : 编译看左边,运行看右边 方法存在重写,不调用不执行
③静态成员方法: 随着类的加载而加载,与对象无关,编译看左边,运行看左边
④构造方法: 子类创建对象的时候,先加载父类的构造,再加载子类构造
5、一道经典案
<span style="font-size:18px;color:#666666;">/* 教练和运动员案例(学生分析然后讲解)
乒乓球运动员和篮球运动员。
乒乓球教练和篮球教练。
为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所学知识:
分析,这个案例中有哪些抽象类,哪些接口,哪些具体类。
*/
//学习英语接口
interface StudyEnglish{
public abstract void study();
}
//定义人类
abstract class Person{
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public abstract void eat();
public void sleep(){
System.out.println("晚安,洗洗睡吧");
}
public void show(){
System.out.println(name+"---"+age);
}
}
//定义教练类
abstract class Coach extends Person{
public Coach(){}
public Coach(String name,int age){
super(name,age);
}
public abstract void teach();
}
//定义篮球教练
class BBCoach extends Coach {
public BBCoach(){}
public BBCoach(String name,int age){
super(name,age);
}
//重写抽象方法
public void teach(){
System.out.println("教打篮球");
}
public void eat(){
System.out.println("吃牛肉,喝牛奶");
}
}
//定义乒乓球教练
class PPCoach extends Coach implements StudyEnglish{
public PPCoach(){}
public PPCoach(String name,int age){
super(name,age);
}
//重写方法
public void teach(){
System.out.println("教打乒乓球");
}
public void eat(){
System.out.println("吃羊肉,喝羊奶");
}
//你实现了接口就要重写接口中的方法
public void study(){
System.out.println("学英语是出国的前提");
}
}
class InterfaceTest2{
public static void main(String[] args) {
//创建一个篮球教练
BBCoach bb = new BBCoach("宫鲁鸣",55);
bb.teach();
bb.eat();
bb.sleep();
bb.show();
//bb.study();
//创建一个乒乓球教练
PPCoach pp = new PPCoach("刘国梁",45);
pp.study();
pp.show();
}
}
</span>
二、类之间的关系
①泛化关系,类和类之间的继承关系及接口与接口之间的继承关系;
②实现关系,类对接口的实现;
③关联关系,类与类之间的连接,一个类可以知道另一个类的属性和方法,在java 语言中使用成员变量体现;
④聚合关系,是关联关系的一种,是较强的关联关系,是整体和部分的关系,如:汽车和轮胎,它与关联关系不同,关联关系的类处在同一个层次上,而聚合关系的类处在不平等的层次上,一个代表整体,一个代表部分,在java 语言中使用实例变量体现;
⑤合成关系,是关系的一种,比聚合关系强的关联关系,如:人和四肢,整体对象决定部分对象的生命周期,部分对象每一时刻只与一个对象发生合成关系,在java 语言中使用实例变量体现;
⑥依赖关系,依赖关系是比关联关系弱的关系,在java 语言中体现为返回值,参数,局部变量和静态方法调用。
⑦is-a、is-like-a和has-a关系
<span style="font-size:18px;color:#666666;">// is-a关系
public class Animal {
public void method1();
}
public class Dog extends Animal { // Dog is a Animal
}
// is-like-a关系
public interface I {
public void method1();
}
public class A implements I { // A is like a I;
public void method1() {
// 实现
}
}
// has-a关系
public class A { // A has a B;
private B b;
}
public class B {
}</span>
三、包和import
1、包
包其实就是目录,特别是项目比较大,java 文件特别多的情况下,我们应该分目录管理,在java 中称为分包管理,包名称通常采用小写。
<span style="font-size:18px;color:#666666;">/*
1、包最好采用小写字母
2、包的命名应该有规则,不能重复,一般采用公司网站逆序,
如:com.bjpowernode.项目名称.模块名称
com.bjpowernode.exam
*/
//package 必须放到所有语句的第一行,注释除外
package com.bjpowernode.exam;
public class PackageTest01 {
public static void main(String[] args) {
System.out.println("Hello Package!!!");
}
}</span>
运行出现类不能找到错误,提示给的很明显,如com.bjpowernode.exam.PackageTest01 类不能找到,因为我们加入了包,所以我们的class 文件必须放到和包一样的目录里才可以,这就是采用包来管理类,也就是采用目录来管理类,建立目录com/bjpowernode/exam,采用javaPackageTest01 执行,同样出现上面的错误,如果采用了包在执行该类时必须加入完整的包名称,正确的执行方式为java com.bjpowernode.exam.PackageTest01,正确执行通过。另外还有一点需要注意:必须在最外层包采用java 来执行,也就是classpath 必须设置在chapter03 目录上,以chapter03 目录为起点开始找我们的class 文件。
2、import
如何使用包下的class 文件?
<span style="font-size:18px;color:#666666;">package com.bjpowernode.exam;
//采用import 引入需要使用的类
//import com.bjpowernode.exam.model.User;
//import com.bjpowernode.exam.model.Student;
//import com.bjpowernode.exam.model.Employee;
//可以采用* 通配符引入包下的所有类
//此种方式不明确,但简单
import com.bjpowernode.exam.model.*;
//package 必须放到所有语句的第一行,注释除外
//package com.bjpowernode.exam;
public class PackageTest02 {
public static void main(String[] args) {
User user = new User();
user.setUserId(10000);
user.setUserName("张三");
System.out.println("user.id=" + user.getUserId());
System.out.println("user.name=" + user.getUserName());
}
}</span>
如果都在同一个包下就不需要import 引入了,以上的示例都没有包,可以理解为都在同一
个包下,在实际开发过程中不应该这样做,必须建立包。
四、内部类
1、内部类的含义
在一个类的内部定义的类,称为内部类,内部类主要分类:实例内部类、局部内部类、静态内部类。
2、实例内部类
①创建实例内部类,外部类的实例必须已经创建;
②实例内部类会持有外部类的引用;
③实例内部不能定义static 成员,只能定义实例成员;
<span style="font-size:18px;color:#666666;">public class InnerClassTest01 {
private int a;
private int b;
InnerClassTest01(int a, int b) {
this.a = a;
this.b = b;
}
// 内部类可以使用private 和protected 修饰
private class Inner1 {
int i1 = 0;
int i2 = 1;
int i3 = a;
int i4 = b;
// 实例内部类不能采用static 声明
// static int i5 = 20;
}
public static void main(String[] args) {
InnerClassTest01.Inner1 inner1 = new InnerClassTest01(100, 200).new Inner1();
System.out.println(inner1.i1);
System.out.println(inner1.i2);
System.out.println(inner1.i3);
System.out.println(inner1.i4);
}
}</span>
3、静态内部类
①静态内部类不会持有外部的类的引用,创建时可以不用创建外部类;
②静态内部类可以访问外部的静态变量,如果访问外部类的成员变量必须通过外部类的实例访问。
<span style="font-size:18px;color:#666666;">public class InnerClassTest02 {
static int a = 200;
int b = 300;
static class Inner2 {
// 在静态内部类中可以定义实例变量
int i1 = 10;
int i2 = 20;
// 可以定义静态变量
static int i3 = 100;
// 可以直接使用外部类的静态变量
static int i4 = a;
// 不能直接引用外部类的实例变量
// int i5 = b;
// 采用外部类的引用可以取得成员变量的值
int i5 = new InnerClassTest02().b;
}
public static void main(String[] args) {
InnerClassTest02.Inner2 inner = new InnerClassTest02.Inner2();
System.out.println(inner.i1);
}
}</span>
4、局部内部类
局部内部类是在方法中定义的,它只能在当前方法中使用。和局部变量的作用一样,局部内部类和实例内部类一致,不能包含静态成员
<span style="font-size:18px;color:#666666;">public class InnerClassTest03 {
private int a = 100;
// 局部变量,在内部类中使用必须采用final 修饰
public void method1(final int temp) {
class Inner3 {
int i1 = 10;
// 可以访问外部类的成员变量
int i2 = a;
int i3 = temp;
}
// 使用内部类
Inner3 inner3 = new Inner3();
System.out.println(inner3.i1);
System.out.println(inner3.i3);
}
public static void main(String[] args) {
InnerClassTest03 innerClassTest03 = new InnerClassTest03();
innerClassTest03.method1(300);
}
}</span>
5、匿名内部类
匿名内部类是一种特殊的内部类,该类没有名字。
<span style="font-size:18px;color:#666666;">//没有使用匿名类的情况
public class InnerClassTest04 {
public static void main(String[] args) {
MyInterface myInterface = new MyInterfaceImpl();
myInterface.add();
}
}
interface MyInterface {
public void add();
}
class MyInterfaceImpl implements MyInterface {
public void add() {
System.out.println("-------add------");
}</span>
<span style="font-size:18px;color:#666666;">//使用匿名类的情况
public class InnerClassTest05 {
public static void main(String[] args) {
/*
* MyInterface myInterface = new MyInterface() { public void add() {
* System.out.println("-------add------"); } }; myInterface.add();
*/
/*
* MyInterface myInterface = new MyInterfaceImpl(); InnerClassTest05
* innerClassTest05 = new InnerClassTest05();
* innerClassTest05.method1(myInterface);
*/
InnerClassTest05 innerClassTest05 = new InnerClassTest05();
innerClassTest05.method1(new MyInterface() {
public void add() {
System.out.println("-------add------");
}
});
}
private void method1(MyInterface myInterface) {
myInterface.add();
}
}
interface MyInterface {
public void add();
}
/*
* class MyInterfaceImpl implements MyInterface { public void add() {
* System.out.println("-------add------"); } }
*/</span>
五、参数传递与返回值
1、 接口名作为返回值类型
<span style="font-size:18px;color:#666666;">/*
接口名作为返回值类型
返回的是该接口的实现类的对象
*/
interface Love{
public abstract void love();
}
//接口的实现类
class Teacher implements Love{
public void love(){
System.out.println("爱学生,爱java,爱生活");
}
}
class LoveDemo{
public Love method(){
//返回的时候你要找Love类型的对象 Love是一个接口 他不能直接创建对象
//所以你需要找他的实现类对象(接口多态)
return new Teacher();
}
}
class ReturnDemo3{
public static void main(String[] args) {
//因为接口不能直接创建对象 要通过多态去实现
LoveDemo ld = new LoveDemo();
Love l= ld.method();//Love l = new Teacher();
l.love();
}
}
</span>
2、抽象类名作为返回值类型
<span style="font-size:18px;color:#666666;">/*
抽象类名做为返回值类型
返回的是该类子类对象
*/
abstract class Animal{
public abstract void eat();
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼!");
}
}
class AnimalDemo{
public Animal method(){
//要return一个对象 什么类型的对象 Animal类型的
// 问:Animal是抽象类,不能直接创建对象
// 他的子类对象,是不是Animal类型的对象 是 因为继承
//返回该类的子类对象
return new Cat();
}
}
class ReturnDemo2 {
public static void main(String[] args) {
//通过AnimalDemo 调用eat()方法
AnimalDemo ad = new AnimalDemo();
Animal a = ad.method();
a.eat();
}
}
</span>
3、类名作为返回值类型
<span style="font-size:18px;color:#666666;">/*
类名作为返回值类型
返回的是该类对象
*/
class Student{
public void show(){
System.out.println("好好学习,天天向上");
}
}
class StudentDemo{
public Student method(){
return new Student();
}
}
class ReturnDemo {
public static void main(String[] args) {
//怎么用
StudentDemo sd = new StudentDemo();
Student s = sd.method();
s.show();
}
}
</span>
4、链式编程
<span style="font-size:18px;color:#666666;">/*
sd.method().show(); 链式编程
前提: 最后调方法的返回值要是一个类或者对象才可以
每次调用完毕后返回的是一个对象
*/
class Student{
public void show(){
System.out.println("好好学习,天天向上");
}
}
class StudentDemo{
public Student method(){
return new Student();
}
}
class LinkedDemo {
public static void main(String[] args) {
//怎么用
StudentDemo sd = new StudentDemo();
Student s = sd.method();
s.show();
//简化写一下调用
//改进一
sd.method().show();
//在简化
new StudentDemo().method().show();
}
}
</span>