认识Stream流
Stream流大量结合了Lambda的语法风格来编程,功能强大,性能高效,可读性好
简化集合,数组操作的API
使用步骤
- 获取Stream流
- 调用流水线的各种方法
- 获取处理的结果

获取Stream流
Collection<String> list=new ArrayList<>();
Stream<String> s1=list.stream();
//map集合拿Stream流
Map<String,Integer> map=new HashMap<>();
Stream<String> s2=map.keySet().stream();
Stream<Integer> s3=map.values().stream();
Stream<Map.Entry<String,Integer>> s4=map.entrySet().stream();
//获取数组的流
String[] names={"2","3"};
Stream<String> s5= Arrays.stream(names);
System.out.println(s5.count());
Stream<String> s6=Stream.of(names);
中间方法

List<String> ls=new ArrayList<>();
ls.add("ww");
ls.add("fw");
ls.add("dw");
ls.add("sw");
ls.add("aw");
//过滤方法
List<String> newlist=ls.stream().filter(s->s.startsWith("w")).filter(s->s.length()==2).collect(Collectors.toList());
//排序
List<Double> scores=new ArrayList<>();
scores.add(53.2);
scores.add(73.2);
scores.add(345.2);
scores.add(63.2);
scores.add(33.2);
scores.stream().sorted().forEach(System.out::println);
scores.stream().sorted((o1,o2)->Double.compare(o2,o1)).skip(2).forEach(System.out::println);
//映射/加工方法,将原来的数据拿出来变成新数据再放上去
ls.stream().map(s->(s+10)).forEach(System.out::println);
方法中可变参数
在形参列表中只能出现一个
public static void main(String[] args) {
sum();
sum(10,20,30);
sum(new int[]{2,3});
}
public static void sum(int...nums){
System.out.println(nums.length);
System.out.println(Arrays.toString(nums));
System.out.println(nums);
}
Collections工具类

