Java学习笔记(三)

Java学习笔记(三)

抽象abstract

概念:如果父类当中的方法不确定如何进行{}方法体实现,那么着应该是一个抽象方法

抽象方法和抽象类的格式

抽象方法:就是加上 abstract 关键字,然后去掉大括号,直接分号结束

抽象类:抽象方法所在的内,必须是抽象类才行,在 class 之前写上 abstract 关键字即可

//抽象类
public abstract class Animal{
    //抽象方法,代表吃东西具体吃什么(大括号的内容)不确定
    public abstract void eat();
}

抽象方法和抽象类的使用

如何使用抽象类和抽象方法:

  1. 不能直接new抽象类对象

  2. 必须用一个子类来继承抽象父类

  3. 子类必须覆盖重写抽象父类当中所有的抽象方法

    覆盖重写(实现):子类去掉抽象方法的 abstract 关键字,然后补上方法体大括号

  4. 创建子类对象进行使用

public abstract class Animal{
    public abstract void eat();
}

public class Cat extends Animal{
    @Override
    public void eat(){
        System.out.println("猫吃鱼");
    }
}
public class Demo{
    public static void main(String[] args){
        Cat cat = new Cat();
        cat.eat();//猫吃鱼
    }
}

抽象方法和抽象类的注意事项

关于抽象类的使用,一下语法上要注意的细节,虽然条目很多,但如果理解抽象的本质,无需死记硬背

  1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能拆功能键其非抽象子类对象

    理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有就具体的方法体,没有意义

  2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的

    理解:子类的构造方法中,有默认的super(),需要访问父类的构造方法

  3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定时抽象类

    理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计

  4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类

    理解:假设不重写所有抽象方法,则类中可能包含抽象方法,那么创建对象后,调用抽象的方法,没有意义

接口interface

接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。

Java中的接口更多的体现在对行为的抽象!

接口是一个应用数据类型,最重要的内容就是其中的抽象方法

接口定义的基本格式

/*
备注:换成了关键字interface之后,编译生成的字节码文件仍然是:.java --->  .class

如果时Java 7,那么接口中可以包含内容有:
1、常量
2、抽象方法
如果时Java 8,还可以额外包含有:
3、默认方法
4、静态方法
如果时Java 9,还可以额外包含有:
5、私有方法
*/
public interface 接口名称{
    //接口内容
}

接口的抽象方法定义和使用

在任何版本的Java中,接口都能定义抽象方法

格式:

public interface 类名称{
    //这是一个抽象方法
    public abstract 返回值类型 方法名称(参数列表);
    .....
}
/*
注意事项:
1、接口中的抽象方法,修饰符必须时两个固定的关键字:public abstract
2、这两个关键字修饰符,可以选择性省略
*/
public interface Demo{
    //这是一个抽象方法
    public abstract void method1();
    //这是一个抽象方法
    abstract void method2();
    //这是一个抽象方法
    public void method3();
    //这是一个抽象方法
    void method4();
}

接口使用步骤:

  1. 接口不能直接使用,必须有一个实现类来实现该接口

  2. 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法

    实现:去掉 abstract 关键字,加上方法体大括号

  3. 创建实现类的对象,进行使用

实现类的格式:

public class 实现类名称 implements 接口名称{
    //.....
}

注意事项:如果实现类并没有覆盖重写接口中的所有抽象方法,那么这个实现类自己就必须时抽象类

接口中的默认方法定义和使用

Java 8开始,接口里允许定义默认方法

格式:

public default 返回值类型 方法名称(参数列表){
	//方法体
}

备注:接口中的默认方法,可以解决接口升级问题

public interface MyInterfaceDefault{
    //抽象方法
    public abstract void methodAbs();
    //新添加的方法,改为默认方法
    public default void methodDefault(){
        System.out.println("这是新添加的默认方法");
    }
}

public class MyInterfaceDefaultA implements MyInterfaceDefault{
    @Override
    public void methodAbs(){
        System.out.println("实现了抽象方法,AAA");
    }
}

public class MyInterfaceDefaultB implements MyInterfaceDefault{
    @Override
    public void methodAbs(){
        System.out.println("实现了抽象方法,BBB");
    }
}

