점프투자바

SUADI·2022년 5월 25일

7. 자바 날개 달기

(1) 패키지(Package)

ㄱ) 패키지

사진을 정리할 때 어떤 종류의 사진이냐에 따라 가족사진, 풍경사진 등 폴더별로 정리를 해놓으면 분류가 용이해진다. 사진을 정리해 놓은 나뿐만 아니라 어느 다른 사람이 원하는 사진을 찾고 싶을 때에도 찾기 수월하다. 패키지는 종류별로 나뉜 사진 폴더와 같다. 패키지는 비슷한 성격의 클래스들을 분류해 놓은 자바의 디렉토리이다. 자바 코드를 작성하다 보면 다른 사람의 자바 클래스나 라이브러리를 사용해야 하는 경우가 있는데 이 때 혹시 자바 클래스 명이 내가 만든 클래스명과 이름이 겹치거나 하더라도 패키지가 다르면 서로 충돌할 일이 없다.

  • 특징 1 : 클래스의 분류가 용이하다.
  • 특징 2 : 패키지가 다르면 동일한 클래스 명을 사용해도 문제없다.
package family;

class parents {}
class sibling {}
package friends;

class highSchool {}
class college {}
package foods;

class parents {} 
// 부모님과 외식할 때 찍은 사진이라고 치자.
// family 패키지에서도 parents 클래스가 있지만 패키지가 달라 문제없다.
class koreanFood {}

ㄴ) 서브패키지

패키지 내에 또 다른 패키지를 만들 수 있는데 이를 서브패키지라고 한다. 서브패키지 명은 패키지명.서브패키지명 으로 짓는다.

package family.fatherSide;
...

package family.matherSide;
...

(2) 접근제어자(Access Modifier)

접근제어자란 변수, 메소드, 클래스 등의 사용권한을 설정할 수 있는 기능이다. private -> default -> protected -> public 순으로 보다 많은 접근을 허용한다. 이와 같이 접근의 허용 범위를 지정하는 이유는 정보 은닉(Data Hiding)을 위해서이다. 프로그램을 사용할 사용자가 알 필요 없는 많은 정보를 숨겨 최소한의 정보만 가지고도 프로그램을 사용할 수 있도록 하게 하기 위함이다.

ㄱ) private

private 접근제어자는 메소드, 변수 등을 해당 클래스 내에서만 사용할 수 있게 하는 기능이다.

package family;

class parents {
	private int fatherAge = 63;
}

class siblings {
	// fatherAge 변수 사용 못함
}

ㄴ) default

default 접근제어자는 동일 패키지 내에서만 접근이 가능하다. 따로 접근제어자를 지정하지 않으면 자동적으로 자바가 default 접근제어자로 설정한다.

package friends;

class highSchool {
	default String class1 = "Avengers";
}

class college {
	String getGroupName { // default가 생략된 메소드
    	return "Five Guys";
    }
}

default가 붙은 변수,메소드 모두 friends 패키지 내에서만 이용가능하다.

ㄷ) protected

protected 접근제어자는 동일 패키지뿐만 아니라 접근제어자가 붙은 변수 혹은 메소드를 포함한 클래스를 임포트한 다른 패키지에서만 사용가능하다.

package family;

class parents {
	protected String lastName = "Kang";
}
package family.fatherSide;

import family.parents;

public class KangFamily extends parents {
	public static void main(String[] args) {
    	KangFamily kang = new KangFamily();
        
       	System.out.println(kang.lastName); // Kang 출력
    }
}

lastName 변수의 접근제어자가 protected였기 때문에 다른 패키지에서도 parents 클래스를 임포트해서 lastName 변수를 사용할 수 있었다. default나 private이였다면 컴파일 에러가 났을 것이다.

ㄹ) public

public 접근제어자는 어디서든 사용할 수 있게 해주는 기능이다.
접근제어자를 모두 public으로 사용해도 프로그램은 잘 동작하겠지만 접근제어자를 잘 이용하면 코딩 실수를 방지할 수 있고, 기타 위험요소를 제거할 수 있다.

(3) 정적(static) 변수와 메소드

자바에서 객체는 각각 독립적으로 존재한다. 만약 프로그램 전역적으로 자주 재사용되는 변수나 메소드가 있다면 굳이 독립적으로 존재할 필요가 있을까? 이럴 때 쓰는 키워드가 staic이다.

ㄱ) 정적(Static) 변수

class HouseLee {
	String lastName = "Lee";
}

public class Sample {
	public static void main(String[] args) {
    	HouseLee lee1 = new HouseLee();
    	HouseLee lee2 = new HouseLee();
    }
}
  • main 메소드에 생성된 객체 2개는 독립적이기 때문에 HouseLee 클래스 내의 lastName 변수를 각각 가지고 있다. 하지만 굳이 메모리를 두배로 쓰지 않아도 되는 경우가 있는데 이럴때 static이라는 키워드를 사용하면 메모리를 별도로 할당하지 않을 수 있다.
	static String lastName = "Lee";
  • 변수에 저장된 값을 고정시켜 변경되지 않게 하도록 하려면 final이라는 키워드를 사용하면 된다. 변수를 변경하려고 하면 컴파일 에러가 나게 된다.
	final static String lastName = "Lee";
  • static을 사용하는 이유는 단순히 메모리의 이점이 있어서도 있지만 변수의 값을 공유하려는 의미도 있다. 예를 들어 웹페이지의 조회수를 기록하는 코드를 작성한다고 하면,
