14 Oct 2019
|
Java
JAVA 프로그래밍 면접 이렇게 준비한다 - 한빛미디어를 학습하며 공부한 것을 정리한 내용입니다.
소개
-
원시 타입
-
객체란 무엇인가?
-
final 키워드의 역활
- 원시 타입
- Boolean, int, double 같은 각각의 기본 타입은 원시 타입이라고 알려져 있다.
JVM은 이들을 객체라고 알려진 참조 타입과는 다른 방식으로 다룬다.
원시타입들은 항상 값이 있는 상태, 즉 Null이 될 수 없기 때문이다.
- 객체란 무엇인가?
- 상태와 행위의 모음.
원시 타입을 제외하면 자바에서 모든 변수들은 참조 타입이다.
객체는 여러 가지 의미에서 원시 타입과 다르며 가장 중요한 차이점은 빈 객체를 의미하는 표현인 null이 존재한다는 것이다.
즉, 변수들은 null로 설정될 수 있으며 메서드 또한 null을 반환할 수 있다.
그러나 Null 참조에 대한 메서드를 호출할 수는 없다. 호출하려고 하면 NullPointerException이 발생한다.
객체가 ‘참조 타입이다’라는 말이 정확히 무슨 의미인지 살펴보자.
/* 원시 타입 */
int i = 42; // 변수 i에 42라는 값이 할당된다.
int j = i; // 변수 j에 i와 같은 42라는 값을 할당한다.
j = 50; // j의 값을 50으로 변경
System.out.println(i); // 42
System.out.println(j); // 50
/* 참조 타입 */
List<Integer> list = new ArrayList(); // ArrayList를 생성하고 주소값을 list에 할당한다.
list.add(1);
List<Integer> list2 = list; // list2에 ArrayList의 주소값을 할당한다.
list2.add(2);
System.out.println(list.size()); // list와 list2는 같은 주소값을 가지므로 list.size()의 반환 값은 2이다.
- final 키워드의 역활
- 변수를 선언하고 값을 할당하고 나면 메모리 위치가 변경되지 않는다.
객체에 선언하는 final 키워드는 원시 타입에 선언하는 final 키워드와 동일한 역활을 한다.
단, 객체 내부의 값들은 개별 값들이 final이 아니라면 변경할 수 있다.
final int num = 1;
num = 2; // 컴파일 에러. 할당이 되고 난 뒤에는 변경이 불가능하다.
final List<Integer> list = new ArrayList<>();
list = new ArrayList<>(); // // 컴파일 에러. 할당이 되고 난 뒤에는 변경이 불가능하다.
14 Oct 2019
|
Java
JAVA 프로그래밍 면접 이렇게 준비한다 - 한빛미디어를 학습하며 공부한 것을 정리한 내용입니다.
소개
- 메서드와 변수에 사용되는 static 키워드의 역활은 무엇인가?
- String과 interning
- 메서드와 변수에 사용되는 static 키워드의 역활은 무엇인가?
Class Person{
// 정적 변수
public static List<String> nationality = new ArrayList();
// 정적 메셔드
public static void staticMethod(){
...
}
}
static 키워드가 붙은 변수는 정적 변수, static 키워드가 붙은 메서드는 정적 메서드라 한다.
- 정적 변수와 정적 메서드는 클래스가 로딩될때 static 영역에 할당되어 프로그램 종료시까지 객체 생성없이 접근이 가능하다.
- 클래스.정적변수, 클래스.정적메서드() 로 접근하고 사용한다.
- ex) Person.nationality, Person.staticMethod()
- 프로그램 종료 시까지 static 영역에 존재하므로 정적 변수와 정적 메서드를 남발한다면 메모리를 낭비시킬 수 있다.
정적 변수와 정적 메서드는 클래스 내부에 정의하지만 인스턴스에는 속하지 않는다.
-
정적 변수를 사용하는 이유
모든 인스턴스에서 공통으로 사용하는 변수를 정적 변수로 선언하고 사용한다면
객체 생성시마다 중복된 값이 생성되어 발생하는 메모리 낭비를 막을 수 있다.
-
정적 메서드를 사용하는 이유
정적 변수를 사용하려면 메서드를 정적 메서드로 선언해야 하며,
자주 사용하는 Util 성격의 클래스들을 정적 메서드로 선언하면 인스턴스화 없이 편하게 사용가능 하다.
Class Person{
// 정적 변수가 아니라면 Person 객체가 인스턴스화 될때마다 nationality가 생성되어 메모리 낭비가 발생할 겻이다.
public List<String> nationality = new ArrayList();
// 정적 메셔드
public static void staticMethod(){
...
}
}
- String과 interning
String 객체를 생성하는 방법은 2가지가 있다.
-
리터럴을 이용하는 방식.
-
new 연산자를 이용하는 방식.
두 가지 방법의 차이는 무엇일까?
리터럴을 이용하여 String 객체를 생성하면 string constant pool이라는
Heap 영역 중에 특별한 곳에 저장되고, new 연산자를 이용하여 객체를 생성하면 Heap 영역에 생성된다.

