-
[자바] 객체지향(1)자바 2022. 7. 7. 10:20728x90
1. 객체지향언어
객체 지향 언어의 장점
1. 코드의 재사용성
2. 코드의 관리가 용이하다
3. 신뢰성이 높은 프로그래밍을 가능하게 한다.
객체지향개념을 학습할 때 재사용성, 유지보수, 중복된 코드 제거의 관점에서 바라보면 된다.2. 클래스
클래스와 객체의 정의 및 용도
클래스의 정의 : 클래스란 객체를 정의해 놓은 것이다.
클래스의 용도 : 객체를 생성하는데 사용
객체의 정의 : 실제로 존재하는 것
객체의 용도 : 객체가 가지고 있는 기능과 속성에 따라 다름클래스의 또 다른 정의
클래스 - 데이터 + 함수
1. 변수 - 하나의 데이터를 저장할 수 있는 공간
2. 배열 - 같은 종류의 여러 데이터를 하나의 집합으로 저장할 수 있는 공간
3. 구조체 - 서로 연관된 여러 데이터를 종류에 관계없이 하나의 집합으로 저장할 수 있는 공간
4. 클래스 - 데이터와 함수의 결합 ( 구조체 + 함수 )클래스 - 사용자정의 타입
서로 관련된 변수들을 묵어서 하나의 타입을 새로 추가하는 것
int hour; int minute; int second; //등을 하나로 묶어서 클래스로 표현하는 방법 class Time { int hour; int minute; int second; }
인스턴스
- 객체를 만드는 과정을 클래스의 인스턴스화라고 한다. ( 클래스 ----(인스턴스화)----> 인스턴스(객체) )
속성(property) - 멤버 변수, 특성, 필드, 상태
기능(function) - 메서드, 함수, 행위- 인스턴스 생성과 사용
클래스명 변수명 = new 클래스명(); // 생성한 클래스의 객체를 참조변수 선언후, 참조변수에 저장 Tv t = new Tv(); // Tv라는 클래스 타입, t 라는 참조변수 // Tv는 생성자다 // t는 참조변수이다.
참고, 참조변수의 초기값은 NULL 이다.
약간의 TIP
인스턴스는 참조변수를 통해서만 다룰 수 있으며, 참조변수 타입은 인스턴스 타입과 일치해야한다.
Tv t; t = new Tv(); // 여기서 Tv의 t라는 참조변수와 // new Tv()를 나타내는 t라는 인스턴스 타입이 일치해야한다는 뜻이다. 고로 Tv t = new Tv(); 이렇게 표현이 가능하다
class Tv { /* ... 여러 코드 있음 ... */ } class TvTest { public static void main(String[] args) { Tv t1 = new Tv(); // 참조변수로 받으니까 기본값 null이 있음 Tv t2 = new Tv(); t2 = t1; // point t1.channel = 7; // t1을 7로 바꾸니까 t2로 7로 바꿈 } }
POINT
- t2가 가지고 있던 값은 잃어버리게 되고, t1에 저장되어 있던 값이 t2에 저장되게 된다. 따라서, t2의 기존 null 값은 사라지고 t2에는 t1의 값이 들어오게 되어서 t2도 7로 바뀌게 된다.
=> 참조변수에 새로운 주소를 넣으면, 기존의 참조변수의 주소값은 사라지게 된다.
( 새로운 t2가 기존의 t2를 덮어버린다고 생각하기 )
참조변수와 인스턴스의 관계 - 하나의 인스턴스를 여러개의 참조변수를 가리키는것은 가능하다
- 여러 인스턴스를 하나의 참조변수가 가리키는것은 불가능하다
객체 배열
객체를 생성해서 객체 배열의 각 요소에 저장하는 것을 잊으면 안된다.
Tv[] tvArr = new Tv[3]; 이렇게하면 기본값인 null로 저장이 된다 // 1 way Tv[] tvArr = new Tv[3]; tvArr[0] = new Tv(); tvArr[1] = new Tv(); tvArr[2] = new Tv(); // 2 way Tv[] tvArr = { new Tv(), new Tv(), new Tv() } // 3 way 객체의 수가 많을땐 for문 사용하기 Tv[] tvArr = new Tv[100]; for( int i = 0; i < tvArr.length; i++) { tvArr[i] = new Tv(); }
3. 변수와 메소드
변수
- 변수의 종류를 결정짓는 중요한 요소는 변수의 선언된 위치 이다.
- 멤버 변수를 제외한 모든 변수들은 지역변수이며, 멤버 변수 중 static이 붙은것은 클래스 변수, 붙지 않은것은 인스턴스 변수이다.
{ int iv; // 멤버 변수 - 인스턴스 변수 static int cv; // 멤버 변수 - 클래스 변수 void method1() { // 메서드 영역 int lv = 0; // 지역 변수 } }
변수 종류 선언위치 생성시기 클래스 변수 클래스 영역 클래스가 메모리에 올라갈 때 인스턴스 변수 인스턴스가 생성되었을때 지역변수 클래스 영역 이외의 영역 ( 메서드, 생성자, 초기화 블럭 내부 ) 변수 선언문이 수행되었을때 인스턴스 변수
- 인스턴스를 먼저 생성하여 인스턴스 변수의 값을 읽거나 저장한다
- 독립적인 저장공간을 가지므로 서로 다른 값을 가질 수 있다.
클래스 변수 ( 전역변수다! 이런식으로 생각하기 )
- 인스턴스 변수 앞에 static을 붙이면 된다.
- 인스턴스가 공통된 저장공간(변수)을 공유한다
- 클래스 변수의 선언이 필요한 경우는 한 클래스의 모든 인스턴스들이 공통적인 값을 유지하는 속성의 경우이다
클래스이름.클래스변수 -> Variables.cv == 전역변수 특징을 갖음 ( 예시 - Variavles라는 클래스이다. )
or
Variables v1 = new Variables();
v1.pi라고 선언 ( pi는 클래스 멤버 라고 할때)// 포커 카드를 생각해보자 // 각 카드들은 카드의 무늬나 숫자가 다르지만, 카드 자체의 크기 즉, 같은 폭과 너비를 갖는다 class Card{ String kind; int number; static int width = 100; static int height = 100; } // width와, height는 공통된 값을 가지므로 static이 붙여서 클래스 변수로 선언한다 class CardTest { public static void main(String[] args) { Card c1 = new Card(); c1.width; // 이렇게 사용하고 c1.height = 100; // 이렇게 값 변경이 가능하다 } }
메소드의 선언과 구현
// 선언부 반환타입 메서드의 이름 ( 타입변수명, 타입변수명, ... ) // 구현부 { // 메소드 호출시 수행될 코드 } // 선언부 int add(int a, int b) // 구현부 { int res = a+b; return res; }
add 메소드의 반환타입이 int이므로, res의 타입이 int가 되어야 한다.
선언부
- 두 변수(매개변수)의 타입이 같아도 변수의 타입을 생략할 수 없다.
- 반환 타입에는 반환값의 타입을 적고, 반환값이 없는 경우에는 void 작성구현부
- return 문에서 반환타입이 void가 아니면, 'return 반환값' 이 반드시 포함되어야 한다.
- 이 값의 타입은 반환 타입과 일치하거나 적어도 자동 형변환이 가능한 것이여야 한다.
-> 자동 형변환은 int가 반환타입이면 return에는 int이상인 float등이 되어야 한다void의 경우 return문이 없는 이유는, 자동적으로 return; 을 반환하기 때문이다.
메소드의 호출
- 인자와 매개변수는 인자의 개수와 순서가 메서드에 선언된 변수와 일치해야한다
메소드의 실행흐름
- 같은 클래스 내의 메소드글 끼리는 참조변수를 사용하지 않고도 서로 호출이 가능하지만, static 메서드는 같은 클래스 내의 인스턴스 메서드를 호출할 수 없다
class MyMathTest { public static void main(String[] args) { MyMath mm = new MyMath(); long res1 = mm.add(5L,3L); long res2 = mm.substract(5L,3L); long res3 = mm.multiply(5L,3L); double res4 = mm.divide(5L,3L); System.out.println("res1"); System.out.println("res2"); System.out.println("res3"); System.out.println("res4"); } } class MyMath { long add(long a, long b) { long res = a+b; return res; // return a + b; 랑 같음 } long substract(long a, long b) { long res = a-b; return res; } long multiply(long a, long b) { long res = a*b; return res; } double divide(double a, double b) { long res = a/b; return res; } }
return 문
- return문은 메서드를 종료하고 호출한 메서드로 되돌아간다 ( void가 아니면 return문 하나는 적어도 있어야 한다 )
매개변수 유효성 검사!!
- 메서드 구현부 {} 를 작성할때, 매개변수의 값이 적절한지 체크하기
if ( ) { System.out.println("불가능"); return 0; } 이런식으로 꼭 매개변수 유효성 체크하기
public static String[] getMembers() { String[] members = {"최진", "최유", "한이"}; return members; } public static void main(String[] args) { String[] members = gerMembers(); // 메서드를 이런식으로 return을 통해서 배열로 받을 수 있다. }
JVM 메모리 구조
- 메서드 영역
1. 클래스 파일을 분석해 클래스에 대한 정보 ( 클래스 데이터 )를 저장
2. 클래스 변수 저장
- 힙
1. 인스턴스 저장
- 호출스택
1. 메서드의 작업에 필요한 메모리 공간을 제공
2. 메서드가 작업을 마치면 할당되었던 메모리 공간은 반환
메모리 할당 flow
- 메서드 호출 -> 필요한 메모리를 스택에 할당받음
- 메서드 수행 끝 -> 메모리 반환하고 스탹에서 제거
- 호출 스택 맨위의 메서드가 현재 실행중이 메서드
- 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드class callStack { public static void main(String[] args) { firstMethod(); // static 메서드는 객체 생성 없이 호출 가능하다. } static void firstMethod() { secondMethod(); } static void secondMethod() { System.out.println("second"); } }
객체를 생성하지 않고, 메서드를 호출할 수 있으려면, 메서드 앞에 static을 붙여야한다.
class className { public static void main(String[] args) { firstMethod(); // 이렇게 객체 없이 메소드 호출 } static void firstMethod() { } }
기본형 매개변수와 참조형 매개변수
참조 vs 기본
- 기본형 매개변수는 8가지 기본타입을 생각하고, 나머지는 모두 참조형 매개변수이다.
- 기본형은 기본형 값이 복사된다. ( 변수의 값을 읽기만 할 수 있다. )
- 참조형은 인스턴스의 주소값이 복사된다. ( 변수의 값을 읽고 변경할 수 있다. )
기본형
class Data { int x; } class primitiveParamex { public static void main(String[] args) { Data d = new Data(); d.x = 10; System.out.println(d.x); // 10 change(d.x); System.out.println("after"); System.out.println(d.x); // 10 } static void change(int x) { // 기본형 매개변수 x = 1000; System.out.println("chagne" + x); // 1000 } }
change라는 메소드 내에서 x의 값을 불러와서 그 안에서만 변경 가능하고, main 함수까지 영향을 끼치지 못함 ( 지역변수같이 )
참조형
// first example class Data { int x; } class ReferenceParamex { public static void main(String[] args) { Data d = new Data(); d.x = 10; System.out.println(d.x); change(d); System.out.println("after"); System.out.println(d.x); } static void change(Data d) { // 참조형 매개변수 x = 1000; System.out.println("chagne" + d.x); // 여기서 x가 아닌 d.x } } // second example class referenceParams { public static void main(String[] args) { int[] x = {10}; System.out.println(x[0]); change(x); System.out.println("after change"); System.out.println(x[0]); } static void change(int[] x) { // 참조형 매개변수 x[0] = 1000; System.out.println(x[0]); } }
참조형 매개변수는 ' 값이 저장된 주소 ' 를 가져온다. ( 기본형처럼 값을 가져오는것이 아니다. )
참조형 반환타입
- 매개변수뿐만 아니라 반환타입도 참조형이 될 수 있다.
- 모든 참조형 타입의 값은 ' 객체의 주소 ' 이다.
반환타입이 참조형이라는 것은 메서드가 '객체의 주소'를 반환한다는 의미이다.
static Data copy(Data d) { // Data d가 참조형 매개변수일때, Data tmp = new Data(); tmp.x = d.x; return tmp;// 반환타입이 참조형이다 -> 메서드가 객체의 주소를 반환한다는 의미이다. }
재귀호출
- 조건문이 필구적으로 필요하다 ( 메서드를 종료시키기 위함 )
int factorial(int n) { int res = 0; // 조건문 if( n == 1 ) res = 1; else res = n * factorial(n-1); return res; }
- 매개변수의 유효성 체크가 필수적
static int factorial(int n) { if (n <= 0 || n > 12 ) return -1; // 이 부분이 유효성 체크하는 부분 if ( n == 1 ) return 1; return n* factorail(n-1); }
클래스 메서드와 인스턴스 메서드
- 인스턴스 메서드는 메서드의 작업을 수행하는데 인스턴스 변수를 필요로 하는 메서드이다.
- 인스턴스와 관계없는 메소드는 전부 클래스 메서드로 static을 붙인다.
1. 클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통으로 사용하는 것에 static을 붙인다.
-> 모든 인스턴스에서 같은 값이 유지되어야 하는 변수는 static을 붙인다.
2. 클래스 변수는 인스턴스를 생성하지 않아도 사용할 수 있다.
3. 클래스 메서드는 인스턴스 변수를 사용할 수 있다.
4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 고려한다.
-> 인스턴스 변수를 필요로 한다면, static을 붙이지 못한다.모든 인스턴스에 공통된 값을 유지해야하는것이 있으면 static
클래스 멤버와 인스턴스 멤버간의 참조와 호출
클래스 멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우는 인스턴스를 생성해야 한다.
이유 : 인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재 하지만, 클래스 멤버가 존재하는 시점에 인스턴스 멤버가 존재하지 않을 수도 있기 때문이다
if 클래스 멤버 O -> 인스턴스 멤버 O ( O )
if 인스턴스 멤버 O -> 클래스 ? 있을지 없을지 모름 ? ( X )- 같은 클래스 내의 메서드는 서로 객체의 생성이나 참조변수 없이 직접 호출
- static메서드는 인스턴스 변수를 사용할 수 없다.
class Test { int iv; static int cv; void instanceMethod() { // 인스턴스 메소드 System.out.println(iv); System.out.println(cv); } static void staticMethod() { // static 메소드 System.out.println(iv); // 에러 : 인스턴스 변수 사용 불가 System.out.println(cv); } }
class MemberCall { int iv = 10; static int cv =20; int iv2 = cv; // static int cv2 = iv; // 에러. 클래스 변수는 인스턴스 변수를 사용할 수 없음 static int cv2 = new MemberCall().iv; // 이처럼 객체를 생성해야 사용 가능 static void staticMethod1 () { System.out.println(cv); // System.out.println(iv); // 에러. 클래스 메서드에 인스턴스변수 사용불가 MemberCall c = new MemberCall(); System.out.println(c.iv); // 객체룰 생성한 후에야 인스턴스 변수의 참조가능 } void instanceMethod1() { System.out.println(cv); System.out.println(iv); // 인스턴스 메서드에서는 인스턴스변수를 바로 사용가능 } static void staticMethod2 () { staticMethod1(); // instanceMethod1(); // 에러. 클래스메서드에서는 인스턴스메서드를 호출할 수 없음. MemberCall c = new MemberCall(); c.instanceMethod1(); // 인스턴스 생성한 후 호출가능 } void instanceMethod2() { // 인스턴스메서드에서는 인스턴스 메서드와 클래스메서드 staticMethod1(); // 모두 인스턴스 생성없이 바로 호출이 가능하다. instanceMethod1(); } }
4. 오버로딩
- 하나의 클래스 내에 같은 이름의 메서드를 여러개 정의하는 것을 의미한다
오버로딩의 조건
1. 메서드의 이름이 같아야 한다
2. 매개변수의 개수 또는 타입이 달라야한다오버로딩 주의
변환타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다
// 오버로딩으로 간주 x int add(int a, int b) { return a+b; } long add(int a, int b) { return (long)(a+b); } // 오버로딩으로 간주 o long add(int a, long b) { return a+b; } long add(long a, int b) { return a+b; }
오버로딩 장점
1. 기억하기 쉽고 이름도 짧게 할 수 있다.
2. 메서드의 이름으로 쉽게 예측 가능가변인자와 오버로딩
- 가변인자는 JDK 1.5이후로 생겼으며, 매개변수가 고정적이지 않고 가변적으로 변할수있는 인자를 의미
사용 예시 ( '타입... 변수명' )
public PrrintStream prinf(String format, Object... args) { } String concatenate(String... str) {}
가변 인자는 인자중에서 가장 마지막 위치에 있어야 한다
-> 2번째 예시 처럼 여러 문자열을 하나로 결합하여 반환하는 concatenate 같은 메서드를 만들때 효율적이다
주의
- 가변인자는 오버로당을 안하는게 낫다
5. 생성자
- 인스턴스가 생성될 때 호출되는 ' 인스턴스 초기화 메서드 '
생성자는 메서드 처럼 클래스 내에 선언되며, 리턴값이 없고, 오버로딩이 가능하다.
1. 생성자의 이름은 클래스의 이름과 같아야 한다
2. 생성자는 리턴 값이 없다. ( void 사용하지 않고, 단지 아무 것도 적지 않는다. )
3. 생성자도 오버로딩이 가능하다 ( 하나의 클래스에 여러개의 생성자가 가능하다 )class Card { Card() { // 매개변수가 없는 생성자 } Card(String k, int num) { // 매개변수가 있는 생성자 } }
- 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것이 아니다.
Card c = new Card(); 클래스타입 참조변수 = new 생성자(); 클래스이름() == 생성자 인것이다
기본 생성자
- 컴파일 할때, 소스파일(*.java)의 클래스에 생성자가 하나도 정의되지 않은 경우 컴파일러는 자동적으로 기본 생성자를 추가하여 컴파일함
-> 기본 생성자는 매개변수없고 아무런 내용이 없는 간단한 것이다.
매개 변수 있는 생성자
매개변수를 갖는 생성자를 사용하는 것은 코드를 간결하게 만든다
// 매개변수 없는 생성자 사용 Car c = new Car(); c.color = "white"; // 매개변수 있는 생성자 사용 Car c = new Car("white"); Car c = new Car("white","auto",4);
생성자에서 다른 생성자 호출하기 - this(), this
생성자 간에도 서로 호출이 가능하다
- 생성자 이름으로 클래스 이름 대신 this 사용
- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫줄에서만 호출 가능class Car { String color; String type; int door; Car() { this("white", "auto", 4); } /* 위랑 같은 코드 Car() { color = "white"; type = "auto" door = 4; } */ Car(String color) { this(color, "auto", 4); } }
this : 인스턴스 자신을 가리키는 참조변수 ( 인스턴스의 주소가 저장되어 있다 )
this(), this(매개변수) : 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용한다.생성자를 이용한 인스턴스 복사
현재 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성지 이용할 수 있다.
두 인스턴스가 같은 상태를 갖는다는 것은 두 인스턴스의 모든 인스턴스 변수(상태)가 동일한 값을 갖고 있다는 뜻
class Car { String color; String type; int door; // 복사를 위한 생성자 Car(Car c) { color = c.color; type = c.type; door = c.door; } } class Test{ public static void main(String[] args) { Car c1 = new Car(); Car c2 = new Car(c1); // c1의 복사본 c2를 생성한다 } }
알아야 할점은 c1값들이 변경이 되어도 c2는 영향을 받지 않는다.
인스턴스 생성시 2가지 사항 결정하기
1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것 인가?
2. 생성자 - 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?'자바' 카테고리의 다른 글
[자바] 객체지향(2) - 2 (0) 2022.07.13 [자바] 객체지향(2) - 1 ( 패키지 ) (0) 2022.07.09 [자바] 배열의 활용 (0) 2022.07.01 [자바] 배열 (0) 2022.06.28 [자바] JDK1.5 이후 향상된 for문 (0) 2022.06.27