Java的一些知识点(快速复习用,新手勿看)

2023.10.13

枚举

1.用static final修饰表示常量

2.枚举是引用数据类型

  1. 里面是常量(枚举常量/枚举实例),后面可加可不加;
  2. 枚举不能用数字,可以用中文,单词,字母
  3. 多个常量之间用,隔开

注意,enum这个字母的单词都是小写单词

  1. 枚举的关键字是enum
  2. 获取枚举常量的方法
  1. 枚举类名.枚举常量值;(通过枚举常量获取枚举实例)(返回,输出值)
  2. 枚举类名.valueOf(“枚举常量值”);(通过指定名称获取枚举实例)(可以看成字符串转枚举)(返回,输出值)
  3. 枚举类名.values();(获取所有的枚举实例)(获取枚举数组,遍历)
  1. 实体类
  1. 属性封装
  2. 提供set,get方法
  3. toString方法
  1. 枚举是一种引用数据类型
  2. 枚举类中可以写方法
  1. 但写方法常量值后面必须加‘;’
  2. 构造方法必须用私有的(private)
  3. 有参构造必须赋值
  1. enum是抽象类,由枚举类型自动继承
  2. 容器:变量-存储1个;数组-存储多个(定长);集合-存储多个(长度可变的)
  3. 集合

  1. 单列集合

  • Collection(父接口),里面由List和Set
  • List:可重复,有序(不是排序,是存入的顺序和取出的顺序一致,顺序不会颠倒)
  • Set:不可重复,无序(存入的顺序和取出的顺序不一致)
  • List有三个儿子,ArrayList、LinkedList和Queue
  1. 双列集合

  2. 集合中存储元素类型 构造一个初始容量为十的空列表
  3. 集合输出方法

① 用toString方法输出(toString可省略)

② foreach循环

③ for循环(必须有下标的才能用)(用到size和get)

④ 迭代器(iterator)

迭代器

  • 迭代器只能迭代一次,用iterator
  • iterator和listiterator
  • Iterator,list和set都可以用,而listiterator只有list可以用
  • 先hasNext判断,再hasPrevious判断(先正向迭代,再反向迭代)

2023.10.14

  1. Vector:是集合,也能自动增加容量,底层也是数组,线程安全的,有检查机制,效率低,而Arraylist恰恰相反
  2. 一般用Arraylist,因为只是存数据用。Vector是遗留的
  3. Stack集合:是集成Vector集合的,是Vector的子类,所有它也是线程安全的,具有后进先出的特点
  4. 集合具有删掉元素后,元素自动向前补齐
  5. Foreach效率最慢的,for循环效率最高,迭代器效率居中
  6. Queue集合:具有先进先出的特点,LinkedList是它的实现类
  7. LinkedList:底层是链表
  8. Arraylist和LinkedList的区别

(1)Linked的插入和删除的效率高,把它看成元素相连用链子

(2)ArrayList的查询的效率高,因为它有下标,遍历和随机访问的效率高,随机访问(查询)

(3)LinkedList有头部和尾部,所以add、get和remove能结合frist和last使用

基于Queue集合,具有先进先出的特点

构造了一个空链表

  1. 迭代器只能迭代一次
  2. util包是工具包
  3. 一般用ArrayList用于集合的冒泡排序,因为list集合有下标。
  4. 泛型:<>。不加泛型,一般默认是类型是Object,加上泛型,限制类型
  5. 泛型要求是引用数据类型
  6. 泛型通过参数类型提供代码的可重用性,并提供编译期提供强制检查的机制
  7. 泛型有泛型类、泛型接口、泛型方法

