본문 바로가기
Study/JAVA

[JAVA] 4주차_정적멤버, 문자열, 배열 (this와 정적멤버, 문자열, 배열)

by 8희 2022. 3. 25.

1차시 this와 정적멤버

 


this: 생성한 객체(= 인스턴스) 자신

class Square_1 {
	private double side; //side는 클래스 안에 선언된 멤버 필드
    
    //설정자
    public void setRadius(double s) { //매개변수 s는 멤버 필드 side를 의미하지만 변수 이름은 다름
    	side = s; //설정자 역할 -> 전달된 값의 정보 s가 side라는 필드 정보에 초기화
    }
}
class Square_2 {
	private double side; //side는 멤버 필드
  
	//설정자  
    public void setRadius(double side) { //멤버 필드와 같은 이름으로 매개변수 설정
    	this.side = side; //this.side는 멤버필드, side는 매개변수
    }
}

위 코드 Square_1과 Square_2는 같은 의미의 함수!

this를 사용함으로써 매개변수와 멤버 필드 이름을 똑같이 설정 가능 

 

 

this() (this 메서드)

: 생성자에서 다른 생성자를 호출할 수 있도록 기존 생성자를 나타낼 때 this() 사용

  this()는 자기자신의 생성자에 접근하게 한다.

public Circle(double radius, String color) { //(1)
	this.radius = radius; //this.radius는 필드, radius는 매개변수
    this.color = color; //this.color는 필드, color는 매개변수
}

public Circle() { //(2)
	//this.radius = 0.0;
    //this.color = "";
	this(0.0, ""); //위의 두 줄을 this()를 이용해 합쳐서 표기
}

public Circle(double radius) { //(3)
	//this.radius = radius;
    //this.color = "";
    this(radius, ""); //위의 두 줄을 this()를 이용해 합쳐서 표기
}

생성자 (2)와 생성자(3)이 실행하면서 생성자(1)에 접근해서 radius와 color 값 설정

 

/* this와 this()를 이용한 Circle 클래스 */
/* this()는 가장 첫 번째줄에 나와야 한다! */

class Circle {
	double radius;
	String color;

	public Circle(double radius, String color) {
		this.radius = radius;
		this.color = color;
	}

	public Circle(double radius) {
		this(radius, "파랑");
	}

	public Circle(String color) {
		this(10.0, color);
	}

	public Circle() { 
		this("빨강"); //(1)
		this.radius = 10.0; //(2)
		//this("빨강"); //this()는 가장 첫 번째 줄에 나와야 돼서 this() 앞에 다른 실행문이 있으면 오류 발생
	}
}

(1) - (2) 순서는 오류 발생 X

(2) - (1) 순서는 오류 발생 O

 

this()는 가장 첫 번째 줄에 나와야 한다!

기존 생성자를 호출하기 전에 다른 실행문이 있으면 오류 발생

 


연속 호출 - this를 가지고 재활용

메서드의 반환 타입이 this라면 연속 호출 가능

연속 호출을 사용하면 같은 객체에 대해 문장 하나로 연속되는 다수의 메서드 호출 가능

/* 반환 타입이 void인 setName(String name), setAge(), sayHello() 메서드를 가진
Person 클래스가 있다고 가정 */

Person person = new Person();

/*person.setName("민형"); (1)
  person.setAge(24); (2)
  person.setHello(); (3) */
  
  /* setName()과 setAge()의 반환 타입이 this라면 */
  person.setName("민형").setAge(24).sayHello(); //(1), (2), (3)을 모두 합쳐서 연속 호출 가능

person.setName("민형").setAge(24).sayHello();

-> . 이전은 객체이다.

 

setName()이 this를 반환하므로 Person 객체이다. 따라서 person.setName()은 setAge() 호출 가능

setAge()도 this를 반환하므로 Person 객체이다. 따라서 person.setName().setAge()는 sayHello() 호출 가능

 

/* 연속 호출을 이용한 클래스 */

public class MethodChainDemo { //main()를 갖는 클래스에만 public 지정
	public static void main(String[] args) { //메인 메서드
		Person person = new Person(); //객체 person 생성
		person.setName("민형").setAge(24).sayHello(); //연속 호출
	}
}

