Java/혼자 공부하는 자바

[혼공자] 3주차_chapter 6

예린lynn 2024. 1. 19. 01:01
728x90

√ 미션

1. 기본 미션 : 어렵거나 중요하다고 생각하는 용어를 혼공 용어 노트에 정리하고 공유하기

  • 객체 : 물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서 자신의 속성을 가지고 있으면서 식별 가능한 것
  • 인스턴스 멤버 : 객체마다 가지고 있는 멤버
  • 정적 멤버 : 클래스에 위치시키고 객체들이 공유하는 멤버
  • 오버로딩 : 클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것
  • 싱글톤 : 전체 프로그램에서 단 하나만 만들어지는 객체

 
2. 선택 미션 : 객체 지향 프로그래밍의 개념 정리하기

  • 객체 지향 프로그래밍 : 객체를 만들고 이를 조립해 완성된 프로그램을 만드는 기법
  • 객체 모델링 : 현실 세계 객체의 속성과 동작을 추려내어 소프트웨어 객체의 필드와 메소드로 정의하는 과정
  • 클래스 : 객체를 생성하기 위한 필드와 메소드가 정의되어 있는 설계도

Chapter 06 : 클래스

1 . 객체 지향 프로그래밍

  1) 객체

객체는 속성과 동작으로 구성되어 있으며, 자바는 속성을 필드, 동작을 메소드라고 부른다. 소프트웨어의 모든 현상은 객체와 객체 간의 상호작용으로 이루어지는데, 이때 객체가 다른 객체의 기능을 이용하는 것이 바로 메소드 호출이다. 

 
메소드 호출 코드는 다음과 같다. 객체의 상호작용은 객체 간의 메소드 호출을 의미하며 매개값과 리턴값을 통해서 데이터를 주고받는다.

//리턴값 = 전자계산기객체.메소드(매개값1, 매개값2, ...);

int result = Calculator.add(10,20);

 
- 객체 간의 관계

  • 집합 관계 : 객체 하나는 부품이고, 다른 하나는 완성품 ex) 부품과 자동차
  • 사용 관계 : 객체가 다른 객체의 메소드를 호출하여 원하는 결과를 얻어내는 상호작용 ex) 사람과 자동차
  • 상속 관계 : 상위 객체(종류)를 기반으로 하위 객체(구체적인 사물)를 생성하는 관계 ex) 기계와 자동차

- 객체 생성 과정
클래스에는 객체를 생성하기 위한 필드와 메소드가 정의되어 있으며, 클래스로부터 만들어진 객체를 해당 클래스의 인스턴스라고 한다.

 

2) 클래스

사용하고자 하는 객체를 구상한 후, 객체의 대표 이름을 클래스 이름으로 정한다. 이때 클래스 이름 작성 규칙은 다음과 같다.

  • 하나 이상의 문자로 이루어져야 한다.
  • 첫 글자에는 숫자가 올 수 없다.
  • '$', '_' 외의 특수 문자는 사용할 수 없다.
  • 자바 키워드는 사용할 수 없다. ex) int(x), for(x)
  • 통상적으로 클래스 이름이 단일 단어이면, 첫 글자를 대문자로 하고 나머지는 소문자로 작성한다.

소스 파일 생성 후, 소스 파일을 열고 다음과 같이 클래스를 선언해준다. 참고로 public 접근 제한자는 파일 이름과 동일한 이름의 클래스 선언에만 붙일 수 있다.

public class 클래스 이름 {
}

class 클래스 이름2

 
클래스를 선언하고 컴파일을 한 후에는, new 연산자를 사용해서 클래스로부터 객체를 생성한다. new 연산자는 힙 영역에 객체를 생성시킨 후 객체의 번지를 리턴해야 한다.

클래스 변수;
변수 = new 클래스();

클래스 변수 = new 클래스();

 
- 클래스의 용도

  • 라이브러리 클래스 : 다른 클래스에서 이용할 목적으로 설계
  • 실행 클래스 : 프로그램의 실행 진입점인 main() 메소드 제공

라이브러리 클래스에 main() 메소드를 작성해서 라이브러리인 동시에 실행 클래스로 만들 수도 있다. 그러나 대부분 라이브러리와 실행 클래스가 분리되어 있다.
 
- 클래스의 구성

  • 필드 : 객체의 고유 데이터, 부품 객체, 상태 정보를 저장하는 곳
  • 생성자 : 필드를 초기화하거나 메소드를 호출해서 객체를 사용할 준비를 한다. 클래스로 되어있고 리턴 타입이 없다.
  • 메소드 : 필드를 읽고 수정하고, 다른 객체를 생성해서 다양한 기능을 수행한다. 또한 객체 간의 데이터를 전달하는 수단이다.

 

2 . 필드

