欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaSE 第二次学习随笔(五)

程序员文章站 2022-05-25 15:43:43
/* * 中文乱码出现的情况研究 * 注意点:乱码解决的办法是再编码再解码 * 但是如果是编码出错了,无法解决.如果是解码出错了,可以利用再编码再解码 * * * 编码 解码 结果 * GBK utf8 不可以(GBK2个字节,utf83个字节) * GBK ISO8859-1 可以 * utf8 ......

 

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