[Java] 연산자_1

삶걀·2022년 4월 24일
0

Java

목록 보기
4/15

연산자

연산자: 연산을 수행하는 기호
피연산자 : 연산자의 연산 수행 대상
x + 3
x, 3 > 피연산자
+ > 연산자

->모든 연산자는 연산 결과를 반환한다.
연산 결과를 반환하지 않으면 연산자가 아니라는 의미임.

연산은 꼭 변수에 담지 않아도 됨! System.out.println(4*x+3);처럼 바로 입력해도 연산 가능함.

종류연산자설명
산술 연산자+ - * / % << >>사칙 연산, 나머지 연산
비교 연산자> < >= <= == !=크고 작음, 같고 다름을 비교
논리 연산자&& || ! & | ^ ~'그리고(and)'와 '또는(or)'으로 조건을 연결
대입 연산자=우변의 값을 좌변에 저장
기 타(type) ?: instanceof형변환 연산자, 삼항 연산자, instanceof연산자

<<, >>: 비트 연산자, 잘 안씀
논리 연산자에서 &&, ||, !만 쓰고 나머진 몰라도 된다!

예시
x > 3 && x < 5
:x는 3보다 크고(and) 5보다 작다.
!(x>3)
:x는 3보다 작거나 같다. (x<=, 부정하면 반대가 됨)

||는 또는(or), !는 부정(not)으로 쓰임


03. 연산자의 우선순위

연산자의 우선순위
->자동으로 다 결정됨, 내가 원하는 우선순위는 괄호를 사용하여 묶어주자>수동으로 결정

그럼 연산자들끼리 우선순위가 어떻게 되는지 알아야함 -> 표 참조
상식적으로 생각하자~ 우리가 이미 다 알고있는것이다.

예제

-x + 3
:여기서 +는 -x와 3으로 이뤄진 이항 연산자이다. 그리고 -x에서 -는 뺄셈 연산자가 아니라 부호 연산자임. -와 x로 이뤄진 1항 연산자인데 이를 단항 연산자라고 부른다. -x 다음에 +3을 계산하므로 2항 연산자보다 단항 연산자의 우선순위가 더 높다.

result = x + y * 3;
:대입 연산자는 연산자 중에서 제일 우선순위가 낮다. 다 계산산하고 최종 결과를 대입하기 때문.


04. 연산자의 결합규칙

대입과 단항 연산자를 제외하면 모두 왼>오른쪽으로 연산을 수행한다.

대입, 단항 연산자는 오른쪽에서 왼쪽으로 진행
x = y = 3
< --------
'모든 연산자는 연산 결과를 반환'하므로 y=3에서 3을 변수 y에 저장하고, 그 결과를 반환한다(3) 그 값을 다시 x에 저장해서 x에 3이 저장되고 연산 결과가 3이 나온다.


나머지 연산자 대부분은 왼쪽에서 오른쪽으로 진행
3 + 4 - 5
-------- >

정리

  1. 산술>비교>논리>대입, 대입은 제일 마지막에 수행된다
  2. 단항>이항>삼항 단항 연산자의우선순위가 이항 연산자보다 높다
  3. 단항 연산자와 대입 연산자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽이다.

05. 증감 연산자

증가 연산자(++) : 피연산자의 값을 1증가 시킨다.
감소 연산자(--) : 피연산자의 값을 1감소 시킨다.

타입설명사용예
전위형값이 참조되기 전에 증가시킨다.j=++i;
후위형값이 참조된 후에 증가시킨다.j=i++;

증감 연산자가 독립적으로 사용된 경우 전위형과 후위형의 차이가 없다!

이해하기 힘들면 따로 분리해서 보자.

j=++i; //전위형 -> ++i; // 증가 후에
				   j = i; // 참조하여 대입 
                   
j=i++; //후위형 -> j = i; // 참조하여 대입 후에
				  i++; // 증가
				  

분리해서 생각하니까 훨씬 편함! 전위형은 일단 먼저 증가!하고 대입하니까 j에 그 증가한값이 들어가고 후위형은 일단 나중에 대입 하고 증가하니까 j에 증가 전의 값이 들어간다.


06. 부호 연산자

부호연산자는 우리가 수학에서 배우는 2랑 -2같은 개념이라고 생각하면 편하다.
'부호 연산자' 이름 그대로 앞에 -나 +를 붙여 피연산자의 부호를 반대로 변경해줌!
(+는 주로 생략하는것처럼 실제로도 사용 잘 하지 않음.)

07. 형변환 연산자

형변환(casting)?
서로 다른 타입간의 연산이 필요할 때 타입을 일치시켜야 하는데, 변수나 리터럴(상수)의 타입을 다른 타입으로 변환하는 것.

(타입)피연산자

+여기서 괄호는 '캐스트 연산자' 또는 '형변환 연산자' 라고 부른다.

!주의!
형변환 연산자는 피연산자의 값을 지정된 타입으로 바꿔줄 뿐임.
형변환 연산자를 사용한다고 해서 피연산자의 값 자체가 바뀌진 않는다.

예시

public class Ex_3_1_0 {

	public static void main(String[] args) {
		float a = 23.3f;
		int real = (int) a;
		System.out.println("real=" + real);
		System.out.println("a=" + a);

	}

}
결과값: real=23
	   a=23.3

08. 자동 형변환

서로 다른 타입간 대입, 연산을 할 때 먼저 형변환으로 타입을 일치 시켜야되는데 편의상 이유로 형변환 생략이 가능하다. 컴파일러가 생략된 형 변환을 자동적으로 추가하는 원리.