public class Demo{
    public static void main(String[] args){
        MyInterfaceDefaultA a = new MyInterfaceDefaultA();
        a.methodAbs();
        //调用默认方,如果实现类当中没有,会向上找接口
        a.methodDefault();
        
        MyInterfaceDefaultB b = new MyInterfaceDefaultB();
        b.methodAbs();
        //调用默认方,如果实现类当中没有,会向上找接口
        b.methodDefault();
    }
}

注意事项:

  1. 接口的默认方法,可以通过接口实现类对象,直接调用
  2. 接口的默认方法,也可以被接口实现类进行覆盖重写

接口的静态方法定义和使用

从Java 8开始,接口当中允许定义静态方法

格式:

public static 返回值类型 方法名称(参数列表){
    //方法体
}
/*
注意事项:不能通过接口实现类的对象来调用接口中的静态方法
正确用法:通过接口名称,直接调用其中的静态方法
*/
public interface MyInterfaceStatic{
    public static void methodStatic(){
        System.out.println("这是接口的静态方法!");
    }
}

public class MyInterfaceStaticImpl implements MyInterfaceStatic{
    //接口中没有抽象方法,没有必要覆盖重写
}

public class Demo{
    public static void main(String[] args){
        //直接通过接口名称调用静态方法
        MyInterfaceStatic.methodStatic();
    }
}

接口的私有方法定义和使用

从Java 9开始,接口中允许定义私有方法

  1. 普通私有方法:解决多个默认方法之间重复代码问题

    格式:

    private 返回值类型 方法名称(参数列表){
    	//方法体
    }
    
  2. 静态私有方法:解决多个静态方法之间重复代码问题

    格式:

    private static 返回值类型 方法名称(参数列表){
        //方法体
    }
    
    public interface MyInterfacePrivateA{
        public default void methodDefault1(){
            System.out.println("静态方法1");
            methodCommon();
        }
        public default void methodDefault2(){
            System.out.println("静态方法2");
            methodCommon();
        }
        //实现类访问不到methodCommon方法
        private void methodCommon(){
            System.out.println("AAA");
        }
    }
    
    public interface MyInterfacePrivateB{
        public static void methodStatic1(){
            System.out.println("静态方法1");
            methodStaticCommon();
        }
        public static void methodStatic2(){
            System.out.println("静态方法2");
            methodStaticCommon();
        }
        实现类访问不到methodStaticCommon方法
        private static void methodStaticCommon(){
            System.out.println("AAA");
        }
    }
    

    接口的常量定义和使用

    接口中也可以定义“成员变量”,但是必须使用 public static final 三个关键字进行修饰

    从效果上看,这其实是接口的常量

    格式:

    pubilc static final 数据类型 常量名称 = 数据值;
    
    /*
    备注:一旦使用final关键字进行修饰,说明不可改变
    注意事项:
    1、接口中的常量,可以省略public static final
    2、接口中的常量,必须进行赋值
    3、接口中常量的名称,使用完全大写的字母,用下划线进行分割
    */
    public interface MyInterfaceConst{
        //这其实就是一个常量,一旦赋值不可修改
        public static final int NUM_OF_MY_CLASS = 10;
    }
    public class Demo{
        public static void main(String[] args){
            System.out.println(MyInterfaceConst.NUM_OF_MY_CLASS);
        }
    }
    

    接口的内容小结([]里的内容可选)

    在Java 9+版本中,接口的内容可以有:

    1. 成员变量其实是常量,格式:

      [public] [static] [final] 数据类型 常量名称 = 数据类型;
      

      注意:

      ​ 常量必须进行赋值,而且一旦赋值不能改变

      ​ 常量名称完全大写,用下划线进行分割

    2. 接口中最重要的就是抽象方法,格式:

      [public] [abstract] 返回值类型 方法名称(参数列表)

      注意:实现类必须覆盖重写接口中所有的抽象方法,除非实现类是抽象类

    3. 从Java 8开始,接口中允许定义默认方法,格式:

      [public] default 返回值类型 方法名称(参数列表) {   方法体   }
      

      注意:默认方法也可以被覆盖重写

    4. 从Java 8开始,接口中允许定义静态方法,格式

      [public] static 返回值类型 方法名称(参数列表) {    方法体   }
      

      注意:应该通过接口名称调用,不能通过实现类对象调用接口静态方法

    5. 从Java 9开始,接口中允许定义私有方法,格式:

      //普通私有方法
      private 返回值类型 方法名称(参数列表) {   方法体    }
      //静态私有方法
      private static 返回值类型 方法名称(参数列表) {   方法体    }
      

      注意:private 的方法只有接口自己才能调用,不能被实现类或别人调用

    使用接口的时候,需要注意:

    1. 接口是没有静态代码块或者构造方法的

    2. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口

      格式:

      public class MyInterfaceImpl implements MyInterfacerA,MyInterfacerB{
          //覆盖重写所有的抽象方法
      }
      
    3. 如果实现类所实现的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可

    4. 如果实现类美哟i覆盖重写所有接口当中的所有抽象方法,那么实现类必须是一个抽象类

    5. 如果实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写

    6. 一个类如果直接父类当中的方法和接口当中的默认方法产生冲突,优先使用父类当中的方法

