houjiheng-java-day06

抽象类

  • 概述: 使用abstract关键字修饰的类就是抽象类
  • 特点: 这种类不能被创建对象,它就是用来做父类的,被子类继承的
    格式
修饰符 abstract class 类名{
    
}

抽象类中的成员

  • 成员变量
  • 成员方法
  • 构造方法
  • 抽象方法

抽象方法: 没有方法体,使用abstract修饰的方法就是抽象方法

修饰符 abstract 返回值类型 方法名(形参列表);
例如:
	public abstract void work();

抽象方法的作用: 强制要求子类重写的

  • 抽象方法: 没有方法体,使用abstract修饰的方法就是抽象方法
  • 抽象方法定义格式: 修饰符 abstract 返回值类型 方法名(形参列表);
  • 使用场景:如果父类中某个方法,所有子类都有不同的实现,那么就可以把该方法定义为抽象方法
  • 抽象方法的作用: 强制要求子类重写
package com.company.day06.chouxianglei;
// 抽象类
public 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();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }


}
public class Dog extends Animal{

    @Override
    public void eat() {
        System.out.println("狗狗吃肉");
    }
}
public class Cat extends Animal {

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

public class Test {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.eat();
        Dog dog = new Dog();
        dog.eat();
    }
}

抽象类的注意事项

  • 抽象类不能被创建对象,就是用来做“父类”,被子类继承的。
  • 抽象类不能被创建对象,但可以有“构造方法”——为成员变量初始化。
  • 抽象类中可以没有抽象方法,但抽象方法必须定义在抽象类中
  • 子类继承抽象类后,必须重写抽象类中所有的抽象方法,否则子类必须也是一个抽象类

匿名类

 new Animal() {
            @Override
            public void eat() {
                System.out.println("匿名动物吃");
            }
        }.eat();

模板设计模式

设计模式就是解决一些问题时的固定思路,也就是代码设计思路经验的总结。
针对某些情况,在父类中指定一个模板,然后根据具体情况,在子类中灵活的具体实现该模板

public abstract class Person{
    // 有方法体的方法: 通用模板
    public void sleep(){
        System.out.println("两眼一闭,就睡觉...");
    }
    
    // 没有方法体的方法(抽象方法):  填充模板(要子类重新实现的)
   public abstract void eat();
}

抽象类体现的就是模板设计思想,模板是将通用的东西在抽象类中具体的实现,而模板中不能决定的东西定义成抽象方法,让使用模板(继承抽象类的类)的类去重写抽象方法实现需求

模板模式的实现步骤

  • 定义抽象父类作为模板
  • 在父类中定义"模板方法"— 实现方法(通用模板)+抽象方法(填充模板)
  • 子类继承父类,重写抽象方法(填充父类的模板)
  • 测试类: -创建子类对象,通过子类调用父类的“实现的方法”+ “子类重写后的方法”

案例

假如我现在需要定义新司机和老司机类,新司机和老司机都有开车功能,开车的步骤都一样,只是驾驶时的姿势有点不同,新司机:开门,点火,双手紧握方向盘,刹车,熄火,老司机:开门,点火,右手握方向盘左手抽烟,刹车,熄火。那么这个时候我们就可以将固定流程写到父类中,不同的地方就定义成抽象方法,让不同的子类去重写

分析:

  • 司机类
    • 开车方法: 确定实现–通用模板
      • 开门
      • 点火
      • (姿势)
      • 刹车
      • 熄火
    • 姿势方法: 不确定实现–填充模板
  • 新司机类继承司机类,重写姿势方法
  • 老司机类继承司机类,重写姿势方法

public abstract class Driver {
    // 开车方法 通用模板
    public void driveCar(){
        System.out.println("开门");
        System.out.println("点火");
        // 姿势??
        ziShi();
        System.out.println("刹车");
        System.out.println("熄火");
    }

    // 姿势方法  填充模板
    public abstract void ziShi();
}
public class NewDriver extends Driver{