’==’ 연산자와 ‘equals()’ 메서드를 이용하여 구체적으로 살펴보자.
’==’ 연산자는 물리적인 메모리 주소가 같은 지 판별하는 연산자이고,
String 클래스의 ‘equals()’ 메서드는 문자열이 같은 지 판별하는 메서드이다.
Class Main{
public static void main(String[] args) {
String literal = "Cat"; // 리터럴을 이용한 방식
String obj = new String("Cat"); // new 연산자를 이용한 방식
System.out.println(literal == obj); //false
System.out.println(literal.equals(obj)); //true
}
}
-
결과
string constant pool에 생성된 literal과 일반 객체처럼 Heap 영역에 생성된 obj의
주소 값은 다르므로 == 연산 결과는 false일 수 밖에 없으며,
equals()의 결과는 true이다.
-
intern() 메서드
String을 literal로 선언할 경우 내부적으로 intern() 메서드를 호출하게 된다.
intern() 메서드는 주어진 문자열이 string constant pool에 존재하는 지 확인하고 존재한다면
그 주소값을 반환하고 없다면 string constant pool에 넣고 새로운 주소값을 반환하게 된다.
Class Main{
public static void main(String[] args) {
String literal = "String"; // 리터럴을 이용한 방식
String obj = new String("String"); // new 연산자를 이용한 방식
String intern = obj.intern();
System.out.println(literal == obj); //false
System.out.println(literal == intern); //true
System.out.println(literal.equals(obj)); //true
System.out.println(literal.equals(intern)); //true
System.out.println(obj == intern); //false
System.out.println(obj.equals(intern)); //true
}
}
ref. https://www.journaldev.com/797/what-is-java-string-pool
ref.https://medium.com/@joongwon/string-%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0-57af94cbb6bc
10 Oct 2019
|
Java
객체지향 프로그래밍의 특성 중 하나인 캡슐화에 대해 찾아보다가
캡슐화에 대한 설명과 예제가 잘 정리되어 있는 블로그를 발견하여 그 내용을 토대로 정리 해보고자 한다.
캡슐화는 무엇인가 ?
- 캡슐화(영어: encapsulation)는 객체 지향 프로그래밍에서 다음 2가지 측면이 있다.
- 객체의 속성(data fields)과 행위(메서드, methods)를 하나로 묶고,
- 실제 구현 내용 일부를 외부에 감추어 은닉한다.
객체의 속성과 행위를 하나로 묶으면 응집도가 높아져서 좋다는 건 직관적으로 알겠는데
실제 구현 내용 일부를 외부에 감추는 은닉화는 굳이 왜 하는걸까?
예를 보며 이해해보자.
요구사항
-
- ‘Account’ 클래스는 은행 계좌 클래스다.
-
- getBalance() 메서드로 한화/달러 잔액을 확인 할 수 있다.
-
- CountryCode는 추가될 수 있다.
public class Account {
private String accountName;
private String accountNumber;
private int balance;
public Account(String accountName, String accountNumber, int balance){
this.accountName = accountName;
this.accountNumber = accountNumber;
this.balance = balance;
}
public String getAccountName(){
return accountName;
}
public String accountNumber(){
return accountNumber;
}
public int getBalance(CountryCode countryCode) {
switch (countryCode){
case KR:
return balance;
case EN:
return balance / 1000; // 1$ = 1000원인 경우
default:
return 0;
}
}
public void add(int money) {
this.balance += money;
}
public int withdraw(int money){
if(balance - money >= 0)
this.balance -= money;
else
throw new IllegalArgumentException("not enough balance");
return money;
}
}
public enum CountryCode{
KR,EN
}
은닉화는 크게 2가지로 나뉜다.
1) 필드 데이터의 은닉화
private String accountName;
private String accountNumber;
private int balance;
필드 데이터의 접근제어자를 private으로 선언함으로써 필드데이터 조작을 막을 수 있다.
2) 기능(메서드)의 은닉화
public int getBalance(CountryCode countryCode) {
switch (countryCode){
case KR:
return balance;
case EN:
return balance / 1000; // 1$ = 1000원인 경우
default:
return 0;
}
}
사용자 입장에서 getBalance()만 호출하면 나라별 환율에 맞는 잔액을 알 수 있다.
1) 국가가 추가되는 경우
2) 환율이 변동하는 경우를 신경쓸 필요가 없으므로
추후에 변경사항이 생기더라도 Account 클래스 내부만 수정하면 된다.
따라서 결합도를 낮추고 코드의 재사용성을 높일 수 있다는 장점이 있다.
결론
캡슐화를 하게 되면 내부에 데이터를 어떻게 저장하는 지, 그 데이터를 어떻게 처리하는 지, 또는 특정 기능을 어떻게 제공하는 지에 대한 내용은 드러내지 않는다. 단지, 객체가 어떤 기능을 제공하는 지만 공유하게 된다.
ref. https://cheese10yun.github.io/encapsulation-part-1/
ref. https://javacan.tistory.com/entry/EncapsulationExcerprtFromJavaBook
ref. https://ko.wikipedia.org/wiki/%EC%BA%A1%EC%8A%90%ED%99%94