/*
* 中文乱码出现的情况研究
* 注意点:乱码解决的办法是再编码再解码
* 但是如果是编码出错了,无法解决.如果是解码出错了,可以利用再编码再解码
*
*
* 编码 解码 结果
* GBK utf8 不可以(GBK2个字节,utf83个字节)
* GBK ISO8859-1 可以
* utf8 GBK 有时可以
* utf8 ISO8859-1 可以
* ISO8859-1 GBK 不可以(编码就出错了)
* ISO8859-1 utf8 不可以(编码就出错了)
*/
/*
* Properties:实质上是一个Map(HashTable)集合,存储的是属性,属性以键值对的形式存在.键值对内部的键值必须是字符串,不需要泛型
*
* 为什么要在这里将Properties?
* 因为使用与流结合
*
* 优点:
* 1.以键值对的形式存储数据
* 2.内部针对属性的存储封装了大量的专有方法:load,store,list
*/
public synchronized void load(Reader reader); public synchronized void load(InputStream inStream); public void store(Writer writer, String comments); public void store(OutputStream out, String comments);
以上方法都会调用load0()方法或者store0()方法,在调用时都会对其流进行包装, 包装成BufferedReader 或者 BufferedWriter;
虽然在store上没有直接的synchronized关键字保证方法同步, 但是在Writer类中对write()方法进行了锁同步(至于为什么, 应该是配置文件一般同时只有一个写流对其操作?)
在使用完流之后记得要关闭流~
/*
* 序列流:把多个输入流的内容一次性的打印(操作)---字节流 (两种方式)
*/
//创建三个输入流 FileInputStream fileInputStream1 = new FileInputStream("src\com\qianfeng\test\Demo2.java"); FileInputStream fileInputStream2 = new FileInputStream("src\com\qianfeng\test\Demo2.java"); FileInputStream fileInputStream3 = new FileInputStream("src\com\qianfeng\test\Demo1.java"); //将三个输入流放入序列流 ------------------------------------------------------------------------ //方式一:先放入一个Vector Vector<FileInputStream> vector = new Vector<>(); vector.add(fileInputStream1); vector.add(fileInputStream2); vector.add(fileInputStream3); //得到枚举器 Enumeration<FileInputStream> e = vector.elements(); ------------------------------------------------------------------------- //方式二:先放入一个list ArrayList<FileInputStream> list = new ArrayList<>(); list.add(fileInputStream1); list.add(fileInputStream2); list.add(fileInputStream3); //将集合转换成枚举 Enumeration<FileInputStream> e = Collections.enumeration(list); ------------------------------------------------------------------------- SequenceInputStream sequenceInputStream = new SequenceInputStream(e); FileOutputStream fileOutputStream = new FileOutputStream("filepath"); byte[] arr = new byte[1024]; int num; while ((num = sequenceInputStream.read(arr)) != -1) { fileOutputStream.write(arr, 0, num); fileOutputStream.flush(); } sequenceInputStream.close(); fileOutputStream.close();
/*
* 数据流:字节流
* DataInputStream: 数据输入流
* DataOutputStream: 数据输出流
*
* 注意:数据流要与字节输入流,输出流配合使用, 如果数据类型不同, 那么写出和读入的顺序要有规则
*/
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("filepath")); //写 dataOutputStream.writeInt(97);//4个字节 dataOutputStream.writeBoolean(true);//1个 dataOutputStream.write(33);//1个 dataOutputStream.writeDouble(34.56);//8个 //关闭流 dataOutputStream.close(); //读 DataInputStream dataInputStream = new DataInputStream(new FileInputStream("filepath")); dataInputStream.readBoolean(); dataInputStream.readInt(); dataInputStream.readByte(); dataInputStream.readDouble(); dataInputStream.close();
/*
* 内存流(byte数组流):
* ByteArrayInputStream:写入内存,在内部有一个数组,数据被放在这里面
* ByteArrayOutputStream:将数据取出,放在字节数组里面
*/
//创建输入流,关联一个byte型的数组,作为缓冲区数据 ByteArrayInputStream bais = new ByteArrayInputStream("hello world".getBytes()); //创建输出流-不需要指定参数 ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] arr = new byte[1024]; int num; while ((num = bais.read(arr)) != -1) { baos.write(arr, 0, num); } System.out.println(new String(arr)); bais.close(); baos.close(); //注意:将流关闭了之后,还可以调用方法,不会报错. baos.write(45);
Java 序列化的高级认识 https://www.ibm.com/developerworks/cn/java/j-lo-serial/
以下是基本用法
/*
* 序列化流:是将短期存储的数据实现长期存储
* 数据的存储分成两类:
* 1.短期存储:存放在内存中,随着程序的关闭而释放---对象,集合,变量,数组
* 2.长期存储:存储在磁盘中,即使程序关闭了,数据仍然存在------文件
*
* 序列化:将数据从内存放入磁盘,可以实现数据的长久保存--数据持久化的手段
* 反序列化:将数据从磁盘放回内存
*
* 进行序列化的步骤:--通过对象的序列化讲解
* 1.创建一个类
* 2.使用对应的流将对象存入磁盘中----序列化----ObjectOutputStream
* 3.使用对应的流将对象从磁盘中取出放回内存--反序列化------ObjectInputStream
* 4.关闭流
*
* 注意点:序列化流在工作时也要关联对应的输入流和输出流
*/
//创建类用于序列化
//类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
//可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
/*
* 解释:
* 一个类如果没有实现Serializable,进行序列化会报异常:NotSerializableException
*
* 实现了Serializable接口的类可以达到的目的:
* 1.可以进行序列化
* 2.进行序列化的类的元素都必须支持序列化
* 3.接口本身没有方法或字段,只是用来表示可序列化的语义
*
* * 注意点:
* 1. ClassNotFoundException:当前的类没有找到
* 分析:将Person对象进行序列化之后,将Person类删除,再进行反序列化的时候出现了异常
* 原因:反序列化在执行的时候依赖字节码文件,当类没有了,字节码文件无法创建,反序列化失败
*
* 2.java.io.InvalidClassException 无效的类
* 出现的原因:没有声明自己的serialVersionUID,而使用系统的.在进行反序列化的时候,类被改动了,系统认为现在的类
* 已经不是原来的类了(在使用系统的id进行识别的时候,重写给Person设置了id),认为此类无效
*
* 3.使用系统的serialVersionUID与自定义的ID的区别?
* 使用系统的,序列化和反序列化,id不能手动设置,使用的是编译器默认生成的,一旦类发生了改动,id会重新赋值
* 使用自定义的,序列化和反序列化,id不会发生改变,所以当反序列化的时候,即使对Person类进行了一些改动,也能继续反序列化
* private static final long serialVersionUID = 1L;
*
* 4.总结序列化,反序列化工程的注意点:
* a.合理使用序列化流和反序列化流,要与输入流与输出流配合使用
* b.进行序列化的类一定要实现Serializable接口,只要实现了接口就可以序列化.包括集合,包装类等
* c.进行序列化的类要保证当前类与内部的类都要实现Serializable接口
*
*/
//写出--序列化 //创建序列化流并关联文件 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("filepath")); //调用方法实现序列化 //序列化后的内容不能直接查看,要想查看进行反序列化 objectOutputStream.writeObject(obj); objectOutputStream.close(); ---------------------------------------------------------------------------------------------------- //读入--反序列化 ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("filepath")); //实现反序列化 Object object = objectInputStream.readObject(); //向下转型 /* * ClassNotFoundException:当前的类没有找到 * 分析:将Person对象进行序列化之后,将Person类删除,再进行反序列化的时候出现了异常 * 原因:反序列化在执行的时候依赖字节码文件,当类没有了,字节码文件无法创建,反序列化失败 */ System.out.println(object); objectInputStream.close();
网络流部分
/*
* 网络通信:三大要素:IP,端口号,协议 1.IP:可以在网络中唯一的标记一台主机 127.0.0.1(本地地址/本机地址/保留地址)
* java中将IP面向对象了--InetAddress 2.端口:用来区分一台主机上的多个服务器(不可以重复) 取值范围:(0,65535)
* 注意点:在通信时两边的端口号要一致 3.网络协议:相当于指定的一个统一的标准
*
*
*
* 七层协议: 了解 应用层
*
* 与其它计算机进行通讯的一个应用,它是对应应用程序的通信服务的。 例如,一个没有通信功能的字处理程序就不能执行通信的代码,
* 从事字处理工作的程序员也不关心OSI的第7层。但是,如果添加了一个 传输文件的选项,那么字处理器的程序员就需要实现OSI的第7层。
* 示例:TELNET,HTTP,FTP,NFS,SMTP等。
*
* 表示层
*
* 这一层的主要功能是定义数据格式及加密。例如,FTP允许你选择以二进制 或ASCII格式传输。如果选择二进制,那么发送方和接收方不改变文件的内容。
* 如果选择ASCII格式,发送方将把文本从发送方的字符集转换成标准的ASCII后
* 发送数据。在接收方将标准的ASCII转换成接收方计算机的字符集。示例:加密,ASCII等。
*
* 会话层
*
* 它定义了如何开始、控制和结束一个会话,包括对多个双向消息的控制和管理, 以便在只完成连续消息的一部分时可以通知应用,从而使表示层看到的数据是连续的,
* 在某些情况下,如果表示层收到了所有的数据,则用数据代表表示层。示例:RPC,SQL等。
*
* 传输层
*
* 这层的功能包括是否选择差错恢复协议还是无差错恢复协议,及在同一主机上对不同 应用的数据流的输入进行复用,还包括对收到的顺序不对的数据包的重新排序功能。
* 示例:TCP,UDP,SPX。
*
* 网络层
*
* 这层对端到端的包传输进行定义,它定义了能够标识所有结点的逻辑地址,还定义了 路由实现的方式和学习的方式。为了适应最大传输单元长度小于包长度的传输介质,
* 网络层还定义了如何将一个包分解成更小的包的分段方法。示例:IP,IPX等。
*
* 数据链路层
*
* 它定义了在单个链路上如何传输数据。这些协议与被讨论的各种介质有关。示例:ATM,FDDI等。
*
* 物理层
*
* OSI的物理层规范是有关传输介质的特性标准,这些规范通常也参考了其他组织制定的标准。
* 连接头、帧、帧的使用、电流、编码及光调制等都属于各种物理层规范中的内容。 物理层常用多个规范完成对所有细节的定义。示例:Rj45,802.3等。
*/
/*
* IP地址:
* java将IP地址面向对象了---InetAddress
*/
//获取自己的主机 InetAddress inetAddress = InetAddress.getLocalHost(); inetAddress.getHostName(); //HostName inetAddress.getHostAddress(); //URI //获取网络上任意一台主机 InetAddress inetAddress2 = InetAddress.getByName("www.baidu.com");//根据域名 IP地址也可以
/*
* 网络通信
* Socket通信--TCP/UDP
*
* TCP与UDP的区别:
* TCP (建立在链接之上安全,丢包率低) UDP (类似于发快递,适合于消息的发送)
* 1.是建立在连接的基础上 建立在非连接的基础上
* 2.安全性更高 安全性低
* 3.传输速度低 速度高
* 4.适合传输数据量大的数据 数据量小的数据
* 5.必须建立连接,效率会稍低 每个数据包的大小限制在64K内
* 客户端:(app/浏览器)
* 服务器端:!=主机
*
* 端口号:同一台主机上的每一个服务器都拥有自己的端口号,取值范围(0,65535),常用的端口:80,8080
* 注意点:1.要保证客户端和服务器端的端口号一致 2.要保证同一台主机上的不同服务器端口号不同
*
* 先讲UDP:
* 注意点:在工作的时候,应该先将服务器端运行起来,处于一个监听的状态,再运行客户端
* 应用:
* UDP如QQ聊天,视频会议等
* TCP如下载等
*
* 客户端:
* 1.创建UDP通信的对象-socket对象:对应的类是DatagramSocket.(用于UDP数据的发送与接收)
* 2.数据的准备-封装包:DatagramPacket(数据包,包括相关的属性,数据)
* 3.发送数据,通过send方法
* 4.关闭socket对象
*/UDP
/*
//以下是基本概念 : Java UDP通信:DatagramSocket和DatagramPacket 来自于 https://blog.csdn.net/corozonut/article/details/71514509
UDP 是在IP网络上收发数据的传输层协议,其速度快但不可靠。为什么会使用这种不可靠的协议呢?许多应用保持最快的速度比保证每一位数据都正确更为重要,
例如实时音频或视频丢失数据只会作为干扰出现并且可以容忍;另外一些应用,利用UDP数据传输可靠性就需要在应用中进行控制。DNS、NFS、TFTP等都可以配置使用UDP协议。
我们都知道TCP是面向连接的,所谓连接就是在端点通信前建立起一条“虚电路”,利用逻辑标识来控制路由寻路,这个逻辑标识记录了目的IP、端口、虚电路标识等信息。
UDP是无需连接,每个传输单元就称为数据包,数据包上就记录了相应的路由信息,且前后传输的数据包可不相关,这就要求各数据包中记录路由信息。而TCP建立好虚电路后,
每个传输单元是不需要关心这些信息的。面向连接的TCP有诸多优点,如可靠性、拥塞可控等,但是其仅能用于端点间通信而不能用于广播或者组播通信,后两者还需要UDP发挥作用。
Java用两个类实现UDP:DatagramPacket和DatagramSocket,前者将数据字节填充到UDP包,后者收发UDP包。用法很简单,DatagramSocket收发DatagramPacket即可。
与TCP不同,UDP的socket并没有客户端和服务端的区别而统一应用此对象。
UDP向底层IP数据添加少部分内容:源端口、目的端口、IP数据部分长度和可选校验和,这些总共占用8字节。
端口、地址、数据长度和数据等信息都可以从DatagramPacket中提取或者向其设置,
以上UDPServer.java就是提取这些信息而获取客户端信息并作为回应地址的。UDP包中数据理论最大为65507(65535-20IP基本头-8UDP头),
但实际上几乎总是比这个少得多,在很多平台上往往限制在8KB,因此某些程序依赖于发送超过8KB数据的UDP包要多加小心,大多数情况下更大的包都会被简单地截取位8KB数据,
为保证最大的安全性,UDP数据部分尽量保持在512B以下。可以发现判断DatagramPacket是发数据还是收数据依据的是其构造差异。发数据在构造函数中需要指定目的Socket地址,而收数据并不需要(在生产环境中必须要考虑这边的安全策略)。
类似地,DatagramSocket在默认构造时常用于客户端——其并不关心某个本地端口,由系统指定即可;而指定端口的构造常用于服务端,意义不言而喻。
这边还有个UDP应用场景需要注意的问题,UDP服务器一般不会与客户端做太多的交互,通常可以在连接线程中直接响应客户端。不过如果需要做大量处理的话,可以创建处理线程来做此工作。
*/
//发送端 //* 1.创建UDP通信的对象-socket对象:对应的类是DatagramSocket.(用于UDP数据的发送与接收) DatagramSocket datagramSocket = new DatagramSocket(); //* 2.数据的准备-封装包:DatagramPacket(数据包,包括相关的属性,数据, 数据包像快递一样, 需要地址自己发送...) public DatagramPacket(byte buf[], int offset, int length, SocketAddress address); public DatagramPacket(byte buf[], int length, InetAddress address, int port); public DatagramPacket(byte buf[], int length, SocketAddress address); //* 3.发送数据, 通过send方法 datagramSocket.send(datagramPacket); //* 4.关闭socket对象 datagramSocket.close(); //侦听某个UDP端口 DatagramSocket datagramSocket = new DatagramSocket(port); // (因为是收数据包, 不需要指定自己的地址, 只需要接收就完事了) // 将数据读入到buf中, 使用getBytes()可以获取到buf数据 public DatagramPacket(byte buf[], int length); public DatagramPacket(byte buf[], int offset, int length); //调用receive(DatagramPacket)将数据从网络流接收到 packet 的 buf 数组中; receive是一个阻塞方法! 可以使用socket.setSoTimeout(ms);设置阻塞超时时间,超过时间未接收到视为数据丢失 datagramSocket.receive(packet); packet.getData(); //byte[] 获取buf数组
TCP
IP,TCP 和 UDP, DatagramSocket 与 DatagramPacket, TCP传输 https://blog.csdn.net/mp624183768/article/details/70892015
/*
* TCP: 建立连接后,通过Socket中的IO流进行数据的传输
* 客户端
* 在客户端与服务器端通信的时候,对于客户端既要进行输入又要进行输出,所以在Socket对象的内部就内置了输入流和输出流,
* 当进行数据传输的时候,将数据放入socke对象的内部,将socket对象传到服务器端,相当于在客户端与服务器端建立了一个通道,
* 两端使用同一个socket对象.即通道仅需要建立一次就可以
*/
客户端 //1.创建Socket对象并绑定服务器的地址和端口 Socket socket = new Socket(InetAddress:InetAddress.getLocalHost(), port:22000); //2.准备数据 String data = "BigData1712,你好"; //3.获取socket内部的输出流 OutputStream outputStream = socket.getOutputStream(); //4.将数据写入网络IO outputStream.write(data.getBytes()); //接收从服务器传回的数据 InputStream inputStream = socket.getInputStream(); byte[] arr = new byte[1023]; int num = inputStream.read(arr); //5.关闭资源 socket.close(); 服务端 //1.创建ServerSocket对象并绑定接口 ServerSocket serverSocket = new ServerSocket(port:22000); //2.接收套接字的连接,保证客户端与服务器端使用同一个socket对象---对端口一直监听 Socket socket = serverSocket.accept(); //3.获取输入流 InputStream inputStream = socket.getInputStream(); //4.将内容写到控制台 byte[] arr = new byte[1023]; int num = inputStream.read(arr); System.out.println(new String(arr,0,num)); //实现将服务器的数据写回客户端 OutputStream outputStream = socket.getOutputStream(); outputStream.write("你好,BigData1712".getBytes()); //5.关闭资源 serverSocket.close();
反射 反射的高级运用 http://www.infoq.com/cn/articles/cf-java-reflection-dynamic-proxy
/** * 反射 * 动态获取类的.class文件 * 并对其成员进行抽象 * 整体: 通过字节码文件直接创建对象 */ //以下仅仅是对Java反射的基础概念,和基本方法的运用 Class cc = Class.forName("java.lang.Integer"); //获取全部的变量(无视权限) Field[] fields = cc.getDeclaredFields(); //获取全部的方法(无视权限) Method[] ms = cc.getDeclaredMethods(); //获取构造函数~ int.class是该方法的参数类型的class对象 Constructor constructor = cc.getConstructor(int.class); //对Constructor 或Method 或 Field 的权限的修改改为setAccessible(true) constructor.setAccessible(true); //对获得的方法的调用 Object integer = constructor.newInstance(1010); Method m = cc.getMethod("valueOf", int.class); m = cc.getMethod("intValue"); System.out.println(m.invoke(integer)); //对获得的变量的调用 Field value = cc.getDeclaredField("value"); value.setAccessible(true); System.out.println(value.get(integer));