嵌入式之路,贵在日常点滴

                                                                ---阿杰在线送代码

目录

一、背景

二、继承的概念和实现

extends关键字 

super关键字

继承过来的属性和方法的权限研究

方法重写Override

三、Object类常见方法

toString()

常把toString()方法重写后应用

equals()

四、继承实例 

五、继承类型 


一、背景

下面C语言的代码中定义了两个在内容上高度重复的结构体

#include <stdio.h>
 
struct Person  //人
{
	int name;
	char* address;
	void (*pEat)();
	void (*pDrink)();
};
 
struct Student //学生也是人,所以有一定的重复性
{             
	int name;
	char* address;
	void (*pEat)();
	void (*pDrink)();
 
	int score; //学生独有的特征,有分数,要上学
	void (*goToSchool)();
};

然而对于C语言来说这两个结构体没有任何联系,这样造成了代码的浪费,重复。

而java的继承特性,就解决了这一问题。

二、继承的概念和实现

生活中,继承的概念随处可见。

比如:

  • java继承背后的思想是:基于已存在的类来构建新类。

  • 当从已存在类继承时,就重用了他的方法和属性,还可以添加新的方法和属性来定制新类以应对要求。

  • 约定:

            从其他类导出的类叫做子类;

            被导出的类叫做父亲

  • 父子类关系通常父类更通用更抽象,子类更特殊更具体

  • 在java中,除了object外,所有类都是子类,都只有唯一的父类,即为object。

  •  继承的意义:代码重用,体现不同抽象层次

继承在OOP(Object-Oriented Program面向对象编程 )中不可或缺,创建一个类时,总是在继承 

extends关键字 

在java语言中,用extends关键字来表示一个类继承了另一个类,如Student继承了Person 

public class Student extends Person{
...
}
class Person {
    String name;
    String address;
 
    void eat() {
        System.out.println("人吃饭");
    }
    void drink() {
        System.out.println("人喝水");
    }
}
 
class Student extends Person{ //可以直接使用Person中的属性了
    int score;
    void goToSchool() {
        System.out.println("学生要上学");
    }
}
 
public class Demo1 {
    public static void main(String[] args) {
        Person p = new Person();
        p.name = "人";
        p.eat();
 
        Student s = new Student();
        s.name = "学生";  //person中的特征
        s.eat();         //person中的方法
    }
}

super关键字

super和this关键字的特点类似:super代表的是对父类对象的引用

作用:

(1)当子父类的成员出现同名时,可以通过super来区分
(2)子类的构造方法中,通过super关键字调用父亲的构造方法

强调:

(1)当构造一个子类对象的时候一定会先调用父类的构造方法来构造父类的对象。
(2)调用父类构造方法的语句必须是子类构造方法中的第一条指令

class Person {
    String name;
    String address;
 
    void eat() {
        System.out.println("人吃饭");
    }
    void drink() {
        System.out.println("人喝水");
    }
 
    //Person(){}//解决方法一:无参的构造方法
    Person(String name,String address){
        this.name = name;
        this.address = address;
        System.out.println("父类构造方法被调用");
    }
}
 
class Student extends Person{ //可以直接使用Person中的属性了
    int score;
    void goToSchool() {
        System.out.println("学生要上学");
    }
 
    /*
    Student(){ //如果不写构造方法的话,系统分配类似于这样的无参的构造方法
               //如果父类写了构造方法而子类没有“对应的”构造方法,就会出错
                //解决方法一:在父类中写一个无参的构造方法;
                //解决方法二:应用super关键字,在子类中完成对父类构造方法的引用
    }*/         //父类可能有多个构造方法,子类只需要引用一个初始化自己就好
 
    //解决方法二:
    Student(String name,String address){ //super重要用法
        super(name, address); //调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
        System.out.println("子类构造方法被调用");
    }
 
    public void eat(){ //因为父类已经有了方法eat,这里权限不能降低,所以加public,后面讲多态会介绍:方法的重写
        super.eat(); //调用父类的eat,super是对父类的引用。
                     //子类中有eat,父类也有同名的方法,如何区分是父类还是子类的eat,用super!
        System.out.println("student eat");
    }
}
 
public class Demo {
    public static void main(String[] args) {
    	//Person r = new Person("alin","yezaijia");
    	//System.out.println("r.name:"+r.name);
 
    	//在实例化一个对象的时候,子类的构造方法调用会导致父类的构造方法也被调用:
        Student s = new Student("ajie","zaijai");
        s.eat();
        System.out.println("s.name:"+s.name);
        //System.out.println("r.name:"+r.name);
    }
}

运行结果:

父类构造方法被调用
子类构造方法被调用
人吃饭
student eat
s.name:ajie 

可当

public class Demo {
    public static void main(String[] args) {
        Person r = new Person("alin","yezaijia");
        System.out.println("r.name:"+r.name);

        //在实例化一个对象的时候,子类的构造方法调用会导致父类的构造方法也被调用:
        Student s = new Student("ajie","zaijai");
        s.eat();
        System.out.println("s.name:"+s.name);
        System.out.println("r.name:"+r.name);
    }

其运行结果为:

r.name:alin
父类构造方法被调用
子类构造方法被调用
人吃饭
student eat
r.name:alin
s.name:ajie 

前面我们提过:构造一个子类对象的时候一定会先调用父类的构造方法来构造父类的对象。 

但是 你观察一下其父类初始化的值和得到的结果值! 

继承过来的属性和方法的权限研究

重点看看父类中的私有属性private会不会被子类继承,如果可以,显然其他的默认属性,public等肯定也可以。

class Person {
    String name;
    private String address;  //私有的属性
 
    void eat() {
        System.out.println("人吃饭");
    }
 
    Person(String name,String address){
        this.name = name;
        this.address = address;
        System.out.println("父类构造方法被调用");
    }  
    
    public void printname(){
    	System.out.println("name:"+name);
    }
    
    public void printaddr(){
    	System.out.println("address:"+address);
    }
    
    //private void printaddr(){//主函数的s.printaddr就会报错,表明私有方法无法被子类继承
    //	System.out.println("address:"+address);
    //}
}
 
class Student extends Person{
    int score;
    void goToSchool() {
        System.out.println("学生要上学");
    }
 
    Student(String name,String address){
        super(name, address); //调用了父类的构造方法
        System.out.println("子类构造方法被调用");
    }
    /*
    void setAddress(String address){
        this.address = address; //这里提示了address是父类私有的,说明实际上私有属性没有被继承过来                
                               //不给你用,父亲”私藏的“所有东西儿子都是不知道的,继承不到
    }
     */
}
 
public class Test {
	public static void main(String[] args) {
		Student s = new Student("ajie","zaijia");
		s.printname();
		s.printaddr();
	}
}

运行结果:

父类构造方法被调用
子类构造方法被调用
name:ajie
address:zaijia 

结论:父类私有的无论是方法还是属性,都无法被子类继承。 

方法重写Override

方法重写是指子类可以根据需要对从父类继承过来的方法进行改写,是多态机制的前奏。

(1)重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值
(2)重写方法不能比被重写方法有更严格的访问权限
(public>protected>default>private)。
(3)父类中的私有方法,不能被重写
(4)在子类重写的方法中继承调用父类被重写的方法可以通过super.函数名获取
 

注意区别:前面java的面向对象基础(1) —— 封装提到的方法重载是同一方法名,不同参数列表)  ----面试官可能比较喜欢问这个

class Person
{
    String name;
    private String address;
 
    public void printName(){
        System.out.println("父类name="+name);
    }
 
    private void printAddress(){   //父类的私有方法,想要"重写"这个方法,子类中的方法不能是private
        System.out.println("address="+address);
    }
}
 
class Student extends Person
{
    int score;
    
    //(1)重写方法必须和被重写方法具有相同的名称、参数列表和返回值
    //(2)重写方法不能比被重写方法有更严格的访问权限
    public void printName() {   
    	super.printName();//想继续调用父类被重写的方法
        System.out.println("子类name=" + name); //方法的实现改了,也是重写啊,别看就改了一点
    }
 
    public void printAddress(){             
        System.out.println("想用你的私有方法");
    }
    //如果这里用的是private,那下面的stu1.printAdd就会报错
    //注意:如果父类方法权限是private,子类不能用private去重写这个方法
    //这对于子类来说,父类是不可见的,是看不到父类的方法的
    //所以这对于子类来说,就是构造了一种新的方法,既不是方法重写也不是方法重载!
    //是不是重写,就概念的东西,初学,也不必争辩,知道这么用就行
}                                           
                                            
