继承 多态 接口 抽象类 内部类

继承 多态 接口 抽象类 内部类

1.继承概述
class Student{
    String name;
    int age;
    void study(){
        System.out.println("study...");
    }
}
class Worker{
    String name;
    int age;
    void work(){
        System.out.println("work...");
    }
}

以上代码可以看到的是,多个事物之间有共同的属性或行为,这种代码的复用性比较差,怎么办?

可以将多个事物之间重复性的属性或行为进行抽取,抽取出来之后放在另外一个单独的类里面即可

class Student{
    void study(){
        System.out.println("study...");
    }
}
class Worker{
    void work(){
        System.out.println("work...");
    }
}
class Teacher{
   void teach(){
       System.out.println("teach...");
   }
}
class Person{
    String name;
    int age;
}

虽然我们将Person类抽取出来,但是目前Worker和Student和Teacher与Person有关系吗?没有

那么如何让类之间产生父(Person)子(Worker Student Teacher)的关系?需要使用关键字extends

class Student extends Person{
    void study(){
        System.out.println("study...");
    }
}
class Worker extends Person{
    void work(){
        System.out.println("work...");
    }
}
class Teacher extends Person{
    void teach(){
    System.out.println("teach...");
    }
}
class Person{
    String name;
    int age;
}
class Dog{
    void kanmen(){}
}

总结:把多个事物中重复性的内容进行抽取,并生成另一个类,该类就是其他类的父类,其他类称之为子类,父类与子类之间用extends关键字来标明

继承的好处

  • 继承的出现了提高了代码的复用性
  • 继承的出现让类与类之间产生关系,也为我们后面的多态提供了前提

单继承与多继承

在现实生活中,父与子是一对多关系,子与父是一对一关系

使用继承时,除了要考虑类与类之间重复的内容之外,更重要的是去靠率关系,必须是一种 is a关系,即XXX是YYY的一种!苹果是水果的一种,狗是动物的一种

Java中,类与类之间只能支持单继承,接口与接口之间可以支持多继承

class Test extends Test1{}
class Test extends Test1,Test2{}//Error

继承体系

既然有了单继承,也有了父子关系,爷孙关系,曾祖父-重孙,继承出现了层级,继承体系

class A{}
class B extends A{}
class C extends A{}
class D extends C{}
......

继承使用需要注意的问题

  • 继承和传统的理解稍微有一些不同,子类并不是父类的一个子集!实际上,一个子类通常要比它的父类包含更多的信息和方法。(子类更多的是父类的一种升级和延续)
  • 父类中的一些私有内容,子类是获取不到的。如果父类对其自身的私有内容设置了公共的访问器和修改器的话,子类可以通过该访问器和修改器来获取父类的私有内容。
  • 不要为了获取某一个特殊的属性或行为,而去乱认爸爸
  • 总之,在设计类和其继承关系时,一定要符合社会常识认知

子父类中成员变量的特点

public class Test {
    public static void main(String[] args) {
        Zi zi = new Zi();
        System.out.println(zi.num);
    }
}
class Fu{
    int num = 10;
}
class Zi extends Fu{

}

如果子类没有,父类有且非私有,子类对象可以获取num


public class Test {
    public static void main(String[] args) {
        Zi zi = new Zi();
        System.out.println(zi.num);
    }
}
class Fu{
    private int num = 10;
}
class Zi extends Fu{

}

子类没有,父类有且私有,子类对象不能获取num

public class Test {
    public static void main(String[] args) {
        Zi zi = new Zi();
        System.out.println(zi.num);
    }
}
class Fu{
    //int num = 10;
}
class Zi extends Fu{
    int num = 20;
}

子类有,父类有且非私有或是没有,子类对象获取的是子类的num

public class Test {
    public static void main(String[] args) {
        Zi zi = new Zi();
        System.out.println(zi.num);
        zi.Show();
    }
}
//父类 超类 基类
class Fu{
    int num = 10;
}
class Zi extends Fu{
    int num = 20;
    void Show(){
        int num  = 30;
        System.out.println(num + "," + this.num + "," + super.num);
    }
}

如果子类中,成员变量和父类变量和局部变量重名时 ,以上代码可以解决。有一个特殊的super

  • this表示的是当前对象,存的是当前对象在堆内存中的地址
  • super不表示父类的对象,因为在此我们并没有去创建父类的对象。super仅仅表示父类的空间,并没有创建父类对象

函数的重写

