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

객체(Object)와 클래스(class) 개념

객체(Object)란?

"의사나 행위가 미치는 대상" - 사전적 의미
구체적, 추상적 데이터 단위

Java의 방식은?

객체지향 프로그래밍(Object Oriented Programming, OOP)
객체를 기반으로 하는 프로그래밍
cf. 절차 프로그래밍(Procedural Programming) 예) C언어

Java에서는 객체를 정의하고, 객체의 기능을 구현하고, 객체 사이의 협력을 구현한다.

ex) 학교가는 과정

실간의 흐름에 따른 절차 프로그래밍은?

일어난다 -> 씻는다 -> 밥을 먹는다 -> 버스를 탄다-> 등등등
시간에 흐름에 따른 과정

클래스(class)란?

객체에 대한 속성과 기능을 코드로 구현한 것으로, "클래스를 정의한다."라고 함.

객체의 속성

객체의 속성은 property(특성), attribute(속성), memver variable(멤버 변수)라고 표현함.

객체의 기능

객체의 기능은 method(메서드), member function(멤버 함수)라고 표현함.

클래스 정의하기

(접근 제어자) class 클래스명 {
	멤버 변수;
    메서드;
}
  • 클래스명은 영어로 쓰고, 첫글자는 대문자로 작성한다.
  • 하나의 java 파일에 하나의 클래스를 두는 것이 원칙이나,
    여러 개의 클래스가 같이 있는 경우 public 클래스는 단 하나이며,
    public 클래스와 자바 파일의 이름은 동일해야 한다.
  • 자바의 모든 코드는 class 내부에 위치해야 한다.
  • 접근 제어자는 작성하지 않을 수도 있다. (뒤에서 다시 다룸)

표(1)
속성자료형변수 이름설명
학번intstudentID학번은 정수로 나타낼 수 있기 때문에 int형으로 선언
이름StringstudentName학생 이름은 문자로 되어있고, 하나의 문자가 아닌 여러 개의
문자로 이루어진 문자열로 표현하므로 String 클래스를 사용
학년intgrade학년은 정수로 나타낼 수 있기 때문에 int형으로 선언
사는 곳Stringaddress문자열을 나타내기 위해 String 클래스를 사용

적용

학생 클래스 만들기

위 표(1)를 참고해서 만들었다.

public class Student {
	int studentID;
	String studentName;
	int grade;
	String address;
    //  ㄴ> 멤버변수들
	
	
	public void showStudentInfor() { // 메소드 선언부
		System.out.println("학번 : " + studentID);
		System.out.println("이름 : " + studentName);
		System.out.println("학년 : " + grade);
		System.out.println("주소 : " + address);
        // ㄴ> 메소드 구현부
	}
	
	public static void main(String[] args) {
		Student studentLee = new Student();	//클래스 생성
		studentLee.studentID = 20220210;
		studentLee.studentName = "이순신";
		studentLee.grade = 2;
		studentLee.address = "서울시 서초구 서초동";
		studentLee.showStudentInfor();
         // ㄴ> 클래스의 메소드 호출
	}
}
    

위 결과물은 코드가 시작되는 main 함수를 class 안에 썻지만, 아래와 같이 같은 package내에 있는 다른 class파일에서 Student class를 생성하여 사용할 수 있다.

package first;

public class StudentTest {

	public static void main(String[] args) {

			Student studentwe = new Student();
			studentwe.studentID = 20220210;
			studentwe.studentName = "위원종";
			studentwe.address = "경기도 성남시 분당구";
			studentwe.grade = 2;
			
			studentwe.showStudentInfor();
	}

}

여기서 알아야 할 부분은 패키지가 단순히 클래스 묶음이 아닌 프로젝트 전체 소스 코드를 구성하는 계층 구조가 되고, 이 계층 구조를 잘 구성해야 소스 코드 관리와 유지 보수가 편리하다는 것이다.

메소드 개념

메소드란?

함수의 일종으로, 객체의 기능을 제공하기 위해 클래스 내부에 구현되는 함수

함수란?

