java学习总结(三)

33.IO流(文件操作)

在这里插入图片描述
一.文件操作
文件是相关记录或存放于相同位置的数据的集合
File类主要常量及方法
方法或常量 类型 描述
String separator 常量 文件路径分隔符
public File(String filename) 构造方法 创建File类对象并传入完整路径
public boolean createNewFile() 方法 创建新文件
public boolean delete() 方法 删除文件
public boolean exists() 方法 判断文件是否存在
public boolean isDirectory() 方法 判断给定的路径是否为目录
public long length() 方法 返回文件的大小
public String[] list() 方法 列出指定目录的全部内容,只列出名称
public File[] listFiles() 方法 列出指定目录的全部内容
public boolean mkdir() 方法 创建目录
public boolean renameTo(File dest) 方法 为已有的文件重命名
//File.separato,文件分隔符
File f = new File(“d:”+File.separator+“source”);
//文件对象常用方法1
File file = new File(“yjwh.txt”);
if(file.exists()){ //判断文件是否存在
file.delete();//如果文件存在,则删除
System.out.println(“文件删除成功!”);
}else{
try {
file.createNewFile();//创建新文件
System.out.println(“文件创建成功!”);
String path1 = file.getPath();//获取文件的相对路径
System.out.println(“文件的相对路径是:”+path1);
String path2 = file.getAbsolutePath();//获取文件的绝对路径
//D:\workspace\yj_wh10\yjwh.txt
System.out.println(“文件的绝对路径是:”+path2);
String name = file.getName();//获取文件名
System.out.println(“文件名:”+name);
long length = file.length();//获取文件长度
System.out.println(“文件长度为:”+length);
System.out.println(file.isHidden()?“该文件是隐藏文件”:“该文件不是隐藏文件”);
System.out.println(file.isDirectory()?“文件是目录”:“文件不是目录”);
System.out.println(file.canWrite()?“该文件可写入”:“该文件不可写入”);
System.out.println(file.canRead()?“该文件可读取”:“该文件不可不可”);
System.out.println(“修改文件名为pengsir:”+file.renameTo(new File(“pengsir.txt”)));
} catch (IOException e) {
e.printStackTrace();
}
}

//文件对象常用方法2
		File file = new File("E:\\yj03");
		file.mkdir();
		//创建单级目录 mkdir
		//成员
		//课件
		//案例
		//娱乐
		File file1 = new File("E:\\yj03\\成员");
		File file2 = new File("E:\\yj03\\课件");
		File file3 = new File("E:\\yj03\\案例");
		File file4 = new File("E:\\yj03\\娱乐");
		file1.mkdir();
		file2.mkdir();
		file3.mkdir();
		file4.mkdir();
		
		//创建多级子目录 mkdirs
		//yj03-分享-电影-电影类型
		File file5 = new File("E:\\yj03\\分享\\电影");
		file5.mkdirs();
		
		//使用文件夹来装载文件
		File file6 = new File("E:\\yj03\\娱乐\\五花八门");//单级目录
		File file7 = new File("E:\\yj03\\娱乐\\五花八门\\奥古斯塔·艾达·洛夫莱斯.jpg");
		File file8 = new File("E:\\yj03\\娱乐\\五花八门\\毕业论文开题报告.doc");
		File file9 = new File("E:\\yj03\\娱乐\\五花八门\\盗墓笔记.txt");
		File file10 = new File("E:\\yj03\\娱乐\\五花八门\\JDK2.0.chm");
		File file11 = new File("E:\\yj03\\娱乐\\五花八门\\我的美照.png");
		File file13 = new File("E:\\yj03\\娱乐\\五花八门\\明月几时有.mp3");
		File file14 = new File("E:\\yj03\\娱乐\\五花八门\\南海行动.avi");
		File file15 = new File("E:\\yj03\\娱乐\\五花八门\\绝密文档");
		File file16 = new File("E:\\yj03\\娱乐\\五花八门\\你懂的");
		file6.mkdir();
		file15.mkdir();
		file16.mkdir();
		try {
			file7.createNewFile();
			file8.createNewFile();
			file9.createNewFile();
			file10.createNewFile();
			file11.createNewFile();
			file13.createNewFile();
			file14.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//获取指定目录下得所有文件名
		File file12 = new File("E:\\yj03\\娱乐\\五花八门");
		String[] names= file12.list();
		System.out.println("五花八门目录下的文件有:");
		for (String string : names) {
			System.out.println(string);
		}
		
		//作业
		//jpg,doc,txt,chm,png,mp3,avi,目录:绝密文档,你懂的
		//指定目录"E:\\yj03\\娱乐\\五花八门"
		//判断哪些文件是目录:XXX文件是目录   isDirectory
		//判断哪些文件是音乐:XXX文件是音乐 mp3
		//判断哪些文件是电影...  avi
		//判断哪些文件是文档...  doc,txt,chm
		//判断哪些文件是图片...  jpg,png
		//提示:使用listFiles()获取指定目录下的文件数组
		File file20 = new File("E:\\yj03\\娱乐\\五花八门");
		File[] files =  file20.listFiles();
		for (int i = 0; i < files.length; i++) {
			if(files[i].isDirectory()){
				System.out.println(files[i].getName()+"是目录");
			}else if(files[i].getName().endsWith("avi")){
				System.out.println(files[i].getName()+"是电影");
			}else if(files[i].getName().endsWith("mp3")){
				System.out.println(files[i].getName()+"是音乐");
			}else if(files[i].getName().endsWith("jpg") || files[i].getName().endsWith("png")){
				System.out.println(files[i].getName()+"是图片");
			}else{
				System.out.println(files[i].getName()+"是文件");
			}
		}

34.IO流(字节流、数据读写流 、缓冲流、文件copy)

一、流
数据流是一串连续不断的数据的集合,即一连串流动的字符

//IO流---数据读写流 
//I-input读取,O-output输出
//流向:input(输入流 ) output(输出流)
//数据类型: 
//字节流(类输出字节流Outputstream,输入的字节流Inputstream),
//字符流(输出字符流Writer,输入的字符流Reader)
//以上四个都是抽象类,在开发中主要使他们的子类来进行读写

二、字节流





三、流使用步骤   

四、InputStream和OutputStream(字节)
InputStream:   程序可以从中连续读取字节的对象称为字节输入流
 InputStream 的主要方法
方法 描述
public void close() 关闭输入流
public abstract int read() 以数字的方式读取内容 
public int read(byte[] b) 将内容读到byte数组中,同时返回读入的个数
 OutputStream:   程序可以向其中连续写入字节的对象称为字节输出流 
OutputStream 的主要方法
方法 描述
public void close() 关闭输出流
public abstract void write(int b) 在数据流中写入一个字节
public void write(byte[] b,int off,int len) 在数据流中写入一个指定范围的byte数组
public void write(byte[] b) 在数据流中写入一个byte数组
public void flush() 刷新缓冲区
InputStream和OutputStream是两个抽象类
使用子类FileInputStream和FileOutputStream分别读取和写入文件的内容
//使用File类打开文件,指定要写的文件
		//File file = new File("E:\\yj03\\sutdent.txt");
		//FileOutputStream是Outputstream的子类,使用输出流指定文件
		FileOutputStream fos = null;
		try {//FileOutputStream(String name) 
			fos=new FileOutputStream("E:\\yj03\\sutdent.txt");
			fos.write("you are good students".getBytes());
			System.out.println("输出成功!");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				fos.flush();//清理数据流管道
				fos.close();//关闭流管道
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		//追加输出 FileOutputStream(String name, boolean append) 
		FileOutputStream fos1 = null;
		try {
			fos1 = new FileOutputStream("E:\\yj03\\sutdent.txt",true);
			fos1.write("\r\n".getBytes());//\r\n在windows下换行,linux \n换行
			fos1.write("你们都是好宝宝".getBytes());
			fos1.write("\r\n".getBytes());
			//参数为int类型,对应了ASCII编码的字符
			fos1.write(97);
			fos1.write("\r\n".getBytes());
			fos1.write(new byte[]{65,66,67,68},0,4);
			System.out.println("输出成功!");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				fos.flush();//清理数据流管道
				fos.close();//关闭流管道
			} catch (IOException e) {
				e.printStackTrace();
			}

//使用Inputstream的子类FileInputStream来读取数据
		//FileInputStream(String name) 
		try {
			FileInputStream fis = new FileInputStream("E:\\yj03\\sutdent.txt");
			
			//单个字节读取
//			int temp = fis.read();//调用read()读取了一个字节
//			System.out.println((char)temp);//121--y
			
			//使用循环来读取单个字节,遇到中文会乱码,是因为每次读取都把字节强行转换成了字符。
			//temp == -1表示没有读到数据
//			int temp = fis.read();//循环的起始变量
//			while(temp != -1){//循环条件
//				System.out.print((char)temp);
//				temp = fis.read();//每循环一次读取一次
//			}
			
			//尝试使用read(byte[] b)一次读取一个字节数组
			byte[] bytes = new byte[1024];
			int len = 0;
			while((len=fis.read(bytes)) != -1){
//				//bytes这个数组如何解析成字符串输出
				System.out.print(new String(bytes,0,len));
				System.out.println("读取成功");
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

五、缓冲流
//BufferedInputStream
//BufferedOutputStream
//在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。
//在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。
//他的性能非常高
FileOutputStream fos = null;
		BufferedOutputStream bos = null;
		try {
			fos = new FileOutputStream("E:\\yj03\\缓冲流.txt");
			//BufferedOutputStream(OutputStream out) 
			//在缓冲字节输出流中需要一个OutputStream的实例
			//可以将它的子类FileOutputStream的实例作为参数传递
			bos = new BufferedOutputStream(fos);
			//使用缓冲流写数据,会自动默认回家写出的数据
			//缓冲流写数据,一定要记得清空且关闭流管道
			bos.write("明天上课非常辛苦啊。".getBytes());
			bos.write("但是痛并快乐着。".getBytes());
			bos.write("\r\n".getBytes());
			bos.write("醉卧职场君莫笑,古来编码几人回。".getBytes());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				bos.flush();//清理流管道
				bos.close();//关闭流管道
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

//使用缓冲字节输入流来读取数据
		BufferedInputStream bis = null;
		try {
			FileInputStream fis = new FileInputStream("E:\\yj03\\缓冲流.txt");
			bis = new BufferedInputStream(fis);
			
			byte[] bytes = new byte[1024];
			int data =0;
			while((data = bis.read(bytes))!=-1){
				System.out.println(new String(bytes,0,data));
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				bis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

六、使用字节流的读写来实现文件的copy
public static void main(String[] args) {
		try {
			//使用输入流指定需要复制的文件路径,从哪里读取
			FileInputStream fis = new FileInputStream("E:\\yj03\\平凡之路.mp3");
			//使用输出流指定文件复制的地址,写到哪里去
			FileOutputStream fos = new FileOutputStream("平凡之路.mp3");
			//作业,复制一首歌曲,要求
			Demo3 demo = new Demo3(); 
			//先使用字节读写
			//demo.copyByte(fis, fos);//成绩为35533毫秒
			//使用字节数组读写
			//demo.copyByteArray(fis, fos);//成绩为72毫秒
			//使用缓冲流来读写
			demo.copyByteBuffer(fis,fos);//成绩为13毫秒-冠军
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void copyByte(FileInputStream fis,FileOutputStream fos){
		//读写数据,使用字节
		int data = 0;
		try {
			//获取一个开始的毫秒数
			long start = System.currentTimeMillis();//从1970年1月1号到目前的毫秒数
			while((data = fis.read())!=-1){
				fos.write(data);
			}
			long end = System.currentTimeMillis();
			System.out.println("使用字节复制共耗时"+(end-start)+"毫秒");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void copyByteArray(FileInputStream fis,FileOutputStream fos){
		//读写数据,使用字节
		//读取文件,使用字节数组读取
		byte[] bytes = new byte[1024];
		int len = 0;
		try {
			long start = System.currentTimeMillis();//从1970年1月1号到目前的毫秒数
			while((len = fis.read(bytes))!=-1){
				fos.write(bytes);
			}
			long end = System.currentTimeMillis();
			System.out.println("使用字节数组复制共耗时"+(end-start)+"毫秒");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void copyByteBuffer(FileInputStream fis,FileOutputStream fos){
		//声明两个缓冲流
		BufferedInputStream bis = null;
		BufferedOutputStream bos =null;
		try {
			long start = System.currentTimeMillis();
			bis = new BufferedInputStream(fis);
			bos = new BufferedOutputStream(fos);
			byte[] bytes = new byte[1024];
			int data =0;
			while((data = bis.read(bytes))!=-1){
				bos.write(bytes,0,data);
			}
			long end = System.currentTimeMillis();
			System.out.println("使用缓冲流复制共耗时"+(end-start)+"毫秒");
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			try {
				bis.close();
				bos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

35.IO流编解码(编码表+转换流+编解码)

一、编码表
//编码表:由现实四节的字符和对应的数值组成的一张表
//ASCII码表:(美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言
//    'a' : 97   'b' : 98    'c' :99
//ISO-8859-1 : 拉丁字符编码表(世界范围内,基于拉丁字母的语言字符以及符号)
//GB2312 : 中华人民共和国简体字符编码表 3个字符。汉字1字符 = 2字节
//GBK : 是GB2312编码表的扩展表,融合更多的汉字和字符,约有2到3个
//GB18030 : 对GBK编码进行了进一步的规范和拓展
//BIG-5 :  对港澳台地区所值得繁体字符编制的一个编码解决方案
//Unicode : 国际范围编码表,基本上融合了全世界范围的字符编码,他所有的字符都是2字节表示,Java语言的默认编码
//UTF-8 : 是一种针对Unicode的可变长度字符编码,又称万国码。

二、编解码问题
//编解码问题,字节(看不懂内容)--- 字符(内容看懂),之前通过char强行转,可能出现乱码
//转换流 = 字节流 + 编码表 (ASCII,GBK,BIG-5,GB2312,UTF-8...)
//举例
//中共地下党peng同志  String -- byte[] -- getBytes("编码格式")
		//  "危险" --数字--二进制--发送
//我党接头人黄同志  byte[] -- String -- new String(byte[],"编码格式")
	    //  二进制 -- 十进制 -- 数字 -- "危险"
String str = "危险";
		//byte[] bytes = str.getBytes();//默认为GBK [-50, -93, -49, -43]
		//byte[] bytes = str.getBytes("gb2312");//[-50, -93, -49, -43]
		byte[] bytes = str.getBytes("UTF-8");//[-27, -115, -79, -23, -103, -87]
		System.out.println(Arrays.toString(bytes));
		
		//String info = new String(bytes);//默认解码为GBK
		String info = new String(bytes,"UTF-8");
		System.out.println(info);

三、编解码
//IO流的编解码
//使用转换流进行字节和字符的转换
//文件数据---通常使用字节读写
//文本数据---通常使用字符读写,或者转换流
//OutputStreamWriter 输出转换流 他继承自Writer是字符流通向字节流的桥梁:
//可使用指定的字符集 将要写入流中的字符编码成字节。
//它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。 

//InputStreamReader 输入转换流他继承自Reader
//它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
FileOutputStream fos = new FileOutputStream("密电.txt");
		//OutputStreamWriter(OutputStream out) 默认使用GBK
		//OutputStreamWriter osw = new OutputStreamWriter(fos);
		OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
		osw.write("今天他们要对你下手");
		osw.close();
		
		FileInputStream fis = new FileInputStream("密电.txt");
		InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
		//System.out.print((char)isr.read());
		int ch = 0;
		while((ch = isr.read())!=-1){
			System.out.print((char)ch);
		}

36.IO流(字符流)

一、 Reader和Writer
//字符流(输出字符流Writer,输入的字符流Reader)
//文本数据---通常使用字符读写,或者转换流
Reade和Writer是两个抽象类,分别与InputStream和OutputStream对应 
Reader的主要方法 
方法 描述
public abstract void close() 关闭输入流
public int read() 读取单个字符
public int read(char[] c) 将内容读取到字符数组中,并返回读取的长度
Writer主要方法
方法 描述
public abstract void close() 关闭输出流
public void write(String str) 输出字符串
public void write(char[] c) 输出字符数组
public abstract void flush() 强制清空缓存
二、 FileReader和FileWriter
 FileReader和FileWriter是Reader和Writer的子类,主要用于读写字符文件 
 // 构建输出字符流对象
		FileWriter fw = null;
		try {
			fw = new FileWriter("字符流.txt");
			fw.write("我是一匹来自南方的狼");
			fw.write("\r\n");
			fw.write(97);
			fw.write("\r\n");
			fw.write(new char[] { '林', '彭', '聂', '徐', '刘', '朱', '陈', '贺', '叶', '罗' });
			fw.write("\r\n");
			fw.write(new char[] { '科', '处', '局', '省', '国' }, 1, 3);
			fw.write("\r\n");
			fw.write("中国戊戌变法", 2, 4);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				fw.flush();
				fw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

FileReader fr = null;
		try {
			fr = new FileReader("字符流.txt");
			//单个字符读取
			//System.out.println((char)fr.read());
			
			//字符数组读取
			char[] chs = new char[1024];
			int len = 0;
			while((len = fr.read(chs)) !=-1){
				System.out.print(new String(chs,0,len));
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				fr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
三、InputStreamReader和OutputStreamWriter
 InputStreamReader和OutputStreamWriter是字节流和字符流之间的转换类
字符流转换字节流

字节流转换字符流


四、 BuffedReader和BufferedWriter
缓冲字符流
FileWriter fw = null;
		BufferedWriter bw = null;
		try {
			fw = new FileWriter("缓冲字符流.txt");
			bw = new BufferedWriter(fw);
			bw.write("去年今日此门中,");
			bw.newLine();
			bw.write("人面桃花相映红。");
			bw.newLine();
			bw.write("人面不知何处去,");
			bw.newLine();
			bw.write("桃花依旧笑春风。");
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				bw.flush();
				bw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

FileReader fr = null;
		BufferedReader br = null;
		try {
			fr = new FileReader("缓冲字符流.txt");
			br = new BufferedReader(fr);
			//BufferedReader 
			//中提供readLine()一次读取一行数据,如果没读到数据则为null
			//System.out.println(br.readLine());
			String data = null;
			while((data = br.readLine())!=null){
				System.out.println(data);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				br.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

在这里插入图片描述
在这里插入图片描述

37.IO流(序列化、反序列化)

一、对象序列化和反序列化
内存中的整个对象存储至文件中称为对象序列化 
对象序列化步骤

 ObjectInputStream类主要方法
方法 类型 描述
public ObjectInputStream (InputStream in) 构造方法 构造输入对象 
final Object readObject() 方法 从指定的位置读取对象
ObjectOutputStream类主要方法
方法 类型 描述
public ObjectOutputStream (OutputStream out) 构造方法 传入输出对象 
final void writeObject(Object o) 方法 输出对象

二、使用对象流实现序列化和反序列化
//ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。
//ObjectInputStream  读取(反序列化)对象。通过在流中使用文件可以实现对象的持久存储

//  对象所属的类实现Serializable接口
//一个标准的Java实体模型(JavaBean)
//1.属性必须进行封装
//2.添加构造方法
//3.实现序列化接口(该类可以被序列化和反序列化)
public class Student implements Serializable { }

//ObjectOutputStream 序列化
FileOutputStream fos = null;
		ObjectOutputStream oos = null;
		try {
			fos = new FileOutputStream("E:\\yj03\\student.txt");
			oos = new ObjectOutputStream(fos);
			//通过writeObject()方法序列化学生对象
			Student xiaoming = new Student(9527,"小明",'男',22,"写代码");
			oos.writeObject(xiaoming);
			System.out.println("序列化成功!");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				oos.flush();
				oos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}	
		}

//使用ObjectInputStream进行反序列化
ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new FileInputStream("E:\\yj03\\student.txt"));
//  使用ObjectInputStream读取对象
			Object obj = ois.readObject();
			Student stu = (Student)obj;
		
			System.out.println("俺乃是寒假班的"+stu.getName());
			System.out.println("俺今年"+stu.getAge());
			System.out.println("俺最大的爱好就是"+stu.getHobby());
			System.out.println("反序列化完毕!");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			try {
				ois.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

在这里插入图片描述
在这里插入图片描述

38.多线程(继承Thread类+线程生命周期)

一、线程入门
进程与线程
进程是指在系统中正在运行的一个应用程序,线程是比进程更小的执行单位
一个进程可以产生多个线程,多线程机制使程序运行效率变得更高
a.进程:
1.每启动一个程序,就可以通过任务管理器查看其对应的进程
2.CPU控制管理调度多个进程,一个进程一旦开启,内存将会为其开辟空间
b.进程有多少个
有主要是由系统管理者和系统内置的启动设置项来决定的
c.多核心CPU和单核心CPU的区别主要在哪里
多核心CPU能够让进程更加有效率的调度,能够产生更加积极的处理进程争抢效果
d.什么是线程?
一个程序启动开启一个进程,一个进程中至少含有一个线程
//Java中的多线程
//Jvm中默认是有两个线程
//主线程:main
//垃圾回收线程:GC机制用来回收内存空间
//无侵入性的POJO-一个没有继承任何类的标准Java实体
public static void main(String[] args) {
int a = 10;
String msg = “马上要过节”;
Object obj = new Object();
System.out.println(“线程到此结束”);
//a,msg,obj没有被使用,那么GC机制将在程序结束后,回收这些赋予这些变量的内存空间
//这两个线程称为系统线程
System.out.println(“这个程序中有几个线程在运行”);
}

二、多线程实现方式
1.继承Thread类(//观察线程争抢效果)start()
//线程是程序中的执行线程。Java虚拟机允许应用程序并发地运行多个执行线程。
//通过继承Thread类来创建线程
//1.继承Thread类
//2.重写run方法 目的:在于给这个线程配线任务,主要要的任务都需要写在run方法中
//3.实例化当前线程,调用start方法启动线程
//观察线程争抢效果
public class Demo2 extends Thread {
public void run(){
for (int i = 0; i < 200; i++) {
//getName()获取当前线程的名字
System.out.println(this.getName()+“我肚子饿了~有吃的吗?”);
}
}
public static void main(String[] args) {
Demo2 demo1 = new Demo2();
Demo2 demo2 = new Demo2();
Demo2 demo3 = new Demo2();

	//setName给当前线程起名字
	demo1.setName("祝:");
	demo2.setName("张:");
	demo3.setName("彭:");
	
	//demo1.run();
	//demo2.run();
	//demo3.run();
	//如果调用run()方法,无法体现出线程的争抢特显,就是一个顺序执行的结果
	//启动线程需要调用start();//启动线程需要调用
	//线程的争抢效果是没有规律的
           。。一个线程类对象只能调用一次start()方法 
	demo1.start();
	demo2.start();
	demo3.start();
}

}

三、Thread类中的主要方法如下:
方法 描述
public static Thread currentThread() 返回当前的线程
public final String getName() 返回线程名称
public final void setpriority(int priority) 设置线程优先级
public void start() 开始执行线程
public static void sleep(long m) 使用目前的线程休眠m秒
public final void yield() 暂停目前的线程,运行其他线程
public void run() 执行线程
2.//线程的调度=》setPriority()
public class Demo3 extends Thread{
public void run(){
for (int i = 0; i < 200; i++) {
//getName()获取当前线程的名字
System.out.println(this.getName()+“我肚子饿了~有吃的吗?”);
}
}
public static void main(String[] args) {
Demo2 demo1 = new Demo2();
Demo2 demo2 = new Demo2();
Demo2 demo3 = new Demo2();

	//setName给当前线程起名字
	demo1.setName("祝:");
	demo2.setName("张:");
	demo3.setName("彭:");
	//调度线程
	//Java中线程执行有两种模式
	//1.分时模式:所有的线程按照一定顺序轮流在使用CPU的调度权,达到一个基本的平均分配效果
	//  所以在分时模式中,每个线程都能得到基本相同的CPU的占用时间
	//2.争抢模式:在该模式中,优先级高的线程,会有跟多的几率争抢到CPU的使用权,
	//	因为争抢模式导致了线程的随机性和不确定性,如果优先级相同的两个线程出现争抢,那么
	//  CPU会随机的挑选一个线程。Java中的默认调度模式是争抢模式
	
	//获取3个线程的优先级
	//System.out.println(demo1.getPriority());
	//System.out.println(demo2.getPriority());
	//System.out.println(demo3.getPriority());
	
	//尝试设置不同的优先级,观察争抢效果
	//setPriority设置线程的调度优先级,不是一个绝对值,只是一个调度的趋势
	demo1.setPriority(1);
	demo2.setPriority(1);
	demo3.setPriority(10);
	
	//默认情况下,所有线程的优先级都是5

// System.out.println(MAX_PRIORITY);//10
// System.out.println(NORM_PRIORITY);//5
// System.out.println(MIN_PRIORITY);//1

	demo1.start();
	demo2.start();
	demo3.start();
}

}

3、线程礼让效果=》yield()
//暂停当前的线程,并执行其他的线程
public class Demo4 extends Thread {

public void run(){
	for (int i = 1; i <=100; i++) {
		System.out.println(this.getName()+"今年已经"+i+"岁了");
	}
	//可以使用线程礼让的yield方法实现,让另一个线程先执行的效果
	//由于线程的争抢特性,就算使用了yield()方法,不能完全保证一个线程执行一次
	//public static void yield()礼让是一个静态方法
	Demo4.yield();
}

public static void main(String[] args) {
	Demo4 demo1 = new Demo4();
	demo1.setName("杨过");
	
	Demo4 demo2 = new Demo4();
	demo2.setName("小龙女");
	
	demo1.start();
	demo2.start();
}

4.线程join=》.join()
//有些业务中需要让一个线程先完成工作,让后再让其他的线程继续工作
//线程join之前,必须让着线程先就绪(start)
//start(预备)—run(跑)
public void run(){
for (int i = 1; i <=100; i++) {
System.out.println(this.getName()+“今年已经”+i+“岁了”);
}
}
public static void main(String[] args) {
Demo5 demo1= new Demo5();
Demo5 demo2= new Demo5();
Demo5 demo3= new Demo5();
demo1.setName(“刘备”);
demo2.setName(“关羽”);
demo3.setName(“张飞”);
//如果想实现线程join,最好让该线程点调用start方法然后马上调用join。以保障优先执行
demo1.start();
try {
demo1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
demo2.start();
demo3.start();
}

5.//线程休眠=》sleep()
public void run(){
for (int i = 1; i < 100; i++) {
System.out.println(this.getName()+“正在收第”+i+“个人头。”);
//休息一秒钟收一个人头
//public static void sleep(long millis)
try {
Demo6.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Demo6 demo1 = new Demo6();
Demo6 demo2 = new Demo6();
Demo6 demo3 = new Demo6();
demo1.setName(“吴:”);
demo2.setName(“小:”);
demo3.setName(“田:”);
demo1.start();
demo2.start();
demo3.start();
}

6.//守护线程:setDaemon()
最明显的实际案例可以参考坦克大战,当司令部线程被打爆的时候,那么坦克的线程也将终止
//当其中的主要的线程声明周期结束时,其他的线程也停止工作
//为了实现这个效果,就需要给其他的线程设置为守护线程
public void run(){
for (int i = 1; i <= 100; i++) {
System.out.println(this.getName()+":活了"+i+“岁了。”);
}
}
public static void main(String[] args) {
Demo7 demo2 = new Demo7();
Demo7 demo3 = new Demo7();

	demo2.setName("关羽");
	demo3.setName("张飞");
	
	demo2.setPriority(1);
	demo3.setPriority(1);
	
	//将demo2,demo3设置为守护线程
	//setDaemon(boolean on) 
	demo2.setDaemon(true);		
	demo3.setDaemon(true);
	
	//让守护线程调用start启动线程
	demo2.start();
	demo3.start();
	
	//让主线程main方法当中的业务
	//currentThread()获取当前的线程(主线程)
	Thread.currentThread().setName("刘备");
	Thread.currentThread().setPriority(10);//设置线程的优先级
	for (int i = 1; i <= 63; i++) {
		System.out.println(Thread.currentThread().getName()+":已经"+i+"岁了");
	}
}

7.//线程生命周期结束(终止线程)=》interrupt()
//编写一个run方法,实现钓鱼的子线程业务
public void run(){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
System.out.println(“今天是”+sdf.format(date)+",天气不错,挺风和日丽的。我来护城河钓鱼");
try {
Demo8.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Date date1 = new Date();
System.out.println(sdf.format(date1)+“我钓起来一条中华鲟”);
}

//尝试关闭线程
//方法1:public final void stop() -- 已过时。 
//方法2:public void interrupt() -- 中断线程,推荐使用
public static void main(String[] args) {
	Demo8 demo = new Demo8();
	demo.start();
	try {
		Thread.sleep(3000);
		//stop()方法过于暴力,他直接杀死子线程,安全性比较在jdk1.5就被不推荐以上了
		//demo.stop();
		demo.interrupt();//表线程设置到终止状态,让后抛出异常 InterruptedException。 
		System.out.println("钓了3秒都没钓起来,我看你还是回家算了");
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

39.多线程(实现Runnable接口+线程同步synchronized())

一、实现Runnable接口
//创建线程的方式2
//实现Runnable接口
//1.使用当前类 implements Runnable
//2.实现implements接口中的run方法
//3.实例化当前类,作为资源
//4.借助线程类Thread产生线程实例,调用start方法启动线程
public class Demo1 implements Runnable{
public void run() {
for (int i = 1; i <= 200; i++) {
System.out.println(Thread.currentThread().getName()+“钓起了第”+i+“条鱼”);
}
}
public static void main(String[] args) {
Demo1 demo = new Demo1();
//借助线程了生成实例,使用当前类作为共享资源提供给线程实例
Thread t1 = new Thread(demo,“pengsir”);
Thread t2 = new Thread(demo,“祝”);
Thread t3 = new Thread(demo,“张”);
t1.start();
t2.start();
t3.start();
}
}
二、Thread类与Runnable接口区别

继承Thread类受单继承影响,不适合多个线程共享资源
实现Runnable接口,方便实现资源共享

三、实现多线程资源共享
//作业:
//在人信汇5楼万达影城,需要发一批免费观影卷
//现在招募咱们3个同学去做兼职,使用学过的两种实现线程的方式来发这100张票

Thread类
public class Demo2 extends Thread{
//如果需要使用继承Thread类来实现多线程资源共享的话,需要给资格共享的资源添加static修饰
public static int ticket=100;

public void run(){
	while(ticket>0){
		System.out.println(this.getName()+":已经卖掉了第"+(ticket--)+"张票");
	}

// for (;ticket > 0;) {
// System.out.println(this.getName()+":已经卖掉了第"+(ticket–)+“张票”);
// }
}
public static void main(String[] args) {
Demo2 ma = new Demo2();
Demo2 qi = new Demo2();
Demo2 chen = new Demo2();
ma.setName(“马”);
qi.setName(“祁”);
chen.setName(“陈”);
ma.start();
qi.start();
chen.start();
}
}

Runnable接口
public class Demo3 implements Runnable {
// 不需要添加static ?
public int ticket=100;
public void run() {
while(ticket>0){
System.out.println(Thread.currentThread().getName()+":已经卖掉了第"+(ticket–)+“张票”);
}
}
public static void main(String[] args) {
Demo3 demo = new Demo3();//当前类只实例化了一次,只产生了一个对象
//三个线程对象共享了同一个demo资源,所以他们能做协同工作,共同争抢一个资源
Thread t1 = new Thread(demo,“马”);
Thread t2 = new Thread(demo,“陈”);
Thread t3 = new Thread(demo,“祁”);
t1.start();
t2.start();
t3.start();
}
}

四、同步与死锁
1.线程安全问题

2.线程同步
同步是指在同一时间段内只能运行一个线程
同步可以解决线程中的安全问题

public void run() {
synchronized (str) {
while (ticket > 0) {
//synchronized线程同步代码块:同步是指在同一时间段内只能运行一个线程
//同步可以解决线程中的安全问题
//obj表示这个同步快的对象锁,这个对象锁可以是任意对象
try {
//尝试用sleep()产生一个时间间隔,来避免线程争抢产生的错误
//即便是产一个时间间隔还是无法完全的解决线程争抢产生的错误
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName() + “:已经卖掉了第” + (ticket–) + “张票”);
}
}

public void run() {
while(ticket>0){
sellTicket(this);
}
}

	//synchronized修饰的方法,成为同步方法
	//同步方法的对象锁,需要使用当前类的实例作为锁
	public synchronized void sellTicket(Object obj){
		if(ticket>0){
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+":已经卖掉了第"+(ticket--)+"张票");

		}
	}
	
	public static void main(String[] args) {
		Demo5 demo = new Demo5();//当前类只实例化了一次,只产生了一个对象
		//三个线程对象共享了同一个demo资源,所以他们能做协同工作,共同争抢一个资源
		Thread t1 = new Thread(demo,"马");
		Thread t2 = new Thread(demo,"陈");
		Thread t3 = new Thread(demo,"祁");
		t1.start();
		t2.start();
		t3.start();
	}
	//线程同步的小总结
//1.解决什么问题:同步可以解决因为线程争抢产生安全问题,其跟着的同步原理在对象锁上
	//必须让所有线程使用同一个对象锁
	//2.同步的好处:数据的安全性。
	//3.同步的弊端:当线程过多时,每线程都是通过对象锁的开发和关闭来执行自己的业务。
	//所以会造成资源的过度消耗,降低程序的运行效率

//线程安全集合
public static void main(String[] args) {
StringBuffer str = new StringBuffer();
Vector v = new Vector();
Hashtable<String,String> table = new Hashtable<String,String>();

	//因为这些类是线程安全,但是为什么我们平时开发用的也不多
	//如何弄出一个线程安全的List集合呢?
	//需要使用一个工具类+包装类
	//Collections:是一个包装类,他包涵了各种关于集合的操作方法,这个类不能被实例化
	//也可以看做一个对集合提供各种方法支持的工具类
	//使用public static <T> List<T> synchronizedList(List<T> list)
	List<String> list = Collections.synchronizedList(new ArrayList<String>());
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

40.多线程(加锁 lock() +解锁unlock() +死锁)

一、处理线程不同的第二种方式
//如果需要更加明确的了解在哪里加锁,又在哪里解锁,更加细腻度的控制线程同步问题
//可以使用JDK1.5之后提供的一个接口Lock
//虽然 synchronized 方法和语句的范围机制使得使用监视器锁编程方便了很多,
//而且还帮助避免了很多涉及到锁的常见编程错误,但有时也需要以更为灵活的方式使用锁。
//Lock接口 --- 实现类 ReentrantLock 重入锁
//synchronized和ReentrantLock他们的原理基本一致
//但是synchronized时依赖于JVM是去实现的,ReentrantLock是JDK来实现的
/*lock() 加锁   
 *unlock() 解锁
 */
public class Demo1 implements Runnable {	
	public int ticket = 100;
	Lock lock = new ReentrantLock();
	public void run() {
		while(ticket>0){
			try {
				lock.lock(); //添加锁
				if(ticket>0){
					try {
						Thread.sleep(50);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+":正在卖第"+(ticket--)+"张票");
				}
			} finally {
				lock.unlock(); //释放锁
			}
		}
	}
	public static void main(String[] args) {
		Demo1 demo = new Demo1();
		Thread t1 = new Thread(demo,"李");
		Thread t2 = new Thread(demo,"沈");
		Thread t3 = new Thread(demo,"黄");
		t1.start();
		t2.start();
		t3.start();
	}	
}

二、死锁:彼此占用对方所需要的资源  

//多线程的死锁问题
//线程同步的弊端:1.降低程序运行效率  2.容易出现死锁
//如果出现了线程同步的嵌套场景就容易产生死锁
//死锁:两个或两个以上的线程在执行任务中,因为争取同一个资源产生的一种互相等待的现象


三、实现死锁场景
public class DiaLock2 implements Runnable {
	private static Father father = new Father();//父亲对象锁
	private static Son son = new Son();	//小孩对象锁
	private boolean flag = false;
	public void run(){	
		if(flag){
			synchronized(father){
				father.say();
				try{
					Thread.sleep(500);
				}catch(InterruptedException e){
					e.printStackTrace();
				}
				synchronized(son){
					father.get();
				}
			}
		}else{
			synchronized(son){
				son.say();
				try{
					Thread.sleep(500);
				}catch(InterruptedException e){
					e.printStackTrace();
				}
				synchronized(father){
					son.get();
				}
			}
		}
	}	
	public static void main(String[] args) {		
		DiaLock2 t1 = new DiaLock2();		
		DiaLock2 t2 = new DiaLock2();			
		t1.flag = true;
		t2.flag = false;		
		Thread thA = new Thread(t1);
		Thread thB = new Thread(t2);		
		thA.start();
		thB.start();
	}
}
public class Father {	
	public void say(){
		System.out.println("对孩子说:给成绩单,我就把玩具给你");
	}
	public void get(){
		System.out.println("爸爸得到成绩单");
	}
}
public class Son {
	public void say(){
		System.out.println("对爸爸说:给玩具,我就把成绩单给你");
	}	
	public void get(){
		System.out.println("孩子得到玩具");
	}
}

在这里插入图片描述
在这里插入图片描述

41.多线程(多线程的生产消费问题)

一、多线程的生产消费问题(线程的生产消费模式)

//特点:不同类型的线程使用同一个对象做资源
//设计:
/*1.Student(资源类)
*2.SetThread(生产线程)
*3.GetThread(消费线程)
*4.Demo1(测试类)
*/
//问题1,因为消费,生产使用不同对象,所以无法打印正确属性
//解决:创建一个统一的对象做为资源,同构构造方法的方式进行传递
//问题2:生产消费同事加入循环后:出现重复数据,姓名年龄不匹配
//解决:主要是因为线程的争抢性,和不确定性导致的,所以我们需要枷锁
//提问:线程出现不安全要的原因
//1.在多线程环境下
//2.共享了同一个资源
//3.有多少条语句操作共享的数据
//解决:
//1.加锁(所有线程同事加锁)
//2.必须是同一把锁
//通过同步,我们结局了姓名年龄不匹配的问题
//如果需要实现轮流输出,那么该如何实现?
//这里是使用Object类中所提供的等待wait()和唤醒方法notify()方法,notifyAll()方法
方法 描述
public final void wait() 线程等待
public final void wait(long timeout) 线程等待,并指定等待时间,以毫秒为单位
Public void notify() 唤醒第一个等待的线程
Public void notifyAll() 唤醒全部等待的线程
//生产线程-
//消费线程

public class Demo1 {
public static void main(String[] args) {
Student stu = new Student();
SetThread set = new SetThread(stu);
GetThread get = new GetThread(stu);
Thread t1 = new Thread(set,“生产线程”);
Thread t2 = new Thread(get,“消费线程”);
t1.start();
t2.start();
}
}
public class SetThread implements Runnable{
public Student stu;
public SetThread(Student stu){
this.stu = stu;
}
public int num = 0;
public void run() {
while(true){
synchronized (stu) {
if(stu.flag == true){
try {
stu.wait();//让线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(num % 2 == 0){
stu.name = “白”;
stu.age = 20;
}else{
stu.name = “彭”;
stu.age = 36;
}
num++;
//重新修改表示
stu.flag = true;
//唤醒线程
stu.notify();
}
}
}
}
public class GetThread implements Runnable{
public Student stu;
public GetThread(Student stu){
this.stu = stu;
}
public void run() {
while (true) {
synchronized (stu) {
if(stu.flag == false){
try {
stu.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(“我是”+stu.name+":今年"+stu.age);
//修改标记
stu.flag = false;
//唤醒线程
stu.notify();
}
}
}
}
public class Student {
String name;
int age;
boolean flag;//默认为false 木有数据–去生产,如果为true 有数据–等待
}
在这里插入图片描述
在这里插入图片描述

42.线程组+线程池+Callable接口

一、线程组:就把是多个线程分到一区
//对多个线程进行分类管理,Java默认对线程是有分组管理的
//请问,能不能自定线程组,然后给这个线程组分配线程呢?
public static void main(String[] args) {
method1();
method2();
}

//创建线程组,分配线程
private static void method2() {
	ThreadGroup group = new ThreadGroup("清河县");
	MyRunnable my = new MyRunnable();
	Thread t1 =new Thread(group,my,"武大郎");
	Thread t2 =new Thread(group,my,"武二郎");
	
	System.out.println(t1.getThreadGroup().getName());
	System.out.println(t2.getThreadGroup().getName());
	
	//设置线程组的优先级只能在1-10之间
	group.setMaxPriority(10);
	//通过组对象将这个组里所有的线程都设置守护线程
	group.setDaemon(true);
}
private static void method1() {
	MyRunnable my = new MyRunnable();
	Thread t1 =new Thread(my,"武大郎");
	Thread t2 =new Thread(my,"武二郎");
	//public final ThreadGroup getThreadGroup()获得当前线程所在的线程组
	ThreadGroup g1 = t1.getThreadGroup();
	ThreadGroup g2 = t2.getThreadGroup();
	//通过线程组,获得组名
	System.out.println(g1.getName());
	System.out.println(g2.getName());
	//默认情况下,所有线程同属一组(main组)
	System.out.println(Thread.currentThread().getThreadGroup().getName());
}

public class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}

二、线程池

//由于在程序中启动一个线程的代价比比较高的,因为他涉及到与操作系统交互
//如果想在开发场景中使用多线程,又不想以牺牲程序性能为代价,就可以考试使用线程池
//优点:
//1.线程池中的每一个线程的业务结束后,并不会死亡,而是再次回到线程池中,成为空闲状态等待执行下一次业务
//2.线程池在JDK1.5版本后引入,在1.5之前需要自己来通过代码来实现线程池,1.5之后JAVA内置了线程池
public class Demo1 {
public static void main(String[] args) {
//如何创建一个线程池,使用Executors来调用创建线程池的静态方法
//创建一个可重用固定线程数的线程池,
//public static ExecutorService newFixedThreadPool(int nThreads)
//新创建的线程池
//public static ExecutorService newCachedThreadPool()
//其他方法可参考JDK
//用过调用线程池的Future<?> submit(Runnable task)去执行线程任务
ExecutorService pool = Executors.newFixedThreadPool(3);

	//执行Runnable对象代表的线程
	pool.submit(new MyRunnable());
	pool.submit(new MyRunnable());
	pool.submit(new MyRunnable());	
	
	//默认情况下线城池中的线程是不会死亡,可以回收
	//但是可不可以终止线程池,调用线程池的shutdown方法
	pool.shutdown();
}

}

三、线程的第三种实现方式(Callable接口)
public class Demo1 {
public static void main(String[] args) {
//如何创建一个线程池,使用Executors来调用创建线程池的静态方法
//创建一个可重用固定线程数的线程池,
//public static ExecutorService newFixedThreadPool(int nThreads)
//新创建的线程池
//public static ExecutorService newCachedThreadPool()
//其他方法可参考JDK
//用过调用线程池的 Future submit(Callable task)去执行线程任务
ExecutorService pool = Executors.newFixedThreadPool(3);

	//执行Runnable对象代表的线程
	pool.submit(new MyCallable());
	pool.submit(new MyCallable());
	pool.submit(new MyCallable());
	
	//默认情况下线城池中的线程是不会死亡,可以回收
	//但是可不可以终止线程池,调用线程池的shutdown方法
	pool.shutdown();
}

}
//Callable是一个带泛型的接口,线程池的submit方法可以将它的实例作为线程任务执行
//Callable实现多线程必须借助线程池来实现
//这里Callable指定的泛型与call的类型相同,如果执行泛型。则默认为Object
public class MyCallable implements Callable {
//?
public Object call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
return null;
}
}
——————————————————————————————————
ExecutorService pool = Executors.newFixedThreadPool(3);
//执行Runnable对象代表的线程
Future sum1 = pool.submit(new MyCallable(100));
Future sum2 = pool.submit(new MyCallable(200));
Future sum3 = pool.submit(new MyCallable(300));
try {
System.out.println(“1到100的合是”+sum1.get());
System.out.println(“1到200的合是”+sum2.get());
System.out.println(“1到300的合是”+sum3.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
//默认情况下线城池中的线程是不会死亡,可以回收
//但是可不可以终止线程池,调用线程池的shutdown方法
pool.shutdown();

public class MyCallable implements Callable {
public int number;
public MyCallable(int number){
this.number = number;
}
//启动三个线程分别都调用call方法计算
//线程1计算1-100的结果
//线程2计算1-200的结果
//线程3计算1-300的结果
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= number ; i++) {
sum+=i;
}
return sum;
}
}
在这里插入图片描述

43.Java网络编程

出现 java.net.MalformedURLException: no protocol 异常的原因及解决方法
https://blog.csdn.net/qq_35246620/article/details/70162106
##异常原因
通过观察上图标记出来的异常描述,咱们可以知道:
java.net.MalformedURLException: no protocol
此异常,为:no protocol,没有指定通信协议异常。
##解决方法
既然咱们已经知道了是因为没有指定通信协议,从而导致异常的发生。
那么,咱们再回过头来,看看上面的 URL 是不是少了什么东西啊?少了吗?
好吧,答案是:没有指定 http 协议,在 URL 前面加上http://即可解决此异常。

//在JAVA要是实现网络编程需要遵循三要素
//1.需要发送端和服务端的IP地址   发送端 --- 指定服务端的IP
//2.需要这两段IP地址的端口
//3.指定传输协议TCP/UDP 

怎么加密的=》加报头,里面的值就是值,外面的封装就是键,键值对。。。

网络数据传输的封装
https://blog.csdn.net/sinat_26554783/article/details/51027079具体看java
http://www.elecfans.com/d/630956.html
http和socket
一、网络编程基础知识

在这里插入图片描述

在这里插入图片描述
1.IP地址与端口号
在这里插入图片描述
2. TCP与UDP
TCP协议:传输控制协议,提供可靠无差错的数据传输
UDP协议:用户数据报协议,不可靠的数据传输
3.Socket简介
Socket(套接字)是网络驱动层提供给应用程序的接口和机制
Socket是TCP网络编程的核心,发送接收消息都需要使用Socket
在这里插入图片描述

44.Java网络编程(TCP网络编程)

一、 TCP网络编程概述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ServerSocket类的主要方法 
方法 类型 描述
public ServerSocket(int port) 构造方法 创建ServerSocket实例
public Socket accept() 方法 等待客户端连接
public InetAddress getInetAddress() 方法 返回服务器的IP地址
public boolean isClosed() 方法 返回ServerSocket的关闭状态
public void close() 方法 关闭ServerSocket
 Socket类常用方法
方法 类型 描述
public Socket(String host,int port) 构造方法 构造Socket对象,同时指定要连接服务器的主机名和端口号
public InputStream getInputStream() 方法 返回套接字的输入流
public OutputStream getOutputStream() 方法 返回套接字的输出流
public boolean isClosed() 方法 判断套接字是否关闭
public void close() 方法 关闭此Socket
TCP协议网络程序中,需提供服务器端和客户端
服务器端调用ServerSocket类的accept()方法监听客户端的请求


二、使用TCP协议实现网络通讯
1.//客户端=》服务端通讯
//使用TCP协议实现网络通讯(客户端)
public class ClientDemo {
	public static void main(String[] args) throws UnknownHostException, IOException {
		//在客户端创建Socket对象
		Socket socket = new Socket("192.168.0.165",9527);
		//从控制台接收录入的数据
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		//缓冲输出流来写数据
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
		//读写数据
		String line = null;
		while((line = br.readLine())!=null){
			bw.write(line);//写数据
			bw.newLine();//换行
			bw.flush();//清理流数据流管道
		}
		//释放资源
		br.close();
		bw.close();
		socket.close();
	}
}
//使用TCP协议实现网络通讯(服务端)
public class ServerDemo {
	public static void main(String[] args) throws IOException {
		//创建服务器端的Socket(套接字)
		ServerSocket ss = new ServerSocket(9527);
		//监听客户端的连接信息
		//一旦接收到客服端发送的信息,那么会产生一个Socket对象与其对接
		Socket socket = ss.accept();//阻塞状态
		//使用缓冲字符流读取客户端发送的信息
		BufferedReader bf = new BufferedReader
				(new InputStreamReader(socket.getInputStream()));
		System.out.println("服务器已经启动...");
		String line = null;
		String ip =  socket.getInetAddress().getHostAddress();
		while((line = bf.readLine())!=null){
			System.out.println(ip+"发送慰问信息:"+line);
		}
		bf.close();
		socket.close();
	}
}

2.//服务端和客户端互相通讯
public class ClientDemo {
	public static void main(String[] args) throws UnknownHostException, IOException {
		//在客户端创建Socket对象
		Socket socket = new Socket("192.168.0.100",9527);
		//获取输出流
		System.out.println("客户端输出信息");
		OutputStream os = socket.getOutputStream();
		os.write("今天天气不错,挺风和日丽的".getBytes());

		//获取输入流
		System.out.println("客户端接收信息");
		InputStream is = socket.getInputStream();
		byte[] bytes = new byte[1024];
		int len = is.read(bytes);//阻塞
		String client = new String(bytes,0,len);
		System.out.println("client:"+client);
		
		socket.close();
	}
}
public class ServerDemo {
	public static void main(String[] args) throws IOException {
		//创建服务器端的Socket(套接字)
		ServerSocket ss = new ServerSocket(9527);
		System.out.println("服务器开启");
		//监听客户端的连接信息
	//一旦接收到客服端发送的信息,那么会产生一个Socket对象与其对接
		Socket socket = ss.accept();//阻塞状态
		
		//获得输入流
		System.out.println("服务器接收信息");
		InputStream is = socket.getInputStream();
		byte[] bytes = new byte[1024];
		int len = is.read(bytes);//阻塞
		String server = new String(bytes,0,len);
		System.out.println("Server:"+server);	

		//获取输出流
		System.out.println("服务器输出信息");
		OutputStream os = socket.getOutputStream();
		os.write("数据已经收到".getBytes());
		socket.close();
	}
}

3.//将客户端输入的数据封装成一个文本
public class ClientDemo {
	public static void main(String[] args) throws UnknownHostException, IOException {
		//在客户端创建Socket对象
		Socket socket = new Socket("192.168.0.100",9527);
		//从控制台接收录入的数据
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		//缓冲输出流来写数据
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
		//读写数据
		String line = null;
		while((line = br.readLine())!=null){
			bw.write(line);//写数据
			bw.newLine();//换行
			bw.flush();//清理数据流管道
		}
		//释放资源
		br.close();
		bw.close();
		socket.close();
	}
}
public class ServerDemo {
	public static void main(String[] args) throws IOException {
		//创建服务器端的Socket(套接字)
		ServerSocket ss = new ServerSocket(9527);
		//监听客户端的连接信息
	//一旦接收到客服端发送的信息,那么会产生一个Socket对象与其对接
		System.out.println("服务器已经启动...");
		Socket socket = ss.accept();//阻塞状态
		//使用缓冲字符流读取客户端发送的信息
		BufferedReader br = new BufferedReader
				(new InputStreamReader(socket.getInputStream()));
		//封装客户端发送的数据成一个文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("九阴真经.txt"));
		String line = null;
		String ip =  socket.getInetAddress().getHostAddress();
		while((line = br.readLine())!=null){
			bw.write(line);
			bw.newLine();
			bw.flush();
		}
		bw.close();
		//br.close();
		socket.close();
	}
}

标题45.Java网络编程(UDP网络编程)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一、UDP编程
UDP不能保证数据的可靠传输
UDP使用DatagramSocket发送和接接收信息
UDP协议中数据传输形式是DatagramPacket类型
1、DatagramSocket类主要用于实现信息的发送和接收 。主要方法如下
方法 类型 描述
public DatagramSocket() 构造方法 构造DatagramSocket对象,不指定监听的端口
public DatagramSocket(int port) 构造方法 构造DatagramSocket对象,同时指定监听的端口
public void send (DatagramPacket p) 方法 发送数据报
public void receive(DatagramPacket p) 方法 接收数据报
2、DatagramPacket类用于包装需要发送或接收的信息 。主要方法如下
方 法 类 型 描 述
public DatagramPacket(byte[] buf,int length) 构造方法 构造DatagramPacket对象时,指定内存空间和大小
public DatagramPacket(byte[] buf,int length,InetAddress address,int port) 构造方法 构造DatagramPacket对象时,指定内存空间和大小以及目标地址和端口
public byte[] getData() 方法 返回接收数据
public int getLength() 方法 返回要发送或接收数据的长度
public InetAddress getAddress() 方法 返回机器的地址
3、 InetAddress类用于表示计算机地址。 主要方法
方法 类型 描述
public static InetAddress getByName(String host) 方法 通过主机名或IP地址得到一个InetAddress对象
public String getHostName() 方法 获取IP地址对应的主机名
public String getHostAddress() 方法 返回IP地址字符串
4、UDP程序设计
UDP网络程序由消息发送方与消息接收方两部分组成

二、UDP协议网络通讯具体步骤
1.//UDP协议接受数据步奏:
/*
 *1.创建接收端的套接字对象
 *2.创建一个数据包来接收数据(接收容器)
 *3.调用套接字的接收方法方法接收数据包
 *4.解析数据包,显示数据包中的数据
 *5.释放资源close
*/
public class ReceiveDemo {
	public static void main(String[] args) throws IOException {
		//创建接收端的套接字对象,并且指定端口
		DatagramSocket ds = new DatagramSocket(9527);
		//创建一个数据包来接收数据
		byte[] bytes = new byte[1024];
		DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
		//使用DatagramSocket对象接受数据
		//public void receive(DatagramPacket p)
		System.out.println("下面请各位听众发送短信参加我们的节目...");
		ds.receive(dp);//阻塞状态
		//直到有发送端发送数据,阻塞状态结束

		//解析发送过来的数据包
		//1.获取数据包中的数据。
		byte[] newBytes = dp.getData();//获得数据缓冲区中的数据
		//2.获取发送端的IP
		InetAddress address = dp.getAddress();
		//public String getHostAddress()
		String sendIp = address.getHostAddress();
		//将byte[] ---- 转换为String数据
		String messge = new String(newBytes,0,newBytes.length);
		System.out.println("IP为"+sendIp+"的听众发来信息:"+messge);
		//释放资源
		//ds.close();
	}
}

//UDP协议发送数据步奏:
/*1.创建发送端的套接字对象
 *2.创建数据,把数据打包
 *3.调用套接字的send方法发送数据包
 *4.释放资源close
 */
public class SendDemo {
	public static void main(String[] args) throws IOException {
		//创建发送端的套接字对象
		//套接字--类似一个快递员
		//DatagramSocket();
		DatagramSocket ds = new DatagramSocket();
		
		//创建数据,并且数据打包
//因为使用的是UDP协议,所以数据数传传送需要数据报文包DatagramPacket
		//DatagramPacket(byte[] buf, int length, InetAddress address, int port) 
		//构建数据
		byte[] bytes = "今晚老地方见,你懂的!".getBytes();
		//数据的长度
		int length = bytes.length;
		//IP地址
		//public static InetAddress getByName(String host) 确定主机的 IP 地址
		InetAddress address = InetAddress.getByName("192.168.0.162");
		//端口号
		int post = 9527;
		//数据打包
		DatagramPacket packet = new DatagramPacket(bytes,length,address,post);
		//调用套接字DatagramSocket的发送方法发送数据包
		ds.send(packet);
		System.out.println("消息发送完毕!");
		//释放资源
		ds.close();
	}
}

2.//使用控制台输入来进行网络传输
public class SendDemo {
	public static void main(String[] args) throws IOException {
		//创建发送端的套接字对象
		DatagramSocket ds = new DatagramSocket();
		//使用缓冲字符流来向控制台写数据
		BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
		//循环控制台输入的读取-创建数据的过程
		String line = null;
		while((line = bf.readLine()) !=null ){
			if("拜拜".equals(line)){
				break;
			}
			byte[] bytes = line.getBytes(); //将读到的字符串转成byte[]数组
			//数据打包
			DatagramPacket packet = new DatagramPacket
					(bytes,bytes.length,InetAddress.getByName("192.168.0.165"),9527);
			//发送数据包
			ds.send(packet);
			System.out.println("消息发送完毕!");
		}
		//释放资源close
		ds.close();
	}
}
public class ReceiveDemo {
	public static void main(String[] args) throws IOException {
		//创建套接字对象
		DatagramSocket ds = new DatagramSocket(9527);
		System.out.println("下面请各位听众发送短信参加我们的节目...");
		while(true){
			//创建数据包
			byte[] bytes = new byte[1024];
			DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
			//接受数据
			ds.receive(dp);//在循环中阻塞状态
			//继续数据
			String sendIp = dp.getAddress().getHostAddress();
			String messge = new String(dp.getData(),0,dp.getLength());
			System.out.println("IP为"+sendIp+"的听众发来信息:"+messge);
		}
		//ds.close();
	}
}

3.聊天室
//使用多线程来改进之前聊天室。这样能够实现每一个会话都有一个线程来单独处理
public class ChatRoom {
	public static void main(String[] args) throws SocketException {
		DatagramSocket dsSend = new DatagramSocket();
	       DatagramSocket dsReceive = new DatagramSocket(9527);
		SendThread send = new SendThread(dsSend);
		ReceiveThread receive = new ReceiveThread(dsReceive);	
		//因为目前线程是使用实现runnable接口实现的
		//所以需要借助Thread类来启动线程
		Thread t1 = new Thread(send);
		Thread t2 = new Thread(receive);		
		//启动聊天室
		t1.start();
		t2.start();
	}
}

//服务器线程
public class ReceiveThread implements Runnable  {
	public DatagramSocket ds;
	
	public ReceiveThread(DatagramSocket ds){
		this.ds = ds;
	}
	public void run() {
		System.out.println("下面打进电话的听众,敢报真实姓名吗?");
		try {
			while(true){
				//创建包裹
				byte[] bytes= new byte[1024];
				DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
			    //接收数据
				ds.receive(dp);
				//解析数据
				String sendId = dp.getAddress().getHostAddress();
				String msg = new String(dp.getData(),0,dp.getLength());
				System.out.println("来自"+sendId+"的听众发来消息:"+msg);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
//发送端线程
public class SendThread implements Runnable{
	public DatagramSocket dsSend;	
	public SendThread(DatagramSocket dsSend){
		this.dsSend = dsSend;
	}
	public void run() {
		try {
			//使用缓冲字符流来获取控制台数据
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			String line = null;
			while((line = br.readLine())!=null){
				//创建数据打包
				byte[] bytes = line.getBytes();
				DatagramPacket dp = new DatagramPacket(bytes,bytes.length,
						InetAddress.getByName("192.168.0.165"),9527);
				dsSend.send(dp);
				System.out.println("消息发送完毕");
			}
			//关闭资源
			dsSend.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

三、TCP与UDP协议比较
TCP UDP
传输控制协议 用户数据报协议
提供可靠无差错的数据传输 不能保证数据的可靠传输
资源消耗较大,性能和效率较低 资源消耗小,性能和效率较高
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值