본문 바로가기
☕ Java 웹 프로그래밍/Java

[프로그래머스] Java(자바) 입문 | Part 5. 클래스와 객체

by 일단연 2023. 5. 2.

 클래스 선언 

클래스

  • 자바는 객체를 만들기 위해 반드시 클래스를 먼저 만들어야 함. 클래스는 객체를 만들기 위한 일종의 틀.
  • 클래스는 멤버(member)로 속성을 표현하는 필드(field)와 기능을 표현하는 메소드(method)를 가짐
  • 클래스를 만드는 형식: class 클래스명 { };
  • 붕어빵이 객체라면, 붕어빵 틀은 클래스
  • 자동차 클래스 생성
    public class Car{

    }
  • Car.java란 파일을 만듦
  • 저장을 하면 이클립스는 컴파일하여 디스크에 Car라는 클래스를 생성
  • 자동차 클래스가 생성되었다고 해서 자동차가 만들어진 것은 아님

 

Car객체 생성하기 (자동차 만들기)

public class CarExam{
  public static void main(String args[]){
    Car c1 = new Car();
    Car c2 = new Car();
  }
}
  • new 연산자: new연산자 뒤에 나오는 생성자를 이용해 메모리에 객체를 만들라는 명령
  • 메모리에 만들어진 객체를 인스턴스(instance)라고도 함
  • 이렇게 만들어진 객체를 참조하는 변수가 c1 , c2 (클래스가 참조형 타입이 됨)
  • 위의 코드가 실행되면 Car라는 객체가 2개가 만들어지고 각각의 객체를 참조하는 c1과 c2변수가 선언됨

 

실습

public class Car{

}
public class CarExam{
  public static void main(String[]args){
    // c1에 Car 객체를 생성해서 넣어주세요.
    Car c1 = new Car();
  }
}


 

 참조타입 

  • Java에선 변수를 선언하려면 변수의 데이터타입을 정해줘야 함
  • Java의 변수 타입
    • 기본 타입
      • 논리형(boolean), 문자형(char), 정수형(byte, short, int, long), 실수형(float, double)
    • 참조 타입
      • 기본형 타입을 제외한 모든 타입
      • 앞서 배웠던 배열도 참조 타입이고, 클래스도 모두 참조 타입
  • 참조형 변수
    • String str = new String("hello");
      • str 변수 앞에 기본형 타입이 아닌 String클래스가 적혀있음
      • 이퀄(=)뒤에는 new 다음에 생성자가 있음
      • new 라는 키워드는 객체를 메모리에 올려줌(클래스를 메모리에 올려주세요). 이렇게 메모리에 올라간 객체를 인스턴스라고 함.
  • (메모리에 올라간 인스턴스를 가리키는 변수, 참조하는 변수, 레퍼런스 하는 변수가 str 이다). (클래스를 참조한다. 레퍼런스한다)라는 것은 변수가 인스턴스를 가지고 있는 게 아니라 말 그대로 가리킨다는 의미
  • str이라는 변수에는 메모리의 위치 값이 저장됨. 메모리의 위치값이 저장된다고 하더라도, 어떤 메모리에 저장되는지 그 정보를 알 수 있는 방법은 없음. 그렇기 때문에 str변수는 String 인스턴스를 참조한다라고만 알면 됨
  • 앞으로 배울 클래스들은 모두 참조형

 

 

