5주차
클래스
클래스 선언
class MyClass {
// field, constructor, and
// method declarations
}
기본 선언 방식은 위와 같다.
클래스 몸체는 새로운 객체를 초기화하는 생성자
, 클래스와 그 객체의 상태를 제공하는 필드
, 행동을 구현하는 메소드
를 포함한다.
class MyClass extends MySuperClass implements YourInterface {
// field, constructor, and
// method declarations
}
클래스가 상위 클래스의 상속을 받는지, 인터페이스를 구현하는지에 대한 정보 역시 선언할 때 제공할 수 있다.
또한 public
과 private
과 같이 다른 클래스들이 구현하는 클래스에 접근가능한지의 여부를 결정하는 접근자 역시 클래스 선언 가장 앞부분에 쓸 수 있다.
클래스 이름은 관례상 앞글자는 대문자로 한다. 상위 클래스 앞에 extends
를 붙이는데, 자바에서는 다중상속은 되지 않는다. 오직 한 클래스만 상속할 수 있다.
변수 선언
변수의 종류
필드
- 클래스에서의 멤버변수지역변수
- 메소드나 코드 내의 변수파라미터
- 메소드 생성에서의 변수
접근 제어자
public
- 모든 클래스에서 접근 가능한 필드private
- 자신의 클래스에서만 접근가능한 필드
예시
public int age;
public String name;
메서드 선언
public int add(int a, int b) {
return a + b;
}
관습적으로 메서드 이름은 소문자로 된 동사의 형태
이거나 소문자인 동사로 시작하고, 뒤에는 형용사, 명사 등 여러단어가 합쳐진다. 여러 단어로 구성될 때 두 번째 부터의 단어는 반드시 대문자로 시작해야한다.
ex) run, runFas, getFinalData, isEmpty, compareTo, etc.
오버로딩
자바는 오버로딩을 지원하는데, 자바는 메소드 시그니처가 다른 메소드들을 구별할 수 있다. 즉 다른 형태의 파라미터를 가지지만 같은 이름인 클래스의 메소드들을 가질 수 있다는 뜻이다.
public class Calculation {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
생성자 선언
클래스는 객체를 생성할 때 호출되는 생성자를 가진다. 생성자 선언은 메소드 선언과 유사해보이지만, 대신 클래스 이름을 그대로 쓰고 리턴 타입이 없다.
public class Bicycle {
private int geer;
private int cadence;
private int speed;
public Bicycle(int startCadence, int startSpeed, int startGeer) {
geer = startGeer;
cadence = startCadence;
speed = startSpeed;
}
}
- 메서드 또는 생성자에 대한 선언은 해당 메서드 또는 생성자에 대한 파라미터 개수와 유형을 선언한다.
- 메서드 또는 생성자의 파라미터에는 어떤 데이터 타입이라도 사용 가능하다. 프리미티브 타입은 물론, 레퍼런스 타입도 가능하다.
가변인수(varargs)
JDK1.5부터 동적으로 매개변수를 지정할 수 있게 되었다. 즉 인자값의 개수가 모호할 때 사용할 수 있다. 이전에는 배열을 주로 사용했는데, 배열을 만드는 것 보다 더 간단하다.
void test(String... args){};
- 사용법 :
String... args
타입이름 + …+ 공백 + 파라미터 이름 형식
public Polygon polygonFrom(Point... corners) {
int numberOfSides = corners.length;
double squareOfSide1, lengthOfSide1;
squareOfSide1 = (corners[1].x - corners[0].x)
* (corners[1].x - corners[0].x)
+ (corners[1].y - corners[0].y)
* (corners[1].y - corners[0].y);
lengthOfSide1 = Math.sqrt(squareOfSide1);
}
- 내부에서는 corners가
배열
처럼 다뤄진다.
중첩 클래스
자바에서는 클래스 내부에 다른 클래스를 정의하는 것이 가능하다. 이런 클래스들은 중첩클래스라고 불린다.
class OuterClass {
...
class NestedClass {
}
}
- 중첩 클래스는
static
중첩 클래스와non-static
중첩 클래스로 나뉜다. non-static 중첩 클래스는 내부 클래스(inner class)라고도 불린다.
class OuterClass {
...
static class StaticNestedClass {
}
class InnerClass {
}
}
중첩 클래스들은 둘러싼 클래스의 멤버이다. inner 클래스들은 설령 private으로 선언되었다고 해도 둘러싼 클래스들의 멤버들에 접근할 수 있다. static 중첩 클래스들은 둘러싼 클래스의 멤버에 접근할 수 없다. OuterClass의 멤버로서, 중첩 클래스들은 private, public, protected, package private으로 선언할 수 있다. (외부 클래스들은 오직 public이나 package private으로만 선언가능하다.
왜 중첩 클래스문을 쓸까?
- 단 한 곳에서만 사용되는 클래스들을 논리적으로 그룹화한다. 만약 클래스가 딱 한 가지 다른 클래스에서만 유용하다면, 그 클래스에 삽입하고 같이 묶는 것이 논리적이다.
- 캡슐화에 더 도움을 준다. 최상위클래스 A와 B가 있다고 가정해보자. B는 A의 멤버에 접근해야 하는데, 이 것이 아니라면 A의 멤버들은 private으로 선언될 수 있다. B를 A내에 감춤으로써, A의 멤버들은 private으로 선언될 수 있고, B역시 접근가능하다. 게다가 B 자신도 외부로부터 감춰진다.
- 코드를 더 가독성 있게 만들고, 유지가능하게 만든다. 작은 클래스들을 상위클래스로 중첩하는 것은 코드를 더 사용할 곳 가까이에 위치시킨다.
익명 클래스
InnerClass의 한 종류인 익명클래스는 코드를 보다 간결하게 만들어주고, 클래스를 선언함과 동시에 인스턴트화할 수 있다. 이름이 없는 것을 제외하고 로컬 클래스처럼 사용할 수 있다. 클래스를 단 한 번만 사용해야한다면 익명클래스를 사용하는 것이 좋다. 또한 부모 클래스를 상속받는 서브 클래스를 생성하지 않고도, 단일 객체를 만들어 부모 클래스에 정의된 행위를 추가할 수 있다. 익명클래스는 클래스와 인터페이스로부터 만들 수 있다.
익명클래스 vs 일반 클래스
- 일반 클래스는 인터페이스를 제한없이 구현가능하지만 익명 클래스는 단 하나의 인터페이스/클래스만을 구현할 수 있다.
- 익명 클래스는 별도의 생성자를 작성할 수 없다. 만들어진 클래스의 생성자와 동일하게 객체를 생성할 뿐이다.
[익명 클래스를 사용하지 않은 예제]
interface Age {
int age = 29;
void getAge();
}
class MyClass implements Age {
@Override
public void getAge() {
System.out.print("Age is " + age);
}
}
class demo {
public static void main(String... args) {
MyClass myClass= new MyClass();
myClass.getAge();
}
}
[익명 클래스를 사용한 예제]
class AnonymousDemo {
public static void main(String[] args) {
Age age = new Age() {
@Override
public void getAge() {
System.out.print("Age is " + age);
}
};
age.getAge();
}
}
Enum Type
열거형은 변수가 미리 정의된 상수집합이 되도록 하는 특수한 데이터 타입이다. 변수는 반드시 사전에 정의된 값 중 하나와 같아야한다.
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
public class EnumTest {
Day day;
public EnumTest(Day day) {
this.day = day;
}
public void tellItLikeItIs() {
switch (day) {
case MONDAY:
System.out.println("Mondays are bad.");
break;
case FRIDAY:
System.out.println("Fridays are better.");
break;
case SATURDAY: case SUNDAY:
System.out.println("Weekends are best.");
break;
default:
System.out.println("Midweek days are so-so.");
break;
}
}
public static void main(String[] args) {
EnumTest firstDay = new EnumTest(Day.MONDAY);
firstDay.tellItLikeItIs();
EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
thirdDay.tellItLikeItIs();
EnumTest fifthDay = new EnumTest(Day.FRIDAY);
fifthDay.tellItLikeItIs();
EnumTest sixthDay = new EnumTest(Day.SATURDAY);
sixthDay.tellItLikeItIs();
EnumTest seventhDay = new EnumTest(Day.SUNDAY);
seventhDay.tellItLikeItIs();
}
}
/*
Mondays are bad.
Midweek days are so-so.
Fridays are better.
Weekends are best.
Weekends are best.
*/
객체
객체(Object)
객체란, 세상에 존재하는 모든 것
들이다. 각각의 사물은 고유하고, 속성(상태)
을 갖고 행위
를 한다. 즉, 객체는 속성(field)
와 행위(method)
로 이루어져 있다고 볼 수 있다. 예를들어 강아지는 이름, 색, 털, 배고픔이라는 속성(상태)과, 짖기, 꼬리말기 등의 행위를 가진다.
객체 생성
객체는 클래스로부터 만들 수 있고, 객체 생성에서는 다음과 같은 과정을 가진다.
-
Declaration
: 객체를 참조하는 변수 선언변수를 선언할 때,
type name;
형태로 타입을 type으로 가지는 데이터를 참조하기 위해, name이라는 변수를 사용할 것이라고 컴파일러에게 알린다.마찬가지로
Person person;
과 같이 레퍼런스 타입을 선언할 수 있다. 이런식으로 객체를 선언한다면, 객체의 값은 객체가 실제로 만들어지고 할당될 때 까지 정해지지 않는다. 단순히 레퍼런스 변수를 선언하는 것이 객체를 생성하는 것을 뜻하지 않는다. 객체를 생성하기 위해서는new
연산자가 필요하다. 반드시 person에 객체를 할당해야 컴파일 에러가 나지 않는다.어떤 객체도 참조하지 않는 변수의 상태는 아래와 같이 묘사될 수 있다.
person은 어떤 것도 가리키지 않는다.
-
Instantiation
: 클래스 인스턴트화new
연산자는 새로운 객체에 메모리를 할당하고 그 메모리에 참조를 반환함으로써 클래스를 인스턴트화 시킨다. 또한 new 연산자는 객체 생성자를 호출한다.‘클래스를
인스턴트화
’ 한다는 것은 객체를 생성하는 것과 같은 의미이다. 객체를 생성할 때, 클래스의 ‘인스턴스’를 만드는 것이 즉 클래스를 인스턴트화 한다는 것이다.new 연산자는 생성자 호출이 필요하다. 생성자의 이름은 인스턴트화할 클래스가 뭔지 알려준다. new 연산자는 자신이 생성한 객체에 대한 참조를 반환한다. 이 참조는 일반적으로 알맞은 타입의 변수로 할당된다.
new 연산자가 반환하는 참조는 변수에 할당될 필요는 없다. (값 그대로 사용)
또한
int height = new Rectangle().height;
와 같이 표현식에서 직접적으로 사용할 수 있다.아래에서 계속..
-
Initialization
: 객체 초기화public class Person { public String name; public int age; // constructor public Person(String name, int age) { this.name = name; this age = age; } }
이 클래스는 하나의 생성자만 가진다. 생성자는 클래스와 같은 이름을 가지고 리턴 타입이 없기 때문에 생성자임을 인식할 수 있다. 생성자는 String name, int age 와 같이 String, int 인자를 가진다.
Person person = new Person("Seul", 26);
위와 같은 선언문은 각각의 인자에 “Seul”, 26을 제공한다.
또한 초기화된 객체는 Heap에 그 값이 저장되고, Stack엔 참조변수만 저장된다.
T 메모리
-
Static 영역
JVM이 종료될 때 까지 사라지지 않는다. Class에 대한 정보, 패키지, static 키워드가 선언된 클래스 멤버나 메소드 등이 static 영역에 속한다.
-
Stack 영역
primitive가 저장되고, refernce는 참조값만 저장된다. 스레드 역시 스택 영역에 생성되는데, 하나의 스레드는 별개의 T메모리를 갖는다. 따라서 하나의 스레드는 다른 스레드로 접근할 수 없지만 static과 heap은 공유한다.
-
Heap 영역
new
를 통해 인스턴스화 되면, 객체의 값들은Heap
에 메모리 공간이 할당되고 저장된다.
package week5; public class Person { public String name = "Jone Doe"; public int age = 1; public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } public static void main(String... args){ Person person1 = new Person(); Person person2 = new Person("Seul", 26); String name1 = person1.name; String name2= person2.name; int age1 = person1.age; int age2 = person2.age; System.out.println(name1 + " : " + age1); System.out.println(name2 + " : " + age2); } }
-
객체 지향
그렇다면 객체지향의 개념은 무엇일까? 인간이 현실세계를 인지하는 방법은 직관적이다. 이와같은 방식으로, 눈으로 보고, 느끼고, 생활하는 현실세계 처럼 프로그래밍 하는 것이다. 또한 프리미티브 타입은 변수에 적당한 메모리를 할당한다.
클래스와 객체의 관계?
클래스는 분류에 대한 개념이고 실체가 아니다. 하자민 객체는 실체이다. 절대 붕어빵틀과 붕어빵의 관계가 아니다.
this
keyword
인스언스 메서드나 생성자 내에서, this
는 그것의 메서드 또는 생성자가 호출되는 최근 객체에 대한 참조이다.
[this
없이 사용]
public class Point {
public int x = 0;
public int y = 0;
//constructor
public Point(int a, int b) {
x = a;
y = b;
}
}
[this
와 사용]
public class Point {
public int x = 0;
public int y = 0;
//constructor
public Point(int a, int b) {
x = a;
y = b;
}
}
명시적 생성자 호출
생성자 내에서 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
를 이용하여 생성자를 호출할 수 있다.
객체 지향의 4대 특성
캡슐화(capsulation)
: information hiding
객체의 메소드로만 상호작용 함으로써, 객체 내부 구현의 세부한 것들은 외부로부터 숨길 수 있다.
접근 제어자를 통해 가능하다.
접근제어자 | 설명 |
---|---|
private - | 본인만 접근 가능 |
default ~ | 같은 패키지 내의 클래스에서 접근 가능 |
protected # | 상속 / 같은 패키지 내의 클래스에서 접근 가능 |
public + | 모두가 접근 가능 |
- 상속을 받지 않았다면, 객체 멤버는 객체를 생성 후 객체 참조 변수를 이용하여 접근해야한다.
- 정적멤버는 클래스명.정적멤버 형식으로 접근하는 것이 좋다.
상속(extends)
: 재사용 + 확장
객체지향에서의 상속은 상위 클래스의 특성을 하위 클래스에서 상속하고, 거기에 필요한 특성을 추가, 즉 확장하여 사용할 수 있다는 의미이다.
예를들어 동물이라는 상위클래스가 있다면, 이 동물 클래스를 상속받아 포유류와 조류 클래스를 각각 만들 수 있다. 또한 포유류라는 상위클래스를 고래와 박쥐 클래스가 각각 상속할 수 있다.
✔️ 상속관계에서 반드시 만족해야 할 문장
- 하위클래스는 상위클래스이다
- 하위 클래스 is a kind of 상위 클래스 관계
추상화(abstraction)
: 모델링
구체적인 것을 분해해서 관심영역(Application Boundary)에 있는 특성만 가지고 재조합하는 것, 즉 모델링
이다. 공통 특성 혹은 공통 속성을 추출한다.
모델링과 객체지향의 추상화와 무슨 관련?
- 클래스를 설계할 때 객체들을 관찰하여, 객체들이 가진 “공통된 특성” 을 찾아야함
- 메서드(기능/ 행위) : 동사(먹다, 자다, 일하다 …) 로 표현되는 특성은 수행절차(로직) 을 가짐
- 애플리케이션 경계(context)
-
Q. 내가 만들고자 하는 app은 어디에서 사용될 것인가?
애플리케이션 경계 정하기 → 추상화 → 클래스 설계 → 클래스
-
다형성(polymorphism)
: 사용편의
하나의 class 메서드가 다양한 방식으로 동작
-
오버로딩 - 같은 메서드 이름, 다른 인자
두 메소드가 같은 이름을 갖고 있지만, 인자의 개수나 자료형이 다른 경우
-
오버라이딩 - 같은 메서드 이름, 같은 인자
상속관계에 있는 클래스의 성격에 맞게 부모 클래스의 함수를 재정의 → 메서드와 이름과 용례가 같음
-
출처
https://dduddublog.tistory.com/169
java 공식 레퍼런스