class Counter {
	int count = 0;
	Counter() {
    	this.count++;
        System.out.println(this.count);
    }
}

public class Sample {
	public static void main(String[] args) {
    	Counter c1 = new Counter();
    	Counter c2 = new Counter();
    }
}
1
1
  • Counter 클래스를 생성한 후, 생성자에 객체를 생성할 때마다 count변수가 1씩 커지고, count변수를 출력하도록 했다. main 메소드에 객체를 두개 생성했으므로 1과 2가 출력되야만 목표한 결과가 나오는 것이지만 1,1이 출력된다. 이렇게 되는 이유는 c1과 c2는 서로 다른 메모리를 갖기 때문이다. 메모리를 공유하도록 static 키워드를 사용하면
class Counter{
	static int count = 0;
    ...
    
1
2
  • 객체간 값을 공유하기 때문에 1,2가 출력된다.

ㄴ) 정적(static) 메소드

class Counter {
	static int count = 0;
    String line = "테스트용 변수";
	Counter() {
    	count++;
        System.out.println(count);
    }
    
    public static int getCount() {
    	return count;
    }
}

public class Sample {
	public static void main(String[] args) {
    	Counter c1 = new Counter();
    	Counter c2 = new Counter();
        
        System.out.println(Counter.getCount());
        System.out.println(c1.getCount());
        
    }
}
1
2
2
2
  • getCount라는 static method를 생성해서 count를 리턴하게끔 했다. static method 역시 값을 공유하기 때문에 2를 출력한다.

  • static 메소드의 또 다른 특징은 객체를 생성하지 않고도 클래스를 통해 메소드를 호출할 수가 있다.

  • static 메소드에서는 객체변수가 접근하지 못하는 특징도 가지고 있다. line 객체 변수를 static 메소드인 getCount에서 사용하려고 하면 컴파일 에러가 난다.

ㄷ) 싱글톤 패턴(Singleton pattern)

싱글톤 패턴이란 객체를 단 한개만 생성할 수 있게끔 강제하는 것이다. 싱글톤 패턴을 이용하면 클래스를 통해 단 하나의 객체만을 생성할 수 있다. 어떤 경우에 싱글톤을 이용하는지는 아직까지는 잘 모르겠다.

class SingleTon {
	private SingleTon() {
    }
}

public class Sample {
	public static void main(String[] args) {
    	SingleTon singleton = new SingleTon();
    }
}
  • 단 한개의 객체를 생성하게끔 강제하기 위해서 생성자에 private를 붙여 new 키워드를 사용하지 못하게끔 했다. new 키워드를 사용하면 객체를 무한히 생성할 수 있기 때문이다. 하지만 아예 다른 클래스에서 SingleTon 클래스의 생성자로 접근을 막았기 때문에 컴파일 에러가 나게 된다.
class SingleTon {
	private SingleTon() {
    }
    
    public static SingleTon getInstance() {
    	return new SingleTon();
    }
}

public class Sample {
	public static void main(String[] args) {
    	SingleTon singleton = SingleTon.getInstance();
    }
}
  • getInstance라는 SingleTon 자료형의 스태틱 메소드를 생성하여 new SingleTon()을 리턴하게끔 했다. main 메소드에서 객체를 생성할 때엔 스태틱 메소드를 생성했으므로 클래스를 이용하여 메소드를 호출하게끔 했다. 하지만 getInstance 메소드에서 다시 new라는 키워드를 사용했기 때문에 객체 하나만을 생성하도록 강제할 수는 없다.
class SingleTon {
	private static one;
	private SingleTon() {
    }
    
    public static SingleTon getInstance() {
    	if (one==null) {
        	one = new SingleTon();
        }
    	return one;
    }
}

public class Sample {
	public static void main(String[] args) {
    	SingleTon singleton1 = SingleTon.getInstance();
    	SingleTon singleton2 = SingleTon.getInstance();
    }
}
  • one이라는 static 변수를 생성해서 값을 지정해 놓지 않는다. one 변수에는 null값이 저장되어 있는 상태이다. getInstance 메소드에 if문을 이용하여 one변수가 null일 때에만 SingleTon 객체를 리턴하게끔 했다. 이렇게 코드를 짜면 객체를 아무리 많이 만들어도 one값이 null일때만, 즉 객체가 생성되지 않은 경우에만 객체가 생성되게끔 했기 때문에 객체를 여러개 생성해도 동일한 객체가 되고 객체는 하나밖에 생성되지 못한다.

0개의 댓글