새소식

TIL

[TIL] 240424 <자바> 클래스

  • -

 

[7. 클래스]

 

 

객체

  • 필드(속성) & 메소드(행위)
  • 객체 간의 관계 : 사용관계, 포함관계, 상속관계

 

1. 필드 - 객체의 데이터 저장 (고유데이터, 상태 데이터, 객체 데이터)

 

<필드 타입별 기본값>

데이터 타입 기본값
byte 0
char \u0000 (공백)
short 0
int 0
long 0L
float 0.0F
double 0.0
boolean false
배열 null
클래스 null
인터페이스 null

 

 

 

<필드 사용방법>

- 클래스는 설계도일 뿐 실제로 필드의 데이터를 가지고 있는 것은 객체이기 때문에 객체를 생성한 후에 필드 사용 가능

 

  • 외부 접근 : 도트(.)연산자 사용
    ex) Car car = new Car();  //Car클래스의 객체인 car 인스턴스 생성
          car.color = "blue";  //내부 필드에 접근


  • 내부 접근 : 객체 내부 메소드에서 객체의 필드 바로 호출해서 사용가능

 

 


 

2. 메소드 - 객체의 행위. 객체 간의 협력 위해 사용됨.

 

<매개변수> : 메소드 호출 시 메소드로 전달하려는 값 받기 위해 사용되는 변수 (없다면 생략가능)

- 기본형 매개변수 : 메소드 호출시 전달할 매개값으로 지정한을 메소드의 매개변수에 복사해서 전달 (원본값 변경X)

- 참조형 매개변수 : 메소드 호출시 전달할 매개값으로 지정한 값의 주소 메소드의 매개변수에 복사해서 전달 (값 변경가능)

package week03.parameter;

public class Main {
    public static void main(String[] args) {
    
        Car car = new Car(); // 객체 생성


        // <기본형 매개변수> : 원본값 변경X!!!
        char type = 'D';
        car.brakePedal(type);  //type값을 매개변수에 복사해서 전달! (메소드 외부 호출)

        // 메서드 실행 완료 후 전달할 매개값으로 지정된 type 값 확인
        System.out.println("type = " + type); // 기존에 선언한 값 'D' 출력 -> 원본 값 변경 X!!!
        // 메서드 실행 완료 후 반환된 car 인스턴스의 gear 타입 확인
        System.out.println("gear = " + car.gear); // 객체 내부에서 type을 변경하여 수정했기 때문에 'P' 출력

        
        
        
        
        System.out.println();
        // <참조형 매개변수> : 주소값 전달하기 때문에 원본 변경 발생!!!
        Tire tire = new Tire();  //타이어 객체 생성
        tire.company = "금호";   //필드 외부호출

        // 차 객체의 타이어를 등록하는 메서드 호출한 후 반환값으로 차 객체의 타이어 객체 반환
        Tire carInstanceTire = car.setTire(tire);  //tire값의 주소를 매개변수에 복사해서 전달!! (메소드 외부 호출)

        // 메서드 실행 완료 후 전달할 매개값으로 지정된 참조형 변수 tire의 company 값 확인
        System.out.println("tire.company = " + tire.company); // "KIA" 출력
        // 전달할 매개값으로 지정된 tire 인스턴스의 주소값이 전달되었기 때문에 호출된 메서드에 의해 값이 변경됨!!!

        // 메서드 실행 완료 후 반환된 car 인스턴스의 tire 객체 값이 반환되어 저장된 참조형 변수 carInstanceTire의 company 값 확인
        System.out.println("carInstanceTire.company = " + carInstanceTire.company); // "KIA" 출력
    }
}

 

 

 

- 가변 길이의 매개변수도 선언 가능

  • double … speeds처럼 … 을 사용하면 아래처럼 매개값을 , 로 구분하여 개수 상관없이 전달 가능
    • carSpeeds(100, 80);  //매개변수 2개
    • carSpeeds(110, 120, 150);  //매개변수 3개
// 가변 길이의 매개변수 선언 가능
void carSpeeds(double ... speeds) {  //speeds는 배열형태
    for (double v: speeds) {
        System.out.println("v = " + v);
    }
}

 

 

 

 

