一、对象IO流
1、什么情况下需要使用对象IO流?
Student stu = new Student(“小张”,89);
//想要把这个对象保存到文件中
//FileOutputStream write(一个字节) write(字节数组) write(字节数组,start,count)
//FileWriter write(一个字符) write(字符数组) write(字符数组,start,count)
//而这里的学生对象,不是String类型,也不是字节类型
//如果把stu对象的成员变量name,score分别输出,需要用DataOutputStream writeUTF(name),writeInt(score)
//如此处理可以实现,但是如果成员变量非常多的,一个一个输出就比较麻烦
//我们现在想要整个对象一起输出
2、ObjectOutputStream:不能单独使用,它只负责把对象转为字节序列。
所以它需要配合其他的IO流一起工作。需要包装其他的OutputStream字节输出流对象使用。
序列化:(把对象变为字节序列的过程)
要求:(1)对象的类型必须实现java.io.Serializable接口
对象中的引用数据类型的成员变量,也必须实现java.io.Serializable接口。
例如:String,Teacher类型等都要实现java.io.Serializable接口
(2)需要的IO流:ObjectOutputStream,调用writeObject(对象)
Serializable接口是一个标识型接口。
3、ObjectInputStream:不能单独使用,它只负责把字节序列重构成一个Java对象。
所以它需要配合其他的IO流一起工作。需要包装其他的InputStream字节输入流对象使用。
反序列化:把字节序列构建为一个Java对象的过程。
要求:(1)需要的IO流:ObjectInputStream,调用readObject()方法
(2)不止需要数据文件,要保证要new的对象的类型也必须存在。
4、当我已经把对象序列化了之后,修改了对象的类的代码,意味着这个类重新编译生成了新的.class文件。
但是数据没有变化。
此时如果一开始对象的类型没有固定序列化版本Id,就会导致反序列化时报InvalidClassException。
如果一开始就固定序列化版本Id,就不会报InvalidClassException。
private static final long serialVersionUID = 1L;值是多少都可以。
5、类中的静态变量是不会序列化的;
类中的transient修饰的成员变量也不会序列化。
public class TestObjectIO {
@Test
public void test02() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("stu.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
//数据:stu.dat==》fis(字节数据) ==> ois(把字节重构为一个Java对象)
//把字节序列的数据 放到 你新new的一个对象中,那么new对象,就要有类型
//如果单独拥有stu.dat数据文件是无法正常反序列化的,还需要Student.class文件。
Object obj = ois.readObject();
System.out.println(obj);
Student stu = (Student) obj;
System.out.println(Student.getCountry());
/*
java.io.InvalidClassException(无效类异常):com.atguigu.test01.Student;
local(本地) class incompatible(不相容的;矛盾的):
stream(流中) classdesc(类的描述) serialVersionUID(序列化版本ID) = -8599123853736567486,
local class serialVersionUID = -1035089529866949311
为了解决这个问题,我们应该在一开始就固定序列化版本ID
*/
ois.close();
fis.close();
}
@Test
public void test() throws IOException {
Student stu = new Student("小张",89);
stu.setTeacher(new Teacher("萌萌"));
stu.setAge(28);
Student.setCountry("中华人民共和国");//不建议用对象.静态方法
//静态变量修改了,所有对象都会修改
FileOutputStream fos = new FileOutputStream("stu.dat");//不是纯文本数据,所以不建议文件的后缀名用.txt,容易误导用户
ObjectOutputStream oos = new ObjectOutputStream(fos);
//数据:程序(对象)==>oos(对象->字节序列)==>fos==>stu.dat
oos.writeObject(stu);//java.io.NotSerializableException:编译时异常,是IOException
//不支持序列化异常
//当我们要序列化的对象的类型没有实现java.io.Serializable接口时,就会抛出这个异常
//即要求Student类必须实现java.io.Serializable接口
oos.close();
fos.close();
}
}
二、补充
1、如果实现Serializable接口的话,
如果序列化,哪些成员变量序列化,都是默认的方式。我们无法自己决定。
例如:static和transient修饰的成员变量不能序列化等。
如果我们想要自己决定哪些成员变量序列化,顺序是怎么安排等等,那么我们可以实现java.io.Externalizable接口。
实现这个接口,我们就要重写两个方法:
(1)void writeExternal(ObjectOutput out):在这个方法中,编写哪些成员变量需要序列化
(2) void readExternal(ObjectInput in):在这个方法中,按照序列化的顺序读取数据进行反序列化
public class TestExternalizable {
@Test
public void testOut() throws IOException {
Employee emp = new Employee("小张三",23);
Employee.setCountry("中华人民共和国");
FileOutputStream fos = new FileOutputStream("emp.data");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(emp);
oos.close();
fos.close();
}
@Test
public void testIn() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("emp.data");
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
System.out.println(o);
ois.close();
fis.close();
}
}
三、打印流
1、PrintStream:SE一直在用
(1)PrintStream 永远不会抛出 IOException;
(2)自动刷新
(3)提供了两个最常用的方法:println()和print()
(4)不管是字符串还是基本数据类型,还是其他对象类型,都支持输出。
只不过引用数据类型,会自动调用对象的toString()进行输出。
System.out
System.err
它们就是PrintStream类型
2、PrintWriter(SE不学,在web用到,在web阶段Servlet类中reponse响应用户请求对象,getWriter()方法得到的就是PrintWriter对象,
用于从服务器端往客户端发送文本数据用。
四、System类中
System.in:InputStream
默认初始化为键盘输入
System.out:PrintStream
默认初始化为往控制台输出
System.err:PrintStream
默认初始化为往控制台输出,以错误信息的方式输出
它们三个常量对象有set方法,不是通过Java方法修改的,而是通过C代码修改的。包括它们的初始化也是通过C代码完成的。
public class TestPrintStream {
@Test
public void test(){
System.out.println("hello");
System.out.println();
// System.out.print();//错误的原因,是因为PrintStream类型中没有提供这个方法
}
@Test
public void test02() throws FileNotFoundException {
System.setOut(new PrintStream("1.txt"));
}
}
class MyClass{
private static final String info = null;//值是null,没有set方法修改
public static String getInfo() {
return info;
}
}
五、java.uitl.Scanner
1、next()与nextLine()方法的区别
2、nextInt()等 与 紧接着的nextLine()的问题
public class TestScanner {
public static void main(String[] args) {
//Scanner(InputStream source)
Scanner input = new Scanner(System.in);
/* System.out.print("姓名:");
// String name = input.next();//遇到空格等空白符结束
String name = input.nextLine();//遇到回车换行符结束
System.out.println("name = " + name);*/
System.out.print("请输入年龄:");
int age = input.nextInt();//把整数值读取了,回车换行符还在流中
// input.nextLine();//读取数字后面的回车换行符
System.out.print("请输入地址:");
String address = input.nextLine();//读取数据,一看是回车换行符,就结束了
System.out.println(age);
System.out.println(address);
}
@Test
public void test02() throws FileNotFoundException {
// Scanner(InputStream source)
Scanner input = new Scanner(new FileInputStream("1.txt"));
//这里不是键盘输入
/*System.out.println(input.nextLine());
System.out.println(input.nextLine());
System.out.println(input.nextLine());
System.out.println(input.nextLine());
System.out.println(input.nextLine());*/
while(input.hasNextLine()){
System.out.println(input.nextLine());
}
}
@Test
public void test03(){
Scanner input = new Scanner(System.in);
System.out.println("请输入姓名:");
String naem = input.next();
System.out.println(naem);
}
}
六、JDK1.7之后,允许try…catch的新写法
try(需要关闭的IO流的声明与初始化){
IO流的业务处理代码
}catch(异常类型 e){
异常处理代码
}
没有finally。所有在try()中声明的IO流会自动关闭,不需要手动关闭
public class TestException {
@Test
public void test03() {
try(
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("d:/hello.txt"),"GBK"));
OutputStreamWriter osw = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream("1.txt")),"UTF-8");
) {
//一边读一边写
String line;
while((line = br.readLine()) != null){
osw.write(line);
osw.write("\r\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void test02() {
try(
FileInputStream fis = new FileInputStream("d:/hello.txt");
InputStreamReader isr = new InputStreamReader(fis,"GBK");
BufferedReader br = new BufferedReader(isr);
FileOutputStream fos = new FileOutputStream("1.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
OutputStreamWriter osw = new OutputStreamWriter(bos,"UTF-8");
) {
//一边读一边写
String line;
while((line = br.readLine()) != null){
osw.write(line);
osw.write("\r\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void test01() {
FileInputStream fis = null;
InputStreamReader isr = null;
BufferedReader br = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
OutputStreamWriter osw = null;
try {
//复制文件,并且加上缓冲,并且加上转码,源文件是GBK,目标文件是UTF-8
//源文件:d:/hello.txt(GBK),项目路径下:1.txt(UTF-8)
fis = new FileInputStream("d:/hello.txt");
isr = new InputStreamReader(fis,"GBK");
// BufferedInputStream/ BufferedReader ,
// 因为我是在InputStreamReader继承上加缓冲,而InputStreamReader是字符流,所以选择BufferedReader
br = new BufferedReader(isr);
fos = new FileOutputStream("1.txt");
// BufferedOutputStream / BufferedWriter
//因为FileOutputStream是字节流,需要用BufferedOutputStream
bos = new BufferedOutputStream(fos);
osw = new OutputStreamWriter(bos,"UTF-8");
//一边读一边写
String line;
while((line = br.readLine()) != null){
osw.write(line);
osw.write("\r\n");
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
网络编程整章来说:
1、API的学习:*
2、通信的原理和过程:***
一、软件结构
程序员都是些“软件”,C/S结构或B/S结构
C/S结构:有客户端程序的,需要用户在它的手机、电脑安装独立的客户端,例如:qq,微信等
有服务器端程序。例如:腾讯服务器。
B/S结构:没有独立的客户端,客户端都是浏览器。例如:网页版淘宝,京东,网页版qq
有服务器端程序。例如:阿里服务器,京东服务器,腾讯服务器。
其实B/S结构也可以看成特殊的C/S结构,只不过客户端不是我们自己开发的,而是通用的。
不管B/S还是C/S结构,都有客户端与服务器的概念。它们是两个独立的程序,甚至在不同的电脑上运行,
我们就需要网络通信来协助它们进行数据的交互。
二、网络程序的几个基本要素
1、IP地址:负责定位到网络世界中的某一台主机上。
意味着大家的IP地址是不能重复的。
2、端口号:负责定位到一台主机上的某一个应用程序的进程中。
因为一台电脑上可能有很多的程序需要联网,那么网卡接收到数据之后,把数据给哪个程序,需要端口号来区别。
意味着同一台电脑上不同的应用程序的同一种协议下端口号是不能重复的。
同一个应用程序不同的协议端口号可以相同。
3、网络协议:在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。
(1)TCP:Transmission Control Protocol,传输控制协议
面向连接的,可靠的,基于字节流的,适用于大数据量传输的控制协议
如果中间的某段数据丢失了,会要求传输方重传。例如:文件上传下载。
在传输数据之前要经过三次握手确保连接是正常的,才会开始传输数据。
在传输数据之后还要经过四次挥手断开连接。
(2)UDP:User Datagram Protocol,用户数据报协议
非面向连接的,不可靠的,基于数据报,适用于小的数据的传输的控制协议
如果中间的某段数据丢失了,不会要求传输方重传,丢了就丢了。例如:视频等实时通信的场景。
在传输之前不会检测接收方是否在线,直接发送,能收到就收到,不能收到拉倒。
一般适用于,广告消息推送。
平时视频通话,先TCP连接成功,然后UDP传输数据。
三、IP地址
1、IPV4:4个0~255之间的十进制值,
每一个十进制值相当于是8位的二进制值,无符号。
正常一个字节:-128~127,无符号:0~255
例如:192.168.12.34
2、IPV6:分成8组十六进制数,每16个字节一组,表示成例如:ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
因为IPV4几乎枯竭,不够用了。不管在局域网中还是广域网中,设备的数量越来越多。
IPV6的推广词:让世界上的每一粒沙子都有自己的IP地址。
但是IPV6从设计出来,到现在还没有全面普及。
从硬件层面到软件层面都要设计到IP规则的更改。
3、在api层面如何支持IP地址?
java.net.InetAddress及其Inet4Address, Inet6Address。
InetAddress:包含主机名和IP地址信息,如果可以获取主机名,就会获取它,如果得不到,就是空的。
String getHostName() :获取主机名
String getHostAddress() :获取IP地址
4、域名与IP地址的不同?
因为对于普通用户来说,IP地址不方便记忆。设计出域名方便普通用户记忆。
域名与IP是映射关系,某个域名会对应一个IP地址。
域名需要提前注册,在域名服务器中注册,当别人访问这域名时,就会解析对应的IP地址。
public class TestIP {
@Test
public void test01() throws UnknownHostException {
InetAddress ip = InetAddress.getLocalHost();//本机
System.out.println(ip);//Irene-PC/192.168.12.71
}
@Test
public void test02() throws UnknownHostException {
InetAddress ip = InetAddress.getByName("www.baidu.com");
System.out.println(ip);//www.baidu.com/182.61.200.6
}
@Test
public void test03() throws UnknownHostException {
byte[] bytes = {(byte)192,(byte)168,12,34};//编译期间byte的范围:[-128~127],运行时内部是按照int处理
InetAddress ip = InetAddress.getByAddress(bytes);
System.out.println(ip);// /192.168.12.34
}
}
四、端口号
1、用0~65535之间的数字表示,两个字节的无符号数字范围,类似于char的范围。
2、分类:
公认端口:0~1023 即<1024:大家千万不要用,被预先定义的服务通信占用
如:HTTP(80),FTP(21),Telnet(23)全世界大家公认的服务端口
注册端口:1024~49151,建议大家不要用
如:mysql:3306
tomcat:8080
oracle:1521
。。。
动态/ 私有端口:49152~65535,自动分配,或者自己测试随便用
五、Socket编程
1、Socket是什么?
翻译:套接字
解释:代表网络通信的一个端点,通信的两边各有一个Socket对象。
服务器端有一个Socket,客户端有一个Socket对象。
如果有多个客户端同时与服务器通信,那么在服务器端就会有多个Socket,一个Socket代表一个客户端的连接。
2、Socket分为两大类:
(1)流套接字:ServerSocket和Socket
适用于TCP协议的Socket
ServerSocket:用于服务器端,接收客户端连接用的,不负责传输数据。
如果客户端连接成功,就会给他分配一个独立的Socket对象。
Socket:用于数据传输用的,客户端与服务器端各有一个。
(2)数据报套接字:DatagramSocket
适用于UDP协议的Socket
客户端与服务器端各有一个