Home Java 프로그래밍 교육 4일차
Post
Cancel

Java 프로그래밍 교육 4일차

접근 지정자(Access Modifier) 
클래스 내 필드나 메서드의 접근 허용 범위를 지정한다.

접근 지정자허용 범위
private 같은 클래스 내에서만 접근 가능
없음(default) 같은 디렉터리 내에서만 접근 가능
protected 상속 관계에서는 어디서든 접근 허용(public)  상속이 아닌 경우 같은 디렉터리 내에서만 접근 허용(default)
public어디서든 접근 허용

생성자는 접근지정자를 지정하지 않으면 클래스의 접근 지정자를 따라간다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Parent{
   public String publicVar = "public";
   protected String protectedVar = "protect";
   String var = "default";
   private String privateVar = "private";
}

class Child extends Parent{
   public void testAccess() {
      System.out.println(publicVar); 
      // 어디서든 접근 가능
      System.out.println(protectedVar); 
      // 서로 다른 패키지이지만 상속관계이기때문에 접근 가능
      System.out.println(var); 
      // 접근 불가 (The field Prarent.var is not visible.)
      System.out.println(privateVar); 
      // 접근 불가 (The field Prarent.privateVar is not visible.)
   }
}


상속(Inheritance) 
다양한 서브(자식) 클래스들을 슈퍼(부모) 클래스에서 단일하게 관리하기 위해 사용한다. 동일한 부모 클래스를 상속받는 경우 자식 클래스들을 하나의 배열로 묶을 수 있다. 이러한 배열을 Heterogeneous Collection 라고도 한다.

  • 부모가 가진 필드는 자식에게 물려진다. 자식은 부모의 필드에 직접 접근해서 쓰면 된다.
  • 부모가 가진 메서드는 자식에게 물려진다. 자식은 자신에게 맞도록 고쳐서 사용한다. > 오버라이딩(Overriding)


다형성(Polymorphism)
부모 타입으로 다양한 객체를 생성하는 것이다.

1
2
3
4
5
class Child extends Parent{...}

Child child = new Child();
// 부모 타입으로 지정 후 자식 생성자 호출 가능
Parent child = new Child();

다형성을 활용해 부모 타입으로 자식 객체 생성 시 주의해야할 점이 있다.

1
2
3
4
5
6
7
8
9
//부모 클래스
public class Parent{
   private String p1;
   private int p2;
   
