21일차 내용 정리

채공부·2025년 6월 23일

Git

🔴폴더 & 파일 생성

test11 폴더에 그 안에 index.html 파일 생성
h 요소 추가
VS Code에서 File ➜ Open Folder로 test11만 단독 열기

🔴 Git 저장소 초기화

VS Code GUI 또는 터미널에서 git init
또는
VS Code의 Source Control ➜ Initialize Repository click
git status : Modified 상태 ➜ git 저장소 초기화 완료

🔴 Git 상태 변화

변경된 파일 "+" click ➜ git add 실행과 동일
add 된 파일은 Staged 상태로 전환
"-" click ➜ add 취소
commit 메세지 작성 후 ✔Commit click
아래 GRAPH 에서 main 브랜치에 커밋이 반영된 걸 확인

🔴 브랜치 생성 및 이동

p 요소 추가 후 git mode를 확인해 ➜ changes 상태
Changes 에 "..." click ➜ Git 관련 기능 제공
Checkout to ➜ Create new branch ➜ lab1 입력 ➜ enter
현재 HEAD는 lab1에 얻혀져 있는걸 밑에 GRAPH에 표시

🔴 lab1 브랜치에서 작업 후 병합

style 요소 추가 후 commit
main 브랜치로 이동
Branch ➜ Merge ➜ lab1 click ➜ 병합 완료
Branch ➜ Delete branch ➜ lab1 click ➜ lab1 브랜치 삭제 완료

🔴 lab2 브랜치 생성 및 작업

lab2 브랜치 생성 (생성과 동시에 자동 checkout)
<a href="study.html"></a> 요소 추가
study.html 파일 추가

M : Modified files
U : Untracked filed

🔴 stash 사용 (작업 임시 저장)

급하게 브랜치 전환이 필요한 경우 ➜ stash 사용
Stash ➜ Stash (Include Untracked)
 ⤷ 임시저장 메세지 작성 후 enter
 ⤷ study.html 파일과 <a> 요소 작업이 사라짐
Stash ➜ Pop Stash : 다시 복원
 ⤷ 이전 작업이 되돌아옴

🔴 lab2 브랜치 작업 마무리

study.html 파일에 p요소 추가
add ➜ commit
main으로 checkout 해서 브랜치 이동
Branch ➜ Merge ➜ lab2 병합
Branch ➜ Delete branch ➜ lab2 브랜치 삭제

🔴 원격 저장소 연결 (GitHub 업로드)

1. Github 에서 test11 저장소 생성
  ⤷ README 파일 check X (기본 커밋이 새이면 충돌 가능성)
2. 저장소 URL 복사
3. VS Code에서 Remote ➜ Add Remote ➜ URL 붙여넣기
  ⤷ 이름은 origin 으로 작성 후 enter
  
4. Push click
  ⤷ 알림창 확인 후 ok ➜ Github에 test11 업로드 완료

🔴 Push 이후 상태 및 주의점

p요소 추가 후 add, commit
이때 VS Code에서는 origin/main이 한칸 뒤쳐짐으로 표시

지금 상태에서는 push 가능하지만
만일 다른 사람이 먼저 origin에 push했다면 충돌로 인해 Push 불가
  
현재 상태에서는 Push 정상 작동
➜ origin/main 브랜치 포인터가 한 칸 뒤로 이동

JAVA

복습 시작

기본 데이터 타입

  • Java의 기본 데이터 타입은 총 8가지
분류타입설명
정수형byte1바이트 정수
short2바이트 정수
int4바이트 정수 (기본)
long8바이트 정수
실수형float4바이트 실수
double8바이트 실수 (기본)
기타boolean논리값 (true/false)
char문자 하나 (유니코드 기반)

참조 데이터 타입

  • 클래스, 배열, 인터페이스 등 객체의 주소값(참조값)을 저장
  • 힙(Heap) 영역에 객체가 생성됨
  • 예시:
    • 클래스 : String, Random, Scanner, Post, Car
    • 배열 : int[], String[]
    • 사용자 정의 클래스 : MemberDto, MemberInfo
    • 기타 : PrintStream

객체의 구성 요소

객체는 크게 저장소(필드)기능(메소드)로 구성

public class 클래스명 {
    // 1. 필드 (Field)
    // 2. 메소드 (Method)
    // 3. 생성자 (Constructor)
    // 4. 클래스 (내부 클래스 등)
}