class Person { //Person 클래스
	String name; //멤버 필드
	int age; //멤버 필드
 
	public Person setName(String name) { //반환형이 this라서 반환 타입이 Person
		this.name = name;
		return this; //반환형이 this, 생성된 객체 자신을 반환
	}

	public Person setAge(int age) { //반환형이 this라서 반환 타입이 Person
		this.age = age;
		return this; //반환형이 this, 생성된 객체 자신을 반환
	}

	public void sayHello() { //반환형이 없으므로 반환 타입이 void
		System.out.println("안녕, 나는 " + name + "이고 " + age + "살이야.");
	}
}

실행 결과

 


정적 멤버

▶ 자바는 static 키워드로 클래스의 필드를 공유할 수 있도록 지원

 

인스턴스 변수

- 인스턴스란 메모리 상으로 완전히 분리되어 있는 상태

- 인스턴스 변수는 static 키워드로 지정되지 않아 공유되지 않은 필드로 인스턴스마다 자신의 필드 생성

- 객체별로 관리

- 객체가 생성할 때 인스턴스 변수도 생성하므로 객체 생성 후에 접근 가능, 객체가 소멸될 때 자동 소멸

- 객체를 여러 개 생성하면 인스턴스 변수도 여러 개 생성

 

정적 변수(= 클래스 변수)

- static 키워드로 지정하여 모든 인스턴스가 공유하는 필드

- 같은 클래스 명칭이면 공유 가능

- static은 main()가 실행될 때 같이 생성하므로 객체 생성 전에도 접근 가능

- 객체를 여러 개 생성해도 정적 변수는 하나뿐

 

인스턴스 메서드

- 객체를 생성한 후에만 호출

 

정적 메서드(= 클래스 메서드)

- 객체를 생성하기전에도 호출 가능

- 정적 메서드와 인스턴스 변수, 메서드는 생성 시점이 다르기 때문에 정적 메서드에선

  객체와 관련된 인스턴스 변수 사용, 인스턴스 메서드 호출 불가능

- 객체가 생성되어야만 this라는 키워드에 의미가 부여되므로 정적 메서드에선

  객체 자신을 가리키는 this 키워드 사용 불가능

 

 

정적 멤버의 활용

 

1. 정적 멤버 접근

 

클래스이름.정적변수이름

클래스이름.정적메서드이름()

 

정적 멤버는 일반적으로 클래스 이름과 연결해서 사용!

 

2. 상수 선언

 

static final 데이터형 상수 = 초기값

 

상수는 초기값이 대입되면 더 이상 수정할 수 없는 변경되지 않는 변수이므로 final 키워드로 지정

그러나 final로만 지정하면 객체마다 자신의 기억 공간이 할당되므로 모든 객체가 공유할 수 있게 static으로 선언

(상수는 값이 변경되지 않으므로 객체마다 따로 기억 공간을 할당할 필요 X)

 

/* 정적 변수 활용 */

class Circle {
	double radius; //인스턴스 변수
	static int numOfCircles = 0; //정적 변수
	int numCircles = 0; //인스턴스 변수

	public Circle(double radius) {
		this.radius = radius;
		numOfCircles++; //정적 변수이기 때문에 객체가 생성되는 시점에 공유 가능 
        	//객체를 생성할 때마다 하나씩 증가
		numCircles++; //인스턴수 변수라서 객체마다 별도의 기억 공간을 사용하기 때문에 항상 0에서 증가
	}
}

public class CircleDemo {
	public static void main(String[] args) { //메인 메서드
		Circle myCircle = new Circle(10.0); //객체 myCircle 생성 
		Circle yourCircle = new Circle(5.0); //객체 yourCircle 생성

		// print(); //main()는 정적 메서드이므로 인스턴스 호출 불가능
		System.out.println("원의 개수 : " + Circle.numOfCircles); 
        	//Circle 클래스가 직접 정적 멤버 접근 
       		//원의 개수 2
		System.out.println("원의 개수 : " + yourCircle.numCircles); //원의 개수 1
        	System.out.println("원의 개수 : " + yourCircle.numOfCircle); 
            	//객체를 통해 정적 멤버 접근 
        	//정적 멤버는 기억 공간을 공유하기 때문에 원의 개수 2
	}