   public void parentMethod(){
      ...
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
//자식 클래스
public class Child extends Parent{
   private String c1;
   
   @Overriding
   public void parentMethod(){
      ...
   }
  
   public void childMethod(){
      ...
   }
}

위에 부모 클래스와 이를 상속받은 자식 클래스가 존재한다. 자식 클래스에서 부모 클래스의 메서드를 오버라이딩한다.

1
2
3
Parent obj = new Child();
obj.parentMethod(); // 호출 가능
obj.childMethod(); // 자식 메서드 호출 불가 (The method childMethod() is undefined for the type Parent.)
  • Virtual Method Invocation 에 따라 부모 메서드 호출 시 부모의 메서드나 자식에서 오버라이딩된 메서드를 호출한다. 상속관계에서 오버라이딩된 동일한 이름의 메서드가 충돌했을 때 런타임 시점의 메서드가 컴파일 시점의 메서드보다 우선 실행된다. 컴파일 시점에는 부모(Parent) 메서드가 호출되지만 JVM은 런타임 시점에 자식(Child) 메서드를 사용한다.
  • 부모에겐 없고 자식에게만 있는 메서드 호출은 불가하다. Child() 생성자 호출(변수 생성) 시 메모리에는 올라가있으나 변수가 부모 타입으로 지정되어있어 메서드를 찾지 못한다. 이때 객체 형변환(Object Casting)이 필요하다.
1
({필요한_클래스}) {형변환_대상_클래스}

형변환 이후 자식의 메서드 호출이 가능하다.

1
2
3
4
Child obj2 = (Child) obj;
obj.childMethod();
// 또는
((Child)obj).childMethod();

만약 자식 클래스에서 메서드 구현 시 부모 클래스의 필드가 필요하면 super 키워드를 사용한다.

1
2
3
public void childMethod(){
   String s = super.p1; // Parent.p1
}

부모 타입으로 자식 객체 생성할 때, 자식만의 멤버(필드, 메서드)를 호출하고자할 때 반드시 Object Casintg

부모 타입(B)과 자식 타입(A)이 상속관계가 맞는지 확인하는 키워드는 instanceOf 이다.

1
A instanceOf B

A가 B에 속하거나 A가 B를 상속받는 경우 true를 리턴하고 아니면 false를 리턴한다.

Polymorphic Argument 부모 타입의 인자값을 hasing 함으로써 다양한 자식 클래스들을 단일하게 관리할 수 있다.

Static 키워드 
static은 고정된 이라는 의미를 갖고 있다. 해당 키워드를 사용하여 만든 변수를 정적 변수(멤버) 또는 전역 변수라고도 한다.
[참고] https://honbabzone.com/java/java-static/

  • static으로 지정된 멤버는 객체 생성과정 없이 바로 메모리에 올라간다. 클래스 로더가 해당 키워드를 보는 순간 객체 생성이 되지 않아도 메모리를 할당한다. static키워드가 붙은 멤버들은 객체(인스턴스)에 소속된 변수가 아닌 클래스에 소속된 변수로 클래스 변수, 클래스 메서드라고도 한다.
  • static한 변수는 멤버 레벨로만 사용된다.
    • static은 객체 생성과정이 없다. 메모리에 올라가지 않은 상태로 로컬 레벨에서 사용하지 못한다.
    • static 메서드에서 로컬 필드는 사용하지 못한다.
    • static 키워드로 생성된 메서드는 이미 메모리에 올라가있기 때문에 변수 생성 안하고 바로 사용 가능하다. (대표적으로 Integer.parseInt() 등이 있다.)
    • static이 아닌 멤버는 변수 생성, 초기화 후 접근 가능하다. 메모리에 올라간 후 로컬 변수로 사용 가능하기 때문이다. 따라서 해당 클래스로 변수 생성 후 필드에 접근한다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    class Example {
    int count=1; 
        
    // 필드 레벨에서 static 사용 가능
    static String name = "홍길동"; // static 변수 또는 class 변수라 한다.
    static int age = 19;
        
    public void method1() {
        static String address = "민속촌"; // 로컬 레벨(메서드 내 변수)에서 static 사용 불가
    }
        
    public static void method2() {
        count++; // static 메서드 내에서 로컬 변수 사용 불가
    }
    }
    
    // 필드 접근
    public static void main(String[] args) {
    Example.method2(); // static 필드 바로 사용 가능
    Example.name; 
        
    Example e = new Example();
    e.count; // static 아닌 필드 변수 생성 후 사용 가능
    e.method1(); 
    }
    
  • static한 변수는 같은 값을 공유한다.new 키워드로 생성된 객체는 서로 독립적이지만 static으로 생성된 객체들은 메모리 영역을 공유한다.
  • static은 거의 final 키워드와 함께 사용된다.
  • SingleTone Pattern 구현에 사용된다.

static의 동작 과정과 메모리 구조는 아래와 같다.

1
2
3
4
5
6
7
8
class Obj {
   int num; 
   static int counter; // 필드, static 변수 둘 다 기본값 0으로 초기화
   Obj() { // 생성자 - 카운터 증가 후 num에 저장
      counter ++;
      num = counter;
   }
}

먼저 필드로 num과 static 변수인 counter를 지닌 클래스 Obj가 있다.

1
2
3
Obj obj1 = new Obj();
Obj obj2 = new Obj();
Obj obj3 = new Obj();

객체 Obj 3개를 생성했다.

image

  1. obj1 변수 생성 시 Stack에 공간이 할당되고 그에 따라 Heap에는 obj의 멤버 필드 num가, Class Area에는 static 변수인 counter가 올라간다.
  2. Heap에 num의 값과 Class Area의 counter 모두 0으로 초기화된다. 
  3. Heap 객체 주소가 라벨링되고 Stack의 obj1에 해당 객체 주소값을 저장한다.
  4. 생성자 구현부에 따라 counter는 1 증가해 1로 초기화되고 obj.num은 1로 초기화된다. 
  5. 두 번째 obj2 객체 생성 시 1~3 동일한 과정을 반복한다. 이때 counter는 이미 있는 변수로 생성 과정 생략한다.
  6. 생성자 구현부에 따라 counter가 2로 증가하고 ojb2.num은 2로 초기화된다. (명시적 초기화)
  7. 세 번째 obj3 객체 생성 시 1~3 동일한 과정을 반복한다. 마찬가지로 counter 생성은 생략된다.
  8. 마찬가지로 생성자 구현부에 따라 counter가 1 증가해 3이 되고 obj3.num은 3으로 초기화된다. (명시적 초기화)
1
2
3
System.out.println("obj1 "+obj1.num); //1
System.out.println("obj2 "+obj2.num); //2
System.out.println("obj3 "+obj3.num); //3
1
2
3
4
System.out.println("obj1 "+obj1.counter); //3
System.out.println("obj2 "+obj2.counter); //3
System.out.println("obj3 "+obj3.counter); //3
System.out.println("Obj "+Obj.counter); //3
  • 객체마다 다른 값을 저장해야할 때 필드
    static이 아닌 그냥 필드는 Heap에 존재하며 각 객체마다 메모리 공간을 갖고 각자 다른 값을 갖는다.
  • 객체간 모두 동일한 값을 공유해야할 때 static
    반면 static 변수는 객체마다 갖고 있는 값이 아니라 별도 공간에 객체 상관없이 존재한다. 생성된 객체들은 값을 공유한다. 주로 static은 변경되지 않고 공유되는 값으로 쓰이기 때문에 final 키워드와 함께 쓰인다.
    static final 이나 final static 둘 다 가능한 표현이다.

 final 키워드는 고정된 값을 지정하고자할 때 사용된다. 변수와 메서드, 클래스 모두에서 사용 가능한 키워드이다.

  • final 변수의 값은 변하지 않는다. 상수 정의 시 사용한다.
  • final 메서드는 변하지 않는 메서드로 자식이 부모의 메서드를 오버라이딩 하지 못한다. 
  • final 클래스는 최종 클래스로 상속하지 못하는 클래스를 정의한다.

객체 생성 과정 생략을 위해 static을 남발해서는 절대 안된다!
static 지정 변수는 Heap이 아닌 Class Area 라는 JVM 내 다른 공간에 올라간다. Heap은 GC의 동작 대상인 반면 static 변수가 올라가는 Class Area는 대상이 아니다. static 변수 남발 시 메모리 해제가 되지 않고 곧 퍼포먼스 저하를 일으킨다.
Heap에는 new 키워드를 통해 생성된 객체나 생성 후 참조가 끊어진 객체들이 있으며 GC(Garbage Collector)의 대상이다.

싱글톤 패턴(SingleTone Pattern)
하나의 클래스 타입으로 오직 단 하나의 인스턴스만 생성되도록 코드를 강제하는 패턴이다.
객체 지향적인 자바 특성과는 다소 반대되는 개념이나 서비스단에서 요청에 대한 매핑을 메서드가 하여 서버의 과부하를 방지한다.
메서드 하나가 서비스 하나 즉, 클래스가 아닌 메서드가 서비스의 단위가 되어야한다. 서비스 클래스는 1개여야하며 요청이 아무리 많아도 서비스 객체는 여전히 1개여야한다. 서비스 클래스 하나만 메모리에 올리고 서비스 메서드가 각 요청에 대한 비즈니스 로직을 처리하도록 한다.

하나의 클래스로부터 단 한 개의 인스턴스만 생성하는 패턴이다. 코드 작성 순서는 아래와 같다.

  1. 일단 클래스 안에서 싱글톤 패턴을 적용시킬 하나의 객체는 생성한다. private static 으로 지정한다.
    1
    
     private static XXService service = new XXService();
    
  2. 다른 클래스에서는 해당 클래스 타입으로 객체 생성을 못하도록 막는다. 생성자를 private 로 지정하여 접근을 막아 객체 생성을 못하게할 수 있다.
    1
    
     private XXService() {};
    
  3. 1번에서 하나 생성해둔 객체는 여기저기서 불러다 쓸 수 있도록(주입받을 수 있도록) public 메서드를 하나 만든다. 여기서 중요한 점은 객체 생성을 막아두었음으로 객체 생성 없이 쓸 수 있도록 static으로 지정한다.

    1
    2
    3
    
     public static XXService getInstance() { 
         return service;
     }
    
  4. 해당 객체를 사용할 곳에서 주입을 위한 메서드를 호출한다. static으로 지정했기에 변수 생성(new)없이 호출할 수 있다.

    1
    
     XXServcie.getInstance();
    

만약 주입 메서드를 여러번 호출하여 여러개의 객체를 만들더라도 모든 객체는 메모리상 하나의 객체만 참조하고 있다.

1
2
3
4
5
6
7
XXService s1 = XXService.getInstance();
XXService s2 = XXService.getInstance();
XXService s3 = XXService.getInstance();

System.out.println(s1); // XXService@626b2d4a
System.out.println(s2); // XXService@626b2d4a
System.out.println(s3); // XXService@626b2d4a 모두 같은 주소값

Service 클래스의 기능들은 클라이언트가 요청하는 각각의 서비스를 수행하는 것들이다. 요청 당 서비스 클래스가 매핑되는 것이 아니라 서비스 클래스의 기능(메서드) 하나가 완벽하게 매핑되는 구조이다. 서비스 클래스는 아무리 요청이 많이 들어와도 무조건 하나만 서버상에 생성되고 하나의 객체 안의 기능들이 요청을 처리하는 완벽한 단위가 되어야한다.

스프링 DAO의 경우 서버에서 디폴트로 싱글톤으로 동작하도록 설정되어있다고 하는데 이는 더 알아봐야겠다.


변수 부르는 말 구분하기

  • field 클래스 내 멤버이다.
  • static variable static으로 지정된 변수이며 Heap이 아닌 별도의 Class Area에 하나만 존재하고 생성된 객체들이 동일 값을 공유한다.
  • local variable 메서드 내에서 선언하여 사용하는 변수로 메서드 호출 시 생성되었다가 메서드의 수행이 완료되면 함께 사라진다.

지정자(Modifiler)

  • Access Modifier(접근 지정자) public, protected, private
  • Usage Modifier static, final, abstract, synchronized
This post is licensed under CC BY 4.0 by the author.

Java 프로그래밍 교육 3일차

Java 프로그래밍 교육 5일차(와 후기)