JAVA, JSP

java 6장- 객체지향프로그래밍

suji0730 2024. 8. 1. 19:06

 

1) 객체지향 프로그래밍

 

1-1. 객체지향 프로그래밍의 특징

① 캡슐화

- 객체의 데이터(필드), 동작(메서드)을 하나로 묶고 실제 구현 내용을 외부에 감추는 것.

- 외부 객체는 객체 내부의 구조를 알지 못하며 객체가 노출해서 제공하는 필드와 메서드만 이용할 수 있다.

- 자바 언어는 캡슐화된 멤버를 노출시킬 것인지 숨길 것인지 결정하기 위해 접근 제한자를 사용한다.

 

② 상속

- 객체지향 프로그래밍에서는 부모 역할의 상위 객체와 자식 역할의 하위 객체가 있다. 부모 객체는 자기가 가지고 있는 필드와 메서드를 자식 객체에게 물려주어 자식 객체가 사용할 수 있도록 한다.

- 코드의 재사용성을 높여 준다 : 잘 개발된 부모 객체의 필드와 메서드를 자식이 그대로 사용할 수 있어 자식 객체에서 중복 코딩을 하지 않아도 된다.

- 유지 보수 시간을 최소화시켜 준다 : 부모 객체의 필드와 메서드를 수정하면 모든 자식 객체들은 수정된 필드와 메서드를 사용할 수 있다.

 

③ 다형성

- 사용 방법은 동일하지만 실행 결과가 다양하게 나오는 성질.

- 자동차의 부품을 교환하면 성능이 다르게 나오듯이, 프로그램을 구성하는 객체(부품)를 바꾸면 프로그램의 실행 성능이 다르게 나올 수 있다.

- 다형성을 구현하기 위해서는 자동 타입 변환과 재정의 기술이 필요하다.

 

1-2. 추상화
- 추상화는 클래스들의 공통적인 요소를 뽑아서 상위 클래스를 만들어내는 것이다.

- 반드시 상위 클래스일 필요는 없어서, 공통적인 속성과 기능을 정의한 하위 클래스를 생성할 수도 있다.

- 한 마디로 추상화는 공통적인 속성과 기능을 정의함으로써 코드의 중복을 줄이고, 클래스 간 관계를 효과적으로 설정하고, 유지/보수를 용이하게 하는 것이다.

- 자바에서는 추상 클래스와 인터페이스라는 문법 요소를 통해 추상화를 구현한다.

 

 

1-3. 클래스의 구성 멤버

- 클래스 선언에는 객체 초기화 역할을 담당하는 생성자와 객체에 포함될 필드와 메서드를 선언하는 코드가 포함된다. 그래서 생성자, 필드, 메서드를 클래스 구성 멤버라고 한다.

public class ClassName{
    int fieldName;	//필드 : 객체의 데이터가 저장되는 곳


    ClassName(){...}	//생성자 : 객체 생성 시 초기화 역할 담당


    int methodName(){...}	//메서드 : 객체의 동작으로 호출 시 실행하는 블록
    }

① 필드 : 객체의 데이터를 저장하는 역할을 한다. 선언 형태는 변수 선언과 비슷하지만 쓰임새는 다르다

② 생성자 : new 연산자로 객체를 생성할 때 객체의 초기화 역할을 담당한다. 선언 형태는 메서드와 비슷하지만, 리턴 타입이 없고 이름은 클래스 이름과 동일하다.

③ 메서드 : 객체가 수행할 동작이다. 다른 프로그램 언어에서는 함수라고 하기도 하는데, 객체 내부의 함수는 메서드라고 부른다. 메서드는 객체와 객체간의 상호작용을 위해 호출된다.

 

 

1-4. 클래스 변수

- 클래스로부터 객체를 생성하려면 객체 생성 연산자인 new가 필요하다.

- new 연산자 뒤에는 생성자 호출 코드가 오는데, 클래스() 형태를 가진다.

- new 연산자는 객체를 생성시킨 후 객체의 주소를 리턴하기 때문에 클래스 변수에 다음과 같이 대입할 수 있다.

