1. 实现继承关系
继承需要使用extends关键字。
- 创建父类
/**
* 父类
*/
public class Father {
}
被继承类可称为超类、基类、父类。
- 创建子类,并继承父类
/**
* 子类
*/
public class Son extends Father{
}
继承类成为子类、派生类、孩子类。
继承的作用:子类可以使用父类中的一些成员变量和方法,从而提高代码的重用性,提高开发效率。
父类并非高于子类,恰恰相反,子类往往比父类更加丰富,除了可以调用父类的一些成员变量和方法外,还有自己独有的属性和方法。
2. 子类调用父类的一些成员变量和方法
- 父类
/**
* 父类
*/
public class Father {
// 共有属性
public String name;
// 受保护属性
protected String power;
// 私有属性
private int age;
// 默认属性
boolean tall;
// 共有方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
// 私有方法
private void salary(){
}
// 保护方法
protected String getPower(){
power = "父类的权益";
return power;
}
// 默认方法
void testDefaultMethod(){
}
}
- 子类
/**
* 子类
*/
public class Son extends Father{
/**
* 调用父类的一些成员变量或方法
*/
public void test01(){
// 调用父类共有的属性和方法
name = "父类" + getName();
// 调用父类受保护的属性和方法
String powerStr = power + getPower();
// 调用父类私有的属性和方法
// 'age' has private access in 'Father'
// age;
// 'salary()' has private access in 'Father'
// salary();
// 调用父类默认权限的属性和方法
boolean tall = super.tall;
testDefaultMethod();
}
}
之所以说子类可以调用父类的一些成员变量和方法,是只能调用父类的public、protected、default(没有权限修饰)的成员变量和方法,父类私有(private修饰)的就不能调用。
protected:只可以被本类,子类,同一个包中可以访问。如果是多层继承的话,若是修改protected修饰的属性和方法会很麻烦。
3. 方法重写
当子类重写父类的方法时会使用 @Override关键字标记。
- 子类
@Override
public String getName() {
// todosomething
return super.getName();
}
一般重写父类方法是为了自定义属性。去掉关键字其实就是子类的普通方法,若是还想在该方法中方位父类方法,需要使用super关键字,否则无法区分调用的是哪一个类中的方法,可能出现死循环问题。
public String getName() {
return "子类" + super.getName();
}
方法重写有一定的规则:
- 重写的方法参数要完全相同。
- 返回类型可以一样,也可以是父类返回值的子类,其他的都不可以。
- 访问权限不能比父类低,如果父类是protected,子类只能是public或protected,不能是其他的。
- 重写的方法抛出的异常只是父类抛出的异常的子类或本身,不能是其他的。
- 构造方法不能被重写。
- final和private方法不能被重写,当然也写不到。
- 子类只能重写父类的方法,显而易见的,这是重写的前提,也算是废话。
4. 构造方法
- 父类构造
public Father() {
System.out.println("这是父类的构造");
}
- 子类构造
public Son() {
System.out.println("这是子类的构造");
}
- 测试
public static void main(String[] args) {
Son son = new Son();
}
- 结果
S:\JDK16\bin\java.exe "-javaagent:S:\IntelliJIDEA\IntelliJ IDEA 2021.3.3\lib\idea_rt.jar=49229:S:\IntelliJIDEA\IntelliJ IDEA 2021.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\java\extend\out\production\extend Test
这是父类的构造
这是子类的构造
如果子类构造没有显式调用父类的构造方法,则会自动调用父类的无参构造。
- 父类
public class Father {
// public Father() {
// System.out.println("这是父类的构造");
// }
public Father(String name) {
System.out.println("这是父类的有参构造");
}
}
- 子类
public class Son extends Father{
public Son() {
Father father = new Father("father");
System.out.println("这是子类的构造");
}
}
- 编译
如果父类中没有无参构造,并且在子类中又显式调用父类的其他构造,则编译报错。但是如果使用super关键字就没有问题。
public Son() {
// Father father = new Father("father");
super("father");
System.out.println("这是子类的构造");
}
- 编译
S:\JDK16\bin\java.exe "-javaagent:S:\IntelliJIDEA\IntelliJ IDEA 2021.3.3\lib\idea_rt.jar=52386:S:\IntelliJIDEA\IntelliJ IDEA 2021.3.3\bin" -Dfile.encoding=UTF-8 -classpath F:\java\extend\out\production\extend Test
这是父类的有参构造
这是子类的构造
5. 继承层次
Java中支持多层继承,但不支持多继承。
- 多层继承
public class Son extends Father{}
public class Grandson extends Son{
}
Grandson继承Son,Son继承Father。若是多继承则会报错。
Java类不支持多继承,但接口可以多继承。
public interface Interface01 {
}
...
public interface Interface02 {
}
...
// 接口多继承
public class Test implements Interface01, Interface02 {
...
}
...
6. 多态
父类引用指向子类对象称为多态。
// 多态
Father father = new Son();
置换法则:程序中任何出现父类对象的地方都可以用子类对象替换。
Father[] fathers = new Father[10];
fathers[0] = new Son();
fathers = new Son[10];
即使父类数组被全部置换,但申明的还是父类,所以获取的对象还是父类,无法访问子类对象特有的属性和方法,也不能赋值给子类变量。
// 置换法则
Father[] fathers = new Father[10];
fathers[0] = new Son();
// 无需强转
fathers = new Son[10];
// 得到的只能是父类,不是子类
Father father1 = fathers[0];
// 非法
Son son1 = fathers[0];
// 强转
Son son2 = (Son) fathers[0];
强转会忽略对象实际类型,使用对象的全部功能,所以强转之前最好判断一下类型。
Father[] fathers = new Father[10];
fathers[0] = new Son();
// 类型判断
if(fathers[0] instanceof Son){
Son son1 = (Son) fathers[0];
}
7. 方法重载
重载就是在一个类中,方法名相同,参数不同,返回值可以不同,也可以相同,多用于构造函数。
public Father() {
System.out.println("这是父类的构造");
}
public Father(String name) {
System.out.println("这是父类的有一个参的构造");
}
public Father(String name, String power) {
this.name = name;
this.power = power;
System.out.println("这是父类的有两个参的构造");
}
public Father(String name, String power, int age) {
this.name = name;
this.power = power;
this.age = age;
System.out.println("这是父类的有三个参的构造");
}
// 无参
private void test(){
}
// int 参
private int test(int a){
return -1;
}
// 参数名相同,类型不同
private int test(boolean a){
return -1;
}
private String test(String str,int a){
return str;
}
// 改变参数位置
private String test(int a,String str){
return str;
}
// 只改变返回值类型,不能算重载
// private int test(String str,int a){
// return a;
// }
8. final
使用final类和方法,可以阻止继承。
public final class FinalTest {
void test() {
}
}
尝试继承final类。
final方法也是不能被重写的。
public class FinalTest {
final void test() {
}
}
尝试重写final方法。
Java中基本数据类型和String都是final类,为了防止被继承。
总结
- 继承时将公共方法和属性放在父类。
- 不要过度使用protected。同一个包中所有类都可以访问protected,不管是不是其子类,此时使用protected作用不大。其次,任何一个类都可能是某一个类的子类,都能直接访问protected,破环了封装性。
- 继承不要滥用,尽量在”is-a“的关系中使用继承。
- 如果不是继承的所有的方法都有意义,就不要使用继承。
- 对于类型信息的判断,要是有继承关系,尽量的使用多态。
- 反射不要滥用,因为反射只有运行时才会报错,很难发现错误。
- 重写的方法不要改变其原有作用,就是重写后的作用要和重写之前的作用差不多,若是相差太多还不如新写新的方法。