day01面向对象高级-继承

学习目标

1、能够写出类的继承格式
2、能够说出继承的特点
3、能够说出子类调用父类的成员特点
4、能够说出方法重写的概念
5、能够说出this可以解决的问题
6、能够说出super可以解决的问题
7、能够掌握static关键字修饰的变量调用方式
8、能够掌握static关键字修饰的方法调用方式

一、static

1.static修饰成员变量

概念:静态,常用于修饰变量和方法。
成员变量的分类
	类变量,有static修饰的成员位置变量
	实例变量,无static修饰的成员位置变量
成员变量内存原理
	静态变量存在于方法区
	实例变量存在于对象内存中

2.static修饰成员变量的应用场景

当类中存在需要被共享的数据,该变量可以定义为静态
需求示例:在User类中定义个表示已创建用户数量。

3.static修饰成员方法

成员方法分类:
	类方法:有static修饰的成员位置方法
	实例方法:无static修饰的成员位置方法
成员方法执行原理
	静态方法可以通过类名(推荐)或对象名调用后,加载到栈内存执行。
小知识:main方法是被我们定义后,由jvm自动调用的静态方法。

4.static修饰成员方法的应用场景

工具类:由大量静态方法组成的,便于使用的类。
为什么工具类中的方法多实用静态?
	节约内存,便于调用。

5.static的注意事项

类方法中可以直接访问类的成员,不可以直接访问实例成员。
实例方法中既可以直接访问类成员,也可以直接访问实例成员。
实例方法中可以出现this关键字,类方法中不可以出现this关键字的。

为什么?
	静态的内容优先于非静态存在,调用静态方法的时候,不能保证非静态的内容已经被加载完。

6.static的应用:代码块

分类
	静态代码块
		格式:static { }
        特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次。
        作用:完成类的初始化,例如:对类变量的初始化赋值。
	实例代码块
		  格式:{ }
          特点:每次创建对象时,执行实例代码块,并在构造器前执行。
          作用:和构造器一样,都是用来完成对象的初始化的,例如:对实例变量进行初始化赋值。

7.static的应用:单例设计模式

饿汉式
	类加载到内存的时候,就创建对象
懒汉式
	第1次获取对象的时候,创建对象。

二、继承

1.继承入门

继承概述:子类通过extends与父类创建关系。
特点:子类可以继承父类的非私有成员(成员变量,成员方法)
子类的对象是由子类、父类共同完成。

2.继承的执行原理

1.创建子类对象时,回通过调用父类的构造方法(super(...)),加载父类的内容。
2.继承后,实际只会在堆内存开辟一个空间,表示子类对象,父类成员变量会初始化在子类内存空间中。

3.继承的好处

好处:减少重复代码的编写
思路:抽取共性的属性和方法到父类中,将由父类继承给子类。

4.继承注意事项

1.权限修饰符

private 同一个类中使用
缺省     同一个包中使用
protected 同一个包 或子类中使用
public  任意位置

2.单继承、Object

单继承
多层继承
多个子类
Ojbect类是所有类的间接或直接父类

3.方法重写

前提:当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时。
实现:子类可以重写一个与父类的方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
就近原则:子类对象优先使用本类的方法。
其他注意事项:
	推荐使用Override注解
	子类重写方法权限要大于等于父类被重写的方法。
	重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
	私有方法,不能被重写。
	静态方法可以定义一样的,但是不视作被重写。
重写应用:
	我们自己定义的类,可以重写Object类的toString()方法,以便返回对象的内容。

4.子类中访问其他成员特点

子类方法中访问成员变量/成员方法,依照就近原则。
	先子类局部范围找。
	然后子类成员范围找。
	然后父类成员范围找,如果父类范围还没有找到则报错。
子类方法中访问同名成员,优先使用子类的。
子类方法中想要明确访问父类成员,需要用过super关键字解决。
	super.父类成员变量
	super.父类成员方法(实参)

5.子类构造器的特点

特点:子类全部构造器,先调用父类构造器,再执行自己。
实现方式:
	首行+默认:默认情况下,子类全部构造器的第一行代码都是 super() (写不写都有) ,它会调用父类的无参数构造器。

	手动替代默认:如果父类没有无参构造,需要手动书写super(xxx),指定调用父类有参构造,避免报错。
为什么一定要执行父类构造器
	子类对象由子类和父类内容共同创建,创建子类对象,需要先调用父类构造器,优先初始化父类成员变量,确保若子类中需要对父类成员变量操作时该变量已确保存在。
应用场景:
	通过子类构造方法,对父类中的成员变量初始化。
	子类构造方法对父类成员变量初始化的同时,也可以对本类成员变量初始化。
补充知识:
	this()可以调用本类其他构造器。
	有this()没super(),两个都只能放在构造器第一行。
	

6.this和super总结

this:代表本类对象的引用;super:代表父类存储空间的标识。

image-20221125175427088

day02面向对象高级-多态

学习目标

1、能够描述抽象方法的概念
2、能够写出抽象类的格式
3、能够写出抽象方法的格式
4、能够说出父类抽象方法的存在意义
	为了让子类重写该方法
5、能够描述final修饰的类的特点
6、能够描述final修饰的方法的特点
7、能够描述final修饰的变量的特点

10、能够写出接口的定义格式
11、能够写出接口的实现格式
	单实现,多实现,继承并实现
12、能够说出接口中的成员特点
	只有静态的常量
	有抽象方法
13、能够说出多态的前提
	继承/实现关系     父类引用指向子类对象    方法的重写
14、能够写出多态的格式
	父类/接口类型 变量名= 子类/实现类对象
15、能够理解多态向上转型和向下转型
	向上转型:自动类型转换
	向下转型:强制类型转换   子类类型  变量名= (子类类型)父类类型的变量;
16、能够说出内部类概念
	类里面定义的类
17能够理解匿名内部类的编写格式
	必须掌握
	new 类名/接口名(参数){ 重写方法 }  new 谁表示创建了谁的子类对象

一、多态

1.多态入门

什么是多态
	多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态。
代码体现:
	父类类型引用指向子类对象,调用子类重写后的方法。
多态前提:
	有继承/实现关系;
	存在父类引用子类对象;
	存在方法重写。 
注意事项:
	多态是针对对象和行为的多态。
	Java中的成员变量不存在多态的说法。

2、多态的好处,类型转换

多态的好处:
	1.实现了一定程度的解耦合,便于扩展和维护。
	2.使方法的形参可以接收一切子类对象,提高扩展性和便利性。
存在的问题:
	不能调用子类独有功能。
	解决办法:使用类型转换。
类型转换:
	向下转型:将指向子类对象的父类引用的,转为子类类型引用。
	向上转型:父类引用指向子类对象。
注意事项:
	错误的类型转换,会导致运行期间,出现类型转换异常。
	解决建议:使用instanceof关键字,判断当前对象的真实类型,再进行强转

二、final关键字

修饰类:该类被称为最终类,特点是不能被继承了。
修饰方法:该方法被称为最终方法,特点是不能被重写了。
修饰变量:该变量只能被赋值一次。
修饰变量的注意事项
	修饰基本类型,变量存储的数据不能改变
	修饰引用类型,存储的地址值不能改变,对象的内容可以被改变。
补充:被final修饰的常量
使用了 final 修饰的成员变量就被称为常量;可以和static 配合使用。
作用:通常用于记录系统的配置信息。
注意!常量名的命名规范:建议使用大写英文单词,多个单词使用下划线连接起来。
优点:可读性好,便于维护,程序编译后,常量会被"宏替换",即被替换成字面量,从而保证使用常量和字面量的性能一致。

三、抽象类

1.认识抽象类

格式:
	修饰符 abstract class 抽象类类名{ 
		修饰符 abstract 返回值类型 抽象方法名称(形参列表);
 	}
 注意事项、特点
 	抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
    类该有的成员(成员变量、方法、构造器)抽象类都可以有。
    一个类继承抽象类,要么重写全部抽象方法,要么这个类也必须定义成抽象类。
    抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。

2.使用场景和好处

多个类中只要有重复代码(包括相同的方法签名),我们都应该抽取到父类中去,
此时,如果子类方法的方法体实现各不相同,父类中就有可能存在只有方法签名的方法。
这时,父类必定是一个抽象类了,我们抽出这样的抽象类,就是为了更好的支持多态。

3.模板设计模式

模板方法设计模式的写法
	1、定义一个抽象类。
	2、在里面定义2个方法
		一个是模板方法:把相同代码放里面去,建议用final修饰该方法。
		一个是抽象方法:具体实现交给子类完成。

四、接口

1.认识接口

格式
	public interface 接口名 {
           // 成员变量(常量)
           // 成员方法(抽象方法)
    } 
接口的实现
    修饰符 class 实现类 [extends 父类]  implements 接口1, 接口2, 接口3 , ... {
    } 
规则:
	实现类实现接口,要么重写接口中的抽象方法,要么实现类也要定义成抽象的。

2.接口的好处

弥补了类单继承的不足,一个类同时可以实现多个接口。
让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。

3.接口综合案例

需求:开发一个学生信息管理模块,学生的数据有姓名、性别、成绩,模块具备以下两个功能。
	功能1:要求打印出全班学生的信息; 
    功能2:要求打印出全班学生的平均成绩。
最终,要求能够按照以下两种情况,打印学员信息,且方法可灵活选择。
	第1套方案:能打印出班级全部学生的信息;能打印班级全部学生的平均分。
     第2套方案:能打印出班级全部学生的信息(包含男女人数);能打印班级全部学生的平均分(要求是去掉最高分、最低分)。

4.JDK8中接口的新特性

默认方法:可以被实现类的实例调用。
静态方法:只能通过接口名调用。
私有方法:jdk9加入,只能在接口中使用。
意义:增强了接口的能力,更有利于项目的扩展和维护。

5.接口的多继承

public interface C extends B , A{}
有意义:遍历实现类实现多个接口中的内容。

6.接口使用注意事项

一个接口继承多个接口,如果存在默认方法同名,子接口必须重写1次。
一个类实现多个接口,如果存在默认方法同名,实现类必须重写1次。
一个类继承父类,并实现接口,父类成员方法与接口默认方法同名,优先继承父类成员方法。

五、内部类

是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类。
场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类。

2.成员内部类(了解)

格式
    public class Outer {
        // 成员内部类
        public class Inner {        
        }
    }
使用
	外部类名.内部类名 对象名 = new 外部类(...).new 内部类(...);
	Outer.Inner in =  new Outer().new  Inner();
访问特点
	成员内部类的实例方法可以直接访问外部类的实例成员,静态成员。
	

3.静态内部类(了解)

格式
    public class Outer {
        // 成员内部类
        public static class Inner {        
        }
    }
使用
	外部类名.内部类名 对象名 = new 外部类(...).内部类(...);
	Outer.Inner in =  new Outer().Inner();
访问特点
	可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员。

4.局部内部类(了解)

鸡肋语法,纯了解。
public class Test {

	public static void go(){
		class A{
			
		}
		
		abstract class B{
			
		}
		
		interface C{
			
		}
	}
}

5.匿名内部类(掌握)

理解:一种特殊的局部内部类,程序员不需要为其声明名字。
格式:
	new  类或接口(参数值…) {
		类体(一般是方法重写);
		};
	};
特点:由底层,创建一个局部内部类,并会立即创建出一个子类对象。
作用:用于更方便的创建一个子类对象。
使用场景
	以子类或实现类对象的身份,作为实参。可以简化代码书写。

day03面向对象进阶、常用API

学习目标

1、能够说出Object类的特点
	所有类的父类.意味着所有类都拥有Object中的方法
2、能够重写Object类的toString方法
3、能够重写Object类的equals方法
	方式1:使用idea自带的重写操作。//推荐
	方式2:使用Objects中 的equals方法,避免空指针异常。
4、能够说出自动装箱、自动拆箱的概念
5、能够将基本类型转换为对应的字符串】
	最简单的方式:+""
6、能够将字符串转换为对应的基本类型
	使用对应包装类型的里面的parseXxx  和  valueOf方法

一、枚举

1.基本格式

格式
	修饰符 enum 枚举类名{
           名称1 ,  名称2, ... ; //是一些常量。全大写
           其他成员…
    }
注意:
	枚举类中的第一行,只能写一些合法的标识符(名称),多个名称用逗号隔开。
	这些名称,本质是常量,每个常量都会记住枚举类的一个对象。
特点
	1.第一行只能罗列名称,这些名称默认被final修饰,并且每个常量记住的都是枚举类的对象。
	2.枚举构造器默认被私有,因此,枚举类对外不能创建对象。
	3.枚举类被final修饰。
	4.枚举类,可以想类一样定义其他成员。
	5.类枚举默认继承java.langEnum类,从而继承到一些方法。
单例设计
	只罗列一个名称表示枚举的对象。
	

2.应用场景

用来表示一组(数量较少且固定的)信息,然后作为参数进行传输。

二、泛型

1.初识泛型

定义类、接口、方法时,可以声明一个或者多个表示未知类型的变量(如:<E>) ,这些表示未知类型的变量统称为泛型。
作用:编译期间,约束操作的数据类型,避免了强制类型转换和运行时期可能出现的一些问题。
泛型的本质:把具体的数据类型作为参数传给类型变量。

2.含有泛型的类

定义:
    修饰符 class 类名<类型变量,类型变量,…> { 

    }
使用:
	类名<具体的数据类型,具体的数据类型> 变量名= new 类名();

3.含有泛型的接口

定义:
    修饰符 interface 接口名<类型变量,类型变量,…> { 

    }
使用1:
	public class ArrayList implement 接口名<具体的数据类型>{
        . . .
    }
使用2:
	public class ArrayList<类型变量> implement 接口名<类型变量>{
        . . .
    }

4.含有泛型的方法

定义:
	修饰符 <类型变量,类型变量,…>  返回值类型 方法名(形参列表) { 

    }
使用1:
	修饰符 <类型变量,类型变量,…>  类型变量作为返回值类型 方法名() { 

    }
    修饰符 <类型变量,类型变量,…>  类型变量 方法名(类型变量作为形参类型) { 

    }

5.泛型通配符

就是 “?” ,可以在“使用泛型”的时候代表一切类型;  E T K V 是在定义泛型的时候使用。
泛型受限
	泛型上限:   ? extends Car:   ? 能接收的必须是Car或者其子类 。
	泛型下限:   ?  super Car : ?  能接收的必须是Car或者其父类。

6.泛型注意事项

泛型的擦除
	泛型只存在于编译阶段,编译后的class文件,是没有泛型的。
泛型只支持引用类型
	基本类型不可以传给泛型。基本类型,必须使用其对应的包装类。

三、java.lang包下常用API

1.概念

API(Application Programming interface)  :应用程序编程接口

image-20221203181832594

2.Object

概述:
	Object类是所有类的父类
常用方法:
    public String toString()	返回对象的字符串表示形式。
    	意义:默认打印地址值,可以重写该方法用于打印对象的内容。
    public boolean equals(Object o)	判断两个对象是否相等。
    	意义:默认通过地址值比较,可以重写通过对象的内容比较两个对象是否相同。
了解: protected Object clone()	对象克隆
浅克隆:对象中的引用类型内容,克隆后使用原引用类型内容的地址值。
深克隆:对象中的引用类型内容,克隆后没有使用原引用类型内容,而是通过新建对象后,将引用类型内容复制给新的引用类型内容对象。

3.Objects

定义了大量避免空指针的方法。
	public static boolean equals(Object a, Object b)
		比较是否相同,先做非空判断,避免了空指针。
	public static boolean isNull(Object obj) 
		判断对象是否为null,为null返回true ,反之为false
	public static boolean nonNull(Object obj) 
		判断对象是否不为null,不为null则返回true, 反之为false

4.包装类

为什么要有包装类?
	①为了将基本类型以对象的形式表达,可以定义方法,便于操作基本数据类型。
	②一些场景中只能使用引用数据类型,比如泛型。
分类
	每种基本类型对应一个包装类。处理int-->Integer char-Character其他的包装类,只需要将首字母大写即可。
基本类型与包装类类型转换
	装箱和拆箱
	自动装箱和自动拆箱
其他操作(掌握)
	基本类型转字符串
	字符串转基本类型

5.StringBuilder/StringBuffer

简介:
	StringBuilder代表可变字符串对象,相当于是一个容器,它里面装的字符串是可以改变的,就是用来操作字符串的。
	好处:StringBuilder比String更适合做字符串的修改操作,效率会更高,代码也会更简洁。

构造器:
	public StringBuilder()
	public StringBuilder(String str)
常用方法:
	public StringBuilder append(任意类型)   添加字符串
    public StringBuilder reverse()    	字符串反转
    public int length()    字符串长度
    public String toString()   转为String类型字符串
为什么推荐使用StringBuilder操作字符串?
	StringBuilder在做字符串的拼接、修改等,效率更高。
	字符串较少或不需要操作的情况下,优先使用String.
StringBuffer的用法与StringBuilder是一模一样的
StringBuilder是线程不安全的  StringBuffer是线程安全的
案例:返回整形数字内容的字符串形式。

6.StringJoiner

为什么学?
	高效 StringBuilder实现了
	便于拼接 StringBuilder没有实现
构造器
	public StringJoiner (间隔符号)
	public StringJoiner (间隔符号,开始符号,结束符号)
常用方法
	public StringJoiner add (字符序列)
    public int length()
    public String toString()

day04常用API

学习目标

重点:Math、System、Runtime、BigDecimal、JDK8之前传统的日期、时间、Arrays
理解:JDK8开始新增的日期、时间

1、能够使用日期类输出当前日期
2、能够使用将日期格式化为字符串的方法
3、能够使用将字符串转换成日期的方法
4、能够使用Calendar类的get、set、add方法计算日期
5、能够使用System类获取当前系统毫秒值
6、能够使用Arrays类的sort方法
7、能够使用Arrays类的toString方法

