JavaRush /Java 博客 /Random-ZH /Java 中的 Final、常量和不可变

Java 中的 Final、常量和不可变

已在 Random-ZH 群组中发布
你好!“修饰符”这个词你已经很熟悉了。至少,您遇到过访问修饰符(public、private)和 static 修饰符。今天我们来谈谈特殊的final修饰符。可以说,它“巩固”了我们计划中需要持续、明确、不变行为的领域。它可以用在我们程序的三个区域:类、方法和变量。 Java 中的不可变:final、常量和 Immutable - 2 让我们一一分析一下。如果类声明包含final修饰符,则意味着您不能从该类继承。在之前的讲座中,我们看到了一个简单的继承示例:我们有一个父类Animal和两个子类 -Cat并且Dog
public class Animal {
}

public class Cat extends Animal {
   //..поля и методы класса Cat
}

public class Dog extends Animal {

   //..поля и методы класса Dog
}
但是,如果我们为类指定Animal修饰符,类也final将无法从它继承。 CatDog
public final class Animal {

}

public class Cat extends Animal {

   //ошибка! Cannot inherit from final Animal
}
编译器立即产生错误。Java 中已经实现了许多类final。您经常使用的最著名的是String. 此外,如果一个类被声明为final,那么它的所有方法也将变为final。这是什么意思?如果为方法指定了修饰符final,则无法覆盖该方法。例如,我们有一个Animal定义方法的类voice()。然而,狗和猫的“说话”方式显然不同。Cat因此,在每个类中Dog,我们将创建一个方法voice(),但我们将以不同的方式实现它。
public class Animal {

   public void voice() {
       System.out.println("Voice!");
   }
}

public class Cat extends Animal {

   @Override
   public void voice() {
       System.out.println("Meow!");
   }
}

public class Dog extends Animal {

   @Override
   public void voice() {
       System.out.println("Woof!");
   }
}
在类中CatDog我们重写了父类的方法。现在动物会根据它是什么类对象发出声音:
public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       Dog dog = new Dog();

       cat.voice();
       dog.voice();
   }
}
结论: 喵!纬! 但是,如果Animal我们在类中声明一个方法voice()final,则无法在其他类中重新定义它:
public class Animal {

   public final void voice() {
       System.out.println("Voice!");
   }
}


public class Cat extends Animal {

   @Override
   public void voice() {//ошибка! final-метод не может быть переопределен!
       System.out.println("Meow!");
   }
}
然后我们的对象将被迫使用voice()父类中定义的方法:
public static void main(String[] args) {

   Cat cat = new Cat();
   Dog dog = new Dog();

   cat.voice();
   dog.voice();
}
结论: 声音!嗓音! 现在关于final- 变量。否则它们被称为常量。首先(也是最重要的),分配给常量的第一个值不能更改。它被分配一次并且永远。
public class Main {

   private static final int CONSTANT_EXAMPLE = 333;

   public static void main(String[] args) {

       CONSTANT_EXAMPLE = 999;//ошибка! Нельзя присвоить новое meaning final-переменной!
   }
}
该常量不需要立即初始化。这可以稍后完成。但首先分配的值将永远保留。
public static void main(String[] args) {

   final int CONSTANT_EXAMPLE;

   CONSTANT_EXAMPLE = 999;//так делать можно
}
其次,注意变量的名称。Java 常量有不同的命名约定。这不是我们习惯的驼峰命名法。对于常规变量,我们将其称为constantExample,但常量的名称是大写的,并且在单词之间(如果有多个单词)有一个下划线 - “CONSTANT_EXAMPLE”。为什么需要常量?例如,如果您在程序中不断使用某些常量值,它们就会派上用场。假设您决定独自编写《巫师 4》游戏并载入史册。游戏显然会不断使用主角的名字——“利维亚的杰洛特”。最好将这一行和其他英雄的名字分开成一个常量:您需要的值将存储在一个地方,并且您在第一百万次输入时绝对不会出错。
public class TheWitcher4 {

   private static final String GERALT_NAME = "Геральт из Ривии";
   private static final String YENNEFER_NAME = "Йеннифэр из Венгерберга";
   private static final String TRISS_NAME = "Трисс Меригольд";

