Do It Java 핵심 요약
[Day 1]
자바의 장점
-
플랫폼 독립성: 플랫폼(Windows, Rinux, Mac 등)에 영향을 받지 않으므로 다양한 환경에서 사용 가능
-
객체 지향 언어이기 때문에 유지보수가 쉽고 확장성이 좋음
-
프로그램이 안정적이다
-
풍부한 기능을 제공하는 오픈소스다(JDK)
[Day 2]
음의 정수 표현
- 양수의 2의 보수를 취한 후 1을 더하여 음수를 표현
[Day 4]
상수(final)
- 변하지 않는 값
- 한 번 값을 넣으면 다른 값을 대입할 수 없다
형 변환(type conversion)
- 변수에 다른 자료형의 값이 대입되는 경우 형 변환 발생
- 묵시적 형 변환
- 변환되는 자료형을 명시할 필요 없음
- 작은 수에서 큰 수로, 덜 정밀한 수(정수)에서 더 정밀한 수(실수)로 대입되는 경우
- 명시적 형 변환
- 변환되는 자료형을 명시해야 하며 자료의 손실이 발생할 수 있다
- 큰 수에서 작은 수로, 더 정밀한 수에서 덜 정밀한 수로 대입되는 경우
- 자료형 크기 비교
- 정수형: byte(1byte) < short(2byte) = char(2byte, 문자형) < int(4byte) < long(8byte)
- 실수형: float(4byte) < double(8byte)
[Day 5]
증가 감소 연산자
- 1만큼 더하거나 1만큼 뺄 때 사용하는 연산자
- 항의 앞/뒤 위치에 따라 연산의 결과가 달라짐에 주의
int val;
int num = 0;
val = ++num; //num 값이 1증가한 후 val 변수에 num값을 대입, val = 1, num = 1
val = num++; //val 변수에 num 값을 대입한 후 num 값이 1증가, val = 1, num = 2
val = --num; //num 값이 1감소한 후 val 변수에 num값을 대입, val = 1, num = 1
val = num--; //val 변수에 num 값을 대입한 후 num 값이 1감소, val = 1, num = 0
단락 회로 평가(short circuit evaluation)
- 논리 연산자를 사용하는 연산에서 결과가 확정될 경우 뒤의 연산은 생략한다
- 논리 곱(&&)은 앞의 항이 false이면 뒤의 항의 결과를 평가하지 않아도 false이므로 생략
| - 논리 합( | )은 앞의 항이 true이면 뒤 항의 결과를 평가하지 않아도 true이므로 생략 |
[Day 8]
객체지향 프로그래밍
- 컴퓨터 프로그래밍 패러다임 중 하나
- 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 객체 간의 상호작용으로 구상
객체지향 프로그래밍 구성요소
- 클래스(Class)
- 한 집단의 속성(attribute)과 행위(behavior)를 정의한 것
- 객체지향 프로그램의 기본적인 사용자 정의 데이터형
- 각각의 클래스는 독립적으로 디자인해야 한다
- 객체(Object)
- 클래스의 인스턴스(실제로 메모리상에 할당된 것)
- 고유의 속성(attribute)을 가지며 클래스에서 정의한 행위(behavior)를 수행할 수 있다
- 메서드(Method)
- 객체에 명령을 내리는 메시지
- 객체 간의 통신에도 사용
객체지향 프로그래밍 특징
- 추상화
- 불필요한 정보는 숨기고 중요한 정보만을 표현함으로써 프로그램을 간단히 만드는 것
- 캡슐화: 접근 제어를 통해서 자료의 세부 정보를 은닉, 인위적 변형으로 인한 오류 방지
- 상속
- 새로운 클래스가 기존 클래스의 속성과 메서드를 이용할 수 있게 한다
- 상속을 통해 프로그램의 요구에 맞추어 클래스를 수정할 수 있고 클래스 간의 종속 관계를 형성함으로써 객체를 조직화할 수 있다
- 다형성
- 한 요소에 여러 개념을 넣어 놓는 것
- 오버라이딩: 같은 이름의 메소드가 여러 클래스에서 다른 기능을 하는 것
- 오버로딩: 같은 이름의 메소드가 매개변수나 자료형에 따라서 다른 기능을 하는 것
- 동적 바인딩
- 필요할 때마다 동적으로 메모리를 생성하고, 필요 없는 메모리는 제거
- 작은 메모리로 큰 프로그램을 실행할 수 있지만, 정적 바인딩에 비해 프로그램 속도가 느리다
함수와 스택 메모리
- 스택 메모리
- 함수가 호출될 때 사용하는 메모리
- 함수의 기능 수행이 끝나면 자동으로 반환
//함수 만들기
public class FunctionTest {
public static void main(String[] args) {
int num1 = 10;
int num2 = 30;
int sum = addNum(num1, num2);
System.out.println(sum);
}
public static int addNum(int n1, int n2) {
int result = n1 + n2;
return result;
}
}

[Day 9]
인스턴스와 힙(heap) 메모리
- 인스턴스
- 클래스가 메모리에 생성된 것
- 하나의 클래스 코드에서 여러 개의 인스턴스를 생성
- 힙(heap) 메모리에 생성됨
- 각 인스턴스는 메모리에 개별적으로 생성됨

