从零开始学Java_第十章

十、面向对象进阶

10.1 static

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

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

特点

  • 被该类所有对象共享
  • 不属于对象,属于类
  • 静态变量是随着类的加载而加载的,优于对象出现的

调用方式

  • 类名调用
  • 对象名调用

static内存图

java_static内存图

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

特点:

  • 多用在测试类和工具类中
  • javabean类中很少会用

调用方式:

  • 类名调用
  • 对象名调用

static的注意事项

  • 静态方法只能访问静态变量和静态方法
  • 非静态方法可以访问静态变量和静态方法,也可以访问非静态的成员变量和成员方法
  • 静态方法中没有this关键字

非静态的与对象相关,静态共享与某一个对象无关。

java_静态方法不能调用实例对象

重新认识main方法

  • public:被jvm调用,访问权限足够大
  • static:被jvm调用,不能创建对象,直接类名访问。因为main方法是静态的,所以测试类中的其他方法也需要是静态的。
  • void:被jvm调用,不需要给jvm返回值。
  • main:一个通用的名称,虽然不是关键字,但是被jvm识别。
  • String[] args:以前用于接收键盘录入数据的,现在没用。
10.2 继承
  • java提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立继承关系。

public class Student extends Person {}

  • Student称为子类(派生类),Person称为父类(基类)。

使用继承的好处

  • 可以把多个子类中重复的代码提取到父类中,提高代码的复用性。
  • 子类可以在父类的基础上,增加其他的功能,使子类更强大。

什么时候用到继承?

当类与类之间,存在相同的内容,并满足子类是父类中的一种,就可以考虑用继承的来优化代码。

继承的特点

Java只支持单继承,不支持多继承,但支持多层继承。

  • 单继承:一个子类只能继承一个父类。
  • 不支持多继承:子类不能同时继承多个父类
  • 多层继承:子类A继承父类B,父类B可以继承父类C B是A的直接父类,C是A的间接父类

每一个类都直接或者间接继承于Object

子类只能访问父类中非私有的成员。

子类能继承父类中的哪些内容?

两个误区:

  • 误区1:父类私有的东西,子类就无法继承
  • 误区2:父类中非私有的成员,就被子类继承下来了

java_子类继承父类

只有父类中的虚方法才能被子类继承

java_继承的内存图

java_方法继承

继承中成员变量和成员方法的访问特点

继承中成员变量的访问特点:就近原则

  • this关键字 本类中成员变量
  • super关键字 父类中成员变量

继承中成员方法的访问特点:就近原则

方法的重写

当父类的方法不能满足子类现在的需求时,需要进行方法重写。

书写格式:在继承体系中,子类出现了和父类中一模一样的方法申明,我们就称子类这个方法是重写的方法。

@Override重写注释

  • @Override是放在重写后的方法上,校验子类重写时语法是否正确。
  • 加上注解示意虚拟机这是方法重写,此时如果出现红色波浪线表示语法出现错误。
  • 建议重写方法都加上@Override注解,代码安全优雅。

方法重写的本质

方法重写的本质是子类重写的方法是覆盖了虚方法表中的方法。

java_方法重写的本质

方法重写的注意事项和要求

  • 重写方法的名称、形参列表必须与父类中的一致。
  • 子类重写父类方法时,访问权限子类必须大于等于父类。(空<protected<public)
  • 子类重写父类方法时,返回值类型必须子类型小于等于父类型。
  • 重写方法尽量与父类保持一致。
  • 只有被添加到虚方法表中的方法才能被重写。

继承中构造方法的访问特点

  • 父类中的构造方法不会被子类继承。(假设可以继承,那么构造方法名与类名不一致)
  • 子类中所有的构造方法默认先访问父类中的无参构造,再执行自己。
    • 原因:子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类无法使用父类中的数据。
    • 子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化。
  • 怎么调用父类构造方法?
    • 子类构造方法的第一行语句默认都是:super(),不写也存在,且必须在第一行。
    • 如果想调用父类有参构造,必须手动写super进行调用。

this、super使用总结

  • this:理解为一个变量,表示当前方法调用者的地址值。
  • super:代表父类存储空间。

java_this、super

10.3 多态

什么是多态?

同类的对象,表现出不同形态。

多态的表现形式

父类类型 对象名称 = 子类对象;

多态的前提

  • 有继承关系
  • 有父类引用指向子类对象
  • 有方法的重写

多态的好处

使用父类类型作为参数,可以接收所有子类对象,体现多态的拓展性与便利。

多态调用成员的特点

  • 变量调用:编译看左边,运行也看左边。
  • 方法调用:编译看左边,运行看右边。
    • 编译看左边:javac编译代码的时候,会看左边的父类中有没有这个对象,如果有编译成功,如果没有编译失败。
    • 运行看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值。
    • 运行看右边:java运行代码的时候,实际运行的是子类里面的方法。

如何理解?

