API를 확인할 때에는 클래스의 계층 구조(hiarachy)를 먼저 보고 is a 관계를 파악한다. 그리고 class signiture와 모델링 설명을 본다. 메서드를 확인할 때에는 인스턴스 메서드인지 스태틱 메서드인지 확인한다.
java를 이용해 .class파일을 실행할 때 전달받는인자를 명령행 매개변수(command line argument)라고도 한다.
아스키코드에서는 문자 a가 정수값 97을 갖는다. 문자를 숫자로 인식할 수 있다.
암호화 하는 과정을 인코딩, 암호화 해독은 디코딩이라 한다.
메서드 실행문은 메서드 바디라 한다.
<<<<<<<<<<<<<Modifier-static, final>>>>>>>>>>>>>
class나 field, method의 시그니처에 붙는 키워드에는
Access Modifier public, protected, default(friendly), private이 있었던 것처럼 Modifier도 있다.
Access Modifier는 Encapsulation을 구현하기 위해 사용되는 것처럼 Modifier는 Object Modeling 과정에서 특별한 기능(각 키워드마다 다름)을 수행하기 위해 필요하다.
Modifier에는 static, final, abstract가 있다.
static은 다음과 같은 이유에서 필요하다.
객체지향의 기본 원리는 "모든 객체는 서로 다른 상태값을 갖는다"이다. 그러나 상태값이 같은 경우가 있다. 남고에서 학생의 성별은 남자 라든지, 한국인의 국적은 한국 이라는 것이 그 예다.
이 때 객체지향의 기본 원리에서 벗어나지 않는 해결책으로 제시된 방법이 Modifier static 키워드이다.
static을 이해하기 위해서는 메모리 구조를 대략적으로 파악하는 것이 좋다.
메모리 구조에는 위로부터 static, stack, heap이 있다. 그냥 이런 영역이 있나보다 하고 파악하면 된다.
JRE(JVM)를 통해 Application class를 실행시킬 때 JRE는 class 1번라인부터 훑으며 앞에 static이 붙은 변수, 메서드를 static 메모리 영역에 올린다. static 영역에 올릴 때에는 다른 클래스에 같은 이름을 갖는 필드가 있을 것을 염두에 두어 class 이름도 함께 등록한다. static이 붙은 모든 변수와 메서드를 static 메모리 영역에 올리고 나면 JRE가 main method를 호출하고 실행한다.
메인 안에서 다음과 같이 인스턴스를 만드는 상황에서는
ObjectTest obj1 = new ObjectTest();
stack 영역에 obj1이라는 변수를 생성해놓고, heap 영역에 ObjectTest 인스턴스를 만든다.
인스턴스 만드는 작업은 new 키워드가 하고, 생성자는 인스턴스 상태값을 초기화하는 작업을 한다.
생성자를 통해 인스턴스 상태값을 초기화할 때에 static이 붙은 필드가 있으면 heap영역에 다시 만들지 않는데, 그 이유는 이미 static 메모리 영역에 해당 클래스의 해당 필드가 올라가있기 때문이다.
인스턴스 변수에 접근할 때에는 인스턴스 명에 .오퍼레이터를 이용했었다. 그 이유는 생성된 인스턴스 안에 해당 필드값이 저장되어있기 때문이다.
그러나 static 메모리 영역에 올라가있는 변수의 경우에는 인스턴스이름으로 접근할 필요 없이 클래스 이름으로 접근할 수 있다. heap 영역이 아닌 static 영역에 클래스이름과 함께 등재되어있기 때문이다.
다른 인스턴스가 같은 상태값을 가질 때에 static 필드를 모든 인스턴스가 참조한다면 같은 상태값을 공유로 표현하기때문에 간단하며 클래스 이름으로 접근하기에 사용하기도 편하다.
필드에 static이 붙는 경우는 공유하는 상태값(인스턴스 자신만의 상태값이 아니라)이지만, 메서드의 경우에는 field를 변경하지 않는 메서드에 static이 붙는다.
자바진영에서 제공하는 빈 중에 Math class가 있다. 여기에는 절대값을 구하는 메서드가 있다. 절대값을 구할 때에는 어떤 수를 전달받던 그 수 자체를 변경하지 않는다. 절대값을 계산해서 출력할 뿐이다. 이럴 때 static을 사용한다.
final 키워드는 class, field, method 앞에 붙일 수 있다.
final은 직역해 "마지막"이라고 해석하면 좋다.
Math class의 필드에는 PI가 있다. 3.141592... 하는 상태값이다. 이 상태값은 인스턴스 생성에 따라 변하지 않으며 모든 계산에서 파이는 저 값을 의미하므로 static이 붙는다.
(PI의 네이밍은 카멜케이스를 따르지 않는데 상수 값은 카멜케이스를 따르지 않는다.)
만일 누가 Math.PI = 5; 라고 명령한다면 static 메모리 영역에 있는 PI가 변경되므로 다른 메서드 안에서 PI값을 이용하는 경우 제대로 된 3.14 계산이 아닌 5로 계산하게 된다.
이 때 PI 앞에 Modifier final이라는 키워드를 사용한다면 PI는 마지막 값(더 이상 변경이 없는 값)이 되어 바뀌지 않는다.
method의 경우에는 final method를 "마지막 메서드(변경이 없는 메서드)"라고 해석하면 된다. 만일 현재 클래스에서 final method를 만든다면 이 클래스를 상속받는 자식 클래스에서 final method는 overriding(재정의)하지 못한다.
class에 final 키워드가 붙는다면 이 클래스 하위에 hiarachy구조를 만들 수 없음을 의미한다. 이 클래스가 계층 구조에서 "마지막 클래스"이며 이 클래스를 상속받을 수 없다는 뜻이다.
static 변수(클래스 변수)와 static block을 확인해보면 좋다.
<<<<<<<<<<<<<<<<<<<Access Modifier와 Modifier의 사용법>>>>>>>>>>>>>>>>>>>>>
필드에 Access Modifier를 public으로 설정하면 누구든 해당 필드에 접근해서 정보를 가져올 수 있고, 변경할 수 있다.
이 때는 인스턴스를 생성하고 인스턴스 이름으로 필드에 접근할 수 있으니 getter, setter method가 필요하지 않다.
그러나 모델링 과정에서 아무나 봐선 안되는 정보가 있을 수 있다. 이 때는 필드에 public이 아닌 Access Modifier를 붙여 Encapsulation, 접근을 제한해야 한다.
필드에 private Access Modifier를 붙이면 해당 필드는 인스턴스 생성후 식별자와 .오퍼레이터로는 접근이 불가능하다.
이 필드를 가져오려면 getter 메서드가 필요하다.
만일 해당 필드값을 제공은 하지만 수정할 수 없게 하려면 getter 메서드만 필요하고 setter 메서드는 만들지 않는다. (read only file이 이렇다)
이는 결국 해당 필드의 값이 "마지막"이라는 의미이고, Access Modifier와 함께 Modifier final도 붙이면 코드 가독성이 좋아진다.
생성자에도 Access Modifier를 붙일 수 있다. 생성자는 정확히 Construct method이기 때문에 private Access Modifier를 붙이는 데에 문제가 없다.
생성자에 private를 붙이면 해당 클래스의 인스턴스는 해당 클래스 밖에선 new 키워드를 이용해 만들 수 없다.
만일 이 클래스에 클래스 밖에서 사용하도록 만든 method나 field가 있다면 다양한 방법을 사용할 수 있다.
API를 이용해 JAVA진영에서 만든 java.lang.System 클래스를 확인해보면 클래스에 field와 method만 있을 뿐 constructor가 없다.
java의 src파일을 보면 System.java에는 constructor가 private처리 되어있다.
그럼에도 우리는 System.out.println(); 등의 필드와 메서드에 접근할 수 있는데 이는 모든 필드와 메서드가 static 처리 되어있어 인스턴스 생성 없이 클래스 이름으로 접근할 수 있다.
이는 다시말해 System 안에 있는 필드와 메서드는 상태값을 조작하지 않는 상태와 무관한 필드, 상태와 무관한 메서드를 가지고 있다는 뜻이 된다.
만약 생성자에 private Access Modifier를 붙였지만 method에 static을 붙이지 않을 경우에는 인스턴스 생성 없이 접근 가능한 static Modifier를 이용해 getInstance메서드를 만들기도 한다.
getInstance메서드에 입력값으로 매니저 넘버를 받는다면 매니저 넘버가 일치할 경우에는 instance를 생성해주고, 아닐 경우에 null을 return 해 주면 인스턴스 생성에 제한을 할 수 있다.
이 때 인스턴스의 Data type은 클래스 이름과 같음을 기억하자. 따라서 getInstance메서드의 메서드 시그니처는 public static 클래스이름 getInstance(int managerNo){}처럼 사용할 수 있다.
<<<<<<<<<<<<<<<<<<<<<<Modifier - abstract>>>>>>>>>>>>>>>>>>>>>>>
abstract라는 키워드는 Modifier 중 폴리모피즘을 지원하는 키워드이다.
부모 클래스를 모델링하고 자식 클래스가 상속받을 때 모든 자식 클래스가 한 메서드를 overriding한다면 부모의 메서드는 호출될 일이 없다. 그러나 명시적인 모델링을 위해 부모의 메서드를 지울 수는 없다. 다만 부모 클래스에서는 구체적인 메서드가 아닌 메서드 시그니처만 갖는 추상 메서드를 만들면 된다.
만일 object modeling 중 자식클래스가 부모 클래스를 상속받았지만 반드시 overriding해야 하는 메서드를 overriding 하지 않을 경우에는 compile은 되지만 run 할 때 쓰레기값이 출력된다.
이에 대한 해결로 abstract 키워드를 사용한다. 반드시 오버라이딩 해야 하는 메서드를 부모 클래스에 만들 때 abstract키워드를 붙여준다면 자식 클래스가 상속받았을 때 반드시 해당 메서드를 overriding 해야 한다. 그렇지 않으면 class 자체의 compile 오류가 발생한다.
abstract 메서드에는 구체적인 실행문이 없다.
따라서 부모 클래스의 인스턴스를 생성해서 .operator로 해당 abstract 메서드에 접근해 실행시킨다면 실행되지 못한다.
이를 미연에 방지하기 위해 하나의 abstract method라도 가지고 있다면 해당 클래스를 선언할 때 abstract라고 선언하는 것이다.
abstract class는 인스턴스 생성이 불가능하다.
(하지만 해당 클래스를 데이터 타입으로 갖는 변수는 선언할 수 있음을 기억하자)
폴리모피즘은 하나의 사용법으로 여러 사용을 가능하게 한다.
abstract 클래스와 자식 클래스에서 인스턴스를 생성할 때에도 이를 구현할 수 있다.
abstract class를 상속받는 자식 클래스에서 인스턴스를 만들 때 식별성을 갖는 데이터 타입을 abstract 클래스로 하고 생성되는 인스턴스를 자식 클래스로 적는 것이 가능하다.
이는 묵시적 형변환을 통해 작은 데이터 타입인 자식 클래스에서 추상 부모클래스로 변환되기 때문에 가능하다.
<<<<<<<<<<<<<<<<reference data type 형변환>>>>>>>>>>>>>>>>>>>>>>>
primitive data type 형변환에서는 작은 데이터 타입이 큰 데이터 타입으로 시스템 내부에서 변한다는 묵시적 형변환과 casting 연산자를 이용해 큰 데이터 타입을 작은 데이터 타입으로 변환하는 명시적 형변환이 있었다.
primitive data type 형변환은 가능 불가능만 확인하면 형변환 개념이 끝났다.
그러나 reference data type 형변환을 할 때에는 확인해야 할 것이 늘어난다.
예를 들어 아버지를 대신해서 아들이 아버지 친구 자제분의 결혼식에 갔다고 할 때, 아들은 아버지를 대신해서 갈 수 있다.
Super s = new Sub();
자식 인스턴스를 생성할 때에는 constructor 메서드에 생략되어있는 super(); 메서드를 통해 부모 인스턴스 생성이 먼저 되고 그걸 감싸는 자식 인스턴스가 생성되었었다.
이를 부모 클래스 데이터타입 변수에 대입할 수 있다. 부모 클래스가 자식 클래스보다 개념적으로 크기 때문에 Sub is a Super 관계가 성립되고 따라서 큰 데이터타입으로의 묵시적 형변환이 일어나기 때문이다.
reference data type에선 형변환이 일어나고 끝이 아니다.
하객인 아버지 친구분들과 대화할 때에는 나의 개인적인 특성이 아닌 아버지의 대화 주제에 맞추어 대화해야한다.
부모 클래스 데이터타입 변수에 들어있는 자식 인스턴스는 자식 클래스 고유의 필드나 메서드에 접근이 불가능하다.
그러나 만일 부모 클래스의 메서드를 자식이 OverRiding했다면 자식 인스턴스에 있는 메서드에 접근한다.
예를 들어 아버지는 한식만 드시지만 자식은 양식만 먹는다고 할 때 (먹는다 는 메서드를 overriding 했다고 가정) 아버지를 대신해 결혼식장에 갔지만 나는 아버지처럼 한식만 먹는 것이 아니라 양식만 먹을 수 있다.
아버지 클래스 데이터타입 변수에 대입된 자식 인스턴스가 자신의 필드와 메서드를 사용하고 싶다면 아버지 클래스 데이터 타입을 자신의 클래스 데이터 타입으로 명시적 형변환을 해주면 사용할 수 있다.
가시적으로 확인하기 위해 자식 클래스 데이터타입의 변수를 만들어 다음과 같이 표현할 수 있겠다.
Sub sub = (Sub)s;
이렇게 되면 sub는 부모클래스 데이터타입에 저장된 자식 인스턴스지만 casting연산자를 이용해 명시적으로 형변환해주었기 때문에 자식 클래스의 메서드나 필드에 접근할 수 있다.
'IT > 공부' 카테고리의 다른 글
[프로그래밍] Enhanced for Loop, Generic, Wrapper class, AutoBoxing and AutoUnBoxing, Collection (0) | 2022.02.13 |
---|---|
[프로그래밍] 개념정리 - 용어 및 클래스 관계 (0) | 2022.02.02 |
[정보처리기사 필기] 도전 시작 (0) | 2022.02.02 |
[프로그래밍] API, 접근제어자, static/instance, is a/has a관계 (0) | 2019.03.17 |
[프로그래밍] 생성자, 공유, super, this, OverRiding, package (0) | 2019.03.17 |