<메소드 호출 방법>

 

  • 외부 접근 : 객체 생성 후 도트(.)연산자를 사용하여 객체의 내부 메소드에 접근,
                      메소드가 매개변수를 가지고 있다면 반드시 호출할 때 매개변수의 순서와 타입에 맞게 매개값 넣어줘야함

  • 내부 접근 : 객체 내부 메소드에서 해당 객체의 다른 내부 메소드 호출 가능

 

package week03;

public class Main {
    public static void main(String[] args) {
        
        // new연산자를 통해 객체 생성되면 해당 인스턴스의 주소가 반환되기 때문에 해당 클래스의 참조형 변수를 사용하여 받아줄 수 있음
        // 객체는 참조형 변수와 동일하게 취급되기 때문에 배열 또는 컬렉션에도 저장하여 관리 가능
        Car[] carArray = new Car[3];  //객체 배열

        Car car1 = new Car();  //생성자 호출
        car1.changeGear('P');  //메소드 외부접근
        carArray[0] = car1;

        Car car2 = new Car();  //생성자 호출
        car2.changeGear('N');  //메소드 외부접근
        carArray[1] = car2;

        Car car3 = new Car();  //생성자 호출
        car3.changeGear('D');  //메소드 외부접근
        carArray[2] = car3;

        for(Car car: carArray){
            System.out.println("car.gear = " + car.gear);  //필드 외부접근
        }

    }
}

 

 

 

<메소드 오버로딩>

- 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메서드가 있더라도, 매개변수의 개수 또는 타입, 순서가 다르면 동일한 이름을 사용해서 메소드를 정의 가능

