JavaSE-04笔记【面向对象01】

文章目录

  • 1. final 关键字
    • 1.1 采用final修饰的类不能被继承
    • 1.2 采用 final 修饰的方法不能被覆盖
    • 1.3 采用 final 修饰的变量(基本类型)不能被修改
    • 1.4 采用final 修饰的变量必须显示初始化
    • 1.5 如果修饰的引用,那么这个引用只能指向一个对象,也就是说这个引用不能再次赋值,但被指向的对象是可以修改的
    • 1.6 如果final修饰实例变量,则系统不再会给该实例变量赋默认值
  • 2. 抽象类(也是引用数据类型)
    • 2.1 一道面试题
    • 2.2 抽象类不能实例化
    • 2.3 抽象的方法只需在抽象类中,提供声明,不需要实现,起到了一个强制的约束作用,要求子类必须实现
    • 2.4 如果这个类是抽象的,那么这个类被子类继承,抽象方法必须被覆盖。如果在子类中不覆盖该抽象方法,那么必须将此方法再次声明为抽象方法
    • 2.5 抽象类不能被 final 修饰
    • 2.6 抽象方法不能被 final 修饰
    • 2.7 抽象类中可以没有抽象方法
    • 2.8 抽象类虽无法实例化,但是可以有构造方法,供子类使用
  • 3. 接口(行为)
    • 3.1 接口中的方法默认都是 public abstract 的,不能更改
    • 3.2 接口中的变量是 public static final 类型的,不能更改,所以必须显式地初始化
    • 3.3 接口不能被实例化,接口中没有构造方法的概念
    • 3.4 接口之间可以继承,但接口之间不能实现
    • 3.5 如果一个类实现了接口,那么接口中所有的方法必须实现
    • 3.6 一类可以实现多个接口
    • 3.7 接口是一种特殊的抽象类,可以使用多态
    • 3.8 接口与接口之间进行强制类型转换时,没有继承关系,也可以强转,但是在运行时可能会出现`ClassCastException`异常
    • 3.9 一个类可以同时继承抽象类和实现接口
  • 4. 接口的进一步应用
  • 5. 抽象类与接口的区别

1. final 关键字

final 表示不可改变的含义

  • 采用 final 修饰的类不能被继承(String类就被final修饰了,其不可以被继承!!!);
  • 采用 final 修饰的方法不能被覆盖;
  • 采用 final 修饰的变量不能被修改,只能赋一次值;
  • final 修饰的变量必须显示初始化;
  • 如果修饰的引用,那么这个引用只能指向一个对象(该引用所保存的地址不不能改变,也不能重新赋值为null),也就是说这个引用不能再次赋值【因为引用也是一个变量!】,但被指向的对象是可以修改的;
  • 构造方法不能被 final 修饰;
  • 会影响 JAVA类的初始化:final 定义的静态常量调用时不会执行 java 的类初始化方法,也就是说不会执行 static 代码块等相关语句,这是由 java 虚拟机规定的。我们不需要了解的很深,有个概念就可以了。 【针对这句话可以查看博客:调用final static变量是否会触发类初始化(执行静态代码块)】

1.1 采用final修饰的类不能被继承

如下代码:

public class FinalTest01 {
    public static void main(String[] args) {
    }
}

final class A{
    public void test01(){
    }
}

//不能继承 A,因为 A 采用 final 修饰了
class B extends A{
    public void test01(){
    }
}

编译报错:
在这里插入图片描述

1.2 采用 final 修饰的方法不能被覆盖

如下代码:

public class FinalTest02 {
    public static void main(String[] args) {
    }
}

class A1{
    //采用final修饰的方法
    public final void test01(){ 
    }
}

class B1 extends A1{
	//覆盖父类的方法,改变其行为
	//因为父类的方法是 final 修饰的,所以不能覆盖
    public void test01(){
    }
}

编译报错:
在这里插入图片描述

1.3 采用 final 修饰的变量(基本类型)不能被修改

如下代码:

public class FinalTest03 {
    private final long id = 24353465456575677L;
    public static void main(String[] args) {
        id = 454657686787987978L;
    }
}

编译报错:
在这里插入图片描述

1.4 采用final 修饰的变量必须显示初始化

如下代码:

public class FinalTest04 {
    // 如果是 final 修饰的变量必须初始化
    private final long id;
    public static void main(String[] args) {
        int i;
        // 局部变量如果要使用也必须初始化
        System.out.println(i);
    }
}