하나의 기능을 수행하는 일련의 코드
중복되는 기능은 함수로 구현하고, 함수를 호출하여 사용한다.

함수 정의하는 방법

[함수 반환형] [함수 이름] (매개변수1, 매개변수2...){
	기능 코드;
    return [결과값]
}
  1. 함수 반환형은 return값의 자료형을 나타내고, return 값이 없는 경우에는 void라고 쓴다.
  2. 함수 이름은 함수의 기능과 관련하여 명명한다.
  3. 매개변수는 함수의 수행을 위해 필요한 변수로, 함수를 사용할 때 넣어주는 값을 의미한다.

예시

int add (int num1, int num2){
	int result;
    result = num1 + num2;
    return result;
}

package classpart;

public class FunctionTest {

	public static void main(String[] args) {

		int num1 = 10;
		int num2 = 30;
		
		int sum = addNum(num1, num2);
		System.out.println(sum);
	}

	public static int addNum(int n1, int n2) {
		int result = n1 + n2;
		return result;
	}
	
}

함수를 표현할 때 "함수명()" 으로 표현한다.
1. main() 바깥에 addNum()을 정의하였고, main()에서 addNum()을 호출하면서 num1과 num2를 넘겨주었다.
2. addNum()은 int 값 두 개를 매개변수로 받아 두 값을 더해서 return한다.
3. 함수의 return 값은 변수 sum에 대입된다.

함수와 스택 메모리

스택(stack)

  • 함수가 호출될 때 사용하는 메모리
  • 함수의 기능 수행이 끝나면 자동으로 메모리가 반환된다.

함수 호출과 스택(stack) 메모리 구조

위 FunctionTest 함수를 정의하는 예제를 기준으로 설명하면

  1. main()에서 int형의 num1, num2, sum 변수를 선언했으므로
    4byte*3 = 12byte의 공간이 스택에 생성된다.
  2. main()에서 addNum()을 호출하면서 addNum()에서 사용한 int형의
    n1, n2, result 변수로 인해 또 12byte의 공간이 스택에 생성된다.
  3. result값이 sum에 대입되고 addNum()이 사용한 공간은 자동으로
    사라진다.

클래스에 메서드 구현하기

클래스의 메서드는 멤버변수를 사용하여 기능을 구현한다

public class Student {
	int studentID;
	String studentName;
	int grade;
	String address;
    // ㄴ> 멤버 변수를 정의
	
	public String getStudentName() {
		return studentName;
	}  //ㄴ> 학생 이름을 가져오는 메서드
	public void setStudentName(String name) {
		studentName = name;
	}  //ㄴ> 학생 이름을 설정하는 메서드
}
  • 위 코드는 위에서 사용한 Student 클래스에 2개의 메서드를 추가한 것이다.
  • 메서드 이름은 클래스 입장이 아닌 메서드를 사용하는 입장에서 이름을 정해주는 것이 좋다.
  • 학생 이름을 가져오는 메서드는 getStudentName으로 명명하고, return 값이 String 자료형이므로 메서드 이름 앞에 반환형으로 String을 써주었다.
  • 학생 이름을 설정하는 메서드는 setStudentName으로 명명하고, return 값 없이 studentName을 바꾸는 기능만 하므로 반환형을 void로 써주었다.

<리마인드> python에서 사용한 서버 명령어 get post와 유사하다.

함수Tip

  1. 코드를 효율적으로 구현하기 위해 함수로 기능을 분리해서 구현하고, 필요한 기능을 가져다 쓴다.
  2. 유지보수가 용이하다. 변수별로 일일히 함수를 사용하면 수정할 때 실수할 확률이 높아진다. 함수 따로 호출해서 사용하면 함수만, 변수만 수정하기 편함.
  3. 함수의 이름은 함수의 기능을 나타내는 것이 좋고, 하나의 함수는 하나의 기능만 구현하는 것이 좋다.