	void print() {
		System.out.println("인스턴스 메서드입니다.");
	}
}

2차시 문자열

 


문자열의 선언과 생성

String 변수; //String 타입의 변수 선언
변수 = "문자열" //String 타입의 변수에 문자열 대입

Stirng s1 = "안녕, 자바!"; //String 타입의 변수 선언과 초기화
String s2 = "안녕, 자바!"; //String  타입의 변수 선언과 초기화

문자열 리터럴: 내부적으로 new String()을 호출해 생성한 객체

- 리터럴: 대입문에서 오른쪽에 있는 요소 (ex. int x = 8; 에서는 8이 리터럴)

- s1은 new String("안녕 자바!")를 호출해서 생성한 객체를 가리킨다.

 

String의 특징: 문자열 리터럴이 같이 생성되는 경우엔 같은 공간을 본다!

- 내용이 같은 문자열 리터럴이라면 더이상 새로운 String 객체를 생성하지 않고 기존 리터럴을 공유

- 따라서 s1과 s2는 동일한 String 객체를 가리킨다.


문자열의 비교

 

▶  ==와 != 연산자 (비교 연산자)는 두 문자열의 내용을 비교하는 것이 아니라 동일한 객체인지 검사

/* 문자열 객체 비교 */

public static void main(String[] args) {
    	//String으로 선언 시 내부적으로 new String()을 호출해 생성한 객체를 가리킴
		String s1 = "Hi, Java!"; //String 타입의 s1 변수 선언과 초기화
		String s2 = "Hi, Java!"; //s1과 동일한 객체 //문자열 리터럴의 특이점
        
        //새로운 문자열 객체를 생성해 String 타입의 s3, s4 변수를 초기화
        //new String 키워드 사용! 
        //내용이 같은 문자열 리터럴이더라도 새로운 객체를 가리킴
		String s3 = new String("Hi, Java!"); 
		String s4 = new String("Hi, Java!");
        
        //비교 연산자로 동일한 객체인지 검사
		System.out.println("s1 == s2 -> " + (s1 == s2)); //s1 == s2 -> true
		System.out.println("s1 == s3 -> " + (s1 == s3)); //s1 == s3 -> false
		System.out.println("s3 == s4 -> " + (s3 == s4)); //s3 == s4 -> false

		s1 = s3; //이후에는 s1과 s3가 동일한 객체, 즉 같은 공간을 가리키고
        		//가리키는 공간이 아닌 다른 공간은 쓰레기 공간이 됨
		System.out.println("s1 == s3 -> " + (s1 == s3)); //s1 == s3 -> true
	}
}

 

▶  String 클래스에서 제공하는 문자열 비교 메서드

 

메서드 설명
int compareTo(String s) 문자열을 사전 순으로 비교해 정수 값 반환
int compareToIgnoreCase(Sring s) 대소문자를 무시하고, 문자열을 사전 순으로 비교
boolean equals(String s) 주어진 문자열 s와 현재 문자열을 비교한 후 true / false 반환
boolean equalsIgnoreCase(String s) 주어진 문자열 s와 현재 문자열을 대소문자 구분 없이 비교한후 true / false 반환

 

- compareTo: 문자열 둘이 가리키는 내용이 같으면 0, 다르면 0이 아닌 정수 반환

- equals: 문자열 둘이 가리키는 내용이 같으면 true, 다르면 false 반환

- IgnoreCase: 대소문자 구분 X

/* 문자열 내용 비교 */