在子父类中,同名函数

  • 保留父类的功能声明 子类进一步优化
  • 保留父类的功能声明 子类可以重写定义

重写当中应该注意到的一些细节:

  1. 函数重名,但是参数列表不同,不构成重写关系
  2. 函数重名,参数列表相同,构成重写关系,返回值类型也必须是一样的
  3. 子类重写父类函数时,权限>=父类权限
  4. 如果父类函数权限为private 子类压根没有继承到

子父类中构造函数的特点

public class Test {
    public static void main(String[] args) {
        Zi zi = new Zi();
    }
}
//父类 超类 基类
class Fu{
    Fu(){
        System.out.println("Fu show...");
    }
}
class Zi extends Fu{
   Zi(){
       System.out.println("Zi show...");
   }
}

可以看到的是当创建子类对象时,在子类的构造函数执行之前,父类的构造函数先执行,虽然父类的构造函数执行,但不代表父类对象的创建

  • 每一个类中的构造函数第一句如果不是this()调用本类中其他构造函数的话,默认第一句是隐藏的super()
  • 是因为在创建子类对象的时候,需要为父类继承给子类的一些数据进行初始化
public class Test {
    public static void main(String[] args) {
        Zi zi = new Zi();
    }
}
class Fu{
    int num;
    Fu(){
        System.out.println("Fu show..." + num);
        num = 4;
    }
}
class Zi extends Fu{
   Zi(){
       super();//调用父类的无参构造函数 默认隐藏
       //父类的构造函数一旦执行完毕 则紧接着执行子类成员显示初始化
       System.out.println("Zi show..." + num);
   }
}

this()与super()是否冲突

public class Test {
    public static void main(String[] args) {
        Zi zi1 = new Zi();
        Zi zi2 = new Zi(1);
        Zi zi3 = new Zi(1,2);
    }
}
class Fu{
    int num;
    Fu(){
        System.out.println("Fu show...");
        num = 4;
    }
}
class Zi extends Fu{
   Zi(){
       System.out.println("Zi show...0");
   }
   Zi(int a){
       System.out.println("Zi show...1");
   }
   Zi(int a,int b){
       System.out.println("Zi show...2");
   }
}

如果子类的构造函数之间没有调用关系,则每一个构造函数的第一句都是super()

public class Test {
    public static void main(String[] args) {
        Zi zi1 = new Zi();
        Zi zi2 = new Zi(1);
        Zi zi3 = new Zi(1,2);
    }
}
class Fu{
    int num;
    Fu(){
        System.out.println("Fu show...");
    }
}
class Zi extends Fu{
   Zi(){
       this(1);
       System.out.println("Zi show...0");
   }
   Zi(int a){
       this(1,2);
       System.out.println("Zi show...1");
   }
   Zi(int a,int b){
       super();
       System.out.println("Zi show...2");
   }
}

在上一段代码中,Fu show的执行,在每一个构造函数中的第一句super()执行

在这一段代码中,Fu show的执行,在Zi(int,int)调用执行的

在构造函数中,第一句要么是this(),要么是super()

有没有可能每一个构造函数的第一句都是this()?没有,否则递归调用

有没有可能每一个构造函数的第一句都是super()? 有,构造函数之前不调用

this()与super()本身不冲突,如果构造函之间有调用关系,那么最后一个被调用的函数不能回调,那么其第一句就不能是this(),只能是super()

public class Test {
    public static void main(String[] args) {
        Zi zi1 = new Zi();
        Zi zi2 = new Zi(1);
        Zi zi3 = new Zi(1,2);
    }
}
class Fu{
    int num;
    Fu(int a){
        System.out.println("Fu show...");
    }
}
class Zi extends Fu{
   Zi(){
       this(1);
       System.out.println("Zi show...0");
   }
   Zi(int a){
       this(1,2);
       System.out.println("Zi show...1");
   }
   Zi(int a,int b){
       super(a);
       System.out.println("Zi show...2");
   }
}

如果父类中没有无参构造函数存在,只有有参数的构造函数的话,那么子类中默认的super()就调用不到父类无参构造函数,引发错误!

建议每一个类都把它的 无参构造函数 写出来

