본문 바로가기

카테고리 없음

[ Java ] 클래스에 관한 공부기록

클래스는 설계도와 같다. 설계도는 실체가 아니다. 예를 들어 집을 짓기 전에 집을 어떻게 지을 것인가에 대해 설계를 한다. 방, 거실, 화장실 등 위치와 개수를 대략적으로 그림을 그려 집이 완공된다면 이런 구조를 갖을 것이라고 "머리속에 그려볼 수 있는 것", (눈으로 완공된 모습을 직접 확인해볼 수 없지만 대략적으로 알 수 있는) 추상적인 것이 설계, 즉 클래스다. 클래스(설계도)에 기반하여 → 클래스를 토대로 실체를 만들어낸다면 그것은 객체다. 그리고 객체를 참조하는(가리키는) 참조변수가 인스턴스같다. 클래스(설계도)에는 설계 대상(설계도를 기반으로 만드는 실체)이 갖는 속성과 메서드(기능)가 선언(및 정의)되어 있는데, 이를 멤버(속성과 메서드)라고 한다. 객체가 지니는 속성은 객체의 특징을 나타내는 것으로 그 특징을 외부에서 쉽게 접근하여 변경하면 문제가 발생할 수 있기에 속성들은 private 접근제한자를 설정하여 (속성의) 상태(값)를 보호한다. private 접근제한자가 붙은 속성은 외부에서 쉽게 접근할 수 없는데 getter, setter메서드를 통해 해당 객체의 속성 값을 얻거나 설정할 수 있다.

 

클래스 다이어그램을 표현한 UML(설계도) ---> 클래스의 상세정보를 볼 수 있는 클래스 다이어그램

//  클래스명 작성하는 곳
TravelClub
// 필드 작성하는 곳

// -(마이너스 기호)는 private을 의미
// static final로 정의되어 있는 필드에는 밑줄이 그어져 있다.
-MINUM_NAME_LENGTH
-id: String
-clubName: String // clubName이라는 변수는 타입이 String(문자열)이며 private 접근제한자를 갖는 필드다.
// 메서드 작성하는 곳
+toString(): String // toString() 이라는 메서드는 String타입을 반환하며 public 접근제한자를 갖는다.

 

필드 선언시 접근제한자는 보통 private으로 선언한다.

클래스에 작성하는 field는 아주 특별한 경우가 아니라면 private으로 설정하여 외부의 접근(데이터의 보호)을 막아야 한다.

외부에서 임의로 필드의 값을 바꾸기보다는 해당 클래스 안에서 해당 필드의 값을 바꾸어야 한다.

그래서 setter, getter메서드를 클래스에 정의해서 외부에서 간접적으로 접근하여 필드의 값을 바꾸는 로직을 갖추어야 한다.

클래스의 필드의 값 변경에 제약조건을 걸어야 하는 경우, 예를 들면 사람의 나이를 설정할 때, -1살은 없으니까

private int age; 로 클래스에 작성을 하고, 나이를 설정하는 setter 메서드에 제약조건을 작성해준다.

 

// 클래스에 정의하는 필드의 값은 외부에서 직접적으로 접근해서
// 값을 변경하지 못하게 설정하는 것이 보편적이다.
// 예를 들면 사람 나이의 경우 0 또는 음수인 경우가 없다. --> 현재는 법 제정으로 인해 0살부터 시작함.
// 그래서 사람의 나이를 외부에서 직접적으로 접근해서 설정하는 경우 음수를 대입해도 설정이 되기 때문에
// setter 메서드를 통해 간접적으로 설정할 수 있도록 설정, setter 메서드에는 내부적으로 제약조건을 작성

public class Person {
	private String name;
    private int age;
    
    public Person() {
    	// 기본생성자
    }
    
    public Person(String name, int age) {
    
    
    	// 매개변수를 받는 생성자
        this(); // --> 기본생성자 호출
        this.setName(name); 
        // 여기서 this는 기본 생성자로 힙(heap) 영역에 생성된
        // Person 인스턴스를 참조하는[가리키는] 주소값을 의미한다.
        // Person 인스턴스가 위치한 주소값을 따라가면 Person 인스턴스를 만날 수 있고,
        // 만나서 Person 인스턴스가 갖고 있는 setAge(int age) 메서드를 호출한다.

        this.setAge(age);
    }
    
    // 외부에서 해당 인스턴스의 필드가 지닌 값을 간접적으로 얻을 수 있는 수단 --> getter 메서드
    public String getName() {
    	return name;
    }
    
    public String getAge() {
    	return age;
    }
    
    // 외부에서 해당 인스턴스의 필드가 지닌 값을 간접적으로 설정할 수 있는 수단 ---> setter 메서드
    // setter 메서드 내부에는 제약조건이 있다.
    public void setAge(int age) {
   		// 외부에서 사람의 나이를 설정하려고 간접적으로 접근했는데
        // 나이를 설정하는 수가 0보다 작다면
        // 리턴한다 --> 설정을 반려한다는 것 --> 반려가 된다면 결국 필드의 값은 변경되지 않는다.
   		if(age < 0 ) { return; }
        this.age = age;
    }
}

 