class & instance

  • 클래스(class)는 객체를 추상화한 개념이며, 그것을 구체적으로 표현한 것을 인스턴스(instance)라고 한다.
    ex) class : 자동차 / instance : 소나타, 아반떼 등
  • 객체지향 프로그래밍은 객체를 정의하고 객체들을 class라는 코드로 표현한다.
  • class를 구체적으로 작성하는 것을 instance화 한다고 한다.

클래스 생성

  • 클래스를 사용하기 위해서는 클래스를 생성해야 함.
    (new 예약어를 이용하여 클래스를 생성한다.)
  • 클래스형 변수이름 = new 생성자;
ex) Student studentA=new Student();

<리마인드> [학생클래스 만들기] 에서 사용했던 아래 코드에서
예시로 사용한 코드를 볼 수 있다.

public class Student {
	int studentID;
	String studentName;
	int grade;
	String address;
	
	public void showStudentInfor() {
		System.out.println("학번 : " + studentID);
		System.out.println("이름 : " + studentName);
		System.out.println("학년 : " + grade);
		System.out.println("주소 : " + address);
	}
	
	public static void main(String[] args) {
		Student studentLee = new Student();	  //<<<<<이부분
		studentLee.studentID = 20220210;
		studentLee.studentName = "이순신";
		studentLee.grade = 2;
		studentLee.address = "서울시 서초구 서초동";
		studentLee.showStudentInfor();
	}
}

Student studentA=new Student(); << 이 코드에서

  • 맨 앞의 Student는 클래스형(객체형)이며 참조형 데이터타입이라고 한다.
  • 두 번째 studentA는 참조 변수라고 한다.
  • Student()는 기본생성자라고 한다.

인스턴스와 힙(Heap) 메모리

  • 하나의 클래스 코드로부터 여러 개의 인스턴스를 생성
  • 지역변수는 스택메모리에 생성되며, 인스턴스는 힙(Heap) 메모리에 생성
  • 각각의 인스턴스는 다른 메모리에 다른 값을 가짐
  • 힙 메모리는 동적으로 생성되는 메모리로, 필요할 때 allocation(할당) 받는다.

public class Student {			
	int studentID;					<<<<<<< 11111
	String studentName;			<<<<<<< 11111
	int grade;       		<<<<<<< 11111
	String address;    <<<<<<< 11111
    
	public static void main(String[] args) {
		Student studentLee = new Student();	  <<<<< 222222
		studentLee.studentID = 20220210;
		studentLee.studentName = "이순신";
		studentLee.grade = 2;
		studentLee.address = "서울시 서초구 서초동";
		studentLee.showStudentInfor();
	}
}
  1. 위 예시코드의 경우에 studentID, studentName, grade, address 변수들이 Student의 instance로서 힙 메모리에 할당된다.

  2. studentLee 라는 변수에 new Student()로 클래스를 생성하면, studentLee 변수는 stack 메모리에 생성되어 heap 메모리에 저장된 instance의 주소를 가리키게 된다.

  3. studentWe라는 새로운 변수에 new Student()로 클래스를 생성하면, studentWe와 studentLee의 인스턴스는 각각 다른 메모리 공간을 갖는다.
    즉, studentWe와 studentLee는 서로 다른 주소를 가리키게 된다.

  4. heap 메모리는 Garbage Collection을 통해 수거된다.
    ( Garbage Collection은 나중에 배움 )

용어 정리

용어설명
객체객체 지향 프로그램의 대상, 생성된 인스턴스
클래스객체를 프로그래밍하기 위해 코드로 만든 상태
인스턴스클래스가 메모리에 생성된 상태
멤버 변수클래스의 속성, 특성
메서드멤버 변수를 이용하여 클래스의 기능을 구현
참조 변수메모리에 생성된 인스턴스를 가리키는 변수
참조 값생성된 인스턴스의 메모리 주소 값

생성자(constructor)

생성자란?

  • 생성자는 인스턴스를 초기화할 때의 명령어 집합이다.
  • 생성자의 이름은 사용하는 그 class의 이름과 같다.
  • 생성자는 메소드가 아니다. 때문에 상속되지 않으며, 리턴 값은 없다.
  • 인스턴스 생성 시 new 키워드와 함께 사용했던게 생성자
    Student studentLee = new Student() 에서 Student()가 생성자다.
  • 생성자는 객체를 처음 생성하면서 해야될 일들을 구현하는 것이다.

