JRE(java runtime environment)와 JVM(java virtual machine)과, Javac 등 컴파일러, 디버거, 자바 애플릿 등 응용프로그램 개발 도구 포함
OS에 관계 없이 개발 가능 ➡️ OS에 맞는 JDK만 설치
3) 이클립스 원리
JDK 개발 툴
프로그래밍시 컴파일 및 디버깅 등 통합 개발 환경(IDE, Integrated Development Environment) 필요
4) 메모리 구조
Static
Heap
Stack
📕 JAVA 개념
1. 자료형
1) 종류
boolean (1bit) : 참/거짓 표현
int (4Byte) : 정수 표현
double (8Byte) : 실수 표현 (소수점 이하의 자리)
char (2Byte) : 문자 표현
2) 클래스 자료형 (Beans)
개발자가 만드는 custom 자료형
Beans: 여러가지 데이터 자료형을 가지고 있는 클래스
Beans 동적 할당 : heap 공간에 해당 클래스가 가지고 있는 모든 데이터 할당 (static 제외)
참조 변수 : 주소, 컴파일시 크기가 정해져 있지X (실행시 정해짐)
일반 변수 : 값, 컴파일시 크기 정해짐
class MyVar {
staitc int n1 = 10;
static char c1 = 'A';
}
class Note {
int num = 1;
}
public class VarEx03 {
static int num = 100;
public static void main(String[] args) {
System.out.println(MyVar.n1);
System.out.println(VarEx03.num); //생략 해서 num 가능
Note note1; // 레퍼런스 변수 (크기가 정해져 있지 않은 것)
int age; // 일반 변수 (크기가 정해져 있는 것)
note1 = new Note(); // heap 공간
age = 10; // main stack 공간
}
}
연습 문제1) 붕어빵을 표현하기 위한 커스텀 자료형(heap)을 만드시오
클래스명 : 붕어빵
필드 : 붕어빵의 가격=1000, 맛=달콤함, 색깔 = 노란색
class bread {
int price = 1000;
String taste = "달콤함";
String color = "노란색";
}
public class Example {
public static void main(String[] args) {
bread n = new bread();
}
}
연습 문제2) 메모장 코드를 작성하시오
파일명 : HelloWorld.java
폴더 : C:₩javawork₩ch1₩HelloWorld.java
출력 : HelloWorld
class Note {
String memo = "";
}
public class HelloWorld {
public static void main(String[] args) {
Note m = new Note();
m.memo = "HelloWorld";
System.out.println(m.memo);
}
}
➡️ 컴파일 :javac 파일명.확장자 ➡️ 실행
(패키지 있을 경우) 상위 폴더 이동java 패키지명.클래스명
(패키지가 없을 경우)java 클래스명
2. java 실행원리
1) java 모든 코드는 클래스 내부에 존재해야 함
2) java는 실행 전에static키워드를 가지고 있는 부분을static메모리 공간에 로드함
public class Method01 {
static void start () {
System.out.println("Hi");
System.out.println("Exit");
}
public static void main(Stiring[] args) {
Method01.start(); // 메서드 실행
}
}
2) 메서드 Stack 메모리
.class 로드 ➡️ static 찾기 ➡️ main 메서드 실행 ➡️ main 큐 오픈 ➡️ stack의 main메서드 영역 생성
main큐 : main 코드 순차적 실행
static의 main메서드 : 내부 메서드
3) 지역변수 / 전역변수
지역변수 : stack
메서드가 실행될 때 메모리에 할당, 메서드가 종료될 때 메모리에서 삭제
가장 생명 주기가 짧음
전역변수 : static, heap
heap : new 할 때 메모리에 할당, 더이상 참조하지 않을 때 메모리 삭제
static : main메서드 실행되기 전에 메모리 할당, main 메서드 종료되면 메모리 삭제
public class StackEx {
static int num = 20; // 전역변수 (static)
int value = 50; // 전역변수 (heap)
static void a() { // a 메서드의 스택 영역에 저장
int n1 = 1; // 지역변수 (stack)
System.out.println(n1);
// 오류 발생 (static 공간 할당은 main 메서드 실행 전에만 가능)
// static int n2 = 2;
}
public static void main(String[] args) {
a();
System.out.println(sum);
StackEx s = new StackEx();
System.out.println(s.value);
}
}
4) 메서드의 리턴
void : 리턴(돌려주지) 않음
public class MethodEX {
static int add() {
int sum = 5 + 6;
return sum;
}
public static void main(String[] args) {
int result = add(); // add 메서드 호출
System.out.println(result);
}
}
5) 메서드 변수 활용과 연산자
대입 연산자
사칙 연산자 : +, -, /, *
비교 연산자 : ==, !=, >, >=, <, <= (리턴의 결과가 TRUE/FALSE)
논리 연산자 : &&(and), ||(or)
조건 연산자 : 삼항 연산자(조건 ? true : false)
public class OperEx {
public static void main(String[] args) {
int n1 = 10; // 대입 연산자
int n2 = 20;
int sum = n1 + n2; // 사칙 연산자
String s1 = "나의 나이는 ";
int age = 27;
System.out.println(s1 + age); // 결합
}
}
6) 캐스팅
업캐스팅 : 큰 변수 공간에 작은 변수 저장 (묵시적으로 발생)
다운캐스팅 : 작은 변수 공간에 큰 변수 저장 (명시적 형변환)
단점 : 데이터 유실
public class CastEx{
int n1 = 100;
double d1 = n1; // 업캐스팅
double d2 = 100.8;
int n2 = (int)d2 // 다운캐스팅 (명시적 형변환)
}
4. 패키지 / 라이브러리
1) package
패키지는파일 시스템의 폴더형태로 패키지 만들어서 클래스 저장/관리
클래스를 유일하게 구분하는 식별자 역할 (클래스 = 패키지.클래스명)
2) import
다른 패키지명에 있는 클래스를 찾지 못할 경우 사용
static import는 기존과 다르게 메소드나 변수를 패키지, 클래스명 없이 접근 가능
public void divide() { System.out.println("나누기 메서드"); multi(); // 같은 클래스라 접근 가능 } }
public void minus() { System.out.println("빼기 메서드"); }
public class Cal {
App.java
pacakge test2;
import test.Cal;
public class App {
public static void main(String[] args) {
Cal c = new Cal();
// 오류 - default 접근 제어자의 경우 실행X
// c.add();
// 성공 - public 접근 제어자의 경우 실행O
c.minus();
// 오류 - private 접근 제어자의 경우 같은 클래스에서만 가능
// c.multi();
}
}
📖 참고 📖 public static void main(String[] args)
JVM이 main메서드를 찾으려면 public이 필요 (공개)
JVM이 main메서드를 찾으려면 static이 필요 (메모리에 저장)
main 메서드만 return 타입 허용X
메서드의 이름 main
4) 라이브러리
다른 프로그램에서 라이르버리 안에 포함된 기능 활용
생성 java 프로젝트 :Export➡️jar➡️JAR file
Runnable jar file: main 메소드 가지고 있는 실행 파일
JAR file: 그 외 파일
사용 java 프로젝트 :Build path설정 ➡️Configure Build path➡️library➡️ Classpath의Add External jARs
package test2;import test.Cal;importlib.lib2;publicclassApp{publicstaticvoidmain(String[] args){Cal c =newCal(); c.minus(); lib2 l =newlib2();// 라이브러리 l.lib3();}}
5. 클래스
1) 클래스
java는 파일명 = 클래스명
여러가지 특징(상태)를 가지고 있음
다양한 자료형 가질 수 있음
2) 생성자
클래스 만들어서 상태를 정의할 때는 값을 초기화하지 않음
new를 이용해 다른 상태 정의 (다양성)
생성자 = 메서드
Cat c1 = new Cat();
Cat : 커스텀 타입 (사용자가 만든 타입)
c1 : heap 공간을 가리키는 주소
new : 메모리에 할당(heap) ➡️String name, String color
Cat() : 생성자 호출 ➡️ 상태 초기화 목적
default 생성자
개발자가 작성하지 않아도 됨 (생략 가능한 코드)
직접 생성자를 따로 구현하게 되면, 디폴트 생성자 생략 불가능
.java ➡️ .class 컴파일할 때 자바가 자동으로 코드 생성
Cat.java
pulbic class Cat {
String name;
String color;
public Cat() { // default 생성자(메서드)
}
public Cat(String n, String c) { // 생성자(메서드)
System.out.println("고양이 탄생");
}
}
CatApp.java
public class CatApp {
public class void main(String[] args) {
Cat c1 = new Cat();
}
}
3) this
자기 자신의 힙 공간을 가리킴
this.멤버변수: 매개변수와 객체 자신이 가지고 있는 변수의 이름이 같은 경우 구분하기 위해 사용
this(멤버변수): 생성자 내에서 다른 생성자 호출
Food.java
public class Food {
String name;
int price;
public Food(String name, int price) {
this.name = name;
this.price = price;
}
void myPrint() {
System.out.println(name + " 가격은 " + price + "원 입니다.");
}
}
FoodApp.java
public class FoodApp {
public static void main(String[] args) {
Food f1 = new Food("치킨", 2000);
Food f2 = new Food("피자", 5000);
f1.myPrint();
f2.myPrint();
}
}
4) 클래스, 오브젝트, 인스턴스
클래스 : 유사한 특징을 지닌 객체를 묶어놓은 집합 (설계도)
오브젝트 : 객체를 사용할 수 있도록 실체화 (구현할 대상)
OOP : 오브젝트 지향 프로그래밍
인스턴스 : new를 통해 heap 공간에 저장된 객체 (구현된 구체적인 실체)
상태(필드)는 행위(메서드)에 의해서 변함
상태 (필드)
행위 (메서드)
class Player{
String name;
// int thirsty;
private int thirsty; // 외부 클래스에서 접근 불가능
public Player(String name, int thirsty) {
this.name = name;
this.thirsty = thirsty;
}
// 행위 = 메서드
void water() {
this.thirsty = this.thirsty - 50;
}
int thirstyState() {
return this.thirsty;
}
}
public class OOPEx {
public static void main(String[] args) {
Player p1 = new Player("홍길동", 100);
// 1. 잘못된 시나리오 (객체지향에 맞지 않음)
p1.thirsty = 50;
System.out.println(p1.thirsty);
// 2. 잘못된 시나리오 (상태가 행위를 변경함)
p1.water();
System.out.println(p1.thirsty);
// 3. 올바른 시나리오
p1.water();
System.out.println(p1.thirstyState());
}
}
📖 참고 📖 final 변수
1번만 할당할 수 있는 변수 (수정 불가)
객체를 참조하고 있다면, 그 객체의 상태가 바뀌어도 매번 동일한 내용을 참조함
6. 상속
1) 상속
추상화
상태, 행위를 물려 받음
타입 일치가 가능
상태와 행위를 물려받을 수 있지만, 타입이 일치되어야 함
부모 클래스와 겹치지 않는 상태(필드)만 상속됨
부모 클래스와 변수 이름이 동일할 경우, 자신의 클래스 변수 사용
📖 참고 📖 import
다른 패키지 안의 클래스를 사용하기 위해서는 클래스 이름 앞에 패키지 붙여야 함
import를 통해 패키지를 매번 입력하는 번거로움 해소
상태, 행위를 가져와서 사용
타입 일치가 되지X
class Engine {
int power = 2000;
}
class Car { // 자동차는 엔진이 아니기 때문에 상속 불가!
Engine e; // composition (결합)
public Car(Engine e) {
this.e = e;
}
}
class Hamburger {
String name = "hamberger";
String f1 = "양상추";
String f2 = "패티";
}
class CheeseHamburger extends Hamburger { // 상속 (Composition 필요x)
String name = "Cheesehamburger";
}
class ChickenHamburger { // 결합도 가능
Hamberger h;
public ChickenHamburger(Hamberger h) {
this.h = h;
}
}
public class OOPEx {
public static void main(String[] args) {
Engine e1 = new Engine();
Car c1 = new Car(e1);
System.out.println(c1.e.power);
CheeseHamburger h1 = new CheeseHamburger(); // 상속 ➡️ (햄버거, 치즈)
// Hamberger h1 = new CheeseHamburger(); // 동일 ➡️ (햄버거, 치즈)
// CheeseHamberger h1 = new Hamberger(); // X ➡️ (햄버거)
System.out.println(h1.name);
System.out.println(h1.f1);
Hamberger h1 = new Hamberger(); // 컴포지션
ChickenHamburger ch1 = new ChickenHamburger(h1);
System.out.println(ch1.h.name);
}
}
2) 다형성
하나의 객체가 여러가지 타입을 가질 수 있음
부모 클래스의 참조 변수로 자식 클래스의 인스턴스를 참조할 수 있음
Hamberger h1 = new Hamberger(); // 허용 ➡️ (햄버거)
CheeseHamburger h1 = new CheeseHamburger(); // 허용 ➡️ (햄버거, 치즈)
Hamberger h1 = new CheeseHamburger(); // 허용 ➡️ (햄버거, 치즈)
CheeseHamberger h1 = new Hamberger(); // 오류
3) 오버로딩
함수 이름이 동일하지만, 다른 함수로 인식하게 함
매개변수의 개수 또는 타입이 다르면, 같은 이름을 사용해서 메소드를 정의 가능
유닛이 많을 경우 동일한 메서드를 많이 생성해야 해서 불편
조건
메소드의 이름이 같고, 매개변수의 개수나 타입이 달라야 함
'리턴 값'만 다른 것은 오버로딩을 할 수 없음
4) 오버라이딩
부모 클래스로부터 상속받은 메소드를 자식 클래스에서 재정의하는 것
오버라이드
동적바인딩
부모와 자식이 동일한 이름의 메서드가 있을 경우 부모의 메서드를 무효화함
조건
자식 클래스에서는 오버라이딩하고자 하는 메소드의 이름, 매개변수, 리턴 값이 모두 같아야 함
7. 추상클래스와 인터페이스
1) 추상클래스
추상메서드를 1개 이상 가지고 있는 클래스 (다형성)
일반메서드도 가질 수 있음
반드시 사용되어야 하는 메소드를 추상 클래스에 추상 메소드로 선언하여, 상속받는 클래스는 반드시 재정의
new 사용할 수 없음
2) 추상메서드
자식클래스에서 반드시 오버라이딩 해야만 사용할 수 있는 메소드
목적 : 자식 클래스가 반드시 추상 메소드를 구현하도록
선언부만 존재 (구현부-몸체 {} 없음)
abstract class Animal { // 추상클래스
abstract void speak(); // 추상메서드 (몸체{} 없음)
void Hello() {
System.out.println("Hi");
}
}
class Dog extends Animal {
void speak() { // 오버라이드 (Animal의 speak() 무효화됨)
System.out.println("멍멍");
}
}
class Cat extends Animal {
void speak() {
System.out.println("야옹");
}
}
public class OOPEx {
public static void main(String[] args) {
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.speak(); // 동적바인딩
Animal aa = new Animal(); // 오류 (추상클래스)
}
}
3) 인터페이스
클래스들이 필수로 구현해야 하는 추상 자료형
행위에 대한 제약을 줌
추상 메서드와 상수로 구성
(일반) 클래스의 경우 인터페이스 안의 메서드 반드시 모두 구현
(추상) 클래스의 경우 메서드 구현하지 않으면, 자식 클래스로 구현을 위임 (자식 클래스는 반드시 메서드 구현)
Interface MoveAble {
// public abstact 생략되어 있음
void up();
void down();
void left();
void right();
}
abstract class 사나운동물 implements MoveAble{ // 추상 클래스
abstract void 공격(); // 미완성된 메서드
@Override
public void up() {
System.out.println("위");
}
}
abstract class 온순한동물 implements MoveAble{
abstract void 채집();
@Override
public void up() {
System.out.println("위");
}
}
class 호랑이 extends 사나운동물{
@Override
void 공격() {
System.out.println("이빨로 공격");
}
}
class 원숭이 extends 온순한동물{
@Override // 오버라이드 어노테이션
void 채집() {
System.out.println("바나나 채집");
}
}
public class OOPEx{
staitc void 조이스틱(온순한 동물 a1) {
a1.채집();
a1.up();
a1.down();
a1.left();
a1.right();
}
static void 조이스틱(사나운 동물 a1) { // 오버로딩
a1.공격();
a1.up();
a1.down();
a1.left();
a1.right();
}
public static void main(String[] args) {
원숭이 a1 = new 원숭이();
조이스틱(a1);
호랑이 a2 = new 호랑이();
조이스틱(a2);
}
}
📖 참고 📖 어노테이션
JVM이 실행시에 분석해서 해당 메소드 존재하는지 등 확인
8. SRP와 DIP
1) SRP (Single Responsobillity Principle)
단일 책임 원칙
객체는 단 하나의 책임(기능)만 가져야 함
2) DIP (Dependency Inversion Principle)
의존성 역전 원칙
객체에서 어떤 class를 참조해야 할 경우, 직접 참조하지 않고 상위 요소(추상 클래스/인터페이스) 참조해서 사용
📖 참고 📖 CI (Continuous Integretion)
지속적 통합
특징
소프트웨어 개발의 위험성을 줄임
수정이 편함
🗒️ 예시 : 식당 프로세스
추상클래스
홀직원
청소()
종업원
홍길동, 임꺽정
서빙()
주문() : 키오스크(기계)
캐셔
김유신, 이몽룡
계산() : 현금계산/카드계산
정산() : 수기정산/계산기 정산
요리사
장보고, 이순신
요리 ()
종업원은 요리사에게 의존관계 있음
인터페이스
종업원
talk() : 종업원/캐셔만 가능, 요리사는 불가능
interface CanAble {
void talk(); // public abstract 생략
}
abstract class 홀직원 implements CanAble{
abstract void 청소();
public void talk() {
System.out.println("손님과 대화");
}
}
abstract class 종업원 extends 홀직원 {
void 서빙() {
System.out.println("서빙");
}
void 주문() { // 키오스크로 변경된 경우 '주문()' 메소드 삭
System.out.println("주문");
}
}
abstract class 캐셔 extends 홀직원 {
void 정산() {
System.out.println("정산"); // 수기 -> 계산기
}
void 계산() {
System.out.println("계산"); // 현금 -> 카드
}
}
abstract class 요리사 {
abstract void 요리();
}
class 홍길동 extends 종업원 {
요리사 j; // 요리사 의존 관계 (의존성 역전 원칙)
@Override
void 청소() {
System.out.println("화장실 청소!");
}
}
class 임꺽정 extends 종업원 {
@Override
void 청소() {
System.out.println("주방 청소!");
}
}
class 김유신 extends 캐셔 {
요리사 j;
@Override
void 청소() {
System.out.println("홀 청소!");
}
}
class 이몽룡 extends 캐셔 {
@Override
void 청소() {
System.out.println("테이블 청소!");
}
}
class 장보고 extends 요리사 {
@Override
void 요리() {
System.out.println("양식");
}
}
class 이순신 extends 요리사 {
@Override
void 요리() {
System.out.println("한식");
}
}
public class OOPEx {
public static void main(String[] args) {
}
}
📕 자료구조
1. 배열
1) 개념
연관된 데이터를 저장하기 위한 자료 구조
연속된 자료형(int, char)등 공간이 필요
2) 특징
데이터 읽기가 빠름
시작 번지의 주소만 저장
0번지부터 시작함
3) 선언
1차원 배열 : int[] nums = new int[3];
2차원 배열 : int[][] nums = new {{1,2,3}, {4,5,6}};
interface RemoconAble() {
public void 초록버튼();
public void 빨간버튼();
}
class Samsung implements RemoconAble{
void 초록버튼() {
System.out.println("전원 켜짐");
}
void 빨간버튼() {
System.out.println("전원 꺼짐");
}
}
public class Example{
// 삼성 리모콘 2개 만들기 (new)
Samsung[] s = new Samsung[2];
s[0] = new Samsung();
s[1] = new Samsung();
}
2. 반복문
1) 개념
동일한 코드를 자동적으로 반복시켜 주는 것
2) for문
시작값; 조건값; 증감값;
for (int i = 1; i < 101; i=i+1)
int i = 1 : 초기화 (for문 실행시 1번만 실행됨)
i < 101 : 조건문 (처음부터 끝까지 계속 실행)
i=i+1 : 증감식 (for문 한번 실행된 이후 부터 계속 실행)
3) while문
종료가 없음
while(실행조건)
3. 조건문
1) 개념
각 조건에 해당하여 코드를 실행시킴
2) if문
조건이 일치할 경우에만 코드 실행
if(조건)
📕 제네릭 및 스레드
1. Object 클래스
1) 개념
모든 클래스의 부모
타입을 다운캐스팅 필요
class Dog {
String name = "강아지";
}
public class Object{
public static void main(String[] args) {
Object o1 = new Dog();
Dog d1 = (Dog)o1; // 타입 다운캐스팅
}
}
2. 제네릭 클래스
1) 개념
데이터 형식에 의존하지 않고, 하나의 값이 여러 다른 데이터 타입들을 가질 수 있도록 하는 방법
클래스 내부에서 지정하는 것이 아닌 외부에서 사용자에 의해 지정되는 것
2) 특징
다운캐스팅 필요X
class 호랑이 {
String name = "호랑이";
}
// 기존
class 동물 {
Object data;
}
// 제네릭 변경
class 동물<T> {
T data;
}
public class Generic {
public static void main(String[] args) {
// 기존
동물 s1 = new 동물();
s1.data = new 호랑이();
System.out.println(s1.data.name); // 오류 (s1이 동물을 바라보고 있어서)
호랑이 h1 = (호랑이)s1.data;
System.out.println(h1.name);
// 제네릭 변경
동물<호랑이> s1 = new 동물<>();
s1.data = new 호랑이(); // T data가 null 이어서 이 형식 필요
System.out.println(s1.data.name); // 성공
}
}
3) 와일드카드
<\? extends object>
오브젝트를 상속하고 있는 모든 클래스 중에 하나여도 됨
private 변수에 대한 메서드 생성 :Source➡️Generate Getters and Setters
abstract class animal { // 동물 하나로 묶기
abstract String getName();
}
class 호랑이 {
private String name = "호랑이"; // generate get and set
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class 동물<T> {
private T data;
public T getData() {
return data;
public void setData(T data) {
this.data = data;
}
}
public class Generic {
static 동물<? extends animal> 행동 (int time) { // extends Object 생략 가능
if (time == 9) {
호랑이 h1 = new 호랑이();
동물<호랑이> a1 = new 동물<>();
a1.setData(h1);
return a1;
}
}
public static void main(String[] args) {
동물<?> a1 = 행동(9);
동물<? extends animal> a1 = 행동(9);
System.out.println(a1.getName().setName());
}
}
3. 컬렉션
1) 개념
컬렉션은 배열과 유사 (연속된 공간 없어도 할당 가능)
동적으로 공간 사용
2) 특징
기본 자료형(값) : Int, double, char, boolean
레퍼런스 자료형(주소) : String, 커스텀자료형(클래스)
래핑 클래스 : Integer, Double, Character, Boolean
ArrayList<Integer> c1 = new ArrayList<>();
c1.add(1);
4. 스레드
1) 개념
프로세스안에서 실질적으로 작업을 실행하는 단위
자바에서는 JVM(Java Virtual Machine)에 의해 관리
context switching으로 부하가 발생
2) 동기/비동기
동기 : 직렬적으로 태스크(task)를 수행하고, 태스크는 순차적으로 실행되며 어떤 작업이 수행 중이면 다음 작업은 대기 (일의 순서O)
비동기 : 병렬적으로 태스크를 수행하고, 태스크가 종료되지 않은 상태라 하더라도 대기하지 않고 다음 태스크를 실행 (일의 순서X)
class SubTread implements Runnable {
// 자바의 서브스레드
@Override
public void run() {
for (int i = 1; i < 10; i++) {
try {
Thread.sleep(1000);
System.out.println("서브 스레드 : " + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Thread {
// 자바의 메인 스레드
public static void main(String[] args) {
SubThread st = new SubThread();
Thread t1 = new Thread(st); // 타겟 선정
t1.start(); // run 메서드 실행
for (int i = 1; i < 10; i++) {
try {
Thread.sleep(1000); //ms 단위
System.out.println("메인 스레드 : " + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5. 예외처리
1) 개념
컴파일시/런타임시 예외 처리
컴파일 예외 : JAVA가 확인
런타임 예외 : 개발자가 확인
InterruptedException :Thread.sleep();하는 동안 interrupt 예외 발생 가능
e.getMessage() : 오류 로그 (파일로 남겨두기)
e.printStackTrace() : 오류를 추적해주는 로그
2) try/catch문
catch : try(시도)하다가 예외가 발생하면 어떻게 처리할지 정의하는 영역
class 총 {
void shoot() {
System.out.println("총 발사");
}
}
public class Exception{
public static void main(String[] args) {
// 컴파일 에러
Thread.sleep(1000); // try/catch문 필요
// 컴파일 예외
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace(); // 오류를 추적해주는 로그
}
// 런타임 에외
int[] nums = {1, 2, 3};
try {
System.out.println(nums[3]); // 강제 종료
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println(e.getMessage()); // Index 3 out of bounds for length 3 (로그 파일로 남기기)
}
System.out.println("메인 스레드 종료");
// 런타임 예외
총 S = null;
try {
s.shoot();
} catch(RuntimeException e) {
System.out.println("에러메시지");
s = new 총();
s.shoot();
}
}
}
6. String Constant Pool
1) 개념
문자열 상수 공간
string : 기본형이 아닌 참조형 변수
string 객체 생성 방법
String literal "" 사용 (내용이 같으면 동일한 주소)
new 연산자 사용 (내용이 같아도 다른 객체)
2) 특징
같은 문자열이면 같은 공간 공유 (메모리 효율⬆️)
문자열을 자주 변경하면 새로운 공간이 너무 많이 할당됨
public class String{
public static void main(String[] args) {
String s1 = new String("바다");
String s2 = new String("바다");
System.out.println(s1 == s2); // false (주소 일치X)
String s3 = "바다";
String s4 = "바다";
System.out.println(s3 == s4); // true (주소 일치)
}
}
📖 참고 📖 string.equals("문자열");
문자열 비교시 값 자체와 주소 모두 비교
string literal과 new 연산자 상관 없이 비교 가능
String s1 = new String("바다");
System.out.println(s1.equals("바다"); // true
🗒️ 예시 :
📖 참고 📖
✏️
⬆️⬇️➡️
📕 버퍼와 소켓통신
1. 버퍼(Stream)
1) 버퍼
임시 저장 공간
2) Stream
Byte 형태로 데이터를 운반하는데 사용되는 연결 통로
데이터를 통신하기 위해 인코딩한 후 전달 ➡️ 디코딩을 통해 화면에 출력
InputStream(입력), OutputStream(출력)
단방향 통신만 가능해서, 하나의 스트림으로 입력과 출력을 동시에 처리X
큐의 FIFO 구조
3) 과정
1️⃣ : 키보드 A를 인코딩해서 010000000으로 컴퓨터 에전송
2️⃣ : ByteStream으로 흘러들어감 (Input)
3️⃣ : read() 메서드로 01000000 -> 65로 디코딩
4️⃣ : int 65(숫자)를 char A (문자)로 부호화 시킴
import java.io.InputStream;
import java.io.IOException;
public class Stream{
public static void main(String[] args) {
InputStream in = System.in; // System.in : 키보드에 연결된 스트림
try {
int data = in.read(); // 디코딩
System.out.println((char)data); // 부호화
} catch (Exception e) {
e.printStackTrace();
}
}
}
4) BufferedReader
숫자 ➡️ 부호화
문자를 가변적으로 받음 (배열의 공간 낭비가 없음)
5) BufferedReader 특징
양끝단의 버퍼의 크기를 맞춤
버퍼가 가득차면 자동 전송
import java.io.InputStream;
import java.io.InputStreamReader
public class Stream{
public static void main(String[] args) {
InputStream in = System.in;
InputStreamReader ir = InputStreamReader(in); // 65 -> A로 부호화
try {
char[] data = new char[3]; // 여러개 입력 가능
ir.read(data); // 캐스팅 없이 디코딩 가능
System.out.println(data);
} catch (Exception e) {
e.printStackTrace();
}
BufferedReader br = new BufferedReader(ir);
try {
String data = br.readLine(); // 키보드로 입력
System.out.println(data); // 화면에 출력
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
2. 소켓통신 (서버/클라이언트)
1) 소켓통신
포트간 ByteStream을 이용하여 데이터를 주고 받는 것
ip주소를 이용하여 서버 소켓과 클라이언트 소켓 연결 (포트 통신)
2) 포트
데이터를 주고 받는 통로
0 ~ 65535(2^16, 2Byte)
0 ~ 1023포트 사용X
3) 소켓 통신 과정
1️⃣ : 서버 소켓 시작
2️⃣ : 서버 소켓 생성 완료
ServerSocket serverSocket;
Socket socket;
3️⃣ : 클라이언트 접속 대기중
socket = serverSocket.accept();
4️⃣ : 클라이언트 연결 완료
socket.getInputStream(); // 서버 소켓과 클라이언트 소켓 연결하는 ByteStream
ServerFile.java
importjava.net.ServerSocket;importjava.net.Socket;publicclassServerFile{ServerSocket serverSocket;// 클라이언트 연결을 받는 소켓Socket socket;// 실제 통신을 하는 소켓BufferedReader br;publicServerFile(){System.out.println("1. 서버 소켓 시작");try{ServerSocket=newServerSocket(10000);System.out.println("2. 서버 소켓 생성 완료 - 클라이언트 접속 대기중 "); socket = serverSocket.accept();// 클라이언트 접속 대기중System.out.println("3. 클라이언트 연결 완료"); br =newBufferedReader(newInputStreamReader(socket.getInputStream()));// ByteStream 연결String msg = br.readLine();System.out.println("4. 클라이언트로부터 받은 메시지 : "+ msg);}catch(Exception e){System.out.println("서버 소켓 에러 발생 : "+ e.getMessage());}}publicstaticvoidmain(String[] args){newServerFile();}}
ClientFile.java
importjava.net.Socket;importjava.io.BufferedWriter;importjava.io.InputStreamReader;importjava.io.OutputStreamWriter;publicclassClientFile{Socket socket;BufferedWriter bw;BufferedReader br;publicClientFile(){System.out.println("1. 클라이언트 소켓 시작");try{ socket =newSocket("localhost",10000);// 서버 소켓의 accept() 메서드 호출 (localhost 아닌 경우 ip주소 입력)System.out.println("2. 버퍼(write) 연결 완료"); bw =newBufferedWriter(newOutputStreamWriter(socket.getOutpputStream());// 키보드 연결System.out.println("3. 키보드 스트림 + 버퍼(read) 연결 완료"); br =newBufferedReader(newInputStreamReader(System.in));System.out.println("4. 키보드 메시지 입력 대기중");String keyboardMsg = br.readLine(); bw.write(keyboardMsg +"\n");// 메시지 끝을 알려줘야 함 (\n 필요) bw.flush();// 버퍼 비워주기}catch(Exception e){System.out.println("클라이언트 소켓 에러 발생함 : "+ e.getMessage());}}publicstaticvoidmain(String[] args){newClientFile();}}
📖 참고 📖 소켓 2개인 이유
✏️ 클라이언트 소켓에서 ip주소(10000)으로 서버소켓에 요청시
1️⃣ : 서버소켓 실행
2️⃣ : 클라이언트 소켓 실행
3️⃣ : 연결 시도
4️⃣ : 서버소켓은 연결만 받는 용도로, 별도의 통신용 소켓 생성 (1024 ~ 65535중 사용하지 않는 포트 번호를 랜덤으로 선정)
5️⃣ : ByteStream 연결
✏️ 서버 소켓
ServerSocket serverSocket;
Socket socket;
3. 소켓 통신 스레드 적용
1) 수정 사항1️⃣
서버 : 계속 메시지를 read하도록 while문으로 수정
클라이언트 : 계속 키보드에서 input과 write 대기하도록 while문으로 수정
ServerFile2.java
importjava.net.ServerSocket;importjava.net.Socket;publicclassServerFile2{ServerSocket serverSocket;// 클라이언트 연결을 받는 소켓Socket socket;// 실제 통신을 하는 소켓BufferedReader br;publicServerFile(){System.out.println("1. 서버 소켓 시작");try{ServerSocket=newServerSocket(10000);System.out.println("2. 서버 소켓 생성 완료 - 클라이언트 접속 대기중 "); socket = serverSocket.accept();// 클라이언트 접속 대기중System.out.println("3. 클라이언트 연결 완료"); br =newBufferedReader(newInputStreamReader(socket.getInputStream()));// ByteStream 연결//------------------수정-----------------while(true){String msg = br.readLine();System.out.println("4. 클라이언트로부터 받은 메시지 : "+ msg);}catch(Exception e){System.out.println("서버 소켓 에러 발생 : "+ e.getMessage());}}publicstaticvoidmain(String[] args){newServerFile();}}
ClientFile2.java
importjava.net.Socket;importjava.io.BufferedWriter;importjava.io.InputStreamReader;importjava.io.OutputStreamWriter;publicclassClientFile2{Socket socket;BufferedWriter bw;BufferedReader br;publicClientFile(){System.out.println("1. 클라이언트 소켓 시작");try{ socket =newSocket("localhost",10000);// 서버 소켓의 accept() 메서드 호출 (localhost 아닌 경우 ip주소 입력)System.out.println("2. 버퍼(write) 연결 완료"); bw =newBufferedWriter(newOutputStreamWriter(socket.getOutpputStream());// 키보드 연결System.out.println("3. 키보드 스트림 + 버퍼(read) 연결 완료"); br =newBufferedReader(newInputStreamReader(System.in));//------------------수정-----------------while(true){System.out.println("4. 키보드 메시지 입력 대기중");String keyboardMsg = br.readLine(); bw.write(keyboardMsg +"\n");// 메시지 끝을 알려줘야 함 (\n 필요) bw.flush();// 버퍼 비워주기}}catch(Exception e){System.out.println("클라이언트 소켓 에러 발생함 : "+ e.getMessage());}}publicstaticvoidmain(String[] args){newClientFile();}}
2) 수정 사항2️⃣
메인 스레드가 바빠서 서브 스레드 생성
단방향 ➡️ 양방향 통신으로 변경 (버퍼 추가 생성)
ServerThread.java
importjava.net.ServerSocket;importjava.net.Socket;publicclassServerThread{ServerSocket serverSocket;// 클라이언트 연결을 받는 소켓Socket socket;// 실제 통신을 하는 소켓BufferedReader br;// ------------------수정-----------------// 새로운 스레드 필요BufferedWriter bw;// 쓰기를 위한 버퍼BufferReader keyboard;// 키보드로부터 읽는 버퍼 publicServerFile(){System.out.println("1. 서버 소켓 시작");try{ServerSocket=newServerSocket(10000);System.out.println("2. 서버 소켓 생성 완료 - 클라이언트 접속 대기중 "); socket = serverSocket.accept();// 클라이언트 접속 대기중System.out.println("3. 클라이언트 연결 완료"); br =newBufferedReader(newInputStreamReader(socket.getInputStream()));// ByteStream 연결// ------------------수정----------------- keyboard =newBufferReader(newInputStreamReader(System.in); bw =newBufferedWriter(newOutputStreamWriter(socket.getOutputStream());// 쓰기 스레드 역할 (write 스레드 실행)WriteThread wt = nw WriteThread();Thread t1 =newThread(wt); t1.start();// main 스레드 역할 (글 읽기)while(true){String msg = br.readLine();System.out.println("4. 클라이언트로부터 받은 메시지 : "+ msg);}catch(Exception e){System.out.println("서버 소켓 에러 발생 : "+ e.getMessage());}}// 쓰기를 위한 스레드classWriteThreadimplementsRunnable{publicvoidrun(){while(true){try{String keyboardMsg = keyboard.readLine(); bw.write(keyboardMsg +"\n"); bw.flush();}catch(Exception e){System.out.println("서버 소켓 쪽에서 키보드 입력받는 중 오류 발생 : "+ e.getMessage());}}}}publicstaticvoidmain(String[] args){newServerFile();}}
ClientThread.java
importjava.net.Socket;importjava.io.BufferedWriter;importjava.io.InputStreamReader;importjava.io.OutputStreamWriter;publicclassClientThread{Socket socket;BufferedWriter bw;BufferedReader keyboard;publicClientFile(){System.out.println("1. 클라이언트 소켓 시작");try{ socket =newSocket("localhost",10000);// 서버 소켓의 accept() 메서드 호출 (localhost 아닌 경우 ip주소 입력)System.out.println("2. 버퍼(write) 연결 완료"); bw =newBufferedWriter(newOutputStreamWriter(socket.getOutpputStream());// 키보드 연결System.out.println("3. 키보드 스트림 + 버퍼(read) 연결 완료"); keyboard =newBufferedReader(newInputStreamReader(System.in));//------------------수정----------------- br =newBufferedReader(newInputStreamReader(socket.getInputStream());// 읽기 쓰레드 역할 (read 메서드 실행)ReadThread rt =newReadThread();Thread t1 =newThread(rt); t1.start();// main 스레드 역할 (글 쓰기)while(true){System.out.println("4. 키보드 메시지 입력 대기중");String keyboardMsg = keyboard.readLine(); bw.write(keyboardMsg +"\n");// 메시지 끝을 알려줘야 함 (\n 필요) bw.flush();// 버퍼 비워주기}}catch(Exception e){System.out.println("클라이언트 소켓 에러 발생함 : "+ e.getMessage());}}classReadThreadimplementsRunnable{publicvoidrun(){while(true){try{String msg = br.readLine();System.out.println("서버로 부터 받은 메시지 : "+ msg);}catch(Exception e){System.out.println("클라이언트 소켓 쪽에서 서버 소켓 메시지를 입력받는 중 오류 발생 :"+ e.getMessage());}}}}publicstaticvoidmain(String[] args){newClientFile();}}