Java入门高频考查基础知识2(超详细28题2.5万字答案)

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 多态是面向对象编程中的一个重要概念,它允许不同类的对象对同一消息作出不同的响应。在具体实现上,多态允许一个父类的引用指向其子类的对象,并根据实际指向的对象的类型来调用相应的方法。在 Java 中,多态可以通过以下几种方式实现:在同一个类中,方法名相同,但形参列表不同,实现了多态。子类可以重写(覆盖)其父类的方法,实现多态。在父类引用中调用该方法时,根据实际指向的子类对象的类型来调用相应的方法实现。

 Java 是一种广泛使用的面向对象编程语言,在软件开发领域有着重要的地位。Java 提供了丰富的库和强大的特性,适用于多种应用场景,包括企业应用、移动应用、嵌入式系统等。

       

以下是 Java 基础知识的一个概览:

  1. 语法和基本概念:Java 的语法类似于 C++ 和C#,熟悉这些语言的开发者能够很快上手。Java 的基本语法包括数据类型(如整型、浮点型、布尔型等)、运算符、流程控制语句(如 if-else、for 循环、while 循环等)等。

  2. 面向对象编程:Java 是一种面向对象的编程语言,它支持类、对象、封装、继承和多态等特性。面向对象编程是 Java 开发的核心,对于理解和熟练运用类和对象至关重要。

  3. Java 核心库:Java 提供了丰富的类库,包括用于输入输出、字符串处理、集合框架、多线程、网络编程等方面的库。熟悉这些库能够使开发者更高效地进行编程和问题解决。

  4. 异常处理:Java 通过异常处理机制提供了一种结构化的方式来处理程序中的异常情况,这有助于编写更加健壮的程序。

  5. 内存管理:Java 使用自动内存管理,开发者不需要手动管理内存。Java 的垃圾回收机制能够自动释放不再使用的对象,使得内存管理更加方便。

  6. 多线程编程:Java 提供了多线程编程的支持,可以创建多个线程并发执行任务。这对于提升程序性能和响应能力非常重要。

  7. 输入输出:Java 通过输入输出流提供了丰富的文件操作和网络编程功能,使得 Java 在处理文件和网络数据方面非常强大。

       

以上只是 Java 基础知识的一部分,Java 还包括许多其他重要的概念,如集合框架、反射、注解、泛型等。深入学习这些知识可以让开发者更加熟练地运用 Java 进行软件开发。

       

以下是几个面试技巧:

  1. 复习核心概念:回顾 Java 的核心概念,如面向对象编程、类和对象、继承和多态、异常处理、集合框架等。确保对这些基础知识有清晰的理解。

  2. 熟悉常见问题:预测并准备常见的面试问题,如 "什么是 Java 的封装、继承和多态?","什么是抽象类和接口?它们的区别是什么?" 等。熟悉这些问题的答案,以便能够流利、清晰地回答面试官的提问。

  3. 编码实践:练习编写一些简单的 Java 代码,以加深对基础概念的理解。尝试解决一些常见的编程问题,如逆序字符串、查找数组中的最大值等。这将有助于您在面试中展示自己的编码能力。

  4. 项目经验准备:复习您在 Java 开发方面的项目经验。准备一些项目细节和亮点,强调您在项目中所承担的角色和技术贡献。面试官通常会关注您的项目经验,因此务必能够清晰而有条理地介绍您的项目经历。

  5. 注意优缺点:在回答问题时,尽量不仅停留在正确的答案上,还要深入思考并表达特定功能、概念或语言特性的优缺点。面试官通常会更关注您的思考能力和对技术的全面理解。

  6. 积极参与:在面试中,积极与面试官互动。表达自己的观点和思考,提出问题或寻求澄清。这不仅能展示您的积极性和好奇心,还能促进面试的互动和对话。

  7. 准备好问题:在面试结束时,通常会给您提供机会提问。为了展示您对岗位和公司的兴趣,准备一些相关问题,例如关于公司文化、技术栈、团队合作等方面的问题。

       

最重要的是保持自信和冷静。提前准备,并对自己的知识和经验有自信,这样您就能在面试中展现出最佳的表现。祝您面试顺利!


一、Java语言采用何种编码方案?有何特点

Java 语言采用的编码方案是 Unicode。

       

Unicode:

  • Unicode 是一个国际标准,旨在为全世界所有的字符提供一个唯一的编码。Unicode 提供了一个统一的方式来表示不同语言的文字,包括汉字、希腊字母、阿拉伯字母和数学符号等。

ava 在 Unicode 中的应用:

  • Java 源代码是以 Unicode 字符集编写的,这意味着你可以在 Java 程序中直接使用几乎任何语言的字符。
  • Java 中的 `char` 数据类型是一个 16 位的数据类型,用来表示 Unicode 编码下的字符。
  • Java 使用 UTF-16 编码作为字符和字符串的内部表现形式。UTF-16 是 Unicode 转换格式(UTF)的一种,它使用 16 位(即两个字节)为大多数常用字符编码,对于超出 `U+FFFF` 范围的字符,使用一对代理项(surrogate pairs)表示。
  • 在 Java 5 之后,`java.lang.String` 类提供了将字符串转换成不同编码的方法,如 UTF-8、UTF-16 或其他支持的字符集编码,允许使用 `String.getBytes(Charset charset)` 方法根据需要进行转换。

       

特点:

  1. 兼容性:Unicode 是跨语言、跨平台的字符编码方案,因此,使用 Unicode 的 Java 应用非常适合国际化和本地化。
  2. 统一性:使用 Unicode,一个字符在任何系统,任何程序中都有一个唯一的编码,这消除了传统编码方案中常见的不一致性和混淆。
  3. 可扩展性:Unicode 能表示超过 100 万个字符,使 Java 拥有非常广泛的字符集支持。
  4. 简易性:对于程序员而言,解决由于字符编码引起的问题变得更加简单,因为他们不必担心因为平台、语言或编码标准的不同而导致的字符映射问题。

       

不过,也应注意到使用 UTF-16 编码表示所有字符意味着在存储和传输上可能比其他一些变长编码(如 UTF-8)更为低效,尤其是对于以 ASCII 文本为主的数据。UTF-8 是一种变长字符编码,对于 ASCII 范围的字符,它只使用一个字节,而对于其他 Unicode 字符使用 2 到 4 个字节。这使得 UTF-8 在存储和传输上面临的挑战更小,尤其是当数据主要包含 ASCII 文本时。

       

二、JDK 和 JRE 的区别

JDK(Java Development Kit)和 JRE(Java Runtime Environment)是 Java 开发中两个重要的概念。它们之间的区别如下:

  1. JDK(Java Development Kit):JDK是用于开发和编译 Java 程序的工具集合。它包括Java 编译器、Java 虚拟机(JVM)、Java 类库、调试工具和其他一些用于开发 Java 应用程序的实用工具。JDK 是 Java 开发者需要安装的主要组件,它提供了编写、编译和调试 Java 程序所需的所有工具和依赖。

       

  2. JRE(Java Runtime Environment):JRE 是运行 Java 程序所需的环境。它包含 Java 虚拟机(JVM)、Java 类库和支持运行 Java 应用程序所需的其他文件。JRE 只提供运行 Java 程序的能力,无法进行开发和编译。

       

简而言之,JDK 包括了完整的开发工具和运行环境,供开发人员使用。而 JRE 则仅包含运行 Java 程序所需的运行环境,供最终用户使用。

