[Java] 접근 제어자(access modifier) - public, protected, ( default ), private

접근 제어자(access modifier)

 

 


 

 

멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다.

 

접근 제어자가 지정되어 있지 않다면, 접근 제어자가 default이다.

default임을 알리기 위해 실제로 default를 붙이지 않는다.

 

접근 제어자가 사용될수 있는 곳은 클래스, 멤버변수, 메서드, 생성자 입니다.

 

 private  같은 클래스 내에서만 접근 가능하다.
 default  같은 패키지 내에서만 접근 가능하다. < -- 비추합니다.
 protected  같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근이 가능하다.
 public  접근 제한이 전혀 없다.

 

 

 대상 사용가능한 접근 제어자 
 클래스   public, (default)
 메서드   public, protected, (default), private
 멤버변수
 지역변수   없음

 

 

 

 


 

 

접근 제어자를 이용한 캡슐화

 

접근 제어자를 사용하는 이유는 무엇인가!?

클래스의 내부에 선언된 데이터를 보호하기 위해서 입니다.

 

비밀번호와 같은 데이터를 외부에서 함부로 변경하지 못하도록 하기 위해서는 외부로부터의 접근을 제한하는 것이 필요하다.

이것을 데이터 감추기(data hiding)이라고 하며, 객체지향개념의 캡슐화(encapsulation)에 해당하는 내용이다.

 

또 다른 이유는 클래스 내에서만 사용되는, 내부 작업을 위해 임시로 사용되는 멤버변수나 부분작업을 처리하기 위한 메서드 등의 멤버들을 클래스 내부에 감추기 위해서이다.

외부에서 접근할 필요가 없는 멤버들을 private으로 지정하여 외부에 노출시키지 않음으로써 복잡성을 줄일 수 있다.

 

1. 외부로부터 데이터를 보호하기 위해

2. 외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해

 

 

 


public class Test
{
    public static void main(String[] args) 
    {
        Time t = new Time(12, 35, 30);
        System.out.println(t);
        t.setHour(t.getHour() +1);
        System.out.println(t);
    }    
}

class Time
{
    private int hour;
    private int minute;
    private int second;
    
    Time(int hour, int minute, int second)
    {
        setHour(hour);
        setMinute(minute);
        setSecond(second);
    }
    
    public int getHour(){ return hour;}
    public void setHour(int hour)
    {
        if (hour < 0 || hour > 23) return ;    // 0~23의 값을 갖는 hour
        this.hour = hour;
    }
    public int getMinute(){ return minute;}
    public void setMinute(int minute){
        if(minute <0 || minute >59) return; // 0~59의 값을 갖는 minute
        this.minute = minute;
    }
    public int getSecond(){ return second;}
    public void setSecond(int second){
        if(second <0 || second > 59) return; // 0~59의 값을 갖는 second
        this.second = second;
    }
    
    public String toString()    //이건 오버라이딩인가?? 음.. ㅎ
    {
        return hour + ":" + minute +":" + second;
    }
}



 

 

 

* 아래 내용은 많이 중요합니다.

 

생성자 접근 제어자

 

생성자에 접근 제어자를 사용한다면 인스턴스의 생성 제한 할 수 있다.

 

생성자의 접근 제어자를 private으로 지정하면, 외부에서 생성자에 접근할 수 없으므로 인스턴스를 생성할 수 없게 된다. 그래도 클래스 내부에서는 인스턴스의 생성이 가능하다.

 

 

 


class Singleton
{
    private static Singleton s = new Singleton();    //getInstance()에서 사용될 수 있도록 인스턴스가 미리 생성되어야 하므로 static이어야 한다.
    private Singleton()
    {
    }
    
    //인스턴스를 생성하지 않고도 호출할 수 있어야 하므로 static이어야 한다.
    public static Singleton getInstance()
    {
        return s;
    }
}

 

