JAVA开发学习笔记-下
双列集合
Map简介
- 特点
- 需要存一对数据,分别为键和值
- 键不能重复,值可以重复
- 键值对一一对应,每一个键只能找到自己对应的值
- 键+值,整体称之为键值对或者键值对对象,在Java中叫做“Entry对象”
- 体系结构
- Map
- HashMap
- LinkedHashMap
- TreeMap
- HashMap
- Map
- 常见API
- put:添加/覆盖元素,添加时返回null,覆盖时返回对应的覆盖值
- remove:根据键删除键值对元素
- clear:移除所有的键值对元素
- containsKey:判断集合是否包含指定的键
- containsValue:判断集合是否包含指定的值
- isEmpty:判断集合是否为空
- Size:返回集合的长度,即键值对的个数
- 遍历方式
- HashMap的特点
- HashMap是Map里面的一个实现类
- 没有额外需要学习的特有方法, 直接使用Map里面的方法就可以了。
- 特点都是由键决定的:无需、不重复、无索引
- HashMap和HashSet底层原理是一样的,都是哈希表结构
- 总结
- HashMap底层是哈希表结构的
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键存储的是自定义对象,需要重写hashCode和equals方法,如果值存储的是自定义对象,不需要重写hashCode和equals方法
- HashMap底层原理
- 哈希表+链表
LinkedHashMap
- 有序、不重复、无索引。
- 有序指的是保证存储和取出的元素顺序一致,原理是哈希表+双向链表
- 无特定api,使用Map的api即可
TreeMap
- 底层原理和TreeSet一样,都是红黑树结构
- 不重复、无索引、可排序,排序指的是对键进行排序(按照键从小到大,也可自定义)
- 无特定api,使用Map的api即可
- 自定义排序规则
- 实现Comparable,指定比较规则
- 创建集合时传递Comparator比较器对象,指定比较规则
可变参数
- 格式:参数类型 …参数名
- 细节:
- 在方法的形参中,最多只能写一个可变参数
- 在方法中,同时存在可变参数和其他形参,那么可变参数要写在最后
Collections
- java.util.Collections:集合工具类
- 常用API:
- addAll(Collection
c, T… elements):批量添加元素,只能给单列集合进行添加 - shuffle(List<?> List): 打乱List集合元素的顺序
- sort:排序
- binarySearch:以二分查找法查找元素
- copy:拷贝集合中的元素
- fill:使用指定的元素填充集合
- max/min:根据默认的自然排序获取最大/最小值
- swap(List<?>list, int I, int j):交换集合中指定元素的位置
- addAll(Collection
不可变集合
- 定义:不可以被修改(修改长度 or 修改内容)的集合
- 应用场景:不想被别人修改集合的内容
- 书写格式:使用参数类型.of方法,如List.of、Set.of、Map.of/ofEntries获取不可变集合
Stream流
- 作用:结合Lambda表达式,简化集合、数组的操作
- 使用步骤
- 先得到一条Stream流
- 单列集合:stream()
- 双列集合:无法直接Stream流,需要先转换为keySet()或者entrySet()
- 数组:使用Arrays.Stream(array)生成对应的Stream流
- 零散数据:使用Stream.of生成对应的Stream流,必须传递引用类型数据
- 再使用中间方法进行操作,如使用filter、foreach等
- 最后使用终结方法进行操作
- 先得到一条Stream流
- 用法:集合.stream().filter(e->(e)).foreach(e->表达式(e))
- Stream常见中间方法
- filter:过滤
- limit: 获取前几个元素
- skip:跳过前几个元素
- distinct:元素去重,依赖HashCode和equals方法
- concat: 合并a和b两个流为一个流
- Map:转换流中的数据类型
- 注意用法:
- 中间方法会返回新的Stream流,原来的Stream流只能用一次,建议使用链式编程
- 修改Stream流中的数据,不会影响原来结合或者数组重的数据
- Stream常见终结方法
- 把已经有的方法拿过来用,当做函数式接口中抽象方法的方法体
- 使用要求
- 引用处必须是函数式接口
- 被引用的方法必须已经存在
- 被引用方法的形参和返回值需要和抽象方法保持一致
- 被引用方法的功能要满足当前需求
- 方法引用符用双冒号::表示
- 方法引用的分类
- 引用静态方法
- 类名::静态方法名,如Integer::parseInt
- 引用成员方法
- 格式-对象::成员方法
- 其他类-其他类对象::方法名
- 本类-this::方法名(引用处不能是静态方法)
- 父类-super::方法名(引用处不能是静态方法)
- 引用构造方法
- 类名::new,如Student::new,结合map使用
- 其他调用方式
- 使用类名引用成员方法
- 格式:类名::成员方法
- 独有且不同的规则:
- 被引用方法的形参,需要跟抽象方法的形参到最后一个形参保持一致,返回值需要保持一致
- 抽象方法参数解析
- 第一个参数:表示被引用的调用者,决定了可以引用哪些类中的方法,在Stream流当中,第一个参数一般都表示流里面的每一个数据。
- 参数二到最后一个参数:需要跟抽象方法的形参到最后一个形参保持一致,返回值需要保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法。
- 局限性:
- 不能引用所有类的成员方法
- 是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法
- 引用数组的构造方法
- 格式-数据类型[]::new,结合toArray使用
- 细节:创建数据的类型,需要和流中的数据类型保持一致。
- 使用类名引用成员方法
- 引用静态方法
异常
- 误区:不是让我们以后不出异常,而是程序出了异常以后,该如何处理
- 异常的继承体系
- java.lang.Throwable
- Error:系统级别错误,属于严重错误,硬件问题,不需要程序员关注
- Exception:异常,代表程序可能出现的问题,会用Exception及其子类来封装程序出现的问题
- RuntimeException及其子类,即运行时异常,在编译阶段不会出现异常提醒,在编译阶段不需要处理,是程序运行出现的,一般由于参数传递错误或者方法错误调用导致。
- 编译时异常:在编译阶段就会出现异常提醒,在编译阶段必须要手动处理,否则代码报错,作用在于提醒程序员
- java.lang.Throwable
- 异常的常用方法
- getMessage():返回此throwable的详细消息字符串
- toString():返回此可抛出的简单描述
- printStackTrace():把异常的错误信息输出在控制台,底层是System.err.println()
- 作用:
- 异常是用来查询Bug的关键参考信息
- 异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况
- 异常的处理方式
- JVM默认的处理方式
- 把异常的名称,异常原因及异常出现的位置等信息输出在了控制台
- 程序停止执行,下面的代码不会再执行了
- 自己处理异常(捕获异常)
- try{可能出现异常的代码;} catch(异常类名 变量名){异常的处理代码;}
- 当代码出现异常时,可以让程序继续往下执行。
- 常见问题
- 如果try没有遇到问题:会执行try里的全部代码,不执行catch中代码
- 如果try中遇到多个问题:
- 需要写多个catch来捕获所有对应的异常。
- 如果捕获的多个异常存在父子类关系,父类异常捕获放到最后。
- jdk7后可以用|来同事捕获多个异常并采取同一个处理方案。
- 如果try中遇到的问题没有被捕获:使用JVM虚拟机异常处理方式打印异常
- 如果try中遇到了问题:
- 问题语句下面的其他代码不会执行,会跳到对应catch进行后续代码执行。
- 调用finally能够保证后续语句一定能够执行
- 抛出异常
- throws:
- 写在方法定义处,表示声明一个异常
- 告诉调用者,使用本方法可能会有哪些异常
- 编译时异常必须要写,运行时异常可以不写
- throw:
- 格式:throw new xxxException();
- 写在方法内,结束方法
- 手动抛出异常对象,交给调用者
- 方法中的代码不再继续执行
- throws:
- JVM默认的处理方式
- 自定义异常
- 意义:为了让控制台的报错信息更加的见名知意
- 步骤:定义异常类、写继承关系、空参构造、带参构造
File
File对象表示一个路径,可能是文件路径,也可以是文件夹路径,这个路径可以是存在的,也可以是不存在的
相对路径与绝对路径:相对路径不带盘符、绝对路径带盘符
构造方法
- File(String pathname)
- File(String parent, String child)
- File(File parent, String child)
成员方法
判断/获取
- isDirectory()
- isFile()
- exists()
- length()
- getAbsolutePath()
- getPath()
- getName()
- lastModified()
创建/删除
- createNewFile():创建一个新的空的文件
- mkdir():创建单级文件夹
- mkdirs():创建多级文件夹
- delete():删除文件、空文件夹
遍历
listRoots()
listFiles():获取文件夹中的所有内容
list() or list(Filenamefilter)
listFiles() or listFiles(fileFilter) or listFiles(nameFilter)
IO流
- 存储和读取数据的解决方案。用于读写文件或网络中的数据
分类
- 根据流的方向:输入流/输出流
- 根据操作文件类型:字节流/字符流
IO流体系结构
- 字节流
- InputStream(抽象类)
- FileInputStream:字节输入流
- read()/read(byte[] data)/readAllBytes(),读完返回-1
- close()
- FileInputStream:字节输入流
- OutputStream(抽象类)
- FileOutputStream:字节输出流
- write(),参数为int、byte[]或者(byte[], int, int)
- close():关闭文件句柄
- 换行:windows下\r\n,Linux为\n,Mac为\r
- 续写开关,FileOutputStream的第二个参数设置为true;
- FileOutputStream:字节输出流
- InputStream(抽象类)
- 字符流
- Reader(抽象类)
- FileReader():字符输入流
- Writer(抽象类)
- FileWriter():字符输出流
- 转换流
- Reader(抽象类)
- 缓冲流:
- 字节缓冲流
- BufferedInputStream:字节缓冲输入流
- BufferedOutputStream:字节缓冲输出流
- 字符缓存流
- BufferedReader():字符缓冲输入流
- BufferedWriter():字符缓冲输出流
- 字节缓冲流
- 转换流(属于字符流)
- InputStreamReader():转换输入流
- OutputStreamWriter():转换输出流
- 序列化流(属于字节流)
- ObjectOutputStream():把基本流包装成序列化流
- ObjectInputStream():把基本流包装成反序列化流
IO流释放资源模式
- 基础做法:try{}catch{}finally{}-手动释放资源
- 自动释放资源:
- 条件:实现了AutoCloseable接口的类才能在小括号中创建对象
- JDK7方案:try(创建流对象1;创建流对象2){}catch{}
- JDK9方案:创建流对象1;创建流对象2;try(流1;流2){}catch{}
字符集
- 计算机以二进制的形式存储数据,计算机中最小的存储单元是一个字节
- ASCII:英文用一个byte(8bit)就可以,不够8位前面补0,转成十进制是正数
- GBK:表示英文或中文; GB2312表示简体中文,BIG5表示繁体中文,GBK包含以上两部分。
- 英文是一个字节存储,完全兼容ASCII
- 汉字是两个字节存储;高位字节二进制一定以1开头,转成十进制之后是一个负数,高位字节为0为1是为了区分并识别中英文
- Unicode:万国码/国际标准字符集
- 英文:
- UTF(unicode transfer format)
- UTF-16用2-4字节存储,UTF-32用4字节存储
- UTF-8用1~4个字节保存
- ASCII为1个字节表示),前面补0
- 中文为3个字节表示,第一个字节补1110,第二个字节补10,最后一个字节补10
- 英文:
- 编码与解码
- 乱码原因->解法:
- 读取数据时未读完整个汉字->不要用字节流读取文本文件
- 编码和解码的方式不统一->编码解码时使用同一个码表,同一个编码方式
- 编码与解码
- 编码方法:String.getBytes()/String.getBytes(String charsetName)
- 解码方法:String(byte[] bytes)/String(byte[] bytes, String charsetName)
- 乱码原因->解法:
字节流
- InputStream(抽象类)
- FileInputStream:字节输入流
- read()/read(byte[] data)/readAllBytes(),读完返回-1
- close()
- FileInputStream:字节输入流
- OutputStream(抽象类)
- FileOutputStream:字节输出流
- write(),参数为int、byte[]或者(byte[], int, int)
- close():关闭文件句柄
- 换行:windows下\r\n,Linux为\n,Mac为\r
- 续写开关,FileOutputStream的第二个参数设置为true;
- FileOutputStream:字节输出流
字符流
- 字符流的底层其实就是字节流,即字符流=字节流+字符集
- 输入流:一次读一个字节,如果遇到中文,一次读多个字节
- 输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中
- FileReader和FileWriter用法和字节流基本类似,不再赘述
- 字符输入流原理
- 在创建输入流对象时,创建一个8192的字节数组,作为缓冲区
- 每次read()时先读缓冲区,没有的话读取文件数据放入缓冲区,后续从缓冲区读取以提高效率
- 如果缓冲区和文件都没有数据时,返回-1
- 空参的read方法执行读取+转码,有参的read方法执行读取+转码+强转
- 字符输出流原理
- 在创建输出流对象时,创建一个8192的字节数组,作为缓冲区,输出数据先写入缓冲区
- 情况
- 装满了:将缓冲区的内容直接保存到文件中
- flush:将缓冲区中的数据,刷新到本地文件中
- close:把缓冲区数据写入文件,并关闭文件操作,无法继续操作文件
字节流与字符流的适用场景
- 字节流:拷贝任意类型的文件
- 字符流:读取纯文本文件中的数据;往纯文本文件中写出数据
缓冲流
- 字节缓冲流
- BufferedInputStream:字节缓冲输入流
- BufferedInputStream(InputStream is)、
- BufferedOutputStream:字节缓冲输出流
- BufferedOutputStream(OutputStream os)
- BufferedInputStream:字节缓冲输入流
- 字符缓存流
- BufferedReader():字符缓冲输入流
- readLine():读取一行数据(不包含换行),如果没有数据可读了,会返回null
- BufferedWriter():字符缓冲输出流
- newLine():跨平台的换行
- BufferedReader():字符缓冲输入流
- 使用注意事项:底层自带了长度为8192字节/8k缓冲区提高性能,但缓冲流不能直接操作数据,仍然需要创建基本流
转换流
- 转换流归属于字节流m是字符流和字节流之间的桥梁
- 类型
- InputStreamReader():转换输入流,需要包装一个字节流,将其转换为字符流
- OutputStreamWriter():转换输出流,需要包装一个字符流,将其转换为字节流
- 作用:
- 完成编码转换-jdk11后废弃使用
- 字节流想要使用字符流的方法。
序列化流
序列化
- 序列化流可以把java中的对象写到本地文件中
- 构造方法:ObjectOutputStream():把基本流包装为序列化流
- 成员方法:writeObject(Object obj):把对象序列化(写出)到文件中去
- 使用方法(注意javabean类需要实现Serializable接口):
- 创建对象、创建文件输出流、创建序列化流、序列化对象到文件中。
反序列化
- 反序列化可以把序列化到本地文件中的对象,读取到程序中来
- 构造方法:ObjectInputStream():把基本流包装为反序列化流
- 成员方法:readObject()把序列化到本地文件中的对象,读取到程序中来
- 使用方法
- 创建文件输入流,创建反序列化对象,反序列化文件内容为对象
使用细节
- 创建文件输入流,创建反序列化对象,反序列化文件内容为对象
- 序列化对象后类被修改导致无法反序列化
- 解法:固定版本号,可以避免版本迭代后无法反序列化回原来的对象。
- 格式:private static final long serialVersionUID = 1L;
- 优化:通过IDEA插件提醒设置版本号
- 解法:固定版本号,可以避免版本迭代后无法反序列化回原来的对象。
- 不想把特定字段序列化,可以在成员方法前加关键字transient(瞬态关键字)
- 多个对象序列化时,先放到集合中再进行序列化。反序列化时直接反序列化回集合即可。
打印流
- 介绍:打印流不能读,只能写,所以只有输出流
- 分类
- PrintSteam:字节打印流
- PrintWriter:字符打印流
- 特有的写出方法可以实现:
- 数据原样写入
- 自动刷新、自动换行(打印一次数据=写出+换行+刷新)
- 字节打印流(无缓冲区)
- 构造方法
- PrintStream(OutputStream/File/String):关联字符输出流/文件/文件路径
- PrintStream(String fileName, Charset charset):指定字符编码
- 成员方法
- write():常规方法,规则跟之前一样,将指定的字节写出
- println():特有方法:打印任意数据,自动刷新,自动换行
- print():特有方法:打印任意数据,不换行
- printf():特有方法:带有占位符的打印语句,不换行
- System.out本质上就是一种字节打印流
- 构造方法
- 字符打印流(有缓冲区)
- 构造方法
- PrintWriter(OutputStream/File/String):关联字节输出流/文件/文件路径
- PrintWriter(String fileName, Charset charset):指定字符编码
- PrintWriter(Write w, boolean autoFlush):自动刷新
- PrintWriter(OutputStream out, boolean autoFlush, Charset charset):指定字符编码且自动刷新
- 成员方法
- write():常规方法,规则跟之前一样,将指定的字节写出
- println():特有方法:打印任意数据,自动刷新,自动换行
- print():特有方法:打印任意数据,不换行
- printf():特有方法:带有占位符的打印语句,不换行
- 构造方法
压缩流
- 压缩流和解压缩流有需要的时候再学吧
Commons-io
- Apache开源基金组织提供的一组有关IO操作的开源工具包,用于提高IO流的开发效率。
- FileUtils.method():按需查询文档即可。
- IOUtils.method():按需查询文档即可。
hutool
- 难得糊涂,无所谓失,无所谓得
- 工具包相关类
- IOUtil:流操作工具类
- FileUtil:文件读写和操作的工具类
- FileTypeUtil:文件类型判断工具类
- WatchMonitor:目录、文件监听
- ClassPathResource:针对ClassPath中资源的访问封装
- FileReader:封装文件读取
- FileWriter:封装文件写入
网络爬虫
- 创建url对象、url对象建立连接,获取字节输入流,按需使用转换流,然后按需使用
properties的基本使用
- 文件格式:xxx.properties
- 存储均以键值对形式,对应Map子类Properties,键不可重复
- 一般情况下Properties中只存储String类型数据
- 通过Store存储配置至本地,并通过load方法从文件中读取配置
多线程
- 进程:进程是程序的基本执行实体。
- 线程:线程是操作系统进行运算调度的基本单位,它被包含在进程之中,是进程中的实际运作单位。
- 使用多线程可以来提高程序的运行效率,CPU可以利用让程序同时做多个事情
多线程中的概念
- 并发:在同一时刻,有多个指令在单个CPU上交替执行。
- 并行:在同一时刻,有多个指令在多个CPU上同时执行。
多线程的实现方式
继承Thread类的方式进行实现
- 自定义类并重写run方法,然后创建新类,新类执行start方法即可。
实现Runnable接口的方式进行实现
- 自定义类并实现runnable接口,然后创建新类,然后创建new Thread(新类).start()
利用Callable接口和Future接口(需要管理返回值)
- 创建一个自定义类实现Callable接口
- 重写call方法(有返回值,表示多线程运行的结果)
- 创建自定义类的对象(表示多线程要执行的任务)
- 创建FutureTask的对象(作用管理多线程运行的结果)
- 创建Thread类的对象,并启动线程
多线程的常见成员方法
- Thread.currentThread():获取当前线程
- setName()/getName():设置/获取当前线程的名字
- sleep(long time):让当前线程休眠指定时间
- getPriority/setPriority(int newPriority):查看/设置线程的优先级(0-10,数值越大优先级越高)
- setDaemon(boolean on):设置为守护线程, 其他非守护线程执行完毕之后,守护线程也会随之结束,通常附属线程设置为守护线程
- yield():出让线程/礼让线程,可以直接出让CPU的执行权,让多线程程序运行相对更加均匀。
- join():插入线程/插队线程,join可以直接让当前进程直接获取CPU的优先权
线程优先级
- 线程调度模式
- 抢占式调度(随机性):Java采用此类调度,数值越大抢占到CPU的概念越高
- 非抢占式调度
线程的生命周期
- 新建:创建线程对象
- 就绪:start或sleep/阻塞方法结束后有执行资格,没有执行权(需要抢占CPU来获取执行权)
- 运行:有执行资格,有执行权(由就绪态抢到CPU执行权转换而来,如果CPU执行权被抢走,则会变为就绪状态)
- 阻塞:没有执行资格,没有执行权(执行sleep或其他阻塞式方法会变为此状态)
- 死亡:线程死亡,变成垃圾(run执行完毕)
线程安全问题
- 多个线程操作一个共享资源时,会出现并发写导致的非预期问题,比如卖票卖超问题。
同步代码块
- 把操作共享数据的代码锁起来
- 格式:synchronized(锁){操作共享数据的代码},锁需要是唯一的对象(比如ClassName)。
- 特点:
- 锁默认打开,有一个线程进去了,锁自动关闭
- 里面的代码全部执行完毕,线程出来,锁自动打开
- 使用注意点:
- 循环不能写在synchronized里面,否则CPU就会一直被单线程占用无法切换
- 锁要上在条件判定语句之前,比如一定要把条件包含if给包进去,锁内不能有循环
- synchronized的锁对象需要是唯一的。
同步方法
格式:修饰符 synchronized 返回值类型 方法名(方法参数){……}
特点
同步方法时锁住方法里面所有的代码
锁对象不能自己指定
- 非静态方法锁的是this
- 静态方法锁的是当前类的字节码对象文件
其他:
- StringBuilder线程不安全,StringBuffer线程安全
Lock锁
- JDK5后新增锁对象Lock锁,可以更清晰的表达加锁和释放锁
- Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来进行实例化。
- 同步代码块搭配Thread,需要锁为static
- 同步方法搭配Runnable,需要锁为非static
- Lock搭配Thread,需要锁为static
- Lock搭配Runnable,需要锁为非static
- 锁的unlock操作可以放到finally中以保证锁可以被正确释放
- 死锁:双锁嵌套,导致两个锁处于竞争态,需要避免此类用法
等待唤醒机制-生产者和消费者
- 生产者notify() 、消费者wait()
- 生产者wait()、消费者notify()
- 常见方法:
- wait():当前线程等待,直到被其他线程唤醒,与锁绑定
- notify():随机唤醒单个线程,与锁解绑
- notifyAll():唤醒所有线程,与锁解绑
- 注意:
- wait、notify、notifyAll都是调用在锁上的方法
- 生产者与消费者数量不一致且使用notify时会导致死锁,比如两个生产者都生产完了,然后都在等消费者消费
等待唤醒机制-阻塞队列
- 体系结构
- 实现接口
- Iterable
- Collection
- Queue
- BlockingQueue
- 实现类
- ArrayBlockingQueue:数组实现、有界
- LinkedBlockingQueue:链表实现、无界,但有最大值
- 实现接口
- 使用方法
- 创建Producer产生数据
- 创建Producer消费数据
- 创建阻塞队列来实现数据互通,阻塞队列自带锁,无需手动加锁
线程的6种状态
线程池
构造方法
- ExecutorService.newCacheThreadPool():创建没有上限的线程池
- ExecutorService.newFixedThreadPool(int max):创建有上限的线程池
成员方法
- submit():将任务提交至线程池,会自动执行run方法
- shutdown():销毁线程池
自定义线程池
- 任务拒绝粗略
- 任务拒绝粗略
七个创建线程池必备的参数
- corePoolSize:核心线程池数量
- maximumPoolSize:最大线程数
- keepAliveTime:线程存活时间(值)
- unit:线程存活时间(单位)
- workQueue:阻塞队列,可选ArrayBlockingQueue、LinkedBlockingQueue
- threadFactory:线程池创建模式,可以设置为Executors.defaultThreadFactory()
- RejectedExecutionHandler:任务拒绝策略,内部静态类,如new ThreadPoolExecutor.AbortPolicy()
线程池大小:
- 类型
- CPU密集型:计算比较多,读取本地文件或者读取数据库比较少
- I/O密集型:读取本地文件或读取数据库比较多
- 计算公式(使用ThreadDump测试耗时)
* 例子:4核8线程,最大并行数为8
* Java代码获取可用最大并行数:Runtime.getRuntime().availableProcessors()
## 扩展(暂未展开学习)
- 类型
线程的状态
线程池
- 设计思路
- 代码实现
volatile
- JMM
原子性
- volatile
- AtomicInteger
- CAS、Synchronized、悲观锁、乐观锁
并发工具类
- ConcurrentHashMap:
- CountDownLatch
- CyclicBarrier
- Semaphore
- Exchanger:线程间交换数据
网络编程
网络编程三要素:IP、端口、协议
IPV4:2^32、IPV6:2^128
InetAddress的使用
网络模型
使用DataGramSocket和DataGramPacket发送和接受收UDP数据
UDP通信的三种方式
- 单播:一对一
- 组播:一对多(发到224.0.0.0~224.0.0.255),使用MulticastSocket
- 广播:一对所有(255.255.255.255)
使用ServerSocket发送和接收TCP数据
TCP三次握手
- A->B:客户端A向服务端B发出连接请求,等待服务器确认
- B->A:服务器B向客户端A返回响应,告诉客户端已收到请求
- A->B:客户端A向服务端B再次发出确认信息,连接建立
TCP四次挥手
- A->B:客户端A向服务端B发出取消连接请求,等待服务器确认
- B->A:服务器B向客户端A返回响应,告诉客户端服务端已收到取消请求
- B->A:服务器B向客户端A发出确认取消信息(数据传输完毕后发送此条)
- A->B:客户端A向服务端B再次发出确认信息,连接取消
HTTP基于TCP协议实现
反射
定义:反射允许对成员变量,成员方法和构造方法的信息进行编程访问。
获取初步信息->可进一步解刨获取的信息:
- 字段(成员变量):修饰符、名字、类型、赋值/获取值
- 构造方法:修饰符、名字、获取形参、创建对象
- 成员方法:修饰符、名字、获取形参、获取返回值、异常、注解、运行方法
获取Class对象
- Class.forName(“全类名”):源代码阶段
- 类名.class:加载阶段
- 对象.getClass():运行阶段
Class对象成员方法-获取构造方法Constructor
- Class.getConstructor(ParameterType.class):获取某个public类型构造方法
- Class.getDeclaredConstructor(ParameterType.class):获取某个类型构造方法
- Class.getConstructors():获取全部public类型构造方法
- Class.getDeclaredConstructors():获取全部类型构造方法
Constructor对象成员方法
- Constructor.getModifiers():获取构造方法的修饰符
- Constructor.getParameters():获取构造方法的形参
- Constructor.getName():获取构造方法名字
- Constructor.setAccessible():临时修改构造方法的权限修饰符
- Constructor.newInstance():创建对象
Field对象成员方法
- Field.getFields():获取所有public成员变量
- Field.getDeclaredFields():获取所有成员变量
- Field.getField(ParamName):获取某个成员变量
- Field.getDeclaredFields(ParamName):获取所有public成员变量
- Field.getModifiers():获取某个成员变量的修饰符
- Field.getType():获取某个成员变量的数据类型
- Field.getName():获取某个成员变量的名字
- Field.setAccessible():临时修改成员变量的权限修饰符
- Field.set( object, ParamValue):给某个变量的某个成员变量赋值
- Field.get(Object):获取某个对象的某个成员变量
Class对象成员方法-获取成员方法Method
- Class.getMethods():获取所有public成员方法(包括父类)
- Class.getDeclaredMethods():获取所有成员方法(不包括父类)
- Class.getMethod(MethodName,ParamType.class):获取某个public成员方法
- Class.getDeclaredMethod(MethodName,ParamType.class):获取某个成员变量
Method对象成员方法
- Method.getModifiers():获取成员方法的修饰符
- Method.getName():获取成员方法名字
- Method.getParameters():获取成员方法的形参
- Method.getExceptions():获取成员方法的异常
- Method.setAccessible():临时修改成员方法的权限修饰符
- MethodObject.invoke(object, Object …params)
动态代理
- 特点:
- 无侵入式的给代码增加额外的功能
- 对象如果嫌身上干的事情太多的话,可以通过代理来转移部分职责
- 对象有什么方法想被代理,代理就一定要有对应的方法
- 动态代理创建过程
- 动态代理类创建
- 创建Interface,定义所有需要动态代理的抽象接口方法
- 创建实现类,重写Interface中的全部抽象方法
- 创建动态代理类ProxyUntil,定义静态方法createProxy,返回值类型为抽象接口,传参为实现类对象
- 创建接口类,初始化为Proxy.newProxyInstance(param1, param2, param3)
- param1:当前类的加载器ProxyUntil.class.getClassLoader()
- param2:new Class[]{接口类.class}
- param3: new InvocationHandler(){invoke(Object proxy, Method method, Object[] args){xxx}}
- Object proxy:当前动态代理类,不用管
- Method method:当前正在调用的Method
- args:调用Method需要传递的参数
- xxx:实际需要MethodName等特征动态执行的内容
- 最后return Method.invoke(实现类对象,args);
- 创建接口类,初始化为Proxy.newProxyInstance(param1, param2, param3)
- 测试类创建
- Class instanceName = new Class();
- InterfaceName proxy = ProxyUntil.createProxy(instanceName);
- proxy.methodName(args);
- proxy.methodName(args);
- 动态代理类创建
本文链接: https://blog.yd0ng.top/2025/04/08/JAVA%E5%9F%BA%E7%A1%80%E7%AC%94%E8%AE%B0-%E4%B8%8B/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!