public class String2Demo {
	public static void main(String[] args) {
		String s1 = "Hi, Java!"; //String 타입의 s1 변수 선언과 초기화
		String s2 = new String("Hi, Java!"); //새로운 문장ㄹ 객체를 생성해 String 타입의 s2 변수 선언과 초기화
		String s3 = "Hi, Code!";  //String 타입의 s3 변수 선언과 초기화
		String s4 = "Hi, java!";  //String 타입의 s4 변수 선언과 초기화

		System.out.println(s1.equals(s2)); //true 
		System.out.println(s1.equals(s3)); //false
		System.out.println(s1.equals(s4)); //false
		System.out.println(s1.equalsIgnoreCase(s4)); //true
 
 		//J의 아스키코드는 74, C의 아스키코드는 67, j의 아스키코드는 106
       	 	//compareTo는 아스키코드를 이용한 빼기 연산
		System.out.println(s1.compareTo(s3)); //7
		System.out.println(s1.compareToIgnoreCase(s4)); //0
		System.out.println(s3.compareTo(s4)); //-39
		System.out.println("Hi, Java!".compareToIgnoreCase("hi, java!")); //0
		
		System.out.printf("'J' - 'C' = %d\n", 'J' - 'C'); //'J' - 'C' = 7 
		System.out.printf("'C' - 'j' = %d\n", 'C' - 'j'); //'C' - 'j' = -39
	}
}

 

▶  String 클래스에서 제공하는 메서드

 

메서드 설명
char charAt(int index) index가 지정한 문자 반환
String concat(String s) 주어진 문자열 s를 현재 문자열 뒤에 붙이기
boolean contains(String s) 문자열 s를 포함하는지 조사
boolean endsWith(String s) 끝나는 문자열이 s인지 조사
boolean isEmpty() 문자열의 길이가 0이면 true 반환
int length() 문자열의 길이 반환
boolean startsWith(String s) 시작하는 문자열이 s인지 조사
String substring(int index) index부터 시작하는 문자열의 일부 반환
String toLowerCase() 문자열을 모두 소문자로 변환
String toUpperCase() 문자열을 모두 대문자로 변환
String trim() 문자열 앞뒤에 있는 공백을 제거한 후 반환
/* String 메서드 이용 */

public class String3Demo {
	public static void main(String[] args) {
		String s1 = new String("Hi,"); //새로운 문자열 객체를 생성해 String 타입의 s1 변수를 초기화
		String s2 = new String(" Java"); //새로운 문자열 객체를 생성해 String 타입의 s2 변수를 초기화
		String s3, s4, s5; //String 타입의 변수 s3, s4, s5 선언

		System.out.println("문자열 길이(s1) : " + s1.length()); //3 //문자열 길이 반환
		char c = s1.charAt(1); //인덱스가 지정한 문자 반환
		System.out.println(c); //i

		s1 = s1.concat(s2); //s2를 s1 뒤에 붙이기

		s3 = s1.toLowerCase(); //문자열을 모두 소문자로 변환
		s4 = s1.substring(4, 8); //인덱스부터 시작한는 문자열의 일부 반환 

		System.out.println(s1 + "!"); //Hi, Java!
		System.out.println(s3 + "!"); //hi, java!
		System.out.println(s4 + "!"); //Java!
	}
}

s4 = s1.substring(4, 8);

-> s4 출력하면 Java!

-> 자바에서 일반적인 메서드 끝은 (맨 끝 -1)의 정보를 추출하라는 의미

-> 그래서 s1의 8번째 정보는 공백이지만, 8번째 정보를 반환하라고 하면 7번째 정보인 a를 반환!

 

자바 String 문자열은 쓰기 권한이 없어서 고정된 개념이다.

따라서 s1 = s1.concat(s2);을 실행하면 s1이 가리키는 공간이 달라지고

원래 가리키던 공간은 쓰레기 공간이 된다. (다시 접근할 수 없는 상태)

 

/* 문자열 결합 */

public class String4Demo {
	public static void main(String[] args) {
		int i = 7;
		System.out.println("Java " + i); //Java 7
		System.out.println("Java " + 7); //Java 7
		System.out.println(7 + 1 + "Java " + 7 + 1); //8Java 71
	}
}

+ 연산자 앞뒤에 적어도 하나의 문자열이 있으면 연결 연산자로 바뀐다.


3차시 배열

 


배열의 선언과 생성

 

배열: 동일한 자료 형태가 연속적으로 있는 것

/* 배열 변수의 선언  */

int [] scores; //방법 (1) 
int scores[]; //방법 (2)

/* 배열 변수의 선언과 초기화 */

// int scores[5]; -> 절대 안 됨! 
scores = new int[5]; //이게 맞음