이렇게 생성자를 통해 직접 인스턴스를 생성하지 못하게 하고, public 메서드를 통해 인스턴스에 접근하게 함으로써 사용할 수 있는 인스턴스의 개수를 제한할 수 있다.

 

생성자가 private인 클래스는 다른 클래스의 조상이 될 수 없다.

 

 

자손 클래스의 인스턴스를 생성할 때 조상클래스의 생성자를 호출해야만 하는데, 생성자의 접근 제어자가 private이므로 자손클래스에서 호출하는 것이 불가능 하기 때문이다.

 

 

Tip.

그래서 클래스 앞에 final을 더 추가하여 상속할 수 없는 클래스라는것을 알리는 것이 좋다.

 

 

Math클래스는 몇 개의 상수와 static메서드만으로 구성되어 있기 때문에 인스턴스를 생성할 필요가 없다. 그래서 외부로부터의 불필요한 접근을 막기 위해 다음과 같이 생성자의 접근 제어자를 private으로 지정한다.

 


public final class Math{
         private Math(){}
}

 


public class Test
{
    public static void main(String[] args) 
    {
//        Singleton s = new Singleton();    //Singleton()은 private이므로 접근할 수 없어요.
        Singleton s = Singleton.getInstance();
    }    
}

class Singleton
{
    private static Singleton s = new Singleton();    //getInstance()에서 사용될 수 있도록 인스턴스가 미리 생성되어야 하므로 static이어야 한다.
    private Singleton()
    {
    }
    
    //인스턴스를 생성하지 않고도 호출할 수 있어야 하므로 static이어야 한다.
    public static Singleton getInstance()
    {
        if(s == null)
        {
            s = new Singleton();
        }    return s;
    }
}

 

 

 

 

 

정리.

 대상 사용가능한 제어자 
 클래스 public, (default), final, abstract 
 메서드  모든 접근 제어자, final, abstract, static
 멤버변수  모든 접근 제어자, final, static
 지역변수  final


 

주의사항

 

 1. 메서드에 static과 abstract를 함께 사용할 수 없다.
 static메서드는 몸통이 있는 메서드에만 사용할 수 있기 때문이다.

 

 2. 클래스에 abstract와 final을 동시에 사용할 수 없다.
 클래스에 사용되는 final은 클래스를 확장할 수 없다는 의미이고,
 abstract는 상속을 통해서 완성되어야 한다는 의미이므로 서로 모순이 된다.

 

 

 

 

 

 

 

 

 

 

 

 3. abstract메서드의 접근 제어자가 private일 수 없다.
 abstract메서드는 자손클래스에서 구현해주어야 하는데 접근 제어자가 private이면,
 자손클래스에서 접근 할 수 없기 때문이다.

 

 

 

 

 

 4. 메서드에 private과 final을 같이 사용할 필요는 없다.
 접근 제어자가 private인 메서드는 오버라이딩될 수 없기 때문이다. 이 둘 주 하나만 사용해도 의미가 충분하다.

 

 


 

참고 서적

Java의 정석

http://book.naver.com/bookdb/book_detail.nhn?bid=4473030

[Java] 제어자 (modifier)에 대한 이야기

제어자 (modifier)




클래스, 변수 또는 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여한다.

제어자의 종류는 크게 접근 제어자와 그 외의 제어자로 나눌 수 있다.

 

 접근 제어자

 public, protected, default, private

 그외

 static, final, abstract, native, transient, synchronized, volatile, strict

 

 

제어자는 클래스나 멤버변수와 메서드에 주로 사용 된다.

단, 접근 제어자는 네 가지 중 하나만 선택해서 사용할 수 있다.

 

참고.

제어자들 간의 순서는 관계없지만 주로 접근 제어자를 제일 왼쪽에 놓는 경향이 있다.

 



 

1. static ? - 클래스의, 공통적인

 

