Skip to content

Latest commit

 

History

History
750 lines (628 loc) · 21.4 KB

220305.md

File metadata and controls

750 lines (628 loc) · 21.4 KB

메소드의 호출

메소드이름(값1, 값2, ...)
public class Ex2 {

	public static void main(String[] args) {
		Mymath math_class = new Mymath();
		long result1 = math_class.add(10, 1);
		long result2 = math_class.substract(10, 1);
		long result3 = math_class.multiply(10, 1);
		long result4 = math_class.divide(10, 1);
		
		System.out.printf("%d %d %d %d", result1, result2, result3, result4);
	}

}
class Mymath{
	long add(long a, long b) {
		return a + b;
	}
	
	long substract(long a, long b) {
		return a - b;
	}
	
	long multiply(long a, long b) {
		return a * b;
	}
	
	long divide(long a, long b) {
		return a / b;
	}
}

11 9 10 10

return 문

실행 중인 메소드를 종료하고 호출한 곳으로 되돌아 간다.
반환타입이 void가 아닌 경우, 반드시 return 문이 필요하다.

호출 스택 (call stack)

스택 : 밑이 막힌 상자. 위에 차곡차곡 쌓인다. 선입후출
메소드 수행에 필요한 메모리가 제공되는 공간을 말한다.
메소드가 호출되면 호출스택에 메모리 할당, 종료되면 해제된다.

아래 있는 메소드가 위의 메소드를 호출한 것이다.
맨 위의 메소드 하나만 실행 중이고 나머지는 대기중이다.

기본형 매개변수

기본형 매개변수 : 변수의 값을 읽기만 할 수 있다.(read only)
참조형 매개변수 : 변수의 값을 읽고 변경할 수 있다.(read & write)

public class Ex2 {

	public static void main(String[] args) {
		Data d = new Data();
		d.x = 10;
		System.out.printf("x = %d%n", d.x);
		change(d.x);
		System.out.println("After change()");
		System.out.printf("x = %d%n", d.x);
	}
	static void change(int x) {
		x = 1000;
		System.out.printf("x = %d%n", x);
	}

}
class Data{
	int x;
}

x = 10
x = 1000
After change()
x = 10

참조형 매개변수

public class Ex2 {

	public static void main(String[] args) {
		Data d = new Data();
		d.x = 10;
		System.out.printf("x = %d%n", d.x);
		change(d);
		System.out.println("After change()");
		System.out.printf("x = %d%n", d.x);
	}
	static void change(Data d) {
		d.x = 1000;
		System.out.printf("x = %d%n", d.x);
	}

}
class Data{
	int x;
}

x = 10
x = 1000
After change()
x = 1000

참조형 반환타입

public class Ex2 {

	public static void main(String[] args) {
		Data d = new Data();
		d.x = 10;
		Data d2 = copy(d);
		System.out.printf("d.x = %d%n", d.x);
		System.out.printf("d2.x = %d%n", d2.x);
	}
	static Data copy(Data d) {
		Data tmp = new Data();
		tmp.x = d.x;
		return tmp;
	}

}
class Data{
	int x;
}

d.x = 10
d2.x = 10

반환타입이 참조형이라는 것은 객체의 주소를 반환한다는 것이다.

static 메소드와 인스턴스 메소드

class MyMath2 {
    long a, b;

    long add() {
        return a + b;
    }

    static long add(long a, long b) {
        return a + b;
    }
}

메소드 앞에 static이 붙은 것이 클래스 메소드(static 메소드), 안 붙은 것이 인스턴스 메소드다.

인스턴스 메소드

  • 인스턴스 생성 후, '참조변수.메소드이름()'으로 호출한다.
  • 인스턴스 멤버(인스턴스 변수, 인스턴스 메소드)와 관련된 작업을 하는 메소드
  • 메소드 내에서 인스턴스 변수 사용 가능

static 메소드(클래스 메소드)

  • 객체 생성 없이 '클래스이름.메소드이름()'으로 호출
  • 인스턴스 멤버와 관련없는 작업을 하는 메소드
  • 메소드 내에서 인스턴스 변수 사용 불가
// 클래스 메소드 호출
MyMath2.add(200L, 100L);

// 인스턴스 메소드 호출
MyMath2 mm = new MyMath2();
mm.a = 200L;
mm.b = 100L;
mm.add();