필드는 객체의 고유 데이터, 부품 객체, 상태 정보를 저장하는 곳이다.

 
필드 선언은 생성자와 메소드 중괄호 {} 블록 내부를 제외하고 어디서든 선언될 수 있다. 선언 형태는 변수와 비슷하지만 필드를 변수라고 부르지는 않으며, 필드의 초기값은 필드 선언 시 생략될 수도 있다. 이때 초기값이 지정되지 않은 필드는 객체 생성 시 자동으로 기본 초기값으로 설정된다.

//타입 필드 [ = 초기값]

String company = "현대자동차";
int maxSpeed = 300;
int currentSpeed;

 

- 필드 사용
필드 사용은 필드값을 읽고 변경하는 작업이다. 클래스 내부의 생성자나 메소드에서 사용할 경우 필드 이름으로 읽고 변경하면 된다. 반면에 클래스 외부에서 사용할 경우 클래스로부터 객체를 생성한 뒤 필드를 사용한다.

 

 3 . 생성자

 생성자는 new 연산자로 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당한다. 객체 초기화는 객체를 사용할 준비를 하는 것으로, 생성자를 실행하지 않고서는 클래스로부터 객체를 만들 수 없다.
 
클래스가 public class로 선언되면 기본 생성자에도 public이 붙지만, 클래스가 class로만 설정되면 기본 생성자에도 public이 붙지 않는다. 

 
- 생성자 선언
생성자는 메소드와 모양이 비슷하지만, 리턴 타입이 없고 클래스 이름과 동일하다. 생성자 블록 내부에는 필드에 초기값을 저장하거나 메소드를 호출하여 객체 사용 전에 필요한 준비를 한다. 이때 매개 변수는 new 연산자로 생성자를 호출할 때 외부의 값을 생성자 블록 내부로 전달하며 생략 가능하다.

클래스( 매개변수선언, ... ){
    //객체의 초기화 코드
}

 
다음과 같이 3개의 매개값을 이용하여 Car 생성자를 호출한다고 해보자.

Car myCar = new Car("A", "B", 300);

public class Car {
   // 생성자
   Car(String model, String color, int maxSpeed){...}
}

 
- 필드 초기화
필드를 기본 초기값이 아닌 다른 값으로 초기화하는 방법은 두 가지가 있다.

  • 필드를 선언할 때 초기화 : 동일한 클래스로부터 생성되는 객체들은 모두 같은 값을 갖는다
  • 생성자에서 초기값 부여 : 객체 생성 시점에 외부에서 제공되는 다양한 값들로 초기화되어야 하는 경우
//필드 선언 시 초기값 부여
public class Korean{
String nation = "대한민국";
String name;
String ssn;
}

Korean k1 = new Korean(); //대한민국
Korean k2 = new Korean(); //대한민국

//생성자에서 초기값 부여
public class Korean{

//필드
String nation = "대한민국";
String name;
String ssn;

//생성자
public Korean(String n, String s){
  name = n;
  ssn = s;
  }
}

Korean k1 = new Korean("A", "01122-34"); 
Korean k2 = new Korean("B", "93084-53");

 
일반적으로 필드와 동일한 이름을 갖는 매개 변수를 사용한다. 그러나 이 경우 필드와 매개 변수 이름이 동일하기 때문에 생성자 내부에서 해당 필드에 접근할 수 없는 문제가 발생한다. 이를 해결하기 위해 필드 앞에 this를 붙여서 'this.필드'라는 참조 변수를 사용한다.

 
- 생성자 오버로딩
생성자 오버로딩이란 매개 변수를 달리하는 생성자를 여러 개 선언하는 것이다. 참고로 매개 변수의 타입과 개수 그리고 선언된 순서가 똑같을 경우 매개 변수 이름만 바꾸는 것은 생성자 오버로딩이 아니다.

/**
public class 클래스 {
  클래스 ([타입 매개변수, ...]){
  ...
  }
  
  클래스 ([타입 매개변수, ...]){
  ...
  }
}  
*/

//생성자 오버로딩
public class Car {
  Car(){...}
  Car(String model){...}
  Car(String model, String color){...}
  Car(String model, String color, int maxSpeed){...}
  }
  
Car car1 = new Car();
Car car2 = new Car("A");
Car car3 = new Car("A", "흰색");
Car car4 = new Car("A", "흰색", 300);

//생성자 오버로딩x
Car(String model, String color){...}
Car(String color, String model){...}

 
- 다른 생성자 호출 : this()
생성자 오버로딩이 많아질 경우 생성자 간의 중복된 코드가 발생할 수 있다. 이 경우 필드 초기화 내용은 한 생성자에만 집중적으로 작성하고, 나머지 생성자는 초기화 내용을 가진 다른 생성자를 호출하도록 한다. 이때 사용하는 것이 this() 코드이다.

/**
클래스 ([매개변수, ...]){
  this(매개변수, ... , 값, ...); //클래스의 다른 생성자 호출
  실행문;
}
*/

//중복 코드 발생
Car(String model){
  this.model = model;
  this.color = "은색";
  this.maxSpeed = 250;
  }
  
