본문 바로가기
Web & Mobile/JAVA

Lecture 24 - Java(5) 생성자, 변수의 초기화, 상속, 인스턴스, 패키지, import, 클래스

by Bennyziio 2019. 4. 19.
반응형

JVM의 메모리 구조
응용프로그램이 실행되면, JVM은 시스템으로부터 프로그램을 수행하는데 필요한 메모리를 할당받고 JVM은 이 메모리를 용도에 다라 여러 영역으로 나누어 관리한다. 그중 3가지 주요 영역은 아래와 같다.
1. 메서드 영역(Method area)
- 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스 파일(*. class)을 읽어서 분석하여 클래스에 대한 정보(클래스 데이터)를 이곳에 저장한다. 이때, 그 클래스의 클래스 변수(class variable)도 이 영역에 함께 생성된다.
2. 힙(Heap)
- 인스턴스가 생성되는 공간, 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성된다. 즉, 인스턴스 변수(instance variable)들이 생성되는 공간이다.
3. 호출 스택(Call stack or Execution stack)
- 호출스택은 메서드의 작업에 필요한 메모리 공간을 제공한다. 메서드가 호출되면, 호출 스택에 호출된 메서드를 위한 메모리가 할당되며, 이 메모리는 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는 데 사용된다. 그리고 메서드가 작업을 마치면 할당되었던 메모리 공간은 반환되어 비워진다.

호출 스택의 특징
- 메서드가 호출되면 수행에 필요한 만큼의 메모리를 스택에 할당받는다.
- 메서드가 수행을 마치고 나면 사용했던 메모리를 반환하고 스택에서 제거된다.
- 호출스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드이다.
- 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.

this - 자기 참조 주소값
this() - 생성자 호출
- 생성자의 이름으로 클래스 이름 대신 this를 사용한다.
- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.

ConstructorEx04 - this(), this

class Constructor {
    Constructor() {
        System.out.println("Constructor() 호출");
    }
    Constructor(int a) {
        System.out.println("Constructor(int a) 호출");
    }
}

public class ConstructorEx04 {
    public static void main(String[] args)
    {
        Constructor c1 = new Constructor();
        Constructor c2 = new Constructor(10);
    }
}

class Constructor {
    Constructor() {
        System.out.println("Constructor() 호출");
        this.Constructor(10);
    }
    Constructor(int a) {
        System.out.println("Constructor(int a) 호출");
    }
}

public class ConstructorEx04 {
    public static void main(String[] args)
    {
        Constructor c1 = new Constructor();
        //Constructor c2 = new Constructor(10);
    }
}

에러남

class Constructor {
    Constructor() {
        System.out.println("Constructor() 호출");
        //this.Constructor(10);
        Constructor(10);
    }
    Constructor(int a) {
        System.out.println("Constructor(int a) 호출");
    }
}

public class ConstructorEx04 {
    public static void main(String[] args)
    {
        Constructor c1 = new Constructor();
        //Constructor c2 = new Constructor(10);
    }
}

이것도 에러남

class Constructor {
    Constructor() {
        this(10);
        System.out.println("Constructor() 호출");
        //this.Constructor(10);
        //Constructor(10);
        
    }
    Constructor(int a) {
        System.out.println("Constructor(int a) 호출");
    }
}

public class ConstructorEx04 {
    public static void main(String[] args)
    {
        Constructor c1 = new Constructor();
        //Constructor c2 = new Constructor(10);
    }
}

this(10)이 먼저 실행되고 println을 해야 호출이 됨

class Constructor {
    Constructor() {
        int a;
        this(10);
        System.out.println("Constructor() 호출");
        //this.Constructor(10);
        //Constructor(10);
        
    }
    Constructor(int a) {
        System.out.println("Constructor(int a) 호출");
    }
}

public class ConstructorEx04 {
    public static void main(String[] args)
    {
        Constructor c1 = new Constructor();
        //Constructor c2 = new Constructor(10);
    }
}

this위에 주석제외 그 무엇이 와도 안된다 무조건 첫째줄은 this여야 한다.

this를 사용하는 이유는 멤버 변수인 클래스 멤버 변수와 인스턴스를 초기화하는 메서드를 응용하기 위해서 사용한다.

ConstructorEx05

class Constructor {
    int data1;
    String data2;

