CodeGym /Java Blog /무작위의 /Java의 고정 값: 최종, 상수 및 불변
John Squirrels
레벨 41
San Francisco

Java의 고정 값: 최종, 상수 및 불변

무작위의 그룹에 게시되었습니다
안녕! 당신은 이미 "modifier"라는 단어에 익숙합니다. 최소한 액세스 한정자(공용, 비공개) 및 정적 한정자를 접했습니다. 오늘 우리는 final 이라는 특수 수정자에 대해 논의할 것입니다 . 최종 수정자는 프로그램의 일정하고 명확하며 변경되지 않는 동작이 필요한 부분을 "시멘트"라고 말할 수 있습니다. 프로그램에서 사용할 수 있는 세 위치는 클래스, 메서드 및 변수입니다. Java의 고정 값: 최종, 상수 및 불변 - 2 순서대로 실행해 봅시다. 클래스 선언에 final 한정자 사용되면 클래스를 상속할 수 없음을 의미합니다. Animal이전 수업에서는 간단한 상속 예제를 사용했습니다. 부모 클래스와 두 개의 자식 클래스가 있었습니다 Cat.Dog

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
}
그러나 클래스 에서 final 한정자를 사용하면 및 클래스는 이를 상속할 수 없습니다. AnimalCatDog

public final class Animal {

}

public class Cat extends Animal {

   // Error! Cannot inherit from final Animal
}
컴파일러는 즉시 오류를 생성합니다. Java에서는 많은 최종 클래스가 이미 구현되어 있습니다. 자주 사용하는 것 중에서 String가장 잘 알려진 것입니다. 또한 클래스가 final 로 선언되면 클래스의 모든 메서드도 final 이 됩니다 . 그게 무슨 뜻이야? 메서드가 final 한정자를 사용하여 선언된 경우 해당 메서드를 재정의할 수 없습니다. Animal예를 들어 여기에 메서드 를 선언하는 클래스가 있습니다 speak(). 그러나 개와 고양이는 확실히 다른 방식으로 "말"합니다. Cat따라서 및 클래스 모두에서 speak() 메서드를 선언 Dog하지만 다르게 구현합니다.

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그러나 클래스의 메서드를 final로 선언하면 speak()다른 클래스에서 재정의할 수 없습니다.

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에는 상수에 대해 다른 명명 규칙이 있습니다. 일반적인 camelCase 표기법 이 아닙니다 . 일반 변수였다면 constantExample 이라고 불렀을 것입니다.. 그러나 상수의 이름은 단어 사이에 밑줄이 있는 모두 대문자로 작성됩니다(단어가 두 개 이상인 경우). 예: "CONSTANT_EXAMPLE". 상수가 필요한 이유는 무엇입니까? 예를 들어 프로그램에서 정기적으로 사용하는 고정 값이 있는 경우 매우 유용합니다. 예를 들어, 당신은 스스로 역사를 만들고 "The Witcher 4" 게임을 쓰기로 결정했습니다. 이 게임은 분명히 정기적으로 주인공의 이름인 "Geralt of Rivia"를 사용합니다. 이 문자열(및 다른 영웅의 이름)은 상수로 선언하는 것이 가장 좋습니다. 해당 값은 한 곳에 저장되며 백만 번 입력해도 오타가 발생하지 않습니다.

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 이미 네 번째 Witcher 게임이지만 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);
}
출력: 저는 Java를 좋아합니다. 저는 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의 고정 값: 최종, 상수 및 불변 - 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". 새 개체를 만들고 "I love Java" object "다음과 같이 작성할 수 있습니다 Integer. Byte, Character, Short, , , : 이 모든 클래스는 객체를 생성합니다.(다음 강의에서 객체에 대해 설명하겠습니다.) 여기에는 and 와 같은 큰 숫자를 생성하는 데 사용되는 클래스가 포함됩니다. Boolean최근 에 예외를 다루고 스택 추적을 다루었습니다 . 글쎄요. , java.lang.StackTraceElementLongDoubleFloatimmutableBigIntegerBigDecimal객체도 변경할 수 없습니다. 이것은 의미가 있습니다. 누군가가 스택의 데이터를 변경할 수 있다면 모든 것이 무의미해집니다. 스택 추적을 통해 OutOfMemoryError 를 FileNotFoundException 으로 변경하는 누군가를 상상해 보십시오 . 그런 다음 해당 스택을 사용하여 오류의 원인을 찾습니다. 그러나 프로그램은 파일을 사용하지도 않습니다. :) 그래서 그들은 만일의 경우를 대비하여 이러한 객체를 불변으로 만들었습니다. 자, StackTraceElement 에 대해 어느 정도 이해가 됩니다 . 하지만 왜 Strings를 불변으로 만들어야 할까요? 그들의 가치를 바꾸는 것이 왜 문제가 될까요? 아마 더 편리할 것입니다. :/ 여기에는 몇 가지 이유가 있습니다. 첫째, 메모리를 절약합니다. 변경할 수 없는 문자열을 문자열 풀 에 배치할 수 있습니다., 새 문자열을 만드는 대신 문자열을 재사용할 수 있습니다. 둘째, 보안을 위해. 예를 들어 사용자 이름과 암호는 거의 모든 프로그램에서 문자열입니다. 변경을 가능하게 하면 인증 문제가 발생할 수 있습니다. 다른 이유가 있지만 Java에 대한 연구에서는 아직 다루지 않았으므로 나중에 다시 설명하겠습니다.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION