面试必备基础知识 — Java基础(二)

本文详细介绍了Java中的各种修饰符,包括权限修饰符、状态修饰符和抽象修饰符,以及Object类中的通用方法如equals(), hashCode(), clone()等。深入探讨了static, final, abstract关键字的使用场景,以及继承、抽象类、接口的概念和区别。

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

修饰符

权限修饰符

public、protected、默认修饰符、private 的访问权限

权限修饰符publicprotected默认(void)private
本类中YesYesYesYes
同一包下(子类和无关类)YesYesYesNo
不同包下的子类YesYesNoNo
不同包下的无关类YesNoNoNo

状态修饰符

staticfinal

抽象修饰符

abstract

  • 权限修饰符:public、默认修饰符
  • 状态修饰符:final
  • 抽象修饰符:abstract
  • 用的最多的是public

成员变量

  • 权限修饰符:public、protected、默认修饰符、private
  • 状态修饰符:static、final
  • 用的最多的是private

构造方法

  • 权限修饰符:public、protected、默认修饰符、private
  • 用的最多的是public

成员方法

  • 权限修饰符:public、protected、默认修饰符、private;
  • 状态修饰符:static、final;
  • 抽象修饰符:abstract
  • 用的最多的是public

关键字

final

  • 修饰类
    当用final 修饰一个类的时候,表明这个类不能被继承

  • 修饰方法
    被final 修饰的方法不能被子类重写(可以重载)
    如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时子类的方法不是重写了基类的方法,而是在子类中重新定义了新的方法。

  • 修饰变量
    被final修饰的成员变量 表示常量,只能被赋值一次,赋值后,值不再改变。

    • 对于基本类型,final 使数值不变;
    • 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
public class Test { 
    public static void main(String[] args)  { 
        final MyClass myClass = new MyClass(); 
        System.out.println(++myClass.i); // i = 1
    } 
} 

class MyClass { 
    public int i = 0; 
} 

static

静态变量

  • 静态变量:又称为类变量,也就是说静态变量属于类,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量随着类的加载完成初始化,它在内存中仅有一个,且JVM也只会为它分配一次内存。
  • 实例变量(没有被static修饰的变量):与静态变量不同,它是伴随着实例的,没创建一个实例就会产生一个实例变量,它与该实例同生共死。
public class A {

    private int x;         // 实例变量
    private static int y;  // 静态变量
    
    public static void main(String[] args) {
        // int x = A.x; //这个语句是错误的,实例变量不能通过类名来调用
        A a = new A();
        int x = a.x;
        int y = A.y;  //或者 int y = a.y
    }
}

静态方法

由于静态方法不依赖于任何对象就可以进行访问,所以静态方法必须有实现,也就是说它不能是抽象方法,而且对于静态方法来说,是没有this 的。因为这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法。非静态成员变量 / 方法都是必须依赖具体的对象才能够被调用。但是在非静态成员方法中是可以调用静态成员方法和变量的。

静态语句块

静态语句块只在类初始化时运行一次,因此静态代码块可以用来优化程序性能。

public class A {
    static {
        System.out.println("123");
    }
    public static void main(String[] args) {
        A a = new A();
    }
}
//运行代码,输出 123

Java中的static关键字解析

静态导包

在使用静态变量和方法时,不用再指明ClassName,从而简化代码,但可读性大大降低。

import static com.xxx.ClassName.*

初始化顺序

静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。

public static String staticField = "静态变量";
static {
    System.out.println("静态语句块");
}
public String field = "实例变量";
{
    System.out.println("普通语句块");
}

然后才是构造函数的初始化

public InitialOrderTest() {
    System.out.println("构造函数");
}

如果存在继承关系,初始化顺序为:

  • 父类 静态变量 和 静态语句块
  • 子类 静态变量 和 静态语句块
  • 父类 实例变量 和 普通语句块
  • 父类 构造函数
  • 子类 实例变量 和 普通语句块
  • 子类 构造函数

Object的通用方法

equals()

public boolean equals(Object obj)

等价与相等 / equals 与 ==

  • 对于基本类型, == 判断两个值是否相等,基本类型没有equals()方法
  • 对于引用类型
    • == 判断两个变量是否引用同一个对象(所指向的对象地址)
    • 如果没有对 equals() 方法进行重写,则比较的是引用类型的变量所指向的对象地址;
    • 比如String等类重写了 equals()方法 比较的是所指向的对象的内容

hashCode()

public native int hashCode()

由方法声明可知,该方法返回一个int类型的数值,并且是本地方法,因此,在Object类中并没有给出具体的实现。

Hash,一般翻译做散列、杂凑,或音译为哈希

hashCode()方法的主要作用是为了配合基于散列集合一起正常运行,这样的散列集合包括HashSet、HashMap 以及 HashTable。
散列集合使用hashCode方法来计算对象应该存储的位置,因此要将对象添加到这些集合类中,需要让对应的类实现 hashCode()方法

当向集合中插入对象时,如何判断该对象是否已经存在,此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对象的哈希值,通过判断,如果集合中没有该哈希值,它就可以直接存进去,不用再进行任何比较。如果存在该哈希值,就调用它的equals方法 与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了。

Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值
//new两个等价对象
EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
//此EqualExample类没有实现 hashCode()方法,因此这两个对象的哈希值是不同的
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
//最终导致集合中添加了两个等价对象
System.out.println(set.size());   // 2

hashCode() 返回的是哈希值,而equals() 是用来判断的。等价的两个对象散列值一定相同,但散列值相同的两个对象不一定等价。

在重写 equals()方法时,应当总是重写 hashCode() 方法,保证等价的两个对象哈希值也相等。

浅谈Java中的hashcode方法

clone()

下面这篇文章写的很棒,通俗易懂。
Java如何复制对象

继承

抽象类

抽象方法
抽象方法是一种特殊的方法,它只有声明,没有具体实现。

abstract void method();
  • 抽象方法必须用abstract关键字修饰
  • 如果一个类含有抽象方法,则这个类为抽象类

抽象类
抽象类必须在类前面用 abstrac 关键字修饰

  • 因为抽象类中含有无具体实现的方法(抽象方法),所以不能用抽象类来创建对象。
  • 一个抽象类中不一定必须含有抽象方法
public abstract class ClassName {
    abstract void method();
}

抽象类和普通类的最大区别就是,抽象类不能被实例化,只能被继承。

接口

接口(interface),它是对行为的抽象。

public interface InterfaceName {
 
}

特点

  • 在Java 1.8之前,它可以看成是一个完全抽象的类,也就是说,他不能有任何方法的实现。
  • Java 1.8开始,接口也可以有默认的实现方法。
  • 接口中的变量会被隐式的指定为public static finale 变量,用 private 修饰会报编译错误
  • 接口中的方法会被指定为 public abstract 方法,其它修饰也会报编译错误

抽象类与接口的区别

  • 一个类可以实现多个接口,但只能继承一个抽象类
  • 接口的字段(变量)只能是static 和 finale 类型的,而抽象类的字段没有这种限制
  • 接口的成员只能是public 的,而抽象类的成员可以有多种访问权限

深入理解abstract class和interface

super

  • 访问父类的构造方法,从而委托父类完成一些初始化工作
  • 访问父类的成员,如果子类重写父类的某个方法,可以使用 super 关键字来引用父类的方法实现
//父类
public class SuperExample {
    protected int x;
    protected int y;
    public SuperExample(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public void func() {
        System.out.println("SuperExample.func()");
    }
}
//子类
public class SuperExtendExample extends SuperExample {
    private int z;
    public SuperExtendExample(int x, int y, int z) {
        super(x, y); //使用super访问父类成员变量
        this.z = z;
    }
    @Override
    public void func() {
        super.func();//使用super访问父类成员方法
        System.out.println("SuperExtendExample.func()");
    }
}

重写 与 重载

重写存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的方法。
使用 @Override 注解,可以让编译器检查是否满足重写的限制条件。

重载存在于同一个类中,指一个方法与一个已经存在的方法名称上相同,但是参数类型、个数和顺序至少有一个不同。
需要注意的是,返回值不同,其它都相同 不算是重载。

文章学习自 CYC

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值