* 본 포스팅은 제가 국비지원교육을 받으며 노션에 정리한 내용을 옮겨놓은 것입니다.
발전을 위한 피드백과 지적은 언제나 환영합니다.
6.1 객체지향 프로그래밍
6.1.1 객체란?
객체 → 사물
객체지향 → 사물을 만드는 방법들
ex)
사람
이름, 나이 (속성) → 필드
웃다, 걷다 (동작) → 메소드
자동차
색상, 모델명 (속성) → 필드
달린다, 멈춘다 (동작) → 메소드
6.1.2 객체의 상호작용
객체들은 각각 독립적으로 존재하고, 다른 객체와 서로 상호작용.
이 상호작용 수단이 메소드
객체가 다른 객체의 기능을 이용하고 싶을 때에는? 메소드 호출
int result = Calculator.add(10, 20);
// Calculator 클래스의 add 메소드 호출 (매개변수 두개)
-> 변수 result 에 저장
6.1.3 객체간의 관계
- 사용관계 : 객체간의 상호작용 (사람과 자동차)
- 상속관계 : 상위(부모) 객체를 기반으로 하위(자식) 객체를 생성하는 관계 일반적으로 상위 객체는 종류, 하위 객체는 구체적인 사물을 의미함 "자동차는 기계의 종류이다" → 기계(상위)와 자동차(하위)는 상속 관계에 있음
6.1.4 객체 지향 프로그램의 특징
- 캡슐화(Encapsulation) : 객체의 필드, 메소드를 하나로 묶고 실제 구현 내용을 감추는 것 함부로 건드릴 수 없도록 접근제한자 (Access Modifier) 를 사용하여 코드를 클래스라는 캡슐에 씌운다.
- 상속(Inheritance) : 상위 객체인 부모의 기능을 하위 객체인 자식이 사용할 수 있도록 하는 것. 재사용을 통해 하위객체를 쉽고 빠르게 설계 가능, 반복된 코드의 중복을 줄여줌.
- 다형성(Polymorphism) : 같은 타입이지만 실행 결과가 다양한 객체를 이용할 수 있는 성질. 부모 타입에는 자식 객체가 대입될 수 있고, 인터페이스 타입에는 모든 구현 객체가 대입될 수 있다.
6.2 객체와 클래스
자바에서는 설계도가 클래스
클래스 : 객체를 생성하기 위한 필드와 메소드가 포함.
인스턴스 : 클래스로부터 만들어진 객체
(자동차 객체는 자동차 클래스의 인스턴스)
인스턴스화 : 클래스로부터 객체를 만드는 과정
하나의 클래스로 여러개의 인스턴스를 만들 수 있다.
Print p1 = new Print();
Print p2 = new Print();
//p1, p2 : 인스턴스
6.3 클래스 선언
- 일반적으로 하나의 파일에는 하나의 클래스
- 파일 이름과 동일한 이름의 클래스 선언에만 public 접근 제한자를 붙일 수 있다.
6.4 객체 생성과 클래스 변수
new 클래스명();
→ 메모리에 올라가지만 어디인지는 알 수 없다. 한번 쓰고 버리는 경우에 사용하고, idle time 에 garbage collection을 돌려서 정리함. (자바는 누군가가 참조하지 않은 객체는 정리한다)
//1번 방법
Car car;
car = new Car();
//2번 방법
Car car = new Car();
→ new 연산자는 heap 영역에 객체를 생성시킨 후 객체의 주소를 리턴하는데, 이 리턴한 주소를 저장하는 곳이 변수이다. (예시에서는 변수 car)
이렇게 new 연산자로 객체를 생성하고 리턴한 객체의 주소를 변수에 저장하면, 힙 영역에 있는 객체의 주소를 스택 영역의 변수가 저장한다. (주소만 기억하고 있다!)
※기본형 8가지 빼고는 다 참조형!
이렇게 하나의 클래스로 인스턴스를 여러개 생성할 경우, 각각의 Print 객체는 자신만의 고유 데이터를 가지면서 메모리에서 활동하게 된다.
Print p1 = new Print();
Print p2 = new Print();
//p1, p2 : 인스턴스
p1과 p2가 참조하는 Print 객체는 완전히 독립된 서로 다른 객체이다.
- 같은 객체인데 왜 서로 다른 주소를 참조할까? ⇒ New 로 생성했기 때문에.
- 라이브러리용과 실행용 클래스 : 프로그램 전체에서 사용되는 클래스가 100개라면 99개가 라이브러리, 1개가 실행 클래스 실행클래스는 main() 메소드에서 실행 진입점만 제공.
- 라이브러리 코드 : 필드, 생성자, 메소드
6.5 클래스의 구성 멤버
6.5.1 필드
- 변수는 생성자와 메소드 내에서만 사용되고 생성자와 메소드가 실행 종료되면 자동 소멸.
- 필드는 생성자와 메소드 전체에서 사용되며 객체와 함께 존재함
6.5.2 생성자
- 생성자는 new 연산자로 호출되는 특별한 중괄호 { } 블록
- 객체 생성시 초기화를 담당
- 필드를 초기화하거나, 메소드를 호출해서 객체를 사용할 준비를 함.
- 클래스이름과 같고 리턴 타입이 없다.
6.5.3 메소드
- 필드를 읽고 수정하는 역할
- 다른 객체를 생성해서 다양한 기능을 수행
- 객체 간의 데이터 전달의 수단. 외부로부터 매개값을 받아 실행 후 어떤 결과를 리턴할 수 있음.
6.6 필드
- 객체의 고유 데이터, 객체가 가져야 할 부품, 객체의 현재 상태 데이터를 저장하는 곳.
6.6.1 필드 선언
- 로컬 변수 : 생성자와 메소드 블럭 내부에 선언된 것
- 필드 : 멤버 변수
- 초기값이 지정되지 않으면 객체 생성 시 자동으로 기본 초기값으로 설정된다.
6.6.2 필드 사용
자기 클래스 내에서 사용할 경우에는 그냥 사용
외부 클래스에서 사용할 경우에는 우선 객체 생성 후에, 클래스 통해서 필드 값 불러와야함
//ex) Person 클래스에서 Car 클래스에 있는 speed 필드값을 사용하고 싶을때.
Car mycar = new Car();
mycar.speed = 60;
6.7 생성자
- 생성자는 객체의 초기화를 담당한다.
- 객체 초기화 : 필드를 초기화하거나, 메소드를 호출해서 객체를 사용할 준비를 하는 것.
- 생성자를 실행시키지 않고 클래스로부터 객체를 만들 수 없음
6.7.1 기본 생성자
- 모든 클래스는 생성자가 반드시 존재하며 하나 이상을 가질 수 있다. ⇒ 중복선언 (overloading 가능)
- 생성자를 따로 기술하지 않더라도 JVM이 잠깐 빌려준다. (하나라도 있으면 빌려주지 X)
6.7.2 생성자 선언
- 생성자 선언 안 해도 JVM이 빌려주는데 굳이 선언하는 이유? ⇒ 매개변수를 이용해 다양하게 오버로딩하기 위해서.
public class Car(String model, String colour, int speed){ ... }
// 3개의 매개변수값을 가진 클래스 Car
Car mycar = new Car ("Hyundai", "red", 300); // parameter값을 전달
- 이렇게 Car 클래스에 매개변수 세개 가진 생성자를 명시적으로 선언했으므로 매개변수가 없는 기본 생성자는 호출할 수 없다.
Car mycar = new Car(); // 불가함! 기본 생성자를 호출할 수 없다
6.7.3 필드 초기화
- 필드 초기화 방법 1) 필드를 선언할 때 초기값을 주는 방법 2) 생성자에서 초기값을 주는 방법
public Class Korea(){
String nation = "대한민국";
String name;
String ssn;
} // 생성자에서 초기값 주기
-------------------------
Korean k1 = new Korea();
Korean K2 = new Korea();
// 두 인스턴스는 같은 필드값이 저장되어 있다
But, 저렇게 생성자 내에서 초기값을 먼저 줘버리면 외부에서 제공되는 다양한 값에 의해서 초기화되기 힘들다. ⇒ 매개값으로 이 값들을 받아 초기화하기
public Korean(String n, String s){
this.name = n;
this.ssn = s;
}
매개변수의 이름은 관례적으로 필드의 이름과 동일하게 선언한다.
그러나 같은 이름이면 로컬변수가 우선순위가 높기 때문에 생성자 내부에서 해당 필드로 접근할 수 없다. ⇒ "this"로 "나는 필드입니다"라는 것을 명시해주기
Public Korean(String name, String ssn){
this.name(필드변수) = name(매개변수);
this.ssn = ssn;
}
6.7.4 생성자 오버로딩(Overloading)
- 매개변수를 달리하는 생성자를 여러개 선언하는 것.
- 매개변수의 개수, 타입, 순서가 다르게 선언.
- 생성자뿐만 아니라 메소드도 오버로딩이 가능하다 (But 반환타입은 상관 x, 기준은 무조건 매개변수)
Car(String model, String colour){...}
Car(String colour, String model){...} // 오버로딩이 아님 (타입 같으니까)
- new 연산자로 생성자를 호출할 때 제공되는 매개값의 타입과 수에 의해 호출될 생성자가 결정된다.
6.7.5 다른 생성자 호출(this())
- 중복된 코드를 제거하기 위해서 필드 초기화 내용은 한 생성자에 집중적으로 작성하고 나머지 생성자는 초기화 내용을 가지고 있는 생성자를 호출하는 방법으로 개선.
- 메소드는 생성자 호출 X
- 부모 클래스는 어떻게 부를까? ⇒ super(소괄호), super.필드명, super.메소드명( )
public class Car {
String company = "현대자동차";
String model;
String color;
int maxSpeed;
//생성자
Car(){
}
Car(String model){
this(model, "은색", 250)
}
Car(String model, String color){
this(model, color, 250)
}
Car(String model, String color, int maxSpeed){
this.model = model;
this.color = color;
this.maxSpeed = maxSpeed;
}
}
- 생성자로 객체를 생성할 때, 매개변수를 어떤걸 주느냐에 따라서 선택할 수 있는 것.
6.8 메소드 (p.214~)
- 메소드 선언부 (리턴타입, 메소드이름, 매개변수 선언) = 메소드 시그너쳐
- 메인에서 메소드를 호출할때도 클래스 객체를 생성하고 변수를 이용하여 호출
Calculator a = new Calculator();
a.powerOn(); // 객체 통하지 않고 메소드 호출할 순 없다 (static의 경우 제외)
매개변수의 수를 모를경우
⇒ 매개변수의 수를 모르거나 변수들이 가변적이면 배열을 선언한다.
6.8.2 리턴(return)문
- return 문 이후의 실행문은 결코 실행되지 않는다 (Unreachable code)
- if문의 경우 return문이 두개 이상 나올 수 있음
package practice;
public class GasExam {
int gas;
void setGas(int gas) {
this.gas = gas;
}
boolean isLeftGas() {
if(gas==0) {
System.out.println("가스가 없습니다");
return false;
}else {
System.out.println("가스가 있습니다");
return true;
}
}
void run() {
while(true) { // break나 return 없는 이상 무한루프
if(gas>0) {
System.out.println("달립니다. (gas 잔량 : "+gas+")");
gas -= 1;
} else {
System.out.println("멈춥니다. (gas 잔량 : "+gas+")");
return; // return문이 없는 if에 return문으로 강제종료
}
} // end of while
}
public static void main(String[] args) {
GasExam g = new GasExam();
g.setGas(10);
boolean gState = g.isLeftGas();
if(gState) {
System.out.println("출발합니다");
g.run();
}
if(g.isLeftGas()) {
System.out.println("가스를 주입할 필요가 없습니다");
}else {
System.out.println("가스를 주입하세요");
}
}
}
6.8.3 메소드 호출
- 클래스 내부 다른 메소드에서 호출할 경우 : 단순히 메소드 이름만 호출
boolean isLeftGas() {
if(gas==0) {
System.out.println("가스가 없습니다");
run(); // 클래스 내 다른 메소드에서 호출
return false;
}else {
System.out.println("가스가 있습니다");
return true;
}
}
- 클래스 외부 (메인도 포함) 에서 호출할 경우 : 클래스로부터 객체를 생성한 뒤, 참조 변수를 이용하여 메소드를 호출한다. 객체가 존재해야 메소드도 존재하기 때문
- 메소드는 객체에 소속된 멤버!
6.8.4 메소드 오버로딩 (p. 230~ )
- 클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것
- 매개변수의 타입, 개수, 순서 중 하나가 달라야 오버로딩
- 매개값을 다양하게 받아 처리할 수 있도록 하기 위해서 (int, String, boolean 등 다양한 매개변수를 받기 위해)
위와 같이 매개변수 타입이 다른 메소드가 두개 있을 경우,
다음과 같은 코드를 실행하면 둘 중 어떤 메소드가 실행될까?
System.out.println(g.plus(10, 15.5));
=> int 타입은 double로 자동변환될 수 있으므로, 범위가 넓은 double 타입 매개변수의 메소드 실행
- 메소드 오버로딩의 가장 대표적인 예 : System.out.println( ) 메소드!
6.9 인스턴스 멤버와 this (p. 233~)
- 인스턴스 멤버 : 객체(인스턴스)를 생성한 후 사용할 수 있는 필드와 메소드 (인스턴스 필드, 인스턴스 메소드)
- 인스턴스 필드와 메소드는 객체에 소속된 멤버이기 때문에 객체 없이는 사용할 수 없다.
public class Car {
//필드
int gas;
//메소드
void setSpeed(int speed){ ... }
}
--------------------------------------------------------
// 인스턴스 1 : mycar
Car myCar = new Car();
myCar.gas = 10;
myCar.setSpeed(50);
// 인스턴스 2 : yourcar
Car youCar = new Car();
yourCar.gas = 20;
yourCar.setSpeed(100);
- 인스턴스 필드 gas는 객체마다 따로 존재하고, 인스턴스 메소드 setSpeed( ) 는 객체마다 존재하지 않고 메소드 영역에 저장되고 공유된다.
6.10 정적 멤버와 static (p. 236~)
- 정적 멤버 : 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메소드 (정적 필드, 정적 메소드 = 클래스형 변수, 클래스형 메소드)
- 객체에 소속된 멤버가 아니라 클래스에 소속된 멤버이기 때문에 클래스 멤버라고도 한다.
6.10.1 정적 멤버 선언
- 정적 필드와 정적 메소드는 클래스에 고정된 멤버이므로 클래스 로더가 클래스(바이트 코드)를 로딩해서 메소드 메모리 영역에 적재할 때 클래스별로 관리된다. (그래서 메모리 차지 높음)
- 객체마다 가지고 있어야 할 데이터라면 인스턴스 필드, 객체마다 가지고 있을 필요성이 없는 공용적인 데이터라면 정적 필드로 선언!
6.10.2 정적 멤버 사용
- 클래스가 메머리로 로딩되면 정적 멤버를 바로 사용할 수 있다.
Car.color;
Car.run(매개변수);
=> 객체 생성하지 않고 바로 클래스 이름을 통해 사용할 수 있다.
6.10.3 정적 초기화 블록
- 정적 필드는 필드 선언과 동시에 초기값을 준다
static double pi = 3.14159;
- 인스턴스 필드는 생성자에서 초기화하지만, 정적 필드는 객체 생성 없이도 사용해야 하므로 생성자에서 초기화 작업을 할 수 없다. (생성자는 객체 생성시에만 실행되기 때문에!)
public class Television {
static String company = "samsung";
static String colour = "red";
static String info;
static { // 정적 초기화 블록
info = company + "-" + colour;
}
}
- 주의할 점 : 정적 메소드와 정적 블록을 선언할 때 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없다. (객체가 없어도 실행된다는 특징 때문에)
- 정적 메소드 내에서는 this 키워드도 사용이 불가능하다.
static void Method3 {
this.field1 = 10; (x)
this.method1(); (x)
field2 = 20;
method2();
}
- 인스턴스 변수/메소드는 객체가 생성된 이후에 사용할 수 있고, 정적(static)은 객체가 생성되기 전에 메모리에 올라간다.
static void Method3(){
ClassName obj = new ClassName();
obj.field1 = 10;
obj.method1();
}
- 정적 메소드와 정적 블록에서 인스턴스 멤버를 사용하고 싶다면 위와 같이 객체를 먼저 생성하고 참조 변수로 접근해야 함.
- main도 static이기 때문에 객체를 생성하기 전까지는 바로 사용할 수 없는 것.
6.10.5 싱글톤(Singleton)
- 단 하나의 객체만 만들도록 보장해야 하는 경우가 있는데, 단 하나의 객체만 생성된다고 해서 이 객체를 싱글톤(singleton)이라고 한다.
public class 클래스 {
//정적 필드
private static 클래스 singleton = new 클래스();
//생성자
private 클래스(){}
//정적 메소드
static 클래스 getInstance(){
return singleton;
}
}
1) 자신의 클래스 타입인 정적필드 하나 선언, 자신의 객체 생성 2) 생성자도 private 3) getInstance( ) : 외부에서 호출할 수 있는 정적 메소드. 자신이 참조하고 있는 자신의 객체를 리턴 ⇒ 외부에서 객체를 얻는 유일한 방법은 getInstance( ) 메소드를 호출하는 것!
⇒ 단, getInstance( ) 메소드는 단 하나의 객체만 리턴하기 때문에 아래 코드에서 변수1과 2는
동일한 객체를 참조함
클래스 변수1 = 클래스.getInstance();
클래스 변수2 = 클래스.getInstance();
- 결론 : 객체가 아무리 많이 만들어졌어도 싱글톤에서는 하나의 객체만 접근해서 쓴다!
6.11 final 필드와 상수
6.11.1 final 필드
- final : 초기값 = 최종적인 값 (수정 불가)
1) 변수에 붙이는 경우 :값을 변경할 수 없음 (상수화 된다) ex) final double PI = 3.14; ex2) final static double PI = 3.14;(완벽)
2) 메소드에 붙이는 경우 :자식클래스가 재정의하지 못하게 함. (override)
3) 클래스에 붙이는 경우 상속을 하지 못함. (클래스가 거기서 끝) 상속 -> 부모 클래스의 기능을 확장해서 자식 클래스가 쓰는 개념. final 붙어있으면 재건축, 클래스 확장 X
6.11.2 상수(static final)
- final 필드는 객체마다 저장되고, 생성자의 매개값을 통해서 여러가지 값을 가질 수 있기 때문에 상수가 될 수 없다.
- 상수는 static 이면서 final
- 객체마다 저장 x, 클래스에만 포함
6.13 접근제한자
- public : 모든 클래스에서 접근 가능
- protected : 자식 클래스, 동일한 패키지, 상속된 패키지 ("우리 가족만")
- default : 같은 패키지내에서만 접근 가능
- private : 같은 클래스 (외부는 전부 접근 x)
6.13.1 클래스의 접근 제한
- 클래스에 적용할 수 있는 접근 제한자는 public, default
6.13.2 생성자의 접근 제한
- 자동으로 생성되는 기본 생성자의 접근 제한은 클래스의 접근 제한과 동일
6.13.3 필드와 메소드의 접근 제한
- public, protected, default, private 접근 제한
6.14 Getter와 Setter 메소드
- 외부에서 마음대로 객체를 읽고 변경할 경우 무결성이 깨지는 문제때문에, 데이터는 외부에서 접근할 수 없도록 막고 메소드는 공개해서 외부에서 메소드를 통해 데이터에 접근하도록 유도함. (Setter)
vode setSpeed(double speed){
if(speed < 0) {
this.speed = 0;
return;
}else {
this.speed = speed;
}
}
// 매개값이 음수일 경우 speed 필드에 0으로 저장하고, 메소드 실행 종료
- 객체 외부에서 객체의 필드값을 사용하기에 부적절할때, 메소드로 필드값을 가공한 후 외부로 전달 (Getter)
double getSpeed(){
double km = speed*1.6;
return km;
}
// 필드값인 마일을 km 단위로 환산 후 외부로 리턴
- 클래스를 선언할 때 가능하면 필드를 private으로 선언해서 외부로부터 보호하고, 필드에 대한 Setter와 Getter 메소드를 사용하여 필드값을 안전하게 변경/사용하는 것이 좋다.
6.15 어노테이션
- @Override : 메소드가 오버라이드 (재정의) 된 것임을 컴파일러에게 알려주어 컴파일러가 검사하도록 해준다.
'JAVA' 카테고리의 다른 글
[이것이 자바다] 11 기본 API 클래스 (2) getProperty, Collection (0) | 2021.03.10 |
---|---|
[이것이 자바다] 11 기본 API 클래스 (1) 객체비교 compare, 오름차순까지 (0) | 2021.03.09 |
[이것이 자바다] 10 예외 처리 (Exception) (0) | 2021.03.09 |
[이것이 자바다] 08 인터페이스 (0) | 2021.03.09 |
[이것이 자바다] 07 상속 (인터페이스 포함) (0) | 2021.03.09 |
Uploaded by Notion2Tistory v1.1.0