类、抽象类和接口

本文深入探讨Java中的类与对象概念,包括类变量、实例变量、类方法、实例方法的区别与联系,构造方法的特点及作用,以及静态初始化代码块、非静态代码块的执行顺序。同时,讲解了继承、封装、多态等面向对象特性,重写与重载的使用,以及抽象类和接口的对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据类型

  • 类变量(静态变量或静态成员变量):类型本身具有的属性, static修饰
  • 类方法 (静态方法):类型本身可以进行的操作,static修饰
  • 实例变量:类型实例具有的属性,每个实例都可以有不同值
  • 实例方法:类型实例可以进行的操作

static不允许用来修饰局部变量

public class BigDecimal extends Number implements Comparable<BigDecimal> {
    // 实例变量
    private final int scale;  
    // 类变量
    public static final BigDecimal ZERO =
        zeroThroughTen[0];
    
    // 实例方法
    public BigDecimal abs(MathContext mc) {
        return (signum() < 0 ? negate(mc) : plus(mc));
    }
    
    // 类方法
    private static long add(long xs, long ys){
        long sum = xs + ys;
        if ( (((sum ^ xs) & (sum ^ ys))) >= 0L) { // not overflowed
            return sum;
        }
        return INFLATED;
    }
}
复制代码
成员变量:类变量和实例变量
成员方法:类方法和实例方法

注意:

  • 实例变量和实例方法只能通过实例(对象)来访问
  • 类方法只能访问类变量不能访问实例变量,只能调用类方法不能调用实例方法
  • 实例方法既能访问类变量又能访问实例变量,既能调用类方法又能调用实例方法
对象和数组有两块内存:一块存放实际内容,一块存放实际内容的位置
静态初始化代码块(只会在类加载时被执行一次)
非静态代码块

构造方法

特点
  • 名称固定,与类名相同
  • 没有返回值,也不能有返回值。隐含的返回值为实例本身
  • 可以重载
  • 子类通过super调用父类的构造方法,没有显示调用会自动调用父类默认的构造方法
this用法
  • 表示当前实例
  • 在构造方法中调用其他构造方法,必须放在方法第一行
默认构造方法
  • 每个类至少有一个构造方法,编译器自动生成
  • 如果定义了构造方法则不会再生成
私有构造方法(private 修饰)使用场景
  • 不能创建类的实例,类只能被静态访问
  • 能创建类的实例,但只能类的静态方法调用(单例模式)
  • 被其他构造方法调用
public class BigDecimal extends Number implements Comparable<BigDecimal> {

    public BigDecimal(BigInteger val) {
        scale = 0;
        intVal = val;
        intCompact = compactValFor(val);
    }
    // 重载构造方法
    public BigDecimal(BigInteger val, MathContext mc) {
        // this 调用其他构造方法
        this(val,0,mc);
    }
}
复制代码
new 操作即创建对象
分配内存(包括本类和父类所有的实例变量,不包括静态变量)
给实例变量设置默认值
执行实例初始化代码(定义实例变量是的赋值语句、非静态代码块和构造方法)
复制代码

类加载时内部成员加载顺序

public class A {
    public A(String className){
        System.out.println("静态变量:"+className);
    }
}

public class B {
    public B(String className){
        System.out.println("实例变量:"+className);
    }
}

public class Base {
    private B baseB = new B("父类");
    private static A baseA = new A("父类");

    static{
        System.out.println("父类静态代码块");
    }
    
    {
        System.out.println("父类非静态代码块");
    }
    public Base(){
        System.out.println("父类构造方法");
    }
}

public class Test extends Base {

    private B baseB = new B("子类");
    private static A baseA = new A("子类");
    {
        System.out.println("子类非静态代码块");
    }

    static{
        System.out.println("子类静态代码块");
    }

    public Test(){
        System.out.println("子类构造方法");
    }

    public static void main(String[] args) {
        new Test();
    }
}

静态变量:父类
父类静态代码块
静态变量:子类
子类静态代码块

实例变量:父类
父类非静态代码块
父类构造方法

实例变量:子类
子类非静态代码块
子类构造方法

// 变换静态代码块和静态变量的顺序
// 变换非静态代码块和实例变量的顺序
public class Base {

    public Base(){
        System.out.println("父类构造方法");
    }
    static{
        System.out.println("父类静态代码块");
    }
    {
        System.out.println("父类非静态代码块");
    }
    private B baseB = new B("父类");
    private static A baseA = new A("父类");

}

public class Test extends Base {
    public Test(){
        System.out.println("子类构造方法");
    }

    {
        System.out.println("子类非静态代码块");
    }

