나를 기록하다
article thumbnail
반응형

1. 객체지향언어


1.1 객체지향언어의 역사

1.1.1 객체지향언어의 기본 개념

  • 실제 세계는 사물(객체)로 이루어져 있으며, 발생하는 모든 사건들은 사물간의 상호작용 a) 실제 사물의 속성과 기능을 분석b) 데이터(변수)와 함수로 정의 → 실제 세계를 컴퓨터 속에 옮겨 놓은 것과 같은 가상 세계를 구현d)
    상속, 캡슐화, 추상화 개념을 중심
    으로 점차 구체적으로 발전

    → 그 당시에는 FORTRAN이나 COBOL과 같은 절차적 언어들이 주류였음

    f) 1980년대 중반에 C++을 비롯하여 여러 객체지향언어가 발표g)
    프로그램의 규모가 점점 커지고 사용자들의 요구가 빠르게 변화
    해가는 상황h)
    1995년에 자바
    발표, 1990년대 말에 인터넷의 발전과 함께 크게 유행
  • → 객체지향언어는 프로그래밍언어의 주류로 자리 잡음
  • → 절차적 언어로는 극복하기 어렵다는 한계
  • → 객체지향언어를 이용한 개발방법론이 대안으로 떠오름
  • → 객체지향언어가 본격적으로 관심을 끌기 시작 but 여전히 사용자층 넓지 못함
  • e) 1960년대 중반에 객체지향이론을 프로그래밍언어에 적용한 시뮬라(Simula)라는 최초의 객체지향언어 탄생
  • c) 가상세계에서 모의실험을 함으로써 많은 시간과 비용을 절약 가능

 

 

1.2 객체지향언어

💡
주요 특징 1. 코드의 높은 재사용성 (재사용성) - 새로운 코드를 작성 시 기존의 코드를 이용하여 쉽게 작성 가능 2. 코드의 관리가 용이 (유지보수성) - 코드간의 관계를 이용 → 적은 노력으로 쉽게 코드를 변경 가능 3. 신뢰성이 높은 프로그래밍 가능 (중복 제거) - 제어자와 메서드를 이용 → 데이터를 보호하고 올바른 값을 유지 - 코드의 중복을 제거 → 코드의 불일치로 인한 오작동을 방지

 

 

 

2. 클래스와 객체


2.1 클래스와 객체의 정의와 용도

💡
클래스 정의 : 객체를 정의해 놓은 것 용도 : 객체를 생성하는데 사용
💡
객체 정의 : 실제로 존재하는 것. 사물 또는 개념 용도 : 객체가 가지고 있는 기능과 속성에 따라 다름 유형의 객체 : 책상, 의자, 자동차, TV와 같은 사물 무형의 객체 : 수학공식, 프로그램 에러와 같은 논리나 개념

 

 

2.2 객체와 인스턴스

  • 인스턴스화(instantiate) : 클래스로부터 객체를 만드는 과정
  • 인스턴스(instance) : 어떤 클래스로부터 만들어진 객체 ex) Tv클래스로부터 만들어진 객체를 Tv클래스의 인스턴스라고 함.
💡
(인스턴스화) 클래스 → 인스턴스(객체)

 

 

2.3 객체의 구성요소 - 속성과 기능

  • 객체는 속성과 기능의 집합이라고 할 수 있다. 객체가 가지고 있는 속성과 기능을 그 객체의 멤버라 한다.
💡
속성(property) : 멤버 변수(member variable), 특성(attribute), 필드(field), 상태(state) 기능(function) : 메서드(method), 함수(function), 행위(behavior)

 

  • ex) TV의 속성과 기능
    • 속성 : 크기, 길이, 높이, 색상, 볼륨, 채널 등
    • 기능 : 켜기, 끄기, 볼륨 높이기, 볼륨 낮추기, 채널 변경하기 등

 

  • 객체지향 프로그래밍에서는 속성과 기능을 각각 변수와 메서드로 표현
    • 속성(property) → 멤버변수(variable)
    • 기능(function) → 메서드(method)

 

 

2.4 인스턴스의 생성과 사용

2.4.1 [예제]Tv 클래스 만들기

속성 크기, 길이, 높이, 색상, 볼륨, 채널 등
기능 켜기, 끄기, 볼륨 높이기&낮추기, 채널 변경하기 등
class Tv {     // Tv의 속성(멤버변수)     String color;       // 색상     boolean power;      // 전원상태(on/off)     int channel;        // 채널      // Tv의 기능(메서드)     void power() {         power = !power;     }   // TV를 켜거나 끄는 기능을 하는 메서드      void channelUp() {         ++channel;     }   // TV의 채널을 높이는 기능을 하는 메서드      void channelDown() {         --channel;     }   // TV의 채널을 낮추는 기능을 하는 메서드 }  public class _01_TvTest {     public static void main(String[] args) {         Tv t;                // Tv인스턴스를 참조하기 위한 변수 t 선언         t = new Tv();        // Tv인스턴스를 생성         t.channel = 7;       // Tv인스턴스의 멤버변수 channel의 값을 7로 설정         t.channelDown();     // Tv인스턴스의 메서드 channelDown()을 호출         System.out.println( 				"현재 채널은 " + t.channel + " 입니다." 				);     } }
// 결과 현재 채널은 6 입니다.
💡
인스턴스는 참조변수를 통해서만 다룰 수 있으며, 참조변수의 타입은 인스턴스의 타입과 일치해야한다.

 

2.4.2 Tv 클래스 만들기 2

public class _02_TvTest2 {     public static void main(String[] args) {         Tv t1 = new Tv();         Tv t2 = new Tv();         System.out.println("t1의 channel 값은 " + t1.channel + "입니다.");         System.out.println("t2의 channel 값은 " + t2.channel + "입니다.");          t1.channel = 7; // t1이 가리키고 있는 인스턴스의 멤버변수 channel의 값읕 7로 변경         System.out.println("t1의 channel값을 7로 변경하였습니다.");          System.out.println("t1의 channel값은 " + t1.channel + "입니다.");         System.out.println("t2의 channel값은 " + t2.channel + "입니다.");     } }
// 결과 t1의 channel 값은 0입니다. t2의 channel 값은 0입니다. t1의 channel값을 7로 변경하였습니다. t1의 channel값은 7입니다. t2의 channel값은 0입니다.

 

2.4.3 Tv 클래스 만들기 3

public class _03_TvTest3 {     public static void main(String[] args) {         Tv t1 = new Tv();         Tv t2 = new Tv();         System.out.println("t1의 channel값은 " + t1.channel + "입니다.");         System.out.println("t2의 channel값은 " + t2.channel + "입니다.");          t2 = t1;         t1.channel = 7;         System.out.println("t1의 channel값을 7로 변경하였습니다.");          System.out.println("t1의 channel값은 " + t1.channel + "입니다.");         System.out.println("t2의 channel값은 " + t2.channel + "입니다.");     } }
// 결과 t1의 channel값은 0입니다. t2의 channel값은 0입니다. t1의 channel값을 7로 변경하였습니다. t1의 channel값은 7입니다. t2의 channel값은 7입니다.

→ 참조변수에는 하나의 값(주소)만이 저장될 수 있으므로 둘 이상의 참조변수가 하나의 인스턴스를 가리키는(참조하는) 것은 가능

→ 하나의 참조변수로 여러 개의 인스턴스를 가리키는 것은 불가능

 

 

2.5 객체 배열

💡
객체를 배열로 다루는 것을 ‘객체 배열’이라 한다. 객체 배열 안에 객체가 저장 X → 객체의 주소가 저장 → 객체 배열은 참조변수들을 하나로 묶은 참조변수 배열인 것
Tv tv1, tv2, tv3;  ->  Tv[] tvArr = new Tv[3];

 

