构造方法
- 构造方法也要加上修饰词
public
- 如果我们自定义了一个构造方法,那么,编译器就不再自动创建默认构造方法
- 没有在构造方法中初始化字段时,引用类型的字段默认是
null
,数值类型的字段用默认值,int
类型默认值是0
,布尔类型默认值是false
:
方法重载 Overload
- 方法名相同,但各自的参数不同,称为方法重载(
Overload
) - 注意:方法重载的返回值类型通常都是相同的
继承 extends
- Java只允许一个class继承自一个类,因此,一个类有且仅有一个父类。只有
Object
特殊,它没有父类。 - 继承有个特点,就是子类无法访问父类的
private
字段或者private
方法。为了让子类可以访问父类的字段,我们需要把private
改为protected
。用protected
修饰的字段可以被子类访问。 super.xx
调用父类的字段和方法super()
调用父类的构造方法- 子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。
- 向上转型
- 子类可以给父类赋值(向上转型),即将父类的指针指向子类的实例。因为父类所具有的字段和方法子类都有。 ```java Person p = new Student();
//or Student s = new Student(); Person p = s;
- 向下转型
- 但是父类不能给子类赋值(向下转型),即不能用子类的指针指向父类的实例,因为子类的很多字段和方法都是父类的实例不具备的。
- 也有向下转型成功的例子
```java
Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok 因为p1实际上本身就是指向学生实例,所以向下转型成功
Student s2 = (Student) p2; // runtime error! ClassCastException! 转型失败
- 要向下转型时,先用
instanceof
判断一个变量的实例实际上是否是指定类型。Person p = new Student(); if (p instanceof Student) { // 只有判断成功才会向下转型: Student s = (Student) p; // 一定会成功 }
多态 override
覆写 Override
- 子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)
覆写方法的前面要加上
@Override
class Student extends Person { @Override public void run() { System.out.println("Student.run"); } }
Override
和Overload
的区别Overload
:新方法,不同的方法- 方法签名不同(即参数不同)
- 返回值不同(但是 Java 编译器会报错)
Override
:同样的方法- 方法签名相同,并且返回值也相同
覆写 Object 方法:因为所有的
class
最终都继承自Object
, 因此可以覆写Object
的几个重要方法toString()
:把instance输出为String
equals()
:判断两个instance是否逻辑相等hashCode()
:计算一个instance的哈希值class Person { ... // 显示更有意义的字符串: @Override public String toString() { return "Person:name=" + name; } // 比较是否相等: @Override public boolean equals(Object o) { // 当且仅当o为Person类型: if (o instanceof Person) { Person p = (Person) o; // 并且name字段相同时,返回true: return this.name.equals(p.name); } return false; } // 计算hash: @Override public int hashCode() { return this.name.hashCode(); } }
覆写时可通过
super()
调用父类被覆写的方法 ```java class Person { protected String name; public String hello() {return "Hello, " + name;
} }
Student extends Person { @Override public String hello() { // 调用父类的hello()方法: return super.hello() + “!”; } }
<a name="k3Hqy"></a>
## final
- `final` 修饰的方法不能被覆写
```java
class Person {
protected String name;
public final String hello() { //final 修饰的方法不能被覆写
return "Hello, " + name;
}
}
Student extends Person {
// compile error: 不允许覆写
@Override
public String hello() {
}
}
final
修饰的类不能被继承 ```java final class Person { protected String name; }
// compile error: 不允许继承自Person Student extends Person { }
- `final` 修饰的字段是常量,必须在创建对象时初始化,而不能被重新赋值(如果是类中的字段,可在构造函数中初始化赋值该字段)
<a name="VKzHR"></a>
# 抽象类 abstract
- 抽象类不能被实例化
- **抽象类**本身被设计成只用于继承,强迫子类实现其定义的抽象方法。父类的抽象方法只需定义,无需实现,相当于**只给子类定义了规范**,目的是让子类去覆写它
- 抽象类 和 抽象方法都要用 **abstract** 修饰
```java
public class Main {
public static void main(String[] args) {
Person p = new Student(); //尽量引用高层类型,避免引用实际子类型 —— 面向抽象编程
p.run();
}
}
abstract class Person { //抽象类要使用 abstract 修饰
public abstract void run(); //抽象方法要使用 abstract 修饰
}
class Student extends Person {
@Override
public void run() { //子类覆写父类的抽象方法
System.out.println("Student.run");
}
}
- 尽量引用高层类型,通过高层抽象类去引用具体子类的实例,避免引用实际子类型 —— 面向抽象编程
抽象类规定高层类的接口,从而保证所有子类都有相同的接口实现——从而发挥多态的威力
Person s = new Student(); Person t = new Teacher(); Person e = new Employee(); // 不关心Person变量的具体子类型,不关心新的子类是如何实现run()方法的 s.run(); t.run(); e.run();
如果子类继承了抽象类,没有实现抽象方法,则该子类仍是一个抽象类
接口 interface
- 如果一个抽象类没有字段,所有方法全部都是抽象方法,可以把该抽象类改写为接口:
interface
```java interface Person { void run(); String getName(); }
//相当于: abstract class Person { public abstract void run(); public abstract String getName(); }
- 子类继承实现接口,使用修饰符 `implements` ,而不是使用 `extends`
```java
class Student implements Person { //继承接口使用 implements
private String name;
public Student(String name) {
this.name = name;
}
@Override //覆写接口的方法,可以省略 @Override
public void run() {
System.out.println(this.name + " run");
}
@Override
public String getName() {
return this.name;
}
}
一个类只能继承一个父类,但是可以继承实现多个
interface
class Student implements Person, Hello { // 实现了两个interface ... }
接口可以继承另一个接口,但是通过
extends
继承。这相当于扩展了接口的方法 ```java interface Hello { void hello(); }
interface Person extends Hello { //接口通过 extends 继承另一个接口 void run(); String getName(); }
- 在接口中,可以定义`default`方法。例如,把`Person`接口的`run()`方法改为`default`方法。实现类可以不必覆写default方法。
```java
interface Person {
String getName();
default void run() {
System.out.println(getName() + " run");
}
}
静态字段 static
- 用
static
修饰的字段,称为静态字段 - 静态字段只有一个共享“空间”,所有实例都会共享该字段。因此,对于静态字段,无论修改哪个实例的静态字段,效果都是一样的,所有实例的静态字段都被修改了
- 因此,推荐用类名来访问静态字段,而不是用实例(因为静态字段实际上并不属于实例)
```java
public class Main {
public static void main(String[] args) {
} }Person ming = new Person("Xiao Ming", 12); Person hong = new Person("Xiao Hong", 15); Person.number = 88; //推荐用类名来访问静态字段 System.out.println(Person.number);
class Person { public String name; public int age;
public static int number;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
<a name="lEjRH"></a>
## 静态方法
- 用`static`修饰的方法称为静态方法
- 调用静态方法则不需要实例变量,通过类名就可以调用
```java
public class Main {
public static void main(String[] args) {
Person.setNumber(99); //通过类名就可以调用静态方法
System.out.println(Person.number);
}
}
class Person {
public static int number;
public static void setNumber(int value) {
number = value;
}
}
- 静态方法属于
class
而不属于实例,因此,静态方法内部,无法访问this
变量,也无法访问实例字段,它只能访问静态字段 - 接口(
interface
)不能定义实例字段,但可以有可以有final
类型的静态字段 ```java public interface Person { public static final int MALE = 1; public static final int FEMALE = 2; }
//或者简写为: public interface Person { // 编译器会自动加上public statc final,因为interface的字段只能是public static final类型 int MALE = 1; int FEMALE = 2; }
<a name="C7qsE"></a>
# 包 package
- 实例:eg. "`Person.java`"文件
```java
package ming; // 申明包名ming
public class Person {
}
就可以通过 ming.Person
访问 Person
类
- 没有定义包名的class,它使用的是默认包,非常容易引起名字冲突,因此,不推荐不写包名的做法。
- 不用
public
、preotected
、private
修饰的字段和方法就是包作用域的,位于同一个包的类可以访问包作用域的字段和方法 - 包的引用 ```java mr.jun.Arrays arrays = new mr.jun.Arrays(); //使用完整类名引用mr.jun.Arrays类
//或 // 导入mr.jun包的所有class: import mr.jun.*; //or: //import mr.jun.Arrays; Arrays arrays = new Arrays(); ```
作用域
public
- 定义为
public
的class
、interface
可以被其他任何类访问 - 定义为
public
的field
、method
可以被其他类访问,前提是首先有访问class
的权限 - 一个
.java
文件只能包含一个public
类,但可以包含多个非public
类。如果有public
类,文件名必须和public类的名字相同
- 定义为
private
- 定义为
private
的field
、method
的访问权限被限定在类里,无法被其他类访问,除非是内部嵌套类 - 因此一般把
public
的方法定义写在前面(对外提供的功能),方便阅读
- 定义为
protected
- 用于继承,可以被子类(及子类的子类…)访问
- 包作用域
- 不用
public
、preotected
、private
修饰的字段和方法就是包作用域的,位于同一个包的有访问权限
- 不用
- 局部变量
- 尽可能把局部变量的作用域缩小,尽可能延后声明局部变量