JavaRush /Java Blog /Random-KO /Java의 기본 유형: 그다지 원시적이지 않습니다.
Viacheslav
레벨 3

Java의 기본 유형: 그다지 원시적이지 않습니다.

Random-KO 그룹에 게시되었습니다

소개

애플리케이션 개발은 일부 데이터를 다루는 작업, 오히려 데이터를 저장하고 처리하는 작업으로 간주될 수 있습니다. 오늘 저는 첫 번째 핵심 측면을 다루고 싶습니다. Java에서는 데이터가 어떻게 저장되나요? 여기에는 참조 데이터 유형 과 기본 데이터 유형이라는 두 가지 가능한 형식이 있습니다 . 기본 유형의 유형과 이를 사용할 수 있는 가능성에 대해 이야기해 보겠습니다(누가 뭐라고 말하든 이것이 프로그래밍 언어에 대한 지식의 기초입니다). Java 기본 데이터 유형은 모든 것이 기반이 되는 기초입니다. 아니요, 전혀 과장하지 않습니다. Oracle에는 기본 요소에 대한 별도의 튜토리얼이 있습니다. 기본 데이터 유형 Java의 기본 유형: 그다지 원시적이지 않습니다. - 1 약간의 역사. 처음에는 0이 있었습니다. 하지만 제로는 지루해요. 그리고 비트가 나타났습니다 . 왜 그렇게 불렸나요? 약어 "binary digit"(이진수)에서 이름 붙여 졌습니다 . 즉, 두 가지 의미만 가지고 있습니다. 그리고 0이었으니 이제는 0 아니면 1이라는 것이 논리적이다. 그리고 삶은 더욱 재미있어졌다. 비트가 무리를 지어 모이기 시작했습니다. 그리고 이 무리를 바이트 (byte) 라고 부르기 시작했다 . 현대 세계에서는 바이트 = 2의 3승입니다. 8. 그러나 항상 그런 것은 아니었다는 것이 밝혀졌습니다. 바이트라는 이름이 어디서 왔는지에 대한 많은 추측, 전설 및 소문이 있습니다. 어떤 사람들은 그 당시의 인코딩에 관한 것이라고 생각하는 반면, 다른 사람들은 이런 식으로 정보를 읽는 것이 더 유익하다고 생각합니다. 바이트는 주소를 지정할 수 있는 가장 작은 메모리 조각입니다. 메모리에 고유한 주소를 갖는 바이트입니다. ByTe는 기계어인 Binary Term의 약어라는 전설이 있습니다. 기계어 - 간단히 말해서 프로세서가 한 번의 작업으로 처리할 수 있는 데이터의 양입니다. 이전에는 기계어 크기가 주소 지정이 가능한 가장 작은 메모리와 동일했습니다. Java에서 변수는 바이트 값만 저장할 수 있습니다. 위에서 말했듯이 Java에는 두 가지 유형의 변수가 있습니다.
  • Java 기본 유형은 데이터 바이트의 값을 직접 저장합니다(아래에서 이러한 기본 유형을 자세히 살펴보겠습니다).
  • 참조 유형은 객체 주소의 바이트를 힙에 저장합니다. 즉, 이러한 변수를 통해 객체 자체에 직접 액세스할 수 있습니다(객체에 대한 일종의 원격 제어).

자바 바이트

그래서 역사는 우리에게 사용할 수 있는 최소 메모리 양인 바이트를 제공했습니다. 그리고 8비트로 구성되어 있습니다. Java에서 가장 작은 정수 데이터 유형은 바이트입니다. 이는 부호 있는 8비트 유형입니다. 무슨 뜻이에요? 세어 보자. 2^8은 256입니다. 하지만 음수를 원하면 어떻게 될까요? 그리고 Java 개발자는 이진 코드 "10000000"이 -128을 나타내기로 결정했습니다. 즉, 최상위 비트(가장 왼쪽 비트)는 숫자가 음수인지 여부를 나타냅니다. 이진수 "0111 1111"은 127과 같습니다. 즉, 128은 어떤 방식으로도 지정할 수 없습니다. -128이 됩니다. 전체 계산은 다음 답변에 제공됩니다. Java에서 바이트 범위가 -128에서 127인 이유는 무엇입니까? 숫자를 얻는 방법을 이해하려면 그림을 살펴봐야 합니다.
Java의 기본 유형: 그다지 원시적이지 않습니다. - 2
따라서 크기를 계산하려면 2^(8-1) = 128입니다. 이는 최소 한계(마이너스가 있음)가 -128임을 의미합니다. 그리고 최대값은 128 – 1입니다(0을 뺍니다). 즉, 최대값은 127입니다. 실제로 우리는 "높은 수준"에서 바이트 유형을 자주 사용하지 않습니다. 기본적으로 이는 "원시" 데이터를 처리하는 것입니다. 예를 들어, 네트워크를 통한 데이터 전송 작업을 할 때 데이터가 일부 통신 채널을 통해 전송되는 0과 1의 집합인 경우입니다. 또는 파일에서 데이터를 읽을 때. 문자열 및 인코딩 작업 시에도 사용할 수 있습니다. 예제 코드:
public static void main(String []args){
        byte value = 2;
        byte shortByteValue = 0b10; // 2
        System.out.println(shortByteValue);
        // Начиная с JDK7 мы можем разделять литералы подчёркиваниями
        byte minByteValue = (byte) 0B1000_0000; // -128
        byte maxByteValue = (byte) 0b0111_1111; // 127
        byte minusByteValue = (byte) 0b1111_1111; // -128 + 127
        System.out.println(minusByteValue);
        System.out.println(minByteValue + " to " + maxByteValue);
}
그런데 바이트 유형을 사용하면 메모리 소비가 줄어들 것이라고 생각하지 마세요. 바이트는 주로 데이터를 배열에 저장할 때 메모리 소비를 줄이는 데 사용됩니다(예: 네트워크를 통해 수신된 데이터를 바이트 배열로 구현될 일부 버퍼에 저장하는 경우). 그러나 데이터에 대한 작업을 수행할 때 바이트를 사용하면 기대에 미치지 못할 것입니다. 이는 JVM(Java Virtual Machine)의 구현 때문입니다. 대부분의 시스템이 32비트 또는 64비트이므로 계산 중 byte 및 short가 32비트 int로 변환되는데 이에 대해서는 나중에 설명하겠습니다. 이렇게 하면 계산이 더 쉬워집니다. 자세한 내용은 Java 언어 규칙 때문에 또는 jvm 때문에 바이트 추가가 int로 변환됩니까?를 참조하십시오. . 답변에는 JLS(Java 언어 사양)에 대한 링크도 포함되어 있습니다. 또한, 바이트를 잘못된 위치에 사용하면 어색한 상황이 발생할 수 있습니다.
public static void main(String []args){
        for (byte i = 1; i <= 200; i++) {
            System.out.println(i);
        }
}
여기에 루프가 있을 것입니다. 카운터 값이 최대값(127)에 도달하므로 오버플로가 발생하여 값이 -128이 됩니다. 그리고 우리는 결코 그 순환에서 벗어나지 못할 것입니다.

짧은

바이트 값의 제한은 매우 작습니다. 따라서 다음 데이터 유형에서는 비트 수를 두 배로 늘리기로 결정했습니다. 즉, 이제는 8비트가 아닌 16비트입니다. 즉, 2바이트입니다. 값도 같은 방법으로 계산할 수 있습니다. 2^(16-1) = 2^15 = 32768. 이는 범위가 -32768에서 32767까지임을 의미합니다. 특별한 경우에는 거의 사용되지 않습니다. Java 언어 문서에 따르면 " 대규모 배열의 메모리를 절약하려면 짧은 형식을 사용할 수 있습니다 ."라고 나와 있습니다.

정수

그래서 우리는 가장 자주 사용되는 유형을 얻었습니다. 32비트, 즉 4바이트를 차지합니다. 일반적으로 우리는 계속 두 배로 늘어납니다. 값의 범위는 -2^31부터 2^31 – 1까지입니다.

최대 정수 값

int 2147483648의 최대값은 1로 전혀 작지 않습니다. 위에서 언급한 것처럼 계산을 최적화하기 위해서는 최신 컴퓨터에서는 비트 용량을 고려하여 계산하는 것이 더 편리합니다. 데이터는 암시적으로 int로 변환될 수 있습니다. 간단한 예는 다음과 같습니다.
byte a = 1;
byte b = 2;
byte result = a + b;
이러한 무해한 코드이지만 "오류: 호환되지 않는 유형: int에서 바이트로 손실이 있는 변환이 가능합니다."라는 오류가 발생합니다. 이를 byte result = (byte)(a + b); 로 수정해야 합니다. 그리고 또 하나의 무해한 예입니다. 다음 코드를 실행하면 어떻게 될까요?
int value = 4;
System.out.println(8/value);
System.out.println(9/value);
System.out.println(10/value);
System.out.println(11/value);
그리고 우리는 결론을 얻을 것입니다
2
2
2
2
*공포의 소리*
사실 int 값으로 작업할 때 나머지는 버려지고 전체 부분만 남습니다(이런 경우 double을 사용하는 것이 더 좋습니다).

우리는 계속 두 배로 늘고 있습니다. 32에 2를 곱하면 64비트가 됩니다. 전통적으로 이는 4 * 2, 즉 8바이트입니다. 값의 범위는 -2^63에서 2^63 – 1까지입니다. 충분합니다. 이 유형을 사용하면 크고 큰 숫자를 셀 수 있습니다. 시간 작업을 할 때 자주 사용됩니다. 또는 장거리 이동 등이 있습니다. 숫자가 길다는 것을 나타내려면 숫자 뒤에 리터럴 L – Long을 배치하세요. 예:
long longValue = 4;
longValue = 1l; // Не ошибка, но плохо читается
longValue = 2L; // Идеально
나는 나 자신보다 앞서 나가고 싶다. 다음으로, 기본 요소를 객체로 사용하여 작업할 수 있게 해주는 기본 요소에 해당하는 래퍼가 있다는 사실을 고려할 것입니다. 그런데 흥미로운 특징이 있습니다. 예는 다음과 같습니다. 동일한 Tutorialspoint 온라인 컴파일러를 사용하여 다음 코드를 확인할 수 있습니다.
public class HelloWorld {

     public static void main(String []args) {
        printLong(4);
     }

    public static void printLong(long longValue) {
        System.out.println(longValue);
    }
}
이 코드는 오류 없이 작동하며 모든 것이 정상입니다. 그러나 printLong 메소드의 유형이 long에서 Long으로 바뀌자마자(즉, 유형이 원시적이지 않고 객체가 됨), 우리가 전달하는 매개변수가 Java에 명확하지 않게 됩니다. int가 전송되고 있고 오류가 있을 것이라고 가정하기 시작합니다. 따라서 메소드의 경우에는 4L을 명시적으로 표시할 필요가 있을 것이다. 데이터베이스 작업 시 long이 ID로 사용되는 경우가 많습니다.

Java float 및 Java double

이러한 유형을 부동 소수점 유형이라고 합니다. 즉, 정수 유형이 아닙니다. float 유형은 (int와 같은) 32비트이고, double은 배정밀도 유형이라고 하므로 64비트입니다(원하는 대로 2를 곱함). 예:
public static void main(String []args){
        // float floatValue = 2.3; lossy conversion from double to float
        float floatValue = 2.3F;
        floatValue = 2.3f;
        double doubleValue = 2.3;
        System.out.println(floatValue);
        double cinema = 7D;
}
다음은 (유형 정밀도로 인한) 값 차이의 예입니다.
public static void main(String []args){
        float piValue = (float)Math.PI;
        double piValueExt = Math.PI;
        System.out.println("Float value: " + piValue );
        System.out.println("Double value: " + piValueExt );
 }
예를 들어 이러한 기본 유형은 수학에서 사용됩니다. 다음은 숫자 PI를 계산하기 위한 상수인 증명입니다 . 일반적으로 Math 클래스의 API를 보면 됩니다. 중요하고 흥미로운 내용은 다음과 같습니다. 문서에서도 다음과 같이 말합니다. “ 이 데이터 유형은 통화와 같은 정확한 값에 사용되어서는 안 됩니다. 이를 위해서는 java.math.BigDecimal 클래스를 대신 사용해야 합니다. 숫자 및 문자열에는 BigDecimal 및 Java 플랫폼에서 제공하는 기타 유용한 클래스가 포함됩니다. " 즉, float 및 double의 돈을 계산할 필요가 없습니다. NASA에서의 작업 예를 사용한 정확성에 대한 예: Java BigDecimal, 고정밀 계산 처리 음, 직접 느껴보세요:
public static void main(String []args){
        float amount = 1.0000005F;
        float avalue = 0.0000004F;
        float result = amount - avalue;
        System.out.println(result);
}
이 예를 따른 다음 숫자 5와 4 앞에 0을 추가하면 모든 공포를 볼 수 있습니다.) 주제에 대한 float 및 double에 대한 러시아어 보고서가 있습니다: https://youtu.be/1RCn5ruN1fk 작업 예 BigDecimal을 사용하는 방법은 여기에서 볼 수 있습니다. BigDecimal을 사용하여 센트를 만드세요 . 그런데 float와 double은 단순한 숫자 이상의 결과를 반환할 수 있습니다. 예를 들어 아래 예에서는 Infinity를 반환합니다.
public static void main(String []args){
        double positive_infinity = 12.0 / 0;
        System.out.println(positive_infinity);
}
그리고 이것은 NAN을 반환합니다:
public static void main(String []args){
        double positive_infinity = 12.0 / 0;
        double negative_infinity = -15.0 / 0;
        System.out.println(positive_infinity + negative_infinity);
}
무한대에 대해서는 분명합니다. NaN이란 무엇입니까? 이는 숫자가 아님 입니다 . 즉, 결과를 계산할 수 없으며 숫자가 아님을 의미합니다. 예를 들면 다음과 같습니다. -4의 제곱근을 계산하려고 합니다. 4의 제곱근은 2입니다. 즉, 2를 제곱해야 하며 4를 얻습니다. -4를 얻으려면 무엇을 제곱해야 할까요? 작동하지 않을 것입니다. 왜냐면... 양수가 있으면 그대로 유지됩니다. 그리고 그것이 음수라면 마이너스를 마이너스로 하면 플러스가 됩니다. 즉, 계산할 수 없습니다.
public static void main(String []args){
        double sqrt = Math.sqrt(-4);
        System.out.println(sqrt + 1);
        if (Double.isNaN(sqrt)) {
           System.out.println("So sad");
        }
        System.out.println(Double.NaN == sqrt);
}
부동 소수점 숫자 주제에 대한 또 다른 훌륭한 개요는 다음과 같습니다. 요점은 어디에 있습니까?
그 밖에 읽을 내용:

자바 부울

다음 유형은 Boolean(논리 유형)입니다. 키워드인 true 또는 false 값만 허용할 수 있습니다. while 루프와 같은 논리 연산과 if, 스위치를 사용한 분기에 사용됩니다. 여기서 어떤 흥미로운 사실을 발견할 수 있나요? 예를 들어, 이론적으로는 0 또는 1, 즉 참 또는 거짓이라는 1비트의 정보만 필요합니다. 그러나 실제로 Boolean은 더 많은 메모리를 차지하며 이는 특정 JVM 구현에 따라 달라집니다. 일반적으로 비용은 int와 동일합니다. 또 다른 옵션은 BitSet을 사용하는 것입니다. 다음은 Java Fundamentals 책의 간단한 설명입니다. BitSet

자바 문자

이제 우리는 마지막 기본 유형에 도달했습니다. 따라서 char의 데이터는 16비트를 차지하고 문자를 설명합니다. Java는 char에 유니코드 인코딩을 사용합니다. 기호는 두 개의 표에 따라 설정될 수 있습니다( 여기에서 볼 수 있음 ).
  • 유니코드 문자 테이블
  • ASCII 문자 테이블
Java의 기본 유형: 그다지 원시적이지 않습니다. - 3
스튜디오의 예:
public static void main(String[] args) {
    char symbol = '\u0066'; // Unicode
    symbol = 102; // ASCII
    System.out.println(symbol);
}
그런데 char는 본질적으로 숫자이므로 합계와 같은 수학 연산을 지원합니다. 때로는 이것이 재미있는 결과를 가져올 수도 있습니다.
public class HelloWorld{

    public static void main(String []args){
        String costForPrint = "5$";
        System.out.println("Цена только для вас " +
        + costForPrint.charAt(0) + getCurrencyName(costForPrint.charAt(1)));
    }

    public static String getCurrencyName(char symbol) {
        if (symbol == '$') {
            return " долларов";
        } else {
            throw new UnsupportedOperationException("Not implemented yet");
        }
    }

}
tutorialspoint에서 온라인 IDE를 확인하는 것이 좋습니다 . 한 컨퍼런스에서 이 퍼즐을 봤을 때 기분이 좋아졌습니다. 예시도 마음에 드셨기를 바랍니다) 업데이트됨: 이것은 Joker 2017에서 보고되었습니다: " Java Puzzlers NG S03 - 다들 어디서 오셨나요?! "

리터럴

리터럴은 명시적으로 지정된 값입니다. 리터럴을 사용하면 다양한 숫자 체계로 값을 지정할 수 있습니다.
  • 십진법: 10
  • 16진수: 0x1F4, 0x로 시작
  • 8진수 시스템: 010, 0부터 시작합니다.
  • 바이너리 시스템(Java7 이후): 0b101, 0b에서 시작
나는 8진수 체계에 대해 조금 더 이야기하고 싶습니다. 왜냐하면 그것이 재미있기 때문입니다:
int costInDollars = 08;
다음 코드 줄은 컴파일되지 않습니다.
error: integer number too large: 08
말도 안되는 것 같습니다. 이제 이진법과 팔진법에 대해 기억해 봅시다. 이진법에는 2가 없습니다. 왜냐하면 두 개의 값이 있습니다(0부터 시작). 그리고 8진수 시스템에는 0부터 시작하여 8개의 값이 있습니다. 즉, 값 8 자체가 존재하지 않습니다. 그러므로 이는 얼핏 터무니없어 보이는 오류이다. 그리고 값을 번역하기 위한 "후속 조치" 규칙은 다음과 같습니다.
Java의 기본 유형: 그다지 원시적이지 않습니다. - 4

래퍼 클래스

Java의 기본 요소에는 자체 래퍼 클래스가 있으므로 이를 개체로 사용할 수 있습니다. 즉, 각 기본 유형에 대해 해당 참조 유형이 있습니다. Java의 기본 유형: 그다지 원시적이지 않습니다. - 5래퍼 클래스는 변경할 수 없습니다. 즉, 객체가 생성되면 해당 상태(값 필드의 값)를 변경할 수 없습니다. 래퍼 클래스는 최종 객체, 즉 읽기 전용으로 선언됩니다. 또한 이러한 클래스에서는 상속이 불가능하다는 점도 언급하고 싶습니다. Java는 기본 유형과 해당 래퍼 간의 변환을 자동으로 수행합니다.
Integer x = 9;          // autoboxing
int n = new Integer(3); // unboxing
기본 유형을 참조 유형(int->Integer)으로 변환하는 과정을 autoboxing 이라고 하고, 그 반대를 unboxing 이라고 합니다 . 이러한 클래스를 사용하면 객체 내부에 기본 요소를 저장할 수 있으며 객체 자체는 객체처럼 동작합니다(다른 객체와 마찬가지로). 이 모든 것을 통해 숫자 비교, 기호를 대소문자로 변환, 기호가 문자인지 숫자인지 확인, 최소 숫자 검색 등과 같은 다양하고 유용한 정적 방법을 많이 얻을 수 있습니다. 제공된 기능 세트는 래퍼 자체에만 의존합니다. int에 대한 래퍼를 직접 구현한 예:
public class CustomerInt {

   private final int value;

   public CustomerInt(int value) {
       this.value = value;
   }

   public int getValue() {
       return value;
   }
}
메인 패키지인 java.lang에는 이미 Boolean, Byte, Short, Character, Integer, Float, Long, Double 클래스의 구현이 있으므로 우리가 직접 아무것도 만들 필요가 없고 이미 만들어진 클래스를 재사용하기만 하면 됩니다. 것들. 예를 들어, 이러한 클래스는 List를 생성하는 기능을 제공합니다. , 목록에는 기본 요소가 아닌 개체만 포함되어야 하기 때문입니다. 기본 유형의 값을 변환하려면 정적 valueOf 메소드가 있습니다. 예를 들어 Integer.valueOf(4)는 Integer 유형의 객체를 반환합니다. 역변환에는 intValue(), longValue() 등의 메소드가 있습니다. 컴파일러는 자체적으로 valueOf 및 *Value에 대한 호출을 삽입합니다. 이것이 autoboxing 및 autounboxing의 핵심입니다. 위에 제시된 자동 포장 및 자동 포장 풀기의 예는 실제로 다음과 같습니다.
Integer x = Integer.valueOf(9);
int n = new Integer(3).intValue();
이 기사에서 자동 포장 및 자동 포장 풀기에 대한 자세한 내용을 읽을 수 있습니다 .

깁스

При работе с примитивами существует такое понятие How приведение типов, одно из не очень приятных свойств C++, тем не менее приведение типов сохранено и в языке Java. Иногда мы сталкиваемся с такими ситуациями, когда нам нужно совершать взаимодействия с данными разных типов. И очень хорошо, что в некоторых ситуациях это возможно. В случае с ссылочными переменными, там свои особенности, связанные с полиморфизмом и наследованием, но сегодня мы рассматриваем простые типы и соответственно приведение простых типов. Существует преобразование с расширением и преобразование сужающее. Всё на самом деле просто. Если тип данных становится больше (допустим, был int, а стал long), то тип становится шире (из 32 бит становится 64). И в этом случае мы не рискуем потерять данные, т.к. если влезло в int, то в long влезет тем более, поэтому данное приведение мы не замечаем, так How оно осуществляется автоматически. А вот в обратную сторону преобразование требует явного указания от нас, данное приведение типа называется — сужение. Так сказать, чтобы мы сами сказали: «Да, я даю себе отчёт в этом. В случае чего — виноват сам».
public static void main(String []args){
   int intValue = 128;
   byte value = (byte)intValue;
   System.out.println(value);
}
Whatбы потом в таком случае не говорor что «Ваша Джава плохая», когда получат внезапно -128 instead of 128 ) Мы ведь помним, что в byteе 127 верхнее meaning и всё что находилось выше него соответственно можно потерять. Когда мы явно превратor наш int в byte, то произошло переполнение и meaning стало -128.

Область видимости

Это то место в codeе, где данная переменная будет выполнять свои функции и хранить в себе Howое-то meaning. Когда же эта область закончится, переменная перестанет существовать и будет стерта из памяти и. How уже можно догадаться, посмотреть or получить ее meaning будет невозможно! Так что же это такое — область видимости? Java의 기본 유형: 그다지 원시적이지 않습니다 - 6Область определяется "блоком" — вообще всякой областью, замкнутой в фигурные скобки, выход за которые сулит удаление данных объявленных в ней. Или How минимум — сокрытие их от других блоков, открытых вне текущего. В Java область видимости определяется двумя основными способами:
  • Классом.
  • Методом.
Как я и сказал, переменная не видна codeу, если она определена за пределами блока, в котором она была инициализирована. Смотрим пример:
int x;
x = 6;
if (x >= 4) {
   int y = 3;
}
x = y;// переменная y здесь не видна!
И How итог мы получим ошибку:

Error:(10, 21) java: cannot find symbol
  symbol:   variable y
  location: class com.javaRush.test.type.Main
Области видимости могут быть вложенными (если мы объявor переменную в первом, внешнем блоке, то во внутреннем она будет видна).

Заключение

Сегодня мы познакомorсь с восемью примитивными типами в Java. Эти типы можно разделить на четыре группы:
  • Целые числа: byte, short, int, long — представляют собой целые числа со знаком.
  • Числа с плавающей точкой — эта группа включает себе float и double — типы, которые хранят числа с точностью до определённого знака после запятой.
  • Булевы значения — boolean — хранят значения типа "истина/ложь".
  • 문자 - 이 그룹에는 char 유형이 포함됩니다.
위의 텍스트에서 볼 수 있듯이 Java의 기본 요소는 그다지 원시적이지 않으며 많은 문제를 효과적으로 해결할 수 있습니다. 그러나 이는 또한 프로그램에서 예측할 수 없는 동작이 발생하지 않도록 하기 위해 염두에 두어야 할 몇 가지 기능을 소개합니다. 그들이 말했듯이 모든 비용을 지불해야합니다. "가파르게"(넓은) 범위(긴 것과 같은)를 갖는 기본 요소를 원한다면 더 큰 메모리 조각의 할당을 반대 방향으로 희생합니다. 메모리를 절약하고 바이트를 사용하면 -128에서 127까지의 제한된 범위를 얻을 수 있습니다.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION