JAVA 面试知识点 5 -- 面向对象编程( OOP)之继承(Inheritance)

1. java的继承(Inheritance)

继承是OOP一个重要特性。的主要目的就是代码的re-use,以减少代码量。并且可以很好的理解和管理class们。对java中的继承,了解下面这些要点就差不多了:
1. 继承的基本概念
2. is-a relationship
3. 关键词 extends
4. 构造函数和继承
5. 调用父类方法和变量: super
6. type-casting: upcasting 和 downcasting
7. instanceof
8. override和override的规则 **(hin重要)
9. inner class的继承
10. abstract class 和继承
11. 其他

1. 继承的基本概念

继承是一个允许class继承另一个class的一些通用的fields和methods。几个概念:

  • Super Class: 父类(也被叫做base class or a parent class)。是那个被别人继承自己属性的class。
  • Sub Class: 子类(也被叫做derived class, extended class, or child class)。是继承了另一个class的属性的class。子类也可以添加自己的fields和methods, 也可以覆盖重写(override)父类的方法(不影响父类)。
  • Reusability: 当我们想创建一个新的class的时候,有另一个class包含了一些这个class需要的属性,就可以用继承的方式来re-use代码。
  • JAVA 面试知识点 5 -- 面向对象编程( OOP)之继承(Inheritance)_第1张图片

2. is-a relationship

要好好的理解继承,一定要牢牢的记住,is-a relationship
子类 is-a 父类。
一只猫也是一只动物。

3. 关键词```extends```

一个继承的例子:

//猫是动物,有动物的属性猫都有,所以Cat可以继承Animal
class Cat extends Animal{
	private String color; //猫自己的属性。
	//同时猫也继承了父亲的两个属性: vegetarian和noOfLegs。
	//虽然在父类里是private的,并不能被子类access,但是还是会继承来。
	public Cat(String color) {
		//猫不是吃素的,有四条腿。
		super(false,4); //调用了Animal里面第二个构造函数。
		this.color=color;
	}
	//这类method叫setter和getter
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
}
public class Main 
{ 
    public static void main(String args[]) 
    { 
        Cat cat = new Cat("black");
        System.out.println("Cat color is " + cat.getColor());
        //cat 可以调用父类里的方法。
        //private的field只能通过getter来access
		System.out.println("Cat is Vegetarian?" + cat.isVegetarian());
		System.out.println("Cat has " + cat.getNoOfLegs() + " legs.");
    }
} 

输出:

an animal with 4 legs created
Cat is Vegetarian?false
Cat has 4 legs.
Cat color is black

4. 构造函数和继承

  • 父类里的构造函数不能被子类继承。super()是子类用来调用父类构造函数的方法。 而且只能在子类的构造函数的第一行。
  • 如果要造一个subclass,会先调用父类的构造函数,再调用自己的构造函数super() 就是用来说明要用父类哪个构造函数。
  • 如果在子类的构造函数里没有super(),系统会会调用父类默认 构造函数。如果父类里面没有默认的构造函数,那么子类必须调用相应的构造函数,不然会报错。
     class Animal {
     	//如果添加一个带参数的constructor,那么Animal里面就没有default的constructor了
     	public Animal(boolean vegetarian, int legs){
    			System.out.println("an animal with " + legs + " legs created");
    		}
     }
     class Cat extends Animal{
     	//此时必须有构造函数,如果没有构造函数,那么系统会给一个默认的构造函数。
     	//默认的构造函数会调用父类默认的构造函数,然而Animal里面没有默认构造函数。
    	public Cat(String color) {
    			super(false,4); //这一行是必须的,否则会调用默认的构造函数。
    			this.color=color;
    	}
    }
    

5. 调用父类方法和变量: super

  • super(): 用来调用父类的构造函数。
  • super: 除了用来调用构造函数,还可以直接用super可以在子类里access父类的 非private 的field和method:
    class Animal {
    	private boolean vegetarian;
    	public int noOfLegs; //public的field
    	//....和上面的一样
    	public void setNoOfLegs(int noOfLegs) {
    		this.noOfLegs = noOfLegs;
    	}
    }
    class Cat extends Animal{
    	public Cat() {
    		super(false,4);
    		System.out.println(super.noOfLegs); //可以通过super访问父类的field
    		System.out.println(super.vegetarian);//这一行出错了,因为vegetarian是private
    	}
    	@Override //不要求写,但是如果写了,编译时就可以查出一些低级错误
    	public void setNoOfLegs(int noOfLegs){
        	super.setNoOfLegs(noOfLegs);//调用父亲的方法。
        }
    }
    

