CS61B_值得注意的知识

1.Golden Rule of Equals For primitives, the line int y = x copies the bits inside the x box into the y box. For reference types, we do the exact same thing. In the line Walrus newWalrus = oldWalrus;, we copy the 64 bit address in the oldWalrus box into the newWalrus box. So we can think of this golden rule of equals (GroE) as: when we assign a value with equals, we are just copying the bits from one memory box to another!

等号黄金法则 对于原始类型,行int y = x将x盒子内的位复制到y盒子中。对于引用类型,我们做完全相同的事情。在行Walrus newWalrus = oldWalrus;中,我们将oldWalrus盒子中的64位地址复制到newWalrus盒子中。因此,我们可以认为这个等号黄金法则(GroE)是:当我们使用等号分配一个值时,我们只是从一个内存框复制位到另一个!

对于int double这种值来说,y = x 就是单纯的将x的bit值copy一份赋给y,当我们改变x的时候,y的值不会改变。

而对于一个类,譬如Walrus newWalrus = oldWalrus,我们则是将 oldwalrus 地址复制给 newwalrus ,此时就如同C++的传递指针,这个时候,如果改变newWalrus 也会改变oldWalrus.

 

Parameter Passing Say we have a method average(double a, double b). This method takes two doubles as parameters. Parameter passing also follows the GRoE, i.e. when we call this method and pass in two doubles, we copy the bits from those variables into the parameter variables.

 

2.数组的size 不能改变

the size of the array was specified when the array was created, and cannot be changed!

 

3.如果double 比int 更加精确,那么为什么不一直使用double 呢? 

Doubles are more versatile than ints because they can represent fractional values with greater precision, as they have a larger range and a higher degree of precision. However, there are some potential disadvantages to using doubles over ints:

1. Memory usage: Doubles take up more memory than ints, so if you are working with large datasets or need to optimize memory usage, ints may be a better choice.

2. Performance: Since doubles require more memory and have more precision, they may be slower to process than ints, especially on older or lower-end hardware.

3. Comparisons: Due to the way that computers store floating-point values, comparing doubles for equality can be more complicated than comparing ints, and may lead to unexpected results.

4. Type safety: Because doubles are more permissive in terms of the range of values they can represent, it is easier to inadvertently introduce bugs or errors when using doubles, such as overflow or underflow issues.

In general, the decision to use ints or doubles (or another data type altogether) will depend on the specific requirements of your program or application, as well as the resources and constraints you are working with.

 

4.How much does the memory cost differ between the storing of an address of a 32 entry int array and a 300 entry int array?

The memory cost of storing the address of a 32 entry int array and a 300 entry int array is generally the same, regardless of the size of the array. This is because the address is typically represented using a fixed number of bits, which is determined by the computer's architecture and the size of its memory bus.

For example, on a 32-bit architecture, the address of an int array would be represented using 32 bits (4 bytes), regardless of whether the array has 32 or 300 entries. On a 64-bit architecture, the address would be represented using 64 bits (8 bytes).

However, the memory cost of storing the actual data in the array would be significantly different between a 32 entry int array and a 300 entry int array. Assuming each int requires 4 bytes of memory, a 32 entry int array would require 32 * 4 = 128 bytes of memory, while a 300 entry int array would require 300 * 4 = 1200 bytes of memory. Therefore, storing a 300 entry int array would require almost 10 times the memory of storing a 32 entry int array.

 

5.关于什么时候将嵌套类声明为static,以及声明为static的好处

If the nested class has no need to use any of the instance methods or variables of SLList, you may declare the nested class static, as follows. Declaring a nested class as static means that methods inside the static class can not access any of the members of the enclosing class.

A simple rule of thumb is that if you don't use any instance members of the outer class, make the nested class static.

This saves a bit of memory, because each IntNode no longer needs to keep track of how to access its enclosing SLList.

 

6.list中如何快速添加删除任意节点,包括最后一个。

Incorporating the Sentinel Recall that we added a sentinel node to our SLList. For DLList, we can either have two sentinels (one for the front, and one for the back), or we can use a circular sentinel. A DLList using a circular sentinel has one sentinel. The sentinel points to the first element of the list with next and the last element of the list with prev. In addition, the last element of the list’s next points to the sentinel and the first element of the list’s prev points to the sentinel. For an empty list, the sentinel points to itself in both directions.

public class IntNode {
    public IntNode prev;
    public int item;
    public IntNode next;
}

 

7.实例化array 的三种方式

