面向对象三大特性之多态(抽象类多态,接口多态)--引用类型作为形参类型或返回值类型的研究--匿名内部类---多态的案例实践

多态

多态的概念
一个事物在不同时刻显示出的不同状态(在堆内存中的变化)
多态的前提条件
1)必须存在继承关系 (继承的好处第三点)
2)要有方法重写 :
	举例:动物的吃和睡的功能,只要看到具体的动物类:才能知道吃和睡的功能
3)需要存在:父类引用指向子类对象  (向上转型:使用的父类的东西,必须编译看左边!)
	格式:父类名 对象名 = new 子类名() ;
多态的成员访问特点
父类名 对象名 = new 子类名() ;
1)成员变量: 编译看左,运行看左边
2)成员方法:(非静态的):编译看左边,运行看右(因为存在子类覆盖父类的方法:方法重写)
3)静态的成员方法:编译看左,运行看左
	(子类出现了和父类一摸一样的静态方法,静态方法跟类相关,不存在重写!,访问方式:类名.方法名())
4)构造方法:
	由于存在继承关系,还需要分层初始化!
多态的好处
1)提高了代码的扩展性(由多态)
2)提高了代码的复用性,维护性(由继承关系保证)
多态的弊端
不能够访问子类的特有功能!

解决方案:
如何解决呢?
	方式1:可以创建子类具体类对象 :
		子类名 对象名 = new 子类名() ;
        对象名.访问子类的自己的功能;
        但是,从内存角度考虑,在堆内存中产生一个内存空间(比较消耗内存空间!)
	方式2:
        多态的第三个条件:父类引用指向子类对象 (向上转型)
        Fu f = new Zi() ;
        想办法:将父类的引用强制转换为子类的引用(向下转型)
        Zi z = (Zi)f;
        好处:不需要重新new对象,在堆内存是比较节省内存空间的!
	前提条件:要使用向下转型,必须先使用向上转型!,不然会产生异常
多态的向下转型使用不当,程序出现异常:
  	注意事项:
 	产生一个ClassCastException:运行时期异常(RuntimeException)的一种
  	使用向下转型的时候类型不匹配导致的!

抽象类

抽象的概念
在一个类中,将某个功能给出一个声明(抽象方法:没有方法体的方法),那么该类定义为抽象类!

有抽象方法的类一定是抽象类, 抽象类不一定有抽象方法(就是让当前类不能够直接实例化
抽象类的本质
强制子类必须完成的事情!
抽象类的特点:
1)成员变量:
	既可以定义常量,也可以定义变量
2)成员方法:
	即可定义抽象方法(必须要有abstract),也可以定义非抽象方法
3)构造方法:
	还是继承关系,构造方法:对数据进行初始化---->分层初始化!
抽象方法
权限修饰符(public) abstract 返回值类型 方法名() ;
public abstract void show() ;
抽象类中的注意事项
1)如果一个类中,有抽象方法,那么该类一定是抽象类;抽象类中一定是抽象方法吗?,也可以有非抽象方法
2)抽象类如何实例化呢? 抽象类如何创建对象
    抽象类不能直接实例化! ,通过具体的子类进行实例化
        格式:
        父类名 对象名  =new  子类名() ; 抽象类多态
3)抽象类的子类:
    子类要么具体类,要么抽象类
    子类是具体类,直接可以抽象类多态的形式实例化!
    子类是抽象类,应该还要提供更最具体的子类,否则都无法实例化(没有意义!)
4)在抽象类中定义抽象方法的时候,那么抽象方法必须带上修饰符:abstract,不能省略!

接口

接口概念
是描述一个事物的扩展功能,本身事物不具备的,
如果该事物想使用这个扩展功能,必须将接口的功能进行实现
命名格式和子实现类
interface 接口名{
		...
  		}
 接口中的方法只能是抽象方法,不能有方法体
 		
 接口的命名规则:多个单词---->遵循"大驼峰命名法"
  
接口的实现类和接口是一种:implements 实现关系
  将接口的实现类--->称为"接口的子实现类"
