Java 정리 29
21 Dec 2018
Reading time ~6 minutes
Java 정리 29 - IO Stream(3), 실행가능한 JAR파일 만들기
파일복사
// 복사될 파일 경로명을 설정하는 예제
public class Test {
public static void main(String[] args) {
String s = "C:/Users/owner/youngRepositories/SSangYoung/dev/temp/java_read.txt";
StringBuilder sb = new StringBuilder(s);
// 파일명에 '.'이 여러개 나올 수 있으므로 파일 확장자는 마지막 .뒤에 문자열이다.
// 따라서 lastIndexOf를 사용해서 확장자를 잘라내야 함.
// StringBuilder에 삽입하는 insert method로 .있는 부분부터 "_bak"을 추가함
System.out.println(sb.insert(s.lastIndexOf("."),"_bak"));
}
}
-
FileInputStream, FileOutputStream으로 파일 복사 구현
- hdd에서 데이터를 읽을때 access arm이 움직여 헤드로 데이터를 읽음
-
read 메서드는 헤드가 얼마나 많이 읽을 수 있는지와는 상관없이 1byte씩 읽어온다.
- 오버로드된 read 메서드를 보면 파라미터로 byte배열을 주면 한번에 배열의 크기만큼 읽어들일 수 있음
byte[] b = new byte[512];
int len = 0;
// 읽어들일 때 byte배열의 크기만큼 데이터가 없을 수 있다.
len = fis.read(b); // int로 읽어들인 byte의 수를 반환
// byte배열의 크기만큼 데이터가 존재하지 않을 수 있기 때문에 읽어들인 길이만큼만 출력
while(len = fis.read(b) != -1) {
fos.write(b, 0, len); // 0번부터 len까지 출력
}
// 파일복사 예제
@SuppressWarnings("serial")
public class FileCopy extends JFrame implements ActionListener {
private JButton jb;
private JProgressBar jpb;
public FileCopy() {
super("파일 복사");
jb = new JButton("파일 선택");
jpb = new JProgressBar(0, 100);
jpb.setString("진척도");
jpb.setStringPainted(true);
jpb.setValue(50);
JPanel jp = new JPanel();
jp.add(jb);
add("Center",jp);
add("South",jpb);
jb.addActionListener(this);
setBounds(100, 100, 500, 200);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
@Override
public void actionPerformed(ActionEvent e) {
FileDialog fd = new FileDialog(this, "선택", FileDialog.LOAD);
fd.setVisible(true);
String path = fd.getDirectory();
String name = fd.getFile();
if (path != null) { // 파일 선택시
File file = new File(path+name);
try {
copy(file);
JOptionPane.showMessageDialog(this, file+" 복사 성공.");
} catch (FileNotFoundException fnfe) {
JOptionPane.showMessageDialog(this, "파일이 존재하지 않습니다.");
fnfe.printStackTrace();
} catch (IOException ie) {
JOptionPane.showMessageDialog(this, "입출력 작업에 문제가 발생.");
ie.printStackTrace();
}
}
}
public void copy(File file) throws FileNotFoundException, IOException {
int selectValue = JOptionPane.showConfirmDialog(this, "파일을 복사하시겠습니까?");
switch(selectValue) {
case JOptionPane.OK_OPTION :
StringBuilder copyFileName = new StringBuilder(file.getAbsolutePath());
copyFileName.insert(copyFileName.lastIndexOf("."), "_bak");
FileInputStream fis = null;
FileOutputStream fos = null;
try {
// 원본 파일에 스트림 연결
fis = new FileInputStream(file);
fos = new FileOutputStream(copyFileName.toString());
// 파일과 연결된 스트림에서 값을 얻는다.
// HDD가 읽어들이는 크기를 무시하고 1byte씩 읽어들여 사용
int temp = 0;
long fileLen = file.length();
int i = 0;
while( (temp = fis.read()) != -1 ) {
// 읽어 들인 내용을 _bak가 붙은 파일을 생성하여 출력(복사)
fos.write(temp);
fos.flush();
jpb.setValue((int)(i/(double)fileLen*100));
// Thread를 사용해야 프로그레스바 구현가능, 여기선 값만 찍음
System.out.println(jpb.getValue());
i++;
}
/*// HDD가 읽어들이는 크기를 그대로 사용
byte[] temp = new byte[512];
int len = 0;
while ((len = fis.read(temp)) != -1) {
fos.write(temp,0,len);
fos.flush();
}*/
} finally {
if (fis != null) fis.close();
if (fos != null) fos.close();
}
}
}
public static void main(String[] args) {
new FileCopy();
}
}
ObjectStream
-
객체(instance)는 Stream을 타고 JVM 밖으로 나갈 수 없다.
- 객체의 주소는 4byte이기는 하나 객체 자체의 크기를 알 수 없기 때문에 쪼갤 수 없다.
-
기본형 데이터형은 Stream을 타고 JVM 밖으로 나갈 수 있다.
- 크기를 알 수 있기 때문에 쪼개져서 나갈 수 있다.
-
8bit Stream
-
ObjectInputStream
- JVM 외부 객체를 읽기
-
ObjectOutputStream
- JVM 외부에 객체를 내보내기
-
ObjectInputStream
- 객체가 직렬화(Serialization)되면 JVM 외부로 나갈 수 있다.
- 객체는 Serializable이란 인터페이스를 구현하면 직렬화 될 수 있다.
- 직렬화된 객체는 instanceof를 사용해서 판별
FileOutputStream fos = new FileOutputStream(파일객체);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(직렬화가된객체); // 스트림에 객체를 기록
oos.flush(); // 목적지로 분출
-
객체를 byte단위의 일정크기로 자름는 것이 Marshalling
- byte를 직렬화(일렬로) stream에 내보냄, Stream을 타고 데이터는 나뉜 byte로 출력되어 저장됨
- 직렬화를 방지하려면 변수앞에 접근지정자로 transient를 붙이면 그 변수는 JVM 외부로 나갈 수 없게된다.
transient String s; //
- 파일을 읽어와 객체로 다시 만들어 사용하는 것이 Unmarshaling
FileInputStream fis = new FileInputStream("파일명");
ObjectInputStream ois = new ObjectInputStream(fis);
B b = (B)ois.readObject();
// 데이터를 가지고 있는 클래스로 직렬화 대상 클래스
public class UserData implements Serializable {
private int age;
private double weight;
private String name;
public UserData() {
}
public UserData(int age, double weight, String name) {
super();
this.age = age;
this.weight = weight;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "UserData [age=" + age + ", weight=" + weight + ", name=" + name + "]";
}
}
// UserData를 객체로 생성, 출력스트림을 통해 파일로 내보내고 읽어들이기
public class UseObjectStream {
public void useObjectOutputStream() throws IOException {
ObjectOutputStream oos = null;
try {
File file = new File("C:/Users/owner/youngRepositories/SSangYoung/dev/temp/obj_data.dat");
// 객체(JVM)를 직렬화하여 목적지 (HDD File)로 내보내는 스트림
oos = new ObjectOutputStream(new FileOutputStream(file));
// 객체는 직렬화가 되지 않으면 스트림으로 내보낼 수 없다.
UserData ud = new UserData(26,70.1,"이재현");
oos.writeObject(ud); // 객체를 직렬화하여 스트림에 쓴다.
oos.flush(); // 스트림에 기록된 내용을 목적지로 분출한다.
System.out.println("객체를 파일로 기록!");
} finally {
if (oos != null) { oos.close(); }
}
}
public void useObjectInputStream() throws IOException, ClassNotFoundException {
// 객체를 읽기위한 스트림 열기
ObjectInputStream ois = null;
try {
File file = new File("C:/Users/owner/youngRepositories/SSangYoung/dev/temp/obj_data.dat");
ois = new ObjectInputStream(new FileInputStream(file));
UserData ud = (UserData)ois.readObject();
System.out.println(ud.toString());
} finally {
if (ois != null) ois.close();
}
}
public static void main(String[] args) {
UseObjectStream uos = new UseObjectStream();
try {
uos.useObjectOutputStream();
} catch (IOException ie) {
ie.printStackTrace();
}
System.out.println("=================================================");
try {
uos.useObjectInputStream();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// transient로 직렬화를 막기
public class UserData implements Serializable {
private int age;
private transient double weight;
private transient String name;
serialVersionUID
- JVM 밖으로 내보내고 들어오는 instance는 코드의 변형 또는 가공이 될 수 있기 때문에 내보냈던 코드를 검증하기 위해 serialVersionUID를 사용
- serialVersionUID는 클래스의 hashcode로 사용
- 대표적인 예가 공인인증서
- serialVersionUID 추가 후 파일 내보내고 serialVersionUID값변경 후 다시 읽어들이려고하면 에러발생
// 클래스 객체를 JVM 밖으로 내보낸다면 serial Version ID를 생성해준다.
// 클래스를 안내보낸다면 SuppressWarning처리를 해주면 됨
@SuppressWarnings("serial")
public class UserData implements Serializable {
실행가능한 jar파일 만들기
- 프로젝트 선택 - Export - Runnable JAR File Export
- Launch configuration : main method가진 클래스 선택
- Export destination : 파일을 내보낼 위치, 파일명 지정
-
실행을 위해 bat파일 생성
- java_memo_env.bat (기존 env.bat 복사)
- java_memo_run.bat (기존 eclipse.bat 복사)
# java_memo_env
rem path - 특정프로그램(javac.exe, java.exe)를 설치된 경로에 상관없이 사용하기위한 path
rem xxx_home - 프로그램끼리 경로를 참조하기 위해 설정하는 path
set dev_home=C:\Users\owner\youngRepositories\SSangYoung\dev
set java_home=%dev_home%\Java\jdk1.8.0_191
set path=%java_home%\bin;
# java_memo_run
rem path를 사용하기위해 path가 설정된 파일을 호출
call java_memo_env.bat
rem 설정된 경로에서 필요한 실행파일을 실행
rem java -jar 실행가능한 jar의 이름
java -jar java_memo.jar
메모장 saveFontConfig 수정
- 기존 fontConfig를 내보냈던걸 ObjectOutputStream으로 구현
- Font가 Serializable을 구현한 클래스이므로 객체를 밖으로 빼낼 수 있다.
- 사용자가 악의적인 목적을가지고 변경하기 힘들어진다.
private void saveFontConfig() throws IOException {
ObjectOutputStream oos = null;
try {
Font fontTemp = mf.getLblPreview().getFont();
oos = new ObjectOutputStream(new FileOutputStream(
"C:/Users/owner/youngRepositories/SSangYoung/dev/temp/fontConfig.dat"));
oos.writeObject(fontTemp);
oos.flush();
} finally {
if (oos != null) oos.close();
}
}
public Font readFontConfig() throws IOException, ClassNotFoundException {
Font fontConfig = null;
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(
"C:/Users/owner/youngRepositories/SSangYoung/dev/temp/fontConfig.dat"));
fontConfig = (Font)ois.readObject();
} finally {
if (ois != null) ois.close();
}
return fontConfig;
}