/* 배열의 선언과 생성의 예 */

int[] scores = {1. 2. 3. 4. 5}; //방법 (1)
int[] scores = new int[] {1, 2, 3, 4, 5}; //방법 (2)
int[] scores;
scores = new int[] {1, 2, 3, 4, 5}; //방법 (3)

/*배열의 선언과 생성 잘못된 예 */
int [] scores;
scores = {1,2, 3, 4, 5}; //절대 안 됨!

 

/* 배열에 정수를 입력하고 평균 계산 */

import java.util.Scanner;

public class Array1Demo {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in); //입력 장치
		int scores[] = new int[5]; //배열 선언
		int sum = 0; //sum 변수 선언과 초기화

		for (int i = 0; i < scores.length; i++) //배열.length는 배열의 크기 반환
			scores[i] = in.nextInt(); //nextInt()는 int 입력 받기 메서드

		for (int i = 0; i < scores.length; i++)
			sum += scores[i];

		System.out.println("평균 = " + sum / 5.0);
	}
}

배열을 위한 반복문

 

for~each 반복문

- for 문을 개선한 방식 (JDK 5부터 도입)

- 특정 원소를 나타내기 위한 인덱스 사용 x

 

for (데이터 타입 변수 : 배열_혹은_컬렉션) { //배열_혹은_컬렉션에서 가져올 항목이 있으면 변수에 대입

 

    //본체 실행

 

}

/* for-each 문을 이용한 배열 원소의 평균 */

import java.util.Scanner;

public class ForEachDemo {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int scores[] = new int[5];
		int sum = 0;

		for (int i = 0; i < scores.length; i++)
			scores[i] = in.nextInt();

		for (int s : scores) //for-each문 이용
			sum += s;
           
		System.out.println("평균 = " + sum / 5.0);
	}
}

for (int i = 0; i < scores.length; i++)  sum += scores[i]; //(1)

for (int s : scores) sum += s; //(2)

 

(1)을 for-each문을 이용한 게 (2)


메서드의 인수로 배열 전달 - Call by reference

/* 메서드 인수로 배열 전달 */

public class IncrementDemo {
	public static void main(String[] args) {
		int[] x = { 0 }; //1개의 원소로 구성된 정수 배열
		System.out.println("호출 전의 x[0] = " + x[0]); //0

		increment(x); //전달하는 값이 배열이므로 받는 값도 배열 타입
		System.out.println("호출 후의 x[0] = " + x[0]); //1
	}

	public static void increment(int[] n) { //인수인 배열 변수 x를 매개변수 n에 복사
		System.out.print("increment() 메서드 안에서 "); 
		System.out.print("n[0] = " + n[0] + " ---> ");
		n[0]++; 
		System.out.println("n[0] = " + n[0]); //1 //x와 n이 같은 공간 공유
	}
}

메서드의 인수로 배열을 전달 시 배열 변수와 매개변수는 같은 공간을 공유한다!


다차원 배열

 

- 배열의 배열

- ex) int[][] scores = new int[3][5];

/* 평균 이자율 계싼 */

public class Array2Demo {
	public static void main(String[] args) {
		double[][] interests = { { 3.2, 3.1, 3.2, 3.0 }, //다차원 배열
								{ 2.9, 2.8, 2.7, 2.6 }, 
								{ 2.7, 2.6, 2.5, 2.7 } };
		double[] sum1 = { 0.0, 0.0, 0.0 }; //그냥 배열
		double sum2 = 0.0;

		System.out.println(interests.length); //다차원 배열의 행 출력 //3
		System.out.println(interests[0].length); //다차원 배열의 열 출력 //4
		
		for (int i = 0; i < interests.length; i++) {
			for (int j = 0; j < interests[i].length; j++) {
				sum1[i] += interests[i][j];
			}
			
			System.out.println(sum1[i]); //12.5
			
			System.out.printf("%d차년도 평균 이자율 = %.2f%%\n", i + 1, sum1[i] / 4);
			sum2 += sum1[i];
		}
		System.out.println(sum2);
		System.out.printf("3년간 평균 이자율 = %.2f%%\n", sum2 / 3);
	}
}