【Java学习笔记】基础知识

一、关键字

final

  1. 数据
    声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。
  • 对于基本类型,final 使数值不变;
  • 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
final int x = 1;
// x = 2;  // cannot assign value to final variable 'x'
final A y = new A();
y.a = 1;
  1. 方法
    final 声明方法不能被子类重写。
    private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。


  2. 声明类不允许被继承。

static

  1. 静态变量
  • 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。
  • 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。
public class A {
    
    

    private int x;         // 实例变量
    private static int y;  // 静态变量

    public static void main(String[] args) {
    
    
        // int x = A.x;  // Non-static field 'x' cannot be referenced from a static context
        A a = new A();
        int x = a.x;
        int y = A.y;
    }
}
  1. 静态方法
    静态方法在 类加载 的时候就存在了,它不依赖于任何实例。所以 静态方法必须有实现 ,也就是说它不能是抽象方法。
public abstract class A {
    
    
    public static void func1(){
    
    
    }
    // public abstract static void func2();  // Illegal combination of modifiers: 'abstract' and 'static'
}

只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字,因为这两个关键字与具体对象关联。

public class A {
    
    

    private static int x;
    private int y;

    public static void func1(){
    
    
        int a = x;
        // int b = y;  // Non-static field 'y' cannot be referenced from a static context
        // int b = this.y;     // 'A.this' cannot be referenced from a static context
    }
}
  1. 静态语句块
    静态语句块在类初始化时运行一次。
public class A {
    
    
    static {
    
    
        System.out.println("123");
    }

    public static void main(String[] args) {
    
    
        A a1 = new A();
        A a2 = new A();
    }
}
123
  1. 静态内部类
    非静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才能用这个实例去创建非静态内部类。而静态内部类不需要。
public class OuterClass {
    
    

    class InnerClass {
    
    
    }

    static class StaticInnerClass {
    
    
    }

    public static void main(String[] args) {
    
    
        // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
        OuterClass outerClass = new OuterClass();
        InnerClass innerClass = outerClass.new InnerClass();
        StaticInnerClass staticInnerClass = new StaticInnerClass();
    }
}

静态内部类不能访问外部类的非静态的变量和方法。

  1. 静态导包
    在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。
import static com.xxx.ClassName.*
  1. 初始化顺序
    静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。
public static String staticField = "静态变量";
static {
    
    
    System.out.println("静态语句块");
}
public String field = "实例变量";
{
    
    
    System.out.println("普通语句块");
}

最后才是构造函数的初始化。

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

存在继承的情况下,初始化顺序为:

父类(静态变量、静态语句块)
子类(静态变量、静态语句块)
父类(实例变量、普通语句块)
父类(构造函数)
子类(实例变量、普通语句块)
子类(构造函数)

二、Object 通用方法

概览

代码如下(示例):


public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native Class<?> getClass()

protected void finalize() throws Throwable {
    
    }

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

equals()

  1. 等价关系

两个对象具有等价关系,需要满足以下五个条件:

  • Ⅰ 自反性
x.equals(x); // true
  • Ⅱ 对称性
x.equals(y) == y.equals(x); // true
  • Ⅲ 传递性
if (x.equals(y) && y.equals(z))
    x.equals(z); // true;
  • Ⅳ 一致性

多次调用 equals() 方法结果不变

x.equals(y) == x.equals(y); // true
  • Ⅴ 与 null 的比较

对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false

x.equals(null); // false;
  1. 等价与相等
  • 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
  • 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。
Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y);      // false
  1. 实现
    检查是否为同一个对象的引用,如果是直接返回 true;
    检查是否是同一个类型,如果不是,直接返回 false;
    将 Object 对象进行转型;
    判断每个关键域是否相等。
public class EqualExample {
    
    

    private int x;
    private int y;
    private int z;

    public EqualExample(int x, int y, int z) {
    
    
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public boolean equals(Object o) {
    
    
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        EqualExample that = (EqualExample) o;

        if (x != that.x) return false;
        if (y != that.y) return false;
        return z == that.z;
    }
}

hashCode()

hashCode() 返回哈希值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价,这是因为计算哈希值具有随机性,两个值不同的对象可能计算出相同的哈希值。

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

HashSet 和 HashMap 等集合类使用了 hashCode() 方法来计算对象应该存储的位置,因此要将对象添加到这些集合类中,需要让对应的类实现 hashCode() 方法。

下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。我们希望将这两个对象当成一样的,只在集合中添加一个对象。但是 EqualExample 没有实现 hashCode() 方法,因此这两个对象的哈希值是不同的,最终导致集合添加了两个等价的对象。

EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size());   // 2

三、继承

访问权限

Java 中有三个访问权限修饰符:private、protected 以及 public。

可以对类或类中的成员(字段和方法)加上访问修饰符。

  • 类可见表示其它类可以用这个类创建实例对象。

  • 成员可见表示其它类可以用这个类的实例对象访问到该成员;

  • protected 用于修饰成员,表示在继承体系中成员对于子类可见,但是这个访问修饰符对于类没有意义。

  • public修饰符表示任何包下都能够访问,而不加任何访问修饰符的,即默认情况下,表示只能同级包内访问。

  • private修饰的变量只能在本类中使用,其他类或者子类都无法访问

  • 处于同级包下的类在调用另一个类中的变量的时候,除了private修饰的私有变量不能访问外,其他修饰符修饰的变量都能访问;
  • 处于不同级包的类只能访问public修饰的变量,其他都不能访问。
  • public修饰的变量在其他类和子类中都能被访问(不管其他类是在同级包还是不在都能够访问)。
  • protected和default的区别就在于子类是否和父类在同级包中,protected修饰的变量子类在其他包中也可以访问,但是default情况下的变量子类只能在同级包中才可以访问。

抽象类与接口

  1. 抽象类
    抽象类和抽象方法都使用 abstract 关键字进行声明。如果一个类中包含抽象方法,那么这个类必须声明为抽象类。

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

  1. 接口

接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。

从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类,让它们都实现新增的方法。

3.比较
从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。
接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。
接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。

super

访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。应该注意到,子类一定会调用父类的构造函数来完成初始化工作,一般是调用父类的默认构造函数,如果子类需要调用父类其它构造函数,那么就可以使用 super() 函数。
访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。

重写与重载

  1. 重写(override)
    存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。

为了满足里式替换原则,重写有以下三个限制:

子类方法的访问权限必须大于等于父类方法;
子类方法的返回类型必须是父类方法返回类型或为其子类型。
子类方法抛出的异常类型必须是父类抛出异常类型或为其子类型。

使用 @Override 注解,可以让编译器帮忙检查是否满足上面的三个限制条件。

在调用一个方法时,先从本类中查找看是否有对应的方法,如果没有再到父类中查看,看是否从父类继承来。否则就要对参数进行转型,转成父类之后看是否有对应的方法。总的来说,方法调用的优先级为:

this.func(this)
super.func(this)
this.func(super)
super.func(super)

  1. 重载(overload)

存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是 参数类型、个数、顺序 至少有一个不同。

应该注意的是,返回值不同,其它都相同不算是重载。

猜你喜欢

转载自blog.csdn.net/weixin_43821215/article/details/129789407
今日推荐