    @Override
    public void ziShi() {
        System.out.println("双手紧握方向盘");
    }
}
public class OldDriver extends Driver{


    @Override
    public void ziShi() {
        System.out.println("右手握方向盘左手抽烟");
    }
}

public class Test2 {
    public static void main(String[] args) {
        NewDriver newDriver = new NewDriver();
        newDriver.driveCar();

        OldDriver oldDriver = new OldDriver();
        oldDriver.driveCar();
    }
}

final关键字

final关键字的概述

final: 不可改变。可以用于修饰类、方法和变量。

  • 类:被修饰的类,不能被继承 比如String。
  • 方法:被修饰的方法,不能被重写。
  • 变量:被修饰的变量,就只能赋值一次,不能被重新赋值。

修饰类

修饰符 final class 类名 {
  
}
例如:
public final class FinalClassFu {
}
public class FinalClassZi /*extends FinalClassFu*/ {
    // FinalClassFu类被final修饰了,所以不能被继承
}

查询API发现像 public final class String 、public final class Math 、public final class Scanner 等,很多我们学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。

修饰方法

修饰符 final 返回值类型 方法名(参数列表){
    //方法体
}

重写被 final修饰的方法,编译时就会报错。

public class FinalMethodFu {
    public final void show(){

    }
}
public class FinalMethodZi extends FinalMethodFu {

    /*@Override
    public void show() {

    }*/
    // 无法重写父类中的show方法,因为父类中的show方法被final修饰了
}

修饰变量

局部变量——基本类型

基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。代码如下:

   final int NUM = 10;
        NUM = 220; // 报错

局部变量——引用类型

引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改,代码如下:

public class Test3 {
    public static void main(String[] args) {
        final Student student1 = new Student("eric", 11);
        System.out.println(student1);
//        student1 = new Student("bob", 20);  不能有新的指向
        student1.setAge(30);
        System.out.println(student1.getAge());
        System.out.println(student1);
    }
}

成员变量涉及到初始化的问题,初始化方式有两种,只能二选一:

public class FinalVariable {
    final int NUM1 = 10;
}
public class FinalVariable {
    final int NUM2;
    public FinalVariable(int NUM2){
        this.NUM2 = NUM2;
    }
    public FinalVariable(){
        this.NUM2 = 10;
    }
}

被final修饰的常量名称,一般都有书写规范,所有字母都大写

  • final修饰类,类不能被继承。
  • final修饰方法,方法不能被重写。
  • final修饰变量,变量不能被改值,只能赋值一次.

static关键字

static是一个静态修饰符关键字,表示静态的意思,可以修饰成员变量和成员方法以及代码块。

static修饰成员变量

当 static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作

static 数据类型 变量名;
// 调用
对象名.静态成员变量名; 不推荐
类名.静态成员变量名;  推荐 

public class Person {
    String name;
    static int id;

    public Person(String name) {
        this.name = name;
        ++id;
    }

    public static void main(String[] args) {
        System.out.println(Person.id);
        Person p1 = new Person("张三");
        Person p2 = new Person(";李四");
        Person p3 = new Person(";李四");
        System.out.println(Person.id); //3

        System.out.println(p3.id);

    }
}

静成员会随着类的加载而加载, 并且只加载一次, 会加载到静态区

static修饰成员方法

被static修饰的方法会变成静态方法,也称为类方法,该静态方法可以使用类名直接调用。


修饰符 static 返回值类型 方法名 (参数列表){ 
	// 执行语句 
}
访问
对象名.方法名(实参);
类名.方法名(实参);   推荐
package com.company.day06.chouxianglei;


import java.util.Arrays;

public class Person {
    String name;
    static int id;

    public Person(String name) {
        this.name = name;
        ++id;
    }
    public static void m1(){
        System.out.println("m1()");
    }
    public static void main(String[] args) {
        Person p1 = new Person("张三");

        p1.m1();
        Person.m1();
        int arr[] = {111, 223, 444};
        System.out.println(Arrays.toString(arr));

    }
}

