Basic

访问修饰符

Java 访问修饰符有四个,权限由小到大依次为:

  • private:可以修饰数据成员、构造方法、方法成员,不可修饰类(外部类或顶层类,不包括内部类)。被 private 修饰后,只能在定义它们的类中访问。

  • default:类、数据成员、构造方法、方法成员都能使用,即不写修饰符。默认权限即同包权限,只能在定义它们的类中以及同包的类中使用。

  • protected:可以修饰数据成员、构造方法、方法成员,不可修饰类(外部类或顶层类,不包括内部类)。可以被定义它们的类、同包类、子类访问。

  • public:类、数据成员、构造方法、方法成员都能使用。可以在任何地方访问。

变量

类变量

  1. 有static修饰。

  2. 存储在方法区。

成员变量

  1. 存储在堆中。

  2. 修饰符有:public、protected、private、final、volatile、transient。

局部变量

  1. 存储在栈中。

  2. 在方法体、代码块、形参中。

  3. 修饰符有:final。

方法

重写

哪些方法不能被重写

  • final方法。

  • static方法。

  • private方法。

  • 构造方法。

内部类

  • 顶层类(Top-level):类的定义代码不嵌套在其它类中。

  • 内部类(Inner Class):

    • 静态内部类(Static Inner Class):

      • 不依赖外部类实例而被实例化。

      • 不能访问外部类的普通成员,只能访问外部类的静态成员和静态方法(包括私有)。

    • 成员内部类(Member Inner Class):

      • 可以自由引用外部类的属性和方法,无论是静态还是非静态。

    • 局部内部类(Local Inner Class):

      • 定义在一个代码块中的类,作用范围为所在代码块。

      • 与局部变量一致,不能被public、protected、private、static 修饰。

      • 只能访问final 类型的局部变量。

    • 匿名内部类(Anonymous Inner Class):

      • 没有构造方法。

      • 必须继承(extend)其它类或接口。

  • 非静态内部类只能在外部类实例化后,才能被实例化;不能有静态成员

  • 静态内部类、成员内部类可以看做是类的静态成员和非静态成员,支持的访问修饰符一致。

  • 局部内部类可以看做局部变量,支持的访问修饰符一致。

  • 外部类可以看做是顶层类,所以与文件名一致的顶层类可以被 public 修饰,而其它则不行。

继承

  • 子类可以继承父类的非私有方法和属性。

  • Java 只能单一继承。

抽象类

  • 如果一个类中有一个方法被 abstract 修饰,那么这个类是抽象类,必须被 abstract 修饰。

  • abstract 只能修饰类的方法,不能修饰变量。

  • 被 abstract 修饰的方法,不能被 private、static、synchronized、native 修饰。

  • 被 abstract 修饰的方法,实现后必须是相同或更低的访问级别。

抽象类的特点是:

  1. 不允许被实例化,只能被继承。

  2. 可以包含属性和方法,方法可以实现也可以不实现。

  3. 非抽象子类继承抽象类,比如实现抽象方法。

抽象类可以解决的问题:代码复用问题。

我们也可以继承非 final 的 public 类的一个空的方法,为什么还需要抽象类的抽象方法? 1. 抽象类从编译层面强制要求实现抽象方法,减少程序员犯错。 2. 一个空的方法引起误会,影响可阅读性。 3. public 类可以被实例化,调用空方法无意义。

接口

  • 接口只能被 public 和 abstract 修饰,或者没有修饰符。当没有修饰符,只能被同包使用。接口默认被 abstract 修饰的,所以添不添加 abstract 修饰符都一样。

  • 接口方法默认被 public、abstract 修饰,所以有没有上述两个修饰符都一样。

  • 当接口方法被 default(JDK1.8增加)修饰时,表示有默认实现,需要用{}来实现 body。

  • 当接口方法被 static(JDK1.8增加)修饰时,只能用接口类(不包括子类)访问方法,不能用接口实例访问。

  • 接口中的成员变量默认是 public、static、final 的,所以有没有这三个修饰符都一样。必须赋初值。

