网络三要素:
1)IP地址:InetAddress 网络中设备的标识,不易记忆,可用主机名
A类->第一个号段是网络号段,后面的是主机号段(国家政府机关)
B类->前面两个号段是网络号段,后面的是主机号段(校园网/公司内网等)
C类->私人地址,前面的三个都是网络号段,后面是主机号段 "点分十进法"
2)端口号 用于标识进程的逻辑地址,不同进程的标识
范围:0-65535 其中的0-1024是保留端口号,自己指定的端口号是1025-65535(不要和系统的端口号冲突否则报错->Address already in use :BindException:地址值被占用)
3)传输协议通讯的规则 常见协议:TCP,UDP
TCP/UDP 底层网络协议
UDP:
1)不可靠连接,不需要建立连接通道,以"数据报包"方式进行数据传输,不安全
2)不同步,执行效率相对TCP来说高
3)发送数据大小有限制!(字节流形式)
TCP:
1)可靠连接,需要建立连接通道。
2)同步的,执行效率低,但是安全性能较UDP高,
3)发送数据大小无限制(字节流形式)
类InetAddress
对IP地址的获取和操作
获取任意主机:
getByName()
主机名:
getHostName()
主机Ip地址:
getHostAddress()
例子
package AdressDemo;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class AdressDemo {
public static void main(String[] args) throws UnknownHostException {
InetAddress address=InetAddress.getByName("凤梨罐头");
String ip = address.getHostAddress();
System.out.println(ip);//10.35.165.80
String name = address.getHostName();
System.out.println(name);//凤梨罐头
}
}
网络编程(Socket编程)
发送端/客户端
接收端/服务器端
不管UDP还是TCP都要满足以下条件
两端都必须要有Socket对象(”套接字“);
发送端或者客户端必须绑定主机IP以及端口
接收端或者服务器端必须指定端口号
1.使用UDP协议发送数据
1)创建发送端的socket对象
2)常见数据报包(DatagramSocket) 包括数据内容 ip 端口名
3)使用发送端的socket发送数据报包
4)释放资源close
接收端不能开多次 只能开一次 因为端口已经’'被占用了
UDP发送
package com.qf.udp_02;
import java.net.*;
/**
* 使用UDP协议发送数据
* 1)创建发送端的socket对象
* 2)创建"数据报包"--里面包括:数据内容,ip地址,端口号
* 3)使用发送端的Socket对象发送"数据报包"
* 4)释放资源
*/
public class UdpSend {
public static void main(String[] args) throws Exception {
//1)创建发送端的socket对象 ---java.net.DatagramSocket:此类表示用于发送和接收数据报数据包的套接字
//public DatagramSocket()throws SocketException
DatagramSocket ds = new DatagramSocket() ;
//2)创建"数据报包"--里面包括:数据内容,ip地址,端口号
//java.net DatagramPacket
//参数1:分组数据(字节数组)
//参数2:包的长度
//参数3:ip地址对象
//参数4:端口号
//public DatagramPacket(byte[] buf,int length,InetAddress address,int port)
byte[] bytes = "hello,udp我来了".getBytes() ;
int length = bytes.length ;
InetAddress inetAddress = InetAddress.getByName("10.35.165.17") ;
int port = 6666 ;
DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port) ;
//使用发送端的Socket对象发送"数据报包"
//public void send(DatagramPacket p)throws IOException从此套接字发送数据报包
ds.send(dp) ;
//释放资源
ds.close();
}
}
UDP接受
package com.qf.udp_02;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* Udp接收端
* 1)创建接收端的Socket对象
* 2)创建一个"数据报包"在里面自定义缓冲区:将发送端是数据存储到缓冲区中(字节数组,长度:1024或者1024的整数倍)
* 3)使用2)接收容器(缓冲区),接收发送端的数据
* 4)解析接收容器中的实际内容
* 5)展示数据即可
* 6)释放接收端的资源
*
* 注意:
* 接收端开启一次就可以;否则
* Exception in thread "main" java.net.BindException: Address already in use: Cannot bind
*/
public class UdpReceive {
public static void main(String[] args) throws Exception {
//1)创建接收端的Socket对象
//public DatagramSocket(int port)throws SocketException
DatagramSocket ds = new DatagramSocket(6666) ;
//2)创建一个"数据报包"在里面自定义缓冲区:将发送端是数据存储到缓冲区中(字节数组,长度:1024或者1024的整数倍)
//参数1:用于保存传入"数据报"的缓冲区。
//参数2:要读取的字节数
//public DatagramPacket(byte[] buf,int length)
byte[] buffer = new byte[1024] ;
DatagramPacket dp = new DatagramPacket(buffer,buffer.length) ;
//3)使用2)接收容器(缓冲区),接收发送端的数据
//public void receive(DatagramPacket p)throws IOException从此套接字接收数据报包
ds.receive(dp) ;
//4)解析接收容器中的实际内容
//public byte[] getData() :解析数据报包中DatagramPacket缓冲的实际的字节数组
byte[] bytes = dp.getData();
//public int getLength():解析数据报包中DatagramPacket中的实际字节数组长度
int length = dp.getLength();
//展示数据
String message = new String(bytes,0,length) ;
//谁发送的数据---获取ip地址字符串形式
//数据报包DatagramPacket--->public InetAddress getAddress()
//InetAddress:ip地址--->String getHostAddress()
String ip = dp.getAddress().getHostAddress() ;
System.out.println("data from is--->"+ip+",data is--->"+message) ;
//释放资源
ds.close();
}
}
2.键盘录入版本
UDP发送
package UDPDemo2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class TestSend {
public static void main(String[] args) {
DatagramSocket ds=null;
try {
ds= new DatagramSocket();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入对话");
String line=null;
while((line=br.readLine())!=null) {
DatagramPacket dp = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("10.35.165.80"), 1234);
ds.send(dp);
}
} catch (SocketException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally {
if(ds!=null){
ds.close();
}
}
}
}
UDP接受
package UDPDemo2;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class TestReceive {
public static void main(String[] args) {
try {
DatagramSocket ds = new DatagramSocket(1234);
while(true){
byte[] buffer=new byte[1024];
DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
ds.receive(dp);
String message = new String(dp.getData(), 0, dp.getLength());
String IP=dp.getAddress().getHostAddress();
System.out.println("来自"+IP+"IP的信息:"+message);
}
} catch (SocketException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally {
}
}
}
线程版本写在同一个对话框的
发送端资源类
package com.qf.udp_04;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* 发送端的资源类
*/
public class SendTread implements Runnable{
private DatagramSocket ds ;
public SendTread(DatagramSocket ds){
this.ds = ds ;
}
@Override
public void run() {
try {
//键盘录入数据--->BufferedReader
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in)) ;
String line = null ;
while((line=br.readLine())!=null){
//数据报包:将数据发送接收端
DatagramPacket dp = new DatagramPacket(
line.getBytes(),
line.getBytes().length,
InetAddress.getByName("10.35.165.17"),
10086) ;
//发送数据
ds.send(dp) ;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
ds.close();
}
}
}
接收端资源类
package com.qf.udp_04;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* 接收端的资源类
*/
public class ReceiveThread implements Runnable{
private DatagramSocket ds ;
public ReceiveThread(DatagramSocket ds){
this.ds = ds ;
}
@Override
public void run() {
try {
//不断的展示数据
while(true){
//接收数据:接收容器
byte[] buffer = new byte[1024] ;
DatagramPacket dp = new DatagramPacket(buffer,buffer.length) ;
//接收
ds.receive(dp) ;
//解析缓冲区(接收容器)的数据
String message = new String(dp.getData(),0,dp.getLength()) ;
//获取ip地址
String ip = dp.getAddress().getHostAddress() ;
System.out.println("data from --->"+ip+",content is--->"+message);
}
} catch (IOException e) {
e.printStackTrace();
}
//接收端不需要释放资源,一直开启状态
}
}
测试类
package com.qf.udp_04;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* 需要优化:
* 发送端不断键盘录入数据,接收端不断展示数据,在一个窗口中聊天!
* 使用多线程的方式:开启两条线程,一条线程:发送端发送数据
* 另一条线程:接收端不断接收数据,展示数据
*/
public class ChatRoom {
public static void main(String[] args) {
try {
//创建发送端Socket
DatagramSocket sendDs = new DatagramSocket() ;
//创建接收端的Socket
DatagramSocket receiveDs = new DatagramSocket(10086) ;
//创建发送端的线程所在的资源类对象
SendTread st = new SendTread(sendDs) ;
//创建接收端的线程所在资源类对象
ReceiveThread rt = new ReceiveThread(receiveDs) ;
//创建线程
Thread t1 = new Thread(st) ;
Thread t2 = new Thread(rt) ;
//启动线程
t1.start();
t2.start();
} catch (SocketException e) {
e.printStackTrace();
}
}
}
网络编程(Socket编程)三要素
IP地址
A/B/C三类:私人地址 C类:前面三个网络号段,后面是主机号段
port端口号
0-65535---里面0-1024 属于"保留端口号"
指定端口:1025-65535
协议规则:
UDP协议/TCP协议 区别
1)是否是可靠连接
UDP:不可靠连接(不建立通道)
TCP:是一种可靠连接(建立通道)
2)是线程安全
UDP:不同步,执行效率高,线程不安全
TCP:同步,执行效率低,线程安全!
3)是否传输数据大小有限制
UDP--->有限制 "数据报包"的方式 DatagramPacket
TCP--->无限制 以"字节流"的方式 客户端写数据---服务器端---读数据--->反馈客户端
UDP发送端和接收端的代码实现
UDP发送端
//1)创建发送端的Socket对象
DatagramSocket ds = new DatagramSocket() ;
//2)创建数据报包对象:包含的数据,数据的长度,ip地址对象,端口号
byte[] bytes = "hello,UDP我来了".getBytes() ;
DatagramPacket dp = new DatagramPacket(
bytes,
bytes.length,
InetAddress.getByName("10.35.165.17"),
10000) ;
//3)发送数据
ds.send(dp) ;
//4)释放资源
ds.close() ;
UDP接收端
//1)创建服务器端的Socket对象
DatagramSocket ds = new DatagramSocket(10000) ;
//2)创建一个"接收容器"(数据报包--->自定义字节缓冲区)
byte[] buffer = new byte[1024] ;
DatagramPacket dp = new DatagramPacket(buffer,buffer.length) ;
//3)使用"接收容器" 来接收数据
ds.receive(dp) ;
//4)解析数据
//获取实际的字节数组
byte[] bytes = dp.getData() ;
//获取实际的字节数长度
int length = dp.getLength() ;
//获取发送端的ip地址
String ip = dp.getAddress().getHostAddress() ;
//5)展示数据
String message = new String(bytes,0,length) ;
System.out.println("data from --->"+ip+",数据是:"+message) ;
//6)接收端一直开启,不关闭
TCP协议
创建TCP协议的socket对象
写数据--发给服务端
关闭资源
步骤
客户端
1)创建TCP协议这种方式的客户端的socket对象
2)写数据---写给服务器
3)关闭资源
服务器端
1)创建服务器端的Socket对象-->绑定端口
2)服务器端监听客户端连接--->获取到指定客户端的Socket对象
3)获取通道的内输入流--->读数据
4)关闭服务器端的Socket对象
例子老师
客户端
package com.qf.tcp_01;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
/**
* TCP方式来发送和接收数据
*
* 1)创建TCP协议这种方式的客户端的socket对象
* 2)写数据---写给服务器
* 3)关闭资源
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1)创建TCP协议这种方式的客户端的socket对象
//java.net.Socket -->public Socket(String host,int port)
Socket socket = new Socket("10.35.165.17",6666) ;
//2)写数据---写给服务器
//获取客户端通道内的输出流对象,写数据
//public OutputStream getOutputStream()
OutputStream out = socket.getOutputStream() ;
out.write("hello,TCP我来了".getBytes());
//3)释放资源
socket.close();
}
}
服务器端
package com.qf.tcp_01;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* TCP服务器端
* 1)创建服务器端的Socket对象-->绑定端口
* 2)服务器端监听客户端连接--->获取到指定客户端的Socket对象
* 3)获取通道的内输入流--->读数据
* 4)关闭服务器端的Socket对象
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1)创建服务器端的Socket对象-->绑定端口
//public ServerSocket(int port)throws IOException
ServerSocket ss = new ServerSocket(6666) ;
System.out.println("正在等待客户端连接......");
//2)服务器端监听客户端连接--->获取到指定客户端的Socket对象
//public Socket accept()throws IOException:
Socket socket = ss.accept();
System.out.println("客户端已连接...");
//3)获取监听到当前客户端的通道的内输入流--->读数据
//public InputStream getInputStream()throws IOException
InputStream inputStream = socket.getInputStream();
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = inputStream.read(bytes);
//4)展示数据
String message = new String(bytes,0,len) ;
//获取ip地址
//public InetAddress getInetAddress()
String ip = socket.getInetAddress().getHostAddress() ;
System.out.println("data from is ---->"+ip+",data is--->"+message) ;
//5)释放资源
ss.close();
}
}
例子自己
客户端
package TCPDemo.test1;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class Cilent {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("10.35.165.80",8848);
OutputStream out=socket.getOutputStream();
out.write("你好 我想和你玩个游戏....".getBytes());
socket.close();
}
}
服务器端
package TCPDemo.test1;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Serve {
public static void main(String[] args) throws IOException {
ServerSocket s = new ServerSocket(8848);
System.out.println("客户端正在连接中........");
Socket ss=s.accept();
System.out.println("客户端已连接!!");
InputStream in=ss.getInputStream();
byte[] bytes=new byte[1024];
int len=in.read(bytes);
String mes=new String(bytes,0,len);
String IP=ss.getInetAddress().getHostAddress();
System.out.println("来自地址: "+IP+"的信息: "+mes);
s.close();
}
}
先运行服务器端 再运行客户端
TCP客户端发送给服务器端并得到反馈
例子老师
客户端
package com.qf.tcp_02;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* 客户端发送数据,服务器端接收数据,展示客户端发送的数据,并同时反馈客户端数据,客户端读取反馈的数据
*/
public class TcpClient {
public static void main(String[] args) throws IOException {
//创建客户端的Coekt对象
Socket s = new Socket("10.35.165.17",7777) ;
//获取客户端通道内的输出流对象
OutputStream out = s.getOutputStream() ;
//写数据
out.write("hello,TCPServler".getBytes());
//读取服务器反馈的数据
//获取客户端通道内的输入流对象
InputStream in = s.getInputStream();
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = in.read(bytes);
//展示数据
System.out.println("serverMessage:"+new String(bytes,0,len));
//释放资源
s.close() ;
}
}
服务器端
package com.qf.tcp_02;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
*服务器端
*/
public class TcpServer {
public static void main(String[] args) throws IOException {
//创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(7777) ;
//监听客户额连接
Socket s = ss.accept();
//获取通道内的输入流对象
InputStream inputStream = s.getInputStream();
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = inputStream.read(bytes);
String clientMessage = new String(bytes,0,len) ;
//展示数据
System.out.println("data from --->"+s.getInetAddress().getHostAddress()+
",内容是:"+clientMessage);
//服务器端反馈给客户端
//服务器端获取监听客户端所在通道内的输出流对象
OutputStream out = s.getOutputStream();
out.write("数据已接收到".getBytes()) ;
//释放资源
ss.close() ;
}
}
客户端/服务器端不断的去发送(接受)消息
例子
老师:
客户端
package com.qf.tcp_03;
import java.io.*;
import java.net.Socket;
/**
* TCP客户端不断的去发送消息,服务器端不断的去接收数据,展示数据(服务器端不关闭)
*/
public class TCPClient {
public static void main(String[] args) {
BufferedReader br = null ;
Socket s = null ;
try {
//创建客户端的Socket
s = new Socket("10.35.165.17",10086) ;
//客户端不断键盘录入数据--->BufferedReader一次读取一行
br = new BufferedReader(
new InputStreamReader(System.in)) ;
//获取客户端所在的通道内的字节输出流----OutputStream-->封装成字符缓冲输出流
OutputStream outputStream = s.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream)) ;
//一次读取一行
String line = null ;
while((line=br.readLine())!=null){
bw.write(line) ;//写一行,通过封装后的通道的字符输出流写进去
bw.newLine() ; //换行
bw.flush() ; //刷新
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(s!=null){
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务器端
package com.qf.tcp_03;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
*服务器端不断的去接收数据,展示数据(服务器端不关闭)
*/
public class TCPServer {
public static void main(String[] args) {
try {
//创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(10086) ;
//监听客户端连接
Socket s = ss.accept();
//封装通道内的字节输入流
BufferedReader br = new BufferedReader(
new InputStreamReader(s.getInputStream())) ;
String line = null ;
while((line=br.readLine())!=null){
//服务器自定义结束条件
if("886".equals(line)){
break ;
}
//展示数据
System.out.println("数据来自于"+s.getInetAddress().getHostAddress()+"内容是:"+line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
自己
客户端
package TCPDemo.test3;
import java.io.*;
import java.net.Socket;
public class Cilent_3 {
public static void main(String[] args) {
BufferedReader br =null;
Socket s=null;
try {
s= new Socket("10.35.165.80",1234);
br= new BufferedReader(new InputStreamReader(System.in));
OutputStream outputStream = s.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
String line=null;
while ((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush(); }
} catch (IOException e) {
e.printStackTrace();
}finally {
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(s!=null){
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务器端
package TCPDemo.test3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Serve_3 {
public static void main(String[] args) {
try {
ServerSocket ss = null;
ss = new ServerSocket(1234);
Socket s = ss.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println("数据来自: " + s.getInetAddress().getHostAddress() + "内容是:" + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
复制的课堂例子
需求:
* 客户端读取当前项目下的xx.java文件,写给服务器端,服务器端读取这个xx.java文件,将这个文件
* 复制到D:\EE_2302\day29\code\copy.java文件中
* 分析:
* 客户端---读取xx.java文件----->BufferedReader--->一次读取一行
* 获取客户端通道字节输出流--->BuffferedWriter-->写一行/换行/刷新
*
* 服务器---->读取客户端写过来的数据--->监听到指定客户端,获取客户端所在的通道内的字节输入流--->
* BufferedReader:一次读取一行
* 创建一个字符缓冲输出流:BufferedWriter---->一次写一行/换行/刷新--->
* 输出到D:\EE_2302\day29\code\copy.java
客户端
package TCPDemo.test4;
import java.io.*;
import java.net.Socket;
@SuppressWarnings("all")
public class Clinet_4 {
public static void main(String[] args) throws IOException {
//创建字符缓冲输入流--->读源文件
BufferedReader br=new BufferedReader(new FileReader("test_4_Test.java"));
//创建客户端的Socket对象
Socket s=new Socket("10.35.165.80",4321);
//获取通道内的字节输出流
OutputStream out=s.getOutputStream();
//将上面的字节输出流--->封装成字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
//一次读取一行内容--文件上传给服务器
String line=null;
while((line=br.readLine())!=null){
//通过通道内的流写给服务器
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
if(br!=null){
br.close();
}
if(s!=null){
s.close();
}
}
}
服务器端
package TCPDemo.test4;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
@SuppressWarnings("all")//压制警告
public class Serve_4 {
public static void main(String[] args) throws IOException {
//创建服务器端的Socket
ServerSocket ss = new ServerSocket(4321);
//监听客户端连接
Socket s = ss.accept();
//获取通道内的字节输入流
InputStream in = s.getInputStream();
//封装成字符缓冲输入流
BufferedReader br = new BufferedReader(new InputStreamReader(in));
//创建字符缓冲输出流对象--->操作目的地
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\QFday\\3c-日常\\6week\\DAY1\\code me\\copy.java"));
//一次读取一行
String line=null;
while ((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
if(bw!=null){
bw.close();
}
if(ss!=null){
ss.close();
}
}
}
复制图片并反馈
客户端
package com.qf.tcp_06;
import java.io.*;
import java.net.Socket;
/**
* 客户端上传图片文件--->读取当前项目xxx.jpg图片文件---写给服务器
*/
public class Client {
public static void main(String[] args) throws IOException {
//创建客户端的Socket
Socket s = new Socket("10.35.165.17",2222) ;
//创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("bug2.jpg")) ;
//获取通道内的字节输出流,----封装字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream()) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){
//给通道内的输出流写数据
bos.write(bytes,0,len) ;
//刷新
bos.flush();
}
//告知服务器端,没有流数据写过去
s.shutdownOutput();
//读取反馈
//获取客户端通道内的字节输入流
InputStream inputStream = s.getInputStream();
//一次读取一个字节数组
byte[] buffer = new byte[1024] ;
int length = inputStream.read(buffer);
String str = new String(buffer,0,length) ;
System.out.println("str:"+str) ;
//释放资源
bis.close();
s.close();
//关闭资源
}
}
服务器端
package com.qf.tcp_06;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务器端--读取传过来的xx.jpg图片文件---复制到指定磁盘上的 copy.jpg文件中,服务器端加入反馈(反馈客户端)!
* "图片文件复制完毕"
*/
public class Server {
public static void main(String[] args) throws IOException {
//服务器端的Socket
ServerSocket ss = new ServerSocket(2222) ;
//监听客户端连接
Socket s = ss.accept() ;
//获取通道内字节输出流---->封装字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(s.getInputStream()) ;
//创建字节缓冲输出流---写数据到指定文件中
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("D:\\EE_2302\\day29\\code\\my.jpg")) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
//刷新
bos.flush();
}
//服务器加入反馈
//获取通道内字节输出流
OutputStream outputStream = s.getOutputStream();
outputStream.write("图片上传完毕".getBytes()) ;
//释放资源
bos.close();
ss.close();
}
}
1.TCP和UDP的区别
1)是否建立连接通道
TCP需要建立连接通道的
UDP不需要建立连接通道
2)是否是可靠连接
TCP是可靠连接,---同步的---执行效率低
UDP是不可靠连接,---不同的--->执行效率高
3)是否传输数据大小有限制
TCP是无限制传输,以一种"字节流"的方式
UDP有数据大小限制(理论值64kb),以一种"数据报包"的方式
2.TCP客户端和服务器端进行数据传输的代码实现
//客户端
//1)创建客户端的Socket
Socket s = new Socket("10.35.165.17",6666) ;
//2)获取通道内的字节输出流
OutputStream out = s.getOutputStream() ;
//3)写数据
out.write("hello,TCP".getBytes()) ;
//4)释放资源
s.close() ;
//服务器端
//1)创建服务器端的Socket
ServerSocket ss = new ServerSocket(6666) ;
//2)监听客户端连接
Socket s = ss.accept() ;
//3)获取通道 的字节输入流对象
InputStream in = s.getInputStream() ;
//4)一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = in.read(bytes) ;
String message = new String(bytes,0,len) ;
//获取ip
String ip = s.getInetAddress().getHostAddress() ;
//展示数据
System.out.println("data from-->"+ip+",内容是:"+message) ;
//释放资源
ss.close() ;
3.获取一个类的字节码文件对象的方式
三种方式:
1)Object类的getClass()方法---->使用对象访问这个方法
2)任意Java类型的class属性
3)Class---->字节码文件对象(类对象)
public static Class forName(String classPathname) :参数是当前类或者接口的全限定名称
包名.类名
4.IO流的分类
BIO:阻塞式流
流的方向:
输入/输出
类型:
字节
字节输入流
InputStream
FileInputStream 文件字节输入流
BufferedInputStream:字节缓冲输入流
ObjectInputStream:反序列化
字节输出流
OutputStream
FileOutputStream 文件字节输出流
BufferedOutputStream:字节缓冲输出流
ObjectOutputStream:序列化
PrintStream:字节打印流
System.out.println("hello") ;
PrintStream ps = System.out ;
ps.println("xxx") ;
字符
字符输入流
Reader
InputStreamReader(InputStream in) --->将字节输入流转换字符输入流
便捷类---直接操作源文件FileReadr
BufferedReader:字符缓冲输入流
字符输出流
Writer
OutputStreamWriter(OutputStream out)--->将字节输出流转换成字符输出流
便捷类--->FileWriter
BufferedWriter:字符缓冲输出流
PrintWriter:字符打印流
NIO---->非阻塞式流----->
dubbo框架(发现服务/治理服务)--->底层NIO方式
5.如何读取src下面的xx.properties文件的内容(重要)
class Demo{
public static void main(String[] args){
//src下面---类路径下 user.properties (xx.proprties配置文件)
zhangsan=30
//获取配置文件所在字节输入流
InputStream in = Demo.class.getClassLoader().getResourceAsStream("user.properties") ;
//创建属性集合列表
Properties prop = new Properties() ;
//将配置文件的内容"以字节输入流"的方式加载到属性集合列表中
prop.load(in) ;
}
}
IO序列化/反序列化
序列化:
就是用ObjectOutputStream将一个java对象的信息写入流中
对象--->数据流(流数据)
构造方法:
public ObjectOutputStream(OutputStream out)
成员方法:
public final void writeObject(Object obj) :将指定的对象写入到序列化流中,变成流数据
反序列化
:就是使用ObjectInputStream将数据流中的内容--->还原成--->对象
构造方法:
public ObjectInputStream(InputStream in)
成员方法:
public final Object readObject():从反序列流中将数据流的内容----解析成对象
Serializable
uid:Seeting–Editor–Inspections–勾选除第三个以外的所有
只有支持 java.io.Serializable 接口的对象才能写入流中
当前这个类能够实现java.io.Serializable接口---->这个类对象就可以被序列化(对象---流数据)
java提供一些接口:如果字段,连成员方法都没有,都称为"标记接口"
序列化的时候--->当前类的所有的成员(成员变量/成员方法/构造方法等等)---->内存中--->"类的签名信息" 序列化的ID值
如果反序列化的时候,修改了类的成员信息,会导致和之前序列化的信息不一致:就会出现
java.io.InvalidClassException:序列化中的序列化版本UID值和反序列化的UID值不相同(类的签名信息不一致)
序列化:
简单理解就是把程序里面生成的对象以文件的形式保存到本地硬盘中,序列化写入文件的IO是ObjectOutputStream流。
反序列化:
就是把序列化的对象文件导入到程序中,并生成为一个对象,供程序使用。. 反序列化的读取对象文件的IO流是 ObjectInputStream。
例子老师
public class Student implements Serializable {
//生成固定的序列化的版本Id
private static final long serialVersionUID = -5794781839275410852L;
String name ;
transient int age ; //transient 让这个字段(属性) 不参与序列化和反序列化
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.io.*;
public class ObjectStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// write() ;
read() ;
}
//读:将流数据---还原成--->对象
public static void read() throws IOException, ClassNotFoundException {
//创建反序列化流对象ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ;
// public final Object readObject():从反序列流中将数据流的内容----解析成对象
Object object = ois.readObject();
//强转学生类型
Student s = (Student) object;
System.out.println(s.getName()+"---"+s.getAge());
}
//写数据
public static void write() throws IOException {
//创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt")) ;
//public final void writeObject(Object obj) :将指定的对象写入到序列化流中,变成流数据
//创建一个学生对象
Student s = new Student("高圆圆",44) ;
oos.writeObject(s) ;
//java.io.NotSerializableException: com.qf.io_01.Student 当前这个类未实现序列化异常
}
}
反射
源码时期 编译时期 运行时期
<?>:通配符 代表所有java类型什么是反射? (jvm正在编译的时候,就可以获取这个类所有的信息!)
(Reflection),一个类在加载运行状态的时候,可以动态获取正在运行的类(Class)
创建正在运行类的对象(构造器-Constructor)
使用正在运行的这个类的对象去给 成员变量赋值(Field)
调用正在运行的这个类中成员方法(Method)
反射应用----Java设计模式
正射 ----就是我们自己书写的代码的时候, 类名 对象名 = new 类名() ;
面试题:
获取一个类的字节码文件的方式?
1)调用Object类的getClass方法
2)任意Java类型的class属性
3)第三种方式:(推荐)
Java提供类Class:表示正在运行的Java应用程序中的类和接口
有一个静态方法:
参数:类的完全限定名称。 (包名.类名) String类型:经常用在配置文件中xx.properties public static Class<?> forName(String className):
以下例子的自定义类:
package ReflectDemo;
public class Person {
private String name ; //私有的姓名
int age ; //默认修饰符的年龄
private String address ; //住址
//公共的无参构造方法
public Person(){}
//默认修饰符的带有两个参数的构造方法
Person(String name,int age){
this.name = name ;
this.age = age ;
}
//私有的:带有三个参数的构造方法
private Person(String name,int age,String address){
this.name = name ;
this.age = age ;
this.address = address ;
}
//一堆成员方法
//是否带参/是否有返回值
public void show(String name){
System.out.println(name) ;
}
private String method(int number){
return "hello"+number ;
}
protected void function(){
System.out.println("function Person...");
}
//默认修饰符
String myFunction(){
return "今天天气不错!" ;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
例子:
package ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@SuppressWarnings("all")
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
System.out.println("正常的创建对象的格式");
Person p= new Person();
System.out.println(p);//Person{name='null', age=0, address='null'}
System.out.println("----反射的方式动态获类的构造器并创建实例-----");
//1)获取正在运行的类的字节码文件对象
Class c=Class.forName("ReflectDemo.Person");
System.out.println(c);//class ReflectDemo.Person
System.out.println("-----------");
//2)public Constructor<?>[] getConstructors()throws SecurityException
//获取正在运行的类中的所有公共的构造方法所在的Constructor类对象
Constructor[] con1 = c.getConstructors();
for(Constructor con1_1:con1){
System.out.println(con1_1);//public ReflectDemo.Person()
}
System.out.println("-----------");
//3)public Constructor<?>[] getDeclaredConstructors():
//获取正在运行的类中所有的构造方法所在的Constructor类对象
Constructor[] con2 = c.getDeclaredConstructors();
for(Constructor con2_1:con2){
System.out.println(con2_1);
/*public ReflectDemo.Person()
ReflectDemo.Person(java.lang.String,int)
private ReflectDemo.Person(java.lang.String,int,java.lang.String)*/
}
System.out.println("-----------");
//4)获取指定的构造器的Constructor对象,创建当前类实例
//public Constructor<T> getConstructor(Class<?>... parameterTypes)
//获取指定的公共的构造方法的Constructor对象,参数:指定参数类型的Class类对象,如果没有参数,可以不写
//举例:String类型的参数---java.lang.String
//int---->java.lang.Integer
Constructor con3 = c.getConstructor();
System.out.println(con3);//public ReflectDemo.Person()
System.out.println("-----------");
//Constructor提供一个方法
//参数:通过构造器里面给当前正在运行的类的成员变量赋值:实际参数
//返回T---Type:代表任意Java对象
//public T newInstance(Object... initargs):创建当前类实例
Object obj = con3.newInstance(); 多态 类似于 --->Object obj = new Person() ;
System.out.println(obj);//Person{name='null', age=0, address='null'}
Person p2= (Person) obj;
System.out.println(p2);//Person{name='null', age=0, address='null'}
System.out.println("----通过有参构造方法创建当前类实例-----");
//通过当前类的字节码文件对象获取指定的构造方法
//参数是:当前参数类型的字节码文件 String--->String.class
//public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
Constructor con4 = c.getDeclaredConstructor(String.class, int.class, String.class);
System.out.println(con4);//private ReflectDemo.Person(java.lang.String,int,java.lang.String)
//构造器所在Constructor继承AccessibleObject 提供
// public void setAccessible(boolean flag) :参数为true:取消Java语言访问检查
con4.setAccessible(true);
//public T newInstance(Object... initargs):创建当前类实例
Object obj2 = con4.newInstance("张三", 22, "河北");
System.out.println(obj2);
}//Person{name='张三', age=22, address='河北'}
public static void get (int ...a){/* //a代表多个参数:可变参数(当不明确具体有多个参数的时候,可以使用...*/}
}
反射获取对象给类的成员变量赋值
例子
现在通过反射获取指定的类的字节码文件对象并去给类的成员变量进行赋值
package ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> c = Class.forName("ReflectDemo.Person");
//Person类的无参构造方法本身就公共的
//两种方案创建类的实例
//1)获取这个类的无参构造方法所在的Constructor--->T newInstance()
Constructor constructor = c.getConstructor();
Object obj = constructor.newInstance();
System.out.println(obj);//Person{name='null', age=0, address='null'}
System.out.println("---------------");
//2)可以直接使用当前类的字节码文件对象Class--->public T newInstance()
Object obj2 = c.newInstance();
System.out.println(obj2);//Person{name='null', age=0, address='null'}
System.out.println("---------------");
//public Field[] getFields():获取所有的公共字段(成员变量)
//public Field[] getDeclaredFields():获c取"这个类"所有的字段所在的Field类对象,公共的/默认的/私有的/受保护的
Field[] field = c.getDeclaredFields();
for(Field field1:field){
System.out.println(field1);
/*private java.lang.String ReflectDemo.Person.name
int ReflectDemo.Person.age
private java.lang.String ReflectDemo.Person.address*/
}
System.out.println("---------------");
//通过这个类Class字节码文件对象获取当前类的指定的成员变量的Field类对象
//public Field getDeclaredField(String name) 参数:这个类的属性名称
Field nameField = c.getDeclaredField("name");
System.out.println(nameField);
nameField.setAccessible(true);
//Field:代表类的字段(成员变量)
//public void set(Object obj,Object value):将指定的value实际参数作用在指定的当前类的实例上
nameField.set(obj2,"陈六");
System.out.println(obj2);
Field ageField = c.getDeclaredField("age");
System.out.println(ageField);
ageField.setAccessible(true);
//Field:代表类的字段(成员变量)
//public void set(Object obj,Object value):将指定的value实际参数作用在指定的当前类的实例上
ageField.set(obj2, 18);
System.out.println(obj2);
Field addressField = c.getDeclaredField("address");
System.out.println(addressField);
addressField.setAccessible(true);
//Field:代表类的字段(成员变量)
//public void set(Object obj,Object value):将指定的value实际参数作用在指定的当前类的实例上
addressField.set(obj2, "河南");
System.out.println(obj2);
//取消Java语言访问检查功能
//私有成员变量名Field.setAccessible(true) ;
//java.lang.IllegalAccessException:非法访问:name 是私有的
}
}
动态获取-创建实例-调用-成员方法
指定类的字节码文件对象,当前类实例,调用里面的指定的成员方法!
invoke
主要是用来调用某个类中的方法的,但是他不是通过当前类直接去调用而是通过反射的机制去调用。
例子
package ReflectDemo;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@SuppressWarnings("all")
public class ReflectDemo4 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
System.out.println("以前的写法");
Person p = new Person() ;
p.show("helloworld") ;
System.out.println("------------");
System.out.println("反射的写法");
//1)获取指定类的字节码文件对象
Class<?> c= Class.forName("ReflectDemo.Person");
//2)无参构造方法公共的,Person类中,字节创建当前类实例
Object obj = c.newInstance();
//public Methods[] getMethods():获取这个类的所有的公共的成员方法的类对象Method(包括继承过来的以及实现接口的方法)
//public Methods[] getDeclaredMethods():获取类所有的的指定的成员方法的类对象Method,不包括继承过来的
//3)获取指定的公共成员方法
//public Method getMethod(String name,Class<?>... parameterTypes)
//参数1:方法的名称
//参数2:当前方法中参数类型的Class
Method m = c.getMethod("show", String.class);
System.out.println(m);//public void ReflectDemo.Person.show(java.lang.String)
System.out.println("------------");
//4)调用方法
//Method--->public Object invoke(Object obj,Object... args)
//参数1:这个类的实例
//参数2:方法中传入的实际参数
m.invoke(obj, "你好");
System.out.println("------------");
//调用method方法
//获取指定的成员方法所在的类对象Method
//public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
Method m2 = c.getDeclaredMethod("method", int.class);
System.out.println(m2);//private java.lang.String ReflectDemo.Person.method(int)
//取消Java语言检查
m2.setAccessible(true);
Object obj2 = m2.invoke(obj, 250);//Person的method方法返回值是hello+number
System.out.println(obj2);//hello250
}
}
反射的应用
属性集合列表里创建配置文件
开发原则:
所有开发原则必须"低耦合,高内聚"
开闭原则:
对现有代码的修改进行关闭,对扩展开放!
例子
package Reflect_Use;
public class Work {
public void love(){
System.out.println("爱赚钱");
}
}
package Reflect_Use;
public class Student {
public void love(){
System.out.println("我爱学习");
}
}
//src下创建"my.properties" 写入
//className=Reflect_Use.Work
//methodName=love
package com.qf.reflect_test_01;
import com.sun.corba.se.spi.orbutil.threadpool.Work;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 测试类
*
* 开发原则:所有开发原则必须"低耦合,高内聚"
* 开闭原则:
* 对现有代码的修改进行关闭,对扩展开放!
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
//使用学生的love方法
/*Student s = new Student() ;
s.love() ;*/
//切换为工人类的love方法
Worker worker = new Worker() ;
worker.love();
System.out.println("--------------------------------------------") ;
//使用反射:--src下面提供xx.properties配置文件
//创建一个属性集合列表---空的
Properties prop = new Properties() ;
//读取src下面的配置文件
InputStream inputStream = ReflectTest.class.getClassLoader().
getResourceAsStream("my.properties");
//将配置文件中的内容 以"字节输入流"的方式加载到属性集合列表中
prop.load(inputStream);
// System.out.println(prop);
//通过属性集合列表特有功能:getProperty(String key)---获取String value
String className = prop.getProperty("className");
//System.out.println(className) ;
String methodName = prop.getProperty("methodName");
//System.out.println(methodName);
//通过反射获取当前类的字节码文件对象
Class c = Class.forName(className) ;//class com.qf.reflect_test_01.Student
//直接创建当前类实例
Object obj = c.newInstance();//等价于字节码文件对象获取无参构造方法的Constructor类对象--->Object newIntance()
//System.out.println(obj);
//获取当前类的成员方法的method类对象
Method method = c.getDeclaredMethod(methodName);
//调用方法
method.invoke(obj) ;
}
}
1.什么是单例设计模式,有哪两种,分别什么有特点
单例设计模式:
始终在内存只有当前这个类或者接口的一个实例;
分为:
饿汉式和懒汉式
饿汉式的特点(不会出现安全的单例模式)
1)当前类具体类
2)这个一加载,就创建当前类的静态实例(私有的)
3)构造方法私有化--外界不能new
4)对外提供静态的公共方法,返回值是当前类本身!
懒汉式的特点(可能出现线程安全问题的一种单例设计模式)
1)当前类具体类
2)当类的成员位置:声明私有的当前类的变量 (静态的)
3)构造私有化--外界不能new
4)对外提供静态的公共方法,返回值是当前类本身
需要对当前类的变量进行判断,如果为null, 才创建当前类实例
返回当前类实例;
class Student{
private static Student s ;
private Student(){}
public synchronized static Student getInsance(){//静态的同步方法,解决线程安全问题!
if(s==null){//需要用的时候才创建对象
s = new Student() ;
}
return s ;
}
}
2.什么是反射?
某一个类在运行状态中,可以动态的获取类的字节码文件同时创建当前类实例,可以调用成员方法,去动态去给成员变量进行赋值;--满足Java语言特点 "动态性"----->就可以在运行过程某个类或者接口的成员方法,来完成一种
"动态代理"
jdk动态代理--->前提必须有接口
cglib动态代理
3.如何获取一个类的构造器并去创建对象?
//1)获取这个类的字节码文件对象
Class c = Class.forName("类的完全限定名称-就是包名.类名") ;
//2)获取私有的构造方法所在的Constructor类对象---带有两个参数:String和int类型
Constructor con = c.getDeclaredConstructor(String.class,int.class) ;
//3)防止出现非法访问异常--->取消Java语言访问检查
con.setAccessiable(true) ;
//4)创建当前类实例:使用Constructor类对象
Object obj = con.newInstance("高圆圆",44) ;
4.如何获取一个类的成员变量所在的Field类对象,去给成员变量赋值
//Constructor:代表构造方法的类对象
//Field:代表成员变量的Field类对象
//Method:代表成员方法的Method类对象
//---->如果访问私有,必须都取消java语言检查!
//1)获取这个类的字节码文件对象
Class c = Class.forName("类的完全限定名称-就是包名.类名") ;
//2)假设如果当前类的无参构造方法--公共的或者是你没用提供任何构造方法,系统会默认提供公共的无参构造方法
//可以直接创建当前类实例
Object obj = c.newInstance() ;
//3)获取私有的属性(成员变量)所在Field类对象
Field field = c.getDeclaredField("属性名称") ;//String类型的属性
//4)取消Java语言访问检查
field.setAccessiable(true) ;
//5)赋值
field.set(obj,"高圆圆") ;
5.获取一个类的成员方法所在的Method类对象,去调用方法
(使用反射思想解决问题—使用反射调用成员方法 (使用最多))
//1)获取这个类的字节码文件对象
Class c = Class.forName("类的完全限定名称-就是包名.类名") ;
//2)假设如果当前类的无参构造方法--公共的或者是你没用提供任何构造方法,系统会默认提供公共的无参构造方法
//可以直接创建当前类实例
Object obj = c.newInstance() ;
/*
public String getAnimalMessage(Animal a){
return a.getName() ; //获取动物名称;
}
*/
//3)通过字节码文件对象获取类的成员方法的Method类对象 --私有的
Method m = c.getDeclaredMethod("getAnimalMessage",Animal.class) ;//假设本身成员方法:带有一个参数 Animal类型(引用类型)
//4)取消Java语言访问检查
m.setAccessiable(true) ;
//调用方法
Class c2 = Class.forName("包名.Animal") ; //Animal类的无参构造方法也是公共的
Object obj2 = c2.newInstance();
Object returnObj = m.invoke(obj,obj2) ;
动态代理
代理设计模式---属于"结构型设计模式"
代理核心思想:代理角色帮助真实角色对他的业务功能进行增强!
静态代理
特点:代理角色和真实角色必须实现同一个接口!
弊端:业务功能和 增强的功能没有完全分离!
动态代理
1)jdk动态代理--->jdk提供 java.lang.reflect.Proxy:动态代理类
静态方法:
public static Object newProxyInstance(
ClassLoader loader, //获取接口实例的类加载器Class<?>[] interfaces,//实现的接口列表的字节码文件(代理类要的实现接口列表的Class)
InvocationHandler h)//有代理实例调用 接口方法的处理程序:接口类型 throws IllegalArgumentException
InvocationHandler:接口
public Object invoke(Object proxy, Method method, Object[] args)
参数1:调用该方法的代理实例
参数2:调用该代理实例的接口方法的Method类对象
参数3:调用接口方法中所有的参数,组成数组,没有参数,可不写
2)cglib动态代理----不需要接口,基于类就可以实现动态代理
必须提供第三方jar包 (cglib-3.3.0.jar)
例子
定义接口
public interface UserDao {
/**
* 添加用户操作
*/
void add() ;
/**
* 修改用户
*/
void update() ;
/**
* 删除用户
*/
void delete() ;
/**
* 查询所有用户
*/
void findAll() ;
}
实现InvocationHandler接口
public class MyInvocationHandler implements InvocationHandler {
//声明Object变量
private Object target ;
public MyInvocationHandler(Object target){// 传入真实角色
this.target = target ;
}
/**
* @param proxy 调用该方法的代理实例
* @param method 调用该代理实例的接口方法的Method类对象
* @param args 调用接口方法中所有的参数,组成数组,没有参数,可不写
* @return 从代理实例上的方法调用返回的值
* @throws Throwable 可能调用方法失败!
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("操作业务功能之前,先权限校验!" );
//反射的写法调用接口方法--add()/delete()/udate()/findAll()
Object obj = method.invoke(target, args);
System.out.println("操作业务功能之后,产生日志记录!");
return obj; //方法的返回值
}
}
定义接口实现类(代理角色)
public class UserDaoImpl implements UserDao{
/**
* 添加用户操作
*/
@Override
public void add() {
System.out.println("添加用户操作") ;
}
/**
* 修改用户
*/
@Override
public void update() {
System.out.println("修改用户") ;
}
/**
* 删除用户
*/
@Override
public void delete() {
System.out.println("删除用户") ;
}
/**
* 查询所用
*/
@Override
public void findAll() {
System.out.println("查询所有用户") ;
}
测试类
public class UserTest {
public static void main(String[] args) {
//创建UserDao接口对象---子实现类创建
UserDao ud = new UserDaoImpl() ; //ud:真实角色
//接口多态
InvocationHandler handler = new MyInvocationHandler(ud) ;
UserDao ud3 = (UserDao) Proxy.newProxyInstance(
ud.getClass().getClassLoader(),//代理实例要实现的接口的字节码文件---类加载
//代理实例要实现接口列表的Class--->Class类中--public 类<?>[] getInterfaces()
ud.getClass().getInterfaces(),
//代理实例调用接口方法的处理程序
handler);
ud3.add() ;
ud3.delete() ;
ud3.update() ;
ud3.findAll() ;
}
}
静态代理
例子
定义接口
package static_proxy;
public interface UserDao {
/**
* 添加用户操作
*/
void add() ;
/**
* 修改用户
*/
void update() ;
/**
* 删除用户
*/
void delete() ;
/**
* 查询所有用户
*/
void findAll() ;
}
代理角色
package static_proxy;
public class UserDaoImpl2 implements UserDao{
private UserDao ud ;
public UserDaoImpl2(UserDao ud){
this.ud = ud ;
}
@Override
public void add() {
System.out.println("权限校验") ; //---->第三方提供的: 系统监控代码 shiro框架:权限框架/SpringSecurity权限框架
//自己的业务功能
ud.add() ;
System.out.println("产生日志记录");
}
@Override
public void update() {
System.out.println("权限校验" ) ;
//自己的业务功能
ud.update(); ;
System.out.println("产生日志记录");
}
@Override
public void delete() {
System.out.println("权限校验" ) ;
//自己的业务功能
ud.delete(); ;
System.out.println("产生日志记录");
}
@Override
public void findAll() {
System.out.println("权限校验" ) ;
//自己的业务功能
ud.findAll(); ;
System.out.println("产生日志记录");
}
}
真实角色
package static_proxy;
public class UserDaoImpl implements UserDao{
/**
* 添加用户操作
*/
@Override
public void add() {
System.out.println("添加用户操作") ;
}
/**
* 修改用户
*/
@Override
public void update() {
System.out.println("修改用户") ;
}
/**
* 删除用户
*/
@Override
public void delete() {
System.out.println("删除用户") ;
}
/**
* 查询所用
*/
@Override
public void findAll() {
System.out.println("查询所有用户") ;
}
}
测试类
package static_proxy;
//静态代理
public class UserTest {
public static void main(String[] args) {
//创建UserDao接口对象---子实现类创建
UserDao ud = new UserDaoImpl() ; //ud:真实角色
ud.add() ;
ud.update() ;
ud.delete() ;
ud.findAll();
System.out.println("------------------优化后:对业务功能增强(静态代理)-------------------") ;
UserDaoImpl2 ud2 = new UserDaoImpl2(ud) ; //代理角色
ud2.add() ;
ud2.delete();
ud2.update();
ud2.findAll();
}
}
动态代理和静态代理的区别
静态代理:
需要代理对象和真实对象实现同一个接口,单独创建一个代理类实现接口
动态代理:
允许在运行期动态创建一个接口的实例; 动态代理是通过 Proxy 创建代理对象,然后将接口方法“代理”给 InvocationHandler 完成的。
1.什么是jdk动态代理/代码体现
/*
JDK动态代理-->Jdk提供的java.lang.reflect.Proxy 动态代理的类(前提必须有一个接口),动态的去调用(反射的方式)接口中方法,去对业务方法进行增强,同时产生代理角色!
*/
interface Love{
void love() ;
}
class LoveImpl implements Love{
public void love(){
//....
}
}
class MyInvocationHandler implements InvocationHandler{
private Object target;
public MyInvocationHandler(Object target){
this.target = target;
}
//重写invoke方法
public Object invoke(Object proxy,Method method,Object[] args){
//love()调用之前,系统监控代码
//增强的功能---调用接口的love()方法
Object obj = method.invoke(target,args) ;
//love()调用方法之后,完成一些事情
return obj ;
}
}
class Test{
public static void main(String[] args){
//接口的对象
Love love = new LoveImpl() ;
love.love() ;
//对love真实角色---产生代理角色
//java.lang.reflect.Proxy
Love love2 =(Love)Proxy.newInstance(
love.getClass().getClassLoader(),
love.getClass().getInterfaces(),
new MyInvocationHandler(真实角色对象)) ; //第三个参数:代理实例需要调用接口方法的处理程序---接口InvocationHandler
love2.love() ;
}
}
---->JDBC----三阶段高级jdbc(框架Mybatis /MP)---->全部都是jdk动态代理实现
MyBatis--->MapperProxy:mybatis代理的类
2.数据库定义语句DDL之创建库/删除库/修改库字符集/查询库字符集语法
/*
多行注释
*/
-- 单行注释
# 特殊注释
-- 创建库
create database 库名;
create database if not exists 库名;
-- 删除库
drop database 库名;
drop database if exists 库名;
-- 查询库的字符集
show create database 库名;
-- 修改库的字符集
alter database 库名 (default) character 字符集格式;
-- 查询msyql服务器中已知存在的所有的库
show databases ;
-- 使用哪一个库
use 库名;
-- mysql自带的函数--查询正在使用的库的名称
select database() ;
3.创建表/删除表/修改表相关语法/删除表
-- 创建表
create table 表名(
字段名称1 字段类型1,
字段名称2 字段类型2,
字段名称3 字段类型3,
...
字段名称n 字段类型n
);
-- 删除表
drop table 表名;
drop table if exists 表名;
-- 修改表的字段
alter table 表名 change 字段名称 新的字段名称 以前的字段类型;
-- 修改表的字段类型 modify
alter table 表名 modify 字段名称 新的字段类型;
-- 修改表:添加新的字段
alter table 表名 add 字段名称 字段类型;
-- 修改表:删除某一列(字段)
alter table 表名 drop 字段名称;
-- 查询表的结构
desc 表名;
-- 查询指定库的所有表
show tables ;
-- 复制表
create table 新表名 like 旧表名;
4.线程的创建方式
1)继承关系
a)自定一个类继承自Thread
b)重写run方法
c)在用户线程中创建当前类对象(设置线程名称),启动线程
2)实现Runnable接口
a)自定义一个类实现Runnable接口
b)重写run
c)在用户线程main创建Runnable接口实现类对象(资源类),创建Thread类对象,将资源类对象作为参数传递,启动线程
3)线程池
ExecutorService pool = Executors.newFixedThreadPool(几条线程) ;--->创建固定可重用的线程数的线程池
//提交异步任务
要么pool.submit(Runnable接口对象) ;--->计算异步的结果---Future
要么pool.submit(Callable接口对象) ;---->Future
关闭线程池
pool.shutdown() ;
5.通过反射如何获取一个类的成员方法的Method类对象并调用
//获取字节码文件对象
Class c = Class.forName("包名.类名") ;
//创建当前类实例--公共的无参构造方法/创建Constructor了对象
Object obj = c.newIntance() ;
//获取成员方法的类对象Method
Method m = c.getDeclaredMethod("方法名",形式参数类型.class) ;
//取消java访问检查
m.setAccessiable(true) ;
//调用
m.invoke(obj,实际参数) ;
子父关系:
子类继承父类:要在父类中调用子类的方法----通过反射的去完成!
父类调用子类方法
子类继承父类:
在父类去调用子类的方法
反射的最终目的:解耦 (降低耦合性)
例子
父类
public class Father {
public void show(String str) throws Exception{
// System.out.println("Father"+str);
//创建子类对象
//Father s = new Son() ; //实际开发中:不推荐这种写法 (存在耦合)
//Son s = new Son() ;//销毁资源
//通过反射的方式:
//获取到正在运行的Son类
Class c = Class.forName("com.demo.Son") ;
//直接创建当前类的实例
Object obj = c.newInstance() ;
//通过字节码文件对象获取子类的方法所在Method类对象
Method m = c.getDeclaredMethod("show", String.class) ;
m.setAccessible(true) ;
m.invoke(obj,"hello") ;
Method m2 = c.getDeclaredMethod("function", int.class);
m.setAccessible(true) ;
m2.invoke(obj,100) ;
}
子类
public class Son extends Father{
@Override
public void show(String str) {
System.out.println("son"+str);
}
//子类有自己的特有功能
public void function(int number){
System.out.println(number) ;
}
}
测试类
public static void main(String[] args) throws Exception {
Father f = new Father();
f.show("100") ;
}
}
MySQL-DDL(定义)语句
终端(管理员)dos窗口中
net start mysql57:先开启服务
mysql -uroot -p
net stop mysql57:关闭服务
数据库
存储的数据的仓库
最早开始存储:定义临时变量 int a = 10 ;
数组存储数据:弊端长度固定
StringBuffer:可以存储数据---字符序列---->转换成String
Collection/Map:存储一些元素或者键值元素,集合创建完毕,使用完毕也就不存在了
IO流可以永久的存储数据,IO--写数据---读数据(字节流)耗时 (本地存储)
常见的数据库
使用数据库DataBase(db)存储数据 :
分为:
关系型数据库: 支持"事务系统":读/写的过程:保证数据的安全性/一致性
支持高并发访问 :同时多个同时访问数据库
存储大类型数据: 大字节类型/大字字符类型
oracle/sqlServer/MySql/SQLite(轻量级的关系型数据库)/mariaDB
中小型企业:mysql居多 (InnoDB---存储引擎:支持行级锁)
大公司:Oracle居多
非关系数据库
常见noSQL数据库:没有sql语句
redis---->作为 "缓存用户的" (三阶段后期/四阶段前期)
key-value元素存储进去
常见关系型数据库的sql语法(操作数据库定义语句DDL)
操作库:
1)查询所有库
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema | --mysql自带配置信息相关的库文件(_schema约束文件)
| mysql | --有mysql里面user用户相关的信息表数据:root用户或者添加普通用户
| performance_schema | --mysql数据库本身自带的优化mysql性能相关的
| sys | -- mysql系统相关的库文件
+--------------------+
4 rows in set (0.00 sec)
2)创建一个新的数据库
2.1)create database 库名; 直接创建
mysql> create database mydb01;
Query OK, 1 row affected (0.01 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb01 |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
2.2)create database if not exists 库名; 判断不存在库才创建
mysql> create database if not exists mydb_02;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb01 |
| mydb_02 |
| mysql |
| performance_schema |
| sys |
+--------------------+
6 rows in set (0.00 sec)
3)查看创建指定库的字符集编码格式
show create database 库名;
mysql> show create database mydb01;
+----------+-------------------------------------------------------------------+
| Database | Create Database |
+----------+-------------------------------------------------------------------+
| mydb01 | CREATE DATABASE `mydb01` /*!40100 DEFAULT CHARACTER SET latin1 */ |
+----------+-------------------------------------------------------------------+
1 row in set (0.01 sec)
4)修改指定库的字符集格式
alter database 库名 (default) character set 字符集格式;
mysql> alter database mydb01 default character set = utf8;
Query OK, 1 row affected (0.01 sec)
mysql> show create database mydb01;
+----------+-----------------------------------------------------------------+
| Database | Create Database |
+----------+-----------------------------------------------------------------+
| mydb01 | CREATE DATABASE `mydb01` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+----------+-----------------------------------------------------------------+
5)删除库
5.1)drop database 库名; 直接删除
mysql> drop database mydb01;
Query OK, 0 rows affected (0.03 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb_02 |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
5.2)drop database if exists 库名; 判断存在库名,直接删除
mysql> drop database if exists mydb_02;
Query OK, 0 rows affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
mysql>
6)进入到指定的库中–使用这个库
use 库名;---提示 "database changed"表示正在使用
mysql> use ee2302;
Database changed
7)mysql字段的查询正在使用库的函数 select database() ;
mysql> select database();
+------------+
| database() |
+------------+
| ee2302 |
+------------+
1 row in set (0.00 sec)
操作表
数据库中表的创建/删除/修改/查询DDL语句
常见的数据表的字段类型:
1)int :整数类型 默认11位 里面所占的实际值 (推荐)
age年龄字段 ----20
int(指定的字符数)
int(3) 指定当前值的位数3位 1--->001 (不推荐,八进制0开头)
2)字符串 varchar(指定最大长度)
name字段---varchar(10) :最大长度10位
3)小数类型 double
double(3,1)---小数是3位,小数点后保留一位数
4)日期类型 date 仅仅表示日期
datetime:日期+时间
timestamp:时间戳(某个时间点去操作了数据库中某个表或者某个表的记录的时间 )或者瞬时时间
1)创建表的语法
----创建表之前,选库(use 库名;)
create table 表名(
字段名称1 字段类型1,
字段名称2 字段类型2,
字段名称3 字段类型3,
...,
字段名称n 字段类型n) ;
创建一个学生表
mysql> create table student(
-> id int,
-> name varchar(10),
-> age int,
-> gender varchar(3),
-> address varchar(50),
-> birthday date);
Query OK, 0 rows affected (0.05 sec)
不能最后一行单独的);
2.查询库中的所有表
show tablse;
mysql> show tables;
+------------------+
| Tables_in_ee2302 |
+------------------+
| student |
+------------------+
1 row in set (0.00 sec)
3.查询表的结构:查询这个表的"列"有哪些
desc 表名;
mysql> create table student(
-> id int,
-> name varchar(10),
-> age int,
-> gender varchar(3),
-> address varchar(50),
-> birthday date);
Query OK, 0 rows affected (0.05 sec)
mysql> show tables;
+------------------+
| Tables_in_ee2302 |
+------------------+
| student |
+------------------+
1 row in set (0.00 sec)
4.修改表—修改的表的字段名称(列的名称)
ALTER TABLE 旧表名 RENAME 新表名;”语句来修改表名
alter table 表名 change 以前的字段名称 现在的新的字段名称 以前的字段类型;
mysql> alter table student change gender sex varchar(3);
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(10) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| sex | varchar(3) | YES | | NULL | |
| address | varchar(50) | YES | | NULL | |
| birthday | date | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
4.1 修改表---修改表的字段类型
alter table 表名 modify address 对应的新的字段类型;
mysql> alter table student modify address varchar(20) ;
Query OK, 0 rows affected (0.08 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(10) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| sex | varchar(3) | YES | | NULL | |
| address | varchar(20) | YES | | NULL | |
| birthday | date | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
6 rows in set (0.01 sec)
4.2)修改表---给表中添加一个新的字段名称(新的列)
alter table 表名 add 字段名称 字段类型;
mysql> alter table student add zipcode varchar(10) ;
Query OK, 0 rows affected (0.07 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(10) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| sex | varchar(3) | YES | | NULL | |
| address | varchar(20) | YES | | NULL | |
| birthday | date | YES | | NULL | |
| zipcode | varchar(10) | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
7 rows in set (0.00 sec)
4.3)修改表---将表中的字段名称删除
alter table 表名 drop 字段名称;
mysql> alter table student drop zipcode;
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(10) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| sex | varchar(3) | YES | | NULL | |
| address | varchar(20) | YES | | NULL | |
| birthday | date | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
5.复制一张一模一样的表
create table 新的表名 like 旧表名;
mysql> create table teacher like student;
Query OK, 0 rows affected (0.03 sec)
mysql> show tables;
+------------------+
| Tables_in_ee2302 |
+------------------+
| student |
| teacher |
+------------------+
2 rows in set (0.00 sec)
mysql> desc teacher;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(10) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| sex | varchar(3) | YES | | NULL | |
| address | varchar(20) | YES | | NULL | |
| birthday | date | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
6.删除表
6.1)drop table 表名 ;直接删除
mysql> drop table teacher;
Query OK, 0 rows affected (0.02 sec)
mysql> show tables;
+------------------+
| Tables_in_ee2302 |
+------------------+
| student |
+------------------+
1 row in set (0.00 sec)
6.2)drop table if exists 表名; 如果存在这个表,删除
mysql> drop table if exists student;
Query OK, 0 rows affected (0.02 sec)
mysql> show tables;
Empty set (0.00 sec)
mysql>
MySQL-DML(操作)语句
基本操作
-- 单行注释
/*
多行注释
*/
# 特殊注释
-- 给表中插入全表记录: 每一个值需要和当前字段类型一致
-- 1)insert into 表名 values(值1,值2,值3,....值n) ;
INSERT INTO student VALUES(1,'张三丰',50,'1973-03-10','西安市南窑国际','男') ;
-- 2)插入全表记录:一次性插入多条
-- insert into 表名 valeues(值1,值2,值3...值n),(值1,值2,值3...值n),(...) ;
INSERT INTO student VALUES(2,'高圆圆',44,'1981-10-30','鄠邑区','女'),
(3,'张三',23,'2000-11-20','南窑国际','男') ;
插入表中记录注意事项:
1)插入全表字段的值的数量要和字段的数量匹配
2)插入同类型字段要注意(字段名称和字段值的类型必须匹配)
-- 删除的表记录
-- 1)一般带条件删除(基本带条件)
-- delete from 表名 where 字段名称= 值 ;
-- delete from 表名 where 字段名称= 值1 and 字段名称2 = 值2 ;
--满足多个条件:删除 (如果这个字段是业务字段:可能会出现重复)
-- 不带条件删除
-- delete from 表名; :删除全表记录(删除了所有的信息 但是架构还在)
-- 删除全表记录的:truncate table 表名
-- 3)插入部分字段,没有插入字段的值默认值null
-- insert into 表名(字段名称1,字段名称2,部分字段) values(值1,值2,部分字段值),(值1,值2,部分字段值) ;
-- 删除id为x的学生信息
DELETE FROM student WHERE id = x ;
-- 删除名称为xxx的学生信息 (通过业务字段删除)+附件条件 同时年龄20 的
DELETE FROM student WHERE NAME = 'xxx' AND age = 20 ;
基本查询全表数据 select * (所有字段名称) from 表名;
开发中: *:不写*, 必须写上所有的字段名称
delete from 表名和truncate table 表名的区别?
删除全表
delete from 表名和truncate table 表名的区别?
共同点:
delete from 表名和truncate table 表名都是删除全表的数据
不同点:
delete from 表名:仅仅只是删除表的记录,表的结构还在(约束相关的信息也就在)
不影响自增主键id的值,下一次插入数据之后,id值在上一次基础上继续自增!
truncate table 表名:不仅仅删除全表的记录,
会将表删除(表的结构都不存在了),会自己创建一张结构一一的空表
直接影响自增长主键id 的值;
修改表update
-- mysql内置函数:可以获取最后一次自增长的id值
-- 插入之后,记录最后一次自增长id的值
SELECT LAST_INSERT_ID() ;
-- 修改表的记录 update关键字
-- 1)不带条件(where)修改,属于 "批量修改" (不推荐)
-- update 表名 set 字段名称 = 值 ;或者 update 表名 set 字段名称1= 值1 ,字段名称2 = 值2;
-- 2)推荐:带条件修改
-- update 表名 set 字段名称1 = 值1,字段名称2=值2,...字段名称n = 值n where 非业务字段名称= 值;
MySQL-DQL(查询)语句
-- 基本DQL语句:查询全表:一般写全部字段(开发中)
-- 1)查询表的记录
-- select 字段名称1,字段名称2,部分字段名称或者指定所有字段名称 from 表名;
CREATE TABLE test4 (
id INT, -- 编号
NAME VARCHAR(20), -- 姓名
age INT, -- 年龄
sex VARCHAR(5), -- 性别
address VARCHAR(100), -- 地址
math INT, -- 数学成绩
english INT -- 英语成
);
-- 2)查询的时候给字段名称起别名
INSERT INTO test4(id,NAME,age,sex,address,math,english) VALUES
(1,'马云',55,'男',' 杭州',66,78),
(2,'马化腾',45,'女','深圳',98,87),
(3,'马景涛',55,'男','香港',56,77),
(4,'柳岩 ',20,'女','湖南',76,65),
(5,'柳青',20,'男','湖南',86,NULL),
(6,'刘德华',57,'男','香港 ',99,99),
(7,'马德',22,'女','香港',99,99),
(8,'德玛西亚',18,'男','南京',56,65);
//此时的字段名称都是英语
SELECT *FROM test4;
//''可以给字段名称起别名
SELECT
id'编号',
NAME'姓名',
age'年龄',
sex'性别',
address'地址',
math'数学成绩',
english '英语成绩'
FROM
test4 ;
-- 3)基本select查询,去除重复的字段值
-- (字段名称前面加上DISTINCT:去除重复字段值)
-- 或者distinct(字段名称)
SELECT DISTINCT 字段名 '别名'FROM 表名;
-- 3)基本查询:对int类型字段值进行求和
-- 需求:查询每一个学生的总成绩信息,姓名和总成绩
使用mysql自带的函数:
ifnull(字段名称,预期值) ;如果当前字段值是null,给一个预期值
SELECT
NAME '姓名',
(math+IFNULL(english,0))'总成绩'
FROM
test4;
例子是上面的马云...的例子 其中柳青的英语成绩是null 可以理解是缺考了 但是如果不给预期值 直接加 就会得出null 。
姓名 总成绩
马云 144
马化腾 185
马景涛 133
柳岩 141
柳青 86
刘德华 198
马德 198
德玛西亚 121
-- DQL之条件查询
/*
1)基本条件 where条件
赋值运算符
比较运算符:<,><=,>=,!=,<>,BETWEEN 值1 AND 值2
逻辑运算符:&& || and or
*/
-- 需求:年龄是20岁的学生所有信息
SELECT
id'编号',
NAME'姓名',
age'年龄',
sex'性别',
address'地址',
math'数学成绩',
english '英语成绩'
FROM
test4
WHERE age=20;
条件语句写表名后面
-- 需求2):查询年龄小于等于20岁的学生信息
SELECT
*
FROM
test4
WHERE age < 20
OR age = 20 ;
用or或者||衔接;
-- 需求3):查询年龄 30岁到55岁之间的所有学生信息
SELECT
*
FROM
test4
WHERE age >= 30
AND age <= 55 ;
用&&或者and衔接;
或者--------
SELECT
*
FROM
test4
WHERE age BETWEEN 30
AND 55 ;
-- 需求4):查询年龄不是20岁的学生信息
SELECT
*
FROM
student3
WHERE age != 20 ;
SELECT
*
FROM
student3
WHERE
age <> 20 ;-- 也可以<> 代表不等于
聚合函数
-- 2)聚合函数:查询出来结果:单行单列的数据
-- count(字段名称):统计总条数 --->count(非业务字段) :统计总记录数
-- max(字段名称):最大值
-- min(字段名称):最小值
-- avg(字段名称):平均分
-- sum(字段名称):求和
不用带* 号在字段后面
-- 需求:查询学生的总人数
SELECT SUM(math)'数学成绩' FROM test4;
SELECT COUNT(id)'编号'FROM test4;
SELECT MIN(math)'数学成绩'FROM test4;
SELECT MAX(math)'数学成绩'FROM test4;
SELECT AVG(math)'数学成绩'FROM test4;
-- 需求:查询数学成绩的最高分的学生信息
SELECT
*
FROM
test4
WHERE math=
(SELECT
MAX(math) '数学成绩'
FROM
test4 );
-- 需求:查询出大于等于英语平均分的学生信息
-- 1)英语平均分是多少
SELECT
AVG(IFNULL(english, 0)) '英语平均分'
FROM
test4 ;
一步到位:
SELECT
*
FROM
test4
WHERE english >=
(SELECT
AVG(IFNULL(english, 0)) '英语平均分'
FROM
test4
) ;
排序
升序 语法:select 字段名1,字段名2,字段名3 from 表名 (where 条件)order by (字段)
降序 语法:select 字段名1,字段名2,字段名3 from 表名 (where 条件)order by (字段)desc ;
模糊查询
-- 模糊查询(重点)
-- 应用场景:前端搜索框---输入 "关键字"---进行搜索
-- select 字段列表 from 表名 where 字段名称 like '%关键字%' ; '关键字%'
-- %:代表任意字符
-- _:代表一个字符
-- 需求:查询student3中所有的姓柳的人
SELECT*FROM test4 WHERE NAME LIKE '柳%';
-- 需求:查询student3中姓名是3个字符的人
一个下划线"_"代表一个字符
SELECT *FROM test4 WHERE NAME LIKE '___';
-- mysql 查询全局的字符集的命令
SHOW VARIABLES LIKE '%character%' ;
Variable_name Value
character_set_client utf8
character_set_connection utf8
character_set_database utf8
character_set_filesystem binary
character_set_results utf8
character_set_server utf8
character_set_system utf8
character_sets_dir D:\\MySQL\\share\\charsets\\
条件查询 in关键字
-- 条件查询---in关键字 (值1,值2,值3,,,值n) ; 代表多个or连接(多个并集查询)
-- 需求:查询student3表中年龄是18或者30或者45岁的学生的所有信息
SELECT
*
FROM
student3
WHERE age IN(18,30,45) ;
-- 条件查询:查询指定的字段是null的信息
-- select 字段列表 from 表名 where 某个字段名称 is null ;
-- 需求:查询student3表中english是null的学生的所有信息
SELECT
*
FROM
student3
WHERE english IS NULL ;
-- 查询某个字段不是null的信息: is not null
-- 需求: 查询英语成绩不是null的学生的所有信息
SELECT
*
FROM
student3
WHERE english IS NOT NULL ;
排序查询 order by/desc/asc
-- select 字段列表 from 表名 order by 字段名称 排序规则;
-- 排序规则:默认值asc :升序
-- desc:降序
-- 多个排序条件
-- select 字段列表 from 表名 order by 字段名称1 排序规则,字段名称2 排序规则...;
-- 需求:按照学生的数学成绩升序排序
SELECT
*
FROM
student3
ORDER BY math ;
-- 需求:查询学生的所有信息,同时 数学成绩降序,英语成绩升序 获取学生所有信息
SELECT
*
FROM
student3
ORDER BY math,
english ASC ;
-- 需求:查询数学成绩大于70分的学生的所有信息并且进行升序排序
-- where 在 order by的前面
SELECT
*
FROM
student3
WHERE math > 70
ORDER BY math ;
分组查询 group by
-- DQL语句之分组查询:group by
/**
group by 后面跟分组字段,在select的时候-查询分组字段
聚合函数在group by的后面不能使用
*/
-- select 字段列表 from 表名 group by 字段名称; -- 分组基本查询
-- 一般情况:group by 和where 搭配使用
-- 需求:按照性别分组,查询数学习成绩大于70分的信息;
-- 数学成绩的大于70分的人参与分组,查询出每一组的数学平均分
-- 先满足条件:当前where 和group by一块使用,先执行where ,然后在执行group by 分组字段;
SELECT
sex '性别',
AVG(math)
FROM
student3
WHERE math > 70
GROUP BY sex ;
筛选 having
-- 筛选having
/*
一个sql语句,where条件,有分组group by ,有having(它的后面可以跟聚合函数)
依次先后顺序,先满足where,在分组,分组基础上进行筛选
*/
需求:查询(分组信息/数学平均分/人数大于2的) 学生的数学成绩大于70分的参与分组(按照性别分组), 同时,筛选出某组人数大于2的一组!
SELECT
sex '性别', -- 查询分组字段
AVG(math) '数学平均分',
COUNT(id) '总人数' -- 查询满足筛选条件的信息
FROM
student3
WHERE math > 70
GROUP BY sex
HAVING
COUNT(id)> 2 ;
分页查询limit
-- DQL语句分页查询
-- limit关键字
-- 语法:select 字段列表 from 表名 limit 起始行数,每页显示的条数;
起始行数:从0开始 --- = (当前页码数-1)*每页显示的条数;
-- 实际开发中:页码数/每页显示的条数 (必须已知数据:前端传来的)
-- student3:9条数据,已知:每页显示2条,
-- student3的第一页的数据;
SELECT * FROM student3 LIMIT 0,2 ;
-- 第二页数据
SELECT * FROM student3 LIMIT 2,2 ;
-- 第三页数据
SELECT * FROM student3 LIMIT 4,2;
-- 第四页数据
SELECT * FROM student3 LIMIT 6,2 ;
-- 第五也数据
SELECT * FROM student3 LIMIT 8,2;
-- 查询 这个表的时候限制指定的条数
-- select * from 表名 limit 值;
SELECT * FROM student3 LIMIT 4;
数据库的备份和还原
-- 数据库的备份和还原
/*
DBA(数据库管理员) 设计完数据库,对库中的所有的数据以及结构进行备份;
防止数据库的信息丢失等等
数据库还原:就是将备份库 加载已知存在的库中(使用备份好.sql脚本文件,执行这个sql脚本)
备份和还原:
1)命令行方式的备份(dos管理员的运行)
mysqldump -uroot -p输入密码 库名称 > 指定磁盘路径 "d:\\mysql_dump\\xx.sql文件"
1)命令行方式的还原:
1.1)dos登录mysql,将之前的库删除
1.2)新建一个新的库,使用库
1.3)source 执行本地或者远程路径上的.sql文件 (加载指定的sql脚本文件)
2)使用图形界面化工具去备份和还原
任何的图形界面化工具都是一样的
备份:
在已知的库上右键---backup--->backup SQL 备份到本地磁盘上的sql脚本
还原:删除已知库,新建库,使用库, 存在库上右键--->import--->选择执行 execute SQL script 本地sql文件
*/
CREATE DATABASE myee_2302;
约束
默认约束:default
-- 数据库的约束
-- 约束:限制用户操作数据库的行为
-- 举例:插入数据的时候,直接插入null值(这个字段是业务字段,是null,,没有意义)
-- 通过约束将上面的用户插入非法数据的行为进行限制!
CREATE TABLE test(
id INT, -- 编号
NAME VARCHAR(10), -- 姓名
gender VARCHAR(3) DEFAULT '女' -- 性别 使用默认约束default关键字
) ;
INSERT INTO test VALUES(1,'高圆圆','女'),(2,'文章','男') ;
-- 插入部分字段
-- insert into test(id,name) values(3,'张佳宁') ; -- 没有插入的数据null值(没有意义)
-- 添加默认约束后,没有插入的字段:默认约束default起作用!
INSERT INTO test(id,NAME) VALUES(3,'张佳宁') ;
INSERT INTO test(id,NAME,gender) VALUES(4,'刘亦菲',NULL) ; -- 直接插入null值,default无法约束
非空约束:not null
例子:
CREATE TABLE test(
id INT , -- 编号
NAME VARCHAR(10) NOT NULL , -- 姓名 不能为空
address VARCHAR(50) NOT NULL -- 地址 不能为空
);
CREATE TABLE test6 (
id INT,
NAME VARCHAR (10) NOT NULL,
address VARCHAR (20) NOT NULL
) ;
INSERT INTO test6 VALUES(1,'张三丰','武当山'),(2,'令狐冲','泰山') ;
INSERT INTO test6 VALUES(3,'张国荣','香港');
-- insert into test6 values(4,'于和伟');-- 会报错 因为没给字段的值
-- sql更改表的默认约束
-- 删除name字段非空约束
ALTER TABLE test6 MODIFY NAME VARCHAR(10);
-- sql 添加字段的not null非空约束
ALTER TABLE test MODIFY NAME VARCHAR(10) NOT NULL ;
Column 'NAME' cannot be null
唯一约束unique不能重复
CREATE TABLE test(
id INT, -- 编号
NAME VARCHAR(10), -- 姓名
telephone VARCHAR(11) UNIQUE -- 手机号 创建表,给手机号添加唯一
) ;
INSERT INTO test VALUES
(1,'高圆圆','13366668888'),
(2,'文章','18788886666') ;
-- 插入一个数据
-- (非法数据了手机号或者邮箱/能够去代表身份信息必须唯一的!)
-- 如果使用手机号获取用户信息:获取不到了!
INSERT INTO test VALUES(3,'李国栋','13366668888') ; -- Duplicate entry '13366668888' for key 'telephone'
-- 通过sql语句更改表,删除唯一约束
-- 错误语法: alter table test modify telephone varchar(11) ;
-- 正常语法:alter table 表名 drop index 唯一约束的名称 (默认和当前的字段名称一致)
ALTER TABLE test DROP INDEX telephone ;
-- 通过sql添加唯一约束
-- alter table 表名 add constraint(声明) 唯一约束索引的名称 unique(给哪个字段名称);
ALTER TABLE test
ADD CONSTRAINT unique_telephone UNIQUE (telephone) ;
例子自己:
CREATE TABLE test7 (
id INT,
-- 编号
NAME VARCHAR (10),
-- 姓名
idcard VARCHAR (50) UNIQUE -- 加了约束了 -- 身份证号(是唯一的!)
) ;
INSERT INTO test7 (id, NAME, idcard)
VALUES
(
1,
'孙悟空',
'610623100008081237'
),
(
2,
'沙悟净',
'610623121107081797'
) ;
DROP TABLE test7;
INSERT INTO test7 VALUES(3,'猪八戒','610623121107081797');-- 没加约束的时候是可以正常出现的
INSERT INTO test7 VALUES(3,'猪八戒','610623121107081797');-- 加了约束就不行了
DELETE FROM test7 WHERE id=3;
-- 通过sql语句更改表,删除唯一删除
ALTER TABLE test7 DROP INDEX idcard;
-- 通过sql添加唯一约束
ALTER TABLE test7 ADD CONSTRAINT UNIQUE(idcard);
SELECT
id '编号',
NAME '姓名',
idcard '身份证号码'
FROM
test7 ;
主键约束 primary key
自增 AUTO_INCREMENT
-- 2.4 主键约束 primary key
/*
特点:非空且唯一
不能直接插入null值,不能重复
primary key 作用每一张表的id(非业务字段,设置为主键)
*/
CREATE TABLE test(
id INT PRIMARY KEY AUTO_INCREMENT, -- 创建表的时候添加主键,同时自增长
NAME VARCHAR(10)
);
-- insert into test values(1,'张三'),(2,'李四') ;
INSERT INTO test(NAME) VALUES('张三'),('李四') ;
INSERT INTO test VALUES(1,'王五') ;-- id重复 无法添加
INSERT INTO test VALUES(NULL,'赵六') ;-- 直接插入null值 .无法获取
-- sql语句直接主键删除
-- alter table 表名 drop primary key ; -- 将唯一删除,not null非空还存在
ALTER TABLE test DROP PRIMARY KEY ;
-- sql语句添加主键约束(唯一特性加上)
-- alter table 表名 modify id int PRIMARY KEY ;
ALTER TABLE test MODIFY id INT PRIMARY KEY ;
-- 一般主键约束和自增长约束auto_increment,一块用
-- 插入数据insert into 之后,id值不断自增1!
外键约束 foreign key
-- 2.5 外键约束 foreign key
-- 创建一个员工表employee
CREATE TABLE emplyee(
id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
NAME VARCHAR(10), -- 员工姓名
gender VARCHAR(3) , -- 员工性别
salary DOUBLE, -- 员工工资
dept_name VARCHAR(10) -- 员工所在的部门名称
) ;
INSERT INTO emplyee(NAME,gender,salary,dept_name)
VALUES('高圆圆','女',8000.0,'测试部'),
('文章','男',6500.0,'运维部'),
('李国栋','男',10000.0,'开发部'),
('马保国','男',6000.0,'安保部'),
('张三丰','男',9500.0,'开发部'),
('邓超','男',7000.0,'运维部'),
('张佳宁','女',9500.0,'测试部');
SELECT * FROM emplyee ;
-- 查询员工的姓名以及它部门名称信息
SELECT
NAME '员工姓名',
dept_name '部门名称'
FROM emplyee ;
-- 问题:查询员工信息,部门名称查询"字段冗余",大量重复数据不友好!
-- 如何优化呢, 创建两张表,分别描述两件事情:一个部门表:描述部门名称
-- 一个员工表:描述员工信息
DROP TABLE emplyee ;
-- 创建一张部门表:id:部门编号 dept_name:部门的名称
CREATE TABLE dept(
id INT PRIMARY KEY AUTO_INCREMENT, -- 部门编号
dept_name VARCHAR(10) -- 部门名称
) ;
-- 插入部门
INSERT INTO dept(dept_name) VALUES ('测试部'),('运维部'),('开发部'),('安保部') ;
-- 创建员工表:id 员工编号,name 员工姓名,gender 性别,salary工资,员工所在的部门编号dept_id
CREATE TABLE employee(
id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
NAME VARCHAR(10), -- 员工姓名
gender VARCHAR(3), -- 员工性别
salary DOUBLE, -- 工资
dept_id INT -- 所在的部门编号
);
INSERT INTO employee
(NAME,gender,salary,dept_id)
VALUES('高圆圆','女',8000.0,1),
('文章','男',6500.0,2),
('李国栋','男',10000.0,3),
('马保国','男',6000.0,4),
('张三丰','男',9500.0,2),
('邓超','男',7000.0,3),
('张佳宁','女',9500.0,2);
-- 新入职一个员工
INSERT INTO employee (NAME,gender,salary,dept_id)
VALUES('王宝强','男',12000.0,5) ;
-- 删除员工表
DROP TABLE employee ;
-- 不存在5号部门,需要建立两张表的关系!
-- 员工表的 dept_id关联dept部门表id字段---外键约束
/*
外键所在的表---从表的某个字段依赖于主表的主键字段
部门表:主表
员工表:从表
*/
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
-- 员工编号
NAME VARCHAR (10),
-- 员工姓名
gender VARCHAR (3),
-- 员工性别
salary DOUBLE,
-- 工资
dept_id INT,
-- 所在的部门编号
-- 声明
CONSTRAINT -- 外键名称:主表名_从表名_fk
dept_emp_fk -- 外键作用在哪个字段上 外键约束名称
FOREIGN KEY (dept_id) -- 关联
REFERENCES -- 主表的主键字段(就是id)
dept(id)
) ;
/*
有了外键约束,直接操作主表(删除/修改),直接报错!
因为从表的数据和主表有关联!
*/
-- 删除id为4号部门
-- delete from dept where id = 4; -- 4号部门有员工--Cannot delete or update a parent row:
-- 将4号部门的员工先删除,(没有关联了),在删除主表数据
DELETE FROM employee WHERE dept_id = 4 ;
DELETE FROM dept WHERE id = 4 ;
-- 将1号部门修改为5号部门
-- update dept set id = 5 where dept_name = '运维部' ; -- 也不能直接修改
-- 将2号部门的员工修改为 "其他部门"
UPDATE employee SET dept_id = 2 WHERE NAME = '高圆圆' ;
-- 修改主表将1号--5号
UPDATE dept SET id = 5 WHERE dept_name = '测试部' ;
UPDATE employee SET dept_id = 5 WHERE NAME = '高圆圆' ;