接口实例化
接口如何实例化呢?
	接口不能直接实例化 ,
	格式:
		接口名 对象名 =new 子实现类名() ; //接口多态
接口的成员特点
  接口成员特点:
  		成员变量: 只能常量,被静态修饰
  			存在默认修饰符:public satic final...(可以省略不写!)
  		成员方法:
  				接口中的方法只能是抽象方法,存在默认修饰符:public abstract(可以省略不写!)
  		构造方法:
  				接口没有构造方法!
接口和抽象类的区别:
1)成员的区别:
	抽象类:  成员变量:可以是常量,也可以是变量
			成员方法:可以是抽象方法,也可以是费抽象方法
			构造犯法:无参/有参构造方法
	接口:	   成员变量:常量  -- 默认 public static final修饰
			成员方法: 只有抽象方法
			构造方法: 不存在构造方法
2)关系的区别
	类与类之间: 可以单继承,不能多继承,但是可以多层继承
	类与接口之间: 一个类在继承一个类的同时可以实现多个接口
	接口与接口: 可以单继承,也可以多继承
3)设计理念的区别
	抽象类:  由继承来保证代码的复用性,由多态来保证代码的扩展性
				is a 的关系
	接口:   是一种额外的功能,体现的是一种"like a"的关系
				like a 的关系

抽象类和接口的应用

1.猫狗吼叫
/*	1.设计一个Java程序

(1)定义一个接口CanCry,描述会吼叫的方法public void cry()
(2)分别定义狗类(Dog)和猫类(Cat),实现CanCry接口。实现方法的功能分别为:
    打印输出“我是狗,我的叫声是汪汪汪”、“我是猫,我的叫声是喵喵喵”
(3)定义一个主类G, 
   定义一个void  makeCry(CanCry c)方法,其中让会吼叫的事物吼叫。
   在main方法中创建狗类对象(dog)、猫类对象(cat)、G类对象(g),用
   g调用makecry方法,让狗和猫吼叫。
*/
//定义一个接口CanCry
public interface CanCry {
    public abstract void cry();
}
//定义一个Dog类 实现 CanCry接口
public class Dog implements CanCry {
    @Override
    public void cry() {
        System.out.println("我是狗,我的叫声是汪汪汪");
    }
}
//定义一个Cat类 实现 CanCry接口
public class Cat implements CanCry {
    @Override
    public void cry() {
        System.out.println("我是猫,我的叫声是喵喵喵");
    }
}
//定义一个主类G  
public class G {
    //定义方法makeCry()传入接口类CanCry类型的变量c 作为参数
    public void makeCry(CanCry c) {
        c.cry();
    }
}
//定义一个测试类
public class Test {
    public static void main(String[] args) {
        G g = new G();
        //直接传入匿名对象
        g.makeCry(new Dog());
        g.makeCry(new Cat());	
    }
}
2.运动员和教练
/*	2. 教练和运动员
乒乓球运动员和篮球运动员。
乒乓球教练和篮球教练。
为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所学知识:
分析,这个案例中有哪些抽象类,哪些接口,哪些具体类(自定义属性以及一些成员方法,进行测试!)

接口: 学英语
抽象类: 人         教练,运动员(extend 人)
具体类-->子类:篮球运动员 乒乓球运动员 (extends 运动员) 篮球教练 乒乓球教练 (extends 教练) 
子实现类 乒乓球运动员 乒乓球教练(implements 学英语)
*/
//学习英语的接口
public interface StudyEnglish {
	public abstract void speakEnglish() ;
}
//人类
public abstract class Person {
	private String name ;//姓名
	private int age ; //年龄
	private String gender ;//性别
	public Person() {
		super();
	}
	public Person(String name, int age, String gender) {
		super();
		this.name = name;
		this.age = age;
		this.gender = gender;
	}
	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 getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	//吃的抽象方法
	public abstract void eat() ;
}
//教练类
public abstract class Coach extends Person {
	public Coach() {
		super();
	}
	public Coach(String name, int age, String gender) {
		super(name, age, gender);
	}
	@Override
	public void eat() {
		System.out.println("coach 吃的普通餐...");
	}
	//教的东西不一样
	public abstract void teach() ;

}
//运动员类
public abstract class Player extends Person {
	public Player() {
		super();
	}
	public Player(String name, int age, String gender) {
		super(name, age, gender);
	}
	@Override
	public void eat() {
		
		System.out.println("player 需要吃营养餐...");
	}
	//运动员都需要学习 抽象功能(具体的运动员有学习的动作)
	public abstract void sutdy() ;
}
//具体的篮球教练
public class BasketBallCoach extends Coach {
	public BasketBallCoach() {
		super();
	}
	public BasketBallCoach(String name, int age, String gender) {
		super(name, age, gender);
	}
	@Override
	public void teach() {
		System.out.println("教授如何运球和投篮...");
	}
}
//篮球运动员
public class BasketBallPlayer extends Player {
	public BasketBallPlayer() {
		super();
	}
	public BasketBallPlayer(String name, int age, String gender) {
		super(name, age, gender);
	}
	@Override
	public void sutdy() {
		System.out.println("学习如果运球和投篮...");
	}
}
//具体的乒乓球教练
public class PingPangCoach extends Coach implements StudyEnglish {
	public PingPangCoach() {
		super();
	}
	public PingPangCoach(String name, int age, String gender) {
		super(name, age, gender);
	}
	@Override
	public void speakEnglish() {
		System.out.println("可以出国交流英语...");
	}
	@Override
	public void teach() {
		System.out.println("教授如何发球和接球...");
	}
}
//乒乓球运动员
public class PingPangPlayer extends Player implements StudyEnglish {
	public PingPangPlayer() {
		super();
	}
	public PingPangPlayer(String name, int age, String gender) {
		super(name, age, gender);
	}
	@Override
	public void speakEnglish() {
		System.out.println("可以出国交流英语了");
 	}
	@Override
	public void sutdy() {
		System.out.println("学习如何发球和接球");
	}
}
//测试类
public class Test {
	public static void main(String[] args) {
		//测试:最具体的类:和乒乓球相关的人员功能多
		//测试乒乓球教练
		PingPangCoach  ppc = new PingPangCoach() ;
		ppc.setName("admin");
		ppc.setAge(60);
		ppc.setGender("男");
		System.out.println(ppc.getName()+"---"+ppc.getGender()+"---"+ppc.getAge());
		ppc.eat();
		ppc.teach();
		ppc.speakEnglish();		
	}
}
3.榨汁机案例
/*	定义榨汁机JuiceMachine 有榨汁方法makeJuice,传入相应的水果。
    如果传入的是Apple 输出   "流出苹果汁"
    传入的是Orange 输出  "流出橙汁"
    传入的是Banana 输出  "流出香蕉酱"
*/
//榨汁机类
public class JuiceMachine {
	//榨汁的方法makeJuice传入相应的水果
	public void makeJuice(Fruit f) { //方法的形式参数传递的是一个具体类 (抽象类)
		f.out();
	}
}

//水果类
public abstract class Fruit {
	public abstract void out();
}
//苹果类
public class Apple extends Fruit {
	@Override
	public void out() {
		System.out.println("流出苹果汁");
	}
}
public class Banana extends Fruit {
	@Override
	public void out() {
		System.out.println("流出香蕉酱");
	}
}
public class Orange extends Fruit {
	@Override
	public void out() {
		System.out.println("流出橙汁");  
	}
}
public class Test {
	public static void main(String[] args) {
		//创建一个榨汁机对象
		JuiceMachine jm = new JuiceMachine() ;
		//抽象类的子类实例化
		Fruit f = new Apple() ; //抽象类多态
		Fruit f2 = new Orange() ;
		Fruit f3 = new Banana() ;
		jm.makeJuice(f);
		jm.makeJuice(f2);
		jm.makeJuice(f3);
	}
}

形式参数类型和返回值类型为引用类型的研究

方法的返回值是类,接口,抽象类,如何返回
    形式参数是引用类型
        如果是类(具体类):调用方式实际参数需要传递该类的具体对象(匿名内部类)
        如果是抽象类:实际参数需要传递该抽象类的子类对象(匿名内部类)
        如果是接口:实际参数需要传递该接口的子实现类对象(匿名内部类)
    返回值是引用类型
        如果是类(具体类):返回该类具体对象(匿名内部类)
        如果是抽象类:需要返回该抽象类的子类对象(匿名内部类)
        如果是接口:需要返回该接口的子实现类对象(匿名内部类)

package(包)

通过包来对代码进行分层:
xx.xx.xx.entity/pojo/domain :实体类 
xx.xx.xx.dao : 数据库访问接口层
xx.xx.xx.dao.impl : 数据库访问的接口实现层
xx.xx.xx.service:业务接口层
xx.xx.xx.service.impl:业务接口实现层
xx.xx.xx.controller/.web:控制业务以及视图的!

带包的编译和运行

在同一个包下:
    手动方式: 
      1)在当前目录下,针对java源文件进行编译,-----类名.class文件
      2)创建指定的包的路径:com.qianfeng;
      3)将class文件存放到指定的包的路径下
      4)java  包名.类名
    自动方式
      1)直接针对java源文件直接编译
      javac -d . xxx.java
      2)直接生产的包名以及下面的class文件
      3)java 包名.类名 
在不同包下:package
    1)需要导包:在当前这个包下的类导入另一个包下的类(被导入的类的访问权限最大的:public) import xx .x.x ;
    2)将被导入的类进行编译:  javac -d . xxx.java
    3)对当前包下的类进行编译: javac - d . xx.java
    4)java 包名.类名(当前这个类名)

权限修饰符的优先级比较

同一个包下的本类(是否能够访问)同一个包的下的子类/无关类(是否能够访问)不同包下的子类(是否能够访问)不同包下的无关类(是否能够访问)
privateYNNN
默认YYNN
protectedYYYN
publicYYYY

内部类

内部类:
  	成员内部类:  
    	如果是非静态的
   			 外部类名.内部类名 对象名 =  外部类对象.内部类对象;

		静态内部类的
			 外部类名.内部类名 对象名  = new 外部类名.内部类名();
  
  局部内部类: 在局部位置中定义的类
  		局部内部类的成员可以直接访问外部类的成员,包括私有的成员
  		
  		
外部类成员方法如何访问局部内部类的成员?
 	在当前外部类的成员方法中,只需要创建内部类对象调用它的方法


局部内部类访问局部变量的时候,为什么将局部变量使用final修饰?
  					--- JDK8版本已经局部变量做了优化(默认使用final修饰)
  	局部变量的生命周期:随着方法调用而存在,随着方法调用完毕而消失,
  	由于当前局部内部类中的成员方法还在使用局部变量,间接通过了外部类的成员方法中创建内部类对象访问内部类的成员方法---访问局部变量,对象不会立即被回收,需要等待GC空闲时候回收,内部类的成员方法还在访问,需要将当前局部变量定义为常量---内存中固定值!

匿名内部类

 外部类和内部类之间是没有继承关系的!
 
 它是内部类的一种简化格式
 	 没有具体类名
 格式:
  		new 类名(抽象类类名)/接口名(){
  				重写方法....
 		};
匿名内部类的本质: 就是继承该抽象类或者实现了该接口的子类对象!
		可以作为抽象类的子类对象/接口的子实现类
匿名内部类的应用
//补全代码,在控制台输出"helloworld"
interface Inter{
	 void show() ;
 }
class Outer{ 		
	  //补全代码
}
//测试类
public class Test {
	public static void main(String[] args) {
		Outer.method().show() ;
	}
}
分析: Outer.method() 使用类名.方法名  method()方法为静态的,
	(	Outer.method()	).show();   -->存在返回值
	----->  Inter i = (	Outer.method()	).show();接收返回值
	返回值类型为 Inter 的method方法
interface Inter{
	 void show() ;
 }
class Outer{ 		
	  //补全代码
	  public static Inter method(){
            return new Inter(){
                @Override
                public void show(){
                    System.out.println("hello world");
                }
            };  	
	  }
}
//测试类
public class Test {
	public static void main(String[] args) {
		Outer.method().show() ;
	}
}
	
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值