Java 정리 - 3 (객체, 힙 메모리, 배열, for each, 클래스, 참조형 타입, String 클래스, 필드 선언, 메소드)

Minseol·2022년 7월 18일
0

Java 공부

목록 보기
4/12

객체 (Object)

  • 의사나 행위가 미치는 대상 (사전적 의미)
  • 구체적, 추상적 데이터의 단위 (학생, 회원, 생산, 주문, 배송)

객체 지향 프로그램과 절차 지향 프로그래밍

아침에 일어나 학교를 가는 과정을 예를 들어 보자.

절차 지향 프로그래밍

  • 시간이나 사건의 흐름에 따른 프로그래밍
    일어난다 -> 씻는다 -> 밥을 먹는다 -> 버스를 탄다-> 요금을 지불한다 -> 학교에 도착

객체 지향 프로그래밍

객체 지향 프로그램은 어떻게 구현하는가?

  • 객체를 정의 하고

  • 각 객체마다 제공하는 기능들을 구현하고

  • 각 객체가 제공하는 기능들 간의 소통(메세지 전달)을 통하여 객체간의 협력을 구현

객체 지향 프로그래밍에서의 협력

객체 지향 프로그램에서 객체 간에는 협력이 이루어짐

협력을 위해서는 필요한 메세지를 전송하고 이를 처리하는 기능이 구현되어야 함

매개 변수로 객체가 전달되는 경우가 발생

힙 메모리

생성된 인스턴스는 동적 메모리(heap memory)에 할당됨

C나 C++ 언어에서는 사용한 동적 메모리를 프로그래머가 해제 시켜야 함 (free() 혹은 delete 이용)

자바에서는 Gabage Collector가 주기적으로 사용하지 않는 메모리를 수거

하나의 클래스로부터 여러 개의 인스턴스가 생성되고 각각 다른 메모리 주소를 가지게 됨

배열

배열은 같은 데이터 타입을 가진 연속된 메모리 공간으로 이루어진 자료구조이다.

같은 데이터 타입을 가진 여러개의 변수가 필요할 때 사용한다.

1차원 배열

  1. 배열 생성 방법
    • 정수를 4개 저장 할 수 있는 배열을 생성 하는 방법
int[] array1 = new int[4];
  1. 배열에 값을 저장하는 방법
    array1[0] = 1;
    array1[1] = 2;
    array1[2] = 3;
    array1[3] = 4;
  1. 배열에 저장된 값을 꺼내서 사용하는 방법
    int value = array1[2]; 
    //array1 이 참조하는 배열의 2번 인덱스에 해당하는 값 3을 꺼내서 int형 변수 value에 담는다. 
    System.out.println(array1[1]); 
    //array1 이 참조하는 배열의 1번 인덱스에 해당하는 값 2가 콘솔에 출력된다. 
  1. 선언과 동시에 초기화하는 방법
	int[] array2 = new int[]{1,2,3,4,5};
	//int 값을 5개저장 할 수 있는 배열이며, 해당 배열에는 1,2,3,4,5가 순서대로 저장되게 된다.

배열의 길이

array.length 

객체 배열 선언과 구현

기본 자료형 배열은 선언과 동시에 배열의 크기만큼의 메모리가 할당되지만,
객체 배열의 경우엔 요소가 되는 객체의 주소가 들어갈(4바이트, 8바이트) 메모리만 할당되고(null) 각 요소 객체는 생성하여 저장해야 함

객체 배열 복사하기

System.arrayCopy(src, srcPos, dest, destPos, length) // 자바에서 제공되는 배열 복사 메서드

arrayCopy는 얕은 복사

  • 객체 주소만 복사되어 한쪽 배열의 요소를 수정하면 같이 수정됨
  • 즉, 두 배열이 같은 객체를 가리킴

깊은 복사는 각각의 객체를 생성하여 그 객체의 값을 복사하여 배열이 서로 다른 객체를 가리키도록 함

2차원 배열

  • 2차원 배열 생성 방법
    - 정수를 4개씩 담을 수 있는 배열이 3개 생성된다.
	int[][] array4 = new int[3][4];
  • 2차원 배열에 값을 저장하는 방법
