黑马程序员,Java基础知识四:继承

继承


继承的概念:

当我们在开发中,有多个类存在相同属性和行为时,就将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可。多个类可以称为子类,单独的这个类称为父类或者超类。子类可以直接访问父类中的非私有的属性和行为。通过extends关键字让类与类之间产生继承关系,例如:

class SubDemo extends Demo{
}

继承的出现提高了代码的复用性,它让类与类之间产生了关系,它为多态提供了前提。
 

特点:

1.Java只支持单继承,不支持多继承。也就是说一个类只能有一个父类,不可以有多个父类。

2.Java支持多层继承,形成继承体系。例如:

class A{}
class B extends A {}
class C extends B {}

注意:

1.不要仅仅为了获取其他类中的某个功能而去继承。

2.类与类之间要有所属关系,即A是B的一种,才可以让A去继承B。

super关键字

super和this的用法相同,只是this代表本类引用,而super代表父类引用。

在子类中定义成员时与父类成员名相同时,可以用super进行区分。

而当子类要调用父类构造函数时,可以使用super语句。

重写

重写override,也称复写,或者函数覆盖,是当子类中出现与父类相同的方法时,会出现覆盖的操作。

父类中的私有方法不可以被覆盖。

在子类的覆盖方法中,继续使用被覆盖的方法可以通过super.函数名来获取

注意:

1.只能用静态方法去覆盖另一个静态方法。

2.覆盖时,子类方法权限一定要大于等于父类方法权限,权限修饰符的大小顺序:public > protected > default > private  (其中default是方法的默认权限,不必写出来)

应用:

当子类需要父类的功能,而功能主体子类有自己特有的内容时,可以复写父类中的方法,这样既沿袭了父类的功能,又定义了子类特有的内容。

子类的实例化过程