一、Math、System、Runtime

1.Math

数学工具类,定义了大量静态方法
public static int abs(int a)  获取绝对值
public static double ceil(double a)  向上取整
public static double floor(double a)  向下取整
public static int round(float a)  四舍五入
public static int max(int a,int b)  获取两个int值中的较大值
public static double pow(double a,double b) 返回a的b次幂结果
public static double random()  返回double的随机值范围[0.0,1.0)

2.System

系统工具类,定义了大量静态方法
public static void exit(int status)	终止当前运行的Java虚拟机。 
public static long currentTimeMillis()	          返回当前系统的时间毫秒值形式
			毫秒值形式指的是从1970年1月1日00:00:00走到此刻的总的毫秒数,应该是很大的。 1s = 1000m

3.Runtime

表示运行环境的工具类,定义了大量静态方法
public static Runtime getRuntime()	返回与当前Java应用程序关联的运行时对象
public void exit(int status)	终止当前运行的虚拟机
public int availableProcessors()	返回Java虚拟机可用的处理器数。
public long totalMemory()	返回Java虚拟机中的内存总量 
public long freeMemory()	返回Java虚拟机中的可用内存
public Process exec(String command)	启动某个程序,并返回代表该程序的对象

二、BigDecimal

1.为什么学?

用于解决浮点型运算出现的结果失真问题
浮点型运算时, 直接+ - * / 可能会出现运算结果失真
	System.out.println(0.1 + 0.2);
	System.out.println(1.0 - 0.32);
	System.out.println(1.015 * 100);
	System.out.println(1.301 / 100);

2.API

构造器
    public BigDecimal(double val)  注意:不推荐使用这个	将 double转换为 BigDecimal 
    public BigDecimal(String val)  	把String转成BigDecimal

常用方法
    public static BigDecimal valueOf(double val)	转换一个 double成 BigDecimal
    public double doubleValue()	将BigDecimal转换为double 
    
    public BigDecimal add(BigDecimal b)	加法
    public BigDecimal subtract(BigDecimal b)	减法
    public BigDecimal multiply(BigDecimal b)	乘法
    public BigDecimal divide(BigDecimal b)	除法
    public BigDecimal divide (另一个BigDecimal对象,精确几位,舍入模式) 	除法、可以控制精确到小数几位

三、JDK8之前传统的日期、时间

1.Date

代表时间的类
构造器
    public Date()	创建一个Date对象,代表的是系统当前此刻日期时间。
    public Date(long time)	把时间毫秒值转换成Date日期对象。
常用方法
    public long getTime()	返回从1970年1月1日    00:00:00走到此刻的总的毫秒数
    public void setTime(long time)	设置日期对象的时间为当前时间毫秒值对应的时间

2.SimpleDateFormat

用于Date和String之间转换的类
为什么要转换
	展示时间的时候,需要格式良好的时间的字符串形式
	计算时间的时候,需要能够表示时间的Date对象
	
构造器
	public SimpleDateFormat(String pattern)	创建简单日期格式化对象,并封装时间的格式
常用方法
	public final String format(Date date)	将日期格式化成日期/时间字符串
	public final String format(Object time)	将时间毫秒值式化成日期/时间字符串
	public Date parse(String source)	把字符串时间解析成日期对象
练习:秒杀活动

3.Calendar

为什么学习?
	对于日期数据的处理更加方便灵活。
构造器
	这里使用Calendar中的getInstance()方法,基于工厂模式提供了当前合适的Calendar对象。
	public static Calendar getInstance()	获取当前日历对象
常用方法
    public int get(int field)	获取日历中的某个信息。
    public final Date getTime()	获取日期对象。
    public long getTimeInMillis()	获取时间毫秒值
    public void set(int field,int value)	修改日历的某个信息。
    public void add(int field,int amount)	为某个信息增加/减少指定的值

四、JDK8开始新增的日期、时间

1.为什么要学JDK8新增的时间?

图1

image-20221206172919186

图2

image-20221206172935236

2.LocalDate、LocalTime、LocalDateTime

作用
	LocalDate:代表本地日期(年、月、日、星期)
	LocalTime:代表本地时间(时、分、秒、纳秒)
	LocalDateTime:代表本地日期、时间(年、月、日、星期、时、分、秒、纳秒)
获取对象
	LocaDate ld = LocalDate.now();
	LocalTime lt = LocalTime.now();
	LocalDateTime ldt = LocalDateTime.now();
获取指定时间对象
	LocalDate localDate1 = LocalDate.of(2099 , 11,11);
	LocalTime localTime1 = LocalTime.of(9,  8, 59);
	LocalDateTime localDateTime1 = LocalDateTime.of(2025, 11, 16, 14, 30, 01);
LocalDate的常用API
常用方法
    public int geYear()	获取年
    public int getMonthValue()	获取月份(1-12)
    public int getDayOfMonth()	获取日
    public int getDayOfYear()	获取当前是一年中的第几天
    Public DayOfWeek getDayOfWeek()	获取星期几:ld.getDayOfWeek().getValue()
其他方法
	withYear、withMonth、withDayOfMonth、withDayOfYear	直接修改某个信息,返回新日期对象
    plusYears、plusMonths、plusDays、plusWeeks	把某个信息加多少,返回新日期对象
    minusYears、minusMonths、minusDays,minusWeeks	把某个信息减多少,返回新日期对象
    equals isBefore isAfter	判断两个日期对象,是否相等,在前还是在后 
LocalTime的常用API
常用方法
    public int getHour()	获取小时
    public int getMinute()	获取分
    public int getSecond()	获取秒
    public int getNano()	获取纳秒
其他方法
    withHour、withMinute、withSecond、withNano	修改时间,返回新时间对象
    plusHours、plusMinutes、plusSeconds、plusNanos	把某个信息加多少,返回新时间对象
    minusHours、minusMinutes、minusSeconds、minusNanos	把某个信息减多少,返回新时间对象
    equals isBefore isAfter	判断2个时间对象,是否相等,在前还是在后 
LocalDateTime的常用API
    getYear、getMonthValue、getDayOfMonth、getDayOfYear	获取年月日、时分秒、纳秒等
    getDayOfWeek、getHour、getMinute、getSecond、getNano	
    withYear、withMonth、withDayOfMonth、withDayOfYear	修改某个信息,返回新日期时间对象
    withHour、withMinute、withSecond、withNano	
    plusYears、plusMonths、plusDays、plusWeeks	把某个信息加多少,返回新日期时间对象
    plusHours、plusMinutes、plusSeconds、plusNanos	
    minusYears、minusMonths、minusDays、minusWeeks	把某个信息减多少,返回新日期时间对象
LocalDateTime转换成LocalDate、LocalTime
    public LocalDate toLocalDate()	转换成一个LocalDate对象
    public LocalTime toLocalTime()	转换成一个LocalTime对象

3.ZoneId、ZonedDateTime

ZoneId 带时区的常见方法
    public static Set<String> getAvailableZoneIds()	获取Java中支持的所有时区
    public static ZoneId systemDefault()	获取系统默认时区
    public static ZoneId of(String zoneId)	获取一个指定时区
ZonedDateTime 带时区时间的常见方法
    public static ZonedDateTime now() 	获取当前时区的ZonedDateTime对象
    public static ZonedDateTime now(ZoneId zone) 	获取指定时区的ZonedDateTime对象
    getYear、getMonthValue、getDayOfMonth、getDayOfYeargetDayOfWeek、getHour、getMinute、getSecond、getNano	获取年月日、时分秒、纳秒等
    public ZonedDateTime withXxx(时间) 	修改时间系列的方法
    public ZonedDateTime minusXxx(时间) 	减少时间系列的方法
    public ZonedDateTime plusXxx(时间) 	增加时间系列的方法

4.Instant

传统的Date类,只能精确到毫秒,并且是可变对象;
新增的Instant类,可以精确到纳秒,并且是不可变对象,推荐用Instant代替Date。
该时间由两部分组成:从1970-01-01 00:00:00 开始走到此刻的总秒数 + 不够1秒的纳秒数


作用:可以用来记录代码的执行时间,或用于记录用户操作某个事件的时间点。


构造器
	public static Instant now() 	获取当前时间的Instant对象(标准时间)
常用方法
    public long getEpochSecond()	获取从1970-01-01T00:00:00开始记录的秒数。 
    public int getNano()	从时间线开始,获取从第二个开始的纳秒数 
    plusMillis plusSeconds plusNanos	判断系列的方法
    minusMillis minusSeconds minusNanos	减少时间系列的方法
    equals、isBefore、isAfter	增加时间系列的方法

5.DateTimeFormatter

相对于SimpleDateFormatter,DateTimeFormatter是线程安全的
构造器
	public static DateTimeFormatter ofPattern(时间格式) 	获取格式化器对象
    public String format(时间对象) 	格式化时间
常用方法
	public String format(DateTimeFormatter formatter)	格式化时间
	public static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) 	解析时间

