JavaRush /Java 博客 /Random-ZH /Java 中静态修饰符的 10 个注意事项

Java 中静态修饰符的 10 个注意事项

已在 Random-ZH 群组中发布
Java中的static 修饰符是直接与类关联的。如果字段是静态的,那么它属于类,如果方法是静态的,那么它也属于类。基于此,您可以使用类名访问静态方法或字段。例如,如果 Counter 类中的 count 字段是静态的,那么您可以使用如下查询访问该变量:Counter.count。 Java 中静态修饰符的 10 个注意事项 - 1在我们开始注释之前,让我们记住(也许还能找出)Java 中 静态是什么以及什么可以是静态的。Static是应用于字段、块、方法或内部类的修饰符。该修饰符表明该主题绑定到当前类。

静态字段

当我们表示一个类级变量时,我们表明该值属于一个类。如果不这样做,变量的值将绑定到使用此类创建的对象。这是什么意思? Java 中静态修饰符的 10 个注意事项 - 2事实是,如果变量不是静态的,那么该类的每个新对象都将拥有该变量自己的值,通过更改该值,我们可以在一个对象中专门更改它:例如,我们有一个 Car 类,它具有非静态变量-静态变量:
public class Car {
  int km;
}
然后在主要部分:
Car orangeCar = new Car();
orangeCar.km = 100;

Car blueCar = new Car();
blueCar.km = 85;

System.out.println("Orange car - " + orangeCar.km);
System.out.println("Blue car - " + blueCar.km);
我们得到的输出是:

Orange car - 100
Blue car - 85
正如你所看到的,每个对象都有自己的变量,变量的变化只发生在这个对象上。好吧,如果我们有一个静态变量,那么这个全局值对每个人来说都是相同的:现在我们有一个带有静态变量的 Car:
public class Car {
  static int km;
}
然后 main 中的相同代码将输出到控制台:

Orange car - 85
Blue car - 85
毕竟,我们所有人都有一个变量,并且每次我们都会更改它。静态变量通常不是通过对象引用(orangeCar.km)访问,而是通过类名(Car.km)访问

静态块

有两个初始化块 - 常规初始化块和静态初始化块。该块旨在初始化内部变量。如果块是普通的,则用它初始化对象的内部变量,但如果是静态的,则将静态变量(即类变量)分配给它们。具有静态初始化块的类的示例:
public class Car {
  static int km;

  static {
     km = 150;
  }
}

静态方法

静态方法与常规方法的不同之处在于它们也绑定到类而不是对象。静态方法的一个重要属性是它只能访问静态变量/方法。作为一个例子,让我们看一个类,它是一种跟踪方法调用的计数器:
public class Counter {
  static int count;

  public static void invokeCounter() {
     count++;
     System.out.println("Current counter value - " + count);
  }
}
我们在 main 中调用它:
Counter.invokeCounter();
Counter.invokeCounter();
Counter.invokeCounter();
我们将输出输出到控制台:

Текущее meaning счётчика - 1
Текущее meaning счётчика - 2
Текущее meaning счётчика - 3

Java中的静态类

只有内部类才能是静态类。同样,这个类与外部类绑定在一起,如果外部类被另一个类继承,那么这个类将不会被继承。而且,这个类可以被继承,就像它可以从任何其他类继承并实现接口一样。本质上,静态嵌套类与任何其他内部类没有什么不同,只是它的对象不包含对创建它的外部类对象的引用。然而,这使得静态类与常规的非嵌套类最相似,因为唯一的区别是它被包装在另一个类中。在某些情况下,这对我们来说是一个优势,因为我们可以从中访问外部类的私有静态变量。嵌套静态类的示例:
public class Vehicle {

