1.Java基础面试题

一、Java好在哪?

从跨平台、垃圾回收、生态三个方面来回答。
首先Java是跨平台的,不同平台执行的机器码是不一样的,而Java中间还加了一层JVM所以可以做到一次编写多平台运行。
其次Java还提供垃圾自动回收功能,虽说手动管理内存意味着自由、精细化地掌控,但是很容易出错。在内存较充裕的当下,将内存的管理交给GC来做,减轻了程序员编程的负担,提升了开发效率,更加划算!
最后Java生态圈太全了,丰富的第三方类库、网上全面的资料、企业级框架、各种中间件等等。

二、抽象类和接口有什么区别?

定义方式

  • 抽象类:使用abstract关键字声明,它可以包含抽象方法和具体方法。抽象方法是没有方法体的方法,只能存在于抽象类中,需要被子类实现。
  • 接口:使用interface关键字声明,它只能包含抽象方法(在Java8及以后版本中,接口还可以包含默认方法和静态方法,但这些方法仍然不能有具体的实现)。接口中的所有方法默认都是public的,且不能有实现。

访问控制

  • 抽象类:其方法和变量的访问控制符没有限制,可以是public、protected或private(但抽象方法不能是private的)。
  • 接口:其方法和变量默认都是public的,且不能声明为private或protected。

变量

  • 抽象类:可以包含实例变量和静态变量。
  • 接口:只能包含常量(即public static final的变量),且通常省略这些修饰符。

构造函数

  • 抽象类:可以有构造函数,这些构造函数用于初始化抽象类中的变量,但不能直接实例化抽象类。
  • 接口:不能有构造函数,因为接口不能被实例化。

继承与实现

  • 抽象类:一个类只能继承一个抽象类(在Java中,类只能单一继承)。
  • 接口:一个类可以实现多个接口,且接口之间可以相互继承(多继承)。

职责与用途

  • 抽象类:主要用于代码复用,通过定义一些通用的属性和方法,让子类继承并扩展。抽象类也常用于定义一组相关的方法,其中一些方法是抽象的,需要在子类中实现。
  • 接口:主要用于定义规范或契约,即规定了一组方法,但不提供具体的实现。接口是实现多态性的基础,允许不同的类实现相同的方法,但具体实现方式可以不同。

实例化

  • 抽象类:不能直接实例化,但可以声明抽象类的引用,该引用指向实现了所有抽象方法的子类对象。
  • 接口:不能直接实例化,但可以声明接口的引用,该引用指向实现了接口中所有方法的类对象。

其他差异

  • 抽象类:可以包含非抽象的方法,这些方法有具体的实现。
  • 接口:在Java8及以后版本中,虽然可以包含默认方法和静态方法,但这些方法仍然不能有具体的实现(即它们不是抽象方法,但也不会执行任何操作,除非被子类重写)。

三、序列化和反序列化是什么?

序列化

  • 序列化是指将对象的状态信息(即对象的成员变量和它们的数据类型)转换为可以存储或传输的形式的过程。通常,这个过程是将对象转换为字节序列,这样它就可以被写入到文件、在网络上传输或保存到内存数据库中。序列化的对象可以被发送到其他程序(可能运行在不同的计算机上),或者稍后重新读取和恢复。
  • Java中的序列化机制允许你将实现了java.io.Serializable接口的对象转换为字节序列,然后通过文件、数据库或网络等方式进行传输或存储。需要注意的是,Serializable接口是一个标记接口,它不包含任何方法,仅用于指示某个类的对象可以被序列化。

反序列化

  • 反序列化是序列化的逆过程,即将存储或传输的字节序列恢复为原始对象的过程。在Java中,这通常涉及到读取包含对象状态信息的字节序列,然后重新构造出相应的对象。

四、JDK JRE JVM 的区别?

Java环境的关系

  • JDK(Java Development Kit)是整个 Java 的核心,是 java 开发工具包,包括了 Java 运行环境
    JRE、Java 工具和 Java 基础类库。
  • JRE(Java Runtime Environment)是运行 JAVA 程序所必须的环境的集合,包含 java 虚拟机和 java
    程序的一些核心类库。
  • JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,是整个java 实现跨平台的最核心的部分,能够运行以
    Java 语言写作的软件程序。

