static
static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量
1.被static修饰的成员变量,叫做静态变量
特点:
① 被该类所有对象共享
② 不属于对象,属于类
③ 随着类的加载而加载,优先于对象存在
调用方式:
① 类名调用(推荐)
② 对象名调用
static内存图:
由图中可以看出两个对象拥有共同的静态变量
2.被static修饰的成员方法,叫做静态方法
特点:
① 多用在测试类和工具类中
② Javabean类中很少会用
调用方式:
① 类名调用(推荐)
② 对象名调用
名词:
① Javabean类:用来描述一类事物的类。比如,Student,Teacher,Dog,Cat等
② 测试类:用来检查其他类是否书写正确,带有main方法的类,是程序的入口
③ 工具类:不是用来描述一类事物的,而是帮我们做一些事情的类
工具类需遵守的规则:
① 类名见名知意
② 私有化构造方法
③ 方法定义为静态
static的注意事项:
① 静态方法只能访问静态变量和静态方法
② 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
③ 静态方法中是没有this关键字的
内存图解释:
继承
Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系
public class Student extends Person {}
Student称为子类(派生类),Person称为父类(基类或超类)
使用继承的好处:
① 可以把多个子类中重复的代码抽取到父类中,提高代码的复用性
② 子类可以在父类的基础上,增加其他的功能,使子类更强大
什么时候用继承:当类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就可以考虑使用继承,来优化代码
继承的特点:
① Java只支持单继承(一个子类只能继承一个父类),不支持多继承(子类不能同时继承多个父类),但支持多层继承
② 每一个类都直接或者间接继承于Object
③ 子类只能访问父类中非私有的成员
子类能继承父类中的哪些内容:
非私有 | 私有 | |
---|---|---|
构造方法 | 不能 | 不能 |
成员变量 | 能 | 能(但不能直接调用) |
成员方法 | 能(虚方法表) | 不能 |
成员变量的继承:
私有的成员变量会被继承下去,但无法被直接调用
内存图:
成员方法的继承:
调用方法时,会先检查自己的虚方法表是否有该方法,若有则可以直接调用,若无则需在父类中继续寻找此方法,以此类推。
注:只有父类的虚方法才能被子类继承
内存图:
继承中成员变量访问特点:就近原则(先在局部位置找,再在本类成员位置找,然后在父类成员位置找,逐级往上)
如果出现了重名的成员变量:
System.out.println(name); //从局部位置开始往上找
System.out.println(this.name); //从本类成员位置开始往上找
System.out.println(super.name); //从父类成员位置开始往上找
继承中成员方法访问特点:就近原则
方法的重写:当父类的方法不能满足子类现在的需求时,需要进行方法重写
书写格式:在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法
@Override重写注解:
① @Override是放在重写后的方法上,校验子类重写时语法是否正确
② 加上注解后如果有红色波浪线,表示语法错误
③ 建议重写方法都加@Override
方法重写的本质:子类覆盖了从父类当中继承下来的虚方法表中的方法
方法重写注意事项和要求:
① 重写方法的名称、形参列表必须与父类中的一致
② 子类重写父类方法时,访问权限子类必须大于等于父类
③ 子类重写父类方法时,返回值类型子类必须小于等于父类
④ 建议:重写的方法尽量和父类保持一致
⑤ 只有被添加到虚方法表中的方法才能被重写
继承中构造方法访问特点:
① 父类中的构造方法不会被子类继承
② 子类中所有的构造方法默认先访问父类中的无参构造,再执行自己
为什么:
① 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据
② 子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化
怎么调用父类构造方法:
① 子类构造方法的第一行语句默认都是:super(),不写也存在,且必须在第一行
② 如果想调用父类有参构造,必须手动写super进行调用
this、super使用总结:
this:理解为一个变量,表示当前方法调用者的地址值
super:代表父类存储空间
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.成员变量 访问本类成员变量 | this.成员方法(…) 访问本类成员方法 | this(…) 访问本类构造方法 |
super | super.成员变量 访问父类成员变量 | super.成员方法(…) 访问父类成员方法 | super(…) 访问父类构造方法 |
多态
多态是同类型的对象,表现出的不同形态
多态的表现形式:父类类型 对象名称 = 子类对象;
多态的前提:
① 有继承 / 实现关系
② 有父类引用指向子类对象
③ 有方法重写
多态调用成员的特点:
① 变量调用:编译看左边,运行也看左边
编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有,则编译失败
运行也看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
② 方法调用:编译看左边,运行看右边
编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有,则编译失败
运行看右边:java运行代码的时候,实际上运行的是子类的方法
多态调用成员的内存图解:
多态的优势:
① 在多态形式下,右边对象可以实现解耦合,便于扩展和维护
② 定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利
多态的弊端:不能使用子类的特有功能
解决方案:使用强制转换转换成真正的子类类型,从而调用子类独有功能。转换的时候用instanceof关键字进行判断。(注意:转换的时候不能转换成其他类的类型,否则会报错)
例:
Animal a = new Dog();
Dog d = (Dog) a;
//以下为判断类的类型
if(a instanceof Dog){
Dog d = (Dog) a;
d.lookhome();
}
else if(a instanceof Cat){
Cat c = (Cat) a;
c.catchMouse();
}
else{
System.out.println("没有这个类型,无法转换");
}
//JDK14新特性
//先判断a是否为Dog类型,如果是,则强转为Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}
else if(a instanceof Cat c){
c.catchMouse();
}
else{
System.out.println("没有这个类型,无法转换");
}
包和final
包就是文件夹。用来管理各种不同功能的Java类,方便后期代码维护
包名的规则:公司域名反写 + 包的作用,需要全部英文小写,见名知意。例如:com.itheima.domain
全类名(或全限定名):包名 + 类名(可以用来区分不同包中相同类名的类)。例如:com.itheima.domain.Student
使用其他类的规则:
① 使用同一个包中的类时,不需要导包
② 使用java.lang包中的类时,不需要导包
③ 其他情况都需要导包
④ 如果同时使用两个包的同名类,需要用全类名
final:
① 用final修饰方法:表明该方法是最终方法,不能被重写
② 用final修饰类:表名该类是最终类,不能被继承
③ 用final修饰变量:叫做常量,只能被赋值一次
常量:
实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性
常量的命名规范:
① 单个单词:全部大写
② 多个单词:全部大写,单词之间用下划线隔开
细节:
① final修饰的变量是基本类型:那么变量存储的数据值不能发生改变
② final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,内部的属性值可以改变
权限修饰符和代码块
权限修饰符:是用来控制一个成员能够被访问的范围的。可以修饰成员变量、方法、构造方法、内部类
权限修饰符的分类:有四种作用范围由小到大(private < 空着不写 < protected < public)
修饰符 | 同一个类中 | 同一个包中其他类 | 不同包下的子类 | 不同包下的无关类 |
---|---|---|---|---|
private | √ | |||
空着不写 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
这里的“不同包下的子类”表示的是子类中使用继承而来的成员,而不是新创建父类对象再去使用对应的成员。若在其他包中创建类的对象,无论创建类对象的语句是不是发生在这个类的子类内,只要这个语句发生的地方和这个类不在同一个包下,都视作“不同包下的无关类”
权限修饰符的使用规则:
实际开发中,一般只用private和public(成员变量私有、方法公开)
特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有
代码块:
① 局部代码块:写在方法内部的代码块
public class Test {
public static void main(String[] args) {
//局部代码块
{
int a = 10;
System.out.println(a);
}
}
}
作用:提前结束变量的生命周期(已淘汰)
② 构造代码块:写在成员位置的代码块
public class Student {
private String name;
private int age;
//构造代码块
{
System.out.println("开始创建对象了");
}
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
作用:可以把多个构造方法中重复的代码抽取出来(不够灵活)
执行时机:在创建本类对象的时候会先执行构造代码块再执行构造方法
③ 静态代码块(重点):写在类内方法外的代码块
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
使用场景:在类加载的时候,做一些数据初始化的时候使用
抽象类
抽象方法:将共性的行为(方法)抽取到父类之后,由于每一个子类执行的内容是不一样的,所以,在父类中不能确定具体的方法体。该方法就可以定义为抽象方法
抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类
抽象方法的定义格式:
public abstract 返回值类型 方法名(参数列表);
抽象类的定义格式:
public abstract class 类名{}
抽象类和抽象方法的注意事项:
① 抽象类不能实例化
② 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
③ 可以有构造方法(作用:当创建子类对象的时候,给属性进行赋值的)
④ 抽象类的子类,要么重写抽象类的所有抽象方法,要么是抽象类
抽象类和抽象方法的意义:强制让子类按照某种格式重写,统一了方法调用的方式
接口
接口就是一种规则,是对行为的抽象
接口的定义和使用:
① 接口用关键字interface来定义
public interface 接口名 {}
② 接口不能实例化
③ 接口和类之间是实现关系,通过implements关键字实现
public class 类名 implements 接口名 {}
④ 接口的子类(实现类),要么重写接口中的所有抽象方法,要么是抽象类
注意:
① 接口和类的实现关系,可以单实现,也可以多实现
public class 类名 implements 接口名1, 接口名2 {}
② 实现类还可以在继承一个类的同时实现多个接口
public class 类名 extends 父类 implements 接口名1, 接口名2 {}
接口中成员的特点:
① 成员变量:只能是常量。默认修饰符:public static final
(若没写,java会自动填写)
② 构造方法:没有
③ 成员方法:只能是抽象方法。默认修饰符:public abstract
JDK7以前:接口中只能定义抽象方法
接口和类之间的关系:
① 类和类的关系:继承关系,只能单继承,不能多继承,但是可以多层继承
② 类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口(要重写所有接口的抽象类)
③ 接口和接口的关系:继承关系,可以单继承,也可以多继承(细节:如果实现类实现了最下面的子接口,那么就需要重写所有的抽象方法)
新增特性:
① JDK7以前:接口中只能定义抽象方法
② JDK8的新特性:接口中可以定义有方法体的方法(默认、静态)
③ JDK9的新特性:接口中可以定义私有方法
JDK8开始接口中新增的方法:
允许在接口中定义默认方法,需要使用关键字default修饰
作用:解决接口升级的问题
接口中默认方法的定义格式:
public default 返回值类型 方法名(参数列表) {}
接口中默认方法的注意事项:
① 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉deafult关键字
② public可以省略,default不能省略
③ 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
允许在接口中定义静态方法,需要用static修饰
接口中静态方法的定义格式:
public static 返回值类型 方法名(参数列表) {}
接口中静态方法的注意事项:
① 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
② public可以省略,static不能省略
JDK9新增的方法:
接口中私有方法的定义格式:
//1.普通的私有方法(为默认方法服务,抽取方法中重复的代码)
private 返回值类型 方法名(参数列表) {}
//2.静态的私有方法(为静态方法服务)
private static 返回值类型 方法名(参数列表) {}
接口的应用:
① 接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了
② 当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态
适配器设计模式:
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性
简单理解:设计模式就是各种套路
适配器设计模式:解决接口与接口实现类之间的矛盾问题
当一个接口中抽象方法过多,但是我们只要使用其中一部分的时候,就可以用适配器设计模式
书写步骤:
① 编写中间类XXXAdapter,实现对应的接口
② 对接口中的抽象方法进行空实现
③ 让真正的实现类继承中间类,并重写需要用的方法
④ 为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
内部类
类的五大成员:属性、方法、构造方法、代码块、内部类
内部类:在类的里面再定义的一个类
内部类表示的事物是外部类的一部分,内部类单独出现没有任何意义
内部类的访问特点:
① 内部类可以直接访问外部类的成员,包括私有
② 外部类要访问内部类的成员,必须创建对象
内部类的分类:
① 成员内部类
② 静态内部类
③ 局部内部类
④ 匿名内部类(掌握)
成员内部类:
写在成员位置的,属于外部类的成员
成员内部类可以被一些修饰符所修饰,比如:private、默认、protected、public、static
在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量
获取成员内部类对象:
方式一:在外部类中编写方法,对外提供内部类的对象(用private修饰内部类)
public Inner getInstance(){
return new Inner();
}
方式二:直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
Outer.Inner oi = new Outer().new Inner();
内部类的内存图:
当外部类成员变量和内部类成员变量重名时,若想在内部类访问外部类成员变量,做法:
System.out.println(Outer.this.变量名);
静态内部类:
静态内部类是一种特殊的成员内部类。静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象
创建静态内部类对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
**调用非静态方法的格式:**先创建对象,用对象调用
调用静态方法的格式:
外部类名.内部类名.方法名();
局部内部类:
1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量
2.外界是无法直接使用,需要在方法内部创建对象并使用
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量
匿名内部类:
匿名内部类是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
匿名内部类的格式:
new 类名或者接口名() {
重写方法;
}; //注意:只有{ }内是匿名的内部类,而前面是创建了一个匿名内部类的对象
格式的细节:
① 包含了继承或实现,方法重写,创建对象
② 整体就是一个类的子类对象或者接口的实现类对象
使用场景:当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果这个实现类对象只要使用一次,就可以用匿名内部类简化代码
创建静态内部类对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:
外部类名.内部类名.方法名();
局部内部类:
1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量
2.外界是无法直接使用,需要在方法内部创建对象并使用
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量
匿名内部类:
匿名内部类是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
匿名内部类的格式:
new 类名或者接口名() {
重写方法;
}; //注意:只有{ }内是匿名的内部类,而前面是创建了一个匿名内部类的对象
格式的细节:
① 包含了继承或实现,方法重写,创建对象
② 整体就是一个类的子类对象或者接口的实现类对象
使用场景:当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果这个实现类对象只要使用一次,就可以用匿名内部类简化代码