  public static class Car {
     public int km;
  }
}
创建此类的实例并设置内部变量的值:
Vehicle.Car car = new Vehicle.Car();
car.km = 90;
要使用静态方法/变量/类,我们不需要创建该类的对象。当然,应该考虑访问修饰符。例如,字段private只能在声明它们的类中访问。字段protected可供包 ( package ) 内的所有类以及包外的所有继承类使用。有关更多详细信息,请查看文章“私有、受保护与公共”。increment()假设类中有一个静态方法,Counter其任务是递增计数器count。要调用此方法,您可以使用表单的调用Counter.increment()。无需实例化类Counter即可访问静态字段或方法。这是静态对象和非静态对象(类成员)之间的根本区别。让我再次提醒您,静态类成员直接属于该类,而不属于其实例。也就是说,静态变量的值count对于所有类型的对象都是相同的Counter。在本文后面,我们将介绍在 Java 中使用 static 修饰符的基本方面,以及一些有助于您理解关键编程概念的功能。

每个程序员都应该了解 Java 中的 Static 修饰符

在本节中,我们将了解使用静态方法、字段和类的基础知识。让我们从变量开始。
  1. 您不能在静态上下文中访问类的非静态成员,例如方法或块。编译下面的代码会出现错误:

    public class Counter{
    private int count;
    public static void main(String args[]){
       System.out.println(count); //compile time error
    }}

    这是 Java 程序员,尤其是新手最常犯的错误之一。由于方法main是静态的,但变量count不是静态的,在这种情况下,方法println内部的方法main将抛出“编译时错误”。

  2. В отличие от локальных переменных, статические поля и методы НЕ потокобезопасны (Thread-safe) в Java. На практике это одна из наиболее частых причин возникновения проблем связанных с безопасностью мультипоточного программирования. Учитывая что каждый экземпляр класса имеет одну и ту же копию статической переменной, то такая переменная нуждается в защите — «залочивании» классом. Поэтому при использовании статических переменных, убедитесь, что они должным образом синхронизированы (synchronized), во избежание проблем, например таких How «состояние гонки» (race condition).

  3. Статические методы имеют преимущество в применении, т.к. отсутствует необходимость каждый раз создавать новый an object для доступа к таким методам. Статический метод можно вызвать, используя тип класса, в котором эти методы описаны. Именно поэтому, подобные методы How нельзя лучше подходят в качестве методов-фабрик (factory), и методов-утorт (utility). Класс java.lang.Math — замечательный пример, в котором почти все методы статичны, по этой же причине классы-утorты в Java финализированы (final).

  4. Другим важным моментом является то, что вы НЕ можете переопределять (Override) статические методы. Если вы объявите такой же метод в классе-наследнике (subclass), т.е. метод с таким же именем и сигнатурой, вы лишь «спрячете» метод суперкласса (superclass) instead of переопределения. Это явление известно How сокрытие методов (hiding methods). Это означает, что при обращении к статическому методу, который объявлен How в родительском, так и в дочернем классе, во время компиляции всегда будет вызван метод исходя из типа переменной. В отличие от переопределения, такие методы не будут выполнены во время работы программы. Рассмотрим пример:

    class Vehicle{
         public static void  kmToMiles(int km){
              System.out.println("Inside parent class/static method");
         } }
    
    class Car extends Vehicle{
         public static void  kmToMiles(int km){
              System.out.println("Inside child class/static method ");
         } }
    
    public class Demo{
       public static void main(String args[]){
          Vehicle v = new Car();
           v.kmToMiles(10);
      }}

    Вывод в консоль:

    Внутри родительского класса/статического метода

    Код наглядно демонстрирует: несмотря на то, что an object имеет тип Car, вызван статический метод из класса Vehicle, т.к. произошло обращение к методу во время компиляции. И заметьте, ошибки во время компиляции не возникло!

  5. Объявить статическим также можно и класс, за исключением классов верхнего уровня. Такие классы известны How «вложенные статические классы» (nested static class). Они бывают полезными для представления улучшенных связей. Яркий пример вложенного статического класса — HashMap.Entry, который предоставляет структуру данных внутри HashMap. Стоит заметить, также How и любой другой внутренний класс, вложенные классы находятся в отдельном файле .class. Таким образом, если вы объявor пять вложенных классов в вашем главном классе, у вас будет 6 файлов с расширением .class. Ещё одним примером использования является объявление собственного компаратора (Comparator), например компаратор по возрасту (AgeComparator) в классе сотрудники (Employee).

  6. Модификатор static также может быть объявлен в статичном блоке, более известным How «Статический блок инициализации» (Static initializer block), который будет выполнен во время загрузки класса. Если вы не объявите такой блок, то Java соберёт все статические поля в один список и выполнит его во время загрузки класса. Однако, статичный блок НЕ может пробросить перехваченные исключения, но может выбросить не перехваченные. В таком случае возникнет «Exception Initializer Error». На практике, любое исключение возникшее во время выполнения и инициализации статических полей, будет завёрнуто Java в эту ошибку. Это также самая частая причина ошибки «No Class Def Found Error», т.к. класс не находился в памяти во время обращения к нему.

  7. Полезно знать, что статические методы связываются во время компиляции, в отличие от связывания виртуальных or не статических методов, которые связываются во время исполнения на реальном an objectе. Следовательно, статические методы не могут быть переопределены в Java, т.к. полиморфизм во время выполнения не распространяется на них. Это важное ограничение, которое необходимо учитывать, объявляя метод статическим. В этом есть смысл, только тогда, когда нет возможности or необходимости переопределения такого метода классами-наследниками. Методы-фабрики и методы-утorты хорошие образцы применения модификатора static. Джошуа Блох выделил несколько преимуществ использования статичного метода-фабрики перед конструктором, в книге «Effective Java», которая является обязательной для прочтения каждым программистом данного языка.

  8. Важным свойством статического блока является инициализация. Статические поля or переменные инициализируются после загрузки класса в память. Порядок инициализации сверху вниз, в том же порядке, в Howом они описаны в исходном файле Java класса. Поскольку статические поля инициализируются на потокобезопасный манер, это свойство также используется для реализации паттерна Singleton. Если вы не используется список Enum How Singleton, по тем or иным причинам, то для вас есть хорошая альтернатива. Но в таком случае необходимо учесть, что это не «ленивая» инициализация. Это означает, что статическое поле будет проинициализировано ещё ДО того How кто-нибудь об этом «попросит». Если an object ресурсоёмкий or редко используется, то инициализация его в статическом блоке сыграет не в вашу пользу.

  9. 在序列化过程中,与transient变量一样,静态字段也不会被序列化。事实上,如果您将任何数据保存在静态字段中,那么在反序列化后,新对象将包含其主要(默认)值,例如,如果静态字段是类型的变量int,则反序列化后其值将为零,如果float如果类型为Object– ,则类型为 0.0 null。老实说,这是 Java 面试中关于序列化最常见的问题之一。不要将有关对象的最重要的数据存储在静态字段中!

  10. 最后,我们来谈谈static import。此修饰符与标准运算符有很多共同点import,但不同的是,它允许您导入类的一个或所有静态成员。当导入静态方法时,可以像在同一个类中定义它们一样访问它们,同样,当导入字段时,我们可以在不指定类名的情况下访问它们。该功能是在 Java 1.5 版本中引入的,如果使用得当,可以提高代码的可读性。这种结构最常见于JUnit测试中,因为 例如,几乎所有测试开发人员都使用static import断言方法assertEquals()及其重载副本。如果不清楚,欢迎提供更多信息

就这样。每个程序员都必须了解以上关于Java 中 static 修饰符的所有要点。本文涵盖了有关静态变量、字段、方法、初始化块和导入的基本信息。包括一些重要的属性,了解这些属性在编写和理解 Java 程序时至关重要。我希望每个开发人员都能完善他们使用静态概念的技能,因为...... 这对于严肃的编程来说非常重要。”
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION