클래스와 객체

Sirius·2023년 11월 4일

1. 객체지향과 자바

우리 주변의 모든것이 객체이다. 객체 지향 언어는 클래스와 객체개념을 도입하였다.

//클래스
class Animal{
	String name; //필드
    int age; //필드
    void eat(){...}; //메소드
    void speak() {...}; //메소드
    void love() {...}; //메소드

}

1) 자바의 3가지 객체지향 원칙

가. Encapsulation

객체를 캡슐로 싸서 그 내부를 보호하고 볼 수 없게함(실제 구현을 외부로부터 숨김)
그러나 외부와의 접속을 위해 몇 부분만을 공개 노출한다.
(public and private)

  • 객체지향 관련 용어정리
    1) 클래스: 틀, 프레임(캡슐화의 역할)
    ex> 붕어빵 틀

    2) 객체: 실제로 찍힌값(메모리에 할당된값)
    ex> 치즈붕어빵, 초코붕어빵, 슈크림붕어빵

    3) 인스턴스: "어떤 클래스의 인스턴스다"라고 표현 = 객체랑 똑같음
    ex> 붕어빵틀의 치즈붕어빵이다.

    4) 필드: 객체의 상태와 같은 개념 (차의 색깔)

    5) 메소드: 객체의 동작(함수)

나. Inheritance

상속 : 상위 개체의 속성이 하위 개체에 물려져서 하위 개체가 상위 개체의 속성을 모두 가지는 관계
-> 코드 재사용성 향상 및 유지보수 시간 최소화

class Animal{
	String name;
    int age;
    void eat(){}
    void speak(){}
    void love(){}

}
class Human extends Animal{
	String hobby;
    String job;
    void work() {...}
    void cry() {...}
    void laugth() {...}
}

Human클래스는 Animal 클래스를 상속받아서, age필드 love()메소드 등을 똑같이 가진다.(다시 선언할 필요X)

다. Polymorphism

같은 이름의 메소드가 클래스 혹은 객체에 따라서 다르게 동작함

1> 메소드 오버로딩

메소드 오버로딩 : 클래스 내에서 같은 이름의 메소드를 여러개 만든다.
각각은 다른 파라미터를 가진다.
-> 메소드 오버로딩은 컴파일 시간에 결졍됨(static polymorphism)

2> 메소드 오버라이딩

메소드 오버라이딩: 슈퍼클래스에서 구현된 메소드를 서브 클래스에서 자신의 이름에 맞게 동일한 이름으로 다시 구현하는 것
-> 메소드 오버라이딩은 런타임에 결정됨(dynamic polymorphism)

2) 객체지향언어의 목적

가. 소프트웨어의 생산성 향상

  • 컴퓨터산업이 발달함에 따라 소프트웨어의 생명주기가 짧아짐
  • 이미만든 SW를 상속받거나 재사용하기가 쉬움(부분수정)

나. 실세계에 쉬운 모델링(실세계 반영이 쉬움)

실세상의 물체를 객체로 표현하고, 이들사이의 관계, 즉 상호작용을 프로그램으로 나타냄.

  • 객체지향 프로그래밍
    1) 객체추출
    2) 객체들간의 관계 결정
    3) 상호작용에 필요한 메소드나 필드를 설계 및 구현

2. 자바 클래스

1) 자바 클래스 구성

Field, Consturctor, Method 3가지로 구성된다.

2) 객체 생성과 활용

public class Circle{
	int radius;
    String name;
    
    public double gertArea(){
    	return 3.14*radius*radius;
    }
    
    public static void main(String[] args){
    	Circle pizza;
        pizza = new Circle();
        pizza.radius = 10;
        pizza.name = "자바피자";
        double area = pizza.getArea();
        System.out.println(pizza.name + "의면적은" + area);
    }
}
  1. 객체에 대한 레퍼런스 변수 선언 : Circle pizza;
  2. 객체 생성: pizza = new Circle();
  3. new 연산자는 어딘가에서 Circle타입의 크기 만한 메모리 할당받아 그 메모리에 대한 주소를 pizza에 리턴한다. : Circle pizza = new Circle();