public class Test {
    public static void main(String[] args) {
        Zi zi1 = new Zi();
        Zi zi2 = new Zi(3);
        Zi zi3 = new Zi(5,6);
    }
}
class Fu{
    int num;
    Fu(){
        System.out.println("Fu constructor...0  " + "num=" + num);
        num = 10;
    }
    Fu(int a){
        System.out.println("Fu constructor...1  " + "num=" + num);
        num = a;
    }
}
class Zi extends Fu{
    int num = 20;
    Zi(){
        System.out.println("Zi constructor...0  " + "num=" +num + " fu num=" + super.num);
    }
    Zi(int a){
        this(a,0);
        System.out.println("Zi constructor...1  " + "num=" +num + " fu num=" + super.num);
    }
    Zi(int a,int b){
        super(a+b);
        num = a +b;
        System.out.println("Zi constructor...2  " + "num=" +num + " fu num=" + super.num);
    }
}
2.final关键字

final翻译过来是最终,final可以修饰变量、函数、类

final修饰变量

表示该变量的值不可被改变

变量主要分为,基本数据类型变量,引用数据类型变量

  • 如果final修饰基本数据类型变量,表示变量所存储的常量值不能改变
  • 如果final修饰引用数据类型变量,表示变量所存的对象地址值不能改变 但是可以改变对象中的数据(如果对象中的数据也是final则也不能修改)
public class Test {
    public static void main(String[] args) {
        Demo d = new Demo();
        System.out.println(d.num);
/*        d.num = 5;
        Error:(7, 10) java: 无法为最终变量num分配值*/
        final int[] arr =new int[]{1,2,3};
        /*arr = new int[]{4,5,6};
        Error:(10, 9) java: 无法为最终变量arr分配值*/
        arr[1] = 10;
        for (int i = 0; i < arr.length;i++){
            System.out.print(arr[i] + ",");
        }
    }
}
class Demo{
    //当前这个变量的值不能被修改
    public final int num = 10;
}
public class Test {
    public static void main(String[] args) {
       final Demo d = new Demo();
      /* d = new Demo();
       Error:(6, 8) java: 无法为最终变量d分配值*/
       /* d.num = 30;
        Error:(8, 10) java: 无法为最终变量num分配值*/
    }
}
class Demo{
    public final int num = 10;
    public int haha = 20;
}

一般而言,当我们在定义常量(字面量 用变量+final来表示),定义成静态变量

public static final 数据类型 变量名 = 常量数据;

对于常量的变量名起名规则为 全部单词大写 单词与单词之间用下划线分隔

public static final double PI = 3.14;

public static final int MAX_VALUE = 100;

final修饰函数

如果某一个类中的函数,不想让其子类去重写的话,该函数就可以声明为final类型

public class Test {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}
class Fu{
    final void show(){
        System.out.println("可以继承到show");
    }
}
class Zi extends Fu{
    /*void show(){}
    Error:(12, 10) java: com.company.Zi中的show()无法覆盖com.company.Fu中的show()
    被覆盖的方法为final*/
}

Zi类确实继承到了show(),也可以调用该函数,但是由于show是final,所以子类不能重写父类的show函数

final修饰类

表示该类不能被继承

public class Test {
    public static void main(String[] args) {
    }
}
 final class Fu{

}
/*class Zi extends Fu{
    Error:(10, 18) java: 无法从最终com.company.Fu进行继承
}*/

3.抽象类

抽象:指的就是不具体,模糊不清这种含义

不能直接将抽象和类进行挂钩,之所以有抽象类的存在,是因为有抽象函数

具有抽象函数的类,称之为抽象类

抽象函数

public class Test {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.jiao();
        d.eat();
        Cat c = new Cat();
        c.jiao();
        c.eat();
       /* Anmial a = new Anmial();
        Error:(11, 20) java: Anmial是抽象的; 无法实例化*/
    }
}
abstract class Anmial{
    abstract void jiao();
    abstract void eat();
}
class Dog extends Anmial{
    @Override
    void jiao(){
        System.out.println("汪汪汪");
    }
    void kanmen(){
        System.out.println("看门");
    }
    @Override
    void eat(){
        System.out.println("狗吃骨头");
    }
}
class Cat extends Anmial{
    @Override
    void jiao(){
        System.out.println("喵喵喵");
    }
    void catMouse(){
        System.out.println("猫捉老鼠");
    }
    @Override
    void eat(){
        System.out.println("猫吃鱼");
    }
}
/*
class Pig extends Anmial{
    void gongbaicai(){
        System.out.println("猪拱白菜");
    }
} 
Error:(45, 1) java: Pig不是抽象的, 并且未覆盖Anmial中的抽象方法eat()*/

