Java

Java(4) 클래스와 객체

hyomee2 2024. 8. 7. 16:28

1. 클래스

: 사용자 정의 자료형으로, 서로 다른 타입의 데이터와 메소드를 정의하여 만든 사용자 정의 타입이다.

- 클래스를 사용하려면 new 연산자로 heap 영역에 할당해야 한다. (heap 영역은 기본적으로 JVM 초기값으로 초기화된다.)

public class Member {
	String id;
    String pwd;
    String name;
    int age;
    char gender;
    String[] hobby;  // 배열 필드도 있을 수 있다.
}

public class Application {
	public static void main(String[] args) {
    	Member member = new Member();
        
        // "레퍼런스변수명.필드명" 으로 필드에 접근
        // .은 참조 연산자로, 레퍼런스 변수가 참조하는 주소로 접근한다는 의미이다.
        
        member.id = "user01";
        member.pwd = "pass01";
        member.name = "홍길동";
        member.age = 18;
        member.gender = 'M';
        member.hobby = new String[] {"축구", "야구", "코딩"};

        System.out.println("member.id = " + member.id);
        System.out.println("member.pwd = " + member.pwd);
        System.out.println("member.name = " + member.name);
        System.out.println("member.age = " + member.age);
        System.out.println("member.gender = " + member.gender);
        System.out.println("member.hobby = " + Arrays.toString(member.hobby));
    }
}

 

2. 캡슐화

: 유지보수성 증가(낮은 결합도)를 위해 필드의 직접 접근을 제한하고,

  public 메소드를 이용하여 간접적으로 접근하여 사용할 수 있도록 클래스를 작성하는 기법

- 클래스를 작성할 시 특별한 목적이 아닌 이상 캡슐화가 기본적인 원칙으로 사용되고 있다.

(1) 필드에 직접 접근 시 발생할 수 있는 문제점

1) 올바르지 않은 값 할당 시

public class Monster {
    String name;
    int hp;
}

public class Application {
	public static void main(String[] args) {
    	Monster monster2 = new Monster();
        monster2.name = "뿌꾸";
        monster2.hp = -200; // hp는 음수일 수 없는데, 필드에 올바르지 않은 값이 들어가도 직접 접근 시 통제 불가능
    }
}

 

2) 필드 이름이나 자료형 변경 시

- Monster 클래스의 name 필드를 제거하고 kinds 필드를 추가하게 되면

  Monster 클래스를 사용하는 코드가 전부 에러가 발생하여 수정이 필요하다. - > 유지 보수에 최

 

(2) 캡슐화의 목적

- 유효성(논리적으로 올바르지 않은 값이 들어가는 걸 검사 가능)

- 유지보수성

 

(3) 캡슐화를 통해 문제 해결하기

* cf. 접근 제한자: 클래스/클래스의 멤버에 참조 연산자로 접근할 수 있는 범위를 제한하기 위한 키워드

  - 클래스의 멤버(필드, 메소드)에 4가지 모두 사용 가능

  - 클래스 선언 시에는 public, default만 사용 가능 

  1) public: 접근 제한 X

  2) protected: 동일 패키지 내 접근 허용 (상속 관계 시 다른 패키지 접근 허용)

  3) default: 동일 패키지 내 접근 허용 (작성하지 않으면 default)

  4) private: 해당 클래스 내부에만 접근 허용

public class Monster {
    // private을 이용해서 필드 직접 접근 제한
    private String kinds;
    private int hp;
    
    // public 메소드를 통해 간접 접근 허용
    public void setKinds(String kinds) {
        this.kinds = kinds; // this: 인스턴스 변수가 생성되었을 때 자신의 주소를 저장하는 레퍼런스 변수
    }
    
    // 메소드 내에 값을 검증하는 로직 추가
    public void setHp(int hp) {
        if(hp > 0) 
            this.hp = hp;
        else 
            this.hp = 0;
    }
    
    public String getInfo() {
        return "몬스터의 종류는 " + this.kinds + "이고, 체력은 " + this.hp + "입니다.";
    }
}

ㄴ> Monster 클래스 내부에서 변경이 일어나도 유지보수 시 사용자 호출 코드에서는 수정이 필요 없다

       -> 유지 보수성 향상