五、String、StringBuffer、StringBuilder 三者之间的区别

String 字符串常量 StringBuffer 字符串变量(线程安全) StringBuilder 字符串变量(非线程安全)
String 中的 String 类中使用 final 关键字修饰字符数组来保存字符串,privatefinal char value[]
String 对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与
StringBuffer 的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf 等公共方法。StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。 StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
小结:
(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据用 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据用StringBuilder。

应用场景:

  • String :
    • 存储不需要修改的文本信息:当你需要存储如用户名、地址、电子邮件等文本信息,且这些信息在程序执行过程中不会改变时,使用String是合适的。这是因为String是不可变的,一旦创建,其内容就不能被修改,这有助于保证数据的安全性和稳定性。
    • 安全性要求高的场景:在需要确保字符串内容不会被意外修改的场景中,使用String可以提供更高的安全性。例如,存储密码时,使用String可以防止密码在后续操作中被修改。
    • 字符串连接且内容不变:在构建数据库查询语句或生成HTML代码等场景中,如果连接的字符串内容是固定的,不会发生变化,那么使用String进行连接操作是合适的。
  • 特点:
    • 不可变性:一旦创建,String对象的内容就不能被修改。
    • 线程安全:由于不可变性,String是线程安全的,可以在多线程环境中安全使用。
    • 字符串常量池:使用直接赋值方式创建的String对象会被存储在字符串常量池中,以节省内存空间。
  • StringBuffer:
    • 多线程环境下的字符串操作:当需要在多线程环境中频繁进行字符串操作时,使用StringBuffer是合适的。StringBuffer是线程安全的,它的所有公共方法都被synchronized关键字修饰,确保了线程安全性。
    • 需要大量修改操作的字符串:如果你需要在一个字符串上进行大量的添加、插入、替换和删除等操作,使用StringBuffer可以避免创建大量的临时对象,从而提高性能。
  • 特点:
    • 可变性:StringBuffer支持对字符串内容的修改。
    • 线程安全:通过synchronized关键字保证线程安全,但这也导致了性能上的开销。
    • 动态扩容:在需要时,StringBuffer可以自动扩容以存储更多的内容。
  • StringBuilder:
    • 单线程环境下的字符串操作:在单线程环境中,如果你需要进行大量的字符串拼接或修改操作,并且对性能有较高的要求,那么使用StringBuilder是最佳选择。StringBuilder不是线程安全的,但它比StringBuffer在单线程环境下提供了更好的性能。
    • 性能要求高的场景:对于性能要求极高的场景,如果确定操作只会在单线程环境下进行,那么使用StringBuilder可以显著提升性能。
  • 特点:
    • 可变性:与StringBuffer类似,StringBuilder也支持对字符串内容的修改。
    • 非线程安全:由于没有线程安全的开销,StringBuilder在单线程环境下性能更高。
    • 动态扩容:同样支持在需要时自动扩容。
  • 总结
    在选择String、StringBuffer或StringBuilder时,主要应考虑以下几个因素:
    • 是否需要修改字符串内容。
    • 是否在多线程环境下操作。
    • 对性能的要求。

六、什么是反射?

在Java中,反射(Reflection)是一种强大的机制,它允许程序在运行时检查或修改其结构和行为。具体来说,通过反射,Java程序可以动态地获取类的信息(如类的成员变量、构造方法、方法等),并且可以动态地创建对象、调用方法、访问和修改字段等。这种能力让Java程序更加灵活和动态。

反射的主要用途包括:

  • 动态创建对象:在运行时,可以使用Class.forName()方法加载类,并通过Class对象的newInstance()方法(在Java
    9及更高版本中已被弃用,推荐使用Class的getDeclaredConstructor().newInstance())来创建类的实例。
  • 调用方法:通过反射,可以在运行时获取方法的信息(包括方法名、参数类型等),并调用这些方法。这允许程序在不知道具体类和方法名的情况下执行操作。
  • 访问和修改字段:反射也允许程序在运行时访问和修改类的私有字段。这通常用于框架开发或测试框架中,以便在不修改类源代码的情况下操作类的内部状态。
  • 获取类的信息:通过反射,可以获取类的各种信息,如类的修饰符(public、private等)、实现的接口、父类、构造方法、字段、方法等。

反射的缺点:

  • 性能开销:反射涉及类型安全检查、动态代码解析等,相对于直接代码调用,反射通常会有更大的性能开销。
  • 安全问题:通过反射可以访问类的私有成员,这可能会破坏类的封装性,导致安全问题。
  • 依赖性强:使用反射的代码对类的结构有较强的依赖,如果类的结构发生变化(如方法名改变、参数类型改变等),那么使用反射的代码可能会失效。

需要反射的原因:

  • 动态加载类:通过反射,Java程序可以在运行时动态地加载需要的类,而不需要在编译时将这些类引入到代码中。这使得程序更加灵活,可以根据不同的运行环境或条件加载不同的类,实现功能的动态扩展。
  • 动态创建对象:利用反射,Java程序可以在运行时动态地创建对象实例,而不需要在编译时确定对象的类型。这样可以根据需要创建不同类型的对象,极大地提高了程序的灵活性。
  • 访问私有成员:反射可以绕过Java的访问权限限制,获取和修改类的私有成员变量以及调用私有方法。这在某些情况下是非常有用的,比如需要访问第三方库中的私有成员时。
  • 开发通用框架:反射是开发各种通用框架(如Spring)的关键技术之一。这些框架通常需要根据配置文件或注解来动态地加载和创建对象,调用不同的方法。反射使得这些框架能够在运行时根据配置信息动态地构建和扩展其功能。
  • 注解处理:注解本身仅仅是起到标记作用,要使其生效,通常需要利用反射机制根据注解标记去调用注解解释器执行相应的行为。没有反射机制,注解的功能将大打折扣。
  • 动态代理:在AOP中,经常需要拦截特定的方法并在方法执行前后添加额外的处理逻辑(如事务管理、安全检查等)。动态代理是实现AOP的一种常用方式,而反射则是实现动态代理的关键技术之一。通过反射,可以在运行时动态地创建代理类并拦截目标方法的调用。
  • 类型分析:使用反射可以分析类的结构,获取类的属性、方法等信息。这对于编写需要处理不同类型对象的通用代码非常有用。
  • 调试支持:在调试过程中,反射可以用于动态地获取和修改类的信息,帮助开发者更好地理解程序的运行状态和排查问题。

七、解释一下Java 的异常

Java异常结构图

Throwable 是所有 Java 程序中错误处理的父类,有两种资类:Error 和Exception。 Error:表示由 JVM
所侦测到的无法预期的错误,由于这是属于JVM层次的严重错误,导致 JVM无法继续执行,因此,这是不可捕捉到的,无法采取任何恢复的操作,顶多只能显示错误信息。 Exception:表示可恢复的例外,这是可捕捉到的。
1.运行时异常:都是 RuntimeException 类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用 throws子句声明抛出它,也会编译通过。
2.非运行时异常(编译异常):是 RuntimeException 以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException
等以及用户自定义的 Exception 异常,一般情况下不自定义检查异常。

八、java的基本数据类型有哪些?

Java的基本数据类型(Primitive Types)是Java语言内置的数据类型,它们直接映射到Java虚拟机(JVM)的指令集中,并且每种类型都分配了固定的内存空间。Java的基本数据类型可以分为四大类:整型、浮点型、字符型、布尔型。具体来说,Java有八种基本数据类型:

  1. 整型(Integer Types)

byte:8位,范围从-128到127(有符号) short:16位,范围从-32,768到32,767(有符号)
int:32位,范围从-2,147,483,648到2,147,483,647(有符号),这是最常用的整数类型
long:64位,范围从-9,223,372,036,854,775,808到9,223,372,036,854,775,807(有符号),使用时需要在数字后加L或l(小写L更常用,以避免与数字1混淆)

  1. 浮点型(Floating-Point Types)

float:32位,单精度,范围非常大,但不精确,用于科学计算或工程计算,使用时需要在数字后加F或f
double:64位,双精度,范围比float大,精度也更高,是Java默认的浮点类型

  1. 字符型(Character Types)

char:16位,用于存储单个Unicode字符,Java中字符采用Unicode编码

  1. 布尔型(Boolean Types)

boolean:用于表示逻辑上的真(true)或假(false),它不对应任何具体的数值

九、什么是面向对象?说说你对面向对象的理解?

面向对象(Object-Oriented,简称OO)是软件开发中的一种设计方法和编程范式。它强调将现实世界中的事物抽象为对象,并通过这些对象之间的交互来设计和实现软件。面向对象的核心概念包括类(Class)、对象(Object)、封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)。

