06 抽象类与接口

利用抽象类和接口可以有效的实现大型系统的设计拆分,避免耦合问题的产生。

01 了解抽象类吗?简单说一下

对于普通类来讲,覆写父类的哪些方法完全是由子类决定的,如果希望子类继承父类时有一些明确的覆写要求,父类就必须通过抽象类来描述。

抽象类仍是类,普通类具有的结构抽象类都具有,除此之外,抽象类中有抽象方法,抽象方法和抽象类都通过abstract关键字描述。

抽象类是不能实例化的,并且抽象类的非抽象类子类需要覆写抽象类中的全部抽象方法。抽象类必须被继承,抽象方法必须被覆写,所以抽象类和抽象方法是不能被final关键字修饰的。

02 说一下模板设计模式吧

模板设计模式是一种行为设计模式。通常会使用模板方法定义一个操作中的算法的骨架,而将一些特定步骤通过抽象方法的形式暴露出去让子类覆盖去实现。

它能解决哪些问题呢?

  1. 可以把子类中的通用步骤提取出来,减少代码重复。如果多个子类有许多相似的行为,但细节上有差异,通过模板方法可以将这些通用步骤提取出来,使子类可以专注实现特定的步骤。
  2. 可以做复杂算法的实现。可以将复杂的算法拆分多个步骤,并且在模板方法中定义这些步骤的执行顺序,子类可以根据需要重写特定的步骤。比如一个复杂的算法分为5步,其中第3步需要对一堆数据进行存储,子类重写第3步的时候可能会用数组存,也可能会用链表存,不管用哪种方式去存储数据,都能实现这个复杂的算法,只是第三步存储方式的不同,可能适用于不同的场景,所以说,模板设计模式可以让复杂算法的实现变的更加灵活,并且还易于扩展。
  3. 做框架和库的设计。为开发者提供一个基础的操作模式,开发者可以在其基础上进行扩展。想想用框架的时候有没有这种感觉,哈哈哈哈。
  4. 可以去固定业务流程中的步骤。比如电商的订单处理流程,包括下单、支付、发货等,可以使用模板设计模式去定义这些步骤的执行顺序和逻辑。
AbstractClass {//抽象类 
// 模板方法
    public void templateMethod() {
        step1();
        step2();
        step3();
    }
    protected void step1() {
        // 默认实现或留给子类实现
    }
    protected void step2() {
        // 默认实现或留给子类实现
    }
    protected void step3() {
        // 默认实现或留给子类实现
    }
}

ConcreteClass extends AbstractClass {//子类 
    @Override
    protected void step1() {
        // 子类具体实现
    }
    @Override
    protected void step2() {
        // 子类具体实现
    }
}

public static void main(String[] args){
    AbstractClass templateClass = new ConcreteClass();
    templateClass.templateMethod();
}

03 讲一下包装类吧

Object能接收所有类的对象实例解决了统一参数类型的问题,但是Object无法接收基本数据类型啊!包装类就是解决这个问题的,它把基本数据类型的内容包装到一个类中,来实现Object的参数统一。

Java针对于8种基本类型都提供了包装类,分为对象型包装类和数值型包装类。

  1. 对象型包装类(Object的直接子类):boolean(Boolean)、char(Character)。

  2. 数值型包装类(Number的直接子类):byte(Byte)、short(Short)、int(Integer)、long(Long)、float(Float)、double(Double)。

    Number抽象类定义了从包装类中获取多种类型数值的方法,数值型包装类继承Number抽象类并实现这些方法,就可以实现多种数值类型的转换,比如float转int,long转double,int转byte等等。

为了便于基本数据类型和包装类之间的转换,Java提供了自动装箱和拆箱机制,不需要包装类的方法,就可以将一个基本数据类型变为一个与之匹配的包装类对象,并且包装类对象无须拆箱可以直接实现数学运算。自动装箱和拆箱机制完善了Object接收一切参数的功能,

转换流程是:基本数据类型 -》 包装类对象 -》Object向上转型

Integer numA = 10;	//自动装箱为Integer
Double numB = 0.5;	//自动装箱为Double

numA++;	//包装类直接计算;
double numC = numB; //将包装类直接赋给基本数据类型

04 Integer数据比较问题

//若使用==去比较Integer和int,会先把Integer进行拆箱,再进行比较
Integer num1 = 500;
System.out.println(num1 == 500);//true

//若是使用==去比较两个Integer,Integer底层维护了-128~127的Integer对象的缓存,只要是在这个范围内的Integer都是用的缓存对象
Integer num2 = 100;
Integer num3 = 100;
Integer num4 = 800;
Integer num5 = 800;
System.out.println(num2 == num3);//在-128~127的范围内,用的同一个缓存对象,所以是true
System.out.println(num4 == num5);//不在范围内,两个Integer对象,地址不同,所以是false
System.out.println(num4.equals(num5));//Integer对equals()进行了重写,所以返回true

Integer部分源码:

//Integer没有构造器,装箱和Integer对象的创建是通过valueOf()完成的
public static Integer valueOf(int i) {
    	//如果在范围(-128~127)内,直接返回Integer类维护的缓存中的对象。
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
    	//否者,就创建一个新对象返回
        return new Integer(i);
 }

05 字符串与基本类型的转换处理

日后开发中,字符串转基本数据类型就使用各个包装类中的parse*方法,比如Integer.parseInt(String str),Double.parseDouble(String str)。

基本数据类型转字符串就使用String类中的valueOf()方法。

06 说一下你对接口的理解

接口就是用来指定不同系统或组件之间的交互标准。

举个例子,Java为了连接数据库提供了JDBC标准,各个数据库厂家按照这个标准做它们各自的实现(jar包),开发者只需要按照JDBC标准进行数据库的连接与操作,具体的行为由这些jar包中提供的实现类来做。

再说一个,在web开发中,ServletRequest是处理请求的标准,HttpServletRequest是其子标准,用来处理Http请求,我们写的程序是要放到web容器(Tomcat)上跑的,所以请求啥的都需要web容器去处理,我们只需要对HttpServletRequest进行操作,其具体的实现子类由web容器提供。

接口的好处可太多了:

  1. 实现系统集成:定义JDBC的接口可以集成数据库系统,定义支付接口可以集成微信或阿里或银联的支付系统,定义AI接口可以集成ChatGPT或者百度或豆包的问答服务等等。
  2. 降低系统耦合度:各部分之间依赖于接口,而不是具体的实现,便于系统的维护和扩展。比如订单处理系统中有一个通知模块,那就定义一个通知接口,里面有发送通知的方法,通知接口根据通知方式的不同有不同的实现类,比如邮件或短信等等,在订单处理系统中,我们是通过通知接口做通知的,不需要关心里面的具体实现,当要更换通知方式时,只需要将对应的实现类替换掉即可,不需要修改订单处理系统中的代码,从而降低了系统的耦合度。
  3. 规范开发:一个大的项目要多个人来开发时,接口可以为开发人员提供统一的标准和规则,确保不同部分的交互符合预期。

07 抽象类和接口有什么区别呢?

  1. 解决的问题不同:抽象类用来约束子类必须覆写某些方法,接口用来打破单继承的限制。
  2. 语法不同:抽象类还是类,除了抽象方法,普通类能有的结构它都能有。接口除了抽象方法,还能有静态常量和默认方法和静态方法,接口中的方法必须是public修饰符。
  3. 子类的使用方式不同:抽象类需要子类继承,并且是单继承。接口需要子类实现,子类可以实现多个接口。

08 讲一下适配器设计模式

适配器的主要作用是把一个接口转化成客户希望的另一个接口。比如说,有一个已存在的类,它的接口不符合当前系统的需求,我们就可以创建一个适配器类,这个适配器继承或关联原来的类,并且提供符合系统要求的新接口。这样,系统就可以通过适配器来使用原来那个类的功能,而无需对其进行大规模的修改。

通过继承实现的叫类适配器,通过关联实现的叫对象适配器。类适配器可能违反了里氏代换原则,不建议使用。

里氏代换原则:在程序中,一个子类对象应该能够替换其父类对象,并且在运行时不产生错误。也就是说,子类可以扩展父类的功能,但是不能改变父类原有的功能。在类适配器中,适配器类是适配者类的子类,适配器可能会重写适配者类的方法,从而改变父类原有的功能。

适配器模式在许多场景下都非常有用,特别是整合不同接口的组件时,能够有效的降低系统的复杂度和耦合度,提高系统的灵活性和可扩展性。

  1. 旧系统整合:当将旧系统的功能集成到新系统,而接口不匹配时,可以使用适配器进行转换。
  2. 第三方库的集成:第三方库的接口与项目自身的需求不完全一致时,利用适配器来适配。
  3. 数据格式转换:在数据处理的过程中,需要将一种数据格式转换为另一种系统所需要的数据格式时,可以用适配器来做。
  4. 不同协议的转换:在网络通信中,将一种协议的数据转换为另一种协议能处理的数据。

09 说一下工厂设计模式吧

工厂设计模式用来解决实例化对象的解耦合问题的。这个耦合指的是某一个接口和某一个子类的耦合。在程序中可能会根据功能动态的进行子类的切换,这样的切换处理就可以交给工厂类负责,如果要进行功能扩充,最终影响到的也仅仅是工厂类,主类不会有任何变化。

10 什么是代理设计模式?

代理设计模式是说,有一个代理对象与真实对象关联,代理对象可以在客户端和真实对象之间起到中介的作用,也就是说,客户端调用代理对象,代理对象调用真实对象中的核心业务,并加上相关的辅助业务。使用代理设计模式可以防止核心业务与辅助业务之间的联系过于紧密。

11 泛型解决了什么问题?

  1. 类型安全:在编译期就能检测到类型不匹配的错误,而不是在运行时才发现,提高了程序的可靠性。
  2. 代码复用:可以编写通用的算法和数据结构,适用于多种不同类型,减少代码重复。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值