java基础2024/3/13

1、这里有两种方法用来检查字符串 s 是否是字符串 t 的子序列。

class Solution {
    public boolean isSubsequence(String s, String t) {
        int lens = s.length(), lent = t.length();
        {
            int falgi = 0, falgj = 0;
            while (falgi < lent && falgj < lens) {
                if (s.charAt(falgj) == t.charAt(falgi)) {
                    falgj++;
                }
                falgi++;
            }
            return falgj == lens;
        }
    }
}
class Solution {
    public boolean isSubsequence(String s, String t) {
        int n = s.length();
        int m = t.length();

        int i = 0;
        int j = 0;

        char[] arr1 = s.toCharArray();
        char[] arr2 = t.toCharArray();

        while(i < n && j < m){
            if(arr1[i] == arr2[j]) ++i;
            ++j;
        }
        return i == n;
    }
}

两种方法的时间复杂度都是 O(m + n),其中 m 是字符串 t 的长度,n 是字符串 s 的长度。因此从时间复杂度的角度来看,这两种方法是相似的。

然而,实际上方法二更倾向于更快一些,因为它在内部使用了字符数组而不是每次都调用 charAt() 方法。在 Java 中,charAt() 方法是用来从字符串中获取单个字符的。在大多数情况下,直接访问字符数组的效率更高,因为它不需要进行方法调用和边界检查。

因此,尽管两种方法在时间复杂度上是相同的,但方法二更倾向于更快,特别是对于长字符串来说。

2、请详细解释一下java中new一个东西的过程

在 Java 中,使用 new 关键字创建一个对象时,会经历以下几个主要步骤:

1. 分配内存空间

首先,当使用 new 关键字创建一个对象时,Java 会根据对象的大小在堆内存中分配一块足够的内存空间。这块内存大小由对象的成员变量和方法所需的空间决定。

2. 初始化成员变量

接下来,Java 会对这块内存空间中的成员变量进行初始化。如果有指定初始值,则会使用指定的初始值,否则会使用默认值。默认值取决于变量的类型,例如 int 默认为 0boolean 默认为 false,对象引用默认为 null 等等。

3. 调用构造方法

一旦内存空间分配并且成员变量初始化完成,Java 将调用相应的构造方法(如果有的话)。构造方法是一个特殊的方法,用于初始化对象。在 new 语句中,可以使用参数来调用合适的构造方法。构造方法会执行一些特定的初始化操作,比如对成员变量赋值、执行一些必要的逻辑等。

4. 返回对象引用

最后,new 操作完成后,会返回指向新创建对象的引用。这个引用可以用来操作对象,比如调用对象的方法、访问对象的属性等。

以下是一个简单的例子,演示了创建一个 Person 类的对象的过程:

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void displayInfo() {
        System.out.println("Name: " + name + ", Age: " + age);
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建一个 Person 对象
        Person person1 = new Person("Alice", 30);

        // 这个过程经历了三个步骤:
        // 1. 分配内存空间
        // 2. 初始化成员变量(name = "Alice", age = 30)
        // 3. 调用构造方法(Person(String name, int age))

        // 使用对象引用调用对象的方法
        person1.displayInfo();
    }
}

在这个例子中,当执行 Person person1 = new Person("Alice", 30); 时,依次经历了上述的三个步骤,并返回一个指向新创建的 Person 对象的引用 person1

3、什么是接口

在 Java 中,接口(Interface)是一种抽象类型,它定义了一组方法的声明,但没有具体的实现。接口可以看作是一种约定,它规定了类应该提供的方法,但不涉及具体实现细节。

接口在 Java 中的作用包括但不限于以下几点:

1. 定义规范

接口定义了一组方法,表示了类应该具有的行为。类可以实现一个或多个接口,从而保证了类具有接口中定义的方法。

2. 多继承

在 Java 中,一个类可以实现多个接口,从而达到多继承的效果。Java 中的类只能单继承,但是可以实现多个接口,从而可以获得多个接口的方法。

3. 实现解耦

接口可以帮助实现解耦。当一个类实现了某个接口时,它必须提供接口中定义的所有方法。这样,在代码编写和维护过程中,接口可以帮助分离不同的功能模块,提高了代码的可读性和可维护性。

4. 支持多态

接口可以用于实现多态性,允许将一个对象视为接口的类型。这意味着一个对象可以根据需要被不同类型的接口引用。

在 Java 中,定义接口使用 interface 关键字,语法如下:

public interface MyInterface {
    // 声明方法,不包含方法体
    void method1();
    void method2();
    // ...
}