int[] y = new int[3];
int[] x = new int[]{1, 2, 3, 4, 5};
int[] w = {1, 2, 3, 4, 5};

 

8.array copy

  System.arraycopy(b, 0,x, 3, 2) is the equivalent of x[3:5] = b[0:2] in Python.

 

 9.在编写测试案例的时候不能使用"=="
因为The == operator compares the literal bits in the memory boxes, e.g. input == expected would test whether or not the addresses of input and expected are the same, not whether the values in the arrays are the same. I
 
10.继承
This inheritance is also multi-generational. This means if we have a long lineage of superclass/subclass relationships like in Figure 4.1.1, AList not only inherits the methods from List61B but also every other class above it all the way to the highest superclass AKA AList inherits from Collection.

可以将subclass 赋给superclass, 但是反过来不行

且此时superclass 调用的是subclass overload的函数,但是不能调用override 的函数,因为superclass 没有这种函数实现

 

11. overload and override

在Java中,方法的重载(overload)和方法的重写(override)是两个不同的概念。

重载(Overload)是指在同一个类中定义多个具有相同名称但参数列表不同的方法。重载的方法可以有不同的返回类型,但不能仅仅通过返回类型的不同来区分。重载的方法在编译时根据传入的参数类型和数量来确定要调用的方法。重载方法通常用于提供一组相似的操作,根据传入的不同参数来执行不同的行为。

下面是一个重载方法的例子:

复制代码
public class MyClass {
public void myMethod(int x) {
// 执行操作A
}

public void myMethod(int x, int y) {
// 执行操作B
}
}
复制代码

 

在上面的例子中,`MyClass`类中定义了两个名为`myMethod`的方法,它们具有相同的名称但参数列表不同,一个接受一个整数参数,另一个接受两个整数参数。编译器根据调用方法时传入的参数数量和类型来确定要调用的具体方法。

重写(Override)是指在子类中重新实现(覆盖)从父类继承而来的方法。被重写的方法必须具有相同的名称、参数列表和返回类型。重写方法用于在子类中改变继承的方法的实现,以满足子类的特定需求。重写方法使用`@Override`注解来明确表示这是一个重写方法。

下面是一个重写方法的例子:

复制代码
public class ParentClass {
public void printMessage() {
System.out.println("ParentClass");
}
}

public class ChildClass extends ParentClass {
@Override
public void printMessage() {
System.out.println("ChildClass");
}
}
复制代码

 

在上面的例子中,`ChildClass`继承自`ParentClass`并重写了`printMessage`方法。当通过`ChildClass`的实例调用`printMessage`方法时,将执行子类中重写的方法而不是父类中的方法。

因此,重载是在同一个类中定义多个具有相同名称但参数列表不同的方法,而重写是在子类中重新实现从父类继承而来的方法。重载是静态绑定(编译时绑定),而重写是动态绑定(运行时绑定)。

 

12.静态类型(Static Type)和动态类型(Dynamic Type)

在Java中,静态类型(Static Type)和动态类型(Dynamic Type)是与对象的引用类型相关的概念。

1. 静态类型(Static Type):
- 静态类型是在编译时确定的,是指变量声明时所声明的类型。
- 它表示变量在编译时可以持有的对象类型的上限,即编译器能够验证的类型信息。
- 静态类型可以确定变量的方法和属性的访问,以及在编译时进行静态类型检查,以确保方法和属性的正确使用。
- 例如,如果一个变量被声明为`Person`类型,那么它的静态类型就是`Person`,只能存储`Person`对象或其子类对象。

2. 动态类型(Dynamic Type):
- 动态类型是在运行时确定的,是指变量当前所引用的实际对象的类型。
- 它表示变量当前持有的对象的类型信息。
- 动态类型决定了可以调用哪些方法和属性,以及在运行时进行动态绑定来确定实际执行的代码。
- 例如,如果一个变量的静态类型是`Person`,但它实际引用的是`Student`对象,那么它的动态类型就是`Student`,可以调用`Person`和`Student`共享的方法,以及`Student`独有的方法。

静态类型和动态类型之间的关系:
- 静态类型是在编译时确定的,因此在编译时只能根据静态类型来检查类型错误。
- 动态类型是在运行时确定的,因此在运行时根据动态类型来决定实际执行的代码。
- Java是一种静态类型语言,编译器会进行类型检查,以确保方法和属性的正确访问和使用。
- 在多态的情况下,可以通过父类的静态类型来访问共享的方法和属性,而通过子类的动态类型来访问子类特有的方法和属性。