static은 인스턴스와 상관 없다. '클래스의' 또는 '공통적인' 이라는 의미를 가지고 있다. 인스턴스 변수는 하나의 변수를 모든 인스턴스가 공유하기 때문이다.

 

인스턴스메서드와 static메서드의 근본적인 차이는 메서드 내에서 인스턴스 멤버를 사용하는가의 여부에 있다.

 

static이 사용될 수 있는 곳은 멤버변수, 메서드, 초기화 블럭 입니다.

 

 제어자

대상 

의미 

 

Static 

멤버변수 

 - 클래스 변수는 인스턴스를 생성하지 않고도 사용 가능하다.

 - 클래스가 메모리에 로드될 때 생성한다.

 메서드

 - 인스턴스를 생성하지 않고도 호출이 가능한 static 메서드가 된다.

 - static 메서드 내에서는 인스턴스멤버들을 직접 사용할 수 없다

 



 

참고.

static초기화 블럭은 클래스가 메모리에 로드될 때 단 한번만 수행되며, 주로 클래스변수(static 멤버변수)를 초기화하는데 주로 사용된다.

 



 

2. final  - 상수 개념, 마지막의, 변경될 수 없는



final 타입 변수 = 값 ;   ----> 값을 변경 할 수 없다.

final 타입 변수()         ----> 오버라이딩 못함.

 

클래스에 사용되면 자신을 확장하는 자손 클래스를 정의하지 못하게 된다.


final이 사용될 수 있는 곳은 클래스, 메서드, 멤버변수, 지역변수 입니다.

 

 제어자

대상 

 의미

 final

 클래스

   변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다.

   그래서 final로 지정된 클래스는 다른 클래스의 조상이 될 수 없다.

 메서드

    변경될 수 없는 메서드

   final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없다.

 멤버변수

    변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다.

 지역변수

참고. 대표적인 final 클래스로는 String과 Math가 있다.

 

 

생성자를 이용한 final 멤버변수 초기화

 

final이 붙은 변수는 상수이므로 일반적으로 선언과 초기화를 동시에 하지만, 인스턴스변수의 경우 생성자에서 초기화 되도록 할 수 있다.

 

클래스 내에 매개변수를 갖는 생성자를 선언하여, 인스턴스를 생성할 때 final이 붙은 멤버변수를 초기화하는데 필요한 값을 생성자의 매개변수로부터 제공받는 것이다.

 

 


public class Test{
    public static void main(String[] args) 
    {
        Card c = new Card("HEART", 10);
        System.out.println(c.KIND);
        System.out.println(c.NUMBER);
    }    
}
class Card
{
    final int NUMBER;    // 상수지만 선언과 함께 초기화 하지 않고
    final String KIND;        // 생성자에서 단 한번만 초기화할 수 있다.
    static int width = 100;
    static int height = 250;
    
    Card(String kind, int num)
    {
        KIND = kind;        //매개변수로 넘겨 받은 값으로 KIND와 NUMBER를 초기화한다.
        NUMBER = num;    
    }
    
    Card()
    {
        this("HEART",1);
    }
    
    public String toString()
    {
        return ""+ KIND +" "+NUMBER;
    }

}

 

 



 

3. abstract - 추상의, 미완성의

 

abstract는 ' 미완성 '의 의미를 가지고 있다.

메서드의 선언부만 작성하고, 실제 수행내용은 구현하지 않은 추상메서드를 선언하는데 사용된다.

 

abstract가 사용될 수 있는 곳은  클래스, 메서드 입니다.

 

 제어자

대상

의미 

 abstract

클래스

 클래스 내에 추상메서드가 선언되어 있음을 의미한다. 

 메서드

 선언부만 작성하고 구현부는 작성하지 않은 추상메서드임을 알린다.

 

참고.

추상메서드가 없는 클래스도 abstract를 붙여서 추상클래스로 선언하는 것이 가능하기는 하지만 그렇게 할 이유는 없다.