3) 생성자(메소드)

가. 생성자의 개념과 목적

생성자는 객체개 생성될 때 초기화를 위해 실행되는 메소드이다.
-> 생성자는 '화장'과 비슷하다. 맨얼굴(얼굴객체)

public class Circle{
	int radius;
    String name;
    
    
    public Circle() {
   		radius=1; name="";
    }
    
    public Circle(int r, String n){
    	radius = r; name = n;
    }
    
    public double gertArea(){
    	return 3.14*radius*radius;
    }
    
    public static void main(String[] args){
    	Circle pizza = new Circle(10, "자바피자");
        double area = pizza.getArea();
        System.out.println(pizza.name + "의면적은" + area);
        Circle donut = new Circle();
        donut.name = "도넛피자";
        area = pizza.getArea();
        System.out.println(donut.name + "의면적은" + area);
        
    }
}
  • 생성자 법칙
  1. 생성자의 이름은 클래스 이름과 동일하다.(다른 메소드와 쉽게 구분할 수 있음)

  2. 생성자를 여러개 작성할 수 있다.(매개변수의 개수와 타입이 다름)

  3. 생성자는 객체를 생성할 때 한번만 호출된다.(객체 생성시 자동으로 호출)

  4. 생성자에 리턴타입 지정할 수 없음(리턴 없음)

나. 기본 생성자

매개변수가 없고 실행코드도 없음

class Circle{
	public Circle() {}
}

컴파일러는 생성자가 하나도 없으면 바이트코드 파일(*.class)에 기본 생성자를 삽입해서 기본생성자가 호출되게 한다
그러나 기본생성자외에 생성자가 하나라도 있으면 기본생성자를 삽입해주지는 않음(그 이미 있는 생성자를 호출한다)

다. this 레퍼런스(생성자 코드의 첫문장!)

객체 자신에 대한 레퍼런스임(객체가 있어야 this도 의미가 있음)
메소드 안에서 사용된다.

public class Book{
	String title;
    String author;
    
    public Book(){
    	this("", "");
        System.out.println("생성자 호출됨");
    }
    
    public Book(String title){
    	this(title, "작자미상");
    }
    
    public Book(String title, String author){
    	this.title = title;
        this.author = author;
    }
    public static void main(String[] args){
    	Book littlePrince = new Book("어린왕자", "생텍쥐페리");
        Book loveStory = new Book("춘향전");
        Book emptyBook = new Book();
    }

}

1) 먼저 인자 2개짜리 생성자가 호출됨(인자로 전달된것이 객체에 담긴다) -> littlePrince가 그 객체의 레퍼런스가 된다.

2) 그다음은 인자 1개짜리 생성자가 호출됨, this는 다른 생성자를 호출함(인자2개짜리) -> Book("춘향전", "작자미상")이렇게 호출됨

라. 객체 배열

자바에서는 객체를 원소로 하는 배열을 만들 수 있다.
레퍼런스를 원소로 갖는 배열임

Circle [] c = new Circle[5];

for(int i=0; i<c.length; i++)
	c[i] = new Circle(i);
  1. 배열의 원소는 객체가 아니라 Circle 객체에 대한 레퍼런스임
    즉 c는 레퍼런스 변수이다.
    (즉 각 객체5개를 가리킬 레퍼런스를 5개 만든것임)
  2. 그 후, Circle 객체를 하나씩 생성하여 배열 c[]의 각 레퍼런스에 대입한다.

4) 메소드 활용과 객체 치환

자바의 메소드 호출시 인자전달방식은 call-by-value 이다. (실인자의 값이 복사되어 메소드의 인자로 전달됨)

가. 인자전달

1> 기본타입이 전달되는경우

int, char, byte 등은 그냥 매개변수에 값이 복사되어 전달됨

2> 객체가 전달되는 경우

메소드의 매개변수가 클래스 타입인 경우는 객체의 레퍼런스값이 전달된다.
즉 객체의 레퍼런스값이 복사되어 전달됨

public class ReferencePassing{
	public static void main (String args[])
    {
    	Circle pizza = new Circle(10);
        increase(pizza);
    }
    static void increase(Circle m){
		m.radius++;
	}
}