理解:

  • 类(Class):类是创建对象的模板或蓝图,它定义了对象的属性和方法。属性是对象的特征(如颜色、大小等),而方法是对象可以执行的操作(如移动、显示等)。
  • 对象(Object):对象是类的实例。每个对象都拥有类定义的属性和方法,但每个对象的属性值可以不同,这使得对象能够表示现实世界中的具体实体。
  • 封装(Encapsulation):封装是将对象的属性和方法结合在一个单一的单元中,并尽可能地隐藏对象的内部细节。这样做的好处是提高了代码的安全性和可维护性,因为外部代码不能直接访问对象的内部数据,只能通过对象提供的方法来操作数据。
  • 继承(Inheritance):继承是一种允许我们根据一个类(父类或基类)来定义另一个类(子类或派生类)的机制。子类继承父类的属性和方法,并可以添加新的属性和方法或覆盖(Override)父类的方法。继承提高了代码的复用性,使得开发者可以基于已有的类来构建新的类。
  • 多态(Polymorphism):多态允许一个接口在多个类中存在,即同一个操作或方法名可以在不同的类中有不同的实现。多态主要有两种形式:编译时多态(主要通过方法重载实现)和运行时多态(主要通过方法覆盖和接口实现)。多态使得程序更加灵活和可扩展。

