클래스 (Class)

2023. 11. 28. 01:03
728x90

자동차를 생각해보자.

> 자동차에는 가속 페달을 밟고, 엔진은 기름을 공급받아 동력을 생산하고, 엔진에서 나온 동력을 적정 수준으로 변환하고, 바퀴로 전달하는 변속기 등이 있다. 

~~ 여러가지 도구, 재료, 사용자, 기계와 같은 "객체"들이 존재한다. 

 

1. 객체와 클래스

자동차이기 위해 가져야하는 정보와, 기능...에 대한 정보를 담는 것이 클래스.

 

- 기본적으로 자바의 기능 수행을 위한 코드는 클래스 내부에만 존재함. 

 

ex.

- Car 라고 하는 class

public class Car {
// 자동차의 설계도(?)
    public String brand; // 브랜드
    public String name; // 차종
    public int fuel; //탱크상태 
    
    public void beep() { // 클락션을 울리는 기능 (메서드)
    	System.out.println("빵~");
    }   
    public void printInfo() { // 자동차 정보를 모두 출력하는 기능
    	System.out.println(String.format("name:\t%s", name));
        ... 
    }
}
  • 속성 (attribute) : 하나의 객체가 가질 수 있는 성질 또는 데이터 정의. 
  • brand, name, fuel ...
  • 메서드 : 반복해서 활용할 기능들을 모아놓는 역할. 

- Car 객체를 만듦. (Car class를 바탕으로 만들어진 객체!) = 클래스의 인스턴스.

  • 속성에는 '.' 을 이용해 접근 가능
  • 속성에도 데이터 할당 가능. 
  • 값을 가져올 때도 '.'을 활용해 가져올 수 있음. 
  • 객체의 메서드를 호출할 때도 '.'을 활용해 호출 가능. 
public static void main(String[] args) {
	Car myCar = new Car(); // Car 객체 생성
    
    // Car class의 속성에 데이터 할당하기
    myCar.name = "K5";
    myCar.brand = "Kia";
    myCar.fuel = 72;
    
    // 메서드도 사용 가능
    myCar.beep();
    myCar.printInfo();
}
즉, Car는 클래스 (~설계도~)
myCar는 Car의 인스턴스인 객체 (~실체~)

 

** 정적(static) 제어자

: 속성이나 메서드 앞에 static 추가하면됨.

정적 메서드/ 정적 변수는 만들어진 인스턴스에 소속되는 것이 아니라~ 클래스 자체에 소속됨!

~~> 그러므로 인스턴스를 굳이 만들지 않고도 바로 접근 가능. 

  • 정적 변수 : 모든 객체를 만드는데 필요한 상수나 설정, 또는 전체 몇개의 객체가 만들어졌는지 세는 용도로 많이 활용.
  • 정적 메서드 : 단순기능을 모아두는 클래스에서 굳이 인스턴스를 만들지 않고도 활용할 수 있도록 하는 용도로 많이 활용.
public class Car {
	public static int count = 0;
    public String name;
    ...
}

public static void main(String[] args) {
	Car myCar = new Car();
    
    Car.count++; //static 속성이므로 객체.count를 사용
    myCar.name = "K5";
}

 

2. 접근 제어자

: 속성이나 메서드에 덧붙여서 속성과 메서드에 접근에 대한 권한을 조절.

- public : 아무나 사용가능

- private : 클래스 내부에서만 사용 가능. 

~!~ 정보 은닉 (Information Hiding) : 접근 제어자를 통해 객체의 구체적인 정보 노출을 방지한다. ~!~

~!~ 캡슐화 (Encapsulation) : 기능과 정보를 하나의 클래스로 묶어놓는 개념. ~!~

 

public class Car {
    private String name;
    private String brand;
    private int fuel;

    public void printInfo() {
        System.out.println(String.format(
                "name:\t%s\nbrand:\t%s\nfuel:\t%s",
                name, brand, fuel
        ));
    }
}

public static void main(String[] args) {
    Car someCar = new Car();
    someCar.name = "K5"; // 불가능
    someCar.brand = "Kia"; // 불가능
    someCar.fuel = 72; // 불가능
    someCar.printInfo(); //가능 (public)
}

 

