前言
本篇文章仅记录自己学习Java的内容。作为小白知识浅薄等原因可能有错误的内容,待后续学习再更正。
多线程
1.线程和进程的区别
线程:线程是程序中一个单一的顺序控制流程。
进程:一个可以独立运行的程序单位,线程的集合,进程就是由一个或者多个线程构成的,而线程是进程中实际运行单位,是操作系统进行运算调度的最小单位。
总结:一个程序运行后至少有一个进程,一个进程可以包含多个线程。
2.并行和并发的区别
并发(concurrency):当有多个线程在操作时,如果系统只有一个CPU,那它就不可能真正同时进行一个以上的线程,它只能把CPU运行时划分成多个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其他线程处于挂起状态,这种方式称为并发。同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行, 使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的, 只是把时间分成若干段,使多个进程快速交替的执行。
并行(parallel):系统有多个CPU时,则线程的操作可能非并发,当一个CPU执行一个线程时,另一个CPU也可以执行另一个线程,两个线程相互不抢占CPU资源。指在同一时刻,有多条指令在多个处理器上同时执行。
并⾏是指两个或者多个事件在同⼀时刻发⽣;⽽并发是指两个或多个事件在同⼀时间间隔内发⽣。
3.单线程与多线程
单线程: 若有多个任务只能依次执行,当上一个任务执行结束后,下一个任务开始执行。
多线程: 若有多个任务,可以同时执行。
4.分时调度和抢占式调度
在Java程序中,JVM负责线程的调度。线程调度是指按照特定的机制为多个线程分配CPU的使用权。
调度的模式有两种:
分时调度是所有线程轮流获得CPU使用权, 并"平均分配"每个线程占用CPU的时间;
抢占式调度是根据线程的优先级别来获取CPU的使用权。 JVM的线程调度模式采用了抢占式模式。 Java使用的为"抢占式调度"。
抢占式:
实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某 个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运 行。其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
5.多线程的优缺点
优点:可以提高程序的效率,例如:有很多的操作是非常耗时的,例如IO操作(下载资料) 如果使用单线程,那么程序就必须下载好一个文件,才能下载下一个文件 如果使用多线程,若下载n个文件,则启动n个线程,每一个线程负责一个文件 就可以使用同时下载多个文件。
缺点:
1 线程对象不是创建越多越好,使用太多线程,是很耗资源, new 对象,就需要在堆中开辟内存,很多线程就需要更多的内存。
2 影响系统性能,java属于抢占式调度,线程之间需要来回的切换。
3 共享资源的时候的问题 例如:卖电影票的问题,票数是固定的,很多线上,窗口卖票使用不当,就会发生卖出假票。
6.Thread类
构造方法
Thread() 分配一个新的 Thread对象。
Thread(String name) 分配一个新的 Thread对象。 设置线程名称
方法
1.static Thread currentThread() 返回对当前正在执行的线程对象的引用。
new Thread() {
@override
public void run() {
for(int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "正在运⾏");
}
}
}.start();
2.获取名称和设置名称
获取线程名称 String getName() ;当你没有设置线程名称的时候,系统会给它命名自动生成的名称格式为"Thread-"+ n ,其中n为整数,n从0开始
设置名称 方式1:通过构造函数 Thread(String name)
方式2: 通过void setName(String name) 方法
Thread t1= new Thread("线程1"){
@override
public void run(){
System.out.println(this.getName()+"正在运行");
}
};
t1.start();
Thread t2= new Thread(){
@override
public void run(){
System.out.println(this.getName()+"正在运行");
}
};
t2.setName("线程2");
t2.start();
3.static void sleep(long millis) 让线程进入阻塞状态,当休眠的时间到达后,则进入就绪状态 接着抢占CPU资源
new Thread() {
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println(getName() + "正在运⾏");
try {
Thread.sleep(10); // 10毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}}.start();
4.void setDaemon(boolean on) 将此线程标记为 daemon线程或用户线程, true 守护线程
boolean isDaemon()测试这个线程是否是守护线程
5.void join() 当前线程暂停,等待指定的线程执行结束后,当前线程再继续执行。
join(int), 可以等待指定的毫秒之后继续
Thread t1 = new Thread() {
public void run() {
for(int i = 1; i <=50; i++) {
System.out.println(getName() + "a");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new Thread() {
public void run() {
for(int i = 0; i < 50; i++) {
if(i == 2) {
try {
//t1.join();//插队,加⼊
t1.join(30);//加⼊,有固定的时间,过了固定时间,继续交替执⾏
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + "b");
}
}
};
t1.start();
t2.start();
6.礼让方法 yield作用:暂停当前正在执行的线程对象,让其他拥有相同优先级的线程执行, 静态方法,只是可能性的,不能保证确定性。 让出cpu的使用权,进入就绪状态,接着抢cpu资源
7.void setPriority(int newPriority) 更改此线程的优先级。
int getPriority() 返回此线程的优先级
static int MAX_PRIORITY 线程可以拥有的最大优先级。 10
static int MIN_PRIORITY 线程可以拥有的最小优先级。 1
static int NORM_PRIORITY 分配给线程的默认优先级。 5
注意:优先级1-10,若不在范围内,会抛出异常IllegalArgumentException 非法参数异常
创建线程的步骤
1 定义一个类继承Thread
2 重写run方法
3 在main方法中创建子类对象,这个子类对象就是线程对象
4 调用start方法启动这个线程让线程执行, 同时JVM去调用run方法。 注意:(不要直接调用run,若直接调用run只是普通方法调用)
代码示例:
public class A extends Thread {
@Override
public void run() {
for(int i=1;i<=10;i++) {
String name=Thread.currentThread().getName();//获取正在执行run方法的线程名
System.out.println(name+"线程正在执行输出:"+i);
}
}
public static void main(String[] args) {
//创建两个线程任务 a1就是线程对象
A a1 = new A();
A a2 = new A();
d.run();//没有开启新线程, 在主线程调⽤run⽅法
d2.start();//开启⼀个新线程,新线程调⽤run⽅法
}
}
7.Runnable接口
public interface Runnable {
public abstract void run();
}
创建线程的步骤
1 创建一个类实现Runnable接口
2 实现接口中抽象方法
3 在run方法中定义线程对象完成的任务
4 在main方法中,创建实现类对象,并把这个对象作为参数传递给 Thread(自定义对象,“线程名称”)
5 调用Thread对象的start方法
代码示例:
public class B implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
String name = Thread.currentThread().getName();// 获取正在执行run方法的 线程名称
System.out.println(name + "线程正在执行输出:" + i);
}
}
public static void main(String[] args) {
B b1=new B();
Thread t=new Thread(b1,"线程①");
t.start();
Thread t2=new Thread(b1,"线程②");
t2.start();
}
}
8.线程的生命周期
线程的五种状态:
新建: 创建线程对象
就绪(有执行资格,没有执行权): 调用start()方法,但是还没有获取CPU的执行权
运行(有执行资格,有执行权): 获取到CPU的执行权,执行run方法中的代码
阻塞(没有执行资格,没有执行权): 没有CPU的执行权,回到就绪状态
死亡:代码运行完毕,线程对象销毁。
9.线程池
1.概念
线程池,其实就是⼀个容纳多个线程的容器,其中的线程可以反复使⽤,省去了频繁创建线程对象的操
作,⽆需反复创建线程⽽消耗过多资源。
2.线程池好处
1.重用已经存在的线程,减少了线程的创建和开销
2.可以有效控制最大并发数,提高了系统资源的使用率避免过得的竞争而导致线程死锁和OOM(OutOfMemory)
3.可以提供定时和定期的功能,控制线程数和并发
3.线程池的核心类
线程池的实现接口 :ExecutorService
ThreadPoolExecutor 线程池核心类
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue)
corePoolSize: 线程池中核心线程大小,也可以理解为最小线程个数
maxiMumPoolSize: 最大线程池大小 核心线程个数+零时创建线程个数
keepAliveTime: 空余线程存活的时间,指超过corePoolSize的空余线程 (临时工没有任务的情况下多久销毁)
unit:销毁的时间的单位
workQueue: 存储等待执行线程的工作队列,线程从队列中获取任务
4. 创建四种线程池的方式:
1>固定线程池
static ExecutorService newFixedThreadPool(int nThreads)指定最大可创建的线程数
ExecutorService service = Executors.newFixedThreadPool(3);
for(int i=0;i<10;i++){
int n=i;
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名:"+
Thread.currentThread().getName()+" 的第"+n+"次任务");
// 假设任务需要执行一会儿
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
//关闭连接池
service.shutdown();
结论 : 无论有多少线程任务,执行线程的 总是这3个线程
好处: 有效控制线程个数 ,让线程对象 重复利用,
弊端: 任务过多,会出现 任务进入等待状态。
2> 带缓冲线程池
public static ExecutorService newCachedThreadPool()按需分配线程数
ExecutorService service = Executors.newCachedThreadPool();
for(int i = 0 ;i < 10 ;i++){
int n=i;
Future future = service.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----第"+n+"次执行");
try {
Thread.currentThread().sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
service.shutdown();
}
总结:
按需分配线程个数如果线程池中没有活动的线程,则创建新线程 ,如果有活动的线程,依然使用之前创建的线程
3>单线程线程池
public static ExecutorService newSingleThreadExecutor()
//指定线程 单线程的方式,执行多个任务
ExecutorService service = Executors.newSingleThreadScheduledExecutor();
for(int i = 0 ;i <10 ;i++){
int n = i;
//任务类 ,可以返回任务类的结果
Future<Integer> future = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"----"+n);
return n+100;
}
});
//获取结果
Integer num = future.get();
System.out.println("结果:"+num);
}
4> 调度线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)定时定期的线程数
对ThreadPoolExecutor一个封装,按一定周期执行任务
ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
// 设定任务 3秒钟之后执行一次
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 执行01任务");
}
} , 3 ,1 , TimeUnit.SECONDS);
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 执行02任务");
}
} , 3 ,3 , TimeUnit.SECONDS);
总结: 在任务运行期间,哪个线程是空闲的都有可能执行该定时任务 。
5.submit 和 execute的区别
execute 这个方法没有返回值,如果不需要知道线程执行后的结果 就推荐使用这个方法
submit: 返回Future对象,通过调用这个对象get()获取线程执行的结果
10.线程安全
实际开发中很多场景会出现多线程共享同一资源的情况,线程之间可能同时对共享资源进行修改,导致数据发生变更而出现数据错乱。 此时需要解决线程并发导致数据不完全问题。
1.线程同步的2种方法
1>同步方法
把修改公共资源的方法 ,使用 synchronized 关键字修饰
例如: public synchronized void m(){ }
备注: synchronized 也可以修饰静态方法,如果调用的是静态的方法 锁住的是整个类。
2>同步代码块
例如: synchronized(object){ }
备注:同步时一个高开销的操作,因此应该尽量减少同步的内容 通常没有必要使用同步整个方法,建议使用同步代码块
11.死锁
线程同步可以解决多个线程对公共资源的操作导致数据安全的问题,但同时也可能带来另一个隐患,当程序中出现多个同步锁时,可能发生锁使用不当,导致线程一直处于等待对方锁资源的释放,而形成一种僵局 ,这就是 “死锁” 。
1. 什么是死锁?
死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现 象,若无外力作用,它们都将无法推进下去。
2. 产生死锁的原因?
竞争资源引起进程死锁 例如:
线程A锁住左筷子并等待右筷子, 线程B锁住右筷子并等待左筷子 这样两个线程就发生了死锁。
synchronized(左筷子){
synchronized(右筷子){ }
}
synchronized(右筷子){
synchronized(左筷子){ }
}
3. 如何避免死锁?
如果我们能够避免在对象的同步方法中调用其它对象的同步方法,那么就可以避免死锁产生的可能性。 尽 量不要嵌套使用 synchronized 关键字
4. 死锁条件
互斥使用:一个资源只能分配给一个线程
不可剥夺:资源只能由占有者释放,申请者不能强制剥夺
请求保持:线程申请资源时,保持对原有资源的占有
循环等待:存在一个进程等待队列:{P1 , P2 , … , Pn}, 其中P1等待P2占有的资源,P2等待P3占有的 资源,…,Pn等待P1占有的资源,形成一个进程等待环路
5. 使用java代码模拟一个死锁
//定义两个资源
private static String left="左筷子";
private static String right="右筷子";
//对象deadLock1占有资源left,需要资源right
Thread deadLock1=new Thread(){
@Override
public void run(){
String name=Thread.currentThread().getName();
synchronized(left){
System.out.println(name+"获取了"+left+",等待拿"+right);
synchronized(right){
System.out.println(name+"拿到了一双筷子");
}
}
}
};
//对象deadLock2占有资源right,需要资源left
Thread deadLock2=new Thread(){
@Override
public void run(){
String name=Thread.currentThread().getName();
synchronized(right){
System.out.println(name+"获取了"+right+",等待拿"+left);
synchronized(left){
System.out.println(name+"拿到了一双筷子");
}
}
}
};
12.等待唤醒机制
Object中定义的方法:
- void wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。
2.void notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。
3.void notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。
唤醒的意思就是让 线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这 些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。
网络编程
1.计算机网络定义
1、计算机网络定义
把分布在不同区域的计算机以及通信设备使用通信线路互联成一个规模庞大,功能强的网络系统,从而使计算机之间互相传递数据,共享硬件,软件,数据信息的资源
计算机网络的主要功能:
1>资源共享
2>信息传递和集中处理
3>均衡负荷和分布处理
4>综合信息服务
2.网络通信协议
通过计算机网络使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信 必须要遵守一定的规则,这好比汽车在马路上行驶也要遵守交通规则一样,在计算机网络中这些连接和通信的 规则我们称为 “网络通信协议”
"网络通信协议"有很多种,java最常用的就是 TCP/IP 协议,UDP协议
上图中,TCP/IP协议中的四层分别是应⽤层.传输层.⽹络层和链路层.每层分别负责不同的通信功能。
链路层:链路层是⽤于定义物理传输通道,通常是对某些⽹络连接设备的驱动协议,例如针对光纤、⽹
线提供的驱动。
⽹络层:⽹络层是整个TCP/IP协议的核⼼,它主要⽤于将传输的数据进⾏分组,将分组数据发送到⽬
标计算机或者⽹络。
传输层:主要使⽹络程序进⾏通信,在进⾏⽹络通信时,可以采⽤TCP协议,也可以采⽤UDP协议。
应⽤层:主要负责应⽤程序的协议,例如HTTP协议、FTP协议等。
3.IP地址
使用一个唯一的IP编号,将网络中的计算机 连接并可以访问 ,这个IP编号就是IP地址
IP地址用于区分 哪台计算机
端口号用于区分 计算机上哪个程序 根据IP地址找到主机,再根据端⼝号找到应⽤程序
IP地址广泛使用的IPv4,有4个字节大小的二进制数来表示。
01100100.00000100.00000101.00000110
由于2进制数表示IP地址不方便,所以通常会将二进制数的IP地址 写成10进制, 每个字节用一个 (0-255)表示,数字之间使用.分割
例如 “192.168.1.8”
由于计算机网络扩张,对IP地址的需求就越来越多,IPv4会使用完, 因此有了IPv6 ,16个字节来表示IP地址。
IP的分类: A类 10.0.0.0–10.255.255.255 B类 172.16.0.0–172.31.255.255 C类 192.168.0.0–192.168.255.255
4.端口号
端口号只有整数,范围是从0 到65535
注意: 0-1023之间端口用于一些知名网络服务和应用 若自己创建网络程序需要指定端口号,建议使用 1024以上的端口,避免端口冲突。
协议名 | 端口号 | 用途 |
---|---|---|
ftp协议 | 21 | 文件传输协议 ,用于传输文件 |
sftp | 文件传输,用于文件上传 下线 | |
http | 80 | 访问浏览器,网站 |
https | 80 | 基于http的加密协议,访问更安全 |
smtp | 25 | 邮件协议 ,simple mail transaction prototal |
pop3 | 110 | 邮件协议,接收邮件 |
telet | 23 | 远程登录 |
ssh | 22 | 安全的登录协议,登录Linux 使用该协议 |
一些默认应用的的端口 MySql :3306 Tomcat: 8080 ,Oracle 1521 SqlServer : 1433 Redis端口: 6379
5.IP相关类
InetAddress 、Inet4Address、 Inet6Address
InetAddress 此类表示 Internet协议(IP)地址。
getName(String addr) :根据ip获取主机IP对象
getHostAddress(): 获取IP对应的字符串
getHostName() :获取IP主机名
getLocalHost() :获取本地主机的地址
// 获取IP地址的类
InetAddress address = InetAddress.getLocalHost();
System.out.println("我的IP地址:"+address.getHostAddress());
System.out.println("我的主机名:"+address.getHostName());
System.out.println("我的address 字节数组:"+ Arrays.toString(address.getAddress()));
// 通过IP寻址到某个计算机
// 获取对方电脑的 IP 和主机名
InetAddress address1 = InetAddress.getByName("192.168.6.104");
System.out.println("主机名:"+address1.getHostName());
System.out.println("ip地址:"+address1.getHostAddress());
// 远程访问IP
InetAddress address2 = InetAddress.getByName("47.97.101.169");
System.out.println("主机名:"+address2.getHostName());
System.out.println("ip地址:"+address2.getHostAddress());
6.UDP与TCP协议(传输层)
传输层用于数据之间的传输,根据传输的效率,数据的可靠性可以分为两类协议
TCP
TCP协议 :传输控制协议 Transmission Control Protocol ,一种面向连接安全可靠基于字节流的网络通信协议
TCP进行通信时,严格的区分 “客户端” 和 “服务器” 要求:
1 必须先由客户端去连接服务器端才能实现通信 服务器不能主动连接客户端
2 服务器要事先启动,等待客户端的连接 (先运行服务器端,再运行客户端,客户端给服务器端发送连接请求)
每次连接的创建都需要经过"三次握⼿"。
第⼀次握⼿,客户端向服务器端发出连接请求,等待服务器确认,
第⼆次握⼿,服务器端向客户端回送⼀个响应,通知客户端收到了连接请求,
第三次握⼿,客户端再次向服务器端发送确认信息,确认连接
在Java中提供了两个类用于实现TCP通信:
1> ServerSocket 服务器端
1,创建服务器端ServerSocket对象,指定服务器端端⼝号
2,开启服务器,等待着客户端Socket对象的连接,如有客户端连接,返回客户端的Socket对象
3,通过客户端的Socket对象,获取客户端的输⼊流,为了实现获取客户端发来的数据
4,通过客户端的输⼊流,获取流中的数据
5,通过客户端的Socket对象,获取客户端的输出流,为了实现给客户端反馈信息
6,通过客户端的输出流,写数据到流中
7,关闭流资源
2> Socket 客户端
1,创建客户端的Socket对象
2,获取Socket的输出流对象
3,写数据给服务器
4,获取Socket的输⼊流对象
5,使⽤输⼊流,读反馈信息
6,关闭流资源
UDP
UDP协议 :数据报文协议 User Datagram Protocal ,一种面向无连接不完全,传输速度较快的通信协议
涉及到类:
DatagramPacket 数据报包(封装数据)
DatagramSocket 发送和接收数据报包
发送端:
1,创建DatagramSocket对象
2,创建DatagramPacket对象,并封装数据
3,发送数据
4,释放流资源
接收端:
1,创建DatagramSocket对象
2,创建DatagramPacket对象
3,接收数据存储到DatagramPacket对象中
4,获取DatagramPacket对象的内容
5,释放流资源
7.URL
我们的项目需要部署在服务器上,使用URL来访问服务器上的文件。 在Java中URL网络类可以让你通过URL去连接网络获取网络资源
String path = "网址";
// 创建URL对象
URL url = new URL(path);
// 将当前程序和URL建立连接
URLConnection conn = url.openConnection();
URL类的方法:URLConnection openConnection()
URLConnection中的方法:
InputStream getInputStream() 返回从此打开的连接读取的输入流。
OutputStream getOutputStream() 返回写入此连接的输出流。
MYSQL
1.数据库卸载
步骤1: window+r 输入 services.msc 找到mysql.exe服务,停止服务
步骤2: 进入控制面板–>程序与功能–>卸载或者更改程序 找到mysql—>右键卸载
步骤3.在此电脑右键属性 --> 高级系统设置 --> 环境变量 --> 系统变量 --> path --> mysql的配置 --> 删除
步骤4:如果默认安装,显示所有隐藏文件 把c://programdata和c://programfiles中的mysql目录删除 “若没有删除,下一次安装会失败”
步骤5:再去服务查看是否删除成功 若没有发现MYSQL服务,说明卸载好了
2.数据库安装
- 双击 mysql-5.5.50-winx64.msi
- 永远下一步, 记住有设置编码格式的界面 (“特征:三个黑人”) 选中最后一个黑人,设置编码格式为utf8 记住有一个设置mysql数据库端口号的界面 默认端口号为3306, “不要乱改!” 记住要设置密码:默认的账户名称root 密码:自己设置123456
- 服务器,安装成功,win+r 输入services.msc查看服务器是否有了 “启动服务器”
3.数据库
数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享 的、统一管理的大量数据的集合。
数据库是以一定方式储存在一起、能与多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集 合,可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据进行新增、查询、更新、删除等 操作。
关系数据库:
关系型数据库和常见的"表格"比较相似,关系型数据库中表与表之间是有很多复 杂的关联关系的。 常见的关系型数据库有Mysql,SqlServer,Oracle等。
非关系型数据库(NoSQL):
大量的NoSql数据库如MongoDB、Redis、Memcache出于简化数据库结构、 避免冗余、影响性能的表连接、摒弃复杂分布式的目的被设计。
NOSQL分为四种:
1.键值对存储(key-value):代表软件Redis, 它的优点能够进行数据的快速查询, 而缺点是需要存储数据之间的关系。
2.列存储:代表软件Hbase,它的优点是对数据能快速查询, 数据存储的扩展性强。 而缺点是数据库的功能有局限性
3.文档数据库存储:代表软件MongoDB,它的优点是对数据结构要求不特 别的严格。而缺点是查询性的性 能不好,同时缺少一种统一查 询语言
4.图形数据库存储:代表软件InfoGrid,它的优点可以方便的利用图结构 相关算法进行计算
4.DBS、DBMS、DBA
6.数据数据库术语
7.数据库指令
1.创建数据库:create database <数据库名>;<>表示此处内容由用户自定义,没有<>符号
2.显示当前所有的数据库:show database;
3.删除数据库:drop database <数据库名>
drop database if exists <数据库名>;表示如果数据库存在,就将这个数据库删除
4.创建Database 并指定默认字符集:
create database <数据库名>
default character set utf8mb4 collate utf8mb4_unicode_ci;
注意: MYSQL中utf8 并非标准的utf8字符编码,utf8mb4才是 utf8mb3是存储一个文字时,最多占3个字节,而utf8mb4 最多占4个字节。
5.查看当前的数据库引擎:show engines; – 显示mysql中支持的引擎
6.看当前默认的存储引擎 show variables like ‘storage_engine’;
8.数据库表操作
1>创建表
use 数据库名; -- 选择表存在哪个数据库中
create table <表名>(
<字段名1> <数据类型> [列级别约束条件] [默认值],
<字段名2> <数据类型> [列级别约束条件] [默认值],
<字段名3> <数据类型> []列级别约束条件] [默认值], ...
)[engine=引擎] [default chaset=字符编码];
注意:<> 必须要有的 [] 可有可无" "最后一个字段后面不要加逗号" "[]部分如果没有设置,则MYSQL会使用默认的设置
create table dept(
deptno int,
dname varchar(15), -- varchar类似于java中String字符串
loc varchar(10)
);
注意 1 SQL语法不区分大小写,按惯例大写,因为小写发送给服务器运行的时候 也会转换成大写
2 严格语法中,数据库名 和 字段名 需要使用反单引号括起来
3 如果数据库名和表名由多个单词组成,惯例 下划线命名法``
创建表的过程是规定数据列的属性的过程,同时也是定义数据完整性约束的过程 表(Table) 是处于数据库(Database)之下的,在创建数据表之前,应该使用 “use 数据库名” 选择一个数据库,来指定当前的操作都在哪个数据里面。
2>字段的数据类型
1>数值型
整数类型 tinyint 1字节 smallint 2字节 mediumint 3字节
[int] 4字节 bigint 8字节
如果字段的数据类型是整型,可以使用auto_increment自动增长约束条件
int(4) 4代表的数字的宽度是4
– 每一种数据类型如果不指定宽度就是默认宽度
tinyint 4 smallint 6 mediumint 9
[int] 11 bigint 20
浮点型 float double decimal(m,n)
m代表总位数,n代表小数点后的位数
salary decimal(6,2) 整数位是4位,小数位是2位
如果不指定长度,就是默认值,decimal(10,0)
2> 字符串类型
char(n) varchar(n) text
char固定长度的字符串类,如果存储的内容的长度没有到n则使用空格填充,如果超过了n,就会报一个错误, Data too long for 字段名
属性 char(4) 就不能存储"abcdefg"
3>日期 year time date [datetime] [timestamp]
select now();//获取到日期和时间
select current_date;//获取到日期
select current_time();//获取到时间
4>SET类,是一个字符串对象,可以有0 或者多个值,最多可以是有64个值,表示的是一系列的范围值
create table tb_01(
sex SET(‘男’,‘女’);
);
insert into tb_01(sex) values(‘男’);
5>进制数据类型 blob存储视频和音频(不建议使用)
太大了,专门用来处理图片,音频资源的服务器
3>约束
【列级别约束】位于列声明的末尾 表级别的约束,位于表声明的尾部
1.表级别约束
1>主键约束 primary key,它等价于 非空 + 唯一 约束,一张表中,主键约束最多只能有1个
-- 主键约束分为两种:
-- 单主键: 通常直接声明在列的尾部
create table 表名(
字段名 数据类型 primary key,
...
);
-- 联合主键:主键涉及2个列或者2个以上的列,多个列一起作为主键
create table 表名(
字段名1 数据类型,
字段名2 数据类型,
字段名3 数据类型,
...
primary key(字段名1,字段名2,..)
);
-- 表已经创建成功了,如何设置主键?
-- 使用alter修改表,添加主键
alter table 表名 add primary key(字段名);
-- 删除表的主键
alter table 表名 drop primary key;
2>外键约束foreign key,被它修饰的列中的数据,需要参考另一张表的 某列中出现的值。
create table A表(
...
...
constraint 约束名 foreign key (A表中的字段名) references B表(某个字段)
);
-- 如果两张表之间存在外键关联,则先创建主表,再创建从表 删除表的时候则相反。
-- 如何删除外键约束
alter table 从表表名 drop foreign key 自定义外键名称;
-- 如何添加外键约束
alter table 从表表名 add constraint 自定义外键名称 foreign key(从表字段名) references 主表(字段名);
2.列级别约束
1>非空约束not null
-- Null类型的特征:所有类型的值都可以是null
create table 表名(
字段名 数据类型 not null,
...
);
2>唯一性约束unique
-- 设定的列不允许插入重复的值
create table 表名(
字段名 数据类型 unique,
...
);
3>默认值约束default
-- 所限定的列,当没有插入值 的时候,会插入这个默认值。
create table 表名(
字段名 数据类型 default 具体的值,
....
);
4.查看表结构
1.查看表的字段信息
desc 表名; describe 表名;
2.查看建表语句
show create table 表名;
5.修改表的元数据
1.添加主键约束
alter table 表名 add [constraint 主键约束名] primary key(主键字段);
2. 添加外键约束
alter table 从表名 add constraint fk_主表_主键字段 foreign key(外键字段名) references 主表(关联字段名);
3. 删除约束
alter table 表名 drop primary key;
alter table 表名 drop foreign key fk_主表_主键字段;
4.添加列
alter table 表名 add 新字段名 数据类型;
5.修改列的类型长度以及约束
alter table 表名 modify 列名 数据类型(长度) 约束; – 列级别的约束
6.修改列的名称
alter table 表名 change 旧列名 新列名 类型(长度) 约束;
7.删除列
alter table 表名 drop 列名;
8.给表重命名
rename table 旧表名 to 新表名;
9. 修改表的字符编码
alter table 表名 character set GBK;
10.修改表的存储引擎
alter table 表名 engine=新的存储引擎;
6.删除表
drop table if exists 表名; – 删除表
truncate table 表名; – 将表中的数据清空 ,表还在
delete from 表名; – 删除表的数据
– 删除一张表的时候,先删除从表,再删除主表
9.SQL语句
1.概述
数据库不认识Java语言,但是我们需要和数据库进行交互,这时需要使用数据库认识的语言SQL语句 SQL(structured query language) 结构化查询语言
SQL分类:
1> 数据定义语言 DDL(Data Definition Language)
– 数据库,表,列
关键字: create alter drop
2> 数据操作语言 DML (Data Manipulation Language)
– 插入,修改,删除 表或者视图的数据
关键字: insert update delete
3> 数据控制语言 DCL (Data Control Language)
– 用于创建用户,控制访问权限
4> 数据查询语言 DQL ( Data Query Language)
– 查询
关键字: select from where order by group by having limit
SQL语句语法:
SQL语句可以单行或者多行书写,以分号结尾 可以使用空格或者缩进提供sql语句可读性 MYSQL数据库的SQL语句不区分大小写
2.DML语句
1.insert 向表中添加数据
要给几个字段赋值,就写几个字段,默认的就不要写了,要做到一一对应
语法:
insert into 表名 values(值1,值2,值3…);
insert into 表名(列名1,列名2,列名3…) values(值1,值2,值3…);
insert into tb_dept(列名1,列名2,列名3…) VALUES(值1,值2,值3…),(值1,值2,值3…),(值1,值2,值3…);
2.update 修改表中的数据
语法:
update 表名 set 字段名=值,字段名=值;
update 表名 set 字段名=值,字段名=值,… where 条件;
注意: 列名的类型与修改的值要一致 修改的值也要注意不要超过此列的数据类型的长度 值如果不是数值,则必须添加单引号 没有where的修改语句,代表修改所有。
3.delete 删除表中的数据
语法:
delete from 表名 [where 条件];
删除表中所有的记录使用 delete from 表名; 还是使用truncate table 表名?
delete 是一条一条的删除,不清空 auto_increment的记录
truncate 直接将表删除,重新快速建表,auto_increment从0开始
3.DQL语句
语法
– select语句中各种子句的顺序
- select {*|字段列表}
- from 表1,表2…
- [where 条件]
- [group by 分组的列]
- [having 分组条件]
- [order by 列名 ASC|DESC] – asc升序(默认)desc 降序
- [limit 偏移行,记录行数]
单表查询
-- 查询工资在800-1500之间的员工所有信息
select * from employee where salary>=800 and salary<=1500;
-- 统计一共有多种职业
select count(distinct job) from employee; -- distinct 关键字去除重复的行
-- 求出所有员工的总工资(总工资=月薪+奖金)
select salary+ifnull(commission,0) from employee; -- ifnull(参数1,参数2) 若参数1为NULL 使用参数2代替
-- 查询not between...and查询工资不在 1000-2000之间的员工信息
select * from employee where salary not between 1000 and 2000;
-- and > or > not -- and 所有条件都必须满足 or 只有有1个条件满足 not 取反
多表查询
SQL最强大的特征之一就是自由的连接表
1.笛卡尔积
select * from 表1,表2;
例如表1中有4条记录 -
例如表2中有14条记录,查询出结果就是 14*4条
有很多不符合规范的数据
2.等值连接
select * from 主表,从表 where 从表.外键 = 主表.主键;
若存在列名相同,则使用表名.列名区分 ,若没有存在列名相同,则可以省掉表名.
--等值连接(通过外键字段进行连接)
-- 部门1 员工n 员工表中有一个外键 department_id 关联着 主表的主键id
select * from department d,employee e where department_id=d.id;
-- 当两个表中列名相同,多表连接查询会冲突,则使用表名.列名解决
3.连接join
使用一种特殊的语法,可以把多个表连接起来
-- 内连接(等值连接)
-- select 主表.列1,.....,从表.列1,... from 主表 inner join 从表 on 主表.主键=从表.外键 where 过滤条件;
-- select * from 主表 inner join 从表 on 主表.主键=从表.外键;
select * from department d inner join employee e on d.id=e.department_id;
-- 若列名不存在重名,则省略掉表名
/*外连接
左外连接
select*from 表1 left join 表2 on 连接条件 where 过滤条件;
不能换表1和表2的位置*/
select d.Depno 部门编号,d.DepName 部门名称,count(e.Depno)部门人数
from depatment d left join employee e on d.Depno=e.Depno
group by d.Depno;
/*查询出来的信息:
满足连接条件的所有的数据
查询左表的不符合条件的数据,右边的列使用null连接起来*/
/*右外连接
select * from 表1 right join 表2 on 连接条件 WHERE 过滤条件。*/
select d.Depno 部门编号, d.DepName 部门名称, COUNT(e.Depno) 部门人数
from employee e RIGHT JOIN depatment d on d.Depno = e.Depno
GROUP BY d.Depno;
4.子查询
子查询:嵌套查询,比较符合我们逻辑的查询,先查什么再查什么 是指select子句或者where子句中嵌入select语句,子查询的语句放在括号中
create table tb1(
num1 int not null
);
create table tb2(
num2 int not null
);
insert into tb1(num1) values(1),(5),(13),(26);
insert into tb2(num2) values(6),(15),(12),(20);
select*from tb1;
select*from tb2;
-- >any(子查询):比子查询结构中的其他一个大就返回为true
select num1 from tb1 where num1>any(select num2 from tb2);-- 13 26
-- >all(子查询):比子查询结构中的所有的值都大才返回true
select num1 from tb1 where num1>all(select num2 from tb2);-- 26
-- 将select语句嵌套到另外一个select子查询中的语句,也叫嵌套查询
5.联合查询
/*联合查询
对多个select语句的结果进行合并(要求:两个结果的列数和对应列的数据类型要一致)
union 和union ALL
union会去除重复的记录
union all 不会去除重复记录
select 查询工资大于10000的员工的姓名和工资
select 查询在1号部门和2号部门的员工的姓名和工资*/
select ename,salary from emp where salary>10000
union
select ename,salary from emp where did in(1,2);
10.表与表之间的关系
1.一对多(使用最为广泛)
例如:
员工- 部门 N : 1
班级- 学生 1 : N
问题- 选项 1 : N
要点: 多方 从表 一方 主表
1> 从表使用外键列 关联 主表的主键列
2> 某些情况下从表的外键列可以NULL
3> 如果从表中外键列有值,则该值必须在主表的主键列中已有的值
2.多对多(生成中间表)
例如:
学生 – 课程
电影 – 演员
订单 – 商品
特点:使用中间表维护双方的关系 中间表有最少有2个列,都是外键 分别引用双方的主键 中间表的这个两个列可以联合起来作为主键 也就是联合主键
3.一对一(使用相对较少)
例如:
工号 - 员工
人 - 身份中
类似于1 : n 的情况,只是n的一端是唯一的
人 ----------------------身份证
张三 ------------100101011001101
也就是可以使用从表的外键关联到主表的主键
自连接
-- 自连接(一张表自己连接自己)
-- 前提: 这张表中有字段相关联
create table tb_emp(
empno int PRIMARY key, -- 员工编号
ename VARCHAR(20) not null, -- 员工姓名
leader int, -- 领导
sal double -- 工资
);
insert into tb_emp(empno,ename,leader,sal) values(7788,'张三',null,10000), (1122,'李四',7788,2000), (1234,'王五',1122,1000), (2222,'赵六',null,100000), (5656,'田七',2222,8000);
select 员工表.ename 员工姓名, IFNULL(领导表.ename,'没有领导') 领导名字
from tb_emp 员工表 LEFT JOIN tb_emp 领导表 on 员工表.leader = 领导表.empno;