抽象函数:当我们将多个事物的共同行为(函数)进行抽取并封装到另外一个类中时,发现在该类中,这些方法的具体执行内容无法确定,只能有这些子类来决定该函数的具体执行,那么在该类中,将这些抽取来的函数仅函数保留函数声明,不保留函数体即可,那么该函数就是抽象函数,用abstract关键字来修饰,既然有了抽象函数的存在,那么具有抽象函数的类也被成为抽象类,也必须用abstract修饰。抽象类不能创建对象,只有其实现子类能够创建对象

/*
class Pig extends Anmial{
    void gongbaicai(){
        System.out.println("猪拱白菜");
    }
} 
Error:(45, 1) java: Pig不是抽象的, 并且未覆盖Anmial中的抽象方法eat()*/

此处的Pig虽然是Anima的子类,但是并未重写/覆盖/实现Animal中的抽象函数,所以报错

如何解决?要么把Pig声明为抽象类,要么Pig重写抽象函数

抽象类的特点

  • 抽象类和抽象函数都需要被abstract修饰,抽象方法一定在抽象类中
  • 抽象类不能创建对象,因为如果一旦创建对象,在调用其函数时,函数没有具体的执行内容
  • 只有覆盖了抽象类中所有的抽象函数后,子类才可以实例化,否则该子类还是一个抽象类

关于抽象类的细节问题

  • 抽象类一定是一个父类吗?

是的,因为抽象类本身就是由多个事物进行抽取而来的

  • 抽象类是否有成员变量,成员函数,构造函数呢?

有,抽象类与一般类唯一的区别就是抽象类中有抽象函数,其他相同(抽象类不能创建对象)

  • 抽象类和一般类的异同点

相同点

  1. 它们都是用来描述事物的
  2. 它们之中都可以去定义属性和行为

不同点

  1. 一般类可以具体的描述事物,抽象类描述事物时会有一些不具体的信息
  2. 抽象类比一般类可以多定义一个成员:抽象函数
  3. 一般类可以创建对象,抽象类不能创建对象
  • 抽象类中是否可以不定义抽象函数?

可以的。有抽象函数的类一定时抽象类,抽象类不一定有抽象函数

  • 抽象关键字abstract不能与哪些其他关键字共存
    • final:final如果修饰类,表示该类不能被继承,final修饰函数时,表示函数不能被重写;不能,抽象类本身就是父类,并且其中的抽象函数就等着被子类重写。
    • private:private修饰函数,表示函数被私有,不能被子类继承;不能,抽象函数就等着被子类重写
    • static:static修饰的函数,属于类的,随着类的加载,从而被加载进方法去,和对象没有关系了,可以直接用类调用静态成员,如果抽象函数被static修饰,被类调用没有意义。

综合案例:线性表

public class Test {
    public static void main(String[] args){
        LinkedList l = new LinkedList();
        l.add(0,1);
        System.out.println(l.get(0));
    }
}
//定义一个线性表List类 声明了该线性表的共同操作
abstract class List{
    //在指定角标index处添加元素e
    public abstract void add(int index,int e);
    //删除执行角标index出的元素,并返回该元素值
    public abstract int delete(int index);
    public abstract int get(int index);
    //修改指定角标处index的元素为新元素e,并返回原先的值
    public abstract int set(int index,int e);
    //返回线性表中有效元素的个数
    public abstract int size();
    //清空线性表
    public abstract void clear();
    //判断线性表是否为空
    public abstract boolean isEmpty();
}
//链表的结点
class Node{
    int data;//数据域
    Node next;//指针域
    public Node(){

    }
    public Node(int data){
        this.data = data;
    }
    public Node(int data,Node next){
        this.data = data;
        this.next = next;
    }
    public String toString(){
        return "(" + data + ")";
    }
}
//基于链表实现线性表,链式存储
class LinkedList extends List{
    private Node head;
    private int size;
    public LinkedList(){
        head = new Node();
    }
    @Override
    //在指定角标index处添加元素e
    public void add(int index, int e) {
        if(index < 0 || index > size){
            throw new IllegalArgumentException("add() index out of range");//异常
        }
        Node pre = getNode(index - 1);
        Node n = new Node(e);
        pre.next = n.next;
        pre.next = n;
        size++;
    }
    @Override
    //删除执行角标index出的元素,并返回该元素值
    public int delete(int index) {
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("get() index out of range");//异常
        }
        Node pre = getNode(index - 1);
        Node p = getNode(index);
        pre.next = p.next;
        p.next = null;
        size--;
        return p.data;
    }
    @Override
    public int get(int index) {
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("get() index out of range");//异常
        }
        return getNode(index).data;
    }
    //返回指定角标index处的结点对象
    private Node getNode(int index){
        Node p = head;
        for(int i = 0;i <= index;i++){
            p = p.next;
        }
        return p;
    }
    @Override
    //修改指定角标处index的元素为新元素e,并返回原先的值
    public int set(int index,int e) {
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("set() index out of range");//异常
        }
        Node p = getNode(index);
        int ret = p.data;
        p.data = e;
        return ret;
    }
    @Override
    //返回线性表中有效元素的个数
    public int size() {
        return size;
    }
    @Override
    //清空线性表
    public void clear() {
        head.next = null;
        size = 0;
    }
    @Override
    //判断线性表是否为空
    public boolean isEmpty() {
        return size == 0 && head.next == null;
    }
}
4.接口