그러나, 

무작정 다 접근하지 못하게 한다면 ? 너무 한정적인 상황이 된다.....

private일 경우 다른 클래스에서는 변경도 못하고, 확인도 불가능하게 됨. ㅠㅡㅠ

원래 목적은 클래스 밖에서 그 Car의 name, brand, fuel을 변경하지 못하게 하지만, 그래도 외부에서 확인은 가능하게 하고싶었다...

 

>> 그럴 땐 어떻게 해야할까?

~~> public 접근 제어자를 가진 메서드를 만들어 해당 메서드가 private 속성을 반환하게 만들 수 있다.~ 

이를 쉽게 할 수 있는 메서드는? GETTER & SETTER 메서드 > 해당 속성을 직접적으로 다룰 때 사용하는 것이다. 

 

public class Car {
    private String name;
    private String brand;
    // ...
    public String getName() {
        return name;
    }
    public String getBrand() {
        return brand;
    }
    // ...
}

public static void main(String[] args) {
	Car someCar = new Car();
	System.out.println(someCar.getBrand());
	System.out.println(someCar.getName());
}

 

Getter메서드를 쓰면 ? private name과 brand를 확인 가능하게 만든다!

 

+ Setter : private fuel을 변경하고 싶다면....? setter를 사용해보자

* this :  객체 자신을 가르키는 키워드

public void setFuel(int fuel) {
	this.fuel = fuel;
}

 

- 생성자 (Constructor)

: 객체를 생성할 때 호출하는 특수한 메서드. 

- 생성자 메서드 이름은 클래스와 동일하게 만듦.

- 따로 만들지 않을 시 아무 정보도 받지 않는 기본 생성자가 만들어진다. 

- 생성자를 만들면 기본 생성자는 사라짐. 

- 생성자도 메서드 오버로딩이 된다!

+ 한번 설정되면 변하지 않도록 만들기 위해서 final을 붙일 수도 있다. 

private final String name; > 다시 변경이 불가능하다.

public class Car {
	private static int count = 0;
    private String brand;
    private String name;
    private int fuel;
    
    //생성자 
    public Car() { // 기본 생성자
    	count++;
    }
    
    //변수를 넣은 생성자
    public Car(String brand, String name, int fuel) {
    	this.brand = brand;
        this.name = name;
        this fuel = fuel;
        count++;
    }

=> 이렇게 만들어진 생성자는 new 키워드와 함께 활용 가능.

 

 

3. 자바의 데이터 저장

클래스.. 객체... ? 메모리의 어디에 저장될까...? 결국 모두 비트로 표현되고,,,, 어디에 어떤식으로 저장될지가 궁금하다! 

 

 

>> Stack Memory (LIFO, Last In First Out)

  • 모든 메서드 (main 포함)는 호출stack memory에 필요한 공간을 할당받음. > 프레임 단위로 올라간다.
  • 메서드가 종료되면, 프레임이 stack에서 사라지고, 모든 값이 사라진다.
  • 원시타입(Primitive Type)은 stack memory의 공간에 데이터가 직접 들어간다.
  • 원시타입? : int, char, boolean, byte, short, long, float, double

 

 

>> Heap Memory 