    Constructor() {
        //this.data1 = 30;
        //this.data2 = "홍길동";

        this(30, "홍길동");
        System.out.println("Constructor() 호출");

    }
    Constructor(String data2) {
        this(30, data2);
        System.out.println("Constructor() 호출");

    }
    Constructor(int data1, String data2) {
        this.data1 = data1;
        this.data2 = data2;

        System.out.println("Constructor(int data1, String data2) 호출");
    }
}

public class ConstructorEx05 {
    public static void main(String[] args)
    {
        Constructor c1 = new Constructor();
    }
}

위 출력 결과는 Constructor() 생성자의 멤버 변수 this(30, "홍길동")의 맞는 타이 브이 타 생성자인 Constructor(int data1, String data2)를 따르게 되어 해당 생성자의 println을 실행하고 난 뒤 다음 줄 println을 실행하게 되어 출력이 된 순서이고, Constructor(String data2)는 콜이 안되었기 때문에 출력이 되지 않는다.

변수의 초기화
변수를 선언하고 처음으로 값을 저장하는 것을 '변수의 초기화'라고 한다. 변수의 초기화는 경우에 따라서 필수적이기도 하고 선택적이기도 하지만, 가능하면 선언과 동시에 적절한 값으로 초기화하는 것이 바람직하다. 
멤버 변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어지므로 초기화하지 않고 사용해도 되지만, 지역변수는 사용하기 전에 반드시 초기화해야 한다.

멤버변수의 초기화 방법
1. 명시적 초기화(explicit initialization)
2. 생성자(constructor)
3. 초기화 블럭(initialization block)
    - 인스턴스 초기화 블럭 : 인스턴스 변수를 초기화하는 데 사용
    - 클래스 초기화 블럭 : 클래스 변수를 초기화 하는데 사용

CodeBlockEx01

class CodeBlock {
    // 블럭
    static {
        System.out.println("클래스 초기화 블럭");
    }

    {
        System.out.println("인스턴스 초기화 블럭");
    }

    CodeBlock() {
        System.out.println("생성자");
    }
}

public class CodeBlockEx01 {
    public static void main(String[] args)
    {
        CodeBlock cb = new CodeBlock();
    }
}

class CodeBlock {
    String name1;
    static String name2;
    // 블럭
    static {
        System.out.println("클래스 초기화 블럭");
        CodeBlock.name2 = "박문수";
    }

    {
        System.out.println("인스턴스 초기화 블럭");
        this.name1 = "홍길동";
    }

    CodeBlock() {
        System.out.println("생성자");

        System.out.println(this.name1);
        System.out.println(CodeBlock.name2);
    }
}

public class CodeBlockEx01 {
    public static void main(String[] args)
    {
        CodeBlock cb = new CodeBlock();
    }
}

 

static은 클래스 멤버라서 this대신 클래스 주소로 CodeBlock.name2라고 초기화 선언을 하는데 인스턴스는 클래스 주소가 반환되어 참조 변수에 저장되고 이 주소를 콜 하기 위해 this를 사용하므로 this.name1으로 초기화 선언한다.

멤버 변수의 초기화 시기와 순서 클래스 변수의
초기화 시점 : 클래스가 처음 로딩될 때 단 한번 초기화 된다.
클래스변수의 초기화 순서 : 기본값 -> 명시적초기화 -> 클래스 초기화 블럭
인스턴스변수의 초기화 시점 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.
인스턴스변수의 초기화 순서 : 기본값 -> 명시적초기화 -> 인스턴스 초기화 블럭 -> 생성자

DocumentTest

class Document {
    static int count = 0;
    String name;

    Document() {
        this("제목없음" + ++count);
    }

    Document(String name) {
        this.name = name;
        System.out.println("문서 " + this.name + "가 생성되었습니다.");
    }
}

public class DocumentTest {
    public static void main(String[] args)
    {
    Document d1 = new Document();
    Document d2 = new Document("자바.txt");
    Document d3 = new Document();
    Document d4 = new Document();
    }
}

OOP 프로그램의 4대 특성 1. 캡슐화(은닉화) 2. 상속성(*) 3. 추상 4. 다형 => 문법 추가

상속(inheritance)
상속이란, 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다. 상속을 통해서 클래스를 작성하면 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 있기 때문에 코드의 추가 및 변경이 매우 용이하다. 이러한
특징은 코드의 재사용성을 높이고 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여한다.
* 각각에서 공통선을 뽑아내서(Abstraction) 공통적인 내용을 적어놓고 그걸 가져다 쓰는 것이다.

