[Java] 5주차 과제: 클래스
by Roel Downey스터디 링크 : 링크
객체 지향 프로그래밍 (OOP) : 객체 지향 프로그래밍에서는 모든 데이터를 객체(Object)로 취급하며, 이러한 객체가 바로 프로그래밍 중심이 된다.
객체(Object)란? 간단히 이야기하자면 실생활에서 우리가 인식할 수 있는 사물로 설명할 수 있다.
이러한 객체의 상태(state)와 행동(behavior)을 구체화 하는 형태의 프로그래밍이 바로 객체지향 프로그래밍이다.
이때 객체를 만들어 내기 위한 설계도와 같은 개념을 클래스(class)라고 한다.
클래스 정의하는 방법
자바에서 클래스(class)란 객체를 정의하는 틀 또는 설계도와 같은 의미로 사용된다.
자바에서는 이러한 설계도인 클래스를 가지고, 여러 객체를 생성하여 사용하게 된다.
클래스는 객체의 상태를 나타내는 필드(field)와 객체의 행동을 나타내는 메소드(method)로 구성된다.
즉, 필드(field)란 클래스에 포함된 변수(variable)를 의미한다.
또한, 메소드(method)란 어떠한 특정 작업을 수행하기 위한 명령문의 집합이라 할 수 있다.
클래스의 구성 요소
자바에서 클래스는 멤버(member)로 속성을 표현하는 필드(field)와 기능을 표현하는 메소드(method)를 가진다.
또한, 클래스는 생성된 객체의 필드를 초기화해주는 특별한 메소드인 생성자(constructor)를 가진다.
class Car { // 클래스 이름
private String modelName; // 필드
private int modelYear; // 필드
Car(String modelName, int modelYear) { // 생성자
this.modelName = modelName;
this.modelYear = modelYear;
}
public String getModel() { // 메소드
return this.modelYear + "년식 " + this.modelName + " " + this.color;
}
}
객체 만드는 방법 (new 키워드 이해하기)
자바에서 클래스를 사용하기 위해서는 우선 해당 클래스 타입의 객체(object)를 선언해야 한다.
이렇게 클래스로부터 객체를 선언하는 과정을 클래스의 인스턴스 화라고 한다.
또한, 이렇게 선언된 해당 클래스 타입의 객체를 인스턴스(instance)라고 한다.
즉, 인스턴스란 메모리에 할당된 객체를 의미한다.
자바에서는 하나의 클래스로부터 여러 개의 인스턴스를 생성할 수 있다.
이렇게 생성된 인스턴스는 독립된 메모리 공간에 저장된 자신만의 필드를 가질 수 있다.
하지만 해당 클래스의 모든 메소드(method)는 해당 클래스에서 생성된 모든 인스턴스가 공유하게 된다.
메소드 정의하는 방법
자바에서 클래스는 멤버(member)로 속성을 표현하는 필드(field)와 기능을 표현하는 메소드(method)를 가진다.
그중에서 메소드(method)란 어떠한 특정 작업을 수행하기 위한 명령문의 집합이라 할 수 있다.
자바에서 메소드를 정의하는 방법은 다음과 같다.
문법 접근제어자 반환타입 메소드이름(매개변수목록) { //선언부 // 구현부 } |
1. 접근 제어자 : 해당 메소드에 접근할 수 있는 범위를 명시한다.
2. 반환 타입(return type) : 메소드가 모든 작업을 마치고 반환하는 데이터의 타입을 명시한다.
3. 메소드 이름 : 메소드를 호출하기 위한 이름을 명시한다.
4. 매개변수 목록(parameters) : 메소드 호출 시에 전달되는 인수의 값을 저장할 변수들을 명시한다.
5. 구현부 : 메소드의 고유 기능을 수행하는 명령문의 집합이다.
자바에서는 하나의 클래스에 같은 이름의 메소드를 둘 이상 정의할 수 없다.
하지만 메소드 오버로딩(overloading)을 이용하면, 같은 이름의 메소드를 중복하여 정의할 수 있다.
메소드 오버로딩이란 매개변수의 개수나 타입을 다르게 하여 같은 이름의 또 다른 메소드를 작성하는 것이다.
이러한 메소드 오버로딩을 사용함으로써 메소드에 사용되는 이름을 절약할 수 있다.
또한, 메소드를 호출할 때 전달해야 할 매개변수의 타입이나 개수에 대해 크게 신경을 쓰지 않고 호출할 수 있게 된다.
class Car {
private int currentSpeed;
private int accelerationTime;
...
public void accelerate(int speed, int second) { // 선언부
// 구현부
System.out.println(second + "초간 속도를 시속 " + speed + "(으)로 가속함!!");
}
...
}
생성자 정의하는 방법
클래스를 가지고 객체를 생성하면, 해당 객체는 메모리에 즉시 생성된다.
하지만 이렇게 생성된 객체는 모든 인스턴스 변수가 아직 초기화되지 않은 상태이다.
따라서 자바에서는 객체의 생성과 동시에 인스턴스 변수를 원하는 값으로 초기화할 수 있는 생성자(constructor)라는 메소드를 제공한다.
자바에서 생성자(constructor)의 이름은 해당 클래스의 이름과 같아야 한다.
즉, Car 클래스의 생성자는 Car()가 된다.
이러한 생성자는 다음과 같은 특징을 가진다.
1. 생성자는 반환값이 없지만, 반환 타입을 void형으로 선언하지 않는다.
2. 생성자는 초기화를 위한 데이터를 인수로 전달받을 수 있다.
3. 객체를 초기화하는 방법이 여러 개 존재할 경우에는 하나의 클래스가 여러 개의 생성자를 가질 수 있다.
즉, 생성자도 하나의 메소드이므로, 메소드 오버로딩이 가능하다는 의미이다
생성자의 선언
문법 클래스이름() {......} // 매개변수가 없는 생성자 선언 클래스 이름(인수1,인수2...) {.....} // 매개변수가 있는 생성자 선언 |
생성자 중에는 매개변수를 전달받아 인스턴스 변수를 초기화하는 생성자도 선언할 수 있다.
class Car {
private String modelName;
private int modelYear;
private String color;
private int maxSpeed;
private int currentSpeed;
Car(String modelName, int modelYear, String color, int maxSpeed) {
this.modelName = modelName;
this.modelYear = modelYear;
this.color = color;
this.maxSpeed = maxSpeed;
this.currentSpeed = 0;
}
public String getModel() {
return this.modelYear + "년식 " + this.modelName + " " + this.color;
}
}
public class Method02 {
public static void main(String[] args) {
Car myCar = new Car("아반떼", 2016, "흰색", 200); // 생성자의 호출
System.out.println(myCar.getModel()); // 생성자에 의해 초기화되었는지를 확인함.
}
}
생성자를 호출은 new 키워드를 사용하여 객체를 생성할 때 자동으로 생성자가 호출된다.
자바의 모든 클래스에는 하나 이상의 생성자가 정의되어 있어야 한다.
하지만 특별히 생성자를 정의하지 않고도 인스턴스를 생성할 수 있다.
이것은 자바 컴파일러가 기본 생성자(default constructor)라는 것을 기본적으로 제공해 주기 때문이다.
기본 생성자는 매개변수를 하나도 가지지 않으며, 아무런 명령어도 포함하고 있지 않다.
자바 컴파일러는 컴파일 시 클래스에 생성자가 하나도 정의되어 있지 않으면, 자동으로 기본 생성자를 추가한다.
문법 클래스이름() { } |
this 키워드 이해하기
this 키워드는 생성자, static이 아닌 블럭에서 사용될 수 있다.
이것의 의미는 this 키워드를 호출한 생성자나 메서드의 현재 객체를 참조한다.
this를 사용하여 인스턴스 메서드 또는 생성자 내에서 현재 객체의 모든 멤버를 참조 할 수 있다.
예를 들어 설명하겠다.
public class Point {
public int x = 0;
public int y = 0;
public Point(int x, int y) {
x = x;
y = y;
}
}
위의 예제 작성한것을 this를 사용해서 나타내보겠다.
public class Point {
public int x = 0;
public int y = 0;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
this 생성자
생성자에서 this 키워드를 사용해서 동일한 클래스의 다른 생성자를 호출 할 수 있다.
이것을 명시적 생성자 호출이라고 한다.
public class Rectangle {
private int x, y;
private int width, height;
public Rectangle() {
this(0, 0, 1, 1);
}
public Rectangle(int width, int height) {
this(0, 0, width, height);
}
public Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
이 클래스는 생성자의 집합을 포함한다. 각각의 생성자는 멤버 변수들의 일부 혹은 전부를 초기화한다. 생성자는 초기 값이 파라미터에 의해 제공되지 않는 모든 멤버 변수에 대한 기본 값을 제공한다.
컴파일러는 인수의 수와 타입에 따라서 호출할 생성자를 선택한다.
규칙으로 this 생성자는 반드시 생성자의 첫줄에 등장시킨다.
- 참고 링크 : docs.oracle.com/javase/tutorial/java/javaOO/thiskey.html
Nested Class
자바 프로그래밍에서는 한 클래스 내부에 다른 클래스를 선언할 수 있다. 이러한 클래스를 Nested Class, 즉 중첩클래스라고 한다.
Nested Class는 두가지의 카테고리로 나누어진다.
- static 키워드를 이용한 경우, static nested class라고 한다.
- static 키워드를 이용하지 않은 경우, inner class라고 한다.
public class OuterClass {
static class StaticNestedClass {
}
class InnerClass {
}
}
중첩 클래스는 중첩 클래스를 둘러싼 클래스의 멤버이기 때문에, 둘러싼 클래스의 필드에 얼마든지 접근이 가능하다.(private로 되어있어도 접근이 가능하다). 하지만 static으로 선언한 중첩 클래스의 경우 필드에 접근이 불가능하다.
중첩 클래스 또한 둘러싼 클래스의 멤버이기때문에, 모든 접근제어 지시자를 사용할 수 있다.
- 중첩 클래스를 사용하는 이유
- 한 곳에서만 사용되는 클래스들을 논리적으로 그룹화할 수 있기 때문.
- 캡슐화를 증가시킬 수 있기 때문.
- 좀 더 읽기 쉽고 유지보수하기 쉬운 코드로 이어질 수 있기 때문.
Static Nested Class
Static Nested Class는 바깥 클래스의 변수나 메소드에 직접 접근이 불가능하다. 따라서 바깥 클래스의 변수나 메소드에 접근하기위해서는 그 클래스의 객체 참조를 사용해야한다.
Static Nested Class는 일반적으로 선언하는 클래스처럼 사용한다. 따라서 단순히 패키징을 보다 편하게 하기 위해 사용된 클래스라고 이해하시면 된다.
Static Nested Class는 바깥 클래스의 이름을 통해 접근할 수 있다.
public class OuterClass {
public static class StaticNestedClass {
}
}
public class Main {
public static void main(String[] args) {
OuterClass.StaticNestedClass staticNestedClass = new OuterClass.StaticNestedClass();
}
}
Inner Class
Inner Class는 객체의 메서드나 필드에 바로 접근이 가능하지만, Inner Class는 인스턴스와 관련되기 때문에(정확히는 인스턴스의 멤버 변수로서 정의되기때문에) static 멤버 변수를 가질 수 없다.
public class OuterClass {
public class InnerClass {
static int NUMBER = 10; // 컴파일 에러 발생
static final int IMMUTABLE_NUMBER = 10; // 선언 가능
}
}
위의 코드를 보면 단순히 static 키워드만 쓴 멤버 변수는 컴파일 에러가 발생하게 된다. 그러한 이유는 Inner Class는 바깥 클래스의 인스턴스에 종속적인데, 바깥 클래스에 대한 인스턴스를 여러개 생성한 경우, 어떤 인스턴스의 static 멤버 변수 값이 옳은 것인지 알 수 없기 때문이다. 하지만 final 키워드를 사용하게 될 경우 값이 컴파일 시점에 결정되고 변경될 수 없기 때문에 사용이 가능하다.
Inner Class를 인스턴스화 하기 위해서는 먼저 바깥 클래스를 인스턴스화 해야한다. 그 후, 바깥 클래스의 인스턴스를 이용하여 Inner Class 객체를 만들 수 있다.
public class OuterClass {
public class InnerClass {
}
}
public class Main {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
}
}
Inner Class에 바깥 클래스의 멤버 변수와 동일한 멤버 변수가 존재할때, {바깥 클래스의 이름}.this.{멤버 변수}로 바깥 클래스의 멤버 변수에 접근할 수 있다.
public class OuterClass {
private int number = 20;
public class InnerClass {
private int number = 30;
public void method(int number) {
System.out.println(number); // 10이 출력됩니다.
System.out.println(this.number); // 30이 출력됩니다.
System.out.println(OuterClass.this.number); // 20이 출력됩니다.
}
}
}
public class Main {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.method(10);
}
}
Local Class
Local Class란 코드 블럭({})안에 정의된 클래스를 의미한다. Local Class는 특정 메소드내에서만 필요한 객체가 필요할때 주로 사용된다.
public class Main {
public static void main(String[] args) {
boolean result = hasInvalid("V111111111", "V123456789", "E123456789");
System.out.println(result); // true 출력
boolean result2 = hasInvalid("V111111111", "V123456789");
System.out.println(result2); // false 출력
}
public static boolean hasInvalid(String... values) {
boolean result = false;
final int length = 10;
final String prefix = "V";
class Value {
private String prefix = "";
private String number = "";
Value(String value) {
if(value.length() == length) {
this.prefix = value.substring(0, 1);
this.number = value.substring(1, value.length());
}
}
public String getPrefix() {
return this.prefix;
}
public String getNumber() {
return this.number;
}
}
for(String value : values) {
Value valueObject = new Value(value);
if(!prefix.equalsIgnoreCase(valueObject.getPrefix()) || valueObject.getNumber().length() != 9) {
result = true;
}
}
return result;
}
}
Anonymous Class
Anonymous Class는 이름이 없다는 것만 제외하면 Local Class와 동일합니다. 따라서 Local Class를 한번만 사용해야하는 경우에 사용할 수 있다. 또한 Anonymous Class 선언과 동시에 인스턴스화가 가능하다.
Anonymous Class는 표현식이기 때문에, 다른 표현식에서 사용이 가능하다.
Anonymous Class는 다음과 같이 선언할 수 있다.
public class Main {
public static abstract class MyAbstractClass {
private int number;
public MyAbstractClass(int number) {
this.number = number;
}
public void method() {}
}
interface MyInterface {
public void method();
}
public static void main(String[] args) {
// 선언과 동시에 인스턴스화를 할 수 있다.
MyInterface myInterface = new MyInterface() {
public void method() {
System.out.println("method 호출!");
}
};
myInterface.method(); // 'method 호출!'이 출력된다.
MyAbstractClass myAbstractClass = new MyAbstractClass(10) {
@Override
public void method() {
System.out.println(super.number);
}
};
myAbstractClass.method(); // 10이 출력된다.
}
}
Anonymous Class 표현식은 다음으로 구성된다.
- new 연산자.
- 구현하고자 하는 클래스 혹은 인터페이스의 이름.
- 구현하고자하는 클래스의 생성자와 동일한 arguments를 가진 괄호. 인터페이스의 경우 빈 괄호 사용한다.
- 클래스를 정의한 body 부분. 메서드는 선언할 수 있지만, 명령어는 선언할 수 없다.
Anonymous Class에는 다음과 같은 제약사항이 있다.
- Anonymous Class는 final 혹은 effectively final이 아닌 지역 변수에 접근할 수 없다.
- Anonymous Class는 중첩클래스처럼 바깥 클래스의 변수와 동일한 이름으로 선언시 바깥 클래스의 변수를 가리게 된다.
- Anonymous Class에는 static 초기화 블럭 및 멤버 interface를 선언 할 수 없다.
- Anonymous Class는 상수 변수에 한해서만 static 멤버를 가질 수 있다.
- Anonymous Class에는 생성자를 정의할 수 없다.
Anonymous Class는 다음과 같은 사항은 허용된다.
- Anonymous Class는 바깥 클래스의 멤버에 접근할 수 있다.
- Anonymous Class에는 필드, 추가 메서드, instance 초기화 블럭 및 Local class를 선언할 수 있다.
과제 :
2020/12/16 - [Java] - [Java] Binary Tree
'Java' 카테고리의 다른 글
[Java] 6주차 과제: 상속 (0) | 2020.12.22 |
---|---|
[Java] Binary Tree (0) | 2020.12.16 |
[Java] 4주차 과제: 제어문 (0) | 2020.12.02 |
[Java] 3주차 과제: 연산자 (0) | 2020.11.25 |
[Java] 동시성(Concurrency)과 병렬성(Parallelism) 차이 (0) | 2020.11.17 |
블로그의 정보
What doing?
Roel Downey