优势:

  • 代码复用:通过继承,可以复用现有的代码,提高开发效率。
  • 易于理解和维护:面向对象的设计方法使得代码更加模块化,易于理解和维护。
  • 可扩展性:由于面向对象支持多态和继承,使得系统更容易扩展。
  • 灵活性:对象之间的交互是通过消息传递实现的,这使得系统更加灵活,易于修改。

十.==和equals的区别?

  • == 操作符
    • 基本数据类型:对于基本数据类型(如int, char, boolean, byte, short, long, float, double),== 用于比较它们的值是否相等。
    • 对象引用类型:对于对象引用类型(如类、接口、数组等),==用于比较两个引用是否指向堆内存中的同一个对象(即地址是否相同)。如果两个引用指向同一个对象,则结果为true;如果指向不同的对象,则结果为false。
  • equals() 方法
    • equals() 方法是Object类的一个方法,所有Java类都继承自Object类,因此都继承了equals()方法。默认情况下,equals()方法也是用来比较两个对象的引用是否相等,即比较它们是否指向堆内存中的同一个对象。
    • 但是,很多Java类(如String,Integer等)都重写了equals()方法,以便比较两个对象的内容是否相等,而不是它们是否指向同一个对象。例如,两个String对象,即使它们不是同一个对象(即地址不同),只要它们包含的字符序列相同,使用equals()方法比较时也会返回true。
  • 注意:
    • 当比较基本数据类型时,应使用==。
    • 当比较对象引用类型时,若需要比较两个对象的内容是否相等,应重写equals()方法(如果类库中的类已经提供了满足需求的equals()实现,则直接使用即可),而不要依赖==。
    • 需要注意的是,equals()方法的使用还依赖于hashCode()方法的正确实现,因为在使用哈希表(如HashMap,HashSet等)时,会同时调用这两个方法来保证对象的正确存储和检索。如果重写了equals()方法,通常也需要重写hashCode()方法,以保持equals()和hashCode()之间的一致性契约。

