프로그래밍 언어 활용/JAVA

05. 클래스와 객체 1

프린이8549 2024. 1. 18. 17:20

1.객체 지향 프로그래밍과 클래스

 

1-1. 객체와 객체 지향 프로그래밍

 

1-1-1. 객체의 의미

  • 국어 사전상, '의사나 행위가 미치는 대상'
  • 프로그래밍에서는 눈에 보이는 사물 외에도 주문, 생산, 관리 등 특정 행동을 나타내는 단어도 객체가 될 수 있음

1-1-2. 객체 지향 프로그래밍

  • 객체를 정의하고(만들고) 객체 간 협력을 프로그래밍(객체 사이에 일어나는 일을 구현)하는 것
  • ex) '학생이 버스에 탄다'는 '학생'이라는 객체가 '버스'라는 객체에 탑승하는 행동이 발생하는 것.
  • 자바의 꽃은 객체. 클래스와 객체를 알아야 향후 자바를 성공적으로 운용할 수 있음.
  • cf) 절차 지향 프로그래밍
    • 순서대로 일어나는 일을 시간순으로 프로그래밍하는 것

출처: https://xangmin.tistory.com/152

1-1-3. 용어 정리

  • 본격적으로 들어가기에 앞서,  본 장에서 등장할 중요한 개념들을 미리 요약해보고자 함.
  • 객체
    • 객체 지향 프로그램의 대상. 생성된 인스턴스
  • 클래스
    • 객체를 프로그래밍하기 위해 코드로 만든 상태. 일종의 설계도(붕어빵 틀)
  • 인스턴스
    • 클래스가 메모리에 생성된 상태
  • 멤버 변수
    • 클래스의 속성, 특성
  • 메서드
    • 멤버 변수를 이용하여 클래스의 기능을 구현
  • 참조 변수
    • 메모리에 생성된 인스턴스를 가리키는 변수
  • 참조 값
    • 생성된 인스턴스의 메모리 주소값.

 

1-2. 클래스란?

1-2-1. 클래스의 의미

  • 객체의 속성과 기능을 코드로 구현한 것
  • 클래스를 정의하기 위해 필요한 요소
    • "class" 예약어 
    • 클래스 이름
    • 클래스가 가지는 속성(특성)
//클래스의 구조
(접근제한자* + ) class 이름 {
	// 필드 영역
  		: 사용할 데이터들을 선언하는 영역
        : 멤버 변수
    // 생성자* 영역
    	: 데이터들을 초기화해주는 특수 목적의 메서드인 생성자를 정의하는 영역
    // 메소드* 영역
    	: 클래스의 기능을 정의하는 영역
}
* 후술
  • 클래스의 속성을 '멤버 변수'라고 함
    • ('인스턴스 변수'라고도 표현)
  • 예시 : 학생 클래스 만들기
public class Student{ // 클래스 이름
	int studentIDs; // 이하 student 클래스가 가지는 속성. 즉, 멤버 변수들 
    String studentName;
    int grade;
    String address; 
}

1-2-2. 클래스를 사용하는 이유

  • 예컨대 학생 객체들을 생성하기 위한 프로그램에서
  • 1) 변수만으로 프로그램 구현시
    • 변수는 하나의 자료형에 하나의 값만을 보관할 수 있기에
    • 각 학생 객체를 생성할 때마다 변수를 직접 명시하는 과정을 반복해야 함
      • String studentName : "홍길동";
      • int age : 20; ...
  • 2) 배열만으로 프로그램 구현시
    • 배열은 하나의 자료형으로 여러 개의 값들 보관 가능
    • 하지만 배열 내에서 기존 학생 정보를 지워야 할 때 뒤의 정보들을 앞으로 땡겨오는 과정을 거쳐야하는데 해당 과정에서 실수가 발생하면 정보가 뒤바뀔 수 있음
    • 또한 새로운 학생 정보를 기록해야할 때 새로운 크기의 배열을 생성하고 기존 배열로부터 복사한 뒤 추가해야하는 번거로운 과정이 수반됨
      • 학생들의 이름을 보관하는 배열 String[] name = {"홍길동", "김말똥", ... };
      • 학생들의 나이를 보관하는 배열 int[] age = {20, 30, ... }; 
  • 3) 구조체 개념 => 클래스
    • 변수와 배열의 한계를 극복하고자 나온 개념이 구조체.
      • 구조체 : 여러 개의 자료형에 여러 개의 값들을 보관 가능
      • 따라서 나만의 자료형의 집합을 만들 수 있음
    • 자바에서는 구조체를 확장하여 클래스라는 개념을 만듦.