2023.10.16

  1. 泛型

    1. 泛型类:class类<泛型>{}
    2. 泛型接口:interface<泛型>{}
    3. 泛型方法:方法返回值前加<泛型>
  2. 泛型不能自动转型
  3. 泛型通配符是代替一个或多个真正字符的特殊字符
    1. 无边界通配符
    2. 固定上边界通配符
    3. 固定下边界通配符
    4. 用?便是通配符
    5. 通配符的弊端:范围太广了,所以有了上边界和下边界通配符
    6. :范围是自身和子类
    7. :范围是自身和父类
    8. 形参只能限制上边界,不能限定下边界,会报错
    9. 实参既能限定上边界,有能限制下边界
    10. 泛型一般用于集合
  4. set集合

    1. 它是无序不可重复的,所以它是无下标的,所以跟下标有关的方法不能用
    2. Hashset底层结构是哈希表,底层是根据hascode进行排序
    3. Hashset and equals方法,先比叫内容,再比较hascode值
    4. HashSet存储数据结构为哈希表
    5. Jdk.18之后:哈希表=数组+链表或者哈希表=数组+红黑树(提高查找速度,再链表长度大于8后)
    6. HashSet:无序不可重复
    7. LinkedHashSet:有序不可重复(是HashSet的子类)(双向链表+哈希表)(里面的有序是存入的顺序和查询的顺序一致)
    8. TreeSet:不可重复,有序(可以排序,还可以去重)
    9. Hashcode and equals只能用在set集合里比较,不能在list集合中比较
      • 如果两个对象相等,那么它们的hashCode ()值一定相同。 这里的相等是指,通过equals ()比较两个对象时返回true。
      • 如果两个对象hashCode ()相等,它们并不一定相等。
    10. Hashcode and equals方法步骤先比较hascode值,如果hascode值不一样就不比较了,如果一样,就再进行对象的比较,对象如果一样,那么就去重。
  5. TreeSet两种方法排序

    1. 一个是自然排序comparable,只有以一个抽象方法compareTo(根据所选的条件可以自己去重)
      • 步骤
        1. 自定义类:
    2. 一个是定制排序Comparator,有抽象方法compare;
      • 步骤
    3. 按字典顺序排序是用compareTo,也就是当要排序字符串时,用compareTo方法
  6. 总结

    1. 单列集合:Collection接口
    2. 子接口:Arraylist和LinkedList
      • 常用方法:add(添加一个对象)
      • addAll(添加一个集合)
      • Clear(清空集合)
      • 删除
        1. Remove(int index)(根据下标删除)
        2. Remove(Object o)(根据集合中的元素删除)
        3. 迭代器删除,有删除一般用迭代器,用迭代器返回的对象.remove();移除要移除的内容
      • 判断是否包含元素:contains
      • 根据下标获取元素:get(int index)
      • Size:获取集合的长度
    3. LinkedList比Arraylist多的方法
      • 在集合的首位添加元素:addFirst
      • 在集合的末尾添加元素:addLast
      • 获取第一个元素:getFirst
      • 获取最后一个元素:getLast
      • 删除第一个元素:removeFirst
      • 删除最后一个元素:removeLast
    4. 最重要
      • 遍历查询
        1. For循环
        2. Foreach循环
        3. 迭代器(iterator)
    5. set有addAll,hashset也有addAll

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

  1. 枚举考点

    1. 枚举作为类中的属性使用
    2. 枚举结合switch应用
  2. 集合考点

    1. List
      • 添加:add 指定添加位置add(下标,元素)
        1. 随机数:Math.random(大-小)+小
        2. 字符串类型转换枚举:枚举对象=枚举类.valueOf(字符串)
      • 查询
      • 排序
        1. 必须把List转换为Treeset集合
        2. 比较器(自然、定制)
      • 统计
        1. 统计数量:size()
        2. 分类统计:比较枚举的时候用compareTo
  3. Map
    1. HashMap
      • Key-去重
    2. 计数问题
      • 方法1:通过tocahrArray转换成字符数组去计算
      • 方法2:通过切割去计算

2023.10.19

1.startWiths():返回开头的内容

2.EndWiths():返回末尾的内容

3.数组工具类排序:Arrays

(1)Arrays.sort();

4.集合工具类

(1)里面的方法都是静态方法

(2)好处:如果想要排序的话,必须用TreeSet/TreeMap,但用集合工具类可以有更多选择

(3)注意:集合工具类(Collections)只能是list和map集合