类实现接口时使用 implements 关键字,语法如下:

public class MyClass implements MyInterface {
    // 实现接口中的方法
    @Override
    public void method1() {
        // 方法1的具体实现
    }

    @Override
    public void method2() {
        // 方法2的具体实现
    }
}

在上面的示例中,MyInterface 是一个接口,定义了 method1method2 这两个方法的声明。MyClass 类实现了 MyInterface 接口,并且提供了 method1method2 方法的具体实现。

需要注意的是,接口中的方法默认是公共的(public),并且不能包含实例变量,但可以包含常量。从 Java 8 开始,接口还支持默认方法(default methods)和静态方法(static methods),这使得接口更加灵活和强大。

4、什么是抽象类

在 Java 中,抽象类(Abstract Class)是一种不能直接实例化的类,它用于定义一组方法的声明,但可以包含抽象方法(没有具体实现的方法)和具体方法(有具体实现的方法)。

抽象类的特点包括:

  1. 不能实例化:抽象类不能直接创建实例对象,也就是不能使用 new 关键字来实例化一个抽象类。抽象类的主要目的是作为其他类的基类,其他类可以继承这个抽象类并实现其中的抽象方法。

  2. 包含抽象方法:抽象类中可以包含抽象方法,这些方法只有声明而没有具体实现。子类必须实现这些抽象方法,否则子类也必须声明为抽象类。

  3. 可以包含具体方法:抽象类不仅可以包含抽象方法,还可以包含具体方法。这些具体方法可以提供一些默认的实现,子类可以选择性地覆盖这些方法。

  4. 用于继承和多态:抽象类主要用于被其他类继承,它可以作为一种模板,定义一些通用的方法和属性,让子类去实现具体细节。同时,抽象类也支持多态性,可以将子类对象当作父类对象使用。

声明抽象类和抽象方法:

在 Java 中,使用 abstract 关键字声明抽象类和抽象方法。抽象类使用 abstract 关键字修饰,而抽象方法也使用 abstract 关键字修饰,没有方法体,只有方法的声明。

// 抽象类
public abstract class AbstractShape {
    // 抽象方法
    public abstract double area();

    // 具体方法
    public void display() {
        System.out.println("This is a shape.");
    }
}

在上面的例子中,AbstractShape 是一个抽象类,包含一个抽象方法 area() 和一个具体方法 display()area() 方法没有具体实现,需要在子类中实现;而 display() 方法有具体实现,子类可以选择性地覆盖它。

继承抽象类:

当一个类继承自抽象类时,它必须实现抽象类中的所有抽象方法,除非这个子类也是抽象类。否则,编译器会报错。

// 继承抽象类
public class Circle extends AbstractShape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

在上面的例子中,Circle 类继承自 AbstractShape 抽象类,并且实现了 area() 方法。由于 area() 是抽象类 AbstractShape 中的抽象方法,因此 Circle 类必须提供具体的实现。

总的来说,抽象类在 Java 中是一种重要的面向对象编程概念,它提供了一种将类的接口和实现分离的方式,有助于代码的重用和维护。

5、继承和多态是什么

继承(Inheritance)和多态(Polymorphism)是面向对象编程中两个重要的概念,它们都涉及到类和对象之间的关系,以及代码的重用性和灵活性。

继承(Inheritance):

继承是面向对象编程中的一种机制,允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。子类可以从父类继承字段和方法,同时还可以定义自己的字段和方法。

通过继承,子类可以获得父类的属性和方法,同时可以通过重写(Override)父类的方法来实现多态性。这样可以减少代码的重复,并且更容易扩展和维护。

// 父类
public class Animal {
    public void sound() {
        System.out.println("Animal makes a sound");
    }
}

// 子类继承父类
public class Dog extends Animal {
    public void sound() {
        System.out.println("Dog barks");
    }
}

在这个例子中,Dog 类继承自 Animal 类,因此可以调用 Animal 类的 sound() 方法。但是 Dog 类也可以选择性地重写 sound() 方法,给出自己特有的实现。

多态(Polymorphism):

多态是面向对象编程的一个重要特性,指的是同一个方法调用可以在不同的对象上产生不同的行为。它允许我们使用基类的引用来指向子类的对象,从而实现了一种“统一的接口,多种实现”的效果。

public class Main {
    public static void main(String[] args) {
        Animal animal1 = new Animal();
        Animal animal2 = new Dog();

        animal1.sound(); // 输出:Animal makes a sound
        animal2.sound(); // 输出:Dog barks
    }
}