在实际开发过程中,开发者需要安装 JDK 来编写和构建 Java 应用程序,而最终用户只需要安装 JRE 来运行 Java 程序。

       

三、== 和equals 的区别

在 Java 中,"==" 和 "equals" 是用于比较对象的两种不同方法。

  1. "==" 运算符:

  - "==" 运算符用于比较两个对象的引用是否相同,即它们是否指向内存中的相同地址。

  - 当使用 "==" 运算符来比较基本数据类型时,它用于比较它们的值是否相等。

  - 对于引用类型(如对象),"==" 比较的是对象的内存地址,而不是对象的内容。

  示例:

String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // false,因为str1和str2引用了不同的对象
image.gif

  2. "equals" 方法:

  - "equals" 方法是 Object 类的一个方法,在 Java 中的大多数类都会重写这个方法。

  - 默认情况下,"equals" 方法用于比较对象的引用是否相同,即与 "==" 运算符的作用相同。

  - 然而,许多类(例如 String、Integer 等)会重写 "equals" 方法,使其比较对象的内容是否相等。

  示例:

String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1.equals(str2)); // true,因为String类重写了equals方法,比较的是字符串的内容
image.gif

总结来说,"==" 运算符比较的是引用的地址,用于判断两个引用是否指向同一块内存;而 "equals" 方法通常被重写用于比较对象的内容是否相等,具体行为取决于对象所属类的实现。在实际编程中,对于大多数类(如 String、Integer 等),应该使用 "equals" 方法来比较对象的内容。

       

四、构造器Construct是否可被override

在 Java 中,构造器(Constructor)不能被重写(override)。构造器不同于一般的成员方法,它们的主要目的是初始化一个新的对象实例。构造器的名字必须与类的名字完全相同,并且它们没有返回类型。由于这些特点,构造器不能被重写,但是它们可以被重载(overload)。

       

不能被重写(Override):

重写是面向对象编程中的一个概念,它允许子类提供一个特定于子类的方法实现,该方法在父类中已经被定义。但构造器不参与类之间的继承层级。每个类都有自己的构造器,当通过关键字 `new` 创建类的实例时,与这个类对应的构造器会被调用。

       

可以被重载(Overload):

尽管构造器不能被重写,它们可以被重载。这意味着你可以在一个类中定义多个具有不同参数列表的构造器。重载允许构造器有不同的参数数量或类型,从而在创建类的实例时提供灵活性。例如:

public class MyClass {
    public MyClass() {
        // 默认构造器(无参数)
    }
    
    public MyClass(int param) {
        // 带有一个整型参数的构造器
    }
    
    public MyClass(String param) {
        // 带有一个字符串参数的构造器
    }
    
    // 其他可能的构造器重载...
}
image.gif

在这个例子中,`MyClass` 有三个构造器,它们都有不同的参数列表。这样,根据传递给 `new` 表达式的参数的数量和类型,会调用不同的构造器。

       

综上所述,Java 中的构造器可以被重载,但它们是和类紧密关联的,因此不能被子类重写。

       

五、两个对象的hashCode()相同,则equals()也一定为true对吗

不一定。

根据 Java 中的规定,如果两个对象的 `hashCode()` 相同,它们可能相等,但并不一定相等。这是因为 `hashCode()` 方法是用于获取对象的哈希码的,而 `equals()` 方法是用于比较对象的内容是否相等的。

在 Java 中,当两个对象通过 `equals()` 方法比较时,如果它们的 `hashCode()` 相同,那么 `equals()` 方法一定会返回 `true`。但是,当两个对象的 `hashCode()` 相同时,它们的 `equals()` 方法并不一定会返回 `true`。这是因为哈希冲突的存在,即不同的对象可能因为哈希算法的限制导致具有相同的哈希码。

因此,在实际编程中,需要根据具体类的实现来判断,确保重写了 `equals()` 方法的类也适当地重写了 `hashCode()` 方法,以保证对象在使用哈希表等数据结构时的正确性。

       

六、对象实体与对象引用

在 Java 中,对象实体和对象引用是两个相关但不同的概念,它们之间的区别主要涉及到存储的内容和在内存中的位置。

1. 对象实体(Object Entity):

  • 对象实体是指在内存中分配的实际数据结构,用来表示一个特定类的实例。
  • 它包含了对象的属性和方法所需要的存储空间,实际上就是对象所占据的内存区域。
  • 一个对象实体可以被创建、初始化、访问和操作。

       

2. 对象引用(Object Reference):

  • 对象引用是一个指向对象实体内存地址的值,它被用来访问对象实体。
  • 定义了对象类型的引用可以指向相应类型的对象实体,也可以指向对象实体的父类。
  • 对象引用本质上是一个指针,它存储了对象实体在内存中的位置信息。

       

举个例子来说明这两者之间的关系:

class MyClass {
    // 类的定义
}
public class Main {
    public static void main(String[] args) {
        // 创建对象实体并将其引用保存在对象引用中
        MyClass obj = new MyClass();
        // 在这里,obj 是对象引用,它指向新创建的 MyClass 对象实体
    }
}
image.gif

在这个例子中,`obj` 是对象引用,它存储了对新创建的 `MyClass` 对象实体的引用。当我们调用 `obj` 引用指向的对象的方法或访问其属性时,实际上是在操作对象实体。

       

总的来说,对象引用是用来操作和访问对象实体的,而对象实体则是在内存中分配的包含数据和方法的实际存储空间。

       

七、构造方法有哪些特性

在 Java 中,构造方法有以下特性:

  1. 与类同名:构造方法的名称与其所在类的名称完全相同,且没有返回类型(包括 void)。
  2. 用于对象实例化:构造方法用于创建对象实例时被调用,当使用 `new` 关键字创建一个新的对象时,构造方法会被自动调用。
  3. 没有返回值:构造方法没有返回值,因为它们返回的是实例化后的对象本身。
  4. 可以重载:与其他方法一样,构造方法也可以进行重载,即在同一个类中可以有多个构造方法,它们具有相同的名称但参数列表不同。
  5. 可以有访问修饰符:构造方法可以使用访问修饰符(public、protected、private、默认),用于控制构造方法的访问范围。
  6. 可以有参数:构造方法可以接受一个或多个参数,通过参数可以为实例变量提供初始值。
  7. 可以调用其他构造方法:在一个类的构造方法中,可以使用 `this()` 关键字来调用该类的其他构造方法,以避免重复的代码。
  8. 自动调用父类的无参构造方法:如果子类的构造方法中没有显式调用父类的构造方法,那么编译器会自动在子类的构造方法中插入对父类的无参构造方法的调用。
  9. 可以抛出异常:构造方法可以声明抛出异常,以便处理构造过程中可能出现的异常情况。
  10. 对静态成员没有访问权限:在构造方法中不能直接访问静态成员(静态变量和静态方法),可以通过类名访问。

       

需要注意的是,如果一个类没有定义任何构造方法,编译器会自动为其生成一个默认的无参构造方法。如果类中定义了至少一个构造方法,则编译器不会自动生成默认构造方法。

 

       

八、无参数的构造方法的作用

在 Java 中,无参数的构造方法,通常被称为默认构造方法(default constructor),它有几个主要作用:

 1. 实例化对象:无参数的构造方法允许你创建一个类的实例而不需要提供任何额外的信息。这样的构造方法最简单,仅需使用 `new` 关键字和类名即可创建对象,例如 `new MyClass()`。