1-2-3. 클래스 이름 짓는 규칙: 코딩 컨벤션(coding convension)

  • 사회의 관습처럼 개발자 간에도 편의를 위해 관습적으로 정한 규칙이 존재
  • 클래스의 이름을 지을 때는 대문자로 시작.

2. 클래스 살펴보기

 

2-1.  멤버 변수 : 클래스의 속성을 구현

2-1-1. 추상화 과정: 클래스라는 틀을 만들기 위한 과정

  • 1) 내가 구현하고자 하는 프로그램에서 필요한 객체들을 생각
  • 2) 해당 객체들이 가지는 공통적인 속성과 기능들을 추출
  • 3) 추출한 것들을 가지고 구현하고자 하는 프로그램의 실질적인 목적에 맞춰 불필요한 속성과 기능 제거
  • 4) 최종적으로 추려진 속성들에 어떤 자료형과 변수명들을 사용할 것인지 정의

2-1-2. 멤버 변수 선언 시 유의할 점

  • 멤버 변수의 속성에 따라 알맞은 자료형을 선언해야 함.
  • 참조 자료형(클래스형, 객체 자료형)도 사용 가능

2-1-3. 참조 자료형

  • JDK에서 제공하거나(ex.String, Date), 개발자가 직접 만든 클래스가 자료형이 될 수 있음.
  • 후술

 

2-2. 메서드 : 클래스의 기능을 구현

  • 멤버 변수를 사용하여 클래스의 기능을 구현한 것.
  • '멤버 함수'라고도 함
  • 후술.

 

2-3. 패키지

2-3-1. 패키지란?

  • 클래스 파일의 묶음
  • 계층 구조를 가질 수 있음
    • 계층 구조를 잘 구성해야 소스 코드 관리와 유지 보수 편리

2-3-2. 패키지 선언하기

  • 클래스 이름이 같다고 해도 패키지 이름이 다르면 클래스 전체 이름이 다른 것이므로 다른 클래스가 됨
package domain.student.view;

public class StudentView{ //클래스 전체 이름은 'domain.student.view.StudentView' 임

}

 

3. 메서드

 

3-1. 함수 정의하기

3-1-1. 함수의 정의

  • 하나의 기능을 수행하는 일련의 코드
  • 어떤 기능을 수행하도록 미리 구현해 놓고 필요할 때마다 호출하여 사용

3-1-2. 함수 정의하기

  • 함수는 함수 이름, 입력값, 결과 값을 가짐
  • 함수 이름
    • 함수 기능과 관련 있게 만드는 것이 호출하거나 이해하기 용이함
//main 함수
int num1 = 10;
int num2 = 20;
int sum = add(num1, num2); // 함수 이름(인자)

=================================================================
// add 함수
int add(int n1, int n2){ // 함수 반환형  함수 이름(매개변수) 
	int result;					
    result = n1 + n2;		
    return result;			// return 반환값
}
  • 입력값 : 인자(인수)
  • 매개변수
    • 해당 함수 호출 시, 입력값(인자)을 받아주기 위한 변수 선언문
      • 함수 내부에서만 사용 가능
    • 함수를 호출할 때 사용하는 변수 이름과 호출되는 함수 내의 변수(매개변수)는 이름이 달라도 무방(서로 다른 변수이기에)
  • 반환값 : 함수를 수행한 후 결과로 돌려 주는 값
  • return 예약어
    • '이 함수의 결과 값을 반환합니다'를 의미
      • 위 add 함수에서는 return을 통해 result 값을 반환
    • 함수를 종료하고 호출한 곳으로 다시 되돌아가는 목적으로 사용하기도 함
      • 이때는 예약어 뒤에 반환값 기입하지 않아도 됨
  • 반환형
    • 반환 값의 자료형을 반환형이라 하며 함수 이름 앞에 기록 
    • void
      • 반환 값이 없는 함수일 경우 반환형 자리를 비워두지 않고 반환할 값이 없다는 의미의 예약어 'void' 사용

 

