| 자료형 | 데이터 | 크기 | 범위 |
|---|---|---|---|
| boolean | 참과 거짓 | 1 바이트 | ture, flase |
| char | 문자 | 2 바이트 | 유니코드 문자 |
| byte | 정수 | 1 바이트 | -128 ~ 127 |
| short | 정수 | 2 바이트 | -32768 ~ 32767 |
| int | 정수 | 4 바이트 | -2,147,483,648 ~ 2,147,483,647 |
| long | 정수 | 8 바이트 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
| float | 실수 | 4 바이트 | 3.4E +/- 38(7자리 숫자) |
| double | 실수 | 8 바이트 | 1.7E +/- 308(15자리 숫자) |
클래스의 이름 규칙
메소드와 변수의 이름 규칙(카멜)
상수의 이름 규칙
키워드 final가 붙는 변수이름은 모두 대문자로 짓는다.
class Constants {
public static void mian(Stirng[] args) {
final int MAX_SIZE = 100;
final char CONST_CHAR ="상";
}
}
자바는 기본적으로 int 형 연산을 한다.
double pi = 3.1415;
int wholeNumber = (int)pi;
데이터와 기능을 묶어놓은 것
class BankAcoount{
static int balance = 0;
public int deposit(int amount) {
balance += amount;
return balance;
}
public int withdraw(int amount) {
balance -= amount;
return balance;
}
public int checkMyBalance() {
System.out.println("잔액: " + balance);
return balance;
인스턴스화
BankAccount myAcnt1; // 참조변수 myAcnt1의 선언
BankAccount myAcnt2; // 참조변수 myAcnt2의 선언
// 클래스 BankAccount의 인스턴스화(Instantiaion)
myAcnt1 = new BankAccount()
myAcnt2 = new BankAccount()
myAcnt1.deposit(1000) // myAcnt1이 참조하는 인스턴스의 deposit 호출
myAcnt2.deposit(2000);
String 클래스
String str = "Happy";
// String형인 Happy는 인스턴스이다.
생성자(Constructor)
생성자 메소드는 값 초기화를 위한 것이다.
public BankAcoount(String acc, String ss, int bal){
accNumber = acc;
ssNumber = ss;
balance = bal;
디폴트 생성자
인자가 없는 생성자, 정의해주지 않으면 자동삽입 된다.
public BankAccount() {
// empty
}
명령 프롬프트 상황에서의 '현재 디렉토리'는 다음과 같다.
C:\PackageStudy
명령 프롬프트 상에서 작업이 진행중인 디렉토리의 위치
클래스 패스의 지정
명령 프롬프트
C:\PackageStudy>set classpath
-- 클래스 패스는 '자바 가상머신의 클래스 탐색 경로'를 의미하며,
이는 프로그래머가 직접 지정할 수 있다.
클래스 패스에는 둘 이상의 경로를 지정할 수 있다. 둘 이상의 경로를 지정할때는 세미콜론(;)으로 이를 구분해준다.
절대 경로
C:\PackageStudy>set classpath=.;C:\PackageStudy\MyClass
상대 경로
.\MyClass
자바 8을 기준으로 Java SE(Standard Edition)에서 제공하는 클래스의 수가 많은데, 같은 이름의 클래스는 반드시 존재할수 밖에 없다.
구분하기 위해선 다른 패키지(디렉토리)에 속해있는것이다.
패키지의 선언
ex) com.wxfx.smart
정보의 은닉을 위한 private 선언을 하며 클래스 내부에서만 접근할 수 있도록 한다.
접근 수준 지시자(Access-level Modifiers)
캡슐화를 중요시 여긴다.
static으로 선언된 변수는 변수가 선언된 클래스의 모든 인스턴스가 공유하는 변수이다.
클래스 변수의 접근 방법
class AccessWay {}
class ClassAccess {
AccessWay way = new AccessWay();
way.num++; // 외부에서 인스턴스의 이름을 통한 접근
AccessWay.num++; // 외부에서 클래스의 이름을 통한 접근
}
클래스 변수는 생성자 기반 초기화를 하면 안된다.
인스턴스 생성할때 마다 값이 리셋되기 때문
메소드 오버로딩의 조건
메소드의 이름이 같아도 매개변수 선언이 다르면 메소드를 구분할 수 있다
상위 클래스에 정의된 메소드를 하위 클래스에서 다시 정의하는 것
자바5에서 추가된 반복문이다.
for(int e: ar) {
System.out.println(e);
}
상속은 "연관된 일련의 클래스들에 대해 공통적인 규약을 정의할 수 있는 것"이다.
-- 하위 클래스의 인스턴스 생성 시 상위 클래스,하위 클래스의 생성자 모두 호출된다.
-- 하위 클래스의 인스턴스 생성시 상위 클래스의 생성자가 먼저 호출된다.
하나의 객체가 여러가지 형태를 가질 수 있는 것을 의미한다.
자바에서 다형성은 한 타입의 참조 변수를 통해 여러 타입의 객체를 참조할 수 있도록 허용하여 상위 클래스가 동일한 메시지로 하위 클래스들이 서로 다른 동작을 할 수 있도록 한다.
다형성을 활용하면, 부모 클래스가 자식 클래스의 동작 방식을 알 수 없어도 오버라이딩을 통해 자식 클래스에 접근할 수 있다.
다형성의 장점
다형성의 조건
instanceof는 참조의 유무를 따져 true or false를 반환하는 것
if (ref instanceof ClassName)
// ref가 ClassName 클래스의 인스턴스를 참조하면 true 반환
// ref가 ClassName 상속하는 클래스의 인스턴스이면 true 반환
기본 정의
interface Printable{
public void print(String doc); // 추상 메소드
}
클래스가 인터페이스를 상속하는 행위는 "상속"이 아닌 "구현(Implementation)"이라 한다. 문법 관계는 상속과 동일하지만 본질은 "구현"이기 때문이다.
인터페이스 간 상속
interface Printable {
void print(String doc);
}
interface ColorPrintable extends Printable{
void printCMYK(String doc);
}
제네릭이 갖는 의미는 "일반화"이다. 그리고 자바에서 그 일반화의 대상은
자료형이다.
인스턴시 생성시 결정이 되는 자료형의 정보를 T로 대체한다.
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
T로 자료형을 임의로 비워둔다는 것을 암시하며, T에 들어갈 자료형은
인스턴스를 생성할 때 정의한다.
Box<Apple> aBox = new Box<Apple>();
혹은
Box<Apple> aBox = new Box<>();
타입 매개변수 (Type Parameter) : Box<T>에서 <T>
타입 인자 (Type Argument) : Box<Apple>에서 Apple
매개변수화 타입 (Parameterized Type) : Box<Apple>
Box<int> box = new Box<int>(); // 사용불가
Box<Integer> box = new Box<Integer>(); // 사용가능
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
class BoxInBox{
public static voide main(String[] args) {
Box<String> sBox = new Box<>();
sBox.set("I am so happy.");
Box<Box<String> wBox = new Box<>();
wBox.set(sBox);
Box<Box<Box<String>>> zBox = new Box<>();
zBox.set(wBox);
System.out.println(zBox.get().get().get());
}
}
class Box<T extends Number> {...}
인스턴스 생성 시 타입 인자로 Number 또는 이를 상속하는 클래스만 올 수 있음
타입 인자를 제한하지 않았을 때
class Box<T> {
private T ob;
....
public int toIntValue() {
return ob.intValue(); // ERROR
}
}
타입 인자를 제한했을 때
class Box<T extens Number>{
private T ob;
....
public int toIntValue() {
return ob.intValue(); // OK
}
}
위의 예시처럼 참조변수 ob가 참조하게 될 것은 인스턴스이다. 하지만 어떠한 클래스의 인스턴스를 참조하게 될지 알 수 없기 때문에 ob를 통해서 호출할 수 있는 메소드는 Object 클래스의 메소드로 제한이 된다.
반면, 타입 인자를 제한하면 Number 클래스의 intValue 메소드를 호출할 수 있다. ob가 참조하는 인스턴스는 intValue 메소드를 가지고 있음을 보장할 수 있기 때문이다.
interface Eatable { public String eat();}
class Apple implements Eatable {
public String eat() {
return "It tastes so good";
}
}
class Box<T extends Eatable> {
T ob;
public void st(T o) {
ob = o;
}
public T get() {
System.out.println(ob.eat()); //Eatable로 제한하였기에 eat 호출 가능
return ob;
class Box<T extends Number & Eatable> {...}
클래스 전부가 아닌 일부 메소드에 대해서만 제네릭으로 정의하고 싶을 때
메소드는 인스턴스 메소드 뿐만 아니라 클래스 메소드에도 가능하다.
public static <T> Box<T> makeBox(T o) {...}
메소드의 이름은 makeBox이고 반환형은 Box<T>이다."
제네릭 메소드의 T는 메소드 호출 시점에 결정한다.
class BoxFactory {
public static <T> Box<T> makeBox(T o) {
Box<T> box = new Box<T>();
box.set(o);
return box;
}
}
// 오토 박싱 전
Box<String sBox = BoxFactory.<String>makeBox("Sweet");
Box<String dBox = BoxFactory.<Double>makeBox("7.59");
// 오토 박싱 후
Box<String sBox = BoxFactory.makeBox("Sweet");
Box<String dBox = BoxFactory.makeBox("7.59");
public static <T extends Number> Box<T> makeBox(T o) {
...
// 타입 인자 제한으로 intValue 호출 가능
System.out.println("Boxed data: " + o.intValue());
return box;
}
// 타입 인자를 Number를 상속하는 클래스로 제한
public static <T extends Number> T openBox(Box<T> box) {
// 타입 인자 제한으로 intValue 호출 가능
System.out.println("Unboxed data: "+ box.get().intValue());
return box.get();
}
어떤 형태의 타입이든 다 받는것
public static void peekBox(Box<?> box) {
System.out.println(box);
}
public static <T> void peekBox(Box<T> box) {
System.out.println(box)
} // 제네릭 메소드의 정의
public static void peekBox(Box<?> box) {
System.out.println(box)
} // 와일드 카드 기반 메소드 정의
상한 제한된 와일드 카드(Upper-Bounded Wildcards), extends
public static void peekBox(Box<? extends Number> box){
System.out.println(box);
}
-- box는 Box<T> 인스턴스를 참조하는 참조변수이다.
-- 이때 Box<T> 인스턴스의 T는 Number 또는 이를 상속하는 하위 클래스이어야 함
하한 제한된 와일드 카드(Lower-Bounded Wildcards), super
public static void peekBox(Box<? super Integer> box) {
System.out.println(box);
}
-- box는 Box<T> 인스턴스를 참조하는 참조변수이다.
-- 이때 Box<T> 인스턴스의 T는 Integer 또는 Integer가 상속하는 클래스어야 함
Box<T>의 T를 Number 또는 Number를 직간접적으로 상속하는
클래스로 제한하기 위한것도 좋은 설명이다.
그리고 인자를 전달되는 대상을 제한하는 것은 그 자체로 프로그램에 안정성을 높여 의미가 있다.
먼저 다음 메서드를 봅시다.
class BoxHandler {
// 매개변수 box가 참조하는 상자에서 인스턴스를 꺼내는 기능
public static void outBox(Box<Toy> box) {
Toy toy = box.get(); // 상자에서 꺼내기
System.out.println(toy);
}
}
이 메서드를 정의할 당시 프로그래머의 생각은 다음과 같을 것 입니다.
'상자에서 내용물을 꺼내는 기능의 메서드를 정의하자'
그러나 현재는 매개변수 box를 대상으로 get은 물론 set의 호출도 가능합니다.
public static void outBox(Box<Toy> box) {
Toy toy = box.get(); // 꺼내는 것! OK!
box.set(new Toy()); // 넣는 것! 이것도 OK!
}
위와 같은 실수는 누구나 할 수 있지만 이러한 오류는 컴파일 과정에서 발견되지 않습니다.
다음과 같이 매개변수를 선언하면 상자에서 꺼내는 것은 가능하지만 넣는 것은 불가능하게 됩니다. 넣으려고 하면 컴파일 오류가 발생합니다.
public static void outBox(Box<? extends Toy> box) {
Toy toy = box.get(); // 꺼내는 것! OK!
box.set(new Toy()); // 넣는 것! ERROR!
}
위의 상황에서 set 메서드의 호출이 불가능한 이유는 위 메서드의 매개변수로 Toy 인스턴스를 저장할 수 있는 상자만(Box<T> 인스턴스만) 전달된다는 사실을 보장할 수 없기 때문입니다.
Toy 클래스는 다음과 같이 다른 클래스들에 의해 얼마든지 상속이 될 수 있습니다.
class Car extends Toy { ... }
class Robot extends Toy { ... }
그리고 이렇게 상속 관계를 맺으면 위의 outBox메소드에 Box<Car> 또는 Box<Robot> 인스턴스가 인자로 전달될 수 있습니다. 이러한 상황에서 다음과 같이 Toy인스턴스를 상자에 담을 수 있을까요?
public static void outBox(Box<? extends Toy> box) {
// box로 Box<Car> 또는 Box<Robot> 인스턴스가 전달된다면?
box.set(new Toy()); // 넣는 것! ERROR!
}
바로 이러한 문제점 때문에 다음과 같이 선언된 매개변수를 대상으로는 저장하는(전달하는) 메서드의 호출이 불가능 합니다.
Box<? extends Toy> box
정리하자면 다음과 같은 매개변수 선언을 보았을 때,
public static void outBox(Box<? extends Toy> box) {
/*
이 안에서는 box가 참조하는 인스턴스에
Toy 인스턴스를 저장하는(전달하는) 메서드 호출은 불가능하다.
*/
}
"box가 참조하는 인스턴스를 대상으로 저장하는 기능의 메서드 호출은 불가능하다." 라는 판단을 할 수 있어야 합니다.
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
class Toy {
@Override
public String toString() {
return "I am a Toy";
}
}
class BoxHandler {
public static void outBox(Box<? extends Toy> box) {
Toy toy = box.get(); // 상자에서 꺼내기
System.out.println(toy);
}
public static void inBox(Box<Toy> box, Toy toy) {
box.set(toy); // 상자에 넣기
}
}
이번에는 다음 메서드를 봅시다.
class BoxHandler {
public static void inBox(Box<Toy> box, Toy toy) {
box.set(toy); // 상자에 넣기
}
}
위의 inBox 메서드도 좋은 코드가 되기 위한 다음 조건을 만족하지 못 합니다.
"필요한 만큼의 기능을 허용하여, 코드의 오류가 컴파일 과정에서 최대한 발견되도록 한다."
이 메서드는 상자에 인스턴스를 저장하는 것이 목적이니, 다음과 같이 get 메서들르 호출하는 코드가 삽입된다면 이는 분명 프로그래머의 실수입니다.
public static void inBox(Box<Toy> box, Toy toy) {
box.set(toy); // 넣는 것! OK!
Toy myToy = box.get(); // 꺼내는 것! 이것도 OK!
}
그러나 이러한 실수는 컴파일 과정에서 발견되지 않습니다. 따라서 이러한 실수가 컴파일 과정에서 발견될 수 있도록 매개변수를 다음과 같이 선언해야 합니다.
public static void inBox(Box<? super Toy> box, Toy toy) {
box.set(toy); // 넣는 것! OK!
Toy myToy = box.get(); // 꺼내는 것! ERROR!
}
위와 같이 매개변수를 선언하면 get 메서드의 호출문에서 컴파일 오류가 발생합니다. 이유는 반환형을 Toy로 결정할 수 없기 때문입니다. 즉 get 메서드 호출 자체는 문제되지 않으나, 반환되는 값을 저장하기 위해 선언한 참조변수 형을 Toy로 결정했다는 사실에서 문제가 발생합니다.
Toy 클래스의 상속관계가 다음과 같다고 가정합시다.
class Plastic { ... }
class Toy extends Plastic { ... }
그러면 inBox 메서드의 첫 번째 인자로 전달 가능한 두 가지 유형의 Box<T> 인스턴스는 Box<Toy>, Box<Plastic> 입니다.
inBox 메서드에 인자로 Box<Toy> 의 인스턴스가 전달되면 메서드 내에서 다음 문장을 실행하는데 문제가 없지만,
// get이 반환하는 것이 Toy 인스턴스이므로 문제가 없음
Toy myToy = box.get();
Box<Plastic> 의 인스턴스가 전달되면 메서드 내에서 다음 문장을 실행하는데 있어서 문제가 됩니다. 그래서 컴파일러는 이 문장 자체를 허용하지 않습니다.
// get이 반환하는 것이 Plastic 인스턴스이므로 문제가 됨
Toy myToy = box.get();
정리하자면 다음과 같은 매개변수 선언을 보았을 때,
public static void inBox(Box<? super Toy> box, Toy toy) {
/*
이 안에서는 box가 참조하는 인스턴스에서
Toy 인스턴스를 꺼내는(반환하는) 메서드 호출은 불가능하다.
*/
}
"box가 참조하는 인스턴스를 대상으로 꺼내는 기능의 메서드 호출은 불가능하다" 라는 판단을 할 수 있어야 합니다.
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
class Toy {
@Override
public String toString() {
return "I am a Toy";
}
}
class BoxHandler {
public static void outBox(Box<? extends Toy> box) {
Toy toy = box.get(); // 상자에서 꺼내기
System.out.println(toy);
}
public static void inBox(Box<? super Toy> box, Toy toy) {
box.set(toy); // 상자에 넣기
}
}
참조변수를 Object형으로 선언한다면
public static void inBox(Box<? super Toy> box, Toy toy) {
Object myToy = box.get();
}
위의 메서드 정의는 컴파일이 됩니다. 그러나 자바는 Object형 참조변수의 선언이나 Object형으로의 형 변환이 불필요하도록 문법을 개선시켜 봤습니다. Object라는 이름이 코드에 직접 등장하는 것은 컴파일러를 통한 오류의 발견 가능성을 낮추는 행위이기 때문입니다. 그러므로 지금 설명하는 부분에서 참조변수를 Object형으로 선언하는 것은 논외로 해야하며, 동시에 당연히 피해야할 일입니다.
앞서 Toy 클래스를 담은 상자를 기준으로 inBox, outBox 메서드를 정의하였습니다.
class BoxHandler {
public static void outBox(Box<? extends Toy> box) {
Toy toy = box.get(); // 상자에서 꺼내기
System.out.println(toy);
}
public static void inBox(Box<? super Toy> box, Toy toy) {
box.set(toy); // 상자에 넣기
}
}
위의 두 메서드는 Box<Toy> 인스턴스를 대상으로 정의된 메서드 입니다. 이 상황에서 다음 클래스를 정의했다고 가정해봅시다.
class Robot { ... }
그리고 Box<Robot>의 인스턴스를 대상으로 outBox, inBox 메서드를 호출하고 싶다면, 오버로딩을 하여 메서드를 정의하는 방법을 고려할 수 있습니다.
public static void outBox(Box<? extends Toy> box) { ... }
public static void outBox(Box<? extends Robot> box) { ... }
public static void inBox(Box<? super Toy> box, Toy n) { ... }
public static void inBox(Box<? super Robot> box, Robot n) { ... }
그 이유는 자바는 제네릭 등장 이전에 정의된 클래스들과의 상호 호환성 유지를 위해 컴파일 시 제네릭과 와일드카드 관련 정보를 지우는 과정을 거치는데, 그로 인해 위의 두 매개변수 선언은 컴파일 과정에서 다음과 같이 수정되고 이로 인해 메소드의 오버로딩이 성립 불가능한 상태가 됩니다.
그런데 다음 두 메서드 정의는 오버로딩이 성립하지 않습니다.
해결책
위와 같은 상황을 해결하는 답은 '제네릭 메서드'에 있습니다.
class BoxHandler {
public static <T> void outBox(Box<? extends T> box) {
T t = box.get();
System.out.println(t);
}
public static <T> void inBox(Box<? super T> box, T n) {
box.set(n);
}
}

List<E>인터페이스를 구현하는 대표적인 컬렉션 클래스
ArrayList<E> : 배열 기반 자료구조LinkedList<E>: 리스트 기반 자료구조List<E>인터페이스를 구현하는 클래스들의 공통 특성
ArrayList<E>의 단점
-- 저장 공간을 늘리는 과정에서 시간이 비교적,많이 소요된다
-- 인스턴스의 삭제 과정에서 많은 연산이 필요할 수 있다. 따라서 느릴 수 있다
ArrayList<E>의 장점
-- 저장된 인스턴스의 참조가 빠르다.
LinkedList<E>의 단점
-- 저장된 인스턴스의 참조 과정이 배열에 비해 복잡하다. 따라서 느릴 수 있다
LinkedList<E>의 장점
-- 저장 공간을 늘리는 과정이 간단하다.
-- 저장된 인스턴스의 삭제 과정이 단순한다.
Iterator<String> itr = list.iterator(); // 반복자 획득
while(itr.hasNext()) { // next 메소드가 반환할 대상이 있다면
str = itr.next(); // next메소드를 호출한다.
}
}
Iterator 반복자의 세 가지 메소드
다음 두 가지 이유로 배열보다 ArrayList<E>가 더 좋다.
-- 인스턴스의 저장과 삭제가 편하다.
-- 반복자를 쓸 수 있다.
List는 배열처럼 선언과 동시에 초기화가 불가능하다. 다만 다음으로 가능
List<String> list = Arrays.asList("Toy","Robot","Box");
// 인자로 전달된 인스턴스들을 저장한 컬렉션 인스턴스의 생성 및 반환
// 이렇게 생성된 리스트 인스턴스는 Immutable 인스턴스이다.
list = new ArrayList<>(list);
// 수정하고싶으면 위와같이 새롭게 생성
ListIterator<String> litr = list.listIterator();
// List<E> 인터페이스의 메소드
while(itr.hasNext()) {
str = str.next();
if(str.eqals("Box))
itr.remove(); // 위에서 next 메소드가 반환환 인스턴스 제
}
HashSet<E>인터페이스의 특성
Set<String> set = new HashSet<>();
해쉬 알고리즘의 이해

class Num{
private int num;
public Num(int n) {num = n;}
@Override
public String toString() { return String.valueOf(num);
@Override
public int hashCode() {
return num % 3;
}
@Override
public boolean equals(Object obj) {
if(num == ((Num)obj.num)
return true;
else
return false;
}
}
TreeSet<E> 클래스Set<E> 인터페이스를 구현하는 TreeSet<E> 클래스
-- 트리 자료구조를 기반으로 인스턴스를 저장, 이는 정렬 상태가 유지되면서 인스턴스가 저장됨을 의미
TreeSet<Integer> tree = new TreeSet<Integer>();
그렇다면 인스턴스의 크고 작음에 대한 기준을 프로그래머가 정해주어야 한다.
public interface Comprable<T>
-> 이 인터페이스에 위치한 유일한 추상 메소드 int compareTo(T o)
Comparable<T> 인터페이스를 구현할 때 정의해야 할 추상 메소드는
int compareTo(T o)
이 메소드의 정의 방법
TreeSet 인스턴스에 저장될 것을 고려한 클래스의 예
class Person implements Comparable<Person> {
private String name;
private int age;
@Override
public int compareTo(Person p) {
return this.age - p.age;
}
}
따라서 TreeSet<T>에 저장할 인스턴스들은 모두 Comparable<T> 인터페이스를 구현한 클래스 인스턴스이어야 한다.
스택과 큐의 이해

Queue<E>인터페이스의 메소드들
예외처리를 반환
값을 반환
Queue<String> que = new LinkedList<>();
// LinkedList<E>는 List<E>와 동시에 Queue<E>를 구현하는 컬렉션 클래스다.
Deque(덱)을 기준으로 스택을 구현하는것이 원칙
Deque<E> 인터페이스의 메소드
값 반환
예외 반환
HaspMap<K,V> 클래스
HashMap<Integer, String> map = new HashMap<>();
// key-value 기반 데이터 저장
map.put(45, "Brown");
map.put(37, "James");
map.put(24, "Martin");
HashMap<K,V>클래스는 Iterable<T>인터페이스를 구현하지 않으니
for-each문을 통해서, 혹은 반복자를 얻어서 순차적 접근이 불가능하다.
대신 Key를 따로 모아 놓은 컬렉션 인스턴스를 얻을 수 있다.
그리고 이때 반환된 컬렉션 인스턴스를 대상으로 반복자를 얻을 수 있다.
public Set<K> KeySet()
// key만 담고 있는 컬렉션 인스턴스 생성
Set<Integer> ks = map.keySet();
// 전체 key 출력 (for-each문 기반)
for (integer n : ks)
System.out.println(n.toString() + '\t';
System.out.println();
// 전체 value 출력 (for-each문 기반)
for (integer n : ks)
System.out.println(n.toString() + '\t';
System.out.println();
// 전체 value 출력(반복자 기반)
for(Iterator<Integer> itr = ks.iterator(); itr.hasNext(); )
System.out.println(map.get(itr.next()) + '\t');
System.out.println();
Set<E>는 Iterable<E>를 상속하므로 for-each문을 통하거나 또는 반복자를 얻어서 순차적 접근을 진행할 수 있다.
TreeMap<K,V> 클래스Hashmap과 다를거 없이 정렬 기준만 다르다.
sort 메소드의 제네릭 선언
// 임시
public static <T extends Comparable<T>> void sort(List<T> list)
// 실제
public static <T extends Comparable<? super T>> void sort(List<T> list)
열거형
enum Scale { // 열거 자료형 Scale의 정의
DO, RE, MI, FA
}
참조변수도 선언이 가능하다.
Scale sc = Scale.DO;
클래스 내에서도 열거형 정의 가능
class Customer {
enum Gender {
MALE, FEMALE
// 클래스 내에서만 사용 가능
}
열거형의 정의에도 생성자가 없으면 디폴트 생성자가 삽입된다.
다만 이 생성자는 private으로 선언이 되어 직접 인스턴스를 생성하는 것이 불가능하다.
enum Person {
MAN, WOMAN;
private Person() {
System.out.println("Person constructor called");
}
class Varargs {
// vargs참조변수로 가변인자를 받으며 몇개든 간의 다 받아들이고 배열로 간주
public static void showAll(String...vargs) {
System.out.println("LEN: " + vargs.length);
for(String s : vargs)
System.out.print(s+ '\t');
System.out.println();
}
public static void main(String[] args) {
showAll("Box"); // new String[]{"Box"});
showAll("Box","Toy"); // new String[]{"Box","Toy"});
showAll("Box","Toy","Apple");
// new String[]{"Box","Toy","Apple"});
}
}
클래스 안에 정의된 클리이스이다.
class Outer{ // 외부 클래스
class Nexted{...} // 네스티드 클래스
}
네스티드 클래스는 두종류로 나뉘는데
이너 클래스는 다시 정의되는 위치나 특성에 따라 세종류로 나뉘어진다
멤버 클래스의 인스턴스는 외부 클래스의 인스턴스에 종속적이다.
class Outer {
private int num = 0;
class Member { // 멤버 클래스 정의
void(int n) { num += n; }
int get() { return num; }
}
}
class MemberInner {
public static void main(String[] args) {
Outer o1 = new Outer();
Outer o2 = new Outer();
// o1기반으로 두 인스턴스 생성
Outer.Member o1m1 = o1.new Member();
Outer.Member o1m2 = o1.new Member();
// o2기반으로 두 인스턴스 생성
Outer.Member o2m1 = o2.new Member();
Outer.Member o2m2 = o2.new Member();
// o1기반으로 생성된 두 인스턴스의 메소드 호출
o1m1.add(5);
System.out.println(o1m2.get());
// o2기반으로 생성된 두 인스턴스의 메소드 호출
o2m1.add(7);
System.out.println(o2m2.get());
}
}
멤버 클래스는 언제 사용하는가?
멤버 클래스는 클래스의 정의를 감추어야 할 때 유용하게 사용이 된다.
알아야 할 클래스의 개수가 줄어둠
interface Printable {
void print();
}
class Papers {
private String con;
public Papers(String s) { con = s; }
public Printable getPrinter() {
return new Printer();
}
// 멤버 클래스를 감춤
private class Printer implements Printable {
public void print() {
System.out.println(con);
}
}
}
public static void main(String[] args) {
Papers p = new Papers("서류 내용");
Printable prn = p.getPrinter();
prn.print();
}
// 클래스 사용자 입장에서 Printable 인터페이스는 알지만
// Printer 클래스는 모르며, 알 필요도 없다.
로컬 클래스는 멤버클래스와 비슷하다.
interface Printable { void print(); }
class Papers {
private String con;
public Papers(String s) { con = s; }
public Printable getPrinter() {
// 메소드 안으로 넣음
class Printer implements Printable {
public void print() {
System.out.println(con);
}
}
return new Printer();
}
}
public Printable getPrinter() {
class Printer implements Printable {
public void print() {
System.out.println(con);
}
}
return new Printer();
}
1. 인터페이스의 이름으로 객체를 생성한다.
2. 인터페이스의 내용을 넣어준다.
// 익명 클레스로 바꿈
public Printable getPrinter() {
// Printable인터페이스를 구현한 인스턴스를 만들기 위함
return new Printable() {
// Printable 인터페이스의 내용을 넣어줘야함
public void print() {
System.out.println(con);
}
};
interface Printable{ void print(String s); }
Printable prin = new Prinatable() {
public void print(String s) {
System.out.println(s);
}
}
// 컴파일러가 s가 매개변수라고 판단해 주길 바라는것은 무리니까
Printable prn = (String s) -> { System.out.println(s); };
// s가 string형 임은 Printable 인터페이스를 보면 알 수 있으니까
// 최종 람다식
Printable prn = (s) -> {System.out.println(s); };
Printable prn = s -> System.out.println(s);
Pridicate<T>
Suplier<T>
Comsumer<T>
Function<T, R>
메소드 참조의 4가지 유형
class ArrangeList {
public static void main(String[] args) {
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList<>(ls);
// 람다식
Comsumer<List<Integer>> c = 1 -> Collections.reverse(1);
// Collection::reverse
c.accept(ls); // 순서 뒤집기 진행
System.out.println(ls); // 출력
}
}
클래스마다 자료구조 방식이 다르니까 반복자로 기준을 정해줘서
각자 다른 자료구조에 인스턴스를 참조할수 있게 된다. 다시 말해서
for-each문을 통한 순차적 접근의 대상이 되기 위한 조건이 컬렉션 클래스가 Iterable 인터페이스를 구현해야 한다
Collection 인터페이스가 Iterable를 상속하니까 메소드 반환타입에도 제한없이 받아들이기 위해서 Iterable 타입으로 하는것
유연성과 호환성
Iterable은 컬렉션 프레임워크의 일반적인 인터페이스입니다.
이는 메소드가 구체적인 컬렉션 유형을 반환하지 않고도 여러 종류의 컬렉션을 반환할 수 있도록 해줍니다.
이렇게 함으로써 클라이언트 코드가 특정 컬렉션 유형에 의존하지 않고 메소드를 사용할 수 있습니다.
코드 재사용
메소드가 Iterable을 반환하면, 해당 메소드는 컬렉션을 직접 만들어 반환할 필요가 없습니다. 대신, 이미 존재하는 컬렉션을 반환하거나, 컬렉션을 생성하는 다른 메소드를 호출하여 그 결과를 반환할 수 있습니다. 이는 코드를 더 간결하게 만들고, 유지보수를 쉽게 해줍니다.
확장성
반환 타입으로 Iterable을 사용하면, 나중에 컬렉션의 구현을 변경하더라도 클라이언트 코드를 변경할 필요가 없습니다. 예를 들어, 메소드가 List를 반환하도록 구현되어 있다면, 나중에 Set으로 변경하고 싶을 때 클라이언트 코드도 변경해야 합니다. 하지만 Iterable을 사용하면 이러한 변경이 더 쉬워집니다.
효율성
Iterable은 컬렉션을 순회할 수 있는 인터페이스를 제공하므로, 메소드가 반환한 컬렉션을 반복해서 사용할 수 있습니다. 이는 메모리 사용량과 성능을 향상시킬 수 있습니다. 예를 들어, 한 번에 모든 요소를 반환하는 대신 필요한 만큼 요소를 반복적으로 가져올 수 있습니다.
따라서 Iterable을 반환 타입으로 사용함으로써 코드의 유연성, 재사용성, 확장성 및 효율성을 향상시킬 수 있습니다.