Java从入门到放弃09---多态/向上转型/向下转型/多态内存图/抽象类/关键字abstract不能和哪些关键字共存/接口/类与类,类与接口,接口与接口的关系/抽象类与接口的区别

Java从入门到放弃09—多态/向上转型/向下转型/多态内存图/抽象类/关键字abstract不能和哪些关键字共存/接口/类与类,类与接口,接口与接口的关系/抽象类与接口的区别

01 多态

  • 多态指的是某个事物,在某个时刻所表现出来的不同状态。

  • 多态的三个必要条件:

    1.要有继承。(存在父类和子类)

    2.要有方法重写,可以没有,但是多态就没有意义了。

    3.父类引用指向子类对象。如Animal为父类,Cat为猫类。父类引用指向子类对象 Animal cat=new Cat();

  • 多态的优势:提高代码的复用性(通过继承来实现),提高代码的扩展性(通过面向父类型传参来实现)。

  • 多态的弊端:父类引用可以访问被子类重写的方法,但不能直接访问子类的特有功能。(可以通过向下转型去访问)

  • 多态其实就是向上转型。子类转为父类


程序示例1:多态形式访问成员变量,编译运行看左边(这里的左边指,new对象的语句中等号的左边)

public class MyTest {
    public static void main(String[] args) {
        //以多态的形式来访问成员变量 使用的还是父类的变量
        //编译看左边,运行也看左边
        Fu f = new Zi();//多态 父类引用指向子类空间
        System.out.println(f.num);//访问的是父类变量中的num
    }
}
class Fu{
    int num = 100;
    public void show(){
        System.out.println("父类的show方法");
    }
}
class Zi extends Fu{
    int num=10;
    @Override
    public void show() {
        System.out.println("子类重写过后的show方法");
    }
}
运行结果:100

程序示例2:多态形式访问成员方法,编译看左边,运行看右边(这里的右边指,new对象的语句中等号的右边)

public class MultiStateTest {
    public static void main(String[] args) {
        Animal cat = new Cat();
        MyUtils.eatTest(cat);
        Animal dog = new Dog();
        MyUtils.eatTest(dog);
        Animal sheep = new Sheep();
        MyUtils.eatTest(sheep);//使用多态传入sheep(属于animal类),先进入父类,再调用子类重写后的方法(运行看右边)
    }
}
class Animal{
    public void eat(){
        System.out.println("吃饭");
    }
}
class Cat extends Animal{
    @Override//检查方法重写 快捷键ctrl+O
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}
class Sheep extends Animal{
    @Override
    public void eat() {
        System.out.println("羊吃草");
    }
}
class MyUtils{
     private MyUtils() {//将工具包私有化,让外部不能使用空参构造创建对象,也不需要
    }
    public static void eatTest(Animal animal){//用static修饰该方法,可以不创建对象,通过类名直接调用调用该方法。
         animal.eat();//根据传入的参数,执行方法重写机制
    }
}
运行结果:猫吃鱼
		狗吃肉
		羊吃草

02 向下转型

  • 父类只能访问子类的重写方法,如果想要父类能够访问子类的特有方法,需要进行向下转型。
  • 向下转型可理解为父类强制转换为子类,语法为 子类名 对象名=(子类名) 父类对象。

程序示例:

public class MultiStateTest01 {
    public static void main(String[] args) {
        Humanbeing s= new Student1();//父类引用指向子类,其实就是向上转型,即多态
        s.eat();//父类可以访问父类的方法,及子类的重写方法
        s.sleep();//父类可以直接访问子类的重写方法
        Student1 s1=(Student1)s;//父类引用指向子类
        s1.learning();//向下转型后,父类才能调用子类的特有方法
        Humanbeing t=new Techer();
        t.eat();//父类可以直接访问子类的重写方法
        t.sleep();//父类可以直接访问子类的重写方法
        ((Techer) t).tech();//省略写法,((Teacher)t)表示向下转型。向下转型后,父类才能调用子类的特有办法
    }
}
class Humanbeing{
    public void sleep(){
        System.out.println("go to sleep");
    }
    public void eat(){
        System.out.println("eat foods");
    }
}
class Techer extends Humanbeing{
    @Override
    public void sleep() {
        System.out.println("sleep with his wife");
    }
    public void tech(){
        System.out.println("teaching");
    }
}
class Student1 extends Humanbeing{
    @Override
    public void eat() {
        System.out.println("eat hot-pot");
    }
    public void learning(){
        System.out.println("study");
    }
}
运行结果:eat hot-pot
		go to sleep
		study
		eat foods
		sleep with his wife
		teaching

  • 向下转型的注意事项:必须先new一个子类,该子类开辟出引用空间后,才能进行强制转换。否则会报错ClassCastException 类型转换异常。

程序示例:

public class MyTest {
    public static void main(String[] args) {
        Cat cat = new Cat();
        Animal an=cat; //多态,父类引用指向子类空间,实质是向上转型
        an.eat();
        Cat c= (Cat) an; //向下转型
        c.eat();//向下转型后,可以调用子类的个性功能
        c.cacheMouse();
        Dog dog = new Dog();
        an=dog;
        Dog d= (Dog) an; //向下转型
        d.lookDoor();
        an.eat();
       // an=new Tiger();
        Tiger t= (Tiger) an;//执行该语句前,由于没有new出tiger的子类空间并将父类引用指向子类空间,因此程序报错 ClassCastException 类型转换异常
        t.goSwimming();
    }
}
class Animal{//父类
    public void eat(){
        System.out.println("吃饭");
    }
}
class Cat extends Animal{
    @Override
    public void eat() {//共性方法重写
        System.out.println("猫吃鱼");
    }
    public void cacheMouse(){//个性方法
        System.out.println("猫抓老鼠");
    }
}
class Dog extends Animal{//子类
    @Override
    public void eat() {//子类重写方法
        System.out.println("狗吃肉");
    }
    public void guardHomer(){//子类个性方法
        System.out.println("狗守家");
    }
}

class Tiger extends Animal{
    @Override
    public void eat() {//子类重写方法
        System.out.println("老虎不吃素");
    }
    public void goSwimming(){//子类个性方法
        System.out.println("老虎去游泳");
    }
}
运行结果:猫吃鱼
		猫吃鱼
		猫抓老鼠
		狗看门
		狗吃骨头
		Exception in thread "main" java.lang.ClassCastException//报错

03 多态内存图

  • 子类在堆内存中开辟的空间内有一块由super标识的空间,父类在这块空间内进行初始化。
  • 在调用方法时,父类引用会依据动态绑定机制,将引用空间指向子类的重写方法。

程序示例:

public class MyTest {
    public static void main(String[] args) {
        Fu fu = new Zi();
        System.out.println(fu.num);
        fu.show();
        Zi zi= (Zi) fu;
        System.out.println(zi.num);
        zi.test();
    }
}
class Fu{
    int num=100;
    public void show(){
        System.out.println("fu show");
    }
}
class Zi extends Fu{
    int num=20;
    @Override
    public void show() {
        System.out.println("zi show");
    }
    public void test(){
        System.out.println("zi test");
    }
}
运行结果:100
		zi show
		20
		zi test

内存图解:


04 多态题目分析

class A {//10.A类虽然有show方法,但是C类对show方法进行了覆盖,因此执行C.show
		public void show() {//3.子类B没有show方法,因此执行A.show
			show2();//4./12.执行show2   
		}
		public void show2() {//5.由于子类B重写了show2,因此不执行该方法 13.子类C重写了show2,因此执行C.show2
			System.out.println("我");
		}
	}
	class B extends A {//9.父类B没有show方法,但是继承的A类有show方法
		public void show2() {//6.执行B.show2,打印爱
			System.out.println("爱");
		}
	}
	class C extends B {
		public void show() {
			super.show();//11.执行父类show方法,即执行父类B继承的父类A的show方法
		}
		public void show2() {//13.执行C.show2,打印你
			System.out.println("你");
		}
	}
	public class DuoTaiTest4 {
		public static void main(String[] args) {
			A a = new B();//1.父类a引用指向子类B
			a.show();//2.调用父类a的show
			B b = new C();//7.父类b引用执行子类C
			b.show();//8.调用父类b的show
		}
	}
运行结果:爱
		你

05 抽象类

  • 场景引申:父类将所有子类的共性功能向上抽取后,并不知道每个子类对共性功能的具体实现,因此没必要在父类中给出共性功能的具体实现,给出声明即可。
  • 抽象类概述:一个没有方法体的方法应该定义为为抽象方法,而类中如果有抽象方法该类必须定义为抽象类。
  • 抽象类和抽象方法必须用abstract关键字修饰
  • 抽象类格式:权限修饰符 abstract class 类名{}
  • 抽象方法格式:权限修饰符 abstract 返回值类型 方法名();

程序示例

public class MyTest {
    public static void main(String[] args) {
        Animal an=new Cat();//抽象类不能直接创建对象,可以采用多态间接的去实例化抽象类
        an.eat();
        an.sleep();
        an.show();
    }
}
abstract class Animal {
    public Animal() {
        System.out.println("父类的构造方法执行了");
    }
    public abstract void eat(); //抽象方法,此方法没有具体方法实现
    public abstract void sleep();
    //抽象类中既可以有抽象方法,也可以非抽象方法
    public void show(){
        System.out.println("这是父类的一个非抽象方法");
    }
}
class Cat extends Animal{
    @Override
    public void eat() {//抽象类的子类必须重写所有抽象方法
        System.out.println("猫爱吃鱼");
    }
    @Override
    public void sleep() {
        System.out.println("猫白天睡觉");
    }
}


  • 抽象类特点:

    1.抽象类不一定有抽象方法,有抽象方法的一定是抽象类。

  1. 抽象类中可以有构造方法,但是抽象类不能直接通过构造方法进行实例化,需要使用多态对抽象类进行实例化。抽象类的构造方法用于子类访问父类数据时的初始化。
  2. 抽象类按照多态的形式,由具体的子类实例化。
  3. 抽象方法必须被重写,否则会报错(要么将子类也定义为抽象类)。

  • 抽象类的面试题

  • 一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?

    答:抽象类中可以没有抽象方法。一个抽象类中没有抽象方法的意义在于该类不用也不能被实例化。比如Java中的有些工具类,虽然被定义为抽象类,但是类中的方法都用static修饰,可以直接通过类名调用,没必要进行实例化,用abstract修饰类名可以防止由该类生成实例。

  • abstract不能和关键字private/final/static并存,为什么?

    答:被abstract修饰的方法需要被子类重写,而被private修饰的类或方法只能在本类中被访问不能被继承,因此冲突。被final修饰的类不能被继承,方法不能被重写,因此冲突。static静态方法存放在方法区的静态区,无需实例化,可用类名调用。而被abstract修饰的方法需要通过多态进行实例化,如果两者并存,通过类名调用抽象的静态方法将是无意义的。


