Java笔记:网络编程,TCP通信协议;函数式接口,函数式编程,stream流;单元测试,注解,反射

parter1 网络编程

1.网络编程入门

软件结构:client/serve browser/serve

网络编程:一定协议下,两台计算机通信。

网络四层结构:物理层/数据链路层 --> 网络层 --> 传输层 --> 应用层

网络通信分类:

  • UDP协议:无连接,不能保证数据完整,耗资小,视频会议。 数据限制在64kb以内,
  • TCP协议:面向连接,先建立链接,再传输数据。三次握手。保证数据安全,文件下载上传,网页浏览。

网络编程三要素:

  1. 协议

  2. IP地址

    cmd --> ipconfig   ip配置信息
        ping ip地址     ping通可以互发信息;ping失败,不可以
        ping 127.0.0.1 	本地ip地址  
        ping localhost	本地服务器ip地址
    
  3. 端口号

    网络软件一打开,操作系统回味网络软件分配一个随机的端口号
        端口号两个字节组成:0-65535
        1024之前的端口号不能使用,已经被系统分配给已知软件
        网络软件端口号不能重复使用
        
    常用端口号:
        1. 80端口,网络端口
        2. 数据库端口号,mysql:3306   oracle:15213. Tomcat服务器:8080
    

2. TCP通信程序

客户端:		ip:端口号    Socket类
    ① OutputStream:发送“你好服务器”	④InputStream
    连接通路:IO流对象
    
    
服务器端:		ip:端口号   ServeSocket类
    ②InputStream:读取 “你好服务器”    ③outputStream:回复
       (c/s交互一次需要四个流对象)
        
实际情况:
        1.多个客户端和服务器进行交互,服务器必须明确和哪个客户端进行交互
        	在服务器端有一个方法,叫accept客户端获取到请求的客户端对象。
        2.多个客户端同时和服务器进行交互,就需要使用多个IO流对象
        	服务器是没有IO流的,服务器可以获取到请求的客户端对象Socket
        	使用每个客户端Socket提供的IO流和客户端进行交互。
  Socket s1 = serve.accept();				Socket s2 = serve.accept();
		一言概之:服务器使用客户端的流和客户端交互。
  1. socket

    TCP通信客户端:向服务器发送连接请求,给服务器发送数据,读取写回数据。
        表示客户端的类:Java.net.Socket,此类实现客户端“套接字”。套接字是两台机器间通信的端点。
        			套接字:包含了IP地址和端口号的网络单位。
        构造方法:
        	Socket(String host, int port):服务器主机名称/服务器的IP地址		服务器的端口号
        成员方法:
        	OutputStream getOutputStream():返回此套接字的输出流。
        	InputStream getInputStream():返回此套接字的输入流。
        	void close();关闭此套接字。
        使用步骤:
        	1.创建一个客户端对象Socket,构造方法中绑定服务器的IP地址和端口号。
        	2.使用Socket对象的方法getOutputStream获取网络字节输出流对象OutputStream。
        	3.使用网络字节输出流OuStream中的方法write,给服务器发送数据。
        	4.使用Socket对象的方法getInputStream()获取网络字节输入流InputStream对象
        	5.使用网络字节输入流的方法read,读取服务器写回的数据。
        	6.释放资源(socket)
        注意事项:
        	1.客户端和服务器端进行交互必须使用socket中提供的网络流,不能使用自己创建的流对象。
        	2.创建客户端对象Socket的时候,就回去请求服务器,进行三次握手,建立连接通路。
        		如果服务器没有启动:会抛出异常。
        		如果服务器启动了:可以正常建立连接。
    
    
    TCP服务器端:接收客户端请求,读取客户端数据,回写给客户端数据。
        表示服务器的类:java.net.ServeSocket 此类实现服务器套接字。
        构造方法:
     	ServeSocket(int port) 创建绑定到特定端口的服务器套接字。  指定端口号。
        	服务器端必须明确,是那个客户端请求的服务器,所以可以用accept方法获取到客户端。
        成员方法:
        	Socket accept()  展厅并接受到此套接字的连接。
        服务器实现步骤:
        	1.创建服务器对象 ServeSocket,和系统要指定端口号。
        	2.使用ServeSocket对象中的方法accept,获取到请求的客户端对象Socket。
        	3.使用Socket对象的方法getInputStream获取网络字节输入流对象InputStream。
        	3.使用网络字节输出流InputStream中的方法read,读取客户端发送的数据。
        	4.使用Socket对象的方法getOutputStream()获取网络字节输出流OutputStream对象
        	5.使用网络字节输出流的方法write,服务器向客户端写回数据。
        	6.释放资源(Socket, ServeSocket)
    

3. 综合案例

TCP通信文件上传原理

读取本地文件上传到服务器,服务器把上传的文件保存到服务器的硬盘上。

    1. 客户端使用本地字节输入流,读取要上传的文件。

    2. 客户端使用网络字节输出流,把读取到的文件上传到服务器上。

    3. 服务器使用网络字节输入流,读取客户端上传的文件

    4. 服务器使用本地字节输出流,把读取到的文件保存到服务器硬盘上,

    5. 服务器使用网络字节输入流,给客户端写一个“上传成功”。

    6. 客户端使用网络字节输入流,读取服务器写回的数据。

    7. 释放资源。

      注意:客户端和服务器对本地硬盘读写,需要使用自己创建的字节流对象(本地流)。

      客户端和服务器之间进行读写,必须使用Socket中提供的字节流对象(网络流)。

      文件上传即文件复制:需要明确 : 数据源+数据目的地。

  • 模拟BS服务器

    创建BS版本TCP服务器

    1. 创建一个服务器对象ServeSocket,和系统要指定的端口号。
    2. 使用accept方法获取到请求的客户端对象(浏览器),获取scoket。
    3. 使用socket对象中大的方法getInputStream对象中的方法read读取客户端请求信息。
    4. 使用网络字节流InputStream对象中的方法read读取客户端的请求信息。

parter2 函数式接口

1. 函数式接口

  1. 概念:有且只有一个抽象方法的接口。函数式接口就是适用于函数式编程场景的接口。

    Java中函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。

    只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利进行推导。 但是可以包含其他的(默认,静态,私有)方法。

  2. 格式

    修饰符 interface 接口名称{
           
        public abstract 返回值类型 方法名称;//抽象方法的 public abstract可以省略
            //其他非抽象方法内容
    }
    
  3. @FunctionalInterface注解

    @FunctionalInterface用来检测是否是一个函数式接口。

  4. 自定义函数式接口

    函数式接口使用:一般可以作为方法的参数和返回值类型。

    • 可以作为方法的传递参数。(可以使用匿名内部类方式;可以使用Lambdda表达式)
    • 做传递参数时,传递的是接口,不是实现类!!!
    • Lambda不是匿名内部类的语法糖,二者原理不一样。Lambda效率更高!

2. 函数式编程

2.1 Lambda的延迟执行

  1. 浪费的日志案例。(有些代码不会执行,会产生性能浪费。)

  2. 体验lambda的更优写法。

    lambda特点:延迟加载。

    使用前提:必须存在函数式接口。

    简言之:只有满足条件了,才会执行Lambda表达式方法体内容!

  3. 证明lambda的延迟

2.2使用Lambda作为参数和返回值

  1. 使用lambda表达式作为方法的参数。要求参数是函数式接口,lambda在此负责实现接口方法。(可以使用匿名内部类)
  2. 函数式接口做方法的返回值类型。

3. 常用函数式接口

jdk提供的函数式接口都放在java.util.function包中

3.1 Supplier接口

java.util.function.Supplier<T>		接口仅包含一个无参的方法,:T get() .用来获取一个泛型参数指定类型的对象数据。
Supplier<T> 接口称之为生产型接口,指定接口泛型。特有方法:T get();方法产生什么类型数据。
    使用lambda表达式。
求int数组最大值。
    在函数式接口中写出主要函数体。

3.2Consumer接口

java.util.function.Consumer<T>   接口与supplier接口相反,消费一个数据。
特有抽象方法:void accept(T t)	消费一个指定类型的数据。
    使用:使用lambda方式实现主要函数体。
默认方法:andThen
    作用:需要两个consumer接口,可以把两个consumer接口组合到一起,在对数据进行消费。
    例如:consumer<String> con1;  consumer<String> con1;  String s = "hello";
		con1.accept(s);			con2.accept(s);
		可以使用lambda表达式方法来写,写lambda表达式时需要写两个。。
	使用andThen方法:  con1.andThen(con2).acccept(s);
		谁写前面,谁先消费。可以使用lambda表达式方法来写,写lambda表达式时需要写两个。

练习:格式化打印信息。

​ 将字符串数组的信息分隔开,再拼接打出。

