一、Map集合
1.Map集合的基本功能
V put(K key,V value) :添加方法---返回值什么意思?
通过返回值是否为null,判断键是否重复,如果不重复,返回值永远是null;
如果键重复,将第一次存储键对应的值返回,将后面键对应的值进行存储
void clear():清空Map集合的所有键值对
V remove(Object key):删除Map集合的指定键,然后返回被删除的值
boolean containsKey(Object key):是否包含指定的键
boolean containsValue(Object value):是否包含指定的值
boolean isEmpty():判断Map集合是否为空
2.Map集合的遍历方式
//Map集合遍历方式:
//1)Set<K> keySet()获取Map集合的所有的键的集合
// V get(Object key):通过键获取值
Map<String,String> map = new HashMap<>();
Set<String> key = map.keySet();
for(String s : key){
String value = map.get(s);
System.out.println(s+value)
}
// 2)Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象
// K getKey()获取键
// V getValue()
Map<String,String> map = new HashMap<>();
Set<Map.Entry<String,String> en = map.entrySet();
for(Map.Entry<String , String> e : en){
String key = e.getKey();
String value = e.getValue()
}
3.HashMap集合
HashMap--->底层哈希表实现,put方法依赖hashCode和equals方法,
Map针对键有效,要针对键的类型自定类型,当前类必须重写equals和hashCode(),保证键唯一(重点)
4.TreeMap
TreeMap<K,V> 存储键值对元素,保证键唯一的同时,还有排序!
使用哪种排序,取决于使用构造方法:
public TreeMap():自然排序,当前键的类型必须实现Compareable接口
public TreeMap(Comparator<? super K> comparator):使用比较器排序 (推荐) 直接可以使用接口匿名内部类
5.Collections工具类
java.util.Collections:针对集合操作工具类
提供一些常用的方法:
public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key):
二分搜索在List中查询元素 (不管集合还是Arrays---->binaraySearch方法,(集合或者数组必须有序))
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) :获取Colection中的最大值
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) :获取Colection中的最小值
public static <T extends Comparable<? super T>> void sort(List<T> list):针对List集合进行自然排序
实现的接口是一个自然排序Compareable
public static <T> void sort(List<T> list,Comparator<? super T> c):针对List集合比较器排序
Collections.sort(list , new Comparator<>(
public int compare(Object o1,Object o2){
return 0;
}
))
public static void shuffle(List<?> list):针对List集合随机置换
public static <T> List<T> synchronizedList(List<T> list):将线程不安全ArrayList,变成线程安全的列表
Map集合和Collection集合的区别?
1)存储类型
Collection<E>,只能存储一种引用类型---属于"单列集合"---->理解为"光棍"
Map<K,V>,可以存储多种类型的引用类型--->属于"双列集合"----理解为"夫妻对"
2)关系区别
Collection和Map没有直接关系,间接关系
Collection具体的子接口Set--->HashSet/Treeset 直接的去依赖于Map接口的HashMap和TreeMap
二、异常
1.什么是异常
程序中会出现异常,开发人员必须解决异常!
异常的父类:Throwable
-->提供一些方法:将异常的信息直接打印在控制上:具体(跟踪堆栈-->底层原码)
public void printStackTrace()
Error(严重问题) Exception
严重问题:内存溢出(借助于其他硬件解决这个问题,加内存条)
Exception:
编译时期异常:只要不是RuntimeException的子类,都属于编译时期
必须让开发人员必须处理,不处理,代码编译通过不了!
运行时期异常:RuntimeException
大部分逻辑不够严谨,导致出现的问题
NullPointerEception:在获取到某个类的对象的时候,并没有非空判断,
可能对象是null,使用这个对象调用它里面的方法,出现空指针了
2.异常的格式
throws:抛出
//标准格式
try{
可能出现问题的代码
}catch(异常类名 对象名){
处理异常
}finally{
//释放资源
}
变形格式
try{
可能出现问题的代码
}catch(异常类名 对象名){
处理异常
}
try{
可能出现问题的代码
}catch(异常类名 对象名){
处理异常
}catch(异常类名2 对象名2){
处理异常..
}
3.throw和throws的区别
异常处理
throws:抛出
throw:抛出
区别:
1)书写位置的区别
throws:用在方法声明上
throw:用在方法体中
2)后面跟的类型不一样的
throws后面跟的异常类名,可以多个,中间逗号隔开
throw:后面只能一个异常对象,new XXException()
3)是否出现异常的时机
throws:可能出现问题,可能性
throw:执行某个逻辑代码,一定会出现这个异常!(肯定性)
4)处理异常:
throws:抛出给调用者,谁调用这个方法(带有throws的方法),谁必须对这个已处理(throws/try...catch...)
throw:异常的处理:交给方法体中逻辑判断,if(xxx){throw new XXException(...);}
4.异常的注意事项
异常的使用的注意事项:
1)子类继承父类的时候,如果子类重写父类的方法,父类的方法如果本身没有throws抛出异常
子类的方法中出现异常,子类只能自己处理,只能try...catch...
2)子类继承父类,子类重写父类方法的时候,如果父类的方法抛出异常,
那么子类的该方法最起码要根父类的方法异常保存一致(要么子类存放的异常是父类方法的异常中的子类)
三、线程
1.线程和进程的概念
线程是依赖于进程的,没有进程就没有线程!
进程
在计算机的任务管理查看到此电脑上所有的客户端的进行!
概念:
调用"系统资源"的独立单位!
开启一个进程----创建 "系统资源"
所有的计算机,包括我们"jvm",都支持多进程,多进程的意义?
一句话:提高CPU的使用率!
在打游戏的同时,听音乐,---同时开启了两个进程,是同时进行的吗?
并不是同时的,CPU(时间片),一点点时间片可以高效在两个进制来回切换!
线程:
是进程中的最小执行单元!(理解为,进程中的某个任务)
举例:
360进程开启之后,可以杀毒/清理/电脑瘦身等等
多线程:在一个程序的过程中,其执行路径有多条,线程的执行具有"随机性"!
多线程的意义?
多个线程"共享同一个资源",互相抢占CPU执行权,从而达到线程的执行速度!
举例
1 v 3 打篮球,3个人抢占的篮球的几率大,可能这1个人也能抢占大篮球!
2.创建线程的两种方式
1).通过继承Thread 创建
/**
1)自定义一个类 继承Thread类
2)重写Thread类的run方法
3)在main()用户线程中,创建当前线程了对象
4)启动线程--->start()不是run方法,run方法是一个普通方法
Thread类提供方法:
public final String getName()获取线程的名称
public final void setName(String name):设置线程名称
*/
class X extends Thread{
public void run(){
//耗时的操作;
//获取线程的名字:getName()
}
}
public static void main(String[] args){
X x = new X();
x.start();
}
2)通过实现Runnable接口创建
/**
线程的创建方式2:
1)自定义一个类,实现Runnable接口
2)重写Runnable接口的run方法
3)在用户线程main中去创建当前类对象--->"资源类"
4)创建Thread类对象,将3)资源类作为Thread类对象的参数进行传递
*/
class X implements Runnable{
public void run(){
//耗时的操作
//获取线程名字:Thread.currThread().getName()
}
}
public static void main(String[] args){
X x = new X();
Thread td = new Thread(x,"线程1");
td.start();
}
3.线程的方法
1)join等待该线程中终止
public final void join() throws InterruptedException
因为线程的执行具有随机性,哪个线程启动之后,调用join(),正常理想情况,等待该线程执行完毕,执行其他线程!
2) yield线程礼让
public static void yield():线程礼让(暂停当前正在执行的线程,执行其他线程)
4.线程的优先级
public final void setPriority(int newPriority):设置优先级
public final int getPriority():获取优先级
Thread提供三个自定义常量:
public static final int MAX_PRIORITY 10 最大优先级
public static final int MIN_PRIORITY 1 最小优先级
public static final int NORM_PRIORITY 5 默认优先级
创建线程,没有设置优先级,线程的默认优先级就是5(抢占CPU执行权的概率一样大)
5.同步方法
/**什么是同步方法?
如果一个成员方法中进来就是一个synchronized(锁对象){}--同步代码块,将synchronized提到方法声明上
默认的锁对象this
如果是静态的同步方法,锁对象是 "当前类名.class"--跟"反射"有关系
反射:Java代码经历三个阶段
"SORUCE" ,"CLASS"(反射中的核心) "RUNTIME"*/
public class Abc implements Runnable{
void run(){
if(){
//sysnchronized(this){//当前类对象的地址引用
sysnchronized(Abc.class){//当前类对象的字节码文件对象
//.....
}
}else{
cell();
}
}
//class synchronized void cell(){//
class static synchronized void cell(){
if(){
//....
}
}
}
6.死锁
1)死锁
两个或者两个以上的线程,在执行的过程中,互相持有对方的锁,需要等待对方释放锁,导致出现了互相等待情况!
举例:
中国人和美国人吃饭
一双筷子 / 一把刀/一把叉
中国人:
一根筷子/一把刀
美国人:
一跟筷子/一把叉
解决死锁:多个线程必须使用统一资源对象! (引入生产者和消费者模式思想)
2)wait()和notify()
属于Object类中:
wait() 等待,当调用这个方法时,会立即释放锁
notify() 唤醒线程
3)解决死锁的办法–生产者/消费者模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dn1JAAZU-1679816832552)(E:\后端\笔记\第一阶段\生产者消费者.png)]
使用生产者和消费者模式思想,来解决线程死锁问题!
组成:
Student类:姓名,年龄两个学数据
SetTread类:生产资源类,里面产生学生数据
GetThred类:消费者资源类,里面使用学生数据
ThreadDemo类:用户线程,等会开启两个生产者所在的线程/消费者所在的线程
问题1:
输出null--0,因为生产资源类中和消费资源类中并没有使用同一个资源!
优化:
需要不断的在生产资源类中产生学生数据,在消费者资源类中不断地使用学生数据!
问题2:
不断的在生产者资源类中产生数据,消费者资源不断使用使用数据,出现数据紊乱!
姓名和年龄不符! ---多线程环境不安全
---需要多线程安全问题
校验多线程安全问题的标准:
1)是否是多线程环境
2)是否存在共享数据
3)是否有多条语句对共享数据操作
使用同步代码块---将3) 包起来
问题3: 加入同步代码块,解决了线程安全问题,数据不会紊乱,但是数据一打印一大片
原因:线程的执行,通过cpu一点点时间片,足够线程执行很多次!
最终版本:
想依次打印数据:------->Java中的等待唤醒机制
生产者不断产生数据,当他没有数据了,等待产生数据之后,通知(唤醒)消费者所在的线程来使用数据!
消费者不断使用数据,当他有数据了,等待先使用数据,使用完毕没有数据了,通知(唤醒)生产者所在的线程来产生数据!
(多线程 "信号灯法")
你一次
我一次
wait()+notify() 也算Java同步机制 必须使用使用锁对象调用方法
7.jdk5以后锁定操作
jdk5以后提供了具体的"锁定操作"
java.util.concurrent.locks.Lock接口---->可重入的互斥锁ReentrantLock
具体锁
Lock lock = new ReentrantLock()
提供一些方法,可以获取锁,可以去释放锁
void lock() 获取锁
void unlock()释放锁
8.问题
面试题:
1)jvm是多线程的吗?
是多线程;至少有两条件线程
1)main---"用户线程"
2)垃圾回收线程,回收没有更多的引用对象,从而释放空间;如果没有
垃圾回收线程,Java中不断的去new,不断的堆内存空间---->导致OOM(Out Of Memory:内存溢出)
2)Java能创建线程吗?
创建线程--->创建进程---->创建系统资源---->Java不能够直接创建资源的,间接的提供了一个Thread
四、线程池–Executors
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5KaTgfut-1679816832554)(E:\后端\笔记\第一阶段\线程池原理.png)]
1.线程池
线程池:
可以通过Executors工厂类(当前类构造方法私有化,对外提供静态方法--->简单工厂模式)提供一些创建线程池的功能
可以创建固定的可重用的线程数的线程池
public static ExecutorService newFixedThreadPool(int nThreads):参数为当前的线程数量
上面的返回值ExecutorService接口
提供一些方法
Future<?> submit(Runnable task)提交异步任务
<T> Future<T> submit(Callable<T> task):提交异步任务
void shutdown():关闭线程池
创建线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
提交异步任务
MyCallable myCallable = new MyCallable() ;
threadPool.submit(myCallable) ;
threadPool.submit(myCallable) ;
关闭线程池
threadPool.shutdown();
定义类
public class MySelfCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
//需要的操作
return sum;
}
}
五、单例设计模式
单例设计模式---属于创建型设计模式(创建对象)
概念:
始终在内存中有且仅有一个当期类的实例!(有一个对象)
1.两种设计模式
单例设计模式:
1)饿汉式
2)懒汉式
饿汉式:不会出现安全问题的单例设计模式
1)当前类是具体类
2)类一加载就创建当前类实例
3)构造私有化,对外隐藏,不能new实例
4)对外提供静态方法,返回值当前类本身
Java中的类Runtime类:标准的单例(饿汉式),和计算机的运行环境有关系
/**单例设计模式之懒汉式(可能存在安全问题的一种单例模式)
1)当前类是个具体类
2)当前类构造方法私有化
3)当前类的成员位置:声明当前类的一个静态变量
4)对外提供静态方法,返回值是当前类本身:需要判断当前变量是否为null
现实开发业务中:
存在懒加载(按需加载)
一个用户有多个账户: 一对多 (从用户维度去看账户) ,查询用户的时候,开启懒加载方式(针对用户用哪个账户查询哪个账户)
账户的维度去看用户:一个账户从属于某个用户的 一对一*/
public class Teacher {
//声明静态变量,t的类型Teacher
private static Teacher t ;
//构造方法私有化:
private Teacher(){}
//对外提供静态方法,返回值是当期类本身
//多个线程同时去访问这个方法,
//线程1,线程2,线程3--导致出现线程安全问题
public static synchronized Teacher getInstance(){//变成了静态的同步方法 :锁对象:类名.class
if(t ==null){
t = new Teacher() ;
}
return t;
}
}
六、IO流
1.File抽象路径形式
java.io.File--->表示文件或者文件夹(目录)一种抽象路径形式
构造方法:
File(File parent, String child)从父抽象路径名和子路径名字符串创建新的 File实例。
File(String pathname)通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
File(String parent, String child) 父路径名字符串和子路径名字符串创建新的 File实例
1).File的基本功能以及特有功能
File的基本功能以及特有功能:
public boolean createNewFile() throws IOException:创建文件
public boolean mkdir():创建文件夹
public boolean mkdirs():创建文件夹,当父目录不存在,自动创建
public boolean delete():删除文件夹或则文件路径,如果是文件夹,必须是空目录
public boolean exists():判断此路径名表示的文件或者目录是否存在
public boolean isDirectory():此抽象路径名表示的文件是否是目录
public boolean isFile():是否是文件
public boolean isHidden():是否隐藏
特有功能:高级功能
public String[] list():获取指定抽象路径名表示的文件或者目录的名称(返回字符串数组)
public File[] listFiles():获取指定路径名表示的文件或者目录的File数组
2).直接获取的同时就将满足条件的内容遍历
之前的写法:
1)先获取当前指定路径形式下的所有的文件以及文件夹的File数组或者String[]数组
2)然后在遍历
另一种方式:直接获取的同时就将满足条件的内容遍历
参数都是接口---FilenameFilter:文件名称过滤器
public boolean accept(File dir,String name)
将指定的文件存储在文件列表中dir,返回值true(存储在文件列表中),否则false,不存储
public File[] listFiles(FilenameFilter filter)
public String[] list(FilenameFilter filter)
2.字节流
1).字节输出流
FileOutputStream 实现文件的字节数追加
public FileOutputStream(String name,boolean append) throws FileNotFoundException
第二个参数是true:自动追加内容
IO流写数据,实现换行效果 widows系统 "\r\n"
2).字节输入流
InputStream:字节输入流--->抽象类--->具体的子类FileInputStream:文件字节输入流
1)创建文件字节输入流对象FileInputStream(String name) :读取指定的文件 name:文件地址
2)读数据
继承它父类的方法:public abstract int read()throws IOException:一次读取一个字节
一次读取一个字节的方式:
针对中文---会出现乱码 (char)字节数,平台默认编码格式utf-8:一个中文对应三个字节
英文字母和中文-拼接一块,一个英文字母对应一个字节,这个时候拼接不上,导致文乱码---Java 提供"字符流",解决乱码
public int read(byte[] b) throws IOException:一次读取一个字节数组
//最终代码:字节流一次读取一个字节数组
//创建一个字节缓冲区:长度:1024或者1024整数倍
byte[] bytes = new byte[1024] ;
//实际字节数从0开始
int len = 0 ;
while((len=fis2.read(bytes))!=-1){//赋值,判断-- read(byte[] bytes):阻塞式方法,没有到-1,就一直读
String str = new String(bytes,0,len) ;
System.out.println(str);
}
3)字节缓冲流
Java之IO流提供了字节缓冲流:让读取的速度更快一些,提供对应的类
字节缓冲输入流/字节缓冲输出流 (高效的字节流)---仅仅是内部提供缓冲区字节数组长度:8192长度,
文件的读写复制还的依赖于基本字节流(InputStream/OutputStream)
BufferedInputStream(InputStream in):字节缓冲输入流
BufferedOutputStream(OutputStream out):字节缓冲输出流
推荐"使用字节缓冲流"完成图片文件/视频文件/音频文件的读取!
3.字符流
1)字符输入流
字符输入流---->Reader(抽象类)--->
具体的子类:字符转换输入流InputStreamReader --- 将字节输入流---->转换--->字符输入流
public InputStreamReader(InputStream in):使用平台默认字符集进行解码---读取数据
public InputStreamReader(InputStream in,String charsetName)
使用指定字符集解码---读取数据
读数据:
int read() :一次读取一个字符 --->返回结果:实际的字符数
int read(char[] cbuf) :一次读取一个字符数组
int read(char[] cbuf,int off, int len):读取字符数组的一部分
2)字符输出流
字符输出流:Writer(抽象类)--->
具体的子类 OutputStreamWriter:字符转换输出流,"可以将字节输出流---转换---字符输出流"
构造方法:
public OutputStreamWriter(OutputStream out):使用平台默认字符集进行编码--输出数据
public OutputStreamWriter(OutputStream out,String charsetName):
使用指定的字符集进行编码---输出数据
写数据:
void write(char[] cbuf) :写入字符数组
void write(char[] cbuf,int off,int len ) :写入字符数组的一部分
void write(int c) :写一个字符
void write(String str) :字符串
void write(String str,int off,int len):写入字符串的一部分
3)字符缓冲流
BufferedReader:字符缓冲输入流---可以读取一行内容
构造方法:
public BufferedReader(Reader in):提供默认缓冲区大小的字符缓冲输入流 (8192个长度)
这个流如果直接操作文件--->参数里面使用FileReader
特有功能:
public String readLine()throws IOException:一次读取一行
返回值是读取到这一行的内容,当流已经到达末尾,则返回null
BufferedReader br = new BufferedReader(new FileReader("copy.java")) ;
键盘录入
1)Scanner(InputStream in)---->Scanner sc = new Scanner(System.in) ;
2)new BufferedReader(new InputStreamReader(System.in))
--->InputStreamReader(InputStream in)
//键盘录入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
字符缓冲输出流:提供字符数组/字符/字符串的一种高效写入
BufferedWriter构造方法
public BufferedWriter(Writer out):提供默认缓冲区大小的字符缓冲输出流,默认缓冲区足够大 8192长度
写的功能:
write(字符/字符数组/字符串...)
特有功能:
不需要在使用"\r\n"换行符号, public void newLine()写入行的分隔符
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt",true)) ;
七、属性集合列表
java.util.Properties:属性集合列表---extends Hashtabl<K,V>-->
本质是Map<K,V>集合
添加功能:put(K key,V value)
遍历方式:Set<K> keySet()
遍历键-->通过键获取值 V get(K key)
自己的特有功能:
java.util.Properties:没有泛型,键和值都只能String---->
作用:用来读取配置文件xx .properties配置文件(src下面)
构造方法:
public Properties()
添加属性列表的键和值:
public Object setProperty(String key,String value)
特有的遍历方式:
public Set<String> stringPropertyNames():获取属性列表中的所有的键(属性名称)
public String getProperty(String key):通过属性列表中的属性名称获取对应的值
将文件的内容加载到属性集合列表中:
public void load(InputStream inStream/Reader)
将属性列表中的内容保存到指定文件中
第二个参数:描述属性列表
prop.store(new FileWriter("D:\\EE_2302\\day27\\code\\day27\\src\\my.properties"),"name'list") ;
读取到src下面配置文件的内容--->存储到字节输入流中
public void store(OutputStream out/Writer,String comments)
InputStream inputStream = PropertiesDemo2.class.getClassLoader().
getResourceAsStream("name.properties");
prop.load(inputStream);
七.递归
方法递归:
方法本身调用方法的一种现象
前提条件:
1)定义一个方法
2)这个方法的逻辑是规律的
3)这个方法是有结束条件,否则---成了"死递归"
八、网络编程
1.三要素
ip地址/端口号/协议
ip地址:
A/B/C三大类
A类--->前面第一个号段:网络号段 后面的号段:主机号段 (国家部门--机房)
B类--->前面两个号段:网络号段 后面号段:主机号段 (大学校园/公司内网等等)
C类---->属于私人地址 前面三个号段:网络号段 后面号段:主机号段
10.35.165.17 --- "点分十进法"
端口号:
范围 0~65535 这里面 0-1024(属于保留端口号)
自己指定端口号--->1025-65535
(不要和系统端口号冲突---->Address already in use:BindException:地址被占用)
协议:
UDP/TCP协议区别 (底层网络协议)
UDP协议:
1)属于不可靠连接,不需要建立连接通道(不安全) ----以"数据报包"的方式进行数据传输的
2)不同步的,执行效率相对TCP协议,效率高
3)发送数据大小有限制的!
TCP协议:
1)属于可靠连接,需要建立连接通道! ----以 "流"的方式发送数据
2)同步的,执行效率低,安全性相对UDP协议(安全性能高)
3)发送数据大小无限制!
2.InterAddress
java.net.InetAddress:代表互联网ip地址对象!
获取ip地址对象---->获取ip地址字符串形式/获取主机名称
参数:要么传入主机名称/要么ip地址字符串
public static InetAddress getByName(String host)throws UnknownHostException :获取互联ip地址对象
InetAddress的成员方法
public String getHostAddress():获取ip地址字符串形式
public String getHostName():获取ip地址的主机名
3.UDP发送端
java.net.DatagramSocket:此类表示用于发送和接收数据报数据包的套接字
public DatagramPacket(byte[] buf,int length,InetAddress address,int port)
创建"数据报包"--里面包括:数据内容,ip地址,端口号
java.net DatagramPacket
参数1:分组数据(字节数组)
参数2:包的长度
参数3:ip地址对象
参数4:端口号
使用发送端的Socket对象发送"数据报包"
public void send(DatagramPacket p)throws IOException从此套接字发送数据报包
4.UDP接收
使用UDP协议发送数据
1)创建发送端的socket对象
2)创建"数据报包"--里面包括:数据内容,ip地址,端口号
3)使用发送端的Socket对象发送"数据报包"
4)释放资源
创建接收端的Socket对象
public DatagramSocket(int port)throws SocketException
创建一个"数据报包"在里面自定义缓冲区:将发送端是数据存储到缓冲区中(字节数组,长度:1024或者1024的整数倍)
public DatagramPacket(byte[] buf,int length)
参数1:用于保存传入"数据报"的缓冲区。
参数2:要读取的字节数
public void receive(DatagramPacket p)throws IOException从此套接字接收数据报包
public byte[] getData() :解析数据报包中DatagramPacket
public int getLength():解析数据报包中DatagramPacket中的实际字节数组长度
谁发送的数据---获取ip地址字符串形式
数据报包DatagramPacket--->public InetAddress getAddress()