  • 클래스 인스턴스를 비롯한 객체들은 Stack에 직접 데이터를 작성하지 않음.  > Heap space라는 별도의 공간에 저장.
  • 메서드의 호출 및 종료와 상관 없이 객체의 데이터가 저장되어 있다. 
  • 총 공간이 stack에 비해 더 크지만, 공간할당에 걸리는 시간이 더 길다. 
  • 원시타입 제외한 모든 참조 타입들(String, 배열, interface 등)이 heap memory에 저장.
  • new 키워드를 만나면 heap상에 객체(클래스의 인스턴스 객체)를 만듦. + 이때 생성된 객체가 어디있는지(Reference)를 stack 변수에 저장함.
  • 객체 내에 또다른 참조 타입이 있다면 그것도 heap에 생성
  • 더이상 객체를 참조하는 변수가 없다면 Garbage Collector가 공간을 비워줌.

객체 내에 또다른 참조 타입이 있다면 그것도 heap에 생성
더이상 객체를 참조하는 변수가 없다면 Garbage Collector가 공간을 비워줌.

 

 

유연성이 필요한 객체 저장을 위해서는 Heap Space 사용하고,
전체적인 코드 수행 및 메서드 메모리 관리를 위해 Stack Memory 사용한다.

 

 

** Pass by Value vs Pass by Reference

: 메서드를 호출할 때 인자가 전달되는 방식

- pass by value : 전달된 인자의 값만 복사하는 방식.

인자로 전달한 데이터가 사실 실제 데이터가 아니라 그 데이터와 동일한 "값"이 복제된 것이므로, 함수 내부에서 이 값을 변경한다해도, 함수 밖의 변수에는 영향이 없다.

- pass by reference : 전달된 인자가 실제로 존재하는 공간을 전달하는 방식. 

인자로 전달한 데이터가 존재하는 공간이 어딘지가 전달되기 때문에 함수 내부에서 해당 값을 조작하면, 실제로 그 데이터가 조작된 것이므로 함수 바깥에서도 변화가 유지된다.

 

 

자바는...? 100% pass by value... 값만 전달할 뿐!!!! 

int a = 10;
int b = 20; 
swap(a,b);
System.out.println(a); //10
System.out.println(b); //20

public static void swap(int a, int b) {
	int tmp = a;
    int a = b;
    int b = tmp;
}

 

~~> BUT!! 진짜 의미는 전달된 인자가 같은 곳에 있는지가 중요! 

public static void main (String[] args) {
	Car someCar = new Car("K5", "Kia", 72); // someCar는 Stack의 main 프레임에 있음.
    someCar.drive(10);
    refuel(someCar);
    System.out.println(someCar.getFuel()); //결과값 : 100
}

public static void refuel(Car car) { // car는 Stack의 refuel 프레임에 있음. (refuel메서드 호출)
	car.setFuel(100);
}

// 그러나, 전달한 값은 someCar가 Heap에 존재하는 위치... > 이 객체가 someCar 객체와 같다.

 

 

** 결론 : 

기술적 의미에서는 pass by value가 맞지만 

인자로 전달된 객체는 결국 같은 객체를 사용한다...!

현상을 보고 기술이 어떨지에 대한 선입견을 가지지 말고, 본질을 배우려고 노력하자. 

 

 

4. String 및 Wrapper class

1) String

: 참조 타입.

- 유틸리티 메서드 사용 가능 (ex. String.format)

 

 

2) 원시 타입 Wrapper class

: 원시 타입 자료형들을 객체지향적 관점에서 사용할 수 있도록 동일한 데이터를 들고 있을 수 있는 wrapper class를 제공. 

- 본래의 원시타입들을 들고 있으면서 각 자료형을 다룰 때 유용한 메서드와 상수를 제공.

- 데이터 할당도 가능 (할당 연산자 등 활용)

- Autoboxing and unboxing : 원시타입과 호환된다 

메서드 기능
Integer.parseInt(String s)  문자열이 나타내는 정수 반환
Double.parseDouble(String s)  문자열이 나타내는 실수 반환
Character.isDigit(char ch) 문자가 숫자를 나타내는지 확인
Character.isLetter(char ch) 문자가 글자를 나타내는지 확인
string.length() 문자열의 글자수 반환
string.substring(int beginIndex) 문자열을 beginIndex부터 자른 문자열을 반환
string.charAt(int index) 문자열의 index 위치의 char를 반환
string.indexOf(String str) 주어진 문자열이 시작하는 index 반환
string.split(String regex) 주어진 정규표현식을 기준으로 문자열을 나눠 배열로 반환

 

728x90

'Programming > Java' 카테고리의 다른 글

예외처리 (Exception Handling)  (1) 2023.11.28
객체지향 프로그래밍  (1) 2023.11.28
메서드 (Methods)  (1) 2023.11.23
제어문 (Control Statements)  (1) 2023.11.22
연산자 (Operators)  (0) 2023.11.21

BELATED ARTICLES

more