6.Duration、Period

Period表示一段时期,用于计算两个 LocalDate对象 相差的年数、月数、
常用方法
    public static Period between(LocalDate start, LocalDate end)	传入2个日期对象,得到Period对象
    public int getYears()	计算隔几年,并返回
    public int getMonths()	计算隔几个月,年返回
    public int getDays()	计算隔多少天,并返回
Duration用于计算两个时间对象相差的天数、小时数、分数、秒数、纳秒数;支持LocalTime、LocalDateTime、Instant
常用方法
    public static Duration between(开始时间对象1,截止时间对象2) 	传入2个时间对象,得到Duration对象
    public long toDays()	计算隔多少天,并返回
    public long toHours()	计算隔多少小时,并返回
    public long toMinutes()	计算隔多少分,并返回
    public long toSeconds()	计算隔多少秒,并返回
    public long toMillis()	计算隔多少毫秒,并返回
    public long toNanos()	计算隔多少纳秒,并返回

五、Arrays

操作数组的一个工具类
常用方法
	public static String toString(类型[] arr)	返回数组的内容
    public static int[] copyOfRange(类型[] arr, 起始索引, 结束索引) 	拷贝数组(指定范围)
    public static copyOf(类型[] arr, int newLength)	拷贝数组
    public static setAll(double[] array, IntToDoubleFunction generator)	把数组中的原数据改为新数据
    public static void sort(类型[] arr)	对数组进行排序(默认是升序排序)

day05Lambda表达式、方法引用、算法、正则

学习目标

重点
	正则表达式
	算法(提升思维,面试用)

理解
	lambda表达式,方法引用
	
1、能够说出Lambda表达式的作用和使用前提
2、能够使用Lambda表达式简化函数式接口的匿名形式
3、能够说出Lambda表达式的省略规则
4、能够理解冒泡排序的代码实现
5、能够理解选择排序的代码实现
6、能够理解二分查找的代码实现
7、能够说出正则表达式的作用
8、能够使用正则表达式完成邮箱、手机号码的格式校验

一、JDK8新特性:Lambda表达式

1.基本使用

Lambda表达式是JDK 8开始新增的一种语法形式; 作用:用于简化匿名内部类的代码写法。
注意 :  Lambda表达式只能简化函数式接口的匿名内部类!!!
什么是函数式接口?
	有且仅有一个抽象方法的接口。
	将来我们见到的大部分函数式接口,上面都可能会有一个@FunctionalInterface的注解,有该注解的接口就必定是函数式接口。
示例:
	简化Arrays.setAll 方法中的  IntToDoubleFunction
	简化Arrays.sort  方法中的  Comparator

2.Lambda表达式的省略规则

参数类型可以省略不写。
如果只有一个参数,参数类型可以省略,同时()也可以省略。
如果Lambda表达式中的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号!此时,如果这行代码是return语句,也必须去掉return不写。

二、JDK8新特性:方法引用

1.静态方法的引用

格式:
	类名::静态方法
使用场景:
	如果某个Lambda表达式里只是调用一个静态方法,并且前后参数的形式一致,就可以使用静态方法引用。

2.实例方法的引用

格式:
	对象名::实例方法
使用场景:
	如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,就可以使用实例方法引用。

3.特定类型方法的引用

格式:
	类型::方法
使用场景:
	如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数是作为方法的主调,
	后面的所有参数都是作为该实例方法的入参的,则此时就可以使用特定类型的方法引用。

4.构造器引用

格式:
	类名::new
使用场景:
	如果某个Lambda表达式里只是在创建对象,并且前后参数情况一致,就可以使用构造器引用。

三、算法

1.简介

什么是算法
	解决问题的过程和方式
为什么学
	锻炼思维能力

2.排序算法

冒泡排序
选择排序

3.查找算法

二分查找

四、正则表达式

1.入门体验

由特定字符组成,表示一个规则。
作用1:用来校验数据格式是否合法。
作用2:筛选数据。
示例:
	1.电话号码  13811112222,10233233A,020-3212323
	2.邮箱  dlei@163.com,dlei#gmail.com
	3.qq号码  12abc,25666666,09999

2.书写规则

使用方式
public boolean matches (String regex)判断字符串是否匹配正则表达式,匹配返回true,不匹配返回false。
字符类
[abc]	   : 只能是a, b, 或c
[^abc]	   : 除了a, b, c之外的任何字符
[a-zA-Z] 	   : a到z A到Z,包括(范围)
[a-d[m-p]]    : a到d,或m到p
[a-z&&[def]]  : d, e, 或f(交集)
[a-z&&[^bc]]  : a到z,除了b和c(等同于[ad-z])
[a-z&&[^m-p]] :  a到z,除了m到p(等同于[a-lq-z])
预定义字符类
.	任何字符
\d	一个数字: [0-9]
\D	非数字: [^0-9]
\s	一个空白字符: 
\S	非空白字符: [^\s]
\w	[a-zA-Z_0-9] 
\W	 [^\w] 一个非单词字符
数量词
X?	X , 一次或0次
X*	X,零次或多次
X+	X , 一次或多次
X {n}	X,正好n次
X {n, }	X,至少n次
X {n,m}	X,至少n但不超过m次
逻辑运算符
	&&
	|

3.应用案例-校验数据

校验电话号码
	多种电话号码规则,使用组的概念。
校验邮箱
	.域名需要多次匹配,也需要给其分一个组,作为一个整体。

4.应用案例-查找信息

数据过滤 
	如果存在多种规则,需要使用组的概念
过滤得到指定的内容
	将要单独获取的数据,分配一个对应的组
过滤指定内容
	贪婪匹配
	非贪婪匹配

5.应用案例-搜索替换,分割内容

replaceAll(正则表达式,要替换的内容)
		叠词替换   ((.)\\1+),$1)
String[] split(切割规则表达式)
		注意:如果用.进行切割,需要使用\\.表示真正的点。

day06集合进阶1

学习目标

重点 
	异常处理方式
	集合的常用方法
	集合的遍历方式
理解:
	数据结构特点与原理
		数组结构:查询快
		链表结构:增删快
		队列结构:先进先出
		堆栈结构:先进后出
	
1、能够说出异常是什么,以及异常的分类是什么样的
2、能够理解自定义异常,并能说出异常的作用
3、能够说出常见的异常处理方式有哪些
4、能够说出Collection集合和Map集合的整体特点
5、能够说出Collection集合的常见体系结构和各自的特点
6、能够说出Collection下的常用方法
7、能够使用迭代器遍历Collection集合
8、能够使用增强for循环遍历Collection集合
9、能够使用Lambda表达式遍历Collection集合
10、能够理解Collection集合存储自定义类型对象的原理
11、能够说出List系列集合的特点和常用方法
12、能够说出ArrayList集合的底层结构和特点
13、能够说出LinkedList集合的底层结构和特点
14、能够说出LinkedList集合新增的常用方法,以及应用场景

零、异常

1.认识异常

异常示例
	角标越界异常
	数学异常
	空指针异常
	类型转换异常
	文件不存在异常
	读取网络失败异常
异常体系
	Error
	Exception
		编译时异常
		运行时异常
异常处理的两种方式
	抛出异常
	捕获异常

2.自定义异常

编译时异常
	继承Exception类	
运行时异常
	继承RuntimeException类

3异常的处理

抛出异常
	throws
捕获异常
	try...catch...
	

一、集合体系

1.集合体系

单列集合
	Collection代表单列集合,每个元素(数据)只包含一个值。
双列集合
	Map代表双列集合,每个元素包含两个值(键值对)。

2.单列集合体系

单列集合体系
	List系列集合:添加的元素是有序、可重复、有索引。
		ArrayList、LinekdList :有序、可重复、有索引。
    Set系列集合:添加的元素是无序、不重复、无索引。
		HashSet: 无序、不重复、无索引;
		LinkedHashSet: 有序、不重复、无索引。
		TreeSet:按照大小默认升序排序、不重复、无索引。

二、Collection常用方法

Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
public boolean add(E e)	把给定的对象添加到当前集合中 
public void clear() 	清空集合中所有的元素
public boolean remove(E e)	把给定的对象在当前集合中删除
public boolean contains(Object obj)	判断当前集合中是否包含给定的对象
public boolean isEmpty()	判断当前集合是否为空
public int size()	返回集合中元素的个数。
public Object[] toArray()	把集合中的元素,存储到数组中

三、Collection遍历方式

1.迭代器遍历

Collection<String> lists = new ArrayList<>();
获取迭代器对象
	Iterator<E> it = lists.iterator()
使用迭代器遍历
	判断是否有下一个元素
		it.hasNext()
	(如果有)获取下一个元素。
		it.next();

2.增强for遍历

for(接收集合或数组元素的变量 : 集合或数组对象){
		
}

3.lambda表达式遍历

Collection中拥有一个方法:default void forEach(Consumer<? super T> action) 

Collection<String> lists = new ArrayList<>();