클래스 변수 = new 클래스();
ReferenceEdu rf = new ReferenceEdu();
rf.referenceSample();

 

 

1-5. 필드 선언과 사용

- 필드는 객체의 데이터를 저장하는 역할을 한다. 객체의 데이터에는 고유, 현재 상태, 부품 데이터가 있다.

 

ⓛ 필드 선언

- 필드 선언은 클래스 블록에서 선언되어야 한다.

타입 필드명 [ = 초기값 ];

- 타입은 필드에 저장할 데이터의 종류를 결정한다. 기본 타입(byte, short, int, long, float, double, boolean)과 참조 타입(배열, 클래스, 인터페이스)이 가능하다.

- 초기값을 제공하지 않을 경우 필드는 객체 생성 시 자동으로 기본값으로 초기화된다.

 

*필드 타입별 기본값

분류 데이터 타입 기본값
기본타입 정수 타입 byte
char
short
int
long
0
\u0000(빈 공백)
0
0
0L
실수 타입 float
double
0.0F
0.0
논리 타입 boolean false
참조 타입 배열
클래스(String 포함)
인터페이스
null
null
null
public class Car{
	String model;	//null
    int speed;	//0
    boolean start;	//false
    Tire tire;	//null
}

 

- 필드와 로컬 변수(전역변수/지역변수) : 로컬변수는 생성자와 메서드 블록에서 선언되며 생성자와 메서드 호출 시에만 생성되고 사용된다. 필드는 클래스 블록에서 선언되며 객체 내부에서 존재하고 객체 내, 외부에서 사용 가능하다.

 

* 필드와 (로컬)변수의 차이점

구분 필드 (로컬)변수
선언 위치 클래스 선언 블록 생성자, 메서드 선언 블록
존재 위치 객체 내부에 존재 생성자, 메서드 호출 시에만 존재
사용 위치 객체 내, 외부 어디든 사용 생성자, 메서드 블록 내부에서만 사용

 

 

② 필드 사용

- 필드값을 읽고 변경하는 것

- 클래스에서 필드를 선언했다고 바로 사용할 수 있는 것은 아니다. 필드는 객체의 데이터이므로 객체가 존재하지 않으면 필드도 존재하지 않는다.

- 클래스로부터 객체가 생성된 후에 필드를 사용할 수 있다. 필드는 객체 내부의 생성자와 메서드 내부에서 사용할 수 있고, 객체 외부에서도 접근해서 사용할 수 있다.

 

//외부객체

void method(){
	//Car 객체 생성
    Car myCar = new Car();
    //외부에서 필드 사용
    myCar.speed = 60;
}
//객체 내부

//필드
int speed;

//생성자에서 사용
Car(){
	speed = 60;	//값 변경
}

//메서드에서 사용
void method(...){
	speed = 60;	//값 변경
}

//생성자와 메서드는 객체가 생성된 후 호출되므로 내부에서 필드를 사용할 수 있다.

- 객체 내부에서는 필드명으로 읽고 변경할 수 있지만, 외부 객체에서는 참조 변수와 도트(.) 연산자를 이용해서 필드를 읽고 변경해야 한다. 도트(.)는 객체 접근 연산자로, 객체가 가지고 있는 필드나 메서드에 접근하고자 할 때 참조 변수 뒤에 붙인다.

 

public class ClassEdu {
    public String model = "소나타";
    public int speed = 300;

    public static void carEdu() {
        String model2= "그랜져";
        int speed2 = 200;
        System.out.println(model2);
        System.out.println(speed2);
    }
    public void carEdu2() {
        System.out.println(model);
        System.out.println(speed);
    }
}

//공통적인 요소가 아니라면 static 사용은 지양
//new 연산자를 통해 새로운 객체를 생성 및 필드에 접근하여 데이터를 수정하는 것이 올바른 방법

 

//기능1 시작
ClassEdu ce = new ClassEdu();
System.out.println(ce.model = "그랜져");
System.out.println(ce.speed = 200);
ce.carEdu2();
//기능1 끝