静态方法调用的注意事项:

  • 静态方法中不能出现this关键字
  • 静态方法中只能直接访问静态成员变量和静态成员方法
  • 静态方法中不能直接访问非静态成员变量和非静态成员方法
  • 非静态方法中可以直接访问一切成员变量和成员方法

public class Person {
    String name;
    static int id;

    public Person(String name) {
        this.name = name;
        ++id;
    }
    public  void m2(){
        this.name = "haha";
        id = 100;
    }
    public static void m1(){
//        this.name 错
//        name = "" 错
        id = 100;
        
    }

以后开发中static的应用

概述

以后的项目中,通常会需要一些“全局变量”或者“全局的工具方法”,这些全局变量和方法,可以单独定义在一个类中,并声明为static(静态)的,可以很方便的通过类名访问

package com.company.day06.utils;

public class Utils {
    
    // 静态代码块, 优先于main方法执行
    static {
        AGE = 10;
    }
    public static int AGE;

    // "全局变量"
    public static final int WIDTH = 800;
    public static final int HEIGHT = 800;


    // "全局方法"
    // 找int数组中的最大值
    public static int getArrayMax(int[] arr){
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] > max){
                max = arr[i];
            }
        }
        return max;
    }
}
import com.company.day06.utils.Utils;

public class Test4 {
    public static void main(String[] args) {
        System.out.println(Utils.WIDTH);
        System.out.println(Utils.HEIGHT);
        int[] arr = {122, 334,444};
        System.out.println(Utils.getArrayMax(arr));
        System.out.println(Utils.AGE);
    }
}

接口

  • 引用数据类型除了类其实还有接口,接下来学习接口的概述

概述: 接口是Java语言中的一种引用类型,是方法的"集合",所以接口的内部主要就是定义方法,包含常量,抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(jdk9)。

接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。

public class 类名{}–>.class

public interface 接口名{}->.class

引用数据类型:数组,类,接口