  • (주의!) 객체 배열을 생성하는 것은 그저 객체를 다루기 위한 참조변수들이 만들어진 것일 뿐, 아직 객체가 저장되지 않음.
Tv[] tvArr = new Tv[3]; // 참조변수 배열(객체 배열)을 생성  // 객체를 생성해서 배열의 각 요소에 저장 tvArr[0] = new Tv(); tvArr[1] = new Tv(); tvArr[2] = new Tv();

 

  • 배열의 초기화 블럭 사용 시 한 줄로 간단히 할 수 있음
Tv[] tvArr = {new Tv(), new Tv(), new Tv()};

 

  • 다뤄야할 객체의 수가 많을 때는 for문을 사용
Tv[] tvArr = new Tv[100];  for(int i = 0; i < tvArr.length; i++) { 		tvArr[i] = new Tv(); }

 

  • 모든 배열이 그렇듯이 객체 배열도 같은 타입의 객체만 저장할 수 있다.→ 여러 종류의 객체를 하나의 배열에 저장할 수 있는 방법은? → ‘다형성(polymorphism)’
  •  

 

2.6 클래스의 또 다른 정의

2.6.1 클래스 - 데이터와 함수의 결합

1. 변수 하나의 데이터를 저장할 수 있는 공간
2. 배열 같은 종류의 여러 데이터를 하나의 집합으로 저장할 수 있는 공간
3. 구조체 서로 관련된 여러 데이터를 종류에 관계없이 하나의 집합으로 저장할 수 있는 공간
4. 클래스 데이터와 함수의 결합(구조체 + 함수)

 

  • 비객체지향적 코드와 객체지향적 코드의 비교
비객체지향적 코드 객체지향적 코드
int hour1, hour2, hour3; int minute1, minute2, minute3; float second1, second2, second3; Time t1 = new Time(); Time t2 = new Time(); Time t3 = new Time();
int[] hour = new int[3]; int[] minute = new int[3]; float[] second = new float[3]; Time[] t = new Time[3]; t[0] = new Time(); t[1] = new Time(); t[2] = new Time();
  • 시간 데이터는 다음과 같은 추가 제약조건이 존재
💡
1. 시, 분, 초는 모두 0보다 크거나 같아야 한다. 2. 시의 범위는 0~23, 분과 초의 범위는 0~59이다.

 

  • [예제]Time 클래스 작성
public class _04_Time {     private int hour;     private int minute;     private float second;      public int getHour() {         return hour;     }      public void setHour(int h) {         if (h < 0 || h > 23) return;         hour = h;     }      public int getMinute() {         return minute;     }      public void setMinute(int m) {         if (m < 0 || m > 59) return;         minute = m;     }      public float getSecond() {         return second;     }      public void setSecond(float s) {         if (s < 0 || s > 59) return;         second = s;     } }

 

⭐︎void에서 return;의 의미 : 함수를 종료하겠다는 의미(if문 종료)

static void 메소드 안에서 return; 질문입니다
대한민국 모임의 시작, 네이버 카페
https://cafe.naver.com/javachobostudy/173957

 

 

3. 변수와 메서드


3.1 선언위치에 따른 변수의 종류

  • 변수의 종류를 결정짓는 중요한 요소는 ‘변수의 선언된 위치’→ 변수의 종류를 파악하기 위해서는 변수가 어느 영역에 선언되었는지를 확인하는 것이 중요!
  • → 멤버변수를 제외한 나머지 변수들 : 지역변수, 멤버변수 중 static이 붙은 것 : 클래스 변수, 붙지 않은 것 : 인스턴스 변수

 

  • [예제] 변수
class Variables { 		int iv;           // 인스턴스 변수                      --- 클래스 영역 		static int cv;    // 클래스 변수(static 변수, 공유 변수) 		 		void method() 		{ 				int lv = 0;   // 지역 변수      --- 메서드 영역  		} }

 

3.1.1 변수의 종류와 특징

변수의 종류 선언 위치 생성 시기
클래스 변수(class variable) 클래스 영역 클래스가 메모리에 올라갈 때
인스턴스 변수(instance variable) 클래스 영역 인스턴스가 생성되었을 때
지역 변수(local variable) 클래스 영역 이외의 영역(메서드, 생성자, 초기화 블럭 내부) 변수 선언문이 수행되었을 때

 

 

3.1.2 인스턴스 변수(instance variable)

  • 클래스 영역에 선언되며, 클래스의 인스턴스를 생성할 때 만들어짐.→ 인스턴스 변수의 값을 읽어 오거나 저장하기 위해서는 먼저 인스턴스를 생성해야함.
  • 인스턴스는 독립적인 저장공간을 가짐 → 서로 다른 값을 가질 수 있음.
  • 인스턴스마다 고유한 상태를 유지해야하는 속성의 경우, 인스턴스 변수로 선언

 

3.1.3 클래스 변수(class variable)

  • 인스턴스 변수 앞에 static을 붙이면 클래스 변수 선언
  • 클래스 변수는 모든 인스턴스가 공통된 저장공간(변수)을 공유
  • 한 클래스의 모든 인스턴스들이 공통적인 값을 유지해야하는 속성의 경우, 클래스 변수로 선언
  • 인스턴스를 생성하지 않고도 언제라도 바로 사용할 수 있음.
  • ‘클래스이름.클래스변수’와 같은 형식으로 사용 ex) Variable 의 클래스 변수 cv를 사용하려면 ‘Variable.cv’와 같이 사용
  • 클래스가 메모리에 ‘로딩(loading)’될 때 생성되어 프로그램이 종료될 때까지 유지
  • public을 앞에 붙이면 같은 프로그램 내에서 어디서나 접근할 수 있는 ‘전역 변수(global variable)’의 성격을 가짐

 

3.1.4 지역 변수(local variable)

  • 메서드 내에 선언되어 메서드 내에서만 사용 가능, 메서드 종료 시 소멸
  • for문 또는 while문의 블럭 내에 선언된 지역 변수는, 지역 변수가 선언된 블럭 {} 내에서만 사용 가능

 

3.2 클래스 변수와 인스턴스 변수

  • [예제] 카드 테스트
public class _05_CardTest {     public static void main(String[] args) {         System.out.println("Card.width = " + Card.width);         System.out.println("Card.height = " + Card.height);          Card c1 = new Card();         c1.kind = "Heart";         c1.number = 7;          Card c2 = new Card();         c2.kind = "Spade";         c2.number = 4;          System.out.println(                 "c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")"         );         System.out.println(                 "c2은 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")"         );          c1.width = 50;         c2.height = 80;          System.out.println(                 "c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")"         );         System.out.println(                 "c2은 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")"         );     } }  class Card {     static int width = 100;     // 클래스 변수     static int height = 250;    // 클래스 변수     String kind;                // 인스턴스 변수     int number;                 // 인스턴스 변수 }
// 결과 Card.width = 100 Card.height = 250 c1은 Heart, 7이며, 크기는 (100, 250) c2은 Spade, 4이며, 크기는 (100, 250) c1은 Heart, 7이며, 크기는 (50, 80) c2은 Spade, 4이며, 크기는 (50, 80)
💡
인스턴스변수는 인스턴스가 생성될 때마다 생성되므로 인스턴스마다 각기 다른 값을 유지할 수 있지만, 클래스 변수는 모든 인스턴스가 하나의 저장공간을 공유하므로, 항상 공통된 값을 갖는다.

 

 

3.3 메서드

3.3.1 메서드를 사용하는 이유