참고 서적

Java의 정석

http://book.naver.com/bookdb/book_detail.nhn?bid=4473030

 

[Java] package와 import

Package 와 Import

 


Package 


클래스의 묶음이며 폴더의 개념과 같다.

서로 관련된 클래스들끼리 그룹 단위로 묶어 놓음으로써 클래스를 효율적으로 관리할 수 있다.

 

같은 이름의 클래스라도 서로 다른 패키지에 존재하는 이 가능하다.

 

다른 개발자가 만든 클래스 라이브러리와 충돌되는것을 막을 수 있다.

단순히 클래스명으로만 클래스를 구분했지만

실제 클래스의 이름(Full name)은 패키지명을 포함한 것.

 

예를 들어

String클래스의 패키지명을 포함한 이름은 java.lang.String이다.


 



Package의 선언

 

package 패키지명;


  • 선언문은 반드시 소스파일에서 주석과 공백을 제외한 첫번째 문장이어야 한다.

  • 하나의 소스파일에는 단 한번만 선언될 수 있다.

  • 패키지는 점(.)을 구분자로 하여 계층 구조로 구성할 수 있다.

  • 패키지는 물리적으로 클래스 파일(.class)을 포함하는 하나의 디렉토리이다.

  • 패키지명은 대소문자를 모두 허용하지만,

    클래스명과 쉽게 구분하기 위해서 소문자로 하는것을 원칙으로 한다.

  • 모든 클래스는 반드시 하나의 패키지에 포함되어야 한다.



 



Import

 

참고.

import문은 프로그램의 성능에 전혀 영향을 미치지 않는다. import문을 많이 사용하면 컴파일 시간이 아주 조금 더 거릴 뿐.


import 패키지명.클래스명 ;

또는

import 패키지명.*;



'패키지명.*' 을 사용하면, 지정된 패키지에 속하는 모든 클래스를 패키지명 없이 사용할 수 있다.

 

클래스 이름을 지정해주는 대신 '*'을 사용하면, 컴파일러는 해당 패키지에서 일치하는 클래스이름을 찾아야하는 수고를 더 해야 할 것이다. 단지 그뿐이니까 실행 시 성능의 차이는 전혀 없다.! 걱정 끝!

 

단, 하위 패키지의 클래스까지 포함하는 것은 아니라는 것!


EX>

 

import java.util.*;

import java.text.*;

이걸

import java.*;

 

한다고 해서 되는게 아니라는 말씀.!

[Java] Super

Super



 

상속받은 멤버를 참조하는데 사용되는 참조변수

 



this VS super


멤버 변수와 지역변수의 이름이 같을 때 this를 사용해서 구별한다.

상속받은 멤버와 자신의 클래스에 정의된 멤버의 이름이 같을 때 super로 구별한다.

 


조상클래스로부터 상속받은 멤버도 자손 클래스 자신의 멤버이므로 super대신 this를 사용할 수 있다.

그러나,

조상 클래스의 멤버와 자손 클래스의 멤버가 중복 정의되어 서로 구별해야하는 경우에만 super를 사용하는 것이 좋다.

 

static메서드(클래스 메서드)는 인스턴스와 관련이 없다.

그래서 this와 마찬가지로 super역시 static메서드에서는 사용할 수 없고, 인스턴스 메서드에서만 사용할 수 있다.





조상 클래스, 자손 클래스에서 멤버변수명을 중복해서 정의하는 것이 가능하다.

참조변수 super를 이용해서 서로 구별한다.


class Parent
{
    int x= 10 ;
}
 
class Child extends Parent
{
    int x = 20;
 
void method()
{
    System.out.println(" x = " + x);
    System.out.println(" this.x = " + this.x);
    System.out.println(" super.x = " + super.x);
}
}


 

결과는?

 

x = 20;

this.x = 20;