Animal a = new dog();

  • 现在用a调用变量和方法。而a是animal类的,所以默认都会从animal这个类中去找。
  • 成员变量:在子类的对象中,会把父类的成员变量也继承下来。具体调用谁看变量是父类还是子类。
  • 成员方法:如果子类对方法进行了重写,那么在虚方法表中会对父类方法进行覆盖。

多态调用成员的内存图理解

java_多态调用的内存原理

多态的优势

  • 在多态形式下,右边对象可以实现解耦合,便于拓展和维护。
  • 定义方法的时候,使用父类类型作为参数,可以接收所有子类对象,体现多态的拓展性和便利。

多态的弊端

  • 不能调用子类的特有的功能。(无法调用子类特有的方法)

    • 原因:编译看左边,编译时会先检查左边的父类有没有这个方法,如果没有直接报错。

    • 解决方案:将父类型强制转换为子类型。

    • 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类型
      //如果不是,则不强转,直接false
      if(a instanceof Dog d) {
          d.lookHome();//直接调用方法
      }else if(a instanceof Cat c) {
          c.catchMouse;
      }else {
          System.out.println("无此类型")
      }
      

包、final、权限修饰符、代码块

什么是包?

包就是文件夹。用来管理各种不同功能的Java类,方便后期代码维护。

  • 包名的规则:公司域名反写 + 包的作用,需要全部英文小写,见名知意。
  • 使用其他类的规则
    • 使用同一个包中的类时,不需要导包。
    • 使用java.lang包中的类时,不需要导包。
    • 其他情况都需要导包。
    • 如果同时使用两个包中的同类名,需要使用全类名。
//方式1
com.xxx.xxx.Student
    s = new com.xxx.xxx.Student();
//方式2
import com.xxx.xxx.Student;
Student s = new Student();

final关键字

final关键字可以修饰三种内容:

  • 方法:表明该方法是最终方法,不能被重写
  • 类:表明该类是最终类,不能被继承
  • 变量:叫做常量,只能被赋值一次

常量

在实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性。

常量的命名规范:

  • 单个单词:全部大写
  • 多个单词:全部大写,单词之间用下划线隔开

细节:

  • final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
  • final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,对象内部的可以改变。

权限修饰符

权限修饰符是用来控制一个成员能够被访问的范围的。可以修饰成员变量,方法,构造方法,内部类。

java_权限修饰符

使用规则:实际开发中,一般只用private和public

  • 成员变量私有
  • 方法公开
  • 特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有。

代码块

  • 局部代码块:写在方法内部的大括号
  • 构造代码块
    • 定义:写在成员位置的代码块
    • 作用:可以把多个构造方法中重复的代码抽取出来
    • 执行时机:在创建本类对象的时候会执行构造代码块再执行构造方法。
    • 逐渐淘汰
  • 静态代码块:在构造代码块前加了static关键字
    • 格式:static {}
    • 特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
    • 使用场景:在类的加载的时候,做一些数据初始化的时候使用。
10.4 抽象类

抽象类和抽象方法的定义

  • 抽象方法:将共性的行为(方法)抽取到父类之后,由于每一个子类执行的内容是不一样,所以,在父类中不能确定具体的方法体。该方法可以定义为抽象方法。

  • 抽象类:如果一个类存在抽象方法,那么该类就必须声明为抽象类。

抽象类和抽象方法的定义格式

  • 抽象方法的定义格式
public abstract 返回值类型 方法名(参数列表);
  • 抽象类的定义格式
public abstract class 类名{}

抽象类和抽象方法的注意事项

  • 抽象类不能实例化
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
  • 可以有构造方法
  • 抽象类的子类
    • 要么重写抽象类中的所有抽象方法
    • 要么是抽象类
10.5 接口

接口就是一种规则,是对行为的抽象。

接口的定义和使用

  • 接口用关键字interfa来定义
public interface 接口名{}
  • 接口不能实例化
  • 接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{}
  • 接口的子类(实现类)

    • 要么重写接口中的所有抽象方法
    • 要么是抽象类
  • 注意事项

    • 接口和类的实现关系,可以单实现,也可以多实现。
    public class 类名 implems 接口名1, 接口名2{}
    
    • 实现类还可以在继承一个类的同时实现多个接口
    public class 类名 extends 父类 implements 接口名1, 接口名2{}
    

接口中成员的特点

  • 成员变量
    • 只能是常量
    • 默认修饰符:public static final
  • 构造方法:没有构造方法
  • 成员方法
    • 只能是抽象方法
    • 默认修饰符:public abstract
      • JDK7以前:接口中只能定义抽象方法。
      • JDK8的新特性:接口中可以定义有方法体的方法。
      • JDK9的新特性:接口中可以定义私有方法。

接口和类之间的关系

  • 类和类的关系:继承关系,只能单继承,不能多继承,但是可以多层继承。
  • 类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
  • 接口和接口的关系:继承关系,可以单继承,也可以多继承。

