【JavaSE】Java(五十五):核心要点总结

文章目录

  • 1. 为什么不允许静态方法访问非静态变量
  • 2. Java的内存模型
  • 3. 在Java中什么时候用重载什么时候用重写
  • 4. 举例说明什么情况下更倾向于用抽象类而不是接口
  • 5. 实例化对象有哪几种方式


在这里插入图片描述

1. 为什么不允许静态方法访问非静态变量


在Java中,静态方法属于类级别的方法,它不依赖于任何实例对象,只能访问类变量(即static修饰的变量)和其他静态方法。而非静态变量是实例级别的变量,需要通过实例对象才能访问。

这里有两个原因导致在静态方法中不能直接访问非静态变量:

对于一个静态方法来说,它的执行是在类加载时完成的,也就是说在运行时并没有实例化任何对象。因此,在静态方法中无法确定我们要操作哪个实例的非静态变量。
类变量存储在静态内存中,可以在整个程序生命周期内访问。如果允许在静态方法中访问实例变量,那么将可能会出现非常危险和意外的情况,因为实例变量的值随着实例对象的创建和销毁而改变,在静态方法中直接引用未知/不存在的值将导致代码错误。

由于静态方法不依赖于实例对象,因此不能访问和操作实例变量,必须使用 this 关键字指定当前对象,并通过对象访问实例变量。


2. Java的内存模型


Java的内存模型(Java Memory Model,简称JMM)是指一组规范,定义了在多线程环境下Java虚拟机(JVM)如何与计算机物理内存交互,以及线程间如何共享变量、协同工作等机制

简单来说,JMM确保了Java程序在不同的硬件平台上都能够正确地运行,避免了由多线程访问共享变量而引发的各种问题(比如死锁、竞争条件和内存可见性等问题)。

JMM主要包括两部分:内存屏障和共享变量。其中,内存屏障指令用来限制指令重排序,从而避免了多线程的内存读写操作产生干扰;共享变量指的是在多个线程之间共享的对象和数据。

在Java中,静态区域、堆区域、栈区域和方法区域都有其特定的内存管理方式,其中静态区和方法区通常被视为共享变量的存储区域。

JMM中的主要概念包括原子性、可见性和有序性。其中,原子性是指一个操作要么完成,要么不完成,不会出现中间状态;可见性是指当一个线程修改了一个共享变量时,其他线程可以立即看到这个修改;有序性是指程序中各操作的执行顺序必须要符合代码的先后顺序。

JMM定义了Java程序在多线程并发执行时应该如何正确地行为,遵循JMM规范可以帮助我们编写更加健壮、高效和可靠的Java多线程程序


3. 在Java中什么时候用重载什么时候用重写


在Java中,方法的重载(Overloading)和重写(Overriding)是两个常用的概念。

方法重载主要用于改变方法的参数列表,使得方法能够接收不同类型或个数的参数。方法名相同,而参数列表不同,返回类型可以相同也可以不同。方法重载是一种静态多态性(编译时多态),它允许我们使用相同的方法名来实现不同的行为,提高了代码的可读性和复用性。

而方法重写则是子类对父类继承的方法进行重新定义,使其具有特定的行为。方法重写必须保证方法名、参数列表和返回类型完全一致,并且满足里氏替换原则。方法重写是一种动态多态性(运行时多态)的体现,它允许我们在运行时根据对象的实际类型来调用相应的方法,实现了多态性。

当我们需要改变方法的参数列表,以适应不同的输入时,应该考虑使用方法重载;而当我们需要修改已有的方法逻辑或者实现某种特定的功能时,应该考虑使用方法重写。同时,在重载和重写中,注意避免产生歧义和冲突,使程序具备更清晰、简洁和易于理解和维护的特点。

4. 举例说明什么情况下更倾向于用抽象类而不是接口


在Java中,抽象类和接口都是用于实现多态性的重要机制。通常情况下,我们应该优先考虑使用接口来定义规范和行为,并在需要时再使用抽象类进行补充或扩展。但在某些场景下,我们更倾向于使用抽象类而不是接口。

以下是一些情况下更适合使用抽象类的例子:

  1. 抽象类可以提供默认的方法实现。如果继承的类没有覆盖这些方法,就会自动继承父类的默认实现。这对于类似模板方法、钩子方法这种公共实现比较重要的场景很有用,而接口需要将方法全部声明为抽象方法,无法提供默认实现。

  2. 当需要将代码逻辑和属性封装到一个基础类中,并为它提供特定的行为时,抽象类更为适合。因为抽象类具有完整的面向对象特性,可以包含抽象方法、构造函数、静态成员变量等所有类型的成员,而接口只能包含静态常量和抽象方法。

  3. 当设计初期不能确保每个子类都能够遵守接口协定时(例如某个接口规定了大量的抽象方法),抽象类就有比较明显的优势。因为设计时可以指定某些方法必须被实现,而某些方法则可以选择性实现或者通过抛出异常来使得子类强制实现。

  4. 当需要向已有的类库中添加新功能时,如果这些类已经是抽象类的形式,就可以方便地定义基于这个抽象类的新子类来扩展功能;而如果这些类只是普通类或接口,又不能轻易地出现胶合代码或空实现等问题。

在使用抽象类和接口时,应该根据具体需求来综合考虑两者的特点和优缺点,以便选择更加适合自己业务场景的方式。


5. 实例化对象有哪几种方式


在Java中,可以通过以下几种方式来实例化对象:

  1. 使用new关键字实例化对象。这是最常见的一种方式,只需要通过类名和参数列表调用构造函数即可创建对象,例如:MyClass obj = new MyClass();

  2. 使用反射机制实例化对象。通过Class对象的newInstance()方法或Constructor对象的newInstance()方法可以创建一个类的实例。例如:MyClass obj = MyClass.class.newInstance();或者 MyClass obj = constructor.newInstance();

  3. 使用克隆方法实例化对象。如果一个类实现了Cloneable接口,并重写了Object类的clone()方法,那么就可以通过调用其clone()方法来创建一个新的对象,例如:MyClass obj2 = (MyClass) obj1.clone();

  4. 使用序列化和反序列化实例化对象。如果一个类实现了Serializable接口,并且将对象序列化到磁盘文件或网络中,那么就可以从磁盘文件或网络中反序列化出一个新的对象,例如:ObjectInputStream in = new ObjectInputStream(new FileInputStream(“object.dat”)); MyClass obj = (MyClass) in.readObject();

  5. 使用工厂模式实例化对象。在工厂模式中,通过定义一个专门的工厂类来负责创建对象,客户端只需要向工厂类请求所需对象即可,例如:MyClassFactory factory = new MyClassFactory(); MyClass obj = factory.createInstance();

以上是Java中常用的实例化对象的方式,不同的方式适用于不同的场景和需求。尤其需要注意的是,在每种方式下都需要保证正确地调用构造函数,在使用反射和序列化时还需要特别关注安全性和性能问题。



在这里插入图片描述

你可能感兴趣的:(Java,java,jvm,开发语言)