새소식

TIL

[TIL] 240424 <자바> 클래스

  • -

 

 

 

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

 

 

<필드 타입별 기본값>

데이터 타입 기본값
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";  //내부 필드에 접근


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

 

 


 

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

- 기본형 매개변수 : 메소드 호출시 전달할 매개값으로 지정한을 메소드의 매개변수에 복사해서 전달 (원본값 변경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만 사용하면 그냥 수정 불가)

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

 

 

 


 

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

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

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