본문 바로가기

JAVA

Java & SpringBoot로 시작하는 웹 프로그래밍 : 자바 인강 - 3주차

3주차 목차

  • 참조 자료형 변수
  • 접근 제어 지시자(access modifier)와 정보은닉 (information hiding)
  • 캡슐화
  • 객체 자신을 가리키는 this
  • 객체 간의 협력
  • static 변수
  • static 메소드 
  • 배열
  • 객체 배열
  • 2차원 배열
  • ArrayList 클래스

 

 

09. 참조 자료형 변수

변수의 자료형

  • 기본 자료형 : int, long, float, double 등
  • 참조 자료형 : String, Date, Student 등

 

참조 자료형

  • 직접 클래스를 만들어 멤버 변수로 사용하고 싶을 때 사용
  • 클래스마다 메모리의 크기가 다름
  • 해당 변수를 선언만 하고 쓸 수 없고 생성을 하고 써야한다 (String 제외)
  • 생성자에서 new로 생성하던가 그 전에 new로 생성하던가 해야함

 

참조 자료형 정의하여 이용하기

학생의 성적 산출 코드를 짤 때 과목명들이 모두 학생 클래스에 있으면 별로임

그래서 과목 클래스와 학생 클래스를 따로 분리해서 사용하기

Subject 클래스에 과목명, 과목점수을 넣고 학생 클래스에서 변수로 가져다 사용

 

Subject.java

package ch03;

public class Subject {

	String subjectName;
	int score;
	int subjectId;
}

Student.java

package ch03;

public class Student {
	
	int studentId;
	String studentName;
	
	Subject korea;
	Subject math;
	
	Student(int studentId, String studentName) { // 생성자
		
		this.studentId = studentId;
		this.studentName = studentName;
		
		// 사용하기 전에 Subject 생성
		korea = new Subject();
		math = new Subject();
	}

	// 멤버변수에 대한 세팅
	public void setKoreaSubject(String name, int score) {
		
		korea.subjectName = name;
		korea.score = score;
	}
	
	public void setMathSubject(String name, int score) {
		
		math.subjectName = name;
		math.score = score;
	}
	
	public void showScoreInfo() {
		
		int total = korea.score + math.score;
		System.out.println(studentName + " 학생의 총 점은 " + total + "점 이다.");
	}
	
}

SubjectTest.java

package ch03;

public class Student {
	
	int studentId;
	String studentName;
	
	Subject korea;
	Subject math;
	
	Student(int studentId, String studentName) { // 생성자
		
		this.studentId = studentId;
		this.studentName = studentName;
		
		// 사용하기 전에 Subject 생성
		korea = new Subject();
		math = new Subject();
	}

	// 멤버변수에 대한 세팅
	public void setKoreaSubject(String name, int score) {
		
		korea.subjectName = name;
		korea.score = score;
	}
	
	public void setMathSubject(String name, int score) {
		
		math.subjectName = name;
		math.score = score;
	}
	
	public void showScoreInfo() {
		
		int total = korea.score + math.score;
		System.out.println(studentName + " 학생의 총 점은 " + total + "점 이다.");
	}	
}



 

10. 접근 제어 지시자(access modifier)와 정보은닉 (information hiding)

접근 제어 지시자

외부클래스에서 클래스의 멤버 변수, 메소드, 생성자를 어떻게 접근할 수 있느냐에 대해서 제한을 하는 키워드

  • public : 클래스 외부 어디서나 접근 가능
  • protected : 하위 클래스가 상위 클래스 접근 가능, 같은 패키지 내부에서도 접근 가능
  • default(아무 것도 없음) : 같은 패키지 내부에서만 접근 가능
  • private : 같은 클래스 내부에서만 접근 가능

get() / set() 메소드 - private에서 사용

  • 값을 가져가거나 값을 다른 값으로 바꾸거나하는 메소드를 제공할 수 있다
  • private로 선언된 멤버 변수(속성, 필드)에 대해 접근, 수정할 수 있는 메소드를 public으로 제공
  • get() 메소드만 제공 되는 경우 read-only 필드
  • 이클립스에서 자동으로 생성됨 : 우클릭 - Source - Generate Getters and Setters

 

BirthDay.java

package ch03;

public class BirthDay {

	private int day;
	private int month;
	private int year;
	
	private boolean isValid;
    // 값이 유효한지 아닌지만 체크하기 때문에 밖에 내보낼 일이 없다 
	
	public int getDay() {
		return day;
	}
	
	public void setDay(int day) {
		this.day = day;
	}

	public int getMonth() {
		return month;
	}

	public void setMonth(int month) {
		
		if(month < 1 || month >12) { // 월 조건
			
			isValid = false;
		}
		
		else {
			isValid = true;
			this.month = month;
		}
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}
	
	public void showDate() {
		
		if(isValid) { // true면 존재하는 날짜 출력
			System.out.println( year + "년 " + month + "월 " + day + "일 입니다.");
			
		}
		
		else {
			System.out.println("유효하지 않는 날짜입니다.");
		}
	}
    
    // private로 묶인 isValid를 밖에서 알고싶은 경우
	// get만 제공 날짜가 유효한지 결정하는 것은 내부로직이라 set메소드는 제공하지 않는다.
	// -> read-only 속성
	public boolean getIsValid() {
		return isValid;
	}
}

BirthDayTest.java

package ch03;

public class BirthDayTest {

	public static void main(String[] args) {
		
		BirthDay date = new BirthDay();
		date.setYear(2019); // 날짜 세팅
		date.setMonth(12);
		date.setDay(30);
		
		date.showDate();
	}
}

 

private으로 막지 않고 다 오픈하는 것과 private로 막아서 get, set을 제공해주는 것 어떤 차이?

다 열게 되면 date.month = 100; 이런식으로 넣을 수 있게 된다 

멤버 변수의 오류를 객체가 잘못 사용되는 것을 노출하게 되고 객체의 역할에 문제가 생길 수 있다.

오류 발생 가능성을 private로 막아주고 메소드에서 제어함

 

 

정보 은닉

private로 제어한 멤버 변수도 public 메소드가 제공되면 접근 가능하지만  

변수가 public으로 공개되었을때 보다 private일 때 변수에 대한 제한을 public 메소드에서 제어할 수 있다.

 

 

11. 캡슐화 (encapsulation)

메소드나 필드를 외부에 제공할 것이냐 말것이냐가 객체 지향에서 중요한 부분

어떻게 공개를 해서 사용하는쪽이 좀 더 편하게 효율적으로 객체 또한 오류 없이 잘 관리되느냐가 관건

  • 정보 은닉을 활용한 캡슐화
  • 정보를 감싸서 꼭 필요한 정보만 외부에서 오픈함
  • 대부분의 멤버변수와 메소드는 감추고 외부의 통합된 인터페이스를 제공해 클라이언트가 조금 더 사용하기 쉽게 일관된 기능들을 제공
  • 오류에 대한 디텍션을 클라이언트 코드와 상관없이 서비스가 제공하는 객체쪽에서 오류에 대한 디텍션이나 객체에 대한 업그레이드를 하면 되기 때문에 디버깅이나 기능 추가에 유연하다
  • 일관된 한두개의 인터페이스를 통해서 (공개된 함수 메소드가 있어서) 클라이언트쪽에서 걔네들만 보고 사용할 수 있도록 구현

 

 

StringBuffer 클래스, StringBuilder 클래스 

String 클래스를 쭉 이어서 쓰는 방법이 +를 계속해서 쓸 수는 있다 하지만 메모리 오버헤드가 큼

그래서 내부적으로 늘어날 수 있는 버퍼를 가지고 있는 클래스에 string을 연결을 하고 나중에 String 값으로 리턴

StringBuffer클래스의 멤버 변수 중 append 메소드를 계속 쓰면 쭉 연결할 수 있다.

(많을때는 객체의 배열을 반복적으로 돌리며 값을 꺼내오는게 더 좋음)

 

 

MakeReport.java

package ch03;

public class MakeReport {

	StringBuffer buffer = new StringBuffer();
	
	private String line = "================================================";
	private String title = "이름 \t   주소 \t\t  전화번호 \n";
	
	private void makeHeader() {
		
		buffer.append(line);
		buffer.append(title);
		buffer.append(line);
	}
	
	private void generateBody() {
		
		buffer.append("James  \t ");
		buffer.append("Seoul Korea \t");
		buffer.append("010-2222-3333 \n");
		
		buffer.append("Tomas \t");
		buffer.append("NewYork US \t");
		buffer.append("010-7777-0987\n");
	}
	
	private void makeFooter() {
		
		buffer.append(line);
	}
	
	public String getReport() { // 외부에서 쓸 수 있는 유일한 메소드
		// Report 생성 가능
		
		makeHeader();
		generateBody();
		makeFooter(); 
		return buffer.toString();
	}
}

MakeReportTest.java

package ch03;

public class MakeReportTest {
	
	public static void main(String[] args) {
		
		MakeReport builder = new MakeReport();
		String report = builder.getReport(); 
		
		System.out.println(report);
	}

}

결과

MakeReport.java의 각각의 코드를 클라이언트 쪽에선 알 필요가 없다

그래서 information hiding을 시켜 private로 해놨고 

public한 메소드 하나를 생성하여 순서도 정확하게 놓고 그 리턴한 결과를 클라이언트에서 확인

 

 

 

 

12. 객체 자신을 가리키는 this

this가 하는 일

  • 인스턴스 자기 자신의 메모리 가리킴

객체를 new 하게 되면 힙메모리에 생성이 된다.

힙메모리가 생성 되었을때 얘의 주소값을 갖는 애를 참조변수그 값 자체를 참조값이라 했음

그때 그 변수 자체는 인스턴스의 외부고 그 변수가 인스턴스를 가리키게 되는데

인스턴스 자체 내부에서 자기 자신의 주소값을 갖는 것이 this

  • 객체가 생성될때 호출되는 생성자에서 또 다른 생성자 호출 가능 
  • 자신의 주소, 참조값 자체를 반활할 때도 사용

 

Birthday day = new Birthday();

day.setYear(2000); 을 하게 되면 

setYear메소드가 호출되면서 setYear에 해당되는 스택 메모리가 잡히게 되는데 

이 setYear안에 this키워드가 갖게 되는 값은 똑같이 heap 안의 값

 

this.year = year;의 this.year의 값은 heap메모리의 값

즉, 생성된 인스턴스의 메모리의 주소를 가리키는 역할

 

 

 

생성자에서 다른 생성자를 호출하는 this

생성자가 여러개인 경우에 생성자에서 다른 생성자를 호출할 때가 있음

파라미터가 더 적은 생성자에서 파라미터가 더 많은 생성자를 호출함

 

생성자의 역할은 인스턴스를 초기화 하는 역할 

this를 호출할 때 ( ) 안에 있는 자료형에 매핑되는

똑같은 데이터 타입의 매개변수를 가진 함수를 찾고 호출 

 

this 가 호출되는 순간에는 인스턴스 생성이 끝난게 아니기 때문에 

이 이전에 다른 코드를 넣을 수 없다.

 

 

 

생성자에서 다른 생성자를 호출하는 코드, this로 자기 자신의 주소를 반환한 코드

package ch03;

public class Person {

	String name;
	int age;
	
	public Person() {
		
		// 생성자에서 밑의 다른 생성자를 호출하는 this
		// 이 위에 다른 코드를 넣는 것은 안된다. 밑은 상관없음
		this("no name", 1);
		
	}
	
	public Person(String name, int age) {
		
		// 자기 자신의 주소를 나타내는 this
		this.name = name; 
		this.age = age;
	}
	
	// getPerson이라는 메소드를 만들고 반환 타입이 자기 자신
	public Person getPerson() {
		
		return this; 
		// this가 Person타입으로 반환되는데 이때의 값은?
	}
	
	public void showPerson() {
		
		System.out.println(name + ", " + age);
	}
	
	public static void main(String[] args) {
		
		Person person = new Person();
		person.showPerson();
		// 생성자에서 다른 생성자를 호출하여 값 출력
		
		
		System.out.println(person);
		// 패키지까지 포함된 클래스의 이름과 JVM이 할당해준 인스턴스 주소가 출력됨
		
		Person person2 = person.getPerson();
		// this가 반환되었을 것임
		System.out.println(person2); // person과 같은 값이 출력됨
	}
}

결과

Person person = new Person();에서 생성된 person과

getPerson()메소드에서 this로 반환해주는 person2의 값이 같다

 

 

 

 

13. 객체 간의 협력 (collabration)

객체 지향 프로그래밍에서의 협력

  • 객체가 하는 일은 그 객체 이름에 맞는 유일한 일들을 한다
  • 객체간의 협력은 하나의 객체가 혼자 돌아가는 것이 아니라 다른 객체와 유기적으로 연동이 된다.
  • 이때 서로 정보가 왔다갔다하는 것을 메세지가 전송된다라고 함
  • 매개변수로 객체 자체가 전달되는 경우가 종종 있다.

 

 

 

14. 버스 타고 학교가는 학생의 과정을 객체 지향 프로그래밍으로 구현해보기

학생이 버스를 탈 건데 버스를 탄다의 행위 안에 이루어지는 여러가지 요소

학생의 여러가지 속성 중 가진 돈이 버스를 타면 적어질거고

버스의 속성 중 버스 번호, 승객 수, 수입이 달라질 것임 이게 객체 사이의 협력

지하철 역시 타면 노선 번호, 승객수, 수입이 달라질 것이다.

 

버스와 지하철을 타는 예제 프로그래밍

James와 Tomas는 각각 버스와 지하철을 타고 학교에 갑니다.

James는 500원을 가지고 있었고, 100번 버스를 타면서 1000원을 지불합니다.

Tomas는 10000원을 가지고 있었고, 초록색 지하철을 타면서 1200원을 지불합니다.

두 학생이 버스와 지하철을 타는 상황을 구현해 봅시다.

 

Student.java

package ch03;

public class Student {

	String studentName;
	int money; // 가진 돈
	
	public Student(String studentName, int money) {
		this.studentName = studentName;
		this.money = money;
	}
	
	public void takeBus(Bus bus) { // 어떤 버스를 탔는지가 매개변수로 넘어왔음
		bus.take(1000); // 버스 탔을때 1000원 지급
		this.money -= 1000; // 1000원 지불했으니 가진 돈에서 빠짐
	}
	
	public void takeSubway(Subway subway) {
		subway.take(1200);
		this.money -= 1200;
	}
	
	public void showInfo() {
		System.out.println(studentName + "님의 남은 돈은 " + money + "원 입니다.");
	}
}

Bus.java

package ch03;

public class Bus {

	int busNumber;
	int passengerCount;
	int money; // 수입
	
	public Bus(int busNumber) {
		this.busNumber = busNumber;
	}
	
	public void take(int money) {
		this.money += money; // 매개변수로 받은 돈을 수입 돈에 더함
		passengerCount++;
		
	}
	
	public void showBusInfo() {
		System.out.println(busNumber + "번의 승객 수는 " + passengerCount + "명이고, 수입은 " + money + "원 입니다.");
	
	}
}

Subway.java

package ch03;

public class Subway {

	int lineNumber;
	int passengerCount;
	int money; // 수입
	
	public Subway(int lineNumber) {
		this.lineNumber = lineNumber;
	}
	
	public void take(int money) {
		this.money += money; // 매개변수로 받은 돈을 수입 돈에 더함
		passengerCount++;
		
	}
	
	public void showBusInfo() {
		System.out.println(lineNumber + "번의 승객 수는 " + passengerCount + "명이고, 수입은 " + money + "원 입니다.");
	
	}
}

TakeTransTest.java

package ch03;

public class TakeTransTest {

	public static void main(String[] args) {
		
		Student studentJ = new Student("James", 5000);
		Student studentT = new Student("Tomas", 10000);
		
		Bus bus100 = new Bus(100);
		Bus bus500 = new Bus(500); // 버스 여러대 가능
		
		studentJ.takeBus(bus100);
		
		Subway greenSubway = new Subway(2);
		
		studentT.takeSubway(greenSubway);
		
		studentJ.showInfo();
		studentT.showInfo();
		
		bus100.showBusInfo();
		greenSubway.showBusInfo();
	}
}

결과

각 객체에 대한 협력이 이루어지고 이 협력을 바탕으로 코딩이 이루어진다.

 

 

 

15. 복습 (객체 협력)

다음과 같은 상황을 구현해 봅시다.

앞의 예제에서 Edward는 지각을 해서 택시를 타야 했습니다.

20000원을 가지고 있었는데 10000원을 택시비로 사용했습니다.

택시는 '잘나간다 운수' 회사 택시를 탔습니다. 

 

 

Student.java

package ch03;

public class Student {

	String studentName;
	int money; // 가진 돈
	
	public Student(String studentName, int money) {
		this.studentName = studentName;
		this.money = money;
	}
	
	public void takeTaxi(Taxi taxi) {
		taxi.take(1200);
		this.money -= 10000;
	}
	
	public void showInfo() {
		System.out.println(studentName + "님의 남은 돈은 " + money + "원 입니다.");
	}
}

Taxi.java

package ch03;

public class Taxi {

	String taxiName;
	int money; // 수입
	
	public Taxi(String taxiName) {
		this.taxiName = taxiName;
	}
	
	public void take(int money) {
		this.money += money; // 매개변수로 받은 돈을 수입 돈에 더함
		
	}
	
	public void showTaxiInfo() {
		System.out.println(taxiName + " 수입은 " + money + "원 입니다.");
	
	}
}

TakeTransTest.java

package ch03;

public class TakeTransTest {

	public static void main(String[] args) {
		
		Student studentE = new Student("Edward", 20000);
		
		Taxi taxi1 = new Taxi("잘나간다 운수");
		
		studentE.takeTaxi(taxi1);
		
		studentE.showInfo();
		taxi1.showTaxiInfo();
		
	}
}

결과

 

 

 

16. static 변수 - 여러 인스턴스에서 공통으로 사용하는 변수를 선언하자

static 변수

  • 동일한 클래스들이 기준 값으로 공유할 변수가 필요한 경우가 있다.
  • static변수는 동일한 클래스에서 생성된 여러개의 인스턴스 변수가 공유할 수 있는 변수
  • 인스턴스가 생성될 때 만들어지는 변수가 아니라( Heap메모리(동적 메모리))
  • 프로그램이 프로세스가 돼서 메모리에 로딩되는 순간 데이터 영역에 잡히고 프로그램이 다 끝나고 unload되는 순간 없어진다.
  • 인스턴스 생성과 상관 없이 사용 가능하므로 클래스 이름으로 직접 참조한다.

 

static 변수 테스트 해보기

Employee.java

package ch03;

public class Employee {

	public static int serialNum = 1000;
	
	private int employeeId;
	private String employeeName;
	private String department;
	
	public int getEmployeeId() {
		return employeeId;
	}
	public void setEmployeeId(int employeeId) {
		this.employeeId = employeeId;
	}
	public String getEmployeeName() {
		return employeeName;
	}
	public void setEmployeeName(String employeeName) {
		this.employeeName = employeeName;
	}
	public String getDepartment() {
		return department;
	}
	public void setDepartment(String department) {
		this.department = department;
	}	
}

EmployeeTest.java

package ch03;

public class EmployeeTest {

	public static void main(String[] args) {
		
		Employee employeeLee = new Employee();
		employeeLee.setEmployeeName("이순신");
		
		System.out.println(employeeLee.serialNum);
		System.out.println();
		
		Employee employeeKim = new Employee();
		employeeKim.setEmployeeName("김유신");
		employeeKim.serialNum++; // employeeKim만을 이용해 시리얼넘버 증가시켜보기
		// static변수가 인스턴스를 공유하는지 알아보기 위해 하나만 증가시킴
		
		System.out.println(employeeLee.serialNum); // 시리얼넘버가 둘 다 1씩 증가해 1001 출력됨
		System.out.println(employeeKim.serialNum);
		
	}
}

결과

static 변수인 serialNum 변수가 인스턴스끼리 공유된다는 사실만을 알 수 있다.

employeeLee, employeeKim 둘 다 시리얼 넘버가 1씩 증가했기 때문

 

 

 

회사원이 입사할 때마다 새로운 사번 부여하기

Employee.java

package ch03;

public class Employee {

	public static int serialNum = 1000;
	
	private int employeeId;
	private String employeeName;
	private String department;
	
	public Employee() { // default 생성자
		
		serialNum++;
		// 이 변수는 모든 인스턴스가 공유해서 모두가 같은 사번을 가져 사용 불가능 그래서 기준값으로 사용
		employeeId = serialNum; // employeeId는 증가하게 된다.

	}
	
	public int getEmployeeId() {
		return employeeId;
	}
	public void setEmployeeId(int employeeId) {
		this.employeeId = employeeId;
	}
	public String getEmployeeName() {
		return employeeName;
	}
	public void setEmployeeName(String employeeName) {
		this.employeeName = employeeName;
	}
	public String getDepartment() {
		return department;
	}
	public void setDepartment(String department) {
		this.department = department;
	}
}

EmployeeTest.java

package ch03;

public class EmployeeTest {

	public static void main(String[] args) {
		
		Employee employeeLee = new Employee();
		employeeLee.setEmployeeName("이순신");
		
		System.out.println(employeeLee.serialNum);
		
		Employee employeeKim = new Employee();
		employeeKim.setEmployeeName("김유신");
		
		
		System.out.println(employeeLee.getEmployeeName() + "님의 사번은 " + employeeLee.getEmployeeId()); 
		System.out.println(employeeKim.getEmployeeName() + "님의 사번은 " + employeeKim.getEmployeeId()); 
		
	}
}

결과

여러 인스턴스가 공유하는 어떤 값이 필요할 때 static을 활용해서 쓴다.

static 변수만 써봤는데 static 메소드도 있음

 

 

 

 

17. static 메소드의 구현과 활용, 변수의 유효 범위

 

  • static 메소드(클래스 메소드)에서는 인스턴스 변수(멤버 변수)를 사용할 수 없다
  • 일반 메소드 안에서는 static 변수를 쓰는 것이 문제되지 않음

이유 : static 메소드는 인스턴스가 생성되지 않더라도 클래스 이름으로 바로 static메소드 호출할 수 있다.

여기에 인스턴스 변수를 쓰게 되면 생성이 안된 인스턴스 변수를 참조하는 경우가 된다. 

 

Employee.java

package ch03;

public class Employee {

	public static int serialNum = 1000;
	
	private int employeeId;
	
	public Employee() { // default 생성자
		
		serialNum++;
		employeeId = serialNum; 

	}
	
	public static int getSerialNum() { 
		
		// static 메소드
		// 이 메소드는 인스턴스 생성과 상관없이 클래스 이름으로 바로 호출해서 사용 가능 
		
		int i = 0; // 함수안에 선언된 지역변수는 사용 가능
		
		// employeeName = "Lee"; 불가능
		// 이 메소드가 불려질 시점에 employeeName가 없을 수도 있다.
		
		return serialNum;
	}


	public int getEmployeeId() {
		
		// 일반 메소드 안에서는 static 변수를 쓰는 것이 문제되지 않는다.
		serialNum = 1000;
		
		return employeeId;
	}
	
}

EmployeeTest.java

package ch03;

public class EmployeeTest {

	public static void main(String[] args) {
	
		System.out.println(Employee.getSerialNum()); // 클래스 이름으로 getSerialNum 참조
		// 인스턴스 생성과 무관하게 바로 메소드 호출가능하다. static 메소드이기 때문
		
	}
}

 

 

변수의 유효 범위와 메모리

변수 유형 선언 위치 사용 범위 메모리 생성과 소멸
지역 변수 함수 내부에 선언 함수 내부 스택 함수가 호출될 때 생성되고 함수 끝나면 소멸
멤버 변수
(인스턴스 변수)
클래스 멤버 변수로 선언 클래스 내부
(private가 아니면 참조 변수로 다른 클래스에서 사용 가능)
인스턴스가 생성될 때 힙에 생성되고,
가비지 컬렉터가 메모리 수거할 때 소멸
static 변수
(클래스 변수)
static 예약어를 사용하여 클래스 내부에서 선언 클래스 내부
(private가 아니면 참조 변수로 다른 클래스에서 사용 가능)
데이터 영역 프로그램이 시작할 때 상수와 함께 
데이터 영역에 생성되고
프로그램 끝나고 메모리 해제될 때 소멸

 

 

 

18. static 응용 - 싱글톤 패턴

  • 인스턴스가 단 하나만 필요한 경우 사용하는 디자인 패턴
  • 여러개의 인스턴스가 생겼을 때 문제가 되는 상황이 있다
  • 예를 들면, 날짜 같은 경우 여러개를 가질 수 없고 회사안에 사원은 많을 수 있어도 회사라는 객체는 하나여야만 한다.

 

Company.java

package ch03;

public class Company {

	// 유일한 인스턴스는 내부에서 만든다.
	private static Company instance = new Company();
	
	// 싱글톤은 private으로 생성자 하나 제공
	private Company() {
		
	}
	
	// 유일한 인스턴스에 접근하기 위한 public 메소드들
	// 인스턴스를 일반 메소드가 쓰는 경우 인스턴스 생성을 해야하기 때문에 static 붙이기
	public static Company getInstance() {
		
		if(instance == null) {
			instance = new Company();
		}
		return instance;
	}
}

CompanyTest.java

package ch03;

import java.util.Calendar;

public class CompanyTest {

	public static void main(String[] args) {
		
		// 인스턴스를 만들지 않고 클래스 이름만으로 호출 가능
		Company company1 = Company.getInstance();
		Company company2 = Company.getInstance();
		
		System.out.println(company1);
		System.out.println(company2);
		
		Calendar calendar = Calendar.getInstance();
		// 유일한 객체라 getInstance만 사용 가능하다
		
	}
}

결과

같은 주소값이 출력된다.

Company는 외부에서 생성할 수 없고 내부에 private으로 하나 생성되어 있고 

getInstance() 메소드만 제공된다.

 

클래스이름 Singleton
멤버변수  -instance ()
생성자와 메소드  -Singleton, +getInstance(얘를 가져다 쓰면 된다.)

- : private

+ : public 

 

19. 복습 (static과 싱글톤 패턴)

설명에 따른 객체 구현하기

자동차 공장이 있습니다. 자동차 공장은 유일한 객체이고 이 공장에서 생성되는 자동차는 제작될 때마다 고유 번호 부여

자동차 번호가 10001번 부터 자동차가 생성될 때 마다 번호가 붙도록 자동차 공장 클래스, 자동차 클래스 구현하시오

 

Car.java

package ch03;

public class Car {
	
	public static int serialNum = 10000;
	private int carNum;
	
	public Car() {
		
		serialNum++;
		carNum = serialNum;
	}

	public int getCarNum() {
		return carNum;
	}

	public void setCarNum(int carNum) {
		this.carNum = carNum;
	}
	
}

CarFactory.java

package ch03;

public class CarFactory {

	
	// 유일한 인스턴스는 내부에서 만든다.
	private static CarFactory instance = new CarFactory();
	
	// 싱글톤은 private으로 생성자 하나 제공
	private CarFactory() {
		
	}
	
	
	public static CarFactory getInstance() {
		
		if(instance == null) {
			instance = new CarFactory();
		}
		return instance;
	}
	
	public Car createCar() {
		Car car = new Car();
		return car;
	}
	
}

CarFactoryTest.java

package ch03;

public class CarFactoryTest {

	public static void main(String[] args) {
		
		CarFactory factory = CarFactory.getInstance();
		Car mySonata = factory.createCar();
		Car yourSonata = factory.createCar();
		
		System.out.println(mySonata.getCarNum());
		System.out.println(yourSonata.getCarNum());
		
	}
}

결과

 

 

 

20. 배열(array) - 자료를 순차적으로 한꺼번에 관리하는 방법 

 

  • 동일한 자료형의 순차적 순서를 가지는 구조
  • 배열은 길이를 가지게 된다. 선언하고 시작
  • 배열의 각각은 요소(element)
  • 물리적 위치와 논리적 위치가 동일 (배열의 a 옆에 b가 있다고 할 때 실제 메모리도 그럼)
  • [ ] 인덱스 연산자를 사용하고 인덱스는 0부터 시작
  • 데이터가 중간에 없으면 안되고 쭉 연결되어 있어야한다.
  • 자료형 배열ArrayList라는 객체 배열을 구현해놓은 클래스가 있다.
  • 배열의 길이와 요소의 개수는 동일하지 않다. 선언한 것과 다르게 일부만 쓰는 경우가 있음

 

배열의 선언

  • int[] arr1 = new int[10];
  • int arr2[] = new int[10];

 

배열의 초기화

  • int[] numbers = new int[] {10 , 20, 30}; // 개수 생략 가능, 선언과 동시에 초기화
  • int[] numbers = {10, 20, 30}; // new int[] 생략 가능
  •  int[] ids;

   ids = new int[] {10, 20, 30}; // 선언 후 배열 생성시 new int[] 생략 불가능

 

 

초기화를 직접 해도 되고 for문으로 해도 된다.

for문으로 초기화하는 방법

ArrayTest.java

package ch03;

public class ArrayTest {

	public static void main(String[] args) {
		
		int[] arr = new int[10]; // new로 초기
		int total = 0;
		
		for(int i = 0, num = 1; i<arr.length; i++) {
			arr[i] = num++; // 값을 넣고 나서 증가시
			System.out.print(arr[i] + " ");
		}
		
		// 첨부터 끝까지 순회할 때 사용
		for(int num : arr) {
			total += num; // num에 순차적으로 값이 들어감
		}
		System.out.print(total);
	}
}

 

 

문자열 배열

CharArrayTest.java

package ch03;

public class CharArrayTest {

	public static void main(String[] args) {
		
		char[] alphabets = new char[26];
		char ch = 'A';
		
		for(int i = 0; i<alphabets.length; i++) {
			alphabets[i] = ch++; // 'A'부터 순서대로 넣기
		}
		
		for(char alpha : alphabets) {
			System.out.println(alpha + ", " + (int)alpha);
		}
	}
}

 

 

 

21. 객체 배열 사용하기

객체의 배열엔 요소가 되는 객체의 주소(참조변수의 값)을 저장한 방이 잡히지 객체가 잡히진 않는다. 

그래서 객체를 하나하나 만들어서 집어 넣어야한다.

 

 

객체 배열 만들어보기

Book.java

package ch03;

public class Book {

	private String title;
	private String author;
	
	public Book() {}
	
	public Book(String title, String author) { // 초기화
		this.title = title;
		this.author = author;
	}
	
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	
	public void showInfo() {
		
		System.out.println(title + ", " + author);
	}
	
}

BookTest.java

package ch03;

public class BookTest {

	public static void main(String[] args) {
		
		Book[] library = new Book[5];
		// 객체가 5개 잡히는게 아니라 객체를 각각 new를 해서 넣어야한다.
		
		library[0] = new Book("태백산맥1", "조정래");
		library[1] = new Book("태백산맥2", "조정래");
		library[2] = new Book("태백산맥3", "조정래");
		library[3] = new Book("태백산맥4", "조정래");
		library[4] = new Book("태백산맥5", "조정래");
		
		for(Book book : library) {
			System.out.println(book);
			book.showInfo();
		}
	}
}

결과

주소가 다 따로 잡혔고 객체가 만들어진 것을 알 수 있다.

객체 배열을 만들 때는 new만 해서 만들어지는 것이 아니라 객체를 각각 생성해서 넣어야한다.

 

 

객체 배열 복사하기

System.arrayCopy(소스배열, 어디서부터(인덱스), 어디로(카피할 배열), 카피할 배열 어디부터(인덱스), 몇개를 카피할지)

: 자바에서 재공하는 배열 복사 메소드 (얕은 복사)

 

  • 얕은 복사 : 두 개의 배열이 같은 객체를 가리키는 상황 
  • 깊은 복사 : 두 개의 배열이 다른 객체를 가리키는 상황 (객체를 각각 다 만들어야한다.)

 

얕은 복사 예제

ObjectCopyTest.java

package ch03;

public class ObjectCopyTest {
 
	public static void main(String[] args) {
		
		Book[] library = new Book[5];
		Book[] copyLibrary = new Book[5]; // 복사할 배열
		
		library[0] = new Book("태백산맥1", "조정래");
		library[1] = new Book("태백산맥2", "조정래");
		library[2] = new Book("태백산맥3", "조정래");
		library[3] = new Book("태백산맥4", "조정래");
		library[4] = new Book("태백산맥5", "조정래");
		
		System.arraycopy(library, 0, copyLibrary, 0, 5);
		
		// 원래 내용
		System.out.println("== library ==");
		for(Book book : library) {
			System.out.println(book);
			book.showInfo();
		}
		
		// 복사된 내용
		System.out.println("== copy library ==");
			for(Book book : copyLibrary) {					
				System.out.println(book);
				book.showInfo();	
		}
			
		// 배열 내용 바꿔보기
		library[0].setAuthor("박완서");
		library[0].setTitle("나목");
		
		System.out.println("== library ==");
		for(Book book : library) {
			System.out.println(book);
			book.showInfo();
		}
		// 카피 배열 출력해도 똑같이 바껴있다. 얕은 복사
	}
}

결과를 출력해보면 library와 copyLibrary 둘 다 0인덱스가 바껴있고 주소값은 같다.

 

 

깊은 복사 예제

ObjectCopyTest.java

package ch03;

public class ObjectCopyTest {
 
	public static void main(String[] args) {
		
		Book[] library = new Book[5];
		Book[] copyLibrary = new Book[5]; // 복사할 배열
		
		library[0] = new Book("태백산맥1", "조정래");
		library[1] = new Book("태백산맥2", "조정래");
		library[2] = new Book("태백산맥3", "조정래");
		library[3] = new Book("태백산맥4", "조정래");
		library[4] = new Book("태백산맥5", "조정래");
		
		// 인스턴스 만들어주기
		copyLibrary[0] = new Book();
		copyLibrary[1] = new Book();
		copyLibrary[2] = new Book();
		copyLibrary[3] = new Book();
		copyLibrary[4] = new Book();
	
		// 직접 요소 하나하나 넣어가며 복사하기, 인스턴스가 따로 만들어져 값만 복사됨
		for(int i = 0; i < library.length; i++) {
			copyLibrary[i].setAuthor(library[i].getAuthor());
			copyLibrary[i].setTitle(library[i].getTitle());
		}
			
		// 배열 내용 바꿔보기
		library[0].setAuthor("박완서");
		library[0].setTitle("나목");
		
		System.out.println("== library ==");
		for(Book book : library) {
			System.out.println(book);
			book.showInfo();
		}
		
		System.out.println("== copy library ==");
		for(Book book : copyLibrary) {
			System.out.println(book);
			book.showInfo();
		}
		
	}
}