생성자 기본 문법

<modifiers> <class_name> ([<argument_list>]){
	[<statements>]
}

default 생성자

  • 클래스의 생성자가 하나도 없는 경우 자바 컴파일러가 코드를 컴파일하기 전에 default 생성자를 넣어준다. (default 생성자 = 매개변수도 없고, 실행 코드도 없는 생성자)
  • but 생성자가 하나라도 있으면 자바 컴파일러가 default 생성자를 생성하지 않음

아래 코드는 위에서 사용했던 Student 코드이다.

public class Student {
	int studentID;
	String studentName;
	int grade;
	String address;
    
    
	
	public static void main(String[] args) {
		Student studentLee = new Student();
		studentLee.studentID = 20220210;
		studentLee.studentName = "이순신";
		studentLee.grade = 2;
		studentLee.address = "서울시 서초구 서초동";
		studentLee.showStudentInfor();
	}
}

이럴 때 default 생성자가 생성되는 것이다.

그렇다면 직접 생성자를 넣어보자.

public class Student {
	int studentID;
	String studentName;
	int grade;
	String address;
    
	public Student(int id, String name) {
		studentID = id;
		studentName = name;
	} // ㄴ> 생성자 정의한거
    
	public static void main(String[] args) {
		Student studentLee = new Student();
		studentLee.studentID = 20220210;
		studentLee.studentName = "이순신";
		studentLee.grade = 2;
		studentLee.address = "서울시 서초구 서초동";
		studentLee.showStudentInfor();
	}
}

위와 같이 class 내부에 생성자를 정의할 수 있지만 main()에서 에러가 난다.
그 이유는 생성자를 정의해주었기 때문에 default 생성자를 자동으로 생성해주지 않기 때문이다.
에러를 수정하기 위한 방법은 두 가지가 있다.

  1. new Student() 부분을 new Student(20220210, "이순신") 처럼 바꿔준다.
public class Student {
	int studentID;
	String studentName;
	int grade;
	String address;
    
	public Student(int id, String name) {
		studentID = id;
		studentName = name;
	} // ㄴ> 생성자 정의한거
    
	public static void main(String[] args) {
		Student studentLee = new Student(20220210, 이순신);
		//studentLee.studentID = 20220210;
		//studentLee.studentName = "이순신";
        // 위 new Student에 써줬기 때문에 지워도 된다.
		studentLee.grade = 2;
		studentLee.address = "서울시 서초구 서초동";
		studentLee.showStudentInfor();
	}
}
  1. class 내부에 default 생성자를 직접 만들어준다.
public class Student {
	int studentID;
	String studentName;
	int grade;
	String address;
    
    public Student() {}      <<<<< 이렇게 직접 써도 됨
    
	public Student(int id, String name) {
		studentID = id;
		studentName = name;
	} // ㄴ> 생성자 정의한거
    
	public static void main(String[] args) {
		Student studentLee = new Student();
		studentLee.studentID = 20220210;
		studentLee.studentName = "이순신";
		studentLee.grade = 2;
		studentLee.address = "서울시 서초구 서초동";
		studentLee.showStudentInfor();
	}
}

생성자 오버로드 (constructor overload)

overload란?

  • 필요에 의해 생성자를 추가하는 경우 여러 개의 생성자가 하나의 클래스에 있음(overload)
    (다른 매개변수, 다른 구현부를 가지고 하나의 클래스에 존재할 수 있다.)

예시

public class Person {
	String name;
	float height;
	float weight;
	
	public Person() {}  // default 생성자
   
	public Person(String pname) { //이름을 매개변수로 입력받는 생성자
		name = pname;
	}
	
	public Person(String pname, float pheight, float pweight) {
    //ㄴ> 이름, 키, 몸무게를 매개변수로 입력받는 생성자
		name = pname;
		height = pheight;
		weight = pweight;
	}
}