十一、重载和重写的区别

  • 定义
    • 重载(Overloading):在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。重载与方法的返回类型无关,即方法名相同,参数列表不同(参数个数、类型或顺序不同),则认为是重载。
    • 重写(Overriding):子类可以重写父类中的方法。如果子类中的方法与父类中的某个方法具有相同的名称、返回类型和参数列表,则称子类中的该方法重写了父类中的方法。注意,子类的访问权限不能比父类中被重写的方法的访问权限更低。
  • 关键点差异
    • 发生位置:重载发生在同一个类中,而重写发生在子类与父类之间。
    • 参数列表:重载要求参数列表必须不同(参数个数、类型或顺序),而重写要求参数列表必须完全相同。
    • 返回类型:重载对返回类型没有要求,可以相同也可以不同;但重写要求返回类型必须相同或是其子类型(协变返回类型,Java 5及以上版本支持)。
    • 访问修饰符:重载没有限制;重写时,子类的方法访问权限不能低于父类中被重写的方法的访问权限(即子类方法不能比父类方法更严格)。
    • 异常:重载时,方法抛出的异常没有限制;但重写时,子类重写的方法抛出的异常类型不能比父类被重写的方法抛出的异常类型更广泛(即子类方法不能抛出父类方法没有声明的异常,但可以抛出父类方法声明的异常的子类或相同类型的异常)。

十二、finally中的代码在哪些情况下不执行?

  • 系统退出(System.exit()):

    • 如果程序在 try 或 catch 块中调用了 System.exit(int status)方法,并且状态码被传递给了该方法(无论是0还是非0),那么JVM会立即停止运行,不会执行 finally 块中的代码。这是因为System.exit() 方法用于终止当前运行的Java虚拟机,这会导致程序立即停止,不再执行后续的代码。
  • JVM崩溃:

    • 如果Java虚拟机因为某些原因(如内存溢出、硬件问题等)崩溃了,那么 finally块中的代码也不会被执行。这种情况比较少见,但确实可能发生。
  • 线程死亡(Thread.stop()):

    • 虽然 Thread.stop()方法在Java中已经被废弃(因为它是不安全的),但如果你在一个线程中使用了它(或等效地通过其他方式强制停止线程),并且这个线程正在执行try 或 catch 块中的代码,那么 finally 块可能不会执行。然而,由于 Thread.stop()的不安全性,这种做法在现代Java编程中是不推荐的。
  • 特定类型的异常(如Error和某些未捕获的RuntimeException):

    • 虽然大多数异常(包括检查型异常和未捕获的RuntimeException)都会导致控制流跳转到 finally 块(如果有的话),但某些Error(如OutOfMemoryError、StackOverflowError等)可能会阻止 finally块的执行,尤其是当它们导致JVM进入不稳定状态时。然而,这主要取决于JVM的具体实现和错误发生的上下文。
  • 无限循环或长时间运行的代码:

    • 如果 try 或 catch 块中的代码包含无限循环或长时间运行的操作,并且这些操作没有被适当地中断或停止,那么 finally块中的代码将无限期地等待执行。虽然这不意味着 finally 块“不执行”,但它可能导致程序的行为看起来像是 finally 块没有执行。操作系统层面的中断: 如果操作系统层面的因素(如电源故障、操作系统崩溃等)导致JVM被强制终止,那么 finally块中的代码也不会被执行。

十三、什么是泛型?泛型的作用是什么?

Java中的泛型(Generics)是JDK 5.0(官方名称为JDK1.5,代号“Tiger”)引入的一个新特性。泛型的本质是参数化类型,即将类型由原来的具体类型参数化,这种参数类型可以用在类、接口、方法中,分别称为泛型类、泛型接口、泛型方法。泛型类型变量由尖括号界定,放在类或接口名的后面,类型变量在创建类或接口的实例时会被具体的类型所替换,从而可以创建出可以工作于多种数据类型的灵活可重用的代码。