3. 추상화

: 유연성 확보를 위해 공통적인 것을 추출하고 공통적이지 않은 것을 제거하는 것

- 추상화 과정을 통해 객체가 도출되고, 객체를 생성하기 위해 클래스를 설계하게 된다.

(1) 행위 중심 추상화

(2) 데이터 중심 추상화(DTO)

- 객체지향에서 중요한 것은 행위(메소드)이지만

  데이터를 중심으로 추상화하여 객체 및 클래스를 설계하는 경우도 존재한다. (DTO 같은 클래스)

- 캡슐화 원칙에 따라 작성하지만

  캡슐화가 의미 없을 정도로 필드명을 그대로 사용한 getter, setter로 인해 유지보수성이 좋지 않다.

  하지만 미리 모든 필드에 접근 가능성을 염두에 두고 작성해두는 관례로 인해 현재도 많이 사용된다.

public class MemberDTO {
	/* 취급하려고 하는 회원정보를 고려해서 필드를 우선 작성.
	 * 주로 화면(UI) 혹은 데이터베이스 테이블을 기준으로 한다.
	 * 값 객체가 가지는 속성(필드)를 추출하는 과정 또한 추상화라고 볼 수 있다.
	 * DTO클래스를 만들기 위해서는 모든 필드를 private로 만든다.
	 * */
	
	private int number;				
	private String name;				  	
	private int age;					   
	private char gender;				
	private double height;			  
	private double weight;			  	
	private boolean isActivated;	
	
	// 설정자(setter)/접근자(getter)는 암묵적으로 통용되는 작성 규칙이 존재한다.
	public void setNumber(int number) {
		this.number = number;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public void setAge(int age) {
		this.age = age;
	}
	
	public void setGender(char gender) {
		this.gender = gender;
	}
	
	public void setHeight(double height) {
		this.height = height;
	}
	
	public void setWeight(double weight) {
		this.weight = weight;
	}
	
	public void setActivated(boolean isActivated) {
		this.isActivated = isActivated;
	}
	
	public int getNumber() {
		return number;
	}
	
	public String getName() {
		return name;
	}
	
	public int getAge() {
		return age;
	}
	
	public char getGender() {
		return gender;
	}
	
	public double getHeight() {
		return height;
	}
	
	public double getWeight() {
		return weight;
	}
	
	// boolean 접근자는 get으로 시작하지 않고 is로 시작하는 것이 일반적인 관례이다.
	public boolean isActivated() {
		return isActivated;
	}
}

 

4. 생성자

: 인스턴스 생성 시 초기 수행할 명령이 있는 경우 미리 작성해두고, 인스턴스 생성 시 딱 한 번 호출되는 함수

- new 클래스명(); 에서 클래스명은 생성자를 호출하는 구문이다.

- 기본 생성자는 컴파일러에 의해 자동으로 추가되기에 명시적으로 작성하지 않고 사용할 수 있다.

 

(1) 생성자 사용 목적

1) 인스턴스 생성 시점에 수행할 명령이 있을 때

2) 매개변수가 있는 생성자의 경우 매개변수로 전달받은 값을 필드 초기화할 때

3) 작성한 생성자 외에는 인스턴스 생성하는 방법을 제공하지 않는다 -> 인스턴스 생성 방법 제한, 초기값 전달 강제화

public class User {
	
	private String id;
	private String pwd;
	private String name;
	private java.util.Date enrollDate;
	
	// 1. 기본생성자(default constructor) 
	public User() {}

	// 2. 매개변수가 있는 생성자 
	public User(String id, String pwd, String name) {
		this.id = id;
		this.pwd = pwd;
		this.name = name;
	}
	
	public User(String id, String pwd, String name, java.util.Date enrollDate) {
		this(id, pwd, name);  
        /* this(): 동일 클래스 내의 다른 생성자 메소드를 호출하는 구문
           리턴되어 돌아오지만 리턴값은 존재하지 않으며 가장 첫 줄에 선언해야 한다.
           (생성자 내부에서 생성자 호출) */
		this.enrollDate = enrollDate;
	}
	