//匿名内部类
lists.forEach(new Consumer<String>() {
   	@Override
   	public void accept(String s) {
   		System.out.println(s);
   	}
   });
   //lambda表达式
      lists.forEach((String s) -> {
      		System.out.println(s);
      });
      //  lists.forEach(s -> System.out.println(s));

4.练习:遍历存储自定义类型对象的集合

定义集合
定义自定义类型对象
存储自定义类型对象
遍历集合,获取每一个对象
打印获取的兑现的详细信息

5.集合的并发修改异常问题

使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误
怎么保证遍历集合同时删除数据时不出bug?
	a.使用迭代器遍历集合,用迭代器自己的删除方法删除数据即可。
	b.如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i --操作。

四、List集合

1.特点和特有方法

有序,可重复,有索引

void add(int index,E element)	在此集合中的指定位置插入指定的元素
E remove(int index)	删除指定索引处的元素,返回被删除的元素
E set(int index,E element)	修改指定索引处的元素,返回被修改的元素
E get(int index)	返回指定索引处的元素

2.遍历方式

for循环(因为List集合有索引)
迭代器 
增强for循环
Lambda表达式

3.ArrayList底层原理与应用场景

底层原理:采用数组结构
	   特点:查询快
应用场景:查找数据

4.LinkedList底层原理与应用场景

底层原理:采用双向链表结构
		特点:增删快
LinkedList特有方法
	public void addFirst(E e)	在该列表开头插入指定的元素
	public void addLast(E e)	将指定的元素追加到此列表的末尾
	public E getFirst()	返回此列表中的第一个元素
	public E getLast()	返回此列表中的最后一个元素
	public E removeFirst()	从此列表中删除并返回第一个元素
	public E removeLast()	从此列表中删除并返回最后一个元素
应用场景:
	模拟实现堆栈 或 队列结构

day07集合进阶2

学习目标

掌握
	HashSet/HashMap原理(面试题)
	TreeSet的使用
	Collections工具类的使用
	Map集合常用方法
	Map集合遍历方式

1、能够说出Set系列集合体系和各自特点
2、能够说出HashSet集合的底层结构,及其特点
3、能够说出HashSet集合去除重复的原理,并能说出如何去除内容一样的对象
4、能够说出LinkedHashSet的底层原理和特点
5、能够说出TreeSet集合的作用和特点
6、能够为自定义类型的对象制定比较规则
7、能够使用Comparator为TreeSet集合申明比较器对象
8、能够说出可变参数的作用和注意事项
9、能够说出Collections下有哪些常用方法
10、能够使用Collections为List集合进行排序
11、能够理解Collection系列集合的综合案例并能独立开发出来
12、能够说出Map集合的格式和体系特点
13、能够说出Map集合的常用方法
14、能够使用“键找值”的方式遍历Map集合
15、能够使用“键值对”的方式遍历Map集合
16、能使用Lambda表达式遍历Map集合

一、Set集合

1.特点

特点
	 无序:添加数据的顺序和获取出的数据顺序不一致;不重复; 无索引;
	 HashSet : 无序、不重复、无索引。
     LinkedHashSet:有序、不重复、无索引。
     TreeSet:排序、不重复、无索引。

2.HashSet集合的底层原理

什么是树结构(了解)
	二叉树
	二叉查找树
	平衡二叉查找树
	红黑树
	
什么是哈希值?
	hashCode方法产生的值,用于和表长度计算,得到元素的存储位置。
	
HashSet集合的底层原理?
	jdk8之前:数组+链表
	jdk8之后:数组+链表+红黑树
	
数据添加流程	
    创建一个默认长度16的数组,默认加载因子为0.75,数组名table
    使用元素的哈希值对数组的长度求与计算出应存入的位置
    判断当前位置是否为null,如果是null直接存入
    如果不为null,表示有元素,则调用equals方法比较
             相等,则不存;不相等,则存入数组
    JDK 8之前,新元素存入数组,占老元素位置,老元素挂下面
    JDK 8开始之后,新元素直接挂在老元素下面
存储自定义类型去重复
	如何让HashSet集合能够实现对内容一样的两个不同对象也能去重复???
	重写hashCode和equals方法  --使用alt+insert快捷键

3.LinkedHashSet集合

特点:有序、不重复、无索引
相对于HashSet多了双向链表机制,用于记录数据添加的顺序。

4.TreeSet集合

特点:特点:不重复、无索引、可排序
底层是基于红黑树实现的排序。
方式一 自然排序
 让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
 	compareTo(o)
 		this代表正在添加的, o代表已添加的
		this vs o2升序
 		
方式二 比较器排序
使用TreeSet有参数构造器,设置Comparator(比较器对象),重写里面的compare方法来指定比较规则。
	compare(o1,o2)  
		o1代表正在添加的, o2代表已添加的
 		o1 vs o2升序
注意:如果类本身有实现Comparable接口,TreeSet集合同时也自带比较器,默认使用集合自带的比较器排序。

二、Collections

1.可变参数

定义在方法、构造器的形参列表里,格式是:数据类型...参数名称;
特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
好处:常常用来灵活的接收数据。
注意事项
	可变参数在方法内部就是一个数组。
    一个形参列表中可变参数只能有一个
    可变参数必须放在形参列表的最后面

2.Collections类

public static <T> boolean addAll(Collection<? super T> c, T... elements)	给集合批量添加元素
public static void shuffle(List<?> list) 	打乱List集合中的元素顺序
public static <T> void sort(List<T> list)	对List集合中的元素进行升序排序
public static <T> void sort(List<T> list,Comparator<? super T> c)	对List集合中元素,按照比较器对象指定的规则进行排序

3.斗地主案例

总共有54张牌
点数: 3","4","5","6","7","8","9","10","J","Q","K","A","2“
花色: "♦“,  "♣", "♥", "♠"
大小王: "小🃏" , "大🃏“
斗地主:发出51张牌,剩下3张做为底牌。

三、Map

1.Map集合

要求
	Map集合称为双列集合,格式:{key1=value1 , key2=value2 , key3=value3 , ...}, 一次需要存一对数据做为一个元素.
	Map集合的每个元素“key=value”称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做“键值对集合”
	Map集合的所有“键是不允许重复的,但值可以重复”,键和值是一一对应的,每一个键只能找到自己对应的值
应用场景
	表达具备映射关系的数据
体系结构
	HashMap(由键决定特点): 无序、不重复、无索引;(用的最多)
		LinkedHashMap (由键决定特点):由键决定的特点:有序、不重复、无索引。
	TreeMap (由键决定特点):按照大小默认升序排序、不重复、无索引。

2.常用方法

Map是双列集合的祖宗,它的功能是全部双列集合都可以继承过来使用的。
public V put(K key,V value)	添加元素
public int size()	获取集合的大小
public void clear()	清空集合
public boolean isEmpty()	判断集合是否为空,为空返回true , 反之
public V get(Object key)	根据键获取对应值
public V remove(Object key)	根据键删除整个元素
public  boolean containsKey(Object key)	判断是否包含某个键
public boolean containsValue(Object value)	判断是否包含某个值
public Set<K> keySet()	获取全部键的集合
public Collection<V> values()	获取Map集合的全部值

3.Map集合遍历

方式1:键找值
方式2:键值对
方式3:forEach方法-->支持Lambda
	方法名称	说明
default void forEach(BiConsumer<? super K, ? super V> action)	结合lambda遍历Map集合
Map集合的案例-统计投票人数

day08集合进阶3

学习目标

掌握
	HashMap原理(面试题)
	

一、Map

4.HashMap

HashMap(由键决定特点): 无序、不重复、无索引;(用的最多)
HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的。
Set系列集合的底层就是基于Map实现的
使用细节
	HashMap的键依赖hashCode方法和equals方法保证键的唯一
	如果键存储的是自定义类型对象,可以通过重写hashCode和equals方法,保证多个对象内容一样,HashMap集合就能认为是重复的。

5.LinkedHashMap

LinkedHashMap, (由键决定特点): 有序、不重复、无索引。
原理同HashMap,由于使用双向链表,保证了有序性。

6.TreeMap

TreeMap (由键决定特点):按照键的大小默认升序排序、不重复、无索引

特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)
原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。

TreeMap集合同样也支持两种方式来指定排序规则
	让类实现Comparable接口,重写比较规则。
	TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则

7.补充知识:集合的嵌套

集合中的元素是一个集合

二、Stream流

1.Stream入门

Jdk8开始新增的一套API (java.util.stream.*),可以用于操作集合或者数组的数据。
优势:结合了Lambda的语法风格来编程,代码更简洁,可读性更好。
入门案例:把集合中所有以“张”开头,且是3个字的元素存储到一个新的集合
使用步骤
	获取Stream流对象
	使用Stream流对象操作
	统计、处理结果

三、File类

0、学前准备

内存中,存储数据的方式,程序结束,数据清除。
	变量
	数组
	对象
	集合
长久保存数据,需要将数据,存储到硬盘中。
File类:
	File是java.io.包下的类, File类的对象,用于代表当前操作系统的文件(可以是文件、或文件夹)。
	File类只能对文件本身进行操作,不能读写文件里面存储的数据。