- 힙 메모리
- new 예약어를 통해 메모리 생성
- garbage collecter를 통해 메모리 제거
참조 변수와 참조 값
- 참조 변수: 인스턴스 생성 시 선언하는 변수
- 참조 값: 인스턴스가 생성되는 힙 메모리 주소
Student student = new Student(); //student가 참조 변수
System.out.println(student); //힙 메모리의 주소 값 반환(참조 값)
용어 정리
- 객체 - 객체 지향 프로그램의 대상, 클래스의 인스턴스
- 클래스 - 한 집단의 속성과 행위를 정의한 것
- 인스턴스 - 클래스가 메모리에 생성된 것
- 멤버 변수 - 클래스의 속성
- 메서드 - 객체에 명령을 내리는 메시지
- 참조 변수 - 인스턴스 생성 시 선언하는 변수
- 참조 값 - 인스턴스가 생성되는 힙 메모리 주소
생성자
- 인스턴스를 초기화하는 명령어 집합
- 생성자의 이름은 클래스의 이름과 같다
-
메소드가 아니므로 상속되지 않으며 리턴 값이 없다
- 생성자를 아예 만들지 않으면 디폴트 생성자 자동 생성
[Day 10]
기본 자료형과 참조 자료형
기본 자료형(primitive data type)
- 자바 컴파일러에 의해 해석되는 자료형
- 스택(Stack) 메모리에 저장
-
사용하기 전에 반드시 선언(Declared)되어야 함
- 8가지를 미리 정의하고 제공
- OS(운영체제)에 따라 자료형의 길이가 변하지 않는다
- 비객체 타입이므로 null 값을 가질 수 없다. 만약 null을 넣고 싶다면 Wrapper Class를 활용해야 한다.

참조 자료형 (reference data type)
- 기본형을 제외한 모든 자료형
- JAVA 최상위 클래스인 Object 클래스를 상속
- 클래스 타입(class type), 인터페이스 타입(interface type), 배열 타입(array type), 열거 타입(enum type) 등 이 있다
- 빈 객체를 의미하는 Null이 존재
- 문법상으로는 에러가 없지만 실행시켰을 때 에러가 나는 런타임 에러가 발생
- 예) 객체나 배열을 Null 값으로 받으면 NullPointException이 발생하므로 변수 값을 넣어야 한다.
- Heap 영역에 생성
정보은닉
private 접근 제어자
- 클래스의 외부에서 내부의 멤버 변수나 메서드에 접근하지 못하게 한다
- 멤버 변수나 메서드를 외부에서 수정하지 못하도록 하여 오류를 줄일 수 있음
- 필요한 경우 get(), set() 메서드를 통해 값을 변경하도록 할 수 있음
[Day 12]
static 변수
//사용 형식: static 자료형 변수명
static int num;
-
여러 개의 인스턴스가 같은 메모리의 값을 공유하기 위해 사용
- 프로그램이 메모리에 적재(load)될 때 데이터 영역의 메모리에 생성(인스턴스가 생성될 때x, heap영역 x)
- 클래스 변수라고도 한다
- 인스턴스 생성과 관계없이 클래스 이름으로 직접 참조
//static변수
Student.serialNum = 100; //serialNum이 static변수
//멤버변수(인스턴스변수)
Student stu = new Student();
stu.serialNum = 100;
static변수 vs 인스턴스 변수