키,몸무게 라는 다른 매개변수, float pheight, float pweight가 추가된 다른 구현부

위와 같이 생성자를 만들어두면 Person클래스를 호출할 때는
new Person()
new Person("이름")
new Person("이름", 키, 몸무게)
세 가지 방식으로 호출할 수 있게 되는 것이다.

참조 자료형(reference data type)

클래스 형으로 선언하는 변수의 자료형

  • 기본 자료형 : int, long, float, double 등
  • 참조 자료형 : String, Data, Student 등

예시

학생의 속성 중 수업에 대한 부분을 만들 때, 수업에 대한 각 속성을 학생 클래스에 정의하지 않고 수업이라는 클래스로 분리해서 사용한다. 이 때, 과목은 참조 자료형으로 선언

위 그림과 같이 학생의 학번, 이름, 국어성적, 수학성적, 수강 과목 이름을 코드로 바꾸면 아래와 같이 나타낼 수 있다.

public class Student {
	int studentID;
	String studentName;
	
	int koreaScore;
    int mathScore;
    String koreaSubject;
    String mathSubject;
}

이런 방식으로 하나의 class인 Student에 모든 데이터를 담으면 데이터를 추가하거나 내용을 수정할 때 번거로움이 발생한다.

예를 들어 설명하자면 위 코드에는 과목이름과 점수만 있지만 만약 과제 점수가 중간고사,기말고사, quiz등 추가되고 또 수강하는 과목의 선생님을 추가하는 등 수정을 하려면 현재는 Student{}에 각각의 변수를 다 적어줘야 한다.

학생과 과목의 class를 분리하여 참조 자료형으로 사용하면 수정 과정에서 번거로움을 줄일 수 있다.

public class Subject {
	String subjectName;
	int score;
    int score2;
    int score3;
    int score4;
    int score5;
}

public class Student {
	int studentID;
	String studentName;
	Subject korea;
	Subject math;
	
	public Student() {
		korea = new Subject();
		math = new Subject();
	}
}

위 코드처럼 과목과 학생의class를 나눠둔다면 각 개체별로 추가되는 변수만 적을 수 있다.

곱셈을 생각해보자.

위 코드에서는 Subject에 6가지 변수(String subjectName, score1~5)가 있고 Student에는 korea,math 두과목이 있다. 이걸 하나의 class에 합친다면 각 학생별로 12줄을 적어야 하는 것이다.

