文章目录
14 GUI
14.1 绘图
-
绘图原理:
- Component类提供了两个和绘图相关最重要的方法:
- paint(Graphics g)绘制组件的外观
- repaint()刷新组件的外观
- 当组件第一次在屏幕显示的时候,程序会自动的调用paint0方法来绘制组件,在以下情况paintQ将会被调用:
- 窗口最小化,再最大化
- 窗口的大小发生变化
- repaint函数被调用
- Component类提供了两个和绘图相关最重要的方法:
-
Graphics类:
- 画直线 drawLine(int x1,int yl,int x2,int y2)
- 画矩形边框 drawRect(int x, int y, int width, int height)
- 画椭圆边框 drawOval(int x, int y, int width, int height)
- 填充矩形fillRect(int x, int y, int width, int height)
- 填充椭园 fillOval(int x, int y. int width, int height)
- 画图片 drawlmage(Image img, int x, int y, …)
- 画字符串 drawString(String str, int x, inty)
- 设置画笔的字体 setFont (Font font)
- 设置画笔的颜色 setColor(Color c)
14.2 事件控制
-
基本介绍:
java事件处理是采取“委派事件模型”。当事件发生时,产生事件的对象,会把此"信息"传递给"事件的监听者” 处理,这里所说的"信息"实际上就是 java.awt.event 事件类库里某个类所创建的对象,把它称为”事件的对象“
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SCMw5ytf-1692079275405)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-01%2023.33.57.jpg)]
-
深入理解:
-
事件源:事件源是一个产生事件的对象,比如按钮,窗口等
-
事件:事件就是承载事件源状态改变时的对象,比如当键盘事件、鼠标事件、窗口事件等等,会生成一个事件对象,该对家保存着当前事件很多信息,比如KeyEvent 对象有含有被按下键的Gode值。java.awt.event包 和javax.swing.event包中定义了各种事
件类型 -
事件类型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mXK3DTqN-1692079275406)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-01%2023.39.47.jpg)]
-
事件监听噐号接口:
(1)当事件源产生一个事件,可以传送给事件监听者处理Interfaces
(2) 事件监听者本质是一个类,该类实现了某个事件监听器接口比如前面我们案例中的MyPanle就是一个类,它实现了
KeyListener接口,它就可以作为一个事件监听者,对接受到的事件进行处理
(3)事件监听器接口有多种,不同的事件监听器接口可以监听不同的事件 一个类可以实现多个监听接口
(4)这些接口在java.awt.event包和javax.swing.event包中定义,列出常用的事件监听器接口,查看jdk 文档聚集了
-
15 线程(基础)
-
基本介绍:
- 线程由进程创建的,是进程的一个实体
- 一个进程可以拥有多个线程
- 单线程:同一个时刻,只允许执行一个线程
- 多线程:同一个时刻,可以执行多个线程
- 并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉
- 并行:同一个时刻,多个任务同时执行,多核cpu可以实现并行
16.1 线程使用
16.1.1 继承Thread
public class Thread01 {
public static void main(String[] args) throws InterruptedException {
//创建Cat对象,可以当做线程使用
Cat cat = new Cat();
//源码
/*
(1)
public synchronized void start() {
start0();
}
(2)
//start0() 是本地方法,是JVM调用, 底层是c/c++实现
//真正实现多线程的效果,是start0(), 而不是 run
private native void start0();
*/
cat.start();//启动线程-> 最终会执行cat的run方法
//cat.run();//run方法就是一个普通的方法, 没有真正的启动一个线程,就会把run方法执行完毕,才向下执行
//说明: 当main线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行
//这时 主线程和子线程是交替执行..
System.out.println("主线程继续执行" + Thread.currentThread().getName());//名字main
for(int i = 0; i < 60; i++) {
System.out.println("主线程 i=" + i);
//让主线程休眠
Thread.sleep(1000);
}
}
}
//1. 当一个类继承了 Thread 类, 该类就可以当做线程使用
//2. 我们会重写 run方法,写上自己的业务代码
//3. run Thread 类 实现了 Runnable 接口的run方法
/*
@Override
public void run() {
if (target != null) {
target.run();
}
}
*/
class Cat extends Thread {
int times = 0;
@Override
public void run() {//重写run方法,写上自己的业务逻辑
while (true) {
//该线程每隔1秒。在控制台输出 “喵喵, 我是小猫咪”
System.out.println("喵喵, 我是小猫咪" + (++times) + " 线程名=" + Thread.currentThread().getName());
//让该线程休眠1秒 ctrl+alt+t
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(times == 80) {
break;//当times 到80, 退出while, 这时线程也就退出..
}
}
}
}
16.1.2 实现Runnable
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
//dog.start(); 这里不能调用start
//创建了Thread对象,把 dog对象(实现Runnable),放入Thread
Thread thread = new Thread(dog);
thread.start();
// Tiger tiger = new Tiger();//实现了 Runnable
// ThreadProxy threadProxy = new ThreadProxy(tiger);
// threadProxy.start();
}
}
class Animal {
}
class Tiger extends Animal implements Runnable {
@Override
public void run() {
System.out.println("老虎嗷嗷叫....");
}
}
//线程代理类 , 模拟了一个极简的Thread类
class ThreadProxy implements Runnable {//你可以把Proxy类当做 ThreadProxy
private Runnable target = null;//属性,类型是 Runnable
@Override
public void run() {
if (target != null) {
target.run();//动态绑定(运行类型Tiger)
}
}
public ThreadProxy(Runnable target) {
this.target = target;
}
public void start() {
start0();//这个方法时真正实现多线程方法
}
public void start0() {
run();
}
}
class Dog implements Runnable { //通过实现Runnable接口,开发线程
int count = 0;
@Override
public void run() { //普通方法
while (true) {
System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());
//休眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) {
break;
}
}
}
}
-
对比:
- java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jidk帮助文档我们可以看到Thread类本身就实现了Runnable接口
- 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制
-
线程退出:
public class ThreadExit_ { public static void main(String[] args) throws InterruptedException { T t1 = new T(); t1.start(); //如果希望main线程去控制t1 线程的终止, 必须可以修改 loop //让t1 退出run方法,从而终止 t1线程 -> 通知方式 //让主线程休眠 10 秒,再通知 t1线程退出 System.out.println("main线程休眠10s..."); Thread.sleep(10 * 1000); t1.setLoop(false); } } class T extends Thread { private int count = 0; //设置一个控制变量 private boolean loop = true; @Override public void run() { while (loop) { try { Thread.sleep(50);// 让当前线程休眠50ms } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("T 运行中...." + (++count)); } } public void setLoop(boolean loop) { this.loop = loop; } }
-
代理模式模拟:
public class Thread02 { public static void main(String[] args) { Dog dog = new Dog(); //dog.start(); 这里不能调用start //创建了Thread对象,把 dog对象(实现Runnable),放入Thread Thread thread = new Thread(dog); thread.start(); // Tiger tiger = new Tiger();//实现了 Runnable // ThreadProxy threadProxy = new ThreadProxy(tiger); // threadProxy.start(); } } class Animal { } class Tiger extends Animal implements Runnable { @Override public void run() { System.out.println("老虎嗷嗷叫...."); } } //线程代理类 , 模拟了一个极简的Thread类 class ThreadProxy implements Runnable {//你可以把Proxy类当做 ThreadProxy private Runnable target = null;//属性,类型是 Runnable @Override public void run() { if (target != null) { target.run();//动态绑定(运行类型Tiger) } } public ThreadProxy(Runnable target) { this.target = target; } public void start() { start0();//这个方法时真正实现多线程方法 } public void start0() { run(); } } class Dog implements Runnable { //通过实现Runnable接口,开发线程 int count = 0; @Override public void run() { //普通方法 while (true) { System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName()); //休眠1秒 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (count == 10) { break; } } } }
16.2 线程方法
-
常用方法1:
-
setName:设置线程名称,使之与参数 name 相同
-
getName:返回该线程的名称
-
start:使该线程开始执行;Java 虚拟机底层调用该线程的 start0方法
-
run :调用线程对象 run 方法
-
setPriority:更改线程的优先级
-
getPriority:获取线程的优先级
-
sleep:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
-
interrupt:中断线程
public class ThreadMethod01 { public static void main(String[] args) throws InterruptedException { //测试相关的方法 T t = new T(); t.setName("老韩"); t.setPriority(Thread.MIN_PRIORITY);//1 t.start();//启动子线程 //主线程打印5 hi ,然后我就中断 子线程的休眠 for(int i = 0; i < 5; i++) { Thread.sleep(1000); System.out.println("hi " + i); } System.out.println(t.getName() + " 线程的优先级 =" + t.getPriority());//1 t.interrupt();//当执行到这里,就会中断 t线程的休眠. } } class T extends Thread { //自定义的线程类 @Override public void run() { while (true) { for (int i = 0; i < 100; i++) { //Thread.currentThread().getName() 获取当前线程的名称 System.out.println(Thread.currentThread().getName() + " 吃包子~~~~" + i); } try { System.out.println(Thread.currentThread().getName() + " 休眠中~~~"); Thread.sleep(20000);//20秒 } catch (InterruptedException e) { //当该线程执行到一个interrupt 方法时,就会catch 一个 异常, 可以加入自己的业务代码 //InterruptedException 是捕获到一个中断异常. System.out.println(Thread.currentThread().getName() + "被 interrupt了"); } } } }
-
-
注意事项和细节:
- start:底层会创建新的线程,调用run,run 就是一个简单的方法调用,不会启动新线程
- interrupt:中断线程,但并没有真正的结束线程,所以一般用于中断正在休眠线程
- sleep:线程的静态方法,使当前线程休眠
-
常用方法2:
-
yield:线程的礼让。让出Cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
-
join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任
public class ThreadMethod02 { public static void main(String[] args) throws InterruptedException { T2 t2 = new T2(); t2.start(); for(int i = 1; i <= 20; i++) { Thread.sleep(1000); System.out.println("主线程(小弟) 吃了 " + i + " 包子"); if(i == 5) { System.out.println("主线程(小弟) 让 子线程(老大) 先吃"); //join, 线程插队 //t2.join();// 这里相当于让t2 线程先执行完毕 Thread.yield();//礼让,不一定成功.. System.out.println("线程(老大) 吃完了 主线程(小弟) 接着吃.."); } } } } class T2 extends Thread { @Override public void run() { for (int i = 1; i <= 20; i++) { try { Thread.sleep(1000);//休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程(老大) 吃了 " + i + " 包子"); } } }
-
-
用户线程和守护线程:
-
用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
-
守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
setDaemon(True)
-
常见的守护线程:垃圾回收机制
public class ThreadMethod03 { public static void main(String[] args) throws InterruptedException { MyDaemonThread myDaemonThread = new MyDaemonThread(); //如果我们希望当main线程结束后,子线程自动结束 //只需将子线程设为守护线程即可 myDaemonThread.setDaemon(true); myDaemonThread.start(); for( int i = 1; i <= 10; i++) {//main线程 System.out.println("辛苦的工作..."); Thread.sleep(1000); } } } class MyDaemonThread extends Thread { public void run() { for (; ; ) {//无限循环 try { Thread.sleep(1000);//休眠1000毫秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("快乐聊天,哈哈哈~~~"); } } }
-
16.3 线程生命周期
-
周期图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OgYcYvnS-1692079275406)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-03%2015.58.12.jpg)]
16.4 Synchronized⭐️
-
线程同步机制:
线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作
-
基本语法:
//同步代码块 synchronized(对象) { // 得到对象的锁,才能操作同步代码 需要被同步代码; } //同步方法 public synchronized void m(String name){ 需要被同步代码; }
-
例:
public class SellTicket { public static void main(String[] args) { SellTicket03 sellTicket03 = new SellTicket03(); new Thread(sellTicket03).start();//第1个线程-窗口 new Thread(sellTicket03).start();//第2个线程-窗口 new Thread(sellTicket03).start();//第3个线程-窗口 } } //实现接口方式, 使用synchronized实现线程同步 class SellTicket03 implements Runnable { private int ticketNum = 100;//让多个线程共享 ticketNum private boolean loop = true;//控制run方法变量 Object object = new Object(); //同步方法(静态的)的锁为当前类本身 //老韩解读 //1. public synchronized static void m1() {} 锁是加在 SellTicket03.class //2. 如果在静态方法中,实现一个同步代码块. /* synchronized (SellTicket03.class) { System.out.println("m2"); } */ public synchronized static void m1() { } public static void m2() { synchronized (SellTicket03.class) { System.out.println("m2"); } } //老韩说明 //1. public synchronized void sell() {} 就是一个同步方法 //2. 这时锁在 this对象 //3. 也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在this对象 public /*synchronized*/ void sell() { //同步方法, 在同一时刻, 只能有一个线程来执行sell方法 synchronized (/*this*/ object) { if (ticketNum <= 0) { System.out.println("售票结束..."); loop = false; return; } //休眠50毫秒, 模拟 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));//1 - 0 - -1 - -2 } } @Override public void run() { while (loop) { sell();//sell方法是一共同步方法 } } }
16.5 互斥锁⭐️
-
基本介绍:
- Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
- 每个对象都对应于一个可称为“互斥锁〞 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
- 关键字synchronized 来与对象的互斥锁联系。当某个对象用synchronized修饰时表明该对象在任一时刻只能由一个线程访问
- 同步的局限性:导致程序的执行效率要降低
- 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
- 同步方法(静态的)的锁为当前类本身
-
注意事项和细节:
-
同步方法如果没有使用static修饰:默认锁对象为this
-
如果方法使用static修饰,默认锁对象:当前类.class
-
实现的落地步骤:
-
需要先分析上锁的代码
-
选择同步代码块或同步方法
-
要求多个线程的锁对象为同一个即可
public class DeadLock_ { public static void main(String[] args) { //模拟死锁现象 DeadLockDemo A = new DeadLockDemo(true); A.setName("A线程"); DeadLockDemo B = new DeadLockDemo(false); B.setName("B线程"); A.start(); B.start(); } } //线程 class DeadLockDemo extends Thread { static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用static static Object o2 = new Object(); boolean flag; public DeadLockDemo(boolean flag) {//构造器 this.flag = flag; } @Override public void run() { //下面业务逻辑的分析 //1. 如果flag 为 T, 线程A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁 //2. 如果线程A 得不到 o2 对象锁,就会Blocked //3. 如果flag 为 F, 线程B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁 //4. 如果线程B 得不到 o1 对象锁,就会Blocked if (flag) { synchronized (o1) {//对象互斥锁, 下面就是同步代码 System.out.println(Thread.currentThread().getName() + " 进入1"); synchronized (o2) { // 这里获得li对象的监视权 System.out.println(Thread.currentThread().getName() + " 进入2"); } } } else { synchronized (o2) { System.out.println(Thread.currentThread().getName() + " 进入3"); synchronized (o1) { // 这里获得li对象的监视权 System.out.println(Thread.currentThread().getName() + " 进入4"); } } } } }
-
-
16.6 死锁
-
基本介绍:
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生
-
释放锁:
- 当前线程的同步方法、同步代码块执行结束
- 当前线程在同步代码块、同步方法中遇到break、return
- 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
- 当前线程在同步代码块、同步方法中执行了线程对象的wait0方法,当前线程暂停,井释放锁
-
不释放锁:
- 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行
- 线程执行同步代码块时,其他线程调用了该线程的suspendO方法将该线程挂起
16 IO流
16.0 文件
-
基本介绍:
文件是保存数据的地方
-
文件流:
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RW8L0bxe-1692079275407)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-06%2016.06.15.jpg)]
-
创建文件:
-
new File(String pathname) //根据文件路径构建
-
new File(File parent,String child) //根据父目录文件+子路径构建
-
new File(String parent,String child) //根据父目录+子路径构建
public class FileCreate { public static void main(String[] args) { } //方式1 new File(String pathname) @Test public void create01() { String filePath = "/Users/henghengzhu/Downloads/代码/news1.txt"; File file = new File(filePath); try { file.createNewFile(); System.out.println("文件创建成功"); } catch (IOException e) { e.printStackTrace(); } } //方式2 new File(File parent,String child) //根据父目录文件+子路径构建 @Test public void create02() { File parentFile = new File("/Users/henghengzhu/Downloads/代码"); String fileName = "news2.txt"; //这里的file对象,在java程序中,只是一个对象 //只有执行了createNewFile 方法,才会真正的,在磁盘创建该文件 File file = new File(parentFile, fileName); try { file.createNewFile(); System.out.println("创建成功~"); } catch (IOException e) { e.printStackTrace(); } } //方式3 new File(String parent,String child) //根据父目录+子路径构建 @Test public void create03() { //String parentPath = "e:\\"; String parentPath = "/Users/henghengzhu/Downloads/"; String fileName = "news3.txt"; File file = new File(parentPath, fileName); try { file.createNewFile(); System.out.println("创建成功~"); } catch (IOException e) { e.printStackTrace(); } } }
-
-
常用方法:
-
file.getName()
-
file.getAbsolutePath()
-
file.getParent()
-
file.exists()
-
file.isFile()
-
file.isFile()
-
isDirectory()
@Test public void info() { //先创建文件对象 File file = new File("e:\\news1.txt"); //调用相应的方法,得到对应信息 System.out.println("文件名字=" + file.getName()); //getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory System.out.println("文件绝对路径=" + file.getAbsolutePath()); System.out.println("文件父级目录=" + file.getParent()); System.out.println("文件大小(字节)=" + file.length()); System.out.println("文件是否存在=" + file.exists());//T System.out.println("是不是一个文件=" + file.isFile());//T System.out.println("是不是一个目录=" + file.isDirectory());//F }
-
16.1 IO流原理及分类
-
基本介绍:
-
I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输
-
Java程序中,对于数据的输入/输出操作以"流(stream)"的方式进行
-
java.io包下提供了各种"流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据
-
输入input:读取外部数据(磁盛、光盘等存储设备的数据)到程序(内存)中
-
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
-
-
流的分类:
-
按操作数据单位不同分为:字节流(8 bit),字符流(按字符)
-
按数据流的流向不同分为:输入流,输出流
-
按流的角色的不同分为:节点流,处理流/包装流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EA4vw5jF-1692079275407)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-07%2019.14.25.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dQLIzqFT-1692079275407)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-07%2019.26.09.jpg)]
-
16.2 节点流和处理流
-
基本介绍:
-
节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter
-
处理流(也叫包装流)是 “连接〞在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,如BufferedReader、BufferedWriter
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tU4Rtdno-1692079275408)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-12%2019.34.04.jpg)]
-
-
区别和联系:
- 节点流是底层流/低级流,直接跟数据源相接
- 处理流包装节点流,既可以消除不同节点流的实现差异,也可以提供重方便的方法来完成输入输出
- 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连
-
优点:
- 性能的提高:主要以增加缓冲的方式来提高输入输出的效率
- 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用重加灵活方便
-
模拟:
public class Test_ { public static void main(String[] args) { BufferedReader_ bufferedReader_ = new BufferedReader_(new FileReader_()); bufferedReader_.readFiles(10); BufferedReader_ bufferedReader_2 = new BufferedReader_(new StringReader_()); bufferedReader_2.readString(5); } }
public abstract class Reader_ { //抽象类 public void readFile() { } public void readString() { } //在Reader_ 抽象类,使用read方法统一管理. //后面在调用时,利于对象动态绑定机制, 绑定到对应的实现子类即可. //public abstract void read(); } class FileReader_ extends Reader_ { public void readFile() { System.out.println("读取文件"); } } class StringReader_ extends Reader_ { public void readString() { System.out.println("读取字符串"); } }
public class BufferedReader_ extends Reader_{ private Reader_ reader_; //属性是 Reader_类型 //接收Reader_ 子类对象 public BufferedReader_(Reader_ reader_) { this.reader_ = reader_; } public void readFile() { //封装一层 reader_.readFile(); } //让方法更加灵活, 多次读取文件, 或者加缓冲byte[] .... public void readFiles(int num) { for(int i = 0; i < num; i++) { reader_.readFile(); } } //扩展 readString, 批量处理字符串数据 public void readStrings(int num) { for(int i = 0; i <num; i++) { reader_.readString(); } } }
16.3 输入流⭐️
16.3.1 InputStream
16.3.1.1 FileInputStream
-
常用方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PA6JZkji-1692079275408)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/IMG_2838.jpg)]
-
使用方法:
public class FileInputStream_ { public static void main(String[] args) { } /** * 演示读取文件... * 单个字节的读取,效率比较低 * -> 使用 read(byte[] b) */ @Test public void readFile01() { String filePath = "e:\\hello.txt"; int readData = 0; FileInputStream fileInputStream = null; try { //创建 FileInputStream 对象,用于读取 文件 fileInputStream = new FileInputStream(filePath); //从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。 //如果返回-1 , 表示读取完毕 while ((readData = fileInputStream.read()) != -1) { System.out.print((char)readData);//转成char显示 } } catch (IOException e) { e.printStackTrace(); } finally { //关闭文件流,释放资源. try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 使用 read(byte[] b) 读取文件,提高效率 */ @Test public void readFile02() { String filePath = "e:\\hello.txt"; //字节数组 byte[] buf = new byte[8]; //一次读取8个字节. int readLen = 0; FileInputStream fileInputStream = null; try { //创建 FileInputStream 对象,用于读取 文件 fileInputStream = new FileInputStream(filePath); //从该输入流读取最多b.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。 //如果返回-1 , 表示读取完毕 //如果读取正常, 返回实际读取的字节数 while ((readLen = fileInputStream.read(buf)) != -1) { System.out.print(new String(buf, 0, readLen));//显示 } } catch (IOException e) { e.printStackTrace(); } finally { //关闭文件流,释放资源. try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
16.3.1.2 BufferedInputStream
-
基本介绍:
Bufferedlnputstream是字节流在创建 Bufferedlnputstream时,会创建一个内部缓沖区数组
-
常用方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4FjM19qI-1692079275408)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/IMG_2858.jpg)]
-
例:
public class BufferedCopy02 { public static void main(String[] args) { // String srcFilePath = "e:\\Koala.jpg"; // String destFilePath = "e:\\hsp.jpg"; // String srcFilePath = "e:\\0245_韩顺平零基础学Java_引出this.avi"; // String destFilePath = "e:\\hsp.avi"; String srcFilePath = "e:\\a.java"; String destFilePath = "e:\\a3.java"; //创建BufferedOutputStream对象BufferedInputStream对象 BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //因为 FileInputStream 是 InputStream 子类 bis = new BufferedInputStream(new FileInputStream(srcFilePath)); bos = new BufferedOutputStream(new FileOutputStream(destFilePath)); //循环的读取文件,并写入到 destFilePath byte[] buff = new byte[1024]; int readLen = 0; //当返回 -1 时,就表示文件读取完毕 while ((readLen = bis.read(buff)) != -1) { bos.write(buff, 0, readLen); } System.out.println("文件拷贝完毕~~~"); } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 , 关闭外层的处理流即可,底层会去关闭节点流 try { if(bis != null) { bis.close(); } if(bos != null) { bos.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
16.3.1.3 ObjectInputStream
-
基本介绍:
- Objectlnputstream 提供反序列化功能
- 反序列化就是在恢复数据时,恢复数据的值和数据类型
- 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable和Externalizable
-
例:
public class ObjectInputStream_ { public static void main(String[] args) throws IOException, ClassNotFoundException { //指定反序列化的文件 String filePath = "e:\\data.dat"; ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath)); //解读 //1. 读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致 //2. 否则会出现异常 System.out.println(ois.readInt()); System.out.println(ois.readBoolean()); System.out.println(ois.readChar()); System.out.println(ois.readDouble()); System.out.println(ois.readUTF()); //dog 的编译类型是 Object , dog 的运行类型是 Dog Object dog = ois.readObject(); System.out.println("运行类型=" + dog.getClass()); System.out.println("dog信息=" + dog);//底层 Object -> Dog //这里是特别重要的细节: //1. 如果我们希望调用Dog的方法, 需要向下转型 //2. 需要我们将Dog类的定义,放在到可以引用的位置 Dog dog2 = (Dog)dog; System.out.println(dog2.getName()); //旺财.. //关闭流, 关闭外层流即可,底层会关闭 FileInputStream 流 ois.close(); } }
16.3.2 Reader
16.3.2.1 FileReader
-
常用方法:
- read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
- read(char):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1
-
方法使用:
public class FileReader_ { public static void main(String[] args) { } /** * 单个字符读取文件 */ @Test public void readFile01() { String filePath = "e:\\story.txt"; FileReader fileReader = null; int data = 0; //1. 创建FileReader对象 try { fileReader = new FileReader(filePath); //循环读取 使用read, 单个字符读取 while ((data = fileReader.read()) != -1) { System.out.print((char) data); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileReader != null) { fileReader.close(); } } catch (IOException e) { e.printStackTrace(); } } } /** * 字符数组读取文件 */ @Test public void readFile02() { System.out.println("~~~readFile02 ~~~"); String filePath = "e:\\story.txt"; FileReader fileReader = null; int readLen = 0; char[] buf = new char[8]; //1. 创建FileReader对象 try { fileReader = new FileReader(filePath); //循环读取 使用read(buf), 返回的是实际读取到的字符数 //如果返回-1, 说明到文件结束 while ((readLen = fileReader.read(buf)) != -1) { System.out.print(new String(buf, 0, readLen)); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileReader != null) { fileReader.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
16.3.2.2 BufferedReader
-
例:
public class BufferedReader_ { public static void main(String[] args) throws Exception { String filePath = "e:\\a.java"; //创建bufferedReader BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath)); //读取 String line; //按行读取, 效率高 //说明 //1. bufferedReader.readLine() 是按行读取文件 //2. 当返回null 时,表示文件读取完毕 while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } //关闭流, 这里注意,只需要关闭 BufferedReader ,因为底层会自动的去关闭 节点流 //FileReader。 /* public void close() throws IOException { synchronized (lock) { if (in == null) return; try { in.close();//in 就是我们传入的 new FileReader(filePath), 关闭了. } finally { in = null; cb = null; } } } */ bufferedReader.close(); } }
16.3.2.3 InputStreamReader
-
基本介绍:
-
InputStreamReader:Reader的子类,可以将lnputStream(字节流)包装成Reader(字符流)
-
当处理纯文本数据时,如果使用宇符流效率重高,井且可以有效解決中文问题,所以建议字节流转换成字符流
-
-
例:
public class InputStreamReader_ { public static void main(String[] args) throws IOException { String filePath = "e:\\a.txt"; //解读 //1. 把 FileInputStream 转成 InputStreamReader //2. 指定编码 gbk //InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk"); //3. 把 InputStreamReader 传入 BufferedReader //BufferedReader br = new BufferedReader(isr); //将2 和 3 合在一起 BufferedReader br = new BufferedReader(new InputStreamReader( new FileInputStream(filePath), "gbk")); //4. 读取 String s = br.readLine(); System.out.println("读取内容=" + s); //5. 关闭外层流 br.close(); } }
16.4 输出流⭐️
-
基本介绍:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UzCnVzSA-1692079275409)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-14%2010.49.52.jpg)]
16.4.1 OutputStream
16.4.1.1 FileOutputStream
-
常用方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4P9aU8YZ-1692079275409)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/IMG_2848.jpg)]
-
使用方法:
public class FileOutputStream01 { public static void main(String[] args) { } /** * 演示使用FileOutputStream 将数据写到文件中, * 如果该文件不存在,则创建该文件 */ @Test public void writeFile() { //创建 FileOutputStream对象 String filePath = "e:\\a.txt"; FileOutputStream fileOutputStream = null; try { //得到 FileOutputStream对象 //说明 //1. new FileOutputStream(filePath) 创建方式,当写入内容是,会覆盖原来的内容 //2. new FileOutputStream(filePath, true) 创建方式,当写入内容是,是追加到文件后面 fileOutputStream = new FileOutputStream(filePath, true); //写入一个字节 //fileOutputStream.write('H');// //写入字符串 String str = "hsp,world!"; //str.getBytes() 可以把 字符串-> 字节数组 //fileOutputStream.write(str.getBytes()); /* write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流 */ fileOutputStream.write(str.getBytes(), 0, 3); } catch (IOException e) { e.printStackTrace(); } finally { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
16.4.1.2 BufferedOutputStream
BufferedOutputstream是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流中,而不必对每次字节写入调用底层
系统
-
常用方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d4MqR6o3-1692079275409)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/IMG_2859.jpg)]
-
例:
public class BufferedCopy02 { public static void main(String[] args) { // String srcFilePath = "e:\\Koala.jpg"; // String destFilePath = "e:\\hsp.jpg"; // String srcFilePath = "e:\\0245_韩顺平零基础学Java_引出this.avi"; // String destFilePath = "e:\\hsp.avi"; String srcFilePath = "e:\\a.java"; String destFilePath = "e:\\a3.java"; //创建BufferedOutputStream对象BufferedInputStream对象 BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //因为 FileInputStream 是 InputStream 子类 bis = new BufferedInputStream(new FileInputStream(srcFilePath)); bos = new BufferedOutputStream(new FileOutputStream(destFilePath)); //循环的读取文件,并写入到 destFilePath byte[] buff = new byte[1024]; int readLen = 0; //当返回 -1 时,就表示文件读取完毕 while ((readLen = bis.read(buff)) != -1) { bos.write(buff, 0, readLen); } System.out.println("文件拷贝完毕~~~"); } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 , 关闭外层的处理流即可,底层会去关闭节点流 try { if(bis != null) { bis.close(); } if(bos != null) { bos.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
16.4.1.3 ObjectOutputStream
-
基本介绍:
-
Objectoutputstream 提供序列化功能
-
序列化就是在保存数据时,保存数据的值和数据类型
-
列化就是在恢复数据时,恢复数据的值和数据类型
-
需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable和Externalizable
-
-
例:
public class ObjectOutStream_ { public static void main(String[] args) throws Exception { //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存 String filePath = "e:\\data.dat"; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath)); //序列化数据到 e:\data.dat oos.writeInt(100);// int -> Integer (实现了 Serializable) oos.writeBoolean(true);// boolean -> Boolean (实现了 Serializable) oos.writeChar('a');// char -> Character (实现了 Serializable) oos.writeDouble(9.5);// double -> Double (实现了 Serializable) oos.writeUTF("韩顺平教育");//String //保存一个dog对象 oos.writeObject(new Dog("旺财", 10, "日本", "白色")); oos.close(); System.out.println("数据保存完毕(序列化形式)"); } }
-
注意事项和细节:
- 读写顺序要一致
- 要求实现序列化或反序列化对象,需要实现 Serializable
- 序列化的类中建议添加SerialVersionUID,为了提高版本兼容性
- 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
- 序列化对象时,要求里面属性的类型也需要实现序列化接口
- 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也己经默认实现了序列化
16.4.1.4 PrintStream
-
例:
public class PrintStream_ { public static void main(String[] args) throws IOException { PrintStream out = System.out; //在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器 /* public void print(String s) { if (s == null) { s = "null"; } write(s); } */ out.print("john, hello"); //因为print底层使用的是write , 所以我们可以直接调用write进行打印/输出 out.write("韩顺平,你好".getBytes()); out.close(); //我们可以去修改打印流输出的位置/设备 //1. 输出修改成到 "e:\\f1.txt" //2. "hello, 韩顺平教育~" 就会输出到 e:\f1.txt //3. public static void setOut(PrintStream out) { // checkIO(); // setOut0(out); // native 方法,修改了out // } System.setOut(new PrintStream("e:\\f1.txt")); System.out.println("hello, 韩顺平教育~"); } }
16.4.2 Writer
16.4.2.1 FileWriter
-
常用方法:
- new File Writer (File/String):覆盖模式,相当于流的指针在首端
- new FileWriter (File/String.true):追加模式,相当于流的指针在尾端
- write(int):写入单个字符
- write(char[]):写入指定数组
- write(char[],off len):写入指定数组的指定部分
- write (string):写入整个字符律
- write(string,off,len):写入字符串的指定部分
-
方法使用:
public class FileWriter_ { public static void main(String[] args) { String filePath = "e:\\note.txt"; //创建FileWriter对象 FileWriter fileWriter = null; char[] chars = {'a', 'b', 'c'}; try { fileWriter = new FileWriter(filePath);//默认是覆盖写入 // 3) write(int):写入单个字符 fileWriter.write('H'); // 4) write(char[]):写入指定数组 fileWriter.write(chars); // 5) write(char[],off,len):写入指定数组的指定部分 fileWriter.write("韩顺平教育".toCharArray(), 0, 3); // 6) write(string):写入整个字符串 fileWriter.write(" 你好北京~"); fileWriter.write("风雨之后,定见彩虹"); // 7) write(string,off,len):写入字符串的指定部分 fileWriter.write("上海天津", 0, 2); //在数据量大的情况下,可以使用循环操作. } catch (IOException e) { e.printStackTrace(); } finally { //对应FileWriter , 一定要关闭流,或者flush才能真正的把数据写入到文件 //老韩看源码就知道原因. /* 看看代码 private void writeBytes() throws IOException { this.bb.flip(); int var1 = this.bb.limit(); int var2 = this.bb.position(); assert var2 <= var1; int var3 = var2 <= var1 ? var1 - var2 : 0; if (var3 > 0) { if (this.ch != null) { assert this.ch.write(this.bb) == var3 : var3; } else { this.out.write(this.bb.array(), this.bb.arrayOffset() + var2, var3); } } this.bb.clear(); } */ try { //fileWriter.flush(); //关闭文件流,等价 flush() + 关闭 fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("程序结束..."); } }
-
文件拷贝:
public class FileCopy { public static void main(String[] args) { //完成 文件拷贝,将 e:\\Koala.jpg 拷贝 c:\\ //思路分析 //1. 创建文件的输入流 , 将文件读入到程序 //2. 创建文件的输出流, 将读取到的文件数据,写入到指定的文件. String srcFilePath = "e:\\Koala.jpg"; String destFilePath = "e:\\Koala3.jpg"; FileInputStream fileInputStream = null; FileOutputStream fileOutputStream = null; try { fileInputStream = new FileInputStream(srcFilePath); fileOutputStream = new FileOutputStream(destFilePath); //定义一个字节数组,提高读取效果 byte[] buf = new byte[1024]; int readLen = 0; while ((readLen = fileInputStream.read(buf)) != -1) { //读取到后,就写入到文件 通过 fileOutputStream //即,是一边读,一边写 fileOutputStream.write(buf, 0, readLen);//一定要使用这个方法 } System.out.println("拷贝ok~"); } catch (IOException e) { e.printStackTrace(); } finally { try { //关闭输入流和输出流,释放资源 if (fileInputStream != null) { fileInputStream.close(); } if (fileOutputStream != null) { fileOutputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
-
注意事项:
- FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件
16.4.2.2 BufferedWriter
-
例:
public class BufferedWriter_ { public static void main(String[] args) throws IOException { String filePath = "e:\\ok.txt"; //创建BufferedWriter //说明: //1. new FileWriter(filePath, true) 表示以追加的方式写入 //2. new FileWriter(filePath) , 表示以覆盖的方式写入 BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath)); bufferedWriter.write("hello, 韩顺平教育!"); bufferedWriter.newLine();//插入一个和系统相关的换行 bufferedWriter.write("hello2, 韩顺平教育!"); bufferedWriter.newLine(); bufferedWriter.write("hello3, 韩顺平教育!"); bufferedWriter.newLine(); //说明:关闭外层流即可 , 传入的 new FileWriter(filePath) ,会在底层关闭 bufferedWriter.close(); } }
-
文件拷贝:
public class BufferedCopy_ { public static void main(String[] args) { //1. BufferedReader 和 BufferedWriter 是安装字符操作 //2. 不要去操作 二进制文件[声音,视频,doc, pdf ], 可能造成文件损坏 //BufferedInputStream //BufferedOutputStream String srcFilePath = "e:\\a.java"; String destFilePath = "e:\\a2.java"; // String srcFilePath = "e:\\0245_韩顺平零基础学Java_引出this.avi"; // String destFilePath = "e:\\a2韩顺平.avi"; BufferedReader br = null; BufferedWriter bw = null; String line; try { br = new BufferedReader(new FileReader(srcFilePath)); bw = new BufferedWriter(new FileWriter(destFilePath)); //说明: readLine 读取一行内容,但是没有换行 while ((line = br.readLine()) != null) { //每读取一行,就写入 bw.write(line); //插入一个换行 bw.newLine(); } System.out.println("拷贝完毕..."); } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 try { if(br != null) { br.close(); } if(bw != null) { bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
16.4.2.3 OutputStreamWriter
-
基本介绍:
- OutputStreamWriter:Writer的子类,实现将Outputstream(字节流)包装成Writer(字符流)
- 当处理纯文本数据时,如果使用宇符流效率重高,井且可以有效解決中文问题,所以建议格字节流转快成字符流
- 可以在使用时指定编码格式
-
例:
public class OutputStreamWriter_ { public static void main(String[] args) throws IOException { String filePath = "e:\\hsp.txt"; String charSet = "utf-8"; OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet); BufferedWriter bw = new BufferedWriter(osw); bw.write("hi, 韩顺平教育"); bw.close(); System.out.println("按照 " + charSet + " 保存文件成功~"); } }
16.4.2.4 PrintWrite
-
例:
public class PrintWriter_ { public static void main(String[] args) throws IOException { //PrintWriter printWriter = new PrintWriter(System.out); PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt")); printWriter.print("hi, 北京你好~~~~"); printWriter.close();//flush + 关闭流, 才会将数据写入到文件.. } }
16.5 Properties
-
基本介绍:
-
专门用于读号配置文件的集合类配置文件的格式:键=值
-
键值对不需要有空格,值不需要用引号一起来
-
默认类型是String
-
-
常用方法:
- load:加载配置文件的键值对到Properties对象
- list:将数据显示到指定设备
- getProperty (key):根据键获取值
- setProperty(key value):设置键值对到Properties对象
- store:将Properties中的键值对存储到配置文件,在idea 中,保存信息到配置文件,如果含有中文,会存储为unicode码
-
读取:
public class Properties02 { public static void main(String[] args) throws IOException { //使用Properties 类来读取mysql.properties 文件 //1. 创建Properties 对象 Properties properties = new Properties(); //2. 加载指定配置文件 properties.load(new FileReader("src\\mysql.properties")); //3. 把k-v显示控制台 properties.list(System.out); //4. 根据key 获取对应的值 String user = properties.getProperty("user"); String pwd = properties.getProperty("pwd"); System.out.println("用户名=" + user); System.out.println("密码是=" + pwd); } }
-
配置:
public class Properties03 { public static void main(String[] args) throws IOException { //使用Properties 类来创建 配置文件, 修改配置文件内容 Properties properties = new Properties(); //创建 //1.如果该文件没有key 就是创建 //2.如果该文件有key ,就是修改 /* Properties 父类是 Hashtable , 底层就是Hashtable 核心方法 public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; for(; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value;//如果key 存在,就替换 return old; } } addEntry(hash, key, value, index);//如果是新k, 就addEntry return null; } */ properties.setProperty("charset", "utf8"); properties.setProperty("user", "汤姆");//注意保存时,是中文的 unicode码值 properties.setProperty("pwd", "888888"); //将k-v 存储文件中即可 properties.store(new FileOutputStream("src\\mysql2.properties"), null); System.out.println("保存配置文件成功~"); } }
17 网络编程
17.1 网络基础
-
ip地址:
- 用于唯一标识网络中的每台计算机/主机
- 查看ip地址:ipconfig
- ip地址的表示形式:点分十进制 xx.xx.xx.xx
- 每一个十进制数的范围:0~255
- ip地址的组成=网络地址+主机地址,比如:192.168.16.69
- ilPv6是互联网工程任务组设计的用于替代1PV4的下一代IP协议
-
ipv4地址分类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXl7d6z9-1692079275410)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-20%2010.42.09.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KVMdaRCA-1692079275410)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-20%2010.43.05.jpg)]
-
域名:
- 将ip地址映射成域名
-
端口号:
- 用于标识计算机上某个特定的网络程序
- 表示形式:以整数形式,范围0~65535
- 0~1024己经被占用,比如ssh 22,ftp 21, smtp 25 http 80
- 常见的网络程序端口号:
tomcat :8080
mysql:3306
oracle:1521
sqlserver:1433
-
网络通信协议:
-
TCP/IP (Transmission Control Protocol/Internet Protocol的简写,中文译名为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是internet最基本的协议,internet国际互联网络的基础就是由网络层的IP协议和传输层的TCP协议组成的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nLgaQxHK-1692079275410)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-20%2010.46.15.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fi3V6jfe-1692079275411)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-20%2010.46.39.jpg)]
-
-
TCP:
- 使用TCP协议前,须先建立TCP连接,形成传输数据通道
- 传输前,采用”三次握手"方式,是可靠的
- TCP协议进行通信的两个应用进程:客户端、服务端
- 在连接中可进行大数据量的传输
- 传输完毕,需释放已建立的连接,效率低
-
UDP:
- 格数据源、目的封装成数据包,不需要建立连接
- 每个数据报的大小限制在64K内
- 因无需连接,故是不可靠的
- 发送数据结束时无需释放资源(因为不是面向连接的),速度快
17.2 InetAddress
-
常用方法:
-
获取本机InetAddress对象 getLocalHost
-
根据指定主机名/域名获取ip地址对象 getByName
-
获取InetAddress对象的主机名 getHostName
-
获取InetAddress对象的地址 getHostAddress
public class API_ { public static void main(String[] args) throws UnknownHostException { //1. 获取本机的InetAddress 对象 InetAddress localHost = InetAddress.getLocalHost(); System.out.println(localHost);//DESKTOP-S4MP84S/192.168.12.1 //2. 根据指定主机名 获取 InetAddress对象 InetAddress host1 = InetAddress.getByName("DESKTOP-S4MP84S"); System.out.println("host1=" + host1);//DESKTOP-S4MP84S/192.168.12.1 //3. 根据域名返回 InetAddress对象, 比如 www.baidu.com 对应 InetAddress host2 = InetAddress.getByName("www.baidu.com"); System.out.println("host2=" + host2);//www.baidu.com / 110.242.68.4 //4. 通过 InetAddress 对象,获取对应的地址 String hostAddress = host2.getHostAddress();//IP 110.242.68.4 System.out.println("host2 对应的ip = " + hostAddress);//110.242.68.4 //5. 通过 InetAddress 对象,获取对应的主机名/或者的域名 String hostName = host2.getHostName(); System.out.println("host2对应的主机名/域名=" + hostName); // www.baidu.com } }
-
17.3 Socket
-
基本介绍:
- 通信的两端都要有Socket,是两台机器间通信的端点
- Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输
- 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端
-
TCP网络通信编程:
-
基于客户端——服务端的网络通信
-
底层使用TCP/IP协议
-
基于Socket的TCP编程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-51WeFcdq-1692079275411)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-06-20%2011.27.52.jpg)]
-
17.4 TCP编程⭐️
-
服务端:
public class SocketTCP03Server { public static void main(String[] args) throws IOException { //思路 //1. 在本机 的9999端口监听, 等待连接 // 细节: 要求在本机没有其它服务在监听9999 // 细节:这个 ServerSocket 可以通过 accept() 返回多个Socket[多个客户端连接服务器的并发] ServerSocket serverSocket = new ServerSocket(9999); System.out.println("服务端,在9999端口监听,等待连接.."); //2. 当没有客户端连接9999端口时,程序会 阻塞, 等待连接 // 如果有客户端连接,则会返回Socket对象,程序继续 Socket socket = serverSocket.accept(); System.out.println("服务端 socket =" + socket.getClass()); //3. 通过socket.getInputStream() 读取客户端写入到数据通道的数据, 显示 InputStream inputStream = socket.getInputStream(); //4. IO读取, 使用字符流, 老师使用 InputStreamReader 将 inputStream 转成字符流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String s = bufferedReader.readLine(); System.out.println(s);//输出 //5. 获取socket相关联的输出流 OutputStream outputStream = socket.getOutputStream(); // 使用字符输出流的方式回复信息 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); bufferedWriter.write("hello client 字符流"); bufferedWriter.newLine();// 插入一个换行符,表示回复内容的结束 bufferedWriter.flush();//注意需要手动的flush //6.关闭流和socket bufferedWriter.close(); bufferedReader.close(); socket.close(); serverSocket.close();//关闭 } }
-
客户端:
public class SocketTCP03Client { public static void main(String[] args) throws IOException { //思路 //1. 连接服务端 (ip , 端口) //解读: 连接本机的 9999端口, 如果连接成功,返回Socket对象 Socket socket = new Socket(InetAddress.getLocalHost(), 9999); System.out.println("客户端 socket返回=" + socket.getClass()); //2. 连接上后,生成Socket, 通过socket.getOutputStream() // 得到 和 socket对象关联的输出流对象 OutputStream outputStream = socket.getOutputStream(); //3. 通过输出流,写入数据到 数据通道, 使用字符流 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); bufferedWriter.write("hello, server 字符流"); bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束, 注意,要求对方使用readLine()!!!! bufferedWriter.flush();// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道 //4. 获取和socket关联的输入流. 读取数据(字符),并显示 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String s = bufferedReader.readLine(); System.out.println(s); //5. 关闭流对象和socket, 必须关闭 bufferedReader.close();//关闭外层流 bufferedWriter.close(); socket.close(); System.out.println("客户端退出....."); } }
-
图片拷贝
public class TCPFileUploadServer { public static void main(String[] args) throws Exception { //1. 服务端在本机监听8888端口 ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务端在8888端口监听...."); //2. 等待连接 Socket socket = serverSocket.accept(); //3. 读取客户端发送的数据 // 通过Socket得到输入流 BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); byte[] bytes = StreamUtils.streamToByteArray(bis); //4. 将得到 bytes 数组,写入到指定的路径,就得到一个文件了 String destFilePath = "src\\abc.mp4"; BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath)); bos.write(bytes); bos.close(); // 向客户端回复 "收到图片" // 通过socket 获取到输出流(字符) BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); writer.write("收到图片"); writer.flush();//把内容刷新到数据通道 socket.shutdownOutput();//设置写入结束标记 //关闭其他资源 writer.close(); bis.close(); socket.close(); serverSocket.close(); } }
public class TCPFileUploadClient { public static void main(String[] args) throws Exception { //客户端连接服务端 8888,得到Socket对象 Socket socket = new Socket(InetAddress.getLocalHost(), 8888); //创建读取磁盘文件的输入流 //String filePath = "e:\\qie.png"; String filePath = "e:\\abc.mp4"; BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath)); //bytes 就是filePath对应的字节数组 byte[] bytes = StreamUtils.streamToByteArray(bis); //通过socket获取到输出流, 将bytes数据发送给服务端 BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); bos.write(bytes);//将文件对应的字节数组的内容,写入到数据通道 bis.close(); socket.shutdownOutput();//设置写入数据的结束标记 //=====接收从服务端回复的消息===== InputStream inputStream = socket.getInputStream(); //使用StreamUtils 的方法,直接将 inputStream 读取到的内容 转成字符串 String s = StreamUtils.streamToString(inputStream); System.out.println(s); //关闭相关的流 inputStream.close(); bos.close(); socket.close(); } }
-
音乐拷贝:
public class Homework03Server { public static void main(String[] args) throws Exception { //1 监听 9999端口 ServerSocket serverSocket = new ServerSocket(9999); //2.等待客户端连接 System.out.println("服务端,在9999端口监听,等待下载文件"); Socket socket = serverSocket.accept(); //3.读取 客户端发送要下载的文件名 // 这里老师使用了while读取文件名,时考虑将来客户端发送的数据较大的情况 InputStream inputStream = socket.getInputStream(); byte[] b = new byte[1024]; int len = 0; String downLoadFileName = ""; while ((len = inputStream.read(b)) != -1) { downLoadFileName += new String(b, 0 , len); } System.out.println("客户端希望下载文件名=" + downLoadFileName); //老师在服务器上有两个文件, 无名.mp3 高山流水.mp3 //如果客户下载的是 高山流水 我们就返回该文件,否则一律返回 无名.mp3 String resFileName = ""; if("高山流水".equals(downLoadFileName)) { resFileName = "src\\高山流水.mp3"; } else { resFileName = "src\\无名.mp3"; } //4. 创建一个输入流,读取文件 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(resFileName)); //5. 使用工具类StreamUtils ,读取文件到一个字节数组 byte[] bytes = StreamUtils.streamToByteArray(bis); //6. 得到Socket关联的输出流 BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); //7. 写入到数据通道,返回给客户端 bos.write(bytes); socket.shutdownOutput();//很关键. //8 关闭相关的资源 bis.close(); inputStream.close(); socket.close(); serverSocket.close(); System.out.println("服务端退出..."); } }
public class Homework03Client { public static void main(String[] args) throws Exception { //1. 接收用户输入,指定下载文件名 Scanner scanner = new Scanner(System.in); System.out.println("请输入下载文件名"); String downloadFileName = scanner.next(); //2. 客户端连接服务端,准备发送 Socket socket = new Socket(InetAddress.getLocalHost(), 9999); //3. 获取和Socket关联的输出流 OutputStream outputStream = socket.getOutputStream(); outputStream.write(downloadFileName.getBytes()); //设置写入结束的标志 socket.shutdownOutput(); //4. 读取服务端返回的文件(字节数据) BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); byte[] bytes = StreamUtils.streamToByteArray(bis); //5. 得到一个输出流,准备将 bytes 写入到磁盘文件 //比如你下载的是 高山流水 => 下载的就是 高山流水.mp3 // 你下载的是 韩顺平 => 下载的就是 无名.mp3 文件名 韩顺平.mp3 String filePath = "e:\\" + downloadFileName + ".mp3"; BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath)); bos.write(bytes); //6. 关闭相关的资源 bos.close(); bis.close(); outputStream.close(); socket.close(); System.out.println("客户端下载完毕,正确退出.."); } }
-
netstat指令:
- netstat-an 可以查看当前主机网络情况,包括端口监听情况和网络连接情况
- netstat -an l more 可以分页显示
- 要求在dos控制台下执行
17.5 UDP编程
-
基本介绍:
- 类 DatagramSocket 和 DatagramPacket 实现了基于 UDP 协议网络程序
- UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达
- DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号
- UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接
-
基本流程:
-
核心的两个类/对象 Datagramsocket与DatagramPacket
-
建立发送端,接收端
-
建立数据包
-
调用DatagramSocket的发送、接收方法
-
关网DatagramSocket
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zJ9cP3j7-1692079275411)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/IMG_2889.jpg)]
-
-
注意事项和细节:
- 没有明确的服务端和客户端,演变成数据的发送端和接收端
- 接收数据和发送数据是通过 DatagramSocket 对象完成
- 将数据封装到DatagramPacket 对象/ 装包
- 当接收到 DatagramPacket 对象,需要进行拆包,取出数据
- DatagramSocket 可以指定在哪个端口接收数据
-
例:
public class UDPReceiverA { public static void main(String[] args) throws IOException { //1. 创建一个 DatagramSocket 对象,准备在9999接收数据 DatagramSocket socket = new DatagramSocket(9999); //2. 构建一个 DatagramPacket 对象,准备接收数据 // 在前面讲解UDP 协议时,老师说过一个数据包最大 64k byte[] buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf, buf.length); //3. 调用 接收方法, 将通过网络传输的 DatagramPacket 对象 // 填充到 packet对象 //老师提示: 当有数据包发送到 本机的9999端口时,就会接收到数据 // 如果没有数据包发送到 本机的9999端口, 就会阻塞等待. System.out.println("接收端A 等待接收数据.."); socket.receive(packet); //4. 可以把packet 进行拆包,取出数据,并显示. int length = packet.getLength();//实际接收到的数据字节长度 byte[] data = packet.getData();//接收到数据 String s = new String(data, 0, length); System.out.println(s); //===回复信息给B端 //将需要发送的数据,封装到 DatagramPacket对象 data = "好的, 明天见".getBytes(); //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口 packet = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9998); socket.send(packet);//发送 //5. 关闭资源 socket.close(); System.out.println("A端退出..."); } }
public class UDPSenderB { public static void main(String[] args) throws IOException { //1.创建 DatagramSocket 对象,准备在9998端口 接收数据 DatagramSocket socket = new DatagramSocket(9998); //2. 将需要发送的数据,封装到 DatagramPacket对象 byte[] data = "hello 明天吃火锅~".getBytes(); // //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口 DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9999); socket.send(packet); //3.=== 接收从A端回复的信息 //(1) 构建一个 DatagramPacket 对象,准备接收数据 // 在前面讲解UDP 协议时,老师说过一个数据包最大 64k byte[] buf = new byte[1024]; packet = new DatagramPacket(buf, buf.length); //(2) 调用 接收方法, 将通过网络传输的 DatagramPacket 对象 // 填充到 packet对象 //老师提示: 当有数据包发送到 本机的9998端口时,就会接收到数据 // 如果没有数据包发送到 本机的9998端口, 就会阻塞等待. socket.receive(packet); //(3) 可以把packet 进行拆包,取出数据,并显示. int length = packet.getLength();//实际接收到的数据字节长度 data = packet.getData();//接收到数据 String s = new String(data, 0, length); System.out.println(s); //关闭资源 socket.close(); System.out.println("B端退出"); } }