https://github.com/whiteship/live-study/issues/5
학습 목차
1. 클래스를 정의하는 방법
2. 객체 만드는 방법 (new 키워드 이해하기)
3. 메소드 정의하는 방법
4. 생성자 정의하는 방법
5. this 키워드 이해하기
Optional
1. int 값을 가지고 있는 이진 트리를 나타내는 Node 라는 클래스를 정의하세요.
int value, Node left, right를 가지고 있어야 합니다.
2. BinaryTree라는 클래스를 정의하고 주어진 노드를 기준으로 출력하는 bfs와 dfs 메소드를 구현하세요
DFS는 왼쪽,루트,오른쪽 순으로 순회하세요.
1. 클래스를 정의하는 방법
클래스의 정의
class MyClass {
// field, constructor
// method declarations
}
클래스 정의에 필요한 요소
1. 접근제어자 : public, private
2. 클래스명 : 첫 글자는 대문자
3. extends superclass : 키워드 extends를 사용해서 부모클래스를 명시한다.
4. implements interface : 키워드 implements를 사용해서 구현한 interface를 명시한다.
5. class body : {} 내부 멤버변수, 생성자, 메서드
클래스 변수와 인스턴스 변수
변수 | 선언위치 | 생성시기 |
클래스변수 | 클래스 영역 | 클래스가 메모리에 올라갈 때 |
인스턴스변수 | 인스턴스가 생성되었을 때 | |
지역변수 | 클래스 영역 이외의 영역 | 변수 선언문이 수행되었을 때 |
1. 클래스 변수
static modifier와 선언된 변수이다. 같은 클래스의 모든 객체가 공유하는 변수이다. 모든 인스턴스가 공통적인 값을 유지해야할 때 사용한다. 클래스가 메모리에 올라갈 때 생성되고, static 영역에 저장되는데 JVM이 종료될 때까지 사라지지 않는다. 클래스명.변수명과 같은 형식으로 사용되고 예를 들면 Card 클래스의 width 변수에 접근하고자 하면 Card.width와 같이 사용할 수 있다.
2. 인스턴스 변수
인스턴스변수는 각각의 인스턴스가 갖는 고유한 값이다. 인스턴스변수는 인스턴스를 생성할 때 heap 영역에 할당된다. 인스턴스 변수는 스태틱 메서드에서 사용할 수 없는데, 스태틱 메서드는 인스턴스가 생성되기 전 메모리에 클래스가 올라갈 때 생성되기 때문이다.
3. 지역변수
지역변수는 메서드 내에서만 사용 가능하고, 메서드가 종료되면 함께 소멸된다. 그리고 지역변수가 선언된 블럭 내부에서만 사용할 수 있다. 그리고 클래스변수, 인스턴스변수와 다르게 지역변수는 필수적으로 초기화해야 한다.
Naming
1. 첫 글자는 소문자 알파벳을 사용한다. $, _와 같은 기호는 사용하지 않는다.
2. gear, speed, cadence와 같이 full name을 사용한다. g, s, c와 같은 임의의 축약어를 사용하지 않는다.
3. 두 단어 이상으로 구성됐을 때, 두번째 단어 이후로 첫 글자는 대문자를 사용한다. ex) gearRatio, currentGear
constant variable의 경우 모두 대문자로 작성하고, 띄어쓰기는 _를 사용한다. ex) NUM_GEARS
초기화 블럭
반복문, 조건문, 예외처리구문 등 명시적 초기화에 비해 복잡한 초기화에 사용한다. 생성자보다 인스턴스 초기화 블럭이 먼저 수행된다는 점을 기억한다. 클래스 초기화 블럭과, 인스턴스 초기화 블럭으로 나뉘는데, 클래스 초기화 블럭은 클래스 변수의 초기화에, 인스턴스 초기화 블럭은 인스턴스변수의 초기화에 사용된다.
초기화 순서
1. 클래스변수 : 기본값 → 명시적 초기화 → 클래스 초기화 블럭
2. 인스턴스 변수 : 기본값 → 명시적 초기화 → 인스턴스 초기화 블럭 → 생성자
예제
public class Car {
// 스태틱 변수 명시적 초기화
static int count;
static String name;
// 인스턴스 변수 명시적 초기화
private int serialNo;
private String color;
private String gearType;
// 인스턴스 초기화 블럭
{
count ++;
serialNo = count;
}
// 생성자
Car() {
color = "white";
gearType = "Auto";
}
Car(String color, String gearType) {
this.color = color;
this.gearType = gearType)
}
}
count는 모든 Car가 공유하는 static 변수이다. Car를 생성할 때마다 count가 업데이트되고 serialNo에 저장된다. serialNo를 할당하는과정이 모든 생성자에 중복되므로 초기화블럭으로 따로 분리하면, 생성자가 실행되기 전에 인스턴스 초기화 블럭을 실행할 수 있다.
2. 객체를 만드는 방법 (new 키워드 이해하기)
Declaring a Variable to Refer to an Object
type name;
Point originOne;
객체를 할당하기 전 참조변수 origOne을 선언한다. 할당하지 않고 참조변수를 사용하려하면 compile error가 난다. 참조변수에 같은 타입이 아니거나 자식 클래스가 아닌 객체가 할당돼도 compile error가 난다.
Instatiating a Class
객체를 생성한다. new 키워드로 객체의 주소를 얻을 수 있고 참조 변수에 저장한다. 객체는 참조변수로만 접근할 수 있다.
Rectangle rectOne = new Rectangle();
Rectangle rectTwo = rectOne
첫번째줄에 new 연산자로 생성한 객체의 주소를 rectOne에 저장했고, rectTwo에 rectOne에 저장된 주소를 복사해서 rectOne과 rectTwo가 같은 객체를 가리키고 있다.
3. 메서드 정의하는 방법
메서드 정의
public double calculateAnswer(double wingSpam, int numbrOfEngines) {
// do the calculation
}
메서드 정의에 필요한 요소
1. 접근제어자 : public, private ...
2. 반환타입 : 메서드에 의해 반환하는 데이터 타입, 아무것도 반환하지 않을 때는 void를 사용한다.
3. 메서드명 : 첫 글자는 소문자, camelCase를 사용할 것
4. parameter : 데이터 타입을 명시해야한다.
5. exception list
6. method body
method signiture : 메서드 이름과 파리미터
calculateAnswer(double wingSpan, int numberOfEngine)
오버로딩
오버로딩의 조건
1. 메서드의 이름이 같아야 한다.
2. 매개변수의 개수 또는 타입이 달라야한다.
반환타입이나 매게변수 이름은 오버로딩과 관련 없다. 메서드 시그니쳐가 같고, 반환타입만 다르다면 컴파일에러가 난다.
오버로딩의 장점
public class DataArtist {
...
public void draw(String s) {
...
}
public void draw(int i) {
...
}
public void draw(int i, double f) {
...
}
}
오버로딩의 장점을 보여주는 예제인데, 반환타입에 따라 메서드를 drawString, drawInt, drawFloat 등으로 같은 기능을 하는 메서드를 여러 이름으로 작성하는 것보다 하나의 이름으로 작성하는 것이 좋다. 메서드를 작성하는 쪽에서도 이름을 짓기 어렵고, 메서드를 사용하는 쪽에서도 이름을 일일이 구분해서 기억하는 것이 번거롭다. 입력 타입이 달라도 이름이 같으니 어떤 기능을 하는지 유추할 수 있고 오류의 가능성을 줄일 수 있다.
반환타입
반환타입은 메서드 출력값의 타입이다. return '반환값'의 타입과 일치하거나 자동 형변환이 가능해야 한다. 예를 들어 반환타입이 int인데 return 뒤의 값이 long이라면 오류가 난다. (반환타입이 long이고 return 뒤에 int가 오는 것은 허용)
위의 예시로 반환타입을 int로 작성했는데 만약 출력값이 없다면 void를 사용하면 된다. 그리고 void를 사용하면 return문을 생략할 수 있는데, 컴파일러가 메서드의 마지막에 자동적으로 return;을 추가해주기 때문이다. 하지만 반환타입이 void가 아니라면 반드시 return 문이 있어야 한다.
참조형 매개변수와 반환타입
기본형 매개변수의 경우 변수의 값을 읽을 수 있지만 변경할 수 없다. 왜냐하면 값만 복사되기 때문에 메서드 안에서 값을 바꿔도 원래 변수에는 영향을 줄 수 없기 때문이다. 하지만 참조형 매개변수는 변수의 값을 읽을 수 있고 변경할 수도 있다. 왜냐하면 주소 자체를 넘겨주기 때문에 메서드 내에서 값을 변경하면 원래 변수의 값이 바뀔 수 있는 것이다.
반환타입 또한 참조형이 될 수 있는데, 객체의 주소를 반환한다. 마찬가지로 반환타입이 반환값의 타입과 일치해야한다.
class Data
{
int x;
}
public class Example {
public static void main(String[] args) {
Data d = new Data();
d.x = 2;
change(d.x);
System.out.println(d.x);
Data d2 = copy(d);
System.out.println(d.x);
System.out.println(d2.x);
}
static void change(int x)
{
x = 1000;
}
static Data copy(Data d)
{
Data tmp = new Data();
tmp.x = d.x;
d.x = 3;
return tmp;
}
}
위의 코드를 실행시키면 2 3 2 순서대로 출력된다. 첫번째 출력의 경우 change를 호출해도 기본형 매게변수로 넘겨줬기 때문에 원래 값에는 영향을 주지 않는다. 그리고 두번째 출력 d.x의 경우 copy에 참조형 매게변수를 넘겼기 때문에 메서드 내에서 값을 변경한 것이 반영된다. (주소 자체를 넘겨서 메모리에 접근 가능한 것) 그리고 d2.x의 경우 copy에서 d.x를 바꾸기 전의 값을 새로운 객체에 복사해서 return값에 반환했기 때문에 출력의 결과로 2가 출력된다.
클래스 메서드와 인스턴스 메서드
클래스 메서드는 클래스 변수와 마찬가지로 앞에 static을 붙인다. 인스턴스 변수를 사용하지 않을 때 사용한다. 앞서 설명했듯이 인스턴스 메서드는 인스턴스 멤버와 클래스 멤버를 자유롭게 호출하고 참조할 수 있지만 클래스 메서드는 객체를 생성하지 않으면 인스턴스 멤버를 사용할 수 없다.
4. 생성자를 정의하는 법
생성자란?
인스턴스 초기화 메서드이다. 클래스의 이름과 같고, 리턴 값이 없는 메서드이다. 인스턴스를 생성하는 것은 아니다. 앞서 언급했듯 인스턴스를 생성하고 주소를 반환하는 것은 연산자 new의 역할이다. 위의 모든 예시에서 별도의 생성자를 만들지 않았는데, 생성자가 하나도 없다면 컴파일러가 기본 생성자를 만들어 준다.
기본 생성자는 매개변수가 없는 생성자인데, 아래 예시처럼 만약 클래스 내에 다른 생성자를 정의했다면 기본 생성자를 사용할 수 없다. 왜냐하면 컴파일러는 생성자가 하나도 정의되지 않았을 때만 기본 생성자를 추가해주기 때문이다. 따라서 클래스를 만들 때, 항상 직접 만들어주는 습관을 들이면 좋다.
public class Example {
public static void main(String[] args) {
Card card1 = new Card();
}
}
class Card {
String kind;
int num;
Card(String k, int n){
kind = k;
num = n;
}
}
public class Example {
public static void main(String[] args) {
Card card1 = new Card();
card1.kind = "spade";
card1.num = 3;
Card card2 = new Card("heart", 4);
}
}
class Card {
String kind;
int num;
Card() {}
Card(String k, int n){
kind = k;
num = n;
}
}
5. this 키워드 이해하기
this()
생성자들끼리 서로 호출할 수 있는데, 생성자 내에서 다른 생성자를 호출할 때, 클래스이름이 아니라 this() 키워드를 사용해서 다른 생성자를 호출한다. 바로 위 예제에서 card1을 비교적 복잡하게 초기화했는데 매게변수로 값을 넘겨주지 않아도 default값을 설정할 수 있다.
public class example {
public static void main(String[] args) {
Card card1 = new Card();
Card card2 = new Card("heart");
Card card3 = new Card(4);
Card card4 = new Card("heart",4);
}
}
class Card {
String kind;
int num;
Card() {
this("spade", 3);
}
Card(String k) {
this(k, 3);
}
Card(int n) {
this("spade", n);
}
Card(String k, int n) {
kind = k;
num = n;
}
}
위처럼 매개변수와 인스턴스 변수의 이름이 다르면 혼동할 수 있기 때문에 아래와 같이 this를 사용하면 좋다.
this
this는 위에 설명한 this() 생성자가 아니라 참조변수 자신을 의미한다. this에는 인스턴스의 주소가 저장돼있는데, this.kind를 통해서 같은 이름의 매개변수 kind와 구별할 수 있고, 지역변수가 아닌 인스턴스변수로 사용할 수 있다. 위의 코드를 아래와 같이 바꾸면 된다.
public class example {
public static void main(String[] args) {
Card card1 = new Card();
Card card2 = new Card("heart");
Card card3 = new Card(4);
Card card4 = new Card("heart",4);
}
}
class Card {
String kind;
int num;
Card() {
this("spade", 3);
}
Card(String k) {
this(k, 3);
}
Card(int n) {
this("spade", n);
}
Card(String kind, int num) {
this.kind = kind;
this.num = num;
}
}
기억해야할 점으로 클래스메서드에서는 this를 사용하지 않는다는 것이다. 클래스메서드는 인스턴스변수를 사용하지 않는 메서드인데 this는 인스턴스의 참조변수이기 떄문이다.
Optional
1. int 값을 가지고 있는 이진 트리를 나타내는 Node 라는 클래스를 정의하세요.
int value, Node left, right를 가지고 있어야 합니다.
2. BinaryTree라는 클래스를 정의하고 주어진 노드를 기준으로 출력하는 bfs와 dfs 메소드를 구현하세요
DFS는 왼쪽,루트,오른쪽 순으로 순회하세요.
reference
1. 자바의 정석 3판
2. 오라클 튜토리얼
'자바 > 백기선 자바스터디' 카테고리의 다른 글
[백기선 자바스터디] 인터페이스 (0) | 2022.08.10 |
---|---|
[백기선 자바스터디] 패키지 (0) | 2022.07.25 |
[백기선 자바스터디] 상속 (0) | 2022.07.14 |
[백기선 자바스터디] 연산자 : instanceof, shift 연산자 (0) | 2022.07.02 |
[백기선 자바스터디] 자바 데이터 타입, 변수 : 형변환, 타입추론 (0) | 2022.06.25 |