public class Demo1 {
    public static void main(String[] args){
        Student stu1 = new Student();
        stu1.name = "ajie";
        stu1.printName();
        stu1.printAddress();   
    }
}

运行结果:

父类name=ajie
子类name=ajie
想用你的私有方法

三、Object类常见方法

java中,所有类都直接或间接继承自java.lang.Object类,是所有类的根类。如hashcode()、clone()、getClass()、finalize()。

重点介绍下面两类toString()equals() 

toString()

做个简单的测试:

package com.ajie.learn;
 
class Person
{
    String name;
}
 
public class Demo1 {
    public static void main(String[] args) {
        Person p = new Person();
        p.name = "huatianzhu";
 
        System.out.println(p.toString());
    }
}

运行结果:

com.ajie.learn.Person@659e0bfd 

为什么会是上面的结果?

返回格式:

getClass().getName()+"@"+Integer.toHexString(hashCode());

(1)getClass().getName()代表返回对象所属类的包名.类名,即com.ajie.learn.Person
(2)Integer.toHexString(hashCode())代表将对象的哈希值用16进制表示,其中hashCode()代表返回该对象的哈希值。 

常把toString()方法重写后应用

然而,在实际开发中,通常希望toString()方法返回的不只是上面的信息,所以Object的toString()方法通常会被重写,如下:运行结果:使得toString()根据实际需求打印出信息

package com.ajie.learn;
 
class Person
{
    String name;
    String address;
    
    public String toString() {
    	// TODO Auto-generated method stub
    	return ("Person message:" +name +address);
    }
    
}
 
public class Demo1 {
    public static void main(String[] args) {
        Person p = new Person();
        p.name = "ajie";
        p.address = "zaijia";
 
        System.out.println(p.toString());
    }
}

运行结果:使得toString()根据实际需求打印出信息 

Person message:ajiezaijia 

    public String toString() {
        // TODO Auto-generated method stub
        return ("Person message:" +name +address);
    }

 toString+ALT+/

equals()

1.equals方法没有重写的话,用于判断对象的内存地址引用是否是用一个地址。重写之后一般用来比较对象的内容是否相等(比如Person对象,里面有姓名和地址)

没有重写,用于判断对象的内存地址引用是否是用一个地址

package com.ajie.learn;
 
class Person
{
    String name;
    String address;    
}
 
public class Demo1 {
    public static void main(String[] args) {
        Person p = new Person();
        p.name = "ajie";
        p.address = "zaijia";
        
        Person p2 = new Person();
        p.name = "ajie";
        p.address = "zaijia";
 
        System.out.println(p.equals(p2));
    }
}

运行结果:

false 

重写之后一般用来比较对象的内容是否相等 

package com.ajie.learn;
 
class Person
{
    String name;
    String address;
    
    public boolean equals(Person p) {
    	if(this.address == p.address && this.name == p.name)
            return true;
        else
            return false;
    }
}
 
public class Demo1 {
    public static void main(String[] args) {
        Person p = new Person();
        p.name = "ajie";
        p.address = "zaijia";
        
        Person p2 = new Person();
        p2.name = "ajie";
        p2.address = "zaijia";
 
        System.out.println(p.equals(p2));
    }
}

运行结果:

true 

四、继承实例 

活用方法重写

class Wapon
{
	String name;
	
	void waponAttack()
	{
		System.out.println("武器攻击");
	}
}
 
class K98 extends Wapon
{
	void waponAttack()//方法重写是多态机制的前奏
	{
		System.out.println("98k攻击");
	}
}
 
class KouShui extends Wapon
{
	void waponAttack()
	{
		System.out.println("口水攻击");
	}
}
 
class Play
{
	String name;
	String duanwei;
	Wapon wapon;
	int id;
	
	void attack()
	{
		System.out.println("玩家攻击");
		wapon.waponAttack();
	}
}
 
public class Chiji {
	public static void main(String[] args) {
		Play p1 = new Play();
		
		p1.name = "玩家一";
		p1.duanwei = "青铜";
		
		p1.wapon = new KouShui();//多态
		p1.attack();
		
		p1.wapon = new K98();//多态
		p1.attack();
	}
}

运行结果:

玩家攻击
口水攻击
玩家攻击
98k攻击 

五、继承类型 

需要注意的是 Java 不支持多继承,但支持多重继承。