변수(Variable)
- 기본 자료형 데이터 크기
- byte, boolean = 1byte
- char, short = 2byte
- int, float = 4byte
- long, double = 8byte
- UTF-8 인코딩 방식에서 String 문자열은 문자 종류에 따라 데이터 크기가 상이하다.
- 영어/숫자 1byte, 한글 3byte
- 자료형의 허용 데이터 범위를 넘어서 오버플로우가 발생하면 최소값인 음수 값부터 다시 순환된다.
- 별도의 예외는 발생하지 않는다.
- 데이터 범위가 큰 자료형을 작은 범위의 자료형으로 형변환 시 데이터 손실이 발생할 수 있다.
- 변수 / 메모리 구조
- Static 영역 : static 예약어가 붙은 필드 / 메소드가 저장되는 공간
- ex) 클래스 변수
- Heap 영역 : new 연산자에 의해 동적으로 할당하고 저장되는 공간
- ex) 객체, 배열 ...
- Stack 영역 : 메소드 호출 시 Stack 영역에 자동 생성되며 메소드 종료 시 자동 소멸된다.
- ex) 지역 변수, 매개 변수, 메소드 호출 스택 ...
- Static 영역 : static 예약어가 붙은 필드 / 메소드가 저장되는 공간
조건문 / 반복문
- switch문에서 사용할 수 없는 변수 타입
- long, 실수형, boolean 타입은 사용 불가능
- enum, String, char, int 타입은 사용 가능
labelName:
while (조건1) {
while (조건2) {
if (조건) {
break labelName; // labelName이라는 반복문 전체 탈출
}
}
}
- loop label : 반복문에 이름을 붙여서 사용함으로 지정한 반복문 전체를 탈출하거나 건너뛸 수 있다.
- label은 반복문 앞에 붙일 수 있다.
배열
int[] arr1 = new int[4];
int[] arr2 = arr1; //얕은 복사
for(int i = 0; i < arr1.length; i++) { //반복문으로 깊은 복사
arr2[i] = arr1[i];
}
System.arraycopy(arr1, 0, arr2, 0, arr1.length); //System.arraycopy()로 깊은 복사
arr2 = Arrays.copyOf(arr1, arr1.length); //Arrays.copyOf()로 깊은 복사
arr2 = arr1.clone(); //object.clone()으로 깊은 복사
- 얕은 복사 / 깊은 복사
- 얕은 복사 : 객체의 주소 값만 가져와서 참조형 변수에 저장하고 하나의 객체를 두 변수가 참조하는 것
- 깊은 복사 : 새로운 배열 객체를 생성해서 기존 배열의 데이터를 복사하는 것
- 2차원 배열 : 자료형이 같은 1차원 배열의 묶음
객체
- 캡슐화 원칙
- 클래스 멤버 변수에 대한 접근 제한자는 private을 원칙으로 한다.
- 클래스 멤버 변수에 대한 연산처리를 목적으로 하는 함수들은 클래스 내부에 작성한다.
- 멤버 함수는 클래스 밖에서 접근할 수 있도록 public으로 설정한다.
public class Student {
private static String value1;
private String value2
static { //static 블록
value1 = "기본값";
}
{ //초기화 블록
value2 = "기본값";
}
}
- 초기화 블록 : 인스턴스 변수(필드)를 초기화 시키는 블록으로 객체 생성시마다 초기화하는 역할로 생성자보다 실행 순서가 빠르다.
- static 블록은 static 필드를 초기화 시키는 블록으로 프로그램 시작 시 한 번만 초기화한다.
- 필드 초기화 순서
- 클래스 변수 : JVM 기본값 -> 명시적 초기값 -> 클래스 초기화 블록 초기값
- 인스턴스 변수 : JVM 기본값 -> 명시적 초기값 -> 인스턴스 초기화 블록 초기값 -> 생성자를 통한 초기값
- 생성자
- 별도의 생성자를 작성해주지 않으면 JVM이 기본 생성자를 생성해준다.
- 별도의 생성자가 한 개라도 작성되어 있다면 기본 생성자는 생성X
- 생성자 생성 방식 = 오버로딩
- 별도의 생성자를 작성해주지 않으면 JVM이 기본 생성자를 생성해준다.
public class StudentController { //싱글톤 패턴
private static StudentController controller;
private StudentController() {
}
public static StudentController getStudentController() {
if (controller == null) {
controller = new StudentController();
}
return controller;
}
}
- 싱글톤 패턴 : 클래스의 객체를 하나만 생성하도록 보장하는 디자인 패턴
- 전역적으로 하나의 객체만 사용할 수 있다.
- 싱글톤 패턴 구현
- 외부에서 객체를 생성하지 못하도록 생성자를 private으로 막는다.
- 클래스 내부에 static으로 객체를 생성
- 객체 생성 메소드 getInstance()를 별도로 만든다.
- 이 메소드를 통해서만 객체를 반환하도록 한다.
- 객체가 없는 경우에만 새로 만들어서 반환해주고 객체가 있는 경우에는 만들어진 객체를 반환해준다.
- 객체 배열 주의사항
- 객체 배열에서 forEach로 객체를 가져오는 경우 객체를 담는 참조 변수는 참조 값을 담고 있다.
- 값을 바꾸려면 =으로 값을 수정하는 것은 불가능, setter()를 통해 값에 접근해서 변경해야 한다.
- =으로 값을 수정하려고 한다면 참조 변수의 값을 바꾸게 되는 것이지 원본 값을 바꾸는 것이 아님..
- 객체 배열에서 forEach로 객체를 가져오는 경우 객체를 담는 참조 변수는 참조 값을 담고 있다.
//참조로 인한 스택 오버플로우
class A {
B b = new B();
public void callB() {
b.callA(); // B의 callA() 호출
}
}
class B {
A a = new A();
public void callA() {
a.callB(); // A의 callB() 호출
}
}
- 참조로 인해 무한 호출되어 스택 오버플로우 발생 현상
- 예제에서 A는 B를 B는 A를 서로 참조하고 있는 상태에서 서로 무한 호출하다가 스택 오버플로우가 발생하게 된다.
- 해결방법 : 서로의 객체를 직접 new로 생성하지 않고 외부에서 의존성 주입을 받기
클래스 다이어그램
┌──────────────┐
│ 클래스 이름 │ ← 클래스 이름 (Class Name)
├──────────────┤
│ 속성(필드)들 │ ← 속성 (Attributes, Fields)
├──────────────┤
│ 메서드들 │ ← 메서드 (Operations, Methods)
└──────────────┘
- 클래스는 사각형으로 표현되며 3칸으로 구분된다.
- 맨 윗 칸은 클래스명, 가운데는 필드, 마지막은 메소드를 의미한다.
- 클래스 다이어그램 표현법
- + : public
- - : private
- # : protected
- ~ : package(default)
- 밑줄 : static
- 대문자 : final
- 관계 표현법
- 상속 관계 : 비어있는 삼각형 화살표로 표현
- ex) 자식 ──▷ 부모
- 구현 관계 : 점선과 비어있는 삼각형으로 표현
- ex) 인터페이스 ---▷ 클래스
- 의존 관계 : 점선 화살표로 표현
- ex) Controller ---> Service
- 상속 관계 : 비어있는 삼각형 화살표로 표현
- 기타
- 인터페이스 : <<interface>>로 표기
- 추상 타입(abstract) : 클래스명 / 메소드명을 기울임 표시
- 제네릭 : List<T>, Map<K, V>등으로 표현
상속
- 상속 주의사항
- 부모 클래스의 생성자, 초기화 블록은 상속되지 않는다.
- 부모의 private 멤버는 상속 되지만 직접 접근은 불가능하다.
- 생성자, getter() / setter() 사용해서 접근할 것
- 상속 관계에서 기본적으로 자식 생성자에 부모 생성자가 포함되어 있다.
- 자식 객체 생성 시에는 부모로부터 생성이 되기 때문에 자식클래스 생성자 안에는 부모 생성자를 호출하는 super()가 첫 줄에 존재한다.
- 명시적으로 super() 작성 시에도 반드시 첫 줄에 작성
- 상속된 클래스의 객체 생성될 때 순서
- 부모 필드 초기화 -> 부모 생성자 실행 -> 자식 필드 초기화 -> 자식 생성자 실행
오버라이딩(Overriding) | 오버로딩(Overloading) |
하위 클래스에서 메소드 정의 | 같은 클래스에서 메소드 정의 |
메소드 이름 동일 매개변수 동일(개수, 타입) 리턴 타입 동일 |
메소드 이름 동일 매개변수 다름(개수, 타입) 리턴 타입 상관 없음 |
자식 메소드의 접근 범위가 부모 메소드의 접근 범위보다 넓거나 같아야 함 |
접근 제어자와 상관 없음 |
자식 메소드의 예외 수가 부모 메소드의 예외 수보다 적거나 범위가 좁아야 함 |
예외처리와 상관 없음 |
- 오버라이딩
- private, final로 선언된 메소드는 오버라이딩이 불가능!
- ex) hashCode(), equals() ...
- 오버로딩
- ex) 생성자, System.out.println() 메소드 ...
- final
- 클래스에 사용 시 상속 불가
- 메소드에 사용 시 오버라이딩 불가
- 변수에 사용 시 상수처럼 사용 => 한 번 값 할당 후 변하지 않음
다형성
- 다형성 : 상속을 이용해 부모 타입으로부터 파생된 여러 타입의 자식 객체를 부모 클래스 타입 하나로 다룰 수 있는 기술
- 다형성 적용을 위해서 상속은 필수 조건
//Job이 부모 타입, Employee가 자식 타입이라 가정
Job job = new Employee(); //업 캐스팅
Employee emp = new Job(); //이와 같은 반대의 경우는 불가능!!
- 업 캐스팅 : 상속 관계에 있는 부모, 자식 클래스 간에 부모 타입 참조 변수가 모든 자식 타입의 객체 주소를 담을 수 있다.
- 부모 타입 참조 변수에 자식 타입 객체를 담을 수 있다. 반대의 경우는 불가능 (자식 타입 변수는 부모 객체를 담을 수 없음)
- 업 캐스팅한 이후에는 자식 객체 주소일지라도 부모 타입의 멤버(필드, 메소드)만 참조 가능
- 자식 클래스에서 오버라이딩한 메소드는 참조 가능!!!!
Job job = new Employee(); //업 캐스팅
Employee emp = (Employee)job; //다운 캐스팅
- 다운 캐스팅 : 부모 타입의 참조형 변수로 업 캐스팅했던 자식 타입의 객체를 자식 타입으로 되돌리는 것
- 다운 캐스팅과 업 캐스팅은 반대의 개념이 아니고 업 캐스팅했던 객체를 원래 타입으로 되돌리는 것이다.
- 원래 자신의 타입(자식 타입)의 멤버에 접근하기 위해서 다운 캐스팅을 한다.
구분 | 추상클래스 | 인터페이스 |
상속 | 단일 상속 | 다중 상속 |
구현 | extends | implements |
추상 메소드 | abstract 메소드 0개 이상 | 모든 메소드는 abstract |
abstract | 명시적 사용 | 묵시적으로 abstract |
객체 | 객체 생성 불가 | 객체 생성 불가 |
용도 | 참조 타입 | 참조 타입 |
- 추상 클래스 vs 인터페이스
- 공통점 : 상속 / 구현 시 abstract 메소드를 반드시 구현하게 하기 위함, 자체적으로 객체 생성 불가
- 추상 클래스
- 상속을 위한 클래스
- abstract 메소드 0개 이상 포함
- 클래스 내 일반 변수, 메소드 포함 가능
- 단일 상속만 가능
- 인터페이스
- 모든 메소드는 public abstract로 생략이 가능하다.
- 모든 변수는 public static final(상수)로 생략이 가능하다.
- 다중 상속, 구현 가능
- 인터페이스의 default 메소드 : 인터페이스에서 메소드의 구현을 할 수 있도록 하는 기능
- 상속하거나 구현하는 입장에서 반드시 오버라이딩하지 않아도 되도록 하기 위한 기능
public void fly(FlyAble fly) {
...
}
public void run(){
//익명클래스 활용하기
FlyAble flyAble = new FlyAble() {
@Override
public void fly() {
//구현내용
}
};
fly(flyAble);
}
- 익명 클래스 : 추상 메소드를 구현함으로 별도의 구현 클래스를 생성하지 않고 사용하는 방법
- 인터페이스, 추상클래스의 추상 메소드를 구현함으로 만들 수 있다.
- 다른 클래스, 패키지에서 재사용이 불가능하다.
@FunctionalInterface
public interface MyFunctionalInter {
boolean test(String str);
}
- 함수형 인터페이스(=Functional Interface) : 오직 하나의 추상 메소드를 가지는 인터페이스
- 상수 필드나 default 메소드는 존재해도 상관없다.
- @FunctionalInterface 어노테이션을 사용하면 컴파일러가 함수형 인터페이스인지 컴파일 시점에 체크
- 람다식을 사용하기 위한 전제조건
- 자바에서 제공하는 대표적인 Functional Interface
인터페이스 | 추상 메소드 | 설명 |
Runnable | void run() | 매개변수 없이 실행 |
Supplier<T> | T get() | 값을 제공함 |
Consumer<T> | void accept(T) | 값을 소비함(출력 등) |
Function<T,R> | R apply(T) | T를 받아 R을 리턴 |
Predicate<T> | boolean test(T) | 조건 판단, true/false 리턴 |
@FunctionalInterface
public interface MyFunctionalInter { //FunctionalInterface
boolean test(String str);
}
public static void main(String[] args){
testString((s) -> s.length() > 10); //람다 표현식
}
public void testString(MyFunctionalInter func) {
if (func.test("test")) {
System.out.println("맞아");
} else {
System.out.println("아니야");
}
}
- 람다 표현식 : FunctionalInterface 추상메소드를 쉽게 구현하는 방식으로 추상 메소드 구현체를 인자로 전달하는 방식으로 이해하면 쉽다.
- 람다 표현식 사용 시 추상 메소드의 반환타입과 매개변수 숙지는 필수!
- FunctionalInterface는 추상 메소드가 한 개밖에 없기에 메소드 명은 숙지할 필요X
- ex) public void 메소드(){}를 람다로 표현 시 () -> {}
public int 메소드(String param){} 표현 시 (param) -> {return 10;}
- 람다 표현식 사용 시 추상 메소드의 반환타입과 매개변수 숙지는 필수!
API
- String 관련 클래스
- String 클래스 : Immutable(불변)객체로 값을 수정할 수 없는 문자열 클래스
- + 연산 등을 사용해서 값 수정 시 수정된 문자열을 새로운 주소에 할당해서 넘겨준다.
- 자주 사용하는 메소드 => String 클래스에서 제공하는 메소드는 불변 객체이기에 새로운 문자열을 반환
- contains("특정 문자") : 문자열에 특정 문자가 포함되어 있는지 확인
- indexOf("특정 문자") : 문자열에서 특정 문자의 인덱스 번호를 반환
- split("특정 문자") : 문자열을 특정 문자를 기준으로 배열로 변경
- replace("특정문자", "변경문자") : 문자열의 특정 문자를 원하는 문자로 변경
- join("구분자", 대상배열) : 문자열 배열을 문자열로 변경
- substring(시작인덱스[, 끝 인덱스]) : 문자열을 원하는 인덱스만큼 잘라서 반환
- toUpperCase / toLowerCase() : 영문자를 대/소문자로 변경
- trim() : 문자열의 앞 뒤 공백을 제거
- isBlank() / isEmpty() : 문자열 공백을 처리
- isBlank() - 공백을 문자열로 처리하지 않는다.
- isEmpty() - 공백을 문자열로 처리한다.
- StringBuffer 클래스 : Mutable(가변)객체로 값 수정이 가능한 문자열 클래스
- ThreadSafe한 대신 성능 저하가 발생
- StringBuilder 클래스 : StringBuffer와 동일하게 Mutable(가변)객체로 값 수정이 가능한 문자열 클래스
- ThreadSafe 하지 않다.
- StringTokenizer 클래스 : String 클래스의 split() 메소드와 같은 기능을 제공하며 전달받은 문자열을 구분자로 나눠 각 토큰에 저장한다.
- String 클래스 : Immutable(불변)객체로 값을 수정할 수 없는 문자열 클래스
- Wrapper 클래스
- 기본타입과 달리 객체이기 때문에 다형성을 활용할 수 있다.
- JVM이 자동으로 박싱(기본타입->래퍼클래스 변환) / 언박싱(래퍼클래스->기본타입 변환)을 해준다.
- 변환
- String -> 기본자료형
- Wrapper클래스.parse자료형()
ex) Integer.parseInt("3")
- Wrapper클래스.parse자료형()
- 기본자료형 -> String
- Wrapper클래스.valueOf().toString()
ex) Integer.valueOf(3).toString() - String.valueOf(기본타입)
ex) String.valueOf(3)
- Wrapper클래스.valueOf().toString()
- 기본자료형 -> Wrapper 클래스
- Wrapper클래스.valueOf(기본타입)
ex) Integer.valueOf(10)
- Wrapper클래스.valueOf(기본타입)
- String -> 기본자료형
- Date 관련 클래스
- Calendar 클래스 : 월(Month)을 표시할 때 0 ~ 11로 표현
- GregorianCalender 클래스 : Calender 클래스의 자식 클래스로 년, 월, 일, 시, 분, 초 정보를 다룰 수 있다.
예외처리
- 모든 예외의 부모는 Exception 클래스
- Exception : 예외는 크게 CheckedException과 UnCheckedException으로 나뉜다.
- CheckedException : 컴파일 시점에 반드시 예외처리를 해줘야하는 예외
- ex)IOException 등
- UncheckedException : 런타임 중 발생하는 예외로 컴파일러가 강제로 처리하도록 하지 않는 예외
- ex) RuntimeException 자식 예외들로 NPE, IndexOutOfBoundsException ... 등
- CheckedException : 컴파일 시점에 반드시 예외처리를 해줘야하는 예외
- 예외처리 방법
- Exception 처리를 호출한 메소드에게 위임하는 방법
- 선언부에 throws 예외를 명시적으로 작성함으로 호출한 상위 메소드에게 예외 처리를 위임하는 방법
- main() 메소드까지 위임된 경우 처리하지 못하면 비정상 종료
- Exception이 발생한 곳에서 직접 처리하는 방법
- try-catch문을 통해서 예외처리하는 방법
- try : 예외가 발생할 가능성이 있는 코드를 작성
- 예외가 발생하기전 코드까지만 실행된다.
- catch : try 블록 안에서 예외 발생 시 해당하는 예외에 대한 처리 로직 작성, 여러 예외를 처리 가능
- 상속관계에 있는 예외의 경우 더 좁은범위(자식타입) 예외를 먼저 잡아야한다.
- finally : 예외 발생 여부와 상관없이 무조건 처리해야하는 로직 작성
- return문을 만나도 실행되지만 System.exit()을 만나면 실행되지 않고 종료
- Exception 처리를 호출한 메소드에게 위임하는 방법
- 예외 발생시키기 : throw new 예외
try (BufferedReader br=new BufferedReader(new FileReader("C:/data/text.txt"))){
...
} catch (Exception){
...
} finally {
...
}
- try with resource 구문
- try() 소괄호 안에 반납(close)해야하는 자원을 작성하면 자동으로 반납(close)해준다.
- 주로 입출력 스트림들은 자원 사용 후 반납해줘야 메모리가 낭비되지 않기에 무조건 반납해줘야한다.
- try() 소괄호 안에 반납(close)해야하는 자원을 작성하면 자동으로 반납(close)해준다.
- 사용자 정의 예외 : Exception 클래스를 상속받아서 예외 클래스로 작성할 수 있다.
입출력
- File 클래스 : 파일을 표현하는 클래스
- 파일 크기, 속성, 이름 등의 정보와 파일 생성 및 삭제 기능 제공
- 스트림(Stream) : 입출력 장치에서 데이터 읽고 쓰기 위해서 제공하는 클래스
- 모든 스트림은 단방향이며 각 장치마다 연결할 수 있는 스트림이 존재
- 하나의 스트림으로 입출력을 동시에 수행할 수 없기에 2개의 스트림이 필요
- 기반 스트림 종류
- InputStream : 바이트 기반 입력 스트림 중 최상위 부모타입
- OutputStream : 바이트 기반 출력 스트림 중 최상위 부모타입
- Reader : 문자 기반 입력 스트림 중 최상위 부모타입
- Writer : 문자 기반 입력 스트림 중 최상위 부모타입
- FileInputStream / FileOutputStream : 파일로부터 바이트 단위로 읽을 때 / 쓸 때 사용
- FileReader / FileWriter : 파일로부터 문자 단위로 읽고 쓸 때 사용
- 보조 스트림 : 스트림 기능을 향상시키거나 새로운 기능을 추가하기 위해서 사용되며 실제 데이터를 주고받는 스트림이 아니기에 입출력 처리 불가능
- 기반 스트림을 먼저 생성 후 이를 통해 보조 스트림을 생성해서 사용해야 한다.
- 보조 스트림 종류
- InputStreamReader / OutputStreamWriter : 문자 변환 보조 스트림
- BufferedInputStream / BufferedOutputStream : 입출력 성능 보조 스트림
- DataInputStream / DataOutputStream : 기본 데이터 타입 출력 보조 스트림
- ObjectInputStream / ObjectOutputStream : 객체 입출력 보조 스트림
- 직렬화(Serialization) : 객체를 바이트로 변환하는 것
- 직렬화할 객체 클래스에서 Seiralizable 인터페이스를 구현해야한다.
- transient 키워드의 필드는 직렬화에서 제외한다.
- 역직렬화(Deserialization) : 바이트를 객체로 변환하는 것
컬렉션
- 컬렉션 : 자바에서 제공하는 자료구조를 담당하는 프레임워크이다.
- 크기 제약이 없고 추가, 삭제, 정렬 기능이 간단히 해결되며, 제네릭 타입을 지정해주지 않으면 여러 타입의 데이터 저장 가능
- 제네릭 타입을 통해 타입 안정성을 보장받을 수 있다.
//static 메소드인 경우
list.forEach(Person::referenceMethodTest);
//static 메소드가 아닌 경우
list.forEach(new Person()::referenceMethodTest);
- 메소드 참조 : 메소드명을 직접 참조함으로 람다 표현식을 더 간결하게 사용할 수 있는 방법이다.
- 형태 : 클래스이름::메소드명
- Comparable vs Comparator
- Comparable : 객체의 기본 정렬 방식 할 때 사용하는 인터페이스
- 클래스 자체에서 구현, compareTo(T o), 한 가지 기준만 가능, 기본 정렬 방법 제공
- 다른 정렬 기준을 명시하지 않는 경우 Comparable의 compareTo()로 정렬한다. (기본 정렬방식)ex) 매개변수 없이 sort() 호출 시 compareTo() 기준으로 정렬된다.
- Comparator : 기본 정렬 방식 외에 추가로 정렬 방식을 지정할 때 사용하는 인터페이스
- 별도 클래스에서 구현, compare(T o1, T o2), 여러 개의 기준 사용 가능, 다양한 정렬 방식 제공
- Comparator 내부에서도 Comparable을 사용할 수 있음
- Comparable : 객체의 기본 정렬 방식 할 때 사용하는 인터페이스
'BootCamp' 카테고리의 다른 글
Oracle DB 수업 정리 (0) | 2025.04.13 |
---|