编译报错:
在这里插入图片描述
在这里插入图片描述

1.5 如果修饰的引用,那么这个引用只能指向一个对象,也就是说这个引用不能再次赋值,但被指向的对象是可以修改的

如下代码;

public class FinalTest05 {
    public static void main(String[] args) {
        Person p1 = new Person();
        //可以赋值
        p1.name = "张三";
        System.out.println(p1.name);
        final Person p2 = new Person();
        p2.name = "李四";
        System.out.println(p2.name);
        //不能编译通过
        //p2 采用 final 修饰,主要限制了 p2 指向堆区中的地址不能修改(也就是p2 只能指向一个对象)
        //p2 指向的对象的属性是可以修改的
        p2 = new Person();
        p2.name = "王五";
    }
}

class Person {
    String name;
}

编译报错:
在这里插入图片描述

1.6 如果final修饰实例变量,则系统不再会给该实例变量赋默认值

如果final修饰实例变量,则系统不再会给该实例变量赋默认值【因为这样的话,该实例变量就无法再被赋值】,此时,必须由程序员手动赋值!!!
如下代码:

class Person {
   final String name;
}

编译报错:
在这里插入图片描述
手动赋值要么在声明后直接=,要么在构造方法中赋值!

  1. 在声明后直接=
class Person {
   final String name="Wang"; //可以,不报错
}
  1. 在构造方法中赋值
class Person {
   final String name;

    public Person(){
       this.name = "Wang"; //也可以实际上与声明时直接=是等价的,上面也是在构造方法执
                           //行时才执行,声明时只是开辟空间,没有立刻赋值
   }
}

一个小回顾:实例变量在什么时候进行赋默认值(初始化)?
                      =>在构造方法执行过程中(new 的时候赋值!)

注意:一般final会与static联合使用,这样的变量为常量【其一般也是public的】。

2. 抽象类(也是引用数据类型)

看以前示例中的 Animal、Cat 和 Dog,从我们使用的角度来看要对 Cat 和 Dog 进行实例化,Person 中主要包含了一些公共的属性和方法,而 Animal 我们通常不会实例化,所以我们可以把它定义成抽象的:

  • 在 java 中采用 abstract 关键字定义的类就是抽象类,采用 abstract 关键字定义的方法就是抽象方法;
  • 抽象的方法只需在抽象类中,提供声明,不需要实现;
  • 如果一个类中含有抽象方法,那么这个类必须定义成抽象类;
  • 如果这个类是抽象的,那么如果这个类被子类继承,抽象方法必须被重写。如果在子类中不复写该抽象方法,那么必须将此类再次声明为抽象类【抽象类的子类也可以是抽象类 】;
  • 抽象的类是不能实例化的,就像现实世界中人其实是抽象的,张三、李四才是具体的;
  • 抽象类不能被 final 修饰;
  • 抽象方法不能被 final 修饰,因为抽象方法就是被子类实现的;
  • 抽象类中并不要求一定具有抽象方法;
  • 抽象类中可以定义一些普通方法和属性,将一些公共的代码放到抽象类中,另外在抽象类中可以定义一些抽象的方法,这样就会存在一个约束,而子类必须实现我们定义的方法,如:Cat 必须实现 move方法,Dog也必须实现 move方法,方法名称不能修改,必须为 move,这样就能实现多态的机制,有了多态的机制,我们在运行期就可以动态的调用子类的方法。所以在运行期可以灵活的互换实现。

图形的例子:
在这里插入图片描述

2.1 一道面试题

在java中凡是没有方法体的方法都是抽象方法吗?

=>不对,Object类中就有很多方法没有方法体,都是以’;'结尾的,但是他们都不是抽象方法,例如public native int hashcode();这个方法就没有用abstract修饰,其底层调用的是C++的动态链接库,其中native表示调用JVM本地程序。

2.2 抽象类不能实例化

public class AbstractTest01 {
    public static void main(String[] args) {
        //不能实例化抽象类
        //抽象类是不存在,抽象类必须有子类继承
//        Person p = new Person();
        //以下使用是正确的,因为我们 new的是具体类
        Person p1 = new Employee();
        p1.setName("张三");
        System.out.println(p1.getName());
    }
}
//采用 abstract 定义抽象类
//在抽象类中可以定义一些子类公共的方法或属性
//这样子类就可以直接继承下来使用了,而不需要每个
//子类重复定义
abstract class Person {
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    //此方法各个子类都可以使用
    public void commonMethod1() {
        System.out.println("---------commonMethod1-------");
    }
}
class Employee extends Person {}
class Student extends Person {}