    static{
        System.out.println("子类静态代码块");
    }
    private B baseB = new B("子类");
    private static A baseA = new A("子类");

    public static void main(String[] args) {
        new Test();
    }
}
父类静态代码块
静态变量:父类
子类静态代码块
静态变量:子类
父类非静态代码块
实例变量:父类
父类构造方法
子类非静态代码块
实例变量:子类
子类构造方法
复制代码

1、 首先执行Test的入口方法main方法,但在执行main方法前需要先加载Test类,加载Test类时发现Test类继承自Base类,所以要先加载Base类。

2、 加载Base类时,要执行类成员赋值--类变量初始化和静态代码块初始化(具体顺序根据定义的顺序来),加载完Base类后接着加载Test类,执行类变量初始化和静态代码块初始化

3、 类加载完成后开始执行main方法,执行new Test()方法。Test类有父类所以先执行父类的操作,实例成员赋值--实例变量初始化和非静态代码块初始化(具体顺序根据定义的顺序来),调用Base的构造方法,然后执行Test的操,作实例变量初始化和非静态代码块初始化(具体顺序根据定义的顺序来),调用Test的构造方法

执行顺序总结

1、 父类静态成员 2、 子类静态成员 3、 父类实例成员 4、 父类构造方法 5、 子类实例成员 6、 子类构造方法

  • 静态代码块和静态变量执行的顺序根据定义的先后顺序
  • 非静态代码块和实例变量的顺序执行的顺序根据定义的先后顺序
每个类封装器内部细节,对外提供更高层次的功能,使其他类在更高层次上考虑和解决问题,是程序设计的一种基本思维方式。

继承

特点

  • 代码复用,公共的属性和方法放在父类而子类只关注子类特有的功能
  • 统一处理不同的子类对象
如果一个类没有声明父类,会有一个隐含的父类Object
一个类最多只能继承一个父类
子类不能访问父类私有的方法和属性,继承私有外的方法和属性

super 关键字

super用于指代父类,可以调用父类的构造方法,访问父类的方法和变量
this引用一个真实的对象,可以作为函数参数和返回值,但super只是一个关键字,不能作为参数和返回值
复制代码

重写和重载

重写
发生在父类与子类之间
可以加@Override注解
子类重新定义父类中的同名且参数签名相同的方法
复制代码
重载
发生在同一类中
重载是指方法名相同但参数签名不同(参数个数、顺序、类型)
复制代码
当有多个重名函数的时候,在决定调用哪个函数的过程中,首先按照参数的类型进行匹配,寻找素有重载方法中最匹配的,然后才看变量的动态类型,进行动态绑定
向上转型

子类对象赋值给父类应用变量,转型就是转换类型,向上转型就是转换成父类类型

Child child = new Child();
Base base = child; // 向上转型
复制代码
多态

多态指子类对象可以赋值给父类应用变量

一种类型的变量可以引用多种实际子类型的变量

静态类型指声明的类型,动态类型指引用的类型
// 类型Base 称为 base 的静态类型,child1、child2 称为base的动态类型
Child child1 = new Child();
Child child2 = new Child();
Base base = child1;
Base base = child2;
复制代码
动态绑定
动态绑定指调用方法是调用的是动态类型(子类)的方法
需运行时才能确认
复制代码
静态绑定
静态绑定指访问绑定到变量的静态类型
编译阶段即可确认
实例变量、静态变量、静态方法和private方法都是静态绑定
复制代码
一个父类的变量能不能转换成子类的变量,取决于父类变量的动态类型是不是这个子类或子类的子类
instanceof 关键字判定一个引用是不是指定类型
封装就是隐藏实现细节,提供简化接口
继承可能破坏封装,因为子类和父类之间可能存在着实现细节的依赖
优先使用组合和接口

抽象类

  • 定义了抽象方法的类必须声明为抽象类
  • 抽象类可以有非抽象的方法
  • 抽象类可以定义实例变量

接口

  • 接口中的方法的默认修饰符 public abstract
  • 接口中的变量的默认修饰符 public static final
  • 接口可以多继承,类可以实现多个接口
  • 可以用instanceof 关键字判断一个对象是否实现某个接口
  • 使用接口和组合代替继承既可以统一处理又可以服用代码
  • 接口中可以有静态方法和默认方法
public interface List<E> extends Collection<E> {

}
复制代码
针对接口编程可以复用代码,降低耦合,提高灵活性

抽象类和接口对比

  • 抽象类和接口都不能用于创建对象
  • 接口只能定义静态变量不能定义实例变量,抽象类可以定义实例变量和静态变量
  • 一个类可以实现多个接口,只能继承一个类
  • 接口声明能力,抽象类提供默认实现。可以配合使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值