6. type-casting: upcasting 和 downcasting

  • 我们可以创建一个子类的实例,然后把它赋值给它的父类的变量。这叫upcasting。 这个过程是自动发生的。来个例子:
    Cat c = new Cat(); //c是一个Cat的实例(c是一只猫)。
    Animal a = c; //也可以把c赋值给Animal类型的变量(c也是一个动物没毛病的)
    Animal a2 = new Cat(); //等同于上面的写法。
    
  • 如果父类的实例赋值给子类的变量,则需要强制转化,这个过程叫downcasting。如果把一个父类强制转化给一个错误的子类,那会抛出异常。来个例子:
    Animal a = new Cat(); // 虽然a是从Cat 创建的,但是被定义为Animal了。
    a.getColor();//这行不合法,complie-time时的错误。
    //因为getColor是Cat的方法,不是Animal的,而a被认为是动物。
    Cat c1 = (Cat) a; //a是个动物,如果需要转换成Cat的实例,需要特别说明。
    //这个可以成功,因为a是从c转换来的。a其实也是只猫
    Animal a2 = new Dog();
    Cat c2 = (Cat) a2; //runtime时抛出ClassCastException
    //因为a2其实是只狗,没办法强制转成猫。
    

7. instanceof

  • instanceof是一个操作符。用来检测某个object是不是属于某一类。比较没节操,没啥限制。
    Cat c = new Cat();
    Animal a = c; // 或者Animal a = new Cat();
    
    boolean flag1 = c instanceof Cat; // true
    boolean flag2 = c instanceof Animal; // true 因为 c 也是个 Animal
    boolean flag3 = a instanceof Cat; //true 因为它的确也是一个猫。
    

8. override和voerride的规则

子类里面可以override父类的方法。加上 @Override annotation 是很重要的。。。以防拼错写个新方法出来。override的规矩太多,以及面试经常考。
例子:

class Animal{
	public void greeting() {
		System.out.println("Hi, I am a Animal");
	}
}
class Cat extends Animal {
	@Override
	public void greeting() {
		System.out.println("Mome, I am a Cat");
	}
}  
public class Main { 
    public static void main(String args[]) { 
        Animal an = new Animal();
        an.greeting(); //Hi, I am a Animal
        Cat cat = new Cat();
        cat.greeting(); //Mome, I am a Cat
        //顺便看一下runtime 类型的意思 instanceof时提到的
        Animal an2 = new Cat(); 
        //虽然an2是个animal,但是被创建的时候是用Cat class,所以调用cat里的方法
        an2.greeting();//Mome, I am a Cat
    }
} 

8.1override规则:

  • method只能在子类里面继承,不能在同一个class里面。
  • 子类参数和父类参数一定要一模一样。谁都不能是谁的subclass
  • 子类的返回类型要和父类的返回类型一样,或者是父类的返回类型的子类
  • access level 只能越来越大。如果父类是public的,子类只能是public的。如果父类是protected,那子类只能是protected或者public。
  • private的方法不能被override。
  • 一个加了final的method不能被override。
  • 一个加了static的method也不能被overrride。但是可以被重新定义。因为static是class level的。跟子类object没关系。
  • 如果子类和父类在同一个包里,子类可以overrride父类的不是private和final的方法。
  • 如果子类和父类不在同一个包里,那子类只可以overrride父类public 或者 protected的非final方法。
  • 子类可以随意抛出uncheck exceptions,不管父类有没有抛出异常。对于checked exception, 子类抛出的异常需要是父类抛出异常的孩子,或者是抛出比父类少的异常。(narrower or fewer)
  • Constructor 不能被 overridde.

