멋쟁이사자처럼_부트캠프/Java
[멋쟁이사자처럼 부트캠프 TIL 회고] 백엔드 부트캠프 13기: Java 7일차 헷갈릴수 있는 코드 정리, 클래스 내부 필드와 문자열 처리 방식의 차이점, String
모건_Morgan
2024. 12. 11. 01:06
헷갈릴수 있는 코드 정리.
package day07;
public class Pen {
protected String color = "빨강";
public void write(){
System.out.println("Pen이 씁니다.");
}
public void write(String msg){
}
public void write(String msg, int count){
}
@Override
public String toString() {
return "나는 펜이예요.";
}
public void print(){
System.out.println("a");
System.out.println("b");
System.out.println("c");
System.out.println("d");
}
}
package day07;
public class BallPen extends Pen{
public int BallPen(){ //리턴타입을 가지면 메소드로 인지한다. 생성자 아님!!!
return 0;
}
public BallPen(){ //생성자로 인지함.
}
String color = "검정";
//부모가 가진 메소드, 필드를 자식이 똑같이 다시 정의 하는 것!! 오버라이드이다.
public void write(){
//부모가 이미 구현한 write() 메소드에서
System.out.println("BallPen이 씁니다. 색:"+color);
}
public void print(){
//생성자는 객체가 생성될때 한 번 불려쓰이기때문에
//생성자에서만 호출이 가능하다.
// super();
// this();
// System.out.println("a");
// System.out.println("b");
// System.out.println("c");
// System.out.println("d");
super.print();
System.out.println("e");
System.out.println(super.color); //접근제한자가 가능하면 접근가능
}
public static void main(String[] args) {
// BallPen ballPen = new BallPen();
BallPen ballPen = new BallPen();
System.out.println(ballPen.color);
ballPen.write();
ballPen.write("test");
ballPen.write("test",4);
ballPen.print();
}
}
1. 코드의 구조
클래스 정의
- BallPen extends Pen
- BallPen은 Pen 클래스를 상속받음.
- 부모 클래스(Pen)의 필드와 메서드를 자식 클래스(BallPen)에서 사용할 수 있음.
- 생성자
- BallPen 클래스에는 두 개의 생성자가 작성됨:
- public int BallPen(): 리턴 타입이 있으므로 생성자가 아닌 메서드로 인식됨.
- public BallPen(): 생성자로 인식됨. 객체 생성 시 실행됨.
- BallPen 클래스에는 두 개의 생성자가 작성됨:
- 메서드
- write(): 부모 클래스의 메서드를 오버라이딩함.
- System.out.println("BallPen이 씁니다. 색:" + color); 출력.
- print(): 부모 클래스의 print() 메서드를 호출(super.print())한 후, 추가적으로 동작 수행.
- write(): 부모 클래스의 메서드를 오버라이딩함.
필드
- color: 자식 클래스 BallPen에서 정의됨. 기본값은 "검정".
2. 주요 코드 요소 정리
생성자와 메서드의 차이
- public int BallPen():
리턴 타입이 int이므로, 생성자가 아닌 일반 메서드로 인식됨. - public BallPen():
리턴 타입이 없고 클래스 이름과 같으므로 생성자로 인식됨.
오버라이딩
- write() 메서드:
부모 클래스의 메서드와 동일한 이름과 시그니처를 가지며, 재정의된 메서드.- 부모 메서드 대신 자식 클래스의 write() 메서드가 호출됨.
super 키워드
- super.print():
부모 클래스의 print() 메서드를 호출함. - super.color:
부모 클래스의 color 필드에 접근(단, 접근 제한자가 허용해야 함).
3. 출력 결과
코드 실행 흐름
1. 객체 생성
BallPen ballPen = new BallPen();
- BallPen의 생성자가 호출됨.
2. 필드 출력
System.out.println(ballPen.color);
- color 필드는 자식 클래스 BallPen의 필드값 "검정"을 출력.
3. 메서드 호출
- write() 호출:
BallPen이 씁니다. 색:검정 - print() 호출:
부모 클래스의 print() 메서드를 호출한 후, 추가적으로 "e"
예상 출력
검정
BallPen이 씁니다. 색:검정
e
부모 클래스에서 정의된 print() 내용
super.color의 값 출력 (부모 클래스의 color 값)
※ 정확한 출력은 부모 클래스 Pen의 print() 메서드 구현에 따라 달라짐.
4. 코드 정리 후 내용
이 코드는 상속, 오버라이딩, 생성자, super 키워드 등 객체지향의 주요 개념을 다룸.
특히 생성자와 메서드의 차이, 필드 접근 시 부모와 자식의 구분 등 중요한 점을 포함하고 있음.
클래스 내부 필드와 문자열 처리 방식의 차이점(복습)
package day07;
public class FieldEntity {
private String name;
public String concat(String word){
name = name + word;
return name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
FieldEntity c1 = new FieldEntity();
c1.setName("Kim");
System.out.println(c1.getName());
c1.concat(" Dong-gyu");
System.out.println(c1.getName());
String str1 = "Kim";
str1 = str1.concat(" Dong-gyu");
System.out.println(str1);
}
}
코드의 주요 설명
1. FieldEntity의 구조
- name 필드: private 접근 제한자를 사용하여 외부에서 직접 접근하지 못하도록 설정.
- setName과 getName 메서드: name 필드의 값을 설정(set)하거나 가져오는(get) 데 사용.
- concat 메서드: name 필드에 입력받은 문자열(word)을 이어붙이고, 이어붙인 결과를 반환.
2. main 메서드 설명
- 객체 필드와 메서드 사용:
- FieldEntity 객체 c1 생성.
- setName을 통해 name 필드에 "Kim"을 설정.
- concat 메서드를 호출하여 name 필드 값에 " Dong-gyu"를 추가.
- 결과적으로 name 필드는 "Kim Dong-gyu"로 변경됨.
- String의 불변성(Immutability):
- String 객체 str1에 " Kim"을 저장.
- str1.concat(" Dong-gyu")는 새로운 문자열 객체 "Kim Dong-gyu"를 생성.
- str1 변수는 새로운 문자열 객체를 참조하게 됨.
- 기존 문자열 Kim은 변경되지 않음.
코드 설명의 핵심
- 객체 필드 수정과 문자열 처리:
- FieldEntity의 concat 메서드는 name 필드의 값을 직접 변경.
- String 클래스의 concat 메서드는 새로운 문자열 객체를 반환할 뿐, 기존 문자열은 변경되지 않음.
- String 클래스의 불변성:
- String은 자바에서 불변(Immutable) 객체임. 문자열을 수정하려면 새로운 객체를 생성.
- 객체 필드와 지역 변수의 차이:
- FieldEntity 는 name 필드를 객체의 속성으로 유지하고 관리.
- String은 지역 변수 str1을 통해 값이 변하는 것처럼 보이지만, 실제로는 새로운 객체를 생성해 참조.
실행 결과 설명
- 첫 번째 출력: setName("Kim") 호출 후 name 필드 값을 출력.
- 두 번째 출력: concat(" Dong-gyu") 호출 후 name 필드 값 변경된 결과 출력.
- 세 번째 출력: String.concat("Kim") 호출 후 새로운 문자열 반환, 기존 str1 값은 변하지 않음.
String
- String은 조금 독특한 객체다. 메모리상에서 문자열이 너무 많이 생성되므로, 더 효율적으로 관리할 방법을 제공한다.
1. String 클래스가 final인 이유
String 클래스는 불변성(Immutable)을 유지하도록 설계되어 있음. final로 선언된 이유는 다음과 같음:
1.1 불변성 유지
- String은 한 번 생성되면 값이 변경되지 않음.
- 불변성을 통해 동기화 문제를 피할 수 있음:
- 멀티스레드 환경에서도 안전하게 사용할 수 있음.
1.2 보안(Security)
- 암호 키, 네트워크 주소 등 중요 데이터가 String으로 자주 처리됨.
- String이 불변하지 않으면, 데이터 조작 및 보안 문제가 발생할 가능성이 있음.
1.3 해싱(Hashing) 최적화
- String은 equals와 hashCode를 오버라이딩하여 효율적인 해싱을 지원.
- 불변성을 유지함으로써 캐시(HashMap, Set 등)에서 안전하고 빠른 검색 가능.
1.4 리터럴 풀(String Literal Pool) 활용
- 같은 문자열 값은 메모리에서 공유됨.
- 새로운 객체를 생성하지 않고 기존 객체를 재사용하여 메모리 효율성 증가.
2. StringBuffer 클래스
StringBuffer는 가변(Mutable) 객체로, 문자열을 동적으로 변경 가능함.
2.1 특징
- 문자열 내용을 수정하거나 추가할 수 있음.
- 불변성을 가지지 않으며, 내부적으로 배열을 사용해 문자열을 저장.
- 스레드에 안전함: 모든 메서드가 synchronized로 구현되어 있음.
2.2 주요 메서드
- append(String s): 문자열 뒤에 새로운 문자열 추가.
- insert(int offset, String s): 특정 위치에 문자열 삽입.
- replace(int start, int end, String s): 문자열 일부를 교체.
- reverse(): 문자열을 뒤집음.
2.3 사용 예시
public class StringBufferExample {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World");
System.out.println(sb); // Hello World
sb.insert(6, "Java ");
System.out.println(sb); // Hello Java World
sb.replace(6, 10, "Kotlin");
System.out.println(sb); // Hello Kotlin World
sb.reverse();
System.out.println(sb); // dlroW niltoK olleH
}
}
3. String과 StringBuffer 비교
특성 | String | StringBuffer |
가변성 | 불변 (Immutable) | 가변 (Mutable) |
스레드 안전성 | 안전하지 않음 | 안전 (synchronized 사용) |
성능 | 문자열 수정 시 새로운 객체 생성 | 문자열 수정 시 기존 객체에서 처리 |
사용 목적 | 변경이 거의 없는 문자열 | 문자열을 자주 수정해야 하는 경우 |
결론
- String: 값이 변경되지 않는 문자열이 필요하거나, 메모리 효율을 위해 리터럴 풀을 활용하는 경우 사용.
- StringBuffer: 멀티스레드 환경에서 문자열을 자주 수정해야 할 때 사용.