super.x = 10;

 

 

 super.

 조상 클래스로부터 상속받은 멤버변수 x

 this.

 자손 클래스에 선언된 멤버변수 x


 

 



메서드 역시 호출할 수 있다.

 

특히!

조상 클래스의 메서드를 자손 클래스에서 오버라이딩한 경우에 super를 사용한다.

 

 



super() this()처럼 생성자이다.

 

this()의 경우에는 같은 클래스의 다른 생성자를 호출하는데 사용

super()는 조상 클래스의 생성자를 호출하는데 사용

 

 

조상클래스 멤버의 생성과 초기화 작업이 수행되어야 하기 때문에 자손 클래스의 생성자에서 조상 클래스의 생성자가 호출되어야 한다.

생성자의 첫 줄에서 조상 클래스의 생성자를 호출해야 하는 이유는

자손 클래스의 멤버가 조상클래스의 멤버를 사용할 수도 있으므로 조상 멤버들이 먼저 초기화되어 있어야 하기 때문이다.

 

Object클래스를 제외한 모든 클래스의 생성자는 첫 줄에 반드시 자신의 다른 생성자 또는 조상의 생성자를 호출해야 한다.

그렇지 않으면 컴파일러는 생성자의 첫 줄에 ' super() ; '를 자동적으로 추가할 것이다.

 

인스턴스를 생성할 때는 클래스를 선택하는 것만큼 생성자를 선택하는 것도 중요한 일이다.

 

1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?

2. 생성자 - 선택한 클래스의 어떤 생성자를 이용해서 인스턴스를 생성할 것인가?

 

 

 

 

[Java] 오버라이딩(Overriding)

오버라이딩(Overriding)






조상 클래스로부터 상속받은 메서드 내용 변경하는 것

 

 

조건?

  1.  이름이 같아야 한다.

  2.  매개변수가 같아야 한다.

  3.  리턴타입이 같아야 한다.

 

즉, 선언부는 일치해야 한다는 것이다.

단, 접근 제어자와 예외는 제한된 조건 하에서만 다르게 변경 할 수 있다.


오버라이딩할때 주의해야 하는 것들이 있다.


1. 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.

2. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.

3. 인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.

 



1. 접근 제어자는 조상 클래스의 메서드보다 좁은 범위변경 할 수 없다.

조상은 protected라면 오버라이딩 하는 자손은

protected 또는 public 이어야 한다.

 

참고.

넓은것 -> 좁은것 순서

public,  protected,  default,  private

 


 




2. 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.

class Parent

{

void parentMethod() throws IOExceptionSQLException

{

}

}

 

class Child extends Parent

{

void parentMehod() throws IOException

{
}

}

 

질문 : 만약 IOException이 아닌, Exception으로 예외 처리 한다면 될까?

 

답: NO ! 개수의 문제도 있지만,  Exception이 모든 예외의 최고 조상이므로 가장 많은 개수의 예외를 던지기 때문에 안된다!

 

 

 


 

 

[Java] 단일상속(single inheritance) & Object 클래스

단일상속(single inheritance) & Object 클래스



C++에서는 다중상속( multiple inheritance) 허용하지만 자바에서는 단일 상속만 허용한다.

 


다중상속의 장점 ?

복합적인 기능을 가진 클래스를 쉽게 작성




단점?

클래스간의 관계가 매우 복잡해진다는 것






Object 클래스


모든 클래스의 조상

 

 

다른 클래스로부터 상속 받지 않는 모든 클래스들은 자동적으로 Object 클래스로부터 상속받게 함으로써 이것을 가능하게 한다.

 

모든 상속계층도의 최상위에는 Object클래스가 위치한다.

따라서 모든 클래스들은 Object클래스에 정의된 멤버들을 사용할 수 있다.


toString()이나 equals(Object o)와 같은 메서드를 사용할 수 있다.

 

 

 

 

[Java] 상속(inheritance)에 대한 이야기