조상 클래스 
부모(parent) 클래스, 상위(super) 클래스, 기반(base) 클래스 자손
클래스 자식(child) 클래스, 하위(sub) 클래스, 파생된(derived)클래스

- 생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.
- 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.

자손 클래스는 조상 클래스의 모든 멤버를 상속받으므로 항상 조상 클래스보다 같거나 많은 멤버를 갖는다. 즉, 상속에 상속을 거듭할수록 상속받는 클래스의 멤버 개수는 점점 늘어나게 된다.
그래서 상속을 받는다는 것을 조상 클래스를 확장(extend)한다는 의미로 해석할 수도 있으며 이것이 상속에 사용되는 키워드가 'extend'인 이유이기도 하다.

TestEx01 - 상속

class Parent {
    Parent() {
        System.out.println("Parent 생성자");
    }
}
class Child1 {
    Child1() {
        System.out.println("Child1 생성자");
    }
}

public class TestEx01 {
    public static void main(String[] args) {
        Parent p = new Parent();
        Child1 c1 = new Child1();
    }
}

class Parent {
    Parent() {
        System.out.println("Parent 생성자");
    }
}
class Child1 extends Parent {
    Child1() {
        System.out.println("Child1 생성자");
    }
}

public class TestEx01 {
    public static void main(String[] args) {
        //Parent p = new Parent();
        Child1 c1 = new Child1();
    }
}

class Parent {
    Parent() {
        System.out.println("Parent 생성자 : " + this);
    }
}
class Child1 extends Parent {
    Child1() {
        System.out.println("Child1 생성자 : " + this);
    }
}

public class TestEx01 {
    public static void main(String[] args) {
        //Parent p = new Parent();
        Child1 c1 = new Child1();
    }
}

자식 안에 부모가 포함되어 메모리에 저장이 되므로 주소를 보면 부모와 자식이 같은 주소로 메모리 지정이 되어있다.

class Parent {
    Parent() {
        System.out.println("Parent 생성자 : " + this);
    }
}
class Child1 extends Parent {
    Child1() {
        System.out.println("Child1 생성자 : " + this);
    }
}
class Child2 extends Parent {
    Child2() {
        System.out.println("Child2 생성자 : " + this);
    }
}

public class TestEx01 {
    public static void main(String[] args) {
        //Parent p = new Parent();
        Child1 c1 = new Child1();
        Child2 c2 = new Child2();
    }
}

class Parent {
    int page = 20;
    String pname = "박문수";
    Parent() {
        System.out.println("Parent 생성자 : " + this);
    }
}
class Child1 extends Parent {
    int c1age = 30;
    String c1name = "박문수";

    Child1() {
        System.out.println("Child1 생성자 : " + this);

        System.out.println(this.c1age);
        System.out.println(this.c1name);
    }
}
class Child2 extends Parent {
    Child2() {
        System.out.println("Child2 생성자 : " + this);
    }
}

public class TestEx01 {
    public static void main(String[] args) {
        //Parent p = new Parent();
        Child1 c1 = new Child1();
        Child2 c2 = new Child2();
    }
}

class Parent {
    int page = 20;
    String pname = "박문수";
    Parent() {
        System.out.println("Parent 생성자 : " + this);
    }
}
class Child1 extends Parent {
    int c1age = 30;
    String c1name = "박문수";

    Child1() {
        System.out.println("Child1 생성자 : " + this);

        System.out.println(this.c1age);
        System.out.println(this.c1name);
        System.out.println(this.page);
        System.out.println(this.pname);
    }
}
class Child2 extends Parent {
    Child2() {
        System.out.println("Child2 생성자 : " + this);
    }
}

public class TestEx01 {
    public static void main(String[] args) {
        //Parent p = new Parent();
        Child1 c1 = new Child1();
        Child2 c2 = new Child2();
    }
}

자식한테 없어도 부모한테 있으니 가져다 쓸 수 있다(page, pname)

class Parent {
    int page = 20;
    String pname = "박문수";
    Parent() {
        System.out.println("Parent 생성자 : " + this);
    }

    void methodp() {
        System.out.println("methodp() 호출");
    }
}
class Child1 extends Parent {
    int c1age = 30;
    String c1name = "박문수";