	// 3. 복사 생성자 
	public User(User otherUser) {
		this(otherUser.id, otherUser.pwd, otherUser.name, otherUser.enrollDate);
	}
}

public class Application {
	public static void(String[] args) {
        User user1 = new User();
        System.out.println(user1.getInformation());
        
        User user2 = new User("user01", "pass01", "홍길동");
        System.out.println(user2.getInformation());
        
        User user3 = new User("user02", "pass02", "이순신", new java.util.Date());
        System.out.println(user3.getInformation());
        
        User user4 = new User(user3);
        System.out.println(user4.getInformation());
    }
}

 

5. 오버로딩

: 같은 클래스 내에서 같은 이름의 메소드를 매개변수부만 다르게 하여 정의하는 것

- 매개변수의 타입, 개수, 순서가 달라야 한다.

 

* 오버로딩 사용 이유: 매개변수의 종류별로 메소드 내용을 다르게 작성해야 하는 경우가 종종 있는데,

  동일 기능의 메소드를 매개변수에 따라 다른 이름을 붙이면 관리가 어려워지기 때문

public void test() {}
	
//	public void test() {}            // 에러
	
//	private void test() {}           // 에러	// 접근제한자는 메소드 시그니처에 해당하지 않는다.
	
//	public int test() { return 0; }  // 에러남	// 반환형은 메소드 시그니처에 해당하지 않는다.
	
public void test(int num) {}             // 파라미터 선언부는 메소드 시그니처에 해당한다.

//	public void test(int num2) {}    // 에러 	// 매개변수의 이름은 메소드 시그니처에 영향을 주지 않는다.

public void test(int num1, int num2) {}

public void test(int num, String name) {}

public void test(String name, int num) {}

 

* 매개변수로 사용 가능한 자료형

1) 기본 자료형

2) 기본 자료형 배열

3) 클래스 자료형

4) 클래스 자료형 배열

5) 가변인자

- 인자로 전달하는 값의 개수가 정해져 있지 않은 경우 활용

- 매개변수가 몇 개 전달될 지 알 수 없으므로 반드시 매개변수 목록의 가장 뒤에 작성해야 함

- 가변인자를 사용한 메소드를 오버로딩하면 모호해지는 문제가 발생할 수 있음

// 가변인자
public void tesVariableLengthArrayParameter(String name, String... hobby) {
    System.out.println("이름: " + name);
    System.out.println("취미의 개수 : " + hobby.length);
    System.out.println("취미 : " + Arrays.toString(hobby));
}

public void testVariableLengthArrayParameter(String name, int... hobby) {
    System.out.println("이름 : " + name);
    System.out.println("취미의 개수 : " + hobby.length);
    System.out.println("취미 : " + Arrays.toString(hobby));
}

 

6. 변수의 종류

- 클래스에서 쓰이는 변수는 크게 클래스 변수, 인스턴스 변수(클래스의 필드), 지역 변수로 구분된다.

1) 클래스 변수: static 키워드를 가지고 필드에 선언하는 변수. 메모리의 static 영역 사용

2) 인스턴스 변수: static 키워드 없이 클래스의 필드에 선언하는 변수. 메모리의 heap 영역 사용

3) 지역 변수: 메소드, 생성자, 초기화 블록 내부에서 선언하는 변수. 다른 변수들보다 우선권을 갖는다.

class test {
	int a;             // 인스턴스 변수
    static int b;      // 클래스 변수 (static 변수)
    
    void method() {
    	int a = 0; // 지역변수
    }
}
변수 생성 시기 소멸 시기
클래스 변수 프로그램 시작 시 프로그램 종료 시
인스턴스 변수 인스턴스 생성 시 참조하지 않을 시 (GC 소관)
지역 변수 메소드 호출 시 메소드 종료 시

 

 

'Java' 카테고리의 다른 글

Java(6) 상속  (0) 2024.08.07
Java(5) final  (0) 2024.08.07
Java(3) API(Application Programming Interface)  (0) 2024.08.06
Java(2) 문자열 비교하기  (0) 2024.08.05
Java(1) 리터럴, 변수, 오버플로우, 언더플로우, 형변환  (0) 2024.08.05