从代码的角度上而言,接口其实就是抽象类的一种特殊表现形式

当一个抽象类中,所有的方法都是抽象函数时,那么,该类就可以用接口来表示

接口还是类吗?不是,一些类的功能和操作不再适用于接口

接口中没有成员函数,没有成员变量,没有构造函数,没有静态函数,没有静态变量

接口也不能直接去创建对象

interface List{
    public abstract void add(int index,int e);
    public abstract int delete(int index);
    public abstract int get(int index);
    public abstract int set(int index,int e);
    public abstract int size();
    public abstract void clear();
    public abstract boolean isEmpty();
}

接口中的变量和函数就会有一些特殊的含义

  • 接口中的变量 默认是公共静态常量 public static final 类型 就算不写这些关键字 也是默认的
  • 接口中的函数 默认是公共抽象函数 public abstract类型 就算不写这些关键字 也是默认的
interface InterfaceA{
    int num = 0;
    //这里面num已经不是成员变量的含义 默认是 public static final类型
    void show();
    //默认是public abstract类型
}

接口和类之间的关系

类与类之间是单继承关系,类与接口之间是多实现关系,接口与接口之间是多继承关系

类与接口之间的实现关系用imlpements关键字表示

  • 要么这个类实现接口中所有的方法
  • 要么这个类声明为abstract,这一点和继承抽象类一致
interface InterfaceA{
    void showA();
}
//叫做DemoA类实现了InterfaceA这个接口
/*class DemoA implements InterfaceA{
    @Override
    public void showA(){
        System.out.println("DemoA showA");
    }
}*/
abstract class DemoA implements InterfaceA{
    
}

类与接口有着多实现的存在,要么把这几个接口全部实现,要么声明为abstract

interface InterfaceA{
    void showA();
}

interface InterfaceB{
    void showB();
}
class DemoB implements InterfaceA,InterfaceB{
    @Override
    public void showA(){}
    @Override
    public void showB(){}
}

接口与接口有多继承关系,对于InterfaceC接口的实现子类而言,要么把这几个接口全部实现,要么声明为abstract

interface InterfaceA{
    void showA();
}
interface InterfaceB{
    void showB();
}
interface InterfaceC extends InterfaceA,InterfaceB{
    void showC();
}
class DemoC implements InterfaceC{
    @Override
    public void showA() {}
    @Override
    public void showB() {}
    @Override
    public void showC() {}
}

接口与接口之间不会存在实现关系。

接口中的特点

  • 接口中的变量都是全局静态常量
  • 接口中的方法都是全局抽象函数
  • 接口不可以创建对象
  • 子类必须覆盖接口中的所以抽象方法,或声明为abstract
  • 类与接口之间可以存在多实现关系
  • 接口与接口之间可以多继承关系
  • 类与类之间只能是单继承关系

接口的存在,主要解决的就是类与类之间只有单继承的关系

类在继承的同时也可以进行接口的实现

class DemoA{
    void ShowA(){}
}
interface DemoB{
    void ShowB();
}
//如果DemoC既有DemoA的描述 又有DemoB的描述
//也就是意味着DemoC可能要有两个父类 Java机制中不允许多父类的存在
class DemoC extends DemoA interface DemoB{
    public void showA(){}
    public void showB(){}
}

当一个类在继承另外一个类的时候,就已经用有了该继承体系的所有功能,接口的作用就是在这些体系功能之上,增加的一些额外的功能 !大大的扩展了类的功能!