3-2. 함수 호출과 스택 메모리

3-2-1. 스택(stack) 메모리의 구조

  • 스택 메모리
    • 함수 호출 시 함수만을 위해 할당된 메모리 공간
  • 스택 메모리의 구조
    • 호출했던 순서대로 스택에 쌓이고, 맨 마지막 호출한 함수부터 처리(반환)되어 메모리에서 소멸됨
    • 따라서 각 함수의 변수들은 서로 다른 메모리 공간을 사용하기에 변수의 이름이 같든 다르든 상관없음.
  • 지역 변수
    • 위의 설명처럼 함수 내부에서만 사용하는 변수를 지역 변수라고 함
    • 지역 변수는 스택 메모리에서 생성됨

 

3-3. 함수의 장점

  • 함수 사용 시 기능을 나누어 효율적인 코드 구현 가능
  • 기능을 분리하여 구현했기에 가독성이 좋음
  • 디버깅 작업 시에 편리

 

3-4. 클래스 기능을 구현하는 메서드

3-4-1. 예시

// 학생 이름을 반환하는 메서드 구현
public class StudentTest{
	int studentID;
    String studentName;
    int grade;
    String address;
    
    public String getStudentName(){ 	//학생 이름을 반환하는 메서드
    	return studentName;
    }
    
    public void setStudentName(String name){ 	// 학생 이름을 매개변수로 전달
    	studentName = name; 		// 학생 이름을 새로 지정하거나 바꾸는 기능
    }
}

 

3-4-2. 메소드 오버로딩

  동일한 이름의 메소드가 있을 때 매개변수의 개수 또는 자료형에 따라 메소드를 구분할 수 있음

public void print() {
	System.out.println("안녕하세요. " + this.age + "살 " + this.name +"입니다.");
}
	
public void print(String gender) { // 같은 이름의 메소드이지만 매개변수가 존재하는, 다른 메소드임
	System.out.println("안녕하세요. " + this.age + "살 " + this.name +"이고 성별은 " + gender + "입니다.");
    }

4. 클래스와 인스턴스

 

4-1. 클래스 사용과 main() 함수

4-1-1. main() 함수

  • 자바 가상 머신이 프로그램을 시작하기 위해 호출하는 함수

 

4-2. new 예약어로 클래스 생성하기

  • new 예약어 사용하고 이어서 생성자를 써줌
  • 형식: 클래스 자료형 변수 이름 = new 생성자;
  • 클래스가 생성된다는 것은 클래스를 실제로 사용할 수 있도록 메모리 공간을 할당 받는다는 것을 의미
//main 함수로 Student 클래스 활용하기
public class Run{
    public static void main(Stirng[] args){
        Student studentHong = new Student();
        // Student 클래스 생성하여 studentHong에 대입함을 의미
        // studentHong : 참조 변수
        studentHong.studentName = "홍길동";
        
        System.out.println(studentHong.studentName);
        System.out.println(studentHong.getStudentName());
    }
}

 

4-3. 인스턴스와 참조 변수

4-3-1. 객체, 클래스, 인스턴스

  • 객체
    • '의사나 행위가 미치는 대상'
  • 클래스
    • 객체를 코드로 구현한 것
  • 인스턴스
    • new 예약어와 생성자 통해 메모리 공간을 할당해 사용할 수 있도록 만든 클래스를 인스턴스라고 함
      • 생성된 클래스의 인스턴스를 객체라고도 함
    • 하나의 클래스로부터 여러 개의 인스턴스 생성 가능
public class StudentTest{ // Student클래스로 2명의 학생 객체 생성하기
	public static void main(String[] args){
    	Student student1 = new Student(); // 첫 번째 학생 인스턴스 생성
        student1.studentName = "홍길동";
        System.out.println(student1.getStudentName());
        
        Student student2 = new Student(); // 두 번째 학생 생성
        student1.studentName = "나선욱";
        System.out.println(student2.getStudentName());
    }
}

 