2. 初始化状态:虽然构造方法没有参数,但它仍然可以用来初始化实例变量(成员变量)为默认值或预设的固定值。在构造方法体内,可以进行变量赋值、数据结构的初始化、或其他任何启动对象需要的操作。

 3. 提供无特定初始化需求的构造方法:如果定义了带参数的构造方法后没有手动提供一个无参数的构造方法,Java 编译器不会自动生成默认构造方法。这时,如果你想要允许使用者或其他类使用无参数构造方法创建类的实例,就需要显式定义一个这样的构造方法。

       

当定义一个类而没有提供任何构造方法时,Java 编译器会默认提供一个公共的无参数构造方法,以便可以创建类的实例。例如:

public class MyClass {
    // 成员变量
    private int someValue;
    // 无参数的构造方法
    public MyClass() {
        // 这里可以进行各种初始化操作
        someValue = 42; // 可以对成员变量初始化
    }
    // ... 类的其他部分 ...
}
image.gif

在这段代码中,MyClass`有一个无参数的构造方法,允许对象在初始化时将 someValue 设置为 42。如果 MyClass 中没有显式的构造方法,Java 将提供一个默认的无参数构造方法,且不进行任何初始化操作。

       

总结一下,无参数的构造方法允许对象的简单创建和初始化,即使在不提供初始化数据的情况下也能保证对象能够被实例化。

       

九、什么是多态,Java如何实现多态?

多态是面向对象编程中的一个重要概念,它允许不同类的对象对同一消息作出不同的响应。在具体实现上,多态允许一个父类的引用指向其子类的对象,并根据实际指向的对象的类型来调用相应的方法。在 Java 中,多态可以通过以下几种方式实现:

       

 1. 方法重载:

  • 在同一个类中,方法名相同,但形参列表不同,实现了多态。

       

 2. 方法重写:

  • 子类可以重写(覆盖)其父类的方法,实现多态。在父类引用中调用该方法时,根据实际指向的子类对象的类型来调用相应的方法实现。

       

 3. 接口实现:

  • Java 中的接口可以被多个类实现,一个接口类型的引用变量可以指向实现该接口的任意类的对象,从而实现多态。

       

 4. 向上转型:

  • 通过将子类实例赋值给父类类型的引用变量,实现了多态。通过父类的引用,可以调用子类覆盖的方法,实现不同的行为。

       

 5. 运行时的多态性:

  • 在运行时,根据引用指向的实际对象的类型来决定调用的方法,实现了多态性。

       

这里举一个基本示例来说明 Java 中如何实现多态:

class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}
class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof woof");
    }
}
class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog(); // 向上转型
        Animal myCat = new Cat(); // 向上转型
        myDog.makeSound(); // 调用 Dog 类的 makeSound 方法
        myCat.makeSound(); // 调用 Cat 类的 makeSound 方法
    }
}
image.gif

在这个示例中,myDog 和 myCat 都是 Animal 类型的引用,分别指向 Dog 和 Cat 对象。在调用 makeSound 方法时,根据实际指向的对象的类型,分别调用了 Dog 和 Cat 中覆盖的 makeSound 方法,实现了多态性。

       

十、import java和javax

在 Java 中,关键字 `import` 用于导入其他 Java 包中的类或接口,以便可以在当前代码中使用它们。Java 中的包(package)是一种命名空间机制,用于组织类和接口,防止命名冲突。

       

包 java.* 和 javax.* 都是 Java 标准库(Java Standard Edition, SE)的一部分,但在传统上,它们用于表示不同的内容和历史背景:

 1. java.* 包:这是最基本的 Java 标准库的一部分,涵盖了 Java 语言的核心功能。这些核心包是每个 Java 程序必须或可能需要使用的,例如 java.lang, java.util, java.io, java.math, java.net 等。这些都是 Java 最基本的类和接口集合,提供了允许编程语言正常运行的基本功能。

       

 2. javax.* 包:javax.* 系列包最初是作为扩展到 java.* 包的标准 Java API 的一部分被引入的。javax.* 包含扩展功能(如 Swing GUI 组件在 javax.swing 中),一些标准扩展(如 javax.servlet 用于服务端应用),以及后来加入到 Java 平台的其他 API。总体上说,javax.* 包更多关注附加功能,这些功能并不属于 Java 语言的基础核心。

       

随着 Java 的发展,javax.* 包容纳的内容越来越庞大,但总体策略仍然是将 Java 核心功能保持在 java.* 包中,而将可选或者扩展功能放在 javax.* 包里。

       

这种分区主要是源于传统和版本控制的需要,并且,随着时间的推移以及 Java 平台的演进,这个区别逐渐变得模糊。尤其是在 Java 平台模块化(Project Jigsaw)实施后,部分 javax.* 的 API 也被认为是不可或缺的一部分。而在 Java EE(现在是 Jakarta EE)中,很多与企业级应用相关的 API 前缀都是 javax.*。随着 Java EE 的演变,这个前缀也可能会改变,因为一些原来属于 Java EE 的技术已经迁移到了由 Eclipse Foundation 管理的 Jakarta EE,并且新的 API 前缀已经改变为 jakarta.*。

       

总的来说,java.* 包和 javax.* 包的区别更多的反映了 Java 平台的发展历史和命名惯例,而不是它们在功能或使用上的根本差异。随着 Java 技术的发展,新的包和命名策略可能会继续出现。

       

十一、成员变量和局部变量

在 Java 中,成员变量和局部变量是区别于两种不同作用域和生命周期的变量。

 1. 成员变量(Member Variables):

  • 也称为字段(fields),它们定义在类的范围内,成为类的属性。
  • 成员变量的作用域是整个类,它们可以在类的任何方法内被访问(遵循访问控制符,如 private, protected, 和 public)。
  • 它们的生命周期与对象的生命周期相同,当一个对象被创建时成员变量也被创建,对象被销毁时它们也随之销毁。
  • 成员变量可以有默认值,例如 int 类型的成员变量默认值是 0,对象引用(比如 String)的默认值是 null。
  • 成员变量可以是实例变量(每个对象实例有自己的一份副本)或者静态变量(属于类本身,类的所有实例共享这个变量)。

       

2. 局部变量(Local Variables):

  • 定义在方法中、构造方法中或者代码块中。
  • 它们的作用域限制在声明它们的方法或代码块内。
  • 局部变量的生命周期从它们被声明的地方开始,到它们所在的块执行完毕时结束。
  • 在使用局部变量之前必须进行初始化,否则编译器会报错,因为它们没有默认值。
  • 局部变量只在当前方法或代码块的执行过程中可见,外部无法访问它们。

       

下面是成员变量和局部变量的简单示例:

public class MyClass {
    // 成员变量
    private int memberVar;
    public MyClass() {
        // 局部变量在构造方法中
        int localVar = 10;
        this.memberVar = localVar;
    }
    public void someMethod() {
        // 局部变量在方法中
        int anotherLocalVar = 20;
    }
    // ... 更多的方法 ...
}
image.gif

在上述示例中,memberVar 是一个成员变量,它可以在 MyClass 类的任何方法中使用。localVar 和 anotherLocalVar 是局部变量,它们各自在构造方法和 someMethod 方法中声明。每个局部变量只能在其所属的代码块内部使用,它们在这些代码块外部是不可见的。

       

十二、类、方法、成员变量和局部变量的可用修饰符

在 Java 中,修饰符可以用于类、方法、成员变量和局部变量。这些修饰符主要用于控制这些元素的访问权限,以及它们的行为和属性。以下是可以应用于各种 Java 元素的修饰符列表。

       

类的可用修饰符:

  • public: 类可以被任何其他类访问。
  • default (无修饰符): 类只能被同一个包内的类访问。
  • final: 类不能被继承。
  • abstract: 类不能被实例化,它必须被其他类继承并提供具体实现。
  • strictfp: 类中所有的浮点运算都必须严格遵守 IEEE 754 规范。

       

方法的可用修饰符:

  • public: 方法可以被任何其他类访问。
  • protected: 方法可以被同一个包内的类以及其子类访问。
  • default (无修饰符): 方法只能被同一个包内的类访问。
  • private: 方法只能被声明它的类访问。
  • final: 方法不能被子类重写。
  • static: 方法属于类,而不是类的实例。
  • abstract: 方法没有具体实现,必须被子类实现。
  • synchronized: 方法在同一时刻只能被一个线程访问。
  • native: 方法在本地代码(如 C/C++)中实现。
  • strictfp: 方法中的浮点运算都必须严格遵守 IEEE 754 规范。

       

成员变量的可用修饰符:

  • public: 变量可以被任何其他类访问。
  • protected: 变量可以被同一个包内的类以及其子类访问。
  • default (无修饰符): 变量只能被同一个包内的类访问。
  • private: 变量只能被声明它的类访问。
  • static: 变量属于类,而不是类的某个实例;它是一个共享变量。
  • final: 变量一旦被初始化后不能被改变;常量变量名通常用全大写字母表示。
  • transient: 变量在对象序列化时不会被序列化。
  • volatile: 变量的值将从主存中读取,每次更改后立即同步回主存,确保线程之间的可见性。

       

局部变量的可用修饰符:

  • final: 局部变量一旦被初始化后不能被改变。

       

局部变量不能被访问控制修饰符(如 public、protected、private)修饰,也不能被 static、transient 或 volatile 修饰,因为它们是方法或代码块执行时临时创建的,并且在方法或代码块的作用域外不可见和不可用。

       

注意,Java 的修饰符必须按照一定的规则组合使用。例如,abstract 和 final 不能同时修饰一个类,因为这两个修饰符的语义是相互矛盾的。同样的,abstract 和 private 修饰符组合在一起修饰方法也是没有意义的,因为无法实现或重写一个私有的抽象方法。

       

十三、静态方法和实例方法有何不同

在 Java 中,静态方法和实例方法有以下不同之处:

 1. 关联的对象不同:

  • 实例方法属于对象实例,必须通过对象实例来调用。
  • 静态方法属于类本身,可以直接通过类名调用,无需实例化对象。

       

2. 访问限制不同:

  • 实例方法可以访问和修改对象的实例变量,并且可以调用其他实例方法。
  • 静态方法不能直接访问实例变量或调用实例方法,只能访问和修改静态变量。

       

 3. 使用方式不同:

  • 实例方法适用于需要操作对象实例的场景,可以在方法内部直接使用对象的实例变量和其他实例方法。
  • 静态方法适用于不需要操作对象实例而仅仅执行某项任务的场景,例如进行某个计算、打印日志等。

       

 4. 内存中的位置不同:

  • 实例方法在调用时需要创建对象实例,将方法的调用堆栈保存在堆内存,即每个对象都有自己的实例方法。
  • 静态方法在类加载时已经存在于方法区中,它们属于类本身。无论创建多少个对象实例,静态方法在内存中只有一份。

       

 5. 重写方式不同:

  • 实例方法可以被子类重写(override),即在子类中重新实现相同名称和参数的方法,以覆盖父类的实现。
  • 静态方法不能被重写。在子类中定义与父类中相同名称和参数的静态方法,实际上是隐藏了父类中的静态方法。

       

下面是一个简单的示例代码,用于演示静态方法和实例方法的使用:

public class MyClass {
    private static int staticVar;
    private int instanceVar;
    public static void staticMethod() {
        // 静态方法
        staticVar = 10;
        // 不能访问实例变量或实例方法
        // instanceVar = 20;  // 错误!无法访问实例变量
        // instanceMethod(); // 错误!无法调用实例方法
    }
    public void instanceMethod() {
        // 实例方法
        instanceVar = 20;
        // 可以访问和修改实例变量
        staticVar = 30;
        // 可以调用其他实例方法
        someOtherMethod();
    }
    private void someOtherMethod() {
        // 其他实例方法
    }
}
image.gif

在上述示例中,staticMethod() 是一个静态方法,它可以直接访问和修改静态变量 staticVar,但无法访问和修改实例变量 instanceVar 或调用实例方法 instanceMethod()。

instanceMethod() 是一个实例方法,它可以访问和修改实例变量 instanceVar,并且可以调用其他实例方法 someOtherMethod()。同时,也能够访问和修改静态变量 `staticVar`。

       

十四、静态方法内调用一个非静态成员为什么是非法

在 Java 中,静态方法是属于类的,而不是属于类的某个具体实例。因此,静态方法在调用时不需要类的实例。相反,非静态成员(如非静态方法和非静态变量)是属于类的实例的,需要一个具体的对象实例去访问它们。

当你在一个静态方法内试图直接调用非静态成员时,会出现问题,因为静态方法不绑定到任何实例上,所以它并不知道该如何访问那些绑定到对象实例的成员。因此,编译器会抛出一个错误,因为在没有对象实例的情况下不能直接访问非静态成员。这个规则保证了在调用成员时上下文的清晰和一致性。

以下是一个演示这个概念的简单示例:

public class MyClass {
    int nonStaticVariable; // 非静态成员变量
    public void nonStaticMethod() {
        // 非静态方法
    }
    public static void staticMethod() {
        // 错误: 非静态的变量和方法不能从静态的上下文中直接访问
        nonStaticVariable = 10; // 非法操作
        nonStaticMethod();      // 非法操作
    }
}
image.gif

要从静态方法中访问非静态成员,你必须首先创建类的实例,然后通过这个实例去访问成员。例如:

public static void staticMethod() {
    MyClass instance = new MyClass();
    instance.nonStaticVariable = 10; // 通过实例访问
    instance.nonStaticMethod();      // 通过实例访问
}
image.gif

在这个修改后的示例中,我们先创建了 MyClass 类的一个实例,然后通过这个实例去访问非静态变量和方法,这样的调用是合法的。

       

十五、final的作用

在 Java 中,`final` 关键字有多种用途和作用,具体如下:

  1. 定义常量:使用 `final` 关键字可以将一个变量声明为常量,一旦赋值后不可再修改。常量的命名一般使用全大写的格式,多个单词之间用下划线分隔。

final int MAX_VALUE = 100;
image.gif

  2. 防止继承:使用 `final` 关键字可以修饰一个类,表示该类不可被继承。

final class FinalClass {
  // ...
}
image.gif

  3. 禁止方法重写:使用 `final` 关键字可以修饰一个方法,表示该方法不可被子类重写。

class ParentClass {
  final void someMethod() {
    // ...
  }
}
image.gif

  4. 保护对象的不可变性:使用 `final` 关键字可以修饰类的成员变量,表示该变量一旦赋值后不可再进行修改。这通常用于保护对象的状态不被修改。

class ImmutableClass {
  private final int value;
  public ImmutableClass(int value) {
    this.value = value;
  }
  public int getValue() {
    return value;
  }
}
image.gif

  5. 提高性能:在某些情况下,使用 `final` 关键字修饰的变量或方法可以在编译期间进行优化,提高程序的执行效率。

       

总的来说,`final` 关键字可以用来定义常量、控制类和方法的继承与重写,以及保护对象的不可变性。它有助于提高代码的可靠性、安全性和性能。

       

十六、java 中的Math.round(-1.5)等于多少

Math 类提供的一些常见的数学函数包括:

  • 绝对值:Math.abs(x) 返回 x 的绝对值。
  • 四舍五入:Math.round(x) 返回最接近 x 的 long 型或 int 型整数值。
  • 最大值:Math.max(x, y) 返回两个参数中的最大值。
  • 最小值:Math.min(x, y) 返回两个参数中的最小值。
  • 开方:Math.sqrt(x) 返回 x 的平方根。
  • 三角函数:如 Math.sin(x) 返回 x 指定角度的正弦值。
  • 指数函数:Math.exp(x) 返回 e 的 x 次方。
  • 对数函数:Math.log(x) 返回 x 的自然对数。

       

在 Java 中,Math.round() 对于负数使用的是半整数舍入,所以对 -1.5 的处理会取"最接近"整数,但是当数字恰好位于中间位置时,它会舍去半数并取靠近正无穷的那个整数。这意味着 Math.round(-1.5) 会得到 -1 而不是 -2,因为 -1 是离 -1.5 更近的整数。

       

十七、String是否属于基础数据类型

在 Java 语言中,`String` 并不属于基本数据类型,它是一个引用数据类型。Java 的基本数据类型包括八种:

  1. 整数类型:byte, short, int, long
  2. 浮点类型:float, double
  3. 字符类型:char
  4. 布尔类型:boolean

       

这些基本数据类型都不是对象,它们是存储在栈上的简单值,并且有固定的大小和取值范围。而 `String` 是 Java 中一个代表文本的类,它的实例可以用来存储和操作字符序列。字符串在 Java 中是不可变的,即一旦创建,它们的值不能被改变。

当你创建一个 `String` 实例的时候,实际上是在堆上创建了一个对象,你可以通过引用来操作这个对象:

String str = "Hello, World!";
image.gif

在这段代码中,`str` 是一个引用变量,它指向实际数据存储在堆内存中的 `String` 对象。

       

十八、String str="i"与String str=new String("i")一样吗

在大多数情况下,`String str = "i"` 和 `String str = new String("i")` 是等价的,它们都可以用来创建一个包含字符串 "i" 的 `String` 对象。

       

然而,两者在内存中的具体操作有所不同。

  1. String str = "i":这种方式使用字符串字面值创建一个 `String` 对象。在 Java 中,有一个字符串常量池(String Pool),它保存着所有字符串字面值的引用。当你使用字符串字面值创建 `String` 对象时,如果字符串常量池中已经存在该字符串,则直接返回对该字符串的引用,而不会创建新的对象。这意味着如果其他变量也引用了同一个字符串字面值,它们实际上引用的是同一个对象。

       

  2. String str = new String("i"):这种方式使用 `new` 关键字显式地创建一个新的 `String` 对象。无论字符串常量池中是否已经存在该字符串,都会在堆上创建一个新的对象。这意味着每次使用 `new` 创建的 `String` 对象都是独立的,即使它们的内容相同。

       

所以,尽管这两种方式都可以创建包含相同字符串的 `String` 对象,但在实际使用中,通常更推荐使用 `String str = "i"` 这种方式,因为它可以节省内存,并且因为字符串是不可变的,所以在大多数情况下,不需要创建多个相同内容的 `String` 对象。

       

十九、java反转字符串

在 Java 中,你可以使用多种方法来反转一个字符串。下面介绍几种常用的方法:

  1. 使用字符数组:将字符串转换为字符数组,然后倒序遍历数组并将每个字符拼接到一个新的字符串中。

public static String reverseString(String str) {
    char[] charArray = str.toCharArray();
    String reversedStr = "";
    for (int i = charArray.length - 1; i >= 0; i--) {
        reversedStr += charArray[i];
    }
    return reversedStr;
}
image.gif

  2. 使用 StringBuilder 或 StringBuffer:这两个类都提供了 `reverse()` 方法来反转字符串,其中 `StringBuilder` 是非线程安全的,而 `StringBuffer` 是线程安全的。

public static String reverseString(String str) {
    StringBuilder sb = new StringBuilder(str);
    return sb.reverse().toString();
}
image.gif

  3. 使用递归:递归地调用反转方法,并将字符串的首字符与其余部分的反转结果连接起来。

public static String reverseString(String str) {
    if (str.isEmpty()) {
        return str;
    } else {
        return reverseString(str.substring(1)) + str.charAt(0);
    }
}
image.gif

这些都是常用的方法,你可以根据自己的需要选择适合的。注意,以上方法中,字符串在 Java 中是不可变的,所以每次字符串连接操作都会生成一个新的字符串对象,所以如果需要频繁地反转字符串,最好使用 `StringBuilder` 或 `StringBuffer`,以避免不必要的对象创建和性能开销。

       

二十、String类的常用方法

`String` 类在 Java 中非常常用,并且包含了大量方法来方便地操作字符串。以下是一些常用的 `String` 方法及其简要说明:

  1. `length()`: 返回字符串的长度(字符数)。

int length = "Hello".length(); // 结果为 5
image.gif

  2. `charAt(int index)`: 返回指定索引处的字符。

char ch = "Hello".charAt(1); // 结果为 'e'
image.gif

  3. `substring(int beginIndex)`: 返回一个新字符串,它是此字符串的一个子字符串。

String sub = "Hello".substring(2); // 结果为 "llo"
image.gif

  4. `substring(int beginIndex, int endIndex)`: 返回一个新字符串,它是此字符串的一个子字符串。

String sub = "Hello".substring(1, 3); // 结果为 "el"
image.gif

  5. `contains(CharSequence s)`: 当且仅当此字符串包含指定的字符序列时,返回 true。

boolean contains = "Hello".contains("ll"); // 结果为 true
image.gif

  6. `startsWith(String prefix)`: 测试此字符串是否以指定的前缀开始。

boolean startsWith = "Hello".startsWith("He"); // 结果为 true
image.gif

  7. `endsWith(String suffix)`: 测试此字符串是否以指定的后缀结束。

boolean endsWith = "Hello".endsWith("lo"); // 结果为 true
image.gif

  8. `indexOf(int ch)`: 返回指定字符在此字符串中第一次出现处的索引。

int index = "Hello".indexOf('l'); // 结果为 2
image.gif

  9. `lastIndexOf(int ch)`: 返回指定字符在此字符串中最后一次出现处的索引。

int lastIndex = "Hello".lastIndexOf('l'); // 结果为 3
image.gif

  10. `equals(Object anObject)`: 比较此字符串与指定对象的相等性。

boolean equals = "Hello".equals("hello"); // 结果为 false
image.gif

  11. `equalsIgnoreCase(String anotherString)`: 将此字符串与另一个字符串比较,不考虑大小写。

boolean equalsIgnoreCase = "Hello".equalsIgnoreCase("hello"); // 结果为 true
image.gif

  12. `toUpperCase()`: 返回一个字符串,它是将此字符串中所有字符都转换为大写后的结果。

String upperCase = "Hello".toUpperCase(); // 结果为 "HELLO"
image.gif

  13. `toLowerCase()`: 返回一个字符串,它是将此字符串中所有字符都转换为小写后的结果。

String lowerCase = "Hello".toLowerCase(); // 结果为 "hello"
image.gif

  14. `replace(char oldChar, char newChar)`: 返回一个新字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。

String replaced = "Hello".replace('l', 'p'); // 结果为 "Heppo"
image.gif

  15. `replaceAll(String regex, String replacement)`: 用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。

String replacedAll = "Hello".replaceAll("l", "p"); // 结果为 "Heppo"
image.gif

  16. `trim()`: 返回一个字符串,其值为此字符串,并删除任何前导和尾随空格。

String trimmed = "  Hello  ".trim(); // 结果为 "Hello"
image.gif

  17. `split(String regex)`: 根据给定正则表达式的匹配拆分此字符串。

String[] parts = "a,b,c".split(","); // 结果为 {"a", "b", "c"}
image.gif

  18. `join(CharSequence delimiter, CharSequence... elements)`: 返回一个新的 `String`,由指定元素通过指定分隔符拼接而成。

String joined = String.join("-", "a", "b", "c"); // 结果为 "a-b-c"
image.gif

  19. `format(String format, Object... args)`: 使用指定的格式字符串和参数返回一个格式化的字符串。

String formatted = String.format("Name: %s, Age: %d", "Alice", 24); // 结果为 "Name: Alice, Age: 24"
image.gif

       

以上是 `String` 类中常用的一些方法,还有很多其它的方法存在,但这已经包含了日常编程中的大部分需求。

       

二十一、字符串常量和字符串常量池

在 Java 中,字符串常量和字符串常量池是两个与字符串存储相关的概念。让我们来分别理解这两个概念:

字符串常量

   字符串常量是指在 Java 代码中直接用双引号括起来的字符串字面量。例如:

String hello = "Hello, World!";
image.gif

   这里 `"Hello, World!"` 就是一个字符串常量。在 Java 程序中,每当编写这样的字符串字面量时,编译器会创建一个字符串对象,并将其存储在字符串常量池中。

       

字符串对象是不可变的,这意味着创建后,它的内容无法更改。如果需要一个修改过的字符串,JVM 会创建一个新的字符串对象来存储修改后的文本。

       

字符串常量池(String Intern Pool)

   字符串常量池是 Java 堆内存的一部分,用来存储在编译期间已知的字符串字面量和运行时使用 `String.intern()` 方法显式地加入池中的字符串。字符串常量池的目的是减少在 Java 中字符串对象的数量。

       

   因为字符串是不可变的,JVM 可以优化字符串存储通过共享字符串字面量。若程序中存在多个相同内容的字符串常量,编译器会确保它们引用堆中的同一字符串对象。举个例子,看下面的代码段:

String str1 = "Java";
String str2 = "Java";
image.gif

   在这里,尽管我们声明了两个字符串对象,`str1` 和 `str2`,但它们都会指向字符串常量池中的同一个 "Java" 字符串对象,因此避免了不必要的对象创建,从而提高内存效率。

字符串常量池最初位于永久代(PermGen space),但从 Java 7 开始,它被移动到了堆内存中。

       

使用 `intern()` 方法

   String 类的 `intern()` 方法允许将在运行时创建的字符串添加到字符串常量池中。如果池中已有等于该字符串的字符串(使用 `equals()` 方法比较),则返回池中的字符串。否则,会将该字符串添加到池中,并返回其引用。例如:

String s1 = new String("intern");
String s2 = s1.intern();
image.gif

   在这个示例中,s1 引用一个不在常量池中的 `String` 对象,而 `s2` 引用的是常量池中的相同字符串。

       

请注意,`intern()` 方法的使用通常是为了性能优化,但在不恰当的场合使用它可能会带来额外的开销,因此需要谨慎使用。

       

二十二、抽象类必须要有抽象方法吗

不,抽象类不一定要含有抽象方法。在 Java 中,抽象类是使用 abstract 关键字来声明的,它可以包含抽象方法也可以不包含。抽象方法是一种没有实现的方法,它同样使用 abstract 关键字声明并且没有方法体。

尽管一个抽象类没有抽象方法,它还是不能被实例化,意味着你不能直接创建抽象类的对象。通常,如果你想阻止类的实例化,并且为后续的子类提供一个通用的基类,你可能会使用没有抽象方法的抽象类。

       

创建没有抽象方法的抽象类可能用于几种情况:

  1. 当一个类正在被开发过程中,原本预定要有抽象方法,但在当前版本中还未实现。
  2. 当一个类被设计为提供一些通用功能,但不希望该类被直接实例化。
  3. 当一个类要强制子类重写其方法,但没有抽象方法需要提供。

一旦一个抽象类包含了至少一个抽象方法,这个类就不能再直接实例化,并且子类在成为具体类之前必须实现所有的抽象方法。

       

当一个类被声明为抽象类时,它可以有抽象方法,非抽象方法,或者两者都有。

如果一个类包含至少一个抽象方法,那么这个类必须被声明为抽象类。抽象方法是一种没有具体实现的方法,它只有方法的签名,而没有实际的代码。

抽象类的主要目的是作为其他类的基类,它定义了一个接口或者一组方法,而具体的实现由其子类提供。子类必须实现抽象类中的所有抽象方法,除非子类本身也是抽象类。

       

下面是一个示例,展示了一个抽象类和一个继承它的具体子类:

// 抽象类
abstract class Animal {
    // 抽象方法
    public abstract void makeSound();
    
    // 非抽象方法
    public void sleep() {
        System.out.println("Animal is sleeping");
    }
}
// 具体子类
class Dog extends Animal {
    // 实现抽象方法
    public void makeSound() {
        System.out.println("Dog is barking");
    }
}
public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.makeSound(); // 输出 "Dog is barking"
        dog.sleep();     // 输出 "Animal is sleeping"
    }
}
image.gif

在上面的例子中,Animal 类是一个抽象类,它有一个抽象方法 makeSound() 和一个非抽象方法 sleep()Dog 类继承了 Animal 类,并实现了 makeSound() 方法。可以创建一个 Dog 类的实例,并调用其方法。

       

总结来说,抽象类可以有抽象方法也可以没有,它提供了一种可以定义和实现子类所需功能的机制。

       

二十三、抽象类能使用final修饰吗

在 Java 中,抽象类和 final 关键字有着对立的含义,因此抽象类不能同时使用 final 修饰。

       

当一个类被声明为抽象类时,它的主要目的是为了被其他非抽象类继承,并且子类必须实现抽象类中的抽象方法。抽象类的目的在于提供一种基础框架,而让其子类来进行具体实现。

       

相反,当一个类被声明为 final 时,它变得无法被继承,无法被其他类所扩展或修改。final 类的主要目的在于确保这个类不会有子类,从而保持类的稳定性和不可变性。

       

因此,在设计上,抽象类和 final 类是有着明显对立的概念。一个类要么是设计为可以被继承和扩展,要么是设计为不可被继承和修改。因此,Java 不允许将抽象类和 final 关键字一起使用。

       

如果尝试在 Java 代码中这样声明一个抽象类:

final abstract class MyAbstractClass {
    //...
}
image.gif

编译器将会报错,因为这个声明试图同时禁止继承(通过 final)并且要求继承(通过 abstract)。这两个条件不能同时成立。

       

综上所述,抽象类不能使用 final 修饰,这两个关键字在 Java 中代表了相互对立的概念。

       

二十四、接口和抽象类

接口(Interface)和抽象类(Abstract Class)都是 Java 用来实现抽象概念的机制,但它们之间有一些关键性的区别:

  1. 抽象方法:

  • 抽象类:可以包含抽象方法(没有实现的方法)和具体方法(实现了的方法)。
  • 接口:在 Java 8 之前,接口只能包含抽象方法。自 Java 8 起,接口可以包含默认方法和静态方法。

  2. 成员变量:

  • 抽象类:可以包含成员变量,这些变量可以是非 final 和非 static 的,也就是说,它们可以有各种访问权限。
  • 接口:在 Java 8 之前,接口只能包含 public static final 的常量。自 Java 8 起,接口也可以包含私有方法。

  3. 继承和实现:

  • 抽象类:一个类只能继承自一个抽象类。
  • 接口:一个类可以实现多个接口。

  4. 构造函数:

  • 抽象类:可以有构造函数。
  • 接口:不能有构造函数。

  5. 多态性:

  • 抽象类:继承抽象类的子类,如果不是抽象类,那么必须实现父类的所有抽象方法。
  • 接口:实现接口的类必须实现接口中所有的抽象方法,或者如果是接口的子接口,则可以不实现这些方法。

  6. 访问修饰符:

  • 抽象类:方法和属性可以有任何访问修饰符。
  • 接口:在 Java 9 之前,接口中的方法和变量默认都是 public 的。自 Java 9 起,接口允许私有方法和私有静态方法。

  7. 使用场景:

  • 抽象类:通常当有些共通的功能(包括字段和方法实现)需要在几个密切相关的类之间共享时,会使用抽象类。
  • 接口:更多用于定义不同类之间共享的不同行为协议,或者当你希望多个类实现多种能力时。

       

在 Java 8 及之后的版本中,接口和抽象类之间的界限变得模糊,因为接口现在可以包含具有默认实现的方法。不过,如果你的设计中涉及到某些共享状态或字段,通常还是会选择使用抽象类。

       

二十五、Java中IO流分为几种

在Java中,I/O流(Input/Output流)是用于读取和写入数据的一系列类和接口。Java的I/O流主要分为四种类型,基于数据处理的方式和用途进行分类:

  1. 按照数据流的方向:

  • 输入流(Input Stream):用于从源读取数据。
  • 输出流(Output Stream):用于向目标写入数据。

  2. 按照处理数据的单位:

  • 字节流:以字节为单位处理数据,对于非文本文件,如图像和音频视频文件,通常使用字节流。在`java.io`包中,所有以`Stream`结尾的类都是字节流的类。最常用的字节流类是`FileInputStream`和`FileOutputStream`。
  • 字符流:以字符为单位处理数据,适用于处理文本文件。在`java.io`包中,所有以`Reader`和`Writer`结尾的类都是字符流的类。最常用的字符流类是`FileReader`和`FileWriter`。

  3. 是否直接与数据源或目的地交互:

  • 节点流(Node Stream):直接与数据源或目的地交互的流。
  • 处理流(Processor/Wrapper Stream):包装了另一个流(节点流或其他处理流)的流,用于提供额外的功能,比如进行缓冲、字符到字节的转换、数据的序列化和反序列化等。例如`BufferedReader`包装了一个`FileReader`,以提供缓冲功能,提高读取效率。

       

更细致地说,处理流还可以分为几个辅助分类,例如缓冲流(`BufferedInputStream`, `BufferedOutputStream`, `BufferedReader`, `BufferedWriter`),转换流(`InputStreamReader`, `OutputStreamWriter`),数据流(`DataInputStream`, `DataOutputStream`),对象流(`ObjectInputStream`, `ObjectOutputStream`)等,这些都是用于处理特定任务的流。

       

二十六、BIO、NIO和AIO的区别

在 Java 中,涉及到网络和文件 I/O 时,BIO(Blocking I/O)、NIO(New Input/Output)和 AIO(Asynchronous I/O)是三种不同的 I/O 模式,它们分别代表了 Java I/O 的发展的不同阶段和不同的并发处理能力。

  1. **BIO (Blocking I/O)

  • BIO 是 Java 早期的 I/O 模型,它基于流模型实现,为每个连接创建一个线程。
  • BIO 是同步且阻塞的,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,不适用于高性能服务器。
  • 在 Java 中,BIO 主要指的是传统的 `java.io` 包中的类和接口。

       

  2. **NIO (New Input/Output or Non-blocking I/O)

  • NIO 是 Java 1.4 中引入的一个新的 I/O 模型,它不仅包含字节缓冲区 Buffer、通道 Channel 的概念,还支持多路复用器 Selector。
  • NIO 属于同步非阻塞模型,服务器实现模式为一个请求一个线程,即请求到来时会注册到多路复用器上,然后通过一个线程不断在多路复用器上轮询就绪的任务,执行完再处理下一个任务。
  • 在 Java 中,NIO 主要指的是 `java.nio` 包中的类和接口。

       

  3. **AIO (Asynchronous I/O)

  • AIO 是 Java 1.7 中引入的一个新的 I/O 模型,也被称为 NIO.2。
  • AIO 对应于异步非阻塞 IO,服务器实现模式可以理解为一个有效请求一个线程,客户端的 I/O 请求都是异步的,可以直接进行 I/O 操作,不需要像 NIO 那样不断在通道上轮询操作是否完成。
  • 当 I/O 操作完成后,系统会主动调用回调函数通知应用程序进行相应的操作,应用程序可以直接继续执行,不需要等待 I/O 操作的完成。
  • AIO 在 `java.nio.channels` 包中增加了异步通道的概念,提供了 `AsynchronousSocketChannel` 等类。

       

总结:

  • BIO:同步并阻塞 (线程阻塞)
  • NIO:同步非阻塞 (选择器轮询)
  • AIO:异步非阻塞(系统回调通知)

NIO 和 AIO 都是为了解决 BIO 高并发中线程耗用和阻塞问题,NIO 适合连接数目比较多但连接比较短(轻操作)的架构,如聊天服务器,AIO 适合连接数目比较多且连接比较长(重操作)的架构,如相册服务器。

       

二十七、Files的常用方法

在 Java 中,`java.nio.file.Files` 是一个包含了许多静态方法的工具类,这些方法用于操作文件和目录。以下是一些常用的 `Files` 方法:

 1. 检查、读取和写入文件

  • exists(Path path, LinkOption... options): 检查文件是否存在。
  • readAllBytes(Path path): 一次性读取文件的所有字节到一个字节数组中。
  • readAllLines(Path path, Charset cs): 一次性读取所有行到一个字符串列表中。
  • write(Path path, byte[] bytes, OpenOption... options): 将字节写入文件。
  • write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options): 将多行字符串写入文件。

       

 2. 复制、移动和删除操作

  • copy(Path source, Path target, CopyOption... options): 复制文件。
  • move(Path source, Path target, CopyOption... options): 移动或重命名文件。
  • delete(Path path): 删除文件。
  • deleteIfExists(Path path): 如果文件存在,则删除它。

       

 3. 创建文件和目录

  • createFile(Path path, FileAttribute<?>... attrs): 创建一个新文件。
  • createDirectory(Path dir, FileAttribute<?>... attrs): 创建一个目录。
  • createDirectories(Path dir, FileAttribute<?>... attrs): 创建一个目录以及所有不存在的父目录。
  • createTempFile(String prefix, String suffix, FileAttribute<?>... attrs): 创建一个临时文件。
  • createTempDirectory(String prefix, FileAttribute<?>... attrs): 创建一个临时目录。

       

 4. 访问文件属性和元数据

  • size(Path path): 返回文件的大小(字节数)。
  • isRegularFile(Path path, LinkOption... options): 检查是否为一个普通文件。
  • isDirectory(Path path, LinkOption... options): 检查是否为一个目录。
  • isSymbolicLink(Path path)`: 检查是否为一个符号链接。
  • getLastModifiedTime(Path path, LinkOption... options): 获取文件最后修改时间。
  • setLastModifiedTime(Path path, FileTime time): 设置文件最后修改时间。
  • getOwner(Path path, LinkOption... options): 获取文件的所有者。
  • setOwner(Path path, UserPrincipal owner): 设置文件的所有者。
  • getPosixFilePermissions(Path path, LinkOption... options): 获取文件的 POSIX 文件权限。
  • setPosixFilePermissions(Path path, Set<PosixFilePermission> perms): 设置文件的 POSIX 文件权限。

       

 5. 遍历目录

  • newDirectoryStream(Path dir): 打开一个目录,以便遍历它里面的项。

       

