참조 타입
데이터 타입 분류
1.
기본 타입 → 정수 타입, 실수 타입, 논리 타입
2.
참조 타입 → 배열 타입, 열거 타입, 클래스, 인터페이스
기본 타입 변수는 실제 값을 갖지만, 참조 타입 변수는 객체의 주소를 갖고 있다.
참조 변수의 ==, ≠ 연산
기본적으로 주소 값을 비교하는 것이 된다.
String 타입
자바는 기본적으로 문자열 리터럴이 동일하다면 String 객체를 공유하도록 되어있다.
String name1 = "신용권";
String name2 = "신용권";
이라고 하면 동일한 String 객체를 참조하는 것.
다만 new 연산자를 사용하면 새로운 객체를 생성하므로 리터럴이 같더라도 다른 객체를 가리킨다.
객체에 대해서 주소값 비교 말고, 다른 기준으로 비교하고 싶으면 equals()함수를 사용해야한다.
열거 객체
열거 상수는 객체이다.
public enum Week{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
Java
복사
이러면 Monday 부터 Sunday 까지 총 7개의 Enum 객체가 생성된다.
따라서 다음과 같은 결과가 나옴.
Week week1 = Week.SATURDAY;
Week week2 = Week.SATURDAY;
week1 == week2 // true
Java
복사
클래스
다른 생성자를 호출해서 중복 코드 줄이기
public class Car{
String company = "현대자동차";
String model;
String color;
int maxSpeed;
Car(){
}
Car(String model){
this(model,"은색",250);
}
Car(String model,String color){
this(model,color,250);
}
Car(String model, String color, int maxSpeed){
this.model = model;
this.color = color;
this.maxSpeed = maxSpeed;
}
}
Java
복사
정적 멤버와 static
public class Calculator{
String color;
static double pi = 3.141519;
void setColor(String color) { this.color = color; }
static int plus(int x, int y) { return x+y; }
static int minus(int x, int y) { return x - y; }
}
Java
복사
인스턴스 필드를 이용해서 실행해야한다면 인스턴스 메소드로 선언하고, 인스턴스 필드를 이용하지 않는다면 정적 메소드로 선언
사용 예시
double result1 = 10 * 10 * Calculator.pi;
int result2 = Calculator.plus(10,5);
int result3 = Calculator.minus(10,5);
Java
복사
정적 필드는 필드 선언과 동시에 초기값을 주는 것이 보통
계산이 필요한 초기화 작업이 있을 수 있는데, 생성자에서 초기화 작업을 할 수 없다.
생성자는 객체 생성 시에만 실행되기 때문. 따라서 정적 블록을 만들어서 초기화를 시켜줘야한다.
→ 인스턴스 필드나 인스턴스 메소드를 블록 안에서 사용할 수 없다.
public class Television{
static String company = "Samsung";
static String model = "LCD";
static String info;
static {
info = company + "-" + model;
}
}
Java
복사
싱글톤
프로그램에서 단 하나의 객체만 만들도록 보장해야하는 경우
클래스 외부에서 new 연산자로 생성자를 호출할 수 없도록 막아야함. → 생성자 앞에 private 접근 제한자를 붙이자.
자신의 타입인 정적 필드를 하나 선언하고 자신의 객체를 생성해 초기화하고 정적 필드도 private 접근 제한자를 붙여 외부에서 필드값을 변경하지 못하도록 박는다.
대신 외부에서 호출할 수 있는 정적 메소드인 getInstance()를 선언하고 정적 필드에서 참조하고 있는 자신의 객체를 리턴해준다.
public class Singleton {
private static Singleton singleton = new Singleton);
private Singletone(){}
static Singleton getInsance(){
return singleton;
}
}
Java
복사
final 필드와 상수
final 필드: 초기값이 저장되면 이것이 최종적인 값이 되어서 프로그램 실행 도중에 수정할 수 없다.
final 필드 초기화: 선언 시에 주거나 생성자에서 주거나
상수(불변 값): 객체마다 저장할 필요가 없는 공용성을 띠고 있으며, 여러 가지 상수 값으로 초기화될 수 없다.
→ final 필드는 객체마다 저장되고 생성자의 매개값을 통해 여러 가지 값을 가질 수 있기 때문에 상수가 될 수 없다.
따라서 상수로 만들려면 static final이여야한다.
상속
부모 생성자 호출
자식 객체를 생성하면 부모 객체가 먼저 생성되고 자식 객체가 그 다음에 생성됨.
부모 클래스: CellPhone 객체
자식 클래스: DmbCellPhone 객체
1.
자식 클래스에서 생성자가 명시적으로 선언되어있지 않는다면 아래와 같이 암묵적으로 생성
public DmbCellPhone(){
super();
}
Java
복사
2.
직접 자식 생성자를 선언하고 명시적으로 부모 생성자를 호출하고 싶다면
public class Student extends People{
public int studentNo;
public Student(String name, String ssn, int studentNo){
super(name,sn);
this.studentNo = studentNo;
}
}
Java
복사
만약 명시적으로 부모 생성자를 호출하지 않는다면, super()가 추가되고 이 때 부모 클래스에서 기본생성자가 정의되어있지 않다면, 컴파일 에러가 난다.
타입 변환과 다형성
부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능하다.
변수는 자식 객체를 참조하지만, 접근 가능한 멤버는 부모 클래스 멤버로만 한정된다.
다만, 메소드가 자식 클래스에서 오버라이딩 되었다면 자식 클래스의 메소드가 대신 호출된다.
강제 타입 변환
Casting: 부모 타입을 자식 타입으로 변환하는 것.
객체 타입 확인
instanceof 연산자 → 좌항의 객체가 우항의 타입으로 객체가 생성된 것이라면 true, 아니면 false
public void method(Paranet parent){
if(parent instanceof Child) {
Child child = (Child) parent;
}
}
Java
복사
인터페이스
익명 구현 객체
인터페이스 변수 = new 인터페이스(){
};
RemoteControl rc = new RemoteControl(){
public void turnOn(){ //실행문 }
public void turnOff(){ //실행문 }
public void setVolume(int volume) { //실행문 }
};
Java
복사
인터페이스 상속
public interface 하위 인터페이스 extends 상위인터페이스1, 상위인터페이스2 {
...
}
Java
복사
중첩 클래스
멤버 클래스
•
인스턴스 멤버 클래스 : 객체를 생성해야만 사용할 수 있는 중첩 클래스
•
정적 멤버 클래스 : 클래스로 바로 접근할 수 있는 중첩 클래스
로컬 클래스
•
method안에서 정의해서 사용하는 중첩 클래스
인스턴스 멤버 클래스는 바깥 클래스의 인스턴스 필드의 초기값이나 인스턴스 메소드에서 객체를 생성할 수 있으나, 정적 필드의 초기값이 정적 메소드에서는 객체를 생성할 수 없다.
반면 정적 멤버 클래스는 모든 필드의 초기값이나 모든 메소드에서 객체를 생성할 수 있다.
중첩 클래스에서 바깥 클래스 참조 얻기
•
바깥클래스.this.필드
•
또는 바깥클래스.this.메소드() 이렇게 해야함.
예외 처리
Catch 순서
위에서부터 순서대로 잡는다. 따라서 구체적인 예외는 상위에, 추상적인 예외는 하위에 둔다.
예외 떠넘기기
메소드에 throws 키워드를 붙여서 예외를 떠넘길 수 있다. 이러면 이 메소드를 호출한 곳에서 try-catch문으로 예외를 잡아내야한다.
예외를 떠넘김 당한 쪽에서도 예외를 떠넘길 수 있다.
이때 throws Exception 같은 형식으로 모든 예외를 떠넘길 수 있다.
기본 API 클래스
Java.lang 과 Java.util
java.lang → 필수적인 클래스들
•
Object : 자바 클래스의 최상위 클래스
•
System : 표준 입출력 장치, JVM 종료시킬 때
•
Class : 클래스를 메모리로 로딩할 때
•
String : 문자열을 저장하고 여러 가지 정보를 얻을 때
•
StringBuffer, Stringbuilder : 문자열을 저장하고 내부 문자열을 조작할 때 사용
•
Math: 수학 함수를 이용할 때
•
Wrapper : 기본 타입의 데이터를 갖는 객체를 만들 때
java.util → 대부분의 컬렉션들
•
Arrays : 배열을 조작할 때
•
Calendar : 운영체제의 날짜와 시간을 얻을 때
•
Date : 날짜와 시간 정보를 저장하는 클래스
•
Objects : 객체 비교, 널 여부 등을 조사할 때
•
StringTokenizer, 특정 문자로 구분된 문자열을 뽑아낼 때
•
Random : 난수를 얻을 때
hashCode()
•
객체를 특정짓는 id값을 생성한다. 기본적으로 오버라이딩하는데, HashMap 같은 컬렉션의 키값으로 특정 객체를 쓰고 싶다면, 이 함수를 오버라이딩하여 올바르게 객체를 비교하도록 해야한다.
Deep Clone()
•
딥 클론하려면 clone 메소드를 오버라이딩 해야한다.
Class 클래스
•
자바 리플렉션, 로드된 자바 클래스들에 대한 정보를 획득하고 조작할 수 있다.
제네릭
제네릭 타입
public class className<T>{
}
public interface interfaceName<T>{
}
public class Box<T>{
private T t;
public T get() { return t; }
public void set(T t) { this.t = t;}
}
Java
복사
멀티 타입 파라미터
여러개의 파라미터를 제네릭으로 만들 수도 있다.
public class Product<T,M>{
private T kind;
private M model;
public T getKind() { return this.kind; }
public M getModel() { return this.model; }
public void setKind(T kind) { this.kind = kind; }
public void setModel(M model) { this.model = model; }
}
Java
복사
제네릭 메소드
제네릭 메소드는 매개타입과 리턴 타입으로 타입 파라미터를 갖는 메소드를 말한다.
리턴 타입 앞에 <> 기호를 추가하고 타입 파라미터를 기술한 다음, 리턴 타입과 매개 타입으로 타입 파라미터를 사용하면 됨.
이렇게 만들어놓은 타입 파라미터는 기존 클래스 내에서 선언된 타입 파라미터와는 별도의 의미를 가짐. 이는 static 메소드를 위함이다. static 메소드는 인스턴스가 생성되기 전에 메모리에 올라가므로 클래스의 타입 파라미터를 읽어들일 수 없다.
public <타입 파라미터,...> 리턴타입 메소드명(매개 변수,...){ }
public <T> Box<T> boxing(T t){ ... }
Java
복사
두 가지 방식으로 호출할 수 있음.
명시적 호출 or 매개 값을 보고 추론
Box<Integer> box = <Integer>boxing(100);
Box<Integer> box = boxing(100);
Java
복사
public class Util{
public static <T> Box<T> boxing(T t) {
Box<T> box = new Box<T>();
box.set(t);
return box;
}
}
사용 example
Box<Integer> box1 = Util.<Integer>boxing(100);
Box<String> box2 = Util.boxing("홍길동");
Java
복사
제한된 타입 파라미터
public <T extends 상위타입> 리턴타입 메소드(매개변수, ...) { ... }
Java
복사
와일드 카드 타입
<?> : 제한 없다. 타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있다.
<? extends>: 상한이 있음
<? super> : 하한이 있음
제네릭 타입의 상속과 구현
public class ChildProduct<T,M> extends Product<T,M> { ... }
public class ChildProduct<T,M,C> extends Product<T,M> { ... }
Java
복사
람다식
람다식 → 매개 변수를 가진 코드 블록 → 런타임 시에는 익명 구현 객체를 생성함
람다식을 쓰면 자바 코드가 간결해지고, 컬렉션의 요소를 필터링하거나 매핑해서 원하는 결과를 쉽게 집계할 수 있기 때문이다.
Runnable runnable = () -> {...};
Java
복사
람다식 기본 문법
(타입 매개변수, ...) → { 실행문; ...}
타겟 타입과 함수적 인터페이스
코드 블록이기 대문에 마치 자바의 메소드를 선언하는 것처럼 보이지만, 사실은 이 메소드를 가지고 있는 객체를 생성해낸다.
인터페이스 변수 = 람다식;
이기 때문에 인터페이스 변수에 대입된다.
함수적 인터페이스(@FunctionalInterface)
모든 인터페이스를 람다식의 타겟 타입으로 사용할 수는 없다. 람다식이 하나의 메소드를 정의하기 때문에 두 개 이상의 추상 메소드가 선언된 인터페이스는 람다식을 이용해서 구현 객체를 생성할 수 없다.
하나의 추상 메소드가 선언된 인터페이스만이 람다식의 타겟 타입이 될 수 있는데, 이거를 함수적 인터페이스라고 한다.
컴파일러가 체크해주는 기능 → @FunctionalInterface
MyFunctioanlInterface fi = (x,y) -> { ...; return 값;}
Java
복사
클래스 멤버와 로컬 변수 사용
실행 블록에는 클래스의 멤버 및 로컬 변수를 사용할 수 있다.
클래스의 멤버는 제약 사항 없이 사용 가능하지만,
로컬 변수를 사용하면 final 특성을 가져야한다.
표준 API의 함수적 인터페이스
표준 API 패키지로 제공한다.
함수적 인터페이스들을 매개 타입으로 많이 사용한다.
•
Consumer : 매개값은 있고 리턴 값은 없다.
•
Suppiler : 매개값은 없고 리턴 값은 있다.
•
Function : 매개값과 리턴 값이 있다. 주로 매핑(타입 변환) 하는데 사용
•
Operator : 매개값과 리턴값이 있다. 주로 매개값을 연산해서 결과를 리턴
•
Predicate : 매개값은 있고, 리턴이 boolean 이다.
Consumer
Consumer<T> consumer = t → { ... };
T타입을 소비한다.
실행할 때는 accept() 메소드를 통해 실행.
Supplier
getXXX() 메소드를 통해 생성함.
Supplier>String> supplier = () → { ... return T; }
T 타입으로 생성해줌
Function
매개값과 리턴값이 있는 applyXXX() 메소드를 사용함
Function<T,R> R apply(T t) : 객체 T를 객체 R로 매핑한다.
Operator
applyXXX() 메소드를 사용
T appluy(T t, T t) → T와 T를 연산한 후 T리턴
Predicate
testXXX() 메소드 사용
test(T t) → T를 조사
test(T t, U u) → T와 U를 비교 조사
andThen()과 compose() 디폴트 메소드
두 개의 함수적 인터페이스를 순차적으로 연결하고, 첫 번재 처리 결과를 두 번째 매개값으로 제공해서 최종 결과를 얻을 때 사용.
Consumer,Function, Operator → 엥간하면 전부 andThen이 있다.
andThen → 첫번째꺼 하고 두 번째
compose → 두번째꺼 하고 첫번째
메소드 참조
InitBinaryOperator operator = Math::max;
정적 메소드의 경우에는 클래스 이름 뒤에 쓰면 되고 인스턴스 메소드의 경우 참조변수의 이름 뒤에 쓰면 된다.
생성자 참조
생성자 참조도 가능하다.
단순히 객체를 생성하고 리턴하도록 구성된 람다식은 생성자 참조로 대체할 수 있다,
클래스 :: new
컬렉션 프레임워크
List, Set, Map
Collection → List, Set
List: 순서를 유지하고 저장, 중복 저장 가능
Set: 순서를 유지하지 않고 저장, 중복 저장 안됨.
Map: 키와 값의 쌍으로 저장, 키는 중복 저장 안됨.
ArrayList
ArrayList는 특정 인덱스의 객체를 제거하면, 바로 뒤 인덱스부터 마지막 인덱스까지 모두 1씩 당겨지고, 추가하면 밀려난다.
빈번한 객체 삽입이 일어나는 곳에서는 ArrayList를 사용하는 게 바람직하지 않다. Linkedlist is better
인덱스 검색이나, 맨 마지막에 객체를 추가하는 경우에는 ArrayList가 더 좋은 성능을 발휘함.
Vector
ArrayList와 내부 구조가 동일하지만, 동기화된 메소드로 구성되어있다.
쓰레드 세이프하다.
Linkedlist
끝에서부터 순차적으로 추가,삭제하는 경우는 ArrayList가 더 좋다.
근데 중간에 추가 또는 삭제할 경우는 LinkedList가 훨씬 좋음
Set 컬렉션
저장 순서가 유지되지 않고, 중복해서 저장할 수 없으며, 하나의 null만 저장가능하다.
HashSet
객체를 저장하기 전에 객체의 hashCode() 메소드를 호출해서 해시코드를 얻어낸다. 동일한 해시코드가 있다면 다시 equals 메소드로 두 객체를 비교한다.
Map 컬렉션
키와 값으로 구성된 객체를 저장하는 구조
키는 중복 저장될 수 없지만, 값은 중복 저장될 수 있다. 키와 값은 모두 객체임
기존에 저장된 키와 동일한 키로 값을 저장하면 기존 값은 없어지고 새로운 값으로 대치된다.
HashMap
hashCode와 Equals를 사용해서 다른 키인지 같은 키인지 확인한다.
Hashtable
동기화된 메소드로 구성되어있기 때문에 스레드 safe하다.
Tree
일반 객체, Set, Map을 저장하는 Tree가 구현되어있다.
이때 트리에 들어가면 자동으로 정렬이 되는데, 정렬할 때 Comparable과 Comparator가 사용된다.
Comparable인터페이스는 CompareTo 함수가 있는데,
주어진 객체보다 적으면 음수, 크면 양수를 리턴하게끔 하면 정렬이 된다.
Comparator 인터페이스에는 compare 메소드가 있는데, o1, o2에 대해서
o1이 o2보다 앞에 오게하려면 음수를 리턴, o1이 o2보다 뒤에 오게 하려면 양수를 리턴하면 된다.
스트림
중간 처리
•
필터링
•
매핑
•
정렬
•
루핑
최종 처리
•
매칭
•
집계
•
루핑
•
수집
중간처리라면 스트림을 리턴하고,
최종처리라면 기본타입이거나 Optional 타입이다.
Optional
isPresent → 저장되있는지 여부
orElse(T ) → 값이 저장되어있지 않을 경우 디폴트 값 지정
ifPresent(Consumer) → 값이 저장되어있을 경우 Consumer에서 처리
커스텀 집계
reduce(): reduce(T identity, BinaryOperator<T> accumulator)
스트림에 요소가 없을 경우 디폴트 값인 identity. 아니면 람다식 실행
파일 시스템
파일 입출력
File 클래스
File 객체를 생성했다고 해서 파일이나 디렉토리가 생성되는 것은 아니다. 경로가 유효하지 않더라도 컴파일 에러나 예외가 발생하지 않음.
•
createNewFile()
•
mkdir()
•
mkdirs()
•
delete()
등등
그외 파일을 읽고 쓸 때는, FileInputStream, FileOutputStream을 쓴다.
Path
java.io.File 대신에 주로 쓴다.
java.nio.file.Paths 클래스의 정적메소드인 geT() 메소드를 통해 호출해서 Path 객체를 얻어낸다.
•
compareTo
•
getFileName()
•
getFileSystem()
•
getName
•
getNameCount
•
getParent
•
getRoot
•
normalize
Files
파일과 디렉토리의 생성 및 삭제, 속성을 읽는 메소드
java.nio.file.Files
•
copy()
•
createDirectories()
•
createDirectory()
•
createFile()
•
delete
•
deleteIfExists: 존재한다면 삭제
•
exists()
•
getFilestore
•
getLastModifedTime
•
getOwner
•
isReadable 등등