# JavaSE
# Java 概述
# Java 特点
Java 语言是面向对象的 (oop)
Java 语言是健壮的。Java 的强类型机制、异常处理、垃圾的自动收集等是 Java 程序健壮性的重要保证
Java 语言是跨平台性的。[即:一个编译好的.class 文件可以在多个系统下运行,这种特性称为跨平台]
Java 语言是解释型的 [了解]
解释性语言:javascript,PHP
java 编译性语言: c /c++
区别是:解释性语言,编译后的代码,不能直接被机器执行,需要解释器来执行,编译性语言,编译后的代码,可以直接被机器执行,c /c++
# Java 核心机制 - JVM
🎈 基本介绍
JVM 是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器,包含在 JDK 中.
对于不同的平台,有不同的虚拟机。
Java 虚拟机机制屏蔽了底层运行平台的差别,实现了 “一次编译,到处运行”
说明:Test.java 通过编译(javac) Test.class 该.class 文件可以在 Windows,Linux,Mac 系统的 JVM 上运行
# JDK, JRE, JVM 关系
🎈JDK 基本介绍
- JDK 的全称 (Java Development Kit Java 开发工具包)
JDK = JRE + java 的开发工具 [java, javac,javadoc,javap 等]
- JDK 是提供给 Java 开发人员使用的,其中包含了 java 的开发工具,也包括了 JRE。所以安装了 JDK,就不用在单独
安装 JRE 了。
🎈JRE 基本介绍
- JRE (Java Runtime Environment Java 运行环境)
JRE = JVM + Java 的核心类库 [类]
- 包括 Java 虚拟机 (JVM Java Virtual Machine) 和 Java 程序所需的核心类库等,如果想要运行一个开发好的 Java 程序,
计算机中只需要安装 JRE 即可。
🎈JDK、JRE 和 JVM 的包含关系
- JDK = JRE + 开发工具集(例如 Javac,java 编译工具等)
- JRE = JVM + Java SE 标准类库(java 核心类库)
- 如果只想运行开发好的 .class 文件 只需要 JRE
# Java 基础语法
# 数据类型
# 基本数据类型
数值型
- 整数类型,存放整数 (byte [1],short [2],int [4],long [8])
- 浮点 (小数) 类型 (float [4],double [8])
字符型
- 字符型 (char [2]), 存放单个字符 ‘a’
布尔型
- 布尔型 (boolean [1]), 存放 true ,false
# 引用数据类型
类(class)
接口(interface)
数组([] )
# 2 进制
8进制
16进制
# 流程控制
分支控制 if-else
switch 分支结构
for 循环控制
普通 for 循环
for-each
while 循环控制
do..while 循环控制
多重循环控制
跳转控制语句 - break(结束・某个语句块)
跳转控制语句 - continue (结束本次循环)
跳转控制语句 - return (跳出方法)
# 数组
数组使用注意事项和细节
数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用
数组创建后,如果没有赋值,有默认值 int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null
使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值 3 使用数组
数组属引用类型,数组型数据是对象 (object)
二维数组
# 面向对象(OOP)
# 方法
方法注意事项和细节
一个方法最多有一个返回值 [思考,如何返回多个结果 返回数组]
. 返回类型可以为任意类型,包含基本类型或引用类型 (数组,对象)
在实际工作中,我们的方法都是为了完成某个功能,所以方法名要有一定含义,最好是见名知意
方法名: 遵循驼峰命名法
成员方法传参机制
基本数据类型的传参机制: 传递的是值 (值拷贝), 形参的改变不影响实参
引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参!
方法递归调用
# 重载
基本介绍
- java 中允许同一个类中,多个同名方法的存在,但要求 形参列表不一致!
注意事项和使用细节
- 方法名必须相同, 形参列表必须不同, 返回类型无要求
# 封装
- 封装的实现步骤
- 1) 将属性进行私有化 private【不能直接修改属性)
- 2) 提供一个公共的 (public) set 方法,用于对属性判断并赋值 public void setXxx (类型参数名)//Xxx 表示某个属性 / 加入数据验证的业务逻辑 / 属性 = 参数名;}
- 3) 提供一个公共的 (public) get 方法,用于获取属性的值 public 数据类型 getXxx ()
# 继承
基础语法
- class 子类 extends 父类 {} 子类就会自动拥有父类定义的属性和方法 2) 父类又叫超类,基类 3) 子类又叫派生类。
继承的深入讨论 / 细节
- 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父
- 类提供公共的方法去访问、
- 子类必须调用父类的构造器, 完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则
- 必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
- 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super (参数列表)
- super 在使用时,必须放在构造器第一行 (super 只能在构造器中使用)
- super () 和 this () 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java 所有类都是 Object 类的子类,Object 是所有类的基类.
- 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类 (顶级父类)
- 子类最多只能继承一个父类 (指直接继承),即 java 中是单继承机制。
- 思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】
- 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
# 多态
多态具体体现
方法的多态:方法的重写和重载
对象的多态: 一个对象的编译类型和运行类型可以不一致 (2) 编译类型在定义对象时,就确定了,不能改变 (3) 运行类型是可以变
化的.(4 编译类型看定义时 = 号的左边,运行类型看 = 号的右边
多态注意事项和细节
前提
- 两个类之间存在继承关系
多态的向上转型
- 1) 本质:父类的引用指向了子类的对象
- 2) 语法:父类类型 引用名 = new 子类类型 ()
- 3) 特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(需遵守访问权限)
不能调用子类中特有成员,最终运行效果看子类的具体实现!
多态的向下转型
- ) 语法:子类类型 引用名 = (子类类型) 父类引用:
- 2) 只能强转父类的引用,不能强转父类的对象
- 3) 要求父类的引用必须指向的是当前目标类型的对象
- 4) 当向下转型后,可以调用子类类型中所有的成员
# ~ 抽象类
- 介绍
- 1) 用 abstract 关键字来修饰一个类时,这个类就叫抽象类 - 访问修饰符 abstract 类名 {}
- 2) 用 abstract 关键字来修饰一个方法时,这个方法就是抽象方法 访问修饰符 abstract 返回类型方法名(参数列表)/ 没有方法体
3) 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现
- 注意事项和细节
- 1) 抽象类不能被实例化
- 2) 抽象类不一定要包含 abstract. 方法。也就是说,抽象类可以没有 abstract 方法
- 3) 一旦类包含了 abstract 方法,则这个类必须声明为 abstract
- 4) abstract 只能修饰类和方法,不能修饰属性和其它的。
- 5) 抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等 [举例]
- 6) 抽象方法不能有主体,即不能实现如 abstract void aaa(){ }【不能有大括号】
- 7) 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为 abstract 类。
- 8) 抽象方法不能使用 private、final 和 static 来修饰,因为这些关键字都是和重写相违背的。
# ~ 接口
- 介绍
- 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些
方法写出来。
- 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些
- 语法
- interface 接口名
- class 类名 implements 接口
- 总结
- 接口是更加抽象的抽象的类,抽象类里的方法可以有方法体,接口里的所有方法都没有方法体【dk7.0】。
- 接口体现了程序设计的多态和高内聚低偶合的设计思想。
- 特别说明:Jdk8.0 后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
- 注意事项和细节
- 1. 接口不能被实例化
- 2. 接口中所有的方法是 public 方法,接口中抽象方法,可以不用 abstract 修饰
- 3. 一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用 alt+enter 来解决
- 4. 抽象类去实现接口时,可以不实现接口的抽象方法
- 5) 一个类同时可以实现多个接口
- 6) 接口中的属性,只能是 final 的,而且是 public static final 修饰符。比如 int a=1; 实际上是 public static final int a=1;(必须初始化)
- 7) 接口中属性的访问形式:接口名。属性名
- 8) 接口不能继承其它的类,但是可以继承多个别的接口 [举例] interface A extends B,C {}
- 9) 接口的修饰符只能是 public 和默认,这点和类的修饰符是一样的。
# ~ 枚举
enum 关键字实现枚举
当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类,而且是一个 final 类
传统的 public static final Season2 SPRING = new Season2 ("春天", "温暖"); 简化成 SPRING ("春天", "温暖"), 这里必须知道,它调用的是哪个构造器.
如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
当有多个枚举对象时,使用,间隔,最后有一个分号结尾
枚举对象必须放在枚举类的行首
# ~ 常用类
Math 类
Array 类
System 类
BigInteger 和 BigDecimal
- BigIntger 适合保存比较大的整数
- BigDecimal 适合保存精度比较大的小数(浮点数)
包装类
- 八种基本数据类型相应的引用类型
- 装箱:基本类型 -> 包装类型,反之,拆箱
# · String
特性
String 类实现了接口 Serializable【String 可以串行化:可以在网络传输】接口 Comparable [String 对象可以比较大小]
String 是 final 类,不能被其他的类继承,代表不可变的字符序列
String 有属性 private final char value []; 用于存放字符串内容
字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的
方法
equals / 区分大小写,判断内容是否相等
equalslgnoreCase / 惚略大小写的判断内容是否相等
length / 获取字符的个数,字符串的长度indexOf / 获取字符在字符串中第 1 次出现的索引,索引从 0 开始,如果找不到,返回 - 1
lastIndexOf / 获取字符在字符串中最后 1 次出现的索引,索引从 0 开始,如找不到,返回 - 1
substring / 截取指定范围的子串
trim// 去前后空格
charAt: 获取某索引处的字符,注意不能使用 Str [index] 这种方式.
增强
- StringBuffer
- StringBufferf 代表可变的字符序列,可以对字符串内容进行增删很多方法与 String 相同,但 String Buffer 是可变长度的。
- StringBuffer 是一个容器。
- StringBuilder
- 一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API, 但不保证同步 (StringBuilder 不是线程安全)。
- 比较
- Strg: 不可变子付序列,双华低,但是复用率高。
- StringBuffer: 可变字符序列、效率较高(增删)、线程安全
- StringBuilder: 可变字符序列、效率最高、线程不安全
- 如果字符串存在大量的修改操作,一般使用 StringBuffer 或 String Builder
- 如果字符串存在大量的修改操作,并在单线程的情况,使用 StringBuilder
- 如果字符串存在大量的修改操作,并在多线程的情况,使用 StringBuffer
- 如果我们字符串很少修改,被多个对象引用,使用 String, 比如配置信息等
- StringBuffer
# 日期时间
- LocalDate (日期 / 年月日)
- LocalTime (时间 / 时分秒)
- LocalDateTime (日期时间 / 年月日时分秒)
- DateTimeFormatter 格式日期类
- DateTimeFormat dtf = DateTimeFormatter.ofPattern();
- String str=dtf.format (日期对象) i
- Instant 时间戳
- 方法 (查看 API)
# ~ 集合类
- Collection
- 遍历方式
- iterator (迭代器)
- 增强 for
- List
- Vector
- Vector 是线程同步的,线程安全
- ArrayList
- LinkedList
- 比较
- 如果我们改查的操作多,选择 ArrayList
- 如果我们增删的操作多,选择 LinkedList
- Vector
- Set
- TreeSet
- 排序
- HashSet
- 无序
- LinkedHashSet
- 插入输出顺序一致
- TreeSet
- 遍历方式
- Map
- HashMap
- 线程不安全, 效率较高,允许 null 键和 null 值
- Properties
- 读取文件
- TreeMap
- 键排序
- HashTable
- 线程安全,效率低,不允许 null 键和 null 值
- HashMap
# ~ 泛型
语法
interface 接口 <T>{} 和 class 类 < K,V>{}// 比如:List,ArrayList
说明:
1) 其中,T,K,V 不代表值,而是表示类型,必须引用类型
2) 任意字母都可以。常用 T 表示,是 Type 的缩写
要在类名后面指定类型参数的值(类型)。如:
List<String>strList=new ArrayList<String>{};
继承和通配符
- 泛型不具备继承性
- <?>:支持任意泛型类型
- <?extends A>: 支持 A 类以及 A 类的子类,规定了泛型的上限
- <?super A>: 支持 A 类以及 A 类的父类,不限于直接父类,规定了泛型的下限
# ~ 注解
- @Override
- 限定某个方法,是重写父类方法,该注解只能用于方法
- @Deprecated
- 用于表示某个程序元素 (类,方法等) 已过时
- @SuppressWarnings
- 抑制编译器警告
# ~ 异常处理
Error (错误)
- Java 虚拟机无法解决的严重问题。如:JVM 系统内部错误、资源耗尽等严重情况。比如:StackOverflowError [栈溢出] 和
- OOM (out ofmemory),Error 是严重错误,程序会崩溃。
Exception
- 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文
- 件,网络连接中断等等,
- 分类・
- 运行时异常程序运行时,发生的异常]
- NullPointerException 空指针异常
- ArithmeticException 数学运算异常
- ArrayIndexOutOfBoundsException 数组下标越界异常
- ClassCastException 类型转换异常
- NumberFormatException 数字格式不正确异常
- 编译时异常 [编程时,编译器检查出的异常],必须处理
- 运行时异常程序运行时,发生的异常]
处理
try-catch-finally
- 程序员在代码中捕获发生的异常,自行处理
throws
- 将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是 JVM
注意事项和细节
- 对于编译异常,程序中必须处理,比如 try-catch 或者 throws
- 对于运行时异常,程序中如果没有处理,默认就是 throws 的方式处理
- 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出
- 的异常的类型的子类型
- 在 throws 过程中,如果有方法 try-catch, 就相当于处理异常,就可以不必 throws
# ~ 多线程
- 线程创建方式
- 继承 Thread 类
- 实现 Runnable 接口
- 多线程执行
- 线程终止
- 线程方法
- 1.setName/ 设置线程名称,使之与参数 name 相同
- 2.getName/ 返回该线程的名称
- 3.start/ 使该线程开始执行;Java 虚拟机底层调用该线程的 start0 方法
- 4.run/ 调用线程对象 run 方法;
- 5.setPriority/ 更改线程的优先级
- 6.getPriority/ 获取线程的优先级
- 7.sleep/ 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
- 8.interrupt/ 中断线程
- 🎈1.yield: 线程的礼让。让出 cpu, 让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
- 🎈2.join: 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
- 案例:main 线程创建一个子线程,每隔 1s 输出 hello, 输出 20 次,主线程每隔 1 秒,输出 i, 输出 20 次要求:
- 两个线程同时执行,当主线程输出 5 次后,就让子线程运行完毕,主线程再继续,
- 注意事项和细节
- 1.start 底层会创建新的线程,调用 run,run 就是一个简单的方法调用,不会启动新线程
- 2. 线程优先级的范围
- 3.interrupt, 中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程
- 4.sleep: 线程的静态方法,使当前线程休眠
- 线程的生命周期
- 线程的同步
- 线程同步机制:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,
- 直到该线程完成操作,其他线程才能对该内存地址进行操作。
- 同步具体方法 - Synchronized
- 互斥锁
- 注意事项和细节
- 同步方法如果没有使用 static 修饰:默认锁对象为 this;如果方法使用 static 修饰,默认锁对象:当前类.class
- 线程的死锁
- 多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生.
- 释放锁
- 当前线程的同步方法、同步代码块执行结束
- 当前线程在同步代码块、同步方法中遇到 break、return。
- 当前线程在同步代码块、同步方法中出现了未处理的 Error 或 Exception, 导致异常结束
- 当前线程在同步代码块、同步方法中执行了线程对象的 wait () 方法,当前线程暂停,并释放锁。
- 不会释放锁的情况
- 线程执行同步代码块或同步方法时,程序调用 Thread.sleep ()、Thread.yieldO 方法暂停当前线程的执行,不会释放锁
- 线程执行同步代码块时,其他线程调用了该线程的 suspend () 方法将该线程挂起该线程不会释放锁。
- 提示:应尽量避免使用 suspend () 和 resume () 来控制线程,方法不再推荐使用
# ~ IO 流
字节流
- InputStream
- FileInputStream
- OutputStream
- FileOutputStream
- InputStream
字符流
- Reader
- FileReader
- Writer
- FileWriter
- 注意:FileWriter 使用后,必须要关闭 (close) 或刷新 (flush), 否则写入不到指定的文件!
- Reader
注意
- 当处理纯文本数据时,如果使用字符流效率更高问题,所以建议将字节流转换成字符流
处理流
- BufferedReader 和 BufferedWriter 属于字符流,是按照字符来读取数据关闭时处理流,只需要关闭外层流即可
- BufferedReader
- BufferedWriter
- BufferedInputStream
- BufferedOutputStream
对象流
- ObjectInputStream (序列化功能)
- **ObjectOutputStream ** (反序列化功能)
转换流
- 解决中文乱码问题
- InputStreamReader
- OutputStreamWriter
打印流
- PrintStream
- PrintWriter
Properties
- 专门用于读写配置文件的集合类;配置文件的格式:键 = 值 / 键 = 值
- 注意:键值对不需要有空格,值不需要用引号一起来。默认类型是 String
# ~ 反射
- Reflection 相关的类
- 1.java.lang.Class: 代表一个类,Class 对象表示某个类加载后在堆中的对象
- 2.java.lang.reflect.Method: 代表类的方法,Method 对象表示某个类的方法
- 3.java.lang.reflect.Field: 代表类的成员变量,Field 对象表示某个类的成员变量
- 4.java.lang.reflect.Constructor: 代表类的构造方法,Constructor 对象表示构造器
- 反射调用优化–关闭访问检查
- Method 和 Field、Constructor > 对象都有 setAccessible () 方法
- setAccessible 作用是启动和禁用访问安全检查的开关
- 参数值为 true 表示反射的对象在使用时取消访问检查,提高反射的效率。
- 参数值为 false! 则表示反射的对象执行访问检查
- Class 类
- Class 也是类,因此也继承 Object 类
- Class 类对象不是 new 出来的,而是系统创建的
- 对于某个类的 Class 类对象,在内存中只有一份,因为类只加载一次
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过 Class 对象可以完整地得到一个类的完整结构,通过一系列 API
- Class 对象是存放在堆的
- 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码变量名,方法名,访问权限等等)
- Class 类的常用方法
- 获取 Class 类对象
- 哪些类型有 Class 对象
- 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
- interface: 接口
- 数组
- enum: 枚举
- annotation: 注解
- 基本数据类型
- void
- 类加载
- 反射机制是 java 实现动态语言的关键,也就是通过反射实现类动态加载。
1. 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
2. 动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性 - 类加载时机
- 1. 当创建对象时 (new)/ 静态加载
- 2. 当子类被加载时,父类也加载 / 静态加载
- 3. 调用类中的静态成员时 // 静态加载
- 4. 通过反射 // 动态加载
- 反射机制是 java 实现动态语言的关键,也就是通过反射实现类动态加载。
- 通过反射获取类的结构信息
- java.lang.Class 类
- getName 获取全类名
- getSimpleName: 获取简单类名
- getFields: 获取所有 oublic 修饰的属性,包含本类以及父类的
- getDeclaredFields: 获取本类中所有属性
- getMethods: 获取所有 oublic 修饰的方法,包含本类以及父类的
- getDeclaredMethods: 获取本类中所有方法
- getConstructors: 获取本类所有 oublicf 修饰的构造器
- getDeclaredConstructors:: 获取本类中所有构造器
- getPackage:: 以 Package 形式返回包信息
- getSuperClass: 以 Classj 形式返回父类信息
- ,getInterfaces: 以 Class] 形式返回接口信息
- .getAnnotations:: 以 Annotation [] 形式返回注解信息
- java.lang.reflect.Field 类
- 1.getModifiers: 以 int 形式返回修饰符
- [说明:默认修饰符是 0,public 是 1,private 是 2,protected 是 4,static 是 8,final 是 16],public (1)+static (8)=9
- 2.getType: 以 Classi 形式返回类型
- 3.getName: 返回属性名
- java.lang.reflect.Method 类
- 1.getModifiers: 以 int 形式返回修饰符
- [说明:默认修饰符是 0,public 是 1,private 是 2,protected 是 4,static 是 8,final 是 16]
- 2.getReturnType: 以 Classj 形式获取返回类型
- 3.getName: 返回方法名
- 4.getParameterTypes: 以 Class [] 返回参数类型数组
- java.lang.reflect.Constructor 类
- 1.getModifiers: 以 int 形式返回修饰符
- getName 返回构造器名(全类名)
- getParameterTypes: 以 Class [返回参数类型数组
- java.lang.Class 类
- 通过反射创建对象
- 1. 方式一:调用类中的 oublic 修饰的无参构造器
- 2. 方式二:调用类中的指定构造器
- 3.Class 类相关方法
- newlnstance: 调用类中的无参构造器,获取对应类的对象
- getConstructor (Class..clazz): 根据参数列表,获取对应的 publict 构造器对象
- getDecalaredConstructor (Class..clazz): 根据参数列表,获取对应的所有构造器对象
- 4.Constructor: 类相关方法
- ・setAccessible: 暴破
- newlnstance (Object..obj): 调用构造器
- 通过反射访问类中的成员
- 访问属性
- 1. 根据属性名获取 Field 对象
- Field f=clazz 对象.getDeclaredField (属性名);
- 2. 暴破:f.setAccessible (true); //f 是 Field
- 3. 访问
- f.set (o, 值)://o 表示对象
- syso(f.get(o)😕/o 表示对象
- 4. 注意:如果是静态属性,则 set 和 get 中的参数 o, 可以写成 nul
- 访问方法
- 1. 根据方法名和参数列表获取 Method 方法对象:Method m=
- clazz.getDeclaredMethod (方法名,XX.class); // 得到本类的所有方法
- 2. 获取对象:Object o=clazz.newlnstance0
- 3. 暴破:m.setAccessible (true) i
- 4. 访问:Object returnValue=m.invoke (o, 实参列表);/o 就是对象
- 5. 注意:如果是静态方法,则 invoke 的参数 o, 可以写成 null !
- 访问属性
# Java8
# 知识
Stream API
Stream 实例化
三种方式
// 创建 Stream 方式一:通过集合
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
// default Stream<E> stream () : 返回一个顺序流
Stream<Employee> stream = employees.stream();
// default Stream<E> parallelStream () : 返回一个并行流
Stream<Employee> parallelStream = employees.parallelStream();
}
// 创建 Stream 方式二:通过数组
@Test
public void test2(){
int[] arr = new int[]{1,2,3,4,5,6};
// 调用 Arrays 类的 static <T> Stream<T> stream (T [] array): 返回一个流
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1001,"Tom");
Employee e2 = new Employee(1002,"Jerry");
Employee[] arr1 = new Employee[]{e1,e2};
Stream<Employee> stream1 = Arrays.stream(arr1);
}
// 创建 Stream 方式三:通过 Stream 的 of ()
@Test
public void test3(){
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
}
中间操作
筛选与切片
@Test
public void test1(){
List<Employee> list = EmployeeData.getEmployees();
// filter (Predicate p)—— 接收 Lambda , 从流中排除某些元素。
Stream<Employee> stream = list.stream();
// 练习:查询员工表中薪资大于 7000 的员工信息
stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
System.out.println();
// limit (n)—— 截断流,使其元素不超过给定数量。
list.stream().limit(3).forEach(System.out::println);
System.out.println();
// skip (n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit (n) 互补
list.stream().skip(3).forEach(System.out::println);
System.out.println();
// distinct ()—— 筛选,通过流所生成元素的 hashCode () 和 equals () 去除重复元素
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",41,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
// System.out.println(list);
list.stream().distinct().forEach(System.out::println);
}
映射
@Test
public void test2(){
// map (Function f)—— 接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
// 练习 1:获取员工姓名长度大于 3 的员工的姓名。
List<Employee> employees = EmployeeData.getEmployees();
Stream<String> namesStream = employees.stream().map(Employee::getName);
namesStream.filter(name -> name.length() > 3).forEach(System.out::println);
System.out.println();
// 练习 2:
Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream);
streamStream.forEach(s ->{
s.forEach(System.out::println);
});
System.out.println();
// flatMap (Function f)—— 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream);
characterStream.forEach(System.out::println);
}
// 将字符串中的多个字符构成的集合转换为对应的 Stream 的实例
public static Stream<Character> fromStringToStream(String str){//aa
ArrayList<Character> list = new ArrayList<>();
for(Character c : str.toCharArray()){
list.add(c);
}
return list.stream();
}
排序
@Test
public void test4(){
// sorted ()—— 自然排序
List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
list.stream().sorted().forEach(System.out::println);
// 抛异常,原因:Employee 没有实现 Comparable 接口
// List<Employee> employees = EmployeeData.getEmployees();
// employees.stream().sorted().forEach(System.out::println);
// sorted (Comparator com)—— 定制排序
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted( (e1,e2) -> {
int ageValue = Integer.compare(e1.getAge(),e2.getAge());
if(ageValue != 0){
return ageValue;
}else{
return -Double.compare(e1.getSalary(),e2.getSalary());
}
}).forEach(System.out::println);
}
终止操作
匹配与查找
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
// allMatch (Predicate p)—— 检查是否匹配所有元素。
// 练习:是否所有的员工的年龄都大于 18
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(allMatch);
// anyMatch (Predicate p)—— 检查是否至少匹配一个元素。
// 练习:是否存在员工的工资大于 10000
boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
System.out.println(anyMatch);
// noneMatch (Predicate p)—— 检查是否没有匹配的元素。
// 练习:是否存在员工姓 “雷”
boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
System.out.println(noneMatch);
// findFirst—— 返回第一个元素
Optional<Employee> employee = employees.stream().findFirst();
System.out.println(employee);
// findAny—— 返回当前流中的任意元素
Optional<Employee> employee1 = employees.parallelStream().findAny();
System.out.println(employee1);
}
@Test
public void test2(){
List<Employee> employees = EmployeeData.getEmployees();
//count—— 返回流中元素的总个数
long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
System.out.println(count);
// max (Comparator c)—— 返回流中最大值
// 练习:返回最高的工资:
Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
Optional<Double> maxSalary = salaryStream.max(Double::compare);
System.out.println(maxSalary);
// min (Comparator c)—— 返回流中最小值
// 练习:返回最低工资的员工
Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(employee);
System.out.println();
// forEach (Consumer c)—— 内部迭代
employees.stream().forEach(System.out::println);
// 使用集合的遍历操作
employees.forEach(System.out::println);
}
归约
@Test
public void test3(){
// reduce (T identity, BinaryOperator)—— 可以将流中元素反复结合起来,得到一个值。返回 T
// 练习 1:计算 1-10 的自然数的和
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);
// reduce (BinaryOperator) —— 可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
// 练习 2:计算公司所有员工工资的总和
List<Employee> employees = EmployeeData.getEmployees();
Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
// Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
System.out.println(sumMoney.get());
}
收集
@Test
public void test4(){
// collect (Collector c)—— 将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法
// 练习 1:查找工资大于 6000 的员工,结果返回为一个 List 或 Set
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);
System.out.println();
Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
employeeSet.forEach(System.out::println);
}
Optional 类
- 为了在程序中避免出现空指针异常而创建的。
- 常用的方法:
- Optional.of (T t) : 创建一个 Optional 实例,t 必须非空;
- Optional.empty () : 创建一个空的 Optional 实例
- Optional.ofNullable (T t):t 可以为 null
- ofNullable(T t)
- orElse (T t) :如果单前的 Optional 内部封装的 t 是非空的,则返回内部的 t.
- 如果内部的 t 是空的,则返回 orElse () 方法中的参数 t。
Lambda 表达式
语法
** 语法格式一:无参,无返回值 **:
Runnable r1 = () -> {System.out.println("Hello Lambda!");};
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
r1.run();
System.out.println("***********************");
Runnable r2 = () -> {
System.out.println("我爱北京故宫");
};
r2.run();
}
`
语法格式二:Lambda 需要一个参数,但是没有返回值。
Consumer<String>con =(String str)->{System.out.println(str);};
public void test2(){
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("谎言和誓言的区别是什么?");
System.out.println("*******************");
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是听得人当真了,一个是说的人当真了");
}
语法格式三:数据类型可以省略,因为可由编译器推断得出,称为 “类型推断”
Consumer<String>con =(str)->{System.out.println(str);};
public void test3(){
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是听得人当真了,一个是说的人当真了");
System.out.println("*******************");
Consumer<String> con2 = (s) -> {
System.out.println(s);
};
con2.accept("一个是听得人当真了,一个是说的人当真了");
}
语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
Consumer<String>con = str->{System.out.println(str);};
public void test5(){
Consumer<String> con1 = (s) -> {
System.out.println(s);
};
con1.accept("一个是听得人当真了,一个是说的人当真了");
System.out.println("*******************");
Consumer<String> con2 = s -> {
System.out.println(s);
};
con2.accept("一个是听得人当真了,一个是说的人当真了");
}
语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer>com =(x,y)->{
System.out.println ("实现函数式接口方法!"),
return Integer.compare(x,y);}
public void test6(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(12,21));
System.out.println("*****************************");
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(12,6));
}
语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
Comparator<Integer>com =(x,y)->Integer.compare(x,y);
public void test7(){
Comparator<Integer> com1 = (o1,o2) -> {
return o1.compareTo(o2);
};
System.out.println(com1.compare(12,6));
System.out.println("*****************************");
Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
System.out.println(com2.compare(12,21));
}
函数式接口
如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解, 这样做可以检查它是否是一个函数式接口。
消费型接口 Consumer<T> void accept (T t)
public void test1(){
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble);
}
});
System.out.println("********************");
happyTime(400,money -> System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money));
}
public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}
供给型接口 Supplier<T> T get ()
函数型接口 Function<T,R> R apply (T t)
断定型接口 Predicate<T> boolean test (T t)
public void test2(){
List<String> list = Arrays.asList("北京","南京","天津","东京","西京","普京");
List<String> filterStrs = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(filterStrs);
List<String> filterStrs1 = filterString(list,s -> s.contains("京"));
System.out.println(filterStrs1);
}
// 根据给定的规则,过滤集合中的字符串。此规则由 Predicate 的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre){
ArrayList<String> filterList = new ArrayList<>();
for(String s : list){
if(pre.test(s)){
filterList.add(s);
}
}
return filterList;
}
方法引用
使用情境:当要传递给 Lambda 体的操作,已经有实现的方法了,可以使用方法引用!
方法引用,本质上就是 Lambda 表达式,而 Lambda 表达式作为函数式接口的实例。所以方法引用,也是函数式接口的实例。
使用格式: 类 (或对象) :: 方法名
具体分为如下的三种情况:
情况 1 对象::非静态方法
// 情况一:对象::实例方法
//Consumer中的void accept(T t) //PrintStream中的void println(T t) @Test public void test1() { Consumer<String> con1 = str -> System.out.println(str); con1.accept("北京"); System.out.println("*******************"); PrintStream ps = System.out; Consumer<String> con2 = ps::println; con2.accept("beijing"); } //Supplier中的T get() //Employee中的String getName() @Test public void test2() { Employee emp = new Employee(1001,"Tom",23,5600); Supplier<String> sup1 = () -> emp.getName(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<String> sup2 = emp::getName; System.out.println(sup2.get()); }
* **情况2 类 :: 静态方法** * ```java // 情况二:类 :: 静态方法 //Comparator中的int compare(T t1,T t2) //Integer中的int compare(T t1,T t2) @Test public void test3() { Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2); System.out.println(com1.compare(12,21)); System.out.println("*******************"); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(12,3)); } //Function中的R apply(T t) //Math中的Long round(Double d) @Test public void test4() { Function<Double,Long> func = new Function<Double, Long>() { @Override public Long apply(Double d) { return Math.round(d); } }; System.out.println("*******************"); Function<Double,Long> func1 = d -> Math.round(d); System.out.println(func1.apply(12.3)); System.out.println("*******************"); Function<Double,Long> func2 = Math::round; System.out.println(func2.apply(12.6)); }
方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相
同!(针对于情况 1 和情况 2)
情况 3 类::非静态方法
// 情况三:类::实例方法 (有难度)
// Comparator中的int comapre(T t1,T t2) // String中的int t1.compareTo(t2) @Test public void test5() { Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc","abd")); System.out.println("*******************"); Comparator<String> com2 = String :: compareTo; System.out.println(com2.compare("abd","abm")); } //BiPredicate中的boolean test(T t1, T t2); //String中的boolean t1.equals(t2) @Test public void test6() { BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2); System.out.println(pre1.test("abc","abc")); System.out.println("*******************"); BiPredicate<String,String> pre2 = String :: equals; System.out.println(pre2.test("abc","abd")); } // Function中的R apply(T t) // Employee中的String getName(); @Test public void test7() { Employee employee = new Employee(1001, "Jerry", 23, 6000); Function<Employee,String> func1 = e -> e.getName(); System.out.println(func1.apply(employee)); System.out.println("*******************"); Function<Employee,String> func2 = Employee::getName; System.out.println(func2.apply(employee)); }
*
构造器引用
//Supplier 中的 T get ()
//Employee 的空参构造器:Employee ()
@Test
public void test1(){
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println("*******************");
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
System.out.println("*******************");
Supplier<Employee> sup2 = Employee :: new;
System.out.println(sup2.get());
}
//Function 中的 R apply (T t)
@Test
public void test2(){
Function<Integer,Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
System.out.println(employee);
System.out.println("*******************");
Function<Integer,Employee> func2 = Employee :: new;
Employee employee1 = func2.apply(1002);
System.out.println(employee1);
}
//BiFunction 中的 R apply (T t,U u)
@Test
public void test3(){
BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
System.out.println(func1.apply(1001,"Tom"));
System.out.println("*******************");
BiFunction<Integer,String,Employee> func2 = Employee :: new;
System.out.println(func2.apply(1002,"Tom"));
}
数组引用
//Function 中的 R apply (T t)
@Test
public void test4(){
Function<Integer,String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));
System.out.println("*******************");
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
}
新日期时间表达式
LocalDate (日期 / 年月日)
LocalTime (时间 / 时分秒)
LocalDateTime (日期时间 / 年月日时分秒)
DateTimeFormatter 格式日期类
DateTimeFormat dtf = DateTimeFormatter.ofPattern();
String str=dtf.format (日期对象) i
/*
DateTimeFormatter: 格式化或解析日期、时间
类似于 SimpleDateFormat
*/
@Test
public void test3(){
// 方式一:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
// 格式化:日期 --> 字符串
LocalDateTime localDateTime = LocalDateTime.now();
String str1 = formatter.format(localDateTime);
System.out.println(localDateTime);
System.out.println(str1);//2019-02-18T15:42:18.797
// 解析:字符串 --> 日期
TemporalAccessor parse = formatter.parse("2019-02-18T15:42:18.797");
System.out.println(parse);
// 方式二:
// 本地化相关的格式。如:ofLocalizedDateTime ()
// FormatStyle.LONG/ FormatStyle.MEDIUM/ FormatStyle.SHORT : 适用于 LocalDateTime
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
// 格式化
String str2 = formatter1.format(localDateTime);
System.out.println(str2);//2019 年 2 月 18 日 下午 03 时 47 分 16 秒
// 本地化相关的格式。如:ofLocalizedDate ()
// FormatStyle.FULL/ FormatStyle.LONG/ FormatStyle.MEDIUM/ FormatStyle.SHORT : 适用于 LocalDate
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
// 格式化
String str3 = formatter2.format(LocalDate.now());
System.out.println(str3);//2019-2-18
// 重点: 方式三:自定义的格式。如:ofPattern (“yyyy-MM-dd hh:mm:ss”)
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
// 格式化
String str4 = formatter3.format(LocalDateTime.now());
System.out.println(str4);//2019-02-18 03:52:09
// 解析
TemporalAccessor accessor = formatter3.parse("2019-02-18 03:52:09");
System.out.println(accessor);
}
Instant 时间戳
/*
Instant 的使用
类似于 java.util.Date 类
*/
@Test
public void test2(){
//now (): 获取本初子午线对应的标准时间
Instant instant = Instant.now();
System.out.println(instant);//2019-02-18T07:29:41.719Z
// 添加时间的偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);//2019-02-18T15:32:50.611+08:00
//toEpochMilli (): 获取自 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC)开始的毫秒数 ---> Date 类的 getTime ()
long milli = instant.toEpochMilli();
System.out.println(milli);
//ofEpochMilli (): 通过给定的毫秒数,获取 Instant 实例 -->Date (long millis)
Instant instant1 = Instant.ofEpochMilli(1550475314878L);
System.out.println(instant1);
}
方法 (查看 API)
- **ZoneId:** 类中包含了所有的时区信息
- **ZonedDateTime:** 带时区的日期时间
- **Duration:** 用于计算两个 “时间” 间隔,以秒和纳秒为基准
- **Period:** 用于计算两个 “日期” 间隔,以年、月、日衡量
- **TemporalAdjuster:** 时间校正器
接口默认方法
接口默认方法是指在接口中可以包含方法的默认实现,而不需要实现类必须提供该方法的具体实现。
在接口中可以使用关键字
default
来定义默认方法。默认方法可以包含方法体,提供了默认的实现,但实现类也有权选择是否覆盖该默认实现。实现类可以选择性地覆盖默认方法,或者直接继承默认实现。这使得在接口中添加新方法时,已有的实现类不需要修改,因为它们会自动继承默认实现。
eg:
public interface MyInterface {
void regularMethod(); // 普通方法,实现类必须提供具体实现
default void defaultMethod() {
System.out.println("This is a default implementation."); // 默认方法,实现类可以选择性覆盖
}