  1. 높은 재사용성(resuability)한번 만들어 놓은 메서드는 몇 번이고 호출할 수 있으며, 다른 프로그램에서도 사용이 가능
  1. 중복된 코드의 제거반복되는 문장들을 묶어서 하나의 메서드로 작성해 놓으면, 반복되는 문장들 대신 메서드를 호출하는 한 문장으로 대체할 수 있음
  1. 프로그램의 구조화1) main 메서드는 프로그램의 전체 흐름이 한눈에 들어올 정도로 단순하게 구조화하는 것이 좋음
  2. 2) 처음에 프로그램을 설계할 때 내용이 없는 메서드를 작업단위로 만들어 놓고, 하나씩 완성해가는 것도 프로그램을 구조화하는 좋은 방법

 

 

3.4 메서드의 선언과 구현

  • 메서드는 크게 두 부분, ‘선언부(header)’와 ‘구현부(body)’로 이루어져 있다.메서드를 정의한다는 것은 선언부와 구현부를 작성한다는 것을 의미하며 다음과 같은 형식으로 메서드를 정의한다.
// 반환타입 메서드이름 (타입 변수명, 타입 변수명, ...)     // 선언부 {                                               // 구현부 					// 메서드 호출시 수행될 코드                // 구현부 }                                               // 구현부
int add(int a, int b)                // 선언부 {                                    // 구현부 			int result = a + b;            // 구현부 			return result;                 // 구현부 }                                    // 구현부

 

3.4.1 메서드 선언부(method declaration, method header)

int add ( int x, int y)

반환타입(출력)메서드이름메개변수 선언(입력)

→ 메서드의 선언부는 후에 변경사항이 발생하지 않도록 신중히 작성해야 한다.

메서드의 선언부를 변경하게 되면, 그 메서드가 호출되는 모든 곳도 같이 변경해야 하기 때문이다.

 

3.4.2 매개변수 선언(parameter declaration)

  • 매개변수는 메서드가 작업을 수행하는데 필요한 값들(입력)을 제공받기 위한 것
  • 필요한 값의 개수만큼 변수를 선언하며 각 변수 간의 구분은 쉼표 ‘ , ‘를 사용
  • 한 가지 주의할 점은 일반적인 변수 선언과 달리 두 변수의 타입이 같아도 변수의 타입을 생략할 수 없다.

 

3.4.3 메서드의 이름(method name)

  • 변수의 명명규칙을 따름.
  • 메서드는 특정 작업을 수행하므로 메서드의 이름은 ‘add’처럼 동사인 경우가 많음.
  • 이름만으로도 메서드의 기능을 쉽게 알 수 있도록 함축적이면서도 의미있는 이름을 짓도록 노력

 

3.4.4 반환타입(return type)

  • 메서드의 작업수행 결과(출력)인 ‘반환값’을 적는다. 반환값이 없는 경우 void를 쓴다.

 

3.4.5 메서드의 구현부(method body)

  • { } 안의 부분, 메서드를 호출했을 때 수행될 문장들을 넣음.

 

3.4.6 return

  • 메서드의 반환타입이 ‘void’가 아닌 경우, 구현부 { } 안에 ‘return 반환값;’이 반드시 포함되어 있어야 한다.
  • 이 문장은 작업을 수행한 결과인 반환값을 호출한 메서드로 전달→
    이 값의 타입은 반드시 반환타입과 일치하거나 적어도 자동 형변환
    이 가능한 것이어야 한다.

[예제]

int add(int x, int y) { 		int result = x + y; 		return result; }

→ 위의 코드에서 return result ; 는 변수 result에 저장된 값을 호출한 메서드로 반환한다.

변수 result의 타입이 int이므로 메서드 add의 반환타입이 일치하는 것을 알 수 있다.

 

3.4.7 지역변수(local variable)

  • 메서드 내에 선언된 변수들은 그 메서드 내에서만 사용할 수 있으므로 서로 다른 메서드라면 같은 이름의 변수를 선언해도 된다.
int add (int x, int y) { 		int result = x + y; 		return result; }  int multiply (int x, int y) { 		int result = x * y; 		return result; }

 

 

3.5 메서드의 호출

메서드이름(값1, 값2, ...) // 메서드를 호출하는 방법 print99danAll(); // void print99danAll()을 호출 int result = add(3, 5); // int add(int x, int y)를 호출하고, 결과를 result에 저장

 

3.5.1 인자(argument)와 매개변수(parameter)

  • 메서드를 호출할 때 괄호() 안에 지정해준 값들을 ‘인자(argument)’ 또는 ‘인수’라고 하는데, 인자의 개수와 순서는 호출된 메서드에 선언된 매개변수와 일치해야 한다.

 

3.5.2 [예제] 사칙연산을 위한 4개의 메서드, MyMath 클래스

public class _06_MyMathTest {     public static void main(String[] args) {         MyMath mm = new MyMath();         long result1 = mm.add(5L, 3L);         long result2 = mm.subtract(5L, 3L);         long result3 = mm.multiply(5L, 3L);         double result4 = mm.divide(5L, 3L);          System.out.println("add(5L, 3L) = " + result1);         System.out.println("subtract(5L, 3L) = " + result2);         System.out.println("multiply(5L, 3L) = " + result3);         System.out.println("divide(5L, 3L) = " + result4);     } }  class MyMath {     long add(long a, long b) {         long result = a + b;         return result; //        return a + b; // 위의 두 줄을 이와 같이 한 줄로 간단히 할 수 있다.     }      long subtract(long a, long b) {         return a - b;     }      long multiply(long a, long b) {         return a * b;     }      double divide(double a, double b) {         return a / b;     } }
// 결과 add(5L, 3L) = 8 subtract(5L, 3L) = 2 multiply(5L, 3L) = 15 divide(5L, 3L) = 1.6666666666666667

 

 

3.6 return

  • return문은 현재 실행중인 메서드를 종료하고 호출한 메서드로 되돌아간다.
  • 반환값의 유무에 관계없이 모든 메서드에는 적어도 하나의 return문이 있어야 한다.→ 반환타입이 void인 경우, 컴파일러가 메서드의 마지막에 ‘return;’을 자동으로 추가해주기 때문에 문제 없음.

 

3.6.1 매개변수의 유효성 검사

  1. 메서드의 구현부 { }를 작성할 때, 제일 먼저 해야 하는 일이 매개변수의 값이 적절한 것인지 확인하는 것
  1. 타입만 맞으면 어떤 값도 매개변수를 통해 넘어올 수 있기 때문에 가능한 모든 경우의 수에 대비한 코드를 작성해야함.
  1. 적절하지 않은 값이 매개변수를 통해 넘어온다면 매개변수의 값을 보정해야 함.보정하는 것이 불가능하다면 return문을 사용하여 작업을 중단하고 호출한 메서드로 되돌아가야한다.

 

 

3.7 JVM의 메모리 구조

 

  1. 메서드 영역(method area)프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스 파일(*.class)을 읽어서 분석하여 클래스에 대한 정보(클래스 데이터)를 이곳에 저장. 이 때, 그 클래스의 클래스변수(class variable)도 이 영역에 함께 생성됨.

 