接口之间的多继承

特点:

  1. 类与类之间是单继承的,直接父类只有一个
  2. 类与接口之间是多实现的,一个类可以实现多个接口
  3. 接口与接口之间是多继承的

注意事项:

  1. 多个父接口当中的抽象方法如果重复,没关系
  2. 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,而且带着default 关键字

多态

面向对象三大特征:封装性继承性多态性

extends继承或者implements实现多态 的前提

概念:

  • 什么是多态

    ​ 同一个对象,在不同时刻表现出来的不同形态

  • 多态的前提

    • 要有继承或实现关系
    • 要有方法的重写
    • 要有父类引用指向子类对象

多态的格式和使用

代码当中体现多态性,其实就是一句话,父类引用指向子类对象

格式:

父类对象 对象名 = new 子类名称();

或者

接口名称 对象名 = new 实现类名称(); 
public class Fu{
    public void method(){
        System.out.println("父类方法");
    }
    public void methodFu(){
        System.out.println("父类特有方法");
    }
}
public class Zi extends Fu{
    @Override
    public void method(){
        System.out.println("子类方法");
    }
}
public class Demo{
    public static void main(String[] args){
        //使用多态的写法
        //父左侧父类的引用,指向了右侧子类的对象
        Fu obj = new Zi();
        
        obj.method();//子类方法
        obj.methodFu();//父类特有方法
    }
}

多态中成员变量的使用特点

访问成员变量的两种方式:

  1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找
  2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找
public class Fu /*extends Object*/{
    int num = 10;
    public void showNum(){
        System.out.println(num);
    }
}

public class Zi extends Fu{
    int num = 20;
    int age = 16;
}

public class Demo{
    public static void main(String[] args){
        //使用多态的写法,父类引用指向子类对象
        Fu obj = new Zi();
        System.out.println(obj.num);//10
        //父类当中没有age变量会向上找,而Fu类的父类是Object,Object当中没有age变量,所以错误
        //System.out.println(obj.age);//错误写法
        
        //子类没有覆盖重写,就是父类方法,结果为10
        //如果子类覆盖重写了showNum()方法,则运行子类方法,结果为20
        obj.showNum();
    }
} 

多态中成员方法的使用特点

在多态的代码中,成员方法的访问特点是:

new 的是谁,就优先用谁,没有则向上找

口诀:编译看左边,运行看右边

对比:

  1. 成员变量:编译看左边,运行还看左边
  2. 成员方法:编译看左边,运行看右边
public class Fu{
    int num = 10;
    public void showNum(){
        System.out.println(num);
    }
    public void method(){
        System.out.println("父类方法");
    }
    public void methodFu(){
        System.out.println("父类特有方法");
    }
}

public class Zi extends{
    int num = 20;
    int age = 16;
    @Override
    public void showNum(){
        System.out.println(num);
    }
    @Override
    public void method(){
        System.out.println("子类方法");
    }
    public void methodZi(){
        System.out.println("子类特有方法");
    }
}