생성자 (Constructor)

객체 생성 시 사용 : new 클래스명();

특징

  • 클래스명과 동일한 이름
  • 반환 타입이 없음
  • 여러 형태로 오버로딩 가능 (매개변수만 다르게 여러 개 정의 가능)
public class Car {
    Car() {
        // 기본 생성자
    }

    Car(String model) {
        // 오버로딩된 생성자
    }
}

static

  • static이 붙은 필드/메소드는 객체와 무관하게 클래스 로딩 시 메모리에 올라감
  • 클래스 단위로 관리, 공용 데이터에 적합
  • new 하지 않아도 사용 가능
public class Example {
    static int count = 0; // 모든 객체가 공유
}

⤷ new로 객체를 생성하면 각 객체마다 사물함처럼 독립된 공간이 생긴다

this

  • 현재 객체의 참조값을 의미
  • 생성자나 메소드 내에서 객체 자신을 가리킬 때 사용
  • this.는 생략 가능하지만 동일한 이름의 지역변수와 충돌할 경우 생략 불가능
public class User {
    String name;

    User(String name) {
        this.name = name; // 지역변수와 필드 이름이 같기 때문에 this 사용
    }
}

기본 data type 의 참조 data type

기본 데이터 타입참조(Wrapper) 타입
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

Wrapper Class가 필요한 이유

  • 때로는 기본 데이터 타입을 객체처럼 다뤄야 하는 경우가 있으며 이럴 때 사용하는 것이 참조 데이터 타입
  • 기본 데이터 타입을 객체로 포장(Boxing) 하는 형태
  • 객체에서 기본 데이터 타입으로 꺼내는 것을 Unboxing
  • Boxing 과 Unboxing 은 자동 처리
public class MainClass01 {
	public static void main(String[] args) {
		Byte b = 10;
		Short s = 20;
		Integer i = 30;
		Long l = 40L;
		
		Float f = 10.1f;
		Double d = 10.2d;
		
		Character c = 'a';
		Boolean isRun = true;
	}
}
  • new 없이도 자동으로 변환됨 (JDK 1.5부터 지원)

참조 타입이기 때문에 .기능() 사용 가능

public static void main(String[] args) {
		// 기본 데이터 type 변수 num1
		int num1 = 10;
		// 참조 데이터 type 변수 num2
		Integer num2 = 10;
		
		// 참조 데이터 type 이지만 기본 데이터 type 처럼 사용 가능
		int result1 = num2+1;
		int result2 = num1+num2;
		Integer result3 = num1+num2;
	}

Integer도 class

  • Integer는 클래스이기 때문에 다양한 기능을 내장
    동일한 메소드명이 여러 개 ➜ 메소드 오버로딩

parseInt(String s)

  • 문자열을 숫자로 변환할 때 사용
  • 숫자가 아닌 문자열이면 예외 발생
public static void main(String[] args) {
    
	int result = Integer.parseInt("10");10
        
	int result2 = Integer.parseInt("십");NumberFormatException 발생
    
    double result3 = Double.parseDouble("10.1");10.1
}

Scanner()

System class

System.in ➜ InputStream type (입력받을 준비된 객체)

System.out ➜ PrintStream type (출력할 준비된 객체)

➞ Scanner는 System.in을 받아서 사용

public static void main(String[] args) {
	System.out.println("main 메소드가 시작 되었습니다.");
    
	// 콘솔창으로 부터 입력 받을 수 있는 Scanner 객체 생성
	InputStream is = System.in;
	Scanner scan1 = new Scanner(is);

	Scanner scan2 = new Scanner(System.in);
    
    // .print()는 개행기호를 출력하지 않는다
	System.out.print("문자열 입력 : ");
    
	// 콘솔창에 입력한 문자열을 String type 으로 얻어낸다
	String str = scan2.nextLine();
		
	System.out.println("main 메소드가 종료 됩니다.");
	}

nextLine()
   콘솔창에 문자열을 입력하고 Enter 를 입력해야 .nextLine() 메소드가 return
   Enter 를 입력하기 전까지 실행의 흐름이 Blocking 되어 있다

예제

  1. 콘솔창으로 부터 입력받을 수 있는 Scanner 객체를 생성해서 그 참조값을 scan 이라는 지역변수에 담아보세요
  2. scan 에 담겨있는 Scanner 객체를 2번 사용해서 사용창으로부터 숫자를 2개 입력 받으세요.
  3. 입력받은 숫자(문자열)을 실제 숫자로 변경해보세요.
  4. 두수의 합을 구해서 콘솔창에 이쁘게 출력해 보세요.