상속(inheritance)에 대한 이야기




  상속 ?

기존의 클래스 재사용하여 새로운 클래스를 작성하는 것.

 

 

  장점 ?

코드의 재사용성을 높이고 코드의 중복을 제거하여 

프로그램의 생산성과 유지보수에 크게 기여한다.

 

 

구현하는 것은 새로 작성하고자 하는 클래스의 이름뒤에 상속받고자 하는 클래스의 이름을 키워드 'extends'와 함께 써 주기만 하면 된다.

 

class Child extends Parent

{
}

 

조상 클래스 - 부모 (parent) 클래스 , 상위(super) 클래스,  기반(base)클래스

자손 클래스 - 자식(child)클래스,       하위(sub)클래스,      파생된(derived) 클래스

 



상속을 받는다는 뜻은 조상 클래스를 확장(extends)한다는 의미한다.

  • 생성자 초기화 블은 상속되지 않는.

  • 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.

 

 

참고.

접근제어자(access modifier)로 private 또는 default가 사용된 멤버들은 상속되지 않는다기보다는

상속은 받지만 자손 클래스로부터의 접근이 제한되는 것으로 보는것이 옳다.

 

 



Tip.

Parent 클래스, Child 클래스 , Child2 클래스가 있다.

 

만일 Child클래스와 Child2클래스에 공통적으로 추가되어야 하는 멤버(멤버변수나 메서드)가 있다면, 이 두 클래스에 각각 따로 추가해주는 것보다는

이들의 공통조상인 Parent클래스에 추가하는 것이 좋다.

 

코드를 한 곳에서 관리함으로써 코드의 중복이 줄어든다는 것이다.

 

하나 이상의 클래스에 중복적으로 추가해야하는 경우에는 상속관계를 이용해서 코드의 중복을 최소화해야한다.

 

상속이외에도 클래스를 재사용하는 또 다른 방법

그것은!

클래스 간에 '포함(Composite)관계'를 맺어 주는 것이다.

 

 



포함관계


한 클래스의 멤버변수로 다른 클래스를 선언하는 뜻



class Circle
{
    int x ;
    int y ;
    int r ;
}
 
class Point
{
    int x;
    int y;
}
 
class Circle
{
    Point c = new Point(); //원점
    int r ;
}


 

요렇게 포함관계를 만들수 있어요.

 

하나의 거대한 클래스를 작성하는 것보다 단위별로 여러 개의 클래스를 작성한 다음, 이 단위 클래스들을 포함관계로 재사용하면 보다 간결하고 손쉽게 클래스를 작성할 수 있다.


 

 

상속 : ' ~은  ~이다.'

포함 : ' ~은 ~을 가지고 있다.'

 

Ex >

원(Circle)은 점(Point)이다. - Circle is a Point

원(Circle)은 점(Point)을 가지고 있다. - Circle has a Point

 

여기서 보면 두번째 문장이 더 옳다는것을 알수 있으므로 포함관계!

 

 

 

 

 

 

 

 

[Java] 변수 초기화에 대한 이야기

변수 초기화에 대한 이야기




 

가능하면 선언과 동시에 적절한 값으로 초기화 하는 것이 바람직하다.

 

멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어지므로 초기화하지 않고 사용해도 되지만 

지역변수는 사용하기 전에 반드시 초기화를 해야한다.

 

 




멤버변수의 초기화 방법


멤버변수를 초기화 하는데에는 3가지 방법이 있다. 

생성자(constructor), 명시적 초기화 ( Explicit initialization), 초기화 블럭(initialization block)

생성자는 앞선 포스트에서 적었기 때문에 패스하고 명시적 초기화, 초기화 블럭에대해 정리하겠습니다.

 



1. 명시적 초기화 (Explicit initialization)


변수를 선언과 동시에 초기화하는 것을 명시적 초기화라고 한다.

 

class  Character

{

