一、Static静态关键字
1.static是什么
- static是静态的意思,可以修饰成员变量和成员方法。
- static修饰成员变量表示该成员变量在内存中只存储一份,可以被共享访问、修改。
2.成员变量
成员变量分为静态成员变量和实例成员变量,区别是有无static修饰
- 静态成员变量(有static修饰,属于类,内存中加载一次):常表示在线人数等需要被共享的信息,可以被共享访问。
- 实例成员变量(无static修饰,存在于每个对象中):常表示姓名name、年龄age等属于每个对象的信息。
访问静态成员变量:
访问实例成员变量:
3.static修饰成员变量的内存原理
静态成员变量在类创建时创建,存储在堆内存中。
可以通过类名直接访问,也可以通过类的实例间接访问
4.static修饰成员方法的基本用法
成员方法的分类:
- 静态成员方法(有static修饰,归属于类),建议用类名访问,也可以用对象访问。
- 实例成员方法(无static修饰,归属于对象),只能用对象访问
使用场景:
- 表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
- 如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。
5.static注意事项
- 静态方法只能访问静态的成员,不能直接访问实例成员。
- 实例方法可以访问静态的成员,也可以访问实例成员。
- 静态方法中不可以出现this关键字。
6.static应用知识:工具类
工具类: 类中都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员共同使用的,如math类等。
工具类的好处: 调用方便、提高代码复用性(一次编写,处处可用)
为什么工具类中的方法不用实例方法做?
-
实例方法需要耗费内存空间去实例化。工具类提供的是静态方法,通过类就能调用,所以不必浪费内存去实例化工具类对象。
-
由于工具类里都是静态方法,直接用类名即可访问,因此,工具类无需创建对象,建议将工具类的构造器进行私有。
7.static应用知识:代码块
代码块概述:
- 代码块是类的五大成分之一(成员变量、构造器、方法、代码块、内部类),定义在类中方法外。
- 在Java类下,使用
{ }
括起来的代码被称为代码块。
代码块分为:
- 静态代码块
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次。
使用场景:在类加载的时候做一下静态数据初始化的操作,以便后续使用。
- 构造代码块(了解即可)
格式:{}
特点:每次创建对象,调用构造器执行时,都会执行该代码中的代码,并且在构造器执行前执行。
使用场景:初始化实例资源。
8.static应用知识:单例设计模式
(1)设计模式
- 开发中经常遇到一些问题,一个问题通常有n种解法,但其中肯定有一种是最优的,这个最优的解法别人总结出来了,称之为设计模式。
- 设计模式有20多种,对应20多种软件开发中会遇到的问题。
设计模式主要学习两点:
- 第一:这种模式用来解决什么问题。
- 第二:遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。
(2)单例模式
- 可以保证系统中,应用该模式的这个类只有一个实例,即一个类永远只能创建一个对象。
- 例如任务管理器对象只需要一个就可以解决问题了,这样可以节省内存空间。
单例的实现方式很多:
- 饿汉单例模式
- 懒汉单例模式
- …
- …
饿汉单例设计模式
在用类获取对象的时候,对象已经提前为你创建好了。
设计步骤:
- 定义一个类,把构造器私有。
- 定义一个静态变量存储一个对象。
懒汉单例设计模式
在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
设计步骤:
- 定义一个类,把构造器私有。
- 定义一个静态变量存储一个对象。
- 提供一个返回单例对象的方法
二、继承
1.继承概述
什么是继承:
- Java中提供一个关键字extends,可以用它来让一个类和另一个类建立起一种父子关系。
- 提高代码复用性,减少代码冗余,增强类的功能扩展性。
继承的格式: - 子类 extends 父类
继承后子类的特点:
- 子类继承父类,子类可以得到父类的属性和行为,子类可以使用。
- Java中子类更强大
2.继承的设计规范、内存原理
继承设计规范:
- 子类们相同特征(共性属性、共性方法)放在父类中定义,子类独有的属性和行为应该定义在子类自己里面。
- 为什么:如果子类的独有属性、行为定义在父类中,会导致其他子类也会得到这些属性和行为,这不符合面向对象逻辑。
继承内存原理: 同一个对象,不同的空间存储
3.继承的特点
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器(父类构造器用于初始化父类对象)。
- Java是单继承模式:一个类只能继承一个直接父类。
- Java不允许多继承,但是支持多层继承。
- Java中所有的类都是Object类的子类。
1.子类是否可以继承父类的私有成员?
- 可以,只是不能直接访问。
2.子类是否可以继承父类的静态成员?
- 有争议
- 子类可以直接使用父类的静态成员(共享)
- 子类不能继承父类的静态成员(共享并非继承)
4.继承后:成员变量、成员方法的访问特点
在子类方法中访问成员(成员变量、成员方法)满足: == 就近原则==
- 先子类局部范围找
- 然后子类成员范围找
- 然后父类成员范围找,如果父类范围还没有找到则报错。
如果子父类中,出现了重名的成员,会优先使用子类的。此时如果一定要在子类中使用父类:
- 可以通过super关键字,指定访问父类的成员。
5.继承后:方法重写
方法重写:
在继承体系中,子类出现了和父类中一模一样的方法声明,则称子类这个方法是重写的方法。
方法重写的应用场景:
- 当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。
- 子类可以重写父类中的方法。
override重写注解:
- @Override是放在重写后的方法上,作为重写是否正确的校验注解。
- 加上该注解后如果重写错误,编译阶段会出现错误提示。
- 建议重写方法都加@Override注解,代码安全、优雅
方法重写注意事项和要求:
- 重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。
- 私有方法不能被重写。
- 子类重写父类方法时,访问权限必须大于等于父类(缺省<protected<public)
- 子类不能重写父类的静态方法,如果重写会报错。
6.继承后构造器的特点
子类继承父类后构造器的特点:
- 子类中所有的默认构造器都会优先访问父类中的无参构造器,再执行自己,
为什么? - 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
- 子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。
怎么调用父类构造器的?
- 子类构造器的第一行默认语句都是:super(),不写也存在。
super调用父类有参构造器的作用:
- 初始化继承自父类的数据。
如果父类中没有无参构造器,只有有参构造器,会出现什么现象?
- 会报错,因为子类默认是调用父类无参构造器的。
如何解决?
- 子类构造器中可以通过书写super(…),手动调用父类的有参构造器。
7.this、super使用总结
this: 代表本类对象的引用;
==super:==代表父类存储空间的表示。
this(…)和super(…)使用注意点:
- 子类通过this(…)去调用本类的其他构造器,本类其他构造器会通过super去手动调用父类的构造器,最终还是会调用父类的构造器的。
- 注意:this(…)super(…)都只能放在构造器第一行,所以二者不能共存在同一个构造器中。
三、包、常量、权限修饰符、接口、抽象类
1.包
什么是包?
- 包是用来分门别类的管理各种不同类的,类似于文件夹,建包利于程序的管理和维护。
- 建包的语法格式:package 公司域名倒写.技术名称。包名建议全部英文小写,且具备意义
- 建包语句必须在第一行,一般IDEA工具会帮助创建
导包: - 相同2包下的类可以直接访问,不同包下的类必须导包才可以使用!导包格式:import 包名.类名;
- 假如一个类中需要用到不同类,而这两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问
2.权限修饰符
什么是权限修饰符:
- 权限修饰符:是用来控制一个成员能够被访问的范围。
- 可以修饰成员变量,方法,构造器,内部类。不同权限修饰符的成员能够被访问的范围将受到限制。
权限修饰符的分类和具体作用范围: - 权限修饰符:有四种作用范围有小到大(private->缺省->protected->public)
3.final
final的作用
- final关键字是最终的意思,可以修饰(类、方法、变量)
- 修饰类:表明该类是最终类,不能被继承。
- 修饰方法:表明该方法是最终方法,不能被重写。
- 修饰变量:标水该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)。
final修饰变量的注意
- final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
- final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的
4.常量
- 常量:
- 常量是使用了public static final修饰的成员变量,必须有初始值,而且执行的过程中其值不能被改变。
- 常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性。
- 变量命名规范:英文单词全部大写,多个单词下划线连接起来。
常量的执行原理: - 在编译阶段会进行“宏替换”,把使用常量的地方全部替换成真实的字面量。
- 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的。
5.枚举
枚举概述
- 枚举是Java中的一种特殊类型
- 枚举的作用:“是为了做信息的标志和信息的分类”。
定义枚举类的格式:
枚举的特征: - 枚举类都是继承了枚举类型:java.lang.Enum
- 枚举都是最终类,不可以被继承。
- 枚举类的构造器都是私有的,枚举对外不能创建对象。
- 枚举类的第一行默认都是罗列枚举对象的名称的。
- 枚举类相当于是多例模式。
实例:
6.抽象类
抽象类简介
- 在Java中abstract是抽象的意思,可以修饰类,成员方法。
- abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法。
注意事项:
- 抽象方法只有方法签名,不能声明方法体。
- 一个类中如果定义了抽象方法,这个类必须声明成抽象类,否则报错。
抽象的使用场景:
- 抽象类可以理解成不完整的设计图,一般作为父类,让子类来继承。
- 当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成。此时这个类就可以声明成抽象类。
抽象类特征和注意事项
- 类有的成员(成员变量、方法、构造器)抽象类都具备。
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
- 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
- 不能用abstract修饰变量、代码块、构造器。
- 最重要的特征: 得到了抽象方法,失去了创建对象的能力(有得有失)
final和abstract是什么关系?
- 互斥关系
- abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。
- 抽象方法定义通用功能让子类重写,final定义的方法类不能重写。
7.接口
接口概述、特点
接口:一种规范,规范一定是公开的。
jdk 8之前接口中只能有抽象方法和常量。
- 变量:
String NAME = "张三"
(由于接口体现规范思想,规范默认都是公开的,所以代码层面,public static final 可以省略) - 抽象方法:(由于接口体现规范思想,规范默认都是公开的,所以代码层面,public static final 可以省略)
void run();
public abstract void run();
接口的格式如下:
接口的基本使用:被实现
接口的用法:
- 接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类。
- 接口可以被类单实现,也可以被类多实现。
接口实现的注意事项:
- 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。
接口与接口的关系:多继承
- 类和类的关系:单继承。
- 类和接口的关系:多实现。
- 接口和接口的关系:多继承,一个接口可以同时继承多个接口。
接口多继承的作用:
- 规范合并,整合多个接口为同一个接口,便于子类实现。
四、多态
1.多态
多态: 多态是指同一名字的事物可以完成不同的功能,例如有几个相似而不完全相同的对象,有时人们要求在向它们发出同一个消息时,它们的反应各不相同,分别执行不同的操作
Java中的多态: 指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义。
多态的常见形式:
- 父类类型 对象名称 = new 子类构造器;
- 接口 对象名称 = new 实现类构造器;
多态中成员的访问特点:
- 方法调用:编译看坐边,执行看右边。
- 变量调用:编译和运行都看左边。(“多态” 字如其名,就是为实现子类的多种状态,方法自然要 “看右边” 的子类;而变量涉及到父类初始化问题,所以要 “看左边” 的父类)
多态侧重行为多态,所以父类引用指向子类对象,在执行的过程中执行子类重写的方法
Animal a = new Cat();
Cat c = new Cat(); Animal a2 = c;
Cat c2 = (Cat)a;//向下转型(强制转换)
多态的前提:
- 有继承/实现关系;有父类引用指向子类对象;有方法重写。
2.多态的优势
- 在多态形势下,右边对象可以实现解耦合,便于扩展和维护。
Animal a = new Dog();//当需求改变,子对象Dog可以很方便的更改为父类Animal下的任意子对象
a.run();//后续业务行为随对象而变,后续代码无需修改
- 定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。
多态下的问题:不能使用子类的独有功能
3.多态下引用数据类型的类型转换
自动类型转换(从子到父): 子类对象赋值给父类类型的变量指向。
强制类型转换(从父到子):
- 必须进行强制类型转换:子类 对象变量 = (子类)父类类型的变量
- 作用:可以解决多态下的劣势,可以实现调用子类独有的功能。
- 注意:如果转型后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException
Java建议强转前使用instanceof判断当前对象的真实类型,再进行转换
代码示例:
五、内部类
1.静态内部类[了解]
静态内部类:有static修饰,属于外部类本身。
它的特点和使用与普通类完全一致,类有的成分它都有,只是位置在别人里面而已
定义静态内部类:
用静态内部类实例对象:
、
2.成员内部类[了解]
成员内部类:
- 无static修饰,属于外部类对象。
- jdk16之前,成员内部类中不能定义静态成员,jdk16开始也可以定义静态成员了
3.局部内部类[鸡肋,了解]
4.匿名内部类[重点]
匿名内部类:
- 本质上是一个没有名字的局部内部类,定义在方法中、代码块中等。
- 作用 :方便创建子类对象,最终目的是为了简化代码编写。
格式:
new 类 | 抽象类名 | 或者接口名(){
重写方法;
}
实例:
匿名内部类的特点:
- 匿名内部类是一个没有名字的内部类
- 匿名内部类写出来就会产生一个匿名内部类的对象
- 匿名内部类的对象类型相当于是当前new的那个类的类型的子类类型
5.匿名内部类的使用形式
匿名内部类可以作为方法的实际参数进行传输。
注意:接口不能实例化
使用匿名内部类(重写接口)简化以上代码:
甚至可以把匿名内部类直接放进方法里:
开发中不是我们主动去定义匿名内部类的,而是别人需要我们写或者我们可以写的时候才会使用。
匿名内部类的代码可以实现代码进一步的简化(回扣主题)
六、常用API
①Object类
- Java中的所以类都直接间接继承了Object类,Object类是java中的祖宗类。
- 因此一切子类都可以直接使用Object类的方法。
Object类的常用方法:
toString方法
默认返回对象的地址
父类toString()方法存在的意义就是为了被子类重写,以便返回对象的内容,而不是地址
重写toString():
快捷键:右键->generate(Alt+Insert)-> ToString()
equals方法
默认比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false(与“==”作用一致)
父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则
重写equals方法:
getClass()用于获取类的类型
Objects类:比较结果更安全
②StringBuilder类
- StringBuilder是一个可变的字符串类(可以看做是一个对象容器)。
- 作用: 提供字符串的操作效率,如拼接、修改等。
构造器:
常用方法:
StringBuilder支持链式编程
注意:StringBuilder只是拼接字符串的手段,效率高。但最终的目的还是要恢复成String
为什么拼接、反转字符串建议使用StringBuilder?
- String:内容不可变,拼接字符串性能差。
- StringBuilder:内容可变,拼接性能好
定义字符串使用String,拼接、修改等操作字符串使用StringBuilder
③Math类
- 包含执行基本数字运算的方法,Math类中没有提供公开的构造器。
- 如何使用类中的成员? 看类的成员是否都是静态的,如果是,通过类名就可以直接调用
常用方法:
④System类
System的功能是通用的,都是直接用类名调用,所以System不能被实例化。
常用方法:
⑤bigDecimal类
bigDecimal用于解决浮点型运算精度失真的问题
使用步骤:
- 创建对象BigDemical封装浮点型数据(最好的方式是调用方法)
public static BigDecimal valueOf(double val): 包装浮点数成为BigDecimal对象
常用API:
直接使用BigDecimal的构造器也会出丢失精度的情况,最好的办法是调用BigDecimal的valueOf()方法:BigDecimal a1 = BigDecimal.valueOf(a);
BigDecimal只是方便精确运算,最终使用还是要转换回Double类型: doubleValue()
double rs = a1.doubleValue();
强制精度运算(可以用于处理无限小数或数位截断)
例如10除3:
BigDecimal a11 = BigDecimal.valueOf(10.0);
BigDecimal b11 = BigDecimal.valueOf(3.0);
BigDecimal c11 = a11.divide(b11,2,RoundingMode.HALF.UP);//只留两位小数
/*
参数一:除数 参数二:保留小数位数 参数三:舍入模式
舍入模式:
ROUND_UP:进位制:不管保留数字后面是大是小(0除外)都会进1
ROUND_DOWN:保留设置数字,后面所有直接去除
ROUND_HALF_UP:根据保留数字后一位>=5进行四舍五入
ROUND_HALF_DOWN:根据保留数字后一位>5进行四舍五入
*/
⑥日期类
Date
- Date类的对象在Java中代表当前所在系统的此刻日期时间
Date的时间的2种形式: - 日期对象:当前系统的此刻日期时间
- 时间毫秒值:从1970年1月1日 00:00:00到此刻的总毫秒值
时间毫秒值——>日期对象
SimpledateFormat类
- 可以对Date对象或时间毫秒值格式化成需要的格式
- 可以把字符串的时间形式解析成日期对象
构造器与方法:
实例:
解析日期字符串:parse()
Calendar
- Calendar代表系统此刻日期对象的日历对象
- Calendar是一个抽象类,不能直接创建对象
直接调用getInstance()
方法创建对象:
Calendar cal = Calendar.getInstance();
常用方法:
field:字段,如Calendar.DAY、Calendar.YEAR、Calendar.DAY_OF_YEAR等
缺点:修改后对象本身表示的时间将产生变化
JDK8新增日期类
- 新增的API严格区分了时刻、本地日期,并且对日期和时间进行运算更加方便。
- 新API的类型几乎全部是不变类型(与String类似),可以放心使用不必担心被修改。
LocalDate、LocalTime、LocalDateTime:
- 分别表示日期、时间和日期时间对象,这些类的实例都是不可变对象。
- 这三个类构建对象和API都是通用的。
构建对象的方式:
创建对象后直接调方法即可使用:
转换:
修改:
Instant
- Instant类由一个静态的工厂方法now()可以返回当前时间戳。
- 时间戳是包含日期与时间的,与java.util.Date类似(事实上Instant就是类似JDK8以前的Date)
- Instant和Date这两个类可以进行转换。
DateTimeFormatter
日期时间格式化
Duration/Period
- 用来计算日期间隔
- 主要是Period类方法getYears(),getMonths()和getDays()来计算,只能精确地年月日。
- 用于LocalDate之间的比较。
- Duration:用于计算两个“时间”间隔。
- Period:用于计算两个“日期”间隔。
ChronoUnit
时间差计算
⑦包装类
包装类: 8种基本数据类型对应的引用类型。
为什么提供包装类?
- 为了实现一切皆对象。
- 集合和泛型只支持包装类型。
自动装箱: 基本类型的数据和变量可以直接赋值给包装类型的变量。
自动拆箱: 包装类型的变量可以直接赋值给基本数据类型的变量。
包装类特有功能:
- 包装类的变量的默认值可以是null,容错率更高。
- 可以把基本数据类型的数据转换成字符串类型(用处不大)
- 可以把字符串类型的数值转换成真实的数据类型(重点)
valueOf()也可以实现该功能:
⑧正则表达式
- 正则表达式可以用一些规定的字符来制定规则,并用来检验数据格式的合法性。
匹配规则
在方法中的应用