static은 언제 붙여야 하는가

속성(멤버 변수) 중에서 공통 속성에 static을 붙인다.
인스턴스 멤버를 사용하지 않는 메소드에 static을 붙인다.

메소드 간의 호출과 참조

static 메소드는 인스턴스 변수(iv)를 사용할 수 없다.

class TestClass2 {
    int iv;
    static int cv;

    void instanceMethod() {
        System.out.println(iv);
        System.out.println(cv);
    }

    static void staticMethod() {
        System.out.println(iv); // 에러
        System.out.println(cv);
    }
}

static 메소드는 인스턴스 메소드(im)를 호출할 수 없다.

class TestClass {
    void instanceMethod() {}
    static void staticMethod() {}

    void instanceMethod2() {
        instanceMethod();
        staticMethod();
    }

    static void staticMethod2() {
        instanceMethod();   // 에러
        staticMethod();
    }
}

static 메소드 호출 시 객체가 없을 수도 있어서 인스턴스 멤버를 사용하지 못 하는 것이다.

오버로딩(overloading)

한 클래스 안에 같은 이름의 메소드를 여러 개 정의하는 것을 오버로딩이라고 한다.

오버로딩이 성립하기 위한 조건

  1. 메소드 이름이 같아야 한다.
  2. 매개변수의 개수 또는 타입이 달라야 한다.
  3. 반환 타입은 영향없다.
// 매개변수의 개수, 타입이 같기 때문에 오버로딩이 아니다.
int add(int a, int b) { return a+b; }
int add(int x, int y) { return x+y; }

// 반환 타입은 오버로딩에 영향이 없으므로 오버로딩이 아니다.
int add(int a, int b) { return a+b; }
long add(int a, int b) { return (long)(a+b); }

// 오버로딩 성립
long add(int a, long b) { return a+b; }
long add(long a, int b) { return a+b; }

마지막은 오버로딩은 성립하지만 add(3, 3)으로 호출하면 컴파일러가 둘 중 어느 것을 말하는 것인지 찾지 못해 에러가 발생한다. add(3, 3L)과 같은 형식으로 명시해줘야 한다.

생성자(constructor)

인스턴스가 생성될 때마다 호출되는 '인스턴스 초기화 메소드'
인스턴스 생성시 수행할 작업에 사용

이름이 클래스 이름과 같아야 한다.
리턴값이 없다.(void 안 붙임)
모든 클래스는 반드시 생성자를 가져야 한다.

클래스이름(타입 변수명, 타입 변수명, ...) {
    // 인스턴스 생성 시 수행될 코드
}

class Card{
    ...
    Card() { // 매개변수 없는 생성자

    }

    Card(String kind, int number) { // 매개변수 있는 생성자

    }
}

생성자도 오버로딩이 가능하다.

기본 생성자

매개변수가 없는 생성자
생성자가 하나도 없을 때만, 컴파일러가 자동 추가

매개변수가 있는 생성자

new가 객체를 생성해줌, 생성자는 객체 초기화 역할

생성자 this()

생성자에서 다른 생성자 호출할 때 사용한다.
다른 생성자 호출시 첫 줄에서만 사용 가능하다.

class Car2 {
    String colr;
    String gearType;
    int door;

    Car2() {
        this("white", "auto", 4);   // Car2(String color, String gearType, int door)를 호출
    }

    Car2(String color) {
        this(color, "auto", 4);     // Car2(String color, String gearType, int door)를 호출
    }

    Car2(String color, String gearType, int door) {
        this.color = color;
        this.gearType = gearType;
        this.door = door;
    }
}

this를 사용하면 코드의 중복을 제거할 수 있다.

참조변수 this

인스턴스 자신을 가리키는 참조변수
인스턴스 메소드(생성자 포함)에서 사용가능
지역변수와 인스턴스 변수를 구별할 때 사용한다.
인스턴스의 주소가 저장되어 있다.
모든 인스턴스 메소드에 지역변수로 숨겨진 채로 존재한다.

class MyMath2 {
    long a, b; // 진짜 이름은 this.a, this.b

    MyMath2(int a, int b) {
        this.a = a;
        this.b = b;
    }
}

this와 this()는 완전히 다른 것을 이해해야 한다.