  1. 힙(heap)인스턴스가 생성되는 공간.
  2. 프로그램 실행 중 생성되는 인스턴스는 모두 이 곳에 생성된다. 즉, 인스턴스변수(instance variable)들이 생성되는 공간이다.
  1. 호출스택(call stack 또는 execution stack)호출스택은 메서드의 작업에 필요한 메모리 공간을 제공.이 메모리는 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용된다. 그리고 메서드가 작업을 마치면 할당되었던 메모리 공간은 반환되어 비워진다.
  2. 메서드가 호출되면 호출스택에 호출된 메서드를 위한 메모리가 할당됨.
  • 호출스택의 특징
💡
- 메서드가 호출되면 수행에 필요한 만큼의 메모리를 스택에 할당받는다. - 메서드가 수행을 마치고나면 사용했던 메모리를 반환하고 스택에서 제거된다. - 호출스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드이다. - 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.

 

3.7.1 [예제] CallStackTest

public class _07_CallStackTest {     public static void main(String[] args) {         firstMethod();     }      static void firstMethod() {         secondMethod();     }      static void secondMethod() {         System.out.println("SecondMethod()");     } }
// 결과 SecondMethod()

 

  • CallStackTest 예제 실행 시 호출 스택의 변화

 

3.7.2 [예제] CallStackTest2

public class _08_CallStackTest2 {     public static void main(String[] args) {         System.out.println("main(String[] args)이 시작되었음.");         firstMethod();         System.out.println("main(String[] args)이 끝났음.");     }       static void firstMethod() {         System.out.println("firstMethod()이 시작되었음.");         secondMethod();         System.out.println("firstMethod()이 끝났음.");     }      static void secondMethod() {         System.out.println("secondMethod()이 시작되었음.");         System.out.println("secondMethod()이 끝났음.");     } }
// 결과 main(String[] args)이 시작되었음. firstMethod()이 시작되었음. secondMethod()이 시작되었음. secondMethod()이 끝났음. firstMethod()이 끝났음. main(String[] args)이 끝났음.

 

 

3.8 기본형 매개변수와 참조형 매개변수

  • 기본형 매개변수 : 변수의 값을 읽기만 할 수 있다. (read only)
  • 참조형 매개변수 : 변수의 값을 읽고 변경할 수 있다. (read & write)

 

3.8.1 [예제] PrimitiveParamEx

class Data {     int x; }  public class _09_PrimitiveParamEx {     public static void main(String[] args) {         Data d = new Data();         d.x = 10;         System.out.println("main() : x = " + d.x);          change(d.x);         System.out.println("After change(d.x)");         System.out.println("main() : x = " + d.x);     }      static void change(int x) {         x = 1000;         System.out.println("change() : x = " + x);     } }
// 결과 main() : x = 10 change() : x = 1000 After change(d.x) main() : x = 10

 

  • d.x 값의 변화 그림
  1. change 메서드가 호출되면서 ‘d.x’가 change 메서드의 매개변수 x에 복사됨
  1. change 메서드에서 x의 값을 1000으로 변경
  1. change 메서드가 종료되면서 매개변수 x는 스택에서 제거됨
💡
‘d.x’의 값이 변경된 것이 아니라, change 메서드의 매개변수 x의 값이 변경된 것이다. 즉, 원본이 아닌 복사본이 변경된 것이라 원본에는 아무런 영향을 미치지 못한다. 이처럼 기본형 매개변수는 변수에 저장된 값만 읽을 수만 있을 뿐 변경할 수는 없다.

 

3.8.2 [예제] ReferenceParamEx

public class _10_ReferenceParamEx {     public static void main(String[] args) {         Data d = new Data();         d.x = 10;         System.out.println("main() : x = " + d.x);          change(d);         System.out.println("After change(d)");         System.out.println("main() : x = " + d.x);     }      static void change(Data d) {    // 참조형 매개변수         d.x = 1000;         System.out.println("change() : x " + d.x);     } }
// 결과 main() : x = 10 change() : x 1000 After change(d) main() : x = 1000

 

  • d.x 값의 변화 그림
  1. change 메서드가 호출되면서 참조변수 d의 값(주소)이 매개변수 d에 복사됨이제 매개변수 d에 저장된 주소값으로 x에 접근 가능
  1. change 메서드에서 매개변수 d로 x의 값을 1000으로 변경
  1. change 메서드가 종료되면서 매개변수 d는 스택에서 제거됨

 

3.8.3 [예제] ReferenceParamEx2

public class _11_ReferenceParamEx2 {     public static void main(String[] args) {         int[] x = {10}; // 크기가 1인 배열, x[0] = 10;         System.out.println("main() : x = " + x[0]);          change(x);         System.out.println("After change(x)");         System.out.println("main() : x = " + x[0]);     }      static void change(int[] x) {   // 참조형 매개변수         x[0] = 1000;         System.out.println("change() : x = " + x[0]);     } }
// 결과 main() : x = 10 change() : x = 1000 After change(x) main() : x = 1000

 

3.8.4 [예제] ReferenceParamEx3

public class _12_ReferenceParamEx3 {     public static void main(String[] args) {         int[] arr = new int[]{3, 2, 1, 6, 5, 4};          printArr(arr);         sortArr(arr);         printArr(arr);         System.out.println("sum=" + sumArr(arr)); // 배열의 총합을 출력     }      static void printArr(int[] arr) {   // 배열의 모든 요소를 출력         System.out.print("[");          for (int i : arr)   // 향상된 for 문             System.out.print(i + ",");         System.out.println("]");     }      static int sumArr(int[] arr) {  // 배열의 모든 요소의 합을 반환         int sum = 0;          for (int i = 0; i < arr.length; i++)             sum += arr[i];         return sum;     }      static void sortArr(int[] arr) {    // 배열을 오름차순으로 정렬         for (int i = 0; i < arr.length - 1; i++)             for (int j = 0; j < arr.length - 1; j++)                 if (arr[j] > arr[j + 1]) {                     int tmp = arr[j];                     arr[j] = arr[j + 1];                     arr[j + 1] = tmp;                 }     }   // sortArr(int[] arr) }
// 결과 [3,2,1,6,5,4,] [1,2,3,4,5,6,] sum=21

 

3.8.5 [예제] ReturnTest

public class _13_ReturnTest {     public static void main(String[] args) {         _13_ReturnTest r = new _13_ReturnTest();          int result = r.add(3, 5);         System.out.println(result);          int[] result2 = {0};  // 배열을 생성하고 result2[0]의 값을 0으로 초기화         r.add(3, 5, result2); // 배열을 add 메서드의 매개변수로 전달         System.out.println(result2[0]);     }      int add(int a, int b) {         return a + b;     }      void add(int a, int b, int[] result) {         result[0] = a + b;  // 매개변수로 넘겨받은 배열에 연산결과를 저장     } }
// 결과 8 8

 

 

3.9 참조형 반환타입