3> 배열이 전달되는 경우

이것 역시 객체와 마찬가지로 배열에 대한 레퍼런스만 전달된다.

나. 메소드 오버로딩(메소드 중복)

메소드 오버로딩(method overloading): 자바에서는 클래스 내에 이름이 같지만 매개변수의 타입이나 개수가 서로 다른 여러개의 메소드를 작성할 수 있다.

  • 메소드 오버로딩이 성립될 조건
    1) 메소드 이름이 동일하여야 함
    2) 메소드 매개 변수의 개수나 타입이 달라야 함
public class MethodSample{
	public int getSum(int i, int j){
    	return i + j;
    }
    
    public int getSum(int i, int j, int k){
    	return i + j + k;
    }
    
    public double getSum(double i, double j){
    	return i + j;
    }

}

다. 객체치환

public class Samp{
	public Samp(int x) {this.id = x;}
    
    
    public static void main(String[] args)
    {
    	Samp ob1 = new Samp(3);
        Samp ob2 = new Samp(4);
        
        Samp s;
        s = ob2;
        ob1 = ob2;
        System.out.println("ob1.id="+ob1.get());
        System.out.println("ob2.id="+ob2.get());
    }
}

-> ob1은 가리킬 것이 없어지고 ob1이 가리키던 객체는 가비지가 된다.

5) 객체의 소멸과 가비지 컬렉션

가. 객체의 소멸

객체 생성 : new 연산자
객체 소멸 : X

-> 개발자가 마음대로 객체를 소멸시킬 수 없다.
자바에서는 JVM이 알아서 "사용하지 않고 있는 객체나 배열 메모리"를 수거한다.(개발자의 부담 줄어든다)

나. 가비지

할당받은 객체나 배열 메모리 중에서 더 이상 사용하지 않게 된 메모리를 가비지라고 함.

  • 가비지 : 참조하는 레퍼런스가 하나도 없는 객체나 배열
    (프로그램에서 더 이상 접근할 수 없게 됨)

다. 가비지 컬렉션

1> 가비지가 많아지면 응용프로그램에게 할당해줄 수 있는 가용메모리의 양이 줄어든다.
2> 가용메모리의 양이 일정 크기 이하로 줄어들면 JVM이 가비지를 회수하여 가용메모리 공간을 늘린다.(가비지 컬렉션)
-> 실시간 프로그램에서 가끔 일시적으로 프로그램이 실행중에 중단되는 경우가 있다.

(자바프로그램이 실행을 멈추고 가비지 컬렉션이 끝나기를 기다린다.)

System.gc(); //가비지 컬렉션 자동 요청

이 코드로 가비지 컬렉션을 강제 수행시킬 수 있다.

3. 접근 지정자

1) 자바의 패키지 개념

자바에서는 상호 관련 있는 클래스 파일들을 패키지(package)에 저장하여 관리한다.

패키지 이름은 모두 소문자로 쓰인다.

2) 접근 지정자(4가지)

접근 지정자는 클래스나 멤버들을 다른 클래스에서 접근해도 되는지의 여부를 선언하는 지시어이다.

1> private

2> protected

3> public

4> 접근 지정자 생략(디폴트 접근 지정)

3) 클래스 접근 지정(2)

가. public 클래스

패키지와 상관없이 다른 어떤 클래스에게도 사용이 허용됨

public class World{

}

나. 디폴트 클래스(접근 지정자 생략)

오직 같은 패키지 내에 있는 클래스들에게만 사용이 허용됨

class Local{
}

4) 멤버 접근 지정(필드, 생성자, 메소드)

가. public 멤버

패키지를 막론하고 모든 클래스들이 접근 가능하다.

나. private 멤버

클래스 내의 멤버들에게만 접근이 허용됨

다. protected 멤버(상속과 관련)

같은 패키지 내의 모든 클래스에서 접근이 가능함
다른 패키지에 있더라도 자식 클래스인 경우는 접근이 허용됨

라. 디폴트 멤버

동일한 패키지 내에 있는 클래스들만 디폴트 멤버를 자유롭게 접근할 수 있다.