4-3-2. 참조 변수 사용하기

  • 참조 변수
    • 인스턴스를 가리키는 클래스형 변수
    • 힙 메모리에 생성된 인스턴스를 가리킴
  • 도트(.) 연산자
    • 참조 변수 사용시 인스턴스의 멤버 변수와 메서드 참조하여 활용 가능. 이때 도트 연산자 사용
      • 해당 객체에 접근할 때 사용한다고 이해하면 됨. 직접 접근을 통해 값을 가져옴
    • 형식
      • 참조 변수.멤버 변수
      • 참조 변수.메서드
public class StudentTest{ // 참조 변수 사용하기
	public static void main(String[] args){
    	Student student1 = new Student(); 
        student1.studentName = "홍길동"; // 멤버 변수 사용
        System.out.println(student1.getStudentName()); // 메서드 사용
}

 

 

4-4. 인스턴스와 힙 메모리

4-4-1. 힙 메모리(heap memory)

  • new 예약어와 생성자를 통해 클래스 선언 시 인스턴스가 하나 생성되면, 인스턴스가 지니고 있는 멤버 변수를 저장할 공간이 필요. 이때  사용하는 메모리가 힙 메모리.
  • 클래스 생성자를 하나 호출하면 인스턴스가 힙 메모리에 생성됨
    • 힙 메모리에 생성된 인스턴스의 메모리 주소가 참조 변수에 저장됨

4-4-2. 멤버 변수 == 인스턴스 변수

  • 클래스가 생성될 때마다 인스턴스는 다른 메모리 공간 차지. 즉, 멤버 변수들을 저장하는 공간이 따로 생김
Student student1 = new Student();
Student student2 = new Student();
// 두 인스턴스는 각기 다른 메모리 공간 차지
// 따라서 student1.studentName 과 student2.studentName은 서로 다른 값을 가지게 됨
  • 따라서 클래스에 선언한 멤버 변수를 인스턴스 변수라고도 함.

4-4-3. 참조 변수와 참조값

public class StudentTest{ // 참조 값 출력
	public static void main(String[] args){
    	Student student1 = new Student(); 
        student1.studentName = "홍길동";
        System.out.println(student1.getStudentName());
        
        Student student2 = new Student();  
        student1.studentName = "나선욱";
        System.out.println(student2.getStudentName());
        
        System.out.println(student1); // 참조 변수 값 출력
        System.out.println(student2); 
    }
}
  • 참조 값
    • 참조 변수 값을 출력하면 '클래스 이름@ 주소값'이 출력됨
    • 이는 자바 가상 머신에서 객체가 생성됐을 때 생성된 객체에 할당하는 가상 주소값
    • 해당 주소값을 통해 student1 변수를 사용해 student1 인스턴스를 참조할 수 있게 됨
      • 이때 student1을 참조 변수 / 주소 값을 참조 값이라고 함

5. 생성자

5-1. 생성자란?

5-1-1. 생성자(constructor)

  • 클래스를 생성할 때 호출하는 것.
  • 클래스를 처음 만들 때 멤버 변수나 상수를 초기화해주는 역할
    • 멤버 변수에 대한 값들을 매개변수로 받아서 인스턴스가 새로 생성될 때 멤버 변수 값들을 초기화함. 
    • 인스턴스가 생성됨과 동시에 멤버 변수의 값을 지정하고 인스턴스를 초기화해줌
package constructor;

public class Person{
	String name;
    float weight;
    float height;
}

 

package constructor;

public class PersonTest{
	public static void main(String[] args){
    	Person personLee = new Person(); // 'Person()' : 생성자
    }
}

 

5-1-2. 디폴트 생성자