接口的特点是:

  1. 不能包含成员变量。

  2. 只能声明方法,不能实现。(default 例外)。

  3. 非抽象类实现接口时,必须实现接口所有方法。

为什么在 Java8 中接口可以定义 default 方法? Java8 之前,接口不能有默认实现,所以给接口增加一个方法,所有的子类都必须实现,default 可以解决这个问题。

接口解决的问题:侧重于解耦,对行为的抽象,相当于一组协议或契约,约定与实现分离。

抽象类与接口

  • 接口不能有私有变量和方法,而抽象类可以。

  • 接口可以看做是一种特殊的抽象类。

  • 一个类可以实现多个接口,但是只能继承一个抽象类。

  • 接口表示 has-a 的关系,而抽象类表示 is-a 的关系。

  • 接口可以继承接口,抽象类可以实现接口,抽象类可以继承实体类。

接口 OR 抽象类?

  1. 抽象类:表示一种 is-a 的关系,并且是为了解决代码复用的问题;

  2. 接口:表示一种 has-a 关系,并且是为了解决抽象而非代码复用的问题。

设计思路:

  • 抽象类:自下而上,先有子类代码复用,再抽象成上层的父类。

  • 接口:自上而下,先设计接口,再考虑具体实现。

Object 的所有方法

getClass()

final 方法不允许重写,返回运行时的 Class 对象。

hashCode()

默认实现是将对象的内部地址转换成一个整数。可以被覆盖,覆盖的约定如下:

  • 若对象没有被改变,则无论调用多少次 hashCode(),都会返回相同的整数值。

  • 没有必要在不同的程序中保持相同的值。

  • equals()返回 true,则 hashCode()必须相等。

  • equals()返回 falsehashCode()可以相同;但是尽量不相同,可以提高 hash 表的性能。

equals()

默认实现是比较两个对象的地址是否相等。可以被覆盖,约定如下:

  • 自反性,x.equals(x) == true

  • 对称性,若x.equals(y) == true,则y.equals(x) == true

  • 传递性,若x.equals(y) == true && y.equals(z) == true,则x.equals(z) == true

  • x.equals(null) == false

clone()

由于 Object 本身没有实现 Cloneable 接口,所以不重写 clone()方法会抛出异常。

返回对象的浅拷贝

wait()wait(long)wait(long, int)

  • 让当前线程等待。

  • 当前线程必须是调用wait()方法对象的监视器所有者,否则会发生IllegalMonitorStateException异常。

  • wait()方法会将当前线程放置在对象的等待集中,并让当前线程放弃该对象监视器的所有权,即放弃了锁

  • 会被以下事件唤醒:

    • 其它线程调用此对象的notify()方法,并恰巧此线程被选中。

    • 其它线程调用此对象的notifyAll()方法。

    • 其它线程调用 Thread.interrupt()方法中断此线程。

    • 若设置了超时时间,时间超过后。

  • 被唤醒后,线程在等待集中被移除,以常规方式与其它线程竞争,获取该对象的监视器所有权,一旦获得,则 wait()方法返回。

注意

若要成为某个对象监视器的所有者,可以有以下几种方式:

  • 执行对象的同步实例方法。

  • 使用 synchronized内置锁。

  • 对于 Class 对象,执行该类的同步静态方法。

一个线程只能有一个对象的监视器。所以 wait、notify 方法只能在同步代码块或同步方法中使用

wait()释放了锁,而 sleep()仍然持有锁。

notify()notifyAll()

  • 唤醒一个或全部在此对象监视器上等待的线程。

  • 若是一个,则是随机选取一个。

  • 直到当前线程放弃了对象上的锁后,被唤醒的线程才会继续竞争锁。即 notify 方法不会立即释放锁

  • 当前线程必须是该对象监视器的所有者,不然会抛出 IllegalMonitorStateException。

wait 和 notify 一般配套使用,见下面的例子:

Last updated

Was this helpful?