날짜관련 클래스 및 메서드

 

Date: 날짜와 시간정보를 저장하는 클래스

Date 객체는 잘 쓰지 않지만 정리해보자면 Date 객체 생성시

public Date() {
        this(System.currentTimeMillis());
}

 

현재 시간을 밀리세컨드(1000분의 1초) 단위의 시간으로 설정되는데,

January 1, 1970, 00:00:00  가 기준이 된다. 현재의 시간으로부터 1970년 1월 1일 00:00:00 초를 뺀 값(밀리초 단위로 계산)

이 Date() 객체가 갖는 현재 시간의 정보가 되는 것이다. 이는 System.currentTimeMillis(); 이 메서드의 결과 값과 같다.

 

SimpleDateFormat은 DateFormat 클래스를 상속한 클래스로 ---> public class SimpleDataFormat extends DateFormat { ...}

지역설정에 맞춘 방식으로 ( in a locale-sensitive manner ) Date 객체를 text(글자, 문자열타입을 말하는 것 같음) 로, text를 Date타입으로 변환하는 기능을 제공한다.  사용자가 원하는 형식으로 날짜와 시간을 표현할 수 있는 커스터마이징 기능도 있다. 

Formatting 한다는 것 ---> Date타입의 객체를 원하는 형식에 맞게 텍스트타입으로 변환하는 것,

Parsing 한다는 것 ---> 문자열로 표현된 날짜 정보를 분석해서 Date타입의 객체로 변환하는 것을 의미한다.

SimpleDataFormat은 스레드의 안정성 때문에 자바8 이후에는 DateTimeFormatter를 사용하는 것이 좋다는데?

 

Calendar: 달력을 표현한 클래스 --> 연, 월, 일, 오전/오후 시간 등 정보를 제공하는 클래스다.

Calendar 클래스는 추상클래스(abstract class)다 --> Calendar 클래스를 구현한 구현클래스를 사용하여 객체를 생성한다.

 

추상클래스는 추상클래스를 상속한 클래스(구현 클래스)로부터 객체 생성을 할 수 있다.

상속한 클래스는 추상클래스의 모든 메서드를 구현했기 때문에 추상클래스타입의 참조변수가

구현 객체의 메서드를 가리켜

Calendar 추상클래스가 가진 날짜 관련 기능을 모두 사용할 수 있다.

 

Calendar cal1 = new GregorianCalendar();
Calendar cal2 = Calendar.getInstance();

// 두 가지 방법으로 Calendar 추상클래스를 구현한 달력 객체를 생성할 수 있는데
// 직접적으로 new GregorianCalendar() 객체를 생성하는 것보다
// 현재 지역에 맞는 구현클래스로부터 객체를 자동적으로 생성해주는 Calendar.getInstance() 로
// 생성하는 것이 좋다.

 

자바에서 문자열을 다루는 대표적인 클래스 ---> String, StringBuffer, StringBuilder

StringBuffer / StringBuilder 클래스는 문자열을 연산(추가하거나 변경) 할 때 주로 사용하는 자료형

String 자료형만으로도 +연산이나 concat() 메서드로 문자열을 이어 붙일 수 있다.

단, String타입 문자열을 +연산이나 concat() 메서드로 이어 붙인다면 내용이 합쳐진 새로운 String 인스턴스가 생성되어

문자열을 결합하면 결합할수록 힙영역?에서의 공간의 낭비가 발생한다. 공간이라는 자원을 사용함으로써 공간의 낭비뿐만 아니라

성능이슈(실행 속도가 느려짐)가 발생한다.---> 공간의 낭비(자원의 낭비)와 성능이슈 발생의 단점을 보완하려면 StringBuffer / StringBuilder 클래스를 사용하는 것이 적합 ---> StringBuffer 클래스는 내부적으로 버퍼(buffer)라는 독립적인 공간을 갖고(동일 객체 내에서 문자열의 크기 변경이 가능하다는 것, String은 불변이다), 문자열을 바로 추가할 수 있어 공간의 낭비와 문자열 연산속도가 매우 빠르다. StringBuffer 클래스와 StringBuilder 클래스는 기능이 거의 동일하지만 문자열 파싱(분석) 성능면에서는 StringBuilder가 더 우수하다. 문자열의 추가, 수정, 삭제가 빈번하게 발생하는 경우 StringBuilder를 사용하는 것이 좋다.

 

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();

        builder.append("클럽명: ").append(name);
        builder.append(", 소개: ").append(intro);
        builder.append(", 개설일: ").append(foundationDay);
        builder.append(", 멤버: ").append(members.toString());

        return builder.toString();
    }

 

TravelClub 인스턴스가 갖고 있는 정보를 나타내는 toString() 메서드에서

+연산으로 문자열 결합을 수행한다면 힙영역에 새로운 문자열 인스턴스가 많이 생성될 것이다.

---> 공간의 낭비와 정보의 출력 속도가 느려지는 문제점 발생 ---> StringBuilder로 모든 문자열을 결합한 후,

마지막에 String타입으로 문자열을 한 번에 반환함으로써 많은 수의 String타입 문자열 연산에 대한 단점을 보완한 점을 알게되었다.