get(, set() 매소드

get set 메서드란?

private로 선언한 변수를 외부 class에서 사용하기 위한 기능

사용하는 방법

eclipse에서는 getter와 setter를 자동으로 만들어주는 편의기능이 있다.

마우스 우클릭 -> Source -> Generate Getters and Setters -> get, set 메서드를 만들 변수를 선택 -> Generate
단축키 : Alt + Shift + s -> r

적용

get(, set() 매소드도 자료형의 예라고 할 수 있다.

Student class 코드

package reference;

public class Student {
	
	int studentID;
	String studentName;   // 얘처럼 new 해줄 필요가 없는 case가 있다. 나중에 다룰 예정
	Subject korea;
	Subject math;
	
	
	public Student() {
		korea = new Subject("국어");
		math = new Subject("수학");
	}// ㄴ> Q: 아래서 또 선언해주는데 이게 있어야하나?? A: 없어도 되는것 맞다. 
    //다만 기본 생성자와 id값이 들어가는 생성자 등 생성자별로 Subjet의 
    //이름을 다르게 명명해 주고 싶을 때 이렇게 나눠서 쓸 수 있다는걸 보여주는 것
	
	
	public Student(int id, String name) {
		studentID = id;
		studentName = name;
		
		korea = new Subject("국어");
		math = new Subject("수학");
	}
	
	
	public void setKorea(int score) {
		//korea.setSubjectName(name);
		korea.setScore(score);
	}
	
	public void setmath(int score) {
		//math.setSubjectName(name);
		math.setScore(score);
	}
	
	public void showStudentInfo() {
		int total = korea.getScore() + math.getScore();
		System.out.println(studentName + "학생의 총점은" + total + "점 입니다." );
	}	
}

Subject class 코드

package reference;

public class Subject {
	
	String subjectName;
	int score;
	
	public Subject(String name) {     // Student class에서 일일히 String name 적지 않기 위해 써는거
		subjectName = name;
	}
	
	public void setSubjectName(String name) {
		subjectName = name;
	}

	public String getSubjecttName() {
		return subjectName;
	}

	public int getScore() {
		return score;
	}

	public void setScore(int score) {
		this.score = score;
	}
	

}

사실 지금은 Privite을 사용하지 않았기 때문에 굳이 get set을 사용하지 않아도 되긴하지만 get set메소드를 사용한 위 과정이 참조 자료형을 이용한 예시라 설명한 것 같다.

public Subject(String name) {
		subjectName = name;
}

이 코드덕분에 Student class에서 주석처리한
//korea.setSubjectName(name); << 이 코드가 필요 없어진다.

public void setKorea(int score) {}와 같이 생성자에 String name을 뺄 수 있게 된다.

정보은닉 (information hiding)

private 접근 제어자

  • 클래스 외부에서 클래스 내부의 멤버변수나 메서드에 접근(access)하지 못하게 하는 경우에 사용
  • 멤버변수나 메서드를 외부에서 사용하지 못하도록 하여 오류를 줄일 수 있다.
  • 변수에 대해서는 필요한 경우 get(), set() 메서드를 제공한다.
package hiding;

class BirthDay {
	
	public int day;
	public int month;
	public int year;     // 여기에 public 대신 private을 붙이면 오류남 (class 안에서만 쓸 수 있는 제어자이기 때문)
	// private는 변수가 혹시라도 변하게 되면 class에 심각한 오류를 범할 수 있을 때 씀
	
	//pubic = 외부 class에서 다 가져다 쓸 수 있는거
	//private = class 내부에서만 쓸 수 있는거
	//아무것도 안쓰는거 = default라 하며 같은 package 내에서는 다 쓸 수 있다.
	
}

public class BirthDayTest{
	
	public static void main(String[] args) {
		
		BirthDay day = new BirthDay();
		day.month = 2;
		day.year = 2022;
		day.day = 30;
	}
}

위와 같이 BirthDay 클래스의 변수인 day, month, year에 클래스 외부(BirthDayTest 클래스)에서 접근하는 경우에는 2월 30일로 지정할 수 있다.

하지만 2월은 28일까지밖에 없기 때문에 이것은 코드상 오류는 없지만 실제로는 존재하지 않아야 한다.

이럴 때 변수는 private으로 감추고, get set 메서드를 통해 이러한 오류들을 막으면서 private으로 감춘 변수를 가져오는 코드를 작성할 수 있다.

class BirthDay {
	private int day;
	private int month;
	private int year;
	
	public int getDay() {
		return day;
	}
	public void setDay(int day) {
		if(month == 2) {
			if(day < 1 || day > 28) {
				System.out.println("날짜 오류입니다.");
			}
		}else {
			this.day = day;			
		}
	}
	public int getMonth() {
		return month;
	}
	public void setMonth(int month) {
		this.month = month;
	}
	public int getYear() {
		return year;
	}
	public void setYear(int year) {
		this.year = year;
	}
}

public class BirthDayTest{
	public static void main(String[] args) {
		BirthDay day = new BirthDay();
		day.setYear(2022);
		day.setMonth(2);
		day.setDay(30);
	}
}

위처럼 class BirthDay에서 private으로 감춘 변수(dat, month, year)를 get set메소드를 활용하여 class BirthDayTest에서 사용할 수 있다.

접근제어자

  • public : 외부 클래스에서 모두 사용 가능
  • private : 클래스 내부에서만 사용 가능
  • default(아무것도 쓰지 않는 경우) : 같은 package 내에서 사용 가능
  • protected : 상속 관계에서 사용 가능

0개의 댓글

Powered by GraphCDN, the GraphQL CDN