目录
泛型:
泛型定义:
Java 泛型是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
1、提高效率: 限定数据类型,只存某一种特定的类型,减少类型转化的时间
2、防止类型转换出现问题
泛型类
泛型类:把泛型定义在类上
public class 类名 <泛型类型1,...> {
}
定义泛型类,在类名后添加一对尖括号,并在尖括号中填写类型参数,参数可以有多个,多个参数使用逗号分隔
泛型方法
泛型方法概述:把泛型定义在方法上
public <泛型类型> 返回类型 方法名(泛型类型 变量名) {
}
泛型通配符
无边界的通配符:<?>, 比如List<?>
固定上边界的通配符,采用<? extends E>的形式
固定下边界的通配符,采用<? super E>的形式
泛型擦除
泛型信息只存在于代码编译阶段,但是在java的运行期(已经生成字节码文件后)与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。
所有泛型类的类型参数在编译时都会被擦除,虚拟机运行时中没有泛型,只有普通类和普通方法,从这一点上来说,Java中的泛型从某种程度上是一种语法糖
总结
1、泛型是什么?
本质:参数化的数据类型
将数据类型当作参数使用,定义的时候使用 类型占位符(T,E,K,V) ,实际使用时(创建对象时,调用方法时)传入具体的数据类型。
作用:编译期对数据类型进行检查约束。
2、如果想限定泛型的数据类型的范围怎么做?
上边界通配符: <? extends T>
下边界通配符:<? super T>
3、什么是泛型擦除?
泛型只在编译期进行类型检查(约束),生成的字节码中并不保留泛型的类型信息, 程序运行时会将泛型检查给抹去。
4、什么时候使用泛型?
定义是无法确定数据类型,使用时才能确定的
网络编程
网络编程三要素
1、IP地址
要想让网络中的计算机能够互相通信,必须为计算机指定一个标识号,通过这个标识号来指定要接受数据的计算机和识别发送的计算机,而IP地址就是这个标识号,也就是设备的标识。
2、端口:端口号就可以唯一标识设备中的应用程序,也就是应用程序的标识。
3、协议:常见的协议有TCP协议和UDP协议
UDP协议
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据
由于使用UDP协议消耗资源少,通信效率高,所以通常都会用于音频、视频和普通数据的传输
TCP协议
传输控制协议(Transmission Control Protocol)
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据出啊u念书。在TCP连接中必须明确客户端与服务器端,由于客户端向服务器端发出连接请求,每次连接的创建都需要经过“三次握手”
三次握手
TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠
第一次握手:客户端向服务器端发出连接请求,等待服务器确认
第二次握手:服务器端向客户端回送一个响应,通知客户端收到了连接请求
第三次握手:客户端再次向服务器端发送确认信息,确认连接
基于UDP的方式进行网络传输
-
UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象
-
基于UDP协议的通信双方而言,没有客户端和服务器概念
-
DatagramSocket:用于发送或接收数据包的套接字
-
DatagramPacket:数据包
用到类:
DatagramSocket类:发送和接受数据报
方法 | 类型 | 描述 |
---|---|---|
public DatagramSocket() | 构造方法 | 构造DatagramSocket对象,不指定监听的端口 |
public DatagramSocket(int port) | 构造方法 | 构造DatagramSocket对象,同时指定监听的端口 |
public void send (DatagramPacket p) | 方法 | 发送数据报 |
public void receive(DatagramPacket p) | 方法 | 接收数据报 |
DatagramPacket类:用来封装接受的发送的消息的。
方 法 | 类 型 | 描 述 |
---|---|---|
public DatagramPacket(byte[] buf,int length) | 构造方法 | 用来接收长度为 length 的数据包**,length 参数必须小于等于 buf.length** |
public DatagramPacket(byte[] buf,int length,InetAddress address,int port) | 构造方法 | 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号 |
public byte[] getData() | 方法 | 返回接收数据 |
public int getLength() | 方法 | 返回要发送或接收数据 的长度 |
public InetAddress getAddress() | 方法 | 返回机器的地址 |
发送数据的步骤
1、创建发送端的Socket对象(DatagramSocket)。如果没有指定端口,发送到本地主机所有可用端口(不常用),这里可以采用指定端口构造方法 DatagramSocket(),DatagramSocket(int port)|
2、创建数据,并把数据封装成DatagramPacket包裹,数据一定要转成字节数组,同时需要指定IP地址和端口 DatagramPacket(byte[] buf, int length, InetAddress address, int port)
3、调用DatagraSocket对象的方法发送数据包裹 void send(DatagramPacket p)
4、关闭发送端,释放资源
UDP接收数据 接收数据的步骤
1、创建接收端的Socket对象(DatagramSocket),指定端口 DatagramSocket(int port)
2、准备容器,封装成DatagramPacket包裹,用于接收数据 DatagramPacket(byte[] buf, int length)
3、调用DatagramSocket对象的方法,阻塞式接收包裹 void receive(DatagramPacket p)
4、解析数据包,并把数据在控制台显示 byte[] getData() 和 int getLength()
5、关闭接收端,释放资源
基于TCP协议进行网络传输
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信。
使用基于TCP协议的Socket网络编程实现,使用Socket对象来代表两端的通信端口
TCP协议基于请求-响应模式,第一次主动发起的程序被称为客户端(Client)程序
第一次通讯中等待连接的程序被称为服务器端(Sercer)程序
步骤
1、在服务端指定一个端口号来创建ServerSocket,并使用accept方法进行侦听,这将阻塞服务器线程,等待用户请求。
2、在客户端指定服务的主机IP和端口号来创建socket,并连接服务端ServerSocket,此时服务端accept方法被唤醒,同时返回一个和客户端通信的socket。
3、在客户端和服务端分别使用socket来获取网络通信输入/输出流,并按照一定的通信协议对socket进行读/写操作。
4、通信完成后,在客户端和服务端中分别关闭socket。
1、服务器端
-
创建ServerSocket(int port)对象
-
在Socket上使用accept方法监听客户端的连接请求
-
阻塞、等待连接的建立
-
接收并处理请求信息
-
将处理结果返回给客户端
-
关闭流和Socket对象
2、客户端
-
创建Socket(String host, int port)对象
-
向服务器发送连接请求
-
向服务器发送服务请求
-
接受服务结果(服务响应)
-
关闭流和Socket对象
总结
TCP与UDP的区别
TCP UDP 面向连接,可靠的数据传输 无连接的,不可靠的数据库传输 数据安全,有校验, 数据不安全,缺乏校验 速度慢 效率高
Lamdba表达式
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
Lambda表达式的使用场景:用以简化接口实现
何时使用
通过上面的两个需求,发现Lamdba表达式很简单,那何时使用呢
需要显示创建函数式接口对象的地方,都可以使用
函数式接口:
如果说,⼀个接口中,要求实现类必须实现的抽象方法,有且只有⼀个这样的接口,就是函数式接口。使用@FunctionalInterface注解标注。
函数式接口分类
a.系统定义函数接口(Comparator, Runnable)
b.用户自定义函数接口
最为核心的有四个接口:
a.功能性接口:Function<T, R>
有输入参数,有返回值
是对接收一个T类型参数,返回R类型的结果的方法的抽象
通过调用apply方法执行内容
需求:给定一个字符串,返回字符串长度
Function<String,Integer> function=(str)->str.length(); Integer apply = function.apply("zhangsan"); System.out.println(apply);
b.消费型接口:Consumer<T>
有输入参数,没返回值
对应的方法类型为接收一个参数,没有返回值
一般来说使用Consumer接口往往伴随着一些期望状态的改变
或者事件的发生,典型的forEach就是使用的Consumer接口
虽然没有任何的返回值,但是向控制台输出结果
Consumer 使用accept对参数执行行为
需求:输出字符串
Consumer<String> f1 = str -> System.out.println(str); f1.accept("hello");
c.供给型接口:Supplier<T>
无传入参数,有返回值
该接口对应的方法类型不接受参数,但是提供一个返回值
使用get()方法获得这个返回值
需求:生成一个随机数字
Supplier<Integer> f1 = () -> { Random random = new Random(); return random.nextInt(100); }; System.out.println(f1.get());
d.断言型接口:Predicate<T>
有传入参数,有返回值Boolean
该接口对应的方法为接收一个参数,返回一个Boolean类型值
多用于判断与过滤,使用test()方法执行这段行为
需求:输入字符串,判断长度是否大于0
变量作用域
lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
栗子1:对字符串数组按字符串长度排序
Lambda的优点
a.极大的减少代码冗余,同时可读性也好过冗长的匿名内部类
b.与集合类批处理操作结合,实现内部迭代,并充分利用现代多核CPU进行并行计算。之前集合类的迭代都是外部的,即客户代码。而内部迭代意味着由Java类库来进行迭代,而不是客户代码。
方法引用
1.概述
某些lambda表达式里面仅仅是调用了一个已存在的方法,在这种情况下
直接通过方法名称引用方法的形式可读性更高一些,这种形式就是方法引用
方法引用是一种更简洁易懂的lambda 表达式替换
其唯一用途就是支持Lambda的简写
函数接口中抽象方法的参数列表,必须与方法引用方法的参数列表保持一致
函数引用:引用⼀个已经存在的方法,使其替代lambda表达式完成接口的实现
2.类静态方法引用
语法:类::静态方法
范例:Integer::parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据。
实例方法引用
引用对象的实例方法,其实就引用类中的成员方法
格式:对象::成员方法
范例: "HelloWorld"::toUpperCase
Stream流
Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。
Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势.
Stream操作分类
Stream的操作可以分为两大类:中间操作、终结操作
中间操作可分为:
-
无状态(Stateless)操作:指元素的处理不受之前元素的影响
-
有状态(Stateful)操作:指该操作只有拿到所有元素之后才能继续下去
终结操作可分为:
-
短路(Short-circuiting)操作:指遇到某些符合条件的元素就可以得到最终结果
-
非短路(Unshort-circuiting)操作:指必须处理完所有元素才能得到最终结果。
-
Stream 构成与创建
流的构成
当我们使用一个流的时候,通常包括三个基本步骤:
获取一个数据源(source)→ 数据转换 → 执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。
-
操作符
中间操作符
通常对于Stream的中间操作,可以视为是源的查询,并且是懒惰式的设计,对于源数据进行的计算只有在需要时才会被执行,这类操作都是惰性化的,仅仅调用到这类方法,并没有真正开始流的遍历,真正的遍历需等到终端操作时。
-
流方法 含义 filter 用于通过设置的条件过滤出元素 map 接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”) distinct 返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流 sorted 返回排序后的流 limit 会返回一个不超过给定长度的流 skip 返回一个扔掉了前n个元素的流 flatMap 使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Arrays::stream)时生成的单个流都被合并起来,即扁平化为一个流 peek 对元素进行遍历处理 终端操作符
Stream流执行完终端操作之后,无法再执行其他动作,否则会报状态异常,提示该流已经被执行操作或者被关闭,想要再次执行操作必须重新创建Stream流
一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流
终端操作的执行,才会真正开始流的遍历
流方法 含义 collect 收集器,将流转换为其他形式 forEach 遍历流 findFirst 返回第一个元素 findAny 将返回当前流中的任意元素 count 返回流中元素总数 max 最大值 min 最小值 anyMatch 检查是否至少匹配一个元素,返回boolean allMatch 检查是否匹配所有元素,返回boolean noneMatch 检查是否没有匹配所有元素,返回boolean reduce 可以将流中元素反复结合起来,得到一个值