public class Demo{
    public static void main(String[] args){
        Fu obj = new Zi();//多态
        //编译时看左边:左边是Fu类里面有method()方法编译通过,运行看右边:右边是Zi类,Zi类中有method()方法
        obj.method();//父子类都有,优先用子,因为new的是子类
        //编译时看左边:左边是Fu类里面有methodFu()方法编译通过,运行看右边:右边Zi类中没有,向上找Fu类,Fu类当中有methodFu()方法
        obj.methodFu();//子类没有,父类有,向上找到父类
        
        //编译看左边,左边是Fu,Fu类中没有methodZi()方法,所以编译出错
        //obj.methodZi();//编译出错
    }
}

使用多态的好处

无论右边new的对象到时候换成那个子类对象,等号左边调用方法都不会变化
在这里插入图片描述

对象的向上转型

对象的向上转型,其实就是多态的写法,就是父类引用指向子类对象

格式:

父类名称 对象名 = new 子类名称();

含义:右侧创建一个子类对象,把它当作父类来看待使用

注意:向上转型一定是安全的,从小范围转向了大范围

//创建了一只猫,当作动物看待是没问题的,从小范围的猫,向上转换成大范围的动物
Animal animal = new Cat();

对象的向下转型

向上转型一定是安全的,没有问题的,正确的,但是有一个弊端

对象一旦向上转型为父类,那么就无法调用子类原本特有的内容

解决方案:用对象的向下转型(还原)

格式:

子类名称 对象名 = new (子类名称)父类对象;

含义:将父类对象还原成为本来的子类对象

Animal animal = new Cat();//本来是猫,向上转型成为动物
Cat cat = (Cat)animal;//本来是猫,已经被当作动物了,还原回来成为原本的猫

注意:

  1. 必须保证对象本来创建的时候就是猫,才能向下转型成为猫
  2. 如果对象创建的时候本来不是猫,现在向下转型成为猫,就会报错(ClassCastExcetion类转换异常
public abstract class Animal{
    public abstract void eat();
}

public class Cat extends Animal{
    @Override
    public void eat(){
        System.out.println("猫吃鱼");
    }
    //子类特有方法
    public void catchMouse(){
        System.out.ptintln("猫抓老鼠");
    }
}

public class Dog extends Animal{
    @Override
    public void eat(){
        System.out.pritnln("狗吃骨头");
    }
    public void watchHouse(){
        System.out.pritnln("狗看家");
    }
}

public class Demp{
    public static void main(String[] args){
        //对象的向上转型
        Animal animal = new Cat();
        animal.eat();//猫吃鱼
        //对象一旦向上转型为父类,那么就无法调用子类原本特有的内容
        animal.catchMouse();//错误写法
        //对象的向下转型
        Cat cat = (Cat)animal;
        cat.catchMouse();//猫抓老鼠
        //错误的向下转型
        //本来new的时候是一直猫,现在非要当作狗
        Dog dog = (Dog)animal;//错误写法,编译不会报错,运行出错 ClassCastExcetion类转换异常
    }
}

Instanceof关键字的使用

如何才能知道一个父类引用的对象,本来是什么子类?

格式:

对象 instanceof 类名称

这将会得到一个boolean值得结果,也就是判断前面得对象能不能当作后面类型得实例

public class Demo{
    public static void main(String[] args){
        Animal animal = new Cat();//本来是一个猫
        animal.eat();//猫吃鱼
        //如果希望调用子类得特有方法,需要向下转型
        //判断一下父类引用animal本来是不是Dog
        if(animal instanceof Dog){
            Dog dog = (Dog)animal;
            dog.watchHouse();//狗看家
        }
        //判断一下animal本来是不是Cat
        if(animal instanceof Cat){
            Cat cat = (Cat)animal;
            cat.catchMouse();//猫抓老鼠
        }
        giveMeAPet(new Dog());
    }
    //传入的都是动物,通过instanceof判断到底是狗还是猫
    public static void giveMeAPet(Animal animal){
        if(animal instanceof Dog){
            Dog dog = (Dog)animal;
            dog.watchHouse();//狗看家
        }
        if(animal instanceof Cat){
            Cat cat = (Cat)animal;
            cat.catchMouse();//猫抓老鼠
        }
    }
}

向下转型使用 instanceof 进行判断避免出现类转换异常

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

掉发阿龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值