   public static void main(String[] args) {

       System.out.println("Ведьмак 4");
       System.out.println("Это уже четвертая часть Ведьмака, а " + GERALT_NAME + " ниHow не определится кто ему" +
               " нравится больше: " + YENNEFER_NAME + " or " + TRISS_NAME);

       System.out.println("Но если вы никогда не играли в Ведьмака - начнем сначала.");
       System.out.println("Главного героя зовут " + GERALT_NAME);
       System.out.println(GERALT_NAME + " - ведьмак, охотник на чудовищ");
   }
}
结论:
Ведьмак 4
Это уже четвертая часть Ведьмака, а Геральт из Ривии ниHow не определится, кто ему нравится больше: Йеннифэр из Венгерберга or Трисс Меригольд.
Но если вы никогда не играли в Ведьмака — начнем сначала.
Главного героя зовут Геральт из Ривии
Геральт из Ривии — ведьмак, охотник на чудовищ
我们把字符的名字拆成了常量,现在肯定不会拼错了,也不用每次都手写了。另一个优点是:如果我们最终需要在整个程序中更改变量的值,那么在一个地方完成就足够了,而不是在整个代码中手动重做:)

不可变类型

在使用 Java 工作期间,您可能已经习惯了程序员几乎完全控制所有对象的状态这一事实。想要创建一个对象Cat。如果我愿意的话,我就给它重命名了。如果他愿意的话,他可以改变自己的年龄,或者其他什么。但在 Java 中,有几种数据类型具有特殊的状态。它们是不可变的,或者说不可变的。这意味着如果一个类是不可变的,则其对象的状态不能更改。例子?您可能会感到惊讶,但Immutable类最著名的例子是String!看起来我们不能改变字符串的值?咱们试试吧:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   str1 = "I love Python";//но поведение str1 ниHow не влияет на str2
   System.out.println(str2);//str2 продолжает указывать на строку "I love Java", хотя str1 уже указывает на другой an object
}
结论: 我爱 Java 我爱 Java 之后我们写道:
str1 = "I love Python";
带有字符串“I love Java”的对象没有改变,也没有消失。它安全地存在,并且里面的文本与以前完全相同。代码:
str1 = "I love Python";
刚刚创建了另一个对象,现在变量str1指向它。但我们无法以任何方式影响“我爱 Java”对象。好吧,让我们尝试一下不同的方法!该类String充满了方法,其中一些似乎可以更改行的状态!例如,有一个方法replace()。让我们将本行中的单词“Java”更改为单词“Python”!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   str1.replace("Java", "Python");//попробуем изменить состояние str1, заменив слово "Java" на “Python”
   System.out.println(str2);
}
结论: 我爱Java我爱Java 又不行了!也许曲线方法不起作用?让我们尝试另一个。例如,substring()。根据传输的字符数修剪字符串。让我们将其修剪为前 10 个字符:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   str1.substring(10);//обрезаем исходную строку
   System.out.println(str2);
}
结论: 我爱 Java 我爱 Java Java 中的不可变:final、常量和 Immutable - 3 一切都没有改变。它不应该有。正如我们所说,对象String是不可变的。那么这些类方法是什么String?他们可以修剪线条、更改其中的字符等。如果什么都没有发生,为什么还需要它们呢?他们能!但它们每次都会返回一个新的字符串对象。写成这样是没有用的:
str1.replace("Java", "Python");
- 你不会改变原来的对象。但是,如果将方法的结果写入新的引用变量,您将立即看到差异!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   String str1AfterReplacement =  str1.replace("Java", "Python");
   System.out.println(str2);

   System.out.println(str1AfterReplacement);
}
这是所有这些方法起作用的唯一方法String。您无法对“我爱 Java”对象执行任何操作。只需创建一个新对象并写入:“新对象 = 使用“我爱 Java”对象进行一些操作的结果。” 还有哪些其他类型是不可变的? 现在你肯定需要记住的是——所有基本类型上的包装类都是不可变的。 Integer, Byte, Character, Short, Boolean, Long, Double, Float- 所有这些类都创建不可变对象。这还包括用于创建大数的类 -BigIntegerBigDecimal。我们最近经历了例外情况并谈到了StackTrace。因此:java.lang.StackTraceElement类的对象也是不可变的。这是合乎逻辑的:如果有人可以更改堆栈上的数据,则可能会导致所有相关工作失效。想象一下有人进入StackTrace并将OutOfMemoryError更改为FileNotFoundException。您应该使用该堆栈并查找错误原因。并且该程序根本不使用文件:)因此,为了安全起见,这些对象被设置为不可变的。嗯,有了StackTraceElement,情况或多或少就清楚了。为什么有人想要使字符串不可变?如果可以改变他们的价值观,会出现什么问题。这可能会更方便:/ 这样做有几个原因。首先,节省内存。可以放置不可变的字符串String Pool,并且每次都可以使用相同的字符串,而不是创建新的字符串。其次,安全。例如,任何程序中的大多数登录名和密码都是字符串。更改它们的可能性可能会导致授权问题。还有其他原因,但我们在学习 Java 时还没有触及它们——我们稍后会回来。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION