객체 지향 프로그래밍에서 자기 자신을 나타내는 this라는 키워드에 대하여 살펴보도록 하겠습니다.

 

This의 역할

  • 자기자신의 메모리를 가르키는 역할을 합니다.
  • 생성자에서 다른 생성자를 호출 하는 역할을 합니다. 
  • 인스턴스가 자기 자신의 주소를 반환할 때 사용합니다.

 

자기 자신의 메모리를 가리키는 this

  • 인스턴스,힙 메모리에서 배웠듯이 참조 변수는 stack메모리에 저장되어 heap메모리에 있는 해당 인스턴스를 가리키는 역할을 합니다.
  • 예제에서 BirthDay 클래스의 인스턴스를 생성하며 선언한 참조 변수 day는 해당 인스턴스의 heap메모리 위치는 가르키고 있고 day에 사용하는 this는 day가 가리키는 인스턴스의 메모리 위치를 똑같이 가리키는 역할을 합니다.
  • 만약 BirthDay day2 = new BirthDay(); 라는 인스턴스를 생성시켜서 this를 사용한다면 이때 this는 day가 아닌 day2의 메모리의 위치를 가리킬 것 입니다. 

 

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

public class Person {
	
	String name;
	int age;
	//1번
	public Person() {
		
		this("이름없음",1);
	}
	//2번
	public Person(String name, int age) {
		
		this.name = name;
		this.age = age;
	}
	//3번
	public void showPeorsonInfo() {
		
		System.out.println(name +","+age);
}

}
  • Person class를 만들고 name, age 두개의 멤버 변수를 선언한 후 생성자로 이용해 2개의 인스턴스를 구현하는 경우에 2번과 같이 자기자신을 가르키는 this. 외에도 1번 생성자 안에 this()처럼 2번 생성자를 호출하는 역할로 this를 사용할 수 있습니다.
  • 호출하는 생성자를 사용시 this()괄호안에는 2번 생성자에서 매개변수인 String name과 int age가 들어가야 되고 1번 생성자에서는 초기화값을 주기 위해 "이름없음"과 1을 입력해 주었습니다.
  • 결과를 확인해보기위해 name과 age를 출력해주는 showPersoninfo() 메서드를 3번에 작성하였습니다.
public class PersonTest {

	public static void main(String[] args) {

		Person personNoName = new Person(); 
		personNoName.showPeorsonInfo(); //이름없음,1
		
		Person personLee = new Person("이순신",10);
		personLee.showPeorsonInfo(); // 이순신,10
		
	}

}
  • 1번 생성자를 테스트하기 위해 personNoName이라는 참조변수를 만들고 매개변수를 입력하지 않자 초기화 값인 이름없음,1이 출력되었고 2번 생성자가 잘 호출 되었음을 확인 할 수 있습니다.
  • 2번 생성자를 테스트하기 위해 poersonLee를 만들고 매개변수로 "이순신",10을 입력하였고 그대로 출력되는 것을 확인 할 수 있습니다.
public Person() {
		age = 10
		this("이름없음",1);
	}
  • 1번 생성자를 수정한 위의 문장은 "Constructor call must be the first statement in a constructor"라는 에러가 발생하는데 먼저 작성한 문장이 다른 생성자를 호출 할 경우 충돌이 생길 가능성을 예방하기 위한 에러입니다. 그렇기 때문에 호출을 위해사용하는 this()는 항상 첫문장으로  사용 되야 합니다.

 

자기 자신의 주소를 가르킬 때의 this

public Person getSelf() {
		return this;
	}
  • Person 클래스에 자기 자신의 주소를 반환하는 getSelf 메서드를 작성하였습니다. 이때 반환되는 자료형은 클래스명으로 return 값에 this를 사용하게 됩니다. 
package thisex;

public class PersonTest {

	public static void main(String[] args) {

		Person personNoName = new Person();
		personNoName.showPeorsonInfo();
		
		Person personLee = new Person("이순신",10);
		personLee.showPeorsonInfo();
		//2번
		System.out.println(personLee); //thisex.Person@5305068a 
		//1번
		Person p = personLee.getSelf();
		System.out.println(p);         //thisex.Person@5305068a
				
	}

}
  • 테스트하기 위해 1번과 같이 personLee에 getSlef메서드를 참조하고 변수 p의 대입하고 출력을 하고 P값을 출력했습니다. 그리고 2번 출력문과 같이 바로 personLee의 주소를 출력해보니 둘이 값이 같은 것을 확인 할 수 있습니다. 
  • 1번에서 바로 Person p변수를 사용할 수 있는 이유는 이미 생성된 참조변수 personLee를 담는 역할을 하기 때문이다. 만약 personSong과 같이 새로운 참조변수를 사용한다면 변수p를 사용할 수 없습니다.

객체지향 프로그래밍의 가장 큰 특징인 정보 은닉에 대해 알아보도록 하겠습니다.

 

접근제어자(acces modifier)

  •  접근 제어자는 변수, 메서드, 생성자에 대한 접근 권한을 지정하는 역할을 하고 public, private, protected, 생락(기본접근 제어자) 4가지 종류가 있습니다.
  • public은 변수에 대한 모든 접근을 허용하는 것이고 private을 사용하면 클래스 내부에서만 사용되고 외부에서 접근할 수 없습니다. protected는 클래스가 상속될 때 private 변수나 메서드를 public하게 오픈하고 싶을때 사용되는 것인데 상속을 배운 후 더 자세히 알아보도록 하겠습니다. 마지막으로 생략하는 경우는 기본접근 제어자라고 부르며 같은 페키지내에서만 참조할 수 있게 허용하는 기능을 합니다.

정보 은닉(information hiding)

  • 정보 은닉은 private 키워드를 활용해서 외부에서 클래스 내부의 정보에 접근하지 못하도록 하는 기능을 말합니다.
  • private 변수를 외부에서 접근하게 하려면 public 메서드를 사용하여 값을 입력하거나 읽을 수 있습니다. 이는 클래스 내부의 데이터를 잘못 사용하는 오류를 방지하기 위해 사용 됩니다.
package hiding;

public class MyDate {

	int day;
	int month;
	int year;
}
  • 새로운 package hiding을 만든 후 Mydate 클래스를 생성하여 다음과 같이 접근제어자를 생략한 멤버 변수를 선언합니다.
 package hiding;

public class MyDateTest {

	public static void main(String[] args) {

		MyDate date = new MyDate();
		
		date.day = 10;
		date.month = 7;
		date.year = 2020;

	}

}
  • 같은 hiding 페키지 안에 다음과같이 MyDateTest 클래스를 만들고 MyDate 인스턴스를 생성한 후 각각 멤버변수 값을 입력해주면 오류없이 입력되는 것을 확인 할 수 있습니다.  
  • 하지만 MyDateTest를 hiding페키지가 아닌 다른 페키지로 옮기면 오류가 생기는데 hiding 페키지를 벗어났기 때문에 멤버변수에 접근권한이 없어졌기 때문입니다.
public class MyDateTest {

	public static void main(String[] args) {

		MyDate date = new MyDate();
		
		date.day = 100;
		date.month = 70;
		date.year = 20200;

	}

}
  • 만약 사용자가 변수들의 값을 위의 코드 처럼 잘못 입력하게 되면 프로그래머가 의도하지 않은 결과 값이 나올 수 있기 때문에 접근제어자를 통해 정보 은닉하고 public메서드를 사용해 변수에 접근하게 합니다.
package hiding;

public class MyDate {
	//1번
	private int day;
	private int month;
	private int year;
    //2번
	public void setDay(int day) {
		
		this.day = day;
	}
	public int getMonth() {
		return month;
	}
	public void setMonth(int month) {

		this.month = month; 
	}
	public int getYear() {
		return year;
	}
	public void setYear(int year) {
		this.year = year;
	}
	public int getDay() {
		
		return day;
	}
}
  • 정보 은닉을 위해 1번과 같이 멤버 변수들의 접근제어자 private을 입력해 줍니다.
  • 그 후 다른 클래스에서 멤버 변수에 대한 접근 할 수 있게 만들기 위해 2번과 같이 public 메서드인 get~/set~ 메서드를 만들어 줍니다. get 메서드는 변수를 읽어오는 역할을 하고 set 메서드는 변수에 갑을 대입하는 역할을 합니다.
  • set~메서드 안에 this. 은 멤버 변수와 매개 변수와 같을 때 this.뒤의 변수는 멤버 변수임을 알려주는 역할을 합니다. this.에 관한 자세한 내용은 뒤에 더 배우도록 하겠습니다.
  • set과 get은 save/load 등 다른 메서드명으로 쓰는게 가능하지만 Java에서는 일반적으로 set/goal을 많이 씁니다. 또한 하나의 set/goal 메서드를 만들고 오른쪽클릭 - Source - Generate Getters and Setters를 클릭하면 나머지 변수에 대한 set/goal 메서드를 자동 완성합니다.
public class MyDateTest {

	public static void main(String[] args) {

		MyDate date = new MyDate();
		
		date.setYear(2020);
		date.setMonth(7);
		date.setDay(10);
	}

}
  • MyDateTest 클래스에서 앞에서 설정한 set~메서드를 통해 값을 입력 할 수 있습니다. 하지만 아직 여전히 값이 잘못 입력될 수 있다는 위험성이 있습니다.
public class MyDate {

	private int day;
	private int month;
	private int year;
	//1번
	private boolean isValid;
	
	public void setDay(int day) {
		
		this.day = day;
	}
	
	public int getMonth() {
		return month;
	}
    //2번
	public void setMonth(int month) {
		if (month > 12 || month < 1) {
			isValid = false;
		} else {
		this.month = month; }
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public int getDay() {
		
		return day;
	}
	
    //3번
	public void showDate() {
		
		if (isValid) {
			System.out.println(year + "년" + month + "월" + day + "일 입니다.");
		} else {
			System.out.println("유효하지 않은 날짜 입니다.");
		}
	}
}
  • 사용자가 입력하는 값의 범위를 지정하기 위해 2번과 같이 set~메서드 안에 조건문을 통해 특정 범위의 값만 입력되고 그렇지 않을 경우 다른 수행문을 수행 하도록 지정할 수 있습니다.
  • 위의 코드의 경우 1번과 같이 boolean 변수를 지정 후 2번에서 입력 값의 범위를 넘어설 경우 false를 입력하게 되게하여 3번 출력문에서 false일경우 "유효하지 않은 날짜 입니다"라는 메세지를 출력할 수 있게 작성되었습니다. 
  • 참고로 입력값을 받지 않고 클래스에서 입력되어 있는 값을 읽기만 하는 경우라면 set메서드를 작성하지않고 get메서드만 작성하면 됩니다.
  • 정리해 보면 위와 같이 프로그래머가 사용자가 멤버 변수에 대해 조건적인 접근이 필요하다고 판단 하는 경우 변수에 private이라는 접근제어자를 사용하여 정보를 은닉하고 public메서드가 지정한 방식으로만 변수에 접근 할 수 있게 해야 합니다.

참조 자료형(reference data type)

 

  • 변수의 자료형은 int,long,float,double와 같은 기본 자료형과 String, Date, Student등과 같은 참조자료형이 있습니다.
  • 그 중 참조 자료형은 클래스형으로 변수를 선언하는 것으로 기본자료형은 메모리가 정해져 있지만, 참조 자료형은 클래스에 따라 메모리가 다르다는 특징이 있습니다.

 

참조 자료형 직접 만들어서 사용하기

  • 가장 왼쪽 표와 같이 학생이라는 클래스에 함께 들어 있던 속성들을 각각 학생과 과목 클래스로 나누어서 관리하고학생 클래스에서 필요할때 과목이라는 참조 자료형으로 변수를 만들어 과목의 속성들을 활용하도록 해보겠습니다. 
public class Student {

	int studentID;
	String studentName;
	
	int koreanScore;
	int mathScore;
	int engScore;
	
	String koreaName;
	String mathName;
	String engName;
}
  • 첫번째 그림과 같이 Student 클래스에 학생과 과목에 속성이 혼재되어 있는 모습입니다. 이것을 각각 Student와 Subject 클래스로 나누어주도록 하겠습니다.
public class Subject {

	String subjectName;
	int score;
	int subjectID;
	
}
  • 다음과 같이 Subject 클래스를 만들고 그 안에 Subject의 속성들을 멤버 변수로 선언해 줍니다..
public class Student {
	
	int studentID;
	String studentName;
	
	Subject korea;
	Subject math;
	}
	//2번
	public Student (int id, String name ) {
		studentID = id;
		studentName = name;
		
		korea = new Subject();
		math = new Subject();
	}
    //3번
	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 showStudentScore() {
		int total = korea.score + math.score;
		System.out.println(studentName+ "학생의 총점은" + total + "입니다.");
	}
  • Student 클래스로 돌아와 Subject 클래스를 사용하기 위해 1번과 같이 Subject 클래스를 참조변수로 삼는 korea; math; 변수를 선언해 줍니다. 
  • String과 같은 참조형 변수는 기본 자료형과 같이 바로 사용할 수 있는게 있지만 그 외의 참조형 변수는 생성자를 구현해야 사용할 수 있습니다. 그래서 2번과 같이 Student의 생성자를 구현하며 함께 korea와 math를 참조변수로 하는 Subject 인스턴스를 생성합니다.
  • 3번과 같이 각각 과목의 이름과 점수를 입력할 수 있는 setSubjct메소드를 만들고 그 합산을 구해 출력하는 showStudentScore 메서드를 만듭니다.
public class StudentTest {

	public static void main(String[] args) {
		//1번
		Student studentLee = new Student(100,"이순신");
		//2번
		studentLee.setKoreaSubject("국어", 100);
		studentLee.setMathSubject("수학", 80);
        //1번
		Student studentKim = new Student(101,"김유신");
        //2번
		studentKim.setKoreaSubject("국어", 80);
		studentKim.setMathSubject("수학", 90);
		//3번
		studentLee.showStudentScore();
		studentKim.showStudentScore();
	}
    //결과값
    이순신학생의 총점은180입니다.
	김유신학생의 총점은170입니다.


}
  • 테스트를 위해 StudentTest 클래스를 생성하고 1번처럼 StudnetLee를 참조변수로 하는 Student 인스턴스를 생성합니다.
  • 2번과 같이 setSubject메서드를 통해 각각의 과목명과 점수를 입력한 뒤에 3번과 같이 과목 점수의 합계를 출력하는 메소들 통해 결과값이 나오는 것을 확인합니다.
  • 개인적인 정리로는 참조형 변수를 사용한다는 것 = 클래스에 인스턴스를 생성해서 사용한다는 것이라고 정리가 됩니다. 

생성자와 생성자 오버로딩에 대하여 알아보도록 하겠습니다.

 

생성자(constructor)

  • 객체를 생성할 때(인스턴스를 만들 때) new 키워드와 함께 호출합니다. 다른 메서드들이 언제든 불러서 사용할 수 있는 것과는 다르게 객체를 생성할 때 만 사용할 수 있습니다.
  • 생성자는 인스턴스를 생성할 때 멤버 변수를 초기화 하는 코드가 주를 이루어 구현되어 있습니다.
  • 일반 메서드와 다르게 반환 값이 없고 상속이 되지 않습니다.(상속은 뒤에 더 자세히 배워보겠습니다.)
  • 생성자는 클래의 이름과 항상 동일해야 합니다. ex) Studnt clas의 생성자명은 Student

기본 생성자(default constructor).

public class Student {

	public int studentID;
	public String studentName;
	public String address;
	
//  public Student() {}
	
	public void showStudentInfo(){
		System.out.println(studentName +","+ address);
		
	}
	
	public String getStudentName() {
		
		return studentName;
	}
	
	}
  • 하나의 클래스에는 반드시 하나 이상의 생성자가 존재해야 하는데 위 코드와 같이 프로그래머가 생성자를 구현하지 않을 경우 컴파일러가 'public 클래스명() {}' 과 같은 기본 생성자를 자동으로 생성하게 됩니다.
  • 기본 생성자를 더 자세히 살펴보면 public Student() {} 에서 생성자명은 반드시 클래스명과 같고 ()안에 들어가는 매개변수와 {}안에 들어가는 구현부(body)가 없습니다. 그래서 클래스에 생성자를 작성하지 않고 인스턴스를 생성 할시 'class명 참조변수 = new class명()'으로 참조변수를 비운채 생성하는 것 입니다.
  • 만약 클래스안에 다른 생성자가 있을 경우 기본 생성자는 제공되지 않습니다. 만약 기본생성자를 사용하고 싶다면 다른 생성자와 함께 기본생성자도 작성해주면 사용할 수 있습니다.

 

public class Student {

	public int studentID;
	public String studentName;
	public String address;
	
	//생성자1 구현
	public Student(String name) {
		
		studentName = name;
	}
	
	//생성자2 구현
    public Student(int id, String name) {

    	studentID = id;
    	studentName = name;
    	address = "주소 없음"; // 안쓸시 기본값은 null
    }
	
	public void showStudentInfo(){
		System.out.println(studentName +","+ address);
		
	}
	
	public String getStudentName() {
		
		return studentName;
	}
	}
  • 초기화할 멤버변수가 있다면 기본변수를 생성하지 않고 생성자1과 2 처럼 매개변수와 구현부를 작성하여 생성자를 작성하면 됩니다.
public class StudentTest {

	public static void main(String[] args) {

		Student studentLee = new Student("이순신"); //생성자1 사용
		//studentLee.studentName ="이순신";
		studentLee.address = "서울";
		
		studentLee.showStudentInfo();//결과값  이순신,서울
		
		Student studentKim = new Student(1234,"김유신"); //생성자2 사용
		//studentKim.studentName ="김유신";
		//studentKim.address = "경주";
		
		studentKim.showStudentInfo();//결과값 김유신,주소 없음
	
	}

}
  • 생성자를 클래스에서 만든다면 인스턴스를 생성할 때는 사용하고자 하는 생성자의 매개변수를 입력하여 사용하면 됩니다.
  • studentLee의 경우 생성자1을 사용하기위해 name만 입력하고 studentKim의 경우는 생성자2를 사용하기 위해 id와 naem 두개 입력해 줍니다.
  • 생성자2에서 주소를 "주소 없음"으로 초기화 해두었기 때문에 studetKim에 주소를 입력하지 않아도 초기화 값이 출력되는 것을 확인할 수 있습니다.

생성자 오버로딩(constuctor overloading)]

  • 생성자 오버로딩은 위에 Student 클래스처럼 클래스안에서 다른 매개변수를 가지는 두개 이상의 생성자를 구현하는 경우를 뜻합니다. 
  • public int studentID라는 멤버변수를 private int studentID으로 바꾸면 현재 클래스 내에서만 사용한다는 뜻으로 다른 클래스에서 참조변수를 통해 호출하려고해도 참조목록에 안뜨게 됩니다. 이런 경우에도 생성자에서는 멤버변수를 구현하여 초기화 할 수 있기때문에 private변수를 초기화하는 용도로 생성자가 활용되게 됩니다.
  • 구현된 생성자 중에 사용자가 선택해서 사용할 수 있기 때문에 여러개를 구현해두면 사용자 입장에서 상황에 맞게 활용할 수 있어 편의성이 증가합니다.

 

클래스와 객체에 대해 배운내용들을 바탕으로 2가지 예제를 풀어보도록 하겠습니다.

 

Q1.다음 객체에 대한 설명에 맞는 클래스를 만들고 값을 출력해 보세요.

나이가30살, 이름이 James라는 남자가 있습니다. 이 남자는 결혼을 했고 자식이 셋 있습니다.

 

<출력결과>

나이 : 30

이름 : James

결혼여부 : ture

자녀 수 : 3

 

 

package classpart;

public class People {
	
	public int age;
	public String name;
	public boolean isMarried;
	public int children;
	
	public void peopleinfo() {
		
		System.out.println("나이 :"+age);
		System.out.println("이름 :"+name);
		System.out.println("결혼 여부 :"+isMarried);
		System.out.println("자녀 수 :"+children);
	}

}
  • People이라는 클래스를 만들고 나이 이름 결혼여부 자녀 수와 같은 클래스의 속성들을 멤버변수로 선언해 주었습니다.
  • 출력결과와 같이 출력해주는 메소드를 만들고 반환값과 매개변수가 없으니 생략해주고 void를 적어줍니다.
public class PeopleTest {

	public static void main(String[] args) {

		People peopleJames = new People();
		
		peopleJames.age = 40;
		peopleJames.name = "James";
		peopleJames.isMarried = true;
		peopleJames.children = 3;
		
		peopleJames.peopleinfo();
	}

}
//결과값
나이 :40
이름 :James
결혼 여부 :true
자녀 수 :3
  • ㅋ클래스를 테스트해볼 peopleTest 클래스를 만들어주고 참조 변수 poepleJames를 통해 인스턴스를 생성합니다. 
  • pleJames.멤버변수를 통해 James의 정보를 대입시켜주고 마지막에 peopleinfo 메서드를 사용해 결과값을 출력합니다

Q2j.아래 내용을 클래스로 구현하세요

쇼핑몰에 주문이 들어왔습니다. 주문 내용은 다음과 같습니다.

주문번호 : 201907210001
주문자 아이디 : abc123
주문 날짜 : 2019년 7월 21일
주문자 이름 : 홍길순
주문 상품 번호 : PD-345-12
배송 주소 : 서울시 영등포구 여의도동 20번지

위 주문 내용을 구현할 수 있는 클래스를 만들고 인스턴스로 생성한 후 위와 같은 형식으로 출력해보세요.

 

public class Order {
	
	public String orderNum;
	public String customerID;
	public String orderDate;
	public String customerName;
	public String productNum;
	public String shippingAddress;
	
	public void orderInfo() {
		System.out.println("주문 번호 :"+orderNum);
		System.out.println("주문자 아이디 :"+customerID);
		System.out.println("주문 날짜 :"+orderDate);
		System.out.println("주문자 이름 :"+customerName);
		System.out.println("주문 상품 번호 :"+productNum);
		System.out.println("배송 주소:"+shippingAddress);
	}

}
  • order 클래스를 만들고 각 항목들을 멤버변수로 선언한 후 출력양식에 따라 출력되는 orderInfo메서드를 만들었습니다. 

 

public class OrderTset {

	public static void main(String[] args) {

		Order order0001 = new Order();
		
		order0001.orderNum = "201907210001"; 
		order0001.customerID = "abc123";
		order0001.orderDate = "2019년 7월 21일"; 
		order0001.customerName = "홍길순";
		order0001.productNum = "PD-345-12"; 
		order0001.shippingAddress = "서울시 영등포구 여의도동 20번지";
		
		order0001.orderInfo();
	
		
	}

}
//결과값
주문 번호 :201907210001
주문자 아이디 :abc123
주문 날짜 :2019년 7월 21일
주문자 이름 :홍길순
주문 상품 번호 :PD-345-12
배송 주소:서울시 영등포구 여의도동 20번지
  • OrderTest 클래스를 만들어 인스턴스를 선언하고 참조변수로 order0001 선업합니다.
  • 각 멤버변수에 알맞은 값을 대입해주고 마지막에 orderInfo메서드로 결과값을 출력합니다.

인스턴스의 개념을 알아보고 생성된 객체가 어떻게 메모리에 잡혀 있는지 알아보도록 하겠습니다.

 

인스턴스

public class StudentTest {

	public static void main(String[] args) {

		Student studentLee = new Student();
		studentLee.studentName = "이순신";
		studentLee.address = "서울";
		
		studentLee.showStudentInfo();
		
		Student studentKim = new Student();
		studentKim.studentName = "김유신";
		studentKim.address = "경주";
		
		studentKim.showStudentInfo();
	}

}
  • 지난 시간 만들었던 StudnetTest에 studnetKim을 추가로 생성해서 만들었습니다.
  • studentLee 나 studenKim 처럼 하나의 클래스를 기반으로 생성자라는 new키워드를 통해서 여러개의 객체를 생성 할 수 있는데 그 생성된 객체들을 인스턴스라고 부릅니다.
  • studnetLee는 생성된 Student 인스턴스를 가르키는 참조변수라고 부르고 참조변수의 "."을 붙이면 클래스가 가지고 있는 멤버변수나 메소드를 참조할 수 있습니다.

  • 메모리가 어떻게 잡히는지 살펴보면 studnetLee 같은 참조변수는 지역변수 이기 때문에 앞에서 배운것 처럼 Stack 메모리에 잡히게 되고 new 키워드로 생성된 Student 인스턴스는 생성된 순간 Heap 메모리에 잡히게 됩니다. 이때 참조변수는 생성된 메모리가 heap메모리 어디에 잡혀있는지 참조하는 역할을 합니다.
  • 생성된 Student 인스턴스 메모리 안에는 Student의 멤버변수가 함께 저장되게 됩니다.(studentID;studentName;address;) 그래서 참조변수에 .을 했을 때 해당 인스턴스에 멤버변수들이 참조값으로 뜨게 되는 것 입니다.
  • 정리해보면 각각의 인스턴스는 생성된 순간 별개의 독립적인 메모리로 잡히게 되고 참조변수는 해당 인스턴스의 메모리 위치를 참조하는 역할을 하게 됩니다.
	System.out.println(studentLee);
	System.out.println(studentKim);
        //결과값
	classpart.Student@5305068a
	classpart.Student@1f32e575
  • main함수에 참조변수인 studentLee와 studentKim을 출력해보면 다음과 같이 클래스의 풀네임(pakcage name + class name)@참조값 유형으로 나타납니다.
  • 참조값은 16진수로 한자리의 4비트를 포함하기 때문에 Student class에 32비트가 할당됨을 알 수 있습니다.

용어정리

참고사항 - main함수 != 메서드

public class Student {

	public int studentID;
	public String studentName;
	public String address;

	public void showStudentInfo(){
		System.out.println(studentName +","+ address);
		
	}
	
	public String getStudentName() {
		
		return studentName;
	}
	
	public static void main(String[] args) {
		
	}
	}

 

  • 위와 같이 클래스 내에 실행 함수인 main을 포함시킬 수도 있지만 main 함수는 Student클래스에 메서드가 아닌 독립적인 함수 입니다. 이런 부분에 혼동을 피하기 위해 Test 클래스를 따로만들어 실행시키는게 좋습니다.

클래스안에서 사용되는 함수(function)의 일종인 메서드를 만들기 위해 함수의 개념과 함수의 구조, 그리고 호출될 때 사용되는 메모리에 대해서 배워보도록 하겠습니다.

 


함수란?

함수(function)

  • 함수는 다른 프로그램 언어들에도 많이 사용되는 개념으로 하나의 기능을 구현 하는 일련의 코드를 뜻합니다. 함수의 길이와는 상관없이 하나의 기능만을 합니다.
  • 함수로 구현된 기능은 필요한 여러곳에서 호출되어서 사용됩니다. 예를 들어 더하기라는 함수가 있다면 거리를 더하던 숫자를 더 하던 성적을 더하던 더하기라는 한 기능을 여러 곳해서 호출해서 사용하게 됩니다..
  • 그렇기 때문에 재사용이 가능하고, 기능이 분리되어서 작성 되기 때문에 가독성이 좋고 이후에 유지 보수하는데 도움이 된다는 장점이 있습니다.

함수의 입력과 반환

  • 입력값 => 함수 => 결과값의 구조로 되어 있으며 함수에 따라서 입력값이나 반환값이 없을 수 도 있습니다.
  • Java에서는 입력값을 매개변수, 결과값을 반환값이라고 부릅니다.

 

함수 정의 하기

  • 함수는 이름, 매개변수(입력값), 반환 값(결과 값), 함수 몸체(body)  4가지로 구성되지만 매개변수와 반환 값은 경우에 따라 생략 될 수 있습니다.
  • 다음은 더하기 함수 예시이고 아래에 각 항목별 설명을 괄호로 붙여봤습니다.

더하기 함수 예시

 

int(반환하는 결과값의 자료형 없을시 void) add(함수명)(int num1, int num2)(자료형 매개변수1, 자료형 매개변수2)

{

 

      int result;                   ( 함수 몸체 )

      result = num1 + num2 (  body      )

      return result; (반환값)

}

 

실습

public class FunctionTest {
	//function 1
	public static int addNum(int num1, int num2 ) {
		int result;
		result = num1 + num2;
		return result;
	}
	//function 2
	public static void sayHello(String greeting) {
	
		System.out.println(greeting);
	}
	//function 3
	public static int calSum() {
		int sum = 0;
		int i;
		
		for (i = 0 ; i<=100; i++) {
			sum+= i;
		}
		return sum;
	}
	
	public static void main(String[] args) {
		// call function1 
		int n1= 10;
		int n2= 20;
		
		int total = addNum(n1,n2);
		System.out.println(total);  // result = 30
		// call function2
		sayHello("안녕하세요"); // result = 안녕하세요
		// call function3 
		int num = calSum();
		System.out.println(num); // 5950

	}

}
  • function1은 addNum 함수로 num1, num2에 매개변수를 입력하여 int result의 반환값을 내보내는 함수입니다.
  • function2는 String 매개 변수를 출력해주는 함수로 반환값이 없어 void라고 써줍니다.
  • function3은 1~100 까지 더하는 함수로 매개변수는 없고 반환값이 int인 함수입니다.
  • 함수는 기능이고 자체 출력이 되지 않기 때문에 호출해서 사용해야 합니다. main에 3가지 함수를 호출해서 알맞은 함수명과 알맞은 매개변수를 괄호안에 넣으면 함수기능에 따라 결과값을 출력합니다.

매서드(method)

  • 메서드는 객체의 기능을 구현하기 위해 클래스 내부에서 구현되는 함수를 뜻합니다. 즉, 메서드를 구현함으로 객체의 기능이 구현되게 됩니다.
  • 메서드의 이름은 사용하는쪽(클라이언트 코드)에 맞게 명명하는게 좋은데 예를들어 학생의 이름을 불러오는 메서드를 만든다다면 getStudentName이라고 짓는 것을 뜻합니다. (서버쪽 입장에서 생각해보면 sendStudnetName이 될 수도 있기 때문입니다.)
  • 또한 함수명은 Camel Notaion에 맞게 시작은 소문자로 하여 단어가 바뀔 때 마다 대문자를 사용해주는게 좋습니다.

 

함수와 메모리

public class FunctionTest {
	//function 1
	public static int addNum(int num1, int num2 ) {
		int result;
		result = num1 + num2;
		return result;
	}

	
	public static void main(String[] args) {
		// call function1 
		int n1= 10;
		int n2= 20;
		
		int total = addNum(n1,n2);
		System.out.println(total);  // result = 30
	
	}

}
  • 함수에서 사용되는 메모리는 짐이 쌓여있는 모양의 스택메모리라고 하여 먼저 들어가는것 아래로 쌓이고 사용할때는 위에서부터 꺼내서 사용하는 메모리입니다.
  • 위 예시에 메모리 사용을 생각해 보면, 먼저 메모리에 main 함수가 들어가 있고 함수 안에 포함된 지역변수(함수내에서만 사용되는 변수) args[], n1=10, n=20가 포함되어 있습니다.
  • 그 상태에서 addNum을 호출하면 main함수 위에 addNum이 메모리에 잡히게 되고 그 변수인 num1; num2; result;도 함께 메모리에 잡히게 됩니다.
  • addNum 함수가 호출되는 순간 main에 있던 n1의 값10이 num1으로 넘어가 대입되고 num2 값도 넘어가게 됩니다. 
  • 그 후 addNum이 리턴되고 수행이 끝나게 되면 메모리에는 main함수 부분만 남고 addNum은 메모리에서 사라집니다. (함수는 사용 후 메모리를 반환합니다.)

Java 기초 실습을 마치고 다음 단원으로 넘어가 객체지향 프로그래밍을 배우기 위한 첫시간으로 객체지향 프로그래밍과 클래스에 대해 알아보도록 하겠습니다.

 


객체란?

객체 (object)

 

  • 객체는 '의사나 행위가 미치는 대상'이라는 사전적 의미를 가지고 있습니다.
  • 자바에서는 하나의 역할을 수행하는 메소드와 변수의 묶음 혹은 단위라고 볼 수 있고 클래스라는 형태로 묶이게 됩니다.
  • 사람, 자동차와 같은 명사형의 객체도 있지만 주문, 생산, 관리와 같은 동사형의 객체도 존재합니다.

 

객체지향프로그래밍이란?

절차지향 프로그래밍과 객체지향프로그래밍

 

절차지향프로그래밍( Procedural Programming)

  • 초기 프로그래밍 방식으로 시간이나 사건의 흐름에 따라 프로그램을 구현하는 방식입니다. 대표적으로 C언어가 있습니다.
  • 학교를 가는 과정을 절자지향 프로그래밍으로 표현한다면 {일어난다 > 씻는다 > 밥을 먹는다 > 버스를 탄다> 요금을 지불한다 > 학교에 도착한다}  같이 시간에 순서대로 일어나는 일을 표현합니다.
  • 프로그래밍이 복잡해짐에 따라 여러가지 절차가 시간 순서와 상관없이 교차해서 일어나는 상황을 프로그래밍하기  어렵다는 한계가 있습니다.

 

 

 

객체지향프로그래밍(Object Oriented Programming : OOP)

  • 객체를 기반으로 하여 객체를 정의하고 객체의 기능을 구현하며 객체간의 협력(상호작용)을 통해 프로그램을 구현하는 방식입니다.
  • 학교를 가는 과정을 객체지향 프로그래밍으로 표현한다면 다음 그림과 같습니다.

  • 학생, 밥, 버스, 학교 같이 객체를 정의하고 학생-밥, 학생-버스 사이의 연관관계를 프로그래밍하는 것이 객체지향 프로그래밍이라고 할 수 있습니다.
  • 객체간의 관계를 자유롭게 구성할 수 있기 때문에 시간순서에 상관없이 프로그램을 구현할 수 있습니다.

클래스?

  • 객체라는 추상적인 개념을 코드화 한 것을 클래스라고 합니다. 객체 지향 프로그래밍의 가장 기본적인 요소로 청사진(blue print)라고도 부르기도 합니다.
  • 클래스에는 객체의 속성과 기능을 표현하는 변수와 메소드를 포함합니다.

멤버변수, 메서드

멤버변수

  • 클래스 안에서 선언되는 변수를 멤버 변수라고 하며 객체가 가지는 속성을 표현는데 사용됩니다.
  • 예를 들어 사람이라는 객체는 나이, 성별, 직업, 키 등 여러가지 속성을 가질 수 있고 그 필요한 속성을 변수로 선언하여 사용 하는 것 입니다.
  • member variable , property, attribute 으로 표현합니다.

메서드

  • 객체안에서 사용하는 함수로 객체가 제공하는 기능을 구현하는데 사용합니다.
  • method, member function

클래스 정의하기

클래스를 직접 정의해보며 배운내용들을 확인해 보겠습니다.

 

조건에 맞게 학생에 대한 클래스를 구현해 보세요.

 

조건

  • 학생이 있습니다
  • 학생은 학번, 이름, 주소 값을 가집니다.
  • 학생의 정보를 보여줍니다.
  • 학생에 대한 클래스를 구현해보세요
 package classpart;

public class Student {

class ABCD{
	}
}
  • public class Student 에서 Student는 클래스명으로 .java파일명과 동일해야 합니다. Studenta라고 입력하면 오류가 발생합니다
  • clas ABCD와 같이 class내에 여러 class를 만들 수 있지만 public class는 class안에 한 개만 작성할 수 있습니다.
public class Student {

	public int studentID;
	public String studentName;
	public String address;
}

 

  • 학생의 여러가지 속성 중 우리가 필요한 학번, 이름 주소를 변수(멤버 변수)를 선언해 줍니다. 
  • String은 자바에서 제공하는 클래스로 Char[]를 대체하여 문제열을 표현할때 사용합니다.
  • public이란 키워드는 접근제어자라고 하며 4가지 종류가 있다고 알아두고 뒤에 더 자세히 배우도록 하겠습니다. 
	public void showStudentInfo(){
		System.out.println(studentName +","+ address);
		
	}
  • 학생의 정보를 보여주는 기능을 표현하기 위해 showStudentInfo메서드를 작성합니다
  • 메서드는 void는 결과값 자리로 현재는 결과값이 없기 때문에 void로 비우둡니다.
  • showStudentInfo()는 메서드의 이름을 지정해주는 것이고 괄호안에는 매개변수(입력값)가 들어갈 수 있습니다.

클래스 사용하기

작성한 클래스를 사용하기 위해 클래스를 생성하고 클래스의 속성과 메서드를 참조해주어야 합니다. 

public class StudentTest {

	public static void main(String[] args) {

		Student studentLee = new Student();
		studentLee.studentName = "이순신";
		studentLee.address = "서울";
		
		studentLee.showStudentInfo();
        
        //결과값
        이순신,서울
	}

}
  • Student class를 테스트하기 위한 StudentTest class를 만들고 main을 작성합니다. 
  • Student class를 사용하기 위해 Student studentLee = new Student(); 생성자를 작성해 class를 생성해줍니다. class를 생성을하게 되면 메모리가 할당되고 사용가능 하게 되는데 이과정에 대해서는 뒤에 더 자세히 배워 보겠습니다.
  • studentLee. 을 작성하면 선택박스에 우리가 작성해놓은 변수들과 메소드가 뜨게 되는데 그것을 '참조'한다라고 합니다. 그 중 studentName과 address를 참조해 각각의 값을 대입해줍니다.
  • 마지막으로 showStudentInfo메소드를 작성하고 실행을 해보면 결과 값이 나오는 것을 확인할 수 있습니다.

 

 

 

 

이번엔 만들어 놓은 달력에 일정을 등록하고 등록한 일정을 검색하는 추가 기능을 구현해보겠습니다.

 


	public void printMenu() {
		
		System.out.println("+----------------------+");
		System.out.println("| 1. 일정 등록 ");          
		System.out.println("| 2. 일정 검색");           
		System.out.println("| 3. 달력 보기");
		System.out.println("| h. 도움말 q. 종료");
		System.out.println("+----------------------+");
	}

메뉴 목록을 만들기 위해 Prompt 클래스에 printMenu 메소드를 추가합니다.

 

public void runPrompt() {
		
		printMenu();
		Scanner scanner = new Scanner(System.in);
		Calendar cal = new Calendar();
		
		
		
		for (;;) {
								
			String cmd = scanner.next();
			if (cmd.equals("1")) cmdRegister();
			else if(cmd.equals("2")) cmdSearch();
			else if(cmd.equals("3")) cmdCal(scanner, cal);
			else if(cmd.equals("h")) printMenu();
			else if(cmd.equals("q")) break;
			
		}

		System.out.println("Thanks, Bye~");
		scanner.close();
		
	}

메뉴 항목에 맞추어 입력값을 입력 받기 위해 runPrompt() 안에  for문과 if-else-if문을 사용해 각 입력 별 어떤 메소드가 사용되야 하는지 지정해 줍니다. (1-cmdRegister(), 2- cmdSearch()....)

 

private void cmdCal(Scanner s, Calendar c) {
		
		int month = 1;
		int year = 2020;
		
		System.out.println("연도를 입력하세요.(exit: -1)");
		System.out.print("YEAR> ");
		year = s.nextInt();
			
		System.out.println("월을 입력하세요:");
		System.out.print("MONTH> ");
		month = s.nextInt();
		
		if (month >12 || month <1) {
			System.out.println("잘못된 입력 입니다.");
			
		} else{ c.printCalendar(year, month);;
			
			System.out.println("");
		}
			
			return;
		}

runPrompt 메소드 안에 있던 달력의 연과 월을 입력 받던 입력문들을 cmdCal메소드로 이동해 줍니다.

 

cmdCal(Scanner s, Calendar c) 은 이미 본문에서 선언된 Scanner와 Calendar 클래스를 중복으로 선언하는 것을 피하기 위해 입력값으로 참조하여 놓은 모습입니다.

 

중간 결과 값

+----------------------+
| 1. 일정 등록 
| 2. 일정 검색
| 3. 달력 보기
| h. 도움말 q. 종료
+----------------------+
h
+----------------------+
| 1. 일정 등록 
| 2. 일정 검색
| 3. 달력 보기
| h. 도움말 q. 종료
+----------------------+
3
연도를 입력하세요.(exit: -1)
YEAR> 2020
월을 입력하세요:
MONTH> 7
   <<2020년 7월>>
 SU MO TU WE TU FR SA
----------------------
           1  2  3  4
  5  6  7  8  9 10 11
 12 13 14 15 16 17 18
 19 20 21 22 23 24 25
 26 27 28 29 30 31

q
Thanks, Bye~

h를 입력하면 메뉴가 호출되고 3을 입력하면 달력을 볼 수 있으며 q를 입력하면 실행이 종료되는 것을 확인 할 수 있습니다.

 

 

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public void registerPlan(String strDate, String plan) throws ParseException {
		
		Date date = new SimpleDateFormat("yyyy-MM-dd").parse(strDate);
		System.out.println(date);
		
	}

 

일정을 등록하기 위해 먼저 String 입력값 strDate를 date로 바꿔주기 위한 registerPlan 메소드를 작성합니다. 

 

SimpleDateFormat 클래스를 통해 yyyy-MM-dd 형식의 String 입력값을 date형식으로 바꾸어 줍니다.

 

public static void main(String[] args) throws ParseException {
		
		Calendar cal = new Calendar();
		
	cal.registerPlan("2020-07-08", "Let's hava dinner!");
		
		
	}
    //결과값
    Wed Jul 08 00:00:00 KST 2020

registerPaln이 잘 출력 되는지 확인하기 위해 간단한 출력문을 사용해서 확인해보니 date형식의 결과값이 나오는 것을 확인할 수 있었습니다.

 

 

	private HashMap<Date, String> planMap;
	
	public Calendar() {
		planMap = new HashMap<Date, String>();
	
	}

 

다음으로 날짜별 계획을 저장하기 위해 (key,data)형식의  배열로 저장해주는 HashMap 클래스를 사용해 planMap이라는 메소드를 만들어 줍니다.

 

public void registerPlan(String strDate, String plan) throws ParseException {
		
		Date date = new SimpleDateFormat("yyyy-MM-dd").parse(strDate);
		//System.out.println(date);
		planMap.put(date, plan);
		
	}

입력된 날짜와 일정을 저장하는 넣기 위해 registerPlan 메소드에 planMap.put(date, plan); 문장을 넣어줍니다.

 

이것을 통해 사용자가 strDate와 plan을 입력하면 planMap 메소드를 통해 저장이 됩니다.

 

	public String serchPlan(String strDate) throws ParseException {
		
		Date date = new SimpleDateFormat("yyyy-MM-dd").parse(strDate);
		String plan = planMap.get(date);
		return plan;
	}

다음으로 저장된 plan을 불러오기 위해 serchPlan메소드를 작성합니다.

 

Date형식으로 저장되어 있기 때문에 사용자가 strDate를 입력하면 Date형식으로 바꾸어서 planMap.get(date)안에 넣음으로 저장된 plan 값을 검색해서 return 합니다.

 

	//simple test code
	public static void main(String[] args) throws ParseException {
		
		Calendar cal = new Calendar();
		
	cal.registerPlan("2020-07-08", "Let's hava dinner!");
	System.out.println(cal.serchPlan("2020-07-08"));
		
	}
    
    //결과값
	Let's hava dinner!

 

중간 결과를 확인해보기 위해  registerPlan을 통해 (strDate,plan)을 입력하여 저장하고  serchPaln을 통해 (strDate)를 입력하여 검색한 결과 plan 값이 출력됩니다.

 

일정 등록 / 검색 메소드를 완성하였으니 입력 부인 Prompt 클래스로 돌아가서 앞에서 미리 만들어 놓은 cmdRegister/cmdSearch 메소드를 채워 넣겠습니다.

 

private void cmdRegister(Scanner s, Calendar c) throws ParseException {
		System.out.println("[새 일정 등록]");
		System.out.println("날짜를 입력해주세요 (yyyy-MM-dd)");
		String date = s.next();
		String text = "";
		s.nextLine(); // ignore one new line
		System.out.println("일정을 입력해주세요 (yyyy-MM-dd)");
		text = s.nextLine();
		
		c.registerPlan(date, text);
		
	}

cmdRegister 메소드에 Stirng date와 Stirng text 입력값을 받아서 c.regiserPlan 메소드를 이용해 저장하게 합니다.

 

중간에 s.nextLine();문장을 작성 하지 않을 경우 나중에 저장한 일정을 불러 와도 빈칸인 경우가 생기니 꼭 작성해야 합니다.

 

private void cmdSearch(Scanner s, Calendar c) throws ParseException {
		System.out.println("[일정 검색]");
		System.out.println("날짜를 입력해주세요 (yyyy-MM-dd)");
		String date = s.next();
		String plan = c.serchPlan(date);
		
		System.out.println(plan);
	}

 

cmdSearch 메소드에선 date를 입력하면 c.serchPaln 메소드를 통해 해당 date값 옆에 저장 되어있는 plan을 불러와서 출력해주도록 작성 했습니다.

 

public void runPrompt() throws ParseException {
		
		printMenu();
		Scanner scanner = new Scanner(System.in);
		Calendar cal = new Calendar();
				
		for (;;) {
								
			String cmd = scanner.next();
			if (cmd.equals("1")) cmdRegister(scanner, cal);
			else if(cmd.equals("2")) cmdSearch(scanner, cal);
			else if(cmd.equals("3")) cmdCal(scanner, cal);
			else if(cmd.equals("h")) printMenu();
			else if(cmd.equals("q")) break;
		}

		System.out.println("Thanks, Bye~");
		scanner.close();
	}

마지막으로 cmdRegister와 cmdSearch에 참조 값으로 scanner,cal을 입력해줍니다.

 

public void runPrompt() throws ParseException {
		
		printMenu();
		Scanner scanner = new Scanner(System.in);
		Calendar cal = new Calendar();
		
		boolean isLoop = true;
		while (isLoop) {
								
			String cmd = scanner.next();
			
			switch(cmd) {
			case "1" :
				cmdRegister(scanner, cal);
				break;
			case "2" : 
				cmdSearch(scanner, cal);
				break;
			case "3" :
				cmdCal(scanner, cal);
				break;
			case "h" :
				printMenu();
				break;
			case "q" :
				isLoop = false;
				break;
			default :
				printMenu();
				break;
			}
			}
		System.out.println("Thanks, Bye~");
		scanner.close();
			}

 

runPrompt()메소드를 if -else if 문으로 작성하는 것보다 switch-case문이 더 가독성이 좋을 것 같아서 수정해보았습니다.

 

유의할 점은 case "q"의 경우 수행문에 break;만 입력할시 switch-case문만 빠져나감으로 인해 반복문을 계속 반복을 하게 됩니다.

 

q를 입력시 반복문을 빠져나와 실행이 멈추게 하려면 반복문 앞에 boolean isLoop = true; 변수를 선언하여 while(isLoop)를 하여 평소에는 무한 반복을 하게하고 case"q"의 수행문에 isLoop = false를 대입하면 q를 통해 switch-case를 빠져나온 후에 while문에 false가 대입되기 떄문에 반복문 또한 빠져나와 실행도 정지가 됩니다.

 

 

최종 결과값

+----------------------+
| 1. 일정 등록 
| 2. 일정 검색
| 3. 달력 보기
| h. 도움말 q. 종료
+----------------------+
1
[새 일정 등록]
날짜를 입력해주세요 (yyyy-MM-dd)
2020-07-08
일정을 입력해주세요 (yyyy-MM-dd)
공부마치고 집에가기
2
[일정 검색]
날짜를 입력해주세요 (yyyy-MM-dd)
2020-07-08
공부마치고 집에가기

 

 

다음과 같이 1을 입력하여 날짜와 일정을 입력하고 2를 입력하고 해당 날짜를 입력하면 저장된 일정이 출력되는 기능을 구현 하였습니다.

앞에서 수동으로 시작하는 요일을 입력하는 달력에서 연과 월을 입력하면 자동으로 시작하는 요일을 계산해서 출력해주는 달력을 만들어보겠습니다.

 


	
		public static int getWeekDay(int year, int month, int day) {
		
		int syear = 1970; // 목요일
		final int STANDARD_WEEKDAY = 4; //1970/Jan/1st = Thursday
		
		int count = 0;
		
		for (int i = syear; i < year; i++ ) {
			
			int delta = isLeapYear(i) ? 366:365;
			count += delta;			
		}
			
		for (int i = 1; i <month; i ++) {
			
			int delta = getMaxDaysOfMonth(year, i);
			count += delta;
		}
		
		count += day -1;
		
		//System.out.println(count);
		
		int weekday = (count + STANDARD_WEEKDAY) % 7;
		
		return weekday;
	}
		 

 

먼저 Calendar 클래스에 요일을 구하는 getWeekDay 메소드를 만들어 보겠습니다.

 

달력의 요일을 계산하는 방법은 여러가지가 있지만 그중에 기준이 되는 날짜로 부터 몇일이 지났는지를 계산해서 요일을 구하는 방식으로 선택하고 기준이 되는 날을 1970년 1월1월 목요일로 정합니다.

 

기준년을 1970년을 syear로 변수를 선언하고 기준요일인 목요일 숫자 4으로 변환해 변수로 선언합니다. (일요일=0, 월요일=1... 목요일 =4) 

 

연수에 따라 지나간 날짜를 계산하기 위한 반목문을 작성해 syear~year까지 윤년여부에 따라 366 or 365를 더하게 합니다.

 

월수에 따라 지나간 날짜를 계산하기 위해 반복문을 한번더 작성해 이번엔 getMaxDaysOfMonth 메소드를 활용해서 더해줍니다.

 

마지막으로 count에 입력된 day -1을 더해주고(-1을 하는 이유는 1일은 +1을 해줄 필요가 없기 때문입니다.) weekday 변수를 선언해 count+기준요일 해주고 7로 나눈 나머지를 구해줍니다.

 

public void printCalendar(int year, int month) {
		System.out.printf("   <<%d년 %d월>>\n", year, month);
		System.out.println(" SU MO TU WE TU FR SA");
		System.out.println("----------------------");
		
		
		int weekday = getWeekDay(year, month, 1);
		
		
		for (int i = 0; i < weekday; i++) {
			
			System.out.print("   ");
		}
			
		int maxDay = getMaxDaysOfMonth(year, month);
		int count = 7 - weekday;
		int delim = (count < 7)? count:0;
			
		
		
		for(int i = 1; i <= count; i++) {
			System.out.printf("%3d", i);
			
		}
		System.out.println();

		
		for (int j = count+1; j <= maxDay; j++) {
			System.out.printf("%3d", j);
			if (j % 7 == delim) {
				System.out.println();
			}
		}
		System.out.println();
		
			
		}
		
	}

 이전에 작성해 놓은 printCalendar에 weekday변수에 getWeekDay메소드를 넣어주고 year, month는 입력받을 값으로 넣어주고 day는 매달 1을 기준으로 달력이 작성되기에 1을 넣어줍니다.

 

마지막으로 Prompt 클래스에서 요일을 입력값을 받던 부분을 지우서 연도와 월만 입력받을 수 있게 수정해줍니다. (코드생략)

 

결과값

연도를 입력하세요.(exit: -1)
YEAR> 2020
월을 입력하세요:
MONTH> 7
   <<2020년 7월>>
 SU MO TU WE TU FR SA
----------------------
           1  2  3  4
  5  6  7  8  9 10 11
 12 13 14 15 16 17 18
 19 20 21 22 23 24 25
 26 27 28 29 30 31

연도를 입력하세요.(exit: -1)
YEAR> 2020
월을 입력하세요:
MONTH> 8
   <<2020년 8월>>
 SU MO TU WE TU FR SA
----------------------
                    1
  2  3  4  5  6  7  8
  9 10 11 12 13 14 15
 16 17 18 19 20 21 22
 23 24 25 26 27 28 29
 30 31

연도를 입력하세요.(exit: -1)
YEAR> 

연도와 월을 입력하면 입력한 월의 1일의 요일이 자동으로 계산되는 달력이 구현되었습니다.

캘린더 1에서 만들어본 달력에 기능을 추가해서 더 정교한 달력을 만들어 보도록 하겠습니다.


시작하는 날짜의 요일을 입력하는 달력만들기

 

다음 결과 값 같이 첫번째 요일을 입력하여 그 날을 기준으로 1일을 시작하는 달력을 만들어 보겠습니다.

 

<결과값>

연도를 입력하세요.(exit: -1)
YEAR> 2020

달을 입력하세요:
MONTH> 2

첫번째 요일을 선택하세요. (su, mo, tu, we, th, fr, sa
DAY> tu

   <<2020년  2월>>
 SU MO TU WE TU FR SA
----------------------
        1  2  3  4  5
  6  7  8  9 10 11 12
 13 14 15 16 17 18 19
 20 21 22 23 24 25 26
 27 28 29
 
연도를 입력하세요.(exit: -1)
YEAR> 

 

public class Prompt {

	public int parseDay(String week) {
		if (week.equals("su")) return 0;
		else if (week.equals("mo")) return 1;
		else if (week.equals("tu")) return 2;
		else if (week.equals("we")) return 3;
		else if (week.equals("th")) return 4;
		else if (week.equals("fr")) return 5;
		else if (week.equals("sa")) return 6;
		else 
			return 0;
	}
	
	public void runPrompt() {

		Scanner scanner = new Scanner(System.in);
		Calendar cal = new Calendar();
		
		int month = 1;
		int year = 2020;
		int weekday = 0;
		
		for (;;) {
					
			System.out.println("연도를 입력하세요.(exit: -1)");
			System.out.print("YEAR> ");
			year = scanner.nextInt();
			
			if (year == -1) {
				break;
			}
			System.out.println("월을 입력하세요:");
			System.out.print("MONTH> ");
			month = scanner.nextInt();
			System.out.println("첫번째 요일을 선택하세요. (su, mo, tu, we, th, fr, sa");
			System.out.print("DAY> ");
			String str_weekday = scanner.next();
			weekday = parseDay(str_weekday);
			
			if (month >12 || month <1) {
				System.out.println("잘못된 입력 입니다.");
				continue;
			}  else {

				cal.printCalendar(year, month, weekday);;

				System.out.println("");
			}
		}

		System.out.println("bye~");
		scanner.close();
	}

	public static void main(String[] args) {

		// 셀 실행
		Prompt p = new Prompt();
		p.runPrompt();
		
	 
	}

}

시작 요일을 입력받기 위해 Prompt 클래스에서 월을 입력하는 곳 밑에 첫번재 요일을 String str_weekday 변수로 입력을 받습니다.

 

 String형 자료를 int형으로 변경하기 위한 parseDay라는 메소드를 만들어주고 weekday변수에 담아줍니다. 

 

public class Calendar {

	private static final int[] MaxDays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	private static final int[] LeapMaxDays = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

	public static boolean isLeapYear(int year) {

		if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
			return true;
		else
			return false;
	}

	public static int getMaxDaysOfMonth(int year, int month) {
		if (isLeapYear(year))
			return LeapMaxDays[month - 1];
		else
			return MaxDays[month - 1];
	}
	
	
	

	public void printCalendar(int year, int month, int weekday) {
		System.out.printf("   <<%4d년%3d월>>\n", year, month);
		System.out.println(" SU MO TU WE TU FR SA");
		System.out.println("----------------------");
		
		for (int i = 0; i < weekday; i++) {
			
			System.out.print("   ");
		}
			
		int maxDay = getMaxDaysOfMonth(year, month);
		int count = 7 - weekday;
		int delim = (count < 7)? count:0;
			
		
		
		for(int i = 1; i <= count; i++) {
			System.out.printf("%3d", i);
			
		}
		System.out.println();

		
		for (int j = count+1; j <= maxDay; j++) {
			System.out.printf("%3d", j);
			if (j % 7 == delim) {
				System.out.println();
			}
		}
		System.out.println();
	}

	}

Calendar클래스로 돌아와 시작하는 요일에 맞추기 위해 weekday만큼 공백을 넣어주는 반복문을 만들어줍니다. 

 

첫째줄이 달력밖으로 튀어나오는 현상을 고쳐주기 위해 첫 줄만 출력하는 반복문을 만들어주고 범위를 int count = 7- weekday라는 변수를 통해 지정해줍니다.

 

두번째 줄부터 마지막까지 출력하는 반복문을 하나더 만들어 초기값을 count+1로 잡아주면 첫줄에서 출력하지 못한 숫자부터 최대수까지 출력하게 됩니다.

 

출력해보면 출력되는 수는 이제 문제가 없지만 기존에 7의 배수에서 줄바꿈을 하도록 작성되었기에 줄바꿈이 엉망인것을 알수 있습니다. 그것을 해결하기 위해 반복문 안에 if(j%7 ==  count)을 if(j % 7 == count)로 고치면 알맞게 줄바뀜이 되는 것을 알 수 있습니다.

 

요일에 su를 입력할 경우 count=7이되어 오류가 발생하게 되기 때문에 delim이라는 변수를 만들고 삼항연산자로 간단한 조건문을 작성해  count>7 인경우 0이 될 수 있게 설정해주면 문제없이 결과값이 나오게 됩니다.

 

마지막으로 입력값의 범위를 지정해주기 위해 Prompt 클래스에서 year 변수값 받는 곳 밑에 break문을 넣어주고 아래의 다시 month의 범위를 지정해주고 continue문을 넣어주면 입력값의 범위를 지정할 수 있습니다.

 

그 뒤 실행해보면 결과값이 나오는 것을 확인할 수 있습니다.

 

 

앞에서 배운 내용을 바탕으로 단계별로 캘린더 만들기를 해보겠습니다.

 

입력받은 달의 최대 일수 출력

 

 

import java.util.Scanner;

public class DaysA {

	public static void main(String[] args) {

		Scanner sc = new Scanner(System.in);
		System.out.println("궁금하신 월을 입력하세요");
		int input = sc.nextInt();

		int[] Maxdays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

		if (input >0 && input <13) {

		System.out.printf("%d월은 %d까지 입니다.",input,Maxdays[input-1]);

		} else {System.out.println("1~12까지의 숫자를 입력하시오");
		}

	}
	}

먼저 달을 입력받고 Maxdays라는 배열을 만들어 각 월별 최대일수를 저장해놓은 다음 조건문을 통해 각월에 맞는 최대일수를 출력합니다. Maxdays[input-1]인 이유는 입력값은 1~12이지만 배열은 0부터 시작하기에 그 차이를 맞추기 위해 입력값에 -1을 해준 것 입니다.

 

위의 내용을 메소드를 통해 정리하면 다음과 같습니다.

	private static final int[] MAX_DAYS = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

	public  int getMaxDaysOfMonth(int month) {
		return MAX_DAYS[month -1];
	}
	
	public static void main(String[] args) {

		Scanner sc = new Scanner(System.in);

		Calendar cal = new Calendar();

		System.out.println("궁금하신 월을 입력하세요");
		int month = sc.nextInt();
        
		if (month >0 && month <13) {

		System.out.printf("%d월은 %d까지 입니다.\n",month, days.getMaxDaysOfMonth(month) );

		} else {System.out.println("1~12까지의 숫자를 입력하시오");

		}


	}
	}

 

 

반복해서 월별 최대일수를 출력해주기

 

public static void main(String[] args) {

		String prompt = "cal> ";
		Scanner scanner = new Scanner(System.in);
		Calendar cal = new Calendar();

		for (int i = 0; ; i++) {

		System.out.println("월을 입력하세요:");
		System.out.print(prompt);
		int month = scanner.nextInt();

		if(month == -1) {
			System.out.println("Have a nice day!");
			break;
		} else if(month > 12) {
			continue;
		} else {

		System.out.printf("%d월은 %d일 까지 입니다.\n",month,cal.getMaxDaysOfMonth(month));

		System.out.println("");
			}
		}

		System.out.println("bye~");

 

입력값앞에 print문을 사용하여 프롬프트를 만들고 for문안에 최대일수를 구하는 문장들을 넣어 여러번 반복하게 구현하였습니다. break문과 continue문을 활용하여 -1을 입력하면 반복을 중지하고 13이상의 값을 입력하면 다시 입력값을 받도록 구현하였습니다.

 

 

월별 최대 일수를 출력하여 달력모양 만들기

 

public class Calendar {

	private static final int[] MaxDays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

	public static int getMaxDaysOfMonth(int month) {
		return MaxDays[month - 1];
	}

	public static void printCalendar(int year, int month) {

		System.out.printf("   <<%4d년%3d월>>\n", year, month);
		System.out.println(" SU MO TU WE TU FR SA");
		System.out.println("----------------------");
		int maxDay = getMaxDaysOfMonth(month);

		for (int i = 1; i <= maxDay; i++) {
			System.out.printf("%3d", i);
			if (i % 7 == 0) {
				System.out.println();
			}

		}

 	System.out.println();

	}

}
import java.util.Scanner;

public class Prompt {

	private final static String PROMPT = "cal> ";

	public void runPrompt() {

		Scanner scanner = new Scanner(System.in);
		Calendar cal = new Calendar();


		for (int i = 0;; i++) {

			System.out.println("달을 입력하세요:");
			System.out.print(PROMPT);
			int month = scanner.nextInt();

			if (month == -1) {
				System.out.println("Have a nice day!");
				break;
			} else if (month > 12) {
				continue;
			} else {

				cal.printCalendar(2020, month);;

				System.out.println("");
			}
		}

		System.out.println("bye~");
		scanner.close();
	}

	public static void main(String[] args) {

		// 셀 실행
		Prompt p = new Prompt();
		p.runPrompt();

	}

}

Calendar 클래스의 복잡성을 줄여주기 위해 입력받는 부분을 Prompt 클래스로 옮겨주고 달력을 작성하는 부분을 Calendar 클래스에 작성합니다. Calendar에서 반복문을 통해 각 월별 최대일수 만큼 숫자를 출력하게하고 그안의 조건문을 두어 7번째마다 줄바꿈을 할 수 있게하여 달력형식을 만들었습니다. 

 

 

윤년기능 추가하기

 

public class Calendar {

	private static final int[] MaxDays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	private static final int[] LeapMaxDays = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

	public static boolean isLeapYear(int year) {

		if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
			return true;
		else 
			return false;
	}

	public static int getMaxDaysOfMonth(int year, int month) {
		if(isLeapYear(year)) 
			return LeapMaxDays[month-1];
		else
		return MaxDays[month - 1];
	}

	public static void printCalendar(int year, int month) {
		System.out.printf("   <<%4d년%3d월>>\n", year, month);
		System.out.println(" SU MO TU WE TU FR SA");
		System.out.println("----------------------");

		int maxDay = getMaxDaysOfMonth(year, month);
	
		for (int i = 1; i <= maxDay; i++) {
			System.out.printf("%3d", i);
			if (i % 7 == 0) {
				System.out.println();
			}
		}
	}
	}
}
public class Prompt {
	private final static String PROMPT = "cal> ";
	public void runPrompt() {

		Scanner scanner = new Scanner(System.in);
		Calendar cal = new Calendar();

		int month = 0;
		int year = 0;

		for (int i = 0;; i++) {

			System.out.println("연도를 입력하세요:");
			System.out.print(PROMPT);
			year = scanner.nextInt();
			System.out.println("달을 입력하세요:");
			System.out.print(PROMPT);
			month = scanner.nextInt();

			if (month == -1) {
				System.out.println("Have a nice day!");
				break;
			} else if (month > 12) {
				continue;
			} else {

				cal.printCalendar(year, month);;

				System.out.println("");
			}
		}
		System.out.println("bye~");
		scanner.close();
	}
	public static void main(String[] args) {
		// 셀 실행
		Prompt p = new Prompt();
		p.runPrompt();
	}
}

Calendar 클래스에 윤년일때 월별 최대일수로 사용할 LeapMaxDays 배열을 추가하고 윤년조건 체크할 isLeapYear 메소드를 추가하였습니다. 그 후 getMaxDaysOfMonth 메소드에서 입력받은 연도가 윤년인지 여부에 따라 무슨 배열을 사용할지를 선택하게 한후 그 배열을 기존의 반복문에 사용함으로 윤년이 반영될 수 있게 하였습니다. 그 후 연과 월을 같이 입력받을 수 있도록 Prompt 클래스에 입력문을 추가하였습니다.

 

결과값

연도를 입력하세요:
cal> 2020

달을 입력하세요:
cal> 2

   <<2020년  2월>>
 SU MO TU WE TU FR SA
----------------------
  1  2  3  4  5  6  7
  8  9 10 11 12 13 14
 15 16 17 18 19 20 21
 22 23 24 25 26 27 28
 29

이렇게 해서 연도와 월을 입력해서 기본적인 달력을 추가하는 예제를 구현해보았습니다. 캘린더 만들기2에서 아직 미비한 부분을 보충해서 만들도록 하겠습니다.

이번엔 나중에 더 자세히 배울 내용인 배열, 메소드, 클래스를 가지고 구구단을 구현하며 에습해보겠습니다. 위의 개념들은 제가 아직 정확하게 이해하지 못하였고 이런식으로 활용하는구나 정도만을 이해한 것임을 참고해주시기 바랍니다.

 

배열

 

지금까지는 하나의 변수에 하나의 값만을 대입하였지만, 배열을 통해 여러개의 값을 한번에 대입 할 수 있습니다. 

 