9. inner class的继承

  • 一个inner class 可以被另一个属于同一个outter class 的inner class继承:
    class OuterClass {
        class InnerClassOne {
            int x = 10;
            void methodOfInnerClassOne() {
                System.out.println("From InnerClassOne");
            }
        }
     
        class InnerClassTwo extends InnerClassOne
        {
            //一个inner class继承另一个
        }
    }
     
    public class InnerClasses {
        public static void main(String args[]) {
            OuterClass outer = new OuterClass();   //实例化outter class
     
            OuterClass.InnerClassTwo innerTwo = outer.new InnerClassTwo();  //实例化 InnerClassTwo
     
            System.out.println(innerTwo.x);
            innerTwo.methodOfInnerClassOne();
        }
    }
    
  • 一个inner class也可以被外面的class继承。如果是static的inner class,就简单很多,可以直接继承,没任何麻烦事儿。如果不是static 的inner class,那需要在子类里面的构造函数中先造一个outter class的实例出来然后再调用父类的构造函数。因为没有outter class的实例就不能access非static的inner class。
    class OuterClass {
        static class InnerClassOne {}
        class InnerClassTwo {}
    }
    class AnotherClassOne extends OuterClass.InnerClassOne {}
    class AnotherClassTwo extends OuterClass.InnerClassTwo {
        public AnotherClassTwo() {
            new OuterClass().super();
        }
    }
    
  • 当一个outter class作父类的时候,它的inner class不会被子类继承。如果要用子类里的属性,那subclass也需要一个inner class,然后这个inner class去继承父类的inner class。 例如:
    class OuterClass {
        int x;
        void methodOfOuterClass() {
            System.out.println("From OuterClass");
        }
        class InnerClass { //Class as a member
            int y;
        }
    }
    class AnotherClass extends OuterClass {
         //只有field和method被继承了,如果要用inner class 的属性
         //则需要有一个inner class继承父类的inner class
        class AnotherInnerClass extends InnerClass {}
    }
     
    public class InnerClasses {
        public static void main(String args[]) {
            AnotherClass anotherClass = new AnotherClass();  //creating AnotherClass Object
            System.out.println(anotherClass.x);    //accessing inherited field x from OuterClass
            anotherClass.methodOfOuterClass();    //calling inherited method from OuterClass
           //Using the properties of InnerClass
            AnotherClass.AnotherInnerClass anotherInnerClass = anotherClass.new AnotherInnerClass();
            //creating object to AnotherInnerClass
            System.out.println(anotherInnerClass.y);  //accessing inherited field y from InnerClass
        }
    }
    
  • 最后,inner class 也可以继承它的outer class. 没有任何意义但是可以。也是只会继承父类的fields 和method,不会继承自己和其他的inner class。
class OuterClass {
    int x;
    void methodOfOuterClass(){
        System.out.println("From OuterClass");
    }
    class InnerClass extends OuterClass{ //Class as a member
        //only fields and methods are inherited, but not member Inner Classes
    }
    class InnerClassOne{
        //another class as a member
    }
}
 
public class InnerClasses
{
    public static void main(String args[])
    {
        OuterClass outer = new OuterClass();
        OuterClass.InnerClass inner = outer.new InnerClass();  //creating object to InnerClass
        System.out.println(inner.x);   //accesiing inherited field x
        inner.methodOfOuterClass();   //accessing inherited method
    }
}

代码来源: https://javaconceptoftheday.com/inheritance-inner-classes-java/

10. abstract class 和继承

  • abstract class 就是含有一个或者多个abstract 方法的class。abstract class不能跟在new后面,但是可以通过创建子类赋值给它。例如:
    abstract class Base { 
        abstract void fun(); 
    } 
    class Derived extends Base { 
        void fun() { System.out.println("Derived!"); } 
    } 
    class Main { 
        public static void main(String args[]) {  
            // Base b = new Base(); 这一行会引发编译错误。
            //不能跟在new后面,但是可以作为reference。
            Base b = new Derived(); 
            b.fun();  //Derived!
        } 
    } 
    
  • 和普通继承一样,也是需要先call 父类的constructor。
    abstract class Base { 
        Base() { System.out.println("Base Constructor Called"); } 
        abstract void fun(); 
    } 
    class Derived extends Base { 
        Derived() { System.out.println("Derived Constructor Called"); }
    } 
    class Main { 
        public static void main(String args[]) {  
           Derived d = new Derived(); 
        } 
    } 
    
    Base Constructor Called
    Derived Constructor Called
    

11.其他

  1. 主要的好处就是可以reuse代码。
  2. 父类里private的field和method不能被子类访问,但是会被继承,所以cat也可以有是不是素食和几条腿的属性。也可以通过getter和setter访问。
  3. Java 不支持multiple inheritance, 一个子类只有一个父类
  4. 在java里不能继承Final classes
  5. is-a vs has-a

最后,下面的代码创建了几个object?:

class Animal{
}
class Cat extends Animal {
} 
Cat cat = new Cat();

你可能感兴趣的:(java,面试)