在这个例子中,animal1Animal 类的实例,调用 sound() 方法会执行 Animal 类的 sound() 方法;animal2Dog 类的实例,调用 sound() 方法会执行 Dog 类的 sound() 方法。

这种多态性允许我们以统一的方式对不同的对象进行操作,而无需关心对象的具体类型。在编译时,编译器根据引用变量的类型决定调用哪个类的方法,而在运行时,实际上执行的是对象的方法。

继承和多态是面向对象编程中两个基本且重要的概念。它们能够使代码更具有灵活性、可维护性和可扩展性,同时提高了代码的重用性和可读性。

6、抽象类和接口的联系与区别

抽象类(Abstract Class)和接口(Interface)是 Java 中用来实现抽象化的两种机制,它们都有着共同点,也有着各自的特点和用途。

联系:

  1. 抽象性:抽象类和接口都可以用来定义抽象方法,即只有方法声明而没有具体实现的方法。

  2. 规范性:抽象类和接口都可以用来定义一组规范,它们规定了类应该提供的方法,但不涉及具体实现细节。

区别:

  1. 实现

    • 抽象类:可以有抽象方法和具体方法,可以包含实例变量。类通过继承抽象类来实现它的方法。
    • 接口:只能包含抽象方法和常量,不能包含实例变量和具体方法。类通过实现接口来实现接口中定义的方法。
  2. 多继承

    • 抽象类:Java 中的类只能单继承,一个类只能继承一个抽象类。
    • 接口:Java 中的类可以实现多个接口,一个类可以实现多个接口。
  3. 构造方法

    • 抽象类:可以有构造方法,用于初始化抽象类的实例变量。
    • 接口:不能有构造方法,因为接口不能被实例化。
  4. 使用场景

    • 抽象类:当类之间有明显的层次结构,其中一个类是另一个类的扩展时,通常使用抽象类。抽象类可以包含一些通用的方法和字段,同时提供一些默认的实现。
    • 接口:当类需要实现一组规范,但这些类之间没有层次结构时,通常使用接口。接口定义了类应该实现的方法,但不提供实现。

示例:

抽象类示例:
abstract class Shape {
    protected double area;

    public abstract double calculateArea();
}

class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double calculateArea() {
        area = Math.PI * radius * radius;
        return area;
    }
}
接口示例:
interface Shape {
    double calculateArea();
}

class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

在上面的示例中,Shape 类是一个抽象类,其中定义了一个抽象方法 calculateArea(),同时有一个实例变量 areaCircle 类继承了 Shape 类,并实现了 calculateArea() 方法。

在接口的示例中,Shape 是一个接口,其中定义了一个抽象方法 calculateArea()Circle 类实现了 Shape 接口,并实现了 calculateArea() 方法。

总的来说,抽象类和接口都是为了实现抽象化、多态性和代码重用而设计的。在使用时,需要根据具体情况选择合适的抽象机制。如果类之间有明显的层次结构,并且需要共享一些通用的方法和字段,可以使用抽象类;如果需要实现一组规范,类之间没有层次结构,可以使用接口。

7、一个类继承多个接口

在 Java 中,一个类可以实现(implements)多个接口。这种方式允许一个类拥有多个接口所定义的方法和行为。

示例:

// 接口1
interface Interface1 {
    void method1();
}

// 接口2
interface Interface2 {
    void method2();
}

// 类实现多个接口
class MyClass implements Interface1, Interface2 {
    @Override
    public void method1() {
        System.out.println("Method 1 implementation");
    }

    @Override
    public void method2() {
        System.out.println("Method 2 implementation");
    }
}

public class Main {
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.method1();
        obj.method2();
    }
}

在上面的示例中,MyClass 类同时实现了 Interface1Interface2 这两个接口。因此,MyClass 类必须实现 Interface1Interface2 接口中定义的所有方法,即 method1()method2()

Main 类的 main 方法中,我们创建了一个 MyClass 的对象 obj,然后调用了 objmethod1()method2() 方法。

注意事项:

  1. 实现所有接口的方法:当一个类实现多个接口时,必须实现每个接口中定义的所有方法。否则,这个类必须声明为抽象类。

  2. 解决冲突:如果多个接口中有相同名称的默认方法,实现类必须提供自己的实现,并且可以通过类名.方法名的方式来选择性地调用相应的方法。

interface Interface1 {
    default void method() {
        System.out.println("Method from Interface1");
    }
}

interface Interface2 {
    default void method() {
        System.out.println("Method from Interface2");
    }
}