(4)功能代码

排序:Collections.sort();

1)Sort(List list):T类型,可以不用比较器,自带自然排序

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:输入(读)

数据

1)字节流

a.InputStream:字节输入流

b.OutputStream字节输出流

2)字符流

a.Reader:字符输入流

b.Writer:字符输出流

上面四个单词都是抽象类(不能创建对象,所以需要其它类(File)的帮忙)

3)文件字符流

a.FileInputStream:文件字节输入流

b.FileOutputStream文件字节输出流

4)文件字符流

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;

}->可以读取成功了

  1. String s = new String(arr,0,i);->可以将数组中的内容转换成字符串(接着上方的写)
  2. 之后输出就可以了

FileWriter:文件字符输出流(写字符)

1)writer(字符串):写内容(如果是非字符串可以用toString来转换

2)只有时有flush,但没有

3)以字符形式写,就要以字符形式读

(2)缓冲流(缓冲流也叫处理流)(缓冲流也叫高效流(载缓区完成操作))

  • 可以看成将文件流(节点流)包装
  • BufferedInputStream:缓冲字节输入流
  • BufferedOutputStream:缓冲字节输出流
    1. 流实例化名称.write(写的内容.getBytes());
    2. 注意:写完之后一定要刷新flush,不然出不来怨不得别人,切记
  • BufferedReader:缓冲字符输入流
    1. 前提必须创建一个节点流(FileReader),因为Buffered是包装用的
    2. 它的读特点比较多,比普通读的方法简单,用readLine()来读,可以读取一行(返回值是String类型),如果读到空行会输出null,所以可以设定一个结束条件,如果读到null时就结束(break),只有缓冲字符输入(读)流,可以读取一行
    3. 写的话是没有刷新,不必刷新的,写完直接关闭流就可以了
  • BufferedWriter:缓冲字符输出流
    1. 不仅可以用write(\n)换行,还有个特有的方法newLine()方法来换行

5)对象流

2.流按流向分分为输入流和输出流

(1)输入流(读/in方向):从文件到java程序

(2)输出流(写/out方向):从java程序到文件

3.字符和字节判断

(1)文件字符串(字符)

一般遇到汉字就用字符流

(2)音频、视频(字节)

4.流结合集合使用

  1. 注意有流用到循环后,集合要写到循环外边,因为它是一个容器

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.流读的时候高效流不必创建数组

(1)文件流-节点流

(2)缓冲流-处理流(处理节点流)

(3)对象流(重点)-处理流

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循环遍历输出

(4)转换流(了解)->处理流

是一种将字节流按照指定编码的操作

其实就是将字节流转换成字符流(只能这样转)

可以理解成中间纽带的关系,可以让原本的字节流操作换成字符流操作

出现乱码的原因是编码不一样

例如可以用转换流(前面写路径,后面写要转换的格式),只有转换流可以转换格式,转来转去其实就是字节转字符

(5)打印流(了解)

常用到的systeam就是打印流,是字节形式的,它打印到控制台上

注意要用FileOutputStream

打印流分为两种

1)字节打印流

a.PrintStream

b.Pritln()->括号内写要打印的内容

2)字符打印流

a.PrintWriter

b.Pritln()->括号内写要打印的内容

打印流都是输出形式的

注意他是一个流,要创建对象

打印完,要刷新流,关流

(6)随机流(了解)

RandomAccessFile:随机流

r表示只读

seek方法表示设置位置

用字节流读取

2023.10.23

1.(.trim):去除字符串首尾空格

Properties集合