    Child1() {
        System.out.println("Child1 생성자 : " + this);

        System.out.println(this.c1age);
        System.out.println(this.c1name);
        System.out.println(this.page);
        System.out.println(this.pname);

        this.methodp();
    }
}
class Child2 extends Parent {
    Child2() {
        System.out.println("Child2 생성자 : " + this);
    }
}

public class TestEx01 {
    public static void main(String[] args) {
        //Parent p = new Parent();
        Child1 c1 = new Child1();
        Child2 c2 = new Child2();
    }
}

void methodp() 도 출력이 됨

CaptionTvTest

class Tv {
    boolean power;
    int channel;

    void power() {power = !power;}
    void channelUp() {++ channel;}
    void channelDown() {-- channel;}
}

class CaptionTv extends Tv {
    boolean caption;
    void displayCaption(String text) {
        if(caption) {
            System.out.println(text);
        }
    }
}

public class CaptionTvTest {
    public static void main(String[] args) {
        CaptionTv ctv = new CaptionTv();
        ctv.channel = 10;
        ctv.channelUp();
        System.out.println(ctv.channel);
        ctv.displayCaption("Hello, World");
        ctv.caption = true;
        ctv.displayCaption("Hello, World");
    }
}

두 개 클래스를 사용하는 법 1. 인스턴스화 2. 상속

클래스 간의 관계 - 포함관계
지금까지 상속을 통해 클래스 간에 관계를 맺어 주고 클래스를 재사용하는 방법에 대해서 알아보았다. 상속 이외에도 클래스를 재사용하는 또 다른 방법이 있는데, 그것은 클래스 간에 '포함(Composite)' 관계를 맺어 주는 것이다. 클래스 간에 포함관계를 맺어 주는 것은 한 클래스의 멤버 변수로 다른 클래스 타입의 참조 변수를 선언하는 것을 뜻한다.
하나의 거대한 클래스를 작성하는 것보다 단위별로 여러 개의 클래스를 작성한 다음, 이 단위 클래스들을 포함관계로 재사용하면 보다 간결하고 손쉽게 클래스를 작성할 수 있다.

클래스간의 관계 결정하기
클래스를 작성하는 데 있어서 상속관계를 맺어 줄 것인지 포함관계를 맺어 줄 것인지 결정하는 것은 때때로 혼돈스러울 수 있다.
원(Circle)은 점(Point)이다 - Circle is a Point
원(Circle)은 점(Point)을 가지고 있다 - Circle has a Point
원은 원점(Point)과 반지름으로 구성되므로 위의 두 문장을 비교해 보면 첫 번째 문장보다 두 번째 문장이 더 옳다는 것을 알 수 있을 것이다.
이처럼 클래스를 가지고 문장을 만들었을 때 '~은~이다.'라는 문장이 성립한다면, 서로 상속관계를 맺어주고, '~은 ~을 가지고 있다.'는 문장이 성립한다면 포함관계를 맺어 주면 된다.(절대적이진 않다)

DrawShape

class Shape {
    String color = "black";
    void draw() {
        System.out.printf("[color=%s]%n", color);
    }
}

class Point {
    int x;
    int y;
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    Point() {
        this(0,0);
    }
    String getXY() {
        return "("+x+","+y+")";
    }
}

class Circle extends Shape {
    Point center;
    int r;

    Circle() {
        this(new Point(0, 0), 100);
    }

    Circle(Point center, int r) {
        this.center = center;
        this.r = r;
    }

    void draw() {
        System.out.printf("[center=(%d, %d), r=%d, color=%s]%n", 
                                        center.x, center.y, r, color);
    }
}

class Triangle extends Shape {
    Point[] p = new Point[3];

    Triangle(Point[] p) {
        this.p = p;
    }

    void draw() {
        System.out.printf("[p1=%s, p2=%s, p3=%s, color=%s]%n",
            p[0].getXY(), p[1].getXY(), p[2].getXY(), color);
    }
}

public class DrawShape {
    public static void main(String[] args) {
        Point[] p = {   new Point(100, 100),
                        new Point(140,  50),
                        new Point(200, 100)
                    };

        Triangle t = new Triangle(p);
        Circle c = new Circle(new Point(150, 150), 50);

        t.draw();
        c.draw();
    }
}

단일 상속(single inheritance)
다른 객체지향 언어인 C++에서는 여러 조상 클래스로부터 상속받는 것이 가능한 '다중상속(multiple inheritance)'을 허용하지만 자바에서는 단일 상속만을 허용한다.