  • 매개변수뿐만 아니라 반환타입도 참조형이 될 수 있다. 반환타입이 참조형이라는 것은 반환하는 값의 타입이 참조형이다.→ 모든 참조형 타입의 값은 ‘객체의 주소’이므로 그저 정수값이 반환되는 것

 

3.9.1 [예제] ReferenceReturnEx

public class _14_ReferenceReturnEx {     public static void main(String[] args) {         Data d = new Data();         d.x = 10;          Data d2 = copy(d);         System.out.println("d.x = " + d.x);         System.out.println("d2.x = " + d2.x);     }      static Data copy(Data d) {         Data tmp = new Data();         tmp.x = d.x;          return tmp;     } }
// 결과 d.x = 10 d2.x = 10
  1. copy 메서드를 호출하면서 참조변수 d의 값이 매개변수 d에 복사된다.
  1. 새로운 객체를 생성한 다음, d.x에 저장된 값을 tmp.x에 복사한다.
  1. copy 메서드가 종료되면서 반환한 tmp의 값은 참조변수 d2에 저장된다.
  1. copy 메서드가 종료되어 tmp가 사라졌지만, d2로 새로운 객체를 다룰 수 있다.
💡
“반환타입이 ‘참조형’인 것은 메서드가 ‘객체의 주소’를 반환한다는 것을 의미한다.”

 

 

3.10 재귀호출(recursive call)