当我们对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句super();这条语句会访问父类中空参数的构造函数,而且子类中所有的构造函数默认的第一行都是super();
为什么子类一定要访问父类中的构造函数?
因为父类中的数据可以在子类中直接获取,所以当子类对象建立时,需要先查看父类是如何对这些数据进行初始化的。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
当然,我们在子类的构造函数第一行也可以手动指定this语句来访问本类中的其他构造函数,但至少会有一个子类访问到父类的构造函数,导致其他子类也访问到父类的构造函数。

    public class thisClass extends superClass{  
       static String name = "这是儿子";  
       thisClass(){  
        this(0);  
        System.out.println("儿子");  
       }  
       thisClass(int num){  
        super(num);  
        System.out.println("这是子类哦哦哦哦哦哦"+num);  
       }  
    protected void getName(){  
        System.out.println("hello this is your son "+name);  
    }  


     class superClass{  
        String name  = "我是父亲";  
      
        superClass(int num){  
            System.out.println(num);  
        }  
         void getName(){  
            System.out.println(name+"hello好久不见");  
        }  
    }  

    class Extends {  
        public static void main(String[] args){  
            thisClass tc = new thisClass();   
            tc.getName();  
        }  
    }  

在这段代码中,子类thisClass继承了父类superClass,父类中没有默认的空参数构造函数,这时在子类的构造函数中必须指定继承了父类的哪个构造函数,或者用this语句来指定创建了同类中哪一个构造函数,否则,编译无法通过。

final关键字

final可以修饰类,方法和变量。

被final修饰的类不可以被继承。

被final修饰的方法不可以被覆盖。

被final修饰的变量只是一个常量,只能被赋值一次。

内部类只能访问被final修饰的局部变量。

抽象类


抽象类和一般类没有太大的不同。该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西。这些不确定的部分,也是该事物的功能,需要明确出现,但是无法定义主体,通过抽象方法来表示。

抽象类比一般类多个了抽象函数,所以类中可以定义抽象方法,但不可以实例化。


抽象定义:


抽象就是从多个事物中将共性的,本质的内容抽取出来。

例如,狼和狗共性都是犬科,犬科就是抽象出来的概念。


抽象类:


Java中可以定义没有方法体的方法,该方法的具体实现由子类去完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。

抽象方法的由来


多个对象都具有相同的功能,但是功能具体内容有所不同,所以我们在抽取过程中,只抽取了功能定义,并不抽取功能主体,然后这些只有功能声明而没有功能主体的方法就称为抽象方法。

例如:狼和狗都有吼叫的方法,可是吼叫的内容是不一样的。所以抽象出来的犬科虽然有吼叫的功能,但是并不明确吼叫的细节。


抽象类的特点


抽象类和抽象方法必须用abstract关键字来修饰。在类中只要定义了一个abstract方法,这个类也要被定义为abstract。抽象类通过其子类实例化,而子类需要去覆盖掉抽象类中的抽象方法后才可以创建对象,否则该子类也是抽象类。抽象方法只有方法声明,没有方法体,定义在抽象中。格式:

修饰符  abstract  返回值类型  函数名(参数列表);



抽象类不可以被实例化,也就是不可以用new来创建对象。原因如下:

1.抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科就是一个抽象的概念,真正存在的是狼和狗。

2.而且抽象类即使创建了对象,调用抽象方法也没有意义。

 

注意:

1.抽象类中也可以没有抽象方法,这样子仅仅是为了不让该类创建对象。

2.abstract关键字不可以与final关键字共存,被final修饰的类不能有子类,而被abstract修饰的类一定是一个父类。所以它们是无法共存的。

3.abstract关键字不可以与private关键字共存,抽象类中的私有的抽象方法,不被子类所知,就无法被复写。 而抽象方法是必须被复写的。

4.abstract关键字不可以与static关键字共存,如果用static修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法没有运行意义。

5.抽象类也需要构造函数,虽然本身无法被实例化,但它要为它的子类的提供实例的初始化。

抽象类示例:

abstract class Student
{
	abstract void study();
        //抽象类中也可以有不抽象的方法:
        void sleep()
	{
		System.out.println("躺着");
	}
}

class ChongCiStudent extends Student
{
	 void study()
 	{
		System.out.println("chongci study");
	}
}

class BaseStudent extends Student
{
	void study()
	{
		System.out.println("base study");
	}
}

class AdvStudent extends Student
{
	 void study()
	{
		System.out.println("advanced study");
 	}
}

class AbstractDemo 
{
	public static void main(String[] args) 
	{
		new BaseStudent().study();
 	}
}

在父类Student中我们是不需要描述study方法的,具体是个怎样的study就交由子类去重写。

接口


接口定义

我们可以把接口理解为一个特殊的抽象类,当抽象类中的方法都是抽象的时候,那么该类可以通过接口的形式来表示。

class用于定义类
interface 用于定义接口。

在定义接口时,接口中的成员都有固定修饰符,应遵循以下格式:

常量:public static final

方法:public abstract 

接口的特点:

1.接口是对外暴露的规则,是程序的功能扩展。

2.类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。

3.接口与接口之间可以有继承关系,而且一个接口可以继承多个接口。

注意

1.接口是不可以创建对象的,因为有抽象的方法,它需要被子类实现implement,子类对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类。

2.一个类可以实现多个接口,也是对多继承不支持的转换形式,Java支持多实现。

interface Inter
{
	public static final int NUM = 3;
	public abstract void show();
}

interface InterA
{
	public abstract void show();
}

class Demo
{
	public void function(){}
}

class Test extends Demo implements Inter,InterA      //Java支持单继承多实现。
{
	public void show(){}        //在实现接口时要复写接口中所有的抽象方法。
}




interface A
{
	void methodA();
}
interface B 
{
	void methodB();
}

interface C extends B,A   //接口可以多继承
{
	void methodC();
}

class D implements C
{
	public void methodA(){}    //要复写三个方法
	public void methodB(){}
	public void methodC(){}
}

class InterfaceDemo 
{
	public static void main(String[] args) 
	{
		Test t = new Test();
		System.out.println(t.NUM);
		System.out.println(Test.NUM);
		System.out.println(Inter.NUM);

	}
}

多态


多态定义:

可以理解为事物存在的多种体现形态。例如:动物中的猫,狗,多态的出现大大的提高程序的扩展性和后期可维护性。

猫这个对象对应的类型是猫类型。

猫 x = new 猫();

同时猫又是动物中个的一种,也可以把猫称为动物:

动物 y = new 猫();

动物是猫和狗具体事物中抽取出来的父类型,父类的引用指向了自己的子类对象。另外父类的引用也可以接收自己的子类对象。

多态的前提

1.必须是类与类之间有关系。要么继承,要么实现。
2.通常还有一个前提:存在覆盖。


4,多态的弊端:
    提高了扩展性,但是只能使用父类的引用访问父类中的成员。

多态的特点

在多态中成员函数的特点:
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。


在多态中,成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类)。


在多态中,静态成员函数的特点:
无论编译和运行,都参考做左边。

注意

1.当我们使用父类的引用指向自己的子类对象时,它隐式的将类型提升了,我们称之为向上转型。比如:

Animal a = new Cat();   //类型提升,向上转型。

这时候如果要调用子类的特有方法时,要强制将父类的引用转成子类类型,我们称之为向下转型。比如:

<pre name="code" class="java">Animal a = new Cat();
Cat c = (Cat)a;

 一般在做这个操作前,会先判断这个父类引用是否指向了这一个子类对象,用instanceOf,如果是,才可以向下转型,否则编译失败。例如: 

<pre name="code" class="java"><pre name="code" class="java">Animal a = new Cat();

 if(a instanceof Cat){ 
   Cat c = (Cat)a;
  System.out.println("成功的向下转型");}
 2.多态自始至终都是子类对象在做着变化 
class animal {
	void eat() {
	}
}

class dog extends animal {
	void eat() {
		System.out.println("啃骨头");
	}

	void kanMen() {
		System.out.println("看门ing");
	}
}

class tiger extends animal {
	void eat() {
		System.out.println("吃动物");
	}

	void catchAnimal() {
		System.out.println("抓动物来吃");
	}
}

class cat extends animal {
	void eat() {
		System.out.println("吃鱼");
	}

	void catchMouse() {
		System.out.println("抓老鼠");
	}
}

public class duotai {
	public static void main(String[] args) {
		animal xiaomao = new cat();
		xiaomao.eat();
		function(xiaomao);
		animal xiaogou = new dog();
		xiaogou.eat();
		function(xiaogou);
		toEat(xiaomao);
		toEat(xiaogou);
	}
	public static void toEat(animal a ){
		a.eat();
	}
	public static void function(animal a) {
		if (a instanceof cat) {
			cat c = (cat) a;
			c.catchMouse();
		} else if (a instanceof dog) {
			dog c = (dog) a;
			c.kanMen();
		} else if (a instanceof tiger) {
			tiger c = (tiger) a;
			c.catchAnimal();
		}
	}

}

内部类


内部类的定义

将一个类定义在另一个类里面,对里面那个类就称为内部类,也称内置类或者嵌套类。

当描述事物时,事物的内部还有事物,该事物用内部类来描述,因为内部事务在使用外部事物的内容。

内部类的访问规则:

1,内部类可以直接访问外部类中的成员,包括私有。
    之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this
2,外部类要访问内部类,必须建立内部类对象。

内部类的特点

(注意,这里的outer和Iner分别指的是外部类和内部类)

1、内部类可以直接访问外部类的成员,包括私有。

2、外部类要访问内部类的话,要先创建内部类的对象。

3、内部类可以被private修饰。

4、在其他类中想访问内部类,要用此格式:

Outer.Iner.xxx()

或者
new Outer().new Iner();

4、其他类访问内部类的非静态成员:

new Outer.Iner().run();

5、其他类访问内部类的静态成员:

Outer.Iner.run();

(也即不用创建对象即可访问静态方法。)

6、匿名内部类一定是一个匿名子类对象。格式:

new 父类或者接口(){定义子类的内容}。

注意

1.当内部类中定义了静态成员,该内部类必须是static的。

2.注意:用外部类的静态方法访问内部类时,该内部类也必须是静态的。

3.当内部类定义在成员位置上时,它可以被private static成员修饰符修饰,但被static修饰的内部类只能访问外部类中的静态成员。

4.当内部类定义在局部位置上时,它不可以被成员修饰符修饰,可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。

public class OuterAndIner {

	public static void main(String[] args) {

		Outer.Iner in = new Outer().new Iner();
		in.method();
	}

}

class Outer {
	private int x = 3;

	class Iner {
		int x = 5;

		void method() {
			System.out.println("x=" + x);
		}
	}

	void method() {
		Iner in = new Iner();
		in.method();
	}
}


匿名内部类

匿名内部类是内部类的一种简化写法。它其实是一个匿名的子类对象,只是这个对象有点胖,你可以理解为带内容的对象,或者是外部类或者接口的一个带内容的子类匿名对象。(好像更复杂了。。)

定义匿名内部类的前提:

内部类必须是继承一个类或者实现接口。

匿名内部类的格式

new 父类或者接口(){定义子类的内容}

注意

匿名内部类中定义的方法最好不要超过3个。

class neibu {
	void show() {
		System.out.println("匿名内部类 run");
	}
}

class user {
	public static void main(String[] args) {
		new neibu(){
			void heihei(){
				super.show();
				System.out.println("哈哈");
			}
		}.heihei();           //匿名内部类的使用方法1


		function().show();
	}
	static neibu function(){      //匿名内部类的使用方法2
		return new neibu(){
			void show(){
				super.show();
				System.out.println("不仅用了父类方法而且还复写了哦");
			}
		};
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值