下面是一个示例代码,展示了静态类型和动态类型的概念:

复制代码
class Animal {
    public void makeSound() {
        System.out.println("Animal is making a sound");
    }
}

class Dog extends Animal {
    public void makeSound() {
        System.out.println("Dog is barking");
    }
    
    public void fetch() {
        System.out.println("Dog is fetching");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();  // 静态类型为Animal,动态类型为Dog
        animal.makeSound();  // 调用的是Dog类中的makeSound()方法,输出"Dog is barking"
        
        // animal.fetch();  // 编译错误,Animal类型没有fetch()方法
        
        Dog dog = (Dog) animal;  // 进行类型转换,将动态类型转为Dog
        dog.fetch();  //
        // 调用Dog类中的fetch()方法,输出"Dog is fetching"
        
        // 使用 instanceof 运算符进行类型检查
        if (animal instanceof Dog) {
            Dog dog2 = (Dog) animal;  // 进行类型转换,将动态类型转为Dog
            dog2.fetch();  // 调用Dog类中的fetch()方法,输出"Dog is fetching"
        }
    }
}
复制代码

在上面的示例中,变量`animal`的静态类型是`Animal`,但它实际引用的是`Dog`对象,因此它的动态类型是`Dog`。当调用`animal.makeSound()`时,由于动态绑定,实际执行的是`Dog`类中重写的`makeSound()`方法。然而,由于静态类型是`Animal`,编译器在编译时只能检查`Animal`类中存在的方法和属性,因此不能直接调用`fetch()`方法。

通过将`animal`转换为`Dog`类型,并使用`instanceof`运算符进行类型检查,我们可以安全地调用`fetch()`方法。

总结:
- 静态类型是在编译时确定的,用于类型检查和编译期间的方法和属性访问。
- 动态类型是在运行时确定的,用于确定实际执行的代码,包括方法的重写和多态。
- 静态类型和动态类型之间的关系可以通过类型转换和`instanceof`运算符进行处理,以实现动态绑定和类型安全的操作。

 

13.在java中,什么时候可以进行强制转换类

在Java中,可以在以下情况下进行强制类型转换:

1. 类型之间存在继承关系:可以将一个子类对象强制转换为父类类型。例如,如果有一个`Dog`类继承自`Animal`类,那么可以将`Dog`对象强制转换为`Animal`类型。

Animal animal = new Dog(); // 合法的强制类型转换

 

2. 存在实现关系的接口之间进行转换:可以将实现了某个接口的对象强制转换为该接口类型。例如,如果有一个类`Cat`实现了`Animal`接口,那么可以将`Cat`对象强制转换为`Animal`类型。

Animal animal = (Animal) new Cat(); // 合法的强制类型转换

 

需要注意的是,进行强制类型转换时需要确保对象实际上是目标类型或其子类型的实例。否则,在运行时会抛出`ClassCastException`异常。因此,在进行强制类型转换之前,最好使用`instanceof`运算符进行类型检查。

if (animal instanceof Dog) {
Dog dog = (Dog) animal; // 合法的强制类型转换
}

总之,强制类型转换应该谨慎使用,并确保转换是安全和有效的。如果存在疑虑,可以使用类型检查和设计良好的继承和接口层次结构来避免需要进行强制类型转换的情况。

 

 

14.[ == ] vs equal.

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

共同点:
1. 两者都用于比较对象。
2. 无论是"=="还是"equals()",它们都返回一个布尔值。

不同点:
1. "=="是一个运算符,用于比较两个对象的引用是否相等,即判断两个对象是否指向同一个内存地址。如果两个对象的引用相同,即指向同一个对象,则返回true;否则返回false。
例如:

String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // 输出true,因为str1和str2引用的是同一个字符串常量


String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // 输出false,因为str1和str2是两个不同的对象

 

2. "equals()"是Object类中定义的方法,用于比较两个对象的内容是否相等。它的默认实现与"=="的行为相同,即比较两个对象的引用是否相等。但是,许多类(如String、Integer等)重写了"equals()"方法,使其比较对象的内容而不是引用。
例如:

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

总结:

共同点:

1. 都可以用于比较对象的相等性。

不同点:

1. `==` 是一个比较运算符,用于比较两个对象的引用是否相同,即它们是否引用了内存中的同一个对象。当你使用 `==` 时,实际上是在比较两个对象的内存地址。这对于基本数据类型(如 `int`、`double` 等)通常有效,但对于对象类型可能会导致误解。