          //기본형 변수의 초기화

int age =4; 


//참조형변수의 초기화

Homework e = new Homework();

}

 



2. 초기화 블럭 (initialization block)


' 클래스 초기화 블럭 ' 과 '인스턴스 초기화 블럭 ' 두 가지 종류가 있다.



  클래스 초기화 블럭

  인스턴스 초기화 블럭

 클래스변수의 초기화에 사용

  인스턴스변수의 초기화에 사용

 클래스가 메모리에 처음 로딩될 때 

한번만 수행

 생성자와 같이 인스턴스를 

생성할 때 마다 수행

  인스턴스 초기화 블럭 앞 static을 덧붙이기

클래스 내에 블럭 { } 


생성자 보다 인스턴스 초기화 블럭이 먼저 수행된다.


인스턴스 초기화 블럭은 단순히 클래스 내에 블럭 { }을 만들고 그 안에 작성하면 된다.

클래스 초기화 블럭 인스턴스 초기화 블럭앞에  static을 덧붙이기만 하면 된다.

 

class Character

{

 static{  /* 클래스 초기화블럭 */  }

{  /* 인스턴스 초기화 블럭 */  }

}




스턴스 변수 초기화는 주로 생성자안에서 이루어지기 때문에, 잘 사용되지 않는다.

대신 클래스의 모든 생성자에서 공통적으로 수행되어져야 하는 코드가 있는 경우에는

인스턴스 초기화 블럭에 넣어두면 코드의 중복 줄일 수 있다.

 


 Character()
{
     //중복
     System.out.println("Character 인스턴스가 생성되었습니다.");
     this("GYU", false, 27);
 }
 Character(String _name)
 {
     //중복
    System.out.println("Character 인스턴스가 생성되었습니다.");
     this(_name, false, 27);
 }


이러는 것 보다

 

{ System.out.println("Car 인스턴스가 생성되었습니다.");  } // 인스턴스 블럭
Character()
{
      this("GYU", false, 27);
 }
 Character(String _name)
 {
       this(_name, false, 27);
 }

훨씬 간결해진다.


 

객체지향프로그래밍이 추구하는 궁극적인 목표는 뭐!??

재사용성을 높이고, 중복을 제거하는 것!!

 

 


멤버변수의 초기화 시기와 순서

 

 

시기

 

Class - 클래스가 처음 로딩될 때 단 한번 초기화

Instance - 인스턴스가 생성될 때마다 각 인스턴스별로 초기화

 

 

순서

 

Class

1. 클래스 변수 기본값

2. 클래스 명시적 초기화

3. 클래스 초기화 블럭

 

Instance

1. 인스턴스 변수 기본값

2. 인스턴스 변수의 명시적 초기화

3. 인스턴스 초기화 블럭

4. 생성자

 

 

참고.

클래스의 로딩시기는 JVM의 종류에 따라 좀 다를 수 있다. 클래스가 필요할 때 바로 메모리에 로딩하도록 설계가 되어 있는 것도 있고, 실행효율을 높이기 위해서 사용될 클래스들을 프로그램이 시작될 때 미리 로딩하도록 되어 있는 것도 있다.

[Java] 생성자(Constructor)에 대한 이야기

생성자(Constructor)에 대한 이야기




  생성자란 ?

인스턴스가 생성될 때 호출되는 ' 인스턴스 초기화 메서드'이다.

 

  • 생성자의 이름은 클래스의 이름과 같아야 한다.

  • 생성자는 리턴 값이 없다.

  • 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것은 아니다.

 


Character c = new Character();

 

순서

  1.  연산자 new에 의해서 메모리(heap) Character 클래스의 인스턴스가 생성된다.

  2. 생성자 Character()가 호출되어 수행된다.

  3. 연산자 new의 결과로, 생성된 Character인스턴스의 주소가 반환되어 참조변수c에 저장된다.

 



 

기본 생성자(Default Constructor)

 

