[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);

}

[Java] JVM의 메모리 구조

JVM의 메모리 구조




응용프로그램이 실행되면,

JVM은 시스템으로부터 프로그램을 수행하는데 필요한 메모리를 할당받고

JVM은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.

 

1. 메서드 영역 (Method Area)  - 클래스

 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일(*.class)을 읽어서 분석하여 클래스에 대한 정보 (클래스 데이터)를 이곳에 저장한다. 이때, 그 클래스의 클래스 변수(class variable)도 이 영역에 함께 생성된다.

 

2. 힙 (Heap) -  인스턴스

 인스턴스가 생성되는 공간. 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성된다. 즉, 인스턴스 변수(Instance Variable)들이 생성되는 공간.

 

3호출스택(call stack 또는 execution stack)  - 메서드

 호출스택은 메서드의 작업에 필요한 메모리 공간을 제공한다.

 메서드가 호출되면, 호출스택에 호출된 메서드를 위한 메모리가 할당되며, 이 메모리는 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용된다. 그리고 메서드가 작업을 마치면 할당되었던 메모리 공간은 반환되어 비워진다.

 

 호출 스택의 제일 상위에 위치하는 메서드가 현재 실행 중인 메서드이며, 나머지는 대기상태에 있게 된다.

아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드 이다.


main()
{
    // 멤버변수 x 가 있음.
    Data d = new Data();  
    change(d.x);
}

change(int x){}


 

이러면 기본형 매개변수로 변수의 값이 변하지는 않음.

대신


main()
{
    Data d = new Data();
    change(d);
}
change(Data d){}


이러면 참조형 매개 변수로 값이 변해요.

 

 

 

 

 

 

 

[Java] 반환값 return의 TIP

 반환값 return의 TIP




return문을 여러번 쓰는 것보다 가능하면 아래의 오른쪽 코드와 같이 변수를 사용해서 

리턴값을 저장했다가 마지막에 한번만 사용하는 것이 좋다.

 

int max(int a, int b)
{
    if( a > b)
        return a;
    else
        return b;
}
 
int max(int a, int b)
{
    int result = 0;
    if( a > b)
        result = a;
    else
        result =  b;
 
    return resultt;
}



리턴 값이 있는 메서드를 리턴값이 없는 메서드로 바꾸는 방법!


참조형 매개변수를 활용하면 리턴값이 없어도 메서드의 실행결과를 얻어 올 수 있다.!

이방법을 응용하면 여러 개의 값을 리턴 받는것과 같은 효과를 얻을 수 있다.


int add(int a, int b)
{
    return a+b;
}
 
void add(int a, int b, int[] result)
{
    result[0] = a + b;
}