변수의 초기화

지역변수는 사용 전에 반드시 수동 초기화 해야 한다.
멤버변수는 자동 초기화 된다.

멤버변수의 초기화

명시적 초기화(간단한 초기화)

class Car {
    int door = 4;               // 기본형 변수의 초기화
    Engine e = new Engine();    // 참조형 변수의 초기화
}

초기화 블럭(복잡한 초기화)

  • 인스턴스 초기화 블럭 : {}
  • 클래스 초기화 블럭 : static {}
class StaticBlockTest {
    static int[] arr = new int[10]; // 명시적 초기화

    static { // 클래스 초기화 블럭
        for(int i=0; i<arr.length; i++) {
            arr[i] = (int)(Math.random()*10) + 1;
        }
    }
}

생성자(iv초기화, 복잡한 초기화)

Car(String color, String gearType, int door) {
    this.color = color;
    this.gearType = gearType;
    this.door = door;
}

클래스 변수 초기화 시점 : 클래스가 처음 로딩될 때 단 한번
인스턴스 변수 초기화 시점 : 인스턴스가 생성될 때 마다

class InitTest {
    static int cv = 1;
    int iv = 1;

    static { cv = 2; }
    { iv = 2; }

    InitTest() {
        iv = 3;
    }
}

위 예제에서 초기화 단계는 다음과 같다.

  • 클래스 초기화
    1. cv가 0으로 자동 초기화
    2. cv가 1로 명시적 초기화
    3. cv가 2로 클래스 초기화 블럭을 통해 초기화
  • 인스턴스 초기화
    1. iv가 0으로 자동 초기화
    2. iv가 1로 명시적 초기화
    3. iv가 2로 인스턴스 초기화 블럭을 통해 초기화
    4. iv가 3으로 생성자를 통해 초기화

클래스 변수가 먼저 초기화가 되고 그 후에 인스턴스 초기화가 이루어 진다. 자동 > 간단 > 복잡 순으로 초기화 된다.

상속(Inheritance)

기존의 클래스로 새로운 클래스를 작성하는 것(코드의 재사용)
두 클래스를 부모와 자식으로 관계를 맺어주는 것

class 자식클래스 extends 부모클래스{
    ...
}

자손은 조상의 모든 멤버를 상속받는다. (생성자, 초기화블럭 제외)
자손의 멤버 개수는 조상보다 적을 수 없다.(같거나 많다)
자손의 변경은 조상에 영향을 미치지 않는다.

class Point {
    int x;
    int y;
}

// 상속 x
class Point3D {
    int x;
    int y;
    int z;
}

// 상속
class Point3D extends Point {
    int z;
}

포함 관계

클래스의 멤버로 참조변수를 선언하는 것을 말한다.

// Circle이 Point를 포함하는 관계
class Circle {
    Point c = new Point();
    int r;
}

작은 단위의 클래스를 만들고, 이들을 조합해서 클래스를 만든다.

class Car {
    Engine e = new Engine();
    Door d = new Door[4];
}

클래스 간의 관계 결정하기

상속관계 : A는 B이다. (is-a)
포함관계 : A는 B를 가지고 있다. (has-a)

// 포함
class Circle{
    Point c = new Point();
    int r;
}

// 상속
class Circle extends Point {
    int r;
}

원은 점이다.
원은 점을 가지고 있다.

둘 중 "원은 점을 가지고 있다"가 더 자연스러우므로 포함관계가 좋아 보인다.
이는 절대적인 것은 아니나 참고는 할만하다.

상속은 꼭 필요할 때만 사용한다.

단일 상속(Single Inheritance)

Java는 단일 상속만을 허용한다. 하나의 부모만 상속할 수 있다는 것이다.
필요한 경우 비중이 높은 클래스 하나만 상속관계로, 나머지는 포함관계로 한다.

Object 클래스

모든 클래스의 조상으로 부모가 없는 클래스는 자동적으로 Object 클래스를 상속받게 된다.
모든 클래스는 Object 클래스에 정의된 11개의 메소드를 상속 받는다. toString(), equals(Object obj), hashCode(), ...

오버라이딩(overriding)

상속받은 조상의 메소드를 자신에 맞게 변경하는 것

class Point {
    int x;
    int y;
    