2. `equals()` 是一个方法,通常在自定义对象中用于比较对象的内容是否相等。默认情况下,`equals()` 方法是从 `java.lang.Object` 类继承的,它的行为与 `==` 相同,即比较对象的引用。但是,许多类(如 `String`、`List`、`Map` 等)已经重写了 `equals()` 方法,以便根据对象的实际内容进行比较。当你需要比较自定义对象的内容时,应该重写 `equals()` 方法,以便根据需要的属性进行比较。

总之,`==` 用于比较对象引用,而 `equals()` 用于比较对象的内容。在使用它们时,需要根据比较的目标来选择合适的方法。如果你只关心对象是否是同一个实例,可以使用 `==`;如果你关心对象内容的相等性,请使用 `equals()`。

 

 

15.autoboxing and unboxing 

在Java中,Autoboxing和Unboxing是指基本数据类型(如int、double等)与对应的包装类(如Integer、Double等)之间的自动转换过程。

Autoboxing(自动装箱):
Autoboxing是指将基本数据类型自动转换为对应的包装类对象。当我们将基本数据类型赋值给包装类对象时,编译器会自动进行转换。这种自动装箱使得我们能够在需要使用对象的地方直接使用基本数据类型。

例如:

int num = 10;
Integer integer = num; // 自动装箱,将int类型的num转换为Integer对象

Unboxing(自动拆箱):
Unboxing是指将包装类对象自动转换为对应的基本数据类型。当我们将包装类对象赋值给基本数据类型时,编译器会自动进行转换。这种自动拆箱使得我们能够在需要使用基本数据类型的地方直接使用包装类对象。

例如:

Integer integer = 20;
int num = integer; // 自动拆箱,将Integer对象转换为int类型的num

Autoboxing和Unboxing的使用使得我们在基本数据类型和包装类之间进行转换更加方便,可以使代码更简洁易读。它们的背后是编译器在编译时进行的转换,所以在运行时会产生一些额外的开销。在性能要求较高的场景中,可能需要注意使用Autoboxing和Unboxing的频率,避免频繁的装箱和拆箱操作。

值得注意的地方:

  • Arrays are never autoboxes or auto-unboxed, e.g. if you have an array of integers int[] x, and try to put its address into a variable of type Integer[], the compiler will not allow your program to compile.

  • Autoboxing and unboxing also has a measurable performance impact. That is, code that relies on autoboxing and unboxing will be slower than code that eschews such automatic conversions.

  • Additionally, wrapper types use much more memory than primitive types. On most modern comptuers, not only must your code hold a 64 bit reference to the object, but every object also requires 64 bits of overhead used to store things like the dynamic type of the object.

 

16. final

  • Declaring a reference as final does not make the object that reference is pointing to immutable! For example, consider the following code snippet:

           public final ArrayDeque<String>() deque = new ArrayDeque<String>();
    

    The deque variable is final and can never be reassigned, but the array deque object its pointing to can change! ArrayDeques are always mutable!

  • Using the Reflection API, it is possible to make changes even to private variables! Our notion of immutability assumes that we're not using any of the special capabilities of this library.

final int[] arr = new int[1];
arr[0] = 1;
arr[0] = 3  //valid

final int[] arr = new int[3];
arr = new int[4]; // invalid

 

 

17.Type upper bounds

用途: 泛型类在使用compare 的时候,编译器会报错。于是可以用type upper bounds

public static <K extends Comparable<K>, V> K maxKey(Map61B<K, V> map) {...}
 

cs61B相关说明

类型上界,也称为类型参数上界或有界类型参数,指的是对于在Java中的泛型类型参数可用作类型实参的类型施加的限制。

在Java中,当声明一个泛型类或方法时,可以使用类型参数来表示特定类型的占位符。类型上界允许您指定类型实参必须是特定类型的子类型或实现了特定接口。

例如,考虑以下具有类型参数`T`并受限于`Number`类的泛型类声明:

 

public class ExampleClass<T extends Number> {
    // 类实现
}

 

 

在这种情况下,类型参数`T`只能被替换为`Number`类的子类或`Number`本身的类型。它确保用作类型参数`T`的任何类型都具有`Number`类中定义的属性和行为。

类型上界通过限制可以用作类型实参的可接受类型集合提供编译时类型安全性。这允许您为泛型类型定义更具体的要求,确保可以在类型实参上安全访问特定的方法或属性。