class MyClass implements Interface1, Interface2 {
    @Override
    public void method() {
        Interface1.super.method(); // 调用 Interface1 中的默认方法
        Interface2.super.method(); // 调用 Interface2 中的默认方法
    }
}

冲突解决规则

如果一个类实现了多个接口,而这些接口中包含具有相同名称和参数的默认方法,则必须在实现类中提供自己的方法来解决冲突。这可以是提供一个新的方法来实现,也可以是调用某个特定接口中的默认方法。

  1. iinterface Interface1 {
        default void method() {
            System.out.println("Method from Interface1");
        }
    }
    
    interface Interface2 {
        default void method() {
            System.out.println("Method from Interface2");
        }
    }
    
    class MyClass implements Interface1, Interface2 {
        @Override
        public void method() {
            System.out.println("Method implementation in MyClass");
        }
    }
    

    在这个示例中,MyClass 类提供了自己的 method() 方法实现,因此不再有冲突。当然,也可以在 MyClassmethod() 方法中调用特定的接口的默认方法来解决冲突。

    通过实现多个接口,Java 中的类可以获得更大的灵活性,并能够支持更多的行为和功能。

    8、Interface1.super.method();中的super是什么意思

在 Java 中,super 关键字有两种主要用法:

  1. 访问父类的成员:当一个类继承自另一个类时,可以使用 super 关键字来引用父类的成员(字段和方法)。这种用法可以在子类中访问父类中被隐藏的成员。

  2. 在接口中调用父接口的默认方法:当一个类实现了多个接口时,这些接口中可能包含具有相同签名的默认方法。在实现类中,可以使用 接口名.super.方法名() 的语法来显式地调用特定接口的默认方法。

在这里,Interface1.super.method() 使用了第二种用法,它表示调用 Interface1 接口中的 method() 方法。这个语法告诉编译器,我们想要调用 Interface1 接口中的 method() 方法,而不是当前类的同名方法,从而解决了默认方法冲突的问题。

interface Interface1 {
    default void method() {
        System.out.println("Method from Interface1");
    }
}

interface Interface2 {
    default void method() {
        System.out.println("Method from Interface2");
    }
}

class MyClass implements Interface1, Interface2 {
    @Override
    public void method() {
        Interface1.super.method(); // 调用 Interface1 中的默认方法
        Interface2.super.method(); // 调用 Interface2 中的默认方法
    }
}

在这个示例中,MyClass 类实现了 Interface1Interface2 接口,这两个接口都有 method() 方法。在 MyClassmethod() 方法中,通过 Interface1.super.method()Interface2.super.method() 分别调用了 Interface1Interface2 接口中的 method() 方法。

这样做可以明确地指定调用哪个接口的方法,解决了默认方法冲突的问题,并实现了多接口中方法的调用。

9、实际工程运用中什么时候使用抽象类,什么时候使用接口

在实际工程开发中,对于何时使用抽象类和何时使用接口(Interface)通常取决于设计需求和场景。以下是一些指导原则:

使用抽象类的情况:

  1. 共享代码:如果多个相关的类具有相同的方法实现或字段,并且它们之间有明确的层次结构,那么可以使用抽象类来共享这些代码。

  2. 代码重用:抽象类可以包含具体方法的实现,子类可以继承这些实现,并重写抽象方法以提供特定的行为。这种方式有助于代码的重用。

  3. 扩展性:如果有一个基类,并且希望在这个基类的基础上创建新的类来扩展其功能,那么可以使用抽象类作为基类。

  4. 抽象化通用逻辑:当一个类有一些通用的逻辑,但这个逻辑在不同的子类中可能会有所不同,可以将这些通用逻辑放在抽象类中。

  5. 设计框架:在设计框架时,可以使用抽象类作为框架的基础,让开发者通过继承这个抽象类来实现自己的逻辑。

使用接口的情况:

  1. 多继承需求:Java 中类只支持单继承,但是一个类可以实现多个接口。如果一个类需要具备多个类型的行为,可以通过实现多个接口来实现多继承的效果。

  2. 定义规范:接口定义了一组规范,指定了类应该实现的方法,但不涉及具体的实现细节。如果需要定义一个规范,让不同的类实现这个规范,可以使用接口。

  3. 解耦合:接口可以帮助解耦合,降低类与类之间的依赖关系。通过接口,类与类之间通过接口进行交互,而不是直接依赖于具体的实现类。

  4. 版本升级:接口可以用于版本控制和升级,如果一个类实现了某个接口,而后需要修改接口的规范,只需要修改接口的定义而不影响实现类。

  5. 允许多态性:接口支持多态性,允许将一个对象视为接口的类型,从而使得程序更灵活、更易扩展。

