기록은 기억의 연장선

더 많은 것을 기억하기 위해 기록합니다


  • Home

  • Tags

  • Categories

  • Archives

  • Search

[java] 자료형

Posted on 2017-12-12 | Edited on 2020-11-02 | In java | Comments:

기본형(Primitive Type)

자료형 표현 데이터 크기 범위
boolean 참/거짓 1byte true, false
char 문자 2byte 모든 유니코드 문자
byte 정수 1byte -128 ~ 127
short 정수 2byte -32768 ~ 32767
int 정수 4byte -2147483648 ~ 2147483647
long 정수 8byte -9223372036854775808 ~ 9223372036854775807
float 실수 4byte 1.4E-45 ~ 3.4028235E38
double 실수 8byte 4.9E-324 ~ 1.7976931348623157E308

범위에 대한 기본 개념

1byte를 예로 들어보자
1byte는 8bit 이므로, 0000 0000 ~ 1111 1111 까지 표현이 가능하다
이 값은 0 ~ 255 까지의 값인데, java는 unsigned 형이 없으므로 범위에 음수를 포함하여야 한다
하지만 보다시피 현재 상태로는 음수 값을 표현할 수 없다

그래서 가장 왼쪽에 있는 비트를 부호로 사용하며, 이를 MSB 라고 한다
MSB가 0이면 양수, 1이면 음수인 것이다
그러므로 1byte 자료형이 표현할 수 있는 범위는 1111 1111 ~ 0111 1111 까지가 되는 것이다

음수값을 표현할때는 2의 보수(1의 보수를 구한후 + 1)를 사용한다

1
2
ex1) 0111 1111(127) : 1000 0001(-127)  
ex2) 0000 1111(15) : 1111 0001(-15)

여기까지만 보면 1byte의 범위는 -127 ~ 127 까지가 되어야할 것 같은데, 왜 -128까지 일까?

아래는 추측이다
하지만 첫번째 비트를 MSB로 사용했기 때문에, 우리는 0을 2가지 방법으로 표현할 수 있는 특징을 가지게 된다(0000 0000, 1000 0000)
여기서 1000 0000 값을 버리지 않고 1000 0001(-127) 에서 1 빠진 값으로 -128을 사용하는 것으로 보인다


리터럴

정수값은 기본이 int 리터럴이다.
실수값은 기본이 double 리터럴이다.

1
2
int i = 10; // int형
double d = 10.10; // double형

long, float을 사용하고 싶으면 추가적인 문자를 사용해야 한다.

1
2
long l = 3000000000L; // 30억은 int 범위를 넘어가므로 long 리터럴을 사용하지 않으면 표현할 수 없다.
float f = 10.10F;

short, byte는 리터럴이 없다. 그냥 정수값을 넣으면 자동으로 변환된다.

1
2
byte b = 10; // byte형이 됨
short s = 10; // short형이 됨

형변환

명시적 형 변환

캐스팅 연산자를 써서 명시적으로 형을 변환하는 방법이다.

1
2
double d = (double)10; // int를 double로 형 변환
float f = (float)10.10; // double을 float으로 형 변환

크기에 따른 자동 형 변환

byte < short < int < long < float < double
왼쪽으로 오른쪽 순서로 타입의 크기이다.
큰 자료형에 작은 자료형을 저장할 경우 자동으로 형 변환이 일어난다.

1
2
long l = 10; // long형이 됨
float f = 10L; // float이 더 크므로 long을 저장 가능하다

산술연산에 의한 자동 형 변환

연산을 하는 숫자들 중 가장 큰 자료형으로 나머지 숫자의 자료형이 결정된다.

1
2
3
4
5
int i1 = 10L + 10; // long형으로 변환되므로 컴파일 에러
int i2 = 3 + 3.5; // double형으로 변환되므로 컴파일 에러

double d1 = 3 / 2; // 둘다 int 형이므로 소수점 이하가 잘린 1이 저장된다.
double d2 = (double)3 / 2; // 3을 double로 강제 형 변환 하였으므로 연산의 결과는 double이 된다.

byte형이나 short형은 연산 시 모두 int형으로 변환된다.

1
2
3
4
short s1 = 1; // 여기선 자동 변환된다
short s2 = 2; // 여기선 자동 변환된다
short s3 = s1 + s2; // int로 변환되었기 때문에 컴파일 에러
short s4 = (short)s1 + s2; // short로 변환해야 함

char형은 문자형이지만 연산할 수 있다.

1
2
3
char c = 'a';
int i = 100;
System.out.println(c + i); // 문자 a는 아스키코드값(97)로 변환되어 197이 반환된다.