File
File f1=new File("D:\\note\\1.txt");
System.out.println(f1.length());
System.out.println(f1.getName());
File f2=new File("D:\\note\\2.txt");
System.out.println(f2.exists());
//创建多级文件夹
File f3=new File("D:\\note\\aaa\\bbb");
System.out.println(f3.mkdirs());
File f4=new File("D:\\note");
File[] f5=f4.listFiles();
// for(File file:f5){
// System.out.println(file.getAbsoluteFile());
// }
System.out.println(Arrays.toString(f5));
IO流
读文本内容用字符流,转移数据用字节流,如复制文件
字节流
文件字节输入流
public static void main(String[] args) throws Exception{
InputStream is=new FileInputStream("D:\\idea code\\FileDemo\\src\\com\\wsj\\file\\1.txt");
int b;
// 每次读取一个字节的问题,性能较差,读取汉字会乱码
// while((b=is.read())!=-1){
// System.out.println((char) b);
// }
//每次读取多个字节
byte[] buffer=new byte[3];
//定义一个变量,记录读取了多少字节
int len;
while((len=is.read(buffer))!=-1){
String str=new String(buffer,0,len);
System.out.print(str);
}
}
读取小文件可以一次性读取文件中的所有字节
文件字节输出流
public static void main(String[] args) throws Exception{
//使用文件字节输出流
// FileOutputStream fs=new FileOutputStream("D:\\idea code\\FileDemo\\src\\com\\wsj\\file\\2.txt");
//追加
FileOutputStream fs=new FileOutputStream("D:\\idea code\\FileDemo\\src\\com\\wsj\\file\\2.txt",true);
fs.write(97);
fs.write('b');
//写一个字节数组出去
byte[] bytes="66".getBytes(StandardCharsets.UTF_8);
fs.write(bytes);
fs.write(bytes,0,1);
fs.close();
}
复制文件
public static void main(String[] args) {
// 使用字节流完成文件的复制
try {
func("D:\\idea code\\FileDemo\\src\\com\\wsj\\file\\2.txt", "D:\\note\\1.txt");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void func(String first, String end) {
try (
FileInputStream fis = new FileInputStream(first);
FileOutputStream fos = new FileOutputStream(end);
)
//关闭文件
{
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
System.out.println("复制完成");
} catch(Exception e){
e.printStackTrace();
}
}
字符流
文件字符输入流
public static void main(String[] args) throws Exception {
try (Reader fr = new FileReader("D:\\idea code\\FileDemo\\src\\com\\wsj\\file\\1.txt"))
{
char[] c=new char[3];
int len;
while((len=fr.read(c))!=-1){
String str=new String(c);
System.out.println(str);
}
}catch (Exception e){
e.printStackTrace();
}
}
文件字符输出流
和文件字节输出流一致
缓冲流
BufferedInputStream缓冲字节输入流
作用:提高字节输入流读取字符的效率
提供大小为8K的缓冲池,读取数据时可以通过缓冲池将数据放到内存中
将数据写到文件中时同样可以通过缓冲池提高效率
try {
func("D:\\idea code\\FileDemo\\src\\com\\wsj\\file\\2.txt", "D:\\note\\1.txt");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void func(String first, String end) {
try (
FileInputStream fis = new FileInputStream(first);
InputStream bis=new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream(end);
OutputStream bos=new BufferedOutputStream(fos);
)
//关闭文件
{
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
System.out.println("复制完成");
} catch(Exception e){
e.printStackTrace();
}
}
输出流
try (
FileInputStream fis = new FileInputStream(first);
InputStream bis=new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream(end);
OutputStream bos=new BufferedOutputStream(fos);
)
//关闭文件
{
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
System.out.println("复制完成");
} catch(Exception e){
e.printStackTrace();
}
其他流
字符输入转换流
IO框架

多线程
线程是一个程序内部的一条执行流程
多线程指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)
创建方式一:继承Thread类
-
定义子类继承Thread类,重写run()方法
-
创建对象
-
使用start启用
public class demo1 {
//main方法本身有一条主线程负责推动执行
public static void main(String[] args) {
// 创建线程的方式1,继承thread实现
//创建线程类的对象
Thread s1=new MyThread();
s1.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程输出:"+i);
}
}
}
class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 5; i++) {
System.out.println("子线程输出:"+i);
}
}
}
注意事项
启动线程必须调用start方法,如果调用run方法还是相当于单线程执行
只有调用start方法才是启动一个线程的执行
创建方式二:实现Runnable接口
优点:任务类只是实现类接口,可以继续继承其他类,实现其他接口,扩展性强
缺点:需要多一个任务类对象
public class demo2 {
public static void main(String[] args) {
//实现Runnable接口实现
// 创建一个线程任务类的对象
// Runnable r=new MyRunnable();
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程输出:"+i);
}
}
});
t1.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程输出:"+i);
}
}
}
//class MyRunnable implements Runnable{
//
// @Override
// public void run() {
// for (int i = 0; i < 5; i++) {
// System.out.println("子线程输出:"+i);
// }
// }
//}
创建方式三:实现Callable接口
优点:可以返回线程完毕后的结果
- 创建任务对象
- 定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据
- 把Callable类型的对象封装成FutureTask(线程任务对象)
- 把线程任务对象交给Thread对象
- 调用start
- 执行完毕后,通过FutureTask对象的get获取任务执行的结果
public static void main(String[] args) {
Callable<String> cs=new Mycallable(100);
//封装成线程任务对象
FutureTask<String> FS=new FutureTask<>(cs);
// 传给Thread线程对象
Thread t1=new Thread(FS);
t1.start();
Callable<String> c1=new Mycallable(50);
//封装成线程任务对象
FutureTask<String> F1=new FutureTask<>(c1);
// 传给Thread线程对象
Thread t2=new Thread(F1);
t2.start();
try{
//如果主线程发现第一个线程没有执行完毕,会让出CPU,等第一个线程执行完毕后,才会往下执行
System.out.println(FS.get());
}catch (Exception e){
e.printStackTrace();
}
try{
System.out.println(F1.get());
}catch (Exception e){
e.printStackTrace();
}
}
}
class Mycallable implements Callable<String>{
private int n;
public Mycallable(int n){
this.n=n;
}
@Override
public String call() throws Exception {
int sum=0;
for(int i=0;i<=n;i++){
System.out.println(i);
sum+=i;
}
return "子线程计算的"+"1-"+n+"结果:"+sum;
}
}
常用方法
方法名称
说明
String getName()
返回此线程的名称
int getId()
返回此线程的ID
void setName(String name)
设置线程的名字(构造方法也可以设置名字)
static Thread currentThread( )
获取当前线程的对象
static void sleep(long time)
让线程休眠指定的时间,单位为毫秒
setPriority(int newPriority)
设置线程的优先级
final int getPriority( )
获取线程的优先级
final void setDaemon(boolean on)
设置为守护线程
public static void yield( )
出让线程/礼让线程
public static void join( )
插入线程/插队线程
线程安全问题
-
存在多个线程同时进行
-
同时访问共享资源
-
存在修改共享资源
public class DrawThread extends Thread{
private Account acc;
public DrawThread(String name,Account acc){
super(name);
this.acc=acc;
}
@Override
public void run() {
acc.drawmoney(100000);
}
}
public static void main(String[] args) {
// 设计一个账户类
Account acc=new Account(100000,"x");
Thread t1=new DrawThread("小明",acc);
t1.start();
new DrawThread("小红",acc).start();
}
线程同步
核心思想:让多个线程先后一次访问共享资源,这样就可以避免出现线程安全问题
加锁:每次只允许一个线程加锁,加锁后才能访问,访问完毕后自动解锁,然后其他线程才能加锁进来
方式一:同步代码块
作用:把访问共享资源的核心代码上锁,以此保证线程安全
synchronize(同步锁){
访问公共资源的核心代码
}
使用规范:建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象
对于静态方法建议 使用字节码对象作为锁对象
方式二:同步方法
作用:把访问共享资源的核心方法上锁,以此保证线程安全
public synchronized void drawmoney(double money){
String name=Thread.currentThread().getName();
//判断余额是否足够
if(this.money>=money){
System.out.println(name+"取钱成功,取了"+money);
this.money-=money;
System.out.println(name+"取钱成功,剩余"+this.money);
}
else{
System.out.println(name+"取钱失败");
}
}
方式三:lock锁
public void drawmoney(double money){
String name=Thread.currentThread().getName();
//判断余额是否足够
lk.lock();
try {
if(this.money>=money){
System.out.println(name+"取钱成功,取了"+money);
this.money-=money;
System.out.println(name+"取钱成功,剩余"+this.money);
}
else{
System.out.println(name+"取钱失败");
}
} finally {
lk.unlock();
}
}
线程池
一个可以复用线程的技术
创建线程池
ExecutorService–>ThreadPoolExecutor
方式一 通过ThreadPoolExecytor创建线程池
(1)corePoolSize:核心线程数,线程池中始终存活的线程数。
(2)maximumPoolSize: 最大线程数,线程池中允许的最大线程数。
(3)keepAliveTime: 存活时间,线程没有任务执行时最多保持多久时间会终止。
(4)unit: 单位,参数keepAliveTime的时间单位,7种可选。
(5)workQueue: 一个阻塞队列,用来存储等待执行的任务,均为线程安全,7种可选。
(6)threadFactory: 线程工厂,主要用来创建线程,默及正常优先级、非守护线程。
(7)handler:拒绝策略,拒绝处理任务时的策略,4种可选,默认为AbortPolicy。
注意事项
新任务提交时发现核心线程在忙,任务队列也满了,并且可以创建临时线程,此时才能创建临时线程
核心线程和临时线程都在忙,任务队列也满了,拒绝
public static void main(String[] args) {
//创建线程池对象
ExecutorService pool=new ThreadPoolExecutor(3,5,10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
Runnable target=new MyRunnable();
Runnable target1=new MyRunnable();
Runnable target2=new MyRunnable();
Runnable target3=new MyRunnable();
Runnable target4=new MyRunnable();
Runnable target5=new MyRunnable();
pool.execute(target);
pool.execute(target1);
pool.execute(target2);
pool.execute(target3);
pool.execute(target4);
pool.execute(target5);
pool.execute(target);
//关闭线程池,一般不关闭线程池
pool.shutdown();//所有任务执行完毕后关闭线程池
方式二:Executors工具类
ExecutorService pool=Executors.newFixedThreadPool(3);
并发/并行
进程:正在进行的程序就是一个独立的进程
线程是属于进程的
网络编程
IP地址
设备在网络中的地址,是设备在网络中的唯一标识
DNS域名解析
将域名转化为对应IP地址的分布式命名系统,将容易记的域名转化为数字化的IP地址
内网IP
局域网IP,专门为组织机构内部使用
公网IP
是可以连接到互联网的IP地址
InetAddress
获取本机IP对象和对方IP对象
端口
应用程序在程序中的唯一标识
被规定一个16位的二进制,范围是0-65535
端口分类
周知端口:0-1023
注册端口:1024-49151
动态端口:49152-65535
自己开发的程序一般使用注册端口,同一个设备中不能出现两个相同的端口
协议
通信协议
网络上的通信设备,实现预定的连接规则,以及传递数据的规则被称为网络通信协议
开发式网络互联标准:
TCP/IP网络模型:事实上的国际标准

传输层的两个通信协议
UDP:用户数据报协议
特点:通信效率高,无连接,不可靠的协议,发送端把数据封装成一个64KB的包
TCP:传输控制协议
面向连接,可靠通信
保证在不可靠的信道上实现可靠的数据传输
三次握手建立可靠连接
- 客户端发出连接请求
- 服务端返回响应
- 客户端再次发出确认信息,连接建立
四次挥手断开连接
- 客户端发出消息断开连接请求
- 服务端返回一个响应:稍等
- 返回一个响应:消息处理完毕,确认断开
- 客户端发出确认断开消息,连接断开
客户端发送数据
public class TCPdemo1 {
public static void main(String[] args) throws IOException {
//tcp通信下一发一收,客户端开发
//创建客户端管道对象,请求与服务端连接
Socket socket=new Socket("127.0.0.1",9999);
//从Socket管道中得到一个字节输出流
OutputStream os=socket.getOutputStream();
DataOutputStream dos=new DataOutputStream(os);
dos.writeInt(1);
dos.writeUTF("哈哈");
socket.close();
}
}
服务端接收数据
public class TCPdemo2 {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(9999);
//一旦有客户端连接返回一个Socket对象
Socket s=ss.accept();
//获取输入流
InputStream is=s.getInputStream();
//字节输入流包装成特殊数据输入流
DataInputStream ds=new DataInputStream(is);
int id=ds.readInt();
String msg=ds.readUTF();
System.out.println(id+msg);
System.out.println("客户端的ip"+s.getInetAddress().getHostAddress());
System.out.println("客户端的端口"+s.getPort());
}
}
UDP通信
客户端发送数据
System.out.println("客户端启动");
//完成UDP通信一发一收,客户端开发
//发送对象
DatagramSocket socket=new DatagramSocket();//随机端口
// 创建数据包对象
byte[] bytes="我是客户端".getBytes();
//发送数据,发送长度,目的地的IP地址,目的地端口
DatagramPacket packet=new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),8080);
socket.send(packet);
服务端接收数据
System.out.println("服务端启动了");
//接数据的对象
DatagramSocket socket=new DatagramSocket(8080);
//创建数据包接取数据
byte[] buf=new byte[1024*64];
DatagramPacket packet=new DatagramPacket(buf,buf.length);
//接受数据
socket.receive(packet);
//获取数据长度
int len=packet.getLength();
String data=new String(buf,0,len);
System.out.println(data);
//获取对方的IP对象和程序端口
String ip=packet.getAddress().getHostAddress();
int port=packet.getPort();
System.out.println("ip地址"+ip+"端口"+port);
TCP通信-支持多个客户端通信
服务端
public class TCPdemo2 {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(9999);
//一旦有客户端连接返回一个Socket对象
while (true) {
Socket s=ss.accept();
System.out.println("一个客户端上线了"+s.getInetAddress().getHostAddress());
new SeverReader(s).start();
}
}
}
线程
public class SeverReader extends Thread{
private Socket socket;
public SeverReader(Socket socket){
this.socket=socket;
}
@Override
public void run() {
//获取输入流
try {
InputStream is= socket.getInputStream();
//字节输入流包装成特殊数据输入流
DataInputStream ds=new DataInputStream(is);
while (true) {
String msg=ds.readUTF();
System.out.println(msg);
System.out.println("客户端的ip"+socket.getInetAddress().getHostAddress());
System.out.println("客户端的端口"+socket.getPort());
System.out.println("------------------------");
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("客户端下线了");
}
}
}
B/S架构的原理
服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据

public class SeverReader extends Thread{
private Socket socket;
public SeverReader(Socket socket){
this.socket=socket;
}
@Override
public void run() {
//获取输入流
try {
//给浏览器响应一个网页回去
OutputStream os=socket.getOutputStream();
//把字节输出流包装成打印流
PrintStream ps=new PrintStream(os);
ps.println("HTTP/1,1 200 OK");
ps.println("Content-Type:text/html;charset=utf-8");
ps.println();
ps.println("<html>");
ps.println("<head>");
ps.println("<meta charset='utf-8'>");
ps.println("<title>");
ps.println("wsj的网页");
ps.println("</title>");
ps.println("</head>");
ps.println("<body>");
ps.println("<h1>wsj</h1>");
ps.println("</body>");
ps.println("</html>");
ps.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
System.out.println("客户端下线了");
}
}
}