零、面向对象
1.与面向过程的区别
- 面向过程(Procedure Oriented)
- 按自顶向下的步骤依次执行的过程式解决方法,每个步骤可定义为一个模块
- 优点:性能高,流程和步骤清晰,容易理解
- 不足:复用率低,扩展性差,维护难度高
- 面向对象(Object Oriented)
- 将构成问题的事物拆解成各个对象,建立对象的目的是为了描述每个事物在整个解决问题步骤中的行为
- 优点:易扩展、代码复用率高、利于后期扩展和维护,提升开发效率
- 不足:一定程度上增加系统开销
2.分类
OOA:面向对象分析(Object-Oriented Analysis)
OOD:面向对象设计(Object-Oriented Design)
OOP:面向对象编程(Object-Oriented Programming)
3.修饰符权限
Ⅰ.类的访问修饰符
Ⅱ.类成员的访问修饰符
Ⅲ.作用域大小
private < default(缺省/默认) < protected < public
Ⅳ.private【私有】
- 是一个权限修饰符
- 可以修饰成员(成员变量和成员方法)
- 被private修饰得成员只能在本类中才能访问
- 针对于每一个私有化的成员变量,都要提供get和set方法
- get():对外提供成员变量的值
- set():给成员变量赋值
setter&getter的内存原理
Ⅴ.protected【给继承父子类使用】
- 是一个权限修饰符
- 可以修饰成员(成员变量和成员方法)
- 本类、同包其他类、其他包子类可以访问
4.Object类
- 是所有 Java 类的祖先
- 所有的 Java 类都直接或间接地继承了Object类
- 位于java.lang包中,所有的包都默认导入了java.lang包
- 在定义一个类时,如果没有使用extends关键字,即没有显式地继承某个类,那么这个类直接继承Object类
- 常用方法
5.代码块
Ⅰ.局部代码块
在代码中,局部代码块写在方法里的一堆单独的{}
提前结束变量的生命周期(已淘汰)
变量的作用范围,在所属的{}中有效
public void test() {
int a=10;
sout(a);
} //a变量从内存中消失,节约内存
Ⅱ.构造代码块
public class Test{
private String name;
private int age;
//什么是构造代码块:写在成员位置的代码块
//作用:多个构造方法中重复的代码(不够灵活)
//执行时机:创建本类对象的时候,优先于构造方法
{ sout("****");}
public Test(){//sout("****"); }
public Test(String name,int age){
//sout("****");
this.name=name;
this.age=age;
}
}
Ⅲ.静态代码块
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
使用场景:在类加载的时候,做一些数据初始化的时候使用
//加载JDBC驱动
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
6.内部类
Ⅰ.类的五大成员
属性、方法、构造方法、代码块、内部类
在一个类的里面,在定义一个类
Ⅱ.遵守的规则
- 内部类表示的事物是外部类的一部分
- 内部类单独出现没有任何意义
Ⅲ.访问特点
- 内部类可以直接访问外部类的成员、私有
- 外部类要访问内部类的成员,必须创建内部类的对象
public class Car{//外部类
private String brand;//品牌
public void print(){
//打印调用者的品牌
sout(this.brand);
//打印发动机的品牌,创建发动机对象
Engine e=new Engine();
sout(e.engineBrand);
}
class Engine{//发动机,成员内部类
String engineBrand;//发动机品牌
public void print(){
sout(brand);
sout(engineBrand);
}
}
}
Ⅳ.分类
1.成员内部类
写在成员位置的,【类中方法外】
(1)注意事项
①成员内部类可以被修饰成员变量的修饰符修饰,static修饰的成员变量称为静态内部类
②在成员内部类中,JDK16之前不能定义静态变量、方法,JDK16开始才可以定义静态变量
(2)获取成员内部类对象
①当成员内部类被private修饰时,在外部类编写方法,提供内部类对象
②当成员内部类被非私有修饰时,直接创建对象
public class Outer{//外部类
private String name;
private class Inner{//私有的成员内部类
}
//在外部类中编写方法,对外提供内部类对象(private修饰内部类)
public Inner get(){return new Inner();}
}
public class Test{
public static void main(String[] args) {
//创建外部类的对象
Outer outer=new Outer();
//接收外部类里的内部类对象,用其的父类
Object o=outer.get();
sout(outer.get());//打印外部类里的内部类对象的地址
//如果内部类不是私有的,外部类名.内部类名 对象名 =外部类对象.内部类对象
Outer.Inner oi = new Outer().new Inner();
}
}
(3)内部类内存图
2.静态内部类
- 静态内部类也是成员内部类的一种
- 静态内部类只能访问外部类中的静态变量和静态方法
- 如果想要外部类访问非静态的需要创建外部类对象
创建静态内部类对象: 外部类名.内部类名 对象名=new 外部类名.内部类名();
调用静态方法:外部类名.内部类名.方法名();
public class StaticOuter {//外部类
int a = 10;//外部类成员变量
static int b = 20;//外部类静态变量
static class Inner {//静态内部类
public void print() {
//如果想要外部类访问非静态的需要创建外部类对象
StaticOuter staticOuter=new StaticOuter();
System.out.println(staticOuter.a);//10
//静态内部类只能访问外部类中的静态变量和静态方法
System.out.println(b);//20
System.out.println("内部类非静态方法");
}
public static void print1(){
System.out.println("内部类静态方法");
}
}
public static void main(String[] args) {
//静态内部类只能访问外部类中的静态变量和静态方法
StaticOuter.Inner oi=new StaticOuter.Inner();
oi.print();//内部类非静态方法
//可以写,但ideal不提示
oi.print1();//内部类静态方法
//调用内部类静态方法
StaticOuter.Inner.print1();//内部类静态方法
}
}
3.局部内部类
- 将内部类定义在方法里面的叫做局部内部类,类似于方法里面的局部变量
- 外界时无法直接使用局部内部类,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
public class PartOuter {//外部类
int b=20;
public void print(){
int a=10;
class Inner{//局部内部类
int age;
public void method(){
System.out.println(a);//10
System.out.println(b);//20
System.out.println("局部内部类方法");
}
//JDK16开始才能定义静态方法或静态变量
public static void method1(){
System.out.println("局部内部类静态方法");
}
}
//创建局部内部类对象
Inner inner=new Inner();
System.out.println(inner.age);//0
inner.method();//10 20 局部内部类方法
Inner.method1();//局部内部类静态方法
}
public static void main(String[] args) {
PartOuter partOuter=new PartOuter();
partOuter.print();
}
}
4.匿名内部类
(1)定义
匿名内部类本质上就是隐藏了名字的内部类(Java默认:外部类$序号.class)
(2)语法
new 类名或者接口名(){
重写方法
};
整体就是一个类的子类对象或者接口的实现类对象
(3)实现接口
- 实现关系,{}里面的实现了Swim接口
- 方法重写,实现Swim接口,要重写接口的所有抽象方法
- 创建对象,new创建了没有名字的对象
- (),创建空参构造的对象
public interface Swim {
void swim();
}
public class Test {
public static void main(String[] args) {
//匿名内部类对象
new Swim() {
@Override
public void swim() {
System.out.println("重写了游泳的方法");
}
};
}
(4)继承类
- 继承关系,{}里面的继承了Animal类
- 方法重写,子类重写父类的方法
- 创建对象,new创建了没有名字的对象
- (),创建空参构造的对象
public abstract class Animal {
public abstract void eat();
}
public class Test {
public static void main(String[] args) {
new Animal() {
@Override
public void eat() {
System.out.println("重写了eat方法");
}
};
}
}
(5)Java反编译
javap 文件名.class
javap是 Java class文件分解器,可以反编译,也可以查看java编译器生成的字节码。用于分解class文件
-help 帮助
-l 输出行和变量的表
-public 只输出public方法和域
-protected 只输出public和protected类和成员
-package 只输出包,public和protected类和成员,这是默认的
-p -private 输出所有类和成员
-s 输出内部类型签名
-c 输出分解后的代码,例如,类中每一个方法内,包含java字节码的指令,
-verbose 输出栈大小,方法参数的个数
-constants 输出静态final常量
//将反编译后的文件存放到指定文件地址中
javap DocFooter > 文件存放地址
(6)当成对象使用
public class Test02 {
public static void main(String[] args) {
//Swim接口的实现类对象,接口多态
Swim swim=new Swim(){
@Override
public void swim() {}
};
//编译看左边,运行看右边
swim.swim();
//Animal的子类对象
new Animal(){
@Override
public void eat() {}
}.eat();
}
}
(7)应用场景
- 当方法的参数是接口或者类时
- 以接口为例,可以传递这个接口的实现类对象
- 如果实现类只要使用一次,就可以用匿名内部类
7.final关键字
Ⅰ.final修饰
- final修饰方法:表明该方法是最终方法 ,不能被重写
- final修饰类:表明该类是最终类,不能被继承
- final修饰变量:通常使用static 和 final关键字定义类的常量
- 常量名一般由大写字母组成
- 声明常量时一定要赋初值,只能被赋值一次
Ⅱ.final修饰变量类型区别
final修饰的变量是基本类型:那么变量存储的数据值不能发生改变
final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,对象内部的可以改变
package testfinal;
/**
* @Desc描述:final修饰变量类型
*/
public class TestFinal {
public static void main(String[] args) {
//常量记录的数据是不能发生改变的
final double PI = 3.14;
//PI=0.14;//编译报错,Cannot assign a value to final variable 'PI'
//创建对象
final Test test = new Test("测试", 1);
//记录的地址值不能发生改变,内部的属性还是可以改变的
//test = new Test();
test.setName("测试01");
test.setAge(111);
System.out.println(test.getName()+","+test.getAge());//测试01,111
//数组
final int[] ARR={1,2,3,4,5};
//ARR=new int[10];
ARR[0]=99;
ARR[1]=98;
//遍历数组
for (int i = 0; i < ARR.length; i++) {
System.out.print(ARR[i]);//9998345
}
}
}
Ⅲ.关键字冲突
//抽象方法只有声明无具体实现,static方法可通过类名直接访问,但无法修饰一个没有实现的方法
public static abstract void print();
//抽象方法需在子类中重写,但private方法不能被子类继承,自然无法进行重写
private abstract void print();
//抽象方法需要在子类中重写,但final修饰的方法表示该方法不能被子类重写,前后是相互矛盾的
public final abstract void print();
8.方法
Ⅰ.定义
方法是程序中最小的执行单元。
Ⅱ.应用场景
重复的代码、具有独立功能的代码可以抽取到方法中
Ⅲ.好处
- 可以提高代码的复用性
- 可以提高代码的可维护性
Ⅳ.方法调用的基本内存原理
Ⅴ.多个方法调用时
方法在栈内存中的顺序是:先进后出
Ⅵ.方法传递基本数据类型的内存原理
Ⅶ.方法传递引用数据类型的内存原理
Ⅷ.从内存的角度解释
- 基本数据类型:数据值是存储在自己的空间中
- 特点:赋值给其他变量,也是赋的真实的值
- 引用数据类型:数据值是存储在其他空间中,自己空间中存储的是地址值
- 特点:赋值给其他变量,赋的地址值
Ⅸ.方法的值传递
基础数据类型
- 传递基本数据类型时,传递的是真实的数据类型
引用数据类型
- 传递引用数据类型时,传递的是地址值,形参的改变,影响实际参数的值
一、封装
1.定义
将类的某些信息隐藏在类内部,不允许外部程序直接访问
而是通过该类提供的方法来实现对隐藏信息的操作和访问
①属性封装:属性私有化封装,并提供公有的get()和set()方法
②方法封装:把实现相同功能的代码进行封装
③类封装:属性和方法封装在一起
2.好处
- 隐藏类的实现细节
- 方便加入控制语句
- 只能通过规定方法访问数据
- 方便修改实现
3.this关键字
Ⅰ.作用
- 可以区别成员变量和局部变量
- 当成员变量和局部变量重名时,想使用成员变量,可以使用this关键字
public class person(){
private int age=99;
public void method(){
int age=10;
sout(this.age);//99
sout(age);//10
}
}
- 在对象内部指代自身的引用,所以它只能调用实例变量、实例方法和构造方法,不能调用类变量和类方法,也不能调用局部变量
Ⅱ.本质
所在方法调用者的地址值
Ⅲ.setter优化
//给成员变量age进行赋值的
public void setAge(int age){
//局部变量表示测试类中调用方法传递过来的数据
//等号的左边:就表示成员位置的age
this.age = age;
}
Ⅳ.this的内存原理
4.static关键字
静态的之所以被称为类(属性/方法),是因为静态的不属于对象,属于类
静态的调用方法:类名.XX 或 对象名.XX
Ⅰ.特点
被该类所有对象共享
Ⅱ.修饰
属性、方法、块、内部类
Ⅲ.执行时机
static代码块在JVM初始化阶段执行,只会执行一次
一般情况下, 使用static代码块对static变量进行初始化
凡是静态的,加载的时机:jvm启动后,需要的类进入jvm
【静态变量是随着类的加载而加载的,优先于对象出现在内存里的】
Ⅳ.注意事项
在静态方法中,不能直接访问实例变量和实例方法
在实例方法中,可以直接调用类中定义的静态变量和静态方法
-
静态的只能调用静态的
-
静态的不可以调用非静态
-
非静态的可以调用所有
-
静态方法中没有this关键字
Ⅴ.静态变量与实例变量区别
- 在静态方法中,不能直接访问实例变量和实例方法
- 在实例方法中,可以直接调用类中定义的静态变量和静态方法
Ⅵ.static的内存原理
5.final关键字
通常使用static 和 final关键字定义类的常量
- 常量名一般由大写字母组成
- 声明常量时一定要赋初值
6.构造方法
作用:创造对象的时候,由虚拟机自动调用,给成员变量进行初始化的
易错点:构造方法就是用来创建对象的,是错误的
Ⅰ.语法
public class 类名{
修饰符 类名(参数){
方法体;
}
}
Ⅱ.特点
- 方法名与类名相同,大小写也要一致
- 没有返回值类型,连void都没有
- 没有具体的返回值(不能由return带回结果数据)
Ⅲ.执行时机
- 创建对象的时候由虚拟机调用,不能手动调用构造方法
- 每创建一次对象,就会调用一次构造方法
- 在对象初始化的时候执行(new的时候)
Ⅳ.无参构造方法
初始化的对象时,成员变量的数据均采用默认值
- 如果我们自己没有写任何的构造方法,那么虚拟机给我们加一个空参构造方法
- 如果定义了构造方法,系统将不再提供默认的构造方法
public 类名(){
}
Ⅴ.带参构造方法
在初始化对象的时候,同时可以为对象进行赋值
- 带参构造和无参构造方法,两者方法名相同,但是参数不同
private 数据类型1 属性1;
private 数据类型n 属性n;
public 类名(数据类型1 属性1,数据类型n 属性n){
this.属性1=属性1;
this.属性n=属性n;
}
7.方法重载
- 方法重载是指同一个类中包含了两个或两个以上的方法,它们的方法名相同,方法参数的个数或参数类型不同
- 类的成员方法和构造方法都可以进行重载
【同类同名不同参(个数、类型)】
8.方法重写
- 重写方法和被重写方法必须具有相同的方法名
- 重写方法和被重写方法必须具有相同的参数列表
- 重写方法返回值类型必须和被重写方法的返回值类型相同或为其子类
- 重写方法不能小于被重写方法的访问权限
【父子类同名同参、同返回值类型或其子类、访问权限不小于父类】
Ⅰ.重写本质
Ⅱ.方法重载和重写的区别
9.对象内存图
Ⅰ.步骤
1.加载class文件
2.申明局部变量
3.在堆内存中开辟空间
4.默认初始化
5.显示初始化
6.构造方法初始化
7.将堆内存中的地址值赋值给左边的局部变量
Ⅱ.内存图
①一个对象的内存图
②两个对象的内存图
③两个引用指向同一个对象
二、继承
1.概述
Java中提供了一个关键字extends,可以让一个类和另一个类建立起继承关系
【父类 基类 超类】【子类 派生类 衍生类】
2.定义
- 一个类获取现有类的所有属性和行为的机制
- 创建基于现有类的新类,可以重用现有类的属性和方法
- 可以在新创建的子类中添加新属性和方法
3.好处
- 有效的解决了代码的重用问题,使代码拓展更加灵活
- 从始至终完整的体现了一个应用系统,逻辑更加清晰
- 增加软件的可扩展性,以适应不同的业务需求
4.使用场景
当类与类之间,存在相同的内容,并满足子类是父类的一种[is-a关系],就可以考虑使用继承,来优化代码
5.语法
[ 访问修饰符 ] class 子类 extends 父类{
//子类特有的属性和方法
}
6.特点
- Java只支持单继承,即每个类只能有一个直接父类,不支持多继承,但支持多层继承
- Java中所有的类都直接或间接继承于Object类
7.子类不能继承的父类成员
- private成员
- 子类与父类不在同包,使用默认访问权限的成员【权限】
- 构造方法
8.继承后子类的特点
- 子类可以得到父类的属性和行为,子类可以使用
- 子类可以在父类的基础上新增其他功能,子类更加强大
- 实例化子类时,系统会默认调用父类的无参构造,如果是多层继承则会一直调用
9.super关键字
//访问直接父类的构造方法,在子类构造方法中调用且必须是第一句
super(参数);
//访问父类的属性或方法
super.属性/方法;
注意事项
- super关键字必须出现在子类(子类的方法和构造方法)中,而不允许在其他位置
- 使用super关键字可以访问父类的成员(属性、方法、构造方法)
- super调用构造方法时,只能是第一句
- 注意访问权限的限制,如无法通过super访问private成员
10.重名成员变量
sout(name);//从局部位置开始往上找
sout(this.name);//从本类成员位置开始往上找
sout(super.name);//从父类成员位置开始往上找
11.构造方法执行顺序
Java 虚拟机按照先父类后子类的顺序执行一系列的构造方法
public 子类(){//无参构造
// 系统会默认先调用父类的无参构造方法
}
public 子类(参数列表){//有参构造
this();
//方法体
}
/*会调用其父类的无参构造*/
①调用子类构造方法为什么要先加载父类构造方法
- 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据
- 子类在初始化之前,一定要调用父类的构造方法先完成父类数据空间的初始化
②子类继承父类时构造方法的调用规则
- 如果类的构造方法中,没有通过 super关键字显式调用父类的有参构造方法,也没有通过 this关键字显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法,默认第一行有super();
- 如果子类的构造方法中,通过 super关键字显式调用了父类的有参构造方法,则将执行父类相应的构造方法,而不执行父类无参构造方法
- 如果子类的构造方法中,通过 this关键字显式地调用了自身的其他构造方法,在相应的构造方法中遵循以上两条规则
- 【如果存在多级继承关系,则在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止】
12.继承内存图
三、多态
1.概述
- 同一个引用类型,使用不同的实例而执行不同操作(父类引用指向子类对象)
【父类类型 对象名称=子类对象;】 - 同一种操作,由于条件不同,产生的结果也不同
①方法的多态:方法重载和方法重写
②类的多态:父类引用指向子类对象、里氏替换
父类作为形参,父类作为返回值
2.实现多态的三要素
- 继承关系的父类和子类
- 子类重写父类方法
- 父类的引用指向子类的对象
3.好处
- 定义方法的时候,使用父类类型作为参数,可以接收所有子类对象
- 在多态形式下,右边的对象可以实现解耦合,便于扩展和维护
- 体现多态的可扩充性、灵活性、简化性(代码重用)、可替换性【里氏替换】
4.简化里氏替换
只要父类能出现的地方,子类就可以出现,并且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但反之,未要求。
里氏替换原则CSDN
里氏替换详解
5.类型
①向上转型
Ⅰ.语法
<父类型 > < 引用变量名 > = new< 子类型 >();
系统会自动进行类型转换
通过父类引用变量调用的方法是子类覆盖或继承的子类方法,不是父类的方法
通过父类引用变量无法调用子类特有的方法
Ⅱ.代码示意
package polymorphic.test;
/**
* @Desc描述:测试多态动物类
* 子类到父类的转换:自动类型转换
*/
public class TestAnimal {
public static void main(String[] args) {
//创建对象(多态)
//父类 对象名 =new 子类();
Animal animal = new Dog();
/*
调用成员变量:编译看左边,运行看左边
编译看左边:javac编译代码的时候,会看左边的父类中是否有这个变量,有则编译成功,否则反之
运行看左边:java运行代码的时候,实际获取的就是左边的父类中的成员变量的值
在子类的对象中,会把父类的成员变量也继承下来
*/
System.out.println(animal.name);//动物
/*
调用成员方法:编译看左边,运行看右边
编译看左边:javac编译代码的时候,会看左边的父类中是否有这个方法,有则编译成功,否则反之
运行看右边:java运行代码的时候,实际运行的是子类中的方法
如果子类对方法进行了重写,那么虚方法表中是会把父类的方法进行覆盖的
如果子类没有对方法进行重写,那么则会执行父类的方法
*/
animal.print();//子类狗类的方法
}
}
/**
* 父类-动物类
*/
class Animal {
String name = "动物";
public void print() {
System.out.println("父类的方法");
}
}
/**
* 子类-狗类
*/
class Dog extends Animal {
String name = "狗";
//如果有此方法,测试打印出 -->子类狗类的方法
//如果没有此方法,测试打印出 -->父类的方法
public void print() {
System.out.println("子类狗类的方法");
}
}
Ⅲ.内存图
Ⅳ.父类作为返回值类型
/**Desc描述: 商店*/
public class Shop{
//卖水果
public Fruit sellFruit(){
Fruit fruit=null;//水果父类对象
int choice=0;//选择
if(choice==1){
fruit=new Apple();//苹果对象
}else if(choice==2){
fruit=new Pear();//梨对象
}
return fruit;//返回水果对象
}
}
②向下转型
Ⅰ.语法
< 子类型 > < 引用变量名 > = (< 子类型 >)< 父类型的引用变量 >;
将一个指向子类对象的父类引用赋给一个子类的引用,即将父类类型转换为子类类型,称为向下转型
向下转型必须进行强制类型转换
将父类类型转换为它的某个子类类型后,才能调用其子类特有的属性
--->可以解决什么问题?
可以转换成真正的子类类型,从而调用子类特有的功能
转换类型与真实对象类型不一致会报错(创建的对象和强转的对象不一致)
转换的时候用instanceof关键字进行判断
Ⅱ.代码示意
package polymorphic.test;
/**
* @Desc描述:向下转型
*/
public class TestAnimal02 {
public static void main(String[] args) {
//创建D对象
Animal01 animal01=new Dog01();
animal01.print();//狗
//不能调用子类特有的方法
//当调用成员方法的时候,编译检查父类中有无此方法,无则报错
animal01.eat();//编译错误
//将父类变回子类类型即可使用子类特有的方法
Dog01 dog01=(Dog01) animal01;
dog01.eat();//吃饭
//转换类型的时候不能瞎转,不然会报classeastexception类型转换不兼容异常
Cat01 cat01=(Cat01) animal01;
cat01.eat();//报错,因为上面创建的是狗类的对象
//解决异常
if (animal01 instanceof Dog01){
Dog01 dog011=(Dog01) animal01;
dog011.eat();//吃饭
}else if (animal01 instanceof Cat01){
Cat01 cat011=(Cat01) animal01;
cat011.eat();//吃老鼠
}else {
System.out.println("没有这个类型,无法转换");
}
//instanceof 在jdk14的时候新特性
//先判断animal01是否为Dog类型,如果是则强转成Dog类型,转换之后变量名为dog02
//如果不是,则不强转,结果直接是false
if (animal01 instanceof Dog01 dog02){
dog02.eat();
} else if (animal01 instanceof Cat01 cat02) {
cat02.eat();
}else {
System.out.println("没有这个类型,无法转换");
}
}
}
class Animal01 {
public void print() {
System.out.println("动物");
}
}
class Dog01 extends Animal01 {
public void print() {
System.out.println("狗");
}
public void eat(){
System.out.println("吃饭");
}
}
class Cat01 extends Animal01 {
public void print() {
System.out.println("猫");
}
public void eat(){
System.out.println("吃老鼠");
}
}
③instanceof关键字
Ⅰ.作用
用于判断一个对象是否属于一个类或者实现了一个接口
避免引发类型转换异常,提高代码的健壮性
Ⅱ.语法
对象 instanceof 类 或者 接口 //结果true或false
对象的类型必须与instanceof参数后所指定的类或接口在继承树上具有上下级关系;否则,会出现编译错误
6.分类
重载式多态
,也叫编译时多态。也就是说这种多态再编译时已经确定好了。重载大家都知道,方法名相同而参数列表不同的一组方法就是重载。在调用这种重载的方法时,通过传入不同的参数最后得到不同的结果。
同一个行为具有多个不同表现形式或形态的能力就是多态(不要拘于定义)
重写式多态
,也叫运行时多态。这种多态通过动态绑定(dynamic binding)技术来实现,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来,你才知道调用的是哪个子类的方法。 这种多态通过函数的重写以及向上转型来实现,重写式多态才是面向对象编程中真正的多态。
四、面向对象三大特征
- 面向对象的三大特性:封装、继承、多态
- 封装是隐藏对象的属性和实现细节
- 将类的成员属性声明为私有的,同时提供公有的方法实现对该成员属性的存取操作
- 继承是软件可重用性的一种表现
- 新类可以在不增加自身代码的情况下,通过从现有的类中继承其属性和方法充实自身内容
- 多态是具有表现多种形态的能力的特征
- 在程序设计的术语中,意味着一个特定类型的变量可以引用不同类型的对象,自动地调用引用的对象的方法
- 【根据作用到的不同对象类型,响应不同的操作】
五、抽象类和抽象方法
1.定义
- 抽象方法
- 将共性的行为(方法)抽取到父类之后
- 由于每一个子类执行的内容是不一样的,所以在父类中不能确定具体的方法体
- 抽象类
- 如果一个类中存在抽象方法,那么该类就必须声明为抽象类
2.语法
//抽象方法:只有方法声明,没有方法实现(方法体)
public abstract 返回值类型 方法名(参数列表);
//抽象类:一个类中存在抽象方法,该类必须声明为抽象类
public abstract class 类名{}
3.注意事项
- 抽象类不能实例化(不能创建对象)
public class TestAbstractNew {
public static void main(String[] args) {
Demo demo=new Demo();//编译报错,'Demo' is abstract; cannot be instantiated
}
}
- 抽象类中不一定有抽象方法,也可以有普通方法,有抽象方法的类一定是抽象类
public abstract class Demo {
//抽象方法,如果类不是抽象类
public abstract void test();//编译报错,Abstract method in non-abstract class
//普通方法可以存在抽象类中
public void print(){
System.out.println("测试");
}
}
- 可以有构造方法(作用:当创建子类对象的时候,给共有的属性进行赋值),其构造方法可以被本类的其他构造方法调用
- abstract关键字可以用来修饰类和方法,不能用来修饰属性和构造方法
4.抽象的子类
- 要么重写抽象类中的所有抽象方法
- 要么是抽象类
//Class 'DemoExtends' must either be declared abstract or implement abstract method 'test()' in 'Demo'
//要不然子类加abstract
//要不然重写父类全部抽象方法
public class DemoExtends extends Demo{}
5.作用
- 父类提供一系列规定,约束子类的行为
强制子类必须按照抽象方法的格式进行重写,保障统一性
6.好处
①提高可重用性
抽象类可以看作是类的一个模版,定义了子类的行为,可以为子类提供默认实现,无需子类中重复实现这些方法
②代码松耦合,更易于维护
子类可以分别实现抽象父类中定义的抽象方法,将方法定义和方法实现的分离
③方便实现多态
抽象类作为继承关系下的抽象层,不能被实例化,通常定义抽象类类型变量,其具体引用是实现抽象类的子类对象
7.应用场景
- 抽象类用来列举一个类所需要的共性行为
- 抽象类不明确提供具体实现方法
- 抽象类必须由其子类实现它的抽象方法(除非子类也具有抽象性)
六、接口
1.定义
- 是一种能力,比父类更加灵活,体现在方法(抽象方法)上
- 是一种规范和标准
- 可以约束类的行为,使得实现接口的类(或结构)在形式上保持一致
- 是一些方法特征的集合
- 可看作是一种特殊的“抽象类”
/**@Desc描述:USB的工作接口 */
public interface UsbWork {
//抽象方法:开始工作
void start();
//抽象方法:结束工作
void stop();
}
/** @Desc描述:U盘类*/
public class UDisk implements UsbWork{
@Override
public void start() {
System.out.println("U盘开始工作...");
}
@Override
public void stop() {
System.out.println("U盘停止工作...");
}
}
/** @Desc描述:手机类*/
public class Phone implements UsbWork{
@Override
public void start() {
System.out.println("手机开始工作...");
}
@Override
public void stop() {
System.out.println("手机停止工作...");
}
}
/** @Desc描述:测试类-计算机类,模拟插入不同设备进行识别 */
public class Computer {
public static void main(String[] args) {
//创建U盘对象
UDisk uDisk = new UDisk();
uDisk.start();//U盘开始工作...
uDisk.stop();//U盘停止工作...
//创建手机对象
Phone phone = new Phone();
phone.start();//手机开始工作...
phone.stop();//手机停止工作...
}
}
- 关心实现类有何能力,而不关心实现细节
- 面向接口的约定而不考虑接口的具体实现
- 是一种能力,体现在接口的方法上
/** @Desc描述:抽象类-概念打印机*/
public abstract class Printer {
/**
* 打印机需要墨盒和纸,但不是这种,而是具备这种东西的能力
*/
private InkBox inkBox;
private Paper paper;
/*私有属性的setter和getter方法*/
/**抽象方法-打印*/
public abstract void print();
}
/** @Desc描述:和墨盒一样能力的接口*/
public interface InkBox {
/**
* 抽象方法-获取墨盒墨的颜色
* @return 墨的颜色
*/
String getColor();
}
/** @Desc描述:和纸张一样的能力的接口*/
public interface Paper {
/**
* 抽象方法-获取纸张的尺寸大小
* @return 尺寸大小
*/
String getSize();
}
/** @Desc描述:A4尺寸纸类*/
public class A4Paper implements Paper{
@Override
public String getSize() {
return "A4";
}
}
/**@Desc描述:B5尺寸纸类*/
public class B5Paper implements Paper{
@Override
public String getSize() {
return "B5";
}
}
/**@Desc描述:黑白墨盒类*/
public class BlackWhiteInkBox implements InkBox{
@Override
public String getColor() {
return "黑白色";
}
}
/** @Desc描述:彩色墨盒类 */
public class ColorInkBox implements InkBox{
@Override
public String getColor() {
return "彩色";
}
}
/** @Desc描述:神奇打印机*/
public class AmazedPrinter extends Printer {
@Override
public void print() {
//super.getInkBox().getColor() :从父类获取InkBox对象的获取颜色方法
System.out.println("在"+super.getInkBox().getColor()+"上使用"+super.getPaper().getSize()+"使用");
}
}
/**@Desc描述:打印机测试类*/
public class Test {
public static void main(String[] args) {
//创建神奇打印机对象
AmazedPrinter amazedPrinter=new AmazedPrinter();
//创建A4纸对象
A4Paper a4Paper=new A4Paper();
//创建B5纸对象
B5Paper b5Paper=new B5Paper();
//创建黑白墨盒对象
BlackWhiteInkBox blackWhiteInkBox=new BlackWhiteInkBox();
//创建彩色墨盒对象
ColorInkBox colorInkBox=new ColorInkBox();
//搭配01
//设置打印机
amazedPrinter.setInkBox(blackWhiteInkBox);
//设置纸张尺寸
amazedPrinter.setPaper(a4Paper);
amazedPrinter.print();//在黑白色上使用A4使用
//搭配02
//设置打印机
amazedPrinter.setInkBox(colorInkBox);
//设置纸张尺寸
amazedPrinter.setPaper(b5Paper);
amazedPrinter.print();//在彩色上使用B5使用
}
}
- 抽象类利于代码复用,接口利于代码的新功能的扩展和老功能的维护
2.语法
//接口使用关键字interface
[访问修饰符] interface 接口名{}
访问修饰符:如果是public,则在整个项目中可见;如果缺省default,则只在该包中可见
//接口和类之间是实现关系,通过关键字implements表示
public class 实现类名 implements 接口名{}
3.成员特点
Ⅰ.成员变量
声明
- 接口中的变量都是全局静态常量
- 自动使用public static final修饰,必须在定义时指定初始值
public interface Cirlcle {
int P=5; //相当于下面一行的代码
public static final int P = 5;
int P;//编译错误,接口中必须指定初始值
}
使用
- 由于接口中的常量是静态的,所以可以使用实现类的名称直接访问。
- 由于接口中的常量是隐式静态的,所以在访问时应使用接口或实现类的名称来引用常量。
//接口
public interface Inter {
int j=1;//常量
}
public class Main implements Inter{
public static void main(String[] args) {
Main main=new Main();
System.out.println(main.j);
System.out.println(Main.j);
System.out.println(I.j);
}
}
Ⅱ.构造方法
接口不能实例化,因为没有构造方法
Ⅲ.方法
(1)JDK7以前
- 接口只能定义抽象方法
public interface MyInterface {
public MyInterface();//接口没有构造方法(×)
public void method1();//在接口中定义方法时,默认情况下,所有方法都是public且abstract,可以省略这些修饰符(√)
public void method2(){ }//抽象方法的声明,没有方法体(×)
private void method3();//接口中抽象方法要被实现类重写,私有的只能在本类中使用(×)
void method();//接口中抽象方法系统自动添加public abstract修饰(√)
}
(2)JDK8新特性
- 接口中可以定义有方法体的方法(默认方法,静态方法)
默认方法
①作用
解决接口升级的问题
②语法
//使用关键字default修饰
//如果不能满足某个实现类的需求,可在实现类中重写
public default 返回值类型 方法名(参数列表){
//方法体
}
③注意事项
- 默认方法不是抽象方法,所以不强制被重写,但是如果被重写,重写的时候去掉default关键字
- public 可以省略,default 不能省略,否则会被默认当成抽象方法
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
静态方法
①语法
//使用关键字static修饰
public static 返回值类型 方法名(参数列表){
//方法体
}
②注意事项
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public 可以省略,static 不能省略,否则会被默认当成抽象方法
- 静态方法不能被重写
(3)JDK9新特性
- 接口中可以定义私有方法
私有方法
①语法
//只为当前接口服务,不需要外类访问。给默认方法服务的
private 返回值类型 方法名(参数列表){
//方法体
}
//静态的私有方法,给静态方法服务的
private static 返回值类型 方法名(参数列表){
//方法体
}
②代码示意
public interface Inter{
//默认方法中重复的代码,给默认方法服务
private void method(){sout("method");}
//静态方法中重复的代码,给静态方法服务
private static void method0(){sout("method00");}
public default void method01(){sout("method01");method();}
public default void method02(){sout("method02");method();}
public static void method03(){sout("method03");method0();}
public static void method04(){sout("method04");method0();}
}
4.接口和类之间的关系
Ⅰ.类和类的关系
继承关系,只能单继承,不能多继承,但是可以多层继承
Ⅱ.类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
如果实现多个接口的时候,方法名相同(重名方法),只要重写一次重名方法
- 一个普通类只能继承一个父类,但能同时实现多个接口
- extends 关键字必须位于 implements关键字之前
- 类必须实现所有接口(接口1、接口2…)的全部抽象方法,否则必须定义为抽象类
- 子类(实现类)
- 要么重写实现的所有接口的所有的抽象方法
- 要么是抽象类
//接口和类的实现关系,可以单实现,也可以多实现
public class 类名 implements 接口名1,接口名2{}
//实现类还可以在继承一个类的同时实现多个接口
public class 类名 extends 父类 implements 接口名1,接口名2{}
Ⅲ.接口和接口的关系
继承关系,可以单继承,也可以多继承,如果实现类实现了最下面的子接口,要重写所有的方法
//接口和接口的继承关系,可以单继承,也可以多继承,但接口不能继承类
public interface 接口名 extends 接口名1,接口名2{}
5.特征
1、Java接口中的成员变量默认都是public,static,final类型的(都可省略),必须被显示初始化,即接口中的成员变量为常量(大写,单词之间用"_"分隔)
2、Java接口中的方法默认都是public,abstract类型的(都可省略),没有方法体,不能被实例化
3、Java接口中只能包含public,static,final类型的成员变量和public,abstract类型的成员方法
4、接口中没有构造方法,不能被实例化
5、一个接口不能实现(implements)另一个接口,但它可以继承多个其它的接口
6、Java接口必须通过类来实现它的抽象方法
7、当类实现了某个Java接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类
8、不允许创建接口的实例(实例化),但允许定义接口类型的引用变量,该引用变量引用实现了这个接口的类的实例
9、一个类只能继承一个直接的父类,但可以实现多个接口,间接的实现了多继承.