참조 : https://hit-it-sum.tistory.com/1

Read more »

[java] 입출력

Posted on 2017-11-27 | Edited on 2020-11-02 | In java | Comments:

I/O는 Input/Output의 약자로, 입력/출력을 말한다.
입출력은 컴퓨터 내/외부 장치와 프로그램간의 데이터를 주고받는 것을 말한다.

스트림

데이터를 주고받으려면 두 대상을 연결하고 데이터를 전송할 수 있는 연결통로 같은것이 필요한데, 이를 스트림이라고 한다.
스트림은 단방향으로만 가능하므로 하나의 스트림으로 입,출력을 동시에 진행할 수 없다.
그러므로 입력 스트림, 출력 스트림이 따로 존재하고, 입력과 출력을 동시에 수행하려면 2개의 스트림이 필요하다.
스트림은 FIFO 구조로 되어있다.

자바에서 제공하는 스트림은 크게 2가지가 있다.

  • 바이트 단위로 데이터를 전송하는 바이트기반 스트림과 해당 스트림의 기능을 보완하기 위한 필터 스트림
  • 문자 단위로 데이터를 전송하는 문자기반 스트림과 해당 스트림의 기능을 보완하기 위한 필터 스트림

java i/o package

바이트 기반 스트림

바이트 단위로 데이터를 전송하는 스트림이다.
최상위 클래스는 InputStream과 OutputStream이고,
입출력 대상에 따라 이를 상속한 FileInputStream, ByteArrayInputStream, PipedInputStream 등이 있다.

InputStream, OutputStream

  • InputStream
    입력 소스마다 입력 방식이 다르므로, abstract 메서드로 read()를 제공하고 자식쪽에서 이를 구현하도록 했다.
    구현 클래스에서는 입력 소스로부터 1byte를 읽어오는 내용을 구현하면 된다
    이 같은 추상화로 인해 입출력의 방식이 달라져도 일관된 방법으로 입출력이 가능하다.

read()는 입력 소스로부터 가져온 바이트를 반환하는데, 바이트를 받음에도 불구하고 byte가 아닌 int를 사용하고 있음을 볼 수 있다.
1byte의 데이터를 받으러면 0~255 까지의 데이터를 담아야하는데, java는 unsigned 형이 없어서 java의 byte로는 양수로 127까지 밖에 담지 못한다
그리고 만약 java에 unsigned 형이 있어서 255까지 받을 수 있다고 하더라도, EOF 값(-1)을 받지 못하기 때문에(unsigned 로는 음수를 표현할 수 없으니까) 어찌됐든 byte 형태는 사용할 수 없다

그렇다면 short를 사용하면 되지, 왜 int를 사용하는가?
이는 대부분의 연산의 기본 정수형이 int 타입이기 때문이다(JVM 기본 정수형도 int 타입이다)
그러므로 사실상 int를 사용하는 것이 가장 연산이 빠르고(추가적으로 형변환을 하지 않아도 되기 때문에?), 대부분의 정수 관련 연산은 int 타입을 사용하므로 int를 택한것으로 보인다

참고 : https://stackoverflow.com/questions/21062744/why-does-inputstream-read-return-an-int-and-not-a-short

read(byte[] b), read(byte[] b, int off, int len)
1 byte씩 데이터를 가져오면 너무 느리므로, byte 배열을 전달하고 이 배열에 데이터를 담아오도록 한다
하나씩 전달하는 것 보다 바구니에 담아서 전달하는 것이 더 빠른것을 생각하면 된다.
반환되는 int 값은 읽은 바이트의 개수이다.

  • read(byte[] b): 배열 b의 크기만큼 데이터를 읽어와서 b에 저장한다.
  • read(byte[] b, int off, int len) : len의 크기만큼 데이터를 읽어와서 배열 b의 off 위치부터 저장한다.
  • OutputStream
    abstract 메서드로 write(int b) 를 제공한다. 인자 b는 출력소스로 보낼 데이터이다
    (flush의 경우 버퍼가 있는 출력 스트림의 경우에만 의미가 있다.)

write(byte[] b), write(byte[] b, int off, int len)

  • write(byte[] b) : 배열 b에 저장된 모든 내용을 출력소스에 쓴다
  • write(byte[] b, int off, int len) : 배열 b에 저장된 내용을 off 위치부터 len개 만큼 출력소스에 쓴다.

FileInputStream, FileOutputStream

파일에 입출력을 하기 위한 스트림으로, 실제 프로그래밍에서 많이 사용된다.

  • FileInputStream
  • FileOutputStream