Object 클래스 - 모든 클래스의 조상
Object 클래스는 모든 클래스 상속계층도의 최상위에 있는 조상 클래스이다. 다른 클래스로부터 상속받지 않는 모든 클래스들은 자동적으로 Object 클래스로부터 상속받게 함으로써 이것을 가능하게 한다.

오버라이딩(overriding) - 재정의 (오버로딩은 중복정의)
조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버 라이딩이라고 한다. 상속받은 메서드를 그대로 사용하기도 하지만, 자손 클래스 자신에 맞게 변경해야 하는 경우가 많다. 이럴 때 조상의 메서드를 오버라이딩한다.
부모와 자식 간의 같은 이름의 메서드가 있을 때

오버라이딩의 조건
- 이름이 같아야 한다
- 매개변수가 같아야 한다
- 반환 타입이 같아야 한다

오버로딩 vs 오버라이딩
오버로딩(overloading) 기존에 없는 새로운 메서드를 정의하는 것(new)
오버라이딩(overriding) 상속받은 메서드의 내용을 변경하는 것(change, modify)

OverridingEx01

class Parent {
    void viewParent() {
        System.out.println("viewParent() 호출");
    }
}

class Child extends Parent {
    void viewChild() {
        System.out.println("viewChild() 호출");
    }
}

public class OverriddingEx01 {
    public static void main(String[] args) {
        Child c = new Child();
        c.viewChild();
        c.viewParent();
    }
}

class Parent {
    void viewParent() {
        System.out.println("viewParent() 호출");
    }
}

class Child extends Parent {
    void viewChild() {
        System.out.println("viewChild() 호출");
    }
    // void viewParent()
    void viewParent() {
        System.out.println("child viewParent() 호출");
    }
}

public class OverriddingEx01 {
    public static void main(String[] args) {
        Child c = new Child();
        c.viewChild();
        c.viewParent();
    }
}

오버라이딩 - 자식이 부모껄 안불러오고 재정의해서 이를 출력함

class Parent {
    void viewParent() {
        System.out.println("viewParent() 호출");
    }
}

class Child extends Parent {
    void viewChild() {
        System.out.println("viewChild() 호출");
    }
    // void viewParent()
    int viewParent() {
        System.out.println("child viewParent() 호출");
        return 0;
    }
}

public class OverriddingEx01 {
    public static void main(String[] args) {
        Child c = new Child();
        c.viewChild();
        c.viewParent();
    }
}

이름이 같아서 충돌이 남, 형식을 맞춰서 써야 오버라이딩 조건에 부합됨

class Parent {
    void viewParent() {
        System.out.println("viewParent() 호출");
    }
}

class Child extends Parent {
    void viewChild() {
        System.out.println("viewChild() 호출");
    }
    // void viewParent()
    void viewParent(int a) {
        System.out.println("child viewParent() 호출");
    }
}

public class OverriddingEx01 {
    public static void main(String[] args) {
        Child c = new Child();
        c.viewChild();
        c.viewParent();
    }
}

부모꺼 그대로 나오는 이것은 오버로딩임(int a에 의해서 오버로딩이 됨 / 오버라이딩으로 하기엔 형식이 달라져서 기준 미부합)

super
super는 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다.

class Parent {
    void viewParent() {
        System.out.println("viewParent() 호출");
    }
}

class Child extends Parent {
    void viewChild() {
        System.out.println("viewChild() 호출");

        this.viewParent();
    }
    // void viewParent()
    void viewParent() {
        System.out.println("Child viewParent() 호출");
    }
}

public class OverriddingEx02 {
    public static void main(String[] args) {
        Child c = new Child();
        c.viewChild();
    }
}

this로 하면 오버라이딩된 Child viewParent를 출력

class Parent {
    void viewParent() {
        System.out.println("viewParent() 호출");
    }
}

class Child extends Parent {
    void viewChild() {
        System.out.println("viewChild() 호출");

        //this.viewParent();
        super.viewParent();

        // suuper
        // 부모의 멤버변수 / 메서드 호출 (O)
    }
    // void viewParent()
    void viewParent() {
        System.out.println("Child viewParent() 호출");
    }
}

public class OverriddingEx02 {
    public static void main(String[] args) {
        Child c = new Child();
        c.viewChild();
    }
}

