12.JavaSE:注解
12.1 作用
1.Annotation 是从JDK5.0开始引入的新技术 .
2.不是程序本身 , 可以对程序作出解释(这一点和注释(comment)没什么区别),可以被其他程序(比如:编译器等)读取
3.注解是以"@注释名"在代码中存在的
4.还可以添加一些参数值 , 例如:@SuppressWarnings(value=“unchecked”)
5.可以附加在package , class , method , fifield 等上面 , 相当于给他们添加了额外的辅助信息
6.我们可以通过反射机制实现对这些元数据的访问
12.2 内置注解
12.2.1 @Override
定义在 java.lang.Override 中 , 此注释只适用于修辞方法 , 表示一个方法声明打算重写父类中的另一个方法声明
12.2.2 @Deprecated
定义在java.lang.Deprecated中 , 此注释可以用于修辞方法 , 属性 , 类 ;表示不鼓励程序员使用这样的元素 , 通常是因为它很危险或者存在更好的选择,即该方法已被弃用
12.2.3 @SuppressWarnings
定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息
关键字 | 用途 |
---|---|
all | 抑制所有警告 |
boxing | 抑制装箱、拆箱操作时候的警告 |
cast | 抑制映射相关的警告 |
dep-ann | 抑制启用注释的警告 |
deprecation | 抑制过期方法警告 |
fallthrough | 抑制在 switch 中缺失 breaks 的警告 |
finally | 抑制 finally 模块没有返回的警告 |
hiding | 抑制相对于隐藏变量的局部变量的警告 |
incomplete-switch | 忽略不完整的 switch 语句 |
nls | 忽略非 nls 格式的字符 |
null | 忽略对 null 的操作 |
rawtypes | 使用 generics 时忽略没有指定相应的类型 |
restriction | 抑制禁止使用劝阻或禁止引用的警告 |
serial | 忽略在 serializable 类中没有声明 serialVersionUID 变量 |
static-access | 抑制不正确的静态访问方式警告 |
unchecked | 抑制没有进行类型检查操作的警告 |
unqualified-field-access | 抑制没有权限访问的域的警告 |
unused | 抑制没被使用过的代码的警告 |
//需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性的使用就好了
@SuppressWarnings("all")
@SuppressWarnings("unchecked")
@SuppressWarnings(value={"unchecked","deprecation"}
package com.annotation;
//测试内置注解
import java.util.ArrayList;
import java.util.List;
public class Test1{
//重写object中toString方法
@Override
public String toString() {
return super.toString();
}
//方法过时,不建议使用
@Deprecated
public static void stop(){
System.out.println("测试@Deprecated");
}
//@SuppressWarnings 抑制警告
@SuppressWarnings("all")
public void sw(){
List list = new ArrayList();
}
public static void main(String[] args) {
stop();
}
}
12.2.4 @FunctionalInterface
一、在学习 Lambda 表达式时,我们提到如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),那么该接口就是函数式接口
二、@FunctionalInterface 就是用来指定某个接口必须是函数式接口,所以@FunInterface 只能修饰接口,不能修饰其它程序元素
三、编译下面程序,可能丝毫看不出程序中的 @FunctionalInterface 有何作用,因为 @FunctionalInterface 注解的作用只是告诉编译器检查这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错
//函数式接口就是为Lambda表达式准备的,Java 8允许使用Lambda表达式创建函数式接口的实例,因此专门增加了@FunctionalInterface
@FunctionalInterface
public interface FunInterface {
void test(); //只定义一个抽象方法
static void print() {
System.out.println("GuoJingPeng");
}
default void show() {
System.out.println("XiaoGuo");
}
}
//@FunctionalInterface注解主要是帮助程序员避免一些低级错误,例如,在上面的FunInterface接口中再增加一个抽象方法abc(),编译程序时将出现如下错误提示:“@FunctionInterface”批注无效:FunInterface不是functional接口
12.2.5 @Repeatable
一、@Repeatable 注解是 Java 8 新增加的,它允许在相同的程序元素中重复注解,在需要对同一种注解多次使用时,往往需要借助该注解。Java 8 版本以前,同一个程序元素前最多只能有一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”
二、不同的地方是,创建重复注解 Role 时加上了 @Repeatable 注解,指向存储注解Roles,这样在使用时就可以直接重复使用 Role 注解;从上面例子看出,使用@Repeatable 注解更符合常规思维,可读性强一点
三、两种方法获得的效果相同,重复注解只是一种简化写法,这种简化写法是一种假象,多个重复注解其实会被作为“容器”注解的 value 成员的数组元素处理
//Java 8之前的做法
public @interface Roles {
Role[] roles();
}
public @interface Roles {
Role[] value();
}
public class RoleTest {
@Roles(roles = {@Role(roleName = "role1"), @Role(roleName = "role2")})
public String doString(){
return "XiaoGuo";
}
}
//Java 8之后增加了重复注解,使用方式如下
public @interface Roles {
Role[] value();
}
@Repeatable(Roles.class)
public @interface Role {
String roleName();
}
public class RoleTest {
@Role(roleName = "role1")
@Role(roleName = "role2")
public String doString(){
return "XiaoGuo";
}
}
12.5.6 @Native
使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用;对于 @Native 注解不常使用,了解即可
12.5.7 @SafeVarargs
可用@SafeVarargs 注解抑制编译器警告
/*
上述代码在可变参数 display 前添加了@SafeVarargs注解,当然也可以使用 @SuppressWarnings("unchecked") 注解,但是两者相比较来说@SafeVarargs注解更适合
@SafeVarargs注解不适用于非static或非final声明的方法,对于未声明为static或final的方法,如果要抑制unchecked警告,可以使用 @SuppressWarnings注解
*/
public class HelloWorld {
public static void main(String[] args) {
// 传递可变参数,参数是泛型集合
display(10, 20, 30);
// 传递可变参数,参数是非泛型集合
display("10", 20, 30); // 没有@SafeVarargs会有编译警告
}
@SafeVarargs
public static <T> void display(T... array) {
for (T arg : array) {
System.out.println(arg.getClass().getName() + ":" + arg);
}
}
}
12.3 元注解
元注解的作用就是负责注解其他注解 , Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明;这些类型和它们所支持的类在java.lang.annotation包中可以找到 ( @Target , @Retention ,@Documented , @Inherited )
注释 | 作用 |
---|---|
@Target | 用于描述注解的使用范围 |
@Retention | 表示需要在什么级别保存该注释信息 , 用于描述注解的生命周期(SOURCE < CLASS < RUNTIME) |
@Document | 说明该注解将被包含在javadoc中 |
@Inherited | 说明子类可以继承父类中的该注解 |
12.3.1 @Documented
@Documented 是一个标记注解,没有成员变量;用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档;默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中
@Documented
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface MyDocumented {
public String value() default "这是@Documented注解";
}
@MyDocumented
public class DocumentedTest {
/**
* 测试document
*/
@MyDocumented
public String Test() {
return "XiaoGuo";
}
}
//命令行执行语句
javac MyDocumented.java DocumentedTest.java
javadoc -d doc MyDocumented.java DocumentedTest.java
12.3.2 @Target
@Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。@Target 注解有一个成员变量(value)用来设置适用目标,value 是 java.lang.annotation.ElementType 枚举类型的数组,下表为 ElementType 常用的枚举常量
名称 | 说明 |
---|---|
CONSTRUCTOR | 用于构造方法 |
FIELD | 用于成员变量(包括枚举常量) |
LOCAL_VARIABLE | 用于局部变量 |
METHOD | 用于方法 |
PACKAGE | 用于包 |
PARAMETER | 用于类型参数(JDK 1.8新增) |
TYPE | 用于类、接口(包括注解类型)或 enum 声明 |
@Target({ ElementType.METHOD })
public @interface MyTarget {
}
class Test {
//@MyTarget,会编译错误The annotation @MyTarget is disallowed for this location
//提示此位置不允许使用注解@MyTarget,@MyTarget不能修饰成员变量,只能修饰方法
String name;
}
12.3.3 @Retention
一、@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。@Retention 注解中的成员变量(value)用来设置保留策略,value 是 java.lang.annotation.RetentionPolicy 枚举类型,RetentionPolicy 有 3 个枚举常量,如下所示:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在 class 文件中有效(即 class 保留)
3.RUNTIME:在运行时有效(即运行时保留)
二、生命周期大小排序为 SOURCE < CLASS < RUNTIME,前者能使用的地方后者一定也能使用。如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS 注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解
12.3.4 @Inherited
@Inherited 是一个标记注解,用来指定该注解可以被继承;使用 @Inherited 注解的 Class 类,表示这个注解可以被用于该 Class 类的子类;就是说如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解
@Target({ ElementType.TYPE })
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInherited {
}
@MyInherited
public class TestA {
public static void main(String[] args) {
//@MyInherited()
//@MyInherited()
//@MyInherited()
System.out.println(TestA.class.getAnnotation(MyInherited.class));
System.out.println(TestB.class.getAnnotation(MyInherited.class));
System.out.println(TestC.class.getAnnotation(MyInherited.class));
}
}
class TestB extends TestA {
}
class TestC extends TestB {
}
12.4 自定义注解
1.使用**@interface**自定义注解时 , 自动继承了java.lang.annotation.Annotation接口
2.@interface用来声明一个注解 , 格式 : public @ interface 注解名 { 定义内容 }
3.其中的每一个方法实际上是声明了一个配置参数
4.方法的名称就是参数的名称
5.返回值类型就是参数的类型 ( 返回值只能是基本类型,Class , String , enum ).
6.可以通过default来声明参数的默认值
7.如果只有一个参数成员 , 一般参数名为value
8.注解元素必须要有值 , 我们定义注解元素时 , 经常使用空字符串,0作为默认值
package com.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//测试自定义注解
public class Test {
//显示定义值/不显示值就是默认值
@MyAnnotations(age = 18,name = "XiaoGuo",id = 001,schools = {"济南"})
public void test() {
}
//只有一个参数, 默认名字一般是value.使用可省略不写
@MyAnnotation("xiaoguo")
public void test2(){
}
}
******************************************************************************************
@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotations{
// 参数类型 参数名称
String name() default "";
int age() default 0;
int id() default -1;
//String indexOf("abc") -1 , 不存在,找不到
String[] indexof() default {"青岛"};
}
********************************************************************************************
@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation{
// 参数类型 参数名称
String value();
}
12.5 反射读取注解
package com.annotation;
import java.lang.annotation.*;
import java.lang.reflect.Field;
//测试ORM:对象关系映射
//使用反射读取注解信息三步:
// 1.定义注解
// 2.在类中使用注解
// 3. 使用反射获取注解,一般都是现成框架实现,我们手动实现
public class Test {
public static void main(String[] args) {
try {
//反射,Class可以获得类的全部信息,所有的东西
Class clazz = Class.forName("com.annotation.Student");
//获得这个类的注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation:annotations){
System.out.println(annotation);
}
//获得类的注解value的值
TableKuang table = (TableKuang)
clazz.getAnnotation(TableKuang.class);
System.out.println(table.value());
//获得类指定注解的值
Field name = clazz.getDeclaredField("name");
FieldKuang fieldKuang = name.getAnnotation(FieldKuang.class);
System.out.println(fieldKuang.columnName()+"-->"+fieldKuang.type()+"-->"+fieldKuang.length());
//我们可以根据得到的类的信息,通过JDBC生成相关的SQL语句,执行就可以动态生成数据库表
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
********************************************************************************
//假设数据库表名为db_student
@TableTest("db_student")
class Student{
@FieldTest(columnName = "db_id",type="int",length = 10)
private int id;
@FieldTest(columnName = "db_name",type="varchar",length = 10)
private String name;
@FieldTest(columnName = "db_age",type="int",length = 3)
private int age;
public Student() {
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';
}
}
********************************************************************************
//表名注解,只有一个参数,建议使用value命名
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface TableTest{
String value();
}
********************************************************************************
//属性注解
@Target(value = {ElementType.FIELD}) //注意字段
@Retention(value = RetentionPolicy.RUNTIME)
@interface FieldTest{
String columnName(); //列名
String type(); //类型
int length();//长度
}
13.JavaSE:反射机制
13.1 动态/静态语言
一、动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化;通俗点说就是在运行时代码可以根据某些条件改变自身结构
主要动态语言:Object-C、C#、JavaScript、PHP、Python等
//体现动态语言的代码
function test() {
var x = "var a=3;var b=5;alert(a+b)";
eval(x);
}
二、静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言,如Java、C、C++。Java不是动态语言,但Java可以称之为“准动态语言”;即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性;Java的动态性让编程的时候更加灵活!
13.2 Java Reflection
一、Reflflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
二、加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息;我们可以通过这个对象看到类的结构;这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
三、Java反射机制提供的功能
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时判断任意一个类所具有的成员变量和方法
4.在运行时获取泛型信息
5.在运行时调用任意一个对象的成员变量和方法
6.在运行时处理注解
7.生成动态代理
四、Java反射优点和缺点
优点:可以实现动态创建对象和编译,体现出很大的灵活性
缺点:对性能有影响,反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且使它满足我们的要求,这类操作总是慢于直接执行相同的操作
五、相关API
1.java.lang.Class : 代表一个类
2.java.lang.reflflect.Method : 代表类的方法
3.java.lang.reflflect.Field : 代表类的成员变量
4.java.lang.reflflect.Constructor : 代表类的构造器
//正常方式:引入需要的“包类”名称---->通过new实例化---->取得实例化对象
//反射方式:实例化对象--->getClass()方法---->“得到完整的包类名称”
Class c = Class.forName("java.lang.String")
package com.reflection;
public class Test {
public static void main(String[] args) {
try {
//通过反射获取类的Class
Class<?> c1 = Class.forName("com.reflection.User");
//一个类被加载后 , 类的整个结构信息会被放到对应的Class对象中
System.out.println(c1);
//一个类只对应一个Class对象
Class<?> c2 = Class.forName("com.reflection.User");
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
********************************************************************
//创建一个实体类
class User{
private int id;
private int age;
private String name;
public User() {
}
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +"id=" + id +", age=" + age +", name=" + name +'}';
}
}
13.3 Reflection Class类
在Object类中定义了以下的方法,此方法将被所有子类继承;以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称
public final Class getClass();
反射可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口,对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象
package com.reflection;
//测试各种类型获得Class对象的方式
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这是:"+person.name);
//获得class办法一:通过对象获得
Class clazz1 = person.getClass();
//获得class办法二:通过字符串获得(包名+类名)
Class clazz2 = Class.forName("com.reflection.Student");
//获得class办法三:通过类的静态成员class获得
Class clazz3 = Person.class;
//获得class办法四:只针对内置的基本数据类型
Class clazz4 = Integer.TYPE;
//获得父类类型
Class clazz5 = clazz2.getSuperclass();
System.out.println(clazz1);
System.out.println(clazz2);
System.out.println(clazz3);
System.out.println(clazz4);
System.out.println(clazz5);
}
}
************************************************************
class Person {
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +"name='" + name + '\'' +'}';
}
}
************************************************************
class Student extends Person{
public Student(){
this.name = "学生";
}
}
************************************************************
class Teacher extends Person{
public Teacher(){
this.name = "老师";
}
}
12.3.1 Class类常用方法
方法名 | 功能 |
---|---|
static ClassforName(String name) | 返回指定类名name的Class对象 |
Object newInstance() | 调用缺省构造函数,返回Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类,接口,数组类或void)的名称 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
Class[ ] getinterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Constructor[ ] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Method getMothed(Stringname,Class… T) | 返回一个Method对象,此对象的形参类型为paramType |
Field[ ] getDeclaredFields() | 返回Field对象的一个数组 |
12.3.2 获取Class类的实例
1.若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
Class clazz = Person.class;
2.已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class clazz = person.getClass();
3.已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
Class clazz = Class.forName("demo01.Student");
4.内置基本数据类型可以直接用类名.Type
5.还可以利用ClassLoader
12.3.3 哪些类型存在Class对象?
1.class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
2.interface:接口
3.[ ]:数组
4.enum:枚举
5.annotation:注解@interface
6.primitive type:基本数据类型
7.void
package com.reflection;
import java.lang.annotation.ElementType;
//演示:所有类型的class
public class Test {
public static void main(String[] args) {
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = Integer.class;
Class c8 = void.class;
Class c9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
System.out.println(c10);
System.out.println(c11);
//只要元素类型与维度一样,就是同一个Class
System.out.println(c11==c10);
}
}
14.JavaSE:枚举
枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。枚举在曰常生活中很常见,例如一个人的性别只能是“男”或者“女”,一周的星期只能是 7 天中的一个等;类似这种当一个变量有几种固定可能的取值时,就可以将它定义为枚举类型
在 JDK 1.5 之前没有枚举类型,那时候一般用接口常量来替代,而使用Java枚举类型 enum 可以更贴近地表示这种常量
14.1 声明枚举
声明枚举时必须使用 enum 关键字,然后定义枚举的名称、可访问性、基础类型和成员等。枚举声明的语法如下:
//任意两个枚举成员不能具有相同的名称,且它的常数值必须在该枚举的基础类型的范围之内,多个枚举成员之间使用逗号分隔
//enumname 表示声明的枚举名称
//enum-base 表示基础类型
//enum-body 表示枚举的成员,它是枚举类型的命名常数
public/private/internal enumname:enum-base {
enum-body,
}
//如果没有显式地声明基础类型的枚举,那么意味着它所对应的基础类型是int
public enum SexEnum {
male,female;
}
public enum Color {
RED,BLUE,GREEN,BLACK;
//之后便可以通过枚举类型名直接引用常量,如SexEnum.male、Color.RED
}
//使用枚举还可以使switch语句的可读性更强
enum Signal {
// 定义一个枚举类型
GREEN,YELLOW,RED
}
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
switch(color) {
case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
}
}
}
14.2 枚举类
一、Java 中的每一个枚举都继承自 java.lang.Enum 类;
二、当定义一个枚举类型时,每一个枚举类型成员都可以看作是 Enum 类的实例,这些枚举成员默认都被 final、public, static 修饰,当使用枚举类型成员时,直接使用枚举名称调用成员即可;
三、所有枚举实例都可以调用 Enum 类的方法:
方法名称 | 描述 |
---|---|
values( ) | 以数组形式返回枚举类型的所有成员 |
valueOf( ) | 将普通字符串转换为枚举实例 |
compareTo( ) | 比较两个枚举成员在定义时的顺序 |
ordinal( ) | 获取枚举成员的索引位置 |
//通过调用枚举类型实例的values()方法可以将枚举的所有成员以数组形式返回,也可以通过该方法获取枚举类型的成员
enum Signal {
// 定义一个枚举类型
GREEN,YELLOW,RED;
}
public static void main(String[] args) {
for(int i = 0;i < Signal.values().length;i++) {
System.out.println("枚举成员:"+Signal.values()[i]);
}
//枚举成员:GREEN
//枚举成员:YELLOW
//枚举成员:RED
}
//创建一个示例,调用valueOf()方法获取枚举的一个成员,再调用compareTo()方法进行比较,并输出结果
public class TestEnum {
public enum Sex {
// 定义一个枚举
male,female;
}
public static void main(String[] args) {
compare(Sex.valueOf("male")); //比较
}
public static void compare(Sex s) {
for(int i = 0;i < Sex.values().length;i++) {
System.out.println(s + "与" + Sex.values()[i] + "的比较结果是:" + s.compareTo(Sex.values()[i]));
}
}
//male与male的比较结果是:0
//male与female的比较结果是:-1
}
//通过调用枚举类型实例的ordinal()方法可以获取一个成员在枚举中的索引位置
public class TestEnum1 {
enum Signal {
// 定义一个枚举类型
GREEN,YELLOW,RED;
}
public static void main(String[] args) {
for(int i = 0;i < Signal.values().length;i++) {
System.out.println("索引" + Signal.values()[i].ordinal()+",值:" + Signal.values()[i]);
}
}
//索引0,值:GREEN
//索引1,值:YELLOW
//索引2,值:RED
}
14.3 为枚举添加方法
Java 为枚举类型提供了一些内置的方法,同时枚举常量也可以有自己的方法;此时要注意必须在枚举实例的最后一个成员后添加分号,而且必须先定义枚举实例
enum WeekDay {
Mon("Monday"),Tue("Tuesday"),Wed("Wednesday"),Thu("Thursday"),Fri("Friday"),Sat("Saturday"),Sun("Sunday");
// 以上是枚举的成员,必须先定义,而且使用分号结束
private final String day;
private WeekDay(String day) {
this.day = day;
}
public static void printDay(int i) {
switch(i) {
case 1:
System.out.println(WeekDay.Mon);
break;
case 2:
System.out.println(WeekDay.Tue);
break;
case 3:
System.out.println(WeekDay.Wed);
break;
case 4:
System.out.println(WeekDay.Thu);
break;
case 5:
System.out.println(WeekDay.Fri);
break;
case 6:
System.out.println(WeekDay.Sat);
break;
case 7:
System.out.println(WeekDay.Sun);
break;
default:
System.out.println("wrong number!");
}
}
public String getDay() {
return day;
}
}
****************************************************************************************
//遍历该枚举中的所有成员,并调用printDay()方法
public static void main(String[] args) {
for(WeekDay day : WeekDay.values()) {
System.out.println(day+"====>" + day.getDay());
//Mon====>Monday
//Tue====>Tuesday
//Wed====>Wednesday
//Thu====>Thursday
//Fri====>Friday
//Sat====>Saturday
//Sun====>Sunday
}
WeekDay.printDay(5);//Fri
}
//Java中的enum还可以跟Class类一样覆盖父类的方法
//下面示例代码创建的Color枚举类型覆盖了toString()方法
public class Test {
public enum Color {
RED("红色",1),GREEN("绿色",2),WHITE("白色",3),YELLOW("黄色",4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name,int index) {
this.name = name;
this.index = index;
}
// 覆盖方法
@Override
public String toString() {
return this.index + "-" + this.name;
}
}
public static void main(String[] args) {
System.out.println(Color.RED.toString());// 输出:1-红色
}
}
14.4 EnumMap与EnumSet
为了更好地支持枚举类型,java.util 中添加了两个新类:EnumMap和EnumSet,使用它们可以更高效地操作枚举类型;
14.4.1 EnumMap类
一、EnumMap是专门为枚举类型量身定做的Map实现,虽然使用其他的Map(如HashMap)实现也能完成枚举类型实例到值的映射,但是使用 EnumMap会更加高效
二、HashMap 只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值,使得 EnumMap 的效率非常高
三、在实际使用中,EnumMap 对象 urls 往往是由外部负责整个应用初始化的代码来填充的;这里为了演示方便,类自己做了内容填充
四、从本例中可以看出,使用EnumMap可以很方便地为枚举类型在不同的环境中绑定到不同的值上;本例子中getURL绑定到URL 上,在其他的代码中可能又被绑定到数据库驱动上去
//使用EnumMap的一个代码示例
//枚举类型DataBaseType里存放了现在支持的所有数据库类型,针对不同的数据库,一些数据库相关的方法需要返回不一样的值
// 定义数据库类型枚举
public enum DataBaseType {
MYSQL,ORACLE,DB2,SQLSERVER
}
//某类中定义的获取数据库URL的方法以及EnumMap的声明
private EnumMap<DataBaseType,String>urls = new EnumMap<DataBaseType,String>(DataBaseType.class);
public DataBaseInfo() {
urls.put(DataBaseType.DB2,"jdbc:db2://localhost:5000/sample");
urls.put(DataBaseType.MYSQL,"jdbc:mysql://localhost/mydb");
urls.put(DataBaseType.ORACLE,"jdbc:oracle:thin:@localhost:1521:sample");
urls.put(DataBaseType.SQLSERVER,"jdbc:microsoft:sqlserver://sql:1433;Database=mydb");
}
//根据不同的数据库类型,返回对应的URL
// @param type DataBaseType 枚举类新实例
// @return
public String getURL(DataBaseType type) {
return this.urls.get(type);
}
14.4.2 EnumSet类
一、EnumSet 是枚举类型的高性能 Set 实现,它要求放入它的枚举常量必须属于同一枚举类型;EnumSet 提供了许多工厂方法以便于初始化:
方法名称 | 描述 |
---|---|
allOf(Class element type) | 创建一个包含指定枚举类型中所有枚举成员的 EnumSet 对象 |
complementOf(EnumSet s) | 创建一个与指定EnumSet对象 s 相同的枚举类型 EnumSet 对象, 并包含所有s中未包含的枚举成员 |
copyOf(EnumSet s) | 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象, 并与 s 包含相同的枚举成员 |
noneOf(<Class elementType) | 创建指定枚举类型的空 EnumSet 对象 |
of(E first,e…rest) | 创建包含指定枚举成员的 EnumSet 对象 |
range(E from ,E to) | 创建一个 EnumSet 对象,该对象包含了 from 到 to 之间的所有枚 举成员 |
二、EnumSet 作为 Set 接口实现,它支持对包含的枚举常量的遍历
for(Operation op:EnumSet.range(Operation.PLUS,Operation.MULTIPLY)) {
doSomeThing(op);
}
15.JavaSE:网络编程
一、Java是Internet上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序;Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机安装系统;,由JVM进行控制,并且Java实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境;端口号与IP地址的组合,得出一个网络套接字:Socket,所以说一些网络编程也被称为Socket编程
二、网络通信两个要素
1.ip和端口号
2.提供网络通信协议,TCP/IP参考模型(应用层,传输层,网络层,物理+数据链路层)
15.1 IP InetAdderss
一、唯一的标识 internet 上的计算机 ( 通信实体 )
二、本地回环地址(hostAddress):127.0.0.1 主机名 ( hostName ):localhost
三、IP地址分类方式一 : IPV4、IPV6
1.IPV4:4个字节组成,4个0~255,2011年初已经用尽,以点分十进制表示,如192.168.0.1
2.IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示,数之间用冒号隔开,如2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b
四、IP地址分类方式二:公网地址(万维网使用) 和 私有地址(局域网使用–>范围即为 192.168.0.0 ~ 192.168.255.255)
import java.net.InetAddress;
import java.net.UnknownHostException;
//IP---->InetAddress.getByName
public class InetAddressTest {
public static void main(String[] args) {
try {
//获得IP地址
InetAddress inetAddresses1 =InetAddress.getByName("192.168.8.123");
System.out.println(inetAddresses1);
InetAddress inetAddresses2 =InetAddress.getByName("www.baidu.com");
System.out.println(inetAddresses2);
//获取本地IP
InetAddress inetAddresses3 = InetAddress.getByName("127.0.0.1");
System.out.println(inetAddresses3);
InetAddress inetAddresses4 = InetAddress.getByName("localhost");
System.out.println(inetAddresses4);
InetAddress inetAddresses5 = InetAddress.getLocalHost();
System.out.println(inetAddresses5);
//getHostName
System.out.println(inetAddresses2.getHostName());
//getHostAddress
System.out.println(inetAddresses2.getHostAddress());
//Canonical:规范的
System.out.println(inetAddresses2.getCanonicalHostName());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
15.2 端口号
一、端口号标识正在计算机上运行的进程(程序),不同的进程有不同的端口号,用来区分软件;
二、被规定为一个16位的整数 0~65535
三、TCP 和 UDP 各有 65535个端口,单个协议下端口不能冲突
四、端口分类:
1.公认端口: 0~1023:被预先定义的服务通信占用端口
HTTP 默认端口 : 80
HTTPS 默认端口:443
FTP 默认端口: 21
Telnet 默认端口:23
**2.注册端口:**1024~49151:分配给用户进程或应用程序
tomcat 默认端口:8080
Mysql 默认端口:3306
Oracle 默认端口:1521
**3.动态、私有端口:**49152~65535
//使用任务管理器查看PID
netstat -ano #查看所有端口
netstat -ano|findstr "6732" #查看指定端口
tasklist|findstr "6732" #查看指定进程
import java.net.InetSocketAddress;
public class InetSocketAddressTest {
public static void main(String[] args) {
InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8080);
InetSocketAddress socketAddress2 = new InetSocketAddress("localhost",9000);
System.out.println(socketAddress.getHostName());//activate.navicat.com
System.out.println(socketAddress.getAddress());//activate.navicat.com/127.0.0.1
System.out.println(socketAddress.getPort());//8080
System.out.println(socketAddress2.getHostName());//localhost
System.out.println(socketAddress2.getAddress()); //返回地址localhost/127.0.0.1
System.out.println(socketAddress2.getPort()); //返回端口9000
}
}
15.3 网络通信协议
计算机网络中实现通信必须有一些约定,即通信协议,对速率,传输代码,代码结构,传输控制步骤,出错控制等制定标准
15.3.1 TCP/IP协议簇
一、传输层协议中有两个非常重要的协议:
1.用户传输协议 TCP (Transmission Control Protocol)
2.用户数据报协议(User Datagram Protocol)
二、Tcp/IP 以其两个主要协议: 传输控制协议:TCP和网络互联协议:IP;实际上是一组协议,包括多个具有不同功能且互为关联的协议
三、IP(Internet Protocol)协议是网络层的主要协议,支持网间互联的数据通信
四、TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即物理链路层,IP层,传输层和应用层
15.3.2 TCP和UDP的区别
一、TCP:
1.使用TCP协议前,必须建立TCP连接,形成传输数据通道
2.传输前,采用 ‘ 三次握手 ’ 方式,点对点通信,是可靠的
3.TCP协议进行通信的两个应用进程:客户端,服务端
4.在连接中可进行大数据量的传输
5.传输完毕,需要释放已建立的连接,效率低
6.举例:打电话
二、UDP:
1.将数据,源,目的封装成数据包,不需要建立连接
2.每个数据报的大小限制在64K内
3.发送方不管对方是否准备好,接收方收到也不确认,所以不可靠
4.可以广播发送
5.发送数据结束时,无需释放资源,开销小,速度快
6.举例:发短信,导弹(饱和攻击)
15.4 案例总结
一、客户端发送信息给服务端,服务端将数据显示在控制台上
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
//客户端
public class TcpClientDemo01 {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
try {
//1. 连接服务器的地址
InetAddress serverIP = InetAddress.getByName("127.0.0.1");
int port = 8899;
//2. 创建一个Socket
socket = new Socket(serverIP,port);
//3. 创建一个输出流,向外写东西
os = socket.getOutputStream();
os.write("XiaoGuo".getBytes());
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源
try {
if (os!=null){
os.close();
}
if (socket!=null){
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
import javax.sound.midi.Soundbank;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Base64;
//服务端
public class TcpServerDemo01 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket accept = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1. 开放服务器端口,创建ServerSocket
serverSocket = new ServerSocket(8899);
//2. 等待客户端的连接
accept = serverSocket.accept();
//3. 读入客户端的消息
is = accept.getInputStream();
//回忆之前的IO流方案,弊端:存在中文,可能存在乱码
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len=is.read(buffer))!=-1){
baos.write(buffer,0,len);
}
System.out.println(baos.toString());
System.out.println("数据来源地址:"+accept.getInetAddress().getHostName());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源
try {
if (baos!=null){
baos.close();
}
if (is!=null){
is.close();
}
if (accept!=null){
accept.close();
}
if (serverSocket!=null){
serverSocket.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
二、客户端发送文件给服务器,服务端将文件保存在本地
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
//客户端
public class TcpClientDemo02 {
public static void main(String[] args) throws Exception{
//1. 创建socket连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
//2. 创建一个输出流
OutputStream os = socket.getOutputStream();
//3. 读取文件
FileInputStream fis = new FileInputStream(new File("test.jpg"));
//4. 写出文件
byte[] buffer = new byte[1024];
int len;
while ((len=fis.read(buffer))!=-1){
os.write(buffer,0,len);
}
//5. 资源关闭,应该使用 try-catch-finally
fis.close();
os.close();
socket.close();
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
//服务端
public class TcpServerDemo02 {
public static void main(String[] args) throws Exception {
//1. 开启 ServerSocket
ServerSocket serverSocket = new ServerSocket(9090);
//2. 侦听 客户端 Socket
Socket socket = serverSocket.accept();
//3. 获取输入流
InputStream is = socket.getInputStream();
//4. 读取接收的文件并保存
FileOutputStream fos = new FileOutputStream(new File("receive.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
//5.关闭资源,应该使用 try-catch-finally
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
三、客户端发送文件给服务器,服务端将文件保存在本地,接收成功后,返回给客户端,接收成功!然后客户端才关闭连接;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
//客户端
public class TcpClientDemo {
public static void main(String[] args) throws Exception{
//1. 创建socket连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
//2. 创建一个输出流
OutputStream os = socket.getOutputStream();
//3. 读取文件
FileInputStream fis = new FileInputStream(new File("qinjiang.jpg"));
//4. 写出文件
byte[] buffer = new byte[1024];
int len;
while ((len=fis.read(buffer))!=-1){
os.write(buffer,0,len);
}
//告诉服务器,我传输完了,关闭数据的输出,不然就会一直阻塞!
socket.shutdownOutput();
//先别着急关,等待服务器响应,响应到控制台,注意重复的变量问题!
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[1024];
int len2;
while ((len2=inputStream.read(buffer2))!=-1){
baos.write(buffer2,0,len2);
}
System.out.println(baos.toString());
//5. 资源关闭,应该使用 try-catch-finally
baos.close();
inputStream.close();
fis.close();
os.close();
socket.close();
}
}
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//服务端
public class TcpServerDemo{
public static void main(String[] args) throws Exception {
//1.开启ServerSocket
ServerSocket serverSocket = new ServerSocket(9090);
//2.侦听客户端Socket
Socket socket = serverSocket.accept();
//3.获取输入流
InputStream is = socket.getInputStream();
//4.读取接收的文件并保存
FileOutputStream fos = new FileOutputStream(new File("receive2.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
//通知客户端接收成功
OutputStream outputStream = socket.getOutputStream();
outputStream.write("文件已经成功收到".getBytes());
//5.关闭资源,应该使用 try-catch-finally
outputStream.close();
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
15.5 UDP网络编程
一、DatagramSocket 和 DatagramPacket 两个类实现了基于UDP协议的网络程序
二、UDP 数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不确定什么时候可以抵达
三、DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号
四、UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接,如同发快递包裹一样
//多线程客户端
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class UdpTalkClient {
public static void main(String[] args) throws Exception {
System.out.println("发送方启动中");
//1.使用DatagramSocket指定端口,创建发送端
DatagramSocket socket = new DatagramSocket(8888);
//2.准备数据,转成字节数组
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true){
String data = reader.readLine();
byte[] datas = data.getBytes();
//3.封装成DatagramPacket包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost",6666));
//4.发送包裹send
socket.send(packet);
//5.退出判断
if (data.equals("bye")){
break;
}
}
//5. 释放资源
socket.close();
}
}
//多线程服务端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UdpTalkServer {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(6666);
while (true) {
try {
//准备接收包裹
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container, 0,container.length);
//阻塞式接收包裹
socket.receive(packet);
byte[] datas = packet.getData();
int len = packet.getLength();
String data = new String(datas,0,len);
System.out.println(data);
//退出判断
if (data.equals("bye")){
break;
}
}catch (Exception e){
e.printStackTrace();
}
}
socket.close();
}
}
15.6 URL编程
一、URL(Uniform Resource Locator):统一资源定位符,它表示 internet 上某一资源的地址
二、它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate:定位这个资源
三、通过URL 我们可以访问Internet上的各种网络资源,比如最常见的 www,ftp站点,浏览器通过解析给定的URL可以在网络上查找相应的文件或其他资源
四、URL的基本结构由五部分组成:
//如:http://localhost:8080/helloworld/index.jsp#a?username=XiaoGuo&password=123
//片段名,即锚链接,比如我们去一些小说网站,可以直接定位到某个章节位置
//参数列表格式 : 参数名=参数值 & 参数名=参数值...
传输协议://主机名:端口号/文件名 #片段名?参数列表
import java.net.MalformedURLException;
import java.net.URL;
public class URLDemo{
public static void main(String[] args) {
try {
URL url = new URL("http://localhost:8080/helloworld/index.jsp?username=XiaoGuo&password=123");
System.out.println(url.getProtocol()); //获取URL的协议名
System.out.println(url.getHost()); //获取URL的主机名
System.out.println(url.getPort()); //获取URL的端口号
System.out.println(url.getPath()); //获取URL的文件路径
System.out.println(url.getFile()); //获取URL的文件名
System.out.println(url.getQuery()); //获取URL的查询名
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
15.7 下载tomcat下的文件
//首先在tomcat中放入一个资源文件
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class URLDemo{
public static void main(String[] args) {
try {
//1.定位到服务器端的资源
URL url = new URL("http://localhost:8080/helloworld/test.jpg");
//2.创建连接
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
//3.获取输入流
InputStream is = connection.getInputStream();
//4.写出文件
FileOutputStream fos = new FileOutputStream("testOut.jpg");
byte[] buffer = new byte[1024];
int len;
while ((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
//5.关闭资源
fos.close();
is.close();
connection.disconnect(); //断开连接
} catch (Exception e) {
e.printStackTrace();
}
}
}
16.JavaSE:JDK8新特性
16.1 Stream API
一、Java 8 还新增了 Stream、IntStream、LongStream、DoubleStream 等流式 API,这些 API 代表多个支持串行和并行聚集操作的元素。上面 4 个接口中,Stream 是一个通用的流接口,而 IntStream、LongStream、 DoubleStream 则代表元素类型为 int、long、double 的流;
二、Java 8 还为上面每个流式 API 提供了对应的 Builder,例如 Stream.Builder、IntStream.Builder、LongStream.Builder、DoubleStream.Builder,开发者可以通过这些 Builder 来创建对应的流;
三、和list不同,Stream代表的是任意Java对象的序列,且stream输出的元素可能并没有预先存储在内存中,而是实时计算出来的;它可以“存储”有限个或无限个元素
四、比如我们想表示一个全体自然数的集合,使用list是不可能写出来的,因为自然数是的,不管内存多大也没法放到list中,但是使用Sream就可以
16.1.1 Stream的特点
①Stream自己不会存储数据
②Stream不会改变源对象;相反,它们会返回一个持有结果的新Stream对象
③Stream操作时延迟执行的;这就意味着它们等到有结果时候才会执行
16.1.2 使用Stream的步骤
①创建Stream:一个数据源(例如:set 、list),获取一个流
②中间操作:一个中间操作连接,对数据源的数据进行处理
③终止操作:一个终止操作,执行中间操作连,产生结果
//创建方式一:通过集合
//对于Collection接口(List 、Set、Queue等)直接调用Stream()方法可以获取Stream
List<String> list = new ArrayList<>();
Stream<String> stringStream = list.stream();//返回一个顺序流
Stream<String> parallelStream = list.parallelStream();//返回一个并行流(可多线程)
//创建方式二:通过数组
//把数组变成Stream使用Arrays.stream()方法
Stream<String> stream1 = Arrays.stream(new String[]{"CBB", "YJJ", "CB", "CJJ"});
//创建方式三:Stream.of()静态方法直接手动生成一个Stream
Stream<String> stream = Stream.of("A", "B", "C", "D");
//创建无限流
//迭代
//遍历10个奇数
Stream.iterate(1,t->t+2).limit(10).forEach(System.out::println);
//生成
//生成10个随机数
Stream.generate(Math::random).limit(10).forEach(System.out::println);
16.1.3 Stream常用的两类方法
一、关于流的方法还有如下两个特征
①有状态的方法:会给流增加一些新的属性,如元素唯一性、元素最大数量、保证元素以排序的方式被处理等;有状态的方法往往需要更大的性能开销
②短路方法:短路方法可以尽早结束对流的操作,不必检查所有的元素
二、Stream 提供了大量的方法进行聚集操作,这些方法既可以是“中间的”(intermediate),也可以是 “末端的”(terminal)
**1.中间方法:**中间操作允许流保持打开状态,并允许直接调用后续方法;下面程序中的 map() 方法就是中间方法,中间方法的返回值是另外一个流
Stream 常用的中间方法 | 说明 |
---|---|
filter(Predicate predicate) | 过滤 Stream 中所有不符合 predicate 的元素 |
mapToXxx(ToXxxFunction mapper) | 使用 ToXxxFunction 对流中的元素执行一对一转换,该方法返回的新流中包含了 ToXxxFunction 转换生成的所有元素 |
peek(Consumer action) | 依次对每个元素执行一些操作,该方法返回的流与原有流包含相同的元素;该方法主要用于调试 |
distinct() | 该方法用于排序流中所有重复的元素(判断元素重复的标准是使用 equals() 比较返回 true);这是一个有状态的方法 |
sorted() | 该方法用于保证流中的元素在后续的访问中处于有序状态;这是一个有状态的方法 |
limit(long maxSize) | 该方法用于保证对该流的后续访问中最大允许访问的元素个数;这是一个有状态的、短路方法 |
/*常用中间方法案例总结*/
import java.util.*;
import java.util.stream.Collectors;
public class StreamDemo01 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
//map
List<Integer> collect = list.stream().map(n -> n * 2).collect(Collectors.toCollection(ArrayList::new));
collect.forEach(System.out::println);
//filer过滤
List<Integer> list1 = list.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());
list1.forEach(System.out::println);
//distinct去重
List<Integer> list2 = list.stream().distinct().collect(Collectors.toList());
list2.forEach(System.out::println);
//skip跳过
List<Integer> list3 = list.stream().skip(3).collect(Collectors.toList());
list3.forEach(System.out::println);
//limit截取
Set<Integer> set = list.stream().limit(3).collect(Collectors.toSet());
set.forEach(System.out::println);
//skip and limit组合使用
List<Integer> list4 = list.stream().skip(3).limit(5).collect(Collectors.toList());
list4.forEach(System.out::println);
}
}
**2.末端方法:**末端方法是对流的最终操作;当对某个 Stream 执行末端方法后,该流将会被“消耗”且不再可用;上面程序中的 sum()、count()、average() 等方法都是末端方法
末端方法 | 说明 |
---|---|
forEach(Consumer action) | 遍历流中所有元素,对每个元素执行action |
toArray() | 将流中所有元素转换为一个数组 |
reduce() | 该方法有三个重载的版本,都用于通过某种操作来合并流中的元素 |
min() | 返回流中所有元素的最小值 |
max() | 返回流中所有元素的最大值 |
count() | 返回流中所有元素的数量 |
anyMatch(Predicate predicate) | 判断流中是否至少包含一个元素符合 Predicate 条件 |
allMatch(Predicate predicate) | 判断流中是否每个元素都符合 Predicate 条件 |
noneMatch(Predicate predicate) | 判断流中是否所有元素都不符合 Predicate 条件 |
findFirst() | 返回流中的第一个元素 |
findAny() | 返回流中的任意一个元素 |
/*常用末端方法案例总结*/
public class IntStreamTest {
public static void main(String[] args) {
IntStream is = IntStream.builder().add(20).add(13).add(-2).add(18).build();
// 下面调用聚集方法的代码每次只能执行一行
System.out.println("所有元素的最大值:" + is.max().getAsInt());
System.out.println("所有元素的最小值:" + is.min().getAsInt());
System.out.println("所有元素的总和:" + is.sum());
System.out.println("所有元素的总数:" + is.count());
System.out.println("所有元素的平均值:" + is.average());
System.out.println("所有元素的平方是否都大于20: " + is.allMatch(ele -> ele * ele > 20));
System.out.println("是否包含任何元素的平方大于20 : " + is.anyMatch(ele -> ele * ele > 20));
//将is映射成一个新Stream,新Stream的每个元素是原Stream元素的2倍+1
IntStream newIs = is.map(ele -> ele * 2 + 1);
//使用方法引用的方式来遍历集合元素
newIs.forEach(System.out::println); // 输岀 41 27 -3 37
}
}
16.1.4 流式API操作集合
除此之外,Java 8 允许使用流式 API 来操作集合,Collection 接口提供了一个 stream() 默认方法,该方法可返回该集合对应的流,接下来即可通过流式 API 来操作集合元素;由于 Stream 可以对集合元素进行整体的聚集操作,因此 Stream 极大地丰富了集合的功能
/**
* 从上面代码第11~20行可以看出,程序只要调用Collection的stream() 方法即可返回该集合对应的Stream,接下来就可通过Stream提供的方法对所有集合元素进行处理,这样大大地简化了集合编程的代码,这也是Stream编程带来的优势
* 上面程序中第18行代码先调用Collection对象的stream()方法将集合转换为Stream对象,然后调用Stream对象的mapToInt()方法将其转换为IntStream这个 mapToInt;方法就是一个中间方法,因此程序可继续调用IntStream的forEach()方法来遍历流中的元素
*/
public class CollectionStream {
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add(new String("XiaoGuo01"));
objs.add(new String("XiaoGuo02"));
objs.add(new String("XiaoGuo03"));
objs.add(new String("XiaoGuo04"));
objs.add(new String("XiaoGuo05"));
// 统计集合中出现“XiaoGuo”字符串的数量
System.out.println(objs.stream().filter(ele -> ((String) ele).contains("XiaoGuo")).count()); // 输出5
// 统计集合中出现“XiaoGuo01”字符串的数量
System.out.println(objs.stream().filter(ele -> ((String) ele).contains("XiaoGuo01")).count()); // 输出1
// 统计集合中出现字符串长度大于12的数量
System.out.println(objs.stream().filter(ele -> ((String) ele).length() > 12).count()); // 输出1
// 先调用Collection对象的stream()方法将集合转换为Stream
// 再调用Stream的mapToInt()方法获取原有的Stream对应的IntStream
// 调用forEach()方法遍历IntStream中每个元素
objs.stream().mapToInt(ele -> ((String) ele).length()).forEach(System.out::println);// 输出9 9 9 9 9
}
}
16.2 Lamdba表达式
一、Lambda表达式
Lambda是一个匿名函数,我们可以将Lambda表达式理解为一段可以传递的代码(将代码像数据一样传递),使用它可以写出简洁、灵活的代码;作为一种更紧凑的代码风格,使java语言表达能力得到提升
二、从匿名类到Lambda转换
/*
* Logger日志记录器
* 属于log4j下的方法,Logger log = logger.getlogger();
* 打印输出(XXXXX.class)这个类的日志信息到控制台
* Logger.setLevel(Level.ALL)设置Logger的输出级别为ALL
*/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Demo01{
//通过LoggerFactory.getLogger创建Logger实例
private static Logger log = LoggerFactory.getLogger(Demo01.class);
public static void main(String[] args) {
Runnable t1 = new Runnable(){
@Override
public void run(){
log.info("没有使用Lambda表达式:不简洁");
}
};
//匿名类到Lambda转换
Runnable t2 = () -> log.info("使用Lambda表达式:简洁");
t1.run();
t2.run();
}
}
//run result
五月 05, 2023 11:51:50 上午 service.Demo01$1 run 信息: 没有使用Lambda表达式:不简洁
五月 05, 2023 11:51:50 上午 service.Demo01 lambda$main$0 信息: 使用Lambda表达式:简洁
三、Lambda表达式语法
Lambda表达式在java语言中引入了一种新的语法元素和操作,这种操作符号为“->”,将Lambda表达式分割为两部分; 左边:指Lambda表达式的所有参数;右边:指Lambda体,即表示Lambda表达式需要执行的功能
①无参数、无返回值,只需要一个Lambda体
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Demo02 {
private static Logger log = LoggerFactory.getLogger(Demo02.class);
public static void main(String[] args) {
Runnable t1 = ()-> log.info("Lambda表达式:简洁");
t1.run();
}
}
//run result
五月 05, 2023 11:54:22 上午 service.Demo01 lambda$main$0 信息: Lambda表达式:简洁
Process finished with exit code 0
②lambda有一个参数、无返回值
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Consumer;
public class Demo03 {
private static Logger log = LoggerFactory.getLogger(Demo03.class);
public static void main(String[] args) {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
log.info(s);
}
};
consumer.accept("测试输出");
Consumer<String> consumer1 = (s) -> log.info(s);
consumer1.accept("输出成功");
}
}
//run result
23:03:08.992 [main] INFO com.guo.test.JAVA8Features.Demo03 - 测试输出
23:03:09.142 [main] INFO com.guo.test.JAVA8Features.Demo03 - 输出成功
Process finished with exit code 0
③Lambda只有一个参数时,可以省略()
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Consumer;
public class Demo04 {
private static Logger log = LoggerFactory.getLogger(Demo04.class);
public static void main(String[] args) {
Consumer<String> consumer = s -> log.info(s);
consumer.accept("Lambda只有一个参数时,可以省略()");
}
}
//run result
23:08:27.295 [main] INFO com.guo.test.JAVA8Features.Demo04 - Lambda只有一个参数时,可以省略()
Process finished with exit code 0
④Lambda有两个参数时,并且有返回值
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Comparator;
public class Demo05 {
private static Logger log = LoggerFactory.getLogger(Demo05.class);
public static void main(String[] args) {
CompareOldMethod(12,10);
findMaxValue(12,10);
findMinValue(12,10);
}
//没有使用Lambda表达式比较大小
public static void CompareOldMethod(int num1,int num2){
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
log.info("o1:{}",o1);
log.info("o2:{}",o2);
return o1 < o2 ? o2 : o1;
}
};
log.info("OldFindMaxValue:{}",comparator.compare(num1,num2));
}
//使用lambda表达式
public static void findMaxValue(int num1,int num2){
Comparator<Integer> comparatorMax = (o1, o2) ->{
log.info("o1:{}",o1);
log.info("o2:{}",o2);
return (o1<o2)? o2 :(o1);
};
log.info("findMaxValue:{}",(comparatorMax.compare(num1,num2)));
}
public static void findMinValue(int num1,int num2){
Comparator<Integer> comparatorMin = (o1, o2) -> {
log.info("o1:{}",o1);
log.info("o2:{}",o2);
return (o1 < o2) ? o1 : o2;
};
log.info("FindMinValue:{}",comparatorMin.compare(num1,num2));
}
}
//run result
00:17:10.206 [main] INFO com.guo.test.JAVA8Features.Demo05 - o1:12
00:17:10.206 [main] INFO com.guo.test.JAVA8Features.Demo05 - o2:10
00:17:10.206 [main] INFO com.guo.test.JAVA8Features.Demo05 - OldFindMaxValue:12
00:17:10.315 [main] INFO com.guo.test.JAVA8Features.Demo05 - o1:12
00:17:10.315 [main] INFO com.guo.test.JAVA8Features.Demo05 - o2:10
00:17:10.315 [main] INFO com.guo.test.JAVA8Features.Demo05 - findMaxValue:12
00:17:10.315 [main] INFO com.guo.test.JAVA8Features.Demo05 - o1:12
00:17:10.315 [main] INFO com.guo.test.JAVA8Features.Demo05 - o2:10
00:17:10.315 [main] INFO com.guo.test.JAVA8Features.Demo05 - FindMinValue:10
Process finished with exit code 0
⑤当Lambda体只有一条语句的时候,return和{}可以省略掉
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Comparator;
public class Demo05 {
private static Logger log = LoggerFactory.getLogger(Demo05.class);
public static void main(String[] args) {
findMaxValue(12,10);
findMinValue(12,10);
}
//使用lambda表达式
public static void findMaxValue(int num1,int num2){
Comparator<Integer> comparatorMax = (o1, o2) ->{
log.info("o1:{}",o1);
log.info("o2:{}",o2);
return (o1<o2)? o2 :(o1);
};
log.info("findMaxValue:{}",(comparatorMax.compare(num1,num2)));
}
public static void findMinValue(int num1,int num2){
Comparator<Integer> comparatorMin = (o1, o2) -> (o1 < o2) ? o1 : o2;
log.info("FindMinValue:{}",comparatorMin.compare(num1,num2));
}
}
//run result
00:22:31.059 [main] INFO com.guo.test.JAVA8Features.Demo05 - o1:12
00:22:31.075 [main] INFO com.guo.test.JAVA8Features.Demo05 - o2:10
00:22:31.075 [main] INFO com.guo.test.JAVA8Features.Demo05 - findMaxValue:12
00:22:31.075 [main] INFO com.guo.test.JAVA8Features.Demo05 - FindMinValue:10
Process finished with exit code 0
⑥数据类型可以省略,因为编译器可以推断得出,成为“类型推断”
import com.mysql.cj.callback.MysqlCallbackHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
public class Demo07 {
private static Logger log = LoggerFactory.getLogger(Demo07.class);
public static void main(String[] args) {
//数据类型
dateType();
}
public static void dateType(){
Consumer<String> consumer = (String s) -> log.info(s);
consumer.accept("Hello World !");
Consumer<String> consumer1 = (s) -> log.info(s);
consumer1.accept("Hello don't date type !");
}
}
16.3 函数式接口
只包含一个抽象方法的接口,称为函数式接口,并且可以使用Lambda表达式来创建该接口的对象,可以在任意函数式接口上使用@FunctionalInterface注解,来检测它是否是符合函数式接口;同时javac也会包含一条声明,说明这个接口是否符合函数式接口
16.3.1 自定义函数式接口
package com.chen.test.JAVA8Features;
@FunctionalInterface
public interface FunctionDemo1 {
//仅包含一个抽象方法
public void fun();
}
16.3.2 泛型函数式接口
package com.chen.test.JAVA8Features;
@FunctionalInterface
public interface FunctionGeneric<T> {
public void fun(T t);
}
16.3.3 内置函数式接口
在Java.util.function包下,包含Function、Consumer、Supplier、Predicate四种内置函数式接口;
16.3.3.1 Function
/** 函数型接口:有输入参数,也有返回值
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
* @param t the function argument
* @return the function result
*/
R apply(T t);
//其中T表示输入参数,R为返回值
}
//案例
public void functionTest(){
Function function = new Function<String,String>(){
@Override
public String apply(String s) {
return s;
}
};
log.info("函数型接口 :{}",function.apply("没有使用Lambda表达式"));
Function function = s->s;
log.info("函数型接口:{}",function.apply("Function Demo"));
}
16.3.3.2 Consumer
/** 消费型接口:有入参,没有返回值
* @param <T> the type of the input to the operation
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument
* @param t the input argument
*/
void accept(T t);
}
//案例
public void consumerTest(){
//非Lambda表达式
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
log.info(s);
}
};
consumer.accept("消费型函数:没有使用Lambda表达式");
//使用Lambda表达式
Consumer<String> consumer = s -> log.info(s);
consumer.accept("消费型函数:Consumer Demo");
}
16.3.3.3 Supplier
/** 供给型接口:没有输入参数,有返回值
* @param <T> the type of results supplied by this supplier
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result
* @return a result
*/
T get();
}
//案例
public void supplierTest(){
//非Lambda表达式
Supplier supplier = new Supplier<String>(){
@Override
public String get() {
return "供给型接口:没有使用Lambda表达式";
}
};
log.info(String.valueOf(supplier.get()));
Supplier supplier = () -> "供给型接口:Supplier Demo";
log.info(String.valueOf(supplier.get()));
}
16.3.3.4 Predicate
/** 断言型接口:既有输入参数也有返回值,返回类型是boolean类型
* @param <T> the type of the input to the predicate
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}
//案例
public void predicateTest() {
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String s) {
return s.equals("Predicate Demo");
}
};
log.info("断言型接口:{}",predicate.test("没有使用Lambda表达式"));
Predicate<String> predicate = s -> s.equals("Predicate Demo");
log.info("断言型接口:{}",predicate.test("Predicate Demo"));
}
16.3.4 小结
Java内置四种函数式接口可以使用Lambda表达式
import com.google.common.base.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class FunDemo01 {
private static Logger log = LoggerFactory.getLogger(FunDemo01.class);
public static void main(String[] args) {
FunDemo01 demo01 = new FunDemo01();
demo01.functionTest();
demo01.consumerTest();
demo01.supplierTest();
demo01.predicateTest();
}
****************************************************************************************
public void functionTest(){
//非Lambda表达式
Function function = new Function<String,String>(){
@Override
public String apply(String s) {
return s;
}
};
log.info("函数型接口 :{}",function.apply("没有使用Lambda表达式"));
Function function = s -> s;
log.info("函数型接口:{}",function.apply("Function Demo"));
}
******************************************************************************************
public void consumerTest(){
//非Lambda表达式
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
log.info(s);
}
};
consumer.accept("消费型函数:没有使用Lambda表达式");
//使用Lambda表达式
Consumer<String> consumer = s -> log.info(s);
consumer.accept("消费型函数:Consumer Demo");
}
****************************************************************************************
public void supplierTest(){
//非Lambda表达式
Supplier supplier = new Supplier<String>(){
@Override
public String get() {
return "供给型接口:没有使用Lambda表达式";
}
};
log.info(String.valueOf(supplier.get()));
Supplier supplier = () -> "供给型接口:Supplier Demo";
log.info(String.valueOf(supplier.get()));
}
public void predicateTest() {
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String s) {
return s.equals("Predicate Demo");
}
};
log.info("断言型接口:{}",predicate.test("没有使用Lambda表达式"));
Predicate<String> predicate = s -> s.equals("Predicate Demo");
log.info("断言型接口:{}",predicate.test("Predicate Demo"));
}
}
16.4 方法引用和构造器引用
16.4.1 方法引用
一、当要传递给Lambda体的操作已经有实现方法,可以直接使用方法引用(实现抽象方法的列表,必须要和方法引用的方法参数列表一致)
二、方法引用:使用操作符::将方法名和(类或者对象)分割开来
//类::实例方法
//类::静态方法
//对象::实例方法
public class MethodRefDemo {
public static void main(String[] args) {
FunctionGeneric<String> strName = s -> System.out.println(s);
strName.fun("Lambda表达式没有使用方法引用");
//方法引用
FunctionGeneric<String> strName2 = System.out::println;
strName2.fun("使用方法引用");
}
}
16.4.2 构造器引用
一、本质上:构造器引用和方法引用相识,只是使用了一个new方法
二、使用说明:函数式接口参数列表和构造器参数列表要一致,该接口返回值类型也是构造器返回值类型
三、格式:ClassName :: new
import java.util.function.Function;
public class MethodRefDemo {
public static void main(String[] args) {
//构造器引用
Function<String, Integer> fun1 = (num) -> new Integer(num);
Function<String, Integer> fun2 = Integer::new;
//数组引用
Function<Integer,Integer[]> fun3 = (num) ->new Integer[num];
Function<Integer,Integer[]> fun4 = Integer[]::new;
}
}
16.5 接口中默认方法和静态方法
一、默认方法
Java8中允许接口中包含具体实现的方法体,该方法是默认方法,需要使用default关键字修饰
二、静态方法
Java8中允许接口定义静态方法,使用static关键字修饰
public interface DefaultMethod{
default Integer addMethod(int a, int b){
System.out.println("默认方法");
}
public void resultMethod(){
System.out.println("静态方法");
return a + b;
}
}
16.6 新日期接口
一、Java 8新增了LocalDate,LocalTime,LocalDateTime接口,同时Date类被弃用;
二、Date中月份从0开始,一月是0,十二月是11,使开发中经常出现错误;
三、另外simpledateformat方法中format和parse方法都是线程不安全的;
四、Java 8新增的LocalDate,LocalTime,LocalDateTime接口而这些类使用了final来修饰,使得这些类被实例化后是不可变,所以这些类都是线程安全的;
16.6.1 简介
①LocalDate 只包含日期年月日,无法包含时间
②LocalTime 只包含时间时分秒,无法包含日期
③LocalDateTime 同时包含日期和时间
16.6.2 LocalDate API
LocalDate类的实例是一个不可变对象,这个类是不可变的和线程安全的;只提供了简单的日期,并不含当天的时间信息;
常用方法 | 方法描述 |
---|---|
now | 根据当前时间创建LocalDate对象 |
of | 根据指定年月日创建LocalDate对象 |
getYear | 获得年份 |
getMonthValue | 获得月份 |
getMonth | 获得月份枚举值 |
getDayOfMonth | 获得月份天数(1-31) |
getDayOfWeek | 获得星期几 |
getDayOfYear | 获得年份中的第几天(1-366) |
lengthOfYear | 获得当年总天数 |
lengthOfMonth | 获得当月总天数 |
toEpochDay | 与时间纪元(1970年1月1日)相差的天数 |
plusDays | 加天 |
plusWeeks | 加周 |
plusMonths | 加年 |
minusDays | 减天 |
minusWeeks | 减周 |
minusYears | 减年 |
withYear | 替换年份 |
withDayOfMonth | 返回MonthofDay副本 |
withDayOfYear | 返回YearofDay副本 |
isBefore | 是否日期在之前 |
isAfter | 是否日期在之后 |
isEqual | 是否是当前日期 |
isleapYear | 是否是闰年 |
/*获取*/
//获取当前日期
System.out.println(LocalDate.now());
//获取指定日期
System.out.println(LocalDate.of(2023, 5, 5));
//获取日期的年份
System.out.println(LocalDate.now().getYear());
//获取日期的月份
System.out.println(LocalDate.now().getMonthValue());
//获取日期的日子
System.out.println(LocalDate.now().getDayOfMonth());
//获取日期的星期
System.out.println(LocalDate.now().getDayOfWeek());
//当天所在这一年的第几天
System.out.println(LocalDate.now().getDayOfYear());
//获取当年天数
System.out.println(LocalDate.now().lengthOfYear());
//获取当月天数
System.out.println(LocalDate.now().lengthOfMonth());
//与时间纪元(1970年1月1日)相差的天数,负数表示在时间纪元之前多少天
System.out.println(LocalDate.now().toEpochDay());
******************************************************************************************
/*运算*/
//加一天
System.out.println("加1天:"+LocalDate.now().plusDays(1));
//加一周
System.out.println("加1周:"+LocalDate.now().plusWeeks(1));
//加一月
System.out.println("加1月:"+LocalDate.now().plusMonths(1));
//加一年
System.out.println("加1年:"+LocalDate.now().plusYears(1));
//减一天
System.out.println("减1天:"+LocalDate.now().minusDays(1));
//减一周
System.out.println("减1周:"+LocalDate.now().minusWeeks(1));
//减一月
System.out.println("减1月:"+LocalDate.now().minusMonths(1));
//减一年
System.out.println("减1年:"+LocalDate.now().minusYears(1));
******************************************************************************************
/*替换*/
//替换年份
System.out.println("替换年份为1:"+LocalDate.now().withYear(1));
//替换月份
System.out.println("替换月份为1:"+LocalDate.now().withMonth(1));
//替换日子
System.out.println("替换日期为1:"+LocalDate.now().withDayOfMonth(1));
//替换天数
System.out.println("替换天数为1:"+LocalDate.now().withDayOfYear(1));
******************************************************************************************
/*比较*/
//是否在当天之前
System.out.println("是否在当天之前:"+LocalDate.now().minusDays(1).isBefore(LocalDate.now()));
//是否在当天之后
System.out.println("是否在当天之后:"+LocalDate.now().plusDays(1).isAfter(LocalDate.now()));
//是否在当天
System.out.println("是否在当天:"+LocalDate.now().isEqual(LocalDate.now()));
//是否是闰年
System.out.println("今年是否是闰年:"+LocalDate.now().isLeapYear());
16.6.3 LocalTime API
LocalTime是一个不可变的时间对象,代表一个时间,格式为时–分–秒,时间表示为纳秒精度;这个类是不可变的、线程安全的
常用方法 | 方法描述 |
---|---|
static LocalTime now() | 获取默认时区的当前时间 |
static LocalTime now(ZoneId zone) | 获取指定时区的当前时间 |
static LocalTime now(Clock clock) | 从指定时钟获取当前时间 |
of | 根据指定的时、分、秒获取LocalTime实例 |
getHour | 获取小时字段 |
getMinute | 获取分钟字段 |
getSecond | 获取秒字段 |
getNano | 获取纳秒字段 |
plusHours | 增加小时数 |
plusMinutes | 增加分钟数 |
plusSeconds | 增加秒数 |
plusNanos | 增加纳秒数 |
minusHours | 减少小时数 |
minusMinutes | 减少分钟数 |
minusSeconds | 减少秒数 |
minusNanos | 减少纳秒数 |
compareTo | 时间与另一个时间比较 |
isAfter | 检查时间是否在指定时间之后 |
isBefore | 检查时间是否在指定时间之前 |
/*获取*/
//获取默认时区的当前时间
System.out.println(LocalTime.now());
//获取指定时区的当前时间
System.out.println(LocalTime.now(ZoneId.of("Asia/Shanghai")));
//从指定时钟获取当前时间
System.out.println(LocalTime.now(Clock.systemDefaultZone()));
//指定获取时分秒
System.out.println(LocalTime.of(12, 30, 30));
//指定获取时分
System.out.println(LocalTime.of(12, 30));
//指定获取时分秒纳秒
System.out.println(LocalTime.of(12, 30, 30, 123));
//获取小时字段
System.out.println("时: " + LocalTime.now().getHour());
//获取分钟字段
System.out.println("时: " + LocalTime.now().getMinute());
//获取秒字段
System.out.println("时: " + LocalTime.now().getSecond());
//获取纳秒字段
System.out.println("时: " + LocalTime.now().getNano());
******************************************************************************************
/*计算*/
//增加一小时
System.out.println("增加1小时: " + LocalTime.now().plusHours(1));
//增加三十分钟
System.out.println("增加30分钟: " + LocalTime.now().plusMinutes(30));
//增加三十秒
System.out.println("增加30秒: " + LocalTime.now().plusSeconds(30));
//增加一万纳秒
System.out.println("增加10000纳秒:" + LocalTime.now().plusNanos(10000));
//减少一小时
System.out.println("减少1小时: " + LocalTime.now().minusHours(1));
//减少三十分钟
System.out.println("减少30分钟: " + LocalTime.now().minusMinutes(30));
//减少三十秒
System.out.println("减少30秒: " + LocalTime.now().minusSeconds(30));
//减少一万纳秒
System.out.println("减少10000纳秒:" + LocalTime.now().minusNanos(10000));
******************************************************************************************
/*比较*/
//时间与另一个时间比较0(相等)正数(大)负数(小)
System.out.println(LocalTime.now().compareTo(LocalTime.now()));
//检查时间是否在指定时间之后
System.out.println(LocalTime.now().isAfter(LocalTime.now()));
//检查时间是否在指定时间之前
System.out.println(LocalTime.now().isBefore(LocalTime.now()));
16.6.4 LocalDateTime API
LocalDateTime是一个不可变的日期时间对象,代表日期时间,格式为 年–月–日–时–分–秒;这个类是不可变的、线程安全的
常用方法 | 方法描述 |
---|---|
static LocalDateTime now( ) | 获取默认时区的当前日期时间 |
static LocalDateTime now(Clock clock) | 从指定时钟获取当前日期时间 |
static LocalDateTime now(ZoneId zone) | 获取指定时区的当前日期时间 |
static LocalDateTime of(LocalDate date, LocalTime time) | 根据日期和时间对象获取LocalDateTime实例 |
static LocalDateTime of(int year, Month month, int dayOfMonth,int hour, int minute, int second) | 根据指定的年月日时分秒获取LocalDateTime 实例 |
getYear | 获取年份 |
getMonth | 使用月份枚举类获取月份 |
getDayOfMonth | 获取日期在该月是第几天 |
getDayOfWeek | 获取日期是星期几 |
getDayOfYear | 获取日期在该年是第几天 |
getHour | 获取小时 |
getMinute | 获取分钟 |
getSecond | 获取秒 |
getNano | 获取纳秒 |
plusYears | 增加年 |
plusMonths | 增加月 |
plusWeeks | 增加周 |
plusDays | 增加天 |
plusHours | 增加小时 |
plusMinutes | 增加分 |
plusSeconds | 增加秒 |
plusNanos | 增加纳秒 |
minusYears | 减少年 |
minusMonths | 减少月 |
meminusWeeks | 减少周 |
minusDays | 减少天 |
minusHours | 减少小时 |
minusMinutes | 减少分 |
minusNanos | 减少纳秒 |
isEqual | 判断日期时间是否相等 |
minusSeconds | 减少秒 |
isBefore | 检查是否在指定日期时间之前 |
isAfter | 检查是否在指定日期时间之后 |
toLocalDate | 获取日期部分 |
/*获取*/
//获取默认时区的当前日期时间
System.out.println(LocalDateTime.now());
//获取指定时区的当前日期时间
System.out.println(LocalDateTime.now(ZoneId.of("Asia/Shanghai")));
//从指定时钟获取当前日期时间
System.out.println(LocalDateTime.now(Clock.systemDefaultZone()));
//根据日期和时间对象获取LocalDateTime实例
System.out.println(LocalDateTime.of(LocalDate.now(), LocalTime.now()));
//根据指定的年、月、日、时、分、秒获取LocalDateTime实例
System.out.println(LocalDateTime.of(2019, 12, 7, 21, 48, 50));
//获取年份
System.out.println("年 : " + LocalDateTime.now().getYear());
//使用月份枚举类获取月份
System.out.println("月(英文) : " + LocalDateTime.now().getMonth());
//使用月份数字类获取月份
System.out.println(" 月(数字英文): " + LocalDateTime.now().getMonth().getValue());
//获取日期在该月是第几天
System.out.println("天 : " + LocalDateTime.now().getDayOfMonth());
//获取日期是星期几(英文)
System.out.println("星期几(英文) : " + LocalDateTime.now().getDayOfWeek());
//获取日期是星期几(数字英文)
System.out.println("星期几(数字英文) : " + LocalDateTime.now().getDayOfWeek().getValue());
//获取日期在该年是第几天
System.out.println("本年的第几天 : " + LocalDateTime.now().getDayOfYear());
//获取小时
System.out.println("时: " + LocalDateTime.now().getHour());
//获取分钟
System.out.println("分: " + LocalDateTime.now().getMinute());
//获取秒
System.out.println("秒: " + LocalDateTime.now().getSecond());
//获取纳秒
System.out.println("纳秒: " + LocalDateTime.now().getNano());
//获取日期部分
System.out.println(LocalDateTime.now().toLocalDate());
//获取时间部分
System.out.println(LocalDateTime.now().toLocalTime()); ******************************************************************************************
/*计算*/
//增加天数
System.out.println("增加天数 : " + LocalDateTime.now().plusDays(1));
//增加周数
System.out.println("增加周数 : " + LocalDateTime.now().plusWeeks(1));
//增加月数
System.out.println("增加月数 : " + LocalDateTime.now().plusMonths(1));
//增加年数
System.out.println("增加年数 : " + LocalDateTime.now().plusYears(1));
//减少天数
System.out.println("减少天数 : " + LocalDateTime.now().minusDays(1));
//减少月数
System.out.println("减少月数 : " + LocalDateTime.now().minusMonths(1));
//减少周数
System.out.println("减少周数 : " + LocalDateTime.now().minusWeeks(1));
//减少年数
System.out.println("减少年数 : " + LocalDateTime.now().minusYears(1));
//增加小时
System.out.println("增加1小时: " + LocalDateTime.now().plusHours(1));
//增加分钟
System.out.println("增加30分钟: " + LocalDateTime.now().plusMinutes(30));
//增加秒数
System.out.println("增加30秒: " + LocalDateTime.now().plusSeconds(30));
//增加纳秒
System.out.println("增加10000纳秒:" + LocalDateTime.now().plusNanos(10000));
//减少小时
System.out.println("减少1小时:" + LocalDateTime.now().minusHours(1));
//减少分钟
System.out.println("减少30分钟:" + LocalDateTime.now().minusMinutes(30));
//减少秒数
System.out.println("减少30秒: " + LocalDateTime.now().minusSeconds(30));
//减少纳秒
System.out.println("减少10000纳秒:" + LocalDateTime.now().minusNanos(10000)); ******************************************************************************************
/*比较*/
//判断日期时间是否相等
System.out.println(LocalDateTime.now().isEqual(LocalDateTime.now()));
//检查是否在指定日期时间之前
System.out.println(LocalDateTime.now().isBefore(LocalDateTime.now()));
//检查是否在指定日期时间之后
System.out.println(LocalDateTime.now().isAfter(LocalDateTime.now()));
16.6.5 LocalDateTime 与 LocalDate 之间的转换
//LocalDateTime转化为LocalDate
LocalDateTime localDateTime = LocalDateTime.now();
LocalDate localDate = localDateTime.toLocalDate();
System.out.println(localDate);
//LocalDate转化为LocalDateTime
LocalDate localDate = LocalDate.now();
LocalDateTime localDateTime1 = localDate.atStartOfDay();
LocalDateTime localDateTime2 = localDate.atTime(8,20,33);
LocalDateTime localDateTime3 = localDate.atTime(LocalTime.now());
16.6.6 LocalDate与 Date之间的转换
//localDate转化为Date
LocalDate localDate = LocalDate.now();
ZoneId zoneId = ZoneId.systemDefault();
Date date = Date.from(localDate.atStartOfDay().atZone(zoneId).toInstant());
System.out.println(date);
//Date转化为localDate
Date date1 = new Date();
ZoneId zoneId1 = ZoneId.systemDefault();
LocalDate localDate1 = date1.toInstant().atZone(zoneId1).toLocalDate();
System.out.println(localDate1);
16.6.7 LocalDate 与 String 之间的转换
//从文本字符串获取LocalDate实例
LocalDate localdate = LocalDate.parse("2023-06-03");
System.out.println(localdate);
//使用特定格式化形式从文本字符串获取LocalDate实例
String str = "2023-06-03";
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(str, fmt);
System.out.println(date);
//使用特定格式化形式将LocalDate转为字符串
LocalDate today = LocalDate.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String dateStr = today.format(fmt);
System.out.println(dateStr);
16.6.8 LocalDateTime与 String 之间的转换
//字符串转为LocalDateTime
LocalDateTime ldt2 = LocalDateTime.parse("2022-11-07T21:20:06.303995200");
System.out.println(ldt2);
//使用特定格式化将字符串转为LocalDateTime
DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt3 = LocalDateTime.parse("2022-11-07 21:20:06", df1);
System.out.println(ldt3);
//LocalDateTime转为字符串
LocalDateTime today = LocalDateTime.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String dateStr = today.format(fmt);
System.out.println(dateStr);
16.7 Optional类
传统的写代码方式经常会遇到NullPointerException,这就需要我们在代码中经常判空;而判空的写法又会显得很累赘,就可以用到Optional来简化代码
16.7.1 Optional简介
① Optional是一个容器对象,它可能包含或不包含非空值
② 如果存在值,isPresent()将返回true,而get()则返回该值
③ Optional提供了取决于所包含值的存在与否的其他方法,例如orElse()和 ifPresent()
④ 对Optional实例使用身份敏感操作(包括引用相等(==)、身份哈希码或同步)可能会产生不可预测的结果,应避免
//JDK 8以前进行对象校验
public String getCity(User user){
if(user != null){
Address address = user.getAddress();
if(address != null){
return address.getCity();
}else{
return "对象校验";
}
}else{
return "对象校验";
}
}
//JDK 8以后进行对象校验
public String getCity(User user){
return Optional.ofNullable(user)
.map(u -> u.getAddress())
.map(address -> address.getCity())
.orElse("对象校验");
}
16.7.2 构建Optional
构建一个Optional对象;方法有:①empty()②of()③ofNullable()
//返回一个空的Optional实例。 此Optional没有值。
//类型参数:<T> –不存在的值的类型
//返回值:一个空的Optional
//api注意:尽管这样做可能很诱人,但应通过将==与Optional.empty()返回的实例进行比较来避免测试对象是否为空。
// 不能保证它是一个单例。
// 而是使用isPresent()
Optional.empty();// 用来构造一个空的 Optional;
//返回一个Optional描述给定的非null值
//参数:value –要描述的值,必须为非null
//类型参数:<T> –值的类型
//返回值:存在值的Optional
Optional.of(T value);// value需要非null,null会抛异常;
//返回一个描述给定值的Optional ,如果不为null ,则返回一个空的Optional
//参数:值–描述的可能为null值
//类型参数:<T> –值的类型
//返回值:一个Optional与如果指定值是非当前值null ,否则一个空Optional
Optional.ofNullable(T value);//value为null的话返回-Optional.empty();
16.7.3 Optional常用方法
注意:Optional 只有null时表示为空值,其他任何情况都不表示空值,如空字符串打印结果是空字符串
一、isPresent(): 持有非空值,返回true,否则false;
Optional optional = Optional.ofNullable(null);
//构建Optional对象
Optional optional1 = Optional.ofNullable();
Optional optional1 = Optional.of("");
Optional optional2 = Optional.empty();
//进行判断
System.out.println(optional.isPresent());//false
System.out.println(optional1.isPresent());//true
System.out.println(optional2.isPresent());//false
二、ifPresent(): 如果 Optional 中有值,则对该值调用consumer.accept,否则什么也不做
三、orElse(): 参数是一个值,如果 Optional 中有值则将其返回,否则返回 orElse 方法传入的参数
//Optional中有值
Optional optional = Optional.ofNullable("XiaoGuo");
System.out.println(optional.orElse("GuoJingPeng"));//XiaoGuo
//Optional中有没有值
Optional optional = Optional.ofNullable(null);
System.out.println(optional.orElse("XiaoGuo"));//XiaoGuo
四、orElseGet(): 传入的参数为一个Supplier接口的实现
五、orElseThrow(): 没有值的时候会抛出异常,抛出的异常由传入的 exceptionSupplier 提供
return Optional.ofNullable(iNewsMapper.selectNewsWithNewsCategoryById(id))
.orElseThrow(() -> new BaseException(DataSourceResponseEnum.SELECT_ERROR));
六、map(): 为空返回 Optional.empty;否则返回一个新的
七、Optional(): 函数 mapper 在以 value 作为输入时的输出值;可以多次使用map操作
Optional<String> username = Optional.ofNullable(getUserById(id))
.map(user -> user.getUsername())
.map(name -> name.replace('_', ' '));
System.out.println("Username is: " + username.orElse("Unknown"))
八、flatMap(): map 方法参数中的函数 mapper 输出的是值,然后 map 方法会使用 Optional.ofNullable 将其包装为 Optional;而 flatMap 要求参数中的函 数 mapper 输出的就是 Optional
Optional<String> username = Optional.ofNullable(getUserById(id))
.flatMap(user -> Optional.of(user.getUsername()))
.flatMap(name -> Optional.of(name.toLowerCase()));
System.out.println("Username is: " + username.orElse("Unknown"));
九、filter(): 接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty
| 获取日期部分 |
/*获取*/
//获取默认时区的当前日期时间
System.out.println(LocalDateTime.now());
//获取指定时区的当前日期时间
System.out.println(LocalDateTime.now(ZoneId.of("Asia/Shanghai")));
//从指定时钟获取当前日期时间
System.out.println(LocalDateTime.now(Clock.systemDefaultZone()));
//根据日期和时间对象获取LocalDateTime实例
System.out.println(LocalDateTime.of(LocalDate.now(), LocalTime.now()));
//根据指定的年、月、日、时、分、秒获取LocalDateTime实例
System.out.println(LocalDateTime.of(2019, 12, 7, 21, 48, 50));
//获取年份
System.out.println("年 : " + LocalDateTime.now().getYear());
//使用月份枚举类获取月份
System.out.println("月(英文) : " + LocalDateTime.now().getMonth());
//使用月份数字类获取月份
System.out.println(" 月(数字英文): " + LocalDateTime.now().getMonth().getValue());
//获取日期在该月是第几天
System.out.println("天 : " + LocalDateTime.now().getDayOfMonth());
//获取日期是星期几(英文)
System.out.println("星期几(英文) : " + LocalDateTime.now().getDayOfWeek());
//获取日期是星期几(数字英文)
System.out.println("星期几(数字英文) : " + LocalDateTime.now().getDayOfWeek().getValue());
//获取日期在该年是第几天
System.out.println("本年的第几天 : " + LocalDateTime.now().getDayOfYear());
//获取小时
System.out.println("时: " + LocalDateTime.now().getHour());
//获取分钟
System.out.println("分: " + LocalDateTime.now().getMinute());
//获取秒
System.out.println("秒: " + LocalDateTime.now().getSecond());
//获取纳秒
System.out.println("纳秒: " + LocalDateTime.now().getNano());
//获取日期部分
System.out.println(LocalDateTime.now().toLocalDate());
//获取时间部分
System.out.println(LocalDateTime.now().toLocalTime()); ******************************************************************************************
/*计算*/
//增加天数
System.out.println("增加天数 : " + LocalDateTime.now().plusDays(1));
//增加周数
System.out.println("增加周数 : " + LocalDateTime.now().plusWeeks(1));
//增加月数
System.out.println("增加月数 : " + LocalDateTime.now().plusMonths(1));
//增加年数
System.out.println("增加年数 : " + LocalDateTime.now().plusYears(1));
//减少天数
System.out.println("减少天数 : " + LocalDateTime.now().minusDays(1));
//减少月数
System.out.println("减少月数 : " + LocalDateTime.now().minusMonths(1));
//减少周数
System.out.println("减少周数 : " + LocalDateTime.now().minusWeeks(1));
//减少年数
System.out.println("减少年数 : " + LocalDateTime.now().minusYears(1));
//增加小时
System.out.println("增加1小时: " + LocalDateTime.now().plusHours(1));
//增加分钟
System.out.println("增加30分钟: " + LocalDateTime.now().plusMinutes(30));
//增加秒数
System.out.println("增加30秒: " + LocalDateTime.now().plusSeconds(30));
//增加纳秒
System.out.println("增加10000纳秒:" + LocalDateTime.now().plusNanos(10000));
//减少小时
System.out.println("减少1小时:" + LocalDateTime.now().minusHours(1));
//减少分钟
System.out.println("减少30分钟:" + LocalDateTime.now().minusMinutes(30));
//减少秒数
System.out.println("减少30秒: " + LocalDateTime.now().minusSeconds(30));
//减少纳秒
System.out.println("减少10000纳秒:" + LocalDateTime.now().minusNanos(10000)); ******************************************************************************************
/*比较*/
//判断日期时间是否相等
System.out.println(LocalDateTime.now().isEqual(LocalDateTime.now()));
//检查是否在指定日期时间之前
System.out.println(LocalDateTime.now().isBefore(LocalDateTime.now()));
//检查是否在指定日期时间之后
System.out.println(LocalDateTime.now().isAfter(LocalDateTime.now()));
16.6.5 LocalDateTime 与 LocalDate 之间的转换
//LocalDateTime转化为LocalDate
LocalDateTime localDateTime = LocalDateTime.now();
LocalDate localDate = localDateTime.toLocalDate();
System.out.println(localDate);
//LocalDate转化为LocalDateTime
LocalDate localDate = LocalDate.now();
LocalDateTime localDateTime1 = localDate.atStartOfDay();
LocalDateTime localDateTime2 = localDate.atTime(8,20,33);
LocalDateTime localDateTime3 = localDate.atTime(LocalTime.now());
16.6.6 LocalDate与 Date之间的转换
//localDate转化为Date
LocalDate localDate = LocalDate.now();
ZoneId zoneId = ZoneId.systemDefault();
Date date = Date.from(localDate.atStartOfDay().atZone(zoneId).toInstant());
System.out.println(date);
//Date转化为localDate
Date date1 = new Date();
ZoneId zoneId1 = ZoneId.systemDefault();
LocalDate localDate1 = date1.toInstant().atZone(zoneId1).toLocalDate();
System.out.println(localDate1);
16.6.7 LocalDate 与 String 之间的转换
//从文本字符串获取LocalDate实例
LocalDate localdate = LocalDate.parse("2023-06-03");
System.out.println(localdate);
//使用特定格式化形式从文本字符串获取LocalDate实例
String str = "2023-06-03";
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(str, fmt);
System.out.println(date);
//使用特定格式化形式将LocalDate转为字符串
LocalDate today = LocalDate.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String dateStr = today.format(fmt);
System.out.println(dateStr);
16.6.8 LocalDateTime与 String 之间的转换
//字符串转为LocalDateTime
LocalDateTime ldt2 = LocalDateTime.parse("2022-11-07T21:20:06.303995200");
System.out.println(ldt2);
//使用特定格式化将字符串转为LocalDateTime
DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt3 = LocalDateTime.parse("2022-11-07 21:20:06", df1);
System.out.println(ldt3);
//LocalDateTime转为字符串
LocalDateTime today = LocalDateTime.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String dateStr = today.format(fmt);
System.out.println(dateStr);