原始数组信息  string[] arr = {"古力娜扎,25","迪丽热巴,23","马儿扎哈,29"};
分割以后按照格式 姓名:name;年龄:age. 打印输出。
【注意区分原始数组中的 逗号 是中文输入还是英文输入。】

3.3Predicate接口

java.util.function.Predicate<T>接口。  用来对数据类型进行判断,得到一个boolean值。
抽象方法:boolean test(T t);用来对指定数据类型及逆行判断。真truefalse。
	对传递参数进行判断。
默认方法:and
    表示并且关系,也可以用于连接两个判断条件。&&等价
默认方法:or
    表示或关系。等价||。
默认方法:negate
    表示取反关系。等价!。

练习:集合信息筛选。

数组中有多条“姓名+性别”信息,通过predicator接口的拼装条件将符合要求的字符串选到集合AeeayList中,需要同时满足两个条件:必须为女生;姓名为四个字。

4 Function接口

java.util.function.Function<T,R> 接口用来根据一个类型的数据的到另一个类型的数据,
    前者称为前置条件,后者称为后置条件。
抽象方法:R apply(T t)
    根据类型T的参数获取类型R的结果。
    使用场景:例如将String类型转换为Integer类型。
 使用步骤:
    1.定义一个方法,方法参数传递一个字符串类型的整数;
    2.方法参数传递一个Function接口,泛型使用<String,Integer>
    3.使用Function接口中的方法apply,把字符串类型的整数转换为Integer类型的整数。
    
默认方法:andThen
    用来进行组合操作。
 	案例要求:把string类型转换为Integer类型,转换后的结果+10;	把增加之后的数据转换为String类型。
	第一次:string->Integer
    第二次:Integer->String
     在这里就可以使用andThen方法,把两次转换组合在一起使用。fun1.apply完成第一次转换;fun2.apply完成第二次转换。

练习:自定义函数模型拼接。

使用Function进行函数模型的拼接,按照顺序需要执行的多个函数操作为:

String str = “古丽娜,20”;

  1. 将字符串截取数字年龄部分,得到字符串。
  2. 将上不一步的字符串转换为int类型的数字。
  3. 将上一步的int数字+10,转换为字符串。

parter 3 stream流

  • IO流主要用来读写,Stream流主要对集合和数组进行简化操作。
  • 用stream流中的方法对集合和数组进行操作,用来解决集合和数组中现有的一些弊端。
  • 遍历的弊端:循环不是遍历的唯一方法。

1 流思想

  • jdk1.8之后出现的

    使用stream流,遍历集合,对集合中的数据进行过滤。

    list.stream().filter(predicate).filter().....forEach(lambda表达式);
    
    • 过滤 --> 映射 --> 跳过 —> 计数 -->

    • 先建立模型,等到所有步骤解析完以后才会执行;都得益于lambda表达式的延迟执行。Java中的sttream不会存储元素,而是按需计算。

    • 数据的来源:数组,集合。

  • 与collection操作不同,Stream操作还有两个基本特征:

    • Pipelining:中间操作都返回流本身;构成管道。
    • 内部地迭代:增强for外部迭代,Stream提供了内部迭代的方法。
  • 使用流时通常包括三个步骤:获取一个操作数据源 → 数据转换 → 执行操作获取想要的结果,每次原有的stream对象不变,返回一个新的stream对象(可以有多次转换),流就像链条一样,变成一个管道。

2 获取流

java.util.stream.Stream<T>时java8新加入的常用的个流接口。(这不是一个函数式接口,有许多抽象方法。)

获取一个流的方式:

  1. 所有的collection集合都可以通过Stream默认方法获取流。

    只有collection(list  set)集合中有此方法,map集合中没有。
    stream<E>  stream();
    Stream stream1 = list.stream();
    Stream stream2 = set.stream();
    
    Set keySet = map.keySet();		//对于map集合,先获取key值的set集合,再获取流
    Stream stream3 = keySet.stream();
    Collection<String> values = map.values();	//对于map集合,先获取values值的collection集合,再获取流
    Stream stream4 = values.stream();
    Set<Map.Entry<String,String>> entries = map.entrySet();	//获取键值对(鉴于值得映射关系 entrySet)
    Stream<Map.Entry<String,String>> stream5 = entries.stream();	 
    
    
  2. stream接口的静态方法of可以获取对应数组的流。

    //把数组转换为流
    Stream<Integer> stram1 = Stream.of(1,2,3,4,5);
    Integer[] arr = {
           1,2,3,4,5};
    Stream<Integer> stram2 = Stream.of(arr);
    