(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(“”)->括号内写要设置成的编码

3Encode:编码

计算机不能直接识别中文,所以要进行编码

4Decode:解码

IO总结

(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.属性

局部内部类

  1. 在方法中或代码块创建的类是局部内部类
  2. 一般放到方法中
  3. 只能使用抽象、最终修饰,不能与外部类重名

  1. 匿名内部类(它是嵌套类中的一种)

    1. 仅能继承父类的对象,实现父类的方法
    2. 没有名字,没有class
    3. 一般用在比较器上
      1. 比如定制比较器comparator
      2. 直接创建comparator方法,重写定制比较器方法,它有方法体,其实是一个类
      3. 省区了在测试类创建定制比较器对象
      4. 调用比较器时,用comparator后的对象名字
    4. 省去再写一个类实现重写方法
    5. 内部类是非静态嵌套类
  2. Lambda表达式

    1. 它是一个名字
    2. 作用:简化代码,将一些已知数据去掉
    3. 它是在JDK1.8之后才有的
    4. 定制比较器可以用Lambda完成
    5. Foreach也可以用Lambda完成
    6. 格式规则
      • 可选类型声明:不需要声明参数类型
      • 如果只有一个形参,可以省略参数列表的括号

2023.10.24

计算机以毫秒为单位

1秒d等于1000毫秒

  1. 多线程

    1. 程序

不运行的叫程序

    1. 进程

运行的叫程序

    1. 线程

线程的单词叫Thread,一个进程可以有多个线程,一个进程中至少有一个线程。

多任务处理方式(三种)

  1. 串行(特点:效率低,相应速度慢)
  2. 并行(理想型的,任务同时进行)(特点:效率高,相应速度块)
  3. 并发(特点:效率高,相应速度块)
  • 单线程

    1. 相当于把一个任务全都做完,才能做另一个任务
  • 多线程

    1. 相当于多任务同时进行
    2. 多线程其实是并发
    3. 线程是进程中系统进行调度的最小单位
    4. 创建线程的三种方式
      1. 继承thread类,创建线程对象,用start方法运行,重写run方法
      2. 实现runnable接口,重写run方法,启动线程和a方法不一样了,它没有start方法,可以用thread的构造方法,将runnable接口转换成thread对象

Thread t1 = new Thread();之后就能用thread对象的start方法了很有趣吧!

Runnable方法如果没有特别之处,那它真的很没有呀!Thread t1 = new Thread(线程对象名,自定义的名称),它可以结合匿名内部类使用

      1. 实现Callable接口,接口里会有一个泛型V,V代表的是线程返回值数据类型,所以它的重写方法不太一样,是call方法,并且带返回值,想运行也不太一样,先创建线程对象,用FutureTask进行包装,因为FutureTask跟runnable接口有关,然后用b方法,将runnable接口转换成thread对象,然后启动线程,重点:因为目的是获取线程返回值,用FutureTask的对象.get();以此获取返回值,输出结果就完成了Callable获取返回值方法了。重点:先运行线程,再获取返回值,不运行是取不到返回值的
    1. 线程没有固定结果,线程会互相抢
    2. Thread.currentThread:当前线程
    3. getname:获取名字
    4. Thread.currentThread.getname:获取当前线程名字
    5. 可以创建多个线程(名字不同)
    6. 主线程名:main,子线程(自定义线程)名是从Thread-0开始,自动编号,但是名字太单一了,如果想自定义名称,可以用到一个方法->setName(在启动前设置名字,因为启动后,名字已经固定了)(主线程想更改名字,就用当前线程.setname进行改名,因为主线程就是当前线程)
    7. 如果想让线程一直运行下去,就用while(true)方法,但计算机是以毫秒为单位的速度,输出太快
      1. 用sleep()方法可以限制输出方法,当前线程暂停,记住1秒等于1000毫秒,sleep方法重点
      2. 线程不能抛出异常,用try-catch抛出异常

getState:获取线程状态,状态在后台是枚举类型

    1. 线程可以设置优先级(只能是1-10):setPriority(1-10数字)方法来设置,所以getPriority方法来获取优先级,数字越大,优先级越高,获取到CPU概率越高,默认的优先级为5
    2. join():插队,但不一定能插入队伍中,只是当一个线程调用后,调用的概率会变高。用到主线程和子线程上,如果想保证子线程运行概率变高,就用join
    3. Yield(静态方法,所以Thread.yield()):礼让,当一个礼让线程礼让后,让同一级别的线程去抢着运行
    4. Interrupt:中断,true时为开启中断

2.新建(new)通过start()启动,编程就绪

2023.10.25

1.双切:((=|,)),跟单切的区别时在双引号内加竖线,左右两边各写一个要切割的内容

2.守护线程

SetDaemon():守护线程,括号内加true为启动守护线程,为其它线程提供服务,随着用户线程的终止而终止,不一定同时停止,会有时间差,守护线程守护的是别人。当A设置为守护线程,它会守护其它线程,也就是为其它线程提供服务(守护),当其它线程执行完毕后,它会立即终止,但是计算机是以毫秒为单位,当其它线程终止后,A刚收到信息,不会马上终止。

线程同步

一般为非同步线程,是线程不安全的,会导致数据出错,所以就有了这个方法。让它变成线程同步的,就是加上锁,让多线程能有序访问统一资源,让线程安全,数据正常运行。

锁不仅仅只有一只,锁有两种(隐式锁和显示锁

  1. 隐式锁

    1. 同步代码块:synchronized(),括号内就是锁,锁是同一把锁,括号内可以是任意对象,一般在测试类中创建一个Object对象,加上static使其能直接调用,静态(static)在main方法外写,写完之后就实现了上锁和解锁了;括号内还可以写this,但必须保证对象仅有一个;也可以写类名.class(反射对象一个)
    2. 同步方法:在方法外再创建一个方法,用synchronized进行修饰就成为了同步方法了,同步方法就不用Object了,它的对象就是this(当前对象),不用再写了,再一个类中调用方法,直接调用。同步方法必须保证对象仅有一个,因为括号写的是this
  2. 显示锁

    1. Lock lk = new ReentrantLock();ReentrantLock跟隐式锁相关联。
    2. 放到方法外,加上static
    3. 测试类用Test.lk.lock进行上锁,Test.lk.unlock进行解锁

2023.10.26

  1. 线程同步(重点)

线程同步是在线程互斥的基础上实现资源的有序访问

  1. 线程通信(重点)

可以想象成电话。生产者、消费者模式。库存满了生产者,生产者等待消费者消费;库存空了,消费者等待生产者生产

  1. 传统的线程通信

保证线程同步(隐式锁)的基础上,进行线程通信,一般建立三个类,一个生产者线程、一个消费者线程、一个仓库类(Test),方法有等待方法:wait()->线程的等待,线程里没有抛出异常,不等待进行生产,让定义的值++;生产完进行唤醒正在等待的线程(生产者线程),方法notify:唤醒一个线程;notifyAll:唤醒多个线程,以此,可以很轻松的再写出消费者。锁用的是一把锁,就像通话用的是一条线一样

  1. Lock锁的Condition通信

保证线程同步(显式锁)的基础上,newCondition和lock锁有关,通过lk.newcondition,获取两个返回值(库满的和库空的状态),放main方法外,加static,注意:再main方法内写,因为有提示。等待和传统方式不太一样,等待状态:await;唤醒动作:signal(唤醒一个线程)、signalAll(唤醒多个线程)

  1. 多线程产生的原因
  1. 多个线程共享同一个资源
  2. 多个线程都对共享资源进行修改
  3. 多个线程同时对共享资源进行修改

2023.10.27

线程通信

在同一进程中 线程间资源共享和信息传递的机制 成为线程通信

同步方法不同 通信手段不同

多个线程 操作共享数据 交替执行

同步两种方式

隐式同步

Synchronized

传统线程通信

显示同步

Lock同步

Lock的Condition

  1. 阻塞队列

Collection 集合

List

Set

Queue--队列--先进先出,阻塞队列跟它有关

BlockingQueue--先进先出,Blocking是它的特点,创建对象时必须写长度,代表队列中能存储的数据容量

方法:put();代表向队列添加数据 队列满,阻塞等待,程序不会结束

.take获取返回值;代表从队列取出数据 队列空,阻塞等待,程序不会结束

Take方法每次取只能取队列第一个元素

生产消费

生产者负责循环对商品数量++ 达到上线则等待

使用put方法 完成生产功能 像队列添加元素

消费者负责循环对商品数量-- 达到下线则等待

使用take方法 完成消费功能 像队列取出元素

有两种运行效果:一生一用、一生产完一用完

ArrayBlockingQueue--基于数组实现的阻塞队列

  1. 先等待的线程先被唤醒
  2. 构造方法方法必须传参,传入一个参数;传入两个参数,第一个参数代表容量、第二个参数代表公平策略

BlockingQueue的特点:可以不用写同步方法,简单方便

LinkedBlockingQueue

可以指定容量大小、也可以不指定,但不支持公平策略

SynchronousQueue

不能给定大小,它一定是交替执行的

DelayQueue

到期元素先被取出

PriorityBlockingQueue

值最小的优先被取出

  1. 线程池

特点:服用线程,不够用了,再使用新的线程,避免线程的浪费

高频率、短任务

重复使用几个线程,减少使用和销毁线程的开销

就像客服,复用客服人员,完成接打电话短工作请求

案例

创建线程 输出一句话

主线程中 循环100次 启动线程 执行线程

使用线程池的步骤

  1. 获取线程池

Executors类创建线程池,获取返回值pool

  1. 将任务提交到线程池中执行

必须实现runnale任务或callable接口的子类,才叫任务

Executor

Submit(callable/runnale)

mt是类线程对象

pool.submit(mt)是将线程放入线程池提交任务

Execute(runnale)

  1. 关闭线程池

Shutdown()

线程池

固定大小线程池

FixedThreadPool(int)

线程的数量固定了,就定义的那么多

Int来设定线程的数量,但不管数量的多少,它的核心线程仅有一个

核心线程数=固定线程数-最大线程数

缓存线程池

CachedThreadPool

可回收空闲的,按需控制线程数量

单线程线程池

SingleThreadExecutor

仅创建一个线程的线程池

定时线程池

ScheduledThreadPool

执行任务定时和周期性任务的线程池,

提交任务:schedule(任务,时间,时间单位);

不能shutdown关闭线程

Schedule(任务,延迟时间,间隔时间,时间单位):有点像闹钟的贪睡功能

线程池的名字:Pool-1-thread

考点

线程池结合callable方法获取返回值

2023.10.28

  1. 网络编程

重点技能:

同步网络编程TCP UDP

异步网络编程

  1. 计算机网络是由相互连接、一个共享资源和信息传递的系统
  2. 按规模大小LAN(局域网)、MAN(城域网)、WAN(广域网)
  3. 按网络拓扑结构分:星型网络、环形网络、总线型网络、树型网络、星型环形网络
  4. 按传输介质划分:双绞线网、同轴电缆网、光纤网、卫星网(手机没有信号,用的就是卫星网)
  5. 为什么计算机网络分层
  6. OSI七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
  7. 物理层:例如双绞线;链路层:以帧为单位传送数据的;网络层:提供最基本的端到端数据透明传送的(寻址和路由);传输层:实现端到端的数据传输(保证数据的安全);表示层:在不同计算机用户之间建立、维持和同步回话的;表示层(数据加密和解密);应用层(实现进程之间信息的交互)
  8. TCP/IP模型:网络接口层、网络层、传输层、应用层
  9. 网络协议是不同操作系统和硬件体系结构的计算机之间提供通信支持的一种网络通用语言
  10. IP协议是网络层的协议,提供最基本的端到端数据透明传输的协议
  11. IP地址查询方法:ipconfig
  12. DNS:域名解析:就像百度直接输入名字就可以了
  13. IPV4:32位;IPV6:128位
  14. IP地址通常用点分十进制
  15. 特殊IP地址:127.0.0.1表示本机,每个电脑都要有网卡
  16. InetAddress类:表示互联网的IP协议
    1. 它是没有构造方法的
    2. GetByName:获取ip
    3. GetHostAddress:获取第二步或第四部的Ip地址
    4. getLocalHost:获取本机的ip
    5. GetHostName:获取本机主机名
  17. 端口
    1. 物理端口
    2. 逻辑端口:像任务管理器(为了区分服务)
    3. WEB服务:80端口;FTP服务:21端口
    4. 端口号范围是从0到1023之间的
    5. 注册端口是从1024到49151,开放给用户使用、稳定的端口
  18. IP地址和端口号组成了套接字
    1. 套接字分为客户端套接字、服务端套接字
    2. 通信原理:C/S:客户端(软件)和服务器;还有B/S:浏览器和服务器
    3. C/S:C是客户端-主动方;S是服务端-被动方,服务方等待客户端的连接
    4. 一定是客户端主动,它们之间的沟通是通过流的形式传输的
    5. 服务器的套接字(ip地址+端口号):ServerSocker,创建对象注意括号内没有双引号,里面写端口号,让后accept是监听客户端的连接,返回值Socket,接收客户端的连接;客户端的套接字(ip地址+端口号):Socket,没有Server了,他的括号内写服务端的端口号和服务端的ip地址,ip地址写在端口号前面,加双引号。
    6. 服务器先执行,让后客户端执行后,服务器接收到,发消息一定是服务器给客户端发送,消息的发送和接收是通过字节流形式传输的
    7. getOutputStream:产生字节写流

getOutputStream:产生字节读流

    1. 关流关闭ServerSocker和Socker对象就可以了,关闭的重点:先用的后关、后用的先关
  1. 线程池(重点)
    1. 以下有实现runnale接口和实现Callale接口(重点)
    2. 实现runnale接口:要掌握的线程池(单线程池和固定大小线程池)

将原始的线程启动方式换另一种方式启动

创建线程池的工具类:Executors

创建单线程池:Executors.newSingleExecutors

线程池提交任务方式:让线程池对象.submit(放线程对象);

submit可以换成execute,一般用submit,用完后shutdown关闭线程

创建固定大小线程池:Executors.newFixedExecutors,后面的都一样

    1. 实现Callale接口(重点)

没有线程池

  1. 创建线程对象
  2. 封装到任务中
  3. 封装到Thread类中
  4. 启动线程
  5. 获取线程的返回值

用线程池提交任务

单线程池有1条线程,固定大小线程池线程数量自己定义

  1. 创建单/固定大小线程池(无论哪个线程池,都要用Executors)
  2. 提交任务(用submit,这里不能用execute)

这里相当于启动线程,下面就能获取返回值了,获取完返回值后立马就关闭线程池(shutdown:关闭线程池)

  1. 阻塞队列

是生产、消费的另一种,所以根线程有关

先进先出

放的时候用put,拿的时候take

2023.10.30

  1. 异步服务器(属于并发状态)(AIO)

AsynchronousServerSocketChannel

  1. 打开异步服务器套接字通道

用open方法

AsynchronousServerSocketChannel.open获取返回值,假设放回值是a

  1. 绑定端口(相当于accept监听)

bind方法

  1. bind(new inetSocketAddress(端口号))
  1. 接受成功
  1. accept();获取返回值,假设返回值是b
  1. 接受成功后
  1. get();方法获取返回值,建立通道,假设返回值是c
  1. 服务端给客户端发消息
  1. write(ByteBuffer.wrap(“字符串”).getBytes(“转换的类型”));

返回值是future类型

  1. 让6.的返回子get()获取值
  2. 下面是客户端的
    1. 接受字节缓冲区

ByteBuffer b = ByteBuffer.allocate(“”);

  1. TCP:服务器端 客户端 重点
  2. 即时聊天-线程安全(服务器支持并发,发几句话)
    1. 通过结合线程实现服务器并发
    2. 并发是多线程会出现并发操作
    3. 读的时候进行并发操作(在服务器循环上进行内名内部类读的操作)
    4. 服务端接收(写到主方法上方,也就是子线程)
  3. AIO:异步服务器
  4. UDP(理解):TCP特点:面向连接可靠性好、安全性差、占用资源、速度慢、效率低,UDP跟TCP特点相反
    1. 数据报

数据报是再UDP协议下,通过网络传输数据的基本单元

    1. UDP也有客户端和服务端

UDP跟TCP相反,它是客户端先发送数据报

    1. java.net包下(基于UDP的网络编程)
  • DategramSocket

用于发送和接受数据报的套接字,注意:客户端和服务端的套接字是一个

发送:send方法

  • DategramPacket

数据报包,是将数据转换成数据报的方法

封装数据报

DategramPacket 自定义名称 = new DategramPacket(将数据转换成字节数组,获取数据长度,InetAddress.getByName("127.0.0.1"),客户端地址);

InetAddress.getByName("127.0.0.1")->通过地址名称获取服务端地址

getDate():将数据报转换为字节数组

getLength():数据报长度

    1. 相当于发快递,客户端等着服务端发送数据,所以它是先启动客户端,然后再启动服务端
  1. 复习
    1. 线程同步(买票、吃苹果)

共有资源,几个线程抢

    1. 线程通信:抢占资源 排队

传统线程:等待:wait; 唤醒:notifiy、notifiyAll

Lock锁的condit通信:等待:await; 唤醒:signal signalAll

反射

  1. 反射(底层结构 专业5会再深入学习 mybatis框架 专业3:反射获取jdbc连接)
    1. 反射是为了保密
    2. 反射就像照镜子一样,不是自己,但轮廓一样
    3. 反射是动态获取内部对象的信息
    4. 类的结构 修饰符class 类名{}
      • 成员方法(普通方法、构造方法)
      • 成员变量(属性)
    5. 获取反射对象
      • Class c = 类名.class;

Class.forName(全限类名),全限类名=包名+类名

      • new 对象名.getClass(),获取返回值
    • 类-class

属性-Field

方法-Method

构造方法-Constructor

    1. 类中获取属性

      • 获取类中所有的属性-getDeclaredFields
      • 获取类中公共属性-getFields
    2. 类中获取方法

      • 获取类中所有方法-getDeclaredMethod
      • 获取类中公共方法-getMethod
    3. 类中获取构造方法

      • 获取类中所有构造方法-getDeclaredConstructor
      • 获取类中公共构造方法-getConstructor
    4. 类中获取接口方法

getInterfaces

    1. 类中获取类父类

GetSuperclass(父类只能有一个)

    1. 类中获取类修饰符方法

getModifiers,返回值是int类型,

将int类型转换成字符串类型

Modifier.toString(int);获取返回值类型

    1. 类中获取全限定类名(包名+类名)

getName

获取简单类名

getsimplename

2023.10.31

  1. Constructor-构造方法(重点)

构造方法作用:创建对象

  1. 无参构造创建对象

    1. 获取类的反射对象

Class c = Teach.class;

(2)无参构造创建对象

Teach t = c.newInstance

等价Teacher t = new Teacher();

  1. 有参构造创建对象

Teacher t = new Teacher(参数);

    1. 获取类的反射对象

Class c = Teach.class;

(2)有参构造创建对象

c.getDeclaredConstructor(可变传参);

可变传参写法:类型.class。返回值是有参构造,假设变量是a

然后a.newInstance(对应参数写);返回值对应上面Teacher

2023.11.2

  1. 代理分为静态代理和动态代理两种
    1. 静态代理:谁帮我代理我知道
    2. 动态代理:谁帮我代理我不知道

基础上是通过反射执行的

    1. invoke()就是调用类中的方法,第一个参数是obj(对象),在我们平常使用过程中用到的是类,类是对象的一个集合,第二个参数是args(参数),是调用invoke这个方法所使用的参数,我们使用是一般是类中的方法(method),因此invoke方法我们也可以这样理解:invoke(class,method),相当于把类中的方法参数化了。

2023.11.3

冒泡和递归的时间和空间复杂度

  1. 冒泡排序完的时间复杂度(最坏情况):O(n^2)
  2. 冒泡排序完的时间复杂度(最好情况):O(n)
  3. 冒泡排序完的空间复杂度:O(n)
  1. 递归的时间复杂度:O(n)
  2. 递归的空间复杂度:O(n)

你可能感兴趣的:(Java,java,学习)