多线程创建方式
第二种方式—实现Runnab接口
- 1)自定义一个类实现Runnable接口,
- 2)实现接口里面的run方法—>完成耗时操作
- 3)在main用户线程中创建当前这个类的实例—>“资源类对象”
- 4)创建线程类Thread对象,然后将3)资源类对象 作为参数传递,启动线程!
同步方法
什么是同步方法(非静态)?
如果一个方法中第一句话是一个同步代码块,可以将synchronized关键字定义在声明上
权限修饰符 synchronized 返回值类型 方法名(参数列表){
…
}
龟兔赛跑例子
/* 赛道---->资源类对象
*/
public class Race implements Runnable{
//声明String 胜利者
private static String winner ;
@Override
public void run() {
//距离---定义0-199步
for(int x = 0 ; x <200 ; x++){//x就是步数
//如果线程名称是兔子,需要让它睡眠
if(Thread.currentThread().getName().equals("兔子") && (x%10==0)){
//睡眠给5毫秒
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"跑了---->"+x+"步");
//调用一个方法,比赛是否结束
boolean flag = gameOver(x) ;//传入的步数
if(flag){
break;
}
}
}
//定了这个方法,必须是否结束
public boolean gameOver(int x) {//步数
if(winner!=null){
//已经有胜利者了结束
return true ;
}{//局部代码块
//判断x的步数
if(x>=199){
//给winner赋值
winner = Thread.currentThread().getName() ;
//打印胜利者
System.out.println("winer is--->"+winner);
return true ;
}
}
return false ;
}
//用户线程
public static void main(String[] args) {
//创建一个赛道
Race race = new Race() ;
//创建两个线程
//兔子和乌龟共用一个赛道
Thread t1 = new Thread(race,"兔子") ;
Thread t2 = new Thread(race,"乌龟") ;
//启动线程
t1.start() ;
t2.start() ;
}
}
死锁问题
两个线程出现了一种互相等待的情况,A线程等待B线程释放锁,B线程等待A线程释放锁,造成死锁现象!
解决死锁问题—>线程之间的通信必须使用的同一个资源! (生产者和消费者模式思想)
代理设计模式
- 代理核心思想:
真实角色专注自己的事情(开发中,针对自己的业务)
代理角色帮助真实完成一件事情 - 静态代理
代理角色和真实角色要实现同一个接口 - 动态代理:
jdk动态代理
cglib动态代理(需要导入cglib.jar包)
生产者消费者思想
Lock接口
Lock这个接口
- Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作
- 实现类:
可重入的互斥锁 java.util.concurrent.locks.ReentrantLock
获取锁:指定的某个时刻 public void lock()
释放锁 : public void unlock()
Lock l = …;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock(); //释放锁(系统相关的资源)
}
注意:finally代码一定实现
线程池
会创建一些固定的可重复使用的线程数,会在线程池中,循环利用
当某些线程使用完毕,不会被释放掉,而是归还连接池中,等待下一次再去利用!
- 成本比普通创建线程方式要大!
- java.util.concurrent.Exceutors 工厂类
- public static ExecutorService newFixedThreadPool(int nThreads)
- 创建固定的可重复的线程数的线程池
- java.util.concurrent ExecutorService ----->接口的具体实现类 public class ThreadPoolExecutor
- Future submit(Callable task):提交异步任务,返回值就是异步任务计算的结果;
- 上面这个的返回值Future :异步计算的结果—如果现在只是看多个线程是否能够并发的去强转CPU执行权,并没有返回结果
- 这个返回值可以不用返回!
- Callable:接口---->异步任务的执行 类似于 之前Runnable接口
- void shutdown():关闭线程池
Timer计时器
java.util.Timer:定时器(可以执行一次或者重复执行某个任务)
- 构造方法:
Timer():创建一个计时器 - 成员方法
public void cancel()取消定时器
void schedule(TimerTask task, Date time) :在给定的日期时间来执行TimerTask定时任务 —>
String dataStr = “2022-11-22 18:00” ;//---->日期文本---->java.util.Date
(应用场景:引入使用io流的方式,在指定时间点上,删除指定带内容的目录里面的所有.java文件)
void schedule(TimerTask task, long delay) :在给定多少毫秒后(延迟时间)执行这个定时任务
public void schedule(TimerTask task,long delay,long period):在指定延迟时间(delay)执行任务,
然后每经过固定延迟时间(period)重复执行任务
schedule这些方法第一个参数都是定时任务:TimerTask是一个抽象类, - 1)可以定义具体的子类继承自TimerTask
- 2)直接可以使用抽象类的匿名内部类
JAVA中的文件及文件夹等操作
- 基本功能:
创建文件/文件夹
public boolean createNewFile()throws IOException:创建文件,如果不存在,创建,返回true
public boolean mkdir():创建文件夹,如果存在了,则返回false;否则true
public boolean mkdirs():创建多级目录,当父目录不存在的时候创建 - 判断
public boolean isFile():是否是文件 使用居多
public boolean isDirectory():是否是文件夹 使用居多
public boolean isAbsolute():是否为绝对路径
public boolean exists():判断文件或者目录是否存在* - 删除
public boolean delete():删除由此抽象路径名表示的文件或目录 (删除目录,目录必须为空)
io流中File的高级功能
- public File[] listFiles():获取指定抽象路径表示下的所有的File数组 推荐---->使用File的功能进行判断
- public String[] list():抽象路径名表示的目录中的文件和目录。
/* 需求:
* 获取D盘下所有的以.jpg结尾文件--->输出文件名称
*/
public class FileTest {
public static void main(String[] args) {
//1)描述磁盘上抽象路径的表示d://
File file = new File("D://") ;
//public String[] list():抽象路径名表示的目录中的文件和目录。
/* String[] strs = file.list();
for(String s:strs){
System.out.println(s) ;
}*/
//public File[] listFiles():获取指定抽象路径表示下的所有的File数组 推荐---->使用File的功能进行判断
File[] files = file.listFiles();
//遍历之前:非空判断,防止空指针异常
if(files!=null){
for(File f :files){
//f---->有文件/文件夹
//判断是文件
if(f.isFile()){
//以.jpg结尾
if(f.getName().endsWith(".jpg")){ System.out.println(f.getName());//String getName():获取File指定的路径的文件或者文件的名称
}
}
}
}
}
}
- public File[] listFiles(FileFilter filter) 获取File数组的时候,就可以直接获取到指定条件的文件名称或者文件夹
参数是一个接口:文件过滤器接口
boolean accept(File pathname):
抽象路径名称所表示的路径是否放在File列表中,取决于返回值 true,否则false,不放在列表中 - public File[] listFiles(FilenameFilter filter):获取File数组的时候,就可以通过文件名称过滤器按照条件进行过滤
FilenameFilter接口
boolean accept(File dir,String name) :参数1:指定的目录
参数2:文件名称
返回值: true,将指定指定目录下的文件放在File列表中;否则false
递归思想
递归:
- 方法调用本身的一种现象! 不是方法嵌套方法
java.lang.Math.max(10,Math.max(20,15)) ; - 伪代码
public void show(int n){//5
while(n<0){
break ;
}
System.out.println(n) ;//5
show(n–);
} - 方法递归:
1)需要有方法
2)有一定规律
3)有方法结束的条件(出口条件),否则 “死递归”
构造方法没有递归
实例:不死神兔
/**
* 有一个很有名的数学逻辑题叫做不死神兔问题。
* 有一对兔子,
* 从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,
* 问第二十个月的兔子对数为多少?
* 规律:
* 兔子的对数
* 第一个月:1
* 第二个月:1
* 第三个月:2
* 第四个月:3
* 第五个月:5
* 第六个月:8
* 底七个月:13
* ....
* 第一个月和第二个月兔子的对数是1
* 从第三个月开始,每一月兔子对数是前两个月兔子对数之和!
*
*
* 使用a,b代表相邻两个月兔子的对数
* 第一个,第二个月 a=1,b=1
* 第二月,第三个月 a=1,b=2
* 第三个月,第四个月 a=2,b=3
* 第四个月,第五个月 a=3,b=5
*
*/
public class Test1 {
public static void main(String[] args) {
//数组的方式去实现
//创建一个数组:动态初始化
int[] arr = new int[20] ; //第二十个月
//第一个月和第二个都是1
arr[0] = 1 ;
arr[1] = 1 ;
//从第三个月开始,每一月兔子对数是前两个月兔子对数之和!
for(int x = 2 ;x<arr.length;x++){
arr[x] = arr[x-1] + arr[x-2] ;
}
//输出最后一个元素
System.out.println("第二十个月兔子对数:"+arr[19]); //6765
System.out.println("-----------------------------------------------------") ;
//递归:定义一个方法
//long time = System.currentTimeMillis() ;
System.out.println("第二十个月的兔子的对数是:"+getRabbit(20));
// long end = System.currentTimeMillis();
// System.out.println("耗时"+(end-time)+"毫秒");
}
//定义一个方法
public static int getRabbit(int n){//代表的第几个月
//第一个月和第二个月兔子的对数是1---->方法递归的出口条件
if(n==1 || n==2){
return 1;
}else {
//从第三个月开始,每一个月兔子对数是前两个月兔子对数之和!
return getRabbit(n-1)+getRabbit(n-2) ;
}
}
}
Io流
Io流的分类
- 1)按流的方向划分
输入流 ---->读
输出流 ---->写 - 2)按流的类型划分–同时按方向划分:
字节流
字节输入流:InputStream—>不能实例化—>具体的子类:针对文件的字节输入流 FileInputStream
字节输出流:OutputStream—>不能实例化—>具体的子类:针对文件的字节输出流 FileOutputStream
字节缓冲流(字节高效流)
字节缓冲输入流:BufferedInputStream
字节缓冲输出流:BufferedInputStream
字符流
字符输入流
字符输出流 - windows系统下通过io流的方式(字节流) 写换行—>“\r\n” 换行符号
- Linux系统:“\r”
异常捕获
- IO流操作的时候,加入异常处理代码格式---->开发中 try…catch…finally 使用捕获一次
字节输出流
字节输入流:InputStream—>读 抽象类
- 提供子类:FileInputStream:针对文件操作的字节输入流
- 1)创建文件字节输入流对象
- 2)读文件
public int read() throws IOException:一次读取一个字节,返回字节数
public int read(byte[] b) throws IOException:一次读取一个字节数组 - 3)释放资源
文件字节输入流一次读取一个字节,将文件内容输出控制台上,中文出现乱码,因为
字节流读取字节的时候—将字节—>强转成char,只考虑英文(abcdxxx)—>97—(char)97,当英文的后面有中文拼接
无法解析强转了—>乱码---->java才提供了字符流
什么时候使用字符流,当使用记事本打开能读懂的就使用字符;打开读不懂,用字节!(读图片文件/视频/音频) - 使用基本字节流一次读取一个字节数组:
public int read(byte[] b) throws IOException:一次读取一个字节数组
代码实现
//针对文件读写复制,一次读取一个字节
public static void copyFile(String srcFile,String destFile){
//操作源文件
FileInputStream fis = new FileInputStream(srcFile) ;
//操作目的文件
FileOutputStream fos = new FileOutputStream(destFile) ;
//一次读取一个字节
int by = 0 ;//读取的字节数
while((by=fis.read())!=-1){
//写字节
fos.write(by) ;
}
//释放资源
fos.close() ;
fis.close() ;
}
- 当需要显示传送时间时
long start = System.currentTimeMillis() ;
// copyFile(“参数1:源文件地址”,“新文件名”) ;
long end = System.currentTimeMillis() ;
System.out.println(“共耗时:”+(end-start)+“毫秒”) ;
}
throw和throws的区别
1)抛出的位置不同
throws抛出在方法声明上
throw抛出在方法体中
2)后面使用异常格式不同
throws 后面跟的异常类名,而且中间逗号隔开,可以抛出多个异常
throw 后面跟的异常对象 new XXXException() ;跟的具体的某一个异常对象
3)处理异常方式不同
针对带有throws的方法,异常处理是交给调用者处理!
针对throw抛出异常的处理,交给方法体中逻辑语句处理! 举例
if(条件表达式){
thrwo new XXException();
}
4)是否抛出的异常的肯定性
throws:表示抛出异常的可能性,执行某段代码,可能出现问题
String的日期文本---->java.util.Date格式---->解析parse (通过SimpleDateFormat)
“2022-11-24”
throw:表示抛出异常的肯定性,执行方法体中某段代码,一定会出现异常!
字符流
Writer:具体的子类
public OutputStreamWriter(OutputStream out):
字符转换输出流(可以将基本字节输出流转换成字符输出流),平台默认的字符集编码(idea,utf-8)
public OutputStreamWriter(OutputStream out,String charsetName):
字符转换输出流 ,指定一个编码字符集
- 写的功能
void write(char[] cbuf)写入一个字符数组。
abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
void write(int c) 写一个字符
void write(String str)写一个字符串
void write(String str, int off, int len) :写入字符串的一部分
Reader:抽象类
具体子类
public InputStreamReader(InputStream in):创建字符转换输入流,以平台默认字符集解码
public InputStreamReader(InputStream in,String charsetName):
创建字符转换输入流对象,指定字符集解码 - read的功能
public int read(char[] cbuf):读取字符数组
public int read():读一个字符
InputStreamReader/OutputStreamWriter:字符转换流弊端:代码格式复杂,不能直接操作文件!
字节缓冲输出流/输入流 (高效字节流)
-
BuffedOutputStream/BufferedInputStream:只是提供一个字节缓冲区,本身就是一个字节数组,不会直接操作文件
操作具体的文件使用都是基本字节流FileInputStream/FileOutputStream -
public BufferedOutputStream(OutputStream out):创建一个字节缓冲输出流对象,默认缓冲区大小(足够大)
-
public BufferedInputStream(InputStream in):创建一个字节缓冲输入流对象,默认缓冲大小
-
BufferedReader:字符缓冲输入流
-
BufferedWriter:字符缓冲输出流
-
他们不能直接操作文件,提供缓冲区让读写效率更高,特有方式
-
BufferedReader一次读取一行/可以作为键盘录入(录入一行字符串内容)
-
字符流针对文本文件(记事本打开能看懂的)–>字符流读写复制
-
1)字符转换流InputStreamReader/OutputStreamWriter 一次读取一个字符/一次读取一个字符数组
-
2)使用字符转换流的便捷类FileReader/FileWriter 可以直击操作文件 一次读取一个字符/一次读取一个字符数组
-
3)使用字符缓冲流BufferedReader/BufferedWriter: 一次读取一个字符/一次读取一个字符数组
-
4)使用字符缓冲流BufferedReader/BufferedWriter :一次读取一行特有方式(推荐!)
举例实现序列化于反序列化
public class IODemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// write() ;
read() ;
}
//反序列化操作:将流数据--->还原成Java对象
private static void read() throws IOException, ClassNotFoundException {
//public ObjectInputStream(InputStream in)throws IOException
//创建反序列化流对象
ObjectInputStream bis = new ObjectInputStream(
new FileInputStream("oos.txt")) ;
//将序列化的流数据--->还原成Java对象
// public final Object readObject()throws IOException, ClassNotFoundException
Person p = (Person)bis.readObject();
System.out.println(p);
}
//序列化操作
private static void write() throws IOException {
//创建一个Java对象
Person p = new Person("高圆圆",44) ;
//public ObjectOutputStream(OutputStream out)
//创建序列化流中
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("oos.txt")) ;
//public final void writeObject(Object obj)
oos.writeObject(p); //java.io.NotSerializableException:当前p对象所在的类型没有是实现序列化接口
//释放资源
oos.close();
}
}
初步网络编程
Properties类
Properties类表示一组持久的属性。
- Properties可以保存到流中或从流中加载。(重要的地方)
- 属性列表中的每个键及其对应的值都是一个字符串。
- 继承Hashtable---->实现Map接口---->存储数据,取出数据----都可以使用Map的方式
- 构造方法:
public Properties():创建空的属性列表
遍历方式
java.util.Properites属性列表有自己的遍历方式—底层基于Map实现的
- 添加元素:
public Object setProperty(String key, String value)
遍历属性列表
public Set stringPropertyNames()获取属性列表中的所有的键
public String getProperty(String key)使用此属性列表中指定的键搜索属性
Properties特有方式
- public void load(Reader reader)throws IOException :
- 将指定文件中的内容加载属性集合列表中(内容键值对 key=value)
- void store(Writer writer, String comments) :将属性列表中的内容保存指定文件中(以"键=值"元素对进行保存)
第一个参数:字符输出/使用字节输出流
第二个参数:属性列表的描述 - 相当于:
打游戏---->进度的加载
关卡的保存
如何读取src(类路径下)xxx.properties配置文件
属性配置文件它里面有颜色标记的,放在src下面的
public class Test3 {
public static void main(String[] args) throws IOException {
//就需要将src下面的xxx.properties内容加载属性列表汇总
//创建属性列表Properties
Properties prop = new Properties() ;
System.out.println(prop);
//如何读src下面的xx.properties
//1)获取当前类的字节码文件对象 --->Class 正在运行的java类对象
// Class clazz = Test3.class ;
//System.out.println(clazz);//class com.qf.properties_01.Test3
//2)Class类---获取类加载器---解析这个里面所有成员(变量/方法..校验)
//public ClassLoader getClassLoader()
// ClassLoader classLoader = clazz.getClassLoader();
//3)ClassLoader---->public InputStream getResourceAsStream(String name):参数名称:就是src下面配置文件名称
//获取资源文件所在输入流对象--->将资源文件的内容读取到了字节输入流中
// InputStream inputStream = classLoader.getResourceAsStream("name.properties");
//一步走
//前提:配置文件必须在src下面
InputStream inputStream = Test3.class.getClassLoader().
getResourceAsStream("name.properties");
//4)将输入流对象的内容加载属性列表中
prop.load(inputStream);
//通过key获取value
String value = prop.getProperty("甲");
System.out.println(value);
String value2 = prop.getProperty("乙");
System.out.println(value2);
//完成自己的业务操作
System.out.println(prop);
}
}
互联网ip地址
- 如何获取计算机的主机名称或者获取计算机的ip地址字符串形式
public static InetAddress getByName(String host)
throws UnknownHostException
通过计算机主机名称或者是字符串形式的ip地址—>获取互联网ip地址对象
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
//public static InetAddress getByName(String host)throws UnknownHostException
//1)可以通过计算机机器名称获取ip地址对象
InetAddress inetAddress = InetAddress.getByName("主机名"); //还可以指定ip地址字符串
System.out.println(inetAddress);//DESKTOP-Q62EUJH/192.168.5.5
//互联网ip地址对象: 主机名称/ip地址字符串
//public String getHostName() :通过ip地址对象获取计算机机器名
String hostName = inetAddress.getHostName() ;
System.out.println(hostName);
//获取里面的包含ip地址字符串形式
// public String getHostAddress()
String ip = inetAddress.getHostAddress();
System.out.println(ip);
}
}
UDP
- 发送代码实现
Udp发送端的步骤
1)创建发送端的socket
2)创建数据报包
3)使用发送端的Socket将数据存储数据包中, 发送(本质存储数据包)
4)释放资源
public class UdpSend {
public static void main(String[] args) throws IOException {
//1)创建发送端的socket
//此类表示用于发送和接收数据报数据包的套接字。 DatagramSocket
//public DatagramSocket() throws SocketException
DatagramSocket ds = new DatagramSocket() ;
// 2)创建数据报包DatagramPacket
/**
* public DatagramPacket(byte[] buf, 要发送数据---转换成字节数组
* int length, 实际字节数长度
* InetAddress address, ip地址对象
* int port) 端口号
*/
byte[] bytes = "hello,UDP我来了".getBytes() ;
int length = bytes.length ;
InetAddress inetAddress = InetAddress.getByName("192.168.1.5");//如果自己玩,没有网线,无线ip不断变的--->本地回环地址127.0.0.1
int port = 10086 ;
DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port);
//3)使用发送端的Socket将数据存储数据包中, 发送(本质存储数据包)
//public void send(DatagramPacket p) throws IOException
ds.send(dp) ;
//4)释放资源
ds.close() ;
}
}
- 接收代码实现
1)创建接收端的Socket对象,绑定端口
2)创建一个接收容器—>数据包—>自定义字节缓冲区,将发送的数据包
3)接收
4)从接收容器中解析数据包的实际内容数据
5)展示数据 - 先运行接收端,而且接收端不能运行多次,否则端口被占用!
public class UDPReceiver {
public static void main(String[] args) throws IOException {
//1)创建接收端的Socket对象,绑定端口
//public DatagramSocket(int port) throws SocketException
DatagramSocket ds = new DatagramSocket(10086) ;
//2)创建一个接收容器--->数据包--->自定义字节缓冲区,将发送的数据包
// public DatagramPacket(byte[] buf, int length)构造一个DatagramPacket用于接收长度的数据包length 。
byte[] bytes = new byte[1024] ;//1024或者1024整数倍
int length = bytes.length ;
DatagramPacket dp = new DatagramPacket(bytes,length) ; //将发送端数据缓冲到这个接收容器中
//3)接收,以上面这个接收容器来接收
//public void receive(DatagramPacket p)throws IOException
ds.receive(dp);
//4)从接收容器中解析数据包的实际内容数据
//从接收容器中获取public byte[] getData() 实际缓冲区的对象(从上bytes分段取数据)
byte[] bytes2 = dp.getData();
//获取里面实际缓冲区的长度
// public int getLength()
int length2 = dp.getLength();
//展示数据---分段取数据,每次从0开始取实际长度
String msg = new String(bytes2,0,length2) ;
//数据包里面获取哪一个ip地址发来的--->ip地址字符串形式
//public InetAddress getAddress()--->InetAddress--->getHostAddress()--->String IP地址字符串
String ip = dp.getAddress().getHostAddress() ;
System.out.println("data from --->"+ip+",发送内容是:"+msg);
//释放资源
ds.close();
}
}