ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [2주차]자바 데이터 타입, 변수 그리고 배열
    스터디/[whiteship]JAVA 2021. 1. 12. 15:23

    목표

    자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법을 익힙니다.


    학습할 것

    • 프리미티브 타입 종류와 값의 범위 그리고 기본 값
    • 프리미티브 타입과 레퍼런스 타입
    • 리터럴
    • 변수 선언 및 초기화하는 방법
    • 변수의 스코프와 라이프타임
    • 타입 변환, 캐스팅 그리고 타입 프로모션
    • 1차 및 2차 배열 선언하기
    • 타입 추론, var

    프리미티브 타입 종류와 값의 범위 그리고 기본 값

    자바의 내장되어 있는 기본 유형인 프리미어 타입은 정수, 실수, 논리, 문자 타입이 존재한다. 이런 프리미어 타입은 stack메모리 영역에 저장된다. 

    출처 : http://wagunblog.com/wp/?p=1068
    출처 : http://wagunblog.com/wp/?p=1068

    Q.데이터 타입마다 표현할 수 있는 범위는 정해져 있다. 만약 표현가능한 범위를 넘으면 어떻게 될까?

    ->우리가 자주 사용하는 int형 데이터 타입으로 실험해보자.

    package Thirdweek;
    
    public class Btest {
        public static void main(String[] args) {
            int a = 2147483647;
            System.out.println(a+1);
        }
    }
    

     

    다음과 같은 값이 나오는 이유는 비트당 표현할 수 있는 크기(맨 앞에 있는 부호비트 포함)를 초과해서 원치않은 결과값이 출력된다.


     

    프리미티브 타입과 레퍼런스 타입

    프리미티브 타입은 앞에서 설명한 것 처럼 Stack영역에 할당된다 하지만 레퍼런스 타입은 다르다.

    레퍼런스 타입은 클래스 타입, 인터페이스 타입, 배열, 열거(Enum) 타입을 참조하는 레퍼런스로 해당 객체의 실제 메모리 주소 참조값이 변수에 저장된다.(C로 따지면 포인터)

    https://m.blog.naver.com/PostView.nhn?blogId=highkrs&logNo=220242895539&proxyReferer=https:%2F%2Fwww.google.co.kr%2F

    Q. 그렇다면 둘의 차이점은 무엇인가?

    A. 프리미티브 타입과 레퍼런스의 큰 차이점은 변수 선언시 메모리에 공간이 할당되는데, 메모리 공간(stack영역)에 직접 데이터를 담는것인지 아니면 해당 객체에 대한 참조값을 가지는것에 차이를 두고 있다.

     

    (+)그렇다면 얕은복사와 깊은복사의 개념도 살짝 보자

    레퍼런스 타입은 해당 객체의 메모리 주소를 참조 한다. 따라서 두 개의 레퍼런스 변수가 같은 객체를 가르키는 것(얕은 복사)의 경우 어느 한 곳에서 해당 객체의 내용을 변경하면 객체를 참조하는 다른 한곳에서도 영향을 끼친다 소스코드로 예를 보자.

    package Thirdweek;
    
    public class copy {
        int test_value = 1;
    }
    
    package Thirdweek;
    
    public class Btest {
        public static void main(String[] args) {
            copy a = new copy();
            copy b = a;
            b.test_value = 3;
            System.out.println("a="+a.test_value+" b="+b.test_value);
        }
    }
    

    결과는 예상한대로 a=3 b=3이란 결과값이 나온다. 다음은 깊은 복사를 시도해보자.

    package Thirdweek;
    
    public class deep_copy implements Cloneable {
    
        int test_value ;
    
        public int getTest_value() {
            return test_value;
        }
    
        public void setTest_value(int test_value) {
            this.test_value = test_value;
        }
    
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    

    깊은 복사를 위해 Cloneable 인터페이스를 상속받아 clone메서드를 구현해주었다.

    package Thirdweek;
    
    import javax.swing.text.Position;
    
    public class Btest {
        public static void main(String[] args) throws CloneNotSupportedException {
            deep_copy a = new deep_copy();
            a.setTest_value(1);
            deep_copy b= (deep_copy) a.clone();
            b.setTest_value(2);
            System.out.println(a.getTest_value()+" "+b.getTest_value());
        }
    }
    ​

    따라서 깊은 복사는 레퍼런스 타입인 a와 b가 같은 객체의 주소를 가르키는 것이 아닌, 서로 다른 객체를 참조해 서로 다른 값을 가진다.


    리터럴

    리터럴은 변하지 않는 데이터 그 자체를 뜻한다. 즉 변수에 넣는 변하지 않는 데이터들을 말한다.-> 상수는 변하지 않는 변수를 뜻한다.

    예를 들어 리터럴은 다음과 같다.

    int a=1;

    int 앞 a는 변수이고, 1은 리터럴이다. 즉 1과 같이 변하지 않는 데이터들을 리터럴(literal)이라 칭한다.

     

    리터럴의 종류는 다음과 같다 

    1.정수(integer)

    우리가 가장 일반적으로 사용하는 리터럴로. 정수형 리터럴은 10진수, 8진수(데이터 앞에 숫자0), 16진수(데이터 앞 0x) 등으로 활용이 가능하다.

    2.부동소수점(floating point)

    부동소수점 리터럴은 흔히 실수 리터럴이라 해서 소수점 형태나 지수 형태로 실수를 표현한 값이다.

    3.부울(boolean)

    논리 타입 리터럴로 true, false 두 개 밖에 없다.

    4.문자(character)

    단일 인용부호(' ')로 문자를 표현하거나 유니코드를 표현할 때 사용하는 리터럴이다.

    5.문자열(String)

    문자열 리터럴은 string 상수 풀에 존재하는(스트링 상수 풀은 Java 7부터 Heap영역에 존재하게 됨) 실제 문자열의 메모리 주소지를 참조하며 new String()으로 객체를 생성되어 Heap영역에 존재하는 문자열과 다르다.

    public class Btest {
        public static void main(String[] args) {
            String lit1 = "Hello";
            String lit2 = "Hello";
            String HString1= new String("Hello");
            String HString2= new String("Hello");
    
            System.out.println(lit1==lit2);
            System.out.println(lit1==HString1);
            System.out.println(HString1==HString2);
        }
    }
    

    lit1, lit2는 스트링 리터럴로 String constant pool에 존재하는 Hello스트링 리터럴 메모리를 공유한다.

    Hstring1과 Hstring2는 String 객체로 Heap영역에서 별도로 객체들이 생성된다.

    따라서 결과는 다음과 같다.


    변수 선언 및 초기화하는 방법

    Java에서 변수 선언 및 초기화는 다음과 같은 문법으로 선언해주어야 한다.

    접근 지정자(생략하면 default)_데이터 타입_변수명 = 초기화 값

    String lit2 = "Hello";

    단, 변수를 전역 변수로 선언하고 초기화를 안 하면 그 해당 변수에는 초기값이 0, 지역변수에선 쓰레기값(사용 할 수 없는 값)이 할당된다.


    변수의 스코프와 라이프타임

    변수의 종류는 전역변수(인스턴스 변수와 클래스 변수)와 지역변수가 있다.

    1. 인스턴스 변수 : 클래스에 내에 선언 되지만, 메소드와 블록 외부에서 선언 되는 변수를 인스턴스 변수라 칭함. (클래스 변수와 차이는 static키워드로 선언되지 않는 클래스 변수.)

    2. 클래스 변수 : 클래스 내부, 모든 블록 외부에 선언 그리고 static으로 선언되는 변수를 클래스 변수라고 한다.

    3. 지역 변수 : 인스턴스 그리고 클래스 변수가 아닌 것을 지역 변수라고 한다.

    public class scope_and_lifetime {
        int num1, num2;   //Instance Variables
        static int result;  //Class Variable
        int add(int a, int b){  //Local Variables
            num1 = a;
            num2 = b;
            return a+b;
        }
        public static void main(String args[]){
            scope_and_lifetime ob = new scope_and_lifetime();
            result = ob.add(10, 20);
            System.out.println("Sum = " + result);
        }
    }
    변수 종류 스코프 라이프 타임
    인스턴스 변수 스테틱 메서드를 제외한 모든 클래스에 범위를 가짐. 자바 객체가 메모리에서 이용할 때 까지 
    클래스 변수 클래스 전체에 범위를 가진다.  프로그램이 끝날 때 까지 라이프 타임을 가진다.
    지역 변수 지역변수가 선언된 블록 까지만 범위를 가진다. 선언된 블록 내부에서만 라이프 타임을 가진다.

    타입 변환, 캐스팅 그리고 타입 프로모션

    타입 변환이란 어떤 값의 타입이나 변수의 타입을 다른 타입으로 변경한다는 것을 말하며 묵시적 형변환, 명시적 형변환으로 나뉜다.

    묵시적 형변환(자동 형변환/타입 프로모션)이 일어나는 조건

    1. 변환할 두 개의 데이터 타입이 같을 경우,

    2. 작은 데이터 타입을 큰 데이터 타입으로 할당할 경우

    즉, 자신의 표현 범위를 모두 포함한 데이터 타입으로 변환할 수 있을 경우 -> 타입 프로모션이 일어난다.

    https://www.geeksforgeeks.org/type-conversion-java-examples/

    동작 원리

    1. java의 경우 byte, short, char 피연산자들은 int형으로 평가중에 자동으로 변경한다.

    2. 피연산자가 long, float, double 데이터 타입일 경우 각자 long, float, double 타입으로 자동으로 변경한다.

     

    만약 큰 데이터 타입을 작은 데이터 타입으로 자동 형변환을 시도할시 에러가 발생한다.

    -> 따라서 큰 데이터 타입을 작은 데이터 타입으로 할당할 시 필요한 명시적 형 변환(casting)이 필요.

    ex)예제 코드 

    class Test 
    { 
        public static void main(String[] args) 
        { 
            double d = 100.04;  
              
            //explicit type casting 
            long l = (long)d; 
              
            //explicit type casting  
            int i = (int)l;  
            System.out.println("Double value "+d); 
              
            //fractional part lost 
            System.out.println("Long value "+l);  
              
            //fractional part lost 
            System.out.println("Int value "+i);  
        }  
    } 
    

    단, 데이터가 유실된다는 걸 감안해야 한다.


    1차 및 2차 배열 선언하기

    배열은 인덱스와 인덱스에 대응하는 일련의 데이터들로 이루어진 연속적인 자료 구조이다.

    배열은 같은 종류의 데이터들이 순차적으로 저장된다.

    1차원 배열 선언하기

    int [] a = new int[10]

    다음은 a라는 레퍼런스 변수에 heap영역에 int type의 순차적인 데이터 공간 10개를 할당한다. 그림으로 설명하자면 다음과 같다.(그림을 잘 못 그리는 걸 감안해주세요.)

    1. 레퍼런스 변수 a는 배열의 첫 번째 요소에 해당하는 주소를 가르킨다.

    2. 초기값을 할당하지 않으면 다음과 같이 배열의 데이터들은 0으로 초기화된다.

    2차원 배열 선언하기

    2차원 배열은 다음과 같이 선언한다.

    int[][] a = new int[2][10];

    메모리 구조를 보면 다음과 같은 그림이다.

    2차원 배열로 선언된 배열의 1차원 배열은, 2차원 배열(a[0][0]..a[0][9])의 첫 번째 배열의 메모리 주소 즉(a[0][0]의 메모리 주소0x1000)값을 가지고 있고, 순차적으로 접근한다. 만약 a[0].legnth 필드 사용시 값은 5가 반환된다.

    타입 추론, var

    자바 버전 10부터는 컴파일러가 해당 변수의 타입을 추론할 수 있는 Local variable type -Inference(var)가 추가 되었다.

    기존 java에서는 변수를 선언할 때 primitive타입을 명시적으로 선언해 주어야 했다.

    String test = "Hello"

    하지만 자바 10이상 부터는 다음과 같이 개발자가 일일이 primitive타입을 선언을 피할 수 있다.

    var test = "hello";

    컴파일러는 변수에 초기화 되는 값(오른쪽 데이터를)을 기준으로 해당 데이터 타입을 결정한다. 또한 Generic을 이용한 타입 추론도 가능하다.

    var test = new HashMap<String,Object>();

    var 사용시 주의점은 다음과 같다.

    1. var를 이용해 변수를 선언시 초기값은 반드시 필요하다.

    2. var에 대입되는 데이터는 Null이 올 수 없다.

    3. 반드시 로컬 영역에서만 사용이 가능하다.

    '스터디 > [whiteship]JAVA' 카테고리의 다른 글

    [4주차]제어문  (0) 2021.01.26
    [3주차]자바가 제공하는 다양한 연산자  (0) 2021.01.23
    [8주차]인터페이스  (0) 2021.01.06
    [1주차]JVM  (0) 2021.01.02
    [7주차]whiteship 스터디 참여  (0) 2020.12.27
Designed by Tistory.