public static void main(String[] args) {
	Scanner scan = new Scanner(System.in);
		
	System.out.print("첫번째 숫자 입력 : ");
	String first = scan.nextLine();
		
	System.out.print("두번째 숫자 입력 : ");
	String second=  scan.nextLine();
		
	double num1 = Double.parseDouble(first);
	double num2 = Double.parseDouble(second);
		
	double result = num1+num2;
		
	String info = String.format("%.2f과 %.2f의 합은 : %.2f", num1, num2, result);
	System.out.println(info);
	}
  • 사용한 클래스
    Scanner class ➜ import java.util.Scanner; 필요
    System, Double ➜ java.lang 패키지에 포함 ➜ import 불필요

상속 (extends)

public class Phone extends Object {
	// 생성자
	public Phone() {
		System.out.println("Phone 생성자 호출됨");
	}
	public void call() {
		System.out.println("전화를 걸어요!");
	}
public class HandPhone extends Phone {
	// 생성자
	public HandPhone() {
		System.out.println("HandPhone 생성자 호출됨");
	}
	
	// 이동중에 전화를 걸어요
	public void mobileCall() {
		System.out.println("이동중에 전화를 걸어요");
	}
	
	// 사진 찍는 테스트
	public void takePicture() {
		System.out.println("30만 화소의 사진을 찍어요");
	}
public static void main(String[] args) {
	HandPhone p1 = new HandPhone();
	p1.call();         // 부모 클래스의 메소드
	p1.mobileCall();   // 자식 클래스의 메소드
	p1.takePicture();  // 자식 클래스의 메소드
	}
}
  • 상속 정리
    자식 클래스는 부모 클래스의 기능을 모두 상속받는다
    아무것도 상속하지 않으면 자동으로 Object를 상속받는다

  • this ➜ 현재 객체의 참조값
    super ➜ 부모 객체의 참조값

다형성

  • 자바의 객체는 여러 개의 타입을 가질 수 있음
  • 변수 선언 시의 타입은 사용 설명서 역할
  • 따라서 . 을 찍었을 때 해당 타입에 정의된 메소드만 접근 가능
  • 객체는 하나지만 참조하는 변수 타입이 다르면 접근 가능한 기능이 달라진다
HandPhone p1 = new HandPhone(); 

Phone p2 = new HandPhone();

Object p3 = new HandPhone();
           ↱ 지역변수 p1
HandPhone p1 = new HandPhone();
  ↳ 변수 앞에 선언된 핸드폰 타입
    p1 안에 들어 있는 값에 대한 사용 설명서 역할
 ∴ p1에 . 을 찍으면 부모 클래스에 정의된 기능을 포함한 모든 기능 다 사용 가능

객체 하나에 여러 타입의 참조 가능

public static void main(String[] args) {
	// 아래의 3줄을 실행하면 참조값이 몇 개가 나올까? (객체가 몇 개가 생성될까?)
	HandPhone p1 = new HandPhone();
    
	// HandPhone type 안에 있는 값을 Phone type(부모 type) 변수에 대입 가능
	Phone p2 = p1;
    
	// HandPhone type 안에 있는 값을 Object type(부모 type) 변수에 대입 가능
	Object p3 = p1;	
    
    HandPhone p4 = p3; -> error 발생
    // Casting 후에 담을 수 있다
	HandPhone p4 = (HandPhone)p3;
}
  • 객체는 한 개만 생성됨 (new HandPhone())
    p1, p2, p3는 동일한 참조값을 가짐

  • 변수의 타입에 따라 .점 접근으로 사용 가능한 메소드가 달라짐

p1.takePicture() ➜ 가능
p2.takePicture() ➜ 불가능 (Phone에 없음)
p3.toString() ➜ 가능 (Object 메소드)

형변환 예외

public class Car {
	public void drive() {
		System.out.println("달려요!");	
	}
}
public static void main(String[] args) {
		Object p1 = new HandPhone();
		Object c1 = new Car();
		
		// p1 의 사용 설명서를 Phone type 으로 교체하면서 참조값을 p2 에 담아 보세요
		Phone p2 = (Phone)p1;
		p2.call(); ➜ 실제로 HandPhone이 들어있기에 가능
		
		// classCastException 발생
		Phone p3 = (Phone)c1;CarPhone이 아님
		p3.call(); ➜ error 발생
	}
  • 업캐스팅 (Upcasting) : 자식 ➜ 부모 (자동 형변환)
  • 다운캐스팅 (Downcasting) : 부모 ➜ 자식 (강제 형변환 필요)
  • 실제 객체가 무엇인지에 따라 형변환 성공 여부가 달라짐
  • 잘못된 캐스팅은 ClassCastException 발생

부모 타입으로 프로그래밍하면 좋은 점?