需要注意的是,可以使用`&`运算符为类型参数指定多个上界。例如:

public class ExampleClass<T extends Number & Comparable<T>> {
// 类实现
}

在这种情况下,类型参数`T`必须是`Number`的子类型并且实现了`Comparable`接口。

通过使用类型上界,可以定义更精确和受限制的泛型类型,从而确保类型安全并便于在类型实参上使用特定操作。

 

 

18. package private and default package

"Default Package"(默认包)和"Package-Private"(包级私有)是两个不同的概念,在Java中具有不同的含义和作用。

1. 默认包(Default Package):
- 默认包是指没有明确指定包名的类所属的包。
- 默认包在编译和运行时是存在的,但没有包名。
- 默认包中的类对于同一包中的其他类可见,但对于其他包中的类不可见。
- 默认包通常是在小规模项目、教学示例或简单应用中使用。
- 默认包在实际开发中不被推荐使用,因为它会导致可维护性差、命名冲突等问题。

2. 包级私有(Package-Private):
- 包级私有是指将类、方法或变量的访问级别设置为默认(没有明确的访问修饰符)。
- 包级私有的成员只能在同一个包内部访问,对于其他包中的类不可见。
- 包级私有允许在同一包内部实现封装和隐藏,可以防止包外部的类直接访问和修改内部的细节。
- 包级私有是一种访问级别,用于限制对包内部的访问。

总结:
默认包是指没有明确指定包名的类所属的包,而包级私有是指将类、方法或变量的访问级别设置为默认。默认包是指包的命名和组织方式,而包级私有是指访问控制的机制。它们是不同的概念,但都涉及到包的访问权限。

 

19. Delegation vs Extension

在Java中,Delegation(委托)和Extension(继承)是两种不同的代码复用和扩展机制。

1. 委托(Delegation):
- 委托是通过将方法的调用委托给另一个对象来实现代码复用和功能扩展。
- 在委托模式中,一个类(委托类)将部分或全部的工作委托给另一个类(委托对象)来完成。
- 委托可以在运行时动态地改变委托对象,从而实现灵活的行为变化。
- 委托关系可以通过接口实现或者通过组合关系来建立。

2. 继承(Extension):
- 继承是通过创建一个新的类,继承自现有类(父类),并在其基础上添加、修改或重写方法来实现代码复用和功能扩展。
- 继承是一种静态的、编译时的关系,在类的层次结构中建立起父子关系。
- 子类继承了父类的属性和方法,并可以通过重写方法来改变或扩展其行为。
- 继承关系是一种紧耦合的关系,子类与父类之间具有强依赖性。

区别:
- 灵活性:委托提供了更大的灵活性,因为可以在运行时动态地改变委托对象,实现行为的动态切换。而继承在编译时就需要确定类的层次结构和继承关系。
- 关系类型:委托是一种组合关系,不同类之间通过接口或组合关系建立委托关系;而继承是一种父子关系,通过继承关键字建立类之间的继承关系。
- 代码复用:委托模式更注重代码的复用,通过将工作委托给其他对象来实现复用;继承则通过子类继承父类的属性和方法来实现代码的复用。
- 耦合性:委托模式具有较低的耦合性,委托类与委托对象之间通过接口或组合关系进行交互;而继承关系具有较高的耦合性,子类与父类之间具有紧密的依赖关系。

选择使用委托还是继承取决于具体的需求和设计考虑。委托提供了更灵活的代码复用和行为扩展方式,而继承提供了更紧密的父子关系和依赖性。

 

20.当函数参数的类型为父类时,子类可以当做父类代入。

 

 

 20.When to use an Interface Versus an Abstract Class

In Java, the choice between using an interface or an abstract class depends on the specific requirements and design goals of your application. Here are some guidelines to help you decide when to use an interface versus an abstract class:

Use an Interface when:

1. Defining a contract: Interfaces are used to define a contract or set of methods that a class must implement. If you want to enforce a specific behavior across multiple unrelated classes, or if you want to define a common API for different classes, an interface is a good choice.

2. Achieving multiple inheritance of types: Java does not support multiple inheritance of classes, but a class can implement multiple interfaces. If you need a class to inherit from multiple types, such as implementing behaviors from different domains, interfaces provide a way to achieve that.

3. Creating loosely coupled code: Interfaces allow for loose coupling between components. By programming to interfaces rather than concrete implementations, you can achieve flexibility, modularity, and easier testing.