public static void main(String[] args) {

		int[] result = new int[9];
		
		for(int i=0; i<result.length; i++) {
			result[i] = 2 * (i+1);
		}
		for(int i=0; i<result.length; i++) {
			System.out.println(result[i]);
		}		
		
        result = new int[9];  // 기존에 있는 배열을 재사용하기위해 int를 뺌
		                      // 혹은 time3같은 새로운 배열을 만들어서 활용해도됨
		for(int i=0; i<result.length; i++) {
			result[i] = 3 * (i+1);            //위에 반복문이랑 똑같고 안에 2,3,4수만바뀜
			                                  //어떻게 반복 줄일까? 중첩 포
		}
		for(int i=0; i<result.length; i++) {  
			System.out.println(result[i]);
		}		

배열은 int[] result = new int[9]와 같이 자료형뒤에 "[]"대괄호를 붙여주고 배열안에 몇개의 데이터가 들어가는지 표시해주면서 선언 할 수 있습니다.  배열명.length를 하면 배열안에 몇개의 자료가 들어 있는지 알 수 있습니다. 배열안에 자료는 위의 예제에서와 같이 result[0] 을 통해 표현할 수 있으며 0부터 시작하여 순서대로 표현이 됩니다.

 

위의 사실을 바탕으로 result라는 배열을 선언하고 result에 2단의 곱셉을 하나씩 대입해놓고 아래의 반복문을 통해 result의 대입되있는 값들을 출력하는 2개의 반복문을 만들었습니다.

 

위의 2개의 반복문을 복사하여 result[i]=2*(i+1)의 값만 3,4,5... 으로 변환해주면 하나의 구구단이 완성됩니다.

 

public static void main(String[] args) {

        int[] result = new int[9];
		
		for(int j=2; j < 10; j++) {
			
	        for(int i=0; i<result.length; i++) {
				result[i] = j * (i+1);
			}
			for(int i=0; i<result.length; i++) {
				System.out.println(result[i]);
				
			}		
			System.out.println("");
		}

 

위의  result[i]=2*(i+1)의 값을 바꾸기 위해 반복되던 문장들을 반복문을 써서 한눈에 보이는 문장으로 만들었습니다.

 

 

메소드

메소드는 프로그래머가 필요한 수식을 함수화 해서 언제든지 불러내서  사용할 수 있는 것 정도로 이해했습니다.

 

public static int[] calculate(int times) {
		
		int[] result = new int[9];
		
		for(int i=0; i<result.length; i++) {
			result[i] = times * (i+1);
		}
		return result;
	}

	public static void main(String[] args) {
		int[] result = calculate(2);
				
		for(int i=0; i<result.length;i++) {
			System.out.println(result[i]);
		}
		
		int[] times3 = calculate(3);
		
		for(int i=0; i<result.length;i++) {
			System.out.println(times3[i]);
		}
		
        int[] times4 = calculate(3);
		
		for(int i=0; i<result.length;i++) {
			System.out.println(times4[i]);
		}

 

public static int[] calculate[int times] 라는 문장을 통해 calculate라는 메소드를 만들고 중괄호 안에 그 메소드를 호출해서 할 일들을 작성하고 최종 결과물을 return ; 안에 적어주면 main 메소드 안에서 언제든지  호출해서 사용할 수 있는 calculate라는 메소드를 만들 수 있습니다.

 

public static int[] calculate[int times]에서 int[]은 메소드에 최종산물인 return;의 자료형을 뜻하고 calculate는 메소드명, 괄호안에는 입력값의 자료형과 변수명이 들어가게 됩니다.

 

calculate 메소드를 사용해 각 단별 곱셈을 하고 아래의 for을 통해 calculate의 결과물이 대입되어있는 배열을 차례로 출력 하는 방식으로 구구단을 구현하였습니다.

 

 

public static int[] calculate(int times) {
		
		int[] result = new int[9];
		
		for(int i=0; i<result.length; i++) {
			result[i] = times * (i+1);
		}
		return result;
	}

	public static void print(int[] result) {
		
		for(int i=0; i<result.length;i++) {
			System.out.println(result[i]);
		}
	}
	public static void main(String[] args) {
		int[] result = calculate(2);
				
		print(result);
		
		int[] times3 = calculate(3);
		
		print(times3);
		
        int[] times4 = calculate(4);
		
        print(times4);

위의 예제에서 출력을 위해 사용된 for문이 반복되는 것을 발견하고 print 메소드로 만들어 calculate와 print 두 메소드로 간단하게 구구단을 구현할 수 있습니다.

 

 

클래스

 

복잡한 코드일 수록 한 클래스안의 너무 많은 내용들이 들어가 복잡성이 증가하는걸 방지하기 위해 다른 클래스의 필요한 메소드를 만들어 두고 현재의 클래스에 불러와서 사용할 수 있습니다. 

 

public class Gugudan {
	
	public static int[] calculate(int times) {
		int[] result = new int[9];
		
		for(int i = 0 ; i < result.length; i++) {
			result[i] = times * (i+1);
		}
		
		return result;
	}
	
	public static void print(int[] result) {
		
		for(int i = 0 ; i < result.length; i++)
		System.out.println(result[i]);
			
	}

}

 

먼저 마지막 예제인 Gugudan 클래스에 main메소드 아래내용들을 지우고 calculate와 print 메소드만 남겨둡니다.

 

public class GugudanMain {

        public static void main(String[] args) {
		
		for (int i = 2; i < 10; i++) {
			
		
		int[] result = Gugudan.calculate(i);
		Gugudan.print(result);
		
		}
	}

}

GugudanMain이란 새로운 클래스를 만들고 구구단을 구현하기 위해 Gugudan 클래스의 calculate 메소드를 Gugudan.calculate(클래스명.메소드명)으로 불러왔습니다. 마찬가지로 print메소드를 불러오고 for문을 통해 2가지 메소드가 2~9까지 반복될 수 있도록 하여 구구단을 구현하였습니다.

 

 

마지막으로 2가지 예제를 통해 위에서 배운것들을 정리해 보겠습니다.

 

Q1.사용자가 입력한 값에 따라 크기가 다른 구구단을 계산해 출력한다.

예를 들어 사용자가 8을 입력하면 팔팔단, 19를 입력하면 십구십구단(2 * 1에서 19 * 19)을 계산해 출력한다.

 

public static void main(String[] args) {
		
        Scanner scanner = new Scanner(System.in);
        
        int input = scanner.nextInt();
        
        int[] result = new int[input];
        
		for (int i = 2; i <= result.length; i++) {
			
			for(int j = 0; j < result.length; j++) {
				
		            result[j] = i * (j+1);
		        
		        System.out.println(result[j]);
			}
		
		}

 int[] result = new int[input]; 문장을 통해 scanner로 입력받은 input만큼 result라는 배열의 값을 지정해주고 그 배열의 length 만큼 반복되는 반복문을 만들어 구구단을 구현 했습니다.

 

 

Q2 사용자가 입력한 값에 따라 크기가 다른 구구단을 계산해 출력한다.

예를 들어 사용자가 "8,7"과 같은 문자열을 입력하면 팔칠단을 구현한다. 팔칠단은 2 * 1 ... 2 * 7, 3 * 1 ... 3 * 7, ... , 8 * 1 ... 8 * 7 까지 구현하는 것을 의미한다.

 

public static void main(String[] args) {
		
        Scanner sc = new Scanner(System.in);
        
        String input = sc.nextLine();
        
        String[] split = input.split(",");
        
        int input1 = Integer.parseInt(split[0]);
        int input2 = Integer.parseInt(split[1]);
        
        for(int i = 2; i <= input1; i++) {
        	
        	for(int j = 1; j <= input2; j++) {
        		int result = i * j;
        		System.out.println(result);
        	}
        }

 

기존에 정수를 입력받던 nextInt와는 다르게 문자열을 입력받기 위해 nextLine()를 입력하여 문자열을 입력을 받고 입력받은 문자열을 split을 통해 콤마를 기준으로 나누어 문자열 배열인 split에 집었습니다. 그 후 split[0], split[1]에 들어있는 각각의 값을 Integer.paseInt를 통해 정수로 만들어 정수 변수인 input1, inpu2 얻었습니다. 이 두 변수를 활용하여 반복문을 만들어 구구단을 구현하였습니다. split같은 생소한 기능을 사용하였지만 다양한 값을 입력받아 활용하기 위한 여러 장치들이 있다는 것을 배울 수 있었습니다.

구구단을 단계별로 구현하며 지금까지 배운내용들을 복습해보겠습니다.

 

2,3단 구현 - 계산 및 출력

 

public class Example2 {

	public static void main(String[] args) {

		//2단
		
		System.out.println("2단");
		System.out.println(2 * 1);
		System.out.println(2 * 2);
		System.out.println(2 * 3);
		System.out.println(2 * 4);
		System.out.println(2 * 5);
		System.out.println(2 * 6);
		System.out.println(2 * 7);
		System.out.println(2 * 8);
		System.out.println(2 * 9);
		
		//3단
		System.out.println("3단");
		System.out.println(3 * 1);
		System.out.println(3 * 2);
		System.out.println(3 * 3);
		System.out.println(3 * 4);
		System.out.println(3 * 5);
		System.out.println(3 * 6);
		System.out.println(3 * 7);
		System.out.println(3 * 8);
		System.out.println(3 * 9);
	}

간단한 계산 및 System.out.println()을 활용해 결과를 출력을 하는 방법을 복습해 봤습니다. 단순 반복작업이 많음을 알 수 있습니다.

 

값 입력 및 변수

 

public static void main(String[] args) {
		
		
		Scanner scanner = new Scanner(System.in);
		
		System.out.println("구구단 중 출력할 단은?");
		int number = scanner.nextInt();
		
		System.out.println("Number : "+number);
		
		int result = number * 1 ;
		System.out.println(result);
		result = number * 2 ;
		System.out.println(result);
		 result = number * 3 ;
		System.out.println(result);
		 result = number * 4 ;
		System.out.println(result);
		 result = number * 5 ;
		System.out.println(result);
		 result = number * 6 ;
		System.out.println(result);
		 result = number * 7 ;
		System.out.println(result);
		 result = number * 8 ;
		System.out.println(result);
		result = number * 9 ;
		System.out.println(result);
		
		

 

 Scanner를 통해 int number라는 변수의 값을 입력 받고 result라는 변수에 입력받은 값과 1~9까지의 곱셈을 차례대로 대입하며 출력하여 구구단을 표현했습니다.

 

반복문

 

public static void main(String[] args) {
		
		Scanner scanner = new Scanner(System.in);
		System.out.println("단수를 입력하세요 : ");
		int num = scanner.nextInt();
		int i = 1;
		
		System.out.printf("%d단은?\n",num);
		
		while ( i < 10) {
			
			int result = num * i;
			System.out.printf("%d X %d = %d\n",num,i,result);
			
			i++;
		}

앞의 두 예제에서는 반복되는 부분들을 줄여주는 반복문을 통해서 구구단을 구현해보았습니다. 위에에서 입력받은 num에 1~9까지 곱해서 출력하는 과정을 반복문으로 만들어 더 간단하고 가독성 좋게 구구단이 구현되었습니다.

 

 

조건문

 

public static void main(String[] args) {
		
		Scanner scanner = new Scanner(System.in);
		System.out.println("단수를 입력하세요 : ");
		int num = scanner.nextInt();
		
		
		System.out.printf("%d단은?\n",num);
		
		if (num >=2 && num <=9) {
	
			for (int i = 1; i < 10; i++ ) {
			
				int result = num * i;
				System.out.printf("%d X %d = %d\n",num,i,result);
			}
		}   else{
					System.out.println("2이상, 9이하의 값만 입력할수 있습니다.");
			}
		

if문을 통해 입력하는 값의 범위를 2~9까지로 정하고 그 외의 값을 입력했을 경우 에러메세지를 출력하도록 하는 조건문을 만들고 입력받은 값을 통해 구구단을 만드는 반복문을 for문을 통해 구현하였습니다.

 

 

  

지금까지 배운 제어문을 활용하여 두 가지 예제를 풀어보도록 하겠습니다.

 

Q1. 연산자와 두 수를 변수로 선언한 후 사칙연산이 수행 되는 프로그램을 만들어 보세요. 

 if - else ife -else if, swtich -case 두 방식 모두 구현해 봅니다.

 

public class Q1 {

	public static void main(String[] args) {
		
		char operator = '/';
		int i = 10;
		int o = 5;
		int result;
		
		if (operator == '+') {
			result = i + o;
		} else if(operator == '-') {
			result = i - o;
		} else if(operator == '*') {
			result = i * o;
		} else {
			result = i / o;
		}

		System.out.println(result); // 2

if -else if 방식을 활용하여 opreator의 경우에 따라 4가지 조건을 만들어 입력값 i와 o를 사칙연산 하도록 구현하였습니다.

 

public class Q1switch {

	public static void main(String[] args) {
		
		char operator = '/';
		int i = 10;
		int o = 5;
		int result;
		
		switch(operator) {
		  case '+' : result = i + o;
			System.out.println(result);
			break;
		  case '-' : result = i - o;
			System.out.println(result);
			break;
		  case '*' : result = i * o;
			System.out.println(result);
			break;
		  default : result = i / o;
			System.out.println(result);
			break;
		}
		
		

 

switch-case 문의 경우도 마찬가지로 case를 4가지 연산자로 두어 사친연산을 할 수 있게 구현하였습니다.

 

Q2. 다음 다이아몬드를 출력해 보세요.

 

                           *

                          ***

                         *****

                        *******

                         *****

                          ***

                           *

 

import java.util.Scanner;

public class Q222 {

	public static void main(String[] args) {
		
System.out.println("홀수 값을 입력하세요:");
		
		Scanner scanner = new Scanner(System.in);
		int linecount = scanner.nextInt();
		int spacecount = linecount/2;
	    int starcount = 1;
		
		for (int i = 0; i < linecount; i++) {
			
			for(int j = 0; j <spacecount; j++ ) {
				System.out.print(" ");
			}
			for(int j = 0; j <starcount; j++ ) {
				System.out.print("*");
			}
			for(int j = 0; j <spacecount; j++ ) {
				System.out.print(" ");
			}
			System.out.println(" ");
			
			if (i < linecount/2 ) {
				spacecount -=1;
				starcount +=2;
			} else {
				spacecount +=1;
				starcount -=2;
			}
		}

 

다이아몬드를 살펴보면 첫째 줄 공백은 전체 줄에 2를 나눈 값과 같고 별은 하나입니다. 그리고 각줄마다 공백은 1칸씩 줄어들고 별은 2개씩 늘어나다가 전체줄에 반이 지나면 다시 반대로 공백이 1칸씩늘고 별이 2개씩 줄어듬을 알 수 있습니다. 이정보를 토대로 반복문을 만들고 그안에 조건문을 집어 넣어 줄이 늘어남에 따라 공백과 별의 증감을 주어 홀수 입력값에 따라 만들어지는 다이아몬드를 구현해 보았습니다.

 

제어문에서 활용될 수 있는 break문과 continue문에 대해 알아보도록 하겠습니다.

 

break문

 

감싸고 있는 블럭의 제어를 빠져 나오는 기능을 하며 제어문에서 어떤 조건하에 반복문을 빠져나온다라는 방식으로 활용 됩니다. 하나의 블럭만 빠져나오기 때문에 중첩 반복문 중 내부 반복문에서 break문이 활용되었다면 외부 반복에서는 영향을 주지 않습니다.

public class BreakExample {

	public static void main(String[] args) {

		int sum = 0;
		int num;
		
		for( num = 1;  ;num++) {
			 sum += num;
			 if(sum >= 100) {
				 break; //조건이 됬을때 바로 빠져나옴, 그래서 그때의 num값인 14를 캐치할수있음
			 }
		}
		
		System.out.println(sum); // 105
		System.out.println(num); // 14

break문을 활용하여 1부터 숫자를 더해 나가다가 합계가 100을 초과 햇을 때 빠져나오는 반복문을 구현해 보았습니다. 반복문 중간에 조건문을 넣어 합계가 100이 되었을때  break; 로 빠져나오게 됩니다. 

 

continue문

 

continue문은 반복문안에서 어떤 조건을 충족하였을때 수행문을 수행하지 않고 다시 조건을 검토하게 하는 기능을 합니다.

 

public class ContinueExample {

	public static void main(String[] args) {
		// 3의배수만 출력하자
		
		
		
		for(int i = 1; i < 100; i++) {
//		    if(i % 3 == 0) {
//		    	System.out.println(i);
//		    } // 도 같은 결과도출
//		}
			
			if(i %3 != 0) {
				continue;
			}
			System.out.println(i);
		}

 

coninue문을 활용하여 i가 3으로 나누어 떨어지지 않으면 수행문을 수행하지 않고 다음 반복으로 넘어가라는 조건을 추가하여 1~100까지의 숫자 중 3의 배수만 출력하는 반복문을 구현해 보았습니다. 

 

 break, continue 예제

Q. 구구단을 출력할 때 짝수 단만 출력하면서 단보다 곱하는 수가 작거나 같을 때까지만 출력하세요

 

public class BreakContinue {

	public static void main(String[] args) {

		int dan;
		int count;
		
		for (dan = 2; dan <10; dan++) {
			
			if(dan % 2 != 0) {
				continue;
			}
			System.out.println(dan+"단");
			for(count = 1; count <10 ; count++) {
				
				if(count > dan) {
					break;
				}
				
				System.out.println(dan + "X" + count + " = " + dan*count);
			
			}
			
		System.out.println();
			

결과)

2단
2X1 = 2
2X2 = 4

4단
4X1 = 4
4X2 = 8
4X3 = 12
4X4 = 16

...

 

외부 반복문에 continue문을 넣어 홀수단일 경우 수행문을 수행하지 않고 다시 조건을 검토하며 내부 반복문에서 곱하는 수가 단수 보다 높아지면 break를 통해 반복문을 빠져나오게 구현하였습니다.

 

정리해보면 break;문과 continue문은 반복문을 더 세밀한 조건을 설정하여 구현하도록 돕는 역할을 할 수 있음을 알 수 있었습니다. 

for문

앞에서 배운 while문 do-while문에 이어 마지막 반복문인 for문을 알아보겠습니다. 먼저 for문의 구조는 다음과 같습니다.

 

 

for(초기화식; 조건식; 증감식) {

    수행문;

}

 

for문은 다른 반복문들에 비해 가독성이 좋고 일정 횟수에 기반한 반복을 구현할 때 효율적이란 특징이 있는데  while문에 조건식을 넣는자리에 초기화식, 증감식이 같이 들어가 있어 어떻게 시작이되어서 몇번 반복되는지를 한눈에 볼 수 있기 때문입니다.

 

int num

for(num = 1; num<=5; num++) {

   Systemout.pirntln(num);

}

 

위와 예제를 살펴보면 num이 1부터 5까지 증가하며 num을 출력하는 것을 반복하고 num 6까지 증가 되었을 때 반복문을 빠져나오는 것을 한눈에 알아 볼 수 있습니다. 

 

참고로 for문은 while문과 마찬가지로 첫번째 반복에서 조건을 만족하지 않는다면 바로 반복문을 빠져나오게 됩니다. (do-while문만 수행을 먼저하고 조건식이 만족하는지를 검토합니다.)

 

public class ForExample {

	public static void main(String[] args) {
        
		int num;
		int sum = 0;
		
		for (num=1; num<=10; num++) {  // 횟수만 의미가 있으면  num=0으로 두고  num<10으로두고하면 배열과 쓰기편함
			sum += num ;               // 증감식이나 초기값 모두 ,를 통해 여러개줄 수 있다.
		}
		System.out.println(sum); //55
		
		
		int num2 = 1;
		int sum2 = 0;
		
		while (num2<=10) {
			
			sum2 += num2;
			num2++;
		}
		System.out.println(sum2); //55

위의 예제는 for문과 while문으로 각각 1~10까지의 합을 구하는 과정입니다. 두 반복문 모두 같은 결과 값이 나왔지만 for문이 조건들이 한 줄에 정리되어 더 가독성이 좋은 것을 알 수 있습니다.

 

참고로 반복문을 사용할때 10번의 반복을 해야 한다고 할 때 for문에 조건을for( num=1; num<=0; num++)로 하는 것이 아닌 num=0; num <10와 같이 표현하는 것이 나중에 배울 배열과 함께 사용할 때 더 가독성을 좋게 해줄 수 있습니다.

 

또한 for문에 초기화식이나 증감식 모두 ' , '를 통해 여러개 입력 할 수 있습니다. 

 

마지막으로 for()안의 내용들은 생략될 수 있으며 for( ; ; ) 와 같이 모두 생략하게 되면 while(true)와 같이 무한반복을 하게 됩니다.

 

중첩 반복문

 

반복문속에 반복문이 있는 구조로 외부 반복문과 내부 반복문의 변수의 값 변화에 유의하며 구현해야 합니다. 

구구단을 구현하는 예제를 통해 중첩 반복문을 더 자세히 알아보도록 하겠습니다.

 

public class NestedLoopExample {

	public static void main(String[] args) {
        
		// 2 X 3에 2는 dan 3은 count
		int dan;
		int count;
		
		
		
		for(dan = 2; dan < 10; dan++) {
			
			for(count = 1; count < 10; count++) {
//				int multi = dan * count;
//				System.out.printf("%d X %d = %d\n",dan,count,multi);
				
				System.out.println(dan + " X " + count + " = " + dan*count);
			}
			System.out.println();
		}

결과값)

 

2 X 1 = 2
2 X 2 = 4
2 X 3 = 6
2 X 4 = 8
...

 

3X1 =

3 X 1 = 3
3 X 2 = 6
3 X 3 = 9

...

 

for문을 활용 하여 외부 반복문을 통해 각 단을 표현하고 내부 반복문을 통해 단별 곱셈을 출력하는 방식으로 구구단을 구현했습니다. 다음은 while문을 통해 구구단을 구현해 보도록 하겠습니다.

 

public class NestedLoopExample2 {

	public static void main(String[] args) {
        
		int dan   = 2; 
		int count = 1;
		
		while(dan < 10) {
						
			count = 1;  //이걸 안쓰니까 2단밖에 안된다.
						
			while (count < 10) {
				
				System.out.println(dan + " X " + count + " = "+ dan*count);
				count++;
			}
			dan++;
			System.out.println();

 

while문을 활용하여서 구구단을 구현하였습니다. 유의할 점은 for문은 초기화식이 내재되어 있기때문에 따로 변수를 초기화해줄 필요가 없지만 while문을 그렇지 않기 때문에 count =1;로 초기화를 시키지 않을 경우 2단만 출력하는 문제가 발생할 수 있습니다. 

반복문

제어문의 한종류인 반복문은 동일한 수행문을 조건식이 맞는(ture) 동안 수행하도록 하는 기능을 합니다. 자바에서 반복문은 while문 do-while문 for문이 있습니다.

 

while문

while문은 조건식이 참인 수행문을 수행하고 다시 조건식을 검토해서 참이라면 수행을 반복하는 기능을 가진 반복문 입니다.  조건식 검토와 수행을 반복하다가 조건식이 거짓이라면 반복문을 빠져나오게 됩니다. 

1부터 10까지 더하는 예제를 통해 자세히 알아보겠습니다.

 

public class WhileExample {

	public static void main(String[] args) {
         // 1~10을더한다면?
		int num = 1; 
		int sum = 0;
//	    sum += num++;
//	    sum += num++;
//	    sum += num++;
//	    sum += num++;
//	    sum += num++;
//	    sum += num++;
//	    sum += num++;
//	    sum += num++;
//	    sum += num++;
//	    sum += num++;
//	    sum += num++;
//	       이 아니라
	  
	    // while (true) - 무한루프 , 웹서버같이 멈추면 안되는 서비스에 사용.
		
	    while (num <= 10)  {
	    	sum += num;
	        num++; //11이된순간 빠져나옴
	    }
	    
	    System.out.println(sum); //55
	    System.out.println(num); //11 

반복문을 모를땐 sum += num;을 10번을 반복해서 입력해야 하지만 while문을 통해 반복적인 작업을 간단하게 줄일 수 있습니다. 

 

첫번째 반복에서 num=1이기 때문에 (num <=10)이라는 조건식을 만족하여 중괄호 안에 수행문인 sum += num; 을 수행하게 됩니다. 그후 num은 증가연산자로 인해 1에서 2로 증가하게 되고 다시 처음 조건식으로 들어가게 됩니다. 이렇게 조건식을 검토하고 수행문을 수행하는것을 반복하다 num이 11으로 증가하고 조건식으로 들어갈 때 조건문에 부합하지 않게되어 while문을 빠져나오게 됩니다. 그래서 마지막 출력문에 sum은 1~10의 합인 55가되고 n은 11이 됩니다.

 

참고로 while(true)와 같이 반복문에 조건식에 true값을 주게되면 무한루프가 되어 무한대로 수행문을 수행하게 됩니다. 서비스가 멈추면 안되는 웹서버 같은데 사용 한다고 합니다.

 

do-while문

while문은 조건식을 검토하고 수행문을 수행하는 것과는 반대로do-while문은 수행문을 먼저 수행하고 조건식을 검토하는 반복문입니다. do-while문은 수행문이 일단 한번은 수행이 되야 되는 상황에서 사용하게 됩니다.

 

import java.util.Scanner;

public class DoWhileExample {

	public static void main(String[] args) {
		int num = 1; 
		int sum = 0; // int sum;은 안됨 // 0으로초기화 시켜줘야 덧샘가능

		
	    do {
	    	sum +=num;
	    	num++;
	    	
	    } while(num <= 10 );
	    
	    System.out.println(sum); // 55
	    System.out.println(num); // 11

위의 while문에 예제와 같이 1~10까지 더하는 반복문을 do-while문으로 만들면 위와 같습니다. 수행문이 먼저 입력되고 조건문이 뒤로오는 구조만 바뀌고 똑같은 기능을 하게 됩니다.

 

참고로 대입하는게 아닌 연산을 위한 변수는 int sum; 과 같이 선언하는게 아니라 int sum = 0; 과 같이 초기화를 시켜줘야 연산이 반복문에서의 연산이 가능합니다.

 

while문과 do-while문의 차이

예제를 통해 while문과 do-while문의 차이를 한번 더 알아 보겠습니다

Q 입력 받는 정수를 모두 더 해 줍니다. 입력된 정수가 1이면 반복문을 빠져 나옵니다.

 

 

①do-while문으로 작성할 경우

import java.util.Scanner;

public class DoWhileExample {

	public static void main(String[] args) {

	    
	    
		Scanner scanner = new Scanner(System.in);
		int input;
		int sum = 0;
		
		do {
			input  = scanner.nextInt();
		    sum += input;
		} while (input !=1);
		
		System.out.println(sum); // 1, 1

②while문으로 작성할 경우

import java.util.Scanner;

public class WhileExample2 {

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int input;
		int sum = 0;
		input = scanner.nextInt();
				
				
		while (input !=1) {
			sum += input;
			input = scanner.nextInt(); 
		}
		System.out.println(sum); // 1,0

위의 예제에서 두 반복문은 똑같은 기능을 하지만 처음 입력값으로 1을 넣었을때 while문은 조건문이 fasle가 되어 수행문을 수행하지 않고 0을 출력하게 되고 do-while문에서는 수행문을 먼저 수행하기에 sum에 입력한 1을 더하게 되고 조건문이 false인 것을 확인하고 빠져나오기 때문에 1을 출력하게 되는 차이점이 있습니다.

 

또한 작은 차이점 이지만 do-while문이 끝날 때는 세미콜론을 붙인다는 차이점도 있습니다.

swhich - case문은 if-else if 같은 기능을 하지만 조건의 값이 정수 혹은 문자열이어야 사용할 수 있다는 제약이 있어 if -else if 보다는 한정적으로 사용되는 조건문입니다. 하지만 다음 예제를 통해서 알 수 있듯이 if-else if 문보다 코드를 깔끔하게 정리할 수 있다는 장점이 있어서 가끔 활용되는 조건문입니다.

 

위의 예제는 rank의 값에 따라 medalColor가 달라지는 조건문인데 조건인 rank가 정수이기 때문에 if - else if와 switch-case문 모두 사용 할 수 있습니다.

if - else 문과는 다르게 switch -case에는 조건식이 아닌 조건이 되는 변수가 들어가게 되고 중괄호 아래에 변수의 값을 case뒤에 두어 조건을 설정합니다. 그리고 마지막에 default는 else와 비슷한 역할을 하여 조건에 맞지 않는 값들의 수행문을 결정 합니다.

 

import java.util.Scanner;

public class SwitchCase {

	public static void main(String[] args) {

		Scanner scanner = new Scanner(System.in);
		
		int rank = scanner.nextInt();
		char medalColor; 
				
		switch(rank) {
			case 1: medalColor = '금';// char은 '' String은 ""
				System.out.println("금메달");
				break; // 중괄호를 빠져나가라. 안쓰면 금은동메달 다나오고 마지막에 A라고나옴..
			case 2: medalColor = '은';
				System.out.println("금메달");
				break;
			case 3: medalColor = '동';
				System.out.println("금메달");
				break;
			default : medalColor = 'A';  //default 값을 안넣어도 되는데 안낳을거면 초기값을 줘야함- medalcolor : 'A'
		}
		
		System.out.println(rank + "등은" +medalColor +"메달 입니다."); //1, 금메달 1등은금메달 입니다.

정수를 입력받아 조건에 맞는 메달을 출력해주는 예제입니다. 정수의 값이 1~3인경우 해당 case의 수행문을 수행하고 그외의 값이면 default 값을 출력합니다. 

 

case 마다 break;를 사용하는 이유는 break;는 switch-case문을 빠져나가라는 뜻으로 if-else if 문은 조건이 만족해서 수행문을 수행하면 자동으로 조건문 밖으로 빠져나가지만 switch-case문은 그렇지 않고 다음 수행문들도 수행하기 때문에 break;로 빠져나가라고 수동으로 안내해 주는 것 입니다.

 

default 값은 생략 가능 하며, 생략할 경우 medalColor의 지정된 기본 값을 출력합니다. 만약 medalColor 비어있는 변수라면 에러가 나기 때문에 medalColor에 기본값을 대입하거나 default를 조건을 설정해 주어야 합니다.

 

switch - case문에 조건에는 문자열이 올 수 있고 위의 예제에서 rank를 medal로 바꾸어 case "Gold": 이런식으로 변경하여 적용 할 도 있습니다.

 

switch-case문 문제

Q. 각 월에 따른 한달 날짜 수를 day 변수 값으로 출력하세요. 단 2월은 28일 까지 입니다.

 

import java.util.Scanner;

public class SwitchCaseCase {

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		
		int month;
		System.out.println("몇월이 궁금하신가요?"); month = scanner.nextInt();
		
		int day = 31 ;
		
		switch(month) {
			case 1: case 3: case 5: case 7: case 8: case 10:
				day+=0;
			break;
			case 2: 
				day-=3;
			break;
			case 4: case 6: case 9: case 11:
				day-=1;
				break;
			default:
				System.out.println("error");
				day = 0;
			    break;
			
		}
		System.out.println(month+"월은"+day+"까지 있습니다."); //3, 3월은31일까지 있습니다.

 

조건은 다르지만 같은 수행문이 출력된다면 위의 예제처럼 여러개의 케이스를 모아 case 1: case3: case5: 이런 식으로 표현 할 수 있습니다.

 

풀이에서 day는 값이 주어져 있지만 default 값을 설정하면 조건에 맞지 않는 값은 default의 수행문을 수행하게 됩니다.

프로그래밍에서 제어문은 조건문과 반복문이 있으며 그 중 조건문을 먼저 배워 볼 것입니다.

 

if 문 & if-esle 문

가장 많이 사용되는 조건문으로 조건식의 결과에 따라 수행문이 실행되는 조건문으로 구조는 다음과 같습니다.

if문

if (조건식) {

            수행문;

}

 

if-else문

if (조건식) {

            수행문1;

} else {

            수행문2;

 

첫번째 if문은 조건식이 참일 때 수행문을 실행하는 기본적인 조건문 입니다. 두번째 if-else문은 조건식이 참일 때 수행문1을 실행하고 거짓일때 수행문2를 실행하는 조건문 입니다.

 

public class IfExample1 {

	public static void main(String[] args) {
		
		char gender = 'M';
		
		if ( gender == 'F') {// { 중괄호시작 = 블록의 시작, 중괄호사이를 블록이라고 부름// 블록에서는 들여쓰기해야함(가독성)
			System.out.println("여성입니다.");
		}else {
			System.out.println("여성이아닙니다.");
		}
		// 여성이아닙니다.
	}

}

 

예제에 if-else문에서 if 뒤에 나오는 조건식의 결과가 거짓이기 때문에 수행문1가 아닌 수행문 2가 실행되어 결과값이 "여성이아닙니다." 나온 것을 확인 할 수 있습니다.

 

if  - else if - else 문

if문과 if-else문에서 조건식이 한개였지만 if - else if문에서는 여러개의 조건식을 설정하여 조금더 복잡한 조건문을 만들어 낼 수 있습니다. if - else if문은 다음과 같은 구조를 가지고 있습니다.

구조를 살펴보면 if문 뒤에 첫번째 조건식이 나오고 참이면 수행문1을 실행하고 거짓이면 다음 조건식을 검토하여 참이면 수행문2를 실행하고 거짓이면 다음 조건식을 검토하는 것을 반복하다가 마지막 조건식이 거짓이라면 else이후에 나오는 수행문을 실행하는 구조로 되어있습니다.

 

이 구조를 오른쪽 그림과 같이 그림으로 풀어서 설명한 것을 순서도라고 합니다. 어떤 조건식이 맞으면 바로 수행문을 수행하고 조건문에서 빠져나가는 것을 확인 할 수 있습니다. 

 

import java.util.Scanner;

public class IfExample2 {

	public static void main(String[] args) {
		
		Scanner scanner = new Scanner(System.in); // Systme.in 은 입력용 표준 out은 출력용
		
		int age = scanner.nextInt();
		
		int charge = 0;
		
		if (age < 8) {    
			charge = 1000;
		} else if (age <14 ) {  // age >=8 && age <14를 안해도 되는이유?
			                    // 8보다 작다는건 이미 앞에 구문에서 걸러짐
			charge = 1500;
		} else if (age <20 ) {
			charge = 2000;
		} else { 
			charge = 3000;
		}
		
		// 만약 else if 가아닌 그냥 if만 늘여쓰게 되면 질문이 서로 베타적이지 않게된다
		// 그럼 만족하는 숫자는 다 값이 도출된다. ex) 13입력시 1500원이 아닌 2000원도 답이 된다.
		// 여기서 범위를 엄격하게 주어주면 if를 늘여써도 가능하겠다.
		// if문안에 if를 쓰는경우는 조건이 2개일때 이다.
		
		System.out.println("나이 : " + age);  // 나이 : 15
		System.out.println("요금 : " + charge); // 요금 : 2000
	}

}

예제를 살펴보면 나이를 값으로 입력받아 나이를 구간별 조건식으로 만들어 놓은 if - else if 문입니다. 조건식2이 8 < age < 14가 아닌 이유는 앞에 순서도에서 본것 처럼 만약 8보다 작은 값이 라면 이미 조건문을 빠져나갔을 것이기 때문에 <8 이라는 부분을 생략 할 수 있 수 있습니다. 

 

if - else if 문 예제)

점수를 입력 받아 다음과 같이 성적에 따라 학점을 출력하세요.

100 - 90점 : A

89 - 80 점 : B

79 - 70 점 : C

69 - 60 점 : D

59 점 이하 : F

 

풀이

import java.util.Scanner;

public class IfExample3 {

	public static void main(String[] args) {
		
		Scanner scanner = new Scanner(System.in);
		
		int score; 
		char grade;
		System.out.println("성적을 입력하세요 :"); score = scanner.nextInt(); 
		
		if (score >= 90) {
			grade = 'A';
		}else if (score >= 80) {
			grade = 'B';
		}else if (score >= 70) {
			grade = 'C';
		}else if (score >= 60) {
			grade = 'D';
		}else {
			grade = 'F';
		}
		
		System.out.println("점수 : "+score); // 89
		System.out.println("학점 : "+grade); // B
		

 Scanner를 통해 점수를 score 변수로 입력 받아 if - else if문에 각 점수 구간 별 조건식을 만들어 구간에 맞는 학점을 출력하는 코드를 만들었습니다. 앞에 예제 처럼 조건식이 90> 80 >= 80이 아닌 이유는 90이상은 앞에 조건식에서 이미 걸러져서 뒤에 조건식에는 해당사항이 없기 때문입니다. 

 

조건 문과 조건 연산자

 

앞에서 배운 삼항 연산자를 통해 if - else 문을 만들 수 있습니다.

 

if ( a> b) {

 max =a

} esle {

max =b

}

 

max = ( a>b)? a : b ;

 

①번 if -else 문과  ②번 삼항연산자는 a와 b 둘 중 큰 값을 도출하는 같은 기능을 합니다.

위의 예를 통해 간단한 if-else문은 삼항연산자로 대체될 수 있다는 것을 알 수 있습니다.

관계 연산자

 

비교 연산자라고도 하며 우리가 수학시간에 배웠던 부등호를 생각하시면 됩니다. 관계연산자의 결과는 true 혹은 false 값인 boolean 자료형으로 반환이 됩니다. 뒤에서 배울 제어문에서 많이 사용되는 연산자 입니다.

관계 연산자의 대부분의 연산자들은 수학의 연산자와 의미가 같고 '=='이 두항이 서로 같다는 의미에 연산자 이며 '!='(낫이꼴)이 두항이 서로 다르다는 뜻에 연산자입니다.

 

논리 연산자

논리 연산자는 AND(&&), OR(||), NOT(!) 세가지의 연산자가 있으며 관계연산자와 같이 사용 되는 경우가 많습니다. 논리 연산자 역시 연산의 결과가 true 혹은 false로 반환 됩니다. 

 

단락회로평가(short circuit evaluation)

 

단락회로 평가는 논리 연산자를 사용할 때 고려해야 할 사항으로 두항 중 앞의 항에서 결과값이 정해지는 경우 뒤의 값이 참인지 거짓인지 평가하지 않는 것을 단락회로평가라고 합니다. 

 

public class OperatorEx3 {

	public static void main(String[] args) {

		int num1 = 10;
		int i = 2;
		
		boolean value = ((num1 = num1 +10)<10) && ((i = i+2)<10);
		//여기서는 num1은20이 됬고 i는 4가됬을거라고 생각 할 수 있음
		
		System.out.println(num1); //20
		System.out.println(i);  //2  앞에만 봐도 평가가 뒤기때문에 평가를 하지 않아서 값은 2그대로이다..

		System.out.println(value); //false

		
		int num2 = 10;
		int i2 = 2;
		
		boolean value2 = ((num2 = num2 +10)<10) || ((i2 = i+2)<10);
		
		System.out.println(num2); //20
		System.out.println(i2);  //4 // 이번엔 앞에가 false여도 뒤에까지 체크해야되기 때문에 4가됨 
		//만약 앞에가 true였다면 이번에도 계산안됨
		System.out.println(value2); //true
		// 이걸 단락회로평가라고 한다// 코딩할 때 주의할 것!

value를 논리곱을 이용하여 연산하는 과정에서 num1 항의 평가를 마치고 false라고 판단하면 뒤에 i항은 평가하지 않았기 때문에 num1은 평가가 반영된 20이지만 i는 평가가 반영되지 않은 i로 남아 있음을 확인할 수 있습니다.

 

value2를 논리합을 이용하여 연산하는 과정에서 num2가 false로 나왔지만 i항까지 평가해야 되기 때문에 두 변수 모두 평가가 평가가 되어 n2=20; i =4;라는 값이 나왔습니다. 만약 value2에서 i항이 먼저나와 ture가 나왔다면 앞의 value의 경우처럼 뒤에 num2항을 평가 하지 않았을 것 입니다.


이렇듯 실제프로그램에서 단락회로평가에 의해서 뒤의 항이 평가되지 않아 예상치 못한 결과 발생할 수 있으니 주의하여야 합니다.

 

 

조건 연산자

조건 연산자는 유일한 삼항 연산자로  '조건식? 결과1: 결과2;' 이라는 간단한 구조로 되어 있고 조건식이 ture이면 결과1을 실행하고 false면 결과2를 실행하라는 조건문을 만들어내는 연산자 입니다.

 

package operator;

public class OperatorEx3 {

	public static void main(String[] args) {

		
		
		int num3=(5>3)? 10:20;
		
		System.out.println(num3); // 10
		
		int num4 = 10;
		int	num5 = 20;
			
		int max = (num4 > num5)?num4:num5;
		
		System.out.println(max); //20
			
		
	}

}

위에 예제를 살펴보면 변수 num3에서 조건문이 참이기에 첫번째 결과값인 10이 대입되 었고 변수 max에서 조건문이 거짓이기에 뒤에 결과값이 나와 20이 대입 되었습니다. 이처럼 조건연산자는 간단한 조건문을 만들어 내는 연산자입니다. 

 

비트 연산자

비트연산자는 특정 비트를 사용하기위해 꺼내 올때 사용하고 다음과 같은 연산자들을 가지고 있습니다.

  아래 예제를 통해 이 연산자들을 어떻게 사용하는지 살펴보겠습니다.

public class OperatorEx4 {

	public static void main(String[] args) {

		int num1 = 0B00001010 ; //10, 0B는 2진수시작부호
		int num2 = 0B00000101; // 5
		
		System.out.println(num1 & num2); //위아래 둘다 1일때 1 =00000000 = 0
		System.out.println(num1 | num2 ); //둘중하나가 1일때 1 = 00001111 = 15
		System.out.println(num1 ^ num2); // 두개가 다를때 1 = 00001111 = 15
		
		System.out.println(num2 << 1); //num2 한칸씩왼쪽으로 = 00001010 = 10
		System.out.println(num2 << 2); //num2 두칸씩왼쪽으로 = 00010100 = 20 // 여기까지 num2값 변하지 않음
		System.out.println(num2 <<= 2); //대입연산자 쓰면 대입되서 바뀜
		System.out.println(num2); // 10에서 20으로 변함
		
		int num3 = 0B00000101; // 5
		
		System.out.println(num3 >> 2); // 맨오른쪽칸 두개 없어짐 000001 = 1
		System.out.println(num3 >> 1); // 맨오른쪽칸 두개 없어짐 0000010 = 2 
		//결국 나누기랑 같다, >>1은 /2에1승, >>2는 /2의2승
	}

}

 "&, | , ^" 연산자는 위의 예제처럼 두개의 비트를 비교하여 새로운 비트값이 나오게 되는데 이것을 통해 특정한 위치의 비트가 어떤 값을 가지고 있는지 알 수 있습니다. 

 

"<<, >>"와 같은 shift 연산자를 쓰면 비트가 지정한 자리수만큼 이동하여 결과적으로 왼쪽으로 이동시(<<) 곱한 결과 값이 나오고 오른쪽으로 이동시(>>) 나눈 결과 값이 나오게 되며 '='대입 연산자와 함께 사용시 이동된 값이 변수에 대입이 됩니다.

 

위 예제에서 살펴보듯 비트 연산자는 특정한 위치의 비트를 찾아내어 활용하거나 비트를 곱하거나 나누는 용도로 활용할 수 있습니다.

 

 

 

연산자 우선순위

 

참고로 자바의 연산자는 수학의 연산자 처럼 우선순위가 있습니다.  그 중 단항 우선자의 우선순위가 높은편이고 대입연산자(복합대입연산자 포함)의 우선순위가 가장 낮습니다.

앞에서 배운 변수를 이용하여 각종 연산을 할 때 사용하는 연산자들의 종류를 배워 봅니다.

 

항과 연산자

항(operand)는 연산에 사용되는 값으로 연산이 되는 대상을 나타냅니다.

 

연산자(operator)는 항을 이용하여 연산하는 기호입니다.

 

 

항의 개수와 연산자

 

연산자는 사용되는 항의 갯수에 따라 단항연산자, 이항 연산자, 삼항 연산자로 분류 합니다.

단항 연산자는 증감 연산자( ++num)가 대표적이며 이항 연산자는 우리가 일반적으로 알고 있는 양쪽에 두개의 항이 들어가고 가운데이 연산자가 들어가는 형태입니다(num1 +num2;). 삼항 연산자는 조건연산자로 조건문과 역할이 비슷한 연산자 입니다(5>3? 1:0;).

 

대입 연산자

int age =20;

 

'='기호의 연산자로 오른쪽의 값이 왼쪽의 변수의 대입될 때 사용합니다. 대입 연산자는 우선순위가 가장 낮은 연산자로 다른 연산자의 연산이 모두 끝난 뒤에 대입될 때 사용되며 다른 연산자와 같이 복합하여 사용 될 때도 있습니다.

 

부호 연산자

부호 연산자는 단항 연산자로 변수의 부호를 유지하거나(+) 바꿀때(-) 사용됩니다. 부호 연산자 자체에 대입에 의미가 없기 때문에 대입연산자와 같이 쓰여야 변수의 값이 변하게 됩니다.

 

public class OperatorEx1 {

	public static void main(String[] args) {
		
		int num1 = -10;
		int num2 = 20;
		
		System.out.println(+num1); //-10
		System.out.println(+num2); // 20

		System.out.println(-num1); // 10
		System.out.println(-num2); //-20
		
		System.out.println(num1); // -10
		System.out.println(num2); //  20
		
		num1 = -num1;
		
		System.out.println(num1); //10  - 대입연산자를써야부호가바뀐다


	}

}

 

출력문에 변수에 '+'기호를 붙이면 부호가 유지되어서 출력됩니다. 반대로 '-'를 붙이면 부호가 바뀌어서 출력되는 것을 확인 할 수 있습니다.

 

그러나 출력문에서 -부호를 붙였다고해서 변수의 값이 변하는 것은 아니기 때문에 num1,num2를 그대로 출력했을 때 변하지 않은 부호 그대로 출력되는 것을 확인 할 수 있습니다.

 

마지막 부분에서 num1 = -num1;과 같이 대입 연산자와 같이 사용 했을때 비로서 값이 변하여 출력되는 것을 확인 할 수 있습니다.

 

산술 연산자

 

사칙연산자

 

산술 연술자 중 사칙 연산을 할 때 쓰이는 연산자 입니다.

'%'는 나머지를 구하는 연산자로  x라는 값을 n으로 나누었을 때 나머지는 항상 0 ~ n-1 범위 안에 있습니다.

식으로 정리하면 x & n = y 때  y는  0 <= y <= n-1  입니다. 나머지 값의 범위가 정해져 있는 것을 활용해서 사용되는 경우가 많습니다.

 

public class OperatorEx2 {

	public static void main(String[] args) {
		
		int num = 5%3; // 값은 0~n-1 즉 0,1,2 가 올수 있음
		System.out.println(num); //2
		
		num = 10%3;
		System.out.println(num); //1
		
	    int num1 = 10;
	    num1 += 2;
	    System.out.println(num1); //12 
	}

}

 

복합 대입 연산자

 

대입연산자와 다른 연산자가 합께 사용 되는 연산자로 변수를 중복해서 사용하는 것을 줄여주는 역할을 합니다. 

 

증가 감소 연산자

 

단항 연산자로 변수의 값에 1을 더하거나 1을 뺄 때 사용합니다. 연산자가 변수 앞에 있냐 뒤에 있냐에 따라 결과 값이 달라 집니다. 연산자가 값의 앞에 있으면 값이 먼저 증가하고 변수의 대입되게 되고 값의 뒤에 있다면 대입이 먼저된 이후 값이 증가하게 됩니다.  

public class OperatorEx31 {

	public static void main(String[] args) {

		
		int score = 100;
		System.out.println(++score); // 101 // 연산이 끝나고 대입이되기때문에 다음에도 영향이감
//		score = score +1; //101
//		score += 1;       //101 
		System.out.println(score++); // 101// 위에식에서 증가된값에 뒤에++은 다음에 영향을주기에
		System.out.println(score); // 102 //위에서 증가한게 지금 영향이옴
		System.out.println(--score); // 101
		System.out.println(score--); // 101
		System.out.println(score); // 100
		
		
				
		
	}

}

위에 예제를 살펴보면 int score = 100; 아래의 세줄은 결과가 같은 식이지만 변수가 중복 사용되는 score = score +1;과 같은 형태는 잘 사용 되지 않습니다.

 

부호연산자는 대입의 의미가 없지만 증감연산자는 대입의 의미가 있기에 출력문에 사용된 연산자라도 값이 변화하게 됩니다. 그래서 ++score 에서 증가된 값은 다음줄에 score++을 출력했을 때도 여전히 반영이 되어 값이 101이 됩니다.

 

score ++과 같이 연산자가 뒤에 있는 경우는 대입이 먼저되고 값이 증감되기 때문에 증가된 값은 다음줄에서 반영이되서 score의 값이 102가 됩니다.

 

아래의 --의 경우에도 같은 방식으로 작동되는 것을 확인 할 수 있습니다.

 

 

상수 (constant)와 리터럴(literal)

상수는 변하지 않는 수로 앞에 파이널이란 키워드를 사용하여 선언합니다.

 

리터럴은 프로그램내에서 사용되는 모든 숫자, 값, 논리 값을 나타내는 말입니다. 

ex) 10, 3.15, 'A', ture 등 우리가 사용하는 모든 '값'

 

그림과 같이 모든 리터럴은 상수 풀에 저장 되어있습니다. 리터럴이 상수풀에 저장 될 때 정수는 int, 실수는 double로 저장되고 그범위를 넘어가는 수는 따로 식별하여 다른 자료형으로 저장합니다. 그 후 프로그램에서 값이 필요 할 때 상수풀에서 꺼내서 사용합니다. 이것을 통해서 우리가 정수 혹은 실수형 변수를 선언할때 int혹은 double이 아닌 경우 추가 부호를 붙여야 하는 이유가 알 수 있습니다.(리터럴이 상수 풀에 그런 형태로 저장되어 있는걸 변수에서 복사해서 사용하는 것이기에)

 

형 변환

변수마다 자료형이 다르고 자료형마다 서로 사용하는 메모리 방식도 다르기 때문에 프로그램내에서 서로 다른 자료형의 값이 대입되는 경우 형변환이 일어납니다.

 

묵시적인 형변환(implicit type conversion)은 작은수 => 큰수, 덜 정밀한 수 => 더 정밀한 수 로 대입되는 경우에 일어나는 형변환으로 자동으로 형변환이 됩니다. 아래 그림에서 화살표 방향으로 일어나는 형변환들이 묵시적 형변환 입니다.

 

명시적인 형변환(explicit type conversion)작은수 <= 큰수, 덜 정밀한 수 <= 더 정밀한 수 로 대입되는 경우에 일어나는 형변환으로 묵시적인 형변환과 다르게 타입 캐스팅을 표시해주어 프로그래머가 자료의 손실이 발생할 수 있다는것을 인지하고 있음을 알려주어야 일어나는 형변환 입니다.(명시적인 형변환은 자료의 손실이 일어날 수 있다는 뜻을 내포합니다.) 위에 그림에서 화살표 반대방향으로 일어나는 형변환 입니다.

 

예제)

묵시적 형변환

 

public class ImplicitConversion {

	public static void main(String[] args) {

		byte bNum = 10;
		int iNum = bNum;
		
		System.out.println(bNum); // 10
		System.out.println(iNum); // 10
		
		int iNum2 = 20;
		float fNum = iNum2; 
		
		System.out.println(iNum2); //20
		System.out.println(fNum); //20.0
		
		double dNum;
		dNum = fNum + iNum;
		
		System.out.println(dNum); //30.0
		
	}

}

 

byte => int 로 대입될때 자동으로 형변환 되어 10이라는 값이 출력되었습니다.

 

int => float으로 대입될때 정수에서 실수라는 자료형 차이가 있지만 덜 정밀한 수에서 더 정밀한 수로 대입되는 개념으로 묵시적인 형변환이 일어나서 20.0이라는 값이 출력되었습니다.

 

dNum = fNum + iNum;  부분에서 대입연산자는 가장 나중에 연산이 되기 때문에 먼저 iNum(int)이 float 으로 형변환되어서 fNum과 합연산이 일어나고 그다음 합쳐진 float값이 double로 형변환되서 dNum에 대입됩니다.(2번의 묵시적 형변환이 일어났습니다.) 

명시적 형변환

 

public class ExplicitConversion {

	public static void main(String[] args) {

		int i = 1000;
		byte bNum = (byte)i;
		
		System.out.println(bNum); // -24
		
		double dNum1 = 1.2;
		float fNum = 0.9F;
		
		int iNum1 = (int)(dNum1 +fNum);
		int iNum2 = (int)dNum1 +(int)fNum;
		
		System.out.println(iNum1); // 2
		System.out.println(iNum2); // 1
	}

}

 

int 값에 byte를 캐스팅( (byte)i 와 같이 값 앞에 괄호안에 자료형을 써주는것을 타입 캐스팅이라고 합니다.)해서 byte 값으로 명시적 형변환을 했더니 byte의 범위를 넘어선 값이 대입되어 데이터 유실이 발생하여 -24라는 음수가 나왔습니다. 이런 데이터 유실의 발생을 프로그래머가 인지 한다는 뜻에서 명시적인 형변환을 하는 것 입니다.

 

iNum1은 먼저 실수 값을 더한후 int로 형변환을 한것이기에 1.2 + 0.9 = 2.1에 정수로 형변환하면서 소수점을 버리는 효과가 일어나 정수 2라는 결과가 나왔습니다. 이와 다르게 iNum2에서는 두 실수를 먼저 정수로 형변환하여 소수점을 버리고 이후에 더했기 때문에 1.0 + 0 = 1라는 다른 결과가 나오게 됩니다. 이를 통해 우리는 형변환을 언제 하느냐에 따라 결과 값이 다를 수 있다는 것을 알 수 있습니다.

 

2진수, 8진수, 16진수

 

public class BinaryTest {

	public static void main(String[] args) {
		
		int num = 10;
		int bNum = 0B1010;
		int oNum = 012;
		int xNum = 0XA;
		
		System.out.println(num); //10
		System.out.println(bNum); //10
		System.out.println(oNum); //10
		System.out.println(xNum); //10
	}

}

 위에 예제처럼 10진수 외에 다른 진수도 앞에 각 진수에 해당하는 부호를 넣음으로 사용할 수 있음을 알 수 있습니다.

실수

 

public class DoubleTest {

	public static void main(String[] args) {
 
		double dNum = 3.14; 
		
		float fNum = 3.14F;
		
		System.out.println(dNum); // 3.14
		System.out.println(fNum); // 3.14
		
		
	}

}

자바에서 실수는 기본적으로 double을 자료형으로 사용합니다.  => double dNum  = 3.14;

다른 실수 자료형인 float도 사용할 수도 있는데 그때는 값뒤에 f, F 식별자 넣어서 사용해야 합니다.

 

 

부동 소수점

 

자바에서는 실수는 부동 소수점 방식을 사용하여 표현합니다.

 


부동 소수점 방식은 가수부부과 지수부를 따로 나누어서 저장하기 때문에 아주 넓은 범위에 실수를 표현 할 수 있지만 0을 제대로 표현 못하기에 약간의 오차가 생기는 문제가 있습니다.

 

public class DoubleTest2 {

	public static void main(String[] args) {

	   double dNum = 1;
	   
	   for(int i = 0; i <10000; i++) {
		   
		   dNum = dNum + 0.1;
	   }
	   System.out.println(dNum); //1001.000000000159

	}

}

위에 예제에서 dNum = 1001 이어야 하지만 소숫점자리수가 생기는 오차가 발생합니다. 이것이 부동소수점의 오차이지만 자바에서는 이 정도의 오차를 감수하고 더 넓은 실수범위를 표현할 수 있는 부동소수점 방식을 사용합니다.

 

논리자료형

 

수학에서의 명제와 같이 true, false 값만 나타내는 자료형입니다. 그래서  1byte만 사용는 자료형입니다.

 

public class BooleanTest {

	public static void main(String[] args) {
		boolean isMarried = false;
		System.out.println(isMarried); //false
		
		
		var i = 10;
		System.out.println(i); // 10

	}

}

논리자료형은 boolean 자료형으로 표현되고 그 값으로는 true와 false만 올 수 있습니다.

 

자료형 없이 변수를 사용하는 방법

자바와 같은 컴파일 언어는 항상 자료형을 명확히 쓰지만 자바10부터 지역변수(중괄호 안에서 선언되는 변수)의 한에서 지역 변수 자료형 추론(local variable type inference)으로 자료형 없이 변수를 선언 할 수 있습니다.

위에  var i = 10; 처럼  int가 아닌 var으로 변수를 선언해도 자바에서 알아서 int로 자료형을 추론하여 에러가 나지 않고 값이 출력되는 것을 볼 수 있습니다.

문자자료형

문자자료형도 다른자료형과 같이 내부적으로 비트의 조합으로 저장을 하는데 각 문자의 특정한 숫자값을 부여하여 저장합니다.

각 문자를 저장 할 때 숫자화 하는 것을 '인코딩'이라고 하고 숫자화된 자료를 문자화 하는 것을 '디코딩' 이라고 합니다.

 

ex) 'A' -> 65, // 인코딩
     65 -> 'A' // 디코딩

 

문자세트

문자세트는 문자를 어떤 숫자(코드값)으로 저장할 것인가를 정해놓은 세트입니다. ('A' -> 65 'B' ->66)

아스키는 문자를 1바이트로 표현하는 문자세트로 영어는 가능하지만 외국어는 적용이 어려운 문자세트입니다.

유니코드는그래서 나온 문자세트로 멀티바이트를 지원합니다.  UTF-8 UTF-16이 대표적인 유니코드 문자세트 입니다.   

public class CharTest {

	public static void main(String[] args) {

		char ch = 'A';
		
		System.out.println(ch); // A
		System.out.println((int)ch); // 65
		
		int iCh = 66;
		
		System.out.println(iCh); // 66
		System.out.println((char)iCh); //B
		
//		char ch2 = -66;

		char hangul = '\uAC00';
		System.out.println(hangul); // 가
	}

}

위에 코드에서 알 수 있듯이 ch(문자 자료형)는 (int)로 캐스팅할 시 숫자로 변환이 가능하고('A' => 65) 반대의 경우도 가능합니다(66 => 'B'). 문자 세트에서는 양수만 사용되기 때문에  -66같은 음수값은 문자 자료형에 들어갈 수 없습니다.

 

마지막 char hangul = '\uAc00'; 코드 처럼 유니코드를 직접 입력하여서 한글 문자를 출력할 수도 있습니다.(유니코드를 입력할때는 \u를 사용합니다.)

 

 

정수자료형

자바에서 자료형은 기본형 혹은 참조형  2가지로 구분됩니다

 

기본형

자바언어에서 기본적으로 제공해주는 자료형입니다. ( 메모리의 크기가 정해져있습니다.)

종류로는 정수형, 문자형, 실수형, 논리형이 있습니다.

 

참조형

프로그래머가 정의한 클래스를 자료형으로 하기에 클래스 타입의 자료형이라고 불립니다.

클래스에 따라 메모리의 크기가 다를 수 있습니다. 

ex) String, Student... 후에 클래스를 배우고 더 자세히 다룹니다.

 

기본자료형의 메모리 크기를 나타내는 자료입니다. 정수의 대표자료형은 int이고 실수는 double입니다.

 

Int 자료형 

정수형 자료형 중 기본으로 사용되는 자료형으로 4byte 사용합니다.

 

 

 

자료형의 크기의 따라 표현할 수 있는 수의 범위가 달라지는데 그 중 정수의 기본적인 int의 표현 범위를 살펴보면,

1비트로 표현할 수 있는 수는 0,1 즉 2개이고 2비트는 4개 즉 2의 2승입니다. 이런식으로 32비트를 사용하는 int자료형의 범위를 살펴 보면 2의 32승 개라고 생각 할 수 있지만,  맨앞에 붙는 비트는 부호를 나타내기 때문에 빼주어야 되서 +- 2의 31승개 그리고 비트의 값이 모두 0이 됬을때 나오는 한가지 경우를 빼주기 위해 -1까지 해주면 int의 표현범위가 됩니다.

 

 

Int 가아닌 다른 정수 자료형을 사용할 때

 

	public static void main(String[] args) {

     byte bs = 127;   
     
     System.out.println(bs);
     
//     int iVal = 12345678900;
     long lVal = 12345678900L;
     
 	}

}

 

int iVal = 12345678900; -  Integer의 범위를 넘어가서 에러가 납니다 


long lVal = 12345678900;  - long으로 자료형을 바꾸어도 에러가 사라지지 않습니다.  기본적으로 정수는 Integer로 들어가기 때문입니다. 


long lVal = 12345678900L; 그래서 정수 값 끝에 L을 넣어줘야 long으로 취급하여 에러가 나지 않습니다 (L은 대소문자 가 상관 없는데 가독성이  L이 좋습니다.)

 

 

변수란 무엇인가

변수는 사람의 나이, 학생의 학년과 같이 변하는 수를 뜻합니다. 반대로 값이 고정되어 변하지 않는 수를 상수라고 합니다.

 

변수 선언하는 방법

자료형(데이터타입) 변수이름 ;
int age;

 

변수를 선언하는 방법은 int age;와 같이 자료형(데이터타입)을 써주고 그뒤에 변수의 이름을 설정해 주면 됩니다.
ex) int age = 36; , String name = "나개발";

참고로 자바에서 '='(등호)는 수학에서의 두 항이 같다라는 뜻이 오른쪽 항을 왼쪽항에 대입 한다는 뜻 입니다.

 

public class DataType {

	public static void main(String[] args) {
		int age = 30;

		age = 20;

		System.out.println(age); // 20
	}

}

위 코드에서 변수 age에 처음에는 30이 대입 되었지만 아래 코드에서 20을 다시 대입하였기에 age의 값을 출력하면 20이 됩니다.

 

변수는 여러개를 한번에 선언할 수 있습니다.(권장하진 않는다고 합니다.)

ex) int = age, count, users;

 

변수와 메모리

 

변수를 선언한다는 것은 해당자료 형의 크기(데이터 타입)만큼 메모리를 사용하겠다고 선언하는 것입니다.

int age 에서 int는 4byte로 메모리에 4byte가 설정되고 그렇게 설정된 메모리의 이름을 age라고 지정하는 것입니다.

 

변수의 이름

 

변수의 이름은 영문자나 숫자를 사용하지만 항상 영문자로 시작하여야 합니다.
또한 쓰임에 맞는 이름으로 명명하는 것이 가독성에 좋습니다.(약어를 사용하면 가독성이 떨어집니다.)
ex) String numberOfStudent; (O) || String nos; (X)

변수명은 소문자로 시작하여 단어가 바뀔때마다 대문자를 쓰는 Camel Notaion을 따릅니다.(낙타등과 비슷하다는 의미로 생긴 용어라고 합니다.)

 1년 이상 부모님과 같이 하던 일에서 성과가 제대로 나오지 않고 제자리 걸음을 하고 오히려 부모님께 도움을 받는 상황이 되면서서 커리어 전환의 필요를 느끼며 여러 분야들을 찾아보게 되었습니다.  그러던 중 개발쪽에 관심이 생겨 자세히 들여다 보게 되었고, 그결과 개발분야는 제가 어렴풋이 생각했던것 보다 더 크고 다양한 영역이 있고 지속적으로 성장하고 있는 분야 임을 알게 되었습니다.

 

관심이있는 분야이고 성장하는 산업이라면 당연히 도전해보고 싶은 마음이 들었지만 처음엔 비전공자로서 도전이 가능할까? 라는 염려가 앞섰습니다. 하지만 관심을 가지고 찾아보니 현업에 이미 많은 비전공자 개발자분들이 활동하고 계신 것을 알게 되었고 비전공자로서의 어려움이 있겠지만 열심히 공부하고 준비하면 전공자가 아니더라도 도전해 볼 수 있는 영역임을 알게 되었습니다.

 

또한 개발자외에 고려해보았던 공무원이나 국가고시 같은 자격시험에 비해 취업까지의 준비기간이 짧은것도 매력적으로 다가왔습니다. 실제 어떤 업무를 하게 될지 정확히 모르는 상태에서 오랜시간 준비만 하는 것보다 빠르게 현업에 투입되 실전에서 부족한 부분들을 느끼고 채워나가며 업무 역량을 키워나가는 것이 새로운 분야에 빠르게 적응하기 위해 더 나은 방법이라고 느껴졌습니다.

 

또한 개발자로서의 역량을 키우고 빠르게 변화하는 환경에 적응하기 위해 스스로 끊임 없이 공부하고 노력해야 한다는 점도 커리어와 함께 제 자신도 성장해나가며 성취감을 느낄 일이라고 생각해서 더욱 도전해보고 싶은 마음이 들었습니다.

 

이러한 이유들로 저는 개발자가 되고 싶다는 마음을 가지게 되었고 구체적으로 어떻게 준비해야 개발자가 될 수 있는지 준비하기 알아보기 시작하였습니다.

 

 

 개발자가 되기 위해 공부를 시작하면서 그 과정을 정리해 놓기 위해 이 블로그를 시작합니다.

 

매일매일 개발자가 되기 위해 준비하고 공부한 내용들을 정리하여 복습하고 포트폴리오화 하는 것을 목표로 합니다. 

 

 

2020.06.23 

+ Recent posts