CodeGym/Java 博客/随机的/Java 中的固定值:final、constants 和 immutable
John Squirrels
第 41 级
San Francisco

Java 中的固定值:final、constants 和 immutable

已在 随机的 群组中发布
个会员
你好!您已经熟悉“修饰符”一词。至少,您遇到过访问修饰符(public、private)和 static 修饰符。今天我们将讨论一个名为final的特殊修饰符。您可以说 final 修饰符“巩固”了我们程序中需要恒定、明确、不变行为的部分。您可以在程序中的三个地方使用它:类、方法和变量。 Java 中的固定值:final、constants 和 immutable - 2 让我们按顺序浏览它们。如果类声明中使用了final修饰符,则意味着该类不能被继承。在之前的课程中,我们使用了一个简单的继承示例:我们有一个Animal父类和两个子类:CatDog
public class Animal {
}



public class Cat extends Animal {
   // Fields and methods of the Cat class
}


public class Dog extends Animal {

   // Fields and methods of the Dog class
}
但是,如果我们在类上使用finalAnimal修饰符,则类CatDog类不能继承它。
public final class Animal {

}

public class Cat extends Animal {

   // Error! Cannot inherit from final Animal
}
编译器立即生成错误。在 Java 中,许多最终类已经实现。在您经常使用的那些中,String是最著名的。此外,如果一个类被声明为final,则该类的所有方法也将变为final。这意味着什么?如果使用final修饰符声明方法,则不能重写该方法。例如,这里我们有一个Animal声明speak()方法的类。但是,狗和猫肯定以不同的方式“说话”。因此,我们将在CatDog类中声明 speak() 方法,但我们将以不同方式实现它们。
public class Animal {

   public void speak() {
       System.out.println("Hello!");
   }
}

public class Cat extends Animal {

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

public class Dog extends Animal {

   @Override
   public void speak() {
       System.out.println("Woof!");
   }
}
我们使CatDog类覆盖了在父类中声明的方法。现在,动物会说不同的话,这取决于它是什么类型的物体:
public class Main {

   public static void main(String[] args) {

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

       cat.speak();
       dog.speak();
   }
}
输出: 喵!纬! 但是,如果我们将Animal类的speak()方法声明为final,那么我们就不能在其他类中重写它:
public class Animal {

   public final void speak() {
       System.out.println("Hello!");
   }
}


public class Cat extends Animal {

   @Override
   public void speak() {// Error! A final method can't be overridden!
       System.out.println("Meow!");
   }
}
我们的对象将被迫使用speak()父类中定义的方法:
public static void main(String[] args) {

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

   cat.speak();
   dog.speak();
}
输出: 你好!你好! 现在,关于最终变量。它们也称为常量。首先(也是最重要的),不能更改分配给常量值的初始值。它是一次性分配的。
public class Main {

   private static final int CONSTANT_EXAMPLE = 333;

   public static void main(String[] args) {

       CONSTANT_EXAMPLE = 999;// Error! You can't assign a new value to a final variable!
   }
}
常量不需要立即初始化。那可以稍后再做。但是,最初分配给它的值将永远保持不变。
public static void main(String[] args) {

   final int CONSTANT_EXAMPLE;

   CONSTANT_EXAMPLE = 999;// This is allowed
}
其次,记下我们变量的名称。Java 对常量有不同的命名约定。它不是通常的驼峰命名法。如果它是一个普通变量,我们会称它为constantExample. 但是,常量的名称全部大写,单词之间有下划线(如果有多个单词),例如“CONSTANT_EXAMPLE”。为什么我们需要常量?它们非常有用,例如,如果您经常在程序中使用固定值。例如,您决定创造历史并亲手编写游戏“巫师 4”。游戏显然会经常使用主角的名字:“利维亚的杰洛特”。这个字符串(以及其他英雄的名字)最好声明为常量:它的值将存储在一个地方,你输入一百万次绝对不会错字。
public class TheWitcher4 {

   private static final String GERALT_NAME = "Geralt of Rivia";
   private static final String YENNEFER_NAME = "Yennefer of Wengerberg";
   private static final String TRISS_NAME = "Triss Merigold";

