多态
多态概述
某一个事物,在不同时刻表现出来的不同状态。
举例:
猫可以是猫的类型。
猫 m = new 猫();
同时猫也是动物的一种,也可以把猫称为动物。
动物 d = new 猫();
父 f = new 子();
多态前提和体现
有继承关系
有方法重写
有父类引用指向子类对象
多态中的成员访问特点:
A:成员变量
编译看左边(父类有没有这个变量)
运行看左边(父类的这个变量是多少)
(变量没有重写,而方法有重写)
B:构造方法
创建子类对象的时候,访问父类的构造方法
因为new Zi()的时候会调用父类构造。
C:成员方法
编译看左边(父类有没有这个方法)
运行看右边(运行子类重写的这个方法)
(如果子类没有重写这个方法,则执行父类的这个方法(有可能是父类私有))
(变量没有重写,而方法有重写)
D:静态方法
编译看左边,运行看左边。
(静态方法只能被静态方法重写,但是,静态方法算不上重写)
(因为静态跟类相关,算不上重写,所以,访问还是左边的)
由于成员方法存在方法重写,所以它运行看右边。
class Fu {
public int num = 100;
public void show() {
System.out.println("show Fu");
}
public static void function() {
System.out.println("function Fu");
}
}
class Zi extends Fu {
public int num = 1000;
public int num2 = 200;
public void show() {
System.out.println("show Zi");
}
public void method() {
System.out.println("method zi");
}
public static void function() {
System.out.println("function Zi");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
//多态
Fu f = new Zi();
System.out.println(f.num);
//找不到符号。父类没有num2
//System.out.println(f.num2);//能向上访问,不能向下
f.show();//Zi
//找不到符号
//f.method();
f.function();//Fu !!!!!!!
}
}
多态的好处:
A:提高了代码的维护性(继承保证)
B:提高了代码的扩展性(由多态保证)
class Animal {
public void eat(){
System.out.println("eat");
}
public void sleep(){
System.out.println("sleep");
}
}
class Dog extends Animal {
public void eat(){
System.out.println("狗吃肉");
}
public void sleep(){
System.out.println("狗站着睡觉");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
public void sleep() {
System.out.println("猫趴着睡觉");
}
}
class Pig extends Animal {
public void eat() {
System.out.println("猪吃白菜");
}
public void sleep() {
System.out.println("猪侧着睡");
}
}
//针对动物操作的工具类
class AnimalTool {
//使用时不能创建对象,只能用类名调用
private AnimalTool(){}
/*
//调用猫的功能
public static void useCat(Cat c) {
c.eat();
c.sleep();
}
//调用狗的功能
public static void useDog(Dog d) {
d.eat();
d.sleep();
}
//调用猪的功能
public static void usePig(Pig p) {
p.eat();
p.sleep();
}
*/
public static void useAnimal(Animal a) {//Animal a = new Cat();
a.eat();
a.sleep();
}
}
class DuoTaiDemo {
public static void main(String[] args) {
//我喜欢猫,就养了一只
Cat c = new Cat();
c.eat();
c.sleep();
//我很喜欢猫,所以,又养了一只
Cat c2 = new Cat();
c2.eat();
c2.sleep();
//我特别喜欢猫,又养了一只
Cat c3 = new Cat();
c3.eat();
c3.sleep();
//...
System.out.println("--------------");
//问题来了,我养了很多只猫,每次创建对象是可以接受的
//但是呢?调用方法,你不觉得很相似吗?仅仅是对象名不一样。
//我们准备用方法改进
//调用方式改进版本
//useCat(c);
//useCat(c2);
//useCat(c3);
//AnimalTool.useCat(c);
//AnimalTool.useCat(c2);
//AnimalTool.useCat(c3);
AnimalTool.useAnimal(c);//多态
AnimalTool.useAnimal(c2);//传的是Cat对象,用Animal接收
AnimalTool.useAnimal(c3);//Animal a = new Cat();
System.out.println("--------------");
//我喜欢狗
Dog d = new Dog();
Dog d2 = new Dog();
Dog d3 = new Dog();
//AnimalTool.useDog(d);
//AnimalTool.useDog(d2);
//AnimalTool.useDog(d3);
AnimalTool.useAnimal(d);
AnimalTool.useAnimal(d2);
AnimalTool.useAnimal(d3);
System.out.println("--------------");
//我喜欢宠物猪
//定义一个猪类,它要继承自动物,提供两个方法,并且还得在工具类中添加该类方法调用
//这样就修改了工具类,这样不好,改为最终版
Pig p = new Pig();
Pig p2 = new Pig();
Pig p3 = new Pig();
//AnimalTool.usePig(p);
//AnimalTool.usePig(p2);
//AnimalTool.usePig(p3);
AnimalTool.useAnimal(p);
AnimalTool.useAnimal(p2);
AnimalTool.useAnimal(p3);
System.out.println("--------------");
//我喜欢宠物狼,老虎,豹子...
//定义对应的类,继承自动物,提供对应的方法重写,并在工具类添加方法调用
//前面几个必须写,我是没有意见的
//但是,工具类每次都改,麻烦不!!!
//我就想,你能不能不改了
//改用最终的解决方案。
}
/*
//调用猫的功能
public static void useCat(Cat c) {
c.eat();
c.sleep();
}
//调用狗的功能
public static void useDog(Dog d) {
d.eat();
d.sleep();
}
*/
}
多态的弊端
不能使用子类的特有功能。
class Fu {
public void show() {
System.out.println("show fu");
}
}
class Zi extends Fu {
public void show() {
System.out.println("show zi");
}
public void method() {
System.out.println("method zi");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
Fu f = new Zi();
f.show();
//访问不到,不能使用子类特有方法
//f.method();
}
}
如何解决?下一节
多态中的转型问题
多态的弊端:
不能使用子类的特有功能。
我就想使用子类的特有功能?怎么用呢?
A:创建子类对象调用方法即可。(可以,但是很多时候不合理。而且,太占内存了)
B:把父类的引用强制转换为子类的引用。(向下转型)
对象间的转型问题:
向上转型:
Fu f = new Zi();//外面看到的是Fu,所以是向上转型
向下转型:
Zi z = (Zi)f; //要求该f必须是能够转换为Zi的(f是Zi类的父类的对象)。
//外面看到的是Zi,所以是向下转型
class Fu {
public void show() {
System.out.println("show fu");
}
}
class Zi extends Fu {
public void show() {
System.out.println("show zi");
}
public void method() {
System.out.println("method zi");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
Fu f = new Zi();
f.show();
//f.method();//编译错误。访问不到
//创建子类对象
//Zi z = new Zi();//占内存
//z.show();
//z.method();
//你能够把子的对象赋值给父亲,那么我能不能把父的引用赋值给子的引用呢?
//如果可以,代码如下
Zi z = (Zi)f;//不用生成新对象
z.show();
z.method();
}
}
多态成员访问及转型的理解
class 孔子爹 {
public int age = 40;
public void teach() {
System.out.println("讲解JavaSE");
}
}
class 孔子 extends 孔子爹 {
public int age = 20;
public void teach() {
System.out.println("讲解论语");
}
public void playGame() {
System.out.println("英雄联盟");
}
}
//Java培训特别火,很多人来请孔子爹去讲课,这一天孔子爹被请走了
//但是还有人来请,就剩孔子在家,价格还挺高。孔子一想,我是不是可以考虑去呢?
//然后就穿上爹的衣服,带上爹的眼睛,粘上爹的胡子。就开始装爹
//向上转型
孔子爹 k爹 = new 孔子();
//到人家那里去了
System.out.println(k爹.age); //40
k爹.teach(); //讲解论语
//k爹.playGame(); //这是儿子才能做的
//讲完了,下班回家了
//脱下爹的装备,换上自己的装备
//向下转型
孔子 k = (孔子) k爹;
System.out.println(k.age); //20
k.teach(); //讲解论语
k.playGame(); //英雄联盟
多态继承中的内存图解
【0901多态继承中的内存图解】
【0902多态中的对象变化内存图解】
类型转换异常
ClassCastException:类型转换异常 -
/*
ClassCastException:类型转换异常
一般在多态的向下转型中容易出现
*/
class Animal {
public void eat(){}
}
class Dog extends Animal {
public void eat() {}
public void lookDoor() {
}
}
class Cat extends Animal {
public void eat() {
}
public void playGame() {
}
}
class DuoTaiDemo {
public static void main(String[] args) {
//内存中的是狗
Animal a = new Dog();
Dog d = (Dog)a;
//内存中是猫
a = new Cat();
Cat c = (Cat)a;
//a即可以是Dog,也可以是Cat
//同一个对象不同的形态,多态
//内存中是猫
//Dog dd = (Dog)a; //ClassCastException类型转换异常
}
}
多态练习
1.猫狗案例练习多态版
class Animal {
public void eat(){
System.out.println("吃饭");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("狗吃肉");
}
public void lookDoor() {
System.out.println("狗看门");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
public void playGame() {
System.out.println("猫捉迷藏");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
//定义为狗
Animal a = new Dog();//向上转型
a.eat();
System.out.println("--------------");
//还原成狗,向下转型
Dog d = (Dog)a;
d.eat();
d.lookDoor();
System.out.println("--------------");
//变成猫
a = new Cat();//向上转型
a.eat();
System.out.println("--------------");
//还原成猫,向下转型
Cat c = (Cat)a;
c.eat();
c.playGame();
System.out.println("--------------");
//演示错误的内容
//Dog dd = new Animal();
//Dog ddd = new Cat();
//编译通过,运行异常ClassCastException
//Dog dd = (Dog)a;
}
}
"
狗吃肉
狗吃肉
狗看门
猫吃鱼
猫吃鱼
猫捉迷藏
"
2.不同地方饮食文化不同的案例
class Person {
public void eat() {
System.out.println("吃饭");
}
}
class SouthPerson extends Person {
public void eat() {
System.out.println("吃米饭");
}
public void jingShang() {
System.out.println("经商");
}
}
class NorthPerson extends Person {
public void eat() {
System.out.println("吃馒头");
}
public void yanJiu() {
System.out.println("研究");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
Person p = new SouthPerson();
p.eat();
System.out.println("-------------");
SouthPerson sp = (SouthPerson)p;//向下转型
sp.eat();
sp.jingShang();
System.out.println("-------------");
p = new NorthPerson();//p仍是Person()类的引用
p.eat();
System.out.println("-------------");
NorthPerson np = (NorthPerson)p;//向下转型
np.eat();
np.yanJiu();
}
}
"
吃米饭
吃米饭
经商
吃馒头
吃馒头
研究
"
一个特殊的例子(易错高能!!)
class Demo{
private void m(){//注意是私有
System.out.println("Demo的m()方法被调用");
}
public static void main(String[] args){
Demo d = new B();
//子类没有重写该方法,则调用父类方法,虽然是私有。
d.m();//Demo的m()方法被调用
}
}
class B extends Demo{//重写的前提是能继承过来,再谈是否重写
public void m(){//父类中该方法私有,此时不是重写
System.out.println("B的m()方法被调用");
}
}
看程序写结果
/*
看程序写结果:先判断有没有问题,如果没有,写出结果
*/
class Fu {
public void show() {
System.out.println("fu show");
}
}
class Zi extends Fu {
public void show() {
System.out.println("zi show");
}
public void method() {
System.out.println("zi method");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
Fu f = new Zi();
//编译看左边。找不到符号
//f.method();
f.show();
}
}
2.易错!!
多态的成员访问特点:
方法:编译看左边,运行看右边。
继承的时候:
子类中有和父类中一样的方法,叫重写。(若父类该方法私有,则不是重写)
子类中没有父亲中出现过的方法,这个方法就被继承过来了。
class A {
public void show() {
show2();
}
public void show2() {
System.out.println("A");
}
}
class B extends A {
/*继承自父类,可以搬运过来。
public void show() {
show2();
}
*/
public void show2() {
System.out.println("B");
}
}
class C extends B {
public void show() {
super.show(); //只是个搬运工,搬运show2();
}
public void show2() {//B,C里面都有show2()
System.out.println("C");//执行的是C里面的
}
}
public class DuoTaiDemo {
public static void main(String[] args) {
A a = new B();
a.show();//B!!!!!!
B b = new C();
b.show();//C!!!!!!
}
}
抽象类
概述
回想前面我们的猫狗案例,提取出了一个动物类。并且我们在前面也创建过了动物对象,其实这是不对的。
为什么呢?因为动物本身并不是一个具体的事物,而是一个抽象的事物。只有真正的猫,狗才是具体的动物。同理,我们也可以推想,不同的动物吃的东西应该是不一样的,所以,我们不应该在动物类中给出具体体现,而是应该给出一个声明即可。
在Java中,一个没有方法体的方法应该定义为抽象方法.
而类中如果有抽象方法,该类必须定义为抽象类。
抽象类的特点:
A:抽象类和抽象方法必须用abstract关键字修饰
B:抽象类中不一定有抽象方法,但是有抽象方法的类必须定义为抽象类。
抽象类可以有非抽象方法。
C:抽象类不能实例化
因为它不是具体的。
但抽象类有构造方法,但是不能实例化?构造方法的作用是什么呢?
用于子类访问父类数据的初始化
D:抽象类的子类
a:如果不想重写这个抽象类的抽象方法,则该子类必须是一个抽象类。
b:若子类重写了所有的抽象方法,这个时候子类是一个具体的类(可以实例化)。(常用)
抽象类的实例化其实是靠具体的子类实现的。是多态的方式。多态经常用于抽象类。
Animal a = new Cat();
//abstract class Animal //抽象类的声明格式
abstract class Animal {
//抽象方法
//public abstract void eat(){} //空方法体,这个会报错。抽象方法不能有主体
public abstract void eat();//不能加大括号!!!!
public void sleep(){
System.out.println("动物睡觉");
}
public Animal(){}
}
//子类是抽象类
abstract class Dog extends Animal {}
//子类是具体类时,必须重写实现所有抽象方法
//多态中,子类必须重写父类方法
//多态经常在抽象类中使用
class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}
class AbstractDemo {
public static void main(String[] args) {
//创建对象
//Animal是抽象的; 无法实例化
//Animal a = new Animal();
//通过多态的方式
Animal a = new Cat();
a.eat();
a.sleep();
}
}
抽象类的成员特点:
成员变量:既可以是变量,也可以是常量。
构造方法:有。
用于子类访问父类数据的初始化。
成员方法:既可以是抽象的,也可以是非抽象的。
抽象类的成员方法特性:
A:抽象方法 强制要求子类实现的事情。
B:非抽象方法 子类继承的事情,提高代码复用性。
abstract class Animal {
public int num = 10;
public final int num2 = 20;
public Animal() {}
public Animal(String name,int age){}
public abstract void show();
public void method() {
System.out.println("method");
}
}
class Dog extends Animal {
public void show() {//重写父类的所有抽象方法
System.out.println("show Dog");
}
}
class AbstractDemo {
public static void main(String[] args) {
//创建对象
Animal a = new Dog();
a.num = 100;//变量,编译看左边,运行看左边。
System.out.println(a.num);
//a.num2 = 200;//加了final是常量
System.out.println(a.num2);
System.out.println("--------------");
a.show();//方法,编译看左边,运行看右边
a.method();
}
}
抽象类练习
1.猫狗类
/*
分析:由具体到抽象,eat()方法子类各不相同,因此要抽象出来
*/
abstract class Animal{
private String name;
private int age;
public Animal(){//构造方法没有返回类型
}
public Animal(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();//抽象类没有方法体,什么都不做
}
class Dog extends Animal{//继承父类成员
public Dog(){
}
public Dog(String name,int age){
super(name,age);//调用父类构造!
}
public void eat(){//复写父类全部抽象方法
System.out.println("Dog eat");
}
public void watchDoor(){//子类新方法
System.out.println("Dog watch door");
}
}
class Cat extends Dog{
public Cat(){
}
public Cat(String name,int age){
super(name,age);//调用父类构造
}
public void eat(){//复写父类全部抽象方法
System.out.println("Cat eat");
}
public void sleep(){//子类新方法
System.out.println("Cat sleep");
}
}
class AbstractDemo{
public static void main(String[] args){
//此处也可用 Dog d = new dog(); 的方式创建使用
//下面用多态的方式
Animal a = new Dog("小白",10);
System.out.println(a.getName()+"\t"+a.getAge());
a.eat();
Dog d = (Dog)a;
d.watchDoor();//调用子类新方法
a = new Cat();//这样用也可,不用再Animal a
a.setName("小花");
a.setAge(20);
System.out.println(a.getName()+"\t"+a.getAge());
a.eat();
Cat c = (Cat)a;
c.sleep();//调用子类新方法
//a即可以是Dog,也可以是Cat,有扩展性
//同一个对象不同的形态,多态
}
}
抽象类的小问题
一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
A:可以。
B:不让创建对象。只能通过子类访问
abstract不能和哪些关键字共存?
private 冲突(私有不能被继承,抽象要求被继承)
final 冲突(final不能被重写,抽象要求被重写)
static 报错无意义(静态内容可以通过类名调用,但抽象方法没有方法体,调用没意思)
接口
接口概述
回到我猫狗案例,狗一般就是看门,猫一般就是作为宠物。
但是驯养员或者是驯兽师可以训练出:猫钻火圈,狗跳高,狗做计算等。
而这些额外的动作,并不是所有猫或者狗一开始就具备的,这是经过训练出来的。
所以这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中。
因为只有部分猫狗具备这些功能。
为了体现事物功能的扩展性,Java提供了接口来定义这些额外功能,并不给出具体实现(抽象方法)。
将来哪些猫狗需要被培训,只需要这部分猫狗把这些额外功能实现即可
为克服java单继承的缺点,使用了接口,一个类可以实为了现多个接口。
接口的特点:
A:接口用关键字interface表示
interface 接口名 {}
B:类实现接口用implements表示
class 类名+Impl implements 接口名 {}
C:接口是抽象的,不能实例化
那么,接口如何实例化呢?
按照多态的方式来实例化。
D:接口的子类(要么是抽象类,要么重写接口的方法)
a:可以是抽象类。但是意义不大(因为还需子类去实现)。
b:可以是具体类。要重写接口中的所有抽象方法。(推荐方案)
由此可见:
A:具体类多态(几乎没有)
B:抽象类多态(常用)
C:接口多态(最常用)
注:接口名+Impl这种格式是接口的实现类格式
//定义动物培训接口
interface AnimalTrain {
public abstract void jump();
}
//抽象类实现接口,但是意义不大因为还是需要子类实现
abstract class Dog implements AnimalTrain {
}
//具体类实现接口,要重写接口所有抽象方法。推荐方案
class Cat implements AnimalTrain {
public void jump() {
System.out.println("猫可以跳高了");
}
}
class InterfaceDemo {
public static void main(String[] args) {
//AnimalTrain是抽象的; 无法实例化
//AnimalTrain at = new AnimalTrain();
//at.jump();
AnimalTrain at = new Cat();//多态实现
at.jump();
}
}
接口成员特点
接口成员特点
成员变量;只能是常量,并且是静态的。
默认修饰符:public static final(写什么都是这样)
建议:自己手动给出。
构造方法:接口没有构造方法。因为接口用于功能扩展,而没有具体存在
那它的实现类初始化怎么调用接口类构造方法呢?
每个类都默认继承 Object 类,调用的是Object()。(该类只有无参构造)
成员方法:只能是抽象方法。
默认修饰符:public abstract(写什么都是这样)
建议:自己手动给出。
所有的类都默认继承自一个类:Object。
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。
属于lang包,不用导包
//测试代码
interface Inter {
public int num = 10;//系统会加上final
public final int num2 = 20;//系统会自动加上static
public static final int num3 = 30;//建议写法
//错误: 需要<标识符>
//public Inter() {}//接口没有构造方法
//接口方法不能带有主体,说明是抽象方法。
//public void show() {}
//因为实现类中 void show()方法(protected)不能实现该方法
//说明该方法为 public
//abstract void show(); //默认public
public void show(); //没加{}不报错,说明默认abstract
}
//接口名+Impl这种格式是接口的实现类格式
/*
class InterImpl implements Inter {//默认继承Object类
public InterImpl() {
super();
}
}
*/
class InterImpl extends Object implements Inter {
public InterImpl() {
super();//Object类只有无参构造
}
//正在尝试分配更低的访问权限; 以前为public
//说明接口类的 show() 为 public
//void show() {} //默认protected
public void show() {}
}
//测试类
class InterfaceDemo {
public static void main(String[] args) {
//创建对象
Inter i = new InterImpl();
System.out.println(i.num);
System.out.println(i.num2);
//i.num = 100;//无法为最终变量num分配值,说明num是final
//i.num2 = 200;//无法为最终变量num2分配值
//System.out.println(i.num);
//System.out.println(i.num2);
System.out.println(Inter.num);//说明num也是静态
System.out.println(Inter.num2);//静态成员可以通过类名调用
System.out.println("--------------");
}
}
类与类,类与接口以及接口与接口的关系
类与类:
继承关系,只能单继承,可以多层继承。
类与接口:
实现关系,可以单实现,也可以多实现。
并且还可以在继承一个类的同时实现多个接口。
接口与接口:
继承关系,可以单继承,也可以多继承。
interface Father {
public abstract void show();
}
interface Mother {
public abstract void show2();
}
interface Sister extends Father,Mother {//接口间的多继承关系
}
//class Son implements Father,Mother //多实现
class Son extends Object implements Father,Mother {
public void show() {//重写接口抽象方法
System.out.println("show son");
}
public void show2() {//重写接口抽象方法
System.out.println("show2 son");
}
}
class InterfaceDemo {
public static void main(String[] args) {
//创建对象
Father f = new Son();
f.show();
//f.show2(); //报错
Mother m = new Son();
//m.show(); //报错
m.show2();
}
}
抽象类和接口的关系
抽象类和接口的区别
A:成员区别
抽象类:
成员变量:可以变量,也可以常量
构造方法:有
成员方法:可以抽象,也可以非抽象
接口:
成员变量:只可以常量
构造方法:无
成员方法:只可以抽象
B:关系区别
类与类
继承,单继承
类与接口
实现,单实现,多实现
接口与接口
继承,单继承,多继承
C:设计理念区别
抽象类 被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能。
接口 被实现体现的是:”like a”的关系。接口中定义的是该继承体系的扩展功能。
猫狗案例加入跳高功能
猫狗案例,加入跳高的额外功能
分析:从具体到抽象
猫:
姓名,年龄
吃饭,睡觉
狗:
姓名,年龄
吃饭,睡觉
由于有共性功能,所以,我们抽取出一个父类:
抽象类动物:
姓名,年龄
吃饭();
睡觉(){}
猫类:继承自动物类
狗类:继承自动物类
跳高的额外功能是一个新的扩展功能,所以我们要定义一个接口
接口:
跳高
会跳高的猫类:实现跳高
会跳高的狗类:实现跳高
实现;
从抽象到具体
使用:
使用具体类
interface Jump{//跳高接口
public abstract void jump();
}
abstract class Animal{
private String name;
private int age;
public Animal(){}
public Animal(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public void sleep(){
System.out.println("动物睡觉");
}
public abstract void eat();
}
class Dog extends Animal{
public Dog(){}
public Dog(String name,int age){
super(name,age);
}
public void eat(){
System.out.println("狗吃骨头");
}
}
class Cat extends Animal{
public Cat(){}
public Cat(String name,int age){
super(name,age);//不要漏了
}
public void eat(){
System.out.println("猫吃鱼");
}
}
class JumpDog extends Dog implements Jump{
public JumpDog(){}
public JumpDog(String name,int age){
super(name,age);
}
public void jump(){
System.out.println("狗跳高");
}
}
class JumpCat extends Cat implements Jump{
public JumpCat(){}
public JumpCat(String name,int age){
super(name,age);
}
public void jump(){
System.out.println("猫跳高");
}
}
class InterfaceDemo{
public static void main(String[] args){
Animal a = new JumpDog("小白",10);//多态方式
System.out.println(a.getName()+a.getAge()+"岁");
JumpDog jd = (JumpDog)a;
jd.jump();
JumpCat jc = new JumpCat("加菲",20);//具体类方式
System.out.println(jc.getName()+jc.getAge()+"岁");
jc.jump();
}
}
老师学生案例加入抽烟功能
老师和学生案例,加入抽烟的额外功能
分析:从具体到抽象
老师:姓名,年龄,吃饭,睡觉
学生:姓名,年龄,吃饭,睡觉
由于有共性功能,我们提取出一个父类,人类。
人类:
姓名,年龄
吃饭();
睡觉(){}
抽烟的额外功能不是人或者老师,或者学生一开始就应该具备的.
所以,我们把它定义为接口
抽烟接口。
会抽烟老师:实现抽烟接口
会抽烟学生:实现抽烟接口
实现:从抽象到具体
使用:使用具体类
interface Smoke{
public abstract void smoke();
}
abstract class Person{
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public void sleep(){
System.out.println("人睡觉");
}
abstract void eat();
}
class Student extends Person{
public Student(){}
public Student(String name,int age){
super(name,age);
}
public void eat(){
System.out.println("学生吃肉");
}
}
class Teacher extends Person{
public Teacher(){}
public Teacher(String name,int age){
super(name,age);
}
public void eat(){
System.out.println("老师吃菜");
}
}
class SmokeStudent extends Student implements Smoke{
public SmokeStudent(){}
public SmokeStudent(String name,int age){
super(name,age);
}
public void smoke(){
System.out.println("学生抽烟");
}
}
class SmokeTeacher extends Teacher implements Smoke{
public SmokeTeacher(){}
public SmokeTeacher(String name,int age){
super(name,age);
}
public void smoke(){
System.out.println("老师抽烟");
}
}
class InterfaceDemo{
public static void main(String[] args) {
SmokeStudent ss = new SmokeStudent("小白",24);
System.out.println(ss.getName()+ss.getAge()+"岁");
ss.smoke();
ss.sleep();
Person p = new SmokeTeacher("老王",50);
System.out.println(p.getName()+p.getAge()+"岁");
SmokeTeacher st = (SmokeTeacher)p;
st.smoke();
p.sleep();
}
}
引用类型做形式参数
形式参数:
基本类型
引用类型
类名:(匿名对象的时候其实我们已经讲过了)需要的是该类的对象
抽象类:参数需要的是该抽象类的子类对象(抽象类本身不能实例化)
接口:参数需要的是该接口的实现类对象
//参数为类名
class Student {
public void study() {
System.out.println("Good Good Study,Day Day Up");
}
}
class StudentDemo {
public void method(Student s) { //ss; ss = new Student(); Student s = new Student();
s.study();
}
}
class StudentTest {
public static void main(String[] args) {
//需求:我要测试Student类的study()方法
Student s = new Student();
s.study();
System.out.println("----------------");
//需求2:我要测试StudentDemo类中的method()方法
StudentDemo sd = new StudentDemo();
Student ss = new Student();
sd.method(ss);
System.out.println("----------------");
//匿名对象用法
new StudentDemo().method(new Student());
}
}
//参数为抽象类
abstract class Person {
public abstract void study();
}
class PersonDemo {
public void method(Person p) {//p; p = new Student(); Person p = new Student(); //多态
p.study();
}
}
//定义一个具体的学生类
class Student extends Person {
public void study() {
System.out.println("Good Good Study,Day Day Up");
}
}
class PersonTest {
public static void main(String[] args) {
//目前是没有办法的使用的
//因为抽象类没有对应的具体类
//那么,我们就应该先定义一个具体类
//需求:我要使用PersonDemo类中的method()方法
PersonDemo pd = new PersonDemo();
Person p = new Student();//多态,抽象类间接实例化
pd.method(p);
}
}
//参数为接口
//定义一个爱好的接口
interface Love {
public abstract void love();
}
class LoveDemo {
public void method(Love l) { //l; l = new Teacher(); Love l = new Teacher(); 多态
l.love();
}
}
//定义具体类实现接口
class Teacher implements Love {
public void love() {
System.out.println("老师爱学生,爱Java,爱林青霞");
}
}
class TeacherTest {
public static void main(String[] args) {
//需求:我要测试LoveDemo类中的love()方法
LoveDemo ld = new LoveDemo();
Love l = new Teacher();
ld.method(l);
}
}
引用类型做返回值类型
返回值类型
基本类型
引用类型:
类:返回的是该类的对象
抽象类:返回的是该抽象类的子类对象
接口:返回的是该接口的实现类的对象
//类名做接口类型
class Student {
public void study() {
System.out.println("Good Good Study,Day Day Up");
}
}
class StudentDemo {
public Student getStudent() {
//Student s = new Student();
//return s;
return new Student();
}
}
class StudentTest2 {
public static void main(String[] args) {
//需求:我要使用Student类中的study()方法
//但是,这一次我的要求是,不要直接创建Student的对象
//让你使用StudentDemo帮你创建对象
StudentDemo sd = new StudentDemo();
Student s = sd.getStudent(); //new Student(); Student s = new Student();
s.study();
}
}
//抽象类名做返回类型
abstract class Person {
public abstract void study();
}
class PersonDemo {
public Person getPerson() {
//Person p = new Student();
//return p;
return new Student();//Person类是抽象类,不能直接用
}
}
class Student extends Person {
public void study() {
System.out.println("Good Good Study,Day Day Up");
}
}
class PersonTest2 {
public static void main(String[] args) {
//需求:我要测试Person类中的study()方法
PersonDemo pd = new PersonDemo();
Person p = pd.getPerson(); //new Student(); Person p = new Student(); 多态
p.study();
}
}
//接口名做返回值类型
//定义一个爱好的接口
interface Love {
public abstract void love();
}
class LoveDemo {
public Love getLove() {
//Love l = new Teacher();
//return l;
return new Teacher();
}
}
//定义具体类实现接口
class Teacher implements Love {
public void love() {
System.out.println("老师爱学生,爱Java,爱林青霞");
}
}
class TeacherTest2 {
public static void main(String[] args) {
//如何测试呢?
LoveDemo ld = new LoveDemo();
Love l = ld.getLove(); //new Teacher(); Love l = new Teacher(); 多态
l.love();
}
}
链式编程
/*
链式编程。
每次调用完毕方法后,返回的是一个对象。
*/
class Student {
public void study() {
System.out.println("Good Good Study,Day Day Up");
}
}
class StudentDemo {
public Student getStudent() {
return new Student();
}
}
class StudentTest3 {
public static void main(String[] args) {
//如何调用的呢?
StudentDemo sd = new StudentDemo();
//Student s = sd.getStudent();
//s.study();
//大家注意了
sd.getStudent().study();
}
}
包
package关键字
包:
A:其实就是文件夹
B:作用
a:把相同的类名放到不同的包中
b:对类进行分类管理
举例:
学生:增加,删除,修改,查询
老师:增加,删除,修改,查询
...
方案1:按照功能分
cn.itcast.add
AddStudent
AddTeacher
cn.itcast.delete
DeleteStudent
DeleteTeacher
cn.itcast.update
UpdateStudent
UpdateTeacher
cn.itcast.find
FindStudent
FindTeacher
方案2:按照模块分
cn.itcast.teacher
AddTeacher
DeleteTeacher
UpdateTeacher
FindTeacher
cn.itcast.student
AddStudent
DeleteStudent
UpdateStudent
FindStudent
包的定义
package 包名;
多级包用.分开即可
注意事项:
A:package语句必须是程序的第一条可执行的代码
B:package语句在一个java文件中只能有一个
C:如果没有package,默认表示无包名
package cn.itcast;
class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld");
}
}
带包的编译和运行
A:手动式
a:编写一个带包的java文件。
b:通过javac命令编译该java文件。
c:手动创建包名。
d:把b步骤的class文件放到c步骤的最底层包
e:回到和包根目录在同一目录的地方,然后运行
带包运行 java cn.itcast.HelloWorld
B:自动式
a:编写一个带包的java文件。
b:javac编译的时候带上-d即可
javac -d . HelloWorld.java
c:回到和包根目录在同一目录的地方,然后运行
带包运行 java cn.itcast.HelloWorld
不同包下类之间的访问
导包:
格式:import 包名;
这种方式导入是到类的名称。
注意:我们用谁就导谁(类)。
面试题:
package,import,class有没有先后顺序?
有。
package > import > class
Package:只能有一个
import:可以有多个
class:可以有多个,以后建议是一个
//Demo类,求和
package com.liuyi;
public class Demo {
public int sum(int a,int b) {
return a + b;
}
}
//Test类,测试
package cn.itcast;
import com.liuyi.Demo;
class Test {
public static void main(String[] args) {
Demo d = new Demo();
System.out.println(d.sum(10,20));
}
}
/*
第一个问题:程序包com.liuyi不存在。。没有打包
第二个问题:找不到Demo。。没有导包
第三个问题: Demo在com.liuyi中不是公共的; 无法从外部程序包中对其进行访问
*/
权限修饰符
private 同一个类中
默认 同一个包中(子类或其他类)
protected 同一类,同一包,不同包子类
public 同一类,同一包,不同包子类,不同包其他类
修饰符:
权限修饰符:private,默认的,protected,public
状态修饰符:static,final
抽象修饰符:abstract
类:
权限修饰符:默认修饰符,public
状态修饰符:final
抽象修饰符:abstract
用的最多的就是:public
成员变量:
权限修饰符:private,默认的,protected,public
状态修饰符:static,final
用的最多的就是:private
构造方法:
权限修饰符:private,默认的,protected,public
用的最多的就是:public
成员方法:
权限修饰符:private,默认的,protected,public
状态修饰符:static,final
抽象修饰符:abstract
用的最多的就是:public
除此以外的组合规则:
成员变量:public static final
成员方法:public static
public abstract
public final
内部类
内部类概述:
把类定义在其他类的内部,这个类就被称为内部类。
举例:在类A中定义了一个类B,类B就是内部类。
内部的访问特点:
A:内部类可以直接访问外部类的成员,包括私有。
B:外部类要访问内部类的成员,必须创建对象。
内部类位置:
成员位置:在成员位置定义的类,被称为成员内部类。
局部位置:在局部位置定义的类,被称为局部内部类。
成员内部类:
如何直接访问内部类的成员。
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
成员内部类
//内部类的直接使用
class Outer{
private int num = 10;
//成员内部类
class Inner{
public void show(){
//成员内部类访问外部类私有成员
System.out.println(num);
}
}
}
class Test{
public static void main(String[] args){
//创建对象的格式
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
修饰成员内部类的修饰符:
private 为了保证数据的安全性
static 为了方便访问数据
注意:静态内部类访问的外部类数据必须用静态修饰。
//用private修饰成员内部类后,就不能被外部访问,
//需要外部类提供一个公有的访问方法。
class Body {
private class Heart {
public void operator() {
System.out.println("心脏搭桥");
}
}
public void method() {
if(如果你是外科医生) {// 加入校验,保证安全
Heart h = new Heart();
h.operator();
}
}
}
Body b = new Body();b.method();
内部类在用static修饰之后,创建对象的格式与之前不同。
class Outer {
private int num = 10;
private static int num2 = 100;
//外部类不可以用static修饰
//内部类可以用静态修饰,是因为内部类可以看成是外部类的成员
//静态修饰的内部类,其方法可以是静态或者非静态。只能访问外部类静态变量
public static class Inner {
public void show() {
//System.out.println(num);
System.out.println(num2);
}
public static void show2() {
//System.out.println(num);
System.out.println(num2);
}
}
}
class Test{
public static void main(String[] args){
//静态内部类的使用格式与普通内部类不同
Outer.Inner oi = new Outer.Inner();// 注意格式
oi.show();
//静态成员通过类名调用
Outer.Inner.show2();
}
}
内部类访问外部类成员的方法
注意:
内部类和外部类没有继承关系
通过外部类名限定this对象
Out.this.xxx
class Outer{
public int num = 30;
public class Inner{
public int num = 20;
public void show(){
int num = 10;
System.out.println(num);//10
System.out.println(this.num);//20
System.out.println(new Outer().num);//30
System.out.println(Outer.this.num);//30,用这种
}
}
}
class Test{
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
局部内部类
局部内部类
A:可以直接访问外部类的成员
B:在局部位置(局部函数内),可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能
面试题:
局部内部类访问局部变量的注意事项?
A:局部内部类访问的部变量必须用final修饰(JDK8中可以不显示指明final,但仍是final类型)
即使没有局部类去访问,还是final类型。
B:为什么呢?
局部变量是随着方法的调用而调用,随着调用完毕而消失。
而堆内存的对象内容并不会立即消失(等待回收)。
加入final修饰后,这个变量就成了常量。既然是常量。你消失了。
我在内存中存储的是数据20,所以,我还是有数据在使用。
class Outer{
private int num = 10;
public void method(){
int num2 = 20;//JDK8默认为final
int num3 = 30;//JDK7之前,需要显式指明final
class Inner{
public void show(){
//局部内部类可以直接访问外部类成员
System.out.println(num);
//num2 = 20;不能修改final类型值
//num3 = 30;不能修改final类型值
System.out.println(num2);
}
}
//在局部位置,可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能
Inner i = new Inner();
i.show();
}
}
class Test{
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
匿名内部类
匿名内部类
就是内部类的简化写法。
前提:存在一个类或者接口
这里的类可以是具体类也可以是抽象类。
格式:
new 类名或者接口名(){
重写方法;
}
本质是什么呢?
是一个继承了该类或者实现了该接口的子类匿名对象。
因为抽象类和接口不能new对象,必须实现他们才可以。
interface Inter {
public abstract void show();//需要被重写
public abstract void show2();
}
class Outer {
public void method() {
//一个方法的时候
/*
new Inter() {
public void show() {
System.out.println("show");
}
}.show();
*/
//二个方法的时候
/*
new Inter() {
public void show() {
System.out.println("show");
}
public void show2() {
System.out.println("show2");
}
}.show();
new Inter() {
public void show() {
System.out.println("show");
}
public void show2() {
System.out.println("show2");
}
}.show2();
*/
//如果我是很多个方法,就很麻烦了
//那么,我们有没有改进的方案呢?
Inter i = new Inter() { //接口多态
public void show() {
System.out.println("show");
}
public void show2() {
System.out.println("show2");
}
};
//为什么是多态呢?
//new后面,其实是接口Inter的实现类对象
//然后把它赋给了接口,即为多态
i.show();//编译看左边,运行看右边
i.show2();
}
}
class InnerClassDemo6 {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
匿名内部类在开发中的使用
interface Person{
public abstract void study();
public abstract void say();
}
class PersonDemo{
//接口作形参
//这里需要的不是接口,而是接口的实现类的对象
public void method(Person p){
p.study();
p.say();
}
}
//实现类
class student implements Person{
public void study(){
System.out.println("study hard.");
}
public void say(){
System.out.println("说");
}
}
class Test{
public static void main(String[] args) {
PersonDemo pd = new PersonDemo();
Person p = new student();
pd.method(p);
//匿名内部类在开发中的使用
//匿名内部类的本质是继承类或者实现了接口的子类匿名对象
//这种就不用写实现类了
pd.method(new Person(){
public void say(){
System.out.println("说");
}
public void study(){
System.out.println("好好学习");
}
});
}
}
面试题
匿名内部类面试题:
按照要求,补齐代码
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台输出”HelloWorld”
interface Inter{
void show();//public abstract
//子类重写父类,访问权限不能更低
//接口可以省略public
//普通类默认为protected
}
class Outer{
public static Inter method(){
return new Inter(){
public void show(){
System.out.println("Hello");
}
};
}
}
class Test{
public static void main(String[] args) {
Outer.method().show();
//Outer.method()可以看出method()是Outer中的静态方法
//Outer.method()还能调方法,说明method()方法返回值是一个对象
//接口Inter中有一个show方法,所以返回值类型为Inter
}
}