2020. 11. 29. 19:53ㆍ백엔드/JAVA
1. 상속(Inheritance)
1-1. 상속(Inheritance)
- 기존의 클래스로 새로운 클래스를 작성하는 것. (코드의 재사용)
- 두 클래스를 부모와 자식으로 관계를 맺어주는 것.
- 자손은 조상의 모든 멤버만 상속 받음. (생성자, 초기화블럭 제외)
- 자손의 멤버 개수는 조상보다 적을 수 없음. (같거나 많음)
- 자손의 변경은 조상에 영향을 미치지 않음(부모의 변경은 자손에게 영향을 미침)
- 상속을 받는 다는 것은 조상 클래스를 확장(extend)한다는 의미로 해석할 수도 있음
- 접근제어자(access modifier)가 private 또는 default인 멤버들은 상속되지 않는다기보다 상속은 받지만 자손 클래스로부터의 접근이 제한되는 것.
- 클래스 간의 관계에서 형제 관계와 같은 것은 없음.
- 같은 내용의 코드를 하나 이상의 클래스에 중복적으로 추가해야하는 경우에는 상속관계를 이용해서 중복을 최소화해야 함. (공통부분은 조상에서 관리하고 개별부분은 자손에서 관리)
- 자손 클래스의 인스턴스를 생성하면 조상 클래스의 멤버와 자손 클래스의 멤버가 합쳐진 하나의 인스턴스로 생성됨.
class 자식클래스 extends 부모클래스 {
//...
}
위의 그림을 살펴보면 자손(Child)클래스만의 멤버는 없지만, 자손(Child)클래스는 부모(Parent)클래스로부터 상속받고 있기 때문에 부모(Parent)클래스가 가진 멤버변수(age)를 자손(Child)클래스도 가지게 됨
( 자손(Child)클래스 멤버 개수 == 부모(Parent)클래스 멤버변수개수)
위의 그림을 살펴보면 자손(Child)클래스만의 멤버(play())를 추가 하면, 부모(Parent)클래스의 멤버 개수는 그대로 1개(자신의 멩버(age))이지만 자손(Child)클래스의 멤버 개수는 두개(상속받은 멤버(age), 자신의 멤버(play())가 되고 자손 클래스의 변경은 부모 클래스에 아무런 영향을 미치지 않음. (자손(Child)클래스 멤버 개수 > 부모(Parent)클래스 멤버변수개수)
package com.java.oop2;
//예제 7-1
class Tv{
boolean power; //전원상태(on/off)
int channel; //채널
void power() {power = !power; }
void channelUp() {++channel;}
void channelDown() {--channel;}
}
class CaptionTv extends Tv{
boolean caption; //캡션상태(on/off)
void displayCaption(String text) {
if(caption) { //캡션상테가 on(true)일 때만 text를 보여줌
System.out.println(text);
}
}
}
public class Ex7_1 {
public static void main(String[] args) {
CaptionTv ctv = new CaptionTv();
ctv.channel = 10;
ctv.channelUp();
System.out.println(ctv.channel);
ctv.displayCaption("Hello, World");
ctv.caption = true;
ctv.displayCaption("Hello World");
}
}
부모(Tv)클래스 멤버 5개이고 자손(CaptionTv)클래스 멤버는 상속받은 멤버 5개 + 자손(CaptionTv)클래스만의 멤버 2개 총 7개를 가지고 있는 상태인데, 만약 부모(Tv)클래스 멤버를 주석을 치게 되면 자손(CaptionTv)클래스가 기존의 부모멤버를 상속 받지 못하는데 사용하려 하므로 컴파일에러 발생하게 됨
이처럼 자손클래스는 부모클래스의 변경에 영향을 받음.
2. 포함 관계
2-1. 포함(composite)
- 한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것.
- 작은 단위의 클래스를 만들고, 이들을 조합해서 클래스를 만듬.
class Circle {
Point c = new Point(); //포함관계
int r;
}
3. 클래스간의 관계 결정하기
- 상속관계 '~은 ~이다. (is -a)'
- 포함관계 '~은 ~을 가지고 있다. (has -a)'
class Circle {
Point c = new Point(); //포함관계
int r;
}
class Circle extends Point{
int r;
}
원(Circle)은 점(Point)이다. - Circle is a Point ( X(부자연스러움) )
원(Circle)은 점(Point)을 가지고 있다. - Circle has a Point ( O(더 적절) )
보통 90% 이상은 포함관계를 많이 사용하고 상속은 꼭 필요한 경우 사용
package com.java.oop2;
class MyPoint{
int x;
int y;
}
class Circle extends MyPoint{ //상속
int r;
}
class MyCircle extends MyPoint{ //포함
MyPoint c = new MyPoint(); //참조변수의 초기화
int r;
}
public class InhritanceTest {
public static void main(String[] args) {
Circle c = new Circle();
c.x = 1;
c.y = 2;
c.r = 3;
System.out.println("c.x = "+c.x);
System.out.println("c.y = "+c.y);
System.out.println("c.r = "+c.r);
MyCircle mc = new MyCircle();
mc.c.x = 4;
mc.c.y = 5;
mc.r = 6;
System.out.println("mc.c.x = "+mc.c.x);
System.out.println("mc.c.y = "+mc.c.y);
System.out.println("mc.r = "+mc.r);
}
}
package com.java.oop2;
//예제 7-2 318p
public class DrawShape {
public static void main(String[] args) {
Point[] p = { new Point(100, 100),
new Point(140, 100),
new Point(200, 100)
};
Triangle t = new Triangle(p);
Circle c = new Circle(new Point(150, 150), 50);
t.draw();
c.draw();
}
}
class Shape{
String color = "black";
void draw() {
System.out.printf("[color=%s]%", color);
}
}
class Point{
int x;
int y;
Point(int x, int y){
this.x = x;
this.y = y;
}
Point(){
this(0,0);
}
String getXY() {
return "("+x+", "+y+")"; //x와 y의 값을 문자열로 반환
}
}
class Circle extends Shape{
Point center; //원의 원점좌표
int r; //반지름
Circle(){
this(new Point(0,0),100); //Circle(Point center, int r)를 호출
}
Circle(Point center, int r){
this.center = center;
this.r = r;
}
void draw() {//원을 그리는 대신에 원의 정보를 출력하도록 함
System.out.printf("[center=(%d, %d), r=%d, color=%s]%n", center.x, center.y, r, color);
}
}
class Triangle extends Shape {
Point[] p = new Point[3];
Triangle(Point[] p) {
this.p = p;
}
void draw() {
System.out.printf("[p1=%s, p2=%s, p3=%s, color=%s]%n", p[0].getXY(),p[1].getXY(), p[2].getXY(), color);
}
}
package com.java.oop2;
public class DeckTest {
public static void main(String[] args) {
Deck d = new Deck();
Card c = new Card();
System.out.println(c);
d.shuffle();
c = d.pick(0);
System.out.println(c);
}
}
class Deck{
final int CARD_NUM = 52; //카드의 개수
Card cardArr[] = new Card[CARD_NUM]; //Card객체 배열을 포함
Deck() {
int i=0;
for(int k=Card.KIND_MAX; k>0; k--) {
for(int n=0; n<Card.NUM_MAX; n++) {
cardArr[i++] = new Card(k, n+1);
}
}
}
Card pick(int index) {
return cardArr[index];
}
Card pick() {
int index = (int)(Math.random() * CARD_NUM);
return pick(index);
}
void shuffle() {
for(int i=0; i<cardArr.length; i++) {
int r = (int)(Math.random() * CARD_NUM);
Card temp = cardArr[i];
cardArr[i] = cardArr[r];
cardArr[r] = temp;
}
}
}
class Card{
static final int KIND_MAX = 4;
static final int NUM_MAX = 13;
static final int SPADE = 4;
static final int DIAMOND = 3;
static final int HEART = 2;
static final int CLOVER = 1;
int kind;
int number;
Card(){
this(SPADE, 1);
}
Card(int kind, int number){
this.kind = kind;
this.number = number;
}
public String toString() {
String[] kinds = {"", "CLOVER", "HEART", "DIAMOND", "SPADE"};
String numbers = "0123456789XJQK";
return "kind : " + kinds[this.kind] + ", number : "+numbers.charAt(this.number);
}
}
4. 단일 상속(Single Inheritance)
- Java는 단일상속만을 허용(C++은 다중상속 허용) : 만일 다중 상속을 허용한다면, 자손클래스가 여러 개의 부모를 한 번에 다중상속 하였을 때 그 부모 클래스들에 같은 이름의 메서드에 다른 기능을 구현해 놓았다면 자손 클래스에서 어떤 클래스의 메서드를 호출할지 충돌 문제가 발생할 수 있음.
- 비중이 높은 클래스 하나만 상속관계로, 나머지는 포함관계로 함.
5. Object클래스 - 모든 클래스의 최고 조상
- 부모가 없는 클래스는 자동적으로 Object클래스를 상속받게 됨.
- 모든 클래스는 Object클래스에 정의된 11개이 메서드를 상속받음 (toString(), equals(Object obj), hashCode())
package com.java.oop2;
//class MyPoint {
class MyPoint extends Object{ //명시적으로 부모 클래스를 상속받지 않으면 Object클래스를 상속(Object클래스는 모든 클래스의 최고 조상)
int x;
int y;
}
class Circle extends MyPoint{ //상속
int r;
}
class MyCircle extends MyPoint{ //포함
MyPoint c = new MyPoint();
int r;
}
public class InhritanceTest {
public static void main(String[] args) {
Circle c = new Circle();
//Circle클래스에 toString()이라는 메서드가 없는데도 Object클래스에 정의된 메서드이기에 호출 가능
System.out.println(c.toString()); //com.java.oop2.Circle@7852e922
System.out.println(c); //com.java.oop2.Circle@7852e922
// 위의 두 문장이 같음
//'println'메서드 자체가 참조변수가 들어오면 자동으로 참조변수.toSting()메서드를 호출
}
}
6. 오버라이딩(overriding)
6-1. 오버라이딩
상속받은 조상의 메서드를 자신에 맞게 변경하는 것.
내용(구현부)만 변경가능 (선언부는 변경불가).
6-2. 오버라이딩의 조건
선언부가 조상 클래스의 메서드와 일치해야 함. (이름, 매개변수,반환타입이 같아야 함)
접근 제어자가 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없음.
예외는 조상 클래스의 메서드보다 많이 선언할 수 없음.
인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없음.
package com.java.oop2;
class MyPoint3{
int x;
int y;
String getLocation() {
return "x:"+x+", y:"+y;
}
}
class MyPoint3D extends MyPoint3{
int z;
//조상의 getLocation()을 오버라이딩
String getLocation() {
return "x:"+x+", y:"+y+", z:"+z;
}
}
public class OverrideTest {
public static void main(String[] args) {
MyPoint3D p = new MyPoint3D();
p.x = 3;
p.y = 5;
p.z = 7;
System.out.println(p.getLocation());
}
}
오버라이딩 메서드는 자손 클래스가 조상 클래스의 메서드를 상속을 안 받는 것이 아니고 , 자손클래스에도 똑같은 이름의 메서드가 정의되어 있는 구현부를 실행하는 것 뿐임.
6-3. 오버로딩 VS 오버라이딩
오버로딩(overloading) : 기존에 없는 새로운 메서드를 정의하는 것(new)
오버라이딩(overriding) : 상속받은 메서드의 내용을 변경하는 것(modify)
7. super
7-1. 참조변수 super
인스턴스 메서드(생성자) 내에서만 존재함.
조상의 멤버를 자신의 멤버와 구별할 때 사용.
7-2. 조상의 생성자 super()
조상의 생성자를 호출할 때 사용
조상의 멤버는 조상의 생성자를 호출해서 초기화
8. 패키지(package)
8-1. 패키지
- 서로 관련된 클래스의 묶음.
- 클래스는 클래스 파일(*.class), 퍄키지는 폴더, 하위 패키지는 하위 폴더.
- 클래스의 실제 이름(full name)은 패키지를 포함. (java.lang.String).
- 'rt.jar'는 클래스들을 압축한 파일(JDK설치경로\jre\lib에 위치) ->java9부터 'rt.jar'파일 없어짐. (jar는 클래스파일 묶어놓은 것/압축파일, jar.exe로 압축풀 수 있음)
8-2. 패키지의 선언
- 패키지는 소스파일의 첫 번째 문장으로 단 한번 선언.
- 같은 소스 파일의 클래스들은 모두 같은 패키지에 속하게 됨.
- 페키지 선언이 없으면 이름없는(unnamed) 패키지에 속하게 됨.
8-3. 클래스 패스(classpath)
- 클래스 파일(*.class)의 위치를 알려주는 경로(path).
- 환경변수 classpath로 관리하며, 경로간의 구분자는 ';(세미콜론)'을 사용.
- classpath(환경변수)에 패키지의 루트를 등록해줘야 함.
이클립스 없이 명령프롬프트(cmd)창을 열어서 확인할 클래스 파일의 경로를 확인한 뒤
cmd에 클래스 파일의 경로 입력시 아래와 같이 출력 내용 확인 가능함.
그런데 이렇게 하나하나 경로를 접속하는 것이 번거로우므로 '환경변수'에 'classpath'를 지정해주면
'패키지명+클래스명'만 입력해주면 컴퓨터가 알아서 classpath를 읽어서 해당 파일을 불러올 수 있음
환경변수 설정방법
환경변수 확인 명령어
>set classpath
>set classpath
환경변수 추가 명령어
>set classpath=[설정경로1]:[설정경로2]
//':'를 기준으로 환경변수경로 추가 가능
9. import
9-1. import문
- import문을 사용하면, 클래스를 사용할 때 패키지이름을 생략할 수 있음.
- 컴파일러에게 클래스가 속한 패키지를 알려줌.
- java.jang패키지의 클래스는 import하지 않고도 사용할 수 있음. (자주 사용하는 핵심클래스들이어서 패키지 생략가능함) String, Object, System, Thread, ...
9-2. import문 선언
- import문은 패키지문과 클래스선언의 사이에 선언함.
- import문을 선언하는 방법.
import 패키지명.클래스명;
또는
import 패키지명.*;
- import문은 컴파일 시에 처리되므로 프로그램의 성능에 영향없음.
- 아래의 두 코드는 서로 의미가 다름
- 이름이 같은 클래스가 속한 두 패키지를 import할 때는 클래스 앞에 패키지명을 붙여줘야 함.
9-3. static import문
- static멤버(static변수, static메서드)를 사용할 때 클래스 이름을 생략할 수 있게 해줌.
10. 제어자(modifier)
10-1. 제어자(modifier)
- 클래스와 클래스의 멤어(멤버 변수, 메서드)에 부가적인 의미 부여
- 접근제어자는 아래 4개 중에 1만 사용 가능. (default)는 아무것도 안 붙이는 것을 의미
- 하나의 대상에 여러 제어자를 같이 사용가능(접근 제어자는 하나만 사용가능)
public class ModifierTest{
public static final int WIDTH = 200; //여러개의 제어자를 사용
public static void main(String[] args){
System.out.println("WIDTH="+WIDTH);
}
}
10-2. static - 클래스의, 공통적인
class staticTest{
static int width = 200; //클래스변수(static변수)
static int height = 120; //클래스변수(static변수)
static{ //클래스 초기화 블럭
//static변수의 복잡한 초기화 수행
}
static int max(int a, int b){//클래스 메서드(static메서드)
return a > b ? a : b;
}
}
10-3. final - 마지막의, 변경될 수 없는
final 클래스의 예 ) String, Math
final class finalTest{ //조상이 될 수 없는 클래스
final int MAX_SIZE = 10; //값을 변경할 수 없는 멤버변수(상수)
final void getMaxSize(){ //오버라이딩할 수 없는 메서드(변경불가)
final int LV = MAX_SIZE; //값을 변경할 수 없는 지역변수(상수)
return MAX_SIZE;
}
}
10-4. abstract - 추상의, 미완성의
- 추상 메서드는 미완성메서드
- 추상 클래스는 미완성설계도 -> 제품 생성 불가
- 추상 클래스의 인스턴스 생성불가
- 추상 클래스를 상속 받아서 완전한 클래스를 만든 후에 객체 생성가능
abstract class AbstractTest{//추상 클래스(추상 메서드를 포함한 클래스)
abstract void move(); //추상 메서드(구현부가 없는 메서드)
}
10-5. 접근제어자(access modifier)
private 같은 클래스 내에서만 접근이 가능 / 멤버 앞에 사용가능
(default) 같은 패키지 내에서만 접근이 가능 / 멤버, 클래스 앞에 사용가능
protected 같은 패키지 내에서, 그리고 다른 패키지의 자손 클래스에서 접근이 가능 / 멤버 앞에 사용가능
public 접근 제한이 전혀 없음 / 멤버, 클래스 앞에 사용가능
- public > protected > (default) > private
package com.java.oop2;
public class MyParent{
private int prv; //같은 클래스
int dft; //같은 패키지
protected int prt; //같은 패키지 + 자손(다른 패키지)
public int pub; //접근제한 없음.
public void printMembers() {
System.out.println(prv); //OK
System.out.println(dft); //OK
System.out.println(prt); //OK
System.out.println(pub); //OK
}
}
class MyParentTest {
public static void main(String[] args) {
MyParent p = new MyParent();
//System.out.println(p.prv);// ERROR(같은 클래스 내에서만 사용가능)
System.out.println(p.dft); //OK
System.out.println(p.prt); //OK
System.out.println(p.pub); //OK
}
}
//다른패키지
package com.java.oop2_1;
import com.java.oop2.MyParent;
//MyParent와 다른 패키지 자손 클래스 선언
class MyChild extends MyParent{
public void printMembers() {
//System.out.println(prv); //ERROR(같은 클래스 내에서만 사용가능)
//System.out.println(dft); //ERROR(같은 패키지 내에서만 사용가능)
System.out.println(prt); //OK
System.out.println(pub); //OK
}
}
//MyParent와 다른 패키지 , 자손관계도 아님
public class MyParentTest2 {
public static void main(String[] args) {
MyParent p = new MyParent();
//System.out.println(p.prv); //ERROR(같은 클래스 내에서만 사용가능)
//System.out.println(p.dft); //ERROR(같은 패키지 내에서만 사용가능)
//System.out.println(p.prt); //ERROR(같은 패키지 혹은 다른 패키지 자손에서만 사용가능)
System.out.println(p.pub); //OK(접근제한이 없음)
}
}
10-6. 캡슐화와 접근 제어자
접근 제어자를 사용하는 이유
- 외부로부터 데이터를 보호하기 위해서.
- 외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해서.
- 접근제어자는 최대한 좁은 범위로 선언하는 것이 좋고 필요에 맞게 넓혀야 함. (접근제어자의 범위가 좁으면 좁을수록, 코드가 수정되면 테스트의 범위가 좁아져 효율적임)
package com.java.oop2;
class Time{
private int hour; //0~23사이의 값을 가져야함
private int minute;
private int second;
public void setHour(int hour) {
if(isNotValidate(hour)) return;
this.hour = hour;
}
//매개변수로 넘겨진 hour가 유효한지 화인해서 알려주는 메서드
//좀 더 명시적인 메서드기능을 정의하기 위해서 같은 클래스내에서만 사용하는 메서드이므로 접근제어자를 'private'으로 함
//접근제어자를 'private'으로 해 놓으면 해당 메서드를 수정했을 때 어차피 자신의 클래스 내에서만 사용하는 메서드라는게 명시적이므로 테스트 하기가 수월함(효율적)
//따라서 접근제어자의 범위는 좁혀 놓고 필요에 의해 넓혀 나가는 것이 좋음
private boolean isNotValidate(int hour) {
return hour < 0 || hour > 23;
}
public int getHour() {return hour;}
}
public class TimeTest {
public static void main(String[] args) {
Time t = new Time();
//t.hour = -100; //ERROR('private'으로 선언된 변수이므로 직접 접근 할 수 없고 메서드로 값을 변경할 수 있음)
t.setHour(21); //hour의 값을 메서드를 이용하여 21로 변경
System.out.println(t.getHour());
t.setHour(100);
}
}
11. 다형성(polymorphism)
11-1. 다형성이란?
- 여러 가지 형태를 가질 수 있는 능력.
- 조상 타입 참조 변수로 자손 타입 객체를 다루는 것.
- 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 없음.
11-2. 참조변수의 형변환
- 사용할 수 있는 멤버의 갯수를 조절하는 것.
- 조상과 자손 관계의 참조변수는 서로 형변환이 가능함.
- 참조변수가 참조하는 실제 인스턴스가 중요함.(참조변수가 참조하는 실제 인스턴스의 멤버개수가 참조변수의 멤버개수보다 더 적으면 안됨)
package com.java.oop2;
class Car{
String color;
int door;
public void drive() {}
public void stop() {}
}
class FireEngine extends Car{
public void water() {}
}
public class Ex7_7 {
public static void main(String[] args) {
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
fe.water();
car = fe; //car = (Car)fe;
//car.water(); //컴파일 에러. Car타입의 참조변수로는 water()를 호출할 수 없음
fe2 = (FireEngine)car; //자손타입 <- 조상타입 형변환생략불가
fe2.water();
}
}
package com.java.oop2;
class Car{
String color;
int door;
public void drive() {
System.out.println("drive!!!!!!!!!!!!!!!!!!!");
}
public void stop() {
System.out.println("stop!!!!!!!!!!!!!!!!!!!!");
}
}
class FireEngine extends Car{
public void water() {
System.out.println("water!!!!!!!!!!!!!!!!!!!");
}
}
public class Ex7_7 {
public static void main(String[] args) {
Car c = new Car();
FireEngine fe = (FireEngine)c; //형변환 실행 에러 java.lang.ClassCastException
fe.water(); //컴파일은 OK
}
}
출처 : 자바의 정석[도우출판 - 남궁성 지음] (교재) , 자바의 정석 기(유튜브)
[상속] www.youtube.com/watch?v=Pgutf0G3nE4&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=69
[클래스 간의 관계, 상속과 포함] www.youtube.com/watch?v=ukxiyoDaSXk&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=70
[단일상속, Object클래스] www.youtube.com/watch?v=-2QDwrc1j38&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=71
[오버라이딩] www.youtube.com/watch?v=0xzi_FQm0ek&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=72
[참조변수 super(), 생성자 super()] www.youtube.com/watch?v=XT9KmsEk9f8&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=73
[패키지, 클래스 패스] www.youtube.com/watch?v=hcHJgmX8VlA&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=74
[import문, static import문] www.youtube.com/watch?v=BS1Pxm5XVNM&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=75
[제어자,static, final, abstract] www.youtube.com/watch?v=Hmu7YH8AXmI&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=76
[접근제어자] www.youtube.com/watch?v=Qm08p4Vk2sw&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=77
[캡슐화와 접근 제어자] www.youtube.com/watch?v=3NuVD8eOMfc&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=78
[다형성] www.youtube.com/watch?v=fw7Nm_li0pE&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=79
[참조변수의 형변환1] www.youtube.com/watch?v=XP8zpt-yFZs&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=80
[참조변수의 형변환2] www.youtube.com/watch?v=3lSPC37IaNQ&list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp&index=81
[]
'백엔드 > JAVA' 카테고리의 다른 글
[JAVA] JAVA 파일경로 관련 정리 (0) | 2021.01.03 |
---|---|
[JAVA] Java Resource 사용(getResource(),getResourceAsStream()) (0) | 2020.12.28 |
[JAVA] JAVA 프로그램 실행구조 / JAVA 환경변수설정 (0) | 2020.12.28 |
[JAVA] cmd에서 자바 컴파일 후 실행 방법 (0) | 2020.12.28 |
[JAVA] 객체지향프로그래밍1 (0) | 2020.08.11 |