Chapter04面向对象【下】

多态

多态概述

某一个事物,在不同时刻表现出来的不同状态。

举例:
猫可以是猫的类型。
猫 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
    }
}

你可能感兴趣的:(Chapter04面向对象【下】)