ctrl+shift+"-"减少字号
”+“,增大字号
学习笔记五、网络
1、网络编程
计算机网络:是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,再网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程:再网络通信协议下,实现网络互联的不同计算机上运行的程序之间可以进行数据交换。
网络变成三要素:
IP地址:要想让网络中的计算机能够互相通信,必须为了每一台计算机指定一个标识号,通过这个标识号来只当要接受数据的计算机和识别发送的计算机,而IP地址就是这个表示号。也就是设备的标识。
端口:网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信的时候,如何区分这些应用程序呢》如果说IP地址可以唯一的表示网络中的设备,那么端口好就可以唯一i傲视设备中的应用程序了,也就是应用程序的表示
协议:通过计算机网络可以是多台计算机是心啊链接,位于同一个网络的计算机在进行连接和通信的时候需要遵守一定的规则,这就好比在道路上形式中的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则北城为网络同i性能协议,他对数据的传输格式,传输速率,传输步骤等做了同意规定,通信双方必须同时遵守才能王城数据交换。常见的协议有UDP协议和TCP下而已
1.1 IP地址
是网络中设备的唯一标识
IP地址分为两大类:
IPV4:是给每个连接在网络的主机分配一个32bit地址。按照TCP/IP规定,IP地址用二进制来表示,每个IP地址长32bit,也就是4个字节。193.168.1.66,点分十进制表示法。
IPV6:通过这个重新定义地址空间,采用128位地址长度,妹16个字节一组,分为8组16进制数。
ipconfig:查看本机地址
ping IP地址:检查网络是否联通
特殊的IP地址:是回送地址,可以代表本机地址,一般用来测试。
1.2 InetAddress的使用
为了方便我们对于IP地址的获取和使用
InetAddress:此类表示internet协议(IP)地址
static InetAddress getByName(String host):确定主机名称的IP地址,主机名称可以是及其名称,也可以是IP地址。
String getHostName():获取此IP地址的主机名
String getHostAddress():返回文本显示中的IP地址字符串
InetAddress address = InetAddress.getByName("kk"); String name = address.getHostName(); String ip = address.getHostAddress(); System.out.println(name+ip);
1.3 协议
协议:计算机网络中,连接和通信的规则被称为网络通信协议
UDP协议:
用户数据报协议
UDP是无连接通信协议,即在数据传输的时候,数据的发送端和接收端不建立逻辑连接,简单来说,当一台计算机向另外一台计算机发送数据的时候,发送端不会确认接受端是否存在,就会发出数据,同样接收端在收到数据的时候,也不会向发送端反馈是否受到数据。
由于使用UDP协议协议小号资源小,通信效率高,所以通常都会用于阴癖、视频和普通数据传输。
例如视频会议通常采用UDP协议,偶尔丢点包不影响,重要数据要TCP协议。
TCP协议
传输控制协议
TCP协议是面向连接的通信协议,也就是传输数据之前,在发送端和接收端建立逻辑连接,然后在传输数据,它提供了两台计算机之间可靠无差错饿数据传输。在tcp连接中必须要明确客户端和服务器端,由客户端向服务器发出连接请求,每次连接的创建都需要经过“三次握手”。
三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证链接的可靠。
第一次握手,客户端向服务器端发出连接请求,等待服务器确认
第二次握手,服务器向客户端回送一个响应,通知客户端收到了连接请求
第三次握手,客户端再次向服务器发送确认信息,确认链接
完成三次握手之后,连接建立,客户端和服务器就可以开始进行数据传输了,由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如上传文件,下载文件,浏览网页。
1.4 端口
端口:设备上应用程序的唯一表示
端口号:用两个字节表示的整数,它的去支付那我i是0-65535.其中,0-1023之间的端口好用于一些知名的网络服务和用用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或者应用所占用,会导致当前程序启动失败。
1.5 UDP通信程序
UDP是不可靠的,没有所谓的客户端和服务器的概念
JAVA提供了DatagramSocker类作为基于UDP协议的SOCket
UDP发送数据
void sen(DatagramPacket p)
public static void main(String[] args) throws IOException { // InetAddress addresss = InetAddress.getByName("LAPTOP-9DE5LJGC");//192.168.1.109 // String ip = addresss.getHostAddress(); DatagramSocket ds = new DatagramSocket(); //创建数据并把数据打包 //DatagramPacket(byte[] buf , int length , InetAddress address , int port) //构建一个数据包,发soon给长度length的数据包到指定主机 byte[] bys = "hello".getBytes(); DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("192.168.1.109"),10086); //调用对象发送数据 ds.send(dp); //关闭 ds.close(); }
UDP接受数据
void receive(DatagramPacket p)
public static void main(String[] args) throws IOException { //带参构造方法,因为发送数据的端口号已经制指定了 DatagramSocket rs = new DatagramSocket(10086); //创建一个数据包,用于接收数据 //DatagramPacket (byte【】 buf , int length)构造一个DatagramPacket用于接受长度为length数据包 byte[] by = new byte[1024]; DatagramPacket rp = new DatagramPacket(by,by.length); //调用DatagramSocket对象接受数据 rs.receive(rp); //解析数据包,并把数据在控制台上显示 //byte【】 getData()返回数据缓冲区 byte[] datas = rp.getData(); int len = rp.getLength();//实际接收到的数据长度 String dataString = new String(datas,0,len); System.out.println("数据是"+dataString); //关闭 rs.close(); }
UDP练习
需求:UDP发送数据:数据来自于键盘录入,知道输入的数据是886,发送数据结束
UDP接受数据:因为接收端不知道发送端什么时候停止发送,所以采用死循环接收
public class senddemo { public static void main(String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(); //Scanner sc = new Scanner(System.in); //String input = sc.nextLine(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line; while((line= br.readLine())!=null) { if ("886".equals(line)) { break; } byte[] bys = line.getBytes(); DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.1.109"), 10086); ds.send(dp); } ds.close(); } }
public class senddemo { public static void main(String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(); //Scanner sc = new Scanner(System.in); //String input = sc.nextLine(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line; while((line= br.readLine())!=null) { if ("886".equals(line)) { break; } byte[] bys = line.getBytes(); DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.1.109"), 10086); ds.send(dp); } ds.close(); } }
1.6 TCP通信
TCP通信原理:TCP通信是一种可靠的网络协议,它在通信两端各建立一个Socket对象,从而在通信两端形成网络虚拟链路,一旦建立了虚拟的完了过链路,两端的程序既可以通过虚拟链路及逆行通信
JAva对基于TCp协议的昂罗提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。
Java为客户端提供了Socket类,为服务器提供了ServerSocket类
TCp发送数据
public static void main(String[] args) throws IOException { // Socket s = new Socket(InetAddress.getByAddress("192.168.1.109",10000)); //上面与下面等价 Socket s = new Socket("192.168.1.109",10000); //获取输出流,写数据 OutputStream os =s.getOutputStream(); os.write("hello,nihao".getBytes()); s.close(); }
TCP接收数据
public static void main(String[] args) throws IOException { //创建服务器对象 ServerSocket ss =new ServerSocket(10000); //Socket accept() 侦听要连接到此套接字并接受它 Socket s = ss.accept(); //获取输入流 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String data = new String(bys,0,len); System.out.println("独到的数据:"+data); ss.close(); }
练习1:TCP
客户端:发送数据,接受服务器反馈
服务器:接受数据,给出反馈
//客户端 public class senfer { public static void main(String[] args) throws IOException { Socket s = new Socket("192.168.1.109",10000); OutputStream os = s.getOutputStream(); os.write("你好,给你发送一段数据:".getBytes()); //接受服务器反馈 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String data = new String(bys,0,len); System.out.println("客户端:"+data); os.close(); } }
//服务器 public class renfer { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(10000); Socket s = ss.accept(); InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String data = new String(bys,0,len); System.out.println("服务器:"+data); //给出反馈 OutputStream os = s.getOutputStream(); os.write("数据已经收到".getBytes()); ss.close(); } }
练习2:TCP
客户端:数据来自于键盘录入,知道输入的数据是886,发送数据结束。
服务器:接受到的数据在控制太输出
public class s2 { public static void main(String[] args) throws IOException { //客户端 Socket s = new Socket("192.168.1.109",10000); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while((line=br.readLine())!=null){ if(line.equals("886")){ break; } bw.write(line); bw.newLine(); bw.flush(); // OutputStream os = s.getOutputStream(); // os.write(line.getBytes()); } s.close(); } }
public class r2 { public static void main(String[] args) throws IOException { //服务器 ServerSocket ss = new ServerSocket(10000); Socket s = ss.accept(); InputStream is =s.getInputStream(); // byte[] bys = new byte[1024]; // int len = is.read(bys); // String data = new String(bys,0,len); // System.out.println(data); BufferedReader br =new BufferedReader(new InputStreamReader(s.getInputStream())); String line; while((line=br.readLine())!=null){ System.out.println(line); } ss.close(); } }
练习3:TCP
客户端:数据了来自于键盘录入,知道数据的数据是886,发送数据结束。
服务器:接受的数据写入文本文件
public class s2 { public static void main(String[] args) throws IOException { //客户端 Socket s = new Socket("192.168.1.109",10000); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while((line=br.readLine())!=null){ if(line.equals("886")){ break; } bw.write(line); bw.newLine(); bw.flush(); // OutputStream os = s.getOutputStream(); // os.write(line.getBytes()); } s.close(); } }
public class r3 { public static void main(String[] args) throws IOException { //e3,服务器 ServerSocket ss = new ServerSocket(10000); Socket s = ss.accept(); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\java\\a1\\xiancheng\\src\\com\\tcpex2\\c.txt")); String line; while((line= br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); } s.close(); } }
练习4:TCP
客户端:数据来自于文本文件
服务器:接受到的数据写入文本文件
public class s4 { public static void main(String[] args) throws IOException { //客户端 Socket s = new Socket("192.168.1.109",10000); BufferedReader br = new BufferedReader(new FileReader("D:\\java\\a1\\xiancheng\\src\\com\\tcpex2\\c.txt")); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while((line=br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); } s.close(); br.close(); } }
public class r3 { public static void main(String[] args) throws IOException { //e3,服务器 ServerSocket ss = new ServerSocket(10000); Socket s = ss.accept(); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\java\\a1\\xiancheng\\src\\com\\tcpex2\\copy.txt")); String line; while((line= br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); } ss.close(); } }
练习5:TCP
客户端:数据来自于文本文件,接受服务器反馈
服务器:接受到的数据写入文本文件,给出反馈
public void shutdownOutput()方法,推荐
不推荐自定义
public class s5 { public static void main(String[] args) throws IOException { //客户端 Socket s = new Socket("192.168.1.109",10000); BufferedReader br = new BufferedReader(new FileReader("D:\\java\\a1\\xiancheng\\src\\com\\ex5tcp\\c.txt")); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while((line=br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); } //自定义结束标记 // bw.write("886"); // bw.newLine(); // bw.flush(); s.shutdownOutput(); //接受反馈 BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream())); String data = brClient.readLine(); System.out.println("服务器的反馈:"+data); br.close(); s.close(); } }
public class r5 { public static void main(String[] args) throws IOException { //e3,服务器 ServerSocket ss = new ServerSocket(10000); Socket s = ss.accept(); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\java\\a1\\xiancheng\\src\\com\\ex5tcp\\copy.txt")); String line; while((line= br.readLine())!=null){ // if("886".equals(line)){ // break; // } bw.write(line); bw.newLine(); bw.flush(); } //反馈 BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); bwServer.write("接收到了客户端给的数据"); bwServer.newLine(); bwServer.flush(); bw.close(); ss.close(); } }
练习6:TCP+多线程
客户端:数据来自于文本文件,接受服务器反馈
服务端:接受到的数据写入文本文件,给出反馈,代码用线程进行封装,为每一个客户端开启一个线程
public class s6 { public static void main(String[] args) throws IOException { //客户端 Socket s = new Socket("192.168.1.109",10000); BufferedReader br = new BufferedReader(new FileReader("D:\\java\\a1\\xiancheng\\src\\com\\ex5tcp\\c.txt")); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while((line=br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); } //自定义结束标记 // bw.write("886"); // bw.newLine(); // bw.flush(); s.shutdownOutput(); //接受反馈 BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream())); String data = brClient.readLine(); System.out.println("服务器的反馈:"+data); br.close(); s.close(); } }
public class r6 { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(10000); while(true) { Socket s = ss.accept(); new Thread((new ServerThread(s))).start(); } } }
public class ServerThread implements Runnable { private Socket s; public ServerThread(Socket s) { this.s = s; } @Override public void run() { try{ BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); // BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\java\\a1\\xiancheng\\src\\com\\ex5tcp\\copy.txt")); //解决名字冲突问题 int count =0; File file = new File("D:\\java\\a1\\xiancheng\\src\\com\\ex5tcp\\copy["+count+"].txt"); while(file.exists()){ count++; file = new File("D:\\java\\a1\\xiancheng\\src\\com\\ex5tcp\\copy["+count+"].txt"); } BufferedWriter bw = new BufferedWriter(new FileWriter(file)); String line; while((line=br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); } BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); bwServer.write("成功"); bwServer.newLine(); bwServer.flush(); s.close(); } catch (IOException e) { e.printStackTrace(); } } }
2、Lambda表达式
体验:启动一个线程,在控制台输出一句话:多线程启动了
//lambda表达式改进
public static void main(String[] args) { //实现类的方式实现需求 // i1 my = new i1(); // Thread t = new Thread(my); // t.start(); //匿名内部类 new Thread(new Runnable() { @Override public void run() { System.out.println("多线程启动了"); } }).start(); //优化 //lambda表达式改进 new Thread(()->{ System.out.println("多线程启动了"); }).start(); } } }
2.1Lambda表达式的标准格式
new Thread(()->{ System.out.println("多线程启动了"); }).start();
():里面没有内容,可以堪称是方法形式参数为空
-》:用箭头只想后面要做的事情
{}:保安含一段代码,我们惩治为代码块,可以看成是方法体中的内容
组成Labda表达式的三要素:形成参数、箭头、代码块
格式:
(形式参数)-> {代码块}
形式参数:如果由多个参数,参数之间用逗号隔开;如果没有参数,留空即可。
->:由英文符号组成,固定写法,代表只想动作。
代码块:是我么具体要做的事情,也就是以前我们写的方法体的内容。
Lambda表达式的使用前提:
有一个接口
接口中有且仅有一个抽象方法
练习1:
public class Eatabledemo { public static void main(String[] args) { // i1 i = new i1(); // useeat(i); // //匿名内部类 // useeat(new Eatable() { // @Override // public void eat() { // System.out.println("一天一个苹果"); // } // }); useeat(()->{ System.out.println("一天一个苹果"); }); } public static void useeat(Eatable e){ e.eat(); } }
public class i1 implements Eatable{ @Override public void eat() { System.out.println("一天一个苹果"); } }
public interface Eatable { void eat(); }
练习2:
public class Eatabledemo { public static void main(String[] args) { useeat((String s)->{ System.out.println(s); System.out.println("飞机自驾游"); }); } public static void useeat(Eatable e){ e.fly("清空往里"); } }
public interface Eatable { void fly(String s); }
练习3:
public class Eatabledemo { public static void main(String[] args) { useEat((int a, int b) -> { return a + b; }); } public static void useEat(Eatable e) { int sum = e.tt(4, 6); System.out.println(sum); } }
2.2 省略模式
1、参数的类型可以省略
2、但是由多个参数的情况下,不能省略一个
3、如果参数有且只有一个,那么小括号可以省略,直接参数->{}
4、如果代码块的语句只有一条可以省略大括号以及分号。
5、如果有return的情况,不止省略大括号以及分号,还要省略return
2.3 表达式注意事项
1、使用Lambda表达式,必须要有接口,并且要求接口中有且只有一个抽象方法
2、必须要上下文环境,才能推导出Lambda对应的接口
可以根据局部变量的赋值得知Lambda对应的接口:Runnabler = ()-》sout(“dhfjk”);
可以根据调用方法的参数得知Lambda对应的接口:new thread((sout(“KK”))。start()
2.4 Lambda表达式和匿名内部类的区别
1、所需类型不同
匿名内部类:可以是接口、抽象类、具体类
Lambda表达式:只能是接口
2、使用限制不同
如果接口中有且只有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多余一个抽象方法,只能使用匿名内部类。
3、实现原理不同
匿名内部类:编译之后,产生单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
3、 接口组成
3.1 接口组成更新概述
接口的组成:
1、常量
public static final
2、抽象方法:
public abstract
3、默认方法(java8)
4、静态方法(java8)
5、私有方法(java9)
3.2 接口中的默认方法
格式:
public default 返回值类型 方法名(参数列表){ }
注意事项:
默认方法不是抽象方法,所以不强制被重写。但是可以被充给,重写的时候去掉default关键字。
public可以省略,default不可以省略
public interface muin { void show1(); void show2(); public default void show3(){ System.out.println("3"); } }
public class muidemo implements muin{ @Override public void show1() { System.out.println("生活的"); } @Override public void show2() { System.out.println("是大家好好"); } }
public class demo { public static void main(String[] args) { muin my = new muidemo(); my.show1(); my.show2(); my.show3(); } }
3.3 接口中的静态方法
格式:
public static 返回值类型 方法名(参数列表){ }
注意事项!!
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
public可以shenglue,static不能省略
public interface muin { void show1(); public default void show2(){ System.out.println("3"); } public static void show3(){ System.out.println("静态方法"); } }
muin my = new muidemo(); my.show1(); my.show2(); // my.show3(); muin.show3();
3.4 接口中的私有方法
格式:
private 返回值类型 方法名(参数列表){ }
private static 返回值类型 方法名(参数列表){ }
静态方法不能调用普通的方法。
普通的方法可以调用静态的方法。
注意事项:
默认方法可以调用私有的静态方法和非静态方法
静态方法只能能调用私有的静态方法
4、 方法引用
4.1 体验方法引用
通过方法引用来使用已经存在的方案。
方法引用符::。两个冒号
public class demo { public static void main(String[] args) { // useEat(s -> System.out.println(s)); useEat(System.out::println); } public static void useEat(muin s) { System.out.println("爱你"); } }
public interface muin { void show1(String s); }
4.2 方法引用符
方法引用符:
::,该符号为引用运算符,而它所在的表达式被称为方法引用
推导:
如果使用Lambda,那么根据可推导就是可省略原则,无需指定参数类型,无需指定重载形式,他们都会被自动推导。
如果使用方法引用,也是同样可以根据上下文进行推导的。
方法引用是Lambda是孪生兄弟
public class demo { public static void main(String[] args) { // use(i-> System.out.println(i)); use(System.out::println);//666 } public static void use(muin p){ p.show1(666); } }
4.3 Lambda表达式支持的方法引用
4.3.1 引用类方法
引用类的静态方法
格式:类名::静态方法
Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
use(Integer::parseInt);
public class demo { public static void main(String[] args) { use(s->Integer.parseInt(s)); use(Integer::parseInt); } public static void use(muin p){ int num = p.show1("666"); System.out.println(num); } }
4.3.2 引用对象的实例方法
引用类中的成员方法
格式:对象::成员方法
Lambda表达式被对象的实例化方法替代的时候,他的形式参数全部传递给该方法作为参数。
public class demo { public static void main(String[] args) { // use(s-> System.out.println(s.toUpperCase())); oo o = new oo(); use(o::printupper); } public static void use(muin p){ p.show1("asdf"); } }
public class oo { public void printupper(String s){ String r = s.toUpperCase(); System.out.println(r); } }
4.3.3 引用类的实例方法
引用类中的成员方法
格式:类名::成员方法
Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数
public class demo { public static void main(String[] args) { use((String s,int a,int b)->{ return s.substring(a,b); }); use(String::substring); } public static void use(muin p){ String s = p.show1("asdffgvgvgv",2,5); System.out.println(s); } }
4.3.4 引用构造器
引用构造方法
格式:类名::new
Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数
public class demo { public static void main(String[] args) { // use((String name,int age)->{ // return new stu(name,age); // }); use(stu::new); } public static void use(stubuilder s){ stu ss = s.build("ll",20); System.out.println(ss.getAge()+","+ss.getName()); } }
5 、函数式接口
函数式接口:有且仅有一个抽象方法。
Java小红的函数式变成就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口,只有确保接口中有且仅有一个抽象方法,java中的lambda才能顺利进行推导
函数式接口@FunctionalInterface,就是检验接口是否是函数式接口。
一般建议加上。
放在接口上面,如果接口时函数式接口,编译通过;如果不是编译失败
注意:我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口,但是,建议加上该注释。
5.1函数式接口作为方法的参数
startThread(()->sout("线程启动了"));
如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数的传递
psvm{ //匿名内部类 startThread(new Runnable(){ @override public void run(){ sout("线程启动了") } } // startThread(()->sout("线程启动了")); } private static void startThred(Runnable r){//函数式接口 Thread t = new Thread(r); t.start(); }
5.2 函数式接口作为方法的返回值
Comparator <String> getComparater():方法的返回值是一个函数式接口
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回。
psvm{ ArrayList<String> arr = new ArrayList<String>(); arr.add("ddd"); arr.add("aaa"); arr.add("bbb"); sout(arr); //Collections.sort(arr); Collections.sort(arr,getComparator());//刚好第二个参数要的就是Compara方法,刚好方法返回的是Comparator对象 sout(arr); } private static Comparator<String> getComparator(){ Comparator <String> comp = new Comparator<String>(){ 重写方法; @Override public int compara(String s1,String s2){ return s1.length()-s2.length(); } } return comp; //Lambda表达式 return(string s1.String s2)->{return s1.length()-s2.length()} }
5.3 常用接口值
Supplier
主要是用来生产数据的,
supplier<T>:包含一个无参的方法
T get():获得结果
该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
Supplier《T》接口也被称为生产式接口,如果我们指定的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。
psvm { String s = getString(()->{ return "林青霞"; });//可以省略大括号,省略return sout(s); Integer a =getInteger(()->{ return 56; }) } //定义一个方法,返回一个字符串数据 public static String getString(Supplier<String> sup){ return sup.get(); } //定义一个方法,返回一个整数数据 public static Integer getInteger(Supplier<Integer> sup){ return sup.get(); }
练习:
定义一个类,在类中提供两个方法:
一个方法:int getmax(Supplier《Integer》 sup)用于返回一个int数组中的最大值
一个方法是主方法,在主方法中条用getmax方法
public class supplier { public static void main(String[] args) { int[] arr = {19,50,28,37,46}; int maxValue = getMax(()->{ int max = arr[0]; for (int i=0;i<arr.length;i++){ if (arr[i]>max){ max=arr[i]; } } return max; }); System.out.println(maxValue); } private static int getMax(Supplier<Integer> sup){ return sup.get(); } } 50
Consumer
Comsumer《T》:包含两个方法’
void accept(T t):对给定的参数执行此操作
default Consumer《T》 andThen(Consumer after):返回一个组合的Consumer,一次执行此操作,然后执行after操作
Consumer《T》接口也被称为消费性接口,它消费的数据的数据类型由泛型指定
public class Consumerdemo { public static void main(String[] args) { // opretorstring("jhjd",(String s)->{ // System.out.println(s);//jhjd // }); // opretorstring("kkk",s-> System.out.println(s));//kkk // opretorstring("kkk", System.out::println);//kkk // opretorstring("yyds",(s)->{ // System.out.println(new StringBuilder(s).reverse().toString());//sdyy // }); System.out.println("-----------"); opretorstring("asdf",s-> System.out.println(s),s-> System.out.println(new StringBuilder(s).reverse().toString())); //asdf //fdsa } private static void opretorstring(String name,Consumer<String> con1,Consumer<String> con2){ // con1.accept(name); // con2.accept(name); con1.andThen(con2).accept(name); } private static void opretorstring(String name, Consumer<String> con){ con.accept(name); } }
练习:
public class Consumerex1 { public static void main(String[] args) { String[] arr = {"林青霞,30","张曼玉,35","王祖贤,33"}; printINfo(arr,(String s)->{ String name = s.split(",")[0]; System.out.print("姓名:"+name); },(String s)->{ int age = Integer.parseInt(s.split(",")[1]); System.out.println(" "+"年龄:"+age); }); } public static void printINfo(String[] arr, Consumer<String> con1,Consumer<String> con2){ for(String str : arr){ con1.andThen(con2).accept(str); } } }
姓名:林青霞 年龄:30 姓名:张曼玉 年龄:35 姓名:王祖贤 年龄:33
Predicate
Predicate<T> :常用的四个发给发
boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda'表达式实现),返回一个布尔值
default Predicate《T》 negate():返回一个逻辑的否定,对应逻辑非
default Predicate《T》 and(Predicate other):返回一个组合判断,对应短路与
default Predicate《T》 or(predicate other):返回一个组合判断,对应短路与
predicate《T》接口通常用于判断参数是否满足指定的条件
public class Predicatedemo1 { public static void main(String[] args) { boolean a = checkString("hello", s -> { return s.length() > 9; }); System.out.println(a); boolean b = checkString("helloworldyyds", s -> s.length() > 9); System.out.println(b); boolean c = checkString("hello",s->s.length()>8,s->s.length()<15); System.out.println(c);//false boolean d = checkString("helloworld",s->s.length()<8,s->s.length()>15); System.out.println(d);//false } //判断给定字符是否满足要求 // private static boolean checkString(String s, Predicate<String> pre){ // return pre.test(s); // } private static boolean checkString(String s, Predicate<String> pre) { // return !pre.test(s); return pre.negate().test(s);//进行逻辑非的操作 } private static boolean checkString(String s, Predicate<String> pre1,Predicate<String> pre2) { // boolean b1 = pre1.test(s); // boolean b2 = pre2.test(s); // return b1 && b2; // return pre1.and(pre2).test(s); return pre1.or(pre2).test(s); } }
练习:
通过predicate接口的品专将符合要求的字符串筛选到集合arraylist中,并遍历arraylist集合
同时满足:姓名长度大于2,年龄大于33.
public class Predicatedemo2 { public static void main(String[] args) { String[] arr = {"林青霞,30", "柳岩,34", "张曼玉,35", "貂蝉,31", "王祖贤,33"}; // for (String str : arr) { // if (use(str, s -> s.split(",")[0].length() > 2, s -> Integer.parseInt(s.split(",")[1]) > 33)) { // System.out.println(str); // } // } ArrayList<String> str = use(arr, s -> s.split(",")[0].length() > 2, s -> Integer.parseInt(s.split(",")[1]) > 33); for (String s : str) { System.out.println(s); } } private static ArrayList<String> use(String[] s, Predicate<String> pre1, Predicate<String> pre2) { ArrayList<String> arr = new ArrayList<String>(); for (String str : s) { if (pre1.and(pre2).test(str)) { arr.add(str); } } return arr; } } 张曼玉,35
Function
Function《T,R》:常用的两个方法
R apply(T t):将此函数应用于给定的函数
default《V》 Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果。
Function《T,R》接口通常用于对参口进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值。
public class Functiondemo1 { public static void main(String[] args) { convert("100",s->Integer.parseInt(s));//100 convert("100",Integer::parseInt);//100 System.out.println("---------"); convert(5,i->String.valueOf(i+566));//571 convert("56",s->Integer.parseInt(s),s->String.valueOf(s+100));//156 } //定义一个方法,把一个字符串转化为int类型,在控制台输出 private static void convert(String s, Function<String,Integer> fun){ Integer i = fun.apply(s); System.out.println(i); } //定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在哦内阁制太输出 private static void convert(int i, Function<Integer,String> fun){ String o = fun.apply(i); System.out.println(o); } //靠漫画一个String类型的字符串,转化为int类型整数之后,转为字符串在控制台上输出。 private static void convert(String s1, Function<String,Integer> fun1,Function<Integer,String> fun2){ // Integer o = fun1.apply(s1); // String oo = fun2.apply(o); String oo = fun1.andThen(fun2).apply(s1); System.out.println(oo); } }
练习:
String s="林青霞,30";
请按照我指定的要求及逆行操作
1、将字符串截取得到数字年龄部分
2、将上一步的年龄字符转化为int类型数据
3、将上一步的int加上70,得到一个int结果,在控制台输出
请通过function进行函数拼接
public class Functiondemo2 { public static void main(String[] args) { String s = "林青霞,30"; convert(s,p->p.split(",")[1],p->Integer.parseInt(p),p->p+70);//100 } private static void convert(String s, Function<String,String> fun1,Function<String,Integer> fun2,Function<Integer,Integer> fun3){ int i = fun1.andThen(fun2).andThen(fun3).apply(s); System.out.println(i); } }
6、Stream流
6.1 体验Stream
利用Stream流的方式完成过滤操作:
//采用Stream流改写 arr.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3).forEach(System.out::println);
直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:生成流、过滤姓张,过滤长度为3,逐一打印
Stream流把真正的函数式变成风格引入到java中来
public class streamdemo1 { public static void main(String[] args) { ArrayList<String> arr = new ArrayList<String>(); arr.add("林青霞"); arr.add("张曼玉"); arr.add("王祖贤"); arr.add("柳岩"); arr.add("张无忌"); arr.add("张敏"); //将张开头的元素存储到一个新集合中 ArrayList<String> zarr = new ArrayList<String>(); for (String s:arr){ if(s.startsWith("张")){ zarr.add(s); } } System.out.println(zarr);//[张曼玉, 张无忌, 张敏] //把张开头的长度为3的元素存储到一个新集合中 ArrayList<String> threearr = new ArrayList<String>(); for (String s:zarr){ if (s.length()==3){ threearr.add(s); } } System.out.println(threearr);//[张曼玉, 张无忌] //遍历集合 for (String s:threearr){ System.out.println(s); } //张曼玉 //张无忌 System.out.println("-----------------------------"); //采用Stream流改写 arr.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3).forEach(System.out::println); //张曼玉 //张无忌 } }
6.2 Stream流的生成方式
Stream流的使用
生成流:
通过数据源(集合,数组等)生成流
list.stream()
中间操作
一个流后面可以跟随零个或者多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个使用
filter
终结操作
一个流只能由一个中介操作,当这个操作执行后,流就被使用光了,无法在被操作,所以者必顶是流的最后一个操作。
foreach()
生成方式
Collection体系的集合可以使用默认的stream()生成流
default Stream<E> stream()
map体系的集合间接的生成流
数组可以哦通过Stream接口的静态方法of(T。。。values)生成流
public class streamdemo2 { public static void main(String[] args) { //Collection体系的结合可以使用默认方法stream()生成流 List<String> list = new ArrayList<String>(); Stream<String> liststream = list.stream(); Set<String> set = new HashSet<String>(); Stream<String> setstream = set.stream(); //map体系的结合间接的生成流 Map<String,Integer> map = new HashMap<String,Integer>(); Stream<String> keyStream = map.keySet().stream(); Stream<Map.Entry<String,Integer>> entryStream = map.entrySet().stream(); //数组可以通过数组中的静态方法 String[] a = {"aaa","ddd","rrr"}; Stream<String> s = Stream.of(a); } }
中间操作方法:
filter
Stream<T> filter(Predicate predicate):用于对流中的数据进行过滤
Predicate接口中的方法boolean test(T,t)对给定的参数进行判断,返回一个布尔值。
public class streamdemo3 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("林青霞"); list.add("张曼玉"); list.add("王祖贤"); list.add("柳岩"); list.add("张无忌"); list.add("张敏"); list.stream().filter(s->s.startsWith("张")).forEach(System.out::println); System.out.println("-----"); list.stream().filter(s->s.length()==3).forEach(System.out::println); System.out.println("----"); list.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3).forEach(System.out::println); } }
张曼玉 张无忌 张敏 ----- 林青霞 张曼玉 王祖贤 张无忌 ---- 张曼玉 张无忌
limit + skip :
Stream<T> limit (long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据
Stream <T> skip(long n):跳过指定参数个数的数据,返回由该流的成语元素组成的流。
public class streamdemo3 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("林青霞"); list.add("张曼玉"); list.add("王祖贤"); list.add("柳岩"); list.add("张无忌"); list.add("张敏"); //取前三个元素在控制台输出 list.stream().limit(3).forEach(System.out::println); System.out.println("------"); //跳过3个元素,把剩下的元素在控制太输出 list.stream().skip(3).forEach(System.out::println); System.out.println("------"); //跳过2个元素,把剩下的元素中的前两个在控制台输出 list.stream().skip(2).limit(2).forEach(System.out::println); System.out.println("--"+list);
concat + distinct
static<T> Stream<T> concat(Stream a,Stream b):合并a和b为一个流
Stream《T》 distinct():返回由该流的不同元素(根据Object.equals(Object)):组成的流
public class streamdemo3 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("林青霞"); list.add("张曼玉"); list.add("王祖贤"); list.add("柳岩"); list.add("张无忌"); list.add("张敏"); //取前四个数据组成一个流 Stream<String> l1 = list.stream().limit(4); //跳过亮哥哥数据组成一个流 Stream<String> l2 = list.stream().skip(2); //合并需求1和需求2的流,并把结果在控制台输出 // Stream.concat(l1,l2).forEach(System.out::println); //合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复 System.out.println("--------------------------------------------"); Stream.concat(l1,l2).distinct().forEach(System.out::println);
林青霞 张曼玉 王祖贤 柳岩 张无忌 张敏
sorted:
Stream<T> sorted():返回由此流的元素组成的流,根据自然顺序排序。
Stream<T> sorted(Comparator comparator):返回由刺溜的元素组成的流,根据提供的Comparator进行排序
public class streamdemo3 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("linqingxia"); list.add("zhangmanyu"); list.add("wangzuxian"); list.add("liu"); list.add("zhangw"); list.add("zhangmin"); //按照字母顺序把数据在控制台上输出 list.stream().sorted().forEach(System.out::println); System.out.println("-----"); //按照字符串的长度把数据在控制太上输出 list.stream().sorted((s1,s2)->{ int num = s1.length()-s2.length(); int num2 = num==0?s1.compareTo(s2):num; return num2; }).forEach(System.out::println);
map+mapToInt:
<R> Stream<R> map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流
Function接口中的方法 R apply(T,t)
IntStream mapToInt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果
INtStream:表示原始int流
TOIntFuncton接口中的办法 int applyAsInt(T value)
public class streamdemo3 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("10"); list.add("20"); list.add("30"); list.add("40"); list.add("50"); list.add("60"); //将集合中的字符串转化为整数之后在控制台输出 // list.stream().map(s->Integer.parseInt(s)).forEach(System.out::println); // list.stream().map(Integer::parseInt).forEach(System.out::println); // list.stream().mapToInt(Integer::parseInt).forEach(System.out::println); //int sum//返回该流中的总和。是IntStream流的方法 int result = list.stream().mapToInt(Integer::parseInt).sum(); System.out.println(result);//210
终结操作:
forEach(Consumer action):对此流的每一个元素执行操作
Consumer接口中国的方法,void accept(T t):对给定的参数执行此操作
long count():返回此流中的元素数
public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("林青霞"); list.add("张曼玉"); list.add("王祖贤"); list.add("柳岩"); list.add("张无忌"); list.add("张敏"); // list.stream().forEach(System.out::println); long count = list.stream().filter(s->s.startsWith("张")).count(); System.out.println(count);//3
6.3 Stream流的练习
现在有两个Arraylist集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作
男演员只要名字为3个字的前三人
女演员只要姓林的,并且不要第一个
把过滤后的男演员姓名和女演员姓名合并都一起
把上一步操作后的元素作为构造方法的参数创造演员对象,遍历数据
演员类actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get、set方法
public class streamtest { public static void main(String[] args) { ArrayList<String> manList = new ArrayList<String>(); manList.add("周瑞发"); manList.add("刘德华"); manList.add("成龙"); manList.add("吴京"); manList.add("周星驰"); manList.add("李连杰"); ArrayList<String> womanList = new ArrayList<String>(); womanList.add("林心如"); womanList.add("张曼玉"); womanList.add("林青霞"); womanList.add("柳岩"); womanList.add("林志玲"); womanList.add("王祖贤"); Stream<String> l1 = manList.stream().filter(s->s.length()==3).limit(3); Stream<String> l2 = womanList.stream().filter(s->s.startsWith("林")).skip(1); Stream<String> stream = Stream.concat(l1, l2); stream.map(actor::new).forEach(System.out::println);
actor{name='周瑞发'} actor{name='刘德华'} actor{name='周星驰'} actor{name='林青霞'} actor{name='林志玲'}
6.4 Stream流的手机操作
收集方法:
R collection(Collector collector)
但是这个手机方法的参数是一个Collector接口
工具类Colllections提供了具体的手机方法:
pubblic static《T》Collector toList():把元素手机到List集合中去
public static《T》 Collector toSet():把元素手机到set集合中去
public static Collector toMap(Function keyMapper,Function valueMapper):把元素手机到Map集合中去
List<String> names = listStream.collection(Collectors.tolist()); for(String name:names){ sout(name) }