   public static void main(String[] args) {

       System.out.println("The Witcher 4");
       System.out.println("It's already the fourth Witcher game, but " + GERALT_NAME + " still can't decide who" +
               " he likes more: " + YENNEFER_NAME + " or " + TRISS_NAME);

       System.out.println("But, if you've never played The Witcher before, we'll start from the beginning.");
       System.out.println("The protagonist's name is " + GERALT_NAME);
       System.out.println(GERALT_NAME + " is a witcher, a monster hunter");
   }
}
输出: The Witcher 4 已经是巫师的第四场比赛了,但是 Rivia 的 Geralt 仍然无法决定他更喜欢谁:Wengerberg 的 Yennefer 还是 Triss Merigold 但是,如果你以前从未玩过 The Witcher,我们将从开始。主角的名字是利维亚的杰洛特 利维亚的杰洛特是猎魔人,怪物猎人 我们已经将英雄的名字声明为常量。现在肯定不会打错了,也不用每次都手写了。另一个优点:如果我们需要在整个程序中更改变量的值,您可以在一个地方完成,而不是在整个代码库中手动修改它。:)

不可变类型

当您使用过 Java 时,您可能已经习惯了这样的想法,即程序员几乎可以完全控制所有对象的状态。如果你想创建一个Cat对象,你可以。如果你想重命名它,你可以。如果你想改变它的年龄或其他东西,你可以。但是 Java 有几种具有特殊属性的数据类型。它们是不可变的。如果一个类是不可变的,那么它的对象的状态就不能改变。想要一些例子吗?您可能会感到惊讶,但最著名的不可变类是 String!那么,我们真的不能改变一个字符串的值吗?好吧,让我们试试看:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   str1 = "I love Python";// but changing str1 has no impact on str2
   System.out.println(str2);// str2 continues to point to the "I love Java" string, but str1 now points to a different object
}
输出: 我爱 Java 我爱 Java 我们写完之后
str1 = "I love Python";
字符串"I love Java"对象没有改变或去任何地方。它仍然愉快地存在并且具有与以前完全相同的文本。代码
str1 = "I love Python";
只是创建了另一个对象,str1现在指向它。但是,我们似乎无法对“I love Java”字符串对象产生任何影响。好吧,让我们试试别的吧!这个String类充满了方法,其中一些似乎可以改变对象的状态!例如,有一个replace()方法。让我们将字符串中的单词“Java”更改为“Python”!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   str1.replace("Java", "Python");// We try to change the state of str1 by swapping the word "Java" with "Python"
   System.out.println(str2);
}
输出: I love Java I love Java 它又没用了!也许替换方法不起作用? 让我们试试别的。例如,substring()。它根据作为参数传递的字符索引返回一个子字符串。让我们切断字符串的前 10 个字符:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

   str1.substring(10);// Truncate the original String
   System.out.println(str2);
}
输出: 我爱 Java 我爱 Java Java 中的固定值:final、constants 和 immutable - 3 没有任何改变。它不应该有。正如我们之前所说,字符串是不可变的。那么,类中的所有方法是什么String?毕竟,它们可以截断字符串、更改字符等等。如果什么都没发生,那又有什么意义呢?他们居然可以做这些事!但是,他们每次都返回一个新的字符串。写的没意义
str1.replace("Java", "Python");
因为您无法更改原始对象。但是,如果将方法的结果写入新的引用变量,您会立即看到不同之处!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;// Both reference variables point to the same string.
   System.out.println(str2);

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

   System.out.println(str1AfterReplacement);
}
所有String方法都以这种方式工作。无法对该"I love Java"对象进行任何操作。您可以创建一个新对象并写入:“<new object> = 操作的结果"I love Java" object "。还有哪些类型是不可变的?您肯定需要立即记住的一些是原始类型的所有包装类。 IntegerByte, Character, Short, Boolean, Long, Double, Float:所有这些类都创建immutable对象(我们将在接下来的课程中讨论它们)。这包括用于创建大数字的类,例如BigIntegerBigDecimal。我们最近介绍了异常并触及了堆栈跟踪。好吧,猜猜是什么,java.lang.StackTraceElement对象也是不可变的。这是有道理的:如果有人可以更改我们堆栈的数据,那么整个事情就毫无意义了。想象一下有人通过堆栈跟踪并将OutOfMemoryError更改为FileNotFoundException。然后你使用那​​个堆栈来找出错误的原因。但是该程序甚至不使用文件。:) 所以,他们让这些对象不可变,以防万一。好的,所以它或多或少对StackTraceElement有意义。但是,为什么有人需要让字符串不可变呢?为什么改变他们的价值观会成为一个问题?它可能会更方便。:/ 有几个原因。首先,它节省内存。不可变字符串可以放在字符串池中,允许重复使用字符串而不是创建新字符串。第二,为了安全。例如,几乎每个程序中的用户名和密码都是字符串。使更改它们成为可能可能会导致授权问题。还有其他原因,但我们对 Java 的学习还没有涉及到它们,所以我们稍后再回过头来。
评论
  • 受欢迎
你必须先登录才能发表评论
此页面还没有任何评论