array4[0][0] = 10; 
  • 가변크기의 2차원 배열을 생성하는 방법
    int[][] array5 = new int[3][];
    //위와 같이 선언하면 array5는 3개짜리 배열을 참조한다. 3개짜리 배열은 아직 참조하는 배열이 없다는 것을 의미.

    array5[0] = new int[1]; //정수를 하나 담을 수 있는 배열을 생성해서 array5 의 0 번째 인덱스가 참조한다.  
    array5[1] = new int[2]; //정수를 두개 담을 수 있는 배열을 생성해서 array5 의 1 번째 인덱스가 참조한다.  
    array5[2] = new int[3]; //정수를 세개 담을 수 있는 배열을 생성해서 array5 의 2 번째 인덱스가 참조한다. 
  • 선언과 동시에 초기화하는 방법
    int[][] array6 = {{1}, {2,3}, {4,5,6}};
    //위와 같이 선언할 경우 array6[0][0] 는 1이다. array6[1][0]은 2이다. 

for each

    int[] iarr = {10,20,30,40,50};

    for(int value:iarr){
        System.out.println(value);
    }

클래스

자바는 객체를 만들기 위해 반드시 클래스를 먼저 만들어야 한다. 클래스는 객체를 만들기 위한 일종의 틀이다.

붕어빵이 객체라면, 붕어빵 틀은 클래스
class_declare

자동차 클래스 생성

    public class Car{

    }

Car.java란 파일을 만든다.

저장을 하면 이클립스는 컴파일하여 디스크에 Car라는 클래스를 생성한다.

자동차 클래스가 생성되었다고 해서 자동차가 만들어 진것은 아니다.

클래스 코딩하기

  • 클래스는 대문자로 시작하는것이 좋다

  • java 파일 하나에 클래스는 여러 개가 있을 수 있지만, public 클래스는 하나이고, public 클래스와 .java 파일의 이름은 동일하다

  • camel notation 방식으로 명명한다.

Car객체 생성하기 (자동차 만들기)

    public class CarExam{
        public static void main(String args[]){
            Car c1 = new Car();
            Car c2 = new Car();
        }
    }

new연산자는 new연산자 뒤에 나오는 생성자를 이용하여 메모리에 객체를 만들라는 명령이다.

메모리에 만들어진 객체를 인스턴스(instance)라고도 한다.

이렇게 만들어진 객체를 참조하는 변수가 c1 , c2 이다.

위의 코드가 실행되면 Car라는 객체가 2개가 만들어지고 각각의 객체를 참조하는 c1과 c2변수가 선언된다.

참조형 타입

reference_type

참조형 타입은 기본형 타입을 제외한 모든 타입이다.

String str = new String("hello");
  • str 변수 앞에 기본형 타입이 아닌 String클래스가 적혀있다.
  • 이퀄(=) 뒤에는 new 다음에 생성자라는 것이 있다.
  • new 라는 키워드는 객체를 메모리에 올려준다. 이렇게 메모리에 올라간 객체를 인스턴스라고 말한다.
  • 메모리에 올라간 인스턴스를 가리키는 변수, 참조하는 변수, 레퍼런스 하는 변수가 str 이다.
    참조한다, 레퍼런스한다라는 것은 변수가 인스턴스를 가지고 있는게 아니라 말그대로 가리킨다는 의미이다.
  • str이라는 변수에는 메모리의 위치 값이 저장되는 것이다. 메모리의 위치값이 저장된다고 하더라도, 어떤 메모리에 저장되는지 그 정보를 알 수 있는 방법은 없다. 그렇기 때문에 str변수는 String 인스턴스를 참조한다라고만 알면 된다.
  • 앞으로 배울 클래스들은 모두 참조형이다

String 클래스