  • 메서드 내부에서 메서드 자신을 다시 호출하는 것을 ‘재귀호출’, 재귀호출을 하는 메서드를 ‘재귀 메서드’라 한다.
void method() { 			 method();      // 재귀호출. 메서드 자신을 호출한다. }

 

  • 대부분의 재귀호출은 반복문으로 작성하는 것이 가능

→ 왜 굳이 반복문 대신 재귀호출을 사용할까? : 재귀호출이 주는 논리적 간결함 때문!

 

3.10.1 [예제] 팩토리얼(factorial)

  • 팩토리얼의 수학적 메서드 표현 방식
💡
f(n) = n * f(n-1), 단, f(1) = 1
public class _15_FactorialEx {     public static void main(String[] args) {         int result = factorial(4);          System.out.println(result);     }      static int factorial(int n) {         if (n == 1) return 1;         return n * factorial(n - 1);     } }
// 결과 24

 

3.10.2 [예제] 팩토리얼

public class _16_FactorialEx2 {     static long factorial(int n) {         if (n <= 0 || n > 20) return -1;         if (n <= 1) return 1;         return n * factorial(n - 1);     }      public static void main(String[] args) {         int n = 21;         long result = 0;          for (int i = 1; i <= n; i++) {             result = factorial(i);              if (result == -1) {                 System.out.printf("유효하지 않은 값입니다.(0<n<=20):%d%n", n);                 break;             }              System.out.printf("%2d!=%20d%n", i, result);         }     } }
// 결과  1!=                   1  2!=                   2  3!=                   6  4!=                  24  5!=                 120  6!=                 720  7!=                5040  8!=               40320  9!=              362880 10!=             3628800 11!=            39916800 12!=           479001600 13!=          6227020800 14!=         87178291200 15!=       1307674368000 16!=      20922789888000 17!=     355687428096000 18!=    6402373705728000 19!=  121645100408832000 20!= 2432902008176640000 유효하지 않은 값입니다.(0<n<=20):21

 

3.10.3 MainTest

public class _17_MainTest {     public static void main(String[] args) {         main(null);     } }
// 결과 Exception in thread "main" java.lang.StackOverflowError 	at chap_06._17_MainTest.main(_17_MainTest.java:5) 	at chap_06._17_MainTest.main(_17_MainTest.java:5) 	... 	at chap_06._17_MainTest.main(_17_MainTest.java:5) 	at chap_06._17_MainTest.main(_17_MainTest.java:5)
  • main 메서드도 자기 자신을 호출하는 것이 가능, 아무런 조건도 없이 계속해서 자기 자신을 다시 호출하기 때문에 무한호출에 빠지게 된다.
  • main 메서드가 종료되지 않고 호출스택에 계속해서 쌓이게 되므로 결국 호출스택의 메모리 한계를 넘게 되고 StackOverflowError가 발생하여 프로그램은 비정상적으로 종료된다.

 

3.10.4 [예제] PowerTest

public class _18_PowerTest {     public static void main(String[] args) {         int x = 2;         int n = 5;         long result = 0;          for (int i = 1; i <= n; i++) {             result += power(x, i);         }         System.out.println(result);     }      static long power(int x, int n) {         if (n == 1) return x;         return x * power(x, n - 1);     } }
// 결과 62

 

 

3.11 클래스 메서드(static 메서드)와 인스턴스 메서드

  • 클래스데이터(변수)와 데이터에 관련된 메서드의 집합
  • 같은 클래스 내에 있는 메서드와 멤버변수는 밀접한 관계
  • 인스턴스 메서드인스턴스 변수와 관련된 작업을 하는, 즉 메서드의 작업을 수행하는데 인스턴스 변수를 필요로 하는 메서드
  • 클래스 메서드(static 메서드)인스턴스와 관계없는(인스턴스 변수나 인스턴스 메서드를 사용하지 않는) 메서드
💡
1. 클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통으로 사용되는 것에 static을 붙인다. 2. 클래스 변수(static 변수)는 인스턴스를 생성하지 않아도 사용할 수 있다. 3. 클래스 메서드(static 메서드)는 인스턴스 변수를 사용할 수 없다. 4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.
💡
클래스의 멤버변수 중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있으면 static을 붙임 작성한 메서드 중 인스턴스 변수나 인스턴스 메서드를 사용하지 않는 메서드에 static 붙일 것을 고려

 

3.11.1 [예제] MyMathTest2

class MyMath2 {     long a, b;      // 인스턴스 변수 a, b만을 이용해서 작업하므로 매개변수가 필요없다.     long add() {return a + b;}      long subtract() {return a - b;}      long multiply() {return a * b;}      double divide() {return a / b;}      // 인스턴스변수와 관계없이 매개변수만으로 작업이 가능하다.     static long add(long a, long b) {return a + b;} // a, b는 지역변수      static long subtract(long a, long b)     {return a - b;}      static long multiply(long a, long b)     {return a * b;}      static double divide(double a, double b) {return a / b;} }  public class _19_MyMathTest2 {     public static void main(String[] args) {         // 클래스 메서드 호출. 인스턴스 생성없이 호출 가능         System.out.println(MyMath2.add(200L, 100L));         System.out.println(MyMath2.subtract(200L, 100L));         System.out.println(MyMath2.multiply(200L, 100L));         System.out.println(MyMath2.divide(200.0, 100.0));          MyMath2 mm = new MyMath2(); // 인스턴스 생성         mm.a = 200L;         mm.b = 100L;         //인스턴스 메서드는 객체 생성 후에만 호출이 가능         System.out.println(mm.add());         System.out.println(mm.subtract());         System.out.println(mm.multiply());         System.out.println(mm.divide());     } }
// 결과 300 100 20000 2.0 300 100 20000 2.0

 

 

3.12 클래스 멤버와 인스턴스 멤버간의 참조와 호출

  • 같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능
  • 단, 클래스 멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 한다.
  • 이유 ?인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재하지만, 클래스 멤버가 존재하는 시점에서 인스턴스 멤버가 존재하지 않을 수도 있기 때문

 

3.12.1 [예제] MemberCall

class MemberCall {     static int cv = 20;     //    static int cv2 = iv;      // 에러. 클래스 변수는 인스턴스 변수를 사용할 수 없음.     static int cv2 = new MemberCall().iv; // 이처럼 객체를 생성해야 사용 가능.     int iv = 10;     int iv2 = cv;      static void staticMethod1() {         System.out.println(cv); //        System.out.println(iv);   // 에러. 클래스 메서드에서 인스턴스 변수를 사용 불가.         MemberCall c = new MemberCall();         System.out.println(c.iv);   // 객체를 생성한 후에야 인스턴스 변수의 참조 가능.     }      static void staticMethod2() {         staticMethod1(); //        instanceMethod1();        // 에러. 클래스 메서드에서는 인스턴스 메서드를 호출할 수 없음.         MemberCall c = new MemberCall();         c.instanceMethod1();        // 인스턴스를 생성한 후에야 호출할 수 있음.     }      void instanceMethod1() {         System.out.println(cv);         System.out.println(iv);     // 인스턴스 메서드에서는 인스턴스 변수를 바로 사용 가능.     }      void instanceMethod2() {         // 인스턴스 메서드에서는 인스턴스메서드와 클래스 메서드         staticMethod1();            // 모두 인스턴스 생성없이 바로 호출 가능         instanceMethod1();     } }

 

 

 

4. 오버로딩(overloading)


4.1 오버로딩이란?

  • 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 ‘메서드 오버로딩’ 또는 ‘오버로딩’이라고 함.

 

 

4.2 오버로딩의 조건

💡
1. 메서드 이름이 같아야 한다. 2. 매개변수의 개수 또는 타입이 달라야 한다.
  • 반환 타입은 오버로딩을 구현하는데 아무런 영향을 주지 못함

 

 

4.3 오버로딩의 예

4.3.1 PrintStream 클래스

  • 어떤 종류의 매개변수를 지정해도 출력할 수 있도록 아래와 같이 10개의 오버로딩된 println 메서드를 정의
void println() void println(boolean x) void println(char x) void println(char[] x) void println(double x) void println(float x) void println(int x) void println(long x) void println(Object x) void println(String x)

 

 

4.4 오버로딩의 강점

  1. 오버로딩을 통해 여러 메서드들이 println 이라는 하나의 이름으로 정의될 수 있다면, println 만 기억하면 되므로 기억하기도 쉽고 이름도 짧게 할 수 있어서 오류의 가능성을 줄일 수 있음
  1. 이름만 보고도 ‘이 메서드들은 이름이 같으니, 같은 기능을 하겠구나.’라고 쉽게 예측 가능
  1. 메서드의 이름을 절약 가능

 

4.4.1 [예제] OverloadingTest

public class _21_OverloadingTest {     public static void main(String[] args) {         MyMath3 mm = new MyMath3();         System.out.println("mm.add(3, 3) 결과 : " + mm.add(3, 3));         System.out.println("mm.add(3L, 3) 결과 : " + mm.add(3L, 3));         System.out.println("mm.add(3, 3L) 결과 : " + mm.add(3, 3L));         System.out.println("mm.add(3L, 3L) 결과 : " + mm.add(3L, 3L));          int[] a = {100, 200, 300};         System.out.println("mm.add(a) 결과 : " + mm.add(a));     } }  class MyMath3 {     int add(int a, int b) {         System.out.print("int add(int a, int b) - ");         return a + b;     }      long add(int a, long b) {         System.out.print("long add(int a, long b) - ");         return a + b;     }      long add(long a, int b) {         System.out.print("long add(long a, int b) - ");         return a + b;     }      long add(long a, long b) {         System.out.print("long add(long a, long b) -");         return a + b;     }      int add(int[] a) {         System.out.print("int add(int[] a) - ");         int result = 0;         for (int i = 0; i < a.length; i++) {             result += a[i];         }         return result;     } }
// 결과 int add(int a, int b) - mm.add(3, 3) 결과 : 6 long add(long a, int b) - mm.add(3L, 3) 결과 : 6 long add(int a, long b) - mm.add(3, 3L) 결과 : 6 long add(long a, long b) -mm.add(3L, 3L) 결과 : 6 int add(int[] a) - mm.add(a) 결과 : 600
  • add 메서드가 println 메서드보다 먼저 출력될 수 있는 이유pinrtln 메서드가 결과를 출력하려면, add메서드의 결과가 먼저 계산되어야 하기 때문이다. 간단히 위의 문장이 아래의 두 문장을 하나로 합친 것이라고 생각하면 이해가 쉽다.
int result = mm.add(3,3); System.out.println("mm.add(3, 3) 결과:" + result);

 

 

4.5 가변인자(varargs)와 오버로딩

  • 메서드의 매개변수 개수를 동적으로 지정해줄 수 있는 기능을 가변인자(variable arguments)라고 함.
  • ‘타입… 변수명’ 과 같은 형식으로 선언, PrintStream 클래스의 printf()가 대표적인 예
public PrintStream printf(String format, Object... args) { . . . }

 

  • 위와 같이 가변인자 외에도 매개변수가 더 있다면, 가변인자를 매개변수 중에서 제일 마지막에 선언해야 한다. 그렇지 않으면 컴파일 에러가 발생한다. 가변인자인지 아닌지를 구별할 방법이 없기 때문에 허용하지 않는다.
// 컴파일 에러 발생 - 가변인자는 항상 마지막 매개변수이어야 한다. public PrintStream printf(Object... args, String format) { 			 ... }

 

  • 여러 문자열을 하나로 결합하여 반환하는 concatenate 메서드를 작성한다면, 아래와 같이 매개변수의 개수를 다르게 해서 여러 개의 메서드를 작성해야 한다.
String concatenate(String s1, String s2) { ... } String concatenate(String s1, String s2, String s3) { ... } String concatenate(String s1, String s2, String s3, String s4) { ... }

→ 이럴 때, 가변인자를 사용하면 메서드 하나로 간단히 대체 가능

String concatenate(String... str) { ... }

→ 이 메서드를 호출할 때는 아래와 같이 인자의 개수를 가변적으로 할 수 있음

System.out.println(concatenate());          // 인자가 없음 System.out.println(concatenate("a"));       // 인자가 하나  System.out.println(concatenate("a", "b"));  // 인자가 둘 System.out.println(concatenate(new String[]{"A", "B"));  // 배열도 가능

 

  • 가변인자와 매개변수의 타입의 배열로 하는 것과의 차이점
String concatenate(String[] str) { ... }  String result = concatenate(new String[0]);  // 인자로 배열을 지정 String result = concatenate(null);           // 인자로 null을 지정 String result = concatenate();               // 에러. 인자가 필요함.

 

4.5.1 [예제] VarArgsEx

public class _22_VarArgsEx {     public static void main(String[] args) {         String[] strArr = {"100", "200", "300"};          System.out.println(concatenate("", "100", "200", "300"));         System.out.println(concatenate("-", strArr));         System.out.println(concatenate(",", new String[]{"1", "2", "3"}));         System.out.println("[" + concatenate(",", new String[0]) + "]");         System.out.println("[" + concatenate(",") + "]");     }      static String concatenate(String delim, String... args) {         String result = "";          for (String str : args) {             result += str + delim;         }          return result;     }  //    static String concatenate(String... args) { //        return concatenate("", args); //    } }
// 결과 100200300 100-200-300- 1,2,3, [] []

 

 

 

5. 생성자(Constructor)


5.1 생성자란?

  • 인스턴스가 생성될 때 호출되는 ‘인스턴스 초기화 메서드’이다.
  • 생성자 역시 메서드처럼 클래스 내에 선언되며, 구조도 메서드와 유사하지만 리턴값이 없다는 점이 다름.

 

5.1.1 생성자의 조건

  1. 생성자의 이름은 클래스의 이름과 같아야 한다.
  1. 생성자는 리턴 값이 없다.
클래스이름(타입 변수명, 타입 변수명, ...) { 	// 인스턴스 생성 시 수행될 코드, 	// 주로 인스턴스 변수의 초기화 코드를 적는다. }  class Card { 			Card() {            // 매개변수가 없는 생성자.            ...       }       Card(String k, int num) {           // 매개변수가 있는 생성자.            ...       }       ... }
  • 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것이 아니다.
  • 생성자는 단순히 인스턴스 변수들의 초기화에 사용되는 조금 특별한 메서드일 뿐, 메서드와 크게 다르지 않음.

 

5.1.2 [예시] Card 클래스의 인스턴스를 생성하는 코드

Card c = new Card();
  1. 연산자 new에 의해서 메모리(heap)에 Card 클래스의 인스턴스가 생성된다.
  1. 생성자 Card()가 호출되어 수행된다.
  1. 연산자 new의 결과로, 생성된 Card 인스턴스의 주소가 반환되어 참조변수 c에 저장된다.→ 지금까지 인스턴스를 생성하기 위해 사용해왔던 ‘클래스이름()’이 바로 생성자였던 것!

 

 

5.2 기본 생성자(default constructor)

  • 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다.
  • 지금까지 클래스에 생성자를 정의하지 않고도 인스턴스를 생성할 수 있었던 이유→ 컴파일러가 제공하는 ‘기본 생성자(default constructor)’ 덕분
클래스이름() { } Card()    { }

 

5.2.1 [예제] ConstructorTest

class Data1 {     int value; }  class Data2 {     int value;      Data2(int x) {         value = x;  // 매개변수가 있는 생성자     } }  public class _23_ConstructorTest {     public static void main(String[] args) {         Data1 d1 = new Data1(); //        Data2 d2 = new Data2();   // 컴파일 에러 발생     } }
// 결과 ConstructorTest.java:18:20 java: constructor Data2 in class chap_06.Data2 cannot be applied to given types;   required: int   found: no arguments   reason: actual and formal argument lists differ in length
  • 에러메시지: Data2에서 Data2()라는 생성자를 찾을 수 없다는 내용Data2Data2()가 정의되어 있지 않기 때문에 에러가 발생
  • Data1의 인스턴스를 생성하는 코드에는 에러가 없는데, Data2의 인스턴스를 생성하는 코드에서 에러가 발생하는 이유는?Data1에는 정의되어 있는 생성자가 하나도 없으므로 컴파일러가 기본 생성자를 추가해주었지만, Data2에는 이미 생성자 Data2(int x)가 정의되어 있으므로 기본 생성자가 추가되지 않았기 때문
  • 컴파일러가 자동적으로 기본 생성자를 추가해주는 경우는 ‘클래스 내에 생성자가 하나도 없을 때’ 뿐이다!

 

Data1 d1 = new Data1(); //     -->  Data2 d2 = new Data2(); // 에러 -->
Data1 d1 = new Data1(); Data2 d2 = new Data2(10); // OK

 

 

5.3 매개변수가 있는 생성자

  • 인스턴스를 생성한 다음에 인스턴스 변수의 값을 변경하는 것보다 매개변수를 갖는 생성자를 사용하는 것이 코드를 보다 간결하고 직관적으로 만든다.

 

5.3.1 CarTest

class Car {     String color;       // 색상     String gearType;    // 변속기 종류 - auto(자동), manual(수동)     int    door;        // 문의 개수      Car() {}            // 생성자      Car(String c, String g, int d) {  // 생성자         color    = c;         gearType = g;         door     = d;     } }  public class _24_CarTest {     public static void main(String[] args) {         Car c1 = new Car();         c1.color    = "white";         c1.gearType = "auto";         c1.door     = 4;          Car c2 = new Car("white", "auto", 4);          System.out.println("c1의 color = " + c1.color + ", gearType = "  														+ c1.gearType + ", door = " + c1.door);         System.out.println("c2의 color = " + c2.color + ", gearType = "  														+ c2.gearType + ", door = " + c2.door);     } }
// 결과 c1의 color = white, gearType = auto, door = 4 c2의 color = white, gearType = auto, door = 4

 

 

5.4 생성자에서 다른 생성자 호출하기 - this(), this

  • 생성자 간에도 서로 호출이 가능할 조건
    1. 생성자의 이름으로 클래스이름 대신 this를 사용
    1. 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능
Car(String color) { 			door = 5;                     // 첫 번째 줄 			Car(color, "auto", 4);        // 에러1. 생성자의 두 번째 줄에서 다른 생성자 호출 }                                   // 에러2. this(color, "auto", 4);로 해야함

 

5.4.1 [예제] CarTest2

class Car2 {     String color;     String gearType;     int    door;      Car2() {         this("white", "auto", 4);     }      Car2(String color) {         this(color, "auto", 4);     }      Car2(String color, String gearType, int door) {         this.color    = color;         this.gearType = gearType;         this.door     = door;     } }  public class _25_CarTest2 {     public static void main(String[] args) {         Car2 c1 = new Car2();         Car2 c2 = new Car2("blue");          System.out.println("c1의 color = " + c1.color + ", gearType = "  														+ c1.gearType + ", door = " + c1.door);         System.out.println("c2의 color = " + c2.color + ", gearType = "  														+ c2.gearType + ", door = " + c2.door);     } }
// 결과 c1의 color = white, gearType = auto, door = 4 c2의 color = blue, gearType = auto, door = 4
  • this 인스턴스 자신을 가리키는 참조변수, 인스턴스의 주소가 저장되어 있다.모든 인스턴스 메서드에 지역변수로 숨겨진 채로 존재한다.
  • this(), this(매개변수) 생성자. 같은 클래스의 다른 생성자를 호출할 때 사용한다.

 

5.5 생성자를 이용한 인스턴스의 복사

  • 현재 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용 가능
  • 하나의 클래스로부터 생성된 모든 인스턴스의 메서드와 클래스 변수는 서로 동일하기 때문에 인스턴스간의 차이는, 인스턴스마다 각기 다른 값을 가질 수 있는 인스턴스 변수 뿐이다.
Car(Car c) { 			color       = c.color; 			gearType    = c.gearType; 			door        = c.door;
  • 위의 코드는 Car 클래스의 참조변수를 매개변수로 선언한 생성자이다. 매개변수로 넘겨진 참조변수가 가리키는 Car 인스턴스의 인스턴스 변수인 color, gearType, door의 값을 인스턴스 자신으로 복사한 것이다.

 

5.5.1 [예제] CarTest3

class Car3 {     String color;       // 색상     String gearType;    // 변속기 종류 - auto(자동), manual(수동)     int    door;        // 문의 개수      Car3() {         this("white", "auto", 4);     }      Car3(Car3 c) {      // 인스턴스의 복사를 위한 생성자         color    = c.color;         gearType = c.gearType;         door     = c.door;     }      Car3(String color, String gearType, int door) {         this.color    = color;         this.gearType = gearType;         this.door     = door;     } }  public class _26_CarTest3 {     public static void main(String[] args) {         Car3 c1 = new Car3();         Car3 c2 = new Car3(c1);  // c1의 복사본 c2를 생성         System.out.println("c1의 color = " + c1.color + ", gearType = " + c1.gearType + ", door = " + c1.door);         System.out.println("c2의 color = " + c2.color + ", gearType = " + c2.gearType + ", door = " + c2.door);          c1.door = 100;  // c1의 인스턴스 변수 door의 값을 변경한다.         System.out.println("c1.door=100; 수행 후");         System.out.println("c1의 color = " + c1.color + ", gearType = " + c1.gearType + ", door = " + c1.door);         System.out.println("c2의 color = " + c2.color + ", gearType = " + c2.gearType + ", door = " + c2.door);     } }
  • 인스턴스를 생성할 때는 다음의 2가지 사항을 결정해야한다.
    1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?
    1. 생성자 - 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?

 

 

 

6. 변수의 초기화


6.1 변수의 초기화

  • 멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어짐 → 초기화하지 않고 사용해도 됨.
  • 지역변수는 사용하기 전에 반드시 초기화해야함.
class InitTest { 			int x;     // 인스턴스 변수 			int y = x; // 인스턴스 변수  void method() { 			int i;     // 지역변수 			int j = i; // 에러. 지역변수를 초기화하지 않고 사용
  • 멤버변수(클래스 변수와 인스턴스 변수)와 배열의 초기화는 선택적이지만, 지역변수의 초기화는 필수적이다.

 

  • 각 타입의 기본값(default value)
자료형 기본값
boolean false
char ‘\u0000’
byte, short, int 0
long 0L
float 0.0f
double 0.0d 또는 0.0
참조형 변수 null

 

멤버 변수의 초기화 방법

  1. 명시적 초기화(explicit initialization)
  1. 생성자(constructor)
  1. 초기화 블럭(initialization block)
    1. 인스턴스 초기화 블럭 : 인스턴스 변수를 초기화 하는데 사용
    1. 클래스 초기화 블럭 : 클래스 변수를 초기화 하는데 사용

 

 

6.2 명시적 초기화(explicit initialization)

  • 변수를 선언과 동시에 초기화 하는 것을 의미, 가장 기본적이면서 간단하기에 가장 우선적으로 고려되어야 함
class Car { 		int door = 4;            // 기본형(primitive type) 변수의 초기화 		Engine e = new Engine(); // 참조형(reference type) 변수의 초기화  		// ... }

 

 

 

6.3 초기화 블럭(initialization block)

  1. 클래스 초기화 블럭 : 클래스 변수의 복잡한 초기화에 사용
  1. 인스턴스 초기화 블럭 : 인스턴스 변수의 복잡한 초기화에 사용
class InitBlock { 		static { /* 클래스 초기화 블럭 */ }  		{ /* 인스턴스 초기화 블럭 */ }  		// ... }
  • 클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때 한번만 수행 인스턴스 초기화 블럭은 생성자와 같이 인스턴스를 생성할 때마다 수행 생성자보다 인스턴스 초기화 블럭이 먼저 수행된다
  • 예시
Car() {     count++;     serialNo = count;     color = "white";     gearType = "Auto"; } Car(String color,  String egearType) {     count++;     serialNo = count;     this.color = color;     this.gearType = gearType; } 
{ 		count++;     serialNo = count; } Car() {     color = "white";     gearType = "Auto"; } Car(String color,  String egearType) {      this.color = color;     this.gearType = gearType; }

→ 인스턴스 초기화 블럭을 사용하여 중복된 코드 제거

→ 코드의 신뢰성 향상, 오류의 발생가능성 줄어듬 → 재사용성 향상 → 객체 지향 프로그래밍의 궁극적인 목표

 

6.3.1 [예제] BlockTest

class _27_BlockTest {     static {         System.out.println("static { }");     }   // 클래스 초기화 블럭      {         System.out.println("{ }");     }   // 인스턴스 초기화 블럭      public _27_BlockTest() {         System.out.println("생성자");     }      public static void main(String[] args) {         System.out.println("BlockTest bt = new BlockTest(); ");         _27_BlockTest bt = new _27_BlockTest();          System.out.println("BlockTest bt2 = new BlockTest(); ");         _27_BlockTest bt2 = new _27_BlockTest();     } }
// 결과 static { } BlockTest bt = new BlockTest();  { } 생성자 BlockTest bt2 = new BlockTest();  { } 생성자

 

6.3.2 [예제] StaticBlockTest

class _28_StaticBlockTest {     static int[] arr = new int[10];      static {         for (int i = 0; i < arr.length; i++) {             // 1과 10 사이의 임의의 값을 배열 arr에 저장             arr[i] = (int) (Math.random() * 10) + 1;         }     }      public static void main(String[] args) {         for (int i = 0; i < arr.length; i++) {             System.out.println("arr[" + i + "] : " + arr[i]);         }     } }
// 결과 arr[0] : 7 arr[1] : 2 arr[2] : 4 arr[3] : 5 arr[4] : 2 arr[5] : 8 arr[6] : 8 arr[7] : 6 arr[8] : 2 arr[9] : 2

 

 

6.4 멤버변수의 초기화 시기와 순서

💫
클래스 변수의 초기화 시점 클래스가 처음 로딩될 때 단 한 번 초기화 인스턴스 변수의 초기화 시점 인스턴스가 생성될 때마다 각 인스턴스별로 초기화 클래스 변수의 초기화 순서 기본값 → 명시적 초기화 → 클래스 초기화 블럭 인스턴스 변수의 초기화 순서 기본값 → 명시적 초기화 → 인스턴스 초기화 블럭 → 생성자

 

class InitTest { 		static int cv = 1;    // 명시적 초기화 		int iv = 1;           // 명시적 초기화  		static {  cv = 2;  }  // 클래스 초기화 블럭  		{  iv = 2;  }         // 인스턴스 초기화 블럭  		InitTest () {         // 생성자 					iv = 3; 		} }
  • 클래스 변수 초기화(1 ~ 3) : 클래스가 처음 메모리에 로딩될 때 차례대로 수행
  • 인스턴스 변수 초기화(4 ~ 7) : 인스턴스를 생성할 때 차례대로 수행

 

6.4.1 [예제] ProductTest

class Product {     static int count = 0;   // 생성된 인스턴스의 수를 저장하기 위한 변수     int serialNo;           // 인스턴스 고유의 번호      {         ++count;         serialNo = count;     }      public Product() {     }     // 기본생성자, 생략가능 }  class _29_ProductTest {     public static void main(String[] args) {         Product p1 = new Product();         Product p2 = new Product();         Product p3 = new Product();          System.out.println("p1의 제품번호(serial no)는 " + p1.serialNo);         System.out.println("p2의 제품번호(serial no)는 " + p2.serialNo);         System.out.println("p3의 제품번호(serial no)는 " + p3.serialNo);         System.out.println("생성된 제품의 수는 모두 " + Product.count + "개 입니다.");     } }
// 결과 p1의 제품번호(serial no)는 1 p2의 제품번호(serial no)는 2 p3의 제품번호(serial no)는 3 생성된 제품의 수는 모두 3개 입니다.

 

6.4.2 [예제] DocumentTest

class Document {     static int count = 0;     String name;            // 문서명(Document name)      Document() {            // 문서 생성 시 문서명을 지정하지 않았을 때         this("제목없음" + ++count);     }      Document(String name) {         this.name = name;         System.out.println("문서 " + this.name + "가 생성되었습니다.");     } }  class _30_DocumentTest {     public static void main(String[] args) {         Document d1 = new Document();         Document d2 = new Document("자바.txt");         Document d3 = new Document();         Document d4 = new Document();     } }
// 결과 문서 제목없음1가 생성되었습니다. 문서 자바.txt가 생성되었습니다. 문서 제목없음2가 생성되었습니다. 문서 제목없음3가 생성되었습니다.

 


Uploaded by

N2T
반응형
profile

나를 기록하다

@prao

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...