Java进阶(I/O流、多线程、函数式编程、网络编程、类加载、反射、注解)

I/O流

按照数据流向分类: 输入流:读数据 输出流:写数据 按照数据类型分类: 字节流:字节输入流;字节输出流 字符流:字符输入流;字符输出流 IO流的使用场景: 1、如果操作的是纯文本文件,优先使用字符流 2、如果操作的是图片、视频、音频等二进制文件。优先使用字节流 3、如果不确定文件类型,优先使用字节流。字节流是万能的流

  • File类

    • 概述

      它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对象的 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以 是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的

    • 常用方法

      构造方法: File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例 File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例

      File类创建功能: public boolean createNewFile() 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空 文件 public boolean mkdir() 创建由此抽象路径名命名的目录 public boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 File类判断功能: public boolean isDirectory() 测试此抽象路径名表示的File是否为目录 public boolean isFile() 测试此抽象路径名表示的File是否为文件 public boolean exists() 测试此抽象路径名表示的File是否存在

      File类获取功能: public String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串 public String getPath() 将此抽象路径名转换为路径名字符串 public String getName() 返回由此抽象路径名表示的文件或目录的名称 public String[] list() 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 public File[] listFiles() 返回此抽象路径名表示的目录中的文件和目录的File对象数组

      File类删除功能: public boolean delete() 删除由此抽象路径名表示的文件或目录

  • 递归应用,遍历目录/文件

    以编程的角度来看,递归指的是方法定义中调用方法本身的现象 把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解 递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算 注意事项: 1、递归一定要有出口。否则内存溢出。 2、递归虽然有出口,但递归的次数也不宜过多。否则内存溢出。

  • 字节流

    • 输入(读取)InputStream

      • FileInputStream

        FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream ,该文件由文 件系统中的路径名name命名

        字节输入流读取数据的步骤 创建字节输入流对象 调用字节输入流对象的读数据方法 释放资源

        • void reader(int b)

        • void reader(byte[] b)

        • void reader​(byte[] b, int off, int len)

      • 字节缓冲输入流BufferedInOutputStream

        创建字节缓冲输入流对象格式: BufferedInputStream bos = new BufferedInputStream(new FileInputStream("路径")); lBufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过 字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节。

    • 输出(写入)OutputStream

      • FileOutputStream

        FileOutputStream(String name):创建文件输出流以指定的名称写入文件

        使用字节输出流写数据的步骤 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件) 调用字节输出流对象的写数据方法 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)

        • 字节流写数据的三种方法

          void write(int b) 将指定的字节写入此文件输出流 一次写一个字节数据 void write(byte[] b) 将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组 数据 void write(byte[] b, int off, int len) 将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一 次写一个字节数组的部分数据

          • 如何换行

            windows:\r\n linux:\n mac:\r

          • 如何实现追加写入

            public FileOutputStream(String name,boolean append) 创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头

      • 字节缓冲输出流BufferedOutputStream

        创建字节缓冲输出流对象格式: BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("路径")); lBufferOutputStream:该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写 入字节,而不必为写入的每个字节导致底层系统的调用。

        • 方法:

          void flush() 刷新缓冲输出流 void writer(byte[] b,int off,int len) 从偏移量off开始的指定字节数组写入len字节到缓冲输出流 void writer(int b) 将指定的字节写入缓冲输出流

  • 字符流

    • Reader

      • 输入(读取)InputStreamReader(转换流)

        • 创建字符输入流对象FileReader

          格式: FileReader fr = new FileReader("路径") ;

        • 字符流读数据的两种方法

          int read() 一次读一个字符数据 int read(char[] cbuf) 一次读一个字符数组数据

      • 字符缓冲输入流BufferedReader

        将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可 以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途。 格式: BufferedReader br = new BufferedReader(newe FileReader("路径"));

        • 构造方法

          BufferedReader(Reader in) 创建字符缓冲输入流对象

        • 特有方法

          String readLine() 读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经 到达,则为null

    • Writer

      • 输出(写入)OutputStreamWriter(转换流)

        • 创建字符输出流对象FileWriter

          格式: FileWriter fw = new FileWriter("路径");

        • 字符流写数据的五种方式

          void write(int c) 写一个字符 void write(char[] cbuf) 写入一个字符数组 void write(char[] cbuf, int off, int len) 写入字符数组的一部分 void write(String str) 写一个字符串 void write(String str, int off, int len) 写一个字符串的一部分

        • 刷新和关闭方法

          flush() 刷新流,之后还可以继续写数据 close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

      • 字符缓冲输出流BufferedWriter

        从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓 冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途 格式: BufferedWriter br = new BufferedWriter(newe FileWriter("路径"))

        • 构造方法

          BufferedWriter(Writer out) 创建字符缓冲输出流对象

        • 特有方法

          void newLine() 写一行行分隔符,行分隔符字符串由系统属性定义

  • 编码表

    什么是字符集 是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等 计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。 常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等 ASCII字符集: lASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、 换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号) 基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共 256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、 图形符号、数字等 GBXXX字符集: GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了 21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等 Unicode字符集: UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用 一至四个字节为每个字符编码 编码规则: 128个US-ASCII字符,只需一个字节编码 拉丁文等字符,需要二个字节编码 大部分常用字(含中文),使用三个字节编码 其他极少使用的Unicode辅助字符,使用四字节编码

    • 字符串中的编码解码问题

      byte[] getBytes() 使用平台的默认字符集将该 String编码为一系列字节 byte[] getBytes(String charsetName) 使用指定的字符集将该 String编码为一系列字节 String(byte[] bytes) 使用平台的默认字符集解码指定的字节数组来创建字符串 String(byte[] bytes, String charsetName) 通过指定的字符集解码指定的字节数组来创建字符串

    • 字符流中的编码解码问题

      • 转换流

        InputStreamReader:是从字节流到字符流的桥梁 它读取字节,并使用指定的编码将其解码为字符 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集 OutputStreamWriter:是从字符流到字节流的桥梁 是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

      • 构造方法

        InputStreamReader(InputStream in) 使用默认字符编码创建InputStreamReader对 象 InputStreamReader(InputStream in,String chatset) 使用指定的字符编码创建InputStreamReader 对象 OutputStreamWriter(OutputStream out) 使用默认字符编码创建OutputStreamWriter对 象 OutputStreamWriter(OutputStream out,String charset) 使用指定的字符编码创建OutputStreamWriter 对象

  • IO特殊操作流

    • 标准输入流

      public static final InputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的 另一个输入源

    • 标准输出流

      public static final PrintStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的 另一个输出目标 PrintStream ps = System.out; PrintStream类有的方法,System.out都可以使用

      • 打印流

        只负责输出数据,不负责读取数据 永远不会抛出IOException 有自己的特有方法

        • PrintStream字节打印流

          PrintStream(String fileName):使用指定的文件名创建新的打印流 使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出 可以改变输出语句的目的地 public static void setOut(PrintStream out):重新分配“标准”输出流

        • PrintWriter字符打印流

          构造方法: PrintWriter(String fileName) 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新 PrintWriter(Writer out, boolean autoFlush) 创建一个新的PrintWriter out:字符输出流 autoFlush: 一个布尔值,如果为真,则println , printf ,或format方法将刷新输出缓冲区

    • 对象序列化流ObjectOutputStream

      对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存 储的属性等信息 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对 象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或 另一个进程中重构对象 格式: ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("路径"));

      注意: 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口 Serializable是一个标记接口,实现该接口,不需要重写任何方法

      • 方法

        构造方法: ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的 ObjectOutputStream 对象方法: void writeObject(Object obj) 将指定的对象写入ObjectOutputStream

    • 对象反序列化流ObjectInputStream

      ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象 格式: ObjectInputStream ois = new ObjectInputStream(new FileInputStream("路径"));

      • 方法

        构造方法: ObjectInputStream(InputStream in) 创建从指定的InputStream读取ObjectInputStream 对象方法: Object readObject() 从ObjectInputStream读取一个对象

    • serialVersionUID&transient

      用对象序列化流序列化一个对象后,不得对该对象进行任何改动,否则会报错。抛出InvalidClassExcption异常 解决方法: 1、重新序列化 2、给对象所属的类加一个seriaVersionUID 如果一个对象中某个成员变量的值不想被序列化,给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

  • Properties集合

    是一个Map体系的集合类 Properties可以保存到流中或从流中加载 属性列表中的每个键及其对应的值都是一个字符串

    • 特有方法

      Object setProperty(String key, String value) 设置集合的键和值,都是String类型,底层调用 Hashtable方 法 put String getProperty(String key) 使用此属性列表中指定的键搜索属性 Set stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的 值是字符串

    • Properties和IO流结合的方法

      void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对) void load(Reader reader) 从输入字符流读取属性列表(键和元素对) void store(OutputStream out, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流 void store(Writer writer, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流

多线程

  • 进程和线程

    进程:是正在运行的程序 是系统进行资源分配和调用的独立单位 每一个进程都有它自己的内存空间和系统资源 线程:是进程中的单个顺序控制流,是一条执行路径 单线程:一个进程如果只有一条执行路径,则称为单线程程序 多线程:一个进程如果有多条执行路径,则称为多线程程序

  • 多线程实现方式

    • 继承Thread类

      • 常用方法

        运行多线程: void run() 在线程开启后,此方法将被调用执行 void start() 使此线程开始执行,Java虚拟机会调用run方法

        设置和获取线程名称: void setName(String name) 将此线程的名称更改为等于参数name String getName() 返回此线程的名称 Thread currentThread() 返回对当前正在执行的线程对象的引用

        线程控制: static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数 void join() 等待这个线程死亡 void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机 将退出

      • 实现步骤

        1、定义一个MyThread继承Thread类 2、在MyThread类中重写run()方法 3、创建MyThread类的对象 4、启动线程

    • 实现Runnable接口

      • 常用方法

        构造方法: Thread(Runnable target) 分配一个新的Thread对象 Thread(runnable target,String 那么) 分配一个新的Thread对象

      • 实现步骤

        1、定义一个类MyRunnable实现Runnable接口 2、在MyRunnable类中重新run()方法 3、创建MyRunnable类的对象 4、创建Thread类的对象,把MyRunnable对象作为构造方法的参数 5、启动线程 对象名.start()

    • 注意:

      为什么要重写run()方法? 因为run()是用来封装被线程执行的代码 run()方法start()方法的区别? run():封装线程执行的代码,直接调用,相当于普通方法的调用 start():启动线程;然后有jvm调用此线程的run()方法

  • 线程的生命周期

  • 线程优先级

    线程调度,分为两种方式: 1、分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片。 2、抢占调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些。 Java使用的抢占式调度模型 随机性: 假如计算机只有一个CPU,那么CPU在某个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所有说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的。

    • 常用方法

      final int getPriorly() 返回此线程的优先级 final void setPriority(int newPriority) 更改此线程的优先级 线程默认的优先级是5;线程的优先级的范围是:1-10

  • 线程控制

    • 常用方法

      static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数 void join() 等待这个线程死亡 void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出

  • 线程同步解决线程安全

    • 安全问题

      安全问题出现的条件: 1、是对线程环境 2、是共享数据 3、是多条语句操作共享数据 然后解决多线程的安全问题? 让程序没有安全问题的环境 怎么实现? 1、把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可 2、Java提供了同步代码块的方式来解决

    • 解决方法

      • 同步代码块

        相当于给代码加锁,任意对象就可以看成是一把锁

        • 格式

          synchronized(任意对象){ 多条语句操作共享数据的代码 }

      • synchronized同步方法

        就是把synchronized关键字加到方法上

        • 格式

          同步方法: 修饰符 synchronized 返回值类型 方法名(方法参数){ 方法体; } 锁对象: this 静态同步方法: 修饰符 static synchronized 返回值类型 方法名(方法参数){ 方法体; } 锁对象: 类名.class

      • Lock锁

        虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了 锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

        • 构造方法

          ReenttantLock() 创建一个ReenttantLock的实例

        • 加锁解锁方法

          void lock() 获得锁 void lock() 释放锁

    • 线程安全的类

      • StringBuffer

        线程安全,可变的字符序列 从版本JDK 5开始,被StringBuilder 替代。 通常应该使用StringBuilder类,因为它支持所有相同的操 作,但它更快,因为它不执行同步

      • Vector

        从Java 2平台v1.2开始,该类改进了List接口,使其成为Java Collections Framework的成员。 与新的集 合实现不同, Vector被同步。 如果不需要线程安全的实现,建议使用ArrayList代替Vector

      • Hashtable

        该类实现了一个哈希表,它将键映射到值。 任何非null对象都可以用作键或者值 从Java 2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为Java Collections Framework的成 员。 与新的集合实现不同, Hashtable被同步。 如果不需要线程安全的实现,建议使用HashMap代替 Hashtable (需要线程安全建议使用ConcurrentHashMap)

    • 同步的好处和弊端

      好处:解决了多线程的数据安全问题 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

  • 生产者、消费者模式

    生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的 理解更加深刻。 所谓生产者消费者问题,实际上主要是包含了两类线程: 一类是生产者线程用于生产数据; 一类是消费者线程用于消费数据 为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库 生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为 消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为

    • Object类的等待和唤醒方法

      void wait() 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll() 方法 void notify() 唤醒正在等待对象监视器的单个线程 void notifyAll() 唤醒正在等待对象监视器的所有线程

网络编程

计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系 统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统

网络编程 在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换

  • 网络编程三要素

    • IP地址

      要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接收数 据的计算机和识别发送的计算机,而IP地址就是这个标识号。也就是设备的标识 。

      IP地址分为两大类 IPv4:是给每个连接在网络上的主机分配一个32bit地址。按照TCP/IP规定,IP地址用二进制来表示,每 个IP地址长32bit,也就是4个字节。例如一个采用二进制形式的IP地址是“11000000 10101000 00000001 01000010”,这么长的地址,处理起来也太费劲了。为了方便使用,IP地址经常被写成十进制 的形式,中间使用符号“.”分隔不同的字节。于是,上面的IP地址可以表示为“192.168.1.66”。IP地址的这 种表示法叫做“点分十进制表示法”,这显然比1和0容易记忆得多 。 IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发 紧张。为了扩大地址空间,通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8 组十六进制数,这样就解决了网络地址资源数量不够的问题 。 DOS常用命令: ipconfig:查看本机IP地址 ping IP地址:检查网络是否连通 特殊IP地址: 127.0.0.1:是回送地址,可以代表本机地址,一般用来测试使用

      • InetAddress常用方法

        static InetAddress getByName(String host) 确定主机名称的IP地址。主机名称可以是机器名称,也可以 是IP地址 String getHostName() 获取此IP地址的主机名 String getHostAddress() 返回文本显示中的IP地址字符串

    • 端口

      网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区 分这些应用程序呢?如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序 了。也就是应用程序的标识。

      端口: 设备上应用程序的唯一标识 端口号 用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服 务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会 导致当前程序启动失败

    • 通讯协议

      通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定 的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则 被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守 才能完成数据交换。常见的协议有UDP协议和TCP协议 。

      • UDP

        1、用户数据报协议(User Datagram Protocol) 2、UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台 计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在 收到数据时,也不会向发送端反馈是否收到数据。 3、由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输 4、例如视频会议通常采用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太 大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在 传输重要数据时不建议使用UDP协议

        • 常用方法

          UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象,因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念 Java提供了DatagramSocket类作为基于UDP协议的Socket

          构造方法: DatagramSocket() 创建数据报套接字并将其绑定到本机地址上的任 何可用端口 DatagramPacket(byte[] buf,int len,InetAddress add,int port) 创建数据包,发送长度为len的数据包到指定主机 的指定端口

          方法: void send(DatagramPacket p) 发送数据报包 void close() 关闭数据报套接字 void receive(DatagramPacket p) 从此套接字接受数据报包

      • TCP

        1、传输控制协议 (Transmission Control Protocol) 2、TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数 据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由 客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手” 3、三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠 第一次握手,客户端向服务器端发出连接请求,等待服务器确认 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求 第三次握手,客户端再次向服务器端发送确认信息,确认连接 4、完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性, TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等

        • 常用方法

          Java中的TCP通信 Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过 Socket产生IO流来进行网络通信。 Java为客户端提供了Socket类,为服务器端提供了ServerSocket类

          构造方法: Socket(InetAddress address,int port) 创建流套接字并将其连接到指定IP指定端口号 Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号 InputStream getInputStream() 返回此套接字的输入流 OutputStream getOutputStream() 返回此套接字的输出流

          接收数据: ServletSocket(int port) 创建绑定到指定端口的服务器套接字 Socket accept() 监听要连接到此的套接字并接收它 void shutdownInput() 将此套接字的输入流放置在“流的末尾” (告知服务端传输结束) void shutdownOutput() 禁止用此套接字的输出流

函数式编程

函数式编程思想概述 函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做” 而我们要学习的Lambda表达式就是函数式思想的体现

  • Lambda表达式

    • Lambda表达式格式

      格式: (形式参数) -> {代码块} 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可 ->:由英文中画线和大于符号组成,固定写法。代表指向动作 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容 组成Lambda表达式的三要素: 形式参数,箭头,代码块

    • 注意事项

      1、使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法 2、必须有上下文环境,才能推导出Lambda对应的接口 根据局部变量的赋值得知Lambda对应的接口 Runnable r = () -> System.out.println("Lambda表达式"); 根据调用方法的参数得知Lambda对应的接口 new Thread(()->System.out.println("Lambda表达式")).start();

    • Lambda表达式和匿名内部类的区别

      1、所需类型不同 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类 Lambda表达式:只能是接口 2、使用限制不同 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式 3、实现原理不同 匿名内部类:编译之后,产生一个单独的.class字节码文件 Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成

    • 方法引用

      在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作 那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再 写重复逻辑呢?答案肯定是没有必要 那我们又是如何使用已经存在的方案的呢? 这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案

      • 方法引用符

        方法引用符 :: 该符号为引用运算符,而它所在的表达式被称为方法引用 推导与省略 1、如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导 2、如果使用方法引用,也是同样可以根据上下文进行推导 3、方法引用是Lambda的孪生兄弟

      • 引用类方法

        引用类方法,其实就是引用类的静态方法 格式 类名::静态方法 范例 Integer::parseInt Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据 使用说明 Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数

      • 引用对象的实例方法

        引用对象的实例方法,其实就引用类中的成员方法 格式 对象::成员方法 范例 "HelloWorld"::toUpperCase String类中的方法:public String toUpperCase() 将此String所有字符转换为大写 使用说明 Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数

      • 引用类的实例方法

        引用类的实例方法,其实就是引用类中的成员方法 格式 类名::成员方法 范例 String::substring public String substring(int beginIndex,int endIndex) 从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex 使用说明 Lambda表达式被类的实例方法替代的时候 第一个参数作为调用者 后面的参数全部传递给该方法作为参数

      • 引用构造方法

        引用构造器,其实就是引用构造方法 格式 类名::new 范例 Student::new 使用说明 Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数

  • 函数式接口

    • 概述

      有且仅有一个抽象方法的接口 如何检测: @FunctionalInterface 放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败 注意事项: 我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算不写这个注解,只要保证满足函数式接口的定义条件,也照样是函数式接口,建议加上注解

    • 函数式接口作为方法的参数

    • 函数式接口作为方法的返回值

    • Supplier

      Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产 什么类型的数据供我们使用

      • 常用方法

        T get() 按照某种实现逻辑(由Lambda表达式实现)返回一个数据

    • Consunmer

      Consumer接口也被称为消费型接口,它消费的数据的数据类型由泛型指定

      • 常用方法

        void accept(T t) 对给定的参数执行此操作 default Consumer andThen(Consumer after) 返回一个组合的Consumer,依次执行此操作,然后执行after操作

    • Predicate

      Predicate接口通常用于判断参数是否满足指定的条件

      • 常用方法

        boolean test(T t) 对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值 default Predicate negate() 返回一个逻辑的否定,对应逻辑非 default Predicate and(Predicate other) 返回一个组合判断,对应短路与 default Predicate or(Predicate other) 返回一个组合判断,对应短路或

    • Function

      Function<T,R> 接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值

      • 常用方法

        R apply(T t) 将此函数应用于给定的参数 default Function andThen(Function after) 返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果

  • Stream流

    从支持数据处理操作的源生产的元素序列 1、元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元素(如ArrayList 与 LinkedList)。集合讲的是数据,流讲的是计算。 2、源——流会使用一个提供数据的源,如集合、数组或输入/输出资源。 请注意,从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表一致。 3、数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、 map、 reduce、 find、 match、 sort等。流操作可以顺序执行,也可并行执行。 Stream流把真正的函数式编程风格引入到Java中

    • 常见生产方式

      Collection体系集合 使用默认方法stream() 生产流,default Stream stream() Map体系集合 把Map转成Set集合,间接生产流 数组 通过Stream接口的静态方法of(T...values)生产流

    • 常见中间操作方法

      中间操作的意思是,执行完此方法之后,Stream流依然可以执行其他的操作 Stream filter(Predicate predicate) 用于对流中的数据进行过滤 Stream limit(long maxSize) 返回此流中的元素组成的流,截取前指定参数个数的数据 Stream skip(long n) 跳过指定参数个数的数据,返回由该流的剩余元素组成的流 static Stream concat(Stream a, Stream b) 合并a和b两个流为一个流 Stream distinct() 返回由该流的不同元素(根据Object.equals(Object) )组成的流 Stream sorted() 返回由此流的元素组成的流,根据自然顺序排序 Stream sorted(Comparator comparator) 返回由该流的元素组成的流,根据提供的Comparator进行 排序 Stream map(Function mapper) 返回由给定函数应用于此流的元素的结果组成的流 IntStream mapToInt(ToIntFunction mapper) 返回一个IntStream其中包含将给定函数应用于此流的元素的结果

    • 终结操作方法

      终结操作的意思是,执行完成此方法之后,Stream流将不再执行其他操作 void forEach(Consumer action) 对此流的每个元素执行操作 long count() 返回此流中的元素数

    • Stream流的收集操作

      对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中 常用方法: R collect(Collector collector) 把结果收集到集合中 工具类 public static Collector toList() 把元素收集到List集合中 public static Collector toSet() 把元素收集到Set集合中 public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合中

    • Collection API 和 新Stream API 的设计思想

      1、集合与流的差异就在于什么时候进行计算 集合是一个内存中的数据结构,它包含数据结构中目前所有的值——集合中的每个元素都得先算出来才能添加到集合中。(你可以往集合里加东西或者删东西,但是不管什么时候,集合中的每个元素都是放在内存里的,元素都得先算出来才能成为集合的一部分。) 流则是在概念上固定的数据结构(你不能添加或删除元素),其元素则是按需计算的。 这个思想就是用户仅仅从流中提取需要的值,而这些值——在用户看不见的地方——只会按需生成。这是一种生产者-消费者的关系。

      2、打开一个流,只能遍历一次 和迭代器类似,流只能遍历一次。遍历完之后,我们就说这个流已经被消费掉了。你可以从原始数据源那里再获得一个新的流来重新遍历一遍,就像迭代器一样(这里假设它是集合之类的可重复的源,如果是I/O通道就没戏了)。(java.lang.IllegalStateException:流已被操作或关闭)

      3、外部迭代器与内部迭代器 使用Collection接口需要用户去做迭代(比如用for-each),这称为外部迭代。 相反,Streams库使用内部迭代——它帮你把迭代做了,还把得到的流值存在了某个地方,你只要给出一个函数说要干什么就可以了。所以说,stream在内部替我们把迭代给做了,而且在做完的同时,可以根据我们的处理器需要自动的进行并发处理,而我们需要做的就是把lambda表达式(处理这些数据的方法)给出来就可以了。

类加载

类加载的描述: 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始 化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把 这三个步骤统称为类加载或者类初始化 类的加载 就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象 任何类被使用时,系统都会为之建立一个 java.lang.Class 对象 类的连接 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致 准备阶段:负责为类的类变量分配内存,并设置默认初始化值 解析阶段:将类的二进制数据中的符号引用替换为直接引用 类的初始化 在该阶段,主要就是对类变量进行初始化 类的初始化步骤 假如类还未被加载和连接,则程序先加载并连接该类 假如该类的直接父类还未被初始化,则先初始化其直接父类 假如类中有初始化语句,则系统依次执行这些初始化语句 注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3 类的初始化时机 创建类的实例 调用类的类方法 访问类或者接口的类变量,或者为该类变量赋值 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象 初始化某个类的子类 直接使用java.exe命令来运行某个主类

  • 类加载器的作用

    负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象。虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行!

  • JVM的类加载机制

    全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载 器负责载入,除非显示使用另外一个类加载器来载入 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器 无法加载该类时才尝试从自己的类路径中加载该类 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜 索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区

  • Java中的内置类加载器

    Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null ,并且没有父null Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类 System class loader:它也被称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于定义应 用程序类路径,模块路径和JDK特定工具上的类 类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap

  • ClassLoder 常用方法

    static ClassLoader getSystemClassLoader() 返回用于委派的系统类加载器 ClassLoader getParent() 返回父类加载器进行委派

  • 代码示例

    public class ClassLoaderDemo {    public static void main(String[] args) {        //static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器        ClassLoader c = ClassLoader.getSystemClassLoader();        System.out.println(c); //AppClassLoader

           //ClassLoader getParent():返回父类加载器进行委派        ClassLoader c2 = c.getParent();        System.out.println(c2); //PlatformClassLoader

           ClassLoader c3 = c2.getParent();        System.out.println(c3); //null   } }

反射

是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。 由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展

  • 获取Class类对象的三种方式

    同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

    • 类名.class属性: 通过类名的属性class获取

      多用于参数的传递

    • 对象名.getClass():getClass() 方法再Object类中定义着

      多用于对象的获取字节码的发式

    • Class.forName(全类名) 将字节码文件加载进内存,返回Class对象

      多用于配置文件,将类名定义再配置文件中,读取文件,加载类

    • 代码示例

      public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { //使用类的class属性来获取该类对应的Class对象 Class<Student> c1 = Student.class; System.out.println(c1);

          Class<Student> c2 = Student.class;
        System.out.println(c1 == c2);
        System.out.println("--------");

        //调用对象的getClass()方法,返回该对象所属类对应的Class对象        
        Student s = new Student();
        Class<? extends Student> c3 = s.getClass();
        System.out.println(c1 == c3);
        System.out.println("--------");
        //使用Class类中的静态方法forName(String className)
        Class<?> c4 = Class.forName("com.itheima_02.Student");
        System.out.println(c1 == c4);
      }

      }

  • 反射获取构造方法并使用

    • Class类获取构造方法对象的方法

      Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数 组 Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组 Constructor getConstructor(Class<?>... parameterTypes) 返回单个公共构造方法对象 Constructor getDeclaredConstructor(Class<?>... parameterTypes) 返回单个构造方法对象

    • Constructor类用于创建对象的方法

      T newInstance(Object...iniargs) 根据指定的构造方法创建对象

    • Class类获取成员变量的方法

      Field[] getFields() 返回所有公共成员变量对象的数组 Field[] getDeclaredFields() 返回所有成员变量对象的数组 ield getField(String name) 返回单个公共成员变量对象 Field getDeclaredField(String name) 返回单个成员变量对象 Filed类用于给成员变量赋值的方法 voidset(Object obj,Object value) 给obj对象的成员变量赋值为value

    • Class类获取成员方法对象的方法

      Method[] getMethods() 返回所有公共成员方法对象的数组,包 括继承的 Method[] getDeclaredMethods() 返回所有成员方法对象的数组,不包括 继承的 Method getMethod(String name, Class<?>... parameterTypes) 返回单个公共成员方法对象 Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回单个成员方法对象 Method类用于执行方法的方法 Objectinvoke(Object obj,Object...args) 调用obj对象的成员方法,参数是args,返回值是Object类型

注解

  • 概念

    说明程序的,给计算机看的 注解(Annotation),也叫元数据,一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

  • 作用分类

    1、编写文档:通过代码里标识的注解生产文档【生成文档doc文档】 2、代码分析:通过代码里标识的注解对代码进行分析【使用反射】 3、编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

  • 常见注解

    @Override:检测被该注解标注的方法是否是继承自父类(接口)的 @Deprecated:该注解标注的内容,标识已过时 @SupperessWarnings:压制警告 一般传递参数all @SupperssWarning("all")

  • 自定义注解

    格式: 元注解 public @interface 注解名称{ 属性列表; }

    本质:注解本质上就是一个接口,该接口默认继承Annotation接口 public interface MyAnno extends java.lang.annotation.Annotation{}

    属性:接口中的抽象方法 1、属性的返回值类型有下列取值:基本数据类型、String、枚举、注解、以上类型的数组 2、定义了属性,在使用时需要给属性赋值:a.如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值;b.如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义即可;c.数组赋值时,值使用{}包裹。如果数组中只有一个值,这{}可以省略。

    元注解:用于描述注解的注解 @Target:描述注解能够作用的位置。ElementType取值:TYPE:可以作用于类上 METHOD:可以作用于方法上 FIELD:可以作用于成员变量上 @Retention:描述注解被保留的阶段 。 @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中被JVM读取到 @Documented:描述注解是否被抽取到api文档中 @Inherited:描述注解是否被子类继承

猜你喜欢

转载自www.cnblogs.com/zyxyz/p/11527907.html