public class PrintStream extends FilterOutputStream
    implements Appendable, Closeable
{
			...
			
		public void println() {
        newLine();
    }

    public void println(boolean x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(char x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(int x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(long x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(float x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(double x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(char[] x) {
        if (getClass() == PrintStream.class) {
            writeln(x);
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(String x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

    public void println(Object x) {
        String s = String.valueOf(x);
        if (getClass() == PrintStream.class) {
            // need to apply String.valueOf again since first invocation
            // might return null
            writeln(String.valueOf(s));
        } else {
            synchronized (this) {
                print(s);
                newLine();
            }
        }
    }


		  ...
}

 

 

 


 

객체지향 프로그래밍의 특징

 

1. 캡슐화 : 필드와 메소드를 묶어 객체로 만든 후 실제 내부 구현 내용은 외부에서 알 수 없게 감추는 것

- 외부 객체에서 해당 필드와 메소드를 잘못 사용하여 객체가 변하지 않게 하기 위함

- 외부 객체에서는 노출시켜준 필드 혹은 메소드를 통해서 접근 가능

- 접근 제어자 사용함

 

 

2. 상속

- 객체간의 구조 파악 쉬움

- 필드와 메소드 변경하는 경우 부모 객체에 있는 것만 수정하면 자식 객체에 전부 반영되기 때문에 일관성 유지하기 좋음

- 자식 객체가 부모 객체의 필드와 메소드를 물려받아 사용하여 코드의 중복이 줄어들고 재사용성이 증가

 

 

3. 다형성 : 하나의 행위에 대해 각 객체가 가지고 있는 고유한 특성에 따라 다른 여러가지형태로 재구성되는 것

 

4. 추상화 : 객체에서 공통된 부분들을 모아 상위 개념으로 새롭게 선언하는 것

 

 

 

객체와 클래스의 관계

  • 객체를 만들기 위해서는 설계도에 해당하는 클래스 필요
  • 이때 클래스를 토대로 생성된 객체를 해당 클래스의 '인스턴스'
  • 동일한 클래스로 여러 개의 인스턴스 생성가능 (이 여러개의 인스턴스들을 크게 통틀어서 객체라고 표현가능)

 

 


 

클래스 설계

1. 클래스 선언

2. 클래스의 필드 정의

3. 클래스의 생성자 정의

4. 클래스의 메소드 정의

 

package week03;

public class Car {  //<1. 클래스 선언>

    //<2. 클래스의 필드 정의>

    //  (1) 고유 데이터 영역
    String company; // 자동차 회사
    String model; // 자동차 모델
    String color; // 자동차 색상
    double price; // 자동차 가격

    //  (2) 상태 데이터 영역
    double speed;  // 자동차 속도 , km/h
    char gear; // 기어의 상태, P,R,N,D
    boolean lights; // 자동차 조명의 상태

    //  (3) 객체 데이터 영역
    Tire tire;
    Door door;
    Handle handle;




    //<3. 클래스의 생성자 정의>
    //생성자 : 처음 객체가 생성될 때(instance화) 어떤 로직을 수행해야 하며, 어떤 값이 필수로 들어와야 하는지 정의!
    public Car() {
        //기본생성자 : 생략 가능
    }




    //<4. 클래스의 메소드 정의>
    double gasPedal(double kmh) {
        speed = kmh;  //필드 내부접근
        return speed;
    }

    double brakePedal() {
        speed = 0;  //필드 내부접근
        return speed;
    }

    char changeGear(char type) {
        gear = type;  //필드 내부접근
        return gear;
    }

    boolean onOffLights() {
        lights = !lights;  //필드 내부접근
        return lights;
    }

    void horn() {
        System.out.println("빵빵");
    }

    // 가변 길이의 매개변수 선언 가능
    void carSpeeds(double ... speeds) {  //speeds는 배열형태
        for (double v: speeds) {
            System.out.println("v = " + v);
        }
    }
}

 

 

 


 

인스턴스 멤버와 클래스 멤버

" 멤버 = 필드 + 메소드"

인스턴스 멤버는 객체 생성 후에 사용할 수 있고, 클래스 멤버는 객체 생성 없이도 사용가능

 

 

인스턴스 멤버

객체를 생성해야 사용 가능

  • 객체의 인스턴스 필드  → 각각의 인스턴스마다 고유하게 값 가질 수 있음
  • 객체의 인스턴스 메소드
    →  매번 저장한다면 중복 저장으로 인해 메모리 효율이 매우 떨어지기 때문에
         메소드는 메소드 영역에 두고서 모든 인스턴스들이 공유해서 사용
    → 
    대신 무조건 객체를 생성,  즉 인스턴스를 통해서만 메서드가 사용될 수 있도록 제한을 걸어둔 것

 

 

클래스 멤버

  • 클래스는 Java의 클래스 로더에 의해 메서드 영역에 저장되고 사용됨
  • 클래스 멤버란 메서드 영역의 클래스와 같은 위치에 고정적으로 위치하고 있는 멤버를 의미.
  • 따라서 객체의 생성 필요 없이 바로 사용 가능

 

 

<클래스 멤버 선언> : static 사용

  • 인스턴스마다 모두 가지고 있을 필요 없는 공용적인 데이터를 저장하는 필드는 클래스 멤버로 선언하는 것이 좋음
  • 인스턴스 필드를 사용하지 않고 실행되는 메서드가 존재한다면 클래스 메소드로 선언하는 것이 좋음

 

인스턴스 멤버로 선언된 메소드는 클래스 멤버 사용 가능

// 클래스 필드
static String company = "GENESIS"; // 자동차 회사 : GENESIS

// 인스턴스 메소드
String getCompany() {
    return "(주)" + company;  //인스턴스 메소드에서 클래스 필드인 company 사용 가능
}

 

클래스 멤버로 선언된 메소드는 인스턴스 멤버 사용 불가!!!

//클래스 메소드
static String setCompany(String companyName) {

	// 클래스 메소드에서 인스턴스필드 model 사용 불가!!!
    // System.out.println("자동차 모델 확인: " + model);
    company = companyName;
    return company;
}

 

 

 

 

<클래스 멤버 사용> : 클래스의 이름과 함께 도트(.) 연산자를 사용하여 바로 접근!

package week03.staticFolder;

public class Car {

    //클래스 필드 (static으로 표현)
    static String company = "GENESIS"; // 자동차 회사 : GENESIS


    //클래스 메소드 (static으로 표현)
    static String setCompany(String companyName) {
        // System.out.println("자동차 모델 확인: " + model); // 인스턴스 필드 사용 불가
        company = companyName;
        return company;
    }
}

 

package week03.staticFolder;

public class Main {
    public static void main(String[] args) {

        // 클래스 필드 company 확인
        System.out.println(Car.company + "\n");  //바로 접근가능
        // 클래스 필드 변경 및 확인
        Car.company = "Audi"; //바로 접근가능
        System.out.println(Car.company + "\n"); //바로 접근가능



        // 클래스 메서드 호출
        String companyName = Car.setCompany("Benz"); //바로 접근가능
        System.out.println("companyName = " + companyName);





        // 아래처럼 클래스 멤버를 굳이 참조형 변수를 사용하여(객체를 생성하여) 접근할 필요 없음
        System.out.println();
        // 참조형 변수 사용
        Car car = new Car(); // 객체 생성

        car.company = "Ferrari";
        System.out.println(car.company + "\n");

        String companyName2 = car.setCompany("Lamborghini");
        System.out.println("companyName2 = " + companyName2);
    }
}

 

 

 

상수 : 값이 한개이며 불변이라서

인스턴스마다! 상수를 저장할 필요가 없으므로

static final String COMPANY = "GENESIS";

과 같이 static final을 사용하여 한개이며 불변인 상수 선언 가능 (final만 사용하면 그냥 수정 불가)

(상수는 대문자로 작성하는 것이 관례임)

 

 

 


 

this : 인스턴스 자신

public Car(String model, String color, double price) {
    this.model = model;
    this.color = color;
    this.price = price;
}
// this는 인스턴스 자신을 의미하기 때문에
// 객체의 메소드에서 리턴 타입이 인스턴스 자신의 클래스 타입이라면
// this를 사용하여 인스턴스 자신의 주소를 반환할 수 있음!

Car returnInstance() {
    return this;
}

 

 

 

this() : 인스턴스 자신의 생성자 호출

public Car(String model) {
    this(model, "Blue", 50000000);
}

public Car(String model, String color) {
    this(model, color, 100000000);
}

public Car(String model, String color, double price) {
    this.model = model;
    this.color = color;
    this.price = price;
}

this() 키워드를 사용해서 다른 생성자를 호출할 때는 반드시 해당 생성자의 첫 줄에 작성되어야함!

 

 


제어자

하나의 대상에 여러 개의 제어자를 조합해서 사용할 수 있으나, 접근 제어자는 단 하나만 사용가능

  • 접근 제어자 : public, protected, default, private
  • 그 외 제어자 : static, final, abstract

 

 

<사용 가능한 제어자>

- 클래스 : public, default, final, abstract
- 메서드 : public, protected, default, private, final, abstract, static
- 멤버 변수 : public, protected, default, private, final, static
- 지역변수 : final

 

<⚠️ 제어자 사용 시 주의 사항>

  • 메서드에 staticabstract를 함께 사용 불가!
  • 클래스에 abstractfinal을 동시에 사용 불가!
  • abstract메서드의 접근 제어자가 private일 수 없음!
  • 메서드에 privatefinal을 같이 사용할 필요는 없음!

 

 


 

접근제어자

클래스 내부에 선언된 데이터 보호위해 사용(캡슐화), 유효한값 유지하고 함부로 변경못하도록 접근제한

  • public : 접근 제한이 전혀 없음
  • protected : 같은 패키지 내에서, 다른 패키지의 자손 클래스에서 접근이 가능
  • default : 같은 패키지 내에서만 접근 가능
  • private : 같은 클래스 내에서만 접근 가능

 

 

<사용 가능한 접근 제어자> 

- 클래스 : public, default
- 메서드 & 멤버 변수 : public, protected, default, private
- 지역변수 : 없음

 

 

<객체의 private필드 접근방법>

  • Getter 메소드 : 외부에서 객체의 private 필드읽어야 할 때

      - get + 필드 이름(첫 글자 대문자)

//private 필드들
private double speed;  // 자동차 속도 , km/h
private char gear = 'P'; // 기어의 상태, P,R,N,D
private boolean lights; // 자동차 조명의 상태
public String getModel() {
    return model;
}

public String getColor() {
    return color;
}

public double getPrice() {
    return price;
}

 

  • Setter 메소드: 외부에서 객체의 private 필드저장/수정해야 할 때

      - set + 필드 이름(첫 글자 대문자)

public void setModel(String model) {
    this.model = model;
}

public void setColor(String color) {
    this.color = color;
}

public void setPrice(double price) {
    this.price = price;
}
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.