第六周总结

第六周总结

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('市场部') ; 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值