float f = 1234; 에서 1234는 int, 즉 정수이다. 정석대로라면 바로 정수를 입력하면 안되고 float f = (float)1234;라고 입력해야한다. (float f = (float)1234로 해야 정수1234가 float,실수로 바뀌어서 f에 저장되므로)

그치만 편의상 생략이 가능하다. 왜냐면 float타입의 변수가 1234 정수 값을 저장하는데 아무런 문제가 없기 때문이다.

public class Ex_3_1_0 {

	public static void main(String[] args) {
		float a = 1234;
		System.out.println("a=" + a);

	}

}
출력값: a=1234.0

이처럼 정상적으로 float형식으로 자동 형변환이 된다.

"기존값을 최대한 보존할 수 있는 타입으로 자동 형변환 된다" 는것을 알 수 있다.

표현범위가 좁은 타입 -> 넓은 타입으로 형변환 하는 경우 값 손실이없으므로 생략 가능
but, 좁은 타입 -> 넓은 타입으로 형변환 하는 경우 반드시 형변환 연산자를 사용해야한다.

(좁은 범위) byte -> short, char -> int -> long -> float -> double (넓은 범위)

변수가 저장할 수 있는 값의 범위보다 더 큰 값을 저장하려는 경우 형변환을 생략하면
byte a = 1000; -> 에러, byte 타입의 범위를 벗어난 값의 대입

그러나 다음과 같이 형변환을 명시해주면 의도적인 것으로 간주하고 에러가 뜨지 않는다.
byte a = (byte)1000 -> OK, but 값 손실이 발생해서 a에 -24가 저장됨.

위의 예제를 다시 가져와봤다.

public class Ex_3_1_0 {

	public static void main(String[] args) {
		float a = 23.3f;
		int real = (int) a;
		System.out.println("real=" + real);
		System.out.println("a=" + a);

	}

}

int real = (int) a; 에서 (int)는 생략이 가능할까?
정답은 NO다.

float를 int로 바꾸려는것이기 때문에 (int)를 생략하면 컴파일 에러가 뜬다. 그러므로 앞에 반드시 형변환 (int)를 붙여줘야 정상적으로 정수로 형 변환이 되어 real=23 이 출력된다.


09. 사칙 연산자

public class Ex_3_1_0 {

	public static void main(String[] args) {
		int a = 10;
		int b =4;
		System.out.printf ("%d + %d = %d%n", a, b, a+b);
		System.out.printf ("%d - %d = %d%n", a, b, a-b);
		System.out.printf ("%d * %d = %d%n", a, b, a*b);
		System.out.printf ("%d / %d = %d%n", a, b, a/b);
		System.out.printf ("%d / %d = %d%n", a, b, a/b);
		System.out.printf ("%d / %f = %f%n", a, (float)b, a/(float)b);
		System.out.printf ("%d %% %d = %d%n", a, b, a%b);
	}

}

출력값:
10 + 4 = 14
10 - 4 = 6
10 * 4 = 40
10 / 4 = 2
10 / 4 = 2
10 / 4.000000 = 2.500000
10 % 4 = 2

int / int = int 이므로 10 / 2의 결과가 2.5가 아닌 int(정수) 2로 출력된다.
(정수만 남고 소수점 이하는 버려지는데 이때 반올림은 발생하지 않는다.)

정확한 값을 얻고싶으면 한쪽을 실수형으로 형변환 하자. 그러면 다른 한쪽도 실수형으로 자동 형변환 되어 실수형 값을 결과로 얻는다.

10 / 4.0f -> 10.0f / 4.0f -> 2.5f

여기서 두 피연산자의 타입이 일치하지 않으므로, 좁은 int 타입에서 넓은 float 타입으로 일치시킨후 연산이 수행되는것을 볼 수 있다. float타입과 float타입의 연산이므로 결과값도 float타입이다.


10. 산술 변환

이항 연산자는 두 피연산자의 타입이 일치해야 연산이 가능하다.
피연산자의 타입이 서로 다르면 형변환 연산자로 타입을 일치 시켜야함.

(일반)산술 변환이란?

"연산 전에 피연산자의 타입을 일치시키기 위해 자동 형변환 되는것"

이 변환은 이항 연산 뿐만 아니라 단항 연산에서도 일어난다.

산술 변환의 규칙(중요)

  1. 두 피연산자의 타입을 같게 일치 (보다 큰 타입으로 일치, 값 손실 최소화)

    long + int -> long + long -> long
    float + int -> float + float -> float
    double + float -> double + double -> double

  1. 피연산자의 타입이 int보다 작은 타입이면 int로 변환 (간단한 곱 연산만 해도 쉽게 범위를 벗어남-> 오버플로우 발생/ 오버 플로우 방지)

    byte + short -> int + int -> int
    char + short -> int + int -> int

예제
char - char
'2' - '0' -> 50 - 48 -> 2
(유니코드 참조)

char -> int 변환
'2' -(int 형변환)-> 50

그래서 문자->숫자로 바꿀때 문자'0'을 빼는것임!
(유니코드가 연속적으로 있기 때문)

public class Ex_3_1_1 {
	public static void main(String[] args) {
		int a = 1_000_000;
		int b = 2_000_000;
		
		long c = a * b;
		
		System.out.println(c);
	}
}
출력값: -1454759936

여기서 오버플로우가 발생한것을 알 수 있음.
-> 결과값이 int라는 뜻임.

a나 b를 형변환 해줘야 값이 제대로 출력됨.
(하나만 해도 나머지는 자동으로 형변환 된다.)

public class Ex_3_1_1 {
	public static void main(String[] args) {
		int a = 1_000_000;
		int b = 2_000_000;
		
		long c = a * (long)b;
		
		System.out.println(c);
	}
}
출력값: 2000000000000
profile
반숙란 좋아하는사람

0개의 댓글