Car(String model, String color){
  this.model = model;
  this.color = color;
  this.maxSpeed = 250;
  }
  
Car(String model, String color, int maxSpeed){
  this.model = model;
  this.color = color;
  this.maxSpeed = maxSpeed;
  }
  
  //중복 제거
  Car(String model){
  this(model, "은색", 250);
  }
  
  Car(String model, String color){
  this(model, color, 250);
  }
  
  Car(String model, String color, int maxSpeed){
  this.model = model;
  this.color = color;
  this.maxSpeed = maxSpeed;
  }

 

4 . 메소드

1) 메소드 선언
 
메소드 선언은 선언부와 실행 블록으로 구성된다. 

 
 
- 리턴 타입
리턴값이 없는 메소드는 리턴 타입에 void로 기술하며, 리턴값이 있는 메소드는 리턴값의 타입을 기술한다.
powerOn()은 리턴값이 없고, divide()의 리턴 결과가 double이라고 할 때 메소드 코드는 다음과 같다.

//메소드 선언 및 호출
void poerOn(){...}
powerOn();

double divide(int x, int y){...}
double result = divide(10, 20);

 
- 메소드 이름
메소드 이름은 다음과 같은 자바 식별자 규칙에 맞도록 작성해야 한다.

  • 숫자로 시작하면 안 되고, $와 _를 제외한 특수 문자를 사용하면 안 된다.
  • 관례적으로 메소드 이름은 소문자로 작성한다.
  • 서로 다른 단어가 혼합될 경우, 뒤이어 오는 단어의 첫 글자는 대문자로 작성한다.
void run(){...}
String getName(){...}
int[] getScores(){...}

 
- 매개 변수 선언
매개 변수는 메소드가 실행할 때 필요한 데이터를 외부로부터 받기 위해 사용한다. 
double() 메소드는 매개 변수 2개를 필요로 한다.

double divide( int x, int y ){...}
double result = divide (10, 20);

 
- 매개 변수 개수를 모를 경우
메소드를 선언할 때 매개 변수의 개수를 알 수 경우가 있다. 이 경우 매개 변수를 '배열 타입'으로 선언하거나, '...'를 사용해서 선언하면 메소드 호출 시 넘겨준 값의 수에 따라 자동으로 배열이 생성되고 매개값으로 사용된다.

//배열 타입
int sum1(int[] values){}
int result = sum1(new int[] {1, 2, 3});

//... 사용
int sum2(int ... values){}
int result = sum2(1, 2, 3);

 
2) 메소드 호출
 
- 객체 내부에서 호출

//리턴값이 없거나, 있어도 받고 싶지 않을 경우
메소드(매개값, ...);

public class ClassName{
  void method1(String p1, int p2){}

  void method2(){
    method1("홍길동", 100);
}
}

//리턴값이 있는 메소드를 호출하고 리턴값 반환
타입 변수 = 메소드(매개값, ...);

public class ClassName{
  int method1(int x, int y){
    int result = x + y;
    return result;
 }
 
 void method2(){
   int result1 = method1(10,20);
   int result2 = method1(10,20);
   }

 
- 객체 외부에서 호출

//리턴값이 없거나, 있어도 리턴값을 받지 않을 경우
참조변수. 메소드( 매개값, ... );

//리턴값이 있고, 리턴값을 받고 싶을 경우
타입 변수 = 참조변수.메소드( 매개값, ... );

 

 5 . 인스턴스 멤버와 정적 멤버

인스턴스 멤버는 객체마다 가지고 있는 멤버이고, 정적 멤버는 클래스에 위치시키고 객체들이 공유하는 멤버이다.
 
1) 인스턴스 멤버
인스턴스 멤버는 객체를 생성한 후 사용할 수 있는 필드와 메소드이다. 인스턴스 필드와 메소드는 객체에 소속된 멤버이기때문에 객체 없이는 사용 불가하다. 지금까지 작성한 모든 필드와 메소드는 인스턴스 멤버이다.
 
- 인스턴스 멤버 선언

public class Car{
  //필드
  int gas;
  
  //메소드
  void setSpeed(int speed){...}
}

//외부 클래스에서 참조
Car myCar = new Car();
myCar.gas = 10;
myCar.setSpeed(60);

 
- this
객체 내부에서 인스턴스 멤버에 접근하기 위해서는 this를 사용한다. this는 주로 생성자와 메소드의 매개 변수 이름이 필드와 동일한 경우, 인스턴스 멤버인 필드임을 명시하고자 사용한다.

public class Car{
  //필드
  String model;
  itn speed;
  
  //생성자
  Car(String model){
    this.model = model;
  }
  
  //메소드
  void setSpeed(int speed){
    this.speed = speed;
  }
}

public class CarExample{
 public static void main(String[] args){
   
   Car myCar = new Car("A");
   
   myCar.run();
   }
}
728x90