Tip.스태틱 메서드 안에서는 멤버 변수를 사용할 수 없다
public class Student {
static int serialNum = 10000;
int studentID;
public static int getSerialNum() {
this.studentID = 10; //static메서드 안에서는 멤버 변수 사용 불가능
return serialNum;
}
}
3. 변수의 유효 범위
지역 변수(로컬 변수)
- 선언 위치: 함수 내부에 선언
- 사용 범위: 함수 내부에서만 사용
- 메모리: 스택
- 생성과 소멸: 함수가 호출될 때 생성, 함수 동작 완료 후 소멸
멤버 변수(인스턴스 변수)
- 선언 위치: 클래스 내부에 선언
- 사용 범위: 클래스 내부에서 사용, private이 아니면 참조 변수로 다른 클래스에서 사용 가능
- 메모리: 힙
- 생성과 소멸: 인스턴스가 생성될 때 힙에 생성, 가비지 컬렉터에 의해 소멸
static 변수(클래스 변수)
- 선언 위치: static 예약어를 사용해 클래스 내부에 선언
- 사용 범위: 클래스 내부에서 사용, priavate이 아니면 클래스 이름으로 다른 클래스에서 사용 가능
- 메모리: 데이터 영역
- 생성과 소멸: 프로그램이 시작할 때 상수와 함께 데이터 영역에 생성, 프로그램이 끝나고 메모리를 해제할 때 소멸
static응용: singleton 패턴
- 인스턴스를 하나만 만들어야 하는 경우에 사용!
public class Company {
//내부에서 인스턴스 생성
private static Company instance = new Company();
//외부에서 생성자를 사용하지 못하게 한다
private Company() {};
//내부에서 생성한 인스턴스를 외부에서 사용할 수 있도록 해주는 메서드
public static Company getInstance() {
return instance;
}
}
public class CompanyTest {
public static void main(String[] args) {
Company c1 = Company.getInstance();
Company c2 = Company.getInstance();
System.out.println(c1==c2);//true => 같은 인스턴스를 가르킨다
}
}
[Day 17]
접근 제어자(Access Modifier, 접근 제한자, 접근 지정자)
- 변수나 메소드의 사용 권한을 설정
- private: 클래스 내에서만 사용 가능 => 클래스
- default: 같은 패키지의 클래스에서만 사용 가능 => 패키지
- protected: 같은 패키지 혹은 상속 받은 클래스만 사용 가능 => 패키지 + 상속
- public: 모든 클래스에서 사용 가능 => 모든
상속에서 클래스 생성 과정
- 하위 클래스의 인스턴스가 생성될 때 상위 클래스의 인스턴스가 먼저 생성된다
- 상위 클래스의 생성자가 호출된 후 하위 클래스의 생성자가 호출된다
- 하위 클래스의 생성자에서는 무조건 상위 클래스의 생성자가 호출되어야 한다
=> 따로 구현하지 않으면 컴파일러가 상위 클래스의 기본 생성자를 호출하는 super()를 넣어준다
- 만약 상위 클래스에 기본생성자가 없을 경우 하위 클래스는 명시적으로 상위 클래스를 호출해야 한다
super 예약어
-
하위 클래스가 상위 클래스에 접근할 때 사용
-
this가 자기 자신의 인스턴스 주소를 가지는 것처럼 super는 하위 클래스가 상위 클래스에 대한 주소를 가진다
캐스팅(Casting, 형변환)
- 타입을 변환하는 것
- 자바의 상속 관계에 있는 부모와 자식 클래스 간에는 서로 간의 형변환이 가능
업캐스팅
- 자식 클래스의 객체가 부모 클래스 타입으로 형변환 되는 것
- 묵시적 형 변환(타입을 명시하지 않아도 됨)
- 부모 클래스의 멤버 변수와 메서드만 사용 가능
다운캐스팅
-
업캐스팅된 것을 다시 원상태로 돌리는 것
-
다운캐스팅할 때는 타입을 명시적으로 지정해줘야 한다
//부모 클래스
class Person{
String name;
Person(String name){
this.name = name;
}
}
//자식 클래스
class Student extends Person{
String check;
Student(String name){
super(name);
}
}
public class Main{
public static void main(String[] args){
Person p = new Student("홍길동"); //업캐스팅
p.check = "컴파일 에러 발생"; // 컴파일 에러 발생
Student s = (Student)p; // 다운캐스팅
s.name = "김유신";
s.check = "check!";
}
}
가상 메서드(virtual method)
-
객체의 변수나 메서드의 참조는 그 타입에 따라 이루어 지지만, 가상 메서드는 타입과 상관없이 실제 생성된 인스턴스의 메서드가 호출된다
-
자바는 모든 메서드가 가상 메서드이다
Customer vc = new VIPCustomer(); //Customer타입의 VIPCustomer인스턴스 생성
vc.calcPrice(10000); //메서드 실행 시
// 메서드가 재정의 되지 않았으면 타입(Customer)의 메서드가 실행된다
// 메서드가 재정의 되었으면 인스턴스(VIPCustomer)의 메서드가 실행된다
Tip. 업캐스팅한 경우
- 자식 클래스에만 정의된 멤버 변수, 메서드는 사용 불가
- 자식 클래스에서 오버라이드한 메서드는 자식 클래스의 메서드가 사용됨
[Day 19]
추상 클래스
- 추상 메서드를 포함한 클래스
- 추상 메서드는 구현코드 없이 메서드의 선언만 있음
abstract in add(int x, int y); //추상 메서드 O
abstract in add(int x, int y) { }; //구현부({ })가 있으면 추상 메서드 X
- abstract 예약어 사용
- 추상 클래스는 인스턴스화(new)할 수 없음
구상 클래스
- 클래스내의 메소드가 모두 구현된 클래스
- 인스턴스화(new)할 수 있음
[Day 20]
템플릿 메서드 패턴
- java 디자인 패턴 중 하나
- 일부분을 서브 클래스로 캡슐화해 전체 업무 구조는 바꾸지 않으면서 특정 단계의 동작만을 바꾸는 패턴
- 추상 클래스와 구상 클래스를 활용해 기능의 흐름을 정의한다
- 추상 클래스로 선언된 상위 클래스에서 템플릿 메서드를 활용하여 전체적인 흐름을 정의한다
-> 하위 클래스에서 구현되어야 하는 부분은 추상 메서드로 선언
-> 공통으로 사용하는 기능은 일반 메서드로 선언(하위 클래스에서 재정의 하면 안되는 경우 final로 선언)
- 프레임워크에서 많이 사용되는 설계 패턴
후크(Hook) 메서드
- 추상 클래스에서 빈 구현부로 정의된 메서드
- 하위 클래스에서 선택적으로 오버라이드해서 사용할 수 있음
Tip. final예약어
- final 변수는 오직 한 번만 값을 할당할 수 있다.
- final 메서드는 하위 클래스에서 오버라이딩할 수 없다
- final 클래스는 상속되지 않는다
[Day 21]
인터페이스
- 비슷한 목적의 서로 다른 동작을 가지는 클래스들을 구현할 때 틀을 제공하는 일종의 추상 클래스
- 추상 메서드와 상수 만으로 이루어진다(일반적인 추상 클래스는 생성자, 필드, 일반 메서드 포함 가능)
public interface Calc {
//상수( public static final이 자동으로 붙어 상수가 됨)
double PI = 3.14;
int ERROR = -999999999;
//추상 메서드(public abstract가 자동으로 붙어 추상 메서드가 됨)
int add(int num1, int num2);
int substract(int num1, int num2);
}
- 인터페이스를 구현(implements)한 클래스는 인터페이스 형으로 형 변환할 수 있음
-> 이 경우 인터페이스에 선언한 메서드만 사용 가능, 구현 클래스에서 따로 선언한 메서드는 사용 불가(업 캐스팅과 동일)
- 여러 인터페이스 구현 가능(자바의 단일 상속과 다름)
인터페이스의 장점
- 대규모 프로젝트 개발 시 일관되고 정형화된 개발을 위한 표준화가 가능
- 클래스의 작성과 인터페이스의 구현을 동시에 진행할 수 있으므로, 개발 시간 단축
- 클래스와 클래스 간의 관계를 인터페이스로 연결하면, 클래스마다 독립적인 프로그래밍이 가능
Tip. 인터페이스와 추상 클래스는 인스턴스 생성 불가
[Day 23]
디폴트 메서드
- 인터페이스에서 기본 구현을 가지는 메서드
- 공통으로 사용하는 메서드를 구현
- 구현 클래스에서 재정의 가능
인터페이스 상속
- 인터페이스 간에도 상속이 가능
-
구현 코드의 상속이 아니므로 형 상속(type inheritance)라고 한다
-
구현 시 상속 받은 상위 인터페이스의 추상 메서드도 모두 구현해야 한다
- 예시
public interface X {
void x();
}
public interface Y {
void y();
}
//인터페이스 상속
public interface MyInterface extends X, Y {
void myMethod();
}
public class MyClass implements X, Y, MyInterface {
@Override
public void y() {
System.out.println("y()");
}
@Override
public void x() {
System.out.println("x()");
}
@Override
public void myMethod() {
System.out.println("myMethod()");
}
public static void main(String[] args) {
MyClass myClass = new MyClass();
//전부 사용 가능
myClass.x();
myClass.y();
myClass.myMethod();
X xClass = myClass; //X 인터페이스로 형변환
xClass.x(); //사용 가능
//xClass.y(); //사용 불가
//xClass.myMethod(); //사용 불가
}
}
-
클래스를 상속 받으면서 동시에 인터페이스 구현도 가능하다
-
예시
public interface Queue {
void enQueue(String title);
String deQueue();
int getSize();
}
import java.util.ArrayList;
public class Shelf {
protected ArrayList<String> shelf;
public Shelf() {
shelf = new ArrayList<String>();
}
public ArrayList<String> getShelf(){
return shelf;
}
public int getCount() {
return shelf.size();
}
}
public class BookShelf extends Shelf implements Queue {
@Override
public void enQueue(String title) {
shelf.add(title);
}
@Override
public String deQueue() {
return shelf.remove(0);
}
@Override
public int getSize() {
return getCount();
}
}
public class BookShelfTest {
public static void main(String[] args) {
Queue shelfQueue = new BookShelf();
shelfQueue.enQueue("도서1");
shelfQueue.enQueue("도서2");
shelfQueue.enQueue("도서3");
System.out.println(shelfQueue.deQueue());
System.out.println(shelfQueue.deQueue());
System.out.println(shelfQueue.deQueue());
}
}
[Day 24]
기본 클래스
java.lang 패키지
-
많이 사용하는 기본 클래스들이 속한 패키지(Object, String, Integer, System 등)
-
프로그래밍시 import하지 않아도 자동으로 import됨
Object 클래스
- 모든 클래스의 최상위 클래스
- 모든 클래스는 Object 클래스를 상속 받는다(컴파일러가 자동으로 상속 시킴)
- java.lang.Object 경로
toString() 메서드
- 객체의 정보를 String으로 바꿀 때 사용
- String 클래스 => 문자열 반환
- Integer 클래스 => 정수 값 반환
equals() 메서드
- 두 인스턴스의 주소 값을 비교하여 true/false 반환
- 재정의하여 두 인스턴스가 논리적으로 동일한지 판단하도록 하기도 한다
hashCode() 메서드
- 자바 가상 머신이 저장한 인스턴스 주소 값을 10진수로 나타낸다
- 자료의 특정 값(키 값)에 대해 저장 위치를 반환해주는 해시 함수를 사용한다
[저장 위치] = hash.([키 값]);
index = hash.(key);
- 힙 메모리에 인스턴스가 저장되는 방식이 해시이다
서로 다른 메모리의 인스턴스가 같다면? (equals 재정의)
- 재정의 된 equals() 메서드의 값이 true가 나와야 함
- 동일한 hashCode() 반환 값을 가져야 함
=> 논리적 동일함을 위해 equals() 메서드를 재정의 하였다면 hashCode() 메서드도 재정의하여 같은 값이 반환되도록 한다
- String 클래스: 동일한 문자열 인스턴스에 대해 동일한 값이 반환된다
-
Integer 클래스: 동일한 정수 값 인스턴스에 대해 동일한 값이 반환된다
- 예시
class Student {
int studentID;
String studentName;
public Student(int studentID, String studentName) {
this.studentID = studentID;
this.studentName = studentName;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Student) {
Student std = (Student)obj; //매개변수를 Object 타입으로 받으므로 다운 캐스팅
if(studentID == std.studentID)
return true;
else
return false;
}
return false;
}
@Override
public int hashCode() {
return studentID;// 대부분 equals 재정의 할 때 이용했던 멤버변수 값 사용
}
}
public class EqualsTest {
public static void main(String[] args) {
Student std1 = new Student(10001, "Tomas");
Student std2 = new Student(10001, "Tomas");
System.out.println(std1 == std2); //인스턴스 주소가 다르므로 false
System.out.println(std1.equals(std2)); //studentID값이 같으므로 true
System.out.println(std1.hashCode() == std2.hashCode()); //같은 주소 값을 반환하여 true
}
}
clone() 메서드
- 객체의 원본 유지하고 복사본을 만들어 사용할 때 사용하는 메서드
- 기본 틀(prototype)을 두고 복잡한 생성과정을 반복하지 않고 복제
- clone() 메서드를 사용하면 객체의 정보(멤버변수 값)가 같은 인스턴스가 생성되어 객체 지향 프로그램의 정보은닉, 객체보호 관점에 위배될 수 있다
=> 객체의 clone() 메서드 사용을 허용한다는 의미로 cloneable 인터페이스를 명시해 준다
class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public String toString() {
return "x=" + x + ", " + "y=" + y;
}
}
class Circle implements Cloneable{
Point point;
private int radius;
public Circle(int x, int y, int radius) {
point = new Point(x, y);
this.radius = radius;
}
public String toString() {
return "원점은 " + this.point + "이고, 반지름은 " + radius + "입니다";
//this.point하면 point 멤버변수의 타입 클래스의 toString을 실행
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class ObjectCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Circle circle = new Circle(10, 20, 5);
Circle cloneCircle = (Circle)circle.clone();
//서로 다른 해시코드 값을 가진 인스턴스가 확인된다
System.out.println(System.identityHashCode(circle));
System.out.println(System.identityHashCode(cloneCircle));
System.out.println(circle.toString()); // 원점은 x=10, y=20이고, 반지름은 5입니다
}
}
[Day 25]
String 클래스
선언 방법
- 힙 메모리의 인스턴스로 선언
String str = new String("abc");
- 상수 풀에 있는 주소를 참조하여 선언
String str = "abc";
String 클래스로 문자열 연결
- 한 번 생성된 String 값(문자열)은 불변(immutable)
- 두 개의 String 값을 연결하면 새로운 인스턴스가 생성된다
=> 문자열 연결을 계속하면 garbage 메모리가 많이 생길 수 있다
=> 문자열을 자주 연결하고나 변경할 때는 StringBuilder, StringBuffer 사용하면 좋다
public class StringTest {
public static void main(String[] args) {
String str1 = new String("Hello");
int instance1 = System.identityHashCode(str1);
String str2 = new String("World");
str1 = str1.concat(str2); //기존 인스턴스의 값을 바꾸는 것이 아닌 새로운 인스턴스 생성
int instance2 = System.identityHashCode(str1);
System.out.println(instance1 == instance2); //false 출력
}
}
StringBuilder, StringBuffer 사용하기
- 내부적으로 가변적인 char[] 배열을 가지고 있는 클래스
- 문자열을 자주 연결하고나 변경할 때 사용하면 유용하다
- 매번 인스턴스를 새로 생성하지 않고 기존 배열을 변경하여 garbage 메모리 생성 감소
- StringBuffer는 멀티 쓰레드 프로그래밍에서 동기화(sybchronization)를 보장
- 단일 쓰레드 프로그램에서는 StringBuilder 사용을 권장
-
toString 메서드로 String 반환
- StringBuilder 예시
public class StringBuilderTest {
public static void main(String[] args) {
StringBuilder buffer = new StringBuilder("java");
int instanceHash1 = System.identityHashCode(buffer);
buffer.append(" and");
buffer.append(" android");
int instanceHash2 = System.identityHashCode(buffer);
//계속해서 같은 인스턴스 사용
System.out.println(instanceHash1 == instanceHash2); //true
//String으로 출력하기
String str2 = buffer.toString();
System.out.println(str2); //java and android
}
}
[Day 26]
제네릭 클래스
- 변수의 선언, 메서드의 매개변수를 하나의 참조 자료형이 아닌 여러 자료형으로 변환될 수 있도록 프로그래밍하는 것
- 실제 사용되는 참조 자료형으로의 변환은 컴파일러가 검증하므로 안정적인 프로그래밍 방식
- 컬렉션 프레임워크에서 많이 사용
-
여러 참조 자료형으로 대체될 수 있는 부분(매개 변수)을 하나의 문자로 표현
- 예시
public abstract class Material {
public abstract void doPrinting();
}
//<T extends Material>을 통해 Material 클래스를 상속한 클래스만 제네릭 타입에 입력 가능하도록 제한
public class ThreeDPrinter<T extends Material> {
private T material;
public T getMaterial() {
return material;
}
public void setMaterial(T material) {
this.material = material;
}
@Override
public String toString() {
return material.toString();
}
public void printing() {
material.doPrinting();
}
}
public class Plastic extends Material {
public String toString() {
return "재료는 Plastic입니다.";
}
@Override
public void doPrinting() {
System.out.println("Plastic재료로 출력 중입니다.");
}
}
public class Powder extends Material {
public String toString() {
return "재료는 Powder입니다.";
}
@Override
public void doPrinting() {
System.out.println("Powder재료로 출력 중입니다.");
}
}
public class Water {
public String toString() {
return "재료는 Water입니다.";
}
}
public class ThreeDPrinterTest {
public static void main(String[] args) {
ThreeDPrinter<Powder> printer = new ThreeDPrinter<Powder>();
printer.setMaterial(new Powder());
Powder powder = printer.getMaterial();
System.out.println(printer); //재료는 Powder입니다.
ThreeDPrinter<Plastic> plasticPrinter = new ThreeDPrinter<Plastic>();
plasticPrinter.setMaterial(new Plastic());
Plastic plastic = plasticPrinter.getMaterial();
System.out.println(plasticPrinter); //재료는 Plastic입니다.
// Water 클래스가 Material 클래스를 상속하지 않아 사용 불가
/*
ThreeDPrinter<Water> waterPrinter = new ThreeDPrinter<Water>();
waterPrinter.setMaterial(new Water());
System.out.println(waterPrinter); //재료는 Water입니다.
*/
plasticPrinter.printing(); //Plastic재료로 출력 중입니다.
}
}
자료형 매개변수 T
- type의 의미로 T를 많이 사용한다
-
에서 <>는 다이아몬드 연산자라고 한다 - static 키워드는 T에 사용할 수 없다
- 다이아몬드 연산자 내부에서 자료형은 생략 가능
ArrayList<String> list = new ArrayList<>();
[Day 28]
컬렉션 프레임워크
- 데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것
- 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 방법을 제공하는 클래스의 집합
- java.util 패키지에 구현되어 있다