super로 하면 Parent의 viewParent를 출력(오버로딩)

SuperTest2

public class SuperTest2 {
    public static void main(String[] args) {
        Child c = new Child();
        c.method();
    }
}

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

super() - 조상 클래스의 생성자
this()와 마찬가지로 super()역시 생성자이다. this()는 같은 클래스의 다른 생성자를 호출하는 데 사용되지만, super()는 조상 클래스의 생성자를 호출하는데 사용된다.

Object 클래스를 제외한 모든 클래스의 생성자 첫 줄에 생성자, this() 또는 super(), 를 호출해야 한다. 그렇지 않으면 컴파일러가 자동적으로 'super();'를 생성자의 첫줄에 삽입한다.

OverridingEx03

class Parent {
    Parent() {
        System.out.println("Parent Default 생성자");
    }
}

class Child extends Parent {
    Child() {
    System.out.println("Child Default 생성자");
    }
}

public class OverridingEx03 {
    public static void main(String[] args) {
        Child c = new Child();
    }
}

class Parent {
    //Parent() {
    //    System.out.println("Parent Default 생성자");
    //}

    Parent(int data) {
        System.out.println("Parent Default(int data)");
    }
}

class Child extends Parent {
    Child() {
    System.out.println("Child Default 생성자");
    }
}

public class OverridingEx03 {
    public static void main(String[] args) {
        Child c = new Child();
    }
}

class Parent {
    //Parent() {
    //    System.out.println("Parent Default 생성자");
    //}

    // 부모 생성자의 오버로딩으로 인해
    Parent(int data) {
        System.out.println("Parent Parent(int data)");
    }
}

class Child extends Parent {
    Child() {
    //super();
    super(10);
    System.out.println("Child Default 생성자");
    }
}

public class OverridingEx03 {
    public static void main(String[] args) {
        Child c = new Child();
    }
}

PointTest2

public class PointTest2 {
    public static void main(String[] args) {
        Point3D p3 = new Point3D();
        System.out.println("p3.x=" + p3.x);
        System.out.println("p3.y=" + p3.y);
        System.out.println("p3.z=" + p3.z);
    }
}

