第六周总结
1._File类
java.io.File
* 文件和目录路径名的抽象表示形式。
*
* 构造方法:
* public File(String pathname):里面当前文件夹/文件的路径 (推荐方式)
* public File(String parent,String child):parent的字符串路径名和child的字符串路径名构造一个File
* public File(File parent,String child):参数1:需要描述parent的字符串路径名和子文件字符串路径名:构造一个File
*
* URL:这个是URI的子集合(统一资源定位符)
* http://localhost:8080/web/login
*
* URI:(统一资源标识符):/web/login
* 网络协议:http://
* https://
* ftp://
* thunder://
File类的创建功能:
*
* public boolean createNewFile()throws IOException:创建文件
* public boolean mkdir():创建目录(文件夹):如果存在该文件夹,返回值false
* public boolean mkdirs():
* 创建目录(文件夹):创建多级目录,如果父目录不存在,会自动创建!
*
* 如果描述文件或者文件夹没有指定盘符,相对路径:默认是当前项目路径下
File类的删除功能
* public boolean delete():删除目录/文件,
* 如果删除的是目录,那么前提条件:目录必须为空
重命名功能:
* 针对某个文件操作
* public boolean renameTo(File dest):
*
* 需求:将当前项目下的高圆圆.jpg --- 杨桃.jpg
*
* 情况1:
* 当前文件的路径和该名后的路径相同,仅仅是改名字
*
* 情况2:
* 当前文件的路径名和该后面的路径名不相同,剪切并重命名
File的判断功能
*
* public boolean isFile():判断是否是文件
* public boolean isDirectory():判断是否是文件夹(目录)
* public boolean canRead():判断是否可读
* public boolean canWrite():判断是否可写
* public boolean exists():判断所file表示的文件/目录是否存在
* public boolean isAbsolute():判断是否是绝对路径
* public boolean isHidden():判断是否是隐藏文件
File类的获取功能
* 基本获取功能
*
* public String getAbsolutePath():获取绝对路径名
* public String getPath():当前文件所表示的路径
* public long length():获取文件长度
* public long lastModified():获取当前文件最后一次修改的时间(long:毫秒值)
高级获取功能
* public File[] listFiles():获取的是某个盘符下/目录File数组(目录中的文件)
* public String[] list():获取某个盘符/目录的下的所有文件以及目录的字符串数组
*
高级获取功能
* public File[] listFiles()
* 需求:
* 获取当前d盘下的所有的以".jpg"结尾的文件!
* 分析:
* 1)描述下d盘符 d:\\
* File类描述
*
* 2)获取当前盘符下所有的文件/文件夹所表示字符串数组/目录下的文件的File数组
* 3)判断如果当前字符串数组或者File数组不为空,遍历获取到每一个文件/目录
* 4)判断当前file是否是表示的文件
* 再次判断是否以".jpg"结尾---->String --> endsWith(".jpg")
* 5)输出即可!
*
* 使用public File[] listFiles()能够实现功能,但是比较麻烦,提供了一下这个方法:
* public String[] list()
* public String[] list(FilenameFilter filter)
*
* public File[] listFiles(FilenameFilter filter)
*
* 在调用这个方法的时候,列表已经获取到了
* FilenameFilter:文件名称过滤器
* 抽象方法:
* boolean accept(File dir,String name):是否将文件添加到文件列表中
* 返回值为true:添加到指定文件列表中;
* 方法业务:根据具体的情况判断
* "所有的以".jpg"结尾的文件!"
* dir表示是否文件
* name是否"已.jpg结尾"
2.方法递归
什么是方法递归呢?
* 方法递归:
* 就是方法本身调用方法一种现象!
*
* Math.max(10,Math.max(40,70)) ; 方法嵌套方法
* 举例:
* 伪代码
* public void show(int n){ //10
* //出口条件(方法结束条件)
* if(n<0){
*
*
* System.exit(0) ;//Java虚拟机终止了
* }
* System.out.println(n) ;
* show(n--) ;
* }
*
* 递归:
* 1)必须定义一个方法:方法调用方法
* 2)满足一定的规律
* 3)方法必须有出口条件(结束条件)--->否则:就是死递归!
*
*
*注意事项:
* 构造方法不存在递归!
递归案例
求5的阶乘
* 1)for循环思想
* n!= n*(n-1)!
* ....
* ...
*
* 2)递归的思想:
* 1)定义一个方法:方法还需要调用本身
* 2)有一定规律
* 3)出口条件
public class DiGuiTest {
public static void main(String[] args) {
//求阶乘实现
//定义最终结果变量
int jc = 1 ;
for(int x = 1 ; x <= 5 ; x ++) {
jc *= x ;
}
System.out.println("5的阶乘是:"+jc);
System.out.println("5的阶乘是:"+getJc(5));
}
//获取最终结果
private static int getJc(int n) {//5
//出口条件
if(n==1) { // 5 * 4 * 3 * 2 * 1 ...
return 1 ;
}else {
return n*getJc(n-1) ; //5 * getJc(5-1)
}
/**
* 递归的思想:
* 分解法:
* 5!
* 5 * 4 !
* 4 * 3 !
* 3 * 2 !
* 2 * 1! 1!==1
*/
}
}
/*
需求:如果有一对兔子,从第三个月起产生一对兔子,小兔子经过第三个月后有产生一对兔子,
* 假如兔子都不死,最终第二十个月有多少对兔子?
*
* 规律:
* 1)第一个月:1对
* 第二个月:1对
* 第三个月:2对
* 第四个月: 3对
* 第五个月:5对
* 第六个月:8对
* ...
* 2)第一个月和第二个月兔子对数都是1
* 从第三个月开始,每个月兔子的对数等于前两个月兔子对数之和
*
* 解析
* 方式1:利用数组
* arr[1] arr[0]都是已知的 (角标值表示第几个月)
* 从第三个月开始,角标2开始,遍历数组 int[] arr = new int[20] ;
* arr[x] = arr[x-1]+arr[x-2];
*
* 方式2:变量的变化
* 将相邻两个月分别表示a,b
* 第一个月, 第二个月
* a = 1 ,b =1
* 第二个月, 第三个月
* a = 1,b = 2
* 第三个月 第四个月
* a = 2,b = 3
* 第四个月,第五个月
* a= 3,b = 5
* ....
* 下一次的a是上一次的b,下一次的b是上一次的a+b
*
* */
public class Test2 {
public static void main(String[] args) {
//方式1:数组的方式
//创建一个数组,动态初始化
int[] arr = new int[20] ;
//0-19:20个月
//第一个月
//第二个月都是1
arr[0] = 1 ;
arr[1] = 1 ;
//从第三个月开始,角标2开始,遍历数组
for(int x = 2 ; x < arr.length ; x ++) {
// 每个月兔子的对数等于前两个月兔子对数之和
arr[x] = arr[x-1] + arr[x-2] ;
}
System.out.println("第二十个月兔子的对数:"+arr[19]);
System.out.println("----------------------------------");
/**
* 方式2:变量的变化
* 将相邻两个月分别表示a,b
* 第一个月, 第二个月
* a = 1 ,b =1
* 第二个月, 第三个月
* a = 1,b = 2
* 第三个月 第四个月
* a = 2,b = 3
* 第四个月,第五个月
* a= 3,b = 5
* ....
* 下一次的a是上一次的b,下一次的b是上一次的a+b
*/
//定义两个变量:a,b来表示相邻两个月的数据
int a = 1 ;
int b = 1 ;
//前两个月已经存在值了
//18个月
for(int x = 0 ; x <18 ; x ++) {
//使用中间变量的方式
//使用temp变量记录一下的a
int temp = a ;
a = b ;
b = temp + a ;
}
System.out.println("第二十个月兔子的对数是:"+b);
System.out.println("-------------------------");
//递归的思想
/**
* 定义变量n: 表示第几个月
* 当前n==1或者n==2 都是1对兔子 出口条件(结束条件)
*
* 如果不是第一个月或者第二个月:从第三个月开始
* 方法名(n-1)+方法名(n-2)
*
*
*/
System.out.println("第二十个月兔子的对数是:"+getRabNum(20));
}
//n表示的第几个月
private static int getRabNum(int n) {
//出口 条件
if(n==1|| n==2) {
//第一个月或者第二个月的兔子对数都是1
return 1 ;
}else {
// 如果不是第一个月或者第二个月:从第三个月开始,每个月兔子对数等于前两个月之和
return getRabNum(n-1) + getRabNum(n-2) ;
}
}
}
import java.io.File;
/**
* 需求
* 需要删除带内容的目录
* 删除当前项目下:demo文件夹
*
* 分析:
* 1)描述下当前demo文件夹:File file = new File("demo") ;
* 2)定义一个方法:delete(file) 递归删除的方法(删除目录)
* 2.1)高级获取功能:获取当前file所表示的文件以及文件夹的File数组[]
* 2.2)判断当前File数组不为空
* 判断当前file是否是文件夹 isDirectory()
* 是文件夹,回到2)步继续调用
*
* 不是文件夹,是文件
* 直接删除:调用delete()删除(查看删除的是
* 那个文件:获取文件名称)
*
* 2.3)删除文件夹:调用delete()删除当前空目录
*
* @author zhangyang
*
*/
public class Test3 {
public static void main(String[] args) {
//1)使用File 描述当前项目下的demo文件夹
File srcFile = new File("demo") ;
//2)定义一个删除目录的方法
delete(srcFile) ;
}
private static void delete(File srcFolder) {
//获取srcFolder文件夹下面的所有文件以及文件夹的File数组
File[] fileArray = srcFolder.listFiles() ;
if(fileArray!= null) {
//遍历
for(File file :fileArray) {
//获取到每一个file对象,判断当前是否是文件夹
if(file.isDirectory()) {
//回到2)进行递归删除
delete(file);//aaa bbb ccc
}else {
//不是文件夹,直接删除
System.out.println(file.getName()+"---"+file.delete());
}
}
//删除目录
System.out.println(srcFolder.getName()+"---"+srcFolder.delete());
}
}
}
3.IO流
IO流
* 在设备之间进行数据传输的操作!
*
* 按流的方向划分:
* 输入流
* 输出流
*
* 按流的类型划分
* 字节流
*
* 字节输入流:InputStream
* 字节输出流:OutputStream
* 字符流
* 字符输入流:Reader
* 字符输出流:Writer
*
* 字符流是在字节输入流之后出现的,解决了中文乱码问题!
* 一般情况:针对某个文本文件进行读写复制操作: 优先采用字符流 (使用记事本打 开并且能读懂!)
*
* 字节流
*
* 字节输入流:InputStream
* 字节输出流:OutputStream
*
* 两个抽象类,不能直接实例化,提供了一些具体的子类
* XXXInputStream
* XXXOutputStream
* 都是字节输入流和字节输出流的子类
3.1字节流
3.1.1文件字节输出流与文件字节输入流
需求:需要在当前项目下输出文件:fos.txt文件,并同时输出内容:"hello,OutputStream"
*
* FileOuputStream:针对文件操作:文件输出流(文件字节输出流)
* public FileOutputStream(String name):
* public FileOutputStream(File file)
* 推荐使用第一种:直接跟当前的具体路径!
* 使用步骤
* 1)创建OutputStream字节输出流对象,同时指向某个文件路径
* 2)写数据:给文件中写入内容
* 3)关闭相关的系统资源
* 字节输出流写数据的功能
* void write(byte[] b) :给指定的文件写入字节数组
* void write(byte[] b, int off, int len) :写入字节数组的一部分
* abstract void write(int b) :写入一个字节
异常的处理方式
* throws
* try...catch..finally:开发汇中,使用这种格式
*
*IO流中:字节输出流中加入异常操作
* @author zhangyang
*
*/
public class FileOutputStreamDemo3 {
public static void main(String[] args) {
//在当前项目下:输出fos3.txt文件
// 方式1:分别进行try...catch
/*
FileOutputStream fos = null ;
try {
fos = new FileOutputStream("fos3.txt") ;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//写数据
try {
fos.write("hello,io,i'm coming...".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
//关闭资源
if(fos!=null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
*/
//方式2:try...catch...catch...finally...
//创建字节输出流对象
FileOutputStream fos = null;
try {
fos = new FileOutputStream("fos3.txt");
//写数据
fos.write("hello,io!i'm coming...".getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放资源
//如果当前流对象不为null,才能够关闭
if(fos!=null) {
//关闭资源
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileOutputStream的构造方法
* public FileOutputStream(String name, boolean append):
* 创建输出流对象,并指向文件,将文件内容写入到末尾:第二个参数为:true:写入末尾
文件字节输入流:FileInputStream
*
* 构造方法
* public FileInputStream(String name)throws FileNotFoundException
* 读数据
* public abstract int read():一次读取一个字节
* public int read(byte[] b) throws IOException:一次读取一个字节数组
*
* 使用步骤
* 1)创建FileInputStream对象:指向哪个文件
* 2)读数据 :public abstract int read():读取一个字节
* 展示结果
* 3)关闭资源
对应中文存储:第一个字节一定是负数,第二个字节可以是负数,整数...
读写复制操作
* 读写复制操作
* 在d盘下有一个FileInputStreamDemo.java文件的内容
* 复制到当前项目路径下:Copy.java文件中
* 分析:
* 1)封装d盘的文件FileInputStreamDemo.java ---源文件
* 使用输入流读取FileInputStreamDemo.java文件的内容
* 2)封装目标文件:当前项目下:Copy.java
* 使用文件字节输出流写数据,将上面的内容复制进来!
* 3)两种方式
* 要么1)一次读取一个字节
* 要么2)一次读取一个字节数组
* 复制图片文件/视频文件...
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 在d盘:一个mp4文件---将复制到当前项目下:copy.mp4文件中
*D:\JavaEE_2008\day26\avi\02_回顾内容_今日内容.mp4------
*
* 当前目下:Copy.mp4
*
*
*分析:
* 1)源文件:D:\JavaEE_2008\day26\avi\02_回顾内容_今日内容.mp4
* FileInputStream去读取
* 2)目标文件:项目下Copy.mp4
* FileOutputStream:写数据
*
*一次读取一个字节:耗时:240793毫秒 视频文件:17.1M
*一次读取一个字节:耗时:共耗时:343毫秒 视频文件:17.1M
*
*/
public class CopyMp4Demo {
public static void main(String[] args) throws IOException {
//开始时间
long start = System.currentTimeMillis() ;
method("D:\\JavaEE_2008\\day26\\avi\\02_回顾内容_今日内容.mp4","Copy.mp4") ;
//结束时间
long end = System.currentTimeMillis() ;
System.out.println("共耗时:"+(end-start)+"毫秒");
}
//方式1:一次读取一个字节
//方式2:一次读取一个字节数组
private static void method(String srcFile, String destFile) throws IOException {
//封装源文件
FileInputStream fis = new FileInputStream(srcFile) ;
//封装目标文件
FileOutputStream fos = new FileOutputStream(destFile) ;
/*
//读写操作
int by = 0 ;
while((by=fis.read())!=-1) {
//写
fos.write(by);
}*/
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;//缓冲区
int len = 0 ;
while((len=fis.read(bytes))!=-1) {
fos.write(bytes, 0, len);
}
//释放资源
fos.close();
fis.close();
}
}
3.1.2字节缓冲输入流与缓冲输出流
BufferedInputStream extends InputStream:字节缓冲输入流
*
* 构造方法
* BufferedInputStream(InputStream in)
* BufferedOutputStream extends OutputStream:字节缓冲输出流
* 构造方法
* public BufferedOutputStream(OutputStream out)
* 创建一个缓冲输出流对象,默认缓冲区大小
*
* 目前:当前缓冲流只是在流中提供了byte[] 缓冲区,默认足够大,一般通过带一个参的构造方法创建!
* 只是提供缓冲区,具体文件读写复制操作还是需要用底层流(InputStream/OutputStream)
*
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
*
* 四种方式对文件进行读写复制操作
*将当前项目下 BufferedInputStreamDemo.java文件复制到项目下的Copy.java文件中
*
*
* FileInputStream/FileOutputStream:一次读取一个字节 共耗时44毫秒
* FileInputStream/FileOutputStream:一次读取一个字节数组
*
*高效的字节流
* BufferedInputStream/BufferedOutputStream:一次读取一个字节
* BufferedInputStream/BufferedOutputStream:一次读取一个字节数组
*复制视频文件
*/
public class CopyFile {
public static void main(String[] args) throws IOException{
long start = System.currentTimeMillis() ;
// method1("BufferedInputStreamDemo.java","Copy.java") ;
method2("BufferedInputStreamDemo.java","Copy.java") ;
long end = System.currentTimeMillis() ;
System.out.println("共耗时"+(end-start)+"毫秒") ;
}
// FileInputStream/FileOutputStream:一次读取一个字节数组
private static void method2(String srcFile, String destFile) throws IOException {
//封装源文件和目的地文件
FileInputStream fis = new FileInputStream(srcFile) ;
FileOutputStream fos = new FileOutputStream(destFile) ;
byte[] buf = new byte[1024] ;
int len = 0 ;
while((len=fis.read(buf))!=-1) {
fos.write(buf, 0, len);
}
//释放资源
fis.close();
fos.close();
}
//FileInputStream/FileOutputStream:一次读取一个字节
private static void method1(String srcFile, String destFile) throws IOException {
//封装源文件和目的地文件
FileInputStream fis = new FileInputStream(srcFile) ;
FileOutputStream fos = new FileOutputStream(destFile) ;
int by = 0 ;
while((by=fis.read())!=-1) {
fos.write(by);
}
//释放资源
fis.close();
fos.close();
}
}
3.2字符流
3.2.1字符输入流与字符输出流
InputStreamReader:字符输入流 (字符流通向字节流的桥梁)字符转换输入流
*
* 构造方法:
* public InputStreamReader(InputStream in):使用默认字符集进行解码(gbk格式)
* public InputStreamReader(InputStream in, String charsetName)
* 使用指定的字符集进行解码
*
* 成员方法:
* 读的功能
* public int read():读单个字符
* public int read(char[] cbuf):读字符数据
* public int read(char[] cbuf,int offset,int len)读字符数组的一部分
OutputStreamWriter:字符输出流(字符流通向字节流的桥梁):转换流
* 构造方法
* public OutputStreamWriter(OutputStream out):gbk格式 使用默认字符集进行编码的字符输出流
* public OutputStreamWriter(OutputStream out, Charset cs)
* 使用指定的字符集构造出一个字符输出流
*
* 成员方法
* public void write(int c):写入单个字符
* public void write(char[] cbuf):写入字符数组
* public abstract void write(char[] cbuf,int off,int len) 写入字符数组的一部分
* public void write(String str):写入字符串内容
* public void write(String str,int off,int len):写入字符串一部分
*
*
* flush()和close()
* 一个刷新流:将缓冲的数据刷新出来(中文:默认gbk格式:一个中文对两个字节),流刷新之后还可以
* 继续写入数据;
* close()方法:将跟该流相关的系统资源释放掉,
* 不再指向当前操作的文件,关闭之后不能再写入数据否则出现IOException
为了简化字符流读写复制操作:
*
* 提供了字符转换输入流和字符转换输出流的便捷类
* FileReader/FileWriter ---继承自InputStreamReader/OutputStreamWriter
* FileReader(String pathname)
* FileWriter(String pathname)
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* 将当前项目OutputStreamWriterDemo.java文件---复制到D盘下Copy.java
*
* .txt/.java..... 使用记事本打开并能读懂,通常使用的字符流
*
* 分析:
* 1)封装源文件---->InputStreamReader
* 2)封装目的地文件---->D盘下 Copy.java OutputStreamWriter
* 3)读写操作
* 一次读取一个字符
* 一次读取一个字符数组
*/
public class CopyFile {
public static void main(String[] args) throws IOException {
//1)封装源文件---->InputStreamReader
InputStreamReader isr = new InputStreamReader
(new FileInputStream("OutputStreamWriterDemo.java")) ;
// 2)封装目的地文件---->D盘下 Copy.java OutputStreamWriter
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("D:\\Copy.java")) ;
//一次读取一个字符
int ch = 0 ;
while((ch=isr.read())!=-1) {
//写
osw.write(ch);
//刷新
osw.flush();
}
//一次读取一个字符数组(自己测试)
//关闭资源
osw.close();
isr.close();
}
}
编码和解码
*
* 编码:就将能够看懂的内容-----转换 "看不懂的内容"
* String byte[]
*
* 解码: 将看不懂的内容----------转换 "能看懂的内容"
* byte[] String
*
* 举例:
* "今天老地方" ----- > 字节数组 :编码
*
*
* 编码格式:
* big-5:大五码 (繁写字体)
* gbk:中国的中文编码表:一个中文对应两个字节
* gbk-2312:中国的中文编码表:一个中文对应两个字节:对上面 的编码扩展
* iso-8859-1:拉丁文码表
* utf-8:一个中文对应三个字节码
* JS:日本中的编码格式
*
*
* utf-8/gbk
*
* 编码和解码的过程必须保证格式统一:否则出现乱码!
3.2.2字符缓冲输入流与字符缓冲输出流
BufferedReader:字符缓冲输入流
* 构造方法
* BufferedReader(Reader r):构造一个字符缓冲输入流提供默认缓冲区大小
*
* 特有功能:
* public String readLine():一次读取一行内容,当读取到\n(换行了),就终止!
*
*
*Scanner(InputStream in)
*BufferedReader(Reader r):可以作为键盘录入(使用流的方式)
BufferedWriter:字符缓冲输出流
* 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入
* public BufferedWriter(Writer out):构造一个缓冲输出流,默认缓冲区大小
*
* 成员方法
* 特有功能
* public void newLine():写入行的分隔符(换行)
使用BufferedReader/BufferedWriter:读写复制操作:文本文件复制
* 利用特有功能(一种新的方式)
*
* 项目下的Demo.java---复制到D盘下:a.java文件中
* 使用BufferedReader:字符缓冲输入流的readLine():一次读取一行
* 使用BufferedWriter:newLine()可以实现换行
*
* 总结针对文本文件的复制操作:
* InputStreamReader/OutputStreamWriter:一次读取一个字符
* InputStreamReader/OutputStreamWriter:一次读取一个字符数组
* (同上)
* FileReader/FileWriter一次读取一个字符
* FileReader/FileWriter一次读取一个字符数组
*
* BufferdReader/BufferedWriter:一次读取一个字符
* BufferdReader/BufferedWriter:一次读取一个字符数组
* BufferedReader/BufferedWriter:一次读取一行
4.其他的一些流
PrintWriter:字符打印流
* PrintStream:字节打印流
*
* 能够操作直接操作文件地址:String pathname的流有哪些?
* 最基本的字节流
* FileInputStream
* FileOutputStream
*
* FileReader
* FileWriter
*
* PrintWriter:能够操作文件(目标文件) -- 字符输出流一种
*
* 构造方法:
* public PrintWriter(Writer out,boolean autoFlush):
* 第二个参数为true:表示开启自动刷新功能
* public PrintWriter(String fileName):可以操作具体文件路径
* 成员方法:
* public void println(XXX x):可以换行
文本文件的复制----优先采用BufferedReader/BufferedWriter
* 图片文件/音频文件/视频文件----采用BufferdInputStrea/BufferedOutputStream
*
* 文本文件的复制
* 当前项目下:BufferedInputStreamDemo.java
* 复制当前项目下:Test.java
*
* 封装源文件:BufferedReader
* 封装目标文件:PrintWriter
内存操作流:操作临时数据
* ByteArrayOutputStream:内存操作输出流
* 构造方法:
* public ByteArrayOutputStream(){}:构造一个默认的缓冲大小的输出流对象
* 成员方法
* public byte[] toByteArray():将内存操作输出流中流对象---数组格式
*
* ByteArrayInputStream:内存操作输入流
* 构造方法:
* public ByteArrayInputStream(byte[] buf):使用指定的字节数组作为缓冲区,构造
* 内存操作输入流
SequenceInputStream extends InputStream:字节输入流
* 合并流:将两个或者两个以上的流对象合并到一个流中
*
*构造方法
*将两个基本输入流合并到当前SequenceInputStream流中
* public SequenceInputStream(InputStream s1,InputStream s2)
* 之前:
* c:\\a.txt----D:\\b.txt
*
* 现在::c:\\a.txt
* c:\\c.txt
*
* 复制到 d:\\b.txt
* 当前项目下的
* Demo.java
OutputStreamWriterDemo.java
*
* 复制到当前项目下的:Copy.java文件中
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;
/*
public SequenceInputStream(Enumeration<? extends InputStream> e)
* 两个以上的文件复制
*
* 1)创建 Vector<InputStream>集合对象
* 2)给添加多个InputStream对象
* 3)Vector集合中:elements()---->Enumeration<InputStream>
* 4)读写复制
* 需求:
* 将OutputStreamWriterDemo.java/Test.java/CopyFile.java
*
* 三个文件复制到D:\\CopyFileDemo.java
**/
public class SequenceStreamDemo2 {
public static void main(String[] args) throws IOException{
//public SequenceInputStream(Enumeration<? extends InputStream> e)
//创建Vector集合对象
Vector<InputStream> v = new Vector<InputStream>() ;
//创建三个InputStream对象
InputStream s1 = new FileInputStream("OutputStreamWriterDemo.java") ;
InputStream s2 = new FileInputStream("Test.java") ;
InputStream s3 = new FileInputStream("Copy.java") ;
//添加到集合中
v.add(s1) ;
v.add(s2) ;
v.add(s3) ;
//获取类似于迭代器elements()-----iterator()
Enumeration<InputStream> en = v.elements() ;
//创建合并流对象
SequenceInputStream sis = new SequenceInputStream(en) ;
//封装目标文件
FileOutputStream fos = new FileOutputStream("d:\\CopyFileDemo.java") ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=sis.read(bytes))!=-1){
//写
fos.write(bytes, 0, len);
}
//关闭
fos.close();
sis.close();
}
}
* ObjectOutputStream
* public ObjectOutputStream(OutputStream out)
* 序列化:需要将网络中的数据/一些对象转成 "流数据"
* 成员方法:
* public final void writeObject(Object obj):将指定的对象写入到序列化流中
*
* ObjectInputStream
* public ObjectInputStream(InputStream in)
*
* 反序列化:将流数据----还原成Java对象/网络数据
* public final Object readObject():将流数据---还原成对象
//java.io.NotSerializableException: com.qianfeng_other_stream_05.Person
//只有实现接口:Serializable接口的类:才能进行序列化
* 序列化接口-----没有字段,连成员方法都没有----标记接口
* 序列化的类----内存中进行编码:内容包含:类---类产生的字节码文件Person.class 类签名:序列化版本ID
* 类的成员:
* 字段(属性)值
* 改动了当前类的一个成员:
* 直接进行反序列化就出现了一个异常:
* java.io.InvalidClassException: com.qianfeng_other_stream_05.Person;
* local class incompatible: stream classdesc serialVersionUID = 6374026048563988155,
* local class serialVersionUID = 7013884605889541932
*
* 类似于:每一个类在内存中进行序列化的时候,会产生xxx.class文件产生一个
* serialVersionUID:序列化版本ID = 100
*
* 反序列化的时候:将类的成员信息改动了----产生一个新的serialVersionUID = 200
* 前后类的签名不一致,就会出现异常!
* 当前某个成员:transient:不会参与序列化和反序列化
*
* 让当前这个类在序列化和反序列化的时候:产生的版本ID是一个固定值
* 在类上有黄色警告线---点击--- 产生一个固定的版本Id值即可!
Properties 类表示了一个持久的属性集 ,没有泛型(属性列表中的键和值都是String)
* 它继承自Hashtable<K,V> implements Map<K,V>
*
* Map集合的遍历:
* 通用的方式
* keySet()---->Set<K>
* get(K key)---->V value
Properites属性集合类的特有功能
* public Object setProperty(String key, String value):添加键和值
* public Set<String> stringPropertyNames():获取属性列表中的所有的键的集合
* public String getProperty(String key):通过键获取值
* public void load(Reader reader):将一些配置文件中的数据加载到属性集合类中
*
* public void store(Writer writer, String comments)
* 将属性集合类中数据保存到指定的文件中
*
5.网络编程
5.1网络编程的三要素
网络编程的三要素
举例:
1)找到高圆圆---->ip地址
2)对它说话 ----(耳朵说)----端口号
3)找到她了,对她说: ---->协议
i love you(比如:不懂英语,说中文)
协议 ip地址 端口号
网络协议
UDP和TCP
UDP和TCP协议的区别:
1)是否需要建立连接通道
UDP:不需要建立通道 (QQ聊天,发短信)
TCP:需要建立连接通道(打电话...)
2)是否是可靠连接(是否安全)
UDP:是一种不可靠连接,不安全--执行效率高
TCP:是一种可靠连接,服务器端一直阻塞状态(同步的---安全性),执行效率低
三次握手,四次挥手!
3)共同点
UDP/TCP ---两端都需要有Socket(Socket编程)
应用协议
http协议
https协议(比上http协议)
第一要素:
ip地址
192.168.138.1: 使用点分十进制法
A类IP地址:第一段号码为网络号码,剩下的三段号码为本地计算机的号码 (政府部门)
B类IP地址:前二段号码为网络号码,剩下的二段号码为本地计算机的号码 (大学校园)
C类IP地址:前三段号码为网络号码,剩下的一段号码为本地计算机的号码(私人地址)
127.0.0.1:回环地址:表示本机 ---- 域名:localhost
xxx.xxx.xxx.255 广播地址
第二个要素:端口号
使用360软件---查看当前计算机中每个软件 的端口号
有效端口号:0-65535
0-1024:保留端口号
http://www.baidu.com
http://192.168.25.1:80(可以不写)/xx
一般:80端口号:是不写的(省略)
常见端口号
tomcat: 8080
redis:6575.. (数据库---非关系型数据库 key-value )
mysql软件:3306
java.net.InetAddress类:互联网ip地址统称
* 这个类没有构造方法,不能直接创建对象!,提供一些成员方法使用:静态的
* Runtime类: 单例模式
* 提供静态方法,返回该类本身
*
* public static InetAddress getByName(String host):
* 参数为:主机名称:
*
*
* 成员方法
* public String getHostAddress()返回 IP 地址字符串(以文本表现形式)。
* public String getHostName():获取主机名
5.2UDP
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* UDP协议接收端的实现步骤
* 1)创建接收端的Socket对象,绑定端口号
* 2)创建一个数据报包---DatagramPacket:当前接收容器
* public DatagramPacket(byte[] buf,int length)
* 3)接收数据
* 4)从接收容器中解析实际数据
* 5)展示数据
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException{
//1)创建接收端的Socket对象,绑定端口号
// public DatagramSocket(int port)
DatagramSocket ds = new DatagramSocket(10086) ;
//2)创建一个数据报包---DatagramPacket:当前接收容器
// public DatagramPacket(byte[] buf,int length)
byte[] bytes = new byte[1024] ;
int length = bytes.length ;
DatagramPacket dp = new DatagramPacket(bytes, length) ;
//3)接收数据
// public void receive(DatagramPacket p)
ds.receive(dp);
//4)解析当前接收容器中的实际数据
//public byte[] getData():获取缓冲区数据中实际字节数组
//public int getLength():获取缓冲区中实际长度
byte[] buf = dp.getData() ;
int length2 = dp.getLength() ;
//获取接收端发送的数据:ip地址
// public InetAddress getAddress()
InetAddress address = dp.getAddress() ;
String ip = address.getHostAddress() ;
//展示数据
String s = new String(buf,0,length2) ;
System.out.println("data is: "+s+",from "+ip);
//关闭资源
ds.close();
}
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
/**
* UDP协议发送端的步骤
*
* 1)创建发送端的Socket对象
* 2)数据数据报包对象:DatagramPacket
* 3)发送数据
* 4)关闭资源
*/
public class SendDemo {
public static void main(String[] args) throws IOException {
//1)创建发送端的Socket对象
//DatagramSocket
//此类表示用来发送和接收数据报包的套接字。
//public DatagramSocket()
DatagramSocket ds = new DatagramSocket() ;
//2)创建数据数据报包对象:DatagramPacket
//数据报包用来实现无连接包投递服务
//public DatagramPacket(byte[] buf,
// int length,
//InetAddress address,
//int port)
//参数1:当前发送数据的字节数组
//参数2:当前数据的实际长度
//参数3:ip地址对象
//参数4:端口号:0-1024保留端口 (0-65535)
String s = "hello,udp,我来了" ;
byte[] bytes = s.getBytes() ;
int length = bytes.length ;
DatagramPacket dp = new DatagramPacket(bytes, length,
InetAddress.getByName("10.12.156.36"), 10086) ;
//3)发送数据报包
//public void send(DatagramPacket p)
ds.send(dp);
//释放资源
ds.close();
}
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
//接收端不断接收收据,并解析
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//创建一个接收端的Socket
DatagramSocket ds = new DatagramSocket(10000) ;
//不断接收数据
while(true) {
//创建接收容器
byte[] bytes = new byte[1024] ;
int length = bytes.length ;
DatagramPacket dp = new DatagramPacket(bytes , length) ;
//接收
ds.receive(dp);
//解析真实数据
String str = new String(dp.getData(), 0, dp.getLength()) ;
//获取ip地址
String ip = dp.getAddress().getHostAddress() ;
//展示数据
System.out.println("data is :"+str+"from "+ip);
}
//接收端不关闭
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
*
* 需求
* UDP
* 发送端键盘录入数据,接收端不断接收数据(不关闭)
* 键盘录入数据
* Scanner
* 使用字符流的方式
* BufferedReader(new InputStreamReader(System.in))
*/
public class SendDemo {
public static void main(String[] args) throws IOException {
//1)创建发送端的Socket对象
DatagramSocket ds = new DatagramSocket() ;
//2)键盘录入数据
//创建bufferedReader类对象
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in)) ;
//一次读取一行内容
String line = null ;
while((line=br.readLine())!=null) {
//自定义结束条件
if(line.equals("886")) {
break ;
}
//line:发送的数据
byte[] bytes = line.getBytes() ;
int length = bytes.length ;
//创建数据报包对象
DatagramPacket dp = new DatagramPacket(
bytes,
length,
InetAddress.getByName("10.12.156.36"),
10000) ;
//发送数据
ds.send(dp);
}
//释放资源
ds.close();
}
}
import java.net.DatagramSocket;
import java.net.SocketException;
/**
*
* 需要在一个窗口下进行聊天,发送和接收数据
*
* 发送端会开启发送端的线程!
* 接收端开启接收端的线程!
*
* 多线程的实现方式:
* 1)Thread类:继承自它
* 2)实现Runnable接口(静态代理)
* 3)线程池
* 分析:
*
* 1)发送端和接收端都需要有Socket 都需要在当前用户线程(main)中创建出来
* 2)多线程实现方式2
* SendThread /ReceiveThread 实现Runnable接口重写run方法 (这两个作为资源类)
* 这两个资源类对象需要将上面Socket对象传递进来
*
* 3)创建Thread类对象,将上面的SendThread和ReceiveThread作为参数传递
* 4)启动线程
*/
public class ChatRoom {
public static void main(String[] args) {
try {
//发送端的Scoket
DatagramSocket sendDs = new DatagramSocket() ;
//接收端Socket
DatagramSocket receDs = new DatagramSocket(12306) ;
//创建资源类对象:多线程的方式2
SendThread st = new SendThread(sendDs) ;
ReceiveThread rt = new ReceiveThread(receDs) ;
//创建Thread类对象
Thread t1 = new Thread(st) ;
Thread t2 = new Thread(rt) ;
t1.start();
t2.start();
} catch (SocketException e) {
e.printStackTrace();
}
}
}
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[] bytes = new byte[1024];
int length = bytes.length;
DatagramPacket dp = new DatagramPacket(bytes, length);
// 接收
ds.receive(dp);
// 解析真实数据
String str = new String(dp.getData(), 0, dp.getLength());
// 获取ip地址
String ip = dp.getAddress().getHostAddress();
// 展示数据
System.out.println("data is :" + str + "from " + ip);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
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 SendThread implements Runnable {
private DatagramSocket ds ;
public SendThread(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
// 键盘录入数据
// 创建bufferedReader类对象
BufferedReader br = new BufferedReader
(newInputStreamReader(System.in));
// 一次读取一行内容
String line = null;
while ((line = br.readLine()) != null) {
// 自定义结束条件
if (line.equals("886")) {
break;
}
// line:发送的数据
byte[] bytes = line.getBytes();
int length = bytes.length;
// 创建数据报包对象
DatagramPacket dp = new DatagramPacket(bytes, length,
InetAddress.getByName("10.12.156.36"), 12306);
// 发送数据
ds.send(dp);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(ds!=null) {
ds.close();
}
}
}
}
5.3TCP
* TCP协议
*
* 基本使用:
* 客户端的实现步骤
* 1)创建客户端的Socket对象 java.net.Socket(套接字)
* public Socket(String host,int port)
* 2)获取客户端通道内输出流,写入内容
* 3)关闭资源
* TCP
* 服务器端基本使用
*
* 步骤
* 1)创建服务器端的Socket对象,绑定端口
* 2)进入阻塞状态,监听客户端连接!
* 3)获取动态内输入流,读数据
* 4)展示数据
* 5)释放资源
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* 客户端发送数据"hello,Server,我来了"
* 服务器端将数据读取出来,展示
*
* 服务器端还需要加入反馈操作,"我收到了"
* 客户端需要将反馈信息读取 出来
*
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端的socket
Socket socket = new Socket("10.12.156.36",6666) ;
//获取通道内的输出流
OutputStream out = socket.getOutputStream() ;
out.write("hello,Server,我来了".getBytes());
//读取服务器端的反馈数据
//获取通道内的输入流对象
InputStream in = socket.getInputStream() ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = in.read(bytes) ;
//展示数据
String serverStr = new String(bytes, 0, len) ;
System.out.println(serverStr);
//释放资源
socket.close();
}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务器端还需要加入反馈操作,"我收到了"
* 客户端需要将反馈信息读取 出来
*
* 注意事项:
* 服务器端不要开启多次,就会出现BindException:绑定异常: 端口号被占用!
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(6666) ;
//监听客户端的链接
Socket socket = ss.accept() ;
//获取通道输入流,读取数据
InputStream in = socket.getInputStream() ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = in.read(bytes) ;
//展示数据:客户端发来的数据
String clientStr = new String(bytes, 0, len) ;
System.out.println(clientStr);
//服务器端反馈给客户端
//获取通道内的输出流对象
OutputStream out = socket.getOutputStream() ;
out.write("数据已经收到了".getBytes());
//释放资源
ss.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
* 需求
* 键盘录入:BufferedReader
* 1)客户端不断键盘录入数据,服务器端不断将数据展示在控制台上
*/
public class ClientTest {
public static void main(String[] args) throws IOException {
//创建客户端Socket
Socket socket = new Socket("10.12.156.36", 2222) ;
//)客户端不断键盘录入数据
//创建字符输入流:BufferedReader
BufferedReader br =
new BufferedReader(new InputStreamReader(System.in)) ;
//OutputStream getOutputStream():通道内的字节输出流
//输出流需要和BufferedReader对应: BufferedWriter:字符输出流
//将节输出流封装成BuffferedWriter :字符流通向字节流的桥梁 OutputStreamWriter
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())) ;
//一次读取一行数据:键盘录入 数据,写入到BufferedWriter中
String line = null ;
while((line=br.readLine())!=null) {
//结束条件
if(line.equals("over")) {
break ;
}
//录入一行,写入到bw流中
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
socket.close();
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
//服务器端不断将数据展示在控制台上
public class ServerTest {
public static void main(String[] args) throws IOException {
//创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(2222) ;
//监听客户端连接
Socket socket = ss.accept() ;
//不断的去读取数据
//获取通道内输入流:将封装 成BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
String line = null ;
while((line=br.readLine())!=null) {
//输出数据
System.out.println(line);
}
//服务器端不关闭
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
* 2)客户端不断键盘录入数据,服务器端将数据输出在某个文件中
*/
public class ClientTest {
public static void main(String[] args) throws IOException {
// 创建客户端Socket
Socket socket = new Socket("10.12.156.36", 2222);
// )客户端不断键盘录入数据
// 创建字符输入流:BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// OutputStream getOutputStream():通道内的字节输出流
// 输出流需要和BufferedReader对应: BufferedWriter:字符输出流
// 将节输出流封装成BuffferedWriter :字符流通向字节流的桥梁 OutputStreamWriter
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// 一次读取一行数据:键盘录入 数据,写入到BufferedWriter中
String line = null;
while ((line = br.readLine()) != null) {
// 结束条件
if (line.equals("over")) {
break;
}
// 录入一行,写入到bw流中
bw.write(line);
bw.newLine();
bw.flush();
}
// 释放资源
socket.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
//服务器端将数据输出在某个文件中
public class ServerTest {
public static void main(String[] args) throws IOException {
//创建服务器端的Socket
ServerSocket ss = new ServerSocket(2222) ;
//监听客户端连接
Socket socket = ss.accept() ;
//封装通道内的字节输入流:BufferedReader 先读取客户端写过来的数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
//创建BufferedWriter流对象,将服务器端读取的数据写入到文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt")) ;
//一次读取一行
String line = null ;
while((line=br.readLine())!=null) {
//将内容写入到文件中
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
bw.close();
socket.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
/*
3)客户端将当前项目下ReceiveDemo.java文件 写入到通道内的流中
服务器端将客户端的文本进行复制:到当前项目Copy.java文件中
*/
public class UploadClient {
public static void main(String[] args) throws IOException {
//创建Scoket对象
Socket s = new Socket("10.12.156.36", 5555) ;
//创建字符输入流BufferedReader
BufferedReader br = new BufferedReader
(new FileReader("ReceiveDemo.java")) ;
//封装通道内的字节输出流
BufferedWriter bw =
new BufferedWriter(
new OutputStreamWriter(s.getOutputStream())) ;
//将文件的内容写到bw流对象中
String line = null ;
while((line=br.readLine())!=null) {
//写入
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭
br.close();
s.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadServer {
public static void main(String[] args) throws IOException{
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(5555) ;
//监听链接
Socket socket = ss.accept() ;
//封装通道内的字节输入流
BufferedReader br = new BufferedReader
(new InputStreamReader(socket.getInputStream())) ;
//创建BufferedWriter将流中数据进行复制
BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java")) ;
String line = null ;
while((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();
bw.flush();
}
//客户端的文本文件,服务器端输出到另一个文件中,如果复制完毕
//加入服务器端的反馈?
//关闭
bw.close();
socket.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
/*
3)客户端将当前项目下ReceiveDemo.java文件 写入到通道内的流中
服务器端将客户端的文本进行复制:到当前项目Copy.java文件中
//加入服务器端的反馈?
问题:
服务器端和客户端程序没有结束,但是文件已经复制完毕!
String readLine() :返回为null,仅仅表示文件已经读完了,
但是服务器端不知道客户端的文件是否已经写入到通道流中(是否应写入完毕),就等待着客户端告诉服务器端"已经写入完毕"
当前客户端没有告诉服务器端,是否写入完毕,那么客户端也一直等待着服务器的反馈,就出现互相等待了!
解决方案
1)在客户端自定义一个结束标记
通道输出流(BufferedWriter)写入"over",服务器端读取到"over",直接结束!
2)在客户端Socket中:结束的方法 "告诉服务器,这里面没有内容写入"
public void shutdownOutput()
*/
public class UploadClient {
public static void main(String[] args) throws IOException {
//创建Scoket对象
Socket s = new Socket("10.12.156.36", 5555) ;
//创建字符输入流BufferedReader
BufferedReader br = new BufferedReader
(new FileReader("ReceiveDemo.java")) ;
//封装通道内的字节输出流
BufferedWriter bw =
new BufferedWriter(
new OutputStreamWriter(s.getOutputStream())) ;
//将文件的内容写到bw流对象中
String line = null ;
while((line=br.readLine())!=null) { //readLine():阻塞式方法
//写入
bw.write(line);
bw.newLine();
bw.flush();
}
/**
* 方案1
* //自定义一个结束标记
bw.write("over");
bw.newLine();
bw.flush();
*/
//方案2
// public void shutdownOutput()://禁用此套接字的输出流:告诉服务器端,没有内容写入到通道流中
s.shutdownOutput();
//获取通道内的字节输入流,读取服务器端的反馈
//将字节输入流---封装BufferedReader
BufferedReader br2 = new BufferedReader
(new InputStreamReader(s.getInputStream())) ;
//readLine()
String serverMsg = br2.readLine() ;
System.out.println(serverMsg);
//关闭
br.close();
s.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadServer {
public static void main(String[] args) throws IOException{
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(5555) ;
//监听链接
Socket socket = ss.accept() ;
//封装通道内的字节输入流
BufferedReader br = new BufferedReader
(new InputStreamReader(socket.getInputStream())) ;
//创建BufferedWriter将流中数据进行复制
BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java")) ;
String line = null ;
while((line=br.readLine())!=null) {//阻塞式方法
/*
//读取客户端的自定义标记
if("over".equals(line)) {
break ;
}
*/
bw.write(line);
bw.newLine();
bw.flush();
}
//客户端的文本文件,服务器端输出到另一个文件中,如果复制完毕
//加入服务器端的反馈?
//获取通道内的字节输出流---封装成BufferedWriter
BufferedWriter bw2 = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())) ;
bw2.write("文件复制完毕");
bw2.newLine();
bw2.flush();
//关闭
bw.close();
socket.close();
}
}
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
/**
* 图片的复制:BufferedInputStream/BufferedOutputStream
*
* 一次读取一个字节数组
* 客户端的一个图片文件:当前项目下xxx.jpg
* 服务器端将图片文件:进行复制:mv.jpg
*
* 加入服务器端的反馈
*
* 发现问题:
* 图片文件:没有复制完整,图片文件缺失(少字节数)
* 图片文件本身在内存中:缓存数据!
*
* 字节缓冲输出流中:public void flush():强制将缓冲的字节数输出到流中!
*/
public class UploadImageClient {
public static void main(String[] args) throws IOException {
//创建Scoket
Socket s = new Socket("10.12.156.36",6666) ;
//创建字节输入流:封装图片文件
BufferedInputStream bis =
new BufferedInputStream(new FileInputStream("高圆圆.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 in = s.getInputStream() ;
//一次读取一个字节数组
byte[] bytes2 = new byte[1024] ;
int len2 = in.read(bytes2) ;
String fkMsg = new String(bytes2, 0, len2) ;
System.out.println("fkMsg:"+fkMsg);
//释放资源
bis.close();
s.close();
}
}
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadImageServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(6666) ;
Socket socket = ss.accept() ;
//封装通道内在字节输入流
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()) ;
//输出到指定文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mv.jpg")) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len = bis.read(bytes))!=-1) {
//写入
bos.write(bytes, 0, len);
//强制刷新
bos.flush();
}
//加入反馈
//获取通道内的输出流
OutputStream out = socket.getOutputStream() ;
out.write("图片已经复制完毕".getBytes());
//刷新
out.flush();
//释放资源
bos.close();
socket.close();
}
}
6.反射
什么是反射:
* 反射就是通过获取类的字节码文件对象:Class
* 创建该类的实例(通过构造方法:Constroctor类),调用成员方法(Method类),
* 给成员变量赋值(Field类)
*
* 关于面试题:获取字节码文件的方式有几种
* 三种方式
* 1)Object类的getClass():表中正在运行的Java类(当前类字节码文件)
* 2)任意Java类型的.class属性
* 3)反射Class类中forName("类或者接口的全限定名称") ;
* com.xxx.xxx.ReflectDemo
* 之前的写法:
* Person p = new Person();通过无参构造方法创建对象
* Person p = new Person("高圆圆"); 形参String---String类型字节码文件格式
* java.lang.String
*
* 使用反射获取构造方法并使用,(Constructor)
*
*
Class 类的实例表示正在运行的 Java 应用程序中的类和接口
之前的写法:
* Person p = new Person("高圆圆") ;
* System.out.println(p) ; toString()
* 之前的写法:
* Person p = new Person() ;
* p.字段名(属性名) = "赋值";
*
* 通过反射获取成员变量Field并使用
之前的写法:
* 通过无参构造方法创建对象
* Person p = new Pereson() ;
* p.show();
*
* 使用反射获取Person类的字节码文件对象,并获取成员方法Method并使用
反射的一些用途
import java.io.FileReader;
import java.io.Reader;
import java.lang.reflect.Method;
import java.util.Properties;
/**
*
* 利用Class.forName("类的全限定名") ;将类的全限定名放置在配置文件中
* xx.txt 文本文件
* xx.properties文件
*/
@SuppressWarnings("all")
public class Test {
public static void main(String[] args) throws Exception {
//创建一个学生类
Student s = new Student() ;
s.love();
System.out.println("-------------------");
Worker w = new Worker() ;
w.love();
//在没有使用反射之前,需求变化:修改代码
//使用反射
System.out.println("--------------------------------");
//创建属性集合类对象
Properties prop = new Properties() ;
//将文本文件中加载到属性集合类中
Reader r = new FileReader("class.txt") ;
prop.load(r);
r.close();
System.out.println(prop);
//getProperty(String key)---获取值
String className = prop.getProperty("className") ; //当前类的全限定名
String methodName = prop.getProperty("methondName") ;//获取方法名
//反射获取当前类Class对象
Class clazz = Class.forName(className) ;
//字节码文件对象---Constructor ----newInstance():通过构造器创建
//Class类中---public T newInstance():直接创建
Object obj = clazz.newInstance() ;
//获取成员Method
Method m = clazz.getMethod("love") ;
//Student s = new Student() ; s.love();
m.invoke(obj) ;
}
}
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 如何读取src目录下的properties配置文件
*/
public class Test2 {
public static void main(String[] args) throws Exception {
//1)读取src下面的class.properties配置文件
//获取当前类的类加载器
//Class
//public ClassLoader getClassLoader()
ClassLoader classLoader = Test2.class.getClassLoader() ;
//public InputStream getResourceAsStream(String name)
//读取指定资源的输入流
//参数是文件名称 文件必须是在src目录下(类路径)
//方式1
InputStream inputStream = classLoader.getResourceAsStream("class.properties") ;
//方式2:通过类加载器获取网络资源地址对象:URL
//public URL getResource(String name)
//public String getPath()
//创建属性集合类对象
Properties prop = new Properties() ;
//将流中的数据加载到属性集合类中
prop.load(inputStream);
//System.out.println(prop);//className=com.qianfeng.reflect_03.Worker, methondName=love}
//通过键的名称获取值
String className = prop.getProperty("className") ;
String methodName = prop.getProperty("methondName") ;
//获取当前类的Class
Class c = Class.forName(className) ;
//直接获取该类实例
Object obj = c.newInstance() ;
//获取Method成员方法
Method m = c.getMethod(methodName) ;
m.invoke(obj) ;
}
}
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* 需求:
* 有一个ArrayList<Integer>集合,需要给里面添加String类型的数据,如何实现呢?
*/
public class Test3 {
public static void main(String[] args) throws Exception{
//创建ArrayList<Integer>集合
ArrayList<Integer> array = new ArrayList<Integer>() ;
//添加元素
array.add(100) ;
array.add(200) ;
//array.add("javaee") ;
//array.add("hello") ;
//获取类的字节码文件对象有三种
//直接获取arrayList集合的字节码文件对象
Class clazz = array.getClass() ;
//System.out.println(c);//class java.util.ArrayList
//获取当前Method类对象:成员方法
//public boolean add(Object e)
Method m = clazz.getMethod("add", Object.class) ;
//调用add方法
m.invoke(array, "hello") ;
m.invoke(array, "world") ;
System.out.println(array);
}
}
7.设计模式
设计模式:
* 结构型设计模式:
* 代理模式
* 静态代理
* 动态代理
* jdk动态代理
* CGlib动态代理
*
*
* 静态代理:--------Thread 的实现方式2 implements Runnable接口
* 真实角色:专注于自己的功能
* 代理角色:帮助真实角色对实际的方法进行增强
* 都需要实现指定的接口'
*
* 动态代理:在程序的运行过程中产生的代理类
* JDK动态代理
* Proxy 提供用于创建动态代理类和实例的静态方法
*
* 静态方法:
* public static Object newProxyInstance
(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h
)
参数1:表示类的加载器
参数2:获取类的接口列表的Class字节码文件
参数3:代理实例的处理程序 接口:InvocationHandler
JDK动态代理:在程序运行过程中,通过反射的产生的代理类(Proxy以及它里面
代理的处理程序:InvocationHandler)
* 举例:
* 让代理角色完成一些事情(帮
* 助真实角色---对其功能进行增强)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//创建接口类对象
//接口多态
UserDao ud = new UserDaoImpl() ;
ud.add();
ud.delete();
ud.search();
ud.update();
System.out.println("--------------------------");
UserDao2 ud2 = new UserDaoImpl2() ;
ud2.add();
ud2.delete();
ud2.search();
ud2.update();
System.out.println("--------------------------");
// 接口:InvocationHandler:处理程序
InvocationHandler my = new MyInvocationHandler(ud) ;//对ud
//对ud产生一个代理类
//UserDao(真实角色)----UserDaoImpl
UserDao ud3 = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(),
ud.getClass().getInterfaces(), my) ;
ud3.add();
ud3.delete();
ud3.search();
ud3.update();
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 代理类指派的处理程序
*/
public class MyInvocationHandler implements InvocationHandler {
//声明实例:对谁产生代理:UserDao
//使用Object代表所有类型
private Object target ;//目标类型
public MyInvocationHandler(Object target) {
this.target = target ;
}
//参数1:代理实例
//参数2:实际调用的方法
//参数3:实际参数
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("权限校验");
Object obj = method.invoke(target, args) ;
System.out.println("产生日志记录文件");
return obj;//返回代理类实例
}
}
public interface UserDao {
void add() ;//添加
void delete() ;//删除
void search();//查询
void update() ;//修改
}
public interface UserDao2 {
void add() ;//添加
void delete() ;//删除
void search();//查询
void update() ;//修改
}
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("调用了添加功能");
}
@Override
public void delete() {
System.out.println("调用了删除功能");
}
@Override
public void search() {
System.out.println("调用了查询功能");
}
@Override
public void update() {
System.out.println("调用了修改功能");
}
}
public class UserDaoImpl2 implements UserDao2 {
@Override
public void add() {
System.out.println("权限校验");
System.out.println("调用添加功能");
System.out.println("产生一个日志记录");
}
@Override
public void delete() {
System.out.println("权限校验");
System.out.println("调用删除功能");
System.out.println("产生一个日志记录");
}
@Override
public void search() {
System.out.println("权限校验");
System.out.println("调用查询功能");
System.out.println("产生一个日志记录");
}
@Override
public void update() {
System.out.println("权限校验");
System.out.println("调用修改功能");
System.out.println("产生一个日志记录");
}
}
8.MySQL
8.1MySQL基础
数据存储
集合
数组
容器----->使用完毕就不存在了集合对象/数组---被回收!
IO流
字节流/字符流 将数据存储到配置文件/xxx.txt文件中 ,效率低(耗时)
在计算机上--按照数据库软件---
关系型数据库
SQLServer,Oracle,db2,Mysql(开源,小型化)
非关型数据库
典型代表:mangodb/redis(key-value形式)---(缓存一些数据)
验证是否安装完毕(登录成功,说明mysql的环境变量也配置了)
C:\Users\zhangyang>mysql -uroot -p
Enter password: ******
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 6
Server version: 5.5.40 MySQL Community Server (GPL)
Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
DDL语句:数据库的定义:对数据库以及表的操作
四个:mysql默认带的四个库
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema | 相关配置信息库
| mysql | 存储相关表:user 用户表(管理员)
| performance_schema | mysql性能相关的
| test | 测试的库
+--------------------+
4 rows in set (0.00 sec)
-- 创建数据库
create database 数据库名;
mysql> create database mydb_01;
Query OK, 1 row affected (0.01 sec)
-- 方式2: create database if not exists 数据库名; 如果不存在这个库,创建一个新的
mysql> create database if not exists my_db02;
Query OK, 1 row affected (0.02 sec)
-- 查看数据库的信息 (数据库名,以及数据库的默认的编码格式)
mysql> show create database mydb_01;
+----------+------------------------------------------------------------------+
| Database | Create Database |
+----------+------------------------------------------------------------------+
| mydb_01 | CREATE DATABASE `mydb_01` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+----------+------------------------------------------------------------------+
1 row in set (0.01 sec)
-- 修改数据库的这个编码格式gbk
alter databse 数据库名 DEFAULT(省略不写) CHARACTER set gbk ;
mysql> alter database mydb_01 default character set gbk;
Query OK, 1 row affected (0.01 sec)
mysql> show create database mydb_01;
+----------+-----------------------------------------------------------------+
| Database | Create Database |
+----------+-----------------------------------------------------------------+
| mydb_01 | CREATE DATABASE `mydb_01` /*!40100 DEFAULT CHARACTER SET gbk */ |
+----------+-----------------------------------------------------------------+
1 row in set (0.00 sec)
-- 删除数据库
drop database 数据库名;
mysql> drop database my_db02;
Query OK, 0 rows affected (0.02 sec)
-- 方式2
drop database if exists 数据库名; 如果存在,删除该库
mysql> drop database if exists mydb_02;
Query OK, 0 rows affected (0.00 sec)
-- 修改库的字符集:创建数据库的时候直接修改了库的字符集
create database if not exists 数据库名 default(不写) character set 字符集 ;
mysql> create database if not exists mydb_04 default character set gbk ;
Query OK, 1 row affected (0.01 sec)
----------------------------------------------------------------
DDL语句操作表
创建表
create table 表名(
字段名称1 字段类型1,
字段名称2 字段类型2,
字段名称3 字段类型3,
.....
字段名称n 字段端类型n
);
常见字段类型:
varchar(m):m多少个字符-------------mysql字符串类型
int: 默认int(11):11位(记录实际字符) int(num):参数指定长度 mysql:整数类型
int(4) 1 ------>0001
double(5,2) mysql的小数类型 :表示小数5位,小数点后保留2位
时间:
date mysql的日期时间(仅仅是日期,不包含具体时间): 年月日 2020/11/5
datetime mysql的日期时间:是日期+具体时间 2020/11/5 时分秒...
timestamp(时间戳) 具体哪个时间操作的表 2020//11/15 16:16这个时间修改了表的字段
在创建表之前,先使用这个库
use 数据库名
mysql> use mydb_01;
Database changed
查询该库中的表
mysql> show tables;
Empty set (0.00 sec)
在mydb_01创建一个学生表
学生表 数据类型
姓名 varchar(3)
年龄 int
性别 varchar(2)
分数 double
出生日期 date
mysql> create table student(
-> name varchar(3),
-> age int,
-> gender varchar(2),
-> socre double(3,1),
-> birthday date
-> );
Query OK, 0 rows affected (0.03 sec)
mysql> show tables;
+-------------------+
| Tables_in_mydb_04 |
+-------------------+
| student |
+-------------------+
1 row in set (0.00 sec)
--- 查询表的结构
desc 表名;
mysql> desc student;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| name | varchar(3) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| gender | varchar(2) | YES | | NULL | |
| socre | double(3,1) | YES | | NULL | |
| birthday | date | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
-- 修改表
-- 给表中新增一列 (加入一个新的字段)
mysql> alter table student add address varchar(10);
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| name | varchar(3) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| gender | varchar(2) | YES | | NULL | |
| socre | double(3,1) | YES | | NULL | |
| birthday | date | YES | | NULL | |
| address | varchar(10) | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
6 rows in set (0.04 sec)
-- 修改表的数据类型
mysql> alter table student modify gender varchar(3);
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| name | varchar(3) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| gender | varchar(3) | YES | | NULL | |
| socre | double(3,1) | YES | | NULL | |
| birthday | date | YES | | NULL | |
| address | varchar(10) | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
6 rows in set (0.05 sec)
-- 修改表的名称 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 |
+----------+-------------+------+-----+---------+-------+
| name | varchar(3) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| sex | varchar(3) | YES | | NULL | |
| socre | double(3,1) | YES | | NULL | |
| birthday | date | YES | | NULL | |
| address | varchar(10) | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
6 rows in set (0.05 sec)
-- 修改表的名称
alter table 表名 rename to 新表名;
mysql> alter table student rename to teacher;
Query OK, 0 rows affected (0.01 sec)
mysql> show tables;
+-------------------+
| Tables_in_mydb_04 |
+-------------------+
| teacher |
+-------------------+
1 row in set (0.00 sec)
-- 删除表
mysql> drop table teacher;
Query OK, 0 rows affected (0.01 sec)
mysql> show tables;
Empty set (0.00 sec)
mysql> drop table if exists teacher;
Query OK, 0 rows affected (0.01 sec)
desc 表名: 查询表的结构 (字段名称/字段类型)
8.2使用图形化工具SQLyog
-- 使用图形化工具--- 前期写sql语句(练)
-- 进入到mydb_04数据库中
#注释(类上语法语气词)
USE mydb_04;
-- 在mydb_04数据库中创建一张表 student
-- 编号 int类型
-- 姓名 varchar(5)
-- 年龄 int类型
-- 性别 varchar(2)
-- 成绩 double(3,1)
-- 出生日期 data(日期)
CREATE TABLE student(
id INT, -- 编号
NAME VARCHAR(5), -- 姓名
age INT , -- 年龄
gender VARCHAR(2) , -- 性别
socre DOUBLE(3,1) , -- 成绩
birthday DATE
);
-- 查询表的结构
DESC student;
-- DML语句:操作语句
-- 插入数据1) (给表中的每一个字段赋值)
-- insert into 表名 values(值1,值2,值3,.....)
INSERT INTO student VALUES(1,'高圆圆',27,'女',99.8,'1990-2-3') ;
-- 插入部分字段2)
-- 注意事项:没有赋值的字段都是NULL(空值)
-- insert into 表名(id,name,age,gender) values(值1,....) ;
INSERT INTO student(id,NAME,age,gender) VALUES(2,'张冲',23,'男') ;
-- 注意事项:
-- 1)插入数据的时候,插入字段顺序必须要表的字段对应上
INSERT INTO student VALUES('李文擘',3,23,'男',90.8,'1997-11-5') ;
-- 2)如果是给全部字段插入数据,中间逗号隔开,依次插入多条语句!
INSERT INTO student VALUES (3,'赵又廷',38,'男',66.9,'1987-11-5'),
(4,'唐宇飞',26,'男',89.9,'1995-11-2') ;
-- 删除表的记录
-- delete from 表名 条件(where)
-- 带条件删除
DELETE FROM student WHERE socre = 90.8 ;
-- 删除全表数据:
--- TRUNCATE table 表名 和 delete from 表名 有什么区别?
-- 1)delete from 表名:删除全表数据 TRUNCATE table 表名 :删除表以及表中的数据--创建一张一模一样的表
-- 2)是否对自增长的主键影响?
/*
delete from 表名 不会影响自增长的主键(删除全表的数据)(不会清除自增长主键!)
TRUNCATE TABLE 表名 :直接删除了表(并且同时创建一张一模一样的表):自增长主键会清除掉!
*/
CREATE TABLE stu(
id INT PRIMARY KEY AUTO_INCREMENT , -- 增长的主键
NAME VARCHAR(5),
age INT
) ;
DROP TABLE stu ;
SELECT * FROM stu ;
INSERT INTO stu VALUES(1,'张三',20),(2,'李四',22) ;
INSERT INTO stu(NAME,age) VALUES('张钰',29) ;
-- delete from 表名
DELETE FROM stu;
TRUNCATE TABLE stu ;
-- 修改表
-- 带条件修改单个字段
-- update 表名 set 字段名 = 赋值 where 字段 = 值;
UPDATE student SET NAME = '张杨' WHERE id = 2 ;
-- 注意:update语句都需要带上where条件,否则就是"批量修改"
UPDATE student SET NAME = '高圆圆' ;
-- 修改多个字段 :将id=4的这个人 name='张冲' 并将年龄改成23
UPDATE student SET NAME = '张冲',age = 23 WHERE id = 4 ;
-- DQL语句 数据库的查询语句
-- 基本的查询语句
-- select 全部字段(*) from 表名; 查询全表数据
SELECT * FROM student ;
-- 查询指定的字段
-- 需求:查询id,name,age字段
SELECT
id,
NAME,
age
FROM
student ;
-- msyql的基础语法使用
CREATE TABLE student3( id INT, -- 编号
NAME VARCHAR(20), -- 姓名
age INT, -- 年龄
sex VARCHAR(5), -- 性别
address VARCHAR(100), -- 地址
math INT, -- 数学
english INT -- 英语
);
INSERT INTO student3(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 student3;
-- 查询语句
-- 条件查询
-- 基本条件查询:where语句
-- 条件:使用表达式符号<,>,<=,<=
-- !=(java的语法),<>(不等于 mysql的语法)
-- 并列关系:and &&
-- >=xx and <= xx 等价于 between xx and xx
-- 或的关系:or ||
-- 集合in(18,25,19)
-- 条件: is null :xxx是null值 不是null (is not null)
-- 基本查询
-- 需求:查询指定的学生信息 姓名和年龄
SELECT
NAME,
age
FROM
student3;
-- 需求:查询address地址信息
SELECT address FROM student3;
-- 将地址去重(select distinct 字段名称:将该字段去查询)
SELECT DISTINCT address FROM student3;
-- select name,address from student3;
-- 需求:查询name,总分成绩
SELECT
NAME, (math+english)
FROM
student3;
-- 查询的时候想给某个字段起别名 需要查询的字段名称 as(省略不写) '名称'
SELECT
NAME AS '姓名',
(math+english) AS '总分'
FROM student3;
-- 注意事项:对两个字段进行求和 两个字段必须一致:int类型(math+english)
-- 当前柳青的英语成绩为null
-- 在获取英语成绩的时候提供一个函数IFNULL(字段名,默认值):如果当前字段名称是null值,
-- 使用默认值给它赋值
SELECT
NAME '姓名',
(math+IFNULL(english,0))
FROM student3;
SELECT
NAME '姓名',
age '年龄',
sex '性别',
address '地址',
math '数学',
english '英语'
FROM student3;
USE mydb_04;
SELECT * FROM student3;
-- 条件:使用表达式符号<,>,<=,<=
-- !=(java的语法),<>(不等于 mysql的语法)
-- 并列关系:and &&
-- >=xx and <= xx 等价于 between xx and xx
-- 或的关系:or ||
-- 集合in(18,25,19)
-- 条件: is null :xxx是null值 不是null (is not null)
DESC student3 ;
-- 需求:查询全表数据
-- 开发中 :跟上全部字段
SELECT * FROM student3;
SELECT
id '学号',
NAME '姓名',
age '年龄',
sex '性别',
address '家庭住址',
math '数学成绩',
english '英语成绩'
FROM
student3;
-- 查询name为马云的人
SELECT
age ,
sex ,
address
FROM student3
WHERE NAME = '马云' ;
-- 查询年龄是20的人
SELECT
*
FROM
student3
WHERE age = 20 ;
-- 查询的时候:携带一些表达式符号
-- 查询年龄大于20的人
SELECT
*
FROM
student3
WHERE age >= 20 ; -- >= :大于或者等于这个值
-- 需求:查询年龄在20岁和30岁之间的人(包含两端)
SELECT
id '学号',
age '年龄',
NAME '姓名',
sex '性别',
address '地址'
FROM student3
WHERE age >=20 && age <=30;
-- and语法(推荐这种方式)
SELECT
id '学号',
age '年龄',
NAME '姓名',
sex '性别',
address '地址'
FROM student3
WHERE age >=20 AND age <=30;
-- between...and...
SELECT
id '学号',
age '年龄',
NAME '姓名',
sex '性别',
address '地址'
FROM student3
WHERE age BETWEEN 20 AND 30 ;
-- 需求:查询英语成绩为null的学生信息
/*
mysql 多行注释
select
*
from student3
where english = null ; xxx是null---Mysql is null
*/
SELECT
*
FROM
student3
WHERE english IS NULL ;
-- 查询英语成绩不为空的学生信息
SELECT
*
FROM
student3
WHERE english IS NOT NULL ;
-- 需求:查询年龄不等于20岁的学生信息
SELECT
id,
NAME,
age,
sex,
address
FROM
student3
WHERE age != 20 ; -- java语法:!= ,
-- 推荐mysql语法:<> 不等于
SELECT
id,
NAME,
age,
sex,
address
FROM
student3
WHERE age <> 20 ;
-- 需求:查询年龄是25岁或者18岁或者20岁的人
SELECT
*
FROM
student3
WHERE
age = 25 || age =18 || age =20; -- or连接或者是||连接
-- in(集合数据)
SELECT
*
FROM
student3
WHERE
age IN(25,18,20);
-- 模糊条件查询 关键字:like
-- 两个符号
-- %:表示多个任意字符
-- _:代表一个字符
-- 语法格式:select 字段名称 from 表名 where 某个字段 like '模糊符号..'
-- 查询姓马的人的学生信息
SELECT
*
FROM
student3
WHERE
NAME LIKE '马%' ;
-- 查询学生姓名带有第二个字符带有化的 学生信息
SELECT
*
FROM
student3
WHERE
NAME LIKE '_化%' ;
-- 查询学生姓名中有三个字的人
SELECT
*
FROM
student3
WHERE
NAME LIKE '___' ;
-- 查询学生姓名中包含"德"的人
SELECT
*
FROM
student3
WHERE
NAME LIKE '%德%' ;
-- 如果dos中出现中文乱码---查询所有包含'character'的变量
SHOW VARIABLES LIKE '%character%' ; -- 模糊查询
-- order by :排序查询
-- asc :升序排序
-- desc:降序排序
-- select 字段名称列表 from 表名 order by 排序的字段 排序规则;
-- 学生表中:按照数学成绩升序排序
-- 如果执行排序的时候,某个字段后面没有跟排序规则:默认升序排序 (整数类型)
SELECT
*
FROM
student3
ORDER BY
math ASC ;
-- 需求:按照英语成绩降序排序,并且查询出name,年龄,英语成绩
SELECT
NAME,
age,
english
FROM
student3
ORDER BY
english DESC ;
-- 修改表:id=7的英语成绩!
UPDATE
student3
SET
english = 96
WHERE
id = 7 ;
-- 针对多个字段同时排序
-- 需求:查询全表数据,数学成绩升序,英语成绩降序!
SELECT
id,
NAME,
math,
IFNULL(english,0)
FROM
student3
ORDER BY
math ASC,
english DESC ;
-- 多个字段同时排序:优先按照第一个字段的排序规则,
-- 第一个字段排序值如果相同,这个时候再按照第二个字段规则排序
-- 聚合函数
-- count函数,max函数,min函数,avg函数,sum函数
-- 使用最多就是count 统计 总记录数
-- avg使用多 统计平均分
-- 需求:统计当前表中总记录数有多少条
SELECT
COUNT(NAME) '人数'
FROM
student3 ;
/*
由于英语成绩:有一个人null值,忽略了
select
count(english)
from
student3;
*/
-- count 的使用:一般都是里面用的非业务字段
/*
每一张表都有id字段 (非业务字段)
id设置的是一个自增长主键 (非空并且唯一的)
count(id)
*/
SELECT
COUNT(id) '总记录数'
FROM
student3;
-- 需求:查询数学成绩的的最高分的学生信息
-- 聚合函数:单条单列的信息
SELECT
MAX(math)
FROM
student3;
-- 查询英语平均分
SELECT
SUM(math+IFNULL(english,0)) '总分',
AVG(IFNULL(english,0)) '英语平均分'
FROM student3;
-- 分组查询 group by 分组字段
-- 分组查询:查询的是哪些字段
-- 查询的字段:可以是分组字段,使用聚合函数
/*
GROUP BY分组查询,如果带有条件进行分组查询,要先去判断条件,在进行分组
先where,后group by
*/
-- 需求:查询数学平均分并且按照性别进行分组
SELECT
sex '性别',
AVG(math) '数学平均分'
FROM
student3
GROUP BY
sex ;
-- 需求:查询数学的平均分 并且按照性别分组: 条件 数学成绩小于70分的学生不参与分组
/*
select
sex '性别',
avg(math) '数学平均分'
from
student3
GROUP by
sex
where
math > 70;
*/
SELECT
sex '性别',-- 分组的字段
AVG(math) '数学平均分'
FROM
student3
WHERE
math > 70
GROUP BY
sex ;
-- 帅选having
-- 需求:查询数学的平均分 并且按照性别分组: 条件 数学成绩小于70分的学生不参与分组
-- 帅选条件:总人数大于2等于个人的
-- having 是需要放在group by 后面,不能放置在group by 之前
SELECT
sex '性别',
AVG(math) '数学平局分',
COUNT(id)
FROM
student3
WHERE
math > 70
GROUP BY
sex
HAVING
COUNT(id)>= 2;
-- 另一种写法
SELECT
sex '性别',
AVG(math) '数学平局分',
COUNT(id) 人数
FROM
student3
WHERE
math > 70
GROUP BY
sex
HAVING
人数 >= 2;
-- having和group by的区别
-- group by是在where条件后面用, 不能使用聚合函数的!但是查询的时候可以查询聚合函数
-- having是在group by 后面使用 ,后面使用聚合函数
-- 分页查询
-- 在mysql :limit关键字
-- 语法格式:limit 起始行数,每页显示的条数
-- 起始行数:从0开始
-- 每页展示3条数
-- 查询第一页的数据
SELECT
*
FROM
student3
LIMIT 0,3;
-- 查询第二页的数据:每页3条
-- limit 起始行数=(当前页码数-1)*每页显示的条数,每页显示条件
SELECT
*
FROM
student3
LIMIT 3,3;
-- 查询第三页:每页3条
SELECT
*
FROM
student3
LIMIT 6,3 ;
-- 自己模拟多条记录使用分页查询
SELECT * FROM student3;
8.3数据库的约束
-- 数据库的约束
-- 约束:通过一些特定的关键字 保证数据的安全性,正确性和有效性!
-- 数据库的约束
-- 默认约束 字段名称 类型 default '值'
-- 非空约束 not null
-- 唯一约束 unique
-- 主键约束 primary key
-- 自增长约束 auto_increment
-- 外键约束 foreign key
USE mydb_04;
-- 新建一张表
CREATE TABLE stu(
id INT ,-- 学号
NAME VARCHAR(3) -- 姓名
) ;
DROP TABLE stu ;
INSERT INTO stu VALUES(1,'张三','男') ,(2,'王五','男') ;
SELECT * FROM stu ;
-- 加入一列 :修改表
-- 加入性别字段
ALTER TABLE stu ADD gender VARCHAR(2) ;
-- 问题当前表中存在null值(没意义的)
-- 插入的时候:插入部分字段,没有赋值的字段是null
INSERT INTO stu(id,NAME) VALUES(3,'赵六') ;
-- 为了防止没有插入的字段是null值,可以在创建表的时候加入非空约束
-- default默认约束:是当某个字段没有赋值的时候起作用,如果赋值了,那么按照实际值 赋值即可!
CREATE TABLE stu(
id INT ,-- 学号
NAME VARCHAR(3), -- 姓名
gender VARCHAR(2) DEFAULT '男'
) ;
INSERT INTO stu(id,NAME) VALUES(1,'张冲') ;
INSERT INTO stu VALUES(2,'王五','女') ;
INSERT INTO stu VALUE(3,'袁一鸣',NULL) ; -- 数据不安全(默认约束还是存在问题!)
SELECT * FROM stu ;
DROP TABLE stu ;
-- 非空约束 not null
-- 创建表的时候加入非空约束
CREATE TABLE stu(
id INT,
NAME VARCHAR(3) NOT NULL -- 姓名不能null值
);
-- 插入全部字段数据
INSERT INTO stu VALUES(1,'张佳宁'),(2,'高圆圆'),(3,'王晓晨') ;
INSERT INTO stu VALUES(4,NULL) ;-- 当前name值不能null
-- Column 'name' cannot be null 列name不能为空
INSERT INTO stu VALUE(4,' ') ; -- 插入空字符串
DELETE FROM stu WHERE id = 4 ;
-- 将非空约束干掉
-- 修改表的字段类型
ALTER TABLE stu MODIFY NAME VARCHAR(3) ;
INSERT INTO stu VALUES(4,NULL) ;
DELETE FROM stu WHERE id = 4 ;
-- 修改表:再加入非空约束
ALTER TABLE stu MODIFY NAME VARCHAR(3) NOT NULL ;
SELECT * FROM stu ;
DROP TABLE stu ;
-- 唯一约束 unique
-- 创建表的时候加入唯一约束
CREATE TABLE stu(
id INT,
phone_number VARCHAR(11) UNIQUE
) ;
-- 插入数据
INSERT INTO stu VALUES(1,'18666668888') ;
-- insert into stu values(2,'18666668888') ;-- 字段不能重复 (UNIQUE 唯一)
INSERT INTO stu VALUES(2,'18266668888') ;
INSERT INTO stu VALUES(3,'18266668888') ;
DELETE FROM stu WHERE id = 3 ;
SELECT * FROM stu ;
-- 修改表:删除唯一约束
-- alter table stu modify phone_number varchar(11) ;
-- alter table 表名 drop index 带有唯一约束的字段名称;
ALTER TABLE stu DROP INDEX phone_number;
-- 修改表:添加唯一约束
ALTER TABLE stu MODIFY phone_number VARCHAR(11) UNIQUE ;
INSERT INTO stu VALUES(3,'18266668888') ; -- 当前加入唯一约束 不能重复
DROP TABLE stu ;
-- 主键约束 primary key
-- 特点:非空且唯一
CREATE TABLE stu(
INT PRIMARY KEY , -- 主键都应用在非业务字段上
NAME VARCHAR(5)
);
INSERT INTO stu VALUES(1,'张佳宁'),(2,'高圆圆') ;
-- insert into stu values(1,'王晓晨') ; -- id值不能重复的!
-- insert into stu values(null,'王晓晨') ;-- Column 'id' cannot be null id不能null
/*
一个表中
非业务字段 + 业务子弹
id name age sex gender music
通过id设置主键---是唯一的并且非空
举例:
大学的学生的数据库
name age sex
张三 20 男
name age sex
李三 20 男
业务字段可能随着需求的变化不断变化,都不会再业务字段上加入primary key
都是给每一张表设置id字段,并且主键+自增长
*/
-- 通过修改表:删除主键
-- alter table 表名 drop PRIMARY KEY ;
ALTER TABLE stu DROP PRIMARY KEY ;
-- 添加主键约束
ALTER TABLE stu MODIFY id INT PRIMARY KEY ;
INSERT INTO stu VALUES(1,'张三') ;
DELETE FROM stu WHERE NAME = '张三' ;
SELECT * FROM stu ;
DROP TABLE stu ;
-- 自增长约束 auto_increment
CREATE TABLE stu(
id INT PRIMARY KEY AUTO_INCREMENT, -- 自增长约束(id值自增从起始索引0一直++)
NAME VARCHAR(3)
) ;
INSERT INTO stu(NAME) VALUES('高圆圆') ;
INSERT INTO stu(NAME) VALUES('赵又廷') ;
INSERT INTO stu VALUES(5,'张佳宁') ;
INSERT INTO stu(NAME) VALUES('姚笛') ;
SELECT * FROM stu ;
-- 删除 自增长约束
ALTER TABLE stu MODIFY id INT; -- 删除了自增长约束,主键约束还在
INSERT INTO stu(NAME) VALUES('文章'); -- 没有给id插入值,默认null 主键约束了,给默认值0
DELETE FROM stu WHERE id = 0 ;
-- 添加 自增长约束
ALTER TABLE stu MODIFY id INT AUTO_INCREMENT;
INSERT INTO stu(NAME) VALUES('林允儿');
-- 外键约束:让多个表之间产生一种关系,来保证数据的安全性和有效性!
-- 创建员工表:employee
-- 员工编号 主键 并且自增长
-- 员工姓名
-- 员工部门名称
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(5), -- 员工姓名
dept_name VARCHAR(10) -- 员工的部门名称
);
-- 插入数据
INSERT INTO employee(NAME,dept_name) VALUES('张三','研发部') ;
INSERT INTO employee(NAME,dept_name) VALUES('李四','销售部') ;
INSERT INTO employee(NAME,dept_name) VALUES('王五','研发部') ;
INSERT INTO employee(NAME,dept_name) VALUES('赵六','销售部') ;
INSERT INTO employee(NAME,dept_name) VALUES('马七','测试部') ;
INSERT INTO employee(NAME,dept_name) VALUES('高圆圆','研发部') ;
SELECT * FROM employee ;
DROP TABLE employee ;
-- 上面的表的结构-----数据库的设计 部门名称:字段值大量冗余(重复名称)
-- 解决字段的冗余问题
-- 方案:单独的在创建一张表:部门表 dept表 存储者三个部门:研发部,测试部,销售部
-- 部门表
-- 主表:没有外键的表
CREATE TABLE dept(
id INT PRIMARY KEY AUTO_INCREMENT, -- 部门表的主键+自增长id
dept_name VARCHAR(10) -- 部门名称
) ;
-- 插入三个部门
INSERT INTO dept(dept_name) VALUES('研发部') ;
INSERT INTO dept(dept_name) VALUES('销售部') ;
INSERT INTO dept(dept_name) VALUES('测试部') ;
-- 创建员工表
CREATE TABLE employee(
id INT PRIMARY KEY AUTO_INCREMENT,-- 员工表的主键id
NAME VARCHAR(5) , -- 员工姓名
-- 定义字段:部门id -- 它的值和部门表的主键id 有关系
dept_id INT
);
-- 插入员工信息
INSERT INTO employee(NAME,dept_id) VALUES('张三',1) ;
INSERT INTO employee(NAME,dept_id) VALUES('李四',2) ;
INSERT INTO employee(NAME,dept_id) VALUES('王五',1) ;
INSERT INTO employee(NAME,dept_id) VALUES('赵六',2) ;
INSERT INTO employee(NAME,dept_id) VALUES('马七',3) ;
INSERT INTO employee(NAME,dept_id) VALUES('高圆圆',1) ;
-- 上面优化--已经 中文数据字段冗余问题: 创建一个部门表:将部门名称单独在该表中
INSERT INTO employee(NAME,dept_id) VALUES('张冲',5) ;
-- 问题:往员工表中插入一条不存在的部门数据,依然可以插入成功(存在非法数据!)
DROP TABLE dept ;
DROP TABLE employee ;
-- 为了防止非法数据,加入一种潜在关系 外界约束
-- 给某个字段设置外键约束
-- constraint(声明) 外键名称() foreign key
-- (员工表的dept_id) reference(关联) 部门表的主键id
SELECT * FROM dept ; -- 查询部门表
SELECT * FROM emploee ; -- 员工表
-- 员工表:从表:外键是声明在从表上
CREATE TABLE emploee(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(5) ,
dept_id INT,
CONSTRAINT dept_emp_fk FOREIGN KEY (dept_id)
REFERENCES dept(id)
) ;
INSERT INTO emploee(NAME,dept_id) VALUES('张三',1) ;
INSERT INTO emploee(NAME,dept_id) VALUES('李四',2) ;
INSERT INTO emploee(NAME,dept_id) VALUES('王五',1) ;
INSERT INTO emploee(NAME,dept_id) VALUES('赵六',2) ;
INSERT INTO emploee(NAME,dept_id) VALUES('马七',3) ;
INSERT INTO emploee(NAME,dept_id) VALUES('高圆圆',1)
SELECT * FROM dept ; -- 查询部门表
SELECT * FROM emploee ; -- 员工表
INSERT INTO emploee (NAME,dept_id) VALUES('张三丰',4) ; -- 当前4号部门不存在
-- 如果先 存在4号部门-- 给员工表中添加数据
-- 对于外键的关系表:主表,从表(外键)
-- 添加或者修改操作:先修改主表,这个从表
INSERT INTO dept(dept_name) VALUES('市场部') ;