IO流:
	用于读写数据的(可以读写文件,或网络中的数据…)
	

1.创建File对象

public File(String pathname)	根据文件路径创建文件对象
    public File(String parent, String child)	根据父路径和子路径名字创建文件对象
    public File(File  parent, String child)	根据父路径对应文件对象和子路径名字创建文件对象

2.判断文件类型,获取文件信息

public boolean exists()	判断当前文件对象,对应的文件路径是否存在,存在返回true
public boolean isFile()	判断当前文件对象指代的是否是文件,是文件返回true,反之。
public boolean isDirectory()	判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。

public String getName()	获取文件的名称(包含后缀)
public long length()	获取文件的大小,返回字节个数
public long lastModified()	获取文件的最后修改时间。
public String getPath()	获取创建文件对象时,使用的路径
public String getAbsolutePath()	获取绝对路径

3.创建文件,删除文件

public boolean createNewFile()	创建一个新的空的文件
public boolean mkdir()	只能创建一级文件夹
public boolean mkdirs()	可以创建多级文件夹

public boolean delete()	删除文件、空文件夹

4.遍历文件夹

public String[] list()	获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
public File[] listFiles()	获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
注意事项:
		当主调是文件,或者路径不存在时,返回null
        当主调是空文件夹时,返回一个长度为0的数组
        当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回
        当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
        当主调是一个文件夹,但是没有权限访问该文件夹时,返回null

四、递归

1.概述

什么是递归?
	方法调用自己的形式称为方法递归( recursion)。
什么是递归死循环?
	方法无限调用自己,无法终止,最终引起栈内存溢出。

2.应用、执行流程、算法思想

需求:计算n的阶乘,5的阶乘=1*2*3*4*5;  6的阶乘=1*2*3*4*5*6
需求:计算1-n的和的结果,使用递归思想解决

3.文件搜索

需求:从D:盘中,搜索“QQ.exe” 这个文件,找到后直接输出其位置。
需求:删除非空文件夹

day09IO流1

学习目标

1.

一、字符集

ASCII(American Standard Code for Information Interchange):美国信息交换标准代码,包括英文、符号等。
标准ASCII使用1个字节存储一个字符,首尾是0,总共可表示128个字符,对美国来说完全够用。
GBK编码(汉字内码扩展规范,国标)
汉字编码字符集,包含了2万多个汉字等字符,GBK中一个中文字符编码成两个字节的形式存储。
注意:GBK兼容了ASCII字符集。
Unicode(统一码,也叫万国码)
Unicode是国际组织制定的,可以容纳世界上所有文字、符号的字符集。
UTF-8编码
注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码
注意2:英文,数字一般不会乱码,因为很多字符集都兼容了ASCII编码。

字符集的编码、解码
编码:String类中提供了方法
		byte[] getBytes()   按照默认字符集编码
		byte[] getBytes(String charsetName)  按照指定字符集编码
解码:String类中提供了构造方法	
		String(byte[] bytes)	通过使用平台的默认字符集解码指定的字节数组来构造新的 String
		String(byte[] bytes, String charsetName)	通过指定的字符集解码指定的字节数组来构造新的 String

二、IO流

1.概述

理解
	I指Input,称为输入流:负责把数据读到内存中去
	O指Output,称为输出流:负责写数据出去
应用场景
	数据的存取
分类
	按流向分
	按数据最小单位分
	综合分类
		字节输入流
		字节输出流
		字符输入流
		字符输出流
		

2.IO流-字节流

字节流:以字节为单位,在内存和文件之间进行读写的流。
字节输入流:
读取字节
	1次读取1个字节
	1次读取多个字节
	1次读取全部字节
关闭流
自己输出流:
写出字节
	1次写一个字节
	1次写一多个字节
	1次写一个字节数组
关闭流
文件复制案例
	将图片从D盘复制到C盘

3.释放资源

try-catch-finally
try-with-resource

day10IO流2

学习目标

1.

一、字符流

字节流:以字符为单位,在内存和文件之间进行读写的流。
字符输入流:
读取字节
	1次读取1个字符
	1次读取多个字符
	1次读取全部字符
字符输出流:
写出字节
	1次写一个字节
	1次写一多个字节
	1次写一个字节数组

二、缓冲流

1.字节缓冲流

作用:提高字节流读写数据的性能
原理:字节缓冲(输入/输出)分别自带8kb字节缓冲池。
字节缓冲流基本使用:

2.字符缓冲流

作用:提高字符流读写数据的性能
原理:字符缓冲(输入/输出)分别自带8kb字符缓冲池
字符缓冲输入流特殊方法:
字符缓冲输出流特殊方法:

3.字符缓冲流案例

需求:拷贝出师表,回复文本内容顺序

4.原始流与缓冲流性能分析

需求:使用原始的字节流,以及字节缓冲流复制一个很大视频
基本字节流单个字节读取
基本字节流字节数组读取
缓冲流字节流单个字节读取
缓冲流字节流字节数组读取

三、转换流

1.转换流要解决的问题

不同编码读写时会出现乱码

2.字符输入转换流

解决不同编码时,字符流读取文本内容乱码的问题
解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了

3.字符输出转换流

作用:可以控制写出去的字符使用什么字符集编码。 
解决思路:获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了

四、打印流

1.概念

PrintStream/PrintWriter(打印流)
作用:打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。

2.基本使用

基本使用

3.PrintStream和PrintWriter的区别

打印数据的功能上是一模一样的:都是使用方便,性能高效(核心优势)
PrintStream继承自字节输出流OutputStream,因此支持写字节数据的方法。
PrintWriter继承自字符输出流Writer,因此支持写字符数据出去。

4.打印流的应用

修改输出语句的打印位置

五、数据流

1.数据输出流

允许把数据和其类型一并写出去。

2.数据输出流

用于读取数据输出流写出去的数据。 

六、序列化流

1.对象字节输出流

可以把Java对象进行序列化:把Java对象存入到文件中去

2.对象字节输入流

把Java对象进行反序列化:把存储在文件中的Java对象读入到内存中来

3.序列化多个对象

用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可

七、IO框架

1.什么是框架

解决某类问题,编写的一套类、接口等,可以理解成一个半成品,大多框架都是第三方研发的。
好处:在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率
框架的形式:一般是把类、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去。

2.什么是IO框架

封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等
Commons-io是apache开源基金组织提供的一组有关IO操作的小框架,目的是提高IO流的开发效率
FileUtils类提供的部分方法展示
IOUtils类提供的部分方法展示
使用演示

day11特殊文件、日志技术

学习目标

1.

一、特殊文件-properties

1.概述

用于描述键值对数据的文本格式
语法规则:
	1、都只能是键值对
	2、键不能重复
	3、文件后缀一般是.properties结尾的

2.properties文件解析

Properties是用来代表属性文件的,通过Properties可以读写属性文件里的内容。
构造器
	public Properties()	用于构建Properties集合对象(空容器)
读取属性文件的数据
    public void load(InputStream is) 通过字节输入流,读取属性文件里的键值对数据
    public void load(Reader reader)	通过字符输入流,读取属性文件里的键值对数据
    public String getProperty(String key) 根据键获取值(其实就是get方法的效果)
    public Set<String> stringPropertyNames() 获取全部键的集合(其实就是ketSet方法的效果)
写出数据到属性文件
    public Object setProperty(String key, String value) 保存键值对数据到Properties对象中去。
    public void store(OutputStream os, String comments)	把键值对数据,通过字节输出流写出到属性文件里去
    public void store(Writer w, String comments)把键值对数据,通过字符输出流写出到属性文件里去

二.特殊文件-xml文件

1.概述

全称:EXtensible Markup Language, 可扩展标记语言
作用:本质是一种数据的格式,可以用来存储复杂的数据结构,和数据关系。
格式特点
    XML中的“<标签名>” 称为一个标签或一个元素,一般是成对出现的。
    XML中的标签名可以自己定义(可扩展),但必须要正确的嵌套。
    XML中只能有一个根标签。
    XML中的标签可以有属性。
    如果一个文件中放置的是XML格式的数据,这个文件就是XML文件,后缀一般要写成.xml。
语法规则
	XML文件的后缀名为:xml,文档声明必须是第一行  
    		<?xml version="1.0" encoding="UTF-8" ?>
	xml中可以书写注释   
			<!–- 注释内容 -->
	XML中书写”<”、“&”等,可能会出现冲突,导致报错,此时可以用如下特殊字符替代。
			<    <  小于
			>    >  大于
			&   &  和号
			&apos;  '  单引号
			"  "  引号
	XML中可以写一个叫CDATA的数据区,里面的内容可以随便写。
			<![CDATA[   …内容…  ]]>
应用场景
	经常用来做为系统的配置文件;或者作为一种特殊的数据结构,在网络中进行传输。

2.xml文件解析

