Bootstrap

自制笔记 | Java基础——面向对象进阶(持续更新...)

static

static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量

1.被static修饰的成员变量,叫做静态变量

特点:

① 被该类所有对象共享
不属于对象,属于类
③ 随着类的加载而加载,优先于对象存在

调用方式:

① 类名调用(推荐)
② 对象名调用

static内存图:

由图中可以看出两个对象拥有共同的静态变量

static内存图

2.被static修饰的成员方法,叫做静态方法

特点:

① 多用在测试类和工具类中
② Javabean类中很少会用

调用方式:

① 类名调用(推荐)
② 对象名调用

名词:

Javabean类:用来描述一类事物的类。比如,Student,Teacher,Dog,Cat等
测试类:用来检查其他类是否书写正确,带有main方法的类,是程序的入口
工具类:不是用来描述一类事物的,而是帮我们做一些事情的类

工具类需遵守的规则:

① 类名见名知意
私有化构造方法
③ 方法定义为静态

static的注意事项:

① 静态方法只能访问静态变量和静态方法
② 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
③ 静态方法中是没有this关键字的

内存图解释:

static内存图-2

继承

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:代表父类存储空间

关键字访问成员变量访问成员方法访问构造方法
thisthis.成员变量
访问本类成员变量
this.成员方法(…)
访问本类成员方法
this(…)
访问本类构造方法
supersuper.成员变量
访问父类成员变量
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 类名或者接口名() { 
    重写方法;
}; //注意:只有{ }内是匿名的内部类,而前面是创建了一个匿名内部类的对象

格式的细节:

① 包含了继承或实现,方法重写,创建对象
② 整体就是一个类的子类对象或者接口的实现类对象

使用场景:当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果这个实现类对象只要使用一次,就可以用匿名内部类简化代码

;