作用:

  • 类型安全:
    • 泛型可以在编译时检查类型,防止代码中出现类型错误,从而减少程序运行时出现异常的可能性。
    • 使用泛型可以避免类型转换,提高程序的性能。
  • 代码复用:
    • 泛型代码可以应用于多种数据类型,避免了编写大量重复的代码,减少了代码量。
    • 例如,可以编写一个ArrayList类,然后在创建ArrayList实例时指定元素的类型,而不需要为每种类型都编写一个专门的类。
  • 编程效率:
    • 泛型可以减少开发人员的工作量,因为它们允许编写更少的代码来完成同样的任务。
  • 提高可读性和可维护性:
    • 泛型使得代码更加清晰,因为你能够明确看到集合中元素的类型。
    • 如果需要在整个项目中更改某个数据类型的处理方式,只需要修改泛型类型的定义,而不需要修改整个项目中的代码。
  • 支持编译时的类型检查:
    • 在编译时,泛型可以提供额外的类型信息给编译器,使得编译器可以进行更严格的类型检查。这有助于减少运行时错误。
  • 支持类型擦除:
    • Java中的泛型是通过类型擦除来实现的。这意味着在运行时,泛型类型信息会被擦除,泛型代码会转换成普通的非泛型代码。这保证了Java泛型与旧版Java代码的兼容性,并避免了类型膨胀的问题。
  • 支持泛型方法和泛型接口:
    • 除了泛型类之外,Java还支持泛型方法和泛型接口。这进一步增加了代码的灵活性和可重用性。

十四、怎么使用泛型?

在Java中,使用泛型主要涉及到泛型类泛型接口泛型方法的定义和使用。

  1. 泛型类:泛型类就是在类定义时,通过类型参数(type parameters)来指定类中某些属性的类型或某些方法的返回类型、参数类型。
// 定义一个泛型类  
public class Box<T> {  
    // T stands for "Type"  
    private T t;  
  
    public void set(T t) { this.t = t; }  
    public T get() { return t; }  
}  
  
// 使用泛型类  
Box<Integer> integerBox = new Box<>();  
integerBox.set(123);  
Integer integer = integerBox.get();  
  
Box<String> stringBox = new Box<>();  
stringBox.set("Hello World");  
String string = stringBox.get();
  1. 泛型接口:泛型接口与泛型类类似,在接口定义时引入类型参数。
// 定义一个泛型接口  
public interface Pair<K, V> {  
    public K getKey();  
    public V getValue();  
}  
  
// 实现泛型接口  
class OrderedPair<K, V> implements Pair<K, V> {  
    private K key;  
    private V value;  
  
    public OrderedPair(K key, V value) {  
        this.key = key;  
        this.value = value;  
    }  
  
    @Override  
    public K getKey() { return key; }  
  
    @Override  
    public V getValue() { return value; }  
}  
  
// 使用泛型接口  
Pair<Integer, String> p = new OrderedPair<>(1, "apple");  
Integer key = p.getKey();  
String value = p.getValue();
  1. 泛型方法:泛型方法是在方法中引入类型参数,而方法所在的类并不是泛型类。
// 定义一个泛型方法  
public class Util {  
    // 注意,此方法的返回类型是泛型,泛型声明在返回值之前  
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {  
        return p1.getKey().equals(p2.getKey()) &&  
               p1.getValue().equals(p2.getValue());  
    }  
}  
  
// 使用泛型方法  
boolean same = Util.compare(p, new OrderedPair<>(1, "apple"));

注意事项

  • 泛型类型参数在运行时会被擦除,即泛型信息只在编译时有效,运行时并不包含类型信息。
  • 可以使用通配符(?、? extends T、?super T)来表示未知的类型或类型的范围。
  • 泛型与原始类型(raw types,即不带类型参数的泛型类或接口)不兼容,使用原始类型会丢失泛型带来的类型安全检查。
  • 静态方法不能直接访问类的类型参数,因为静态方法属于类本身,而类型参数是在创建类的实例时指定的。但是,静态方法内部可以定义自己的类型参数,从而成为泛型方法。

十五、自己的项目中哪里使用到了泛型?

  • 自定义通用返回结果ResponseResult通过参数可根据具体的返回类型动态指定结果的数据类型

十六、注解是什么?

在Java中,注解(Annotation)是一种元程序中的元数据,用于为Java代码提供附加信息。这些附加信息可以在编译时或运行时被Java虚拟机或其他工具读取和使用。注解是JDK
5.0(即JDK 1.5)及以后版本引入的一个特性,它们与类、接口、枚举等是在同一个层次上。 注解本质是继承了Annotation的特殊接口