  • 코드의 유연성 증가
  • 다양한 자식 클래스들을 하나의 부모 타입으로 다룰 수 있음

Override

  • 부모 클래스에 정의된 메소드를 자식 클래스에 다시 정의 = 덮어쓰기
  • @Override는 생략 가능하지만, 보통은 생략하지 않는다
public class SmartPhone extends HandPhone {
	// 생성자
	public SmartPhone() {
		System.out.println("SmartPhone() 생성자 호출됨");
	}
	
	// 메소드
	public void doInternet() {
		System.out.println("인터넷을 해요!");
	}
	
	// 부모가 가지고 있는 메소드를 재정의 할 일이 많다
	// 메소드 Override (덮어쓰기)
	@Override // 재정의한 메소드임을 표시하는 어노테이션, 생략이 가능하지만 보통은 생략하지 않는다
	public void takePicture() {
		System.out.println("1억 화소의 사진을 찍어요~");
	}
}
  • SmartPhone 클래스는 HandPhone을 상속 받음
    takePicture() 메소드를 오버라이드
    같은 이름, 같은 매개변수 ➜ 부모의 메소드를 덮어쓴다
public static void main(String[] args) {
	SmartPhone p1 = new SmartPhone();
	p1.call();
	p1.mobileCall();
	p1.takePicture();1억 화소
	p1.doInternet();

	System.out.println("------------");
	// SmartPhone 객체를 생성해서 HandPhone type 으로 받은 다음
	HandPhone p2 = new SmartPhone();
	// 사진을 찍으면? 몇 화소 일까?
	p2.takePicture();1억 화소
        
	System.out.println("------------");
	HandPhone p3 = new HandPhone();
	p3.takePicture();30만 화소
	}
}

Java 상속과 생성자 호출 예제 - 가상의 도형(Shape) 상속하기

  • 상속받은 클래스에서 부모 클래스의 생성자 호출 방법 이해하기
  • 생성자에서 super(...) 사용하는 이유와 규칙 익히기
<Shape.java>

// 가상의 도형(모양)을 나타내는 객체를 생성할 클래스
public class Shape {
	// 도형의 위치
	private int x;
	private int y;
	
	public Shape(int x, int y) {
		this.x = x;
		this.y = y;
	}
	public int getX() {
		return x;
	}
	public int getY() {
		return y;
	}
}
  • 생성자를 하나라도 정의한다면 기본(default) 생성자는 사라지나
    즉, new Shape() 방식의 객체 생성 불가

상속만 받고 생성자 정의를 안 했다면?

public class Circle extends Shape{
}
  • error 발생?
    문법이 맞다고 가정하면 new Circle() 이 가능
    상속 관계 때문에 new Shape() 도 호출
    Shape 클래스는 default 생성자가 없어서 생성 불가
    Shape 클래스는 생성자의 매개변수에 x,y 좌표를 전달해야 한다
자식 클래스는 생성자에서 자동으로 super()(부모의 기본 생성자)를 호출
shape 클래슨 기본 생성자가 없다 ➜ super() 호출 실패 ➜ error

해결방법 : 자식 생성자에서 super() 호출

public class Circle extends Shape{

	// 원의 반지름을 저장할 필드
	private int radius;
	
	public Circle(int x, int y, int radius) {
		super(x, y); // super() 는 부모 클래스의 생성자를 의미
		this.radius = radius;
	}
	
	public void printArea() {
		double area = Math.PI*radius*radius;
		System.out.println("반지름은 : "+radius+" 이고 원의 넓이는 : "+area);
	}

⤷ 자식 클래스의 생성자에서 필요한 값을 전달받아 그 값을 이용해 부모 클래스의 생성자를 직접 호출하여 부모 객체의 필드를 초기화

profile
학원 공부 내용 정리

0개의 댓글