  • 생성자는 클래스를 생성할 때만 호출함. 허나, 예시로 든 Person 클래스에는 생성자가 따로 존재하지 않았음에도 해당 클래스를 사용해 객체를 생성할 수 있었음.
  • WHY? 생성자가 없는 클래스는 클래스 파일 컴파일시 자파 컴파일러에서 자동으로 생성자를 만들어 줌. 이렇게 자동으로 생성해준 생성자를 디폴트 생성자라고 함.
  • 프로그래머가 필요에 따라 직접 코드를 구현해 생성할 수도 있음.
package constructor;

public class Person{
	String name;
    float weight;
    float height;
    
    Public Person(){ // 디폴트 생성자
    }
}

 

5-2. 생성자 만들기

public class Person{
	String name;
    float height;
    float weight;
    
    public Person(String pname){ // 사람 이름을 매개변수로 입력받아서
    	name = pname;		// Person 클래스를 생성하는 생성자
    }
    
}

5-3. 생성자 오버로드

5-3-1. 오버로드란?

 객체 지향 프로그램에서 메서드 이름이 같고 매개변수만 다른 경우를 칭함

5-3-2. 생성자 오버로드

 클래스에 생성자가 두 개 이상 제공되는 경우.

 

 필요에 따라 매개변수가 다른 생성자를 여러 개 만들 수 있고, 원하는 생성자를 선택해 사용할 수 있음.

  •  매개변수가 있는 생성자 사용 시 생성자 내부에서 변수를 초기화할 수 있도록 구현되어 있어 가독성이 좋고 사용하기 편리함
public class Person{
	String name;
    float height;
    float weight;
    
    public Person(){ // 디폴트 생성자
    }
    
    public Person(String pname){ // 이름을 매개변수로 입력받는 생성자
    	name = pname;
    }
    
    public Person(String pname, float pheight, float pweight){ // 이름, 키, 몸무게를 매개변수로 받는 생성자
    	name = pname;
        height = pheight;
        weight = pweight;
    }
    
}
public class PersonTest {
	public static void main(String[] args) {
    	Person kim = new Person(); // 디폴트 생성자로 클래스 생성한 후 
        kim.name = "홍길동" //  인스턴스 변수 값을 따로 초기화
        kim.weight = 80.0F;
        KIM.height = 176.0F; 
        
        Person lee = new Person("이순신", 175, 75); // 인스턴스 변수 초기화와 동시에 클래스 생성
    }
}

 

 


6. 정보 은닉(information hiding)

6-1. 정보 은닉의 의미

6-1-1. 정보 은닉이란?

 자바와 같은 객체 지향에서 정보 은닉이라함은,  그 단어의 의미와 유사하게, 클래스 내부에서 사용할 변수나 메서드에 특정 예약어를 사용해 외부에서 접근하지 못하도록 하는 것을 의미함.

 

 이는 객체 지향 프로그래밍의 특징 중 하나로, 자바에서는 접근 제어자를 통해 정보 은닉을 구현함.

 

package hiding;

public class Student{
	int studentID;
    private String Name; // 접근제어자 private 통해 String 자료형의 변수 name을 정보 은닉했음
    int grade;
    String address;
    
    public String getName(){
    	return Name;
    }
    
    public void setName(String Name){
    	this.name = name;
    }
}
package hiding;

public class StudentTest{
	public static void main(String[] args){
    	Student hong = new Student();
        hong.name = "홍길동"; // 오류 발생 (public 일때는 접근 가능했으나 private으로 바꾸자 접근 불가해짐)
        
        System.out.println(hong.getName());
    }
}

6-1-2. 접근 제어자(access modifier)

 접근제어자는 클래스 내부의 변수나 메서드, 생성자에 대한 접근 권한을 지정할 수 있는 예약어를 의미함.

  • 접근제한자 또는 접근지시자라고도 표현함
  • 우리가 흔히 사용했던 public 예약어가 대표적인 접근 제어자 중 하나

 접근 제어자의 종류는 다음과 같음.

접근 제어자 설명
public 외부 클래스 어디에서나 접근 가능
protected 같은 패키지 내부 || 상속 관계의 클래스 에서만 접근 가능
아무것도 없는 경우 default이며 같은 패키지 내부에서만 접근 가능
private 같은 클래스 내부에서만 접근 가능

 

6-1-3. 왜 정보 은닉을 구현하는가? : 정보 은닉의 목적

