CREATE TABLE MEMBER
(
ID VARCHAR2(50),
PWD VARCHAR2(50),
NAME VARCHAR2(50),
GENDER CHAR(50),
AGE NUMBER,
BIRTHDAY CHAR(50),
PHONE CHAR(50),
REGDATE DATE
)
DROP TABLE MEMBER;
☝️ 테이블 명명규칙 ☝️
- 일반적으로 DB 테이블 또는 DB 칼럼명은 대문자 스네이크 기법을 사용한다.
- pf. 과거 컴퓨터 속도가 느리던 시절 아스키 코드의 앞자리에 해당하는 대문자코드를 선호
📌 인스턴스란? = database(데이터의 집합) = table space = 테이블이 위치하는 공간(디렉토리) 📌 계정은 왜 여러개 생성할까? 접속자마다 DB 관리권한이 다를 수 있다. 📌 원격으로 접속하는 컴퓨터에 대한 접속권한도 고려해야 한다. 📌 SQL명령어 - DDL, DML, DCL
| SQL (Structured Query Language)
- 의미 : DBMS와 소통하기 위한 구조화된 질의어
- 구분
데이터 정의어(DDL, Data Definition Langugae)
CREATE, DROP, ALTER, TRUNCATE
데이터 조작어(DML, Data Manipulation Language)
INSERT, UPDATE, DELETE, SELETE ( CRUD )
데이터 제어어(DCL, Data Control Language)
GRANT, REVOKE, TRANSACTION, COMMIT, ROLLBACK..
☝️ 데이터 정의어 : 테이블과 인덱스 구조를 관리
✌️ 데이터 조작어 : DDL를 통해 테이블 구조가 잡히면 그 안에 넣을 데이터를 CRUD하는 언어
// 아래와 같이 테이블스페이스를 만들지 않으면 System(관리자=root)와 같은 공간 공유
DEFAULT TABLE SPACE USERS 기본테이블 스페이스;
TEMPORARY TABLESPACE 임시테이블 스페이스;
B. 데이터베이스 계정 생성
📡 MariaDB
- 데이터베이스 계정 조회
show databases;
use mysql;
select User, Host, Password from user;
- 계정 생성하기
CREATE USER '계정명'@'호스트명' IDENTIFIED BY '비밀번호';
CREATE USER '계정명'@'localhost' IDENTIFIED BY '비밀번호'; // 로컬만 허용
CREATE USER '계정명'@'%' INDENTIFIED BY '비밀번호'; // 다른 ip도 허용
flush privileges;
rf. 원격 계정 접속 : 로그인 옵션에 [ -hip주소 ]를 추가한다.
// 원격 pc에서
mysql -h /*접속하려는 ip*/ -u /*계정명*/ -p /*비밀번호*/
GRANT ALL PRIVILEGES ON 테이블명.* TO '계정명'@'호스트명' IDENTIFIED BY '비밀번호'; // 특정 IP
GRANT ALL PRIVILEGES ON 테이블명.* TO '계정명'@'%' IDENTIFIED BY '비밀번호'; // 전체 IP 대상
FLUSH PRIVILEGES; // 권한 설정사항 FLUSH
> 관리자 아이디로 접속 후
> 권한 부여
grant all --- 모든 권한 부여(위험)
grant connect to 계정 --- db 연결 권한
grant resource to 계정 --- db 자원(table, sequence) 생성 권한
grant dba to 계정 --- 시스템 자원 무제한 사용, 다른 사용자 권한 조정 등 강력한 기능에 관한 권한
grant select on 테이블 to 계정 --- 조회(SELECT) 권한 부여
grant create view to 계정 --- 뷰 권한
> 권한 회수
revoke connect,resource from 계정
revoke select on 테이블 또는 뷰명 from 계정 -- 테이블 또는 뷰 권한 회수
import java.util.*;
class Customer implements Comparable<Customer>{
private int no;
private String name;
public Customer(int no, String name) {
this.no = no;
this.name = name;
}
@Override
public int compareTo(Customer o) {
return this.no - o.no;
}
public void print() {
System.out.println(this.no + "=" + this.name);
}
}
public class CollectionsSortTest {
public static void main(String[] args) {
// Collection에서 Sort를 하는 방식
// A. List를 정렬할 경우 - Collections.sort(List);
// : 오직 List의 경우에만 Collections.sort()를 쓸 수 있다.
// : Map의 Set의 경우 TreeMap, TreeSet을 쓰는 것이 좋다.
List<Integer> list = new ArrayList<>(Arrays.asList(4, 2, 5, 1, 2, 7));
Collections.sort(list);
System.out.println("list = " + list);
// B. 객체를 정렬할 경우 - 객체 내에서 Comparable인터페이스 구현
// (사실 아래의 경우에는 ArrayList를 쓰고 sort를 하기보다 PriorityQueue를 쓰는게 더 효율적이다)
ArrayList<Customer> customers = new ArrayList<>();
for (int i = 0; i < 5; i++) {
int no = (int)(Math.random() * 9999);
String name = "이름없음" + no;
customers.add(new Customer(no, name));
}
Collections.sort(customers);
for (Customer one : customers) {
one.print();
}
}
}
- 컴퓨터 OS환경에 맞춰서 설치하면 된다. (Package Type은 Zip으로 하는 것이 차후 세부설정을 하기 편하다)
(2) 다운되면 압축을 푼다.
(3) 관리자 권한 CMD에서 압축을 푼 경로로 이동 후 설치파일을 실행한다.
** 관리자 권한으로 열어주는 이유 : 보안 이슈로 서비스 등록을 처음할 때는 관리자 권한이 있어야만 가능
** 사용자용 CMD 관련 팁 : 파일탐색기 경로창에 "cmd" 입력 후 엔터를 치면 사용자용 cmd가 알아서 뜬다.
cd /* 압축 푼 폴더 경로 */
cd /bin
** 설치파일을 실행하기 전에, 명령어 옵션에 대한 정보를 --help로 확인하면 좋다.
mariadb-install-db.exe --help // 설치파일에 관한 정보 확인
mysql_install_db.exe Ver 1.00 for Windows
Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license
Usage: mysql_install_db.exe [OPTIONS]
OPTIONS:
-?, --help Display this help message and exit.
-d, --datadir=name Data directory of the new database
-S, --service=name Name of the Windows service
-p, --password=name Root password
-P, --port=# mysql port
-W, --socket=name named pipe name (if missing, it will be set the same as
service)
-D, --default-user Create default user
-R, --allow-remote-root-access
Allows remote access from network for user root
-N, --skip-networking
Do not use TCP connections, use pipe instead
-i, --innodb-page-size=#
Page size for innodb
-s, --silent Print less information
-o, --verbose-bootstrap
Include mysqld bootstrap output
-l, --large-pages Use large pages
-c, --config=name my.ini config template file
> 이 중에서 서비스명, Root password를 지정하는 옵션을 뽑아 명령어를 쓴다.
mariadb-install-db.exe -S 서비스명 -p 비밀번호 // 오류가 뜬다면 관리자권한CMD인지 확인하자
- [작업관리자] - [서비스]로 들어가면 서비스가 잘 등록된 걸 볼 수 있다.
(5) 실행하기
A. 서비스 직접 실행
sc start MariaDB
B. mysql.exe로 실행
- MariaDB 압축파일을 풀었던 경로의 bin폴더로 가서 mysql.exe 명령어 option을 확인한다.
mysql.exe --help
>> 엄청나게 옵션이 많은데 다른 건 필요없고 아래 두가지만 확인하면 된다.
-u, --user=name User for login if not current user.
-p, --password[=name]
Password to use when connecting to server. If password is
not given it's asked from the tty.
- 여기서 user는 root로 지정하고, pwd는 처음 설치할 때 지정한 root password를 넣어준다
(root는 서비스의 최상위 권한자이다.)
mysql.exe -u root -p
지정한RootPassword 입력
- 실행이 잘 되었는지 아래 코드를 통해 확인해본다.
select version(); - 버전명 확인
show databases; - 현재 계정에서 확인할 수 있는 등록된 DB들
use mysql; - 사용
show tables; - 테이블 목록 확인
select Host, User, Password from user; - 호스트(로컬or외부), 계정, 비밀번호 확인
2. MacOS 환경
(1) 패키지 매니저 "홈브루"를 설치한다.
- 맥의 터미널은 iterm2를 다운받아 터미널로 사용할 수 있다.(UI면에서 더 편리하다)
sudo -i //관리자 권한 실행
find / -name mariadb-secure* //파일 경로 확인
apt mariadb-secure-installation //설치파일 실행
(5) 서비스 실행하기
service mariadb start
| 원격으로 외부 DB서버에 접속하는 방법(윈도우 기준)
- 들어가기 전에
- 일반적으로 Root는 원격으로 접속할 수 없도록 되어 있다. - Shell을 통해 원격접속을 할 때에는 A. SSH를 통해서 DB컴퓨터에 접속 후 DBMS에 접근하거나 B. DB컴퓨터에서 외부IP의 포트 및 계정 권한을 허용한 경우 접근할 수 있다. - Shell이 아닌 IDE(IntelliJ, Eclipse등)에서 접속을 할 경우에도, 마찬가지로 DB컴퓨터에서 나의 접근을 허용해주어야만 접근이 가능하다. - 원격 접속하는 걸 연습할 때는 xShell을 쓰면 유용하다 (xShell 다운 바로가기) 현장에서는 PuTTY를 더 자주 쓴다.
사용자를 포함해서 개발자도 웹 서버를 통해 DB와 소통을 한다.
다만, 때에 따라 개발자가 DB서버에 직접적으로 접근을 해야하는 경우도 있는데
보안상 DB 서버는 따로 두는 경우가 많아서 이런 경우 DB 서버에 원격으로 접속하는 것이 필요하다.
원격으로 서버에 접속하기 위해서는 기본적으로 아래와 같은 정보들이 필요하다.
[ 원격으로 서버에 접속하기 위한 정보 ]
구분
설명
예시
주소(IP)
특정 시스템의 주소
- 내컴퓨터(로컬) : 127.0.01, localhost - 외부 : 168.xxx.xx.x
포트(PORT)
그 시스템 내 프로그램 위치
- 3306(MariaDB의 디폴트 포트) - 22(웹) ...
계정
서비스 계정
비밀번호
그 계정의 비밀번호
인스턴스
A. SSH를 통해 내 컴퓨터(윈도우)에서 DB컴퓨터(Linux)에 접속하기
(1) xShell을 설치하고 실행한다.
(2) IP주소를 확인하여 SSH를 통해 접속하기 (접근 시 비밀번호 작성)
ssh satcop@접속할ip주소
(3) 해당 ip 내에서 DBMS 접속하기
mysql -u root -p
B. DB컴퓨터(Linux)가 내 컴퓨터(윈도우) IP의 포트 및 계정 권한 허용
[ DB컴퓨터(Linux)에서 아래와 같이 작업이 필요하다] 1. DBMS 설정(my.cnf)에서 외부 IP도 DBMS 포트에 접속할 수 있게 설정하기 2. DBMS 계정을 만들어 외부IP와 연결하기
- 윈도우 cmd에서 아래와 같은 코드를 작성하여 바로 접근을 하려 하면 "can't connect to server on 'ip주소'"가 나타난다.
mysql -h 외부ip주소 -u root -p
- 이유는 DB를 담고 있는 PC에서 MariaDB 포트를 열어두지 않았기 때문이다.
- DB를 담고 있는 PC(linux)에서 아래의 명령어를 통해 현재 활성화된 포트를 확인해보면
netstat -anpt
- 아래와 같이 MariaDB 포트가 로컬컴퓨터에서만 열려 있는 걸 볼 수 있다.
* 로컬 컴퓨터 주소 : 127.0.0.1
* MariaDB 디폴트 포트 : 3306
Proto (생략) Local Address (생략)
tcp 127.0.0.1:3306
- DB를 담고 있는 PC(linux)의 MariaDB 설정파일(my.cnf)에서 외부ip 접근을 허가한다.
sudo -i //관리자권한 실행
find / -name my.cnf //my.cnf (설정파일) 경로 확인
cd /etc/mysql //해당 경로로 이동
vi my.cnf //my.cnf 실행
[mysqld] //설정파일에서
bind-address=0.0.0.0 //모든 외부ip접근 허가(또는 특정짓기)
service mariadb restart //MariaDB 재실행
- 허가를 하면 아래와 같이 모든 ip에서 3306포트가 열린 걸 볼 수 있다.
Proto (생략) Local Address (생략)
tcp 0.0.0.0:3306
- 이렇게만 하면 접속이 가능해질까? 그렇지 않다.
아래를 보면 알 수 있듯이 root 계정은 localhost에서만 사용가능하기 때문에 새롭게 계정을 만들어 주어야 한다.
use mysql
select Host, User from user;
+-----------------+-------------+
| Host | User |
+-----------------+-------------+
| 127.0.0.1 | root |
| ::1 | root |
| desktop-ivvs8i6 | root |
| localhost | mariadb.sys |
| localhost | root |
+-----------------+-------------+
- 새로운 계정을 만들어 주면서, 모든 ip에서 접속가능하게 설정하기
create user 'root'@'%' indentified by 비밀번호; // '%' : 어떤 호스트에서든 접속 가능
grant all privileges on root.* to 'root'@'%' indentified by 비밀번호; // 권한설정
flush privileges;
| 사용된 CLI 명령어 정리
windows CLI 명령어
cd
경로 이동
cd ~ : desktop 폴더로 이동 cd .. : 상위 경로 cd . : 현재 경로 [tab] : 일부 작성 후 [tab]을 누르면 유사파일명 자동입력
dir
현재 경로의 폴더(디렉토리)와 파일 목록을 보여줌
/w : 목록이 3개씩 표시
cls
(=clear) 화면을 지워준다.
--help
명령어 + --help를 쓰면 명령어 실행 전에 설명을 띄워준다
sc
sc start 서비스명 : 등록된 서비스 시작 sc restart 서비스명 : 등록된 서비스 다시시작 sc stop 서비스명 : 등록된 서비스 멈춤
[사전] Generic = 일반적이다
[정의] 하나의 코드로 여러 타입을 동시에 처리하는 기술
1. 타입 변수 표기법
- 제네릭에 지정하는 타입 변수 이름은 지정하는 사람의 마음이지만,
적어도 그 타입 이름을 사용하는 클래스 또는 인터페이스 내에서는 통일성있게 사용하게 한다.
- 일반적으로 변수명과의 구분을 위해 한 개의 대문자로 표시하는 것이 관례이다.
- 타입 매개 변수는 기초 자료형으로 객체화될 수 없다.
T
Type
E
Element(요소)
N
Number
K
Key
V
Value
S,U,V.....
2. 제네릭 클래스
- 최상위 클래스 Object를 사용하면 어떤 타입의 인스턴스건 참조할 수 있다. (업캐스팅, 다형성)
- 이러한 원리를 이용해서 클래스를 작성할때 데이터 타입을 Object로 하면 여러 타입의 데이터를 담을 수 있게 된다.
public class Box {
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
- 다만, 이럴 경우, 아래와 같이 객체 안의 데이터를 가져올 때 형변환이 불가피하게 된다.
import LibraryPractice.MyClass.Box;
public class GenericTest {
public static void main(String[] args) {
Box box = new Box();
box.setData(123);
int x = (Integer)box.getData(); //cast(형변환)필요
}
}
- 이럴 때 제네릭을 사용하여 미리 타입을 지정한다면 사용시의 형변환의 번거러움을 줄일 수 있게 된다.
public class Box<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
import LibraryPractice.MyClass.Box;
public class GenericTest {
public static void main(String[] args) {
Box<Integer> box = new Box<>();
box.setData(123);
int x2 = box.getData(); // 형변환 필요 없음
}
}
- 만약에 여러 개의 맴버변수에 서로다른 제네릭을 지정해야하는 경우(ex. key와 value가 타입이 다른 경우), 아래와 같이 작성할 수 있다.
public class Box3<K, V> {
private K key;
private V value;
public void setData(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey(V key) {
return this.key;
}
public V getValue(K key) {
return this.value;
}
}
3. 제네릭 메소드
- 제네릭 메소드의 경우 아래와 같이 반환타입 앞에 <E> 와 같은 형식으로 작성을 해준다.
public class Basket<T> {
T data;
public Basket(T data){
this.data = data;
}
// 외부 배열 print
public <E> void print(E[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
- 만약에 타입 변수를 특정 타입(예를 들어 String)으로 제한하고 싶은 경우, 아래와 같이 사용할 수 있다.
DB전문가의 부족 전산화 비용의 증가 대용량 디스크로의 집중적인 접근으로 과부하 발생 파일의 백업과 회복이 어려움 시스템이 복잡
1-1. 파일 시스템의 문제점
데이터의 종속성
응용 프로그램과 데이터간의 상호 의존관계
* 프로그램 명세에는 보조기억장치에 들어가는 프로그램의 파일 구조 및 접근방식에 대해서 명시되어 있다. * 프로그램은 이러한 명세에 따라서 동작하기 때문에 * 만약 데이터의 구성 방법 및 접근 방법을 수정해야할 경우, 이에 맞춰 프로그램도 수정되어야 한다.
데이터의 중복성
한 시스템 내에 내용이 같은 데이터가 중복되게 저장 관리되는 것
* 현실 세계에서는 여러 개의 프로그램이 하나의 데이터를 동시에 사용하는 경우가 있다. * 예를 들어, 대학교 도서관에서 책을 대여 기록을 담는 프로그램A가 있고, 대학교 재학생들의 명단을 담고 있는 프로그램 B가 있다고 하면 프로그램 A와 프로그램B 모두 학교 재학생들의 이름과 학번을 데이터로 사용할 것이다. * 이와 같이 여러 개의 프로그램이 같은 데이터를 중복해서 저장/관리할 경우, 다수의 프로그램들을 시간차를 두고 수정하는 작업이 필요할 것이다. (동시 수정x)
1-2. DBMS의 논리적 독립성 / 물리적 독립성
논리적 독립성
응용 프로그램과 DB를 독립 시킴으로써, 데이터의 논리적 구조를 변경시키더라도 응용 프로그램은 변경되지 않음
물리적 독립성
응용 프로그램과 보조기억장치같은 물리적 장치를 독립시킴으로써, DBMS의 성능 향상을 위해 물리적 장치를 추가해도 프로그램에는 영향을 주지 않음.
| DB와 DBMS, RDMBS
데이터베이스
데이터의 저장소
DBMS(데이터베이스 관리시스템)
데이터베이스를 운영하고 관리하는 시스템
- DB의 특징
실시간 접근성
실시간으로 사용자의 요청이 있을 때 수 초 내로 결과를 제공한다
계속적인 변화
데이터베이스의 내용은 어느 한 순간의 상태이나, 데이터의 값은 시간에 따라 항상 변화한다.
동시 공유
데이터베이스는 서로 다른 업무 또는 사용자에게 동시 공유된다.
* 동시(= 병행, concurrent) : 데이터베이스에 접근하는 프로그램이 여러 개임을 뜻한다.
내용에 따른 참조
데이터베이스에 저장된 데이터는 데이터의 물리적인 위치가 아니라 데이터 값에 따라 참조한다.
데이터 독립성
데이터의 논리적 구조를 변경시켜도 응용 프로그램은 변경되지 않는다.
* 응용 프로그램과 데이터베이스를 독립시킨 것을 의미한다.(데이터 종속성 해결)
- RDBMS
RDBMS(관계형 데이터베이스)
정형화된 데이터를 관리하는 시스템으로, 가장 많이 사용되는 DBMS이다.
2-1. RDMS의 구성
- 테이블의 행과 열로 구성된다.
열(columm), 필드(field),속성(attribute) ↓
행(row), 튜플(tuple), ----> 레코드(record)
2-2. RDMS의 종류
- 종류 : 오라클, MySQL, MariaDB, Microsoft SQL Sever, PostgreSQL, IBM DB2....
- atZone()을 넣는 것은 시간대를 구분하기 위해서 넣는 것이다. (아래는 컴퓨터의 시간대 적용)
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
public class LocalDateTest {
public static void main(String[] args) {
// A. Date와 LocalDateTime으로 현재시각 구하기
// (1) Date 타입의 날짜
Date date = Calendar.getInstance().getTime();
// (2) LocalDateTime 타입의 날짜
LocalDateTime localDateTime = LocalDateTime.now();
// B. LocalDateTime -> Date
Date localToDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
// C. Date -> LocalDateTime
LocalDateTime dateToLocal = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
}
- 아래 표는 위 링크에서 소개하는 java.lang의 인터페이스와 클래스 중 자주 사용할만한 것만 일부를 가져온 표이다.
Interface
Class
Comparable
Object
Cloneable
Math
Runnable
Process
Readable
Thread
Thread.UncaughtExceptionHandler
String
Iterable
System
...
Character, Integer, Long....
| Object
메소드
내용
clone
현재 객체를 복사한 객체를 반환
equals
참조되는 주소값을 비교 (* 주의 : null값은 비교할 수 없다)
getClass
현재 객체타입의 런타임 객체를 반환
hashCode
객체의 해쉬코드를 반환
toString
java.lang.Object@해쉬코드를 반환 오버라이딩하여 주로 사용
| String
1. String은 힙에 저장되며, 두 가지 방식으로 저장된다.
종류
저장 영역
문자열 리터럴
Heap 영역 내 "String Constant Pool"에 생성
문자열 객체
Heap 영역 내 별도의 객체로 생성
// s1과 s2는 같은 곳을 가리킨다.
String s1 = "Spring";
String s2 = "Spring";
// s3와 s4는 다른 곳을 가리킨다.
String s3 = new String("Spring");
String s4 = new String("Spring");
2. String 메소드들
contains
trim
compareTo
toString
indexOf lastIndexOf
substring
equals equalsIgnoreCase
toLowerCase toUpperCase
startsWith endsWith
split
valueOf
toCharArray
length
charAt
replace replaceAll
concat *
* concat : 문자열을 덧붙이는 메소드로 +를 사용하는 것과 동일하다.(자바에서는 잘 사용하지 않는다)
pf. mariaDB, mysql에서 문자열을 조합할 때 concat('a', 'b', 'c')를 사용한다. (Oracle은 ||를 통해 문자열을 조합)
> 확장된 아스키 코드와 유니코드의 역사 - 유니코드 이전에는 각 나라별로 확장된 아스키코드(Extended Ascii)를 사용했다.(아스키 코드의 문자는 1byte) - 나라별로 인코딩 방식이 상이하다보니 전 세계의 데이터가 모두 다르게 읽히는 문제가 발생했다. - 이를 해결하고자 유니코드를 만들었다. (유니코드의 문자는 2byte) - 하지만 유니코드는 영문자의 경우 사용하는 메모리 공간이 1byte -> 2byte가 된다는 단점이 있어, 영어권 같은 나라에서는 유니코드를 사용하는 것이 큰 메리트가 되지 않았다. - 그래서 나온 것이 UTF-8. UTF-8은 가변 길이를 제공하여, 각 나라의 언어별로 최적화된 문자 크기를 할당한다. ex. 영문자의 경우 1byte, 한글의 경우 3byte
> EUC-KR과 UTF-8. - EUC-KR은 유니코드가 나오기 이전에 사용하던 한글 인코딩 방식(Extended Ascii)이다. * 자바의 KSC5601기법은 곧 EUC-KR 인코딩을 말하며, 윈도우OS에서 MS949와 동일한 기법을 사용한다. * 윈도우의 기본 인코딩 방식은 MS949다. 이클립스를 키면 늘 이 인코딩이 디폴트로 설정되어 있어 UTF-8로 바꾸는게 필요 - EUC-KR을 사용하면 온전한 한글을 만들기가 어렵다. (ex.'뷁' 이라는 문자가 없음) - 당연한 얘기지만, 인코딩 방식이 EUC-KR일 때, 디코딩을 UTF-8로 하면 문자가 깨지게 된다. (제대로 인식을 못함) - 그렇기에 항상 에디터를 쓸 때 사전에 인코딩 방식을 UTF-8로 설정을 해두는 것이다.
4. 자바에서 인코딩을 변환하는 메소드 getBytes()
import java.nio.charset.Charset;
public class StringTest {
public static void main(String[] args) {
String sample = "행복한개발자가되자"; // 설정에서 UTF-8 방식으로 바꾼 상태
try {
System.out.println("Default Encoding : " + Charset.defaultCharset());
byte[] default_encoding = sample.getBytes();
byte[] euc_kr = sample.getBytes("EUC-KR");
System.out.println(">> default decoding : " + new String(default_encoding));
System.out.println(">> EUC-KR decoding : " + new String(euc_kr));
} catch (Exception e) {
System.out.println("Encoding Error");
System.out.println(e.getMessage());
}
}
}
String 불변의 법칙이란?
- 문자열 할당 후 수정이 있을 때, 기존 메모리에서 데이터를 수정하지 않고,
- 새로운 메모리 공간을 할당하여 그곳의 주소를 가리키도록 하는 방식
>> 문자열의 변경(추가/삭제/수정)이 빈번한 경우, 단순 덧셈 시 시간/공간복잡도에 영향을 준다.
- 앞서 문자열 리터럴의 경우, 힙의 string constant pool에 저장되며,
서로 다른 참조변수(ex. s1, s2)가 같은 메모리 공간을 참조한다고 했다.
- 문자열에 수정이 있어날 때마다 메모리 공간을 재설정해주지 않으면 s1에서 수정한게 s2에 영향을 미치기 때문에
String에 수정사항이 발생할 때마다 매번 메모리를 재할당해준다. (문자열 불변 법칙)
- 문자열 불변 법칙의 특성 때문에 많은 문자열 데이터를 수정/삭제해야하는 경우, 단순 연산을 사용하면(ex. +) 시간복잡도/공간복잡도 면에서 효율성이 떨어질 수 있다.
- 그래서 사용하는 게, StringBuilder/StringBuffer다.
public class StringBufferTest {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("hello");
sb.append(System.lineSeparator());
sb.append("this is StringBuffer!");
System.out.println(sb);
}
}
>> 결과
hello
this is StringBuffer!
2. StringBuilder와 StringBuffer의 차이점
StringBuilder
StringBuffer
공통점
char[] 배열로 이루어진 버퍼로, String 추가/수정/삭제 연산시 시간/공간 사용을 절약
* 디폴트로 16의 사이즈를 가지며 추가적으로 늘릴 수 있다.
차이점
Thread-safe
Thread-safe x
public synchronized StringBuffer append(String str) {
/*생략*/
}
public AbstractStringBuilder append(Object obj) {
/*생략*/
}
| Math
- 수학 계산에 사용되는 클래스.
- 아래는 Math를 사용하여 소수점 n번째까지 반올림/올림/버림을 하는 법과,
- 두 좌표 간의 거리를 구하는 법을 작성한 코드이다.
public class MathTest {
public static void main(String[] args) {
// 1. 반올림, 올림, 버림
double d = 123.2364;
int n = 3; // 소수점 자릿수
// 소수점 n번째까지 반올림
double d1 = Math.round(d * Math.pow(10, n)) / Math.pow(10, n);
System.out.println(d1);
// 소수점 n번째까지 올림
n = 2;
double d2 = Math.ceil(d * Math.pow(10, n)) / Math.pow(10, n);
System.out.println(d2);
// 소수점 n번째까지 버림
double d3 = Math.floor(d * Math.pow(10, n)) / Math.pow(10, n);
System.out.println(d3);
// -----------------------------------------------------------
// 2. 피타고라스의 법칙을 활용하여 두 좌표의 거리 구하기
int[][] pos = {{2, 1}, {5, 5}};
int xDistance = pos[1][0] - pos[0][0];
int yDistance = pos[1][1] - pos[0][1];
double distance = Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
System.out.println(distance);
}
}
C언어 컴파일 시간에 변수의 데이터타입이 결정 ex. int *p; -- 미리 데이터타입 지정
javascript 런타임에 값에 따라 변수의 데이터타입 결정 ex. var c = 1; -- 미리 데이터타입 지정x ex. Person p = new Person(), p.method() -- p 생성 전엔 p가 가리키는 곳 어딘지 알 수 없음
장단점
컴파일 시간에 많은 정보가 확정되어있어 실행 효율↑ 값이 변하지 않아서 안정적
런타임에 자유롭게 성격이 바뀌므로 적응성↑ 메모리 공간의 낭비, 느린 속도
- 자바에서는 static 메소드의 경우 정적바인딩을, 일반 메소드의 경우 동적바인딩을 사용한다.
| 다형성의 장단점
장점
유지보수가 쉽다
하나의 타입으로 여러 가지의 객체를 관리할 수 있어 유지보수가 쉽다.
재사용성이 증가
객체를 재사용하기가 쉬워 개발자의 코드 재사용성이 높아진다.
느슨한 결합
부모타입으로 자식인스턴스를 참조할 경우 클래스간 의존성이 낮아지며, 확장성이 높고, 결합도가 낮아진다.
단점
프로그램의 가독성이 낮아짐
실제 인스턴스가 무엇인지 instanceof가 없으면 알기 어렵다
디버깅의 어려움
-
| 다형성 구현 - 오버로딩과 오버라이딩
다형성을 개념적으로, 구현적으로 나누기도 하는 걸 봤었는데, 결국 핵심은 오버로딩과 오버라이딩이었다.
여기에 어떤 분들은 추가적으로 함수형 인터페이스를 함께 소개하기도 해서, 아래와 같이 넣어보았다.
오버로딩(중복 정의)
같은 이름의 메소드를 매개변수를 다르게 해서 적재
오버라이딩(재정의)
똑같은 선언부의 메소드를 다른 내용으로 재정의
함수형 인터페이스
한 개의 추상 메소드를 가지고 있는 인터페이스
1. 오버로딩
오버로딩의 조건
1. 메소드의 이름이 같아야 한다. 2. 매개 변수의 갯수 또는 타입이 달라야한다. 3. 반환 타입이 다른 것은 관계 없다.
- 오버로딩의 대표적인 예로, PrintStream의 println()이 있다.
public class PrintStream extends FilterOutputStream
implements Appendable, Closeable
{
public void println() {
newLine();
}
public void println(boolean x) {
synchronized (this) {
print(x);
newLine();
}
}
public void println(char x) {
synchronized (this) {
print(x);
newLine();
}
}
public void println(int x) {
synchronized (this) {
print(x);
newLine();
}
}
public void println(long x) {
synchronized (this) {
print(x);
newLine();
}
}
}
2. 오버라이딩
오버라이딩의 조건
1. 상위 클래스를 상속 또는 구현해야한다. 2. 물려 받은 메소드를 구현할 때는 선언부가 동일해야 한다. 3. 하위 클래스는 상위 클래스보다 접근제어자가 더 공개적이거나 같아야 한다. (ex. 상위가 public -> 하위도 public, 상위가 protected -> 하위는 protected or public) 4. final과 private인 메소드는 오버라이딩이 불가하다.
[1] 상위 클래스를 상속하고 메소드를 오버라이딩하는 경우
- 지난번 상속에서 사용한 Vehicle를 조금 변형해서 '움직인다(move)' 기능만 만들어 보았다.
- Vehicle을 상속하는 Car, Train, Airplane은 move를 오버라이딩하여 저마다의 소리를 낸다.
package polymorphism;
class Vehicle {
public void move() {
System.out.println("unknown");
}
}
class Car extends Vehicle{
public void move() {
System.out.println("부릉부릉");
}
}
class Train extends Vehicle{
public void move() {
System.out.println("칙칙폭폭");
}
}
class Airplane extends Vehicle{
public void move() {
System.out.println("위잉위잉");
}
}
- 메인 함수에서의 출력
각 하위 인스턴스들을 부모 참조변수인 Vehicle로 통일하여 참조하는 걸 아래에서 볼 수 있다.
이렇게 부모 참조변수로 자식 인스턴스를 참조하는 것을 업캐스팅이라고 한다.
업캐스팅
의미
상위 객체 참조 변수로 하위 인스턴스를 참조하는 것
제약사항
상위 객체 참조 변수를 쓰면, 하위 객체의 맴버를 참조할 수 없다. (= 하위 객체 변수를 car.xxx식으로 쓸 수 없다)
ex. Vehicle을 상속받은 Car객체엔 Car만의 맴버 "gasDisplacement"와 "drive()"가 있다. Vehicle car = new Car(); <-- 이 경우에, car.gasDisplaement와 car.drive() 를 쓸 수가 없다
package polymorphism;
public class Test {
public static void main(String[] args) {
Vehicle car = new Car();
car.move();
System.out.println();
Vehicle train = new Train();
train.move();
System.out.println();
Vehicle air = new Airplane();
air.move();
System.out.println();
}
}
부릉부릉
칙칙폭폭
위잉위잉
[2] 인터페이스를 사용하는 경우
- 위와 같이만 만들 경우에는 사실 다형성의 장점인, '느슨한 결합'을 온전히 지키기 어렵다.
- 왜냐하면 여전히 부모의 객체를 수정할 때, 자식에게 미치는 영향이 있기 때문에 (객체 간 의존성 ↑) 자식 입장에서 부모의 정보를 잘 알고 있어야 하기 때문이다.
- 그렇기에 공통 기능, '움직인다(move)'를 뽑아 movable인터페이스를 만드는 것이 느슨한 결합을 만들기에 더 좋다.
package polymorphism;
public interface Movable {
public abstract void move();
}
class Car implements Movable{
@Override
public void move() {
System.out.println("부릉부릉");
}
}
class Train implements Movable{
@Override
public void move() {
System.out.println("칙칙폭폭");
}
}
class Airplane implements Movable{
@Override
public void move() {
System.out.println("위이위잉");
}
}
- 메인 함수에서의 출력
package polymorphism;
import java.util.Arrays;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Movable> movables = Arrays.asList(new Car(), new Airplane(), new Train());
for (Movable one : movables) {
one.move();
}
}
}
3. 함수형 인터페이스
함수형 인터페이스는 한 개의 추상 메소드를 가진 인터페이스를 말한다.
함수형 인터페이스는 람다식을 통해 간편하게 사용할 수 있는데,
앞서 쓴 movable 인터페이스를 람다식으로 바꿔 쓰면 아래와 같다.
// 함수형 인터페이스 사용
public class Test {
public static void main(String[] args) {
List<Movable> movables = Arrays.asList(new Car(), () -> System.out.println("다그닥다그닥"));
for (Movable one : movables) {
one.move();
}
}
}
4. 객체 타입을 확인하는 instanceof, 지양해야할까?
instanceof 를 통해 부모참조변수가 참조하는 인스턴스가 무엇인지 알 수 있다.
그런데 instanceof를 사용하는 것을 지양하라는 이야기가 있다. 왜 그런 걸까?
아래의 코드를 보면, instanceof를 사용해서 실제 참조하는 인스턴스가 무엇인지 볼 수 있다.
class Test {
public static void main(String[] args) {
List<Movable> movables = Arrays.asList(new Car(), new Airplane(), new Train());
for (Movable one : movables) {
if (one instanceof Car) {
System.out.println("This is a Car");
one.move();
} else if (one instanceof Airplane) {
System.out.println("This is an Airplane");
one.move();
} else if (one instanceof Train) {
System.out.println("This is a Train");
one.move();
}
}
}
}
이렇게 실제 인스턴스를 보게 주게 되면 외부의 객체에 정보를 고스란히 노출하는 것이기 때문에, 캡슐화가 깨지게 된다.
캡슐화란, 객체가 가진 상태나 행위를 다른 이가 사용하거나 보지 못하도록 숨기는 것
[출처] https://tecoble.techcourse.co.kr/post/2021-04-26-instanceof/