接口除了作为类的功能扩展之外,接口还可以作为一种对外暴露的规则,来进行解耦操作

public class Test {
    public static void main(String[] args){
        Computer computer = new Computer();
        Mouse mouse = new Mouse();
        KeyBoard keyBoard = new KeyBoard();
        Creame creame = new Creame();
        computer.getDev1(mouse);
        computer.getDev2(keyBoard);
        computer.getDev3(creame);
    }
}
interface USB{
    void getContent();
}
class Computer{
    //获取USB接口的设备1
    public void getDev1(USB dev){//USB dev = new Mouse();
        System.out.println("电脑连接了设备1");
        dev.getContent();
    }
    //获取USB接口的设备2
    public void getDev2(USB dev){
        System.out.println("电脑连接了设备2");
        dev.getContent();
    }
    //获取USB接口的设备3
    public void getDev3(USB dev){
        System.out.println("电脑连接了设备3");
        dev.getContent();
    }
}
class Mouse implements USB{
    public void getContent(){
        System.out.println("鼠标已经连接电脑,开始运行");
    }
}
class KeyBoard implements USB{
    public void getContent(){
        System.out.println("键盘已经连接电脑,开始运行");
    }
}
class Creame implements USB{
    public void getContent(){
        System.out.println("相机已经连接电脑,开始运行");
    }
}

接口之间可以多继承,类之间不能多继承

public class Test {
    public static void main(String[] args){
        Demo d = new Demo();
        d.show();
    }
}
interface InterfaceA{
    void show();//妈妈希望你过得好
}
interface InterfaceB{
    void show();//爸爸希望你过得好
}
interface InterfaceC extends InterfaceA,InterfaceB{

}
class Demo implements InterfaceC{
    @Override
    public void show() {//自己过得好不好 自己决定
        System.out.println("showc...");
    }
}

public class Test {
    public static void main(String[] args){
       /* DemoC dc = new DemoC();
        dc.show();//产生调用的二义性 因为两个父类中的函数都有函数体 不知道执行哪个*/
    }
}

/*
class DemoA{
    void show(){//妈妈希望你成才 成为医生
        System.out.println("Kill DemoB");
    }
}
class DemoB{
    void show(){//爸爸希望你成才 成为教师
        System.out.println("Kill DemoA");
    }
}
class DemoC extends DemoA,DemoB{
//成才 成啥 不知道 有二义性
}*/

归根结底,看函数的函数体

接口与抽象类的区别

相同点:

  • 都位于继承或实现的顶端
  • 都不能实例化
  • 都包含抽象函数,其子类都必须覆盖这些方法

不同点:

  • 一个类只能继承一个父类,但是可以实现多个接口
  • 抽象类中其实可以存在一些已经实现好的方法,有部分未实现的方法由子类决定;接口中只能包含抽象函数,子类必须完全实现
5.多态

多种状态:就是指一个对象可以有多种状态(当前对象在继承体系中的位置变化)

位置的变化,只能是向上变 但不能低于自身

不同的视角,不同的角色,无论在哪个视角,对象本身变了没有?只不过我们需要对象在不同的场合表现出相对应的行为

多态的代码表现

父类的引用指向子类的对象

public class Test {
    public static void main(String[] args){
        Son s = new Son();
        Dady d = new Son();//父类的引用指向子类的对象 
        d.chouyan();
        d.hejiu();
        d.tangtou();
    }
}
class Dady{
    void chouyan(){
        System.out.println("抽烟");
    }
    void hejiu(){
        System.out.println("喝酒");
    }
    void tangtou(){
        System.out.println("烫头");
    }
}
class Son extends Dady{
    @Override
    void chouyan() {
        System.out.println("儿子抽烟");
    }
    @Override
    void hejiu(){
        System.out.println("儿子喝酒");
    }
    @Override
    void tangtou(){
        System.out.println("儿子烫头");
    }
    void lol(){
        System.out.println("儿子玩lol");
    }
}

多态的前提:继承 重写

多态的好处:对代码的功能进行了可扩展性

子类对象可以当作父类对象去使用 但是有限制,只能调用父类函数或者重写的函数,不能使用子类特有的行为

多态当中的特点:

  • 多态当中成员变量的特点,只能访问父类中的成员变量
  • 多态当中成员函数的特点
    • 如果子类没有重写 且父类中有 调用父类
    • 如果子类有重写 调用子类重写的
    • 如果不存在重写 父类没有 那就报错