2.3 抽象的方法只需在抽象类中,提供声明,不需要实现,起到了一个强制的约束作用,要求子类必须实现

public class AbstractTest02 {
    public static void main(String[] args) {
        Person01 teacher = new Teacher();
        teacher.setName("Li");
        System.out.println(teacher.getName());
        teacher.commonMethod();
        teacher.abstractMethod();
    }
}
abstract class Person01{
    private String name;

    public String getName() {
        return name;
    }

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

    // 此方法各个子类都可以使用
    public void commonMethod(){
        System.out.println("----commonMethod----");
    }

    //抽象方法,子类必须实现
    //如果有一个方法为抽象的,那么此类必须为抽象的
    public abstract void abstractMethod();
}

class Teacher extends Person01 {

    @Override
    public void abstractMethod() {
        System.out.println("对Person01抽象方法的实现!");
    }
}

运行结果:
在这里插入图片描述

2.4 如果这个类是抽象的,那么这个类被子类继承,抽象方法必须被覆盖。如果在子类中不覆盖该抽象方法,那么必须将此方法再次声明为抽象方法

public class AbstractTest03 {
    public static void main(String[] args) {
        // 此时Doctor类也是抽象的,不能再new
//        Person doctor = new Doctor();
    }
}
abstract class Person{
    private String name;

    public String getName() {
        return name;
    }

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

    // 此方法各个子类都可以使用
    public void commonMethod(){
        System.out.println("----commonMethod----");
    }

    //抽象方法,子类必须实现
    //如果有一个方法为抽象的,那么此类必须为抽象的
    public abstract void abstractMethod();
}

abstract class Doctor extends Person {
    //再次声明该方法为抽象的
    public abstract void abstractMethod();
}

2.5 抽象类不能被 final 修饰

如下代码;

final abstract class Person{
    private String name;

    public String getName() {
        return name;
    }

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

    // 此方法各个子类都可以使用
    public void commonMethod(){
        System.out.println("----commonMethod----");
    }

    //抽象方法,子类必须实现
    //如果有一个方法为抽象的,那么此类必须为抽象的
    public abstract void abstractMethod();
}

编译报错:
在这里插入图片描述

2.6 抽象方法不能被 final 修饰

在这里插入图片描述

2.7 抽象类中可以没有抽象方法

abstract class Animal{
    //抽象类可以没有抽象方法,其依旧是抽象类
    
    // 普通方法,此方法各个子类都可以使用
    public void commonMethod(){
        System.out.println("----commonMethod----");
    }
}

2.8 抽象类虽无法实例化,但是可以有构造方法,供子类使用

public class AbstractTest04 {
    public static void main(String[] args) {
        Book paperBook = new PaperBook("解忧杂货店", 486);
        System.out.println(paperBook.getName());
        System.out.println(((PaperBook) paperBook).getPageNum());
    }
}

//抽象书类
abstract class Book{
    private String name; //书名

    public String getName() {
        return name;
    }

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

    //抽象类可以定义构造方法
    public Book(String name) {
        this.name = name;
    }
}

//纸质书类
class PaperBook extends Book{
    private int pageNum; //页数

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public PaperBook(String name, int pageNum) {
        super(name);
        this.pageNum = pageNum;
    }
}

3. 接口(行为)

接口我们可以看作是抽象类的一种特殊情况,在接口中只能定义抽象的方法和常量

  1. 在 java 中接口采用 interface 声明;
  2. 接口中的方法默认都是 public abstract 的,不能更改;
  3. 接口中的变量默认都是 public static final 类型的,不能更改,所以必须显式地初始化;
  4. 接口不能被实例化,接口中没有构造方法的概念,且子类实现的方法也必须是public修饰的
  5. 接口之间可以继承,但接口之间不能实现
  6. 接口中的方法只能通过类来实现,使用implements关键字;
  7. 如果一个类实现了接口,那么接口中所有的方法必须实现;
  8. 一个类可以实现多个接口;
  9. 接口编译后也是生成.class文件;
  10. 抽象类可以实现接口:abstract class 抽象类类名 implements 接口名{}
  11. 把接口就看成⼀种特殊的抽象类,那它也可以使用多态;
  12. 经过测试:接口与接口之间进行强制类型转换时,没有继承关系,也可以强转,但是在运行时可能会出现ClassCastException异常;
  13. 接口的父类也是Object