결과

주소도 다르고 library는 바껴도 copyLibrary는 바뀌지 않아있다.

 

 

22. 2차원 배열 사용하기

다차원 배열 : 2차원 이상으로 구현된 배열

 

int[][] arr = new int[행개수][열개수];

행이 기준이고 열이 돈다고 생각하면 됨

 

package ch03;

public class TwoDimensionTest {
 
	public static void main(String[] args) {
		
		int[][] arr = {{1,2,3},{1,2,3,4}};
		
		int i,j;
		
		for(i = 0; i < arr.length; i++) {
			for(j = 0; j < arr[i].length; j++) {
				System.out.print(arr[i][j] + ", ");
			}
			System.out.println("\t" + arr[i].length); // 행의 길이
		}
	}
}

결과

 

 

23. 객체 배열을 구현한 클래스 ArrayList

java.util 패키지에서 제공되는 ArrayList

 

  • boolean add(E e) : 요소를 추가하는 것
  • int size() : 전체 요소 개수를 반환
  • E get(int index) : 지우진 않지만 꺼내와보는 것
  • E remove(int index) : 꺼내서 없애는 것
  • boolean isEmpty() : 배열이 비었는지 확인

 

arrayList로 구현해보기

package ch03;

import ch03.Book; // Book클래스도 import
import java.util.ArrayList;

public class ArrayListTest {
 
	public static void main(String[] args) {
		
		ArrayList<Book> library = new ArrayList<>(); 
		// <>에 어떤 객체를 넣을지 지정
		
		library.add(new Book("태백산맥1", " 조정래"));
		library.add(new Book("태백산맥2", " 조정래"));
		library.add(new Book("태백산맥3", " 조정래"));
		library.add(new Book("태백산맥4", " 조정래"));
		library.add(new Book("태백산맥5", " 조정래"));
		
		for(int i = 0; i < library.size(); i++) {
			library.get(i).showInfo();
		}
	}
}

command+shift+o 하거나 빨간줄에 마우스 대면 소스(클래스)의 위치를 알 수 있다.

 

 

24. ArrayList를 활용한 간단한 성적 산출 프로그램

1001학번 Lee와 1002학번 Kim, 두 학생이 있습니다.

Lee 학생은 국어와 수학 2과목을 수강했고, Kim학생은 국어, 수학, 영어 3과목을 수강하였습니다.

Lee 학생은 국어 100점, 수학 50점 입니다.

Kim 학생은 국어 70점, 수학 85점, 영어 100점 입니다.

Student와 Subject 클래스를 만들고 ArrayList를 활용하여 두 학생의 과목 성적과 총점을 출력하시오

 

Student.java

package ch03;

import java.util.ArrayList;

public class Student {
	
	int studentId;
	String studentName;
	
	// 학생이 수강하는 과목 배열로 가지고있기
	ArrayList<Subject> subjectList;

	
	Student(int studentId, String studentName) { // 생성자
		
		this.studentId = studentId;
		this.studentName = studentName;
		
		subjectList = new ArrayList<>();
		
	}

	// 과목을 수강하면 수강한 과목 정보를 subjectList에 넣기
	public void addSubject(String name, int point) {
		Subject subject = new Subject();
		subject.setName(name);
		subject.setScorePoint(point);
		
		subjectList.add(subject);
		// 학생 마다 add(subject)메소드가 호출되어 배열이 따로따로 만들어진다.

	}
	
	public void showScoreInfo() {
		
		int total = 0;
		for( Subject subject : subjectList) {
			
			total += subject.getScorePoint();
			System.out.println(studentName + " 학생의 " + subject.getName() +"과목의 성적은 "+ subject.getScorePoint() +"점 이다.");
		}
	}
}

Subject.java

package ch03;

public class Subject {
 
	private String name;
	private int scorePoint;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getScorePoint() {
		return scorePoint;
	}
	public void setScorePoint(int scorePoint) {
		this.scorePoint = scorePoint;
	}
}

StudentSubjectTest.java

package ch03;

public class StudentSubjectTest {

	public static void main(String[] args) {
		
		Student studentlee = new Student(1001, "Lee");
		
		studentlee.addSubject("국어", 100);
		studentlee.addSubject("수학", 50);
		
		Student studentkim = new Student(1002, "Kim");
		
		studentkim.addSubject("국어", 70);
		studentkim.addSubject("수학", 80);
		studentkim.addSubject("영어", 100);
		
		studentlee.showScoreInfo();
		System.out.println("===================");
		studentkim.showScoreInfo();
	}
}

결과

 

 

 

3주차 후기

생각보다 진도나가는데 시간이 좀 걸렸음

선택 사항 인강도 듣는게 좋을거 같아서 부지런히 해야만 할 거 같은..&&

이제 4주차 Spring 드가는데 생각보다 열혈 자바 진도를 못나가서

빡세게 병행해서 빨리 개념 1회독을 끝내야겠다는 생각을 햇슴..