6. 文件系统相关

  • isSameFile(Path path, Path path2): 检查两个路径是否表示的是文件系统中同一个文件。
  • getFileStore(Path path): 获取文件所在的文件存储。

       

`Files` 类还提供了多种文件属性视图的访问方式和文件系统工具方法,用于处理高级文件属性或链接文件等场景。这些方法大大简化了文件系统的操作,并且许多方法都利用 `Path` 类来表示文件路径和系统不依赖性。

       

二十八、环境变量Path和ClassPath 的作用是什么?如何设置这两个环境变量

PATH 和 CLASSPATH 是两个常用的环境变量,在 Java 开发中起着重要的作用。

 1. PATH 环境变量的作用:

  • PATH 环境变量用于指定系统在命令行下执行可执行文件时的搜索路径。
  • 当在命令行中输入一个命令时,系统会按照 `PATH` 中指定的路径顺序查找可执行文件,并在找到对应的可执行文件后执行它。
  • 在 Java 开发中,PATH 环境变量通常用于指定 Java 运行时环境的路径,方便在命令行中直接运行 Java 命令(例如 java、javac)。

       

 2. CLASSPATH 环境变量的作用:

  • CLASSPATH 环境变量用于指定 Java 虚拟机(JVM)在运行 Java 程序时搜索类文件的路径。
  • 当 JVM 在运行时需要加载一个类时,它会根据 `CLASSPATH` 中指定的路径顺序查找对应的类文件,然后进行加载。
  • 在 Java 开发中,CLASSPATH 环境变量通常用于指定项目所需的类和库文件的路径,以便 JVM 能够正确地加载和运行项目中的代码。

       

