• JavaSE 第二次学习随笔(五)


    /*
    * 中文乱码出现的情况研究
    * 注意点:乱码解决的办法是再编码再解码
    * 但是如果是编码出错了,无法解决.如果是解码出错了,可以利用再编码再解码
    *
    *
    * 编码 解码 结果
    * 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));
    

      

  • 相关阅读:
    Ubuntu下安装了java但启动eclipse报错说没装java
    Servlet之Filter详解
    使用mybatis-generator自动生成model、dao、mapping文件
    深入浅出MyBatis
    彻底理解字符编码
    Java多线程系列
    【Swagger2】解决swagger文档地址请求404的问题
    【Git】Git如何合并某一次commit的内容到指定分支
    【Iterm2】如何解决iterm2窗口自动隐藏的问题
    【Git】.DS_Store 是什么文件
  • 原文地址:https://www.cnblogs.com/chinashenkai/p/9538338.html
Copyright © 2020-2023  润新知