作用:

  • 编写文档:通过代码里标识的元数据生成文档(如生成doc文档)。
  • 代码分析:通过代码里标识的元数据对代码进行分析。
  • 编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查。
  • 运行时处理:某些注解(如使用@Retention(RetentionPolicy.RUNTIME)声明的注解)的信息可以在运行时通过反射机制被读取和使用,从而实现特定的功能。

Java内置注解:

  • @Override:用于告诉编译器,希望重写(覆盖)父类中的方法。如果父类中不存在与该方法签名匹配的方法,编译器会产生一个错误。
  • @Deprecated:用于标记方法、类或字段已过时,不推荐使用。编译器会发出警告,提示开发者尽量避免使用被标记为过时的元素。
  • @SuppressWarnings:用于告诉编译器忽略特定类型的警告。这对于处理旧代码或集成第三方库时非常有用。
  • @SafeVarargs:用于表示带有可变数量参数的方法是类型安全的。它告诉编译器,该方法不会导致堆污染警告。

元注解:元注解是用于定义注解的注解

  • @Retention:定义了注解的保留策略(RetentionPolicy),包括SOURCE(注解信息仅保留在源文件中,编译时被丢弃)、CLASS(注解信息被保留在class文件中,但JVM在运行时不会保留)、RUNTIME(注解信息被保留在class文件中,且JVM在运行时可以通过反射机制读取)。
  • @Target:定义了注解的作用目标,即注解可以应用的Java元素类型(如类、方法、字段等)。
  • @Documented:定义注解可以包含在javadoc中。 @Inherited:表明注解可以被子类集成使用。

使用场景:

  • 代码分析工具:可以使用注解来标记代码中的一些问题,如未使用的变量或方法,然后通过工具进行检查。
  • 依赖注入:许多依赖注入框架使用注解来标记需要注入的字段或方法。 测试框架:测试框架如JUnit使用注解来标记测试方法。
  • 持久性框架:持久性框架如Hibernate使用注解来映射实体类与数据库表。
  • Web开发:在Spring框架中,注解用于配置和管理Bean,大大简化了配置工作。

十七、3种常见的IO模型?

  • 阻塞IO(Blocking IO)
    • 阻塞IO是最传统的IO模型。在这种模型中,当应用程序执行输入或输出操作时,如果输入或输出数据尚未就绪(例如,网络数据未到达,文件数据还未被加载到内存中),那么该操作将会阻塞当前线程,直到数据就绪为止。这意味着,线程在等待数据的过程中无法执行其他任务,这可能导致应用程序的性能瓶颈,特别是在处理大量并发连接时。
  • 非阻塞IO(Non-blocking IO)
    • 非阻塞IO模型允许线程在等待数据就绪时继续执行其他任务,而不是直接阻塞在那里。非阻塞IO通常通过轮询(polling)机制实现,即线程定期检查数据是否就绪。这种方法虽然避免了线程阻塞,但频繁的轮询会浪费CPU资源,并且如果轮询过于频繁,可能导致性能下降。
  • 异步IO(Asynchronous IO)
    • 异步IO模型进一步改进了非阻塞IO,它允许应用程序向操作系统发出IO操作请求,然后立即返回继续执行其他任务,而不是通过轮询检查操作是否完成。当IO操作完成时,操作系统会通知应用程序,此时应用程序可以读取数据或处理操作结果。这种模型显著提高了程序的响应性和吞吐量。

十八、注解的原理是什么?

注解其实就是一个标记,可以标记在类上、方法上、属性上等,标记自身也可以设置一些值。
有了标记之后,我们就可以在解析的时候得到这个标记,然后做一些特别的处理,这就是注解的用处。比如我们可以定义一些切面,在执行一些方法的时候看下方法上是否有某个注解标记,如果是的话可以执行一些特殊逻辑(RUNTIME类型的注解)。

注解生命周期有三大类,分别是:

  • RetentionPolicy.SOURCE:给编译器用的,不会写入class文件
  • RetentionPolicy.CLASS:会写入class文件,在类加载阶段丢弃,也就是运行的时候就没这个信息
  • RetentionPolicy.RUNTIME:会写入class文件,永久保存,可以通过反射获取注解信息

注解就是一个标记,可以给编译器用、也能运行时候用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Retrograde-lx

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

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

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

打赏作者

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

抵扣说明:

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

余额充值