总结:

  • 使用抽象类可以更方便地实现代码的重用和扩展性,适合有明确层次结构的类之间。
  • 使用接口可以更好地实现解耦合和多态性,适合定义规范和多继承需求。
  • 在实际开发中,通常需要根据具体的设计需求、代码结构和未来的扩展性来选择使用抽象类还是接口。有时候也可以同时使用抽象类和接口来满足不同的需求。

10、什么是实例变量

在面向对象的编程语言中(比如 Java),实例变量(Instance Variables)是属于类的每个对象实例的变量。换句话说,每个类的实例(也就是对象)都有自己的一组实例变量,这些变量用来描述对象的状态或属性。

实例变量的特点包括:

  1. 每个对象都有自己的实例变量:当我们使用 new 关键字创建一个类的新对象时,每个对象都有其自己的一组实例变量,这些变量在堆内存中为该对象分配空间。

  2. 在类的任何非静态方法中可访问:实例变量属于对象,因此它们只能在类的实例方法(非静态方法)中访问。静态方法无法直接访问实例变量,因为静态方法属于类本身而不是特定的对象实例。

  3. 默认初始化:如果不明确初始化实例变量,在创建对象时,它们会被自动初始化为相应类型的默认值。比如 int 类型的实例变量默认为 0boolean 类型的实例变量默认为 false,引用类型(比如对象、数组等)的实例变量默认为 null

  4. 每个对象的值可以不同:由于每个对象都有自己的实例变量,因此每个对象的实例变量的值可以不同。这使得每个对象可以拥有自己的状态,不受其他对象影响。

示例:

考虑一个简单的 Person 类,它有两个实例变量 nameage

public class Person {
    // 实例变量
    private String name;
    private int age;

    // 构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 实例方法
    public void displayInfo() {
        System.out.println("Name: " + name + ", Age: " + age);
    }

    public static void main(String[] args) {
        // 创建两个对象
        Person person1 = new Person("Alice", 30);
        Person person2 = new Person("Bob", 25);

        // 每个对象的实例变量值可以不同
        person1.displayInfo();  // 输出:Name: Alice, Age: 30
        person2.displayInfo();  // 输出:Name: Bob, Age: 25
    }
}

在这个示例中,Person 类有两个实例变量 nameage。当我们创建两个 Person 对象时,每个对象都有自己的 nameage 实例变量。因此,person1person2 对象的 nameage 可以分别设置为不同的值。

11、java中的变量

在 Java 中,变量是用来存储数据的一种命名内存位置。根据变量的作用域和声明位置,Java 中的变量可以分为以下几种类型:

1. 局部变量(Local Variables):

局部变量是在方法、构造方法或任何代码块中声明的变量。它们只能在声明它们的代码块内部访问。局部变量没有默认值,必须在使用前显式赋值。

public void exampleMethod() {
    int localVar = 10; // 局部变量
    System.out.println(localVar);
}

 

2. 实例变量(Instance Variables):

实例变量是在类中声明的变量,但在方法、构造方法或代码块之外。每个实例对象都有自己的一组实例变量的副本,它们的值可以不同。实例变量在对象创建时分配内存。

public class MyClass {
    int instanceVar = 20; // 实例变量
}

 

3. 类变量(Static Variables):

类变量属于类,而不是属于任何对象。它们只有一个副本,无论创建多少个对象,所有对象都共享相同的类变量。类变量在程序加载时被分配内存。

public class MyClass {
    static int staticVar = 30; // 类变量
}

 

注意事项:

  • 变量命名规则:在 Java 中,变量名必须以字母、美元符号 $ 或下划线 _ 开头,后面可以跟字母、数字、美元符号 $ 或下划线 _ 组成。

  • Java 中的默认值:在 Java 中,不同类型的变量有不同的默认值。比如,int 的默认值是 0boolean 的默认值是 false,对象引用的默认值是 null

    int num; // 默认值为 0
    boolean flag; // 默认值为 false
    Object obj; // 默认值为 null
    

    常量(Constants):在 Java 中,可以使用 final 关键字声明常量,一旦赋值则不能再改变。常量通常用大写字母表示,并且在声明时必须进行初始化。

  • public class ConstantsExample {
        public static final int MAX_VALUE = 100;
        public static final double PI = 3.14159;
    }
    

    以上是 Java 中常见的变量类型。理解每种类型的变量,以及它们的作用域和声明位置,有助于更好地理解 Java 编程中的数据存储和管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qq_59037435

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

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

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

打赏作者

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

抵扣说明:

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

余额充值