如何设置环境变量:

在不同的操作系统上设置环境变量的步骤会有所不同。

对于 Windows 操作系统:

  1. 右键点击【此电脑】或【我的电脑】,选择【属性】。
  2. 在弹出的窗口中,点击左侧的【高级系统设置】。
  3. 在弹出的窗口中,点击【环境变量】按钮。
  4. 在【用户变量】或【系统变量】中,点击【新建】。
  5. 输入变量名(如 `PATH` 或 `CLASSPATH`)和变量值,点击【确定】。

       

对于 macOS 和 Linux 操作系统:

 1. 打开终端窗口。

 2. 在终端中,输入以下命令来设置环境变量:

  • 设置 `PATH` 环境变量:`export PATH=/path/to/directory:$PATH`
  • 设置 `CLASSPATH` 环境变量:`export CLASSPATH=/path/to/directory:$CLASSPATH`

  (将 `/path/to/directory` 替换为具体的目录路径)

 3. 当前终端窗口中设置的环境变量仅在当前会话中有效。若要使环境变量永久生效,需要将命令添加到 `~/.bashrc` 或 `~/.bash_profile` 文件中。

       

请注意,设置环境变量时需确保路径指定正确,并使用正确的语法和操作系统相关的命令。

相关文章
|
2天前
|
Java 程序员 调度
Java中的多线程编程:基础知识与实践
【5月更文挑战第19天】多线程编程是Java中的一个重要概念,它允许程序员在同一时间执行多个任务。本文将介绍Java多线程的基础知识,包括线程的创建、启动和管理,以及如何通过多线程提高程序的性能和响应性。
|
4天前
|
Java
Java中int[]与Integer[]相互转化的方法,java基础知识面试重点总结
Java中int[]与Integer[]相互转化的方法,java基础知识面试重点总结
|
4天前
|
算法 Java C++
刷题两个月,从入门到字节跳动offer丨GitHub标星16k+,美团Java面试题
刷题两个月,从入门到字节跳动offer丨GitHub标星16k+,美团Java面试题
|
4天前
|
算法 Java Python
保姆级Java入门练习教程,附代码讲解,小白零基础入门必备
保姆级Java入门练习教程,附代码讲解,小白零基础入门必备
|
6天前
|
消息中间件 并行计算 Java
Java中的多线程编程:基础知识与实践
【5月更文挑战第15天】 在现代计算机编程中,多线程是一个复杂但必不可少的概念。特别是在Java这种广泛使用的编程语言中,理解并掌握多线程编程是每个开发者必备的技能。本文将深入探讨Java中的多线程编程,从基础概念到实际应用场景,为读者提供全面的理论支持和实践指导。
|
6天前
|
Java
Java中的多线程编程:基础知识与实践
【5月更文挑战第13天】在计算机科学中,多线程是一种使得程序可以同时执行多个任务的技术。在Java语言中,多线程的实现主要依赖于java.lang.Thread类和java.lang.Runnable接口。本文将深入探讨Java中的多线程编程,包括其基本概念、实现方法以及一些常见的问题和解决方案。
|
6天前
|
SQL Java 关系型数据库
零基础轻松入门Java数据库连接(JDBC)
零基础轻松入门Java数据库连接(JDBC)
13 0
|
6天前
|
存储 安全 算法
Java一分钟之-Java集合框架入门:List接口与ArrayList
【5月更文挑战第10天】本文介绍了Java集合框架中的`List`接口和`ArrayList`实现类。`List`是有序集合,支持元素重复并能按索引访问。核心方法包括添加、删除、获取和设置元素。`ArrayList`基于动态数组,提供高效随机访问和自动扩容,但非线程安全。文章讨论了三个常见问题:索引越界、遍历时修改集合和并发修改,并给出避免策略。通过示例代码展示了基本操作和安全遍历删除。理解并正确使用`List`和`ArrayList`能提升程序效率和稳定性。
12 0
|
6天前
|
Java API 开发工具
java与Android开发入门指南
java与Android开发入门指南
16 0
|
6天前
|
Java
Java一分钟之-类与对象:面向对象编程入门
【5月更文挑战第8天】本文为Java面向对象编程的入门指南,介绍了类与对象的基础概念、常见问题及规避策略。文章通过代码示例展示了如何定义类,包括访问修饰符的适当使用、构造器的设计以及方法的封装。同时,讨论了对象创建与使用时可能遇到的内存泄漏、空指针异常和数据不一致等问题,并提供了相应的解决建议。学习OOP需注重理论与实践相结合,不断编写和优化代码。
31 1