3.1 接口中的方法默认都是 public abstract 的,不能更改

如下代码:

public class InterfaceTest01 {
    public static void main(String[] args) {

    }
}

//采用 interface 定义接口
//定义功能,没有实现
//实现委托给类实现
interface StudentManager {
    //正确,默认为 public abstract 等同 public abstract void addStudent(int id, String name);
    void addStudent(int id, String name);

    //正确,可以加入 public 修饰符,此种写法较多
    public void delStudent(int id);

    //正确,可以加入 abstract,这种写法比较少
    public abstract void modifyStudent(int id, String name);

    //编译错误,因为接口就是让其他人实现
    //采用 private 就和接口原本的定义产生矛盾了
    private String findStudentById(int id);
}

编译报错:
在这里插入图片描述

3.2 接口中的变量是 public static final 类型的,不能更改,所以必须显式地初始化

如下代码:

public class InterfaceTest02 {
    public static void main(String[] args) {
        //不能修改,因为是 final 的
        StudentManager02.YES = "YES";
        System.out.println(StudentManager02.NO);
    }
}
interface StudentManager02{
    //正确,默认加入 public static final
    String YES = "yes";

    //正确, 开发中一般就按照下面的方式进行声明
    public static final String NO = "no";

    //错误,必须赋值,因为是 final 的
    int ON;
    //错误,不能采用 private 声明
    private static final int OFF = -1;
}

编译报错:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3 接口不能被实例化,接口中没有构造方法的概念

如下代码;

public class InterfaceTest03 {
    public static void main(String[] args) {
        //接口是抽象类的一种特例,只能定义方法和变量,没有实现,所以不能实例化
        StudentManager03 studentManager03 = new StudentManager03();
    }
}
interface StudentManager03{
    void addStudent(int id,String name);
}

编译报错:
在这里插入图片描述

3.4 接口之间可以继承,但接口之间不能实现

如下代码:

public class InterfaceTest04 {
    public static void main(String[] args) {
        
    }
}

interface inter1{
    public void method1();
    public void method2();
}
interface inter2{
    public void method3();
}

//接口可以继承
interface inter3 extends inter1{
    public void method4();
}

//接口不能实现接口
//接口只能被类实现
interface inter4 implements inter2{

}

编译报错;
在这里插入图片描述

3.5 如果一个类实现了接口,那么接口中所有的方法必须实现

如下代码:

public class InterfaceTest05 {
    public static void main(String[] args) {

        //Iter1Impl 实现了 Inter1 接口
        //所以它是 Inter1 类型的产品
        //所以可以赋值
        Inter1 inter1 = new Inter1Impl();
        inter1.method1();

        //Iter1Impl123 实现了 Inter1 接口
        //所以它是 Inter1 类型的产品
        //所以可以赋值
        Inter1 inter123 = new Iter1Impl123();
        inter123.method2();

        //可以直接采用 Iter1Impl 来声明类型
        //这种方式存在问题
        //不利于互换,因为面向具体编程了
        Inter1Impl interImpl = new Inter1Impl();
        interImpl.method1();

        //不能直接赋值给 iter1Impl
        //因为 Iter1Impl123 不是 Iter1Impl 类型
//        iterImpl = new Iter1Impl123();

    }
}

//定义接口
interface Inter1{
    public void method1();

    public void method2();

    public void method3();
}

//接口中的方法必须全部实现
class Inter1Impl implements Inter1 {

    @Override
    public void method1() {
        System.out.println("method1");
    }

    @Override
    public void method2() {
        System.out.println("method1");
    }

    @Override
    public void method3() {
        System.out.println("method1");
    }
}


class Iter1Impl123 implements Inter1 {

    @Override
    public void method1() {
        System.out.println("method1_123");
    }

    @Override
    public void method2() {
        System.out.println("method2_123");
    }

    @Override
    public void method3() {
        System.out.println("method3_123");
    }
}
abstract class IterImpl456 implements Inter1 {
    @Override
    public void method1() {
        System.out.println("method1_123");
    }

    @Override
    public void method2() {
        System.out.println("method2_123");
    }

    //再次声明为抽象方法
    abstract public void method3();
}