아래는 file 복사의 간단한 예제이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void fileCopy(){
try(
FileInputStream fileInputStream = new FileInputStream("/Users/home/test.mov");
FileOutputStream fileOutputStream = new FileOutputStream("/Users/home/test_copy.mov")
){
byte[] temp = new byte[8192];

long start = System.currentTimeMillis();
while(fileInputStream.read(temp) > 0){
fileOutputStream.write(temp);
}
long end = System.currentTimeMillis();

System.out.println((end - start) / 1000.0);
} catch(IOException e){
e.printStackTrace();
}
}

ByteArrayInputStream, ByteArrayOutputStream

바이트 배열에 입출력 하기 위한 스트림이다.
입출력 대상이 메모리(배열은 java heap에 저장되므로) 이므로 close를 하지 않아줘도 된다는 특징이 있다.
자주 사용되진 않지만 read(byte[] b) 사용 시 발생할 수 있는 실수를 알아보기 위해 첨부하였음.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void readFromByte() {
byte[] src = {1,2,3,4,5,6,7,8,9,10};
byte[] dst;

byte[] temp = new byte[4];

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(src);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

try{
while(byteArrayInputStream.read(temp) > 0){
byteArrayOutputStream.write(temp);
}
} catch (IOException e){
e.printStackTrace();
}

dst = byteArrayOutputStream.toByteArray();

System.out.println(Arrays.toString(dst));
}

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 7, 8] 이 출력된다.
미자막 루프에서 9, 10 2byte만 읽었으므로 temp의 앞 2byte만 교체되는데,
write(temp)에서 temp의 모든 내용을 출력하도록 했기 때문이다.

1
2
3
4
5
6
7
8
int len;
try{
while((len = byteArrayInputStream.read(temp)) > 0){
byteArrayOutputStream.write(temp, 0, len);
}
} catch (IOException e){
e.printStackTrace();
}

이처럼 변경해줘야 한다.

바이트 기반 보조 스트림

바이트 기반 스트림의 기능을 보완(성능향상 및 기능추가)하기 위함.
자체적인 입출력 기능은 없다. 입출력 기능은 기반 스트림에 위임한다.
그러므로 생성자에서 항상 기반 스트림을 받는다.
부모 클래스는 FilterInputStream, FilterOutputStream이고,
자식으로는 BufferedInputStream/BufferedOutputStream, DataInputStream/DataOutputStream, SequenceInputStream, PrintStream 등이 있다.
보조 스트림을 close 하면 기반 스트림도 같이 close 된다.

FilterInputStream, FilterOutputStream

  • FilterInputStream
  • FilterOutputStream

위 두 클래스는 생성자가 protected이므로 직접 생성이 불가능하고, 상속을 통해 오버라이딩 되어야 한다.

데코레이터 패턴을 기반으로 디자인 된 클래스 구조이기 때문에, 여러 Filter 클래스들을 겹쳐서 사용할 수 있다!

1
DataInputStream dis = new DataInputStream(new BufferedInputStream(System.in));

BufferedInputStream, BufferedOutputStream

  • BufferedInputStream
  • BufferedOutputStream

생성할 때 지정한 버퍼의 크기만큼 입력소스로부터 읽고, 내부 버퍼에 저장해놓는다
예를 들어 2048의 크기로 BufferedInputStream 을 생성하고 read() 메서드를 호출하게 되면,
입력소스로부터 2048 byte 만큼 읽어와서 내부 배열에 저장하고, read() 메서드의 결과로는 2048 byte 중 1 byte만을 돌려주게 된다
read() 메서드로 내부 버퍼의 내용을 다 읽게 될 때 까지 입력소스에 추가적으로 접근하지 않게되고, 내부 버퍼의 내용을 다 읽으면 다시 입력소스에 접근해서 생성시 지정한 버퍼의 크기만큼 데이터를 읽어온다

BufferedInputStream 을 2048의 크기로 생성하고, read(byte[] b)에 100 크기의 배열을 전달하게 되면,
처음 read(byte[] b)를 호출하는 순간 입력소스로부터 2048 바이트를 읽어온 뒤 BufferedInputStream 에 있는 내부 배열에 저장해놓고, 거기서 100 byte만을 읽어서 반환해주게 된다
즉, read가 200번 호출될 떄 까지는 원본 소스에 직접 접근하는 일은 없을 것이다

BufferedOutputStream 또한 출력버퍼로 바로 전송하지 않고, 내부 버퍼에 데이터를 쌓아두었다가 버퍼의 내용이 가득차면 출력소스로 보낸다.
버퍼가 가득 차지 못해서 출력되지 못할 수도 있으니 항상 마지막에 flush()나 close()를 호출해서 버퍼를 비우도록 해야한다.