class Point {
    int x=10;
    int y=20;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class Point3D extends Point {
    int z = 30;

    Point3D() {
        this(100, 200, 300);
    }

    Point3D(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }
}

Package와 Import
패키지란, 클래스의 묶음이다. 패키지에는 클래스 또는 인터페이스를 포함시킬 수 있으며, 서로 관련된 클래스들끼리 그룹 단위로 묶어 놓음으로써 클래스를 효율적으로 관리할 수 있다. 같은 이름의 클래스 일지라도 서로 다른 패키지에 존재하는 것이 가능하므로, 자신만의 패키지 체계를 유지함으로써 다른 개발자가 개발한 클래스 라이브러리의 클래스와 이름이 충돌하는 것을 피할 수 있다.

java 9 부터 모듈 패키지 클래스

클래스가 물리적으로 하나의 클래스파일(.class)인 것과 같이 패키지는 물리적으로 하나의 디렉토리이다.
- 하나의 소스파일에는 첫 번째 문장으로 단 한 번의 패키지 선언만을 허용한다
- 모든 클래스는 반드시 하나의 패키지에 속해야 한다
- 패키지는 점(.)을 구분자로 하여 계층구조로 구성할 수 있다.
- 패키지는 물리적으로 클래스 파일(.class)을 포함하는 하나의 디렉토리이다.

패키지의 선언
package 패키지명;
위와 같은 패키지 선언문은 반드시 소스파일에서 주석과 공백을 제외한 첫 번째 문장이어야 하며, 하나의 소스파일에 단 한번만 선언될 수 있다. 해당 소스파일에 포함된 모든 클래스나 인터페이스는 선언된 패키지에 속하게 된다.

패키지 이름
    도메인 (인터넷 주소명) - 구입
    www.naver.com <- 전세계적으로 중복되지 않음
    com
        naver
            www - 클래스
    패키지의 구성요소
        클래스 <- 이거만 가지고 있다고 봐도 무방, 제일 크다
        인터페이스
        익셉션
        enum

package exam;

public class Child {
    public Child() {
        System.out.println("Child 생성자 호출");
    }
}

패키지 컴파일시에는 -d .을 넣어줘야 한다

public class ChildMain {
    public static void main(String[] args) {
        Child c = new Child();
    }
}

Child를 가져올 수 없다.

import exam.Child;

public class ChildMain {
    public static void main(String[] args) {
        Child c = new Child();
    }
}

import exam.Child;를 써주어야 패키지된 Child를 불러 올 수 있다

package - 디렉토리 <=> import

같은 패키지이면 import가 필요없고 다른 패키지일 경우에만 import 필요

//import exam.Child;
package exam;

public class ChildMain {
    public static void main(String[] args) {
        Child c = new Child();
    }
}

같은 패키지라서 import가 필요 없다

javaclass라는 폴더에 컴파일하여 Child, ChildMain 클래스 파일을 만들었다

javaclass 폴더내의 패키지 파일을 실행시키려면 위와 같이 한다

import문의 선언
1. package문
2. import문
3. 클래스 선언

ImportTest

import java.text.SimpleDateFormat;
import java.util.Date;

public class ImportTest {
    public static void main(String[] args) {
        Date today = new Date();

        SimpleDateFormat date = new SimpleDateFormat("yyyy/MM/dd");
        SimpleDateFormat time = new SimpleDateFormat("hh:mm:ss a");

        System.out.println("오늘 날짜는 " + date.format(today));
        System.out.println("현재 시간은 " + time.format(today));
    }
}

Oracle에서 제공하는 패키지파일인 SimpleDateFormat을 import하여 println을 실행함

import 빼고 하면 위와 같이 에러가 난다.symbol을 찾을 수 없다고 한다.

static import문
import문을 사용하면 클래스의 패키지명을 생략할 수 있는 것과 같이 static import문을 사용하면 static멤버를 호출할 때 클래스 이륾을 생략할 수 있다.

StaticImportEx1

import static java.lang.System.out;
import static java.lang.Math.*;

public class StaticImportEx1 {
    public static void main(String[] args) {
        out.println(random());
        out.println("Math.PI : " + PI);
    }
}

기존 System.out.println 사용하던 것이 import static java.alng.System.out;을 이용하게 되어 System.out.println()이 out.println()으로 간결하게 사용할 수 있다.

제어자(Modifier)
제어자(modifier)는 클래스, 변수 또는 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여한다.
static - 클래스의, 공통적인
static은 '클래스의' 또는 '공통적인'의 의미를 가지고 있다. 인스턴스변수는 하나의 클래스로부터 생성되었더라도 각기 다른 값을 유지하지만, 클래스변수(static멤버변수)는 인스턴스에 관계없이 같은 값을 갖는다. 그 이유는 하나의 변수를 모든 인스턴스가 공유하기 때문이다.
static이 붙은 멤버변수와 메서드, 그리고 초기화 블럭은 인스턴스가 아닌 클래스에 관계된 것이기 때문에인스턴스를 생성하지 않고도 사용할 수 있다.
인스턴스메서드와 static메서드의 근본적인 차이는 메서드 내에서 인스턴스 멤버를 사용하는가의 여부에 있다.
static이 사용될 수 있는 곳 - 멤버변수, 메서드, 초기화 블럭

제어자 대상 의미
static 멤버변수 - 모든 인스턴스에 공통적으로 사용되는 클래스변수가 된다.
- 클래스변수는 인스턴스를 생성하지 않고도 사용 가능하다.
- 클래스가 메모리에 로드될 때 생성된다.
메서드 - 인스턴스를 생성하지 않고도 호출이 간긍한 static 메서드가 된다.
- static메서드 내에서는 인스턴스 멤버들을 직접 사용할 수 없다.

final - 마지막의, 변경될 수 없는
final은 '마지막의' 또는 '변경될 수 없는'의 의미를 가지고 있으며 거의 모든 대상에 사용 될 수 있다.
변수에 사용되면 값을 변경할 수 없는 상수가 되며, 메서드에 사용되면 오버라이딩을 할 수 없게 되고 클래스에 사용되면 자신을 확장하는 자손클래스를 정의하지 못하게 된다.
final이 사용될 수 있는 곳 - 클래스, 메서드, 멤버변수, 지역변수

제어자 대상 의미
final 클래스 변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다.
그래서 final로 지정된 클래스는 다른 클래스의 조상이 될 수 없다.
메서드 변경될 수 없는 메서드, final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없다.
멤버변수 변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다.
지역변수

 

 

반응형

댓글