public class Test {
    public static void main(String[] args) {
        Dog d = new Dog();
        Cat c = new Cat();
        Pig p = new Pig();
        feed(d);
        feed(c);
        feed(p);
    }
    //Animal a = new Dog();
    //Animal a = new Cat();向上转型(向上类型转换)
    public static void feed(Anmial a){
        a.eat();
       /*
        //ClassCastException 类型转换异常
        Pig pp = (Pig)a;
        pp.gongbaicai();*/
        //判断对象的本质类型 instanof
        if(a instanceof Pig){
            Pig pp = (Pig)a;
            pp.gongbaicai();
        }
    }
}
class Anmial{
    void jiao(){}
    void eat(){}
}
class Dog extends Anmial{
    @Override
    void jiao(){
        System.out.println("汪汪汪");
    }
    @Override
    void eat(){
        System.out.println("狗吃骨头");
    }
    void kanmen(){
        System.out.println("看门");
    }
}
class Cat extends Anmial{
    @Override
    void jiao(){
        System.out.println("喵喵喵");
    }
    @Override
    void eat(){
        System.out.println("猫吃鱼");
    }
    void catMouse(){
        System.out.println("猫捉老鼠");
    }
}

class Pig extends Anmial{
    @Override
    void jiao(){
        System.out.println("哼哼哼");
    }
    @Override
    void eat(){
        System.out.println("猪啥都吃");
    }
    void gongbaicai(){
        System.out.println("猪拱白菜");
    }
}
6.内部类

就是当我们在描述一个事物的时候,发现该事物当中又存在另一个事物的时候

我们把当前的事物成为外部类,另一个事物称之为内部类

如何调用内部类的成员

想要调用内部类的成员,必须先创建内部类的对象new Inner()

但是直接new Inner()发现找不到Inner这个类

因为Inner是Outter的非静态成员

所以Inner这个类想过要存在的前提是创建Outter对象

public class Test {
    public static void main(String[] args) {
        Outtter.Inner inner = new Outtter().new Inner();
        System.out.println(inner.num);
        inner.show();
    }
}
class Outtter{
    int num = 10;
    void show(){
        System.out.println("Outter show...");
    }
    class Inner{
        int num = 20;
        void show(){
            System.out.println("Inner show...");
        }
    }
}

如何区分内部类成员和外部类成员

public class Test {
    public static void main(String[] args) {
        Outtter.Inner inner = new Outtter().new Inner();
        System.out.println(inner.num);
        inner.show();
    }
}
class Outtter{
    int num = 10;
    void show(){
        System.out.println("Outter show...");
    }
    class Inner{
        int num = 20;
        void show(){
            System.out.println("Inner show...");
            //Outter.this.num
            //Outter.(这个类的)this.(的对象)num(的num)
            System.out.println(Outtter.this.num + "," + this.num);
        }
    }
}

如何调用内部类的静态成员

静态的东西是优先加载进方法区的,然而haha在一个非静态的类当中,必须先创建Inner对象

不到Inner这个类

因为Inner是Outter的非静态成员

所以Inner这个类想过要存在的前提是创建Outter对象

public class Test {
    public static void main(String[] args) {
        Outtter.Inner inner = new Outtter().new Inner();
        System.out.println(inner.num);
        inner.show();
    }
}
class Outtter{
    int num = 10;
    void show(){
        System.out.println("Outter show...");
    }
    class Inner{
        int num = 20;
        void show(){
            System.out.println("Inner show...");
        }
    }
}

如何区分内部类成员和外部类成员

public class Test {
    public static void main(String[] args) {
        Outtter.Inner inner = new Outtter().new Inner();
        System.out.println(inner.num);
        inner.show();
    }
}
class Outtter{
    int num = 10;
    void show(){
        System.out.println("Outter show...");
    }
    class Inner{
        int num = 20;
        void show(){
            System.out.println("Inner show...");
            //Outter.this.num
            //Outter.(这个类的)this.(的对象)num(的num)
            System.out.println(Outtter.this.num + "," + this.num);
        }
    }
}

如何调用内部类的静态成员

静态的东西是优先加载进方法区的,然而haha在一个非静态的类当中,必须先创建Inner对象

如果内部类中有静态 那么内部类必须是静态的,此时内部类当中无法访问外部类的非静态,但是可以访问内部类中的非静态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值