编译报错:
在这里插入图片描述
注释上述报错行后运行结果:
在这里插入图片描述

3.6 一类可以实现多个接口

public class InterfaceTest06 {
    public static void main(String[] args) {
        //可以采用 Inter1 定义
        Inter01 inter01 = new InterImpl01();
        inter01.method1();
        ((InterImpl01) inter01).method2(); //调用非实现Inter01接口的方法必须进行强转
        //可以采用 Inter2 定义
        Inter02 inter02 = new InterImpl01();
        inter02.method2();

        //可以采用 Inter3 定义
        Inter03 inter03 = new InterImpl01();
        inter03.method3();

    }
}

interface Inter01{
    public void method1();
}

interface Inter02{
    public void method2();
}

interface Inter03{
    public void method3();
}

//粗粒度
/*
interface inter{
    public void method1();
    public void method2();
    public void method3();
}
*/

//实现多个接口,采用逗号隔开
//这样这个类就拥有了多种类型
//等同于现实中的多继承
//所以采用 java 中的接口可以实现多继承
//把接口粒度划分细了,可以使功能定义的含义更明确
//可以采用一个大的接口定义所有功能,替代多个小的接口,
//但这样定义功能不明确,粒度太粗了
class InterImpl01 implements Inter01,Inter02,Inter03{

    @Override
    public void method1() {
        System.out.println("******method1******");
    }

    @Override
    public void method2() {
        System.out.println("******method2******");

    }

    @Override
    public void method3() {
        System.out.println("******method3******");

    }
}

运行结果:
在这里插入图片描述

3.7 接口是一种特殊的抽象类,可以使用多态

由上3.6即可以看出,也可看如下例子:

interface A{ 其中有方法m1 } interface B{ 其中有方法m2 }
情况1:若有类C:class C implementsA{}
若此时有:A a =new C(); B b = (B)a;//C与B没有关系,出现运行异常
情况2:若有类C:class C implements A,B{}
若此时有:A a =new C(); B b = (B)a;//为了调用m2(),进行接口转型,
可以运行 b.m2();//此时只能调用m2()方法,不能调用 m1()方法,要向下转型成C c = (C)a才可以。

对于以上不用太纠结,反正也没啥用,只要养成好习惯:向下转型前用if+instanceof判断即可。

情况1具体代码:

public class InterfaceTest07 {
    public static void main(String[] args) {
        A a = new C();
        a.m1();
        B b = (B)a; //C 和 B 没有关系,出现运行时异常
    }
}

interface A{
    public void m1();

}
interface B{
    public void m2();

}

class C implements A{

    @Override
    public void m1() {
        System.out.println("=========m1==========");

    }
}

运行结果:
在这里插入图片描述

情况2具体代码:

public class InterfaceTest07 {
    public static void main(String[] args) {
        A a = new C();
        a.m1();
        B b = (B)a; //为了调用m2()进行接口转型
        b.m2();

    }
}

interface A{
    public void m1();

}
interface B{
    public void m2();

}

class C implements A,B{

    @Override
    public void m1() {
        System.out.println("=========m1==========");

    }

    @Override
    public void m2() {
        System.out.println("=========m2==========");

    }
}

运行结果:

在这里插入图片描述

3.8 接口与接口之间进行强制类型转换时,没有继承关系,也可以强转,但是在运行时可能会出现ClassCastException异常

另外,类在强制转换过程中,如果父类 引用转换成接口类型,那么父类和接口之间不需要存在实 现关系,也可以转换,java语法时允许的。(前提父类引用中实际保存的子类引用所指对象有实现该接口!否则运行阶段还是会有类型转换异常)

3.9 一个类可以同时继承抽象类和实现接口

语法:class 类名 extends 抽象类名 implements 接口名{}
只能extends在前,不能class 类名 implements 接口名 extends 抽象类名()
如下代码:

public class InterfaceTest08 {
    public static void main(String[] args) {
        E e = new F();
        e.m2();
        ((F) e).m1(); //强转成子类型才可以调用m1

        D d = new F();
        d.m1();
        ((F) d).m2();  //强转成子类型才可以调用m2
    }
}

interface D{
    public void m1();
}

abstract class E{
    abstract public void m2();
}
class F extends E implements D {

    @Override
    public void m1() {
        System.out.println("----------m1----------");
    }

    @Override
    public void m2() {
        System.out.println("----------m2----------");

    }
}