JDK8开始接口中新增的方法

  • JDK7以前:接口中只能定义抽象方法。
  • JDK8的新特性:接口中可以定义有方法体的方法。(默认、静态)
    • 默认方法:
      • 需要使用default关键字修饰,解决接口升级的问题。
      • 格式:public default 返回值类型 方法名(参数列表){ }
      • 注意事项:默认方法不是抽象方法,所以不强制重写。但是如果被重写,重写的时候去掉default关键字。public可以省略,default不能省略。如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法重写。
    • 静态方法:
      • 格式:public static 返回值类型 方法名(参数列表){ }
      • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用。
      • public可以省略,static不能省略。
  • JDK9的新特性:接口中可以定义私有方法。
    • 默认方法定义格式:private void show(){ }//注意没有default
    • 静态方法定义格式:private static void method(){ }

接口的应用

  • 接口代表规则,是行为的抽象。想让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。
  • 当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。

适配器设计模式

设计模式(design pattern)是一套被反复使用,多数人知晓的,经过分类编目的,代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被人理解、保证代码可靠性、程序的重用性。简单来说,设计模式就是各种套路。

适配器设计模式:解决接口与接口实现类之间的矛盾问题。

  • 当一个接口中抽象方法过多,但是只要用其中一部分的时候,就可以适配器设计模式。
  • 书写步骤
编写中间类xxxAdapter,实现对应的接口;
对接口中的抽象方法进行空实现;
让真正的实现类继承中间类,并重写需要用的方法;
为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰。
10.6 内部类

内部类就是在一个类的里面,再定义一个类。如在A类的内部定义B类,B类就称为内部类。

public class Outer{//外部类
    public class Inner{//内部类
        xxx;//内部类的事物是外部类的一部分,内部类单独出现没有任何意义。
    }
}

内部类的访问特点:

  • 内部类可以直接访问外部类的成员,包括私有。
  • 外部类访问内部类成员,必须创建对象。

内部类的分类

  • 成员内部类
  • 静态内部类
  • 局部内部类
  • 匿名内部类

成员内部类

  • 写在成员位置的,属于外部类的成员。
  • 成员内部类可以被一些修饰符修饰,比如:private,默认,protected,public,static等。
  • 在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。

获取成员内部类对象的两种方式

  • 方式一:在外部类中编写方法,对外提供内部类对象。(private)
public Inner getInstance(){
    return new Inner();
}
  • 方式二:直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
Outer.Inner oi = new Outer().new Inner();

成员内部类获取外部类的成员变量

没有重名直接调用外部类即可

public class Outer {
    private int a = 10;
    class Inner {
        private int a = 20;
        public void show() {
            int a = 30;
            System.out.println(a);//30
            System.out.println(this.a);//20
            //out.this获取了外部类对象的地址值
            System.out.println(Outer.this.a);//10
        }
    }
}

内部类的内存图

java_内部类的内存图

静态内部类

静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。

创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名()

调用非静态方法的格式:先创建对象,用对象调用

调用静态方法的格式:外部类名.内部类名.方法名()

public class Outer{//外部类
    static class Inner{//静态内部类
        xxx;
    }
}
//创建静态内部类对象
Outer.Inner oi = new Outer.Inner();//new关键字与Inner直接交互
//调用静态方法
Outer.Inner.show2();

局部内部类

  • 将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。

  • 外界是无法直接使用局部内部类,需要在方法内部创建对象并使用。

  • 该类可以直接访问外部类成员,也可以访问方法内的局部变量。

匿名内部类

匿名内部类本质上就是隐藏了名字的内部类。可以写在成员位置,也可以写在局部位置。

格式:

new 类名或者接口名() {//内容包含继承/实现 -> 方法重写 -> 创建对象
    重写方法;
};
//举例
new Inter() {
    public void show() {
        
    }
}
public class Student extends Swim {
    @override
    public void swim() {
        xxx;
    }
}

实现过程:

  1. 把前面的class删掉,剩余的内容就变成了一个没有名字的类。
  2. 这个没有名字的类想要实现swim接口。把swim写在大括号的前面,表示这个名字的类实现了swim接口,所以需要在类中重写接口里所有的抽象方法。
  3. 根据new 类名() ;的格式完善代码。如下所示:
//匿名类的主体是{}之间的内容
//类实现了接口swim
//new的类的主体不是接口
new Swim() {//若是接口则为实现关系,若为类则为继承关系
    @override
    public void swim() {
        xxx;
    }
};

使用场景:如果一个类只需要用一次,那么单独定义一个类太麻烦。此时可以采用内部类。

创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;

Outer.Inner oi = new Outer().new Inner();

成员内部类获取外部类的成员变量

没有重名直接调用外部类即可

public class Outer {
    private int a = 10;
    class Inner {
        private int a = 20;
        public void show() {
            int a = 30;
            System.out.println(a);//30
            System.out.println(this.a);//20
            //out.this获取了外部类对象的地址值
            System.out.println(Outer.this.a);//10
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值