接口的使用,它不能创建对象,但是可以被实现(implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类
格式

public interface 接口名称 {
    // 常量(jdk7及其以前)
    // 抽象方法(jdk7及其以前)
    // 默认方法(jdk8)
    // 静态方法(jdk8)
    // 私有方法(jdk9)
}
package com.company.day06.jiekou;

public interface IA {
    // 常量(jdk7及其以前) 使用public static final关键字修饰,这三个关键字都可以省略
    public static final int NUM = 10;
    // 抽象方法(jdk7及其以前) 使用public abstract关键字修饰,这2个关键字都可以省略
    public abstract void method1();
    void  method2();
    // 默认方法(jdk8) 使用public default关键字修饰,public可以省略,default不可以省略
    public default void method3(){
        System.out.println("默认方法");
    }
    // 静态方法(jdk8) 使用public static关键字修饰,public可以省略,static不可以省略
    public static void method4(){
        System.out.println("静态方法");

    }

    // 私有方法(jdk9) 使用private关键字修饰,private不可以省略
    private void method5(){
        System.out.println("method5");
    }
    private static  void method6(){
        System.out.println("method6");
    }
}

实现接口

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。

public class ZI implements IA, IB{
    @Override
    public void method1() {
        
    }

    @Override
    public void method2() {

    }

    @Override
    public void show() {

    }
}

接口中成员的访问特点:

  • 接口中的常量: 主要是供接口直接使用
  • 接口中的抽象方法: 供实现类重写的
  • 接口中的默认方法: 供实现类继承的(实现类中可以直接调用,实现类对象也可以直接调用)
  • 接口中的静态方法: 只供接口直接调用,实现类继承不了
  • 接口中的私有方法: 只能在接口中直接调用,实现类继承不了

多实现时的几种冲突情况

  • 公有静态常量的冲突
  • 公有抽象方法的冲突
  • 公有默认方法的冲突
  • 公有静态方法的冲突
  • 私有方法的冲突

公有静态常量的冲突

public interface A {
    public static final int NUM1 = 10;
}

public interface B {
    public static final int NUM1 = 20;
    public static final int NUM2 = 30;

}
public class Impl implements A, B{
    public static void main(String[] args) {
//        公有静态常量的冲突: 如果多个接口中有相同的常量,那么实现类就无法继承
//        System.out.println(Impl.NUM1); // 报错
        System.out.println(Impl.NUM2);
    }
}

公有抽象方法的冲突

实现类只需要重写一个

public interface A {
    public static final int NUM1 = 10;

    public abstract void method();
}
public interface B {
    public static final int NUM1 = 20;
    public static final int NUM2 = 30;
    public abstract void method();

}
public class Impl implements A, B{
    public static void main(String[] args) {
//        公有静态常量的冲突: 如果多个接口中有相同的常量,那么实现类就无法继承
//        System.out.println(Impl.NUM1); // 报错
        System.out.println(Impl.NUM2);
    }

    @Override
    public void method() {
        System.out.println("重写");
    }
}

公有默认方法的冲突

  • 实现类必须重写一次最终版本

public interface A {
    public static final int NUM1 = 10;

    public abstract void method();

    public default void method2(){
        System.out.println("A 接口的默认方法method2");
    }
}

public interface B {
    public static final int NUM1 = 20;
    public static final int NUM2 = 30;
    public abstract void method();
    public default void method2(){
        System.out.println("B 接口的默认方法method2");
    }
}
public class Impl implements A, B{

    @Override
    public void method() {
        System.out.println("重写");
    }

    @Override
    public void method2() {
        B.super.method2();
    }

    public static void main(String[] args) {
        Impl impl = new Impl();
        impl.method2();
    }

}

公有静态方法的冲突:
静态方法是直接属于接口的,不能被继承,所以不存在冲突
私有方法的冲突

  • 私有方法只能在本接口中直接使用,不存在冲突

抽象类和接口的练习

通过实例进行分析和代码演示抽象类和接口的用法。

1、举例:

犬: —抽象父类

行为:吼叫;吃饭;

缉毒犬:继承犬类,实现缉毒接口

行为:吼叫;吃饭;缉毒;

缉毒接口:

缉毒

  • 如果一个父类中的某个方法,所有子类都有不同的实现,那么该方法就应该定义成抽象方法,所以该父类就是抽象类 (父类一般都是抽象类)
  • 如果某个功能是一个类额外增加的,那么就可以把这个额外的功能定义到接口中,再这个类去实现

分析:

由于犬分为很多种类,他们吼叫和吃饭的方式不一样,在描述的时候不能具体化,也就是吼叫和吃饭的行为不能明确。当描述行为时,行为的具体动作不能明确,这时,可以将这个行为写为抽象行为,那么这个类也就是抽象类。

可是有的犬还有其他额外功能,而这个功能并不在这个事物的体系中 , 例如 : 缉毒犬。缉毒的这个功能有好多种动物都有 , 例如 : 缉毒猪 , 缉毒鼠。我们可以将这个额外功能定义接口中 ,让缉毒犬继承犬且实现缉毒接口 , 这样缉毒犬既具备犬科自身特点也有缉毒功能。

  • 额外的功能—> 在接口中定义,让实现类实现
  • 共性的功能—> 在父类中定义,让子类继承
public interface JiDu {
    public abstract void jiDu();
}

public abstract class Dog {
    public abstract void houJiao();
    public abstract void eat();
}


public class JiDuDog extends Dog implements JiDu{
    @Override
    public void houJiao() {
        System.out.println("缉毒犬找到了毒品,开始吼叫,汪汪汪....");
    }

    @Override
    public void eat() {
        System.out.println("缉毒之前,开始吃骨头...");
    }

    @Override
    public void jiDu() {
        System.out.println("吃完东西后,开始使用鼻子查找毒品....");
    }
}
public class Test {
    public static void main(String[] args) {
        JiDuDog jiDuDog = new JiDuDog();
        jiDuDog.eat();
        jiDuDog.jiDu();
        jiDuDog.houJiao();

    }
}

上一篇:Java语言学习day06-7月05日


下一篇:RHCAS_DAY06