사실 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다.

정의된 생성자가 하나도 없을 경우 컴파일러Compiler ) 기본생성자를 추가한다.

 

그러나! 만약에 매개변수가 있는 생성자가 정의 되어 있다면,

컴파일러가 기본 생성자를 생성해 주지 않는다.

 

컴파일러가 자동적으로 기본 생성자를 추가해주는 경우는 

' 클래스 내에 생성자가 하나도 없을 때 ' 뿐이라는 것.

 

 

 



생성자에서 다른 생성자 호출하기 this(), this

 

아래의 두가지를 만족시켜야 생성자 간에도 서로 호출이 가능하다.

 

  1. 생성자의 이름으로 클래스이름 대신 this를 사용한다.

  2. 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.

 

 thi

 인스턴스 자신을 가리키는 참조변수(인스턴스의 주소)이다.

 모든 인스턴스메서드에 지역변수로 숨겨진 채로 존한다.

 this(), this(매개변수) 

 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용한다.


class Character
{
    String name;
    bool man; 
    int age;
 
 
    Character()
    {
        this("GYU", false, 27);
    }

    Character(String _name)
    {
        this(_name, false, 27);
    }
 
    Character(String _name, bool _isMan, int _age)
    {
        this.name = _name;
        this.man= _isMan;
        this.age = _age;
    }
}


생성자 Character()에서 또 다른 생성자 Character(String _name, bool _isMan, int _age)를 호출한다.


이처럼 생성자간의 호출에는 생성자의 이름 대신 this를 사용해야만 하므로 'Character'대신 'this'를 사용했다. 그리고 생성자 Character() 첫째 줄에서 호출했다는 점이 중요하다.

 



여기서 잠깐!

 

왜 첫 줄에서만 호출이 가능한 걸까?


생성자 내에서 초기화 작업도중에 다른 생성자를 호출하게 되면, 호출된 다른 생성자 내에서도 멤버변수들의 값을 초기화를 할 것이므로 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질 수 있기 때문!

 

 

좋은 점은 뭘까?


 매개변수 이름을 다르게 하는 것 보다 this를 사용해서 구별되도록 하는 것이 의미가 더 명확하고 이해하기 쉽다. 수정이 필요한 경우에도 보다 적은 코드만을 변경하면 되므로 유지보수가 쉬워진다.



static메서드(클래스 메서드)에서는 인스턴스 멤버들을 사용할 수  없는 것처럼, this 역시 사용할 수 없다.

왜냐하면, static메서드는 인스턴스를 생성하지 않고도 호출될 수 있으므로 static메서드가 호출된 시점에 인스턴스가 존재하지 않을 수도 있기 때문이다.


 

 

 

 

 

 

 

 

 

 

 

[Java] 재귀호출에 대한 이야기

재귀 호출(recursive call)




메서드의 내부에서 메서드 자기 자신을 다시 호출하는것

재귀호출은 반복문이 무한반복에 빠지는 것처럼 무한호출이 되기 쉬우므로 주의해야한다.


재귀호출은 다소 효율이 떨어진다는 단점이 있다.! 반복적으로 메서드를 호출하는 것이기 때문에 메서드를 호출하는데드는 비용이 추가적으로 발생하기 때문이다.

그래도 가능하면 재귀호출을 통해 간단한 코드를 작성하는 것이 좋으며, 성능에 문제가 되는 경우에만 재귀호출 대신 반복문을 사용하도록 하자.

 

대표적인 재귀호출의 예는 팩토리얼(factorial)을 구하는 것.

 

static long factorial(int n)

{

      long result =0;

 

if( n == 1){

result =1;

}else{

result =n *  factorial(n-1) ;   // 다시 자기 자신을 호출한다.

}

return result ;

}

 

//이 함수를 삼항연산자 한줄로 요약하면

// return (n == 1) ? 1 : n * factorial(n-1);

}