static静态关键字
static是静态的意思,可以修饰成员变量和成员方法
static修饰成员变量表示该成员变量只在内存中存储一份,可以被共享访问、修改
成员变量分为两类 静态成员变量和实例成员变量
实例成员变量(无static修饰,存在于每个对象):常表示每个对象的信息
访问 对象.静态成员变量的方式来访问
用途:对象的各种信息
静态成员变量(有static修饰,属于类,加载一次,可以被共享访问(即通过类名或对象访问,更推荐前一种))
访问
类名.静态成员变量(推荐)如果在自己的类当中访问,不需要类名. ,直接写你要访问的静态成员变量即可
对象.静态成员变量(不推荐)
用途:像在线人数的展示
成员方法分类 静态成员方法和实例成员方法
静态成员方法(有static修饰,属于类) 建议用类名访问 ,也可用对象名访问
访问
类名.静态成员方法(推荐)如果在自己的类当中访问,不需要类名. ,直接写你要访问的静态成员方法即可
对象.静态成员方法(不推荐)
场景 表示对象自己的行为,且方法中需要访问实例成员的,则方法必须申明成实例方法
实例成员方法(无static修饰,属于对象)
访问 对象.静态成员方法的方式来访问
场景 共用功能
static注意事项
静态方法只能访问静态的成员,不可以直接访问实例成员,可以通过构建对象访问
实例方法可以访问静态的成员,也可以访问实例成员
静态方法不可以出现this(this表示当前对象)关键字
static应用知识
工具(Util)类 类中都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员共同使用的
由于工具类里面都是静态方法,直接用类名即可访问,因此工具类无需创建对象,建议将工具类的构造器进行私有,由于工具类无需创建对象,所以将其构造器私有化会显得很专业
成员变量
构造器 方法 代码块 内部类 是类的五大成分
代码块
定义在类的方法外 使用{ }括起来的代码被称为代码块 分为静态代码块和构造代码块(实例代码块)
静态代码块
格式 static { }
特点 需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
使用场景 在类加载的的时候做一些静态数据初始化的操作,以便后续使用
构造代码块(也称实例代码块)了解,见得少
格式 { }
特点 每次创建对象,调用构造器执行时,都会执行该代码,并且在构造器执行前执行
使用场景 初始化实例资源
什么是设计模式
解决问题最好的方法
单例模式
保证该系统中,应用该模式的类永远只有一个实例,即一个类永远只能创建一个对象
例如任务管理器,这样可以节省空间
饿汉单例设计模式
在用类获取对象的时候,对象已经提前为你创建好了,不管谁拿对象,都是同一个对象
设计步骤 1. 定义一个类,把构造器私有 2.定义一个静态变量存储一个对象
如下代码:在调用对象时,已经加载完了,因为要先加载上边的代码,那么定义的静态变量与类一起加载
打印结果为true,因为是同一个对象
懒汉单例设计模式
在真正需要对象的时候,才会去创建一个对象
设计步骤: 1. 定义一个类,把构造器私有 2.定义一个静态变量存储一个对象()
如下代码:
继承
使用extends关键字,让一个类和另一个类建立起一种父子关系
好处 提供代码的复用性
格式 子类 extends父亲
特点 子类 继承 父类,子类可以得到父类的属性和行为,子类可以使用
Java当中子类更强大
内存图解
特点
子类可以继承父亲的属性和行为,但是不能继承父类的构造器
Java是单继承模式,一个类只能继承一个直接父类
Java不支持多继承,但是支持多层继承
Java中所有类都是Object类的子类
子类可以继承父类的私有成员,只是不能直接访问而已,有方法访问 就好比父亲给儿子一个保险柜,但不知道密码
子类是否可以继承父类的静态成员 不算继承,只是共享,因为静态成员只在父类有一份,谈不上继承,就好比父亲有一辆车,儿子去开,只是共享
不支持多继承的原因
反证法 就好比父类a有个method方法是“复习语文” 而父类b有个method方法是“复习数学”
当调用方法时,不知道到底复习那个
支持多层继承 即子类a继承父类b,父类b继承父类c 当b和c都有一个method方法时,会采取就近原则,即继承爸爸的方法,不去继承爷爷的方法
Object特点
Java中所有类,要么直接继承了Object,要么默认继承了Object,要么间接继承了Object,Object是所有类的爸爸
在子类方法中访问成员(成员变量,方法)满足就近原则
先子类局部范围找
然后子类成员范围找
然后父类成员范围找,如果父类范围还没有找到就报错
如果子父类中,出现重名成员,会优先使用子类的,如果一定要使用父类的:通过super关键字
格式:super.父类成员变量/方法
方法重写
在继承的体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法时重写的方法
应用场景
当子类需要父类的功能,但父类的功能不完全满足自己的需求时 子类可以重写父类中的方法
@Override 重写注解
他放在重写的方法上,作为重写是否正确的校验注解 而且代码更易读(加上就知道是重写的代码)
方法重写的注意事项和要求
重写方法的名称,形参列表必须与被重写方法的名称和参数列表一致
私有方法不能被重写
子类继承父类后构造器的特点
子类中所有构造器都会默认先访问父类中无参的构造器,再执行自己
原因 子类在初始化的时候,有可能会遇到父类中的数据,如果父类没有完全完成初始化,子类将无法使用父类的数据
子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化
怎么实现的 子类构造器第一行语句默认都是 super() 不写也存在
super调用父类有参数构造器的作用 初始化继承自父类的数据
如果父类中没有无参数构造器,只有有参数构造器,会出现 报错,因为子类默认调用的是无参数构造器
如何解决 子类中通过书写super(参数),手动调用父类的有参数构造器 或者我们 创建一个无参构造器
this:代表本类对象的引用 this.成员变量/成员方法 this(…)访问本类构造器
super :代表父类存储空间的标识 super.成员变量/成员方法 super(…)访问父类构造器
注意事项 this(…)和super(…)都只能放在构造器的第一行,所以二者不能存在同一个构造器中
包
同一个包下的类,互相可以直接访问 不同包下的类必须导包(import包名.类名) 一般可以自动导包,
假如一个类中需要用到不同类,而这两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问
权限修饰符
用来控制一个成员能够被访问的范围(修饰成员变量 构造器 方法 内部类)
private 只能在同一个类 被访问
缺省 能在同一个类 同一个包中其他类 被访问
protected 能在同一个类 同一个包中其他类 其他包下的子类 被访问(是指其他包下继承父类的子类,在访问时,也需要创建的是子类的对象,而不是通过父类去访问)
如下在继承父类之后,也不能通过Fu f = new Fu();f.protectedMethod();的方式去访问,因为权限已经给到了子类
public 能在同一个类 同一个包中其他类 其他包下的子类 不同包下的无关类 被访问
final关键字(是最终的意思,修饰类,方法,变量)
修饰类:表明该类是最终类,不能被继承
修饰方法 表明该方法是最终方法,不能被重写
修饰变量(变量包括局部变量和成员变量(包括实例和静态成员变量)) 表名该变量第一次赋值后,不能再次被赋值
注意事项
final 修饰的变量是基本类型:那么变量存储的数据值不能改变
final 修饰的变量是引用类型:那么变量存储的地址值不能改变,但是指向的对象内容可以改变,例子如下
当t2=null;时,是改变地址,肯定不ok,当通过Teacher类的setter来修改值时,ok的
常量
常量是使用了public static final 修饰的成员变量,必须有初始化值,而且执行的过程中不能改变
作用与好处 可用作系统的配置信息,方便程序的维护,同时提高可读性
命名规范 英文单词全部大写 多个单词下划线连接
执行原理 编译阶段会进行“宏替换”,把使用常量的地方全部替换成真实的字面量,因为这样执行更快
图左是未编译的,右图是经过编译的class文件,已经替换成真实的字面量
常量做信息标志和分类
例如开发超级玛丽需接受用户输入的四个方向信号(上下左右),
如图上,这样也不是不对,但是代码不易读,传1不知道什么意思
当我们利用常量做标志分类时就不一样了,如图右,把它用常量做成标志分类,更好阅读,实现了软编码形式
枚举
枚举类都是继承了枚举类型 java.lang.Enum 把class的文件进行反编译如下:
枚举都是最终类,不可以被继承
枚举类的构造器都是私有的,枚举不能对外创建对象
枚举类的第一行默认都是罗列枚举对象的名称的
枚举类相当于是多例模式,里面有几个就是几个了,因为构造器私有了
枚举做信息标志和分类
当常量做信息标志和分类,会有缺陷 见上面常量做信息标志和分类部分,当move(UP);改为move(1)(可以是随便的数),也不会报错。
图上定义一个方向的枚举,图下为超级玛丽控制上下左右的应用
但改为枚举类型的时候,我们在用move()往里传参,只能是采用Orientation.的形式
抽象类
在Java中abstract是抽象的意思,可以修饰类,成员方法
abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法
注意
抽象类不能创建对象
抽象方法只有方法签名,不能声明方法体(public abstract void run());
一个类中如果定义了抽象方法,这个类必须声明称抽象类
一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类必须定义成抽象类
使用场景
抽象类可理解成不完整的设计图,一般作为类,让子类继承
当父类知道子类要完成某些行为,但是每个子类该行为实现又不同(例如加油卡分为金卡与银卡,所以定义一张抽象父类卡,然后在分别定义金银卡继承父类并进行方法的重写),于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成。
注意事项
类有的东西,抽象类都有 成员变量 方法 构造器 内部类 代码块
抽象类可以没有抽象方法 但有抽象方法的必须是抽象类
一个类继承了抽象类,必须写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类
图上是将所有的重写
图下是将其也定义成抽象类
抽象类不能创建对象
用反证法 假设能创建对象,如上面的Animal类,Animal a = new Aniaml();怎么运行a.run();,run方法连方法体都没有
final和abstract的关系
互斥关系
abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承
抽象方法定义通用功能让子类重写,final定义的方法子类不能重写
模板方法模式
使用场景 当系统中出现同一个功能对处在开发,而功能中大部分代码是一样的,只有其中部分不同的时候
实现步骤 把功能定义成一个所谓的模板方法,放在抽象类当中,模板方法中只定义通用且确定的代码
模板方法中不能决定的功能定义成抽象方法具体让子类去实现
好处 提高了代码的复用性 模板方法已经定义了通用结构,模板方法不能确定的部分定义成抽象方法,交给子类实现,
例如 同样是写一篇作文,《我的爸爸》,要小学生和中学生一起写,要求是开头和结尾一样,主文不一样,那我们就可以把主文定义成抽象方法,
Student父类,其中定义了一个抽象方法,如下,writeMain
调用的是子类重写的方法。
中学生的如下
小学生的如下
接口
接口也是一种父类(干爹),而实现类相当于子类
定义方法如图,详细的见下图代码
声明了一个接口,体现了一种规范
接口的基本使用
接口是用来被类实现( implements)的,实现接口的类被称为实现类。实现类可以理解成所谓的子类
接口可以被类单实现,也可以被类多实现
修饰符 class 实现类 implements 接口1,接口2,接口3,…{
如图,就是实现了SportMan和Law两个接口
}
接口实现类的注意事项
一个类实现接口,必须重写完全部接口的全部抽象方法
否则这个类需要定义成抽象类
例子见图下
基本小结
类和类的关系 单继承
类和接口的关系 多实现(如右图,实现了2个接口)
接口和接口的关系 多继承,一个接口可以同时继承多个接口
如下
SportMan接口继承了Law,People接口,那么在下面
BasketBallMan实现SportMan时就同时实现了SportMan,Law,People三个接口
JDK1.8之后,Java只对接口的成员方法进行了新增(了解一下就ok)
允许接口中直接定义带有方法体的方法
默认方法 类似之前写的普通实例方法:必须用default修饰 默认会public修饰。需要用接口实现类的对象来调用,因为接口不能创建对象 如下
静态方法
默认会用public修饰,当为静态时必须static修饰(如下) 注意 接口的静态方法必须用本身的接口名(子类不能调)来调用(如右)
私有方法 就是私有的实例方法 必须使用private修饰,从JDK1.9才开始有的
只有在本也就是在本接口当中其他的默认方法或者私有方法访问 如图是在默认方法调用的go();
面试常问
(平常遇不到,看看就ok)
1.接口不能创建对象
2.一个类实现多个接口,接口中有同样的静态方法不冲突
因为接口中的静态方法只能接口自己调用如A. 或 B.
能够实现(多实现)
3.一个类继承了父类,
同时又实现了接口,父类和接口中有同名方法
,默认用父类的(图下)
4.
一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写方法即可(图下)
5 一个接口继承多个接口,是没有问题的,如果多份接口存在规范冲突则不能多继承
一个是int eat(),一个是void eat()
多态
(同类型的对象,执行同一个行为,会表现出不同的行为特征)
常见形式 父类类型 对象名称 = new 子类构造器; 接口 对象名称 = new 实现类构造器;(不要理解成接口创建了对象,而是多态的一种形式,相当于是接口的实现类对象)
如下图,USB就是我们定义的一个接口类
访问特点
方法调用 :编译看左边,运行看右边 变量调用:编译看左边,运行也看左边
为什么是这样 因为多态强调的是行为,所以变量在编译时就不看右边了
多态的实现前提 有继承/实现关系 有父类引用指向子类对象;有方法重写
优势
在多态形势下,右边对象可以实现解耦合(即new Dog可以换成别的),便于扩展和维护
定义方法的时候,使用父类型作为参数,该方法就可以接受父类的一切子类对象,体现出多态的扩展性与便利
弊端:不能调子类独有功能
多态下引用数据类型的类型转换
自动类型转变换 (从子到父)
强制类型转换(从父到子) 子类 对象变量 =(子类)父类类型的变量
作用
可以解决多态下的劣势,实现调用子类独有功能
如果转型后和对象真实类型不是同一类型,那么转换的时候就会出现ClassCastException 如下是乌龟类型转成狗
在编译阶段,有继承或实现关系,不会报错,在运行时才会报异常
为避免此类情况,我们用instanceof来判断一下
内部类
内部类就是定义在一个类里面类,里面的类可以理解成(寄生),外部类可以理解成(宿主)
使用场景 作用
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整结构又只为外部事物提供服务,那么整个内部的完整结构可以选择使用内布类来设计
内部类通常可以方便访问外部类的成员,包括私有成员
内部类提供了更好的封装性,内部类本身就可以用private protected修饰,封装性可以做更多控制
内部类的分类
静态内部类 成员内部类 局部内部类 匿名内部类(前三个作为了解,后面一个最重要)
静态内部类(用的很少)
有static修饰,属于外部类本身 与普通类完全一样,只不过是在别人里面而已
格式
public class Outer{
//静态成员内部类
public static class Inner{ }
}
静态内部类创建对象格式 外部类名.内部类名 对象名 = new 外部类名.内部类构造器;
范例 Outer.Inner in = new Outer.Inner();
拓展 内部类可以直接访问外部类的静态成员 静态内部类不可以直接访问外部类的实例成员(外部类实例成员需用对象访问,可对照下面的成员内部类一起看)
成员内部类(用的很少)
无static修饰,属于外部类对象 JDK16之前,成员内部类不能定义静态成员,因为成员内部类属于外部类的对象,在对象里,不存在静态成员,静态成员属于外部类 或者这样想 既然属于外部类的对象,如果有100个外部类对象,那么每创建
一个对象都要加载一次,而static只加载一次
格式
public class Outer{
//成员内部类
public class Inner{ }
}
成员内部类创建对象格式 外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器();
范例 Outer.Inner in = new Outer().new Inner(); 属于外部类对象,所以先new Outer
访问静态方法 Outer.Inner.方法名;
拓展 内部类可以直接访问外部类的静态成员 内部类也可以直接访问外部类的实例成员(因为必须先有外部类对象,才有成员内部类对象,所以就可以访问外部类对象的实例成员)
一道面试题
要求 在show方法内部,补全代码,使其分别打印78 110 150 原题看下1图 结果如下2图
在成员内部类中,访问外部类对象,格式 外部类名.this
在题目中150的那个就是外部类对象
局部内部类(别看了不重要)
下面Cat就是一个
匿名内部类(重要)
图下1是没用匿部类的写法 图下2匿部类的写法
匿名内部类的对象类型相当于是当前new的那个类型的子类类型,即是Tiger
匿名内部类的特点
本质上是一个没有名字的局部内部类
作用 方便创建子类对象,最终目的为了简化代码
匿名内部类是一个没有名字的内部类 匿名内部类写出来就创建了一个对象
匿名内部类的对象类型相当于是当前new的那个类型的子类类型
常见使用形式 优化代码如下图2
匿名内部类可以作为方法的实际参数进行传输
开发中不是我们主动定义匿名内部类,而是别人需要我们写或者我们
可以写才会用 代码可以进一步简化(以后会讲)
API 是程序员的字典
API 应用程序编程接口 简单来说,就是Java帮我们写好的一些方法,我们直接用
Scanner 类
(不是基本类型,那就是引用类型)
引用类型一般使用步骤
1导包 import 包路径.类名称;如果需要使用的目标类,和当前类位于同一个包下,则可以省
只有java.lang包下的内容不需要导包
2创建 类名称 对象名 = new 类名称();
3使用 对象名.成员方法;
匿名对象
创建对象的标准格式 类名称 对象名 = new 类名称();
匿名对象就是只有右边的对象,没有左边的名字和赋值运算符
匿名对象只能使用唯一的一次
作为参数
返回值
Random类
第一种 获取一个随机int数字,范围是int所有范围 ,有正负两种
使用也是三个步骤 与Scanner 的区别是,Scanner在使用时nextInt(System.in),括号里有,而Random的括号里什么也没有
第二种 获取一个随机数字,参数代表了范围,左闭右开区间 int num = r.nextInt(3); 实际代表含义[0~3)之间随机取一个
对象数组
当我们想自动生成Getter/Setter方法时,我们可用快捷键 alt+insert 或 code一栏下的generate
当然我们需要定义一个 Person类
但是有一个缺点,因为此数组一旦定义后,便不能再修改元素个数,从而引出了ArrayList集合
ArrayList<E>类
间括号<E>代表泛型 泛型:也就是装在集合当中的所有元素,全部是同一类型
注意 此集合当中的元素只能存储引用类型,不能是基本类型
对于ArrayList集合来说,直接打印得到的不是地址值,而是内容 如果内容是空,得到的是中括号
[ ]
定义的时候,泛型里面是什么类型,给其赋值时就应该赋此类型
ArrayList常用方法
public boolean add(E e):向集合当中添加元素,参数的类型和泛型一致
public E get(int index):从集合当中获取元素,参数时索引编号,返回值就是对应位置的元素
public int size():获取集合的尺寸长度,返回值是集合中包含的元素个数
public E remove(int index):从集合当中删除元素,参数时索引编号,返回值就是被删除的元素
public boolean remove(Object o):直接删除元素,删除成功返回true,失败返回false当要删除元素有重名情况时,会优先删除靠前的那个
怎么统一ArrayList集合操作的元素类型
使用泛型 :<数据类型>
ArrayList<String> list1= new ArrayList<>();
Arraylist集合案例:遍历并删除元素
(见我的 Java 在一组成绩当中,删除多少分以下的成绩,所出现的不能删除问题 博客)
Arraylist集合案例:存储并自定义类型的对象
Arraylist集合案例:元素搜索
上图调用的方法如下
String类
字符串类型,可以定义字符串变量指向字符串对象
String是不可变字符串的原因
String变量每次修改其实都是产生并指向了新的字符串对象 原来的字符串对象都是没有改变的,所以称不可变字符串
创建字符串对象两种方式
1.直接使用” ” 定义(推荐) String name = ” 传智教育”
2.通过String类的构造器创建对象
public String(char [ ] c):根据字符数组的内容,来创建字符串对象
char [ ] chars = {‘a’,’b’}
String s3 = new String (chars);
public String(byte [ ] b):根据字节数组的内容,来创建字符串对象
byte [ ] bytes = {97,98,99,65,66,67};
String s4 = new String(bytes); 他会把字节里面的整数转换成 字符串对象
区别 以 ” ” 方式给出的字符串对象,在字符串常量池中存储,而且相同的内容只会存储一份
通过构造new 对象,每new一次,会在堆中又产生一个
常见面试题
public class TestDemo01 {
public static void main(String[] args) {
String s2 = new String("abc");
String s1 = "abc";
System.out.println(s1 == s2);
}
String s2 = new String(“abc”); 这句话创建了两个对象;new在堆内存中,而双引号的在字符串常量池当中,字符串常量池也是在堆上的。
String s1 = “abc”;这句话创建了0个对象,常量池中已经有
System.out.println(s1 == s2); false 因为s1和s2指向的地址不一样
String类常用API
“==”比较的是地址,当比较基本数据类型时,可以用
equals (不忽略大小写,判断内容是否一样)
equalsIgnoreCase(忽略大小写,常用来做验证码)
length
charAt 获取某位置的字符
toCharArrayba把字符串换成字符数组
substring() 截取某一段 (包前不包后 如下:截取的是“我爱”)
replace 替换
contains 是否包含
startsWith 以….开头
Object类
(是所有类的父类,新写的类会默认有extends Object,写不写都有)
-toString方法(是调用的父类Object的) 默认是打印当前对象的地址
上图的两种写法是一样的,都是打印地址
但我们是一般让子类重写,以便返回子类对象的内容
上图是在Student类当中的进行重写的,使用快捷键alt+insert,然后选择toString即可快捷生成,打印结果如下图
-equals方法
默认是比较两个对象的地址,我们一般不会用(判断地址我们会用“==”),我们一般会重写方法,使其能够去判断两个对象内部的内容是否一样
但是如果equals方法不去重写,equals会和“==”的功能一样,判断两个对象的地址是否一样,纵使s和s2的内容一样,但他们分别new一个对象,地址是不一样的
重写方法后,左是在Student类中重写的equals方法,默认的快捷键生成方法alt+insert,选择equals and hashCode,在生成后我们把那个hashCode的代码删掉
重写完后,就可以判断两个对象的内容是否一样
Objects类
-equals方法
为什么要有Objects的equals方法,因为当我们调用Object的equals方法可能会出现空指针异常,左图,String类已经继承
并默认重写了equals方法,所以直接s1.调用即可,当s1是null的情况下,会出现空指针异常,空指针不能调用方法,他不是实例的对象下图1是Objects的equals方法,他已经考虑到null的情况,当为null,直接返回false,当部位null,又会调用.equals方法,去判断内容是否一样所以更安全,所以调用下图2这样就ok
-isNull方法
判断是否为null,如果为null,返回true,否则返回false
如左图,其实如左图的两种写法其实完全一样,因为API里的也是这样写的,如下图,是官方写的方法
总结
建议使用Objects的equals方法,因为他更安全,但也能判断内容是否一样
StringBuilder类
StringBuilder是一个可变的字符串类,可以把它看成对象的容器 作用 提高字符串操作的效率,如拼接,修改
StringBuilder构造器
public StringBuilder() 创建一个空白的可变的字符串对象,不包括内容
public StringBuilder(String str) 创建一个指定内容的可变字符串对象
StringBuilder常用方法
public StringBuilder append(任意类型) 添加数据并返回StringBuilder对象本身
如图是将他们直接全拼接在一起,并且支持链式编程
因为append返回的是对象,所以还可以
public StringBuilder reverse() 将对象的内容反转
public int length() 返回对象的长度
public String toString() 通过toString() 就可以实现把StringBuilder 转换为String
如图下,
我们将st2直接传会报错,因为st2是StringBuilder类型,而定义的方法接受的一般是String类型的,所以我们一般会去转型,下图是转型成功
为什么StringBuilder效率更高
因为String类型是不可变的字符串,所以在拼接时,+拼接时,会先new 一个StirngBuilder对象,然后再toString转换成Strign,此时又会产生一个对象。所以总共会产生2个对象,每拼接一次都会产生两个对象,并且会抛弃上次的对象,所以造成空间浪费
Math类
包含执行基本数字运算的方法,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)
System类
System的功能是通用的,都是直接类名调用,所以System不能被实例化
public static void exit(int status) 终止当前运行Java虚拟机,非零表示异常终止 零表示正常终止 千万别用,回导致下面的程序崩溃
public static long currentTimeMillis() 返回当前系统的时间毫秒值形式
一般用于性能测试,如下就测试了计算机跑完10万次所需的时间
public static void arraycopy(Object src, int srcPos,Object dest, int destPos,int length)数组拷贝
里面这些参数看源码就行,不用记 分别表示( 源数组 起始位置 目的地数组 起始位置 拷贝长度)
BigDecimal大数据类型
应用:如下打印结果不是0.01,而是0.09999999999999999,会出现浮点型运算失真,引入了BigDecimal,将数值包装成大数据类型,就可以
创建对象BigDecimal封装浮点型数据(最好的方式是调用方法)
public static BigDecimal valueOf(double val):包装浮点数成为BigDecimal对象
如下图 但不能用new构造器来 即 BigDecimal a1 = new BigDecima(0.1F);这样也会失真,要调用valueOf方法
如此就解决了失真问题
但我们最终还是需求double类型
加一个转换成double类型 double rs = …
常用方法
public BigDecimal add(BigDecimal b)
public BigDecimal subtract(BigDecimal b)
public BigDecimal multiply(BigDecimal b)
public BigDecimal divide(BigDecimal b)
public BigDecimal divide(另一个BigDecimal对象,精确几位,舍入模式) 见下面的注意
注意
BigDecimal 是一定要进行精度运算的
下面这个是永远除不尽的,会导致程序崩溃,因为 BigDecimal 认为一定要精确运算,但自己又没办法,只能崩溃,由此我们引入了带参数的divide方法
public BigDecimal divide(另一个BigDecimal对象,精确几位,舍入模式)
输入Rounding Mode会自动提示,具体哪一种舍入模式用的时候在查就可以HALF_UP是四舍五入
日期与时间的处理
Date类概述
Date类的对象在Java中代表的是当前所在系统的此刻日期的时间
Date的构造器
public Date() 创建一个Date对象,代表的是当前系统此刻日期的时间
Date的常用方法
public long getTime() 获取对象的毫秒值
Date类记录时间的两种形式
形式1 日期对象
形式2 日期毫秒值(从1970到现在)
案例 计算当前时间往后走1小时121秒之后的时间是多少
时间毫秒值–> 日期对象
public Date(long time) 把时间毫秒值转换成Date日期对象
public void setTime(long time) 设置日期对象的时间为当前时间毫秒值对应的时间
SimpleDateFormat类
构造器
public SimpleDateFormat() 构造一个SimpleDateFormat,使用默认格式
public SimpleDateFormat(String pattern) 构造一个SimpleDateFormat,使用指定格式(这个格式要和你定义日期是一样的 比如你定义日期 2021-11-11 00:10:11 这个就要写成 yyyy-MM-dd HH:mm:ss)
格式化方法
public final String format(Date date) 将 日期格式化成日 期/时间字符串
public final String format(Object time) 将时间毫秒值式化成日期/时间字符串
SimpleDateFormat解析字符串时间成日期对象
public Date parse(String source) 从给定字符串的开始解析文本以生成日期
案例1
计算出2021年8月06日11点11分11秒,往后走2天14小时49分06秒后的时间是多少
例2 秒杀活动
Calendar类
Calendar代表了系统此刻日期对应的日历对象 Calendar是一个抽象类,不能直接创建对象
public int get(int field) 取日期中的某个字段信息
public void set(int field , int value) 修改日历某个字段的信息
public void add(int field,int amount)为某个字段增加/减少指定的值
public final Date getTime()拿到此刻日期对象 和Date里的拿毫秒值不一样
public long getTimeMillis()拿到此刻时间毫秒值
Calendar是可变日期对象,一旦修改后其对象本身表示的时间将产生变化
JDK8新增日期类
新增的API严格区分了时刻 本地日期 本地时间 并且,对日期和时间进行运算更加方便
其次,新增API 的类型几乎全部是不变类型(和String 的使用类似) 不必担心修改
新增API
1 LocalDate 不包含具体时间的日期
新增API 2
LocalTime:不包含日期的时间
新增API 3
LocalDateTime 包含了日期及时间
上面这些里面的具体方法太多,我们用的时候再去查API文档即可 减轻负担
新增API 4
Instant 代表的是时间戳(时间戳是指某一个时刻 年月日时分秒)
Instant类由一个静态的工厂方法now()可以返回当前时间戳
会与我们差8个小时,这是原子钟的标准时间,我们是东八区
Instant instant = Instant.now();
sout.” 当前时间戳是 + instant”
Instant和Date这两个类可以互相转换
Date date = Date.from(instant);
sout.” 当前时间戳是 + instant”
instant = date.toInstant();
sout.instant
新增API 5
DateTimeFormatter 用于时间的格式化和解析的
正反都能调用format方法
如下 转换成的格式正反结果都一样
解析字符串时间
新增API 6 Duration 用于计算两个”时间”间隔
在Java8中,我们可以使用以下类来计算 时间间隔 差异 java.time.Duration
提供了使用基于时间的值测量时间量的方法
用于LocalDateTime之间的比较,也可用于Instant之间的比较
新增API 7 Period 用于计算两个”日期”间隔
在Java8中,我们可以使用以下类来计算 日期间隔 差异 java.time.Period
主要是Period类方法getYears(),getMonths(),getDays ()来计算,只能精确到年月日
用于LocalDate之间的比较
新增API 8 java.time.terporal.ChronoUnit
ChronoUnit类可用于在单个时间单位内测量一段时间,这个工具类是最全的了,可以用于比较所有的时间单位
包装类
其实就是8中基本数据类型对应的应用类型
基本数据类型 ->引用数据类型
byte->Byte int->Integer char->Character double->Double
short->Short long->Long float->Float boolean ->Boolean
为什么提供包装类
Java为了实现一切皆对象,为8种基本数据类型提供了对应的引用类型
后面的集合和泛型其实也只能支持包装类型,不支持基本数据类型
自动装箱 基本数据类型和变量可以直接赋值给包装类型的变量 (图下)
自动拆箱 包装类型的变量可以直接赋值给基本数据类型的变量(图下)
包装类的特有功能
包装类的变量的默认值可以是null,容错率更高
可以把基本类型的数据转换成字符串类型(用处不大)
调用toString方法得到字符串结果
调用Integer.toString(基本类型的数据)
与其这样,还不如直接这样转字符串
直接在后面加 ” ”
会自动转换成字符串
可以把字符串类型的数值转换成真实的数据类型(有用)
Integer.parselnt(“字符串类型的整数”)
Double.parseDouble(“字符串类型的小数”)
下边是valueOf,同意都是,不用像上边的那样
正则表达式
初体验
以下是判断qq是否符合规则(满足6-20位 全为数字)
这样写很麻烦,而正则表达式可以更简洁那个 \\d 第一个”\” 是告诉后面的”\”是一个”\”,配合起来是表示整数
匹配规则
String类哪个方法可以于正则表达式进行匹配
public boolean matches(String regex)
判断是否匹配正则表达式,匹配返回true,不匹配返回false
以上正则匹配只能匹配校验单个字符例如333 那个就不行
下面这些匹配多字符
案例一 校验电话号码
案例二 校验邮箱只需要改matches里面即可
\\. 表示. 因为. 在这有含义,所以转义,\. ,又因为 \会和 . 组成一个导致无法判断,所以再加一个 \ 把后面的\ 转义
([a-zA-Z0-9]{2,20}) 表示.后面的域名 {1,2}表示后面至少一个域名,最多两个域名
正则表达式在字符串方法中的使用
public String replaceAll(String regex,String newStr) 按照正则表达式匹配的内容进行替换
public String [ ] split(String regex) 按照正则表达式匹配的内容进行分割字符串,返回一个字符串数组
正则表达式支持爬取信息
Arrays类
数组操作工具类,专门用于操作数组元素的
常用API
public static toString(类型[ ]a)对数组进行输出
public static void sort(类型[ ]a) 对数组进行默认升序排序
public static int binarySearch(int[ ]a,int key) 二分搜索数组中的数据,存在返回索引,不存在返回一个负值
Arrays类的排序方法
public static void sort(类型[] a) 对数组进行默认升序排序
public static void sort(类型[] a,Comparator c) 使用比较器对象自定义排序
自定义排序规则
设置Comparator接口对应的比较器对象,来定制比较规则
如果认为左边数据 大于 右边数据 返回正整数
如果认为左边数据 小于 右边数据 返回负整数
如果认为左边数据 等于 右边数据 返回 0
案例二 Student对象的
需要注意的是在身高的比较,因为身高是double类型的,而自己定义的比较的是int类型,所以我们用一个Doouble.compare 方法
选择排序
int [] arr = {5,1,3,2};
当i=0,5与1比,交换,然后1与3比,不换,然后1与2比,不换
{1,5,3,2}
当i=1,5与3比,交换,然后3与2比,交换
{1,2,5,3}
当i=2,5与3比,交换 {1,2,3,5}
二分查找(前提是排好序的数组)
Lambda表达式(不是必须要用,只是遇到这种情况才会去用)
lambda表达式是JDK 8 开始后的一种新语法形式 作用 简化匿名内部类的代码写法
简化格式
(匿名内部类被重写方法的形参列表)->(
被重写方法的方法体代码。
) 注 ->是语法格式,无实际含义
注意
Lambda表达式只能简化函数式接口的匿名内部类的写法形式
函数式接口
必须是接口,其次接口中仅有一个抽象方法的形式 通常我们会加一个@Functionallnterface注解,标记该接口必须是满足函数时接口
Lambda表达式的省略写法(进一步在Lambda表达式的基础上继续简化)
参数类型可以省略不写
如果只有一个参数,参数类型可以省略,同时()也可以省略
如果Lambda表达式方法体代码只有一行,可以省略大括号,同时要省略分号
如果Lambda表达式方法体代码只有一行,可以省略大括号,此时,如果这行代码式return语句,必须省略return不写,同时也必须省略” ;”不写