    String getLocation() {
        return "x :" + x + ", y :" + y;
    }
}

class Point3D extends Point {
    int z;

    String getLocation() {
        return "x :" + x + ", y :" + y + ", z :" + z;
    }
}

선언부는 변경할 수 없고, 내용(구현부)만 변경할 수 있다.

오버라이딩의 조건

  1. 선언부가 조상 클래스의 메소드와 일치해야 한다.
  2. 접근 제어자를 조상 클래스의 메소드보다 좁은 범위로 변경할 수 없다. (public, protected, private)
  3. 예외는 조상 클래스의 메소드보다 많이 선언할 수 없다.

참조변수 super

객체 자신을 가리키는 참조변수. 인스턴스 메소드(생성자)내에만 존재
조상의 멤버를 자신의 멤버와 구별할 때 사용한다.

public class Ex2 {

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

class Parent { int x = 10; /*super.x*/ }

class Child extends Parent {
	int x = 20; // this.x
	
	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() - 조상의 생성자

조상의 생성자를 호출할 때 사용
조상의 멤버는 조상의 생성자를 호출해서 초기화

class Point {
    int x, y

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

class Point3D extends Point {
    int z;

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

생성자의 첫 줄에 반드시 생성자를 호출해야 한다.
그렇지 않으면 컴파일러가 생성자의 첫 줄에 super();를 삽입한다.

기본 생성자는 작성하는 것이 좋다.

패키지(package)

서로 관련된 클래스의 묶음
클래스는 클래스 파일(.class), 패키지는 폴더. 하위 패키지는 하위 폴더
클래스의 실제 이름(full name)은 패키지를 포함(java.lang.String)

패키지의 선언

패키지는 소스파일의 첫 번쨰 문장으로 단 한번 선언
같은 소스 파일의 클래스들은 모두 같은 패키지에 속하게 된다.
패키지 선언이 없으면 이름없는 패키지에 속하게 된다.

package ch02;

클래스 패스(classpath)

클래스 파일(.class)의 위치를 알려주는 경로
환경변수 classpath로 관리하며, 경로간의 구분자는 ';'를 사용 classpath(환경변수)에 패키지의 루트를 등록해줘야 함

import 문

클래스를 사용할 때 패키지 이름을 생략할 수 있다.
컴파일러에게 클래스가 속한 패키지를 알려준다.
java.lang 패키지의 클래스는 import하지 않고도 사용할 수 있다. (String, Object, System, Thread, ...)

Eclipse에서 ctrl + shift + o를 누르면 자동으로 import 해준다.

import 문의 선언

import 패키지명.클래스명;
import 패키지명.*; // 모든 클래스

import 문은 패키지문과 클래스 선언의 사이에 선언한다.
import 문은 컴파일 시에 처리되므로 프로그램의 성능에 영향이 없다.

// java.util과 java.text의 모든 클래스
import java.util.*;
import java.text.*;

// java 패키지의 모든 클래스 (패키지는 포함 안됨, 즉 util과 text는 포함되지 않는다.)
import java.*;

이름이 같은 클래스가 속한 두 패키지를 import 할 때는 클래스 앞에 패키지명을 붙여 줘야 한다.

import java.sql.*;  // java.sql.Date
import java.util.*; // java.util.Date

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

static import 문

static 멤버를 사용할 때 클래스 이름을 생략할 수 있게 해준다.

import static java.lang.Integer.*;    // Integer 클래스의 모든 static 메소드
import static java.lang.Math.random;  // Math.random()만. 괄호 안붙임
import static java.lang.System.out;   // System.out을 out만으로 참조 가능

// System.out.println(Math.random());
out.println(random()); // 생략 O

static import 문은 필요할 때만 사용

제어자(modifier)

클래스와 클래스의 멤버(멤버 변수, 메소드)에 부가적인 의미 부여

  • 접근 제어자 : public, protected, (default), private
  • 그 외 : static, final, abstract, ...

하나의 대상에 여러 제어자를 같이 사용 가능 (접근 제어자는 하나만)

public class ModifierTest {
    public static final int WIDTH = 200;
}

static - 클래스의, 공통적인

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

final - 마지막의, 변경될 수 없는

대상 의미
클래스 변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다.
그래서 final로 지정된 클래스는 다른 클래스의 조상이 될 수 없다.
메소드 변경될 수 없는 메소드, final로 지정된 메소드는 오버라이딩을 통해 재정의 될 수 없다.
멤버변수
지역변수
변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다.
final class FinalTest {             // 조상이 될 수 없는 클래스
    final int MAX_SIZE = 10;        // 상수(멤버변수)