3.常用方法

  • 延迟方法:返回值类型依旧是本身,支持链式调用。

  • 终结方法:返回值类型不再是本身,不在支持链式调用。本小节中包括方法countforEach

  • 属于管道流,只能被使用一次。第一个stream调用完毕即关闭,数据就会流转到下一个流。

    1. 逐一处理:forEach
    	void forEach(Consumer<T> con)
        方法接受一个Consumer接口函数,会将每一个流元素交给该函数进行处理。
        consumer接口是一个消费型的函数式接口,可以传递lambda表达式,消费数据。
        概言之:用来遍历流中数据,是一个终结方法,遍历之后不能继续调用stream流中的其他方法。
        
    2. 过滤:filter
        Stream filter(Predicate<T> pre);
    	predicate用于判断的接口,有一个方法boolean test(T t);
    3. 映射:map
    	将流中的元素映射到另外一个流中,需要使用map映射方法
        Stream map(Function<T> fun);  F接口中有一个方法:R apply(T t).
        这种转换就称之为映射。
    4. 统计个数:count
    	如同collection集合当中的size方法一样,流提供count方法来数一数其中的元素个数。
        long count();	终结方法,不能再调用其他stream方法。
    5. 取用前几个:limit
        limit方法对流进行截取,只截取前几个。延迟方法,只是对流中元素进行截取,返回新流,可以继续调用其他方法。
        集合当前长度较小时不进行操作。
        Stream limit(long MaxSize);
    6. 跳过前几个:skip
        Stream skip(long n);	//跳过前n个,从第n+1个元素开始。
    7. 组合:concat
        把两个流组合为一个流。
        Stream concat(Stream s1,Stream s2)
        返回一个新流,原来两个流的拼接。
    

4 练习:集合元素处理

题目:现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤︰
1.第一个队伍只要名字为3个字的成员姓名﹔存储到一个新集合中。
2.第一个队伍筛选之后只要前3个人;存储到一个新集合中。
3.第二个队伍只要姓张的成员姓名;存储到一个新集合中。
4.第二个队伍筛选之后不要前2个人;存储到一个新集合中。
5.将两个队伍合并为一个队伍;存储到一个新集合中。
6.根据姓名创建Person对象﹔存储到一个新集合中。
7.打印整个队伍的Person对象信息。

parter4 第十章方法引用

  1. 冗余的 lambda

  2. 方法引用改进代码

  3. 方法引用符:::是引用运算符,它所在的表达式称为方法引用。如果lambda表达式要表达的函数已经存在某个方法的视线中,那么可以通过引用双冒号来引用该方法作为lambda的替代者。

    lanbda表达式写法:s -> System.out.println(s);
    	语义:拿到参数经lambda之手,继而传递给System.out.println方法去处理。
    方法引用写法:System.out::println;
    	语义:直接让system.out类中的println方法来取代Lambda。
    【注意】:lambda中传递的参数一定是方法引用中那个方法可以接受的类型,否则会抛异常,       
    
  4. 通过对象名引用成员方法

    使用前提:对象名是已经存在的,成员方法也是已经存在的。就可以使用对象名来引用成员方法。

  5. 通过类名称引用静态成员方法。

    使用前提:类已经存在;静态成员方法也已经存在。就可以使用类名称引用静态成员方法。

  6. 通过super引用成员方法。

    存在继承关系,当lambda表达式中需要super调用时,也可以使用方法引用进行替代。

    使用super引用父类的成员方法。

    super已经存在(子父类继承关系存在);父类成员方法已经存在。

  7. 通过this引用成员方法。

    this已经存在;本类的成员方法已经存在。

    this::本类成员方法。

  8. 类的构造器使用。

    构造方法的引用。

    构造器名称与类名称完全一样,并不固定。构造器的引用使用类名称::new的格式表示。

    构造方法已知;创建对象已知 new。就可以使用person引用new创建对象。

  9. 数组的构造器使用。

    数组的长度已知;已知创建的数组类型;

int[]::new方法引用。

parter5 基础加强