참고 (Java의 메모리 구조)

  • heap
    • new를 통해 생성된 모든 인스턴스가 저장되는 공간
    • 인스턴스는 결국 참조형(reference type) 데이터 타입임을 의미하는데, 참조형으로 선언된 변수의 값은 heap 메모리 공간에 할당됨
    • 참조형 데이터 타입의 값 자체는 heap 메모리 공간에, 해당 값의 주소는 stack영역에 저장됨
    • heap 영역의 메모리는 가비지 컬렉터(Garbage Collector)를 통해 관리되며, 참조되지 않는 경우에는 해당 공간에 할당된 메모리가 해제됨 (때문에 개발자는 메모리 관리를 신경쓸 필요가 없음)
  • static (=method 영역)
    • 전역변수와 static이 붙은 클래스 메서드의 경우에 static 영역에 할당되어 프로그램의 시작부터 종료까지 메모리에 유지되며, 프로그램이 종료되는 때에 메모리에서 해제됨
    • static 메모리가 차지하는 공간은 프로그램 로드시에 결정되기 때문에 프로그램 컴파일 시간에 많은 영향을 줌
    • static으로 선언된 경우 프로그램 전체에서 공유됨
    • import 하여 사용하는 패키지 파일들도 static 영역에 저장됨.
    • 클래스 메서드나 클래스 변수의 경우 인스턴스를 생성하지 않고 ‘ 클래스명.메서드명’ or ‘클래스명.변수명’ 이와 같은 식으로 사용
  • stack (call stack 또는 execution stack)
    • 지역변수와 일반 메서드가 저장되는 공간
    • 기본형의 경우, 값 자체가 stack 영역에 할당됨
    • 참조형 타입의 값을 지역 변수에 할당 시, stack 메모리 공간에 실질적으로 할당되는 것은 heap 메모리 공간에 저장된 값의 참조값(=주소값)
    • 메서드의 경우는 메서드가 호출될 때 메모리에 할당되고, 해당 메서드가 종료되면 메모리에서 해제됨
    • stack 영역은 LIFO(=Last In First Out) 구조로 되어 있음
    • LIFO(Last In Fist Out) 구조
      • 후입선출 즉, 가장 마지막 항목이 가장 먼저 제거됨
      package chapter06;
      
      public class CallStackTest {
          public static void main(String args[]) {
              System.out.println("main(String[] args)실행됨");
              firstMethod();
              System.out.println("main(String[] args)종료됨");
          }
      
          static void firstMethod () {
              System.out.println("firstMethod 실행됨");
              secondMethod();
              System.out.println("firstMethod 실행됨");
          }
      
          static void secondMethod () {
              System.out.println("secondMethod 실행됨");
              System.out.println("secondMethods 실행됨");
          }
      }
      // 출력 순서 -----------------
      // main(String[] args) 실행 > 스택 메모리에 가장 먼저 올라감
      // firstMethod 실행 > firstMethod 메서드가 main 메서드 위에 올라감
      // secondMethod 실행 > firstMethod 메서드 위에 secondMethod 메서드 위에 올라감
      // 메모리에서 제거될 때는 메모리에 올라간 순서와 반대
      // secondMethods 실행 > secondMethod 메서드가 모든 실행을 끝내고 제거
      // firstMethod 실행 > 메모리에서 제거
      // main(String[] args) 종료 > 메모리에서 제거
      
    • cf) 스택오버플로우(StackOverFlow, SOF)
      • 재귀호출이나, 무한반복을 하는 for문이나 while문이 실행되어 함수가 실행되지 않고, 계속해서 스택에 메모리가 쌓이게 되는 경우 스택 메모리가 감당할 수 있는 범위를 초과하는 경우 발생하는 에러를 의미

 

 String클래스 

  • 문자열을 표현하는 클래스 (자바에서 가장 많이 사용)
    • public static void main(String[ ] args( ){ } 에서 String도 String클래스
  • 하나의 문자가 아닌 여러 문자(문자열)를 저장할 수 있으며, char와 다르게 빈 문자열도 저장 가능
  • 큰따옴표(” “)로 값을 감싸야 함 (char는 작은따옴표(’ ‘) 사용)
  • 가장 많이 쓰기 때문에 특별대우 받음
    • new 연산자를 사용하지 않아도 되고, 해도 됨 But, 어떤 인스턴스를 참조하는지는 다름

Java 인스턴스 생성 방법

  1. new연산자를 이용하지 않고 인스턴스를 만드는 경우
String str1 = "hello";
String str2 = "hello";
  • "hello"라는 문자열이 메모리 중에서 상수가 저장되는 영역에 저장됨
    • 상수는 변하지 않는 값을 의미
  • String str2 = "hello"; 이 문장이 실행될 때에 hello 라는 문자열 상수는 이미 만들어져 있으므로 str1이 참조하는 인스턴스를 str2도 참조
  • 메모리를 아끼려면 String은 new를 사용하지 않고 사용하는 것이 좋음
  1. new연산자를 이용해서 인스턴스를 만드는 경우
String str3 = new String("hello");
String str4 = new String("hello");
  • new연산자를 이용하여 인스턴스를 만들면 인스턴스는 무조건 새롭게 만들어짐
  • String str4 = new String("hello"); 이 문장이 실행될 때도 인스턴스를 새롭게 만들게 되므로, str3과 str4는 서로 다른 인스턴스를 참조
if(str1 == str2){
//같은 인스턴스를 참조하므로 결과는 true (출력됨)
  System.out.println("str1과 str2는 같은 레퍼런스이다.");
}

if(str1 == str3){
//str1과 str3은 서로 다른 인스턴스를 참조하므로 결과는 false (출력 안 됨)
  System.out.println("str1과 str3는 같은 레퍼런스이다.");
}

if(str3 == str4){
//str3과 str4는 서로 다른 인스턴스를 참조하므로 결과는 false (출력 안 됨)
  System.out.println("str3과 str4는 같은 레퍼런스이다.");
}
  • 참조형 변수끼리 == 로 비교하면 서로 같은 것을 참조하는지(가리키고 있는 메모리영역의 주소가 같은지) 비교 (값을 비교하는 게 아님)
  • String은 불변 클래스
    • 불변이란, String이 인스턴스가 될 때 가지고 있던 값을 나중에 수정할 수 없다는 뜻
  • String은 문자열과 관련된 다양한 메소드를 가지고 있음
    • 메소드를 호출한다 하더라도 String은 내부의 값이 변하지 않음
    • String이 가지고 있는 메소드 중 String을 반환하는 메소드는 모두 새로운 String을 생성해서 반환
String str5 = "hello world";
String str6 = str5.substring(3);
System.out.println(str6); //결과: lo world
		
System.out.println(str1); //결과: hello
System.out.println(str1.substring(3)); //결과: lo
System.out.println(str1); //결과: hello
  • substring은 문자열을 자른 결과를 반환하는 메소드. 해당 코드가 실행되어도 str5는 변하지 않음
  • str6은 str5가 가지고 있는 문자열 중 3번째 위치부터 자른 결과 즉, 새로운 String을 참조

 

실습


 

 필드(field) 선언 

  • 자동차는 자동차 이름, 번호를 가지고 있고, 자동차는 달리고 멈추는 기능이 있음.
  • 학생은 이름, 학번 등을 속성으로 가짐. 한 반에 학생이 20명 있다면 학생 객체가 20개 있다는 것을 의미. 각각의 학생은 이름이 구별됨(객체별로 속성의 값이 유지된다는 의미)
  • 여기에서 가지고 있는 것을 속성이라고 함. Java에서는 이러한 속성을 **필드(Field)**라는 용어로 사용
  • 필드의 선언
    • 데이터타입 필드명;
    • 이름과 번호를 필드로 가지고 있는 Car클래스 선언
    • public class Car{ String name; int number; }
  • Car 클래스를 인스턴스화 하기
*public class CarExam {

  public static void main(String[] args) {

    Car c1 = new Car();
    Car c2 = new Car();
    //Car라는 인스턴스(객체)가 메모리에 2개 만들어짐
    //객체별로 name과 number라는 속성을 가짐

  }

}*
  • 속성(필드) 이용하기
    • 참조 변수 다음에 나오는 점(dot)은 참조변수가 참조하는 객체가 가지고 있는 것을 사용할 때 사용
    • Car라는 객체가 c1, c2마다 생성되고 그 객체들마다 각각 속성(name, number)이 생김
Car c1 = new Car();
Car c2 = new Car();

*//c1.name은  c1이 참조하는 객체의 name을 의미*

c1.name = "소방차"; *//c1이 참조하는 객체의 name을 소방차로 설정*
c1.number = 1234; *// c1.number = 1234란 c1이 참조하는 객체의 number를 1234로 설정*

c2.name = "구급차"; *//c2가 가리키는 객체의name을 구급차로 설정*
c2.number = 1004; *//c2가 가리키는 객체의 number를 1004로 설정*

System.out.println(c1.name); *//콘솔에 c1이 참조하는 객체의 name을 출력*
System.out.println(c1.number); *//콘솔에 c1이 참조하는 객체의 number를 출력*

String name = c2.name;
*//c2가 참조하는 객체의 name을 String타입 변수 name도 참조하게 함*

 

필드 변경 제한: final

  • final을 필드 앞에 붙이면 그 필드의 내용을 변경할 수 없음
    • 마지막 변수 값을 갖기 때문에 변경할 수 없는 것
  • 보통 final을 붙이는 필드는 상수처럼 고정된 형태이며, static과 함께 클래스 변수를 상수처럼 지정하는 데에 사용됨

 

실습

public class Song {
  // 이곳에 코드를 작성하세요.
  String songTitle;
  String singer;
  String albumName;
  int trackNumber;
}
// Song.java 코드를 실행하기 위한 코드입니다. 이 코드는 수정하지 마세요.
public class SongExam {
  public static void main(String[] args) {
    Song song = new Song();
    song.songTitle = new String("곡명");
    song.singer = new String("가수");
    song.albumName = new String("앨범");
    song.trackNumber = 5;
  }
}

 


 

 메소드(Method)란? 

  • 필드와 메소드
    • 필드: 물체의 상태 (car클래스의 이름과 번호)
    • 메소드: 물체의 행동 (car클래스의 전진 or 후진하는 행동)
  • 메소드
    • 클래스가 가지고 있는 기능. 클래스 안에 선언됨
    • 입력값이 있고 그 입력값을 받아서 무언가 한 다음 결과를 도출해내는, 수학의 함수와 비슷한 개념
    • 이때 입력값을 매개변수라고 하고, 결과값을 리턴값이라고 함
      • 인자(Argument): 어떤 함수를 호출시에 전달되는 값
      • 매개변수(Parameter): 그 전달된 인자를 받아들이는 변수

 

 메소드 선언

메소드 선언 방법

  • public 리턴타입 메소드명 (매개변수들) { 구현할 기능 }
  • 리턴값의 유무, 매개변수의 유무로 메소드의 형태가 나뉨

 

다양한 형태의 메소드

  • 리턴값의 유무
    • 리턴값 O: 리턴타입 자리에 해당 값의 타입 작성 (3,5번 메소드)
    • 리턴값 X: 리턴타입 자리에 void 작성 (1,2,4번 메소드)
  • 매개변수의 개수
    • 여러 개면 콤마( , )로 구분
  • 리턴하는 값 앞엔 return이라는 키워드 사용

1.  매개변수도 없고 리턴하는 것도 없는 형태의 메소드 (매개변수 X, 리턴값 X)

  • 리턴하는 것이 없을 경우 void라는 예약어 작성
public class MyClass{
  public void method1(){
    System.out.println("method1이 실행됩니다.");
  }
}

 

2.  정수를 받아들인 후, 리턴하지 않는 메소드 (매개변수 O, 리턴값 X)

  • 받아들이는 값은 어떤 타입이라도 상관없음
public class MyClass{       
  public void method2(int x){
    System.out.println(x + "를 이용하는 method2입니다.");
  }
}

 

3.  아무것도 받아들이지 않고, 정수를 반환하는 메소드 (매개변수 X, 리턴값 O)

  • 메소드 이름 앞에는 리턴하는 타입을 적어줌
  • 리턴타입은 하나만 사용할 수 있으며 어떤 타입이라도 상관없음 (기본형뿐만 아니라 참조형도 가능)
public int method3(){
  System.out.println("method3이 실행됩니다.");

  return 10;
}
*//위 메소드가 실행되면
//콘솔에 'method3이 실행됩니다.'를 출력, 이 메소드를 호출한 쪽에 10을 리턴*

 

4.  정수를 2개 매개변수로 받고, 아무것도 반환하지 않는 메소드 (매개변수 O, 리턴값 X)

public void method4(int x, int y){
  System.out.println(x + "," + y + " 를 이용하는 method4입니다.");
}

 

5.  정수를 한 개 받아들인 후, 정수를 반환하는 메소드 (매개변수 O, 리턴값 O)

public int method5(int y){
  System.out.println(y + " 를 이용하는 method5입니다.");
  return y*5;
}

 

실습


 

 메소드 사용해보기 

  • 클래스가 가진 메소드를 사용하기 위해서는 객체로 만들어야 함

MyClass

public class MyClass{
  public void method(){
    System.out.println("method1이 실행됩니다.");
  }

  public void method2(int x){
    System.out.println(x + " 를 이용하는 method2입니다.");
  }

  public int method3(){
    System.out.println("method3이 실행됩니다.");
    return 10;
  }

  public void method4(int x, int y){
    System.out.println(x + "," + y + " 를 이용하는 method4입니다.");
  }

  public int method5(int y){
    System.out.println(y + " 를 이용하는 method5입니다.");
    return 5;
  }
}
  • 메소드를 사용하기 위해서는 메소드가 정의된 클래스인 MyClass가 생성되어야 함
  • 객체를 생성할 때는 new 연산자를 이용
  • 메소드 사용할 때는 생성된 클래스를 참조하는 레퍼런스변수.메소드명( ) 으로 사용할 수 있음

MyClassExam

public class MyClassExam{
  public static void main(String args[]){
    MyClass my1 = new MyClass(); //메소드가 정의된 클래스 생성
    my1.method1(); //MyClass에서 정의해 놓은 메소드 method1() 를 호출
    
    my1.method2(10);

    int x = my1.method3();
    //method3의 리턴값을 변수로 받아줘야 함

    System.out.println("method3 이 리턴한 " + x + " 입니다.");

    my1.method4(10,100);

    int x2 = my1.method5(50);
    //method5의 리턴값을 변수로 받아줘야 함

    System.out.println("method5 가 리턴한 " + x2 + " 입니다.");

  }
}
  • 메소드의 리턴값을 출력하기 위해서는 변수에 담아줘야 함

 

실습

class ReferenceTypeExam {
  public static void main(String []args) {
    ReferenceTypeExam exam = new ReferenceTypeExam();
        
    //기본형 변수 value1을 addOne에 전달
    int value = 10;
    exam.addOne(value);
    System.out.println("기본형 변수의 값을 다른 메소드에서 변경한 결과: " + value);
        
    //참조형 변수arr을 addOne에 전달
    int []arr = {10};
    exam.addOne(arr);
    System.out.println("참조형 변수의 값을 다른 메소드에서 변경한 결과: " + arr[0]);
  }
    
    
  public void addOne(int value) {
    value++;
  }
    
  public void addOne(int[] arr) {
    for(int i = 0; i < arr.length; i++){
      arr[i] ++;
    }
  }
}
  • 결과
    • 기본형 변수의 값을 다른 메소드에서 변경한 결과: 10
    • 참조형 변수의 값을 다른 메소드에서 변경한 결과: 11
  • 기본형 타입과 참조 타입의 차이
    • 기본형 타입
      • 다른 메소드에 매개변수로 전달될 때, 10이라는 값이 그대로 전달됨
      • addOne에서 1을 더하더라도 value라는 변수에 영향 X
    • 참조형 타입
      • 다른 메소드에 매개변수로 전달될 때, 변수의 주소가 전달됨
      • '몇 번째 박스에 값이 있다'는 식으로 값이 들어있는 주소가 전달됨 > 그걸 전달받은 메소드 addOne에서는 그 박스에 가서 들어있는 값에 1 더함
      • addOne을 실행하고 나서 arr[0]을 확인해 볼 때도 같은 박스에 가서 값을 확인하기 때문에 값이 11로 변해있는 것

 

실습 1

class ReferenceTypeExam {
  public static void main(String []args) {
    ReferenceTypeExam exam = new ReferenceTypeExam();
        
    //기본형 변수 value1을 addOne에 전달
    int value = 10;
    exam.addOne(value);
    System.out.println("기본형 변수의 값을 다른 메소드에서 변경한 결과: " + value);
        
    //참조형 변수arr을 addOne에 전달
    int []arr = {10};
    exam.addOne(arr);
    System.out.println("참조형 변수의 값을 다른 메소드에서 변경한 결과: " + arr[0]);
  }
    
    
  public void addOne(int value) {
    value++;
  }
    
  public void addOne(int[] arr) {
    for(int i = 0; i < arr.length; i++){
      arr[i] ++;
    }
  }
}
  • 결과
    • 기본형 변수의 값을 다른 메소드에서 변경한 결과: 10
    • 참조형 변수의 값을 다른 메소드에서 변경한 결과: 11
  • 기본형 타입과 참조 타입의 차이
    • 기본형 타입
      • 다른 메소드에 매개변수로 전달될 때, 10이라는 값이 그대로 전달됨
      • addOne에서 1을 더하더라도 value라는 변수에 영향 X
    • 참조형 타입
      • 다른 메소드에 매개변수로 전달될 때, 변수의 주소가 전달됨
      • '몇 번째 박스에 값이 있다'는 식으로 값이 들어있는 주소가 전달됨 > 그걸 전달받은 메소드 addOne에서는 그 박스에 가서 들어있는 값에 1 더함
      • addOne을 실행하고 나서 arr[0]을 확인해 볼 때도 같은 박스에 가서 값을 확인하기 때문에 값이 11로 변해있는 것

실습 2

class Car{
  void run(){
    System.out.println("차가 달립니다.");
  }
}
class CarExam {
  public static void main(String [] args) {
    // 이 아래에서 car에 Car클래스의 인스턴스를 생성하고 run메소드를 사용해 보세요.
    Car car = new Car();
    car.run();  
  }
}


 

 String클래스의 메소드 

  • String클래스를 사용하기 위해 먼저 String을 선언해야 함
  • 일반적으로 new 연산자를 이용해 생성자를 쓰고, 해당 생성자를 이용해 객체를 생성
    • String str = new String("hello");
  • But, String클래스는 new 연산자 없이 객체 생성 가능
    • String str = “hello”;
  • 문자열 길이 구하기 (length)
    • str.length()는 str이 참조하는 문자열의 길이를 구해서 int 타입으로 리턴해주는 메소드
System.out.println(str.length());  //str
  • 문자열 붙히기 (concat)
    • str.concat("world") 메소드는 str이 참조하는 문자열 hello에 메소드의 인자로 들어온 문자열 world를 붙혀서 String 타입으로 리턴하는 메소드
String str = new String("hello");

System.out.println(str.concat(" world"));  //출력결과는 hello world
System.out.println(str);  //출력결과는 hello

  • String Class는 불변 클래스
    • 메소드가 수행되면 새로운 문자열을 만듦 > 원래 클래스는 변하지 않음
  • 변수 str과 str2 모두 hello라는 String 객체를 참조
    • String 객체 자체를 바꿔버리면 같은 객체를 참조하는 다른 변수에 문제가 생기기 때문에 한 번 만들어진 객체를 바꾸지 않음

  • str이 hello world라는 String 객체를 참조하게 하려면
    • str = str.concat(”world”); 이렇게 이퀄 관계를 바꿔줘야 함
  • 문자열 자르기 (subString)
    • str.subString(1,3) 은 str이 참조하는 문자열을 인덱스 1번부터 3번 전까지 자른 결과
    • str.subString(2) 은 str이 참조하는 문자열을 2번 인덱스부터 마지막까지 자른 결과
    • 문자열의 인덱스는 0번부터 시작
System.out.println(str.substring(1, 3)); //출력결과 el
System.out.println(str.substring(2));    //출력결과 llo world

 

실습


 

 변수의 scope와 static 

변수의 scope

  • 변수들이 사용 가능한 범위 > 변수가 선언된 블럭이 그 변수의 사용범위
public class ValableScopeExam{

  int globalScope = 10;   *// 인스턴스 변수*

  public void scopeTest(int value){   
    int localScope = 10;
    System.out.println(globalScope);
    System.out.println(localScpe);
    System.out.println(value);
  }
}
  • 클래스의 속성으로 선언된 변수 globalScope 의 사용 범위는 클래스 전체
  • 매개변수로 선언된 int value 는 블럭 바깥에 존재하기는 하지만, 메서드 선언부에 존재하므로 사용범위는 해당 메소드 블럭 내
  • 메소드 블럭내에서 선언된 localScope 변수의 사용범위는 메소드 블럭 내

 

main메소드에서 사용하기

  • 같은 클래스 안에 있는데 globalScope 변수를 사용할 수 없음
    • 사용 가능하게 하려면 static int glovalScope = 10; 로 바꿔주면 됨
  • main은 static한 메소드
    • static한 메소드에서는 static 하지 않은 필드를 사용할 수 없음
    • static한 메소드가 static하지 않은 필드를 사용하는 시점에 해당 클래스가 인스턴스화되지 않았을 수도 있기 때문
public class VariableScopeExam {
  int globalScope = 10; 

  public void scopeTest(int value){
    int localScope = 20;            
    System.out.println(globalScope);
    System.out.println(localScope);
      System.out.println(value);
  }   
  public static void main(String[] args) {
    System.out.println(globalScope);  *//오류*
    System.out.println(localScope);   *//오류*
    System.out.println(value);        *//오류*  
  }   
}

 

static

  non-static 멤버 non-static 멤버
선언 class Sample{ 
  int n;
  void g( ) {...}
}
class Sample{
  static int n;
  static void g( ) {...}
}
공간적 특성 멤버는 객체마다 각각 존재
- 인스턴스 멤버라고 부름
멤버는 클래스당 하나만 생성
- 멤버는 객체 내부가 아닌 별도의 공간(클래스 코드가 적재되는 메모리)에 생성
- 클래스 멤버라고 부름
공간적 특성 객체 생성 시에 멤버가 생성됨
- 객체가 생길 때 멤버도 생성
- 객체 생성 후 멤버 사용 가능
- 객체가 사라지면 멤버도 사라짐
클래스 로딩 시에 멤버 생성
- 객체가 생기기 전에 이미 생성
- 객체가 생기기 전에도 사용 가능
- 객체가 사라져도 멤버는 사라지지 않음
- 멤버는 프로그램이 종료될 때 사라짐
공유의 특성 공유되지 않음
- 멤버는 객체 내에 각각 공간 유지
동일한 클래스의 모든 객체들에 의해 공유됨
  • 모든 클래스는 인스턴스화 하지 않은 채로 사용할 수 없음 (static은 예외)
    • 인스턴스화: 메소드와 변수를 모아놓은 것에 불과한 클래스를 사용할 수 있도록, 해당 클래스 타입의 객체명을 선언하고 값을 넣어줘서 해당 클래스의 변수나 메소드를 사용 가능한 상태로 만드는 것 (= 클래스로부터 객체를 만드는 것)
    • 문법: 클래스명 변수명 = new 변수명( );
    • 인스턴스: 메모리에 만들어진 객체
  • main 메소드는 static 이라는 키워드로 메소드가 정의되어 있음. 이런 메서드를 static한 메소드라고 함.
  • static한 필드 / static한 메소드: 키워드 static을 사용하면 Class가 인스턴스화되지 않아도 사용할 수 있음
public class VariableScopeExam {
  int globalScope = 10; 
  static int staticVal = 7; //static한 필드

  public void scopeTest(int value){
    int localScope = 20;        
  }

  public static void main(String[] args) { //main 메소드: static한 메소드
    System.out.println(staticVal); *//사용 가능
    //VariableScopeExam staticVal = new VariableScopeExam();로 인스턴스화하지 않아도 사용 가능*
  }

}

 

static한 변수는 공유된다

  • static하게 선언된 변수는 값을 저장할 수 있는 공간이 하나만 생성됨
    • = 인스턴스가 여러 개 생성되어도 static한 변수는 하나임
    • 마지막으로 넣은 값이 최종 값이 됨
  • static한 메소드 안에서 static하지 않은 참조변수를 사용하는 방법
    • 객체를 생성한 후 사용하면 됨
      • ValableScopeExam v1 = new ValableScopeExam( );
  • static한 메소드 안에서 static하지 않은 필드를 사용하는 방법
    • 필드를 static하게 바꿔주면 됨
      • static int globalScope = 10;
ValableScopeExam v1 = new ValableScopeExam();
ValableScopeExam v2 = new ValableScopeExam();
v1.golbalScope = 20;
v2.golbalScope = 30; 

System.out.println(v1.golbalScope);  *//20이 출력*
System.out.println(v2.golbalScope);  *//30이 출력*
v1.staticVal = 10;
v2.staticVal = 20; //마지막으로 넣은 값이 최종 값이 됨

System.out.println(v1.statVal);  *//20이 출력*
System.out.println(v2.statVal);  *//20이 출력
//v1.staticVal 대신* VariableScopeExam.staticVal를 쓰는 게 더 바람직

public class VariableScopeExam {
  static int globalScope = 10; 
}
  • golbalScope같은 변수(필드)는 인스턴스가 생성될 때 생성되기 때문에 인스턴스 변수라고 함
  • staticVal같은 static한 필드를 클래스 변수라고 함
  • 클래스 변수는 레퍼런스.변수명 하고 사용하기보다는 클래스명.변수명으로 사용하는 게 더 바람직
    • VariableScopeExam.staticVal

 

실습


 

 열거형(enum) 

  • Java에서 변수를 선언할 때 열거형을 변수타입으로 사용할 수 있음
  • 열거형은 JDK5에서 추가되었음 (JDK5 이전에는 상수를 열거형 대신 사용)
  • 상수를 이용하는 방법 (public static final)
    • public static final 데이터타입 변수명 = 값;
    • 변수는 모두 대문자로 작성 (예: FEMALE)
public class EnumExam {
  public static final String FEMALE = "FEMALE";
  public static final String MALE = "MALE";
        

  public static void main(String[] args) {
    String gender1;

    gender1 = EnumExam.MALE;
    gender1 = EnumExam.FEMALE;                  
  }
    }

 

상수를 사용했을 때의 문제점

  • String으로 선언된 gender1에는 FEMALE, MALE 둘 중 하나의 값을 갖기 원하는데, gender1의 type이 String이기 때문에 gender1 = "소년"; 과 같이 String 값 아무거나 들어가도 컴파일 시점에는 전혀 문제가 되지 않음 (실행할 때 문제가 생김)
  • 실행할 때 원했던 값인 FEMALE, MALE이 아닌 다른 값이 들어오게 되므로 문제를 발생시킬 수 있음

 

해결 방법

  • 위와 같은 문제를 발생시키지 않게 하기 위해 열거형을 사용
  • 열거형 정의 방법
enum Gender{
  MALE, FEMALE;  //Gender라는 enum에는 FEMALE과 MALE 두 개의 값만 들어올 수 있다고 정의
}
  • 열거형 사용 방법
    • enum인 Gender 자체가 타입이 됨 (따로 String 타입을 쓸 필요 X)
Gender gender2;

gender2 = Gender.MALE;
gender2 = Gender.FEMALE;

//gender2 = "boy"; //컴파일할 때부터 오류 발생

*//Gender타입의 변수에는 MALE이나 FEMALE만 대입이 가능. 다른 값은 저장할 수가 없음*

특정 값만 가져야 한다면 열거형을 사용하는 것이 좋음