运行结果:
在这里插入图片描述

4. 接口的进一步应用

在 java 中接口其实᧿述了类需要做的事情,类要遵循接口的定义来做事,使用接口的本质好处可以归纳为:

  • 采用接口明确的声明了它所能提供的服务;
  • 解决了 Java 单继承的问题;
  • 实现了可接插性(重要),可以使用接口解耦合。

接口解耦合是解开的是调用者和实现者之间的耦合,调用者面向接口调用, 实现者面向接口编写实现。这样在开发时,项目组可以为围绕接口开发,根据接口将项目分成不同的模块,同步进行,提高开发效率。

示例:完成学生信息的增删改操作,系统要求适用于多个数据库,如:适用于 Oracle 和 MySQL。

  • 第一种方案,不使用接口,每个数据库实现一个类:

UML,统一建模语言:
在这里插入图片描述
代码:

//Oracle 的实现
public class StudentOracleImpl {
	public void add(int id, String name) {
		System.out.println("StudentOracleImpl.add()");
	}
	public void del(int id) {
		System.out.println("StudentOracleImpl.del()");
	}
	public void modify(int id, String name) {
		System.out.println("StudentOracleImpl.modify()");
	}
}
Mysql 的实现
public class StudentMysqlImpl {
	public void addStudent(int id, String name) {
		System.out.println("StudentMysqlImpl.addStudent()");
	}
	public void deleteStudent(int id) {
		System.out.println("StudentMysqlImpl.deleteStudent()");
	}
	public void udpateStudent(int id, String name) {
		System.out.println("StudentMysqlImpl.udpateStudent()");
	}
}

调用以上两个类完成向 Oracle 数据库和 Mysql 数据存储数据:

public class StudentManager {
	public static void main(String[] args) {
	//对 Oracle 数据库的支持
	StudentOracleImpl studentOracleImpl = new StudentOracleImpl();
	studentOracleImpl.add(1, "张三");
	studentOracleImpl.del(1);
	studentOracleImpl.modify(1, "张三");
	
	//需要支持 Mysql 数据库
	StudentMysqlImpl studentMysqlImpl = new StudentMysqlImpl();
	studentMysqlImpl.addStudent(1, "张三");
	studentMysqlImpl.deleteStudent(1);
	studentMysqlImpl.udpateStudent(1, "张三");
	}
}

以上代码不能灵活的适应需求,当需求发生改变需要改动的代码量太大,这样可能会导致代码的冗余,另外可能会导致项目的失败,为什么会导致这个问题,在开发中没有考虑到程序的扩展性,就是一味的实现,这样做程序是不行的,所以大的项目比较追求程序扩展性,有了扩展性才可以更好的适应需求

  • 第二种方案,使用接口
    UML,统一建模语言:
    在这里插入图片描述

代码:

public class InterfaceTest09 {
    public static void main(String[] args) {
        /*
        IStudent istudent = new Student4OracleImpl();
        IStudent istudent = new Student4MysqlImpl();
        istudent.add(1, "张三");
        istudent.del(1);
        istudent.modify(1, "张三");
        */
        //进一步简化,创建doCrud()方法
        doCrud(new Student4OracleImpl());
        doCrud(new Student4MysqlImpl());
    }
    //此种写法没有依赖具体的实现
    //而只依赖的抽象,就像你的手机电池一样:你的手机只依赖电池(电池是一个抽象的事物),
    //而不依赖某个厂家的电池(某个厂家的电池就是具体的事物了)
    //因为你依赖了抽象的事物,每个抽象的事物都有不同的实现
    //这样你就可以利用多态的机制完成动态绑定,进行互换,是程序具有较高的灵活
    //我们尽量遵循面向接口(抽象)编程,而不要面向实现编程
    public static void doCrud(IStudent istudent) {
        istudent.add(1, "张三");
        istudent.del(1);
        istudent.modify(1, "张三");
    }
    //以下写法不具有扩展性
    //因为它依赖了具体的实现
    //建议不要采用此种方法,此种方案是面向实现编程,就依赖于具体的东西了
    /*
    public static void doCrud(Student4OracleImpl istudent) {
        istudent.add(1, "张三");
        istudent.del(1);
        istudent.modify(1, "张三");
    }
    */
}

interface IStudent{
    public void add(int id, String name);
    public void del(int id);
    public void modify(int id, String name);
}

