类,抽象类,接口,多态
1. 一般类
1.1. 类的声明与使用
修饰符 class 类名 {
// 构造方法
修饰符 类名(参数列表) {}
// 成员变量
修饰符 变量类型 变量名;
// 成员方法
修饰符 返回类型 方法名(参数列表) {}
}
类名 实例名 = new 类名(参数列表); // 创建对象实例,通过参数列表选择不同的构造方法进行初始化
实例名.变量名; // 调用成员变量
实例名.方法名(); // 调用成员方法
若不写构造方法则默认有个隐式的无参构造方法,若自定义了一个有参构造方法,则需自己定义无参构造方法。
1.2. 继承
子类会继承父类可继承(看权限控制修饰符)的成员变量和成员方法。
class 子类 extends 父类 {}
单继承:extends后只能跟一个类,即一个子类只能继承一个父类。
多重继承:A继承B,B继承C,A和C是在一个继承体系中,即多重继承。
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用,可以用this.成员变量名
来避免同名。
覆盖override
子类中出现一个和父类相同的方法(参数列表,方法名)时,称子类的这个方法覆盖了父类的方法。
子类覆盖父类时,子类权限必须要大于等于父类的权限。
返回类型必须和被覆盖方法的返回类型相同或者是返回类型的子类型。
- final方法不能被覆盖,static方法只能被static方法覆盖。
- 覆盖方法不能抛出新的检查异常,或者是抛出比被覆盖方法声明的检查异常更广泛的检查异常。
构造方法
- 子类不会继承父类的构造方法
- 如果子类没有构造方法,默认会写一个无参构造方法其内部隐式调用父类的无参构造方法。
- 子类的构造方法内部默认第一句是隐式的super(),除非显示的定义了super(参数)来调用父类的构造函数。
- 子类要使用父类有参的构造器,使用super(参数)形式,且super只能是子类构造方法内部的第一条语句。除非是第一句是this(),调用了本类的其他构造方法,且该方法第一句是super。
- 如果父类没有不带参数的构造方法,且子类的构造方法中又没显示的调用父类的有参构造方法,则Java编译器将报告错误。
2. 抽象类
public abstract class Demo
{
public Demo() {
name = "demo";
}
private String name;
public abstract String showName();
}
- 抽象类不可以被实例化。继承抽象类的子类必须覆盖了所有的抽象方法后,该子类才可以实例化。
- 抽象方法:被abstract修饰,没有具体实现,如
public abstract void fn(int a);
。 - 抽象类中有构造函数。抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
抽象关键字不可以和那些关键字共存?
- private 不行 (私有后不能被子类继承)
- static 不行 (没有具体内容不能被创建)
- final 不行 (不能被覆盖)
3. 接口
接口即是只有抽象方法和全局静态常量的特殊抽象类。
访问修饰符 interface 接口名称 [extends 其他的接口名] {
// 声明变量,不写修饰符也是默认为全局静态常量
[public static final] String name;
// 抽象方法,不写也是默认为public abstract
[public abstract] void show();
}
// 接口可以继承多个接口,如
public 接口A extends 接口B,接口C {}
// 一个类可以继承一个类并实现多个接口
public class 子类 extends 父类 implements 接口A,接口B
接口不可以实例化。实现了接口的子类,覆盖了接口中所有的抽象方法后,该子类才可以实例化。否则,这个子类就是一个抽象类。
4. 多态
简单说就是一个对象对应着不同类型。体现就是父类或者接口的引用指向其子类的对象。
多态的好处:提高了代码的扩展性,前期定义的代码可以使用后期的内容。
多态的弊端:前期定义的内容不能使用(调用)后期子类的特有内容。
多态的前提:1,必须有关系,继承,实现。2,要有覆盖方法。
Animal a = new Cat();
向上转型:自动类型提升,Cat对象提升为Animal类型。但是不能使用子类的特有方法。
Cat c = (Cat)a;
向下转型:为了使用子类中的特有方法。
注意:对于转型,自始自终都是子类对象在做着类型的变化。
instanceof:用于判断对象的具体类型。只能用于引用数据类型判断。对象 instanceof 类名
判断对象是否属于该类型,结果为布尔值。
4.1. 多态时创建对象的执行顺序
如果静态代码块内引用了静态变量,则静态变量必须写在静态代码块之前,否则编译失败。非静态变量和构造代码块也一样。
1、父类静态变量和静态代码块(先声明的先执行);
2、子类静态变量和静态代码块(先声明的先执行);
3、父类的变量和代码块(先声明的先执行);
4、父类的构造函数;
5、子类的变量和代码块(先声明的先执行);
6、子类的构造函数。
public class Test {
public static int a = 0;
static {// Step 1
a = 10;
System.out.println("静态代码块在执行a=" + a);
}
{// Step 4
a = 8;
System.out.println("非静态代码块(构造代码块)在执行a=" + a);
}
public Test() {
this("调用带参构造方法1,a=" + a); // Step 2
System.out.println("无参构造方法在执行a=" + a);// Step 7
}
public Test(String n) {
this(n, "调用带参构造方法2,a=" + a); // Step 3
System.out.println("带参构造方法1在执行a=" + a); // Step 6
}
public Test(String s1, String s2) {
System.out.println(s1 + ";" + s2);// Step 5
}
public static void main(String[] args) {
Test t = null;// JVM加载Test类,静态代码块执行
System.out.println("下面new一个Test实例:");
t = new Test();
}
}
执行结果:
静态代码块在执行a=10
下面new一个Test实例:
非静态代码块(构造代码块)在执行a=8
调用带参构造方法1,a=10;调用带参构造方法2,a=10
带参构造方法1在执行a=8
无参构造方法在执行a=8