자바에서 가장 많이 사용하는 클래스이며 문자열을 표현한다.

  1. new연산자를 이용하지 않고 인스턴스를 만드는 경우
    String str1 = "hello";
    String str2 = "hello";
  • "hello"라는 문자열이 메모리 중에서 상수가 저장되는 영역에 저장된다. 상수는 변하지 않는 값을 의미.
  • String str2 = "hello"; 이 문장이 실행될 때에 hello 라는 문자열 상수는 이미 만들어져 있으므로 str1이 참조하는 인스턴스를 str2도 참조한다.
  1. new연산자를 이용해서 인스턴스를 만드는 경우
    String str3 = new String("hello");
    String str4 = new String("hello");
  • new연산자를 이용하여 인스턴스를 만들면 인스턴스는 무조건 새롭게 만들어진다.
  • String str4 = new String("hello"); 이 문장이 실행될때도 새롭게 만들게 되므로, str3 과 str4는 서로 다른 인스턴스를 참조한다.
    if(str1 == str2){  // 같은 인스턴스를 참조하므로 결과는 true 
        System.out.println("str1과 str2는 같은 레퍼런스입니다.");
    }

    if(str1 == str3){  // str1과 str3은 서로 다른 인스턴스를 참조하므로 결과는 false 
        System.out.println("str1과 str3는 같은 레퍼런스입니다.");
    }

    if(str3 == str4){  // str3과 str4는 서로 다른 인스턴스를 참조하므로 결과는 false 
        System.out.println("str3과 str4는 같은 레퍼런스입니다.");
    }
  • 참조변수끼리 == 로 비교하면 서로 같은 것을 참조하는지 비교한다.
  • String은 다른 클래스와 다르게 new를 사용하지 않고 사용할 수 있다. 메모리를 아끼려면 String은 new를 사용하지 않고 사용하는 것이 좋다.
  • String은 불변 클래스이다. 불변이란 String이 인스턴스가 될때 가지고 있던 값을 나중에 수정할 수 없다.
  • String은 문자열과 관련된 다양한 메소드를 가지고 있다. 메소드를 호출한다 하더라도 String은 내부의 값이 변하지 않는다.
  • String이 가지고 있는 메소드중 String을 반환하는 메소드는 모두 새로운 String을 생성해서 반환한다.
    String str5 = "hello world";
    String str6 = str5.substring(3);
  • substring은 문자열을 자른 결과를 반환하는 메소드이다. 해당 코드가 실행되어도 str5는 변하지 않는다.
  • str6은 str5가 가지고 있는 문자열 중 3번째 위치부터 자른 결과 즉 새로운 String을 참조하게 된다.

TODO: 밑의 코드를 스스로 실행해보자.

String testString = "Like a flower in the dawn";

// 밑의 새로운 substring 두 개는 서로 같은 인스턴스를 참조하는가?
String testSubstring1 = testString.substring(3);
String testSubstring2 = testString.substring(3);

결과: testSubstring1과 testSubstring2는 서로 다른 인스턴스를 참조한다.

가지고 있는 값이 같은지 확인할 때는 string 클래스의 equals() 메소드를 사용한다.

public class StringExam {
    public static void main(String[] args) {
        String str1 = new String("Hello world");
        String str2 = new String("Hello world");
        
        if( str1.equals(str2) ){
            System.out.println("str1과 str2는 같은 값을 가지고 있습니다.");
        }
        else{
            System.out.println("str1과 str2는 다른 값을 가지고 있습니다.");
        }
    }
}

필드(field)선언

자동차는 자동차 이름, 자동차 번호를 가지고 있고, 자동차는 달리고 멈추는 기능이 있다. 여기에서 가지고 있는 것을 속성이라고 한다. 자바에서는 이러한 속성을 필드(Field)라는 용어로 사용한다. 그리고 객체의 속성은 클래스의 멤버 변수(member variable)로 선언한다.

  • 이름과 번호를 필드로 가지고 있는 Car클래스 선언
    public class Car{
        String name;    
        int number;
    }
  • Car 클래스를 인스턴스화 하기
    Car c1 = new Car();
    Car c2 = new Car();
    //Car라는 인스턴스가 메모리에 2개 만들어 진다. 객체별로 name과 number라는 속성을 가진다.
  • 속성 이용하기 (참조 변수 다음에 나오는 점(dot)은 참조변수가 참조하는 객체가 가지고 있는 것을 사용할 때 사용)
    //c1.name은  c1이 참조하는 객체의 name 을 의미.

    c1.name = "소방차";  //c1이 참조하는 객체의 name을 소방차로 설정 
    c1.number = 1234;   // c1.number = 1234란 c1이 참조하는 객체의 number를 1234 로 설정 

    c2.name = "구급차"  //c2가 가리키는 객체의name을 구급차로 설정
    c2.number = 1004;  //c2가 가리키는 객체의 number를 1004로 설정


    System.out.println(c1.name);  //콘솔에 c1이 참조하는 객체의 name 을 출력합니다. 
    System.out.println(c1.number); //콘솔에 c1이 참조하는 객체의 number 를 출력합니다. 

    String name = c2.name;   //c2가 참조하는 객체의 name 을 String 타입 변수 name 도 참조하게 합니다.

메소드

필드가 물체의 상태라면, 물체의 행동에 해당하는게 메소드다. car에 이름과 번호가 있기도 하지만, car는 앞으로 전진할수도 있고 후진하는 행동도 할 수 있다.

  • 메소드는 입력값이 있고, 그 입력값을 받아서 무언가 한 다음 결과를 도출해 내는 수학의 함수와 비슷한 개념이다.
  • 멤버 함수(member function)라고도 한다.
  • 구현된(정의된) 메소드는 호출하여 사용하고 호출된 메소드는 기능이 끝나면 제어가 반환(리턴)된다.
  • 이때 입력값을 매개변수라고 하고, 결과값을 리턴값이라고 한다.
  • 인자( Argument ) 는 어떤 함수를 호출시에 전달되는 값을 의미한다.
  • 매개 변수( Parameter )는 그 전달된 인자를 받아들이는 변수를 의미한다.
  • 메소드란 클래스가 가지고 있는 기능이다. 클래스 안에 선언된다.

다양한 형태의 메소드 및 사용

public class MyClass{
    public void method(){
        System.out.println("method1이 실행됩니다.");
    }

    public void method2(int x){
        System.out.println(x + " 를 이용하는 method2입니다.");
    }

    public int method3(){
        System.out.println("method3이 실행됩니다.");

        return 10;
    }

    public void method4(int x, int y){
        System.out.println(x + "," + y + " 를 이용하는 method4입니다.");
    }

    public int method5(int y){
        System.out.println(y + " 를 이용하는 method5입니다.");
        return 5;
    }
}
  • 메소드를 사용하기 위해서는 메소드가 정의된 클래스인 MyClass 가 생성되어야 한다.

  • 객체를 생성할 때는 new 연산자를 이용한다.

  • 메소드를 사용할때는 생성된 클래스를 참조하는 레퍼런스변수.메소드명() 으로 사용할 수 있다.

    public class MyClassExam{
        public static void main(String args[]){
            MyClass my1 = new MyClass(); //메소드가 정의된 클래스 생성 

            my1.method1();   //MyClass에서 정의해 놓은 메소드 method1() 를 호출한다.       

            my1.method2(10);

            int x = my1.method3();

            System.out.println("method3 이 리턴한 " + x + " 입니다.");

            my1.method4(10,100);

            int x2 = my1.method5(50);

            System.out.println("method5 가 리턴한 " + x2 + " 입니다.");

        }
    }

기본형 변수와 참조형 변수를 메소드에?

class ReferenceTypeExam {
    public static void main(String []args) {
        ReferenceTypeExam exam = new ReferenceTypeExam();
        
        //기본형 변수value1을 addOne에 전달합니다.
        int value = 10;
        exam.addOne(value);
        System.out.println("기본형 변수의 값을 다른 메소드에서 변경한 결과: " + value);
        
        //참조형 변수arr을 addOne에 전달합니다.
        int []arr = {10};
        exam.addOne(arr);
        System.out.println("참조형 변수의 값을 다른 메소드에서 변경한 결과: " + arr[0]);
    }
    
    
    public void addOne(int value) {
        value++;
    }
    
    public void addOne(int[] arr) {
        for(int i = 0; i < arr.length; i++){
            arr[i] ++;
        }
    }
}
  • 실행 결과
    기본형 변수의 값을 다른 메소드에서 변경한 결과: 10
    참조형 변수의 값을 다른 메소드에서 변경한 결과: 11

기본형 타입은 다른 메소드에 매개변수로 전달될 때, 10이라는 값이 그대로 전달된다. 따라서 addOne에서 1을 더하더라도 value라는 변수에는 아무 영향이 없다.

하지만 참조형 타입은 다른 메소드에 매개변수로 전달될 때, 변수의 주소가 전달된다. 예를들어 '몇번째 박스에 값이 있다'는 식으로 값이 들어있는 주소가 전달된다. 그럼 그걸 전달받은 메소드 addOne에서는 그 박스에 가서 들어있는 값에 1 더한다. addOne을 실행하고 나서 arr[0]을 확인해 볼 때도 같은 박스에 가서 값을 확인하기 때문에 값이 11로 변해 있다.

함수 호출과 스택 메모리

객체 용어 정리

    객체 : 객체 지향 프로그램의 대상, 생성된 인스턴스

    클래스 : 객체를 프로그래밍 하기위해 코드로 정의해 놓은 상태

    인스턴스 : new 키워드를 사용하여 클래스를 메모리에 생성한 상태

    멤버 변수 : 클래스의 속성, 특성

    메서드 : 멤버 변수를 이용하여 클래스의 기능을 구현한 함수

    참조 변수 : 메모리에 생성된 인스턴스를 가리키는 변수

    참조 값 : 생성된 인스턴스의 메모리 주소 값
profile
귀여운 설이에양

0개의 댓글