상속(inheritance)
객체 지향의 상속
- 부모클래스에 만들어진 필드, 메소드를 자식클래스가 물려받음
- 부모의 생물학적 특성을 물려받는 유전과 유사
상속을 통해 간결한 자식 클래스 작성
- 동일한 특성을 재정의할 필요가 없어 자식 클래스가 간결해짐
객체 지향에서 상속의 장점
- 클래스의 간결화 - 멤버의 중복 작성 불필요
- 클래스 관리 용이 - 클래스들의 계층적 분류
- 소프트웨어의 생산성 향상 - 클래스 재사용과 확장 용이, 새로운 클래스의 작성 속도 빠름
클래스 상속과 객체
public class Person{
...
}
public class Student extends Person{//Person을 상속 받는 클래스 Student선언
...
}
public class StudentWorker extends Student {//Student를 상속받는 StudentWorker선언
...
}
부모 클래스 : 슈퍼 클래스(super class)로 부름
자식 클래스 : 서브 클래스(sub class)로 부름
extends 키워드 사용 : 슈퍼 클래스를 확장한다는 개념
예제 5-1 : 클래스 상속 만들기 -Point와 ColorPoint
(x, y)의 한 점을 표현하는 Point 클래스와 이를 상속받아 색을 가진 점을 표현하는 ColorPoint클래스를 만들고 활용해 보자
class Point{
private int x, y;
public void set(int x, int y){
this.x = x, this.y = y;
}
public void showPoint(){
System.out.println("("+x+","+y+")");
}
}
//Point를 상속받은 ColorPoint 선언
class ColorPoint extends Point{
private String color;
public void setColor(String color){
this.color = color;
}
public void showColorPoint(){
System.out.print(color);
showPoint();
}
}
public class ColorPointEx{
public static void main(String[] args){
point p = new Point(); //포인트 객체 생성
p.set(1,2); //포인트 클래스의 셋함수 호출
p.showPoint();
ColorPoint cp = new ColorPoint(); //컬러포인트 객체
cp.set(3,4); //포인트의 셋함수 호출
cp.setColor("red"); //컬러포인트의 셋컬러 함수 호출
cp.showColorPoint(); //컬러와 좌표 출력
}
}
실행 결과
(1,2)
red(3,4)
서브클래스에서 슈퍼 클래스의 접근
자바 상속의 특징
- 클래스의 다중 상속 지원하지 않음
- 상속 횟수 무제한
- 상속의 최상위 조상 클래스는 java.lang.Object 클래스
- 모든 클래스는 자동으로 java.lang.Object를 상속받음
- 자바 컴파일러에 의해 자동으로 이루어짐
- 자바는 최상위 클래스가 있게 됨으로써 관리하기 편해짐
상속과 접근 지정자
- 자바의 접근 지정자 4가지
- public, protected, 디폴트, private
- 상속 관계에서 주의할 접근 지정자는 private와 protected
- 슈퍼 클래스의 private 멤버
- 슈퍼 클래스의 private 멤버는 다른 모든 클래스에 접근 불허
- 클래스 내의 멤버들에게만 접근 허용
- 슈퍼 클래스의 디폴트 멤버
- 슈퍼 클래스의 디폴트 멤버는 패키지 내 모든 클래스에 접근 허용
- 슈퍼 클래스의 public 멤버
- 슈퍼 클래스의 public 멤버는 다른 모든 클래스에 접근 허용
- 슈퍼 클래스의 protected 멤버
- 같은 패키지 내의 모든 클래스 접근 허용
- 다른 패키지에 있어도 서브 클래스는 슈퍼 클래스의 protected 멤버 접근 가능
슈퍼 클래스의 멤버에 대한 서브 클래스의 접근
예제 5-2: 상속 관계에 있는 클래스 간 멤버 접근
class Person {
private int weight;
int age;
protected int height;
public String name;
public void setWeight(int weight) {
this.weight = weight;
}
public int getWeight(){
return weight;
}
}
class Student extends Person {
public void set() {
age = 30; //슈퍼 클래스의 디폴트 멤버 접근 가능
name = "홍길동"; // 슈퍼 클래스의 public 멤버 접근 가능
height = 172; //슈퍼 클래스의 protected 멤버 접근 가능
//weight = 99; 오류, 슈퍼 클래스의 private 접근 불가
setWeight(99); // private 맴버 weight은 setweight()으로 간접 접근
}
}
public class InheritanceEx {
public static void main(String[] args) {
Student s = new Student();
s.set();
}
}
** 여기서 setWeight를 main에서 출력할 때는?
setWeight() 메서드를 사용하여 weight 값을 설정하고 getWeight() 메서드를 사용하여 해당 값을 main 메서드에서 출력
intweight= s.getWeight(); // getWeight()를 사용하여 weight 값을 가져옴
System.out.println("Weight: " + weight);
서브 클래스/ 슈퍼 클래스의 생성자 호출 및 실행
서브 클래스 객체가 생성될 때 서브 클래스의 생성자와 슈퍼 클래스의 생성자가 모두 실행된다.
-> 서브 클래스의 객체가 생성되면 이 객체 속에 서브 클래스 멤버와 슈퍼 클래스의 멤버가 모두 들어 있다. 생성자의 목적은 객체 초기화에 있으므로, 서브 클래스의 생성자는 생성된 객체 속에 들어 있는 서브 클래스의 멤버 초기화나 필요한 초기화를 수행하고, 슈퍼 클래스의 생성자는 생성된 객체 속에 있는 슈퍼 클래스의 멤버 초기화나 필요한 초기화를 각각 수행한다.
-> 슈퍼 클래스의 생성자가 먼저 실행된 후 서브 클래스의 생성자가 실행된다.
new에 의해 서브 클래스의 객체가 생성될 때
- 슈퍼 클래스 생성자와 서브 클래스 생성자 모두 실행됨
- 호출 순서 : 서브 클래스의 생성자가 먼저 호출, 서브 클래스의 생성자는 실행 전 슈퍼 클래스 생성자 호출
- 실행 순서 : 슈퍼 클래스의 생성자가 먼저 실행된 후 서브 클래스의 생성자 실행
슈퍼클래스와 서브 클래스의 생성자 간의 호출 및 실행 관계
서브 클래스에서 슈퍼 클래스의 생성자 선택
- 상속 관계에서의 생성자 : 슈퍼 클래스와 서브 클래스 각각 여러 생성자 작성 가능
- 서브 클래스 생성자 작성 원칙 : 서브 클래스 생성자에서 슈퍼 클래스 생성자 하나 선택
- 서브 클래스에서 슈퍼 클래스의 생성자를 선택하지 않는 경우 : 컴파일러가 자동으로 슈퍼 클래스의 기본 생성자 선택
- 서브 클래스에서 슈퍼 클래스의 생성자를 선택하는 방법 : super이용
슈퍼 클래스의 기본 생성자가 자동 선택
슈퍼 클래스에 기본 생성자가 없어 오류가 난 경우
서브 클래스에 매개변수를 가진 생성자
super()를 이용하여 명시적으로 슈퍼 클래스 생성자 선택
super()
- 서브 클래스에서 명시적으로 슈퍼 클래스의 생성자 선택 호출
- super(parameter);
- 인자를 이용하여 슈퍼 클래스의 적당한 생성자 호출
- 반드시 서브 클래스 생성자 코드의 제일 첫 라인에 와야 함
super()를 이용한 사례
**super()는 첫 줄에 와야 함
class A {
public A() {
System.out.println("생성자A");
}
public A(int x) {
System.out.println("매개변수생성자A"+x);
}
}
class B extends A{
public B(){
System.out.println("생성자B");
}
public B(int x){
super(x);
System.out.println("매개변수생성자B"+x);
}
}
public class ConstructorEx4 {
public static void main(String[] args){
B b;
b = new B(5);
}
}
실행 결과
매개변수생성자A5
매개변수생성자B5
예제 5-3 : super()를 활용한 ColorPoint작성
super()를 아용하여 ColorPoint 클래스의 생성자에서 슈퍼 클래스 Poinr의 생성자를 호출하는 예
class Point{
private int x,y;
public Point(){
this.x=this.y=0;
}
public Point(int x, int y){
this.x=x; this.y=y;
}
public void showPoint(){
System.out.println("("+x+","+y+")");
}
}
class ColorPoint extends Point{
private String color;
public ColorPoint(int x, int y, String color){
super(x,y);
this.color=color;
}
public void showColorPoint(){
System.out.print(color);
showPoint();
}
}
public class SuperEx {
public static void main(String[] args){
ColorPoint cp = new ColorPoint(5,6,"blue");
cp.showColorPoint();
}
}
업캐스팅(upcasting)
서브 클래스의 객체는
- 슈퍼 클래스의 멤버를 모두 가지고 있음
- 슈퍼 클래스의 객체로 취급할 수 있음 ('사람은 생물이다' 논리와 같음)
업캐스팅이란?
서브 클래스 객체를 슈퍼 클래스 타입으로 타입 변환
class Person{...}
class Student extends Person {...}
Student s = new Student();
Person p = s; //업캐스팅, 자동 타입 변환
즉, 상속 관계에 있는 클래스들에서 상위 클래스 타입으로의 형 변환을 의미합니다.
업캐스팅 레퍼런스
- 객체 내에 슈퍼 클래스의 멤버만 접근 가능
- 업캐스팅을 통해 상위 클래스 타입으로 객체를 참조하면, 상위 클래스의 멤버들만 접근할 수 있음
- 하위 클래스에 추가된 멤버들은 업캐스팅된 상위 클래스 타입으로는 직접 접근할 수 없음.
- 업캐스팅의 주요 목적은 다형성(polymorphism)을 구현하는 것
업캐스팅 사례
다운캐스팅(downcasting)
- 슈퍼 클래스 객체를 서브 클래스 타입으로 변환
- 개발자의 명시적 타입 변환 필요
- 해당 객체가 원래 하위 클래스의 인스턴스인지를 확인하는 과정이 필요: ClassCastException 예외가 발생할 수 있으므로 주의
class Person {...}
class Student extends Person {...}
...
person p = new Student("이재문"); //업캐스팅
...
Student s = (Student)p; //다운캐스팅, (student)의 타입 변환 표시 필요
**Student s = p; //이러한 형태는 컴파일 에러 주의, 상위타입을 하위타입에 넣으려고 했기 때문
다운캐스팅 사례
instanceof 연산자
- 레퍼런스가 가리키는 객체의 타입 식별을 위해 사용
- 업캐스팅된 레퍼런스로 객체 타입 판단 어려움 -> 슈퍼 클래스가 여러 서브 클래스에 상속되기 때문
- 사용법 : 객체레퍼런스 instaceof 클래스 타입
- 연산 결과 : true/false의 불린 값
- (교수님이 최대한 쓰지 말라 하셨다..?)
메소드 오버라이딩(Method Overriding)
- 슈퍼 클래스의 메소드를 서브 클래스에서 재정의
- 슈퍼 클래스 메소드의 이름, 매개변수 타입 및 개수, 리턴 타입 등 모든 것 동일하게 작성
- 매소드 무시하기, 덮어쓰기로 번역되기도 함
- 동적 바인딩 발생
- 서브 클래스에 오버라이딩한 메소드가 무조건 실행되는 동적 바인딩
** 동적 - 런타임, 스테틱 - 컴파일
*** 오버라이딩 되면 -> 동적, 오버라이딩 안되면 -> 정적
**** final이라고 쓰여있으면 정적, static 메소드는 오버라이딩 x, private 메소드는 오버라이팅 x
메소드 오버라이팅 사례
Shape 클래스의 draw() 메소드를 Line, Rect, Circle 클래스에서 각각 오버라이딩한 사례
**주의 Shape 클래스의 draw 메소드가 default였다면 동적 바인딩이 안 돼서 컴파일 에러 뜸
오버라이딩에 의해 서브 클래스의 메소드 호출
**오버라이딩은 무슨 타입으로 가리키든 간에 무조건 아래 것이 실행됨
오버라이딩의 목적, 다형성 실현
오버라이딩
- 슈퍼 클래스에 선언된 메소드를, 각 서브 클래스들이 자신만의 내용으로 새로 구현하는 기능
- 상속을 통해 '하나의 인터페이스(같은 이름)에 서로 다른 내용 구현'이라는 객체 지향의 다형성 실현
- Line클래스에서 draw()는 선을 그리고
- Circle클래스에서 draw는 원을 그리고
- Rect클래스에서 draw는 사각형을 그리고
- 오버라이딩은 실행 시간 다형성 실현
- 동적 바인딩을 통해 실행 중에 다형성 실현 (오버라이딩 안 되는 것은 정적)
- 오버로딩은 컴파일 타임 다형성 실현
예제 5-5: 메소드 오버라이딩으로 다형성 실현
class Shape {
public Shape next;
public Shape() {next = null;}
public void draw() {
System.out.println("Shape");
}
}
class Line extends Shape {
public void draw(){
System.out.println("Line");
}
}
class Rect extends Shape {
public void draw(){
System.out.println("Rect");
}
}
class Circle extends Shape {
public void draw(){
System.out.println("Circle");
}
}
public class MethodOverridingEx {
static void paint(Shape p){
p.draw();
}
public static void main(String[] args){
Line line = new Line();
paint(line);
paint(new Shape());
paint(new Line());
paint(new Rect());
paint(new Circle());
}
}
** 변수를 선언할 때는 상위 타입! 상속은 다형성을 쓰려고 씀
*** 좋은 상속 구조란? 부모를 뚱뚱하게 하는 것, 자식 클래스에서 새로 추가된 메소드 없게 하기!
*** 그런다면! 다향성이 더 좋아짐
위의 예제 실행 과정
오버라이딩 활용
public class UsingOverriding {
public static void main(String[] args){
Shape start, last, obj;
//링크드리스트로 도형 생성하여 연결
start = new Line(); //Line객체 연결
last = start;
obj = new Rect(); //Rect객체 연결
last.next=obj;
last = obj;
obj = new Line();
last.next=obj;
last = obj;
obj=new Circle(); //Circle 객체 연결
last.next=obj;
Shape p = start;
//모든 도형 출력
while (p!=null){
p.draw();
p = p.next;
}
}
}
동적 바인딩
- 실행할 메소드를 실행 시(run time)에 결정
- 오버라이딩 메서드가 항상 호출
오버라이딩과 super키워드
- super는 슈퍼 클래스의 멤버를 접근할 때 사용되는 레퍼런스
- 서브 클래스에서만 사용
- 슈퍼 클래스의 메소드 호출
- 컴파일러는 super의 접근을 정적 바인딩으로 처리
class Shape{
protected String name;
public void paint(){
draw();
}
public void draw(){
System.out.println(name);
}
}
public class Circle extends Shape {
protected String name;
public void draw(){
name = "Circle";
super.name="Shape";
super.draw();
System.out.println(name);
}
public static void main(String[] args){
Shape b = new Circle();
b.paint();
}
}
실행 결과
Shape
Circle
예제 5-6 : 메소드 오버라이딩
class Weapon {
protected int fire(){
return 1;
}
}
class Cannon extends Weapon{
protected int fire(){
return 10;
}
}
public class Overriding {
public static void main(String[] args){
Weapon weapon;
weapon = new Weapon();
System.out.println("기본 무기의 살상 능력은"+weapon.fire());
weapon=new Cannon();
System.out.println("대포의 살상 능력은"+weapon.fire());
}
}
실행결과
기본 무기의 살상 능력은 1
대포의 살상 능력은 10
오버라이딩 정리
선언 : 서브 클래스에서 슈퍼 클래스에 있는 메소드와 동일한 이름의 메소드 재작성
관계 : 상속 관계
목적 : 슈퍼 클래스에 구현된 메소드를 무시하고 서브 클래스에서 새로운 기능의 메소드를 재정의하고자 함. 다형성 실현
조건 : 메소드의 이름, 매개변수 타입과 개수, 리턴 타입이 모두 동일하여야 성립
바인딩 : 동적 바인딩. 실행 시간에 오버라이딩된 메서드를 찾아 호출
추상 메소드와 추상 클래스
추상 메소드(abstract method)
- 선언되어 있으나 구현되어 있지 않은 메소드, abstract로 선언
public abstract String getName();
public adstract void setName(String s);
- 추상 메소드는 서브 클래스에서 오버라이딩하여 구현해야 함
추상 클래스(abstract class)의 2종류
- 추상 메소드를 하나라도 가진 클래스 : 클래스 앞에 반드시 abstract라고 선언해야 함
- 추상 메소드가 하나도 없지만 abstact로 선언된 클래스
2가지 종류의 추상 클래스 사례
//1. 추상 메소드를 포함하는 추상 클래스
abstract class Shape { //추상 클래스 선언
public Shape(){}
public void paint() {draw();}
abstract public void draw(); //추상 메소드
}
//2. 추상 메소드 없는 추상 클래스
abstract class MyComponent {
String name;
public void load(String name) {
this.name = name;
}
}
추상 클래스는 객체를 생성할 수 없다!!
-> 그래서 적당한 행위가 없을 때 사용하기도 함
-> 하지만 new~식으로 객체를 만들지 못하는 거지, 실행 시 생성자가 없는 경우 알아서 컴파일러가 넣어줌!
추상 클래스의 상속
추상 클래스의 상속 2가지 경우
1. 추상 클래스의 단순 상속
- 추상 클래스를 상속받아, 추상 메소드를 구현하지 않으면 추상 클래스 됨
- 서브 클래스도 abstract로 선언해야 함
2. 추상 클래스의 구현 상속
- 서브 클래스에서 슈퍼 클래스의 추상 메소드 구현(오버라이딩)
- 서브 클래스는 추상 클래스 아님
추상 클래스의 용도
- 설계와 구현 분리
- 슈퍼 클래스에서는 개념 정의 -> 서브 클래스마다 다른 구현이 필요한 메소드는 추상 메소드로 선언
- 각 서브 클래스에서 구체적 행위 구현 -> 서브 클래스마다 목적에 맞게 추상 메소드 다르게 구현
- 계층적 상속 관계를 갖는 클래스 구조 만들 때
예제 5-7 : 추상 클래스의 구현 연습
Calculator를 상속받은 GoodCalc 클래스를 구현하라.
abstract class Calculator{
public abstract int add(int a, int b);
public abstract int subtract(int a, int b);
public abstract double average(int[] a);
}
public class GoodCalc extends Calculator{
@Override
public int add(int a, int b) {
return 0;
}
@Override
public int subtract(int a, int b){
return a-b;
}
@Override
public double average(int[] a){
double sum =0;
for(int i=0; i<a.length; i++){
sum +=a[i];
}
return sum/a.length;
}
public static void main(String[] args) {
GoodCalc c = new GoodCalc();
System.out.println(c.add(2,3));
System.out.println(c.subtract(2,3));
System.out.println(c.average(new int[] {2,3,4}));
}
}
자바의 인터페이스
자바의 인터페이스
- 클래스가 구현해야 할 메소드들이 선언되는 추상형
- 인터 페이스 선언 : interface 키워드로 선언 (ex. public interface SeriaDriver {...}
- 인터페이스에는 필드(멤버 변수) 선언 불가
자바 인터페이스 사례
인터페이스의 구성 요소들의 특징
인터페이스의 구성 요소들
- 상수 : public만 허용, public static final 생략
- 추상 메소드 : public abstact 생략 가능
- default 메소드 : 인터페이스에 코드가 작성된 메소드, 인터페이스를 구현하는 클래스에 자동 상속, public접근 지정만 허용. 생략 가능
- private 메소드 : 인터페이스 내에 메소드 코드가 작성되어야 함, 인터페이스 내에 있는 다른 메소드에 의해서만 호출 가능
- static메소드 : public, private 모두 지정 가능. 생략하면 public
자바 인터페이스의 전체적인 특징
- 인터페이스의 객체 생성 불가 : ex. new PhoneInterface(); // 오류. 인터페이스 PhoneInterface 객체 생성 불가
- 인터페이스 타입의 레퍼런스 변수 선언 가능 : ex. PhoneInterface galaxy; //galaxy는 인터페이스에 대한 레퍼런스 변수
- 인터페이스 구현
- 인터페이스를 상속받는 클래스는 인터페이스의 모든 추상 메소드 반드시 구현
- 다른 인터페이스 상속 가능
- 인터페이스의 다중 상속 가능
인터페이스 구현
인터페이스의 추상 메소드를 모두 구현한 클래스 작성
- implements 키워드 사용
- 여러 개의 인터페이스 동시 구현 가능
인터페이스 구현 사례
- PhoneInerface 인터페이스를 구현한 SamsungPhone 클래스
- SamsungPhone 클래스는 PhoneInterface의 default 메소드 상속
class SamsungPhone implements PhoneInterface { //인터페이스 구현
//PhoneInterfaced의 모든 메소드 구현
public void sendCall() {System.out.println("띠리리리링");}
public void receiveCall() {System.out.println("전화가 왔습니다.");}
//메소드 추가 작성
public void flash() {System.out.println("전화기에 불이 켜졌습니다.");}
}
예제 5-8 : 인터페이스 구현
PhoneInterface 인터페이스를 구현하고 flash() 메소드를 추가한 SamsungPhone 클래스를 작성하라.
interface PhoneInterface { //인터페이스 선언
final int TIMEOUT = 10000; //상수 필드 선언
void sendCall(); //추상 메소드
void receiveCall(); //추상 메소드
default void printLogo(){ //default 메소드
System.out.println("** Phone **");
}
}
class SamsungPhone implements PhoneInterface { //인터페이스 구현
//PhoneInterface의 모든 추상 메소드 구현
@Override
public void sendCall() {
System.out.println("띠리리링");
}
@Override
public void receiveCall() {
System.out.println("전화가 왔습니다.");
}
public void flash() {System.out.println("전화기에 불이 켜졌습니다.");}
}
public class InterfaceEx{
public static void main(String[] args){
SamsungPhone Phone = new SamsungPhone();
Phone.printLogo();
Phone.sendCall();
Phone.receiveCall();
Phone.flash();
}
}
실행 결과
** Phone **
띠리리링
전화가 왔습니다.
전화기에 불이 켜졌습니다.
인터페이스 상속
인터페이스가 다른 인터페이스 상속
- extends 키워드 이용
interface MobilePhoneInterface extends PhoneInterface{
void sendSMS(); //새로운 추상 메소드 추가
void receiveSMS(); //새로운 추상 메소드 추가
}
- 다중 인터페이스 상속
interface MP3Interface {
void play(); //추상 메소드
void stop(); //추상 메소드
}
interface MusicPhoneInterface extends MobilePhoneInterface, MP3Interfacec{
void playMP3RingTone(); //새로운 추상 메소드 추가
}
인터페이스의 목적
인터페이스는 스펙을 주어 클래스들이 그 기능을 서로 다르게 구현할 수 있도록 하는 클래스의 규격 선언이며, 클래스의 다형성을 실현하는 도구이다.
**다형성(polymorphism)이란 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미합니다.
다중 인터페이스 구현
클래스는 하나 이상의 인터페이스를 구현할 수 있음
클래스에서 인터페이스의 메소드를 구현할 때 public을 생략하면 오류 발생
예제 5-9 : 인터페이스를 구현하고 동시에 클래스를 상속받는 사례
interface PhoneInterface{ //인터페이스 선언
final int TIMEOUT = 10000; //상수 필드 선언
void sendCall(); //추상 메소드
void receiveCall(); //추상 메소드
default void printLogo(){ //default 메소드
System.out.println("** Phone **");
}
}
interface MobilePhoneInterface extends PhoneInterface{
void sendSMS();
void receiveSMS();
}
interface MP3Interface{
public void play();
public void stop();
}
class PDA{
public int calculate(int x, int y){
return x+y;
}
}
class SmartPhone extends PDA implements MobilePhoneInterface, MP3Interface {
public void sendCall(){
System.out.println("따르릉따르릉~~~");
}
public void receiveCall(){
System.out.println("전화 왔어요.");
}
public void sendSMS(){
System.out.println("문자갑니다.");
}
public void receiveSMS(){
System.out.println("문자왔어요.");
}
public void play(){
System.out.println("음악 연주합니다.");
}
public void stop(){
System.out.println("음악 중단합니다.");
}
public void schedule(){
System.out.println("일정 관리합니다.");
}
}
public class InterfaceEX{
public static void main(String[] args){
SmartPhone phone = new SmartPhone();
phone.printLogo();
phone.sendCall();
phone.play();
System.out.println("3과 5를 더하면"+phone.calculate(3,5));
phone.schedule();
}
}
실행 결과
** Phone **
따르릉따르릉~~~
음악 연주합니다.
3과 5를 더하면8
일정 관리합니다.
추상 클래스와 인터페이스 비교
유사점
- 객체를 생성할 수 없고, 상속을 위한 슈퍼 클래스로만 사용
- 클래스의 다형성을 실현하기 위한 목적
다른 점
명품 JAVA Programming - 개정판 4판/ 황기태
'개발 > TIL' 카테고리의 다른 글
[명품 자바 프로그래밍] 6장 - 모듈과 패키지 개념, 자바 기본 패키지 (0) | 2023.06.03 |
---|---|
[Java] substring( ) 메소드 (0) | 2023.05.29 |
[Git] git 공부 Day2 - 로컬에서 깃허브로 (1) | 2023.05.26 |
[Git] git 공부 Day1 (0) | 2023.05.06 |
[Web] : 짧은 공부 후기-2022년 7월, 짧은 (0) | 2022.10.01 |