4. Supporting polymorphism: Interfaces enable polymorphism, allowing objects of different classes to be treated uniformly when they implement the same interface. This promotes code extensibility and flexibility.

Use an Abstract Class when:

1. Providing default implementations: Abstract classes can contain both abstract and non-abstract methods. If you want to provide some common functionality and default implementations for a group of related classes, an abstract class can serve as a convenient base.

2. Sharing code among related classes: Abstract classes are useful when multiple related classes share common attributes or behavior. By placing the common code in an abstract class, you can avoid code duplication and enforce consistency.

3. Extending a class hierarchy: If you have an existing class hierarchy and want to introduce additional functionality common to certain subclasses, an abstract class can provide a middle layer in the inheritance hierarchy.

4. Defining a template for subclasses: Abstract classes can define a template or structure for subclasses to follow. They can provide a skeletal implementation that subclasses must complete by implementing abstract methods.

In summary, use interfaces when you want to define contracts, achieve multiple inheritance of types, or create loosely coupled code. Use abstract classes when you want to provide default implementations, share code among related classes, extend a class hierarchy, or define a template for subclasses. The choice ultimately depends on the specific needs of your application and the desired design goals.

在Java中,选择使用接口还是抽象类取决于应用程序的具体要求和设计目标。以下是一些指南,可帮助您在使用接口还是抽象类时做出决策:

使用接口的情况:

1. 定义合约:接口用于定义一个类必须实现的合约或一组方法。如果您希望在多个不相关的类之间强制执行特定的行为,或者如果您希望为不同类定义一个通用的API,那么使用接口是一个很好的选择。

2. 实现类型的多重继承:Java不支持类的多重继承,但一个类可以实现多个接口。如果您需要一个类从多个类型继承,例如实现来自不同领域的行为,接口提供了一种实现该目标的方式。

3. 创建松耦合的代码:接口允许组件之间松散耦合。通过针对接口而不是具体实现进行编程,可以实现灵活性、模块化和更容易的测试。

4. 支持多态性:接口实现了多态性,允许以相同的方式处理实现了相同接口的不同类的对象。这促进了代码的可扩展性和灵活性。

使用抽象类的情况:

1. 提供默认实现:抽象类可以包含抽象方法和非抽象方法。如果您想为一组相关类提供一些常用功能和默认实现,抽象类可以作为一个方便的基类。

2. 在相关类之间共享代码:当多个相关类共享通用属性或行为时,抽象类非常有用。通过将通用代码放在抽象类中,可以避免代码重复并确保一致性。

3. 扩展类层次结构:如果您有一个现有的类层次结构,并希望引入一些对某些子类通用的额外功能,抽象类可以在继承层次结构中提供一个中间层。

4. 为子类定义模板:抽象类可以为子类定义一个模板或结构。它们可以提供一个框架实现,要求子类通过实现抽象方法来完成。

5.可以使用 abstract 关键字将方法和类声明为抽象的。抽象类不能被实例化,但可以使用 extends关键字对其进行子类化。与接口不同,抽象类可以为公共方法以外的特性提供实现继承,包括实例变量。实现接口的类将从该接口继承所有方法和变量。如果实现类无法实现从接口继承的任何抽象方法,则该类必须声明为抽象的

总之,当您想定义合约、实现类型的多重继承或创建松耦合的代码时,请使用接口。当您想提供默认实现、在相关类之间共享代码、扩展类层次结构或为子类定义模板时,请使用抽象类。选择最终取决于应用程序的具体需求和所需的设计目标。

In practice, in can be a little unclear when to use an interface and when to use an abstract class. One mostly accurate metaphor that might help is that you can think of an interface as defining a “can-do” or an “is-a” relationship, whereas an abstract class should be a stricter “is-a” relationship. The difference can be subtle, and you can often use one instead of the other.

In practice, large Java libraries often have a hierarchy of interfaces, which are extended by abstract classes that provided default implementations for some methods, and which are in turn ultimately implemented by concrete classes. A good example is the Collection interface: It extends Iterable (which is its superinterface), and is implemented by many subinterfaces (i.e. List, Set, Map), which in turn have their own abstract implementations (AbstractList, AbstractSet AbstractMap). However, for smaller programs, the hierarchy is often stubbier, sometimes starting with an abstract class. For example, we could have just started with AbstractBoundedQueue at the top of the hierarchy and skipped having a BoundedQueue interface altogether.

posted @   哎呦_不想学习哟~  阅读(154)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示