2023.10.13
1.用static final修饰表示常量
2.枚举是引用数据类型
注意,enum这个字母的单词都是小写单词
① 用toString方法输出(toString可省略)
② foreach循环
③ for循环(必须有下标的才能用)(用到size和get)
④ 迭代器(iterator)
2023.10.14
(1)Linked的插入和删除的效率高,把它看成元素相连用链子
(2)ArrayList的查询的效率高,因为它有下标,遍历和随机访问的效率高,随机访问(查询)
(3)LinkedList有头部和尾部,所以add、get和remove能结合frist和last使用
基于Queue集合,具有先进先出的特点
构造了一个空链表
2023.10.16
2023.10.17
1.HashSet:无序、不重复 重写hashcode和equals(使用前面两项)
2.LinkedHashSet:有序,不重复
3.TreeSet:不重复、有序(排序),比较器(自然比较、定制比较)
4.Map(地图)
(1)和Collection是并列的,它是双列的
(2)它可以跟地类似,地图有行和列,它有键(K)和值(V)-双列集合
(3)衍生
①HashMap
1)(基于哈希表实现Map接口,允许有null键和null值,但键只能有一个空)
2)默认空间是16
3)它和set和list有区别,想要添加用put(k,v)
4)查询也不一样(foreach、for循环和迭代器适用单列结合)
a.首先要将双列转换成单列将集合
a)entrySet:将map集合中的k和v转换成set集合(不能转换成list,因为k是不能重复的)
i.转换后可以查询了(将k和v绑定到一块了)
ii.用getKey可以单独获取键(k)
iii.用getVlue可以单独获取值(v)
iv.获取键:entry.getKey
v.获取值:entry.getValue
b)keySet:将map集合中的键转换成set集合(可以看成set(key))
c)get(K):通过键获取所映射的值,v=get(k)
d)values:可以拿到map集合中的value值,返回值是Collection,不能是set,因为value是可以重复的,但set集合去重了
i.根据值不能拿到键(因为键不唯一)
5)键(k)不能重复
a.所以如果想要用put时,相同的数据放到value里
6)值(v)可以重复
7)Remove(Object key):根据键删除,删除会将键和值一起删除
8)ContainsKey:是否包含键
9)ContainsValue:是否包含值
10)线程不同步的
11)插入、删除效率较高
②LinkedHashMap
1)和LinkedHashSet基本一致,因为底层是双向链表,存储有序,不重复
2)用法和HashSet一致,用法一致但结果不一致
③TreeMap
1)按照key来排序,底层是红黑树,有Tree所以要用比较器
2)TreeSet基于TreeMap,其实是TreeMap里的key是TreeeSet,所以它们的用法基本一致
3)键不能有null,值可以有null
4)Key唯一,value不唯一
5)针对键排序
6)线程不安全
7)线程不同步
④HashTable
1)它的键和值都不允许出现null,如果出现空指针异常的报错了,就说明键和值有空(null)
2)基于哈希表实现的,线程安全的map集合,但是它基本淘汰了
⑤ConcurrentHashMap
1)线程安全的map集合,如果想用线程安全的别用HashTable了,用它
2)支持检索的完全并发性和更新的高预期并发性的哈希表。
3)高并发->高可用
(4)Map中不能包含重复的建,一旦重复会把前面的覆盖掉
2023.10.18
2023.10.19
3.数组工具类排序:Arrays
(1)Arrays.sort();
4.集合工具类
(1)里面的方法都是静态方法
(2)好处:如果想要排序的话,必须用TreeSet/TreeMap,但用集合工具类可以有更多选择
(3)注意:集合工具类(Collections)只能是list和map集合
(4)功能代码
①排序:Collections.sort();
1)Sort(List
2)如果类型是自己定义的,那么就需要建立一个比较器了(一般建立自然排序,因为它简单)
3)工具类定制排序(重点)
a.先写Collections方法
b.再建立一个外部比较类进行比较
c.创建比较类实例化对象(假设为a)
d.再Collections.sort(建立的list集合实例化名称,a);
②乱序:Collections.Shuffle();(相当于app中的打牌、打麻将中的弄乱)
1)每次打乱都不一样
③二分查找:Collections.binarySearch(集合实例化名称,要查找的元素);
1)二分查找的前提是有序的
2)返回值是下标
3)二分查找也可以用定制排序进行查找(二分查找的前提是有序的)
a.先写Collections方法
b.再建立一个外部比较类进行比较
c.创建比较类实例化对象(假设为b)
d.再Collections.binarySearch(集合实例化名称,要查找的元素,b);
e.如果返回值小于0的话,相当于没有查找到元素
f.想要删除的话就集合的实例名称.remove(返回值-返回值是下标);
④反转/翻转:Collections.reverse();
⑤最大值和最小值
1)最大值:Collections.max();
2)最小值:Collections.min();
3)好处是不仅能求list、map的最大值、最小值,set也能求
4)因为Collections功能的前提是有序的,所以活学活用,结合两种比较器使用
⑥根据给定集合查找源集合中的位置
1)Collections.indexOf(源集合,查找的集合)(第一次出现的位置)
2)Collections.LastindexOf(源集合,查找的集合)(最后一次出现的位置)
5.文件存储数据(流)
(1)文件和软件的交流是I/0流
(2)软件中数据存储到文件中(写/out)
(3)文件到软件中(读/in)
(4)文件类(文件和目录(文件夹)的表示)
①它用File表示
②它的包是java.io.File
③创建文件
1)File f = new File(“文件路径”);
a.里面中\再java中代表转义,所以用\\,当然也可以用一个斜线,反过来就能用一个写了
b.带盘符的路径是绝对路径(不能更改的路径)
c.不带盘符的时相对路径(相对的是当前项目)
2)CreateNewFile():创建文件
f.createNewFile(); 注释:创建文件
里面的路径可以直接写路径;也可以前面写路径,后面写文件名
f.createNewFile();的返回值时bollean类型,可以判断文件是否存在
3)exits表示文件存在,跟上方bollean比效率会提高
4)CreateNewFile:创建新的文件
④创建文件夹
1)创建文价夹没有后缀
2)mkdir();创建一级文件夹
3)mkdirs();创建多级文件夹
⑤保证文件路径正确
⑥本项目想查看创建的文件需要刷新(刷新时选中项目,鼠标右键F5进行刷新)
(5)递归
①如果想查找一个文件夹中所有的文件(注意,文件夹里还可能有文件夹,文件夹里还有文件)
②ListFile:能找到路径下的所有文件和文件夹(注意:只能找一层,所以要用递归)(注意:ListFile方法得到的时每一个文件和文件夹的地址)
③IfFile:判断是否是文件
④IfDirectory:判断是否时文件夹
通过2(查找地址)、3、4,可以很快的完成递归查找
递归题要点:1.创建一个方法(一般为get名字方法);
2.方法是静态(static)的,为了方法main方法体中直接调用
⑤注意:有的盘符不让随便找
⑥注意:递归只能用在文件夹中
(6)只获取文件名:getName();
(7)删除文件:delete
(8)IO流时一组有序的用于程序输入、输出的一种序列
①Out:输出(写)
②In:输入(读)
a.InputStream:字节输入流
b.OutputStream字节输出流
a.Reader:字符输入流
b.Writer:字符输出流
上面四个单词都是抽象类(不能创建对象,所以需要其它类(File)的帮忙)
a.FileInputStream:文件字节输入流
b.FileOutputStream文件字节输出流
a.FileReader:文件字符输入流
b.FileWriter:文件字符输出流
6)注意:流需要刷新:flush();
7)注意:流需要关闭:close();
8)想换行用write(“\n”);对,就这么简单!
9)续写功能很重要,就把文件字符输出流的括号里放入(路径,true);相当于玩游戏中的日志功能
2023.10.20
1.IO流
(1)文件流(类,可以操作文件了)(文件流也叫节点流)
①FileInputStream:文件字节输入流(读字节(字节是二进制码))
1)比字符难
2)跟普通字符输入(读)流差不多
a.先创建一个字节数组
b.然后写一个while循环
c.里面写字节输入流的实例名称.read(数组实例名称);操作->读取内容存入数组arr中
d.然后写一个结束条件if(字节数组长度<0)时,break结束
e.最后输出字节数组的内容就可以了
②FileOutputStream:文件字节输出流(写字节)
③FileReader:文件字符输入流(读字符)
1)Read():读一个字符,以int形式读,需要强转成char类型
a.Int read = fr.read()->读取的内容
2)当读的个数位置,就要创建一个循环读取
3)Read(char[]):读字符数组
a.Char[] arr = new char[1024];
Int i = fr.read(arr);->读取的个数(读取的个数时有限的)(当i<0时代表没读到,可以设定结束条件)
if(i<0){
Break;
}->结束条件必须写,否则会报错
a)所以可以把b.写成循环条件
b)While(true){
Int i = fr.read(arr);
If(i<0){
Break;
}->可以读取成功了
④FileWriter:文件字符输出流(写字符)
1)writer(字符串):写内容(如果是非字符串可以用toString来转换)
2)只有写时有flush,但读没有
3)以字符形式写,就要以字符形式读
(2)缓冲流(缓冲流也叫处理流)(缓冲流也叫高效流(载缓区完成操作))
2.流按流向分分为输入流和输出流
(1)输入流(读/in方向):从文件到java程序
(2)输出流(写/out方向):从java程序到文件
3.字符和字节判断
(1)文件字符串(字符)
①一般遇到汉字就用字符流
(2)音频、视频(字节)
4.流结合集合使用
5.Map(key,value)集合求个数问题
(1)首先把要统计的内容写成数组(用到切割(切割””中什么也没有,会分割字母))
(2)将数组进行遍历,再遍历里面进行判断(用到containsKey)如果map.containsKey(数组实例化对象),就写map.get(数组实例化对象,map.get(st)+1)->map.get(st):通过键获取值的个数;如果不包含键的数组内容,就写成map.put(数组实例化对象,1)
(3)最后用map集合的遍历方法进行输出内容
6.流可以看成一个通道
7.想要续写的话再文件流后加true
8.转成字节:getbytes
2023.10.21
1.流写的时候不必创建数组
2.流读的时候高效流不必创建数组
①ObjectinputStream:输入方向(读)-反序列化
1)反序列化的前提是必须先序列化(写),才能反序列化(读)
2)ReadObject():写的时候是student内容,那读的时候也必须是student内容,因为读的时候必须先写,所以内容必须一样,这里的括号内无内容
3)如果内容有许多的情况下或不确定的情况下,用while循环,因为它的结束条件和其它的不太一样,不是null,所以需要用到try-catch,将读取的动作放到try中,读完(异常处理)放到catch,不能有final和close,因为要一直运行下去
4)集合反序列化
a.读的返回值是一个对象
b.写的时候以一个集合写出去,读的时候以一个集合读进来
c.不能直接foreach循环,因为读的是字节码,不是上方创建的集合,所以要将读完后还要将返回值对象换成集合的对象
②ObjectOutputStream:输出方向(写)-序列化
1)writeObject(Object obj):将指定的对象写入ObjectOutputStream。 注意:Object,把要序列化的内容->obj写进去,这里容易出错
2)常见报错:没有开启序列化接口功能(Interface Serializable)
3)写出去看不懂很正常,因为是字节形式输出的内容
4)遇到集合或不知道内容的情况下,循环一次写一次
5)集合序列化
a.集合也是一个对象(一个整体)
③难点再序列化和反序列化多行时,定义,上面用遍历序列化,下面用循环反序列化加上try-catch抛出异常;上面直接序列化读取,反序列化要先将对象换成集合对象,然后用for循环遍历输出
①是一种将字节流按照指定编码的操作
②其实就是将字节流转换成字符流(只能这样转)
③可以理解成中间纽带的关系,可以让原本的字节流操作换成字符流操作
④出现乱码的原因是编码不一样
⑤例如可以用转换流(前面写路径,后面写要转换的格式),只有转换流可以转换格式,转来转去其实就是字节转字符
①常用到的systeam就是打印流,是字节形式的,它打印到控制台上
②注意要用FileOutputStream
③打印流分为两种
1)字节打印流
a.PrintStream
b.Pritln()->括号内写要打印的内容
2)字符打印流
a.PrintWriter
b.Pritln()->括号内写要打印的内容
④打印流都是输出形式的
⑤注意他是一个流,要创建对象
⑥打印完,要刷新流,关流
①RandomAccessFile:随机流
②r表示只读
③seek方法表示设置位置
④用字节流读取
2023.10.23
1.(.trim):去除字符串首尾空格
(1)继承hashtable,实现map集合,导包是util包
(2)特殊K V 默认为String类型,是双列集合
(3)表示一组持久的属性
(4)Load(Reader reader):从文件中读取,直接加载到Properties集合中
(5)以.Properties结尾是配置文件
(6)getPropertie(key):根据key获取值的值的方法,写键时必须加双引号,因为K 是String类型
(7)相当于登录用户名和密码,如果更改内容的话,直接更改配置文件里的内容就行,代码就不用动了,很方便的
(8)Store:以集合形式存储数据
BIO NIO AIO
(1)阻塞IO:BIO
(2)非阻塞IO:NIO,也叫新IO
①非阻塞IO是面向缓冲去,非阻塞,同步的IO
②FileChannel类实现channel接口,FileChannel是文件和程序之间的通道
③NIO读和写,必须有个中间区域,存储读和写的数据,与channel进行交互
④getchannel,获取文件和程序之间的通道
⑤CharBuffer和ByteBuffer(缓冲区)
1)方法put:存数据
⑥Filp方法是移动指针
⑦Charset的作用是转换类型
1)Charset.forName(“”)->括号内写要设置成的编码
(3)Encode:编码
①计算机不能直接识别中文,所以要进行编码
(4)Decode:解码
(1)四个抽象类
①字节输入流:inputStream
②字节输出流:outputStream
③字符输入流:Reader
④字符输出流:Writer
(2)文件流-节点流
①字节输入流:FileinputStream
②字节输出流:FileoutputStream
③字符输入流:FileReader
④字符输出流:FileWriter
1)注意:续写
⑤只有文件流有续写(只有FileoutputStream和FileWriter能续写)
(3)缓冲流-处理流(高效流)
①必须包装节点流
②字节输入流:BufferedFileinputStream
③字节输出流:BufferedFileoutputStream
④字符输入流:BufferedFileReader(易考)
⑤字符输出流:BufferedFileWriter(易考)
(4)对象流
①序列化:写对象
1)ObjectOutputStream
②反序列化:读对象(重中之重)
1)ObjectinputStream
2)ObjectinputStream ois = new ObjectinputStream(节点流)
3)读一个对象 ois.readObject();
4)都多个对象
5)While(true){
Try{
Ois.readObject();
}catch{
Break;
}
}
(5)File
①创建文件夹
②创建文件
③递归(文件夹,找文件夹下的所有文件)关键词:列取文件夹下的所有文件(包括文件夹)
④IsFiles:判断是否是文件
⑤IfDirector:判断是否是文件夹(目录)
1)想调用内部类方法
a.先创建外部类对象,假设是a
b.相连接内部类假设是B
c.B b = a.new B();
d.通过b调用内部类方法
④成员内部类
1)Class 外部类{
Class 内部类{
}
}
成员内部类:不能有静态,可以有静态常量,在内部类可以使用外部类属性、方法
外部类不能直接使用内部类属性、方法 必须先创建内部类对象
(1)创建外部类对象 内部类
(2)创建内部类对象 外部类
(3)生成class文件时,内部类的名称:A&B.class
(4)外部类和内部类名字相同时,用内部类
(5)this表示自己,可以通过类名.this来定位类,以此调用定位类的方法
想使用外部类属性外部类类名.this.属性
2023.10.24
计算机以毫秒为单位
1秒d等于1000毫秒
线程的单词叫Thread,一个进程可以有多个线程,一个进程中至少有一个线程。
Thread t1 = new Thread();之后就能用thread对象的start方法了很有趣吧!
Runnable方法如果没有特别之处,那它真的很没有呀!Thread t1 = new Thread(线程对象名,自定义的名称),它可以结合匿名内部类使用
getState:获取线程状态,状态在后台是枚举类型
2.新建(new)通过start()启动,编程就绪
2023.10.25
1.双切:(“(=|,)”),跟单切的区别时在双引号内加竖线,左右两边各写一个要切割的内容
2.守护线程
SetDaemon():守护线程,括号内加true为启动守护线程,为其它线程提供服务,随着用户线程的终止而终止,不一定同时停止,会有时间差,守护线程守护的是别人。当A设置为守护线程,它会守护其它线程,也就是为其它线程提供服务(守护),当其它线程执行完毕后,它会立即终止,但是计算机是以毫秒为单位,当其它线程终止后,A刚收到信息,不会马上终止。
一般为非同步线程,是线程不安全的,会导致数据出错,所以就有了锁这个方法。让它变成线程同步的,就是加上锁,让多线程能有序访问统一资源,让线程安全,数据正常运行。
锁不仅仅只有一只,锁有两种(隐式锁和显示锁)
2023.10.26
线程同步是在线程互斥的基础上实现资源的有序访问
可以想象成电话。生产者、消费者模式。库存满了生产者,生产者等待消费者消费;库存空了,消费者等待生产者生产
保证线程同步(隐式锁)的基础上,进行线程通信,一般建立三个类,一个生产者线程、一个消费者线程、一个仓库类(Test),方法有等待方法:wait()->线程的等待,线程里没有抛出异常,不等待进行生产,让定义的值++;生产完进行唤醒正在等待的线程(生产者线程),方法notify:唤醒一个线程;notifyAll:唤醒多个线程,以此,可以很轻松的再写出消费者。锁用的是一把锁,就像通话用的是一条线一样
保证线程同步(显式锁)的基础上,newCondition和lock锁有关,通过lk.newcondition,获取两个返回值(库满的和库空的状态),放main方法外,加static,注意:再main方法内写,因为有提示。等待和传统方式不太一样,等待状态:await;唤醒动作:signal(唤醒一个线程)、signalAll(唤醒多个线程)
2023.10.27
线程通信
在同一进程中 线程间资源共享和信息传递的机制 成为线程通信
同步方法不同 通信手段不同
多个线程 操作共享数据 交替执行
Synchronized
传统线程通信
Lock同步
Lock的Condition
Collection 集合
List
Set
Queue--队列--先进先出,阻塞队列跟它有关
BlockingQueue--先进先出,Blocking是它的特点,创建对象时必须写长度,代表队列中能存储的数据容量
方法:put();代表向队列添加数据 队列满,阻塞等待,程序不会结束
.take获取返回值;代表从队列取出数据 队列空,阻塞等待,程序不会结束
Take方法每次取只能取队列第一个元素
生产消费
生产者负责循环对商品数量++ 达到上线则等待
使用put方法 完成生产功能 像队列添加元素
消费者负责循环对商品数量-- 达到下线则等待
使用take方法 完成消费功能 像队列取出元素
有两种运行效果:一生一用、一生产完一用完
ArrayBlockingQueue--基于数组实现的阻塞队列
BlockingQueue的特点:可以不用写同步方法,简单方便
LinkedBlockingQueue
可以指定容量大小、也可以不指定,但不支持公平策略
SynchronousQueue
不能给定大小,它一定是交替执行的
DelayQueue
到期元素先被取出
PriorityBlockingQueue
值最小的优先被取出
特点:服用线程,不够用了,再使用新的线程,避免线程的浪费
高频率、短任务
重复使用几个线程,减少使用和销毁线程的开销
就像客服,复用客服人员,完成接打电话短工作请求
案例
创建线程 输出一句话
主线程中 循环100次 启动线程 执行线程
使用线程池的步骤
Executors类创建线程池,获取返回值pool
必须实现runnale任务或callable接口的子类,才叫任务
Executor
Submit(callable/runnale)
mt是类线程对象
pool.submit(mt)是将线程放入线程池提交任务
Execute(runnale)
Shutdown()
线程池
固定大小线程池
FixedThreadPool(int)
线程的数量固定了,就定义的那么多
Int来设定线程的数量,但不管数量的多少,它的核心线程仅有一个
核心线程数=固定线程数-最大线程数
缓存线程池
CachedThreadPool
可回收空闲的,按需控制线程数量
单线程线程池
SingleThreadExecutor
仅创建一个线程的线程池
定时线程池
ScheduledThreadPool
执行任务定时和周期性任务的线程池,
提交任务:schedule(任务,时间,时间单位);
不能shutdown关闭线程
Schedule(任务,延迟时间,间隔时间,时间单位):有点像闹钟的贪睡功能
线程池的名字:Pool-1-thread
考点
线程池结合callable方法获取返回值
2023.10.28
重点技能:
同步网络编程TCP UDP
异步网络编程
getOutputStream:产生字节读流
将原始的线程启动方式换另一种方式启动
创建线程池的工具类:Executors
创建单线程池:Executors.newSingleExecutors
线程池提交任务方式:让线程池对象.submit(放线程对象);
submit可以换成execute,一般用submit,用完后shutdown关闭线程
创建固定大小线程池:Executors.newFixedExecutors,后面的都一样
没有线程池
用线程池提交任务
单线程池有1条线程,固定大小线程池线程数量自己定义
这里相当于启动线程,下面就能获取返回值了,获取完返回值后立马就关闭线程池(shutdown:关闭线程池)
是生产、消费的另一种,所以根线程有关
先进先出
放的时候用put,拿的时候take
2023.10.30
AsynchronousServerSocketChannel
用open方法
AsynchronousServerSocketChannel.open获取返回值,假设放回值是a
bind方法
返回值是future类型
ByteBuffer b = ByteBuffer.allocate(“”);
数据报是再UDP协议下,通过网络传输数据的基本单元
UDP跟TCP相反,它是客户端先发送数据报
用于发送和接受数据报的套接字,注意:客户端和服务端的套接字是一个
发送:send方法
数据报包,是将数据转换成数据报的方法
封装数据报
DategramPacket 自定义名称 = new DategramPacket(将数据转换成字节数组,获取数据长度,InetAddress.getByName("127.0.0.1"),客户端地址);
InetAddress.getByName("127.0.0.1")->通过地址名称获取服务端地址
getDate():将数据报转换为字节数组
getLength():数据报长度
共有资源,几个线程抢
传统线程:等待:wait; 唤醒:notifiy、notifiyAll
Lock锁的condit通信:等待:await; 唤醒:signal signalAll
Class.forName(全限类名),全限类名=包名+类名
属性-Field
方法-Method
构造方法-Constructor
getInterfaces
GetSuperclass(父类只能有一个)
getModifiers,返回值是int类型,
将int类型转换成字符串类型
Modifier.toString(int);获取返回值类型
getName
获取简单类名
getsimplename
2023.10.31
构造方法作用:创建对象
Class
(2)无参构造创建对象
Teach t = c.newInstance
等价Teacher t = new Teacher();
Teacher t = new Teacher(参数);
Class
(2)有参构造创建对象
c.getDeclaredConstructor(可变传参);
可变传参写法:类型.class。返回值是有参构造,假设变量是a
然后a.newInstance(对应参数写);返回值对应上面Teacher
2023.11.2
基础上是通过反射执行的
2023.11.3