class Student4OracleImpl implements IStudent {
    public void add(int id, String name) {
        System.out.println("Student4OracleImpl.add()");
    }
    public void del(int id) {
        System.out.println("Student4OracleImpl.del()");
    }
    public void modify(int id, String name) {
        System.out.println("Student4OracleImpl.modify()");
    }
}

class Student4MysqlImpl implements IStudent {
    public void add(int id, String name) {
        System.out.println("Student4MysqlImpl.add()");
    }
    public void del(int id) {
        System.out.println("Student4MysqlImpl.del()");
    }
    public void modify(int id, String name) {
        System.out.println("Student4MysqlImpl.modify()");
    }
}

运行结果:
在这里插入图片描述

5. 抽象类与接口的区别

  1. 接口描述述了方法的特征,不给出实现,一方面解决 java 的单继承问题,实现了强大的可接插性;
  2. 抽象类提供了部分实现,抽象类是不能实例化的,抽象类的存在主要是可以把公共的代码移植到抽象类中;
  3. 面向接口编程,而不要面向具体编程(面向抽象编程,而不要面向具体编程);
  4. 优先选择接口(因为继承抽象类后,此类将无法再继承,所以会丧失此类的灵活性;而可以实现多个接口,达到类似“多继承”的效果)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/407462.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenBMC的c++代码中的变量初始化问题(二)

1 开发平台 Win11、VS2022、Fedora39。 2 作业目的 通过VS2022跨平台Linux构建openbmc/intel-ipmi-oem的x64可执行模块。 3 问题描述 该模块启动后&#xff0c;出现以下异常&#xff1a; 上图中的调用堆栈消息是&#xff1a; std::__uniq_ptr_impl<sd_bus, sdbusplus::…

Windows下VTK 源码编译(For Qt PCL)

虽然我们在windows下安装PCL的时候就已经安装了VTK&#xff0c;由于跟着PCL安装的VTK是没有和QT联合编译的&#xff0c;所以在使用PCL和QT做点云可视化界面的时候是无法使用QT的插件QVTKWidget。 VTK 源码下载 Tags VTK / VTK GitLab 我这里的环境是Win10 Visual Studio&…

Android 相机启动流程笔记

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、Camera 框架介绍&#xff1a; Camera的框架分为Kernel部分和hal部分&#xff0c;其中kernel部分主要有两块&#xff1a; image sensor driver&…

【计算机学院寒假社会实践】——卫生服务无限情,社区居民乐融融

为了加强社区基层党组织建设和改进社区工作&#xff0c;推动社区更好繁荣发展&#xff0c;曲阜师范大学计算机学院“青年扎根基层&#xff0c;服务走进社区”实践队员周兴睿在2024年2月14日来到了山东省滨州市陈集社区&#xff0c;对社区卫生进行清洁工作。 这一年&#xff0c;…

[剪藏] - “闷声赚票房”的雷佳音,“窝囊废赛道”的成功学代表?

作者| 糖炒山楂 编辑| Mia 猫眼专业版上&#xff0c;雷佳音主演电影票房达到了147亿&#xff0c;排在影人榜第12名。按照平台对《热辣滚烫》和《第二十条》最终票房的预测&#xff0c;他的个人票房还有10亿左右的上升空间。 相比贾玲、赵丽颖等女性扛起了春节档的焦点话题&a…

微服务Day6