读取xml文件中的数据==>一般使用第3方法 开源框架
Dom4J解析xml文件
    构造器
    	public SAXReader() 	构建Dom4J的解析器对象
        public Document read(String url)	把XML文件读成Document对象
        public Document read(InputStream is)	通过字节输入流读取XML文件

    获取根元素节点
    	Element getRootElement() 	获得根元素对象
    	
    Element提供的方法
    	public String getName()	得到元素名字
        public List<Element> elements()	得到当前元素下所有子元素
        public List<Element> elements(String name)	得到当前元素下指定名字的子元素返回集合
        public Element element(String name)	得到当前元素下指定名字的子元素,如果有很多名字相同的返回第一个
        public String  attributeValue(String name)	通过属性名直接得到属性值
        public String elementText(子元素名)	得到指定名称的子元素的文本
        public String getText()	得到文本
案例:利用Dom4J框架,contacts.xml文件中的联系人数据,解析出来,封装成List集合,并遍历输出
写出数据到xml文件中==>不推荐使用dom4j,只用将数据拼接次xml格式的字符串,用IO流写出去更佳方便。

3.xml约束(了解)

目的:限制XML文件只能按照某种格式进行书写

DTD约束
	①编写DTD约束文档,后缀必须是.dtd
	②在需要编写的XML文件中导入该DTD约束文档
	③然后XML文件,就必须按照DTD约束文档指定的格式进行编写,否则报错!

Schema约束

三、日志技术

1.概述

什么是日志?
	希望系统能记住某些数据是被谁操作的,比如被谁删除了?
    想分析用户浏览系统的具体情况,以便挖掘用户的具体喜好?
    当系统在开发中或者上线后出现了bug,崩溃了,该通过什么去分析、定位bug?
常规方式
	public static  void test(String number){
        try {
            int result = Integer.parseInt(number);
            System.out.println("输入的数字为" + result);
        } catch (NumberFormatException e) {
            System.out.println("输入的数字有误,请输入一个整数");
        }
    }
弊端
    日志会展示在控制台
    不能更方便的将日志记录到其他的位置(文件,数据库)
    想取消日志,需要修改源代码才可以完成
日志技术
    可以将系统执行的信息,方便的记录到指定的位置(控制台、文件中、数据库中)。
	可以随时以开关的形式控制日志的启停,无需侵入到源代码中去进行修改。

2.日志技术体系概述

日志框架:牛人或者第三方公司已经做好的实现代码,后来者直接可以拿去使用
    JUL(java.util.logging)
    Log4j
    Logback
    其他实现
日志接口:设计日志框架的一套标准,日志框架需要实现这些接口。
    Commons Logging (JCL)
	Simple Logging Facade for Java (SLF4J)
        
注意1:因为对Commons Logging接口不满意,有人就搞了SLF4J;因为对Log4j的性能不满意,有人就搞了Logback。
注意2Logback是基于slf4j的日志规范实现的框架。
Logback日志框架官方网站:https://logback.qos.ch/index.html 
Logback日志框架有以下几个模块:
	 logback-core:基础模块,是其他两个模块依赖的基础(必须有)
	 logback-classic:完整实现了slf4j标准的模块(必须有)
	 logback-access:提供 HTTP 访问日志的功能,与Tomcat和Jetty等Servlet 容器集成.(可选,以后再接触)
想使用Logback日志框架,至少需要在项目中整合如下三个模块的资源:
	slf4j-api:日志接口
	logback-core
	logback-classic

3.Logback

使用步骤
    ①导入Logback框架到项目中去。
    ②将Logback框架的核心配置文件logback.xml直接拷贝到src目录下(必须是src下)。
    ③创建Logback框架提供的Logger对象,然后用Logger对象调用其提供的方法就记录系统的日志信息。
logback.xml配置
	设置输出日志到控制台
		<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
	设置输入日志到文件中
		<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
	开启日志(ALL)/取消日志(OFF)
		<root level=“ALL">
			<appender-ref ref="CONSOLE"/>
			<appender-ref ref="FILE" />
		</root>
设置日志级别
	日志级别:日志信息的类型,日志都会分级别(常见如下,优先级依次升高)
		trace	追踪,指明程序运行轨迹
        debug	调试,实际应用中一般将其作为最低级别,而 trace 则很少使用
        info	输出重要的运行信息,数据连接、网络连接、IO操作等等,使用较多
        warn	警告信息,可能会发生问题,使用较多
        error	错误信息,  使用较多
	学习目的:日志的级别是大于或等于核心配置文件配置的日志级别,才会被记录,否则不记录。

day12多线程

一、创建多线程

方式1:继承Thread类
	定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
    创建MyThread类的对象
    调用线程对象的start()方法启动线程(启动后还是执行run方法的)
优点:编码简单
缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展。
方式2:实现Runnable接口
	定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
    创建MyRunnable任务对象
    把MyRunnable任务对象交给Thread处理。
优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。
缺点:需要多一个Runnable对象。
方式3:实现Callable接口
	创建任务对象
    	定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据。
    	把Callable类型的对象封装成FutureTask(线程任务对象)。
    把线程任务对象交给Thread对象。
    调用Thread对象的start方法启动线程。
    线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果。
缺点:编码复杂一点。
FutureTask的API
	构造器:
		public FutureTask<>(Callable call)	把Callable对象封装成FutureTask对象。
	常用方法:
		public V get() throws Exception	获取线程执行call方法返回的结果。

二、Thread常用方法

常用方法
    public void run()	线程的任务方法
    public void start()	启动线程
    public String getName()	获取当前线程的名称,线程名称默认是Thread-索引
    public void setName(String name)	为线程设置名称
    public static Thread currentThread()	获取当前执行的线程对象
    public static void sleep(long time)	让当前执行的线程休眠多少毫秒后,再继续执行
    public final void join()...	让调用当前这个方法的线程先执行完!
其他方法
	yield、interrupt、守护线程、线程优先级等线程的控制方法,在开发中很少使用,这些方法会后续需要用到的时候再讲解。
常用构造器
	public Thread(String name)	可以为当前线程指定名称
    public Thread(Runnable target)	封装Runnable对象成为线程对象
    public Thread(Runnable target, String name)	封装Runnable对象成为线程对象,并指定线程名称

三、线程安全

1.什么是线程安全

多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。

2.模拟线程安全问题

需求:小明和小红是一对夫妻,他们有一个共同的账户,余额是10万元,模拟2人同时去取钱10万。
实现步骤:
		①需要提供一个账户类,接着创建一个账户对象代表2个人的共享账户。
        ②需要定义一个线程类(用于创建两个线程,分别代表小明和小红)。
        ③创建2个线程,传入同一个账户对象给2个线程处理。
        ④启动2个线程,同时去同一个账户对象中取钱10万。

四、线程同步

1.什么是线程同步

线程同步:解决线程安全问题的方案。
解决思路:让多个线程实现先后依次访问共享资源,这样就解决了安全问题。

2.同步方式1-同步代码块

作用:把访问共享资源的核心代码给上锁,以此保证线程安全。
格式
	synchronized(同步锁) {
		访问共享资源的核心代码
	}
原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行。
注意事项:对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug。
锁对象的选择:
	不要随便选择锁对象。
	建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象。
	对于静态方法建议使用字节码(类名.class)对象作为锁对象。

3.同步方式2-同步方法

作用:把访问共享资源的核心方法给上锁,以此保证线程安全。
格式:
    修饰符 synchronized 返回值类型 方法名称(形参列表) {
        操作共享资源的代码
    }
原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行
注意事项:
	同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
    如果方法是实例方法:同步方法默认用this作为的锁对象。
    如果方法是静态方法:同步方法默认用类名.class作为的锁对象。
同步代码块好还是同步方法好一点?
	范围上:同步代码块锁的范围更小,同步方法锁的范围更大。
	可读性:同步方法更好。

4.同步方式3-Lock锁

Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。
Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。
构造器
	public ReentrantLock()	获得Lock锁的实现类对象

常用方法
    void lock()	获得锁
    void unlock()	释放锁

5.线程通信(了解)

当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺。
线程通信模型常见模型(生产者与消费者模型)
	生产者线程负责生产数据
    消费者线程负责消费生产者生产的数据。
	注意:生产者生产完数据应该等待自己,通知消费者消费;消费者消费完数据也应该等待自己,再通知生产者生产!
Object类的等待和唤醒方法
void wait()	让当前线程等待并释放所占锁,直到另一个线程调用notify()方法或 notifyAll()方法
void notify()	唤醒正在等待的单个线程
void notifyAll()	唤醒正在等待的所有线程 
注意:上述方法应该使用当前同步锁对象进行调用。

四、线程池

1.什么是线程池

线程池:就是一个可以复用线程的技术。
不使用线程池的问题  
	用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,  而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。
工作原理
	线程池负责调度线程,执行任务队列中的相关任务。
	任务队列,指提交给线程池的Runnable或Callable任务接口的实现类对象。

2.如何创建线程池