4. Static

static : 모든 객체에 1 고정되어 2 공유된다.

1) static 멤버 선언

static int m;
static void f(){}

2) static 멤버의 특성

static 멤버는 클래스당 하나만 생성되는 멤버로서, 동일한 클래스의 모든 객체들이 공유한다.

  • static vs instance

    static 멤버: 클래스 멤버
    non-static 멤버 : 인스턴스 멤버

static 멤버는 프로그램을 시작할 때나 클래스 로딩 시에 생성된다. 즉 객체를 생성하기 전에도 static 멤버는 사용할 수 있다.
-> 따라서 static 멤버는 클래스 이름만으로 해당 필드나 메소드에 접근할 수 있다.

public class A{
	class Sample{
    	static int m = 0;
    }
    
    public static void main(String[] args){
    	Sample.m = 3; //바로 클래스이름으로 접근
    }
}

또한 객체가 소멸된 후에도 static 멤버는 살아 공간을 차지한다.
static 멤버는 프로그램이 종료될때 소멸된다.

반면 non-static a멤버는 객체가 생길때 함께 생성되고 객체가 사라지면 함께 사라진다.(생명주기를 객체와 함께 함)

3) static의 활용

가. 전역 변수와 전역 함수를 만들 때 사용

자바에서는 어떤 변수나, 함수도 클래스 바깥에 존재할 수 없다. 따라서 전역변수나 전역함수를 만들고자 한다면 static으로 선언한다.

나. 공유 멤버를 만들고자 할때 사용

static으로 선언된 필드나 메소드는 이 클래스의 객체들 사이에서 공유된다.

예를 들어, 만약 Counter라는 클래스에 static int count = 0;이라는 필드가 있다면, Counter의 모든 인스턴스는 이 count 필드를 공유하게 된다. 어떤 인스턴스가 이 필드 값을 변경하면, 다른 모든 인스턴스에 대해서도 그 변경이 보여진다. 이는 static 필드가 클래스에 속하고 모든 인스턴스에 의해 공유된다는 사실을 반영한다. -> 객체로 여러개 찍어도 그냥 static은 다같이 공유된다.

4) static 메소드의 제약 조건

가. static 메소드는 오직 static 멤버만 접근할 수 있다.

  • non-static 메소드는 non-static 필드 사용가능
  • static 메소드드는 non-static 필드 사용 불가
  • static 메소드는 static 필드 사용 가능
  • static 메소드는 static 메소드 호출 가능

static은 정적영역에 컴파일되면서 상주한다. 반면 인스턴스 메소드는 객체를 선언해야 사용할 수 있다. 따라서 객체를 선언하지 않고 인스턴스 메소드를 사용하면 이것은 사용할 수 없는 것이다.
따라서 객체를 선언하고 이를 사용해야한다.
인스턴스 메소드에서, 인스턴스 메소드는 사용가능하다

나. static 메소드에서 this를 사용할 수 없다.

static 메소드는 객체 없이도 존재하기 때문임

5.final

1) final 클래스

final이 클래스 이름 앞에 사용되면 클래스를 상속받을 수 없음을 지정한다.

final class FinalClass{
}

class SubClass extends FinalClass{

}

2) final 메소드

메소드 앞에 final이 붙으면 이 메소드는 더이상 오버라이딩 할 수 없음을 지정함
-> 즉 부모클래스의 특정 메소드를 오버라이딩 못하게 하고 상속은 받고 싶다면 사용한다.

public class SuperClass{
	protected final int finalMethod(){...}
}
class SubClass extends SuperClass{
	protected int finalMethod() {...}
}

3) final 필드

final로 필드를 선언하면 필드는 상수가 된다.

public class FinalFieldClass{
	final int ROWS = 10;
    void f(){
    	int[] int Array = new int[ROWS];
        ROWS = 30; // 컴파일 오류
    }
}

만약 final 키워드를 public static과 함꼐 선언하면, 프로그램 전체에서 공유할 수 있는 상수가 됨.

class SharedClass{
	public static final double PI = 3.14;
}

double area = SharedClass.PI*radius*radius;

0개의 댓글