JavaSE下篇

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);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

养匹小马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值