方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。
方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。
ThreadPoolExecutor构造器
	public ThreadPoolExecutor(
            int corePoolSize, //指定线程池的核心线程的数量
            int maximumPoolSize, //指定线程池的最大线程数
            long keepAliveTime,//指定临时线程的存活时
            TimeUnit unit, //指定临时线程存活的时间单位(秒、分、时、天)		
            BlockingQueue<Runnable> workQueue, //指定线程池的任务队列
            ThreadFactory threadFactory, //指定线程池的线程工厂
            RejectedExecutionHandler handler//指定线程池的任务拒绝策略
		) 
注意事项:
1、临时线程什么时候创建?
	新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
2、什么时候会开始拒绝新任务?
	核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。

3.线程池处理Runnable任务

ExecutorService的常用方法
    void execute(Runnable command) 	执行 Runnable 任务
    Future<T> submit(Callable<T> task)	执行 Callable 任务,返回未来任务对象,用于获取线程返回的结果
    void shutdown() 	等全部任务执行完毕后,再关闭线程池!
    List<Runnable> shutdownNow()	立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务
新任务拒绝策略
    ThreadPoolExecutor.AbortPolicy	丢弃任务并抛出RejectedExecutionException异常。是默认的策略
    ThreadPoolExecutor.DiscardPolicy: 	丢弃任务,但是不抛出异常 这是不推荐的做法
    ThreadPoolExecutor.DiscardOldestPolicy	抛弃队列中等待最久的任务 然后把当前任务加入队列中
    ThreadPoolExecutor.CallerRunsPolicy	由主线程负责调用任务的run()方法从而绕过线程池直接执行

4.线程池处理Callable任务

ExecutorService的常用方法
    void execute(Runnable command) 	执行任务/命令,没有返回值,一般用来执行 Runnable 任务
    Future<T> submit(Callable<T> task)	执行任务,返回未来任务对象获取线程结果,一般拿来执行 Callable 任务
    void shutdown() 	等任务执行完毕后关闭线程池
    List<Runnable> shutdownNow()	立刻关闭,停止正在执行的任务,并返回队列中未执行的任务

5.Executors工具类实现线程池

Executors:是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。 

public static ExecutorService newFixedThreadPool(int nThreads)	
		创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。
public static ExecutorService newSingleThreadExecutor()	
		创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。
public static ExecutorService newCachedThreadPool()	
		线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了60s则会被回收掉。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)	
		创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。 
		
注意 :这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。
Executors使用可能存在的陷阱
	大型并发系统环境中使用Executors如果不注意可能会出现系统风险

image-20221223230143810

五、其他细节-并发、并行

什么是并发?
什么是并行?
并发与并行同时存在

六、其它细节知识:线程的生命周期

线程生命周期:线程从生到死的过程中,经历的各种状态及状态转换。
学习目的:理解线程这些状态有利于提升并发编程的理解能力。

image-20221223231048784

image-20221223230857803

day13网络编程

一、概述

1.网络通信架构

S架构( Client客户端/Server服务端 ) 
BS架构(Browser浏览器/Server服务端)

二.网络通信三要素

1.IP地址

IP(Internet Protocol):全称”互联网协议地址”,是分配给上网设备的唯一标志。
IP地址有两种形式:IPv4、IPv6
IPv4:点分十进制表示法
IPv6:冒分十六进制表示法
	共128位,号称可以为地球每一粒沙子编号。
 	IPv6分成8段表示,每段每四位编码成一个十六进制位表示, 数之间用冒号(:)分开。
域名:公共网络中的域名回影射一个对应的互联网的IP地址。
公网IP:是可以连接互联网的IP地址;
内网IP:也叫局域网IP,只能组织机构内部使用。
192.168. 开头的就是常见的局域网地址,,专门为组织机构内部使用。
范围即为192.168.0.0--192.168.255.255 
特殊IP地址:
	127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主机。
IP常用命令:
	ipconfig:查看本机IP地址。
	ping IP地址:检查网络是否连通
InetAddress
    public static InetAddress getLocalHost()	
            获取本机IP,会以一个inetAddress的对象返回
    public static InetAddress getByName(String host)	
            根据ip地址或者域名,返回一个inetAdress对象
    public String getHostName()	
            获取该ip地址对象对应的主机名。
    public String getHostAddress()	
            获取该ip地址对象中的ip地址信息。
    public boolean isReachable(int timeout)	
            在指定毫秒内,判断主机与该ip对应的主机是否能连通

2.端口号

端口:标记正在计算机设备上运行的应用程序的,被规定为一个 16 位的二进制,范围是 0~65535。
分类
    周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21) 
    注册端口:1024~49151,分配给用户进程或某些应用程序。
    动态端口:49152到65535,一般不固定分配某种进程,而是动态分配。
注意:我们自己开发的程序一般选择用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。

3.协议

网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。
开放式网络互联标准:OSI网络参考模型
OSI网络参考模型:全球网络互联标准。 
TCP/IP网络模型:事实上的国际标准。

image-20221224183952810

传输层2个通信协议
    UDP(User Datagram Protocol):用户数据报协议; 
    TCP(Transmission Control Protocol) :传输控制协议。
UDP协议
特点:无连接、不可靠通信。
不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等。
发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的 。
TCP协议
特点:面向连接、可靠通信。
TCP的最终目的:要保证在不可靠的信道上实现可靠的传输。
TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接。

二、UDP通信-快速入门

1.api

特点:无连接、不可靠通信。
不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了。
Java提供了一个java.net.DatagramSocket类来实现UDP通信。
DatagramSocket: 用于创建客户端、服务端
	public DatagramSocket()创建客户端的Socket对象, 系统会随机分配一个端口号。
	public DatagramSocket(int port)	创建服务端的Socket对象, 并指定端口号
	public void send(DatagramPacket dp)	发送数据包
	public void receive(DatagramPacket p) 	使用数据包接收数据
DatagramPacket:创建数据包
	public DatagramPacket(byte[] buf, int length, InetAddress address, int port)	创建发出去的数据包对象
	public DatagramPacket(byte[] buf, int length)	创建用来接收数据的数据包
	public int getLength()	获取数据包,实际接收到的字节个数

2.完成步骤

客户端实现步骤
创建DatagramSocket对象(客户端对象)                      扔韭菜的人
创建DatagramPacket对象封装需要发送的数据(数据包对象)            韭菜盘子
使用DatagramSocket对象的send方法,传入DatagramPacket对象        开始抛出韭菜
释放资源
服务端实现步骤
创建DatagramSocket对象并指定端口(服务端对象)              接韭菜的人
创建DatagramPacket对象接收数据(数据包对象)                 韭菜盘子
使用DatagramSocket对象的receive方法,传入DatagramPacket对象      开始接收韭菜
释放资源

三、UDP通信-多发多收

客户端实现步骤
创建DatagramSocket对象(发送端对象)                      扔韭菜的人
使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序
如果用户输入的不是exit,  把数据封装成DatagramPacket           韭菜盘子
使用DatagramSocket对象的send方法将数据包对象进行发送           开始抛出韭菜
释放资源
接收端实现步骤
创建DatagramSocket对象并指定端口(接收端对象)                  接韭菜的人
创建DatagramPacket对象接收数据(数据包对象)                    韭菜盘子
使用DatagramSocket对象的receive方法传入DatagramPacket对象
使用while死循环不断的进行第3步             

四、TCP通信-快速入门

1.api

特点:面向连接、可靠通信。
通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端。
Java提供了一个java.net.Socket类来实现TCP通信。
客户端开发
    public Socket(String host , int port)​	根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket
    public OutputStream getOutputStream()	获得字节输出流对象
    public InputStream getInputStream()	获得字节输入流对象
服务端开发
    public ServerSocket(int port)	为服务端程序注册端口
    public Socket accept()	阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象。

2.完成步骤

客户端实现步骤
创建客户端的Socket对象,请求与服务端的连接。
使用socket对象调用getOutputStream()方法得到字节输出流。
使用字节输出流完成数据的发送。
释放资源:关闭socket管道。
服务端实现步骤
创建ServerSocket对象,注册服务端端口。
调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。
通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。
释放资源:关闭socket管道

五、TCP通信-多发多收

使用TCP通信实现:多发多收消息。
客户端使用死循环,让用户不断输入消息。
服务端也使用死循环,控制服务端收完消息,继续等待接收下一个消息。

六、TCP通信-同时接收多个客户端

本次是如何实现服务端同时接收多个客户端的消息的?
    主线程定义了循环负责接收客户端Socket管道连接
    每接收到一个Socket通信管道后分配一个独立的线程负责处理它。

七、TCP通信-综合案例

1.即时通信

1、群聊是什么含义? 怎么实现?
是指一个客户端把消息发出去,其他在线的全部客户端都可以收到消息。
需要用到端口转发的设计思想。
服务端需要把在线的Socket管道存储起来,一旦收到一个消息要推送给其他管道
实现流程

2.实现一个简易版的BS架构

HTTP协议规定:响应给浏览器的数据格式必须满足如下格式

image-20221224194354853

线程池优化

day14Java技术

一、单元测试

二、反射

三、注解

四、动态代理