표준 입출력

표준 입출력이란 사용자가 별다른 입출
자바에서는 System 클래스의 in, out, err 를 통해 표준 입출력에 접근 가능하다
자바 어플리케이션의 실행과 동시에 자동적으로 생성되며, 내부적으로 BufferedInputStream, BufferedOutputStream 을 사용한다

아래는 표준 입출력을 이용해 문자를 입력받고 출력하는 소스이다

1
2
3
4
5
int input = 0;

while((input = System.in.read()) != -1) {
System.out.println((char)input);
}

표준 입출력의 끝을 나타내고 싶을 때는 Enter 키를 입력하거나, ^Z(맥에서는 ^D) 를 입력하면 된다
근데 여기서 조금 문제가 되는게, Enter 키를 입력할 경우 두 개의 특수문자 ₩r, ₩n 이 입력된것으로 간주된다는 점이다

₩r은 커서를 라인의 첫번째로 이동, ₩n은 커서를 다음 줄로 이동을 의미한다

이 때문에 Scanner의 nextInt() 같은 명령을 수행하게 되면 입력 버퍼에 ₩r₩n이 그대로 남아있게 되어, 이 후 문자열 입력 명령이 수행되어 버리는 문제점이 있다
그러므로 이를 방지하기 위해서 Scanner의 nextLine() 명령을 사용해주는 것이 좋다

nextLine()으로 받은 문자열에 Integer.parseInt 같은 명령을 사용하면 개행문자는 삭제되기 때문이다

문자 기반 스트림

java는 UTF16 인코딩을 사용하기 때문에 문자형을 2byte로 처리한다.
그러므로 바이트 기반 스트림으로 문자를 처리하기에는 어려움이 많다.
그래서 탄생한 것이 Reader, Writer이다. 이 또한 가장 최상위 클래스이다.
자식은 바이트 기반 스트림 자식에서 이름만 InputStream -> Reader, OutputStream -> Writer로 바꿔주면 된다.(FileReader/FileWriter, PipedReader/PipedWriter 등)

Reader, Writer

  • Reader
  • Writer

byte배열 대신 char배열을 사용한다는 것과, 추상메서드가 read(char[] cb, inf off, int len) 으로 달라졌다는게 차이점이다.
(프로그래밍 관점에서 이 추상메서드를 사용하는게 좀 더 바람직하기 때문이다)

FileReader, FileWriter

File로 부터 문자열을 입출력할 때 사용한다.

  • FileReader
  • FileWriter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void InputStream와Reader의문자처리() {
try(
FileInputStream fileInputStream = new FileInputStream("/Users/home/Desktop/대충 정리.md");
FileReader fileReader = new FileReader("/Users/home/Desktop/대충 정리.md")
){
int a;
while((a = fileInputStream.read()) != -1){
System.out.print((char)a);
}

System.out.println();

int b;
while((b = fileReader.read()) != -1){
System.out.print((char)b);
}

} catch(Exception e){
e.printStackTrace();
}
}

InputStream으로 읽었을 경우, 1byte만 읽어오므로 latin 인코딩이 아니면 문자가 깨진다.

문자 기반 보조 스트림

바이트 기반 보조 스트림과 용도는 동일하다.
FilterInputStream/FilterOutputStream 처럼 기반이 되는 클래스는 따로 없다.

BufferedReader, BufferedWriter

  • BufferedReader
  • BufferedWriter

버퍼를 이용해 입출력의 성능을 높여준다.
BufferedReader는 라인 단위로 읽어올 수 있는 readLine() 메서드를 제공하고,
BufferedWriter는 개행을 출력할 수 있는 newLine() 메서드를 제공한다.

InputStreamReader, OutputStreamWriter

  • InputStreamReader
  • OutputStreamWriter

이름에서 알 수 있듯이 바이트 기반 스트림을 문자 기반 스트림으로 변환해주는 역할을 한다.
인코딩을 직접 지정할 수도 있다. 인코딩을 지정하지 않으면 OS에서 기본으로 사용하는 인코딩을 사용할 것이다.

콘솔 입력을 받을 때 주로 이용한다.(이 외에도 많겠지?)

1
2
3
4
5
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(in);

// do something...
br.readLine();

참고 :

  • 남궁성, 『Java의 정석 2nd Edition』, 도우출판(2010)
  • 표준 입출력 : https://shoark7.github.io/programming/knowledge/what-is-standard-stream
Read more »
1…1819

JunYoung Park

182 posts
18 categories
344 tags
RSS
© 2020 JunYoung Park
Powered by Hexo v3.6.0
|
Theme – NexT.Muse v7.1.0