文章目录 DSL查询文档RestClient查询文档快速入门 旅游案例 DSL查询文档 RestClient查询文档 快速入门 Testvoid testMatchAll() throws IOException {//1.准备RequestSearchRequest request new SearchRequest("hotel");//2.准备DSLrequest.source().query(QueryB…

计算机网络实验四VLAN与三层交换机

一、实验目的和要求 1&#xff09;掌握VLAN的基本配置方法&#xff0c;理解VLAN的功能和作用&#xff1b; 2&#xff09;掌握三层交换机的基本配置方法。 二、实验环境 1&#xff09;运行Windows 2008 Server/XP/7操作系统的PC一台。 2&#xff09;PacketTracer。 实验内…

H12-821_29

29.四台路由器运行IS-S且已经建立邻接关系,区域号和路由器的等级如图中标记,下列说法中正确的有? A.R2和R3都会产生ATT置位的Level-1的LSP B.R1没有R4产生的LSP,因此R1只能通过缺省路由和R4通信 C.R2和R3都会产生ATT置位的Leve1-2的LSP D.R2和R3互相学习缺省路由,该网络出现路…

Github 2024-02-23 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-23统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量非开发语言项目4Python项目3TypeScript项目1HTML项目1Dart项目1Rust项目1 从零开始构建你喜爱的技术 创建周…

CVE-2023-44313 Apache ServiceComb Service-Center SSRF 漏洞研究

本次项目基于go语言&#xff08;本人不精通&#xff09;&#xff0c;虽不是java web框架了 &#xff0c;但搭建web服务的框架一些思想理念却是通用的&#xff0c;我们由此可以得到一些蛛丝马迹....... 目录 漏洞简介 漏洞分析 漏洞复现 漏洞简介 Apache ServiceComb Servi…

PCIe和SATA接口统计

一、PCIe接口 1、Add-in-Card(AIC) AIC是最常见的PCIe接口形态,组装过电脑的同学可能比较清楚,电脑上的主板上都会有下面的几排插槽,这就是典型的PCIe AIC的插槽,比较常见的插槽位宽为x16和x1 插在上面的卡就是PCIe AIC。PCIe AIC常见的有显卡,无线网卡,存储设备等等 A…

Javaweb之SpringBootWeb案例之AOP案例的详细解析

4. AOP案例 SpringAOP的相关知识我们就已经全部学习完毕了。最后我们要通过一个案例来对AOP进行一个综合的应用。 4.1 需求 需求&#xff1a;将案例中增、删、改相关接口的操作日志记录到数据库表中 就是当访问部门管理和员工管理当中的增、删、改相关功能接口时&#xff0c…

Python入门学习——基础语法

一、Python解释器 1. Python解释器的作用是&#xff1a; 将Python代码翻译成计算机认识的O和1并提交计算机执行在解释器环境内可以一行行的执行我们输入的代码也可以使用解释器程序&#xff0c;去执行".py"代码文件 2. Python解释器程序在&#xff1a; <Python…

c++的类型转换方法

一、静态类型转换&#xff08;static_cast&#xff09; 静态类型的转换主要用于基本类型之间的转换&#xff0c;比如int类型转换为double类型。但是static_cast也可以支持上下行的转换&#xff08;存在继承关系之间的转换&#xff09; 基本类型之间的转换举例 上下行转换的举…

代码随想录算法训练营第59天 | 583.两个字符串的删除操作 + 72.编辑距离 + 编辑距离总结篇

今日任务 583. 两个字符串的删除操作 72. 编辑距离 编辑距离总结篇 583.两个字符串的删除操作 - Medium 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以…

秒懂百科,C++如此简单丨专栏导读及学习方法

目录 写在前面 专栏独有亮点 专栏目录总览 口头禅 订阅方式 保证 写在结尾 写在前面 本专栏为C的入门课程&#xff0c;包括了C的基础知识和算法入门。 如果你真心想学C&#xff0c;那么你一定要订阅此专栏&#xff0c;里面的21节课能够让新手快速入门。 &#x1f3c5;跟…

全能代码生成器,自动生成前后端代码、生成项目框架、生成JavaBean、生成数据库文档、自动化部署项目(TableGo v8.4.0)

TableGo_20240224 v8.4.0 正式版发布&#xff0c;此次版本累计更新如下&#xff1a; 1、TableGo专属LOGO上线 2、生成数据库文档ER图新增备注字段名的生成配置 3、生成自定义文件功能新增临时参数配置&#xff0c;用于使用临时数据生成自定义文件 4、新增基于Excel数据生成…

【前端素材】推荐优质后台管理系统Vuesy平台模板(附源码)

一、需求分析 后台管理系统在多个层次上提供了丰富的功能和细致的管理手段&#xff0c;帮助管理员轻松管理和控制系统的各个方面。其灵活性和可扩展性使得后台管理系统成为各种网站、应用程序和系统不可或缺的管理工具。下面详细分析后台管理系统的定义和功能&#xff1a; 1.…

一个小老板的日常管理

昨天在“Daily Briefing”公众号的一文《Daily Briefing下一步怎么办&#xff1f;》&#xff0c;收到很多英语爱好者的留言和祝福。 其实“Daily Briefing”也相当于创业前的一次MVP&#xff0c;失败也好&#xff0c;成功也罢&#xff0c;都是自己不错的一段经历。 咱们“知识大…

力扣 48. 旋转图像

1.题目 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]]…