1. 单元测试Junit

  • 测试分类:

    • 黑盒测试:input --> 黑盒子 -->output。不需要写代码,给输入值,观察输出值。
    • 白盒测试:input --> 白盒子 -->output。需要写代码,关注具体写流程。
  • Junit白盒测试

    使用步骤:

    1. 定义一个测试类。(测试用例)

      这个类所在的包和被测试程序包平行;

      测试类名:xxxxTest

      测试包名:xx.xx.test

    2. 定义测试方法,可以独立运行。

      方法名:test测试的方法名 testAdd

      返回值:void

      参数列表:空参。

    3. 给方法前加上@Test。

    4. 导入Junit环境。

    5. 判定结果:

      Assert.assertEquals(期望结果,实际结果); 红色失败;绿色成功。

  • @Before

    一般用于资源申请。

    所有方法执行前都会执行此方法。一般命名public void init(){...}

  • @After

    一般用于资源释放。

    所有方法执行后比执行此方法。一般命名public void close(){...}

2.反射

  • 框架:半成品软件。在框架基础上开发软件,简化代码。
  • 反射:将类的各个组成部分封装成为其他对象,这就是反射机制。
    • 好处:可以在程序运行中操作这些对象。
    • 可以解耦,提高程序可扩展性。
Java程序的三个阶段:
    Source 源代码阶段					Class 类对象阶段							Runtime 运行时阶段
获取class对象的三种方式:
    1.class.forName("全类名"):第一个阶段。将字节码文件加载进内,返回class对象。
    2.类名.class:通过类名的属性class获取。
    3.对象.getClass():getClass()方法在Object类中定义。所有的额对象都可以调用此方法。
class对象功能:
     1.获取功能
        获取成员变量们 
        	Field getField(String name)  //只能获取 指定名称的public修饰的成员变量
        	Field[] getFields()  		//只能获取public修饰的成员变量
        	
        	Field getDeclaredField(String name) //	获取指定名称的成员变量,不考虑修饰符
        	Field[] getDeclaredFields()  	//	获取所有的成员变量,不考虑修饰符
        
            获取到成员变量以后,成员变量可以进行的操作有
                    set()	//设置成员变量的值
                    get()	//获取成员变量的值
        			setAccessible(true)	//忽略访问权限修饰符的安全检查。暴力反射。
        			//【所有的获取方法中都包含暴力反射方法】暴力反射方法一般都跟随declared方法后面使用,会涉及到安全修饰符问题。
        获取构造方法们	 
        	Constructor<T> getConstructor(Class<?>... parameterTypes) 
        	Constructor<?>[] getConstructors()
        
        	Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)  
        	Constructor<?>[] getDeclaredConstructors()  
        
        	获取到构造方法后,可以创建对象:
        		创建对象: T newInstance(Object... initargs)
        		空参构造时,可以直接调用personClass.Instance()。得到一个空参构造。空参构造都是用这种方法。
        		setAccessible(true)	//也包含暴力发射方法。
        
        获取成员方法们
         	Method getMethod(String name, Class<?>... parameterTypes) //	获取指定名称和参数的方法
 			Method[] getMethods()  //获取所有方法,包括object类中的方法

        	Method getDeclaredMethod(String name, Class<?>... parameterTypes)
			Method[] getDeclaredMethods() 
        
        	获取到方法以后:一件事,执行方法。
        		方法名.invoke(类名);执行方法。

        获取类名
        	String getName()  
        	Package getPackage()  

反射案例:

需求:写一个“框架”,可以帮我们创建任意类的对象,并且执行其中任意方法。
    实现:1.配置文件	2.反射
    步骤:
    	1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中。
    	2.在程序中加载读取配置文件。
    	3.使用反射技术来加载类文件进内存。
    	4.创建对象。
    	5.执行方法。

3.注解

  • 注释:三种注释,单行,多行和文档。用文字描述程序,方便阅读。

  • 注解:说明程序,给计算机看的。Annotation,元数据。jdk1.5之后引入的。

    与类,接口,枚举在同一个层次。它可以声明在包,类,字段,方法,局部变量,方法参数等前面,用来对这些元素进行说明,注释。

    使用注解:@注解名称。

  • 注解的三种功能

    1. 编写文档:通过代码里的元数据[注解]生成文档【生成doc文档】。
    2. 代码分析:通过代码里标识的元数据[注解]对代码进行分析【使用反射】。
    3. 编译检查:通过代码里标识的元数据[注解]让编译器能够实现基本的额编译检查【override】。
  • 主要学习:

    • JDK预定义的一些注解
    • 自定义注解
    • 在程序中使用(解析)注解

你可能感兴趣的:(反射,函数式编程,lambda,stream)