 앞서 살펴본 바와 같이, 클래스 필드 영역 내의 멤버 변수에 public 예약어를 사용하면 클래스 자료형의 객체를 생성할 때  해당 변수에 직접 접근이 가능하여 편리한 반면, private 예약어를 사용했을 시 직접 접근이 불가해지는 현상을 확인할 수 있었음.

 본 현상은 private 접근제어자를 이용한 변수에의 접근은 오직 동일 클래스 내부에서만 가능하기 때문에 발생한 것으로, 외부에서 객체를 생성하여 이에 접근하고자 하면 후술할 get(), set() 메서드를 추가적으로 생성하여 우회적으로 접근해야함.

 

 그렇다면 이렇게 번거로운 과정을 거치면서까지 정보를 은닉하는 목적은 무엇일까?

(목적 출처: https://blog.skby.net/%EC%A0%95%EB%B3%B4-%EC%9D%80%EB%8B%89-information-hiding/)

  • 객체에 포함된 정보의 손상과 오용 방지
    • 클래스의 멤버 변수나 메서드를 public으로 선언 시 접근이 제한되지 않으므로 정보의 오류가 발생할 수 있음 
  • 데이터 변경 시에도 다른 객체에 영향 없이 독립성 유지
  • 객체를 부품화하여 이식성과 호환성 확보

 첫 번째 목적의 예시

public class MyDate{
	public int day;
    public int month;
    public int year;
}
public class MyDateTest{
	public static void main(String[] args){
    	MyDate date = new MyDate();
        date.month = 2;
        date.day = 31; // 2월에는 31일이 없음에도 이런 잘못된 오류를 막을 수 없음
        date.year = 2018;
    }
}
public class MyDate{
	private int day;  // private 예약어로 멤버 변수들에 대한 접근 권한을 제한
    private int month;
    private int year;
    
    public void setDay(int day){ // public 메서드를 통해 우회적으로 변수에 접근할 수 있도록 함
    	if (month == 2) { // 조건문을 통해 오류가 발생하지 않도록 구현
        	System.out.println("오류입니다");
        }
        else {
        	this.day = day;
        }
    }
}

6-1-4. get(), set() 메서드

  private으로 선언한 멤버 변수를 외부 코드에서 사용하기 위해서는 public 메서드를 별도로 제공하여 우회적으로 접근하도록 해야하고, 이를 get(), set() 메서드라고 함.

public class Student{
 int studentID;
 private String name;
 int grade;
 String address;
 
 public String getName(){
 	return name; //private 변수인 name에 접근해 값을 가져오는 public 메서드
 }
 
 public void setName(String name){
 	this.name = name; // private 변수인 name에 접근해 값을 지정하는 public 메서드
 }
 
 // 외부에서 자유로이 접근 가능한 public 메서드를 통해 우회적으로 private 변수를 받아오거나 지정할 수 있도록 해야함 
 
}

 

cf) 이클립스에서는 클래스 내부에서 오른쪽 마우스 클릭 후 [Source -> Generate Getters and Setters...] 메뉴 클릭 시 자동으로 생성 가능

6-2. 캡슐화

6-2-1. 캡슐화의 의미

 추상화를 통해서 정의된 속성들과 기능들을 하나로 묶어 관리하는 기법 중 하나로 클래스에서 가장 중요한 목적인 "데이터의 접근 제한(정보 은닉)"을  원칙으로 "외부로부터의 데이터의 접근을 막고" 대신에 "데이터를 간접적으로 처리(값을 대입, 값을 돌려주거나)"할 메소드들을 클래스 내부에 작성하여 관리하는 것 

 객체 지향 프로그래밍의 4대 요소 중 하나.

6-2-2. 정보 은닉과 캡슐화 비교

항목 정보 은닉 캡슐화
목적 코드의 보호 데이터/함수 모듈화
특징 I/F를 통해서만 접근 속성/메서드 기능 집합
기법 public, private 등 Class
장점 무결성, 보안성 확보 재사용성, 유지보수용이

(표 출처: https://blog.skby.net/%EC%A0%95%EB%B3%B4-%EC%9D%80%EB%8B%89-information-hiding/)