类和对象
1.什么是类和对象
- 类是现实生活中一类具有共同属性和行为的事物的抽象
- 类的组成:属性和行为(方法)
- 对象是由类实例化而来
- 一个类可以有多个对象
- 创建对象的方法:
类名 对象名 = new 类名();
2.成员变量和局部变量
- 成员变量:类中方法外的变量
- 局部变量:方法中的变量
- 区别
区别 |
成员变量 |
局部变量 |
类中位置不同 |
类中方法外 |
方法内或或者方法声明上(形参) |
内存中位置不同 |
堆内存 |
栈内存 |
生命周期 |
随对象的存在而存在,随对象的消失而消失 |
随着方法的调用而存在,随着方法的调用完毕而消失 |
初始化值不同 |
有默认的初始化值 |
没有默认的初始化值,必须先定义,赋值才能使用 |
3.封装
a.private修饰符:
- 权限修饰符,可以用来修饰成员(变量、方法)
- 特点:只能在本类当中进行访问
- 针对被private修饰的成员变量,如需被其它类使用,提供相应的操作:
- 提供
get变量名()
方法,用于获取成员变量的值,方法用public修饰 - 提供
set变量名(参数)
方法,用于设置成员变量的值,方法用public修饰
b.this关键字
- 在类的方法内如果局部变量和成员变量出现同名的情况下,Java会使用“就近原则”,将同名的成员变量当作局部变量
- 为了在方法内使用到成员变量,可以使用this关键字
- this关键字的作用:可以调用本类的成员(变量、方法),解决局部变量和成员变量重名的问题
- this代表所在类的对象引用,方法被哪个对象调用,this就代表哪个对象
c.封装的思想
- 隐藏实现的细节,仅对外暴露公共的访问方式
- 封装的常见体现:
- 私有化成员变量,提供setXxx()和getXxx()方法
- 将代码抽取到方法中,这是对代码的一种封装
- 将属性抽取到类当中,这是对数据的一种封装
- 封装的好处:
- 提高代码安全性
- 提高代码复用性
4.构造方法
- 构造方法是构造、创建对象时所调用的方法
- 格式:
- 方法名与类名相同,大小写一致
- 没有返回值类型,连void都没有
- 没有具体的返回值(不能由return带回结果数据)
- 执行时机:
- 创建对象时调用,每创建一次对象,就会执行一次构造方法
- 不能手动调用构造方法
- 作用:用于给对象的数据(属性)进行初始化
- 注意事项:
- 如果没有定义构造方法,系统将会给出一个默认的无参构造方法
- 如果定义了构造方法,系统将不再提供默认的构造方法
- 构造方法的重载:如果自定义了带参的构造方法,还需要使用无参的构造方法,就必须再手动给出无参构造方法
- 推荐使用方式:无论是否使用,都手动给出无参构造方法和带参构造方法
4.分包的思想
a.包的概念
- 如果将所有的类文件都放在同一个包下,不利于管理和后期维护,所以对于不同功能的类文件,可以放在不同的包下进行管理
- 包的本质就是文件夹
- 创建包(单级包和多级包):
- 多级包之间使用“.”进行分割
- 包名规范:域名的翻转
- 包的定义:
package 包名
- 注意事项:
- package语句必须是程序的第一条可执行的代码
- package语句在一个java文件中只能有一个
- 如果没有package,默认表示无包名
b.类与类之间的访问
- 同一个包下的访问:不需要导包,直接使用即可
- 不同包下的访问:
- import导包后访问
- 通过全类名(包名+类名)访问,应用场景是当有多个包下存在相同的类名并且需要同时使用这些类时,可以使用全类名来区分
- 注意事项:
- package必须是程序的第一条可执行的代码
- import需要写在package下面
5.static关键字
a.概念
- static是静态的意思,是Java中的一个修饰符,可以修饰成员变量和成员方法
- static修饰的成员变量称为静态变量
- static修饰的成员方法称为静态方法
b.特点
- 被类的所有对象共享,这是我们判断是否使用静态关键字的条件
- 随着类的加载而加载,优先于对象存在,而对象是需要类被加载后才能创建
- 可以通过类名调用,也可以通过对象名调用,但是推荐使用类名
c.注意事项
- 静态方法只能访问静态成员,不能访问非静态成员
- 非静态方法可以访问静态成员,也可以访问非静态成员
- 静态方法中不能使用this关键字
6.继承
a.概念
- 继承是类与类之间的关系
- 子类继承了父类所有的非私有成员,除了构造器
- 子类可以直接访问父类中非私有的成员
- 子类不能直接访问父类的私有成员,但是可以通过父类中的公有方法来访问,例如set和get方法
- 使用
extends
关键字来实现继承 - 父类又称为基类、超类,子类又称为派生类
b.好处和弊端
好处:
- 提高代码的复用性、维护性
- 让类与类之间产生关系,这是多态的前提
弊端:
- 继承是侵入性的
- 降低代码灵活性,继承关系导致子类必须拥有父类非私有属性和方法,让子类自由的世界中多了些约束
- 增强了代码的耦合性,耦合是指代码与代码之间存在关联
使用继承的时机:
- 当类与类之间存在相同(共性)内容,并且产生了is a的关系,就可以考虑使用继承来优化代码
c.继承的特点
- Java只支持单继承,不支持多继承,但是支持多重继承
- 继承的成员变量访问特点
在子类方法中访问一个变量的顺序是
- 先在局部范围找
- 在子类成员范围中找
- 在父类成员范围中找
注意:如果在子类和父类中出现重名的成员变量,通过就近原则,会优先使用子类的,如果一定要使用父类的可以通过super关键字来区分
- 继承的成员方法的访问特点
通过子类对象访问一个方法的顺序是
- 子类范围找
- 父类范围找
d.super关键字
- super和this用法相似
- this代表本类对象的引用
- super代表父类存储空间的标识(可以理解为父类对象的引用)
- 区别
关键字 |
访问成员变量 |
访问成员方法 |
访问构造方法 |
this |
this.成员变量访问本类成员变量 |
this.成员方法(参数)访问本类成员方法 |
this(参数)访问本类构造方法 |
super |
super.成员变量访问父类成员变量 |
super.成员方法(参数)访问父类成员方法 |
super(参数)访问父类构造方法 |
e.方法重写和应用场景
- 当子类需要父类的功能,而子类的功能主体比父类又多了特有的功能或者完全不同的功能时,可以重写父类中的方法
- 注意:
- 方法重写(overrite):在继承体系中,子类出现和父类一摸一样的方法声明(方法名,参数列表,返回值类型)
- 方法重载(overlode):在同一个类中,方法名相同,参数列表不同,与返回值无关
- 父类中私有方法不能重写
- 父类中的静态方法,子类必须通过静态的方法进行重写(实际上静态方法不能被重写,但是子类中可以有一个与父类静态方法相同方法声明的静态方法,可以理解为子类将父类中同名的方法隐藏了起来,而不是方法重写)
- 父类中的非静态方法,子类也必须通过非静态方法重写
- 子类重写父类方法时,方法的访问权限必须大于或等于父类的
f.访问权限修饰符
修饰符 |
同一个类中 |
同一个包中的子类或其他类 |
不同包中的子类 |
不同包中的其他类 |
private |
✔ |
|||
默认 |
✔ |
✔ |
||
protected |
✔ |
✔ |
✔ |
|
public |
✔ |
✔ |
✔ |
✔ |
g.继承中构造方法的访问特点
- 子类中所有构造方法默认都会访问父类中的无参构造方法
- 子类在初始化的时候可能需要用到父类中的数据,如果父类没有初始化,子类就无法使用父类的数据,所以子类初始化前一定要先完成父类的初始化
- 构造方法的第一条语句默认都是super()
- 如果编写的类没有指定父类,系统也会自动继承Object(最顶层父类)
如果父类不提供无参的构造方法?解决方法是 - 子类通过super手动调用父类带参构造方法
- 子类通过this调用本类的其他构造方法,其他构造方法再通过super去调用父类的带参构造方法
- 注意:this()和super()在构造方法中必须处于第一行的位置,所以二者不能共存
7.抽象类
a.概述
- 抽象方法:将共性的行为(方法)抽取到父类后,发现该方法的实现逻辑无法在父类中给出具体的含义,该方法就可以定义为抽象方法
- 抽象类:如果一个类中存在抽象方法,那么该类必须声明为抽象类
b.注意事项
- 抽象类不允许实例化
- 抽象类中存在构造方法
- 抽象类的子类必须重写所有的抽象方法,或者子类也是一个抽象类
- 抽象类中可以没有抽象方法,但是存在抽象方法一定是抽象类
8.final关键字
- final可以修饰类,方法,变量
- final修饰类:表明该类是最终类,不能被继承
- final修饰方法:表明该方法是最终方法,不能被重写
- final修饰变量:
- 变量是基本数据类型:数据值不能改变
- 变量是引用类型:引用指向的地址不允许改变,但是地址里面的内容可以
- final修饰成员变量时建议直接赋值或者在构造方法中赋值
9.代码块
- 局部代码块
- 位置:方法内
- 特点:在方法内代码块外的地方访问不到代码块里边的变量
- 作用:限定变量生命周期,及早释放,提高内存利用率
- 构造代码块
- 位置:类中方法外
- 特点:每次构造方法执行时,都会执行改代码块中的代码,并且在构造方法执行前执行
- 作用:将多个构造方法中相同的代码抽取到代码块中,提高代码复用性
- 静态代码块
- 位置:类中方法外
- 特点:需要用static关键字修饰,随着类的加载而加载,只执行一次
- 作用:在类加载时做一些数据初始化的操作
- 执行顺序(对于一个类中):静态代码块->父类构造方法->构造代码块->子类构造方法
10.接口
- 接口使用interface关键字来定义
public interface 接口名{}
- 接口通过implements关键字来实现接口
public classs 类名 implements 接口名{}
- 实现接口的类需要需要重写接口中所有的抽象方法或者实现接口的类是抽象类
- 接口不能实例化
- 接口允许多实现,即一个类可以实现多个接口
- 接口中成员特点
- 成员变量:只能是常量,默认是public static final修饰
- 成员方法:只能是抽象方法,默认是public abstract修饰
- 没有构造方法
- JDK8中接口成员特点
- 默认方法
- 允许在接口中定义非抽象的方法,但是需要关键字default修饰,称为默认方法,作用是解决接口升级的问题
- 定义格式
public default 返回值类型 方法名(参数列表){}
- public是默认的可以省略,但default不能省略
- 默认方法不是抽象方法,不强制重写,但是允许重写,重写时去掉default关键字
- 如果某个类实现了多个接口,接口中出现相同方法声明的默认方法,则必须对该方法重写
- 静态方法
- 允许在接口中定义静态方法,需要关键字static修饰
- 定义格式
public static 返回值类型 方法名(参数列表){}
- public是默认的可以省略,static不能省略
- 接口中的静态方法只能通过
接口名.方法名()
来调用,不能通过实现该接口的类的类名和对象来调用
- JDK9中接口成员特点
- 接口中允许有私有方法,使用关键字private修饰
- 定义格式
private 返回值类型 方法名(参数列表){}
或者private static 返回值类型 方法名(参数列表){}
- 接口使用思路
- 如果某个类全是抽象方法,那么该类可以改进为一个接口
- 设计大面积接口更新(添加新的方法)时不想去修改每一个实现该接口的类,可以将更新的方法定义为带方法体的默认方法
- 希望默认方法调用更加简洁,可以考虑定义静态方法
- 默认方法中出现重复的代码,可以考虑抽取出一个私有方法
- 接口与接口的关系
- 可以单继承,使用关键字extends修饰
- 可以实现多继承,即一个接口可以继承多个接口
- 多继承时如果被继承的多个接口中存在相同方法声明的默认方法,则需要在继承时重写该方法
11.多态
a.多态指同一个对象,在不同时刻表现出来的不同形态
b.多态的前提和体现
- 有继承/实现关系
- 有方法重写
- 有父类引用指向子类对象
c.多态中成员访问特点
- 成员变量:编译时看=号左边(父类),运行时看=号左边(父类)
- 成员方法:编译时看=号左边(父类),运行时看=号右边边(子类)
- 成员变量和方法的访问不一样,理由是成员方法存在动态绑定机制,而成员变量没有
d.多态的好处和弊端
- 好处是提高了程序的扩展性
- 弊端是不能使用子类特有的功能
e.向上转型
- 父类引用指向子类对象
- 语法:
父类类型 引用名 = new 子类类型();
- 可以调用父类的所有成员,需要遵循访问权限
- 不能调用子类特有的成员
f.向下转型
- 父类引用转为子类对象
- 语法:
子类类型 引用名 = (子类类型)父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 父类的引用强转前必须指向的是子类类型的对象,否则出现ClassCastException
- 可以调用子类中所有的成员
g.instanceof关键字
- 用于判断对象的运行类型是否为XX类或XX类的子类
12.内部类
a.内部类指在一个类中定义一个类
b.成员内部类
- 位置:在类的成员位置
- 内部类可以直接访问外部类中的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
- 在外界创建内部类对象的语法是
外部类名.内部类名 引用名 = new 外部类名().new 内部类名();
- private修饰的成员内部类(私有成员内部类)
- 不能在外界创建该内部类,因为private修饰
- 只能在自己所在的外部类中创建对象来访问
- static修饰的成员内部类(静态成员内部类)
- 可以在外界创建
- 创建语法与普通的成员内部类不同
外部类名.内部类名 引用名 = new 外部类名.内部类名();
- 静态成员内部类中的静态方法使用语法是
外部类名.内部类名.方法名();
c.局部内部类
- 局部内部类是在方法中定义的内部类,所以外界无法访问,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
d.匿名内部类
- 匿名内部类本质上是一个特殊的局部内部类
- 前提:需要存在一个接口/类
- 语法是
new 类名/接口名(){重写方法};
- 匿名内部类是将(继承/实现)(方法重写)(创建对象)三个步骤放在了一步进行
- 实例:
public class Test { public static void main(String[] args){ /*情况1,当接口中只有一个方法*/ new Inner1(){ @Override public void show() { System.out.println("重写方法"); } }.show();//可以通过这种方式直接调用方法 /*情况2,当接口中有多个方法*/ Inner2 i = new Inner2(){//这里实际上是多态 @Override public void show1() { System.out.println("重写方法1"); } @Override public void show2() { System.out.println("重写方法2"); } }; //存在多个方法,不能使用情况1中的方式调用多个方法 i.show1(); i.show2(); } } //接口 interface Inner1{ public abstract void show(); } interface Inner2{ public abstract void show1(); public abstract void show2(); }
- 使用场景:当方法的形式参数是接口或者抽象类时可以将匿名内部类作为实际参数进行传参
13.Lambda表达式
- Lambda表达式是函数式编程思想的体现,函数式编程思想尽量忽略面向对象的复杂语法,强调“做什么”而不是以什么形式去做
- Lambda表达式格式:
(形式参数) -> {代码}
- 如果有多个参数,参数之间用逗号隔开,如果没有参数则留空
- ->代表指向动作
- 代码指具体实现的内容
- Lambda表达式使用前提
- 存在接口
- 接口中有且仅有一个抽象方法
- Lambda表达式分类
- 无参无返回值
- 有参无返回值
- 无参有返回值
- 有参数有返回值
- Lambda表达式省略模式
- 参数类型可以省略,如果有多个参数,要么不省略要么全部省略
- 如果参数有且仅有一个,那么参数的括号可以省略
- 如果代码块中只有一条语句,那么大括号和语句的分号可以省略,return关键字也可以省略
- Lambda表达式和匿名内部类的区别
- 所需类型不同
- 匿名内部类:可以是接口,可以是抽象类,还可以是具体的类
- Lambda表达式:只能是接口
- 使用限制不同
- 如果接口中只有一个抽象方法,则可以使用Lambda表达式也可以使用匿名内部类
- 接口中有多于一个抽象方法,只能使用匿名内部类,不能使用Lambda表达式
- 实现原理不同
- 匿名内部类:编译之后,产生单独的.class字节码文件
- Lambda表达式:编译之后,没有产生单独的.class字节码文件,对应的字节码会在运行的时候动态生成
常用API
1.==号比较注意事项
- ==用于比较基本数据类型时,比较的是内容
- ==用于比较引用类型时,比较的是地址
2.String类
- String类代表字符串,Java程序中所有字符串文字(例如“abc”)都被实现为此类的实例,也就是说,Java程序中所有的双引号字符串,都是String类的对象
- 字符串不可变,他们的值在创建后不能被更改
- 使用双引号创建字符串对象和使用new创建字符串对象的区别
- 使用双引号创建字符串对象时,系统检查该字符串是否存在于字符串常量池中,如果不存在则创建,如果存在则复用
- 使用new来创建字符串对象,每一次new都会申请一个内存空间,虽然字符串内容相同,但是地址值不同
- 双引号创建的字符串对象在字符串常量池中存储,构造方法创建的字符串对象在堆内存中存储
- 常见面试题
public class Test { public static void main(String[] args){ String s1 = "abc"; String s2 = "abc"; System.out.println(s1 == s2);//比较地址,输出true } }
- 字符串比较内容
- 使用equals()方法来比较内容,严格区分大小写,返回true或者false
- 如果比较字符串时需要忽略大小写,则可以使用equalsIgnoreCase()方法
3.StringBuilder类
- StringBuilder是一个可变的字符串类,可以将它看成一个容器
- StringBuilder可以提高字符串操作效率,体现在字符串拼接上
- 常用方法
- append()方法,完成字符串拼接,返回值是自己本身
- reverse()方法,返回相反的字符串序列
- toString()方法,把StringBuilder类型转为String类型
4.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) |
返回两个参数值最大的 |
public static int min(int a,int b) |
返回两个参数值最小的 |
public static double pow(double a,double b) |
返回a的b次幂的值 |
public static double random() |
返回值为double类型的正值,[0.0,1.0) |
5.System类
- 与系统相关的类,构造方法私有化所以不能被实例化,提供的方法都是静态方法
- 常用方法
方法名 |
说明 |
public static void exit(int status) |
终止当前运行的Java虚拟机,参数非零表示异常终止 |
public static long currentTimeMillis() |
返回当前距离1970年1月1日 00:00:00的毫秒值 |
public static arraycopy(源数组,起始索引,目的数组,起始索引,拷贝个数) |
数组copy |
6.Object类
- 所有类的超类,所有的对象(包括数组)都实现这个类中的方法
- toString()方法
- 直接打印一个对象就是打印这个对象的toString()方法的返回值
- Object类的toString()方法得到的是这个对象的字符串表示
全类名+@+哈希值的十六进制
- 一般情况下会对toString()方法进行重写,建议所有子类都重写该方法
- equals()方法
- 与另一个对象比较
- 默认是比较地址
- 重写可以比较内容,例如String类和Integer类
- 面试题
public class Test { public static void main(String[] args){ String s1 = "abc"; StringBuilder sb = new StringBuilder("abc"); //1.使用String中的equals()方法比较时会先判断参数是否是String类再判断内容,如果不是则直接返回false System.out.println(s1.equals(sb));//false //2.由于StringBuilder没有重写equals()方法,所以比较的是地址值 System.out.println(sb.equals(s1));//false } }
7.Objects类
- 构造方法被私有化所以不能实例化,提供的方法都是静态方法,注意与Object类的区别
- 常用方法
方法名 |
说明 |
public static String toString(Object o) |
返回参数中对象的字符串表示形式 |
public static String toString(Object o,String nullDefault) |
返回参数中对象的字符串表示形式,如果对象为空,返回第二个参数的默认字符串 |
public static boolean isNull(Object o) |
判断对象是否为空 |
public static boolean nonNull(Object o) |
判断对象是否为非空 |
8.BigDecimal类
- 常用两种构造方法
BigDecimal bd1 = new BigDecimal(数值参数);
BigDecimal bd2 = new BigDecimal(字符串);
- 四则运算(用于精确运算)
方法名 |
说明 |
public BigDecimal add(BigDecimal b) |
加法 |
public BigDecimal subtract(BigDecimal b) |
减法 |
public BigDecimal multiply(BigDecimal b) |
乘法 |
public BigDecimal divide(BigDecimal b) |
除法 |
- 一种特殊的方法
- 语法:
public BigDecimal divide(参数1,参数2,参数3);
- 用于除法中除不尽的情况
- 参数1表示另一个参与运算的数,参数2表示小数点后精确到的位数,参数3表示舍入模式
- 舍入模式有
BigDecimal.ROUND_UP
,进一法BigDecimal.ROUND_FLOOR
,去尾法BigDecimal.ROUND_HALF_UP
,四舍五入
- 总结
- BigDecimal用于精确计算
- 创建BigDecimal对象,构造方法使用参数类型为字符串的
- 除法除不尽使用三个参数的方法
9.包装类
- 八个包装类
基本数据类型 |
包装类 |
byte |
Byte |
short |
Short |
int |
Integer |
long |
Long |
float |
Float |
double |
Double |
char |
Character |
boolean |
Boolean |
- Integer类
- 该对象中包装了一个基本数据类型为int的值
- 获取一个Integer对象的方法
方法名 |
说明 |
public Integer(int value) |
根据int值创建Integer对象(过时) |
public Integer(String s) |
根据String值创建对象(过时) |
public static Integer valueOf(int i) |
返回表示指定int值的Integer对象 |
public static Integer valueOf(String s) |
返回表示指定String值的Integer对象 |
- 自动装箱和自动拆箱
Integer i = 100;//自动装箱,把基本数据类型转换为对应的包装类型
int ii = i;//自动拆箱,把包装类型转换为对应的基本数据类型
- 注意:在使用包装类型时如果有对应的操作,最好先判断是否为空
- int转String的方法
- 方式1:int值加空字符串,例如
int a = 2;
String s = a + "";
- 方式2:使用方法
public static String valueOf(int i);
这是String类中的静态方法
int a = 2;
String s = String.valueOf(a);
- String转int的方法
- 使用方法
public static int parseInt(String s);
这是Integer类中的静态方法
String s = "12";
int a = Integer.parseInt(s);
10.Arrays类
- 私有化构造方法所以不能实例化,提供的方法都是静态方法,用于数组的操作
- 常用方法
方法名 |
说明 |
public static String toString(数组) |
返回数组的字符串形式 |
public static void sort(数组) |
对给定的数组排序 |
public static int binarySearch(int[],int key) |
二分查找,注意数组必须有序,找到返回索引,找不到返回(-插入点-1) |
11.时间日期类
- 计算机中的时间原点是1970年1月1日 00:00:00
- Date类
- 表示一个特定的时间
- 构造方法
方法名 |
说明 |
public Date() |
创建一个Date对象,表示默认事件(计算机当前时间) |
public Date(long date) |
创建一个Date对象,表示指定时间(从时间原点开始,过了指定毫秒的时间,需要考虑时差问题) |
- 常用方法
方法名 |
说明 |
public long getTime() |
获取时间对象的毫秒值 |
public void setTime(long time) |
设置时间为参数内指定的时间 |
- SimpleDateFormat类
- 可以对Date对象进行格式化和解析
- 常用模式字母以及对应关系
巧记为“小大,小大,小小”
字母 |
含义 |
y |
年 |
M |
月 |
d |
日 |
H |
时 |
m |
分 |
s |
秒 |
- 构造方法
方法名 |
说明 |
public SimpleDateFormat() |
构造一个默认格式的对象 |
public SimpleDateFormat(String pattern) |
构造一个指定格式的对象 |
- 格式化和解析的方法
方法名 |
说明 |
public final String format(Date date) |
将日期格式化为指定格式的字符串 |
public Date parse(String source) |
将字符串解析为日期类对象 |
- JDK8新增的时间日期类
- LocalDate日期类、LocalTime时间类、LocalDateTime时间日期类,使用方法类似
- LocalDateTime创建方法
方法名 |
说明 |
public static LocalDateTime now() |
获取当前计算机的时间 |
public static LocalDateTime of(年,月,日,时,分,秒) |
使用指定的年月日时分秒创建一个对象 |
- LocalDateTime常用方法
方法名 |
说明 |
public int getYear() |
获取年 |
public int getMonthValue() |
获取月份(1-12) |
public int getDayOfMonth() |
获取月份中第几天(1-31) |
public Month getMonth() |
获取月份,返回值类型是枚举类 |
public int getDayOfYear() |
获取一年中第几天(1-366) |
public DayOfWeek getDayOfWeek() |
获取星期,返回值类型是枚举类 |
public int getMinute() |
获取分钟 |
public int getHour() |
获取小时 |
- LocalDateTime对象转为LocalDate对象或者LocalTime对象的方法
方法名 |
说明 |
public LocalDate toLocalDate() |
转换成一个LocalDate对象 |
public LocalTime toLocalTime() |
转换成一个LocalTime对象 |
- LocalDateTime的格式化和解析
JDK8中使用DateTimeFormatter类作为时间日期格式化器,使用的方法如下:public static DateTimeFormatter ofPattern(String pattern)
,作用是获取一个指定格式的DateTimeFormatter对象
方法名 |
说明 |
public String format(指定格式) |
将LocalDateTime对象格式化为指定的字符串 |
public static LocalDateTime parse(待解析的字符串,解析格式) |
将时间日期字符串解析为LocalDateTime对象 |
- LocalDateTime中plus系列的方法
注意:参数为正表示添加,参数为负表示减去
方法名 |
说明 |
public LocalDateTime plusYears(long years) |
添加或者减去年,返回新的LocalDateTime对象 |
public LocalDateTime plusMonths(long months) |
添加或者减去月,返回新的LocalDateTime对象 |
public LocalDateTime plusDays(long days) |
添加或者减去日,返回新的LocalDateTime对象 |
public LocalDateTime plusHours(long hours) |
添加或者减去时,返回新的LocalDateTime对象 |
public LocalDateTime plusMinutes(long minutes) |
添加或者减去分,返回新的LocalDateTime对象 |
public LocalDateTime plusSeconds(long seconds) |
添加或者减去秒,返回新的LocalDateTime对象 |
public LocalDateTime plusWeeks(long weeks) |
添加或者减去周,返回新的LocalDateTime对象 |
- LocalDateTime中minus系列的方法
- 该系列方法与plus系列方法相反,作用是减去对应的年月日时分秒周
- 参数为正数表示减去的值,负数表示加上的值
- 例如
public LocalDateTime minusYears(long years)
是minus系列方法之一,其他方法类似
- LocalDateTime中with系列方法
with系列方法用于直接修改LocalDateTime对象中的年月日时分秒
方法名 |
说明 |
public LocalDateTime withYear(int year) |
直接修改年,返回新的LocalDateTime对象 |
public LocalDateTime withMonth(int month) |
直接修改月,返回新的LocalDateTime对象 |
public LocalDateTime withDayOfMonth(int dayofmonth) |
直接修改一月中第几天,返回新的LocalDateTime对象 |
public LocalDateTime withDayOfYear(int dayofyear) |
直接修改一年中第几天,返回新的LocalDateTime对象 |
public LocalDateTime withHour(int hour) |
直接修改小时,返回新的LocalDateTime对象 |
public LocalDateTime withMinute(int minute) |
直接修改分钟,返回新的LocalDateTime对象 |
public LocalDateTime withSecond(int second) |
直接修改秒,返回新的LocalDateTime对象 |
- Period日期间隔类
常用方法
方法名 |
说明 |
public static Period between(LocalDate start,LocalDate end) |
返回两个日期的Period间隔类 |
public int getYears() |
返回间隔的年数 |
public int getMonths() |
返回间隔的月数 |
public int getDays() |
返回间隔的天数 |
public long toTotalMonths() |
返回间隔的总月数 |
- Duration时间间隔类
可以获取时间的间隔
方法名 |
说明 |
public static Duration between(开始时间,结束时间) |
计算两个时间的间隔,返回Duration对象 |
public long toSeconds() |
获取时间间隔的秒 |
public long toMillis() |
获取时间间隔的毫秒 |
public long toNanos() |
获取时间间隔的纳秒 |
异常
1.概念
- Exception类称为异常类,它的子类有RuntimeException(运行时异常)和其他子类(编译时异常)
- RuntimeException也有许多子类
- 编译时异常是在编译成class文件时必须要处理的异常,也称为受检异常
- 运行时异常是在运行字节码文件时出现的异常,也称为非受检异常
2.虚拟机默认处理异常的方式
- 在产生异常的代码处生成一个异常对象,接着查看是否有处理异常的代码,如果没有就交给调用者处理,如果调用者也不处理则最终交给虚拟机
- 虚拟机将异常的名称,异常原因以及异常出现的位置等信息输出在控制台
- 程序停止运行
3.使用throws方式处理异常
- 格式:
throws 异常类名
- 注意:写在方法的定义处,表示显式声明一个异常
- 这种异常异常处理方式主要用于编译时异常,如果声明的异常是运行时异常则声明处的代码可以省略
- throws声明一个异常表示当前方法内不处理这个异常,而是交给调用者处理,如果调用者也不处理则最终交给虚拟机采用默认的处理方式
4.使用throw抛出异常对象
- 格式:
throw new 异常类名();
- 注意:写在方法内部,表示在当前的代码处手动抛出一个异常,下面的代码不用再执行
- 给方法的调用者抛出了一个异常
5.使用try..catch方式处理异常
- 格式
try{ //可能出现异常的代码 }catch(异常类名 变量名){ //处理异常的代码 }
- 作用是可以让程序继续执行下去
- 如果try块中没有出现异常,则程序会将try块的代码执行完毕,然后跳过catch块的代码继续执行下去
- 如果try块中某行代码出现异常,则该行后面的代码不再执行转而去执行catch块中的代码,然后继续执行下去
- 如果try块中出现的异常没有被捕获,则直接在出现异常的代码处停止执行,将异常交给虚拟机处理,程序不再执行下去
- 如果try块中出现多个异常,那么只需要添加多个catch块处理,如果多个异常存在继承关系,那么父类catch块需要写在最下方,发生异常时只会匹配一个catch块
- 写多个catch块的好处是可以针对不同的异常有不同的处理方式
6.Throwable的成员方法
- Throwable是所有异常类的父类,所以所有异常对象都能使用该类的方法
- 常用方法
方法名 |
说明 |
public String getMessage() |
返回详细消息字符串 |
public String toString() |
返回简短描述字符串 |
public void printStackTrace() |
将异常信息打印输出在控制台 |
7.自定义异常
- 自定义异常的目的:为了使异常信息更加的见名知义
- 自定义异常类的步骤
- 定义类名,建议为xxExtception
- 继承Exception类或者RuntimeException类
- 写一个空参构造方法
- 写一个带参构造方法,例如
public XXException(String message){ super(message); }
集合
1.集合简易体系结构
graph TB;
集合-->Collection
集合-->Map
Collection-->List
Collection-->Set
Map-->HashMap
Map-->TreeMap
List-->ArrayList
List-->LinkedList
Set-->HashSet
Set-->TreeSet
- 上图中Collection、Map、List、Set都是接口,其余都是实现类
- 其中Collection及其实现类存放的是单列数据,Map及其实现类存放的是双列数据
- 该体系结构只列出较常使用的类
2.Collection
- Collection是单列集合的顶层接口,表示一组对象,这些对象也称为Collection元素
- JDK不提供此接口的任何直接实现类,而是提供更具体的子接口如List和Set
- 由于是接口,所以创建Collection对象需要使用多态的方式
- Collection常用方法
方法名 |
说明 |
boolean add(E e) |
添加元素 |
boolean remove(Object o) |
从集合中移除指定的元素 |
boolean removeif(Object o) |
根据条件进行删除 |
void clear() |
清空集合 |
boolean contains(Object o) |
判断集合中是否存在指定的元素 |
boolean isEmpty() |
判断集合是否为空 |
int size() |
集合的长度(元素个数) |
- 迭代器
- 迭代器Iterator,用于集合的遍历方式
Iterator<E> iterator();
是Iterable接口提供的一个抽象方法,用于返回一个迭代器对象,而Collection接口继承了该接口,所以实现Collection接口的实现类中根据每个实现类存放数据的方式不同重写各自的Iterator()方法,从而获得不同的迭代器对象- Iterator中的常用方法
boolean hasNext();
用于判断集合中当前位置是否有元素可以被取出E next();
获取当前位置的元素,并将迭代器对象移向下一个位置void remove();
删除上一次调用next()方法时返回的元素,每次调用next()方法只能调用一次此方法
- 迭代器使用案例
Iterator<String> it = collection.iterator();//获取集合中的迭代器对象 while(it.hasNext()){//当前索引位置是否存在元素 String s = it.next();//将索引位置处的元素获取出来,并且将索引移动到下一位置 System.out.println(s);//打印输出 }
- 增强for循环
- 作用是简化数组和Collection集合的遍历
- 内部原理依然是Iterator迭代器
- 实现Iterable接口的类才可以使用增强for循环和Iterator迭代器
- 注意避免在增强for循环中对数组或集合元素删除或更改
- 三种循环的使用场景
需要操作索引使用普通for循环
需要遍历过程中删除元素,使用迭代器
仅仅遍历元素使用增强for循环
3.List
- 存取有序,存元素的顺序和取元素顺序一致,类似数组
- 可以根据索引精确操作元素
- 与Set集合不同,允许存放重复的元素
- List中特有的方法
方法名 |
说明 |
void add(int index,E element) |
在指定位置插入元素 |
E remove(int index) |
删除指定位置的元素并返回 |
E set(int index,E element) |
修改指定位置的元素并返回被修改的元素 |
E get(int index) |
返回指定索引处的元素 |
- List的实现类ArrayList
- 底层数据结构是数组,查询快,增删慢
- 实现自动扩容的机制
- 每次扩容的容量是之前的1.5倍
- List的实现类LinkedList
- 底层数据结构是双向链表,查询慢,增删快
- LinkedList特有方法
方法名 |
说明 |
public void addFirst() |
在开头插入元素 |
public void addLast() |
在末尾插入元素 |
public E getFirst() |
获取第一个元素 |
public E getLast() |
获取最后一个元素 |
public E removeFirst() |
删除并返回第一个元素 |
public E removeLast() |
删除并返回最后一个元素 |
4.Set
- Set集合可以去除重复的元素,而且不保证元素存取顺序
- Set集合没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取、删除元素
- Set集合可以通过迭代器或者增强for循环遍历
- Set的实现类TreeSet
- 底层数据结构是红黑树
- 特点是可以将元素按照规则进行排序,如果想要使用TreeSet需要制定排序规则
- 自然排序
- 使用空参构造器创建TreeSet集合时,会默认调用自然排序
- 存放在TreeSet集合的类需要实现Comparable接口然后重写里面的compareTo()方法,一般用在自定义的类中,比如定义一个学生类,按照学生的年龄排序
- 例如
class Student implements Comparable<Student> { private String name; private int age; @Override public int compareTo(Student o) { int result = this.age-o.age;//this表示当前需要存入的元素,o表示集合中存在的元素 return result; } } ```java class Student implements Comparable<Student> { private String name; private int age; @Override public int compareTo(Student o) { int result = this.age-o.age; result = result == 0 ? this.name.compareTo(o.name) : result;//如果年龄出现相同的情况可以比较姓名,按照字母字典排序,注意这里使用到了String类的CompareTo()方法 return result; } } • 比较器排序 • TreeSet的带参构造方法使用的是比较器排序对元素进行排序 • 比较器排序就是在TreeSet的带参构造方法中传入一个Comparator接口的实现类对象,重写Compare(T o1,T o2)方法 • 例如 public class Test { public static void main(String[] args) throws ParseException { TreeSet<Student> students = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { int result = o1.getAge()-o2.getAge();//比较年龄 result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;//年龄相同时比较姓名 return result; } }); Student s1 = new Student("zhansan",18); Student s2 = new Student("wangwu",18); Student s3 = new Student("lisi",22); Student s4 = new Student("wuliu",16); students.add(s1); students.add(s2); students.add(s3); students.add(s4); System.out.println(students); //输出:[{wuliu,16}, {wangwu,18}, {zhansan,18}, {lisi,22}] } } class Student implements Comparable<Student> { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } @Override public String toString(){ return "{"+ this.getName()+ ","+ this.getAge()+ "}"; } }
- 两种比较方式总结
- 自然排序:自定义类实现Comparable接口,重写compareTo()方法,根据返回值进行排序
- 比较器排序:创建TreeSet对象的时候传递Comparator的实现类对象,重写compare()方法,根据返回值排序
- 使用的时候,默认使用自然排序(当TreeSet集合中存的元素对象已经实现了Comparable接口并且重写compareTo()方法),当自然排序不满足需求时(当TreeSet集合中存的元素对象是自定义的类并且没有实现Comparable接口)必须使用比较器排序,还有一种情况是自定义的类已经实现了Comparable接口并且重写了compareTo()方法,那么也可以通过比较器排序来改变排序规则
- 两种方式中,关于compareTo()方法和compare()方法的返回值有如下规则
- 如果返回值是负数,表示当前存入的元素是较小值,存左边
- 如果返回值是0,表示当前存入的元素跟集合中的元素重复了,不存
- 如果返回值是正数,表示当前存入的元素是较大值,存右边
- Set的实现类HashSet
- 底层数据结构是哈希表
- 不保证数据的存取顺序一致
- 没有带索引的方法所以不能使用普通的for循环遍历
- 由于是Set集合,所以元素唯一
- 哈希值
- 哈希值是JDK根据对象的地址或者属性值,算出来的int类型的整数
- Object类中有一个
public int hasCode()
方法可以根据对象的地址值计算并返回哈希值,同一个对象多次调用这个方法返回的哈希值是一样的 - 如果没有重写HashCode()方法,那么默认哈希值是根据对象的地址计算得出,不同的对象哈希值不同
- 如果重写了HashCode()方法,则哈希值是根据对象的属性计算得出,如果有不同的对象属性值一样那么哈希值相同
- JDK7版本中HashSet底层原理
- 底层结构:哈希表(数组加链表)
- 数组默认长度为16,加载因子为0.75,当数组存储了16*0.75=12个位置时则扩容为原来容量的两倍
- 存入元素时,首先获取元素的哈希值,计算出在数组中应该存放的索引,接着判断该索引处是否为null,如果是null直接添加,如果不是null则与该索引处的链表中每一个元素通过equals()方法比较属性值,只要存在一个相同的就不存,否则存入该链表
- JDK8版本中HashSet底层原理
- 底层结构:哈希表(数组加链表加红黑树)
- 当链表长度超过8时将链表转为红黑树,目的是提高效率
- 存储流程与之前相同,只是当存入的是红黑树时不需要比较完全部元素
- 结论:如果HashSet要存储自定义的类,那么需要重写HashCode()方法和equals()方法
5.Map
interface Map<K,V>
是双列集合的顶层接口,K表示键的数据类型,V表示值的数据类型- 键不能重复,值允许重复,键值是对应的,一个键只能对应一个值
- (键+值)这个整体称为“键值对”或者“键值对对象”,在Java中叫做“Entry对象”
- 由于Map是接口,所以创建Map对象可以通过多态的方式
- Map常用方法
方法名 |
说明 |
V put(K k,V v) |
添加元素 |
V remove(K k) |
根据键删除键值对元素,并且将被删除的键值对中的值返回 |
void clear() |
清空所有的键值对 |
boolean containsKey(K k) |
判断集合是否包含指定的键 |
boolean containsValue(V v) |
判断集合是否包含指定的值 |
boolean isEmpty() |
判断集合是否为空 |
int size() |
返回集合的长度(键值对个数) |
Set keySet() |
获取所有键的集合 |
V get(K k) |
根据键获取值 |
Set<Map.Entry<K,V>> entrySet() |
获取所有键值对对象的集合 |
- put()方法注意事项:如果添加的键值对中的键在集合中不存在,则直接添加进去,返回的是null;如果添加的键值对中的键在集合中已经存在,那么会替换掉键值对中原来的值,并且将被替换的值返回
- Map遍历方式1
- 由keySet()方法获取所有的键,然后遍历键,在遍历键的同时通过get()方法实现键值对的遍历
- 例如
Set<String> set = map.keySet(); for(String key:set){ map.get(key); }
- Map遍历方式2
- 由entrySet()方法获得键值对对象的集合,Map.Entry<K,V>接口中有两个方法
K getKey()
和V getValue()
分别可以获取键值对的键和值 - 例如
Set<Map.Entry<String, String>> entries = map.entrySet(); for(Map.Entry<String,String> entry:entries){ String k = entry.getKey(); String v = entry.getValue(); }
- Map的实现类HashMap
- HashMap底层是哈希表
- 存放的方式与HashSet类似,但是需要将键值对封装成Entry对象再进行存放
- 依赖HashCode()方法和equals()方法保证键的唯一,由键的哈希值决定存放位置,由键的equals()方法判断元素是否重复
- 如果键的类型是自定义的类,那么需要重写HashCode()方法和equals()方法
- Map的实现类TreeMap
- TreeMap底层是红黑树
- 依赖自然排序或比较器排序,对键进行排序
- 如果键是自定义类型的对象,则该类需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则
6.可变参数
- 可变参数是指形参个数可以改变
- 格式:
修饰符 返回值类型 方法名(数据类型...变量名){}
- 范例:
public void func(int...arr){}
- 可变参数其实是一个数组
- 如果方法参数还含有其他参数,可变参数需要放在最后边
7.创建不可变集合
- 在List、Set、Map接口中,存在静态方法of(可变参数),可以创建一个不可变集合
- 这个不可变集合不能添加、删除、修改
- 可以结合集合的带参构造,实现集合的批量添加
- Map接口中还有一个ofEntries(可变参数)方法可以提高代码阅读性,作用和Map.of()相同,但是ofEntries()中的可变参数会先封装成一个Entry对象
- 范例
List<String> list = List.of("a","b","c");//利用可变参数创建不可变集合
ArrayList<String> list1 = new ArrayList<>(list);//利用不可变集合创建集合
8.Stream流
- Stream流中的三类方法
- 获取Stream流的方法:创建一条类似于流水线的流,并且将数据放上流中准备进行处理
- 中间方法:流水线上的操作,一次操作结束后还能继续进行其他操作
- 终结方法:一个Stream流只能有一个终结方法,是流水线上最后的一个操作
- 获取Stream流的方法
- 单列集合:可以使用Collection接口的默认方法stream()生成流
default Stream<E> stream()
- 双列集合:间接生成流,可以先通过keySet或entrySet获取一个Set集合,再获取Stream流
- 数组:Arrays中的静态方法stream()生成
- 同种类型的多个数据:使用Stream.of(T...value)方法获取,其中参数是可变参数
- 常用中间方法
Stream<T> filter(Predicate<? super T> predicate)
,用于按照某规则过滤流中的数据,其中Predicate接口含有抽象方法test(T t),形参是流中的数据,重写该方法时可以指定过滤规则,当返回值是true时保留数据,当false时过滤掉数据,由于Predicate是只含有一个抽象方法的接口,所以可以使用Lambda表达式,也可以使用匿名内部类Stream<T> limit(long maxsize)
,将流中前maxsize个数据截取出来Stream<T> skip(long n)
,跳过n个数据static Stream<T> concat(Stream a,Stream b)
,Stream中的静态方法,用于合并两个流Stream<T> distinct()
,去除流中重复的数据,依赖hashCode()方法和equals()方法
- 常用终结方法
void forEach(Consumer<? super T> consumer)
,用于遍历流中的数据,其中Consumer接口含有一个抽象方法accpet(T t),该方法的形参就是流中的数据,所以可以在重写该方法时设计对流中数据的操作,比如打印输出,由于该接口只有一个抽象方法所以可以使用Lambda表达式long count()
,返回流中的元素个数
- Stream流无法修改集合或者数组等数据源中的数据
- Stream流中的收集操作
R collect(Collector collector)
,用于将流中的数据收集一个集合中并返回这个集合,集合的类型由Collector决定,R表示集合类型- Collectors工具类
- 该类可以由静态方法获取collector对象
public static <T> Collector toList()
,收集到List集合中public static <T> Collector toSet()
,收集到Set集合中public static <T> Collector toMap(Function keyMapper,Function valueMapper)
,收集到Map集合中,注意toMap()方法中的两个参数,可以理解为获取键和值的方式,可以使用Lambda表达式