객체지향 프로그래밍
- Object Oriented Programming
- 객체지향 프로그래밍이 왜 나왔을까?
- 커지는 소스코드의 양을 감당할 수 X
- 하드웨어의 발전 -> 처리량 증가 -> 프로그램이 복잡해짐
- 대규모 소프트웨어 개발의 어려움 해소하기 위해 등장한 개념
- 소프트웨어를 관리하고 개발하는 전체적인
방법론
에 대해 얘기
- 기획 -> 요구사항 분석 -> 설계 -> 구현(개발) -> 테스트 -> 유지보수
소프트웨어 공학
- 클래스와 객체는 자바에서만 사용할 수 있는 고유 기능이 아님
- 객체지향 방법론을 이루고 잇는 하나의 개념(기술. 정의)이고, 그런 개념을 사용할 수 있는 수단을 제공하는 것
자바의 변수 종류
- 지역변수
- 객체변수(인스턴스 변수)
- 메서드 외부, 클래스 내부에서 정의된 변수
- 반드시 객체를 통해서만 접근 가능
객체.속성명
으로 접근 => 객체가 만들어지지 않으면 해당 속성 접근 불가
- 객체 변수는 각 객체에 속한 고유 메모리에 만들어짐(공유 불가)
클래스
- 사용자가 정의하는 새로운 타입
- 기본 타입을 제외한 String, Array 등의 자바에서 미리 정의된 클래스
- 미리 정의된 클래스 뿐만 아니라, 직접 클래스를 정의해서 사용 가능
- 클래스를 정의하는 기본 형태
class 클래스 이름{
// 클래스 블록
}
- 일반적으로 클래스의 이름은 대문자로 시작함.
- 클래스를 제외한 변수의 이름이나, 메서드 이름은 소문자
- 반드시 지킬 필요는 없지만, 권장함
객체
- Object(객체) 또는 Instance(인스턴스)
- 클래스는 정의
- 정의된 클래스랄 사용하기 위해서는 메모리에 만들어줘야 함
- 정의된 클래스를 메모리에 생성하게 되면
객체
라고 부름
인스턴스화
한다고 표현
- 객체는 메서드 호출과 같음
- 메서드를 호출하면, 메모리에 만들어짐(
콜스택
)
- 클래스를 호출하면, 클래스가 메모리에 만들어짐
- 메서드는 실행이 끝나면 메모리에서 사라짐.
- 객체는 종료라는 개념이 존재하지 X (직접 삭제 전까지 존재)
- 같은 클래스에 객체를 여러개 생성 가능
- 동일한 타입의 변수가 여러개인 것으로 볼 수 있음
- 타입이 같을뿐, 생성되는 변수는 다름(메모리의 위치에 따라 구분)
- 모든 것은 주소를 기준으로 구분 가능
- 변수, 메서드, 클래스, 객체 등을 구분하는 기준은 메모리
- 같아 보이는 객체도, 주소가 다르면 다른 객체
- 달라 보이는 객체도, 주소가 같으면 같은 객체
생성자(Constructor)
- 생성 메서드로 이해. 약간 특별한 메서드
- 초기화할 때 주로 사용
static method Vs. Instance method
- Instance(인스턴스) 메서드
- 생성자를 제외한, 지금까지 정의된 메서드는 전부
instance 메서드
- 객체를 통해서 호출 가능
- 클래스 메서드(static method)
- 클래스 변수와 객체 변수는 메모리에 생성되는 시점이 다름
- 클래스 변수: 객체보다 먼저 메모리에 생성
- 객체변수: 객체가 메모리에 생성될 때 메모리에 생성
- 객체가 생성되기 전에 메모리에 만들어진 클래스 변수를 객체를 통해 접근해야 한다?
- 그래서, 객체가 없어도 호출 가능한 메서드를 정의 ==> 클래스 메서드
- 클래스 메서드 ==> 클래스 이름으로 호출 가능
class Person{
static int share = 10;
String name;
int age;
// 클래스 메서드(static method)
static void get_share(){
System.out.println(share);
// 객체 변수에 대한 접근은 불가능
// System.out.println(this.name);
}
}
public class Static_Method {
public static void main(String[] args){
// 객체를 생성하지 않아도 클래스를 통해서 호출 가능
Person.get_share();
}
}
클래스의 종류
- 추상 클래스
- 이너 클래스
- 메타 클래스
- 익명 클래스
클래스의 구조
- 속성(변수)과 기능(메서드)로 정의
- 클래스 내의 변수를 멤버변수라고 표현
- 하나의 클래스에는 여러 개의 변수와 여러 개의 메서드를 정의
- 클래스 내의 변수와 메서드는 해당 클래스의 특징 반영 -> 속성이라 표현
class Person{
// 변수 정의(사람에 대한 속성)
// 사람이라면 누구나 가지는 속성
String name;
int age;
}
public class Chapter6_Class{
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
// 클래스에는 .을 활용하여 접근
// 객체.속성
// 객체.가능
p1.name = "홍길동";
p1.age = 20;
System.out.printf("이름: %s, 나이: %d\n", p1.name, p1.age);
// 서로 다른 객체(정의하지 않았기 때문에 null, 0으로 출력)
// 객체 변수는 공유되지 X
System.out.printf("이름: %s, 나이: %d\n", p2.name, p2.age);
}
}
클래스의 특징
- 클래스: 메서드와 변수를 하나의 타입으로 묶어놓은 것
- 비슷한 특징을 가지고 있는 여러 메서드와 변수를 특징별로 클래스화 한 것(관리 용이)
- 은닉성(캡슐화)
- 상속(inheritance)
- 다형성(Polymorphism)
- 오버로딩(Overloading), 오버라이딩(Overriding)
은닉성(캡슐화)
- 클래스 내에서 정의된 변수나 메서드에 대해 외부에서 함부로 접근하지 못하도록 비공개로 하는 것을
은닉성
이라 함
- 객체지향에서 클래스는 제공되는 기능이 어떻게 동작하는지 알 필요가 없다고 말하는 것
- 외부에서는 클래스에 정의도니 속성이나 기능을 전부 알 수 없도록 함
- 필요한 만큼만 공개, 나머지는
몰라도 사용할 수 있음
이라 하는 것
- 일반적으로 객체지향 프로그래밍 내에서는 모든 속성을
비공개
로 하는 것이 원칙
- 외부 접근이 필요한 경우, 메서드를 통해서 접근할 수 있도록 제공하는 것이 좋음(접근제어자)
- 접근 제어자(Access Modifier) (1~4로 갈수록 접근 허용 범위 증가)
- private: 동일 클래스 내에서만 접근
- default: 동일 패키지에서만 접근
- protected: 해당 클래스를 상속받은 외부 패키지의 클래스까지 가능
- public: 제한이 없음
상속(inheritance)
- 물려받는다는 의미
- 클래스의 재사용성을 더 극대화한 개념
- 잘 정의된 클래스가 있다면, 다시 정의해서 사용하지 않고, 재사용할 수 있게 하자
- 물려받은 클래스를 더 확장해서 사용
- 클래스와 클래스 간의 관계를 표현
- 하나의 프로그램을 만들 때 하나의 클래스만 사용되지 않고 여러 개의 클래스가 서로 유기적으로 잘 연결되어 동작
- 객체지향 프로그래밍에서 가장 중요한 것은 클래스의 설계
- 클래스들의 관계
- is-a(상속)
- has-a(다른 클래스의 객체를 속성으로 갖는 경우)
- 예를 들어, 모든
학생
은 사람
이다
- 학생
is
사람
- 사람
is
학생은 성립하지 않음
- 클래스의 상속은 큰 개념에서 작은 개념으로 상속
- 학생도 사람이기 때문에, 사람이 갖는 기본적인 속성이가 기능은 같을 것이라 가정
- 학생이라는 클래스를 정의한다면?
- 사람에 대한 속성이나 기능 새로 정의X
- 미리 정의된 사람 클래스를 상속 받아서, 나머지 학생에 대한 속성이나 기능만 추가
- 부모 클래스(기반 클래스): 물려주는 클래스
- 자식 클래스(파생 클래스): 물려받는 클래스
class Person{
int share = 10;
String name;
int age;
static void get_share() {
System.out.println(share);
}
void info() {
System.out.printf("이름: %s, 나이: %d\n", this.name, this.age);
}
void set_naem(String name){
this.name = name;
}
String get_name() {
return this.name;
}
}
// Person 클래스를 상속받음
class Student extends Person{
// 부모로부터 물려받은 속성, 기능 그대로 가져오게 됨
// 정의된게 없어도 물려받은 속성, 기능 사용 가능
}
public class inheritance {
public static void main(String[] args){
Student s1 = new Student();
s1.info();
}
}
다형성(Polymotphism)
오버로딩(Overloading)
- 상속과는 무관한 개념
- 오버로딩의 조건
- 오버로딩 하는 메서드의 이름은 반드시 같아야 함
- 오버로딩 하는 메서드의 매개변수는 반드시 달라야 함
class Person{
int share = 10;
String name;
int age;
// 생성자 오버로딩
Person() {
}
Person(String name, int age){
this.name;
this.age;
}
static void get_share() {
System.out.println(share);
}
void info() {
System.out.printf("이름: %s, 나이: %d\n", this.name, this.age);
}
void set_naem(String name){
this.name = name;
}
String get_name() {
return this.name;
}
}
// Person 클래스를 상속받음
class Student extends Person{
// 입력되는 수가 없어도, 혹은 1개여도, 3개여도 동작하려면?
// 메서드 이름이 같아도, 파라미터 갯수가 다르기 때문에 각각을 다르게 사용할 수 있다.
int add(){
return 10+20;
}
int add(int a){
return a+20;
}
int add(int a, int b){
return a+b;
}
/*
변수가 다르더라도 파라미터의 갯수가 같기 때문에 구분이 불가능하다.
int add(int c, int d){
return c+d;
}
*/
// 파라미터의 갯수는 같지만, 타입이 int와 String으로 다르기 때문에 구분 가능
String add(String a, String b){
return a+b;
}
int add(int a, int b, int c){
return a+b+c;
}
}
public class Overloading {
public static void main(String[] args){
Student s1 = new Student();
System.out.println(s1.add());
System.out.println(s1.add(10));
System.out.println(s1.add(10,20));
System.out.println(s1.add("Hello ", "Java"));
System.out.println(s1.add(10,20,30));
// 생성자 오버로딩을 이용하면 객체를 다양하게 생성 가능
Person p1 = new Person();
p1.info();
Person p2 = new Person("김준면", 31);
p2.info();
}
}
오버라이딩(Overriding)
- 메서드 재정의
- 부모 클래스로부터 물려받은 메서드를 그대로 사용하지 않고, 자식이 재정의 후 사용
- 오버라이딩의 조건
- 재정의 하려는 메서드와 물려받은 메서드의 이름이 같아야 함(같지 않을 경우 자식 클래스에서 메서드가 하나 추가되는 것과 같음)
리턴 타입
, 매개변수의 개수
, 타입
이 모두 같아야 함
class Person{
static int share = 10;
String name;
int age;
// 생성자 오버로딩
// 기본 생성자느 아무것도 하지 않음
Person() {
}
Person(String name, int age){
this.name = name;
this.age = age;
}
static void get_share() {
System.out.println(share);
}
void info() {
System.out.printf("이름: %s, 나이: %d\n", this.name, this.age);
}
void set_naem(String name){
this.name = name;
}
String get_name() {
return this.name;
}
}
// Person 클래스를 상속받음
class Student extends Person{
String school;
// 생성자는? -> 상속되지 않음
// 생성자를 정의하지 않으면?
// -> default 생성자가 존재하게 됨 (아무것도 하지 않는 기본 생성자)
Student() { };
Student(String name, int age, String school){
//super(name, age); // 부모 클래스의 생성자를 직접 호출
this.name = name;
this.age = age;
this.school = school;
}
// info() 메서드를 재정의 한다면
void info() {
System.out.println("이 메서드는 재정의 되었습니다");
// 부모 클래스의 메서드를 사용하고 싶다면?
// super: 부모, super.속성
// super(): 부모 클래스의 생성자
// this: 객체 자기 자신
super.info();
System.out.printf("학교이름: %s\n", this.school);
}
}
public class Overriding {
public static void main(String[] args){
Student s1 = new Student("김준면", 20, "multicampus");
s1.info();
}
}
싱글톤 패턴(Singleton Pattern)
- 디자인 패턴(Design Pattern) 중 하나
- 동일한 타입(클래스)의 여러 객체 생성
- 하나의 객체만 생성하게 강제
- 즉, 클래스 하나당 객체 하나
class Singleton{
private Singleton() {
}
}
public class SingletonTest {
public static void main(String[] args) {
// 아무도 객체를 만들 수 없게 함
Singleton single = new Singleton();
}
}
package Standard_Java;
class Singleton{
private static Singleton one = null;
private Singleton() {
}
// 아무도 생성할 수 없으니, 생성을 도와주는 메서드
public static Singleton getObject() {
// 생성자 호출 가능
if(one == null){
one = new Singleton();
}
return one;
}
}
public class SingletonTest {
public static void main(String[] args) {
Singleton single = Singleton.getObject();
System.out.println(single);
// 더 이상의 새로운 객체는 생성 불가
Singleton single2 = Singleton.getObject();
System.out.println(single2);
System.out.println( Singleton.getObject() );
}
}