第13章 泛型
13.1 泛型的概述
泛型:参数化类型
类型形参:< T >,< E >,< K>,< V>,< U>,< R>。。。。
类型实参:必须是引用数据类型,不能是基本数据类型
< String>,< Integer>,< Student>,<ArrayList< String>>。。。
13.2 形式一:泛型类与泛型接口
1、声明语法格式:
【修饰符】 class 类名/接口<类型形参列表>{
}
【修饰符】 class 类名/接口<类型形参1 extends 父类上限>{
}
【修饰符】 class 类名/接口<类型形参1 extends 父类上限 & 父接口上限>{
}
在类名或接口名后面声明的泛型形参类型,可以在当前类或接口中使用,用作声明成员变量、方法的形参、方法的返回值。
但是不能用于静态成员上
2、使用语法格式
在(1)创建泛型类、泛型接口的对象时,为泛型形参指定具体类型
(2)在继承泛型类或实现泛型接口时,为泛型形参指定具体类型
示例代码
ArrayList<String> list = new ArrayList<String>();
ArrayList<String> list = new ArrayList<>();//JDK1.7之后可以省略
class MyStringArrayList extends ArrayList<String>{
}
class Employee implements Comparable<Employee>{
public int compareTo(Employee e){
}
}
Arrays.sort(数组, new Comparator<泛型实参>(){
public int compare(泛型实参类型 o1, 泛型实参类型 o2){
}
});
3、泛型如果没有指定,会被擦除,按照最左边的上限处理,如果没有指定上限,按照Object处理
13.3 形式二:泛型方法
1、声明的语法格式
【修饰符】 <泛型形参列表> 返回值类型 方法名(【数据形参列表】)【throws 异常列表】{}
【修饰符】 <泛型形参 extends 父类上限 & 父接口上限> 返回值类型 方法名(【数据形参列表】)【throws 异常列表】{}
(1)在方法返回值类型前面声明的泛型形参类型,只能在当前方法中使用,用于表示形参的类型或返回值类型,或方法局部变量的类型,和别的方法无关。
(2)泛型方法可以是静态方法,也可以是非静态方法
2、 使用
当调用方法,会根据具体的数据的实参的类型,来确定泛型实参的类型。
13.4 通配符?
(1)?:代表任意引用数据类型
(2)? extends 上限:代表上限本身或它的子类
(3)? super 下限:代表下限本身或它的父类
例如:
ArrayList<?>:表示可以接受任意类型
ArrayList<?> list = new ArrayList<String>();
ArrayList<?> list = new ArrayList<Integer>();
ArrayList<?> list = new ArrayList<Animal>();
ArrayList<? extends 上限>:
ArrayList<? extends Person> list = new ArrayList<Person>();
ArrayList<? extends Person> list = new ArrayList<Animal>();//Animal不行,因为Animal是父类
ArrayList<? extends Person> list = new ArrayList<Student>();
ArrayList<? extends Person> list = new ArrayList<Dog>();//Dog也不行
ArrayList<? super 下限>:
ArrayList<? super Person> list = new ArrayList<Person>();
ArrayList<? super Person> list = new ArrayList<Animal>();
ArrayList<? super Person> list = new ArrayList<Student>();//Student,因为Student是子类
ArrayList<? super Person> list = new ArrayList<Dog>();//Dog也不行
ArrayList<?>:不能添加元素,除了null
ArrayList<? extends 上限>:不能添加元素,除了null
ArrayList<? super 下限>:可以添加下限或下限子类的对象
13.5 Collections工具类
java.util.Collections:工具类,操作集合
(1)public static boolean addAll(Collection<? super T> c, T… elements)
添加elements的几个对象到c集合中。T是elements对象的类型,要求Collection集合的元素类型必须是T或T的父类
(2)public static int binarySearch(List<? extends Comparable<? super T>> list,T key)
在list集合中用二分查找key的下标,如果存在返回的是合理的下标,如果不存在返回的是一个负数下标
T是元素的类型,
<? extends Comparable<? super T>>,要求集合的元素必须实现Comparable接口 <? super T>,在实现Comparable接口,可以指定Comparable<类型实参>为T或T的父类。 (3)public static boolean disjoint(Collection<?> c1, Collection<?> c2)判断c1和c2没有交集就为true
(4)public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
求coll集合中最大元素
<T extends Object & Comparable<? super T>>:要求T或T的父类实现Comparable接口
因为找最大值需要比较大小
(5)public static <T extends Comparable<? super T>> void sort(List list) 给list集合排序
<T extends Comparable<? super T>>:要求T或T的父类实现Comparable接口
(6)public static Collection synchronizedCollection(Collection c)
以synchronizedXX开头的方法,表示把某种非线程安全集合转为一个线程安全的集合。
(7)public static List unmodifiableList(List<? extends T> list)
以unmodifiableXx开头的方法,表示返回一个“只读”的集合。
第14章 IO流
14.1 java.io.File类
它是文件和目录路径名的抽象描述。
API:
(1)getName():获取文件或目录的名称
(2)getPath():获取文件或目录的路径
(3)getAbsolutePath():获取文件或目录的绝对路径
(4) getCanonicalPath():获取文件或目录的规范路径
(5)long length():获取文件的长度,单位字节
(6)long lastModified():最后修改时间,单位毫秒
(7)String getParent():获取上级或父目录
File getParentFile():获取上级或父目录
(8)isFile():判断是否是文件,当且仅当是文件并且是存在的,才会true
(9)isDirectory():判断是否是目录,当且仅当是目录并且存在的,才会true
(10)exists():是否存在
(11)isHidden():是否隐藏
(12)canWrite():是否可写
(13)canRead():是否可读
(14)String[] list():获取下一级
File[] listFiles():获取下一级
File[] listFiles(FileFilter f):获取下一级,但是会过滤掉一下文件和目录
(15)createNewFile():创建文件
createTempFile(String prefix, String suffix)
(16)mkdir():创建一级目录,如果父目录不存在,就失败,但是不报异常
mkdirs():创建多级目录
(17)delete():删除文件或空目录
(18)renameTo(File f):重命名文件或目录
14.2 IO流的四大抽象基类
1、四大超类
(1)InputStream:字节输入流
(2)OutputStream:字节输出流
(3)Reader:字符输入流
(4)Writer:字符输出流
2、分类
(1)按照方向分:输入流和输出流
(2)按照处理的方式不同:字节流和字符流
字符流只能处理纯文本的数据(使用范围很窄)
(3)按照角色不同:节点流和处理流
处理流是建立在节点流的基础上的,处理流需要包装一个节点流的对象。
处理流也可以包装另外一个处理流。
其实JDK中IO体系是用到了一个装饰者设计模式
3、API
(1)InputStream:
int read():一次读取一个字节,返回的是本次读取的字节的值,如果流末尾返回-1
int read(byte[] data):一次读取多个字节,读取的数据存储到data字节数组中,最多读取data.length个字节,返回的是实际本次读取的字节的个数,如果流末尾返回-1。从data[0]开始存储。
int read(byte[] data,int offset, int len):一次读取多个字节,读取的数据存储到data字节数组中,最多读取len个字节,返回的是实际本次读取的字节的个数,如果流末尾返回-1。从data[offset]开始存储。
void close():关闭
(2)OutputStream:
void write(int data):一次写一个字节
void write(byte[] data):一次写整个字节数组
void write(byte[] data, int offset, int len):一次字节数组的一部分,从[offset]开始,一共len个
void close():关闭
void flush():刷新
(3)Reader:
int read():一次读取一个字符,返回的是本次读取的字符的Unicode值,如果流末尾返回-1
int read(char[] data):一次读取多个字符,读取的数据存储到data字符数组中,最多读取data.length个字符,返回的是实际本次读取的字符的个数,如果流末尾返回-1。从data[0]开始存储。
int read(char[] data,int offset, int len):一次读取多个字符,读取的数据存储到data字符数组中,最多读取len个字符,返回的是实际本次读取的字符的个数,如果流末尾返回-1。从data[offset]开始存储。
void close():关闭
(4)Writer
void write(int data):一次写一个字符
void write(char[] data):一次写整个字符数组
void write(char[] data, int offset, int len):一次字符数组的一部分,从[offset]开始,一共len个
void write(String str):一次写整个字符串
void write(String str, int offset, int count):一次写字符串的一部分,从[offset]开始,一共count个
void close():关闭
void flush():刷新
14.3 文件IO流
1、类型
FileInputStream:文件字节输入流,可以读取任意类型的文件
FileOutputStream:文件字节输出流,可以把字节数据输出到任意类型的文件
FileReader:文件字符输入流,只能读取纯文本的文件。按照平台默认的字符编码进行解码。
FileWriter:文件字符输出流,只能把字符数据输出到纯文本文件。按照平台默认的字符编码进行编码。
2、读写文件的代码
示例代码:
public void copy(File src, File dest)throws IOException{
//选择IO流
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
//读写
byte[] data = new byte[1024];
while(true){
int len = fis.read(data);
if(len==-1){
break;
}
fos.write(data,0,len);
}
//关闭
fis.close();
fos.close();
}
14.4 缓冲IO流
1、分为
BufferedInputStream:字节输入缓冲流,给InputStream系列IO流增加缓冲效果
BufferedOutputStream:字节输出缓冲流,给OutputStream系列IO流增加缓冲效果
BufferedReader:字符输入缓冲流,给Reader系列IO流增加缓冲效果
String readLine():按行读取
BufferedWriter:字符输出缓冲流,给Writer系列IO流增加缓冲效果
void newLine():输出换行符
2、默认的缓冲区的大小是8192 = 1024 * 8(字节/字符)
3、可以给读写的过程提高效率
不仅仅是可以给文件IO流增加缓冲效果,可以给任意符合对应类型的IO流增加缓冲效果。
示例代码:
public void copyBuffer(File src, File dest)throws IOException{
//选择IO流
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
//BufferedInputStream只能给FileInputStream增加缓冲效果,读的过程加快了
BufferedInputStream bis = new BufferedInputStream(fis);//fis在里面,bis在外面,fis比喻成内衣,bis比喻成外套
//BufferedOutputStream只能FileOutputStream增加缓冲效果,写的过程加快了
BufferedOutputStream bos = new BufferedOutputStream(fos);
//数据流向:src-->fis-->bis(从fis先缓冲到bis)-->data-->bos(从data缓冲到bos中)-->fos-->dest
//读写
byte[] data = new byte[1024];
while(true){
int len = bis.read(data);
if(len==-1){
break;
}
bos.write(data,0,len);
}
//关闭
//关闭比喻成脱衣服
bos.close();
fos.close();
bis.close();//先脱外套,再脱内衣
fis.close();
}
14.5 编码与解码的IO流(转换流)
1、编码:OutputStreamWriter
可以把字符流转为字节流输出,并且在转换的过程中,可以指定字符编码。
2、解码:InputStreamReader
可以把字节输入流转为字符输入流,并且在转换的过程中,可以指定字符编码。
3、示例代码:解码(文件是GBK,当前平台是UTF-8)
@Test
public void test4()throws IOException{
//因为这里想要用在程序中按照“指定”的编码方式进行解码,而不是按照平台“默认的”编码方式进行解码
//所以,我这里仍然用FileInputStream字节流,把文件编码后的数据,原样读取
//从文件到FileInputStream fis内存的过程中,先不解码
//因为如果选择FileReader,从文件到FileReader的过程中,就已经按照平台“默认的”编码方式解码好了,我们无法控制
FileInputStream fis = new FileInputStream("d:/File类概述.txt");
//我要使用InputStreamReader,把FileInputStream转为字符流
// InputStreamReader isr = new InputStreamReader(fis);//如果没有指定,还是按照默认的编码方式
InputStreamReader isr = new InputStreamReader(fis,"GBK");//如果指定,就按照指定的编码方式解码
//文件-->fis(字节流)-->解码-->isr(字符流)-->br ->读取的是字符
//字符流,要么按照char[]读取,要么可以用BufferedReader包装按行读取
BufferedReader br = new BufferedReader(isr);
String line;
while((line = br.readLine()) !=null){
System.out.println(line);
}
br.close();
isr.close();
fis.close();
}
4、示例代码:编码(文件是GBK,当前平台是UTF-8)
@Test
public void test3()throws IOException{
String hua = "File类可以使用文件路径字符串来创建File实例";
//因为想要用在程序中进行编码,所以这里选用FileOutputStream
FileOutputStream fos = new FileOutputStream("d:/File类概述.txt",true);
//这里xx,想要直接操作字符串,那么必须是字符流,而fos是字节流,无法直接操作字符串
// xx.write("\r\n");
// xx.write(hua);
//数据流向:内存 --> osw (字符流)-->在写入fos过程中进行编码-->fos(字节流)-->文件
OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
osw.write("\r\n");
osw.write(hua);
osw.close();
fos.close();
}
14.6 数据IO流
1、类型
DataInputStream:允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。
DataOutputStream:允许应用程序以适当方式将基本 Java 数据类型写入输出流中。
它俩必须配对使用
读的顺序要与写的顺序一致
2、API
DataOutputStream | DataInputStream |
---|---|
writeInt(xx):输出int类型整数 | int readInt() |
writeDouble(xx):输出double类型 | double readDouble() |
writeBoolean(xx) | boolean readBoolean() |
writeLong(xx) | long readLong() |
writeChar(xx) | char readChar() |
writeByte(xx) | byte readByte() |
writeShort(xx) | short readShort() |
writeFloat(xx) | float readFloat() |
writeUTF(String str):输出字符串的 | String readUTF() |
14.7 对象IO流
1、类型
ObjectOutputStream:对象序列化,输出对象,把对象转为字节序列输出
ObjectInputStream:对象反序列化,读取对象,把字节序列重构成Java对象
2、API
ObjectOutputStream:writeObject(对象)
ObjectInputStream:Object readObject()
3、序列化需要注意哪些内容?
(1)所有要序列化的对象的类型都必须实现java.io.Serializable接口
如果对象的属性类型也是引用数据类型,那么也要实现java.io.Serializable接口
(2)希望类的修改对象反序列化不产生影响,那么我们最后能够增加一个序列化版本ID
private static final long serialVersionUID = 1L;
(3)如果有些属性不想要序列化,可以加transient
(4)如果某个属性前面有static修饰,也不参与序列化
4、除了Serializable接口之外,还可以实现java.io.Externalizable接口,但是要求重写:
void readExternal(ObjectInput in)
void writeExternal(ObjectOutput out)
关于哪些属性序列化和反序列化,由程序员自己定。
14.8 其他的IO流相关内容
1、如果要实现按行读取,可选择什么类型?
BufferedReader:String readLine()
Scanner:String nextLine()
2、如果要按行输出,可以选择什么类型?
(1)自己处理\r\n
(2)BufferedWriter:newLine()
(3)PrintStream和PrintWriter:println()
14.9 JDK1.7之后引入新try…catch
语法格式:
try(需要关闭的资源对象的声明){
业务逻辑代码
}catch(异常类型 e){
处理异常代码
}catch(异常类型 e){
处理异常代码
}
....
它没有finally,也不需要程序员去关闭资源对象,无论是否发生异常,都会关闭资源对象
示例代码:
@Test
public void test03() {
//从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt(UTF-8)文件中
try(
FileInputStream fis = new FileInputStream("d:/1.txt");
InputStreamReader isr = new InputStreamReader(fis,"GBK");
BufferedReader br = new BufferedReader(isr);
FileOutputStream fos = new FileOutputStream("1.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
){
String str;
while((str = br.readLine()) != null){
bw.write(str);
bw.newLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
第十五章 网络编程
为了更好的理解:web(服务器端和客户端的通信)、数据库(服务器端和客户端的数据传输)等原理。
15.1 主机IP
在程序中表示:
(1)值的表示
IPV4:32位整数,8位一组,用.分割,例如:192.168.11.45
每个8位的范围[0,255]
IPV6:128位整数表示,16位一组,用:分割,例如:X:X:X:X:X:X:X:X
每个16位用十六进制值表示
(2)对象表示:InetAddress
此类表示互联网协议 (IP) 地址,它有两个子类Inet4Address和Inet6Address,分别对应IPV4和IPV6。InetAddress类没有提供公共的构造器,而是提供了如下几个静态方法来获取InetAddress实例。
- public static InetAddress getLocalHost()
- public static InetAddress getByAddress(byte[] addr)
- public static InetAddress getByName(String host)
InetAddress提供了如下几个常用的方法:
- public String getHostAddress():返回 IP 地址字符串(以文本表现形式)。
- public String getHostName():获取此 IP 地址的主机名
- public String getCanonicalHostName():获取此 IP 地址的完全限定域名
- public boolean isReachable(int timeout):测试是否可以达到该地址。
15.2 端口号
范围:[0,65535]
常见的端口号:
tomcat/JBoss:8080
http:80
mysql:3306
oracle:1521
sql server:1433
15.3 网络协议
- 应用层:网络服务与最终用户的一个接口。协议有:HTTP、FTP、SMTP、DNS、TELNET、HTTPS、POP3等等。
- 表示层:数据的表示、安全、压缩。格式有:JPEG、ASCll、DECOIC、加密格式等。
- 会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话
- 传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP、UDP。
- 网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP、IGMP、IP(IPV4 IPV6)、ARP、RARP。
- 数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。
- 物理层:建立、维护、断开物理连接。
而IP协议是一种非常重要的协议。IP(internet protocal)又称为互联网协议。IP的责任就是把数据从源传送到目的地。它在源地址和目的地址之间传送一种称之为数据包的东西,它还提供对数据大小的重新组装功能,以适应不同网络对包大小的要求。经常与IP协议放在一起的还有TCP(Transmission Control Protocol)协议,即传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。而通常我们说的TCP/IP协议,其实是指TCP/IP协议族,因为该协议家族的两个最核心协议:TCP(传输控制协议)和IP(网际协议),为该家族中最早通过的标准,所以简称为TCP/IP协议。
TCP:Transmission Control Protocol 传输控制协议
(1)面向连接的:连接之前有三次握手,断开时四次挥手
以下为拟人比喻
(2)可靠的
(3)基于字节流
(4)可以传输大量数据的
UDP:User Datagram Protocol 用户数据报协议
(1)非面向连接的
(2)不可靠的
(3)基于数据报的
(4)数据量大小有限制的64K
15.4 Socket编程
Socket:套接字,代表网络通信的一端,负责和网卡驱动程序沟通的对象。
分为:
(1)流套接字:ServerSocket和Socket
(2)数据报套接字:DatagramSocket
ServerSocket的常用构造方法和其他方法:
- ServerSocket(int port) :指定在某个端口号监听客户端的连接和通信
- Socket accept() :监听和接收客户端的连接
- void close() :关闭
Socket类的常用构造方法:
- public Socket(InetAddress address,int port):创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
- public Socket(String host,int port):创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket类的常用方法:
-
public InputStream getInputStream():返回此套接字的输入流,可以用于接收消息
-
public OutputStream getOutputStream():返回此套接字的输出流,可以用于发送消息
-
public InetAddress getInetAddress():此套接字连接到的远程 IP 地址;如果套接字是未连接的,则返回 null。
-
public void close():关闭此套接字。套接字被关闭后,便不可在以后的网络连接中使用(即无法重新连接或重新绑定)。需要创建新的套接字对象。 关闭此套接字也将会关闭该套接字的 InputStream 和 OutputStream。
-
public void shutdownInput():如果在套接字上调用 shutdownInput() 后从套接字输入流读取内容,则流将返回 EOF(文件结束符)。 即不能在从此套接字的输入流中接收任何数据。
-
public void shutdownOutput():禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流,则该流将抛出 IOException。 即不能通过此套接字的输出流发送任何数据。
注意:先后调用Socket的shutdownInput()和shutdownOutput()方法,仅仅关闭了输入流和输出流,并不等于调用Socket的close()方法。在通信结束后,仍然要调用Scoket的close()方法,因为只有该方法才会释放Socket占用的资源,比如占用的本地端口号等。
DatagramSocket:
- DatagramPacket(byte[] buf, int length)
- DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
15.5 代码示例
15.5.1 TCP协议编程示例一
一个客户端连接服务器,连接成功后,服务器给客户端发送“欢迎你登录"
package com.atguigu.test10;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
* TCP:面向连接,可靠的,基于字节流的
* 服务器:等待被连接的过程
*
* ServerSocket:只负责接受和建立连接,不负责数据的传输
* Socket:负责数据的传输
*
* 步骤:
* 1、开启服务器
* 指定服务器监听的端口号
* 2、等待客户端并接受客户端的连接
*
* 3、接受/发送数据
* 发送方:输出流
* 接受方:输入流
*
* 4、断开连接
*
* 5、关闭服务器
*/
public class TestServer {
public static void main(String[] args) throws IOException {
//1、开启服务器:网卡驱动就监听9999端口号的数据
ServerSocket server = new ServerSocket(9999);
//2、等待客户端并接受客户端的连接
Socket socket = server.accept();//这句代码执行一次,就接受一个客户端连接
System.out.println("一个客户端连接成功!");
//3、例如:发送数据
//发送:欢迎你登录
//字节流,输出流 OutputStream
//(1)获取输出流
OutputStream out = socket.getOutputStream();
//(2)发送数据
out.write("欢迎你登录".getBytes());
//4、断开连接
socket.close();
//5、关闭服务器
server.close();
}
}
package com.atguigu.test10;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;
/*
* TCP:
* 客户端,主动连接服务器
*
* Socket(InetAddress address, int port)
* Socket(String host, int port)
*
* 步骤:
* 1、连接服务器
* Socket socket = new Socket("192.168.30.142",9999);
*
* 2、发送或接受数据
*
* 3、断开连接
*/
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
// 1、连接服务器
Socket socket = new Socket("192.168.30.142",9999);
//2、例如:接受数据
//字节流,输入流,InputStream
InputStream in = socket.getInputStream();
byte[] data = new byte[1024];
int len;
while((len = in.read(data)) != -1){
System.out.println(new String(data,0,len));
}
//3、断开
socket.close();
}
}
15.5.2 TCP协议编程示例二
一个客户端连接服务器,连接成功后,客户端给服务器先传一个“你好”,服务器给客户端返回“欢迎你登录"
package com.atguigu.test11;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TestServer {
public static void main(String[] args) throws IOException {
//1、开启服务器:网卡驱动就监听9999端口号的数据
ServerSocket server = new ServerSocket(9999);
//2、等待客户端并接受客户端的连接
Socket socket = server.accept();//这句代码执行一次,就接受一个客户端连接
System.out.println("一个客户端连接成功!");
//3、接受数据
InputStream in = socket.getInputStream();
byte[] data = new byte[1024];
int len;
System.out.println("服务器收到:");
while((len = in.read(data)) != -1){
System.out.println(new String(data,0,len));
}
//4、例如:发送数据
//发送:欢迎你登录
//字节流,输出流 OutputStream
//(1)获取输出流
OutputStream out = socket.getOutputStream();
//(2)发送数据
out.write("欢迎你登录".getBytes());
//4、断开连接
socket.close();
//5、关闭服务器
server.close();
}
}
package com.atguigu.test11;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
// 1、连接服务器
Socket socket = new Socket("192.168.30.142",9999);
//2、例如:发送你好
OutputStream out = socket.getOutputStream();
out.write("你好".getBytes());
// out.close();//错误的,如果调用out.close()会导致socket的close()
//如果仅仅表示不发送了,还要接收,那么选择半关闭,只关闭输出通道
socket.shutdownOutput();
//3、例如:接受数据
//字节流,输入流,InputStream
System.out.println("客户端收到:");
InputStream in = socket.getInputStream();
byte[] data = new byte[1024];
int len;
while((len = in.read(data)) != -1){
System.out.println(new String(data,0,len));
}
//3、断开
socket.close();
}
}
15.5.3 TCP协议编程示例三
一个客户端连接服务器,连接成功后:
(1)客户端从键盘输入词语,给服务器发送,直到bye为止;
(2)服务器每次手动词语,反转词语 ,然后返回给客户端,直到接收到bye为止
package com.atguigu.test12;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 服务器端:
* (1)接收客户端的连接
* (2)接收客户端的词语
* (3)把词语“反转”返回给客户端
* (2)(3)多次,直到客户端发送"bye"为止
*/
public class TestServer {
public static void main(String[] args) throws IOException {
//1、开启服务器
ServerSocket server = new ServerSocket(8989);
//2、接收一个客户端的连接
Socket socket = server.accept();
//3、先获取输入流和输出流
InputStream in = socket.getInputStream();
/*
* 因为是接收一个词语,反转一个,返回一个
* 那么如果仅仅使用字节流,不好区分词语
* 需要用到字符流
* 那么就意味着需要把字节流转为字符流
*/
InputStreamReader isr = new InputStreamReader(in);//这里不涉及编码问题,仅仅为了转换流的类型
/*
* 字符流中几个字符是一个词语
* 那么我们这里选择“换行符”来作为词语的分割
* 意味着我们可以按行读取Scanner或BufferedReader
*/
BufferedReader br = new BufferedReader(isr);
OutputStream out = socket.getOutputStream();
/*
* 客户端收到字节,同样不方便处理几个字节是一个词语,仍然要把字节输出流转为字符流
* 而且字符之间也不好区分,那么也选择“换行符”进行区别词语
* 我们现在需要把OutputStream转为一个可以按行写的字符流或其他的处理流
*
* 可以按行写的:BufferedWriter(newLine())
* PrintStream(println())
*/
PrintStream ps = new PrintStream(out);
//从客户端接收词语
String word;
while((word = br.readLine()) != null){
if("bye".equals(word)){
break;
}
//如果不是bye,要反转,并且返回
StringBuilder sb = new StringBuilder(word);
sb.reverse();
//返回给客户端
ps.println(sb.toString());
}
//4、断开
socket.close();
//5、关闭服务器
server.close();
}
}
package com.atguigu.test12;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/*
* 客户端:
* (1)从键盘输入词语
* (2)发送给服务器
* (3)接收服务器返回的结果
* (1)(2)(3)多次进行,直到键盘输入bye并发送给发服务器之后就结束
*/
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、连接服务器
Socket socket = new Socket("192.168.30.142",8989);
/*
* * (1)从键盘输入词语
* (2)发送给服务器
* (3)接收服务器返回的结果
* (1)(2)(3)多次进行,直到键盘输入bye并发送给发服务器之后就结束
*/
Scanner input = new Scanner(System.in);
/*
* 同样考虑到发送词语,以及词语之间分割问题,那我们选择PrintStream和BufferedReader
*/
PrintStream ps = new PrintStream(socket.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
//从键盘输入词语
System.out.print("请输入词语:");
String word = input.next();
//发送给服务器端
ps.println(word);
if("bye".equals(word)){
break;
}
//接收服务器返回的结果
String result = br.readLine();
System.out.println("服务器返回的反转后的结果:" + result);
}
input.close();
socket.close();
}
}
15.5.4 TCP协议编程示例四
多个客户端同时连接服务器,连接成功后:
(1)客户端从键盘输入词语,给服务器发送,直到bye为止;
(2)服务器每次手动词语,反转词语 ,然后返回给客户端,直到接收到bye为止
package com.atguigu.test13;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 服务器端:
* (1)接收客户端的连接
* (2)接收客户端的词语
* (3)把词语“反转”返回给客户端
* (2)(3)多次,直到客户端发送"bye"为止
*
* 加一个条件,服务器端可以同时接收n个客户端连接
* 服务器端得加多线程
*/
public class TestServer {
public static void main(String[] args) throws IOException {
//1、开启服务器
ServerSocket server = new ServerSocket(8989);
boolean flag = true;
while(flag){
//2、接收一个客户端的连接
Socket socket = server.accept();//每个客户端的socket是独立的
//为没一个客户端开启一个独立的线程维护它的通信
MessageHandler mh = new MessageHandler(socket);
mh.start();
}
//5、关闭服务器
server.close();
}
}
class MessageHandler extends Thread{
private Socket socket;
public MessageHandler(Socket socket) {
super();
this.socket = socket;
}
public void run(){
try {
//3、先获取输入流和输出流
InputStream in = socket.getInputStream();
/*
* 因为是接收一个词语,反转一个,返回一个
* 那么如果仅仅使用字节流,不好区分词语
* 需要用到字符流
* 那么就意味着需要把字节流转为字符流
*/
InputStreamReader isr = new InputStreamReader(in);//这里不涉及编码问题,仅仅为了转换流的类型
/*
* 字符流中几个字符是一个词语
* 那么我们这里选择“换行符”来作为词语的分割
* 意味着我们可以按行读取Scanner或BufferedReader
*/
BufferedReader br = new BufferedReader(isr);
OutputStream out = socket.getOutputStream();
/*
* 客户端收到字节,同样不方便处理几个字节是一个词语,仍然要把字节输出流转为字符流
* 而且字符之间也不好区分,那么也选择“换行符”进行区别词语
* 我们现在需要把OutputStream转为一个可以按行写的字符流或其他的处理流
*
* 可以按行写的:BufferedWriter(newLine())
* PrintStream(println())
*/
PrintStream ps = new PrintStream(out);
//从客户端接收词语
String word;
while((word = br.readLine()) != null){
if("bye".equals(word)){
break;
}
//如果不是bye,要反转,并且返回
StringBuilder sb = new StringBuilder(word);
sb.reverse();
//返回给客户端
ps.println(sb.toString());
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
//4、断开
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.atguigu.test13;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/*
* 客户端:
* (1)从键盘输入词语
* (2)发送给服务器
* (3)接收服务器返回的结果
* (1)(2)(3)多次进行,直到键盘输入bye并发送给发服务器之后就结束
*
* 加一个条件,服务器端可以同时接收n个客户端连接
* 客户端代码不用修改
*/
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、连接服务器
Socket socket = new Socket("192.168.30.142",8989);
/*
* * (1)从键盘输入词语
* (2)发送给服务器
* (3)接收服务器返回的结果
* (1)(2)(3)多次进行,直到键盘输入bye并发送给发服务器之后就结束
*/
Scanner input = new Scanner(System.in);
/*
* 同样考虑到发送词语,以及词语之间分割问题,那我们选择PrintStream和BufferedReader
*/
PrintStream ps = new PrintStream(socket.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
//从键盘输入词语
System.out.print("请输入词语:");
String word = input.next();
//发送给服务器端
ps.println(word);
if("bye".equals(word)){
break;
}
//接收服务器返回的结果
String result = br.readLine();
System.out.println("服务器返回的反转后的结果:" + result);
}
input.close();
socket.close();
}
}
15.5.5 TCP协议编程示例五
一个客户端连接服务器,连接成功后,给服务器上传一个文件,服务器接收到文件后存到upload的文件夹中。
package com.atguigu.test14;
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 从客户端发送文件到服务器端
* (1)接收客户端的连接
* (2)接收文件名.后缀名
* 思考:
* 存哪里 ①在当前项目中找一个位置存储,例如:upload文件夹
* ②如何解决文件名重名的问题 文件名需要处理,加入时间戳或其他的唯一编码的UUID等值
* ③.后缀名需要保留,因为它代表文件的类型
* (3)接收文件内容
* (4)反馈结果
*
* 思考:
* 这里既要接收文件名.后缀名,又要接收文件内容。
* 这里既有 文本信息“文件名.后缀名”,又有其他类型的数据“文件内容”,只能选择字节流。
*
* 如何区别,文件名.后缀名 与 文件内容呢
* 想哪种字节输入流,可以处理字符串,和字节类型的数据。
*
* FileInputStream
* BufferedInputStream
* DataInputStream
* ObjectInputStream
*
* 这些里面:DataInputStream:readUTF() 和 read(byte[])
* ObjectInputStream也可以,但是麻烦,我这里选择DataInputStream
*
*/
public class TestServer {
public static void main(String[] args) throws IOException {
//1、开启服务器
ServerSocket server = new ServerSocket(9999);
//2、接收客户端的连接
Socket socket = server.accept();
//3、获取输入流
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
//接收文件名.后缀名
String fileName = dis.readUTF();
//处理文件名
/*
* 希望我要在服务器存储的文件名: 原来的文件名 + 时间戳
*/
long timestamp = System.currentTimeMillis();
//.的下标
int index = fileName.lastIndexOf(".");
//后缀名
String ext = fileName.substring(index);
// 原来的文件名
String name = fileName.substring(0, index);
//新文件名
String newFileName = "upload/" + name + timestamp + ext;
//创建文件输出流,把接收到的文件内容,写入新文件
FileOutputStream fos = new FileOutputStream(newFileName);
//接收文件内容
byte[] data = new byte[1024];
int len;
while((len = dis.read(data))!=-1){
fos.write(data, 0, len);
}
//还可以给客户端反馈:文件接收完毕
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
ps.println("文件接收完毕!");
//断开
fos.close();
socket.close();
server.close();
}
}
package com.atguigu.test14;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/*
* 从客户端发送文件到服务器端
*
*
* 客户端:
* (1)从键盘输入文件的路径名,即选择要发送的文件
* (2)给服务器先把“文件名.后缀名"
* (3)发送文件内容
* (4)接收服务器的反馈结果
*
* 这里同样因为既要发送“文件名.后缀名",又要发送“文件内容”,选择字节流,选择DataOutputStream
*
*/
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、连接服务器
Socket socket = new Socket("192.168.30.142",9999);
//2、从键盘输入文件的路径名,即选择要发送的文件
Scanner input = new Scanner(System.in);
System.out.print("请选择你要发送的文件(例如:D:/尚硅谷_190513班_柴林燕_JavaSE/开学典礼所发资料.rar):");
String fileName = input.nextLine();
File file = new File(fileName);
//3、给服务器发送“文件名.后缀名"
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
//发送“文件名.后缀名"
dos.writeUTF(file.getName());
//4、发送文件内容
//先从file文件读取内容
FileInputStream fis = new FileInputStream(file);
byte[] data = new byte[1024];
int len;
while((len = fis.read(data)) != -1){
//一边读,一边给服务器发送
dos.write(data,0,len);
}
socket.shutdownOutput();//表示发送完毕了
//5、接收反馈
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String result = br.readLine();
System.out.println("结果:" + result);
//6、关闭
socket.close();
fis.close();
}
}
15.5.6 TCP协议编程示例六
多个客户端连接服务器,连接成功后,给服务器上传一个文件,服务器接收到文件后存到upload的文件夹中。
package com.atguigu.test15;
import java.io.DataInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TestServer {
public static void main(String[] args) throws IOException {
//1、开启服务器
ServerSocket server = new ServerSocket(9999);
while(true){
//2、接收客户端的连接
Socket socket = server.accept();
FileUploadThread ft = new FileUploadThread(socket);
ft.start();
}
// server.close();//不关闭服务器
}
}
class FileUploadThread extends Thread{
private Socket socket;
public FileUploadThread(Socket socket) {
super();
this.socket = socket;
}
public void run(){
try {
//3、获取输入流
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
//接收文件名.后缀名
String fileName = dis.readUTF();
//处理文件名
/*
* 希望我要在服务器存储的文件名: 原来的文件名 + 时间戳
*/
long timestamp = System.currentTimeMillis();
//.的下标
int index = fileName.lastIndexOf(".");
//后缀名
String ext = fileName.substring(index);
// 原来的文件名
String name = fileName.substring(0, index);
//新文件名
String newFileName = "upload/" + name + timestamp + ext;
//创建文件输出流,把接收到的文件内容,写入新文件
FileOutputStream fos = new FileOutputStream(newFileName);
//接收文件内容
byte[] data = new byte[1024];
int len;
while((len = dis.read(data))!=-1){
fos.write(data, 0, len);
}
//还可以给客户端反馈:文件接收完毕
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
ps.println("文件接收完毕!");
//断开
fos.close();
socket.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.atguigu.test15;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、连接服务器
Socket socket = new Socket("192.168.30.142",9999);
//2、从键盘输入文件的路径名,即选择要发送的文件
Scanner input = new Scanner(System.in);
System.out.print("请选择你要发送的文件(例如:D:/尚硅谷_190513班_柴林燕_JavaSE/开学典礼所发资料.rar):");
String fileName = input.nextLine();
File file = new File(fileName);
//3、给服务器发送“文件名.后缀名"
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
//发送“文件名.后缀名"
dos.writeUTF(file.getName());
//4、发送文件内容
//先从file文件读取内容
FileInputStream fis = new FileInputStream(file);
byte[] data = new byte[1024];
int len;
while((len = fis.read(data)) != -1){
//一边读,一边给服务器发送
dos.write(data,0,len);
}
socket.shutdownOutput();//表示发送完毕了
//5、接收反馈
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String result = br.readLine();
System.out.println("结果:" + result);
//6、关闭
socket.close();
fis.close();
input.close();
}
}
15.5.7 TCP协议编程示例七
群聊
package com.atguigu.test16;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
/*
*
*/
public class TestServer {
private static ArrayList<Socket> online = new ArrayList<Socket>();
public static void main(String[] args) throws IOException {
//1、开启服务器
ServerSocket server = new ServerSocket(9999);
while(true){
//2、接收客户端的连接
Socket socket = server.accept();
//把这个客户端加入到online中
online.add(socket);
//每一个客户端独立的线程
MessageHandler mh = new MessageHandler(socket);
mh.start();
}
}
//私有的静态的内部类
//这里用内部类的原因,是为了用上面的online集合
private static class MessageHandler extends Thread{
private Socket socket;
private String ip;
public MessageHandler(Socket socket) {
super();
this.socket = socket;
this.ip = socket.getInetAddress().getHostAddress();
}
public void run(){
//这个客户端的一连接成功,线程一启动,就可以告诉其他人我上线了
sendToOthers(ip+"上线了");
/*
* (1)接收当前的客户端发送的消息
* (2)给其他在线的客户端转发
*/
//(1)接收当前的客户端发送的消息
try {
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String content;
while((content = br.readLine()) !=null){
if("bye".equals(content)){
//给自己发一句bye
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
ps.println("bye");
break;
}
//收到一句,转发一句
sendToOthers(ip+"说:" + content);
}
sendToOthers(ip+"下线了");
} catch (IOException e) {
sendToOthers(ip+"掉线了");
}
}
//因为转发的代码也很长,独立为一个方法
public void sendToOthers(String str){
//遍历所有online的客户端
Iterator<Socket> iterator = online.iterator();
while(iterator.hasNext()){
Socket on = iterator.next();
if(!on.equals(socket)){//只给其他客户端转发
try {
OutputStream out = on.getOutputStream();
PrintStream ps = new PrintStream(out);
ps.println(str);
} catch (IOException e) {
//说明on这个客户端要么下线了,要么掉线了
iterator.remove();
}
}
}
}
}
}
package com.atguigu.test16;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/*
* 群聊
*/
public class TestClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、连接服务器
Socket socket = new Socket("192.168.30.142",9999);
//2、开启两个线程,一个收消息,一个发消息
SendThread st = new SendThread(socket);
ReceiveThread rt = new ReceiveThread(socket);
st.start();
rt.start();
//等发送线程停下来再往下走
try {
st.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//让接收数据的线程停下
rt.setFlag(false);
//等接收线程停下来,再往下走,断开连接
try {
rt.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
socket.close();
}
}
class SendThread extends Thread{
private Socket socket;
public SendThread(Socket socket) {
super();
this.socket = socket;
}
public void run(){
try {
//键盘输入
Scanner input = new Scanner(System.in);
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
while(true){
//从键盘输入
System.out.print("请输入要发送的消息:");
String content = input.nextLine();
System.out.println("content:" + content);
//给服务器发送
ps.println(content);
//如果bye,就结束发送
if("bye".equals(content)){
break;
}
}
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ReceiveThread extends Thread{
private Socket socket;
private boolean flag = true;
public ReceiveThread(Socket socket) {
super();
this.socket = socket;
}
public void run(){
try {
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
while(flag){
String line = br.readLine();
System.out.println(line);
if("bye".equals(line)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
15.5.8 UDP协议编程示例
package com.atguigu.test17;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class TestSend {
public static void main(String[] args) throws IOException {
//1、发送方,建立一个Socket
//发送方的端口号和IP地址,自动获取
DatagramSocket ds = new DatagramSocket();
//2、准备把要发送的数据打包
String str = "马上下课了";
byte[] bytes = str.getBytes();
InetAddress ip = InetAddress.getByName("192.168.30.142");
DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length, ip, 9999);
//3、发送,通过socket发送
ds.send(dp);
System.out.println("发送完毕");
//4、关闭
ds.close();
}
}
package com.atguigu.test17;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/*
* 接收方:
* DatagramPacket(byte[] buf, int length)
* 参数一:用来装数据的字节数组
* 参数二:数组的长度
*/
public class TestReceive {
public static void main(String[] args) throws IOException {
//1、接收方也要socket
//接收方的端口号,指定,IP自动获取
DatagramSocket ds = new DatagramSocket(9999);
//2、准备一个数据报,接收数据
byte[] data = new byte[1024];
DatagramPacket dp = new DatagramPacket(data,data.length);
//3、接收数据
ds.receive(dp);
//4、把数据拆解出来
byte[] bs = dp.getData();//接收的数据
int len = dp.getLength();//接收的数据的实际长度
System.out.println(new String(bs,0,len));
//5、断开
ds.close();
}
}