1. JVM, 변수
- 바이트 코드 : 내가 작성한 코드가 운영체제가 읽을 수 있는 코드(바이트 코드)로 Java 컴파일러가 변환한 코드
- compiler : .java → .class로 변환해줌
- 인터프리터 : .class 코드 해석기. 운영체제가 읽은 바이트 코드를 기기가 실행할 수 있는 기계어로 번역
- JIT 컴파일러(Just In Time) : 빠른 .class 코드 해석기. 인터프리터의 효율을 높여주는 서포터 해석기
- 메모리 영역 : 운영체제로부터 JVM이 할당받은 메모리 영역
- 클래스 로더 : .class 바이트 코드를 메모리 영역에 담는 운반기. JVM으로 class(바이트 코드)를 불러와 메모리에 저장함
- 가비지 컬렉터
래퍼 클래스 변수
// 박싱 VS 언박싱
// 박싱
// Integer 래퍼 클래스 num 에 21 의 값을 저장
int number = 21;
Integer num = number;
// 언박싱
int n = num.intValue(); // 래퍼 클래스들은 inValue() 같은 언박싱 메서드들을 제공해줍니다.
클래스로 변수 관리하며 객체지향의 많은 기능 사용할 수 있기 때문에 사용.
참조형 변수
- 다른 기본형 변수가 실제 값을 저장하는 저장 공간이라면 참조형 변수는 실제 값이 아닌 원본 값의 주솟값을 저장
- 기본형 변수 : 원본 값이 Stack 영역에 있다.
- 참조형 변수 : 원본 값이 Heap 영역에 있다.
- Stack 영역에는 따로 저장 해둔 원본 값의 Heap 영역 주소를 저장한다.
📌 Stack 영역 vs Heap 영역
- Stack의 경우에는 정적으로 할당된 메모리 영역
- 그래서, 크기가 몇 byte 인지 정해져 있는 기본형 변수를 저장
- 추가로, 크기가 정해져 있는 참조형 변수의 주솟값도 저장
- Heap의 경우에는 동적으로 할당된 메모리 영역
- 그래서, 크기가 계속 늘어날 수 있는 참조형 변수의 원본을 저장
비트&바이트
- Bit
- Bit(비트)는 0,1 형태의 2진수 데이터로써 컴퓨터가 저장(표현) 할 수 있는 최소 단위
- 정수형 값은 10진수 숫자(0~10범위의 숫자)이며 2진수(0~1범위) Bit로 저장(표현)
- 4개의 Bit로 16진수 숫자(0~F(16) 범위의 숫자)를 2진수(0~1범위) Bit로 저장(표현)
- 1 Byte = 8 Bit
- Byte(바이트)는 8개의 Bit(비트)로 구성
- 1 Byte 내에서 숫자 변수는 Bit 2진수를 10진수로 저장(표현)
- 10진수로는 0~255(2의8승)까지 저장(표현)
- 1 Byte 내에서 문자 변수의 경우만 Bit 2진수를 16진수로 저장(표현)
자동 형변환
📌 변수 타입별 크기 순서
byte(1) → short(2) → int(4) → long(8) → float(4) → double(8)
byte byteNumber = 10;
int intNumber = byteNumber; // byte -> int 형변환
System.out.println(intNumber); // 10
char charAlphabet = 'A';
intNumber = charAlphabet; // char -> int 형변환
System.out.println(intNumber); // A의 유니코드 : 65
intNumber = 100;
long longNumber = intNumber; // int -> long 형변환
System.out.println(longNumber); // 100
intNumber = 200;
double doubleNumber = intNumber; // int -> double 형변환
System.out.println(doubleNumber); // 200.0 (소수점이 추가된 실수출력)
2. 연산자, 조건문, 반복문, 배열, 컬렉션
비트 연산
- Bit의 자릿수를 옮기는 것
- <<(왼쪽으로 자릿수옮기기), >>(오른쪽으로 자릿수옮기기)
- 0, 1 은 2진수 값이기 때문에,
- 자릿수를 왼쪽으로 옮기는 횟수만큼 2의 배수로 곱셈이 연산되는 것과 동일하다.
- 자릿수를 오른쪽으로 옮기는 횟수만큼 2의 배수로 나눗셈이 연산되는 것과 동일하다.
// 3의 이진수값은 11(2). 12의 이진수값은 1100(2).
// (2) 표기는 이 숫자가 이진수값이라는 표식
System.out.println(3 << 2);
// 3의 이진수값인 11(2) 에서 왼쪽으로 2번 옮겨져서 1100(2) 인 12값이 된다.
System.out.println(3 >> 1);
// 3의 이진수값인 11(2) 에서 오른쪽으로 1번 옮겨져서 1(2) 인 1 값이 된다.
복사
얕은 복사
- 주소값만 복사되고 실제 값은 1개로 유지되는 것.
- 대입 연산자 = 사용
깊은 복사
- 진짜 새로운 배열을 똑같이 만들고 싶을 때
- 실제 값을 가지고 있는 배열의 기본형 값을 꺼내서 복사
// 1. clone() 메서드
int[] a = { 1, 2, 3, 4 };
int[] b = a.clone();
// 하지만, clone() 메서드는 2차원이상 배열에서는 얕은 복사로 동작
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
// 2. Arrays.copyOf() 메서드
int[] a = { 1, 2, 3, 4 };
int[] b = Arrays.copyOf(a, a.length); // 배열과 함께 length값도 같이 넣어준다.
}
}
컬렉션
List
- LinkedList는 메모리에 남는 공간 요청해서 여기저기 나누어 실제 값을 담아 놓고, 실제 값이 있는 주소값으로 목록 구성하고 저장함.
- 기본적인 기능은 ArrayList와 동일하지만 값을 나누어 담기 때문에 모든 값을 조회하는 속도가 느림. 대신 값을 중간에 추가하거나 삭제할 때는 속도가 빠름
- 중간에 값을 추가하는 기능이 있음. linkedList.add({추가할 순번}, {추가할 값})
Set
- 순서 없고 중복 없는 배열
- HashSet : 가장 빠르며 순서를 전혀 예측할 수 없음
- TreeSet : 정렬된 순서대로 보관하며 정렬 방법을 지정할 수 있음
- LinkedHashSet : 추가된 순서, 또는 가장 최근에 접근한 순서대로 접근 가능
보통 HashSet 을 쓰는데 순서 보장이 필요하면 LinkedHashSet 을 주로 사용한다.
// Set
// (사용하기 위해선 import java.util.Set; 와 java.util.HashSet; 를 추가.)
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Set<Integer> intSet = new HashSet<Integer>(); // 선언 및 생성
intSet.add(1);
intSet.add(2);
intSet.add(3);
intSet.add(3); // 중복된 값은 덮어쓴다.
intSet.add(3); // 중복된 값은 덮어쓴다.
for (Integer value : intSet) {
System.out.println(value); // 1,2,3 출력
}
// contains()
System.out.println(intSet.contains(2)); // true 출력
System.out.println(intSet.contains(4)); // false 출력
// remove()
intSet.remove(3); // 3 삭제
for (Integer value : intSet) {
System.out.println(value); // 1,2 출력
}
}
}
Map
- key-value 형태로 저장, key 값 기준으로 value 조회 가능.
- key 값은 중복 허용 X
- HashMap : 중복을 허용하지 않고 순서를 보장하지 않음, 키와 값으로 null이 허용
- TreeMap : key 값을 기준으로 정렬 가능. 다만, 저장 시 정렬(오름차순)을 하기 때문에 저장시간이 다소 오래 걸림
// Map
// (사용하기 위해선 import java.util.Map; 를 추가)
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, Integer> intMap = new HashMap<>(); // 선언 및 생성
// 키 , 값
intMap.put("일", 11);
intMap.put("이", 12);
intMap.put("삼", 13);
intMap.put("삼", 14); // 중복 Key값은 덮어쓴다.
intMap.put("삼", 15); // 중복 Key값은 덮어쓴다.
// key 값 전체 출력
for (String key : intMap.keySet()) {
System.out.println(key); // 일,이,삼 출력
}
// value 값 전체 출력
for (Integer key : intMap.values()) {
System.out.println(key); // 11,12,15 출력
}
// get()
System.out.println(intMap.get("삼")); // 15 출력
}
}
3. 클래스, 상속, 인터페이스
가변 길이의 매개변수
void carSpeeds(double ... speeds) {
for (double v : speeds) {
System.out.println("v = " + v);
}
}
- 매개값 , 로 구분하여 개수 상관없이 전달 가능
- carSpeeds(100, 80);
- carSpeeds(110, 120, 150);
this와 this()
this
- 객체 즉, 인스턴스 자신을 표현하는 키워드
- 객체 내부 생성자 및 메서드에서 객체 내부 멤버에 접근하기 위해 사용될 수 있음.
public Car(String model, String color, double price) {
this.model = model;
this.color = color;
this.price = price;
}
- 객체의 메서드에서 리턴 타입이 인스턴스 자신의 클래스 타입이라면 this를 사용하여 인스턴스 자신의 주소를 반환할 수도 있음.
Car returnInstance() {
return this;
}
this()
- 객체 즉, 인스턴스 자신의 생성자를 호출하는 키워드
- 객체 내부 생성자 및 메서드에서 해당 객체의 생성자를 호출하기 위해 사용될 수 있음.
- 생성자를 통해 객체의 필드를 초기화할 때 중복되는 코드를 줄여줄 수 있음.
public Car(String model) {
this(model, "Blue", 50000000); // 세 번째 생성자 호출
}
public Car(String model, String color) {
this(model, color, 100000000); // 세 번째 생성자 호출
}
public Car(String model, String color, double price) {
this.model = model;
this.color = color;
this.price = price;
}
📌 this() 키워드를 사용해서 다른 생성자를 호출할 때는 반드시 해당 생성자의 첫 줄에 작성되어야 함. this() 키워드 이전에 코드 존재 시 오류 발생.
패키지와 import
- 다른 패키지에 존재하는 클래스를 사용하고 싶을 때는
- 클래스의 전체 경로를 적어 사용해주거나
-
package oop.main; public class Main { public static void main(String[] args) { oop.pk1.Car car = new oop.pk1.Car(); car.horn(); // pk1 빵빵 oop.pk2.Car car2 = new oop.pk2.Car(); car2.horn(); // pk2 빵빵 } }
- import로 해당 클래스가 존재하는 패키지를 import 한다.
-
package oop.main; import oop.pk1.Car; public class Main { public static void main(String[] args) { Car car = new Car(); car.horn(); // pk1 빵빵 oop.pk2.Car car2 = new oop.pk2.Car(); car2.horn(); // pk2 빵빵 } }
상속
클래스 간의 관계
📌 클래스 간의 관계를 분석하여 관계 설정을 해줄 수 있다.
- 상속관계 : is - a (”~은 ~(이)다”)
- 포함관계 : has - a (”~은 ~을(를) 가지고 있다”)
- 상속관계 : 고래는 포유류다 👍
- 포함관계 : 고래는 포유류를 가지고 있다…? 🙅♀️
- 자동차는 타이어를 가지고 있다. 👍
- 자동차는 차 문을 가지고 있다. 👍
- 자동차는 핸들을 가지고 있다. 👍
final 클래스와 final 메서드
- 클래스에 final 키워드를 지정하여 선언하면 최종적인 클래스가 됨으로 더 이상 상속할 수 없는 클래스가 된다.
-
public final class Car {} ... public class SportsCar extends Car{} // 오류 발생.
- 메서드에 final 키워드를 지정하여 선언하면 최종적인 메서드가 됨으로 더 이상 오버라이딩할 수 없는 메서드가 된다.
-
public class Car { public final void horn() { System.out.println("빵빵"); } } ... public class SportsCar extends Car{ public void horn() { // 오류 발생. super.horn(); } }
super와 super()
- super
- 부모 클래스의 멤버를 참조할 수 있는 키워드
-
public void setCarInfo(String model, String color, double price) { super.model = model; // model은 부모 필드에 set super.color = color; // color는 부모 필드에 set this.price = price; // price는 자식 필드에 set }
- super()
- 부모 클래스의 생성자를 호출할 수 있는 키워드
- 항상 부모 클래스의 생성자가 먼저 호출됨. 따라서 눈에 보이지 않지만 컴파일러가 super();를 자식 클래스 생성자 첫 줄에 자동으로 추가해 줌.
-
// 자식 클래스 SportsCar 생성자 public SportsCar(String model, String color, double price, String engine) { // this.engine = engine; // 오류 발생 super(model, color, price); this.engine = engine; }
다형성
- 자동 타입 변환
- 부모 타입 변수 = 자식 타입 객체;
- 자식 객체는 부모 객체의 멤버를 상속받기 때문에 부모와 동일하게 취급될 수 있음.
- 다만 부모 클래스에 선언된, 즉 상속받은 멤버만 접근 가능.
-
public class Main { public static void main(String[] args) { // 고래는 포유류이기 때문에 포유류 타입으로 변환될 수 있습니다. Mammal mammal = new Whale(); // 하지만 포유류 전부가 바다에 살고 수영을 할 수 있는 것은 아니기 때문에 // 수영 하다 메서드는 실행 불가 // 즉, 부모 클래스에 swimming이 선언되어있지 않아서 사용 불가능합니다. // mammal.swimming(); // 오류 발생 // 반대로 모든 포유류가 전부 고래 처럼 수영이 가능한 것이 아니기 때문에 타입변환이 불가능합니다. // 즉, 부모타입의 객체는 자식타입의 변수로 변환될 수 없습니다. // Whale whale = new Mammal(); // 오류 발생 mammal.feeding(); } }
- 강제 타입 변환
- 자식 타입 변수 = (자식 타입) 부모 타입 객체;
- 부모 타입 객체는 자식 타입 변수로 자동으로 타입 변환되지 않음.
- 무조건 강제 타입 변환을 할 수 있는 것은 아니다!
- 자식 타입 객체가 부모 타입으로 자동 타입 변환된 후 다시 자식 타입으로 변환될 때만 강제 타입 변환이 가능
- 부모 타입 변수로는 자식 타입 객체의 고유한 멤버를 사용할 수 없기 때문에 사용이 필요한 경우가 생겼을 때 강제 타입 변환을 사용
public class Main { public static void main(String[] args) { // 자동 타입변환된 부모타입의 변수의 자식 객체 Mammal mammal = new Whale(); mammal.feeding(); // 자식객체 고래의 수영 기능을 사용하고 싶다면 // 다시 자식타입으로 강제 타입변환을 하면된다. Whale whale = (Whale) mammal; whale.swimming(); Mammal newMammal = new Mammal(); // Whale newWhale = (Whale) newMammal; } }
- instanceof
- 해당 클래스 객체의 원래 클래스명 체크
- {대상 객체} instance of {클래스 이름}
// 다형성 class Parent { } class Child extends Parent { } class Brother extends Parent { } public class Main { public static void main(String[] args) { Parent p = new Parent(); System.out.println(p instanceof Object); // true 출력 System.out.println(p instanceof Parent); // true 출력 System.out.println(p instanceof Child); // false 출력 Parent c = new Child(); System.out.println(c instanceof Object); // true 출력 System.out.println(c instanceof Parent); // true 출력 System.out.println(c instanceof Child); // true 출력 } }
추상 클래스
- 미완성된 설계도
- abstract 키워드 사용
- 추상 메서드를 하나라도 가지고 있는 클래스는 추상 클래스
- 추상 메서드가 없어도 추상 클래스로 선언 가능
- 추상 클래스는 여러 개의 자식 클래스들에서 공통적인 필드나 메서드를 추출해서 만들 수 있음.
- 추상 메서드는 아직 구현되지 않은 미완성된 메서드. 정의만 할 뿐, 실행 내용 갖고있지 않음.
- 상속받은 클래스에서 추상 메서드는 반드시 오버라이딩!
인터페이스
📌 인터페이스의 멤버
- 모든 멤버 변수는 public static final
- 모든 메서드는 public abstract
- 생략되는 제어자는 컴파일러가 자동으로 추가해준다.
📌 인터페이스의 구현
- 인터페이스의 추상 메서드는 구현될 때 반드시 오버라이딩 되어야 한다.
- 만약 인터페이스의 추상 메서드를 일부만 구현해야 한다면 해당 클래스를 추상 클래스로 변경.
📌 인터페이스의 상속
- 인터페이스 간의 상속이 가능.
- 인터페이스 간의 상속은 implements 가 아니라 extends 키워드를 사용.
- 인터페이스는 클래스와는 다르게 다중 상속이 가능.
public class Main implements C {
@Override
public void a() {
System.out.println("A");
}
@Override
public void b() {
System.out.println("B");
}
}
interface A {
void a();
}
interface B {
void b();
}
interface C extends A, B { }
- 인터페이스 C가 A, B를 상속받고 인터페이스 C를 구현하므로 상속받은 a(), b() 메소드가 모두 오버라이딩 되어야 한다.
디폴트 메서드와 static 메서드
- 디폴트 메서드 : 추상 메서드의 기본적인 구현 제공.
- 메서드 앞에 default 키워드 붙이며 블럭{} 존재.
- static 메서드 : 사용 방법 동일
-
public class Main implements A { @Override public void a() { System.out.println("A"); } public static void main(String[] args) { Main main = new Main(); main.a(); // 디폴트 메서드, 재정의 없이 바로 사용 가능. main.aa(); // static 메서드 aaa() 호출 A.aaa(); } } interface A { void a(); default void aa() { System.out.println("AA"); } static void aaa() { System.out.println("static method"); } }
다형성
- 자동 타입 변환
- 인터페이스 변수 = 구현객체;
- 강제 타입 변환
- 구현 객체 타입 변수 = (구현 객체 타입) 인터페이스 변수;
public class Main {
public static void main(String[] args) {
// A 인터페이스에 구현체 B 대입
A a1 = new B();
a1.a();
// a1.b(); // 불가능
System.out.println("\\nB 강제 타입변환");
B b = (B) a1;
b.a();
b.b(); // 강제 타입변환으로 사용 가능
System.out.println();
// A 인터페이스에 구편체 B를 상속받은 C 대입
A a2 = new C();
a2.a();
//a2.b(); // 불가능
//a2.c(); // 불가능
System.out.println("\\nC 강제 타입변환");
C c = (C) a2;
c.a();
c.b(); // 강제 타입변환으로 사용 가능
c.c(); // 강제 타입변환으로 사용 가능
}
}
interface A {
void a();
}
class B implements A {
@Override
public void a() {
System.out.println("B.a()");
}
public void b() {
System.out.println("B.b()");
}
}
class C extends B {
public void c() {
System.out.println("C.c()");
}
}
'내배캠 > TIL' 카테고리의 다른 글
N의 약수 찾기 알고리즘 (0) | 2024.07.25 |
---|---|
Java 문법 종합반 4~5주 (0) | 2024.07.24 |
SQL 정리 2 (7) | 2024.07.22 |
SQL 정리 1 (1) | 2024.07.22 |
24. 07. 19 - 웹 기초 특강 (0) | 2024.07.19 |