java知识点回顾

Java 和 C++ 的区别?

指针支持

  • Java 不支持指针操作,这使得 Java 程序员无法直接访问或修改内存地址。这种设计有助于防止一些常见的编程错误,比如野指针访问和缓冲区溢出。
  • C++ 支持指针,并允许程序员直接操作内存,这提供了更大的灵活性但也带来了更高的风险。

继承模型

  • Java 支持单一继承,即一个类只能继承一个父类。不过,Java 通过接口的概念实现了多重继承的功能,一个类可以实现多个接口。
  • C++ 支持多重继承,一个类可以继承多个父类。

内存管理

  • Java 有内置的垃圾收集机制,会自动清理不再使用的对象,这减轻了程序员的负担并减少了内存泄漏的风险。
  • C++ 中,内存管理通常需要程序员自己负责,可以通过 newdelete 来分配和释放内存。现代 C++ 还引入了智能指针等技术来简化内存管理。

方法与操作符重载

  • Java 支持方法重载(overloading),但不支持操作符重载(operator overloading)。
  • C++ 同时支持方法重载和操作符重载。操作符重载可以使得代码看起来更像数学公式,但可能会增加代码的复杂性和潜在的滥用。
    等等。

Java 语言关键字有哪些?

分类关键字
访问控制privateprotectedpublic
类,方法和变量修饰符abstractclassextendsfinalimplementsinterfacenative
newstaticstrictfpsynchronizedtransientvolatileenum
程序控制breakcontinuereturndowhileifelse
forinstanceofswitchcasedefaultassert
错误处理trycatchthrowthrowsfinally
包相关importpackage
基本类型booleanbytechardoublefloatintlong
short
变量引用superthisvoid
保留字gotoconst

true, false, 和 null 不是关键字,而是字面值。它们不能用作标识符。

自动装箱与拆箱了解吗?原理是什么?

自动装箱和拆箱是 Java 5 之后引入的新特性,它们简化了基本类型和对应的包装类之间的转换。下面分别解释自动装箱和拆箱的概念以及它们的原理。

自动装箱

自动装箱是指将基本数据类型的值自动转换为对应的包装类对象的过程。例如,将一个 int 类型的值转换为 Integer 对象。

例子
int i = 10;
Integer integer = i;  // 自动装箱
原理

自动装箱本质上是调用了包装类的构造方法。例如,将 int 转换为 Integer 实际上是调用了 Integer 的构造方法。

int i = 10;
Integer integer = new Integer(i);  // 显式装箱

在 Java 5 之后,编译器会自动插入这行代码,使得代码更简洁。

自动拆箱

自动拆箱是指将包装类对象自动转换为基本数据类型的值的过程。例如,将 Integer 对象转换为 int 类型的值。

例子
Integer integer = 20;
int i = integer;  // 自动拆箱
原理

自动拆箱本质上是调用了包装类的 intValue()doubleValue() 等方法来获取基本数据类型的值。

Integer integer = 20;
int i = integer.intValue();  // 显式拆箱

在 Java 5 之后,编译器会自动插入这行代码。

实现原理

自动装箱和拆箱的实现依赖于 Java 编译器的智能转换能力。编译器会在编译时自动插入必要的转换代码。

包装类缓存

为了提高性能,Java 为一些常用的数值包装类(如 Integer)提供了一个缓存机制。例如,Integer 类在 -128127 之间有一个缓存池,这个范围内整数的 Integer 对象会被复用,以减少对象的创建。

Integer a = 100;  // 自动装箱
Integer b = 100;  // 自动装箱
System.out.println(a == b);  // 输出 true,因为都在缓存范围内

如果超出这个范围,每次装箱都会创建一个新的对象。

Integer a = 128;  // 自动装箱
Integer b = 128;  // 自动装箱
System.out.println(a == b);  // 输出 false,因为不在缓存范围内

总结

  • 自动装箱:将基本类型转换为对应的包装类对象。
  • 自动拆箱:将包装类对象转换为基本类型。
  • 实现原理:编译器自动插入转换代码,利用包装类的构造方法和拆箱方法。
  • 缓存机制:为了提高性能,某些数值包装类提供了一个缓存池,用于复用常见的数值对象。

静态方法为什么不能调用非静态成员?

静态方法不能调用非静态成员的原因在于静态方法和非静态成员之间的本质区别以及它们在内存中的存储方式。

静态方法的特点

  • 类级别的:静态方法属于类而不是类的实例。即使没有创建类的实例,静态方法也可以通过类名直接调用。
  • 内存中的位置:静态方法在类加载时就已经存在于内存中,存储在方法区(或称为元空间)的常量池中,而不是堆内存中的对象实例中。
  • 生命周期:静态方法的生命周期与类的生命周期一致,即只要类存在,静态方法就存在。

非静态成员的特点

  • 实例级别的:非静态成员(变量或方法)属于类的实例。只有在创建了类的实例之后,非静态成员才存在于内存中。
  • 内存中的位置:非静态成员存储在堆内存中的对象实例中。
  • 生命周期:非静态成员的生命周期与对象实例的生命周期一致,即对象实例创建时非静态成员存在,对象实例销毁时非静态成员也随之消失。

为什么静态方法不能调用非静态成员

  1. 生命周期不一致:静态方法可以在没有实例的情况下被调用,而非静态成员必须通过实例才能访问。如果静态方法可以直接访问非静态成员,那么在没有实例的情况下,非静态成员将无法找到对应的存储位置。

  2. 内存位置不同:静态方法和非静态成员存储在不同的内存区域。静态方法存在于方法区的常量池中,而非静态成员存在于堆内存的对象实例中。因此,静态方法无法直接访问堆内存中的非静态成员。

  3. 设计原则:Java 设计者决定静态方法只能访问静态成员,以保持静态方法和非静态成员之间的清晰界限。这样做的好处是可以避免在没有实例的情况下试图访问实例成员所导致的错误。

示例

考虑以下示例代码:

public class MyClass {
    int instanceVariable = 10;  // 非静态成员变量
    static int staticVariable = 20;  // 静态成员变量

    public void instanceMethod() {  // 非静态方法
        System.out.println("Instance Method: " + instanceVariable);
        System.out.println("Instance Method: " + staticVariable);
    }

    public static void staticMethod() {  // 静态方法
        // System.out.println("Static Method: " + instanceVariable);  // 编译错误
        System.out.println("Static Method: " + staticVariable);
    }
}

在这个例子中,静态方法 staticMethod 不能直接访问非静态成员变量 instanceVariable。如果尝试这样做,编译器会报错,因为静态方法无法直接访问实例成员。

解决方案

如果需要在静态方法中使用非静态成员,可以采取以下几种方式:

  1. 通过实例访问:在静态方法中创建类的实例,然后通过该实例访问非静态成员。
  2. 将非静态成员改为静态:如果非静态成员不需要依赖于实例状态,可以考虑将其改为静态成员。
  3. 使用静态成员:尽量使用静态成员来满足静态方法的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

布说在见

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

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

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

打赏作者

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

抵扣说明:

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

余额充值