    final void getMaxSize() {       // 오버라이딩할 수 없는 메소드
        final int LV = MAX_SIZE;    // 상수(지역변수)
        return MAX_SIZE;
    }
}

abstract - 추상의, 미완성의

대상 의미
클래스 클래스 내에 추상 메소드가 선언되어 있음을 의미한다.
메소드 선언부만 작성하고 구현부는 작성하지 않은 추상 메소드임을 알린다.
abstract class AbstractTest {   // 추상 클래스(추상 메소드를 포함한 클래스)
    abstract void move();   // 추상 메소드(구현부가 없는 메소드)
}

추상 클래스의 인스턴스 생성은 할 수 없다.
추상 클래스를 상속받아서 완전한 클래스(구상 클래스)를 만든 후에 객체 생성이 가능하다.

접근 제어자(access modifier)

  • private : 같은 클래스 내에서만 접근이 가능
  • (default) : 같은 패키지 내에서만 접근이 가능
  • protected : 같은 패키지 내에서, 그리고 다른 패키지의 자손 클래스에서 접근이 가능
  • public : 접근 제한이 전혀 없음

클래스 앞에는 public과 (default)만 붙일 수 있다.

package ch02;

class MyParent {
	private int prv;
	int dft;
	protected int prt;
	public int pub;
	
	public void printMembers() {
		System.out.println(prv);
		System.out.println(dft);
		System.out.println(prt);
		System.out.println(pub);
	}
}

public class EX3 {
	public static void main(String[] args) {
		MyParent mp = new MyParent();
		System.out.println(mp.prv); // 에러
		System.out.println(mp.dft);
		System.out.println(mp.prt);
		System.out.println(mp.pub);
	}
}

MyParent 클래스가 아닌 EX3 클래스에서 접근을 했기 때문에 prv에서 에러가 발생한 것이다.

캡슐화와 접근 제어자

접근 제어자를 사용하는 이유

  • 외부로부터 데이터를 보호하기 위해
  • 외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해
public class Time {
    private int hour;
    private int minute;
    private int second;

    public int getHour() { return hour; }
    public void setHour(int hour) {
        if (hour < 0 || hour > 23) return;
        this.hour = hour;
    }
}

인스턴스 변수들은 private로 하여 외부에서 직접 접근하지 못하게 하고, 메소드는 public으로 하여 외부에서 메소드를 통한 간접 접근을 허용한다.

내부에서만 쓰는 메소드는 private로 하는 것이 나중에 유지보수할 때 편하다. 특히 테스트를 할 때 테스트 범위를 특정할 수 있다.

다형성(polymorphism)

여러 가지 형태를 가질 수 있는 능력
조상 타입 참조 변수로 자손 타입 객체를 다루는 것

class Tv {
    boolean power;
    int channel;

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

class SmartTv extends Tv {
    String text;
    void caption() { /*내용 생략*/ }
}
Tv t = new SmartTv(); // 타입 불일치, 다형성

객체와 참조변수의 타입이 일치할 때와 일치하지 않을 때의 차이

SmartTv s = new SmartTv(); // 참조 변수와 인스턴스의 타입이 일치
Tv t = new SmartTv();      // 조상 타입 참조변수로 자손 타입 인스턴스 참조

s의 경우 7개(power, channel, power(), channelUp(), channerlDown(), text, caption)를 모두 사용 가능, 하지만 t의 경우는 5개(power, channel, power(), channelUp(), channelDown())만 다룰 수 있다.
즉, t는 실제 멤버가 7개여도 정의된 5개의 멤버만 사용 가능하다는 것이다.

자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 없다.

Tv t = new SmartTb(); // 허용
SmartTv s = new Tv(); // 에러

참조변수의 타입은 인스턴스의 타입과 반드시 일치해야 하는 것은 아니다.
참조변수가 조상타입일 때와 자손타입일 때의 차이는 참조변수로 사용할 수 있는 멤버의 갯수가 달라진다는 것이다.