컬렉션 인터페이스
- 컬렉션을 다루는데 가장 기본적인 동작들을 정의하고 관련 메소드를 제공
- 하위에 List와 Set 인터페이스가 있다
- 여러 클래스들이 Collection 인터페이스를 구현한다
List 인터페이스
- 순서(인덱스)가 있음
- 중복 허용
- 배열의 기능을 구현하기 위한 인터페이스
- ArrayList, Vector, LinkedList, Stack, Queue 등이 구현
Set 인터페이스
- 순서가 정해져 있지 않음
- 중복을 허용하지 않음
- HashSet, TreeSet 등이 구현
[Day 30]
내부 클래스
- 클래스 내부에 선언된 클래스
내부 클래스의 장점
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다
- 코드의 복잡성을 줄일 수 있다
인스턴스 내부 클래스
- 구현 위치: 외부 클래스의 멤버 변수 위치
- 사용 가능한 외부 클래스 변수: 인스턴스 변수, 전역 변수
- 객체 생성 방법: 외부 클래스를 먼저 만든 후 내부 클래스 생성
- 정적 변수, 정적 메서드 사용 불가
정적 내부 클래스
- 구현 위치: 외부 클래스의 멤버 변수 위치
- 사용 가능한 외부 클래스 변수: 전역 변수(인스턴스 변수 사용 불가)
- 객체 생성 방법: 외부 클래스와 무관하게 생성
지역 내부 클래스
- 구현 위치: 메서드 내부
- 사용 가능한 외부 클래스 변수: 인스턴스 변수, 역 변수
- 객체 생성 방법: 메서드 호출
익명 내부 클래스
- 구현 위치: 메서드 내부, 변수에 대입하여 직접 구현
- 사용 가능한 외부 클래스 변수: 인스턴스 변수, 전역 변수
- 객체 생성 방법: 메서드 호출 / 인터페이스 타입 변수에 대입할 때 new 예약어를 사용
Tip. 클래스 변수(Static 멤버)
- 클래스 내에
Static키워드로 선언된 변수 - 처음 JVM이 실행되어 클래스가 메모리에 올라갈 때부터 프로그램이 종료될 때까지 유지
- 클래스가 여러 번 생성되어도
Static변수는 처음 한 번만 생성됨 - 동일한 클래스의 모든 객체가 공유
Tip. 인스턴스 변수(Non-static 멤버)
- 클래스 내에 선언된 변수
- 객체 생성 시마다 매번 새로운 변수가 생성됨
- 클래스 변수와 달리 공유되지 않음
인스턴스 내부 클래스, 정적 내부 클래스 예시
class OutClass{
private int num = 10;
private static int sNum = 20;
private InClass inClass;
public OutClass(){
inClass = new InClass();
}
//인스턴스 내부 클래스
private class InClass{
int inNum = 200;
//static int sInNum = 100; //인스턴스 내부 클래스에서는 정적 변수 사용 불가능
void inTest() {
System.out.println(num);
System.out.println(sNum);
}
//인스턴스 내부 클래스에서는 정적 메서드 사용 불가능
//static void sTest() { }
}
public void usingInTest() {
inClass.inTest();
}
//정적 내부 클래스
static class InStaticClass{
int iNum = 100;
static int sInNum = 200;
void inTest() {
//num += 10; //외부 클래스의 인스턴스 변수 사용 불가
sNum += 10; //외부 클래스의 정적 변수는 사용 가능
System.out.println(sNum);
System.out.println(iNum);
System.out.println(sInNum);
}
static void sTest() {
System.out.println(sNum);
//System.out.println(iNum); //정적 메서드는 인스턴스 변수 사용 불가
System.out.println(sInNum);
}
}
}
public class InnerTest {
public static void main(String[] args) {
OutClass outClass = new OutClass();
outClass.usingInTest();
OutClass.InStaticClass sInClass = new OutClass.InStaticClass();
sInClass.inTest();
OutClass.InStaticClass.sTest();
}
}
지역 내부 클래스 예시
class Outer{
int outNum = 100;
static int sNum = 200;
public Runnable getRunnable(){
int localNum = 100;
//지역 내부 클래스
class MyRunnable implements Runnable {
@Override
public void run() {
outNum += 100; //인스턴스 변수는 수정 가능
//localNum += 100; //지역 변수는 수정 불가(지역 변수는 상수가 된다)
System.out.println(outNum);
System.out.println(sNum);
System.out.println(localNum); //지역 변수 사용은 가능
}
}
return new MyRunnable();
}
}
public class LocalInnerTest {
public static void main(String[] args) {
Outer outer = new Outer();
outer.getRunnable().run();
}
}
익명 내부 클래스 예시
- 익명 내부 클래스는 추상메서드가 하나만 정의된 경우에만 사용 가능
class Outer{
//익명 내부 클래스
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("runnable");
}
};
}
public class LocalInnerTest {
public static void main(String[] args) {
Outer outer = new Outer();
outer.runnable.run();
}
}
[Day 31]
람다식(lambda expression)
- 자바에서 함수형 프로그래밍을 구현하는 방식
- 자바8부터 지원
- 클래스를 생성하지 않고 함수의 호출만으로 기능을 수행
함수형 프로그래밍
- 순수 함수: 동일한 입력에는 항상 같은 값을 반환, 함수의 실행이 프로그램 실행에 영향을 미치지 않음
- 불변성: 데이터는 변하지 않는다. 변경이 필요한 경우 원본 데이터의 복사본을 만들어서 작업한다
람다식 구현하기
- 메서드 만들기
- 함수 이름 반환 형을 없애고 화살표(->) 사용
- 매개변수가 하나인 경우 자료형과 괄호 생략하기
- 구현부가 한 문장인 경우 중괄호 생략(retrun이 있으면 return까지 생략)
//원래 메서드
int add(int x, int y){
retrun x + y;
}
//함수 이름 반환 형을 없애고 화살표(->) 사용
(int x, int y) -> {return x + y;}
//구현부가 한 문장인 경우 중괄호 생략
(int x, int y) -> x + y;
함수형 인터페이스
- 추상 메서드가 하나만 있는 인터페이스(다른 메서드는 있어도 됨)
- @FunctionalInterface를 통해 함수형 인터페이스를 표시/검증할 수 있음
-
람다식을 선언하기 위해 사용
- 예시
@FunctionalInterface //함수형 인터페이스(추상 메서드를 하나만 가진다!)
public interface MyNumber {
int getMaxNumber(int num1, int num2);
}
함수를 변수처럼 사용하는 람다식 예시
//함수형 인터페이스
interface PrintString{
void showString(String str);
}
public class LambdaTest {
public static void main(String[] args) {
//일반적인 람다식
PrintString lambdaPrint = str -> System.out.println(str);
lambdaPrint.showString("test"); //test
//람다식으로 구현한 함수 자체가 매개변수로 넘어간다
showMyString(lambdaPrint); //test2
//구현부를 리턴한다
PrintString reStr = returnPrint();
reStr.showString("hello");//hello world
}
//람다식으로 구현한 함수 자체가 매개변수로 넘어간다
public static void showMyString(PrintString lambda) {
lambda.showString("test2");
}
//구현부를 리턴한다
public static PrintString returnPrint() {
return s -> System.out.println(s + " world");
}
}
[Day 32]
스트림(stream) API
자료의 대상과 관계 없이 동일한 연산을 수행
- 배열, 컬렉션을 대상으로 동일한 연산을 수행 함
- 일관성 있는 연산으로 자료의 처리를 쉽고 간단하게 함
한 번 생성하고 사용한 스트림은 재사용 할 수 없음
- 자료에 대한 스트림을 생성하여 연산을 수행하면 스트림은 소모됨
- 다른 연산을 위해서는 새로운 스트림을 생성해야 함
스트림 연산을 기존 자료를 변경하지 않음
- 자료에 대한 스트림을 생성하면 별도의 메모리 공간을 사용하므로 기존 자료를 변경하지 않음
스트림 연산은 중간 연산과 최종 연산으로 구분됨
- 스트림에 대해 중간 연산은 여러 개 적용될 수 있지만 최종 연산은 마치막에 한 번만 적용됨
- 최종 연산이 호출되어야 중간 연산의 결과가 모두 적용됨 => 지연 연산
reduce() 연산
-
정의된 연산이 아닌 프로그래머가 직접 지정하는 연산을 적용
-
최종 연산으로 스트림의 요소를 소모하며 연산 수행
-
예시: reduce로 가장 긴 문자열 반환하기
import java.util.Arrays; import java.util.function.BinaryOperator; class CompareString implements BinaryOperator<String>{ @Override public String apply(String s1, String s2) { if(s1.getBytes().length >= s2.getBytes().length) return s1; else return s2; } } public class ReduceTest { public static void main(String[] args) { String[] greeting = {"안녕하세요~~~~~~~~", "hello", "Good Moring", "반갑습니다"}; //구현부를 만들어서 사용하기 String str1 = Arrays.stream(greeting).reduce("", (s1, s2) ->{ if(s1.getBytes().length >= s2.getBytes().length) return s1; else return s2; }); System.out.println(str1);//안녕하세요~~~~~~~~ //BinaryOperator로 구현부 만들기 String str2 = Arrays.stream(greeting).reduce(new CompareString()).get(); System.out.println(str2);//안녕하세요~~~~~~~~ } }
[Day 33]
예외 처리
오류란?
- 컴파일 오류: 코드 작성 중 발생하는 문법적 오류
- 실행 오류: 프로그램이 의도하지 않은 동작을 하거나 중지되는 서비스 운영에 치명적인 오류
- 자바는 예외 처리를 통해 프로그램의 비정상 종료를 막고 log를 남길 수 있음
오류와 예외 클래스
- 시스템 오류(Error): 가상 머신에서 발생, 프로그래머가 처리할 수 없음
ex) 동적 메모리가 없는 경우, 스택 오버 플로우 등
- 예외(Exception): 프로그램에서 제어할 수 있는 오류
ex) 읽어 들이려는 파일이 존재하지 않는 경우, 네트워크 연결이 끊어진 경우

예외 클래스의 종류
- 모든 예외 클래스의 최상위 클래스는 Exception
- 다양한 예외 클래스가 제공됨

try-with-resources문
- 리소스를 자동 해제하도록 제공해주는 구문
- 자바 7부터 제공
- close()를 명시적으로 호출하지 않아도 try{} 블록에서 열린 리소스는 모두 자동 해제됨
- 단, 해당 리소스가 AutoCloseable을 구현해야 함
public class AutoCloaseObj implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("close()");
}
}
public class AutoCloseTest {
public static void main(String[] args) {
try(AutoCloaseObj obj = new AutoCloaseObj()){
throw new Exception(); //강제로 예외 처리
} catch (Exception e) {
System.out.println("exection");
}
}
}
/* 결과
close()
exection
*/
사용자 정의 예외
- JDK에서 제공되는 예외 클래스 외에 사용자가 필요에 의해 예외 클래스를 정의하여 사용
- 기존 JDK 예외 클래스 중 가장 유사한 클래스에서 상속
- 기본적으로 Exception에서 상속 가능
public class IDFormatException extends Exception {
public IDFormatException (String message) {
super(message);
}
}
public class IDFormatTest {
private String userID;
public String getUserID() {
return userID;
}
public void setUserID(String userID) throws IDFormatException {
if(userID == null) {
throw new IDFormatException("아이디는 null일 수 없습니다.");
} else if(userID.length() < 8 || userID.length() > 20) {
throw new IDFormatException("아이디는 8자 이상 20자 이하로 쓰세요.");
}
this.userID = userID;
}
public static void main(String[] args) {
IDFormatTest idTest = new IDFormatTest();
String userID = null;
try {
idTest.setUserID(userID);
} catch (IDFormatException e) {
System.out.println(e); //exception.IDFormatException: 아이디는 null일 수 없습니다.
}
userID = "1234567";
try {
idTest.setUserID(userID);
} catch (IDFormatException e) {
System.out.println(e); //exception.IDFormatException: 아이디는 8자 이상 20자 이하로 쓰세요.
}
}
}
[Day 34]
자바 입출력
스트림(stream)
- 자바에서 자료의 입출력(I/O)을 처리하는 통로
- 자료의 흐름이 물과 같다는 의미에서 유래
- 단방향 통신만 가능 => 입출력 동시에 불가
스트림의 구분
대상 기준
- 입력 스트림: 대상으로부터 자료를 읽어 들이는 스트림
- 출력 스트림: 대상으로 자료를 출력하는 스트림
자료의 종류
- 바이트 스트림: 동영상, 음악 파일 등을 읽고 쓸 때 사용
- 문자 스트림: 2바이트 단위로 처리, 바이트 단위 처리 시 발생하는 문자 깨짐을 방지
기능
-
기반 스트림: 대상에 직접 자료를 읽고 쓰는 기능의 스트림
-
보조 스트림: 직접 읽고 쓰는 기능은 없이 추가적인 기능을 더해주는 스트림
표준 입출력
- System 클래스의 표준 입출력 멤버
public class System() {
public static PrintStream out;
public static PrintStream in;
public static PrintStream err;
}
System.out
- 표준 출력(모니터) 스트림
System.out.println("표준 출력");
System.in
- 표준 입력(키보드) 스트림
int d = System.in.read(); //한 바이트 읽어내기
System.err
- 표준 에러 출력(모니터) 스트림
System.err.println("데이터"); //빨갛게 출력
Scanner 클래스
- java.util 패키지에 있는 입력 클래스
- 문자뿐 아니라 정수, 실수 등 다른 자료형도 읽을 수 있음
- 콘솔, 파일 등 여러 대상에서 자료를 읽을 수 있음
바이트 단위 스트림 - InputStream
- 바이트 단위 입력용 최상위 스트림
- 추상 메서드를 포함한 추상 클래스로 하위 클래스가 구현하여 사용
주요 하위 클래스
- FileInputStream - 파일에서 바이트 단위로 자료를 읽습니다
- ByteArrayInputStream - Byte배열 메모리에서 바이트 단위로 자료를 읽습니다
- FilterInputStream - 기반 스트림에서 자료를 읽을 때 추가 기능을 제공하는 보조 스트림의 상위 클래스입니다
메서드
- int read() - 입력 스트림으로부터 한 바이트의 자료를 읽습니다. 읽은 자료의 바이트 수를 반환합니다.
- int read(byte b[ ]) - 입력 스트림으로부터 b[ ] 크기의 자료를 b[ ]에 읽습니다. 읽은 자료의 바이트 수를 반환합니다
- int read(byte b[ ], int off, int len) - 입력 스트림으로부터 b[ ] 크기의 자료를 b[ ]의 off변수 위치부터 저장하여 len만큼 읽습니다. 읽은 자료의 바이트 수를 반환합니다.
- void closer() - 입력 스트림과 연결된 대상 리소스를 닫습니다
바이트 단위 스트림 - OutputStream
- 바이트 단위 출력용 최상위 스트림
- 추상 메서드를 포함한 추상 클래스로 하위 클래스가 구현하여 사용
주요 하위 클래스
- FileOutputStream - 바이트 단위로 파일에 자료를 씁니다
- ByteArrayOutputStream - Byte배열에 바이트 단위로 자료를 씁니다
- FilterOutputStream - 기반 스트림에서 자료를 쓸 때 추가 기능을 제공하는 보조 스트림의 상위 클래스입니다
메서드
- int wirte() - 한 바이트를 출력합니다
- int wirte(byte b[ ]) - b[ ] 배열에 있는 자료를 출력합니다
- int wirte(byte b[ ], int off, int len) - b[ ] 배열에 있는 자료의 off 위치부터 len 개수만큼 자료를 출력합니다
- void flush() - 출력을 위해 잠시 자료가 머무르는 출력 버퍼를 강제로 비워 자료를 출력합니다
- void closer() - 출력 스트림과 연결된 대상 리소스를 닫습니다. 출력 버퍼가 비워집니다
flush()와 close() 메서드
- 출력 버퍼를 비울 때 flush() 메서드 사용
- close() 메서드 내부에서 flush()가 호출되므로 close() 메서드가 호출되면 출력 버퍼가 비워짐
문자 단위 스트림 - Reader
- 문자 단위로 읽는 최상위 스트림
주요 하위 클래스
- FileReader - 파일에서 문자 단위로 읽는 스트림 클래스
- InputStreamReader - 바이트 단위로 읽은 자료를 문자로 변환해 주는 보조 스트림 클래스
- BufferedReader - 문자로 읽을 때 배열을 제공하여 한꺼번에 읽을 수 있는 기능을 제공해 주는 보조 스트림
주요 메서드
- int read() - 파일로부터 한 문자를 읽습니다. 읽은 값을 반환합니다
- int read(char[ ] buf) - 파일로부터 buf 배열에 문자를 읽습니다
- int read(char[ ] buf, int off, int len) - 파일로부터 buf 배열의 off 위체에서부터 len 개수만큼 문자를 읽습니다
- void close() - 스트림과 연결된 파일 리소스를 닫습니다
[Day 35]
보조 스트림
- 실제 읽고 쓰는 스트림이 아닌 보조적인 기능을 추가하는 스트림
- 데코레이터 패턴
- FilterInputStream과 FilterOutputStream이 보조스트림의 상위 클래스
- 생성자의 매개 변수로 또 다른 스트림을 가짐
InputStreamReader와 OutputStreamwriter
- 바이트 단위로 읽거나 쓰는 자료를 문자로 변환해주는 보조스트림
public class InputStreamReaderTest {
public static void main(String[] args) {
try(InputStreamReader irs = new InputStreamReader(new FileInputStream("reader.txt"))){
int i = 0;
while((i = irs.read()) != -1) {
System.out.print((char)i);
}
} catch (IOException e) {
System.out.println(e);
}
}
}
Buffered 스트림
- 내부적으로 8192 바이트 배열을 가지고 읽거나 쓰기 기능을 제공하여 속도가 빨라짐
스트림 클래스
- BufferedInputStream - 바이트 단위로 읽는 스트림에 버퍼링 기능을 제공합니다
- BufferedOutputStream - 바이트 단위로 출력하는 스트림에 버퍼링 기능을 제공합니다
- BufferedReader - 문자 단위로 읽는 스트림에 버퍼링 기능을 제공합니다
- BufferedWriter - 문자 단위로 출력하는 스트림에 버퍼링 기능을 제공합니다