继承
继承是面向对象编程的一个重要概念
父类与子类
一个类继承了另一个类,继承的类就是被继承类的子类,被继承的类就是父类。子类拥有父类的所有属性和方法。同一个包的子类可以全部访问父类的属性和方法,可以重写父类的方法,增加属性,但不同包就要看权限修饰符。
子类继承父类要在子类的类名后面使用保留字extends
,后面是父类的类名。Object类默认是所有类的父类,不需要使用extends
。
Java的类只有单继承,即最多只能继承除一个类,当然Object类不算在里面。
父类与子类之间的转换
可以把子类创建的对象看成是父类对象使用,比如所有类创建的对象都可以被当作Object类的对象使用。
可以把父类对象转成子类对象,但需要用(父类类名)
强制转型,举例:
Object obj = new String("abcd");
String str = (String) obj;
子类之间不能相互转换。
方法重写与方法重载
方法重载:在同一个类编写多个同名方法
要求:
方法名一样;
参数类型个数或返回值类型要不一样。
方法重写:修改来自另一个类的同名方法
要求:
方法名、形参要一样;
返回值类型和声明异常的类型小于等于父类;
访问权限,子类大于等于父类;
super
保留字super
指代的是父类的对象,和this一样,主要在设计类的属性和方法的时候使用。super可以调用父类的属性和方法,通过super();
可以调用父类的构造函数。如果子类没有在构造函数里使用super();
,编译的时候编译器会加上。
class Animal {
void eat() {}
}
class Cat extends Animal(){
Cat(){
super.this();
}
void grow{
super.eat();
}
}
final关键字
保留字final
修饰类,这个类就不能被其他类继承,意味着这个类的功能不能被扩充,比如String类就不能被继承。
public final class String ...
修饰方法,表明这个方法不能被重写。
修饰变量,这个变量就成为常量。
如果修饰的是属性,常常和static一起使用,这样就可以在作为常量被其他类使用。
比如System类里的in属性就是用static和final修饰的。
public static final InputStream in;
InputStream类规定了最大值
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
final属性不能默认初始化和在方法中初始化,可以显式初始化、在代码块初始化、在构造器初始化。在构造器里赋值的话,需要每个构造器都赋值。因为新建对象的时候,final修饰的属性和值都在堆空间里加载了,如果能在方法里赋值的话,就会与之冲突。
如果修饰final
的是局部变量,可以修饰形参和方法体内的变量。修饰形参的时候,不能方法体里给形参赋值操作,比如++这种操作就不行,传入实参后,就会成为常量,但如果传入的是一个类,那么类的属性还可以变,只是类本身不能变,即内存地址不能变。
抽象类
使用保留字abstract
,相当于给子类创建了一个模板;
抽象类无法实例化;
public abstract class InputStream ...
抽象方法不能有实体;
所有的抽象方法必须在子类被重写,也可以被重载;
所有的抽象方法都只能在抽象类里创建,否则报错;
例如:
public abstract int read() throws IOException;
抽象类里可以有普通的方法,但无法被调用,因此需要编写方法的实体,比如
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
接口
用保留字interface
修饰,而不是class
类似于抽象类,也是为了创建一个模板,但接口可以多继承,一个类可以继承多个接口,所以接口比抽象类要更加普遍地使用。
接口没有构造函数,只有方法和常量;
接口只能加public权限修饰符,不能加其他权限修饰符;
实现接口的类必须重写接口的全部方法;
public interface Comparable<T> {
int compareTo(T var1);
}
调用的时候使用“implements”加要调用的接口
举例:
public abstract class AbstractCollection<E> implements Collection<E>
如果是一个接口继承另一个接口,则用extends
关键字
public interface List<E> extends Collection<E> ...
接口内的方法默认由编译器添加public
和abstract
关键字,最好不要自己添加修饰符,比如 Comparable接口
public interface Comparable<T> {
int compareTo(T var1);
}
从Java8开始,接口中还有一种默认方法,用保留字default
修饰。因为实现接口的类必须重写接口的全部方法,如果过了很长一段时间后再在接口添加方法,那么实现方法的类就无法编译,除非你花很多精力找出来。这时我们就需要使用默认方法。
默认方法可以有方法体,可以不用被重写。
public interface Iterable<T> {
......
default void forEach(Consumer<? super T> var1) {
Objects.requireNonNull(var1);
Iterator var2 = this.iterator();
while(var2.hasNext()) {
Object var3 = var2.next();
var1.accept(var3);
}
}
......
}
如果这个类继承了这个接口,继承的父类,那么以父类为准,这就是 “class wins” 原则。如果实现其他接口也有一模一样的方法,必须重写这个方法。
接口中很少使用属性,但使用时不加任何修饰符,由编译器添加public static final
三个关键字,如Iterable接口
public interface Iterable<T> {
Iterator<T> iterator();
......
}
多态
子类转换为父类或接口后,调用普通方法依然是调用的子类的方法。
作用:
- 可以实现用接口管理各个实现类的行为。
- 解耦:方法的参数可以是一个接口,这样可以选择任意一个实现类放进这个方法。
举例:
public class User {
public void status() {
System.out.println("身份");
}
}
public class Student extends User{
public void status() {
System.out.println("学生");
}
}
public class Teacher extends User{
public void status() {
System.out.println("老师");
}
}
public class Test {
public static void main(String[] args) {
User s1 = new Student(100,"Jerry","art"); //父类引用指向子类对象
Teacher t1 = new Teacher(100,"Jerry","art");
status((Student)s1);
status(t1);}
static public void status(User u) {
u.status();
}
}
内存分析:
从object类开始创建,一直到最外层的类;
因为所有类的“this”都是指的最外层的类,所有的方法都先从最外层开始调用。