06 接口

  • 接口相关面试题:

  • Java中有多继承吗?

    答:分情况,接口与接口之间可以实现多继承,但是类与类之间不可以多继承,只能单继承或者多层继承。


  • 抽象类和接口的区别:抽象类,抽取所有子类的共性功能,并强制子类对共性功能进行重写。接口定义的是整个继承体系中的一些额外的扩展功能,哪些类想要具备这些扩展功能,可以单独去实现某个接口。区别:1.抽象类中有构造方法,接口中没有构造方法。2.抽象类中既可以有抽象方法,也可以由非抽象方法。接口中全是抽象方法。3.抽象类中既可以有成员变量,也可以有常量,接口中全是公共的静态常量。

  • 注意:JDK1.8之后接口中可以定义默认方法(有具体的方法实现),但是要在权限修饰符后加关键字default,否则会报错。(定义默认方法,能弥补单继承的劣势,类只能单继承,但是类可以实现多个接口,通过接口实现多方法的继承)


  • 接口的特点

    1.接口不能直接实例化,需要通过多态的方式进行实例化。

    2.接口中的子类可以是抽象类,但这样做的意义不大。接口中的子类也可以是具体类,具类要重写接口中的所有抽象方法。

    3.接口用关键字interface表示。

    interface 接口名{}
    

    4.类实现接口用implements表示

    class 类名 implements 接口名{}
    

    类实现多个接口可以表示为(接口可以多继承):

    class 类名 implements 接口名1,接口名2{}
    

  • 接口成员的特点

    interface interface{
    	//没有构造方法
        public static final int NUM=100;//成员变量只能是常量,默认修饰符为public static final
        public abstract void test1(){}//JDK1.7之前成员方法只能是抽象方法,默认修饰符为public abstract
        public default void test2(){}//JDK1.8之后成员方法可以具体实现,但是该方法必须被default修饰。
    }
    

    总结:1.成员变量只能是常量,并且是静态的。

    ​ 2.没有构造方法。

    ​ 3.成员方法只能是抽象方法(JDK1.8之后有默认方法)


  • 程序示例
public class Test {
    public static void main(String[] args) {
        Cat cat = new Cat();
        Animal animal1=cat;//抽象父类引用指向子类,实现实例化
        animal1.eat();
        Cat c=(Cat)animal1;//向下转型
        Calc ca1=c;//接口引用指向子类,实现实例化
        ca1.calc();
        Jump j=c;//接口引用指向子类,实现实例化
        j.jumpFireCircle();
        Dog dog = new Dog();
        Animal animal2=dog;
        animal2.eat();
        Dog d=(Dog)animal2;//向下转型
        d.guardHome();
        Calc ca2=d;//接口引用指向子类,实现实例化
        ca2.calc();
    }
}

abstract class Animal {
    //成员变量
    private String name;
    private int age;
    //空参构造方法

    public Animal() {
    }
    //有参构造方法
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public abstract void eat();
}

interface Calc {
    public abstract void calc();
}

interface Jump {
    public abstract void jumpFireCircle();
}

class Dog extends Animal implements Calc{//类实现单接口
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
    @Override
    public void calc() {
        System.out.println("狗学会了10以内的加减法");
    }
    public void guardHome(){
        System.out.println("狗的个性本领是守家");
    }
}

class Cat extends Animal implements Calc,Jump {//类实现多接口
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    @Override
    public void calc() {
        System.out.println("猫学会了10以内的加减法");
    }

    @Override
    public void jumpFireCircle() {
        System.out.println("猫学会了跳火圈");
    }
}
运行结果:猫吃鱼
		猫学会了10以内的加减法
		猫学会了跳火圈
		狗吃肉
		狗的个性本领是守家
		狗学会了10以内的加减法

07 类与类,类与接口,接口与接口的关系

  • 类与类:继承关系,只能单继承,可以多层继承。
  • 类与接口:实现关系,可以单实现,也可以多实现。并且还可以在继承一个类的同时实现多个接口。
  • 接口与接口:继承关系,可以单继承,也可以多继承。

猜你喜欢

转载自blog.csdn.net/yinyanyao1747/article/details/89332418
今日推荐