public class Person {
// 필드
String name;
// 메소드
void setAge(int num){...}
}
Person 클래스에 name필드와 setAge메소드가 위처럼 선언되어있다. 이 필드와 메소드는 인스턴스 멤버이기 때문에 외부 클래스에서 사용하기 위해서는 Person 객체(인스턴스)를 생성하고 참조변수로 접근해야한다.
(1)
Person person = new Person();
person.name = "홍길동";
person.setAge(20);
(2)
Person ps = new Person();
ps.name = "김대감";
ps.setAge(30);
위 두 코드가 실행되면
-인스턴스 필드인 name은 객체마다 따로 존재한다. 불러온 변수만큼 새롭게 존재하는 것이다.
-인스턴스 메소드인 setAge()는 객체마다 존재하지 않는다. 메소드 영역에서 저장되고 공유된다.
Person(String name) {
this.name = name;
}
void setName(String name){
this.name = name;
// 와 같은 형태로 사용
}
선언 시 static 키워드를 추가적으로 붙이면 된다.
-정적 필드와 정적 메소드는 클래스에 고정된 멤버이므로 클래스 로더가 클래스 (바이트 코드)를 로딩해서 메소드 메모리 영역에 적재할 때 클래스별로 관리된다. 따라서 클래스의 로딩이 끝나면 바로 사용할 수 있다
public class 클래스 {
// 적적 필드
static 타입 필드 [=초기값];
// 정적 메소드
static 리턴 타입 메소드(매개 변수 선언, ...) {... }
}
-'객체마다 가지고 있어야 할 데이터'라면 인스턴스 필드로 선언하고
-객체마다 가지고 있을 필요성이 없는 '공용적인 데이터' 라면 정적 필드로 선언하는 것이 좋다.
public class calcu {
String color;
// 계산기별로 색이 다를 수 있으니까
// 인스턴스 필드로 설정
static double pi = 3.141592;
// 파이값은 어디나 동일하니까
// 정적 필드로 설정
}
클래스가 메모리로 로딩되면 정적 멤버를 바로 사용할 수 있는데, 클래스 이름과 함께 도트 (.) 연산자로 접근한다.
클래스.필드;
클래스.메소드( 매개값, ... );
// calcu 클래스선언
public class Calcu {
static double pi = 3.14159;
static int plus(int x, int y) {...}
static int minus(int x, int y) {...}
}
// 정적 필드 pi와 정적 메소드 plus(), minus() 사용법
double result1 = 10 * 10 * calcu.pi;
int result2 = calcu.plus(10, 5);
int result3 = calcu.minus(10, 5);
정적 필드와 정적 메소드는 '원칙적으로는' 클래스 이름으로 접근해야 하지만 다음과 같이 객체 참조 변수로도 접근이 가능하다
Calcu myCalcu = new Calcu();
double result1 = 10 * 10 * myCalcu.pi;
int result2 = myCalcu.plus(10, 5);
정적 필드는 다음과 같이 필드 선언과 동시에 초기값을 주는 것이 보통이다
static double pi = 3.14159;
그러나 계산이 필요한 초기화 작업이 있을 수 있다. 인스턴스 필드는 생성자에서 초기화하지만, 정적 필드는 객체 생성 없이도 사용해야 하므로 생성자에서 초기화 작업을 할 수 없다. 생성자는 객체 생성 시에만 실행되기 때문이다.
// java 에서는 정적 필드의 초기화 작업을 위해
// 정적 블록(static block)을 제공
static {
...
}
정적 블록은 클래스가 메모리로 로딩될 때 자동적으로 실행된다. 여러개 선언되어도 상관 없다. 같은 타입의 블록은 (인스턴스 끼리, 정적끼리, 기본생성자끼리는) 순서대로 실행된다.
정적을 선언할 때 주의할 점은 객체가 없어도 실행된다는 특징 때문에, 이들 내부에 인스턴스를 사용할 수 없다. 객체 자신의 참조인 this 키워드도 사용이 불가능하다.
public class ClassName {
// 인스턴스 필드와 메소드
int field1;
void method1(){...}
// 정적 필드와 메소드
static int field2;
static void method2(){...}
// 정적 블록
static{
field1 = 10; // x 에러
method1(); // x 에러
field2 = 10; // o 가능
method2(); // o 가능
}
// 정적 메소드
static void Method3 {
this.field1 = 10; // x 에러
this.method1(); // x 에러
field2 = 10; // o 가능
method2(); // o 가능
}
}
정적 메소드와 정적 블록에서 인스턴스 멤버를 사용하고 싶다면 객체를 먼저 생성하고 참조 변수로 접근해야 한다.
static void Method3() {
ClassName obj = new ClassName();
obj.field1 = 10;
obj.method1();
}
가끔 전체 프로그램에서 단 하나의 객체만 만들도록 보장해야 하는 경우가 있다. 단 하나만 생성된다 해서 이 객체를 싱글톤(singleton)이라 한다. 싱글톤을 만드려면 클래스 외부에서 new 연산자로 생성자를 호출할 수 없도록 막아야한다.
public class 클래스 {
// private 접근 제한자를 붙여
// 생성자를 외부에서 호출할 수 없도록한다
// 정적 필드
private static 클래스 singleton = new 클래스();
// 생성자
private 클래스() {}
// 정적 메소드
// 외부에서 호출할 수 있는 정적 메소드 선언
// 정적 필드에서 참조하고 있는 자신의 객체를 리턴해준다.
static 클래스 getInstance() {
return singleton;
}
}
이 상태로 외부에서 객체를 얻는 유일한 방법은 getInstance() 메소드를 호출하는 방법이다. 이 getInstance()메소드는 단 하나의 객체만 리턴하기 때문에 호출 변수를 다르게 해도 동일한 객체를 참조한다.
클래스 변수1 = 클래스.getInstance();
클래스 변수2 = 클래스.getInstance();
최종적 이라는 뜻의 final 처럼, final 필드는 초기값이 저장되면 이것이 최종적인 값이 되어 프로그램 실행 도중에 수정할 수 없다는 것이다.
// 선언방법
final 타입 필드 [=초기값];
final 필드의 초기값을 줄 수 있는 방법은 딱 두가지이다.
-첫번째, 필드 선언 시주는 방법
-두번째, 생성자에서 준다
단순값이라면 필드 선언 시 주는 것이 가장 간단하지만, 복잡한 초기화 코드가 필요하거나 객체 생성 시에 외부 데이터로 초기화해야한다면 생성자에서 초기값을 지정해야 한다.
// final 필드 선언과 초기화------------------------------------
// final 필드
// 최종적인 값을 갖고 있는 필드 = 값을 변경할 수 없는 필드
public class Final245 {
final String nation = "Korea";
final String ssn;
String name;
public Final245(String ssn, String name) {
this.ssn = ssn;
this.name=name;
}
// 같은 메소드 내에서도 변경은 불가
void mehtod() {
// nation = "usa";
// ssn = "143566-4315462"
name = "철수";;
}
}
// final 필드 테스트-----------------------------------------
public class Final246 {
public static void main(String[] args) {
Final245 fi = new Final245("123456-1234567", "홍길동");
System.out.println(fi.nation);
System.out.println(fi.ssn);
System.out.println(fi.name);
// final 설정한 필드는 값 수정이 불가하다
// fi.nation = "usa";
// fi.ssn = "143566-4315462"
fi.name = "철수";
System.out.println(fi.name);
}
}
// 단순 값일때
static final 타입 상수 [= 초기값];
// 복잡한 초기화의 경우 정적 블록에서 할 수는 있음
static final 타입 상수;
static {
상수 = 초기값;
}
// 상수 선언
// 일반적으로 불변의 값을 상수라 부른다.
// 지구의 둘레, 은행의 정해진 금리 등
// 불변 값을 캡슐화
// final필드는 객체마다 저장되고,
// 생성자의 매개값을 통해 여러가지 값을 가질 수 있다
// static fianl 필드는 객체마다 저장되지 않고, 클래스에만 포함된다.
// 한 번 초기값이 저장되면 변경할 수 없다
// static 메소드로만 값 설정이 가능하다
public class StaticFinalEx {
static final double EARTh_RADIUS = 6400;
static final double EARTH_SURFACE_AREA;
static {
EARTH_SURFACE_AREA = 4 * Math.PI * EARTh_RADIUS * EARTh_RADIUS;
}
// 아래는 불가능한 모습
// static이 아닌 다른 메소드에서 값을 지정해주는 것은 안됨
// void StaticFinalEx{
// EARTH_SURFACE_AREA = 4 * Math.PI * EARTh_RADIUS * EARTh_RADIUS;
// }
// }
// 상수 사용----------------------------------------------------------------
public class StaticFinalEx1 {
public static void main(String[] args) {
System.out.println("지구의 반지름 : " + StaticFinalEx.EARTh_RADIUS + " km");
System.out.println("지구의 표면적 : " + StaticFinalEx.EARTH_SURFACE_AREA + " km");
}
}
프로젝트를 개발하다 보면 적게는 수십개, 많개는 수백 개의 클래스를 작성해야한다. 이때 클래스를 체계적으로 관리하지 않으면 클래스 간 관거ㅖ가 뒤엉켜 복잡하고 난해한 프로그램이 되고, 결국 유지 보수를 어렵게 한다.
때문에 자바에서는 파일을 저장 관리 하듯이 패키지를 만들어 클래스를 저장 관리한다. 패키지의 물리적인 형태는 파일 시스템의 폴더이다.
사용 모습 : 상위패키지명.하위패키지명.클래스
패키지는 클래스를 컴파일하는 과정에서 자동적으로 생성되는 폴더. 다음은 패키지를 선언하는 방법이다.
package 상위패키지.하위패키지;
public class ClassName { ... }
패키지 이름은 몇가지 규칙만 지켜 임의대로 지어주면 된다.
-숫자로 시작하면 안된다
-특수문자는 사용해서는 안된다(_ $ 제외)
-java로 시작하는 패키지는 자바 표준 API에서만 사용하므로 사용해서는 안된다
-모두 소문자로 작성하는 것이 관례이다.
다른 패키지에 속하는 클래스를 사용하려면 두 가지 방법 중 하나를 선택해야 한다.
1. 패키지와 클래스를 모두 기술하는 것이다
ex: com.hankook.Tire tire = new com.hankook.Tire();
2. import 선언
import com.hankook.Tire
or import com.hakook.*;
의 형태
사용하고자 하는 패키지를 import문으로 선언하고, 클래스 사용시 패키지를 생략하는 것이다.
package com;
// (2) import로 불러오기
import com.myCom.myCom1.*;
public class BusinessClass {
public static void main(String[] args) {
// (1) 방법. 하나하나 입력해주기------------------------------------------
// ComClass om = new ComClass();
// com.myCom.myCom1.MyCom1Class ComClass = new com.myCom.myCom1.MyCom1Class;
com.myCom.myCom1.MyComClass11 C11 = new com.myCom.myCom1.MyComClass11();
com.myCom.myCom1.MyComClass22 C22 = new com.myCom.myCom1.MyComClass22();
}
}
// 코드 실행
// 메인을 먼저 실행한다
// 객체 생성 된 부분이 있다면 해당 클래스로 ㄱㄱ
// (객체 생성이 안되었어도 다른 클래스에 static 된 부분이 있으면 메모리에 먼저 올라가 있기는 함)
// static 블록이 있는지 확인 -> 있다면 코드블록 실행
public class StaticBlockEx {
public static void main(String[] args) {
new Ex();
}
}
// 작성 순서와 상관 없이
//1.정적블록 2.인스턴스블록영역 3.기본생성자영역 순서로 실행된다
// static 중에 순서대로 실행
// 인스턴스블록 중에 순서대로 실행
// 기본 생성자영역 중에 순서대로 실행
class Ex{
static {
System.out.println("aaa : static 블록 중 첫번째 작성");
}
{
System.out.println("bbb : 인스턴스 블록 중 첫번째 작성");
}
{
System.out.println("ccc : 인스턴스 블록 중 두번째 작성");
}
public Ex(){
// 같은 클래스 내에서 다른 메소드를 불러서 사용하려면 this()를 사용한다
this(10);
System.out.println("ccc : 생성자 블록 중 첫번째 작성");
}
public Ex(int i) {
System.out.println("fff : 생성자 블록 중 두번째 작성");
}
static {
System.out.println("eee : static 블록 중 두번째 작성");
}
}
실행 결과
실행 결과를 보면 블록들이 순서대로 출력되는 것을 볼 수 있다. 생성자 블록의 순서가 바뀐것은 this()를 사용해 중간에서 불러 사용했기 때문이다.
public class StaticEx {
/*
정적(static) 멤버란
-클래스에 고정된 필드와 메소드(ex. 정적필드, 정적 메소드)
정적 멤보는 클래스에 소속된 멤버
-객체 내부에 존재하지 않고, 메소드 영역에 존재
-정적 멤보는 객체를 생성하지 않고 클래스로 바로 접근해 사용
정적 멤버 선언
-필드 또는 메소드 선언할 때 static 카워드를 붙인다
*/
public static void main(String[] args) {
new Television();
}
}
class Television{
// 코드 작성의 순서와 상관없이
// 실행 순서는 아래와 같다
// 1.정적블록 2.인스턴스블록영역 3.기본생성자영역
static int a;
public Television() {
a = 10000;
System.out.println("기본 생성자 영역");
}
{
a = 100;
System.out.println("인스턴스 블록 영역");
}
static {
a = 0;
System.out.println("정적 블록 영역");
}
}
public class StaticEx {
// Static이 안붙은 멤버
// = 인스턴스 멤버
int inVar1;
void inMethod() {
int Var1 = inVar1 + 100 + stVar1;
// 인스턴스 멤버에서 정적 멤버를 호출해도 에러 안남
System.out.println(inVar1);
stMethod();
// 이것도 마찬가지. 사용가능. 에러안남
}
// 정적 멤버
// Static 멤버
static int stVar1;
static void stMethod() {
stVar1 = 100 + stVar1; //+inVar
System.out.println(stVar1);
// inMethod();
// 순서상 Static이 인스턴스 멤버보다 먼저 올라가기 때문에
// Static에서 인스턴스를 불러오거나 사용할 수는 없다
}
}
// 메인 부분----------------------------------------------------
public class StaticEX01 {
public static void main(String[] args) {
// 정적멤버에 200을 넣고 메소드 호출해 보세요
// 정적은 객체 참조 변수 안해도 됨
// 정적멤버사용법은 클래스명.멤버명
StaticEx.stVar1 = 200;
StaticEx.stMethod();
// 인스턴스 멤버에 300 넣고 메소드 호출하기
// 1.인스턴스 멤버를 (객체 참조 변수를 사용) 힙메모리에 올린다
// 2.인스턴스 멤버들 사용방법 : 참조변수.멤버명
StaticEx st = new StaticEx();
st.inVar1 = 300;
st.inMethod();
// st.stVar1=300;
// 가능은 한데, 올바르지는 않은 방법
}
}
// InstanceClass1------------------------------------------
public class InstanceClass1 {
int iC1;
}
// InstanceClass2-------------------------------------------
public class InstanceClass2 {
int iC2;
}
// StaticEx-------------------------------------------------
public class StaticEx {
static int sum;
// 각 클래스의 자료를 누적할 합에 대한 정적 변수
int sum1;
// 각 클래스의 자료를 누적할 때 사용할 인스턴스 변수
// 클래스에 인스턴스 변수 값을 넣는다.
}
// StaticEX01-----------------------------------------------
public class StaticEX01 {
public static void main(String[] args) {
InstanceClass1 i1 = new InstanceClass1();
i1.iC1 = 100;
InstanceClass2 i2 = new InstanceClass2();
i2.iC2 = 200;
// 공용으로 사용되는 sum에 각 자료를 누적해서 넣는다
StaticEx.sum += i1.iC1;
StaticEx.sum += i2.iC2;
System.out.println(StaticEx.sum);
// 인스턴스 변수 sum1사용해 각 자료를 누적해 넣기
StaticEx st = new StaticEx();
st.sum1 += i1.iC1;
st.sum1 += i2.iC2;
System.out.println(st.sum1);
// 이렇게 호출을 두 번하면 또다른 변수로 또 다른 공간이 만들어짐
// st와 st1은 따로따로 누적된다
StaticEx st1 = new StaticEx();
st1.sum1 += i1.iC1;
st.sum1 += i2.iC2;
}
}
// 싱글톤 객체의 구조
// 객체 생성을 단 한번만 하도록 보장해줌
// 싱글톤의 객체에 인스턴스 자료를 캡슐화해서 이용함
// 즉, 객체 생성이 1번만 되므로,
// 주소 변동이 없어 싱글톤 안에 인스턴스 자료를 공용화해 이용할 수 있음
public class Singleton2 {
// 정적 필드
// (링크 로더 단계에서 컴파일해 올라갈 때) 바로 객체 생성해주고 변수에 담dma
// private로 설정해줬기에 s2를 다른 곳에서 사용할 수는 없음
private static Singleton2 s2 = new Singleton2();
// 생성자
private Singleton2() {
}
// 다른 객체와 연결되는 통로(메소드
static Singleton2 getInstance() {
return s2;
}
}
// 메인부분------------------------------------------------
public class SingTon {
public static void main(String[] args) {
Singleton st = Singleton.getInstance();
st.name = "홍길동";
st.age = 20;
// 참조 변수를 다르게 해서 호출해도
// 사용하는 주소가 같다
// st1과 st이 사용하는 곳이 같다
Singleton st1 = Singleton.getInstance();
System.out.println(st1.age);
System.out.println(st1.name);
}
}