//기능2 시작
ClassEdu ce2 = new ClassEdu();
System.out.println(ce2.model);
System.out.println(ce2.speed);
ce2.carEdu2();
//기능2 끝

 

 

 

1-6. 생성자 선언과 호출

① 기본 생성자

- 모든 클래스는 생성자가 존재하며, 하나 이상을 가질 수 있다. 클래스에 생성자 선언이 없으면 컴파일러는 다음과 같은 기본 생성자를 바이트코드 파일에 자동으로 추가시킨다.

[public] 클래스() {}

- 클래스가 public class로 선언되면 기본 생성자도 public이 붙지만, 클래스가 public 없이 class로만 선언되면 기본 생성자에도 public이 붙지 않는다.

//소스파일(Car.java)
public class Car{}

-> 컴파일 후 바이트코드 파일

//바이트코드 파일(Car.class)
public class Car{
	public Car(){}	//자동추가
}

-> new 연산자 뒤에 기본 생성자 호출 가능

Car myCar = new Car();

 

생성자 선언

클래스(매개변수, ...){생성자 블록}

- 생성자는 메서드와 비슷한 모양을 가지고 있으나, 리턴 타입이 없고 클래스 이름과 동일하다. 매개변수는 new 연산자로 생성자를 호출할 때 매개값을 생성자 블록 내부로 전달하는 역할을 한다.

//Car 생성자를 호출할 때 3개의 매개값을 블록 내부로 전달한다고 가정
Car myCar = new Car("그랜저", "검정", 300);
//매개변수의 타입은 매개값의 종류에 맞게 작성
//3개의 매개값을 순서대로 매개변수로 대입받기 위해서는 다음과 같이 생성자가 선언되어야 한다.
public class Car{
	//생성자 선언
    Car(String model, String color, int maxSpeed){...}
}

 

 

③ 필드 초기화

- 객체마다 동일한 값을 갖고 있다면 필드 선언 시 초기값을 대입하는 것이 좋고, 객체마다 다른 값을 가져야 한다면 생성자에서 필드를 초기화하는 것이 좋다.

public class korean{
	//필드 선언
    String nation = "대한민국";
    String name;
    String ssn;
    
    //생성자 선언
    public Korean(String name, String ssn){
    	//name, ssn 초기화
        this.name = n;
        this.ssn = s;
    }
}

 

 

④ 생성자 오버로딩

- 매개값으로 객체의 필드를 다양하게 초기화하려면 생성자 오버로딩이 필요하다. 

- 매개변수를 달리하는 생성자를 여러 개 선언하는 것

 

public class ClassEdu {
	
    public String model = "현대";
    public String color;
    public int maxSpeed;

    public ClassEdu(String color, int maxSpeed) {
        System.out.println("객체 생성 완료");
        this.color = color;
        this.maxSpeed = maxSpeed;
    }


    public static void carEdu() {
        String model2= "그랜져";
        int speed2 = 200;
        System.out.println(model2);
        System.out.println(speed2);

    }

    public void carEdu2() {
        System.out.println(model);
        System.out.println(color);
        System.out.println(maxSpeed);

    }
}
//기능1 시작
ClassEdu ce = new ClassEdu("검정", 200);
ce.carEdu2();
//기능1 끝

//기능2 시작
ClassEdu ce2 = new ClassEdu("빨강", 500);
ce2.carEdu2();
//기능2 끝

 

 

 

 

1-7. 가변길이 매개변수

- 메서드가 가변길이 매개변수를 가지고 있다면 매개변수의 개수와 상관없이 매개값을 줄 수 있다.

int sum(int...values){}
public void carEdu3(int...num) {
    System.out.println(num);
    for(int numArr : num) {
        System.out.println(numArr);
    }
}
ClassEdu ce = new ClassEdu();
ce.carEdu3(50000, 12345, 56789);

 

 

 

 1-8. getter/setter/toString

private String name;
private String ssn;

public void setName(String name) {
    this.name = name;
}

public String getName() {
    return name;
}

public void setSsn(String ssn) {
    this.ssn = ssn;
}

public String getSsn() {
    return ssn;
}

@Override
public String toString() {
    return "ClassEdu [name=" + name + ", ssn=" + ssn + "]";
}
ClassEdu ce = new ClassEdu();
ce.setName("김보쌈");
System.out.println(ce.getName());
ce.setSsn("200730");
System.out.println(ce.getSsn());

System.out.println(ce.toString());

 

 

 

2) DTO / VO

2-1. DTO(Data Transfer Object)

- 계층 간 데이터 교환을 위한 객체
- 로직을 가지지 않고 getter/setter 메서드만 가진 순수한 데이터 객체 클래스(Java Beans)로 DB에서 데이터를 얻어 Service나 Controller 등으로 보낼 때 사용

 

2-2. VO(Value Object)
- 도메인에서 1개 또는 그 이상의 속성들을 묶어서 특정 값을 나타내는 객체.

- 도메인 객체의 일종이고 보통 PK로 식별값을 가지는 엔티티(Entity) 와 구분해서 사용한다.

- 변경 불가능하며 오직 읽기만 가능

 

* DTO/VO 차이점

- DTO는 setter를 가지고 있어 값을 변경할 수 있지만, VO는 getter만을 가지기 때문에 읽기만 가능하고 수정은 불가능
- DTO는 인스턴스 개념이고, VO는 리터럴 값(변하지 않는 데이터) 개념
- DTO는 단지 데이터를 담아 전달하는 역할만 하지만, VO는 값들에 대해 읽기만 가능한 Read-Only 속성을 가져 객체로서 데이터 그 자체에 의미를 갖는다.
출처: https://hstory0208.tistory.com/entry/Spring-DAO-DTO-VO란-각각의-개념에-대해-알아보자

 

 

3) 스프링 프레임워크 = 개발하기 편리한 도구 + mvc 패턴의 개발방법을 지향하는 도구

mvc패턴 : model, view, controller

controller : 단순히 url 요청에 따라서 어떤 메서드를 실행시킬지 결정하는 클래스

view(데이터 전달) > controller(메서드 요청 및 데이터 받고(dto 세팅) > impl(실질적인 기능 구현)

 

--Action.java

package common;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/registerUser")
public class Action extends HttpServlet {
	
	public void service(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("hi");
		System.out.println(req.getParameter("name"));
		System.out.println(req.getParameter("ssn"));
		System.out.println(req.getParameter("nation"));
		
		ActionDTO acDto = new ActionDTO();
		acDto.setName(req.getParameter("name"));
		acDto.setSsn(req.getParameter("ssn"));
		acDto.setNation(req.getParameter("nation"));
		System.out.println(acDto.toString());
		
		ActionServiceImpl serviceImpl = new ActionServiceImpl();
		String result = serviceImpl.registerUser(acDto);
		System.out.println(result);
	}
}

 

--ActionDTO.java

package common;

public class ActionDTO {
	private String name;
	private String ssn;
	private String nation;
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
	public void setSsn(String ssn) {
		this.ssn = ssn;
	}
	
	public String getSsn() {
		return ssn;
	}
	
	public void setNation(String nation) {
		this.nation = nation;
	}
	
	public String getNation() {
		return nation;
	}
	
	@Override
	public String toString() {
		return "ActionDTO [name=" + name + ", ssn=" + ssn + ", nation=" + nation + "]";
	}
}

 

--ActionServiceImpl.java

package common;

public class ActionServiceImpl {
	
	public String registerUser(ActionDTO acDto) {
		System.out.println("---impl 파일 위치---");
		System.out.println(acDto.toString());
		//회원가입에 관련된 기능이 들어가야 하고, java 문법을 이용해서 아이디 중복체크, pw데이터 이상 없는지 등..
		//jdbc 연결하는 구문도 들어가야 한다.
		return "회원가입을 축하합니다";
	}
}

 

--index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	회원가입 폼
	<form method="post" action="/registerUser